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-2011 Michael Bieri                                       # 
  4  # Copyright (C) 2010-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
249 - def generate_popup_menu(self, id=None):
250 """Create and return the popup menu. 251 252 @keyword id: The ID string for the row that was clicked on. 253 @type id: str 254 @return: The popup menu. 255 @rtype: list of dict of wxID, str, str, method 256 """ 257 258 # Default to returning the first initialised menu. 259 return self.popup_menus
260 261
262 - def init_element(self, sizer):
263 """Initialise the GUI element. 264 265 @param sizer: The sizer element to pack the element into. 266 @type sizer: wx.BoxSizer instance 267 """ 268 269 # The list. 270 self.element = wx.ListCtrl(self.panel, -1, style=wx.BORDER_SUNKEN|wx.LC_REPORT) 271 272 # Initialise the columns. 273 for i in range(len(self.columns)): 274 self.element.InsertColumn(i, str_to_gui(self.columns[i])) 275 276 # Properties. 277 self.element.SetFont(font.normal) 278 279 # Store the base heights. 280 self.height_char = self.element.GetCharHeight() 281 282 # Bind some events. 283 self.element.Bind(wx.EVT_SIZE, self.resize) 284 self.element.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.on_right_click) # For wxMSW! 285 self.element.Bind(wx.EVT_RIGHT_UP, self.on_right_click) # For wxGTK! 286 287 # Add list to sizer. 288 sizer.Add(self.element, self.proportion, wx.ALL|wx.EXPAND, 0)
289 290
291 - def is_complete(self):
292 """Base method which always returns True. 293 294 @return: The answer to the question. 295 @rtype: bool 296 """ 297 298 # Assume everything is complete. 299 return True
300 301
302 - def observer_register(self, remove=False):
303 """Register and unregister methods with the observer objects. 304 305 @keyword remove: If set to True, then the methods will be unregistered. 306 @type remove: False 307 """ 308 309 # Register. 310 if not remove: 311 status.observers.gui_uf.register(self.name, self.build_element, method_name='build_element') 312 status.observers.pipe_alteration.register(self.name, self.build_element, method_name='build_element') 313 314 # Unregister. 315 else: 316 status.observers.gui_uf.unregister(self.name) 317 status.observers.pipe_alteration.unregister(self.name)
318 319
320 - def on_right_click(self, event):
321 """Pop up menu for the right click. 322 323 @param event: The wx event. 324 @type event: wx event 325 """ 326 327 # Obtain the position. 328 pos = event.GetPosition() 329 330 # Hack to allow the test suite to pass. 331 wx.Yield() 332 333 # Find the item clicked on. 334 item, flags = self.element.HitTest(pos) 335 336 # Get the ID string (handling wxPython 2.9 ListCtrl.HitTest() bugs). 337 id = None 338 if item != -1: 339 id = self.element.GetItemText(item) 340 341 # Get the menu. 342 popup_menus = self.generate_popup_menu(id=id) 343 344 # No popup menus defined. 345 if popup_menus == []: 346 return 347 348 # Execution lock, so do nothing. 349 if status.exec_lock.locked(): 350 return 351 352 # Initialise the menu. 353 menu = wx.Menu() 354 355 # Loop over the menu items. 356 for i in range(len(popup_menus)): 357 # Alias. 358 info = popup_menus[i] 359 360 # Add the menu item. 361 menu.AppendItem(build_menu_item(menu, id=info['id'], text=info['text'], icon=info['icon'])) 362 363 # Bind clicks. 364 self.element.Bind(wx.EVT_MENU, info['method'], id=info['id']) 365 366 # Pop up the menu. 367 if status.show_gui: 368 self.element.PopupMenu(menu) 369 menu.Destroy()
370 371
372 - def resize(self, event):
373 """Catch the resize to allow the element to be resized. 374 375 @param event: The wx event. 376 @type event: wx event 377 """ 378 379 # Set the column sizes. 380 self.size_cols() 381 382 # Continue with the normal resizing. 383 event.Skip()
384 385
386 - def set_box_label(self):
387 """Set the label of the StaticBox.""" 388 389 # Set the label. 390 self.data_box.SetLabel(self.title)
391 392
393 - def size_cols(self):
394 """Set the column sizes.""" 395 396 # The element size. 397 x, y = self.element.GetSize() 398 399 # Number of columns. 400 n = self.element.GetColumnCount() 401 402 # Set to equal sizes. 403 if n == 0: 404 width = x 405 else: 406 width = int(x / n) 407 408 # Set the column sizes. 409 for i in range(n): 410 self.element.SetColumnWidth(i, width)
411