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