Package gui :: Package components :: Module base_list
[hide private]
[frames] | no frames]

Source Code for Module gui.components.base_list

  1  ############################################################################### 
  2  #                                                                             # 
  3  # Copyright (C) 2009-2010 Michael Bieri                                       # 
  4  # Copyright (C) 2009-2013 Edward d'Auvergne                                   # 
  5  #                                                                             # 
  6  # This file is part of the program relax (http://www.nmr-relax.com).          # 
  7  #                                                                             # 
  8  # This program is free software: you can redistribute it and/or modify        # 
  9  # it under the terms of the GNU General Public License as published by        # 
 10  # the Free Software Foundation, either version 3 of the License, or           # 
 11  # (at your option) any later version.                                         # 
 12  #                                                                             # 
 13  # This program is distributed in the hope that it will be useful,             # 
 14  # but WITHOUT ANY WARRANTY; without even the implied warranty of              # 
 15  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               # 
 16  # GNU General Public License for more details.                                # 
 17  #                                                                             # 
 18  # You should have received a copy of the GNU General Public License           # 
 19  # along with this program.  If not, see <http://www.gnu.org/licenses/>.       # 
 20  #                                                                             # 
 21  ############################################################################### 
 22   
 23  # Module docstring. 
 24  """Module containing the base GUI element for listing things.""" 
 25   
 26  # Python module imports. 
 27  import wx 
 28  import wx.lib.buttons 
 29   
 30  # relax module imports. 
 31  import dep_check 
 32  from gui.components.menu import build_menu_item 
 33  from gui.fonts import font 
 34  from gui.misc import add_border 
 35  from gui.string_conv import str_to_gui 
 36  from gui.uf_objects import Uf_storage; uf_store = Uf_storage() 
 37  from status import Status; status = Status() 
 38  from user_functions.data import Uf_info; uf_info = Uf_info() 
 39   
 40   
41 -class Base_list(object):
42 """The GUI element for listing the software used in the analysis.""" 43
44 - def __init__(self, gui=None, parent=None, box=None, id=None, proportion=0, button_placement='default'):
45 """Build the base list GUI element. 46 47 @keyword gui: The main GUI object. 48 @type gui: wx.Frame instance 49 @keyword parent: The parent GUI element that this is to be attached to. 50 @type parent: wx object 51 @keyword box: The box sizer to pack this GUI component into. 52 @type box: wx.BoxSizer instance 53 @keyword id: A unique identification string. This is used to register the update method with the GUI user function observer object. 54 @type id: str 55 @keyword proportion: The window proportion parameter. 56 @type proportion: bool 57 @keyword button_placement: Override the button visibility and placement. The value of 'default' will leave the buttons at the default setting. The value of 'top' will place the buttons at the top, 'bottom' will place them at the bottom, and None will turn off the buttons. 58 @type button_placement: str or None 59 """ 60 61 # Store the arguments. 62 self.gui = gui 63 self.parent = parent 64 self.proportion = proportion 65 66 # Variables to be overridden. 67 self.title = "" 68 self.spacing = 5 69 self.border = 5 70 self.observer_base_name = None 71 self.columns = [] 72 self.button_placement = None 73 self.button_size = (120, 40) 74 self.button_spacing = 5 75 self.button_info = [] 76 self.popup_menus = [] 77 78 # Override these base values. 79 self.setup() 80 81 # Button placement second override on initialisation. 82 if button_placement != 'default': 83 self.button_placement = button_placement 84 85 # First create a panel (to allow for tooltips on the buttons). 86 self.panel = wx.Panel(self.parent) 87 box.Add(self.panel, self.proportion, wx.ALL|wx.EXPAND, 0) 88 89 # Add a sizer to the panel. 90 panel_sizer = wx.BoxSizer(wx.VERTICAL) 91 self.panel.SetSizer(panel_sizer) 92 93 # A static box to hold all the widgets, and its sizer. 94 self.data_box = wx.StaticBox(self.panel, -1) 95 self.set_box_label() 96 self.data_box.SetFont(font.subtitle) 97 sub_sizer = wx.StaticBoxSizer(self.data_box, wx.VERTICAL) 98 99 # Add the sizer to the static box and the static box to the main box. 100 panel_sizer.Add(sub_sizer, self.proportion, wx.ALL|wx.EXPAND, 0) 101 102 # Add a border. 103 box_centre = add_border(sub_sizer, border=self.border) 104 105 # Add buttons to the top. 106 if self.button_placement == 'top': 107 self.add_buttons(box_centre) 108 box_centre.AddSpacer(self.spacing) 109 110 # Initialise the element. 111 self.init_element(box_centre) 112 113 # Build the element. 114 self.build_element() 115 116 # Add buttons to the bottom. 117 if self.button_placement == 'bottom': 118 box_centre.AddSpacer(self.spacing) 119 self.add_buttons(box_centre) 120 121 # Initialise observer name. 122 if self.observer_base_name: 123 self.name = '%s: %s' % (self.observer_base_name, id) 124 else: 125 self.name = id 126 127 # Register the element for updating when a user function completes. 128 self.observer_register()
129 130
131 - def Enable(self, enable=True):
132 """Enable or disable the element. 133 134 @keyword enable: The flag specifying if the element should be enabled or disabled. 135 @type enable: bool 136 """ 137 138 # Call the buttons' methods. 139 for i in range(len(self.button_info)): 140 # Get the button. 141 button = getattr(self, self.button_info[i]['object']) 142 143 # Call the botton's method. 144 button.Enable(enable)
145 146
147 - def add_buttons(self, sizer):
148 """Add the buttons for manipulating the data. 149 150 @param sizer: The sizer element to pack the buttons into. 151 @type sizer: wx.BoxSizer instance 152 """ 153 154 # Button Sizer 155 button_sizer = wx.BoxSizer(wx.HORIZONTAL) 156 sizer.Add(button_sizer, 0, wx.ALL|wx.EXPAND, 0) 157 158 # Loop over the buttons. 159 for i in range(len(self.button_info)): 160 # The button. 161 button = wx.lib.buttons.ThemedGenBitmapTextButton(self.panel, -1, None, self.button_info[i]['label']) 162 button.SetBitmapLabel(wx.Bitmap(self.button_info[i]['icon'])) 163 164 # Format. 165 button.SetFont(font.normal) 166 button.SetMinSize(self.button_size) 167 168 # Add to the sizer. 169 button_sizer.Add(button, 0, 0, 0) 170 171 # Bind the method. 172 self.parent.Bind(wx.EVT_BUTTON, self.button_info[i]['method'], button) 173 174 # Set the tooltip. 175 button.SetToolTip(wx.ToolTip(self.button_info[i]['tooltip'])) 176 177 # Store as a class object. 178 setattr(self, self.button_info[i]['object'], button) 179 180 # Spacing. 181 if self.button_spacing: 182 button_sizer.AddSpacer(self.button_spacing)
183 184
185 - def build_element(self):
186 """Build the grid.""" 187 188 # Execution lock, so do nothing. 189 if status.exec_lock.locked(): 190 return 191 192 # Build the GUI element in a thread safe way. 193 wx.CallAfter(self.build_element_safe)
194 195
196 - def build_element_safe(self):
197 """Build the spectra listing GUI element in a thread safe wx.CallAfter call.""" 198 199 # First freeze the element, so that the GUI element doesn't update until the end. 200 self.element.Freeze() 201 202 # Update the label if needed. 203 self.set_box_label() 204 205 # Delete the previous data. 206 self.element.DeleteAllItems() 207 208 # Update the data. 209 self.update_data() 210 211 # Size the columns. 212 self.size_cols() 213 214 # Post a size event to get the scroll panel to update correctly. 215 event = wx.PyCommandEvent(wx.EVT_SIZE.typeId, self.parent.GetId()) 216 wx.PostEvent(self.parent.GetEventHandler(), event) 217 218 # Set the minimum height. 219 if not self.proportion: 220 # The number of rows. 221 n = self.element.GetItemCount() 222 223 # Size of the header, plus a bit. 224 head = self.height_char + 10 225 226 # Size of the table central element. 227 centre = (self.height_char + 6) * n 228 229 # Size of the scrollbar for the end of the table. 230 foot = wx.SystemSettings.GetMetric(wx.SYS_HSCROLL_Y) 231 232 # Sum. 233 height = head + centre + foot 234 235 # Set the minimum size, and force a redraw. 236 self.element.SetMinSize((-1, height)) 237 self.element.Layout() 238 239 # Unfreeze. 240 self.element.Thaw()
241 242
243 - def delete(self):
244 """Unregister the class.""" 245 246 # Unregister the observer methods. 247 self.observer_register(remove=True) 248 249 # Destroy any wizards present. 250 if hasattr(self, 'wizard'): 251 self.wizard.Destroy() 252 del self.wizard
253 254
255 - def generate_popup_menu(self, id=None):
256 """Create and return the popup menu. 257 258 @keyword id: The ID string for the row that was clicked on. 259 @type id: str 260 @return: The popup menu. 261 @rtype: list of dict of wxID, str, str, method 262 """ 263 264 # Default to returning the first initialised menu. 265 return self.popup_menus
266 267
268 - def init_element(self, sizer):
269 """Initialise the GUI element. 270 271 @param sizer: The sizer element to pack the element into. 272 @type sizer: wx.BoxSizer instance 273 """ 274 275 # The list. 276 self.element = wx.ListCtrl(self.panel, -1, style=wx.BORDER_SUNKEN|wx.LC_REPORT) 277 278 # Initialise the columns. 279 for i in range(len(self.columns)): 280 self.element.InsertColumn(i, str_to_gui(self.columns[i])) 281 282 # Properties. 283 self.element.SetFont(font.normal) 284 285 # Store the base heights. 286 self.height_char = self.element.GetCharHeight() 287 288 # Bind some events. 289 self.element.Bind(wx.EVT_SIZE, self.resize) 290 self.element.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.on_right_click) # For wxMSW! 291 self.element.Bind(wx.EVT_RIGHT_UP, self.on_right_click) # For wxGTK! 292 293 # Add list to sizer. 294 sizer.Add(self.element, self.proportion, wx.ALL|wx.EXPAND, 0)
295 296
297 - def is_complete(self):
298 """Base method which always returns True. 299 300 @return: The answer to the question. 301 @rtype: bool 302 """ 303 304 # Assume everything is complete. 305 return True
306 307
308 - def observer_register(self, remove=False):
309 """Register and unregister methods with the observer objects. 310 311 @keyword remove: If set to True, then the methods will be unregistered. 312 @type remove: False 313 """ 314 315 # Register. 316 if not remove: 317 status.observers.gui_uf.register(self.name, self.build_element, method_name='build_element') 318 status.observers.pipe_alteration.register(self.name, self.build_element, method_name='build_element') 319 320 # Unregister. 321 else: 322 status.observers.gui_uf.unregister(self.name) 323 status.observers.pipe_alteration.unregister(self.name)
324 325
326 - def on_right_click(self, event):
327 """Pop up menu for the right click. 328 329 @param event: The wx event. 330 @type event: wx event 331 """ 332 333 # Obtain the position. 334 pos = event.GetPosition() 335 336 # Hack to allow the test suite to pass. 337 wx.Yield() 338 339 # Find the item clicked on. 340 item, flags = self.element.HitTest(pos) 341 342 # Get the ID string (handling wxPython 2.9 ListCtrl.HitTest() bugs). 343 id = None 344 if item != -1: 345 id = self.element.GetItemText(item) 346 347 # Get the menu. 348 popup_menus = self.generate_popup_menu(id=id) 349 350 # No popup menus defined. 351 if popup_menus == []: 352 return 353 354 # Execution lock, so do nothing. 355 if status.exec_lock.locked(): 356 return 357 358 # Initialise the menu. 359 menu = wx.Menu() 360 361 # Loop over the menu items. 362 for i in range(len(popup_menus)): 363 # Alias. 364 info = popup_menus[i] 365 366 # Add the menu item. 367 build_menu_item(menu, id=info['id'], text=info['text'], icon=info['icon']) 368 369 # Bind clicks. 370 self.element.Bind(wx.EVT_MENU, info['method'], id=info['id']) 371 372 # Pop up the menu. 373 if status.show_gui: 374 self.element.PopupMenu(menu) 375 376 # Cleanup. 377 menu.Destroy()
378 379
380 - def resize(self, event):
381 """Catch the resize to allow the element to be resized. 382 383 @param event: The wx event. 384 @type event: wx event 385 """ 386 387 # Set the column sizes. 388 self.size_cols() 389 390 # Continue with the normal resizing. 391 event.Skip()
392 393
394 - def set_box_label(self):
395 """Set the label of the StaticBox.""" 396 397 # Set the label. 398 self.data_box.SetLabel(self.title)
399 400
401 - def size_cols(self):
402 """Set the column sizes.""" 403 404 # The element size. 405 x, y = self.element.GetSize() 406 407 # Number of columns. 408 n = self.element.GetColumnCount() 409 410 # Set to equal sizes. 411 if n == 0: 412 width = x 413 else: 414 width = int(x / n) 415 416 # Set the column sizes. 417 for i in range(n): 418 self.element.SetColumnWidth(i, width)
419