Package gui :: Module uf_objects
[hide private]
[frames] | no frames]

Source Code for Module gui.uf_objects

  1  ############################################################################### 
  2  #                                                                             # 
  3  # Copyright (C) 2012 Edward d'Auvergne                                        # 
  4  #                                                                             # 
  5  # This file is part of the program relax.                                     # 
  6  #                                                                             # 
  7  # relax is free software; you can redistribute it and/or modify               # 
  8  # it under the terms of the GNU General Public License as published by        # 
  9  # the Free Software Foundation; either version 2 of the License, or           # 
 10  # (at your option) any later version.                                         # 
 11  #                                                                             # 
 12  # relax is distributed in the hope that it will be useful,                    # 
 13  # but WITHOUT ANY WARRANTY; without even the implied warranty of              # 
 14  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               # 
 15  # GNU General Public License for more details.                                # 
 16  #                                                                             # 
 17  # You should have received a copy of the GNU General Public License           # 
 18  # along with relax; if not, write to the Free Software                        # 
 19  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA   # 
 20  #                                                                             # 
 21  ############################################################################### 
 22   
 23  # Module docstring. 
 24  """Module containing the special objects for auto-generating the GUI user functions and classes.""" 
 25   
 26  # Python module imports. 
 27  from re import search 
 28  from string import split 
 29  import wx 
 30  from wx.lib import scrolledpanel 
 31   
 32  # relax module imports. 
 33  import arg_check 
 34  from graphics import fetch_icon 
 35  from relax_errors import AllRelaxErrors, RelaxError 
 36  from relax_string import strip_lead 
 37  from status import Status; status = Status() 
 38  from user_functions.data import Uf_info; uf_info = Uf_info() 
 39  from user_functions.data import Uf_tables; uf_tables = Uf_tables() 
 40   
 41  # relax GUI imports. 
 42  from gui.components.free_file_format import Free_file_format 
 43  from gui.components.menu import build_menu_item 
 44  from gui.errors import gui_raise 
 45  from gui.fonts import font 
 46  from gui.input_elements.bool import Selector_bool 
 47  from gui.input_elements.dir import Selector_dir 
 48  from gui.input_elements.file import Selector_file 
 49  from gui.input_elements.sequence import Sequence 
 50  from gui.input_elements.sequence_2D import Sequence_2D 
 51  from gui.input_elements.spin_id import Spin_id 
 52  from gui.input_elements.value import Value 
 53  from gui.interpreter import Interpreter; interpreter = Interpreter() 
 54  from gui.misc import format_table 
 55  from gui.wizard import Wiz_page, Wiz_window 
 56   
 57   
58 -def build_uf_menus(parent=None, menubar=None):
59 """Auto-generate the user function sub-menu. 60 61 @keyword parent: The parent window to bind the events to. 62 @type parent: wx instance 63 @keyword menubar: The menubar to attach the user function menus to. 64 @type menubar: wx.MenuBar instance 65 @return: The menu ID. 66 @rtype: int 67 """ 68 69 # The user function menus. 70 menu1 = wx.Menu() 71 menu2 = wx.Menu() 72 73 # The menu splitting. 74 pattern = '^[a-m]' 75 76 # Initialise some variables. 77 class_list = [] 78 uf_store = Uf_storage() 79 80 # Loop over the user functions. 81 class_item = None 82 menu = menu1 83 menu_index = 0 84 for name, data in uf_info.uf_loop(): 85 # Split up the name. 86 if search('\.', name): 87 class_name, uf_name = split(name, '.') 88 else: 89 class_name = None 90 91 # Generate a sub menu. 92 if class_name: 93 if class_name not in class_list: 94 # Add the last sub menu. 95 if class_item != None: 96 menu.AppendItem(class_item) 97 98 # Get the user function class data object. 99 class_data = uf_info.get_class(class_name) 100 101 # Create a unique ID. 102 class_id = wx.NewId() 103 104 # Create the menu entry. 105 class_item = build_menu_item(menu, id=class_id, text=class_data.menu_text, icon=fetch_icon(class_data.gui_icon, size='16x16')) 106 107 # Initialise the sub menu. 108 sub_menu = wx.Menu() 109 class_item.SetSubMenu(sub_menu) 110 111 # Add the class name to the list to block further sub menu creation. 112 class_list.append(class_name) 113 114 # Create the user function menu entry. 115 uf_id = wx.NewId() 116 sub_menu.AppendItem(build_menu_item(sub_menu, id=uf_id, text=data.menu_text, icon=fetch_icon(data.gui_icon, size='16x16'))) 117 118 # No sub menu. 119 else: 120 # Add the last sub menu. 121 if class_item != None: 122 menu.AppendItem(class_item) 123 class_item = None 124 125 # The menu item. 126 uf_id = wx.NewId() 127 menu.AppendItem(build_menu_item(menu, id=uf_id, text=data.menu_text, icon=fetch_icon(data.gui_icon, size='16x16'))) 128 129 # New menu. 130 if menu_index == 0 and not search(pattern, name): 131 menu = menu2 132 menu_index = 1 133 134 # Bind the menu item to the parent. 135 parent.Bind(wx.EVT_MENU, uf_store[name], id=uf_id) 136 137 # Add the very last sub menu. 138 if class_item != None: 139 menu.AppendItem(class_item) 140 141 # Add the user function menu to the menu bar. 142 title1 = "&User functions (a-m)" 143 title2 = "&User functions (n-z)" 144 menubar.Append(menu1, title1) 145 menubar.Append(menu2, title2) 146 147 # Return the menu IDs. 148 return [menubar.FindMenu(title1), menubar.FindMenu(title2)]
149 150 151
152 -class Force_true(object):
153 """A special user function arg element which always returns True.""" 154
155 - def __init__(self):
156 """Initialise the object.""" 157 158 # Default to always being True. 159 self._value = True
160 161
162 - def GetValue(self):
163 """Simple method for returning the internal value.""" 164 165 # Return the stored value. 166 return self._value
167 168
169 - def SetValue(self, value):
170 """Internally store the value being set.""" 171 172 # Store the value. 173 self._value = value
174 175 176
177 -class Uf_object(object):
178 """The object for auto-generating the GUI user functions.""" 179
180 - def __call__(self, event=None, wx_parent=None, wx_wizard_sync=None, wx_wizard_run=True, wx_wizard_modal=False, **kwds):
181 """Make the GUI user function executable. 182 183 All keyword args, apart from 'event', 'wx_parent' and 'wx_wizard_run' will be assumed to be user function arguments and the Uf_page.SetValue() method of the page will be used to set the GUI arg elements to the values supplied. 184 185 186 @keyword event: The wx event. 187 @type event: wx event or None 188 @keyword wx_parent: The parent wx object to associate the user function wizard to. 189 @type wx_parent: wx object 190 @keyword wx_wizard_sync: A flag which if given will switch between synchronous and asynchronous user function operation. 191 @type wx_wizard_sync: None or bool 192 @keyword wx_wizard_run: A flag which if True will call the wizard run() method. 193 @type wx_wizard_run: bool 194 @keyword wx_wizard_modal: A flag which if True will cause the wizard run() method to have the modal flag set so that the wizard is modal. 195 @type wx_wizard_modal: bool 196 @return: The status of the call. If the call failed, False will be returned. 197 @rtype: bool 198 """ 199 200 # Store the sync flag. 201 if wx_wizard_sync != None: 202 self._sync = wx_wizard_sync 203 204 # Create a new wizard if needed (checking that the parent of an old wizard is not the same). 205 if self.wizard == None or (wx_parent != None and wx_parent != self.wizard.GetParent()) or self.wizard._pages[0] == None: 206 status = self.create_wizard(wx_parent) 207 if not status: 208 return False 209 210 # Otherwise reset the wizard. 211 else: 212 self.wizard.reset() 213 214 # Update all of the user function argument choices (ComboBoxes) to be current, returning if a failure occurred. 215 if not self.page.update_args(): 216 return False 217 218 # Loop over the keyword args, using the Uf_page.SetValue() method to set the user function argument GUI element values. 219 for key in kwds: 220 self.page.SetValue(key, kwds[key]) 221 222 # Execute the wizard when asked. 223 if wx_wizard_run: 224 self.wizard.run(modal=wx_wizard_modal)
225 226
227 - def __init__(self, name, title=None, size=None, height_desc=None, apply_button=True, sync=False):
228 """Set up the object. 229 230 @param name: The name of the user function. 231 @type name: str 232 @keyword title: The long title of the user function to set as the window title. 233 @type title: str 234 @keyword size: The window size. 235 @type size: tuple of int 236 @keyword height_desc: The height in pixels of the description part of the wizard. 237 @type height_desc: int or None 238 @keyword apply_button: A flag specifying if the apply button should be shown or not. This defaults to True. 239 @type apply_button: bool 240 @keyword sync: A flag which if True will call user functions via interpreter.apply and if False via interpreter.queue. 241 @type sync: bool 242 """ 243 244 # Store the args. 245 self._name = name 246 self._title = title 247 self._size = size 248 self._height_desc = height_desc 249 self._apply_button = apply_button 250 self._sync = sync 251 252 # Initialise the wizard storage. 253 self.wizard = None
254 255
256 - def create_page(self, wizard=None, sync=False):
257 """Create the user function wizard page GUI object. 258 259 @keyword wizard: The parent wizard. 260 @type wizard: Wiz_window instance 261 @keyword sync: A flag which if True will call user functions via interpreter.apply and if False via interpreter.queue. 262 @type sync: bool 263 @return: The user function page object. 264 @rtype: Uf_page instance 265 """ 266 267 # Initialise and return the page. 268 return Uf_page(self._name, parent=wizard, height_desc=self._height_desc, sync=self._sync)
269 270
271 - def create_wizard(self, parent=None):
272 """Create the user function wizard GUI object, with embedded wizard page. 273 274 @keyword parent: The parent wx window. 275 @type parent: wx.Window instance 276 @return: True if the wizard was created, False if a problem was encountered. 277 @rtype: bool 278 """ 279 280 # The parent object defaults to the main relax window. 281 if parent == None: 282 app = wx.GetApp() 283 parent = app.gui 284 285 # Create the wizard dialog. 286 self.wizard = Wiz_window(parent=parent, size_x=self._size[0], size_y=self._size[1], title=self._title) 287 288 # Create the page. 289 self.page = self.create_page(self.wizard, sync=self._sync) 290 291 # For an update of the argument data. 292 if not self.page.update_args(): 293 return False 294 295 # Add the page to the wizard. 296 self.wizard.add_page(self.page, apply_button=self._apply_button) 297 298 # Success. 299 return True
300 301 302
303 -class Uf_page(Wiz_page):
304 """User function specific pages for the wizards.""" 305
306 - def __init__(self, name, parent=None, height_desc=220, sync=False):
307 """Set up the window. 308 309 @param name: The name of the user function. 310 @type name: str 311 @keyword parent: The parent class containing the GUI. 312 @type parent: class instance 313 @keyword height_desc: The height in pixels of the description part of the wizard. 314 @type height_desc: int or None 315 @keyword sync: A flag which if True will call user functions via interpreter.apply and if False via interpreter.queue. 316 @type sync: bool 317 """ 318 319 # Store the args. 320 self.name = name 321 self.sync = sync 322 323 # Storage of the user function argument elements. 324 self.uf_args = {} 325 326 # Yield to allow the cursor to be changed. 327 wx.Yield() 328 329 # Change the cursor to waiting. 330 wx.BeginBusyCursor() 331 332 # Get the user function data object. 333 self.uf_data = uf_info.get_uf(name) 334 335 # Set the wizard image. 336 self.image_path = self.uf_data.wizard_image 337 338 # Set the user function title. 339 if self.uf_data.title_short != None: 340 self.title = self.uf_data.title_short 341 else: 342 self.title = self.uf_data.title 343 344 # Execute the base class method. 345 super(Uf_page, self).__init__(parent, height_desc=height_desc) 346 347 # Reset the cursor. 348 if wx.IsBusy(): 349 wx.EndBusyCursor()
350 351
352 - def _format_text(self, text):
353 """Format the text by stripping whitespace. 354 355 @param text: The text to strip. 356 @type text: str 357 @return: The stripped text. 358 @rtype: str 359 """ 360 361 # First strip whitespace. 362 stripped_text = strip_lead(text) 363 364 # Remove the first characters if newlines. 365 while True: 366 if stripped_text[0] == "\n": 367 stripped_text = stripped_text[1:] 368 else: 369 break 370 371 # Remove the last character if a newline. 372 while True: 373 if stripped_text[-1] == "\n": 374 stripped_text = stripped_text[:-1] 375 else: 376 break 377 378 # Return the text. 379 return stripped_text
380 381
382 - def _intro_text(self, keys, values, prompt=True):
383 """Build and return the user function intro text. 384 385 @param keys: The user function keys. 386 @type keys: list of str 387 @param values: The values corresponding to the keys. 388 @type values: list 389 @keyword prompt: A flag which if True will cause the prompt text to be included. 390 @type prompt: bool 391 @return: The user function intro text. 392 @rtype: str 393 """ 394 395 # Initialise. 396 text = "" 397 398 # The prompt. 399 if prompt: 400 text += status.ps3 401 402 # The user function name. 403 text += "%s(" % self.name 404 405 # The keyword args. 406 for i in range(len(keys)): 407 # Comma separation. 408 if i >= 1: 409 text += ", " 410 411 # Add the arg. 412 text += "%s=%s" % (keys[i], repr(values[i])) 413 414 # The end. 415 text += ")" 416 417 # Return the text. 418 return text
419 420
421 - def Clear(self, key):
422 """Special wizard method for clearing the value of the GUI element corresponding to the key. 423 424 @param key: The key corresponding to the desired GUI element. 425 @type key: str 426 """ 427 428 # Call the argument element's method. 429 self.uf_args[key].Clear()
430 431
432 - def GetValue(self, key):
433 """Special wizard method for getting the value of the GUI element corresponding to the key. 434 435 @param key: The key corresponding to the desired GUI element. 436 @type key: str 437 @return: The value that the specific GUI element's GetValue() method returns. 438 @rtype: unknown 439 """ 440 441 # The key is not set, so assume this is a hidden argument. 442 if key not in self.uf_args.keys(): 443 return None 444 445 # Call the argument element's method. 446 return self.uf_args[key].GetValue()
447 448
449 - def SetValue(self, key, value):
450 """Special wizard method for setting the value of the GUI element corresponding to the key. 451 452 @param key: The key corresponding to the desired GUI element. 453 @type key: str 454 @param value: The value that the specific GUI element's SetValue() method expects. 455 @type value: unknown 456 """ 457 458 # Find the argument. 459 arg = None 460 for i in range(len(self.uf_data.kargs)): 461 if self.uf_data.kargs[i]['name'] == key: 462 arg = self.uf_data.kargs[i] 463 464 # No match. 465 if arg == None: 466 raise RelaxError("The key '%s' is unknown." % key) 467 468 # Handle the free file format args (for external control, i.e. via the test suite). 469 if 'free_file_format' in self.uf_args and key in ['spin_id_col', 'mol_name_col', 'res_num_col', 'res_name_col', 'spin_num_col', 'spin_name_col', 'data_col', 'error_col', 'sep']: 470 self.uf_args['free_file_format'].SetValue(key, value) 471 472 # Skip functions and function args, as these are not supported in the GUI. 473 elif arg['arg_type'] in ['func', 'func args']: 474 pass 475 476 # Call the argument element's method. 477 else: 478 self.uf_args[key].SetValue(value)
479 480
481 - def UpdateChoices(self, key, combo_choices=None, combo_data=None, combo_default=None):
482 """Special user function page method for updating the list of choices in a ComboBox type element. 483 484 @param key: The key corresponding to the desired GUI element. 485 @type key: str 486 @keyword combo_choices: The list of choices to present to the user. This is only used if the element_type is set to 'combo'. 487 @type combo_choices: list of str 488 @keyword combo_data: The data returned by a call to GetValue(). This is only used if the element_type is set to 'combo'. If supplied, it should be the same length at the combo_choices list. If not supplied, the combo_choices list will be used for the returned data. 489 @type combo_data: list 490 @keyword combo_default: The default value of the ComboBox. This is only used if the element_type is set to 'combo'. 491 @type combo_default: str or None 492 """ 493 494 # Call the argument element's method. 495 self.uf_args[key].UpdateChoices(combo_choices=combo_choices, combo_data=combo_data, combo_default=combo_default)
496 497
498 - def add_contents(self, sizer):
499 """Add the specific GUI elements. 500 501 @param sizer: A sizer object. 502 @type sizer: wx.Sizer instance 503 """ 504 505 # Initialise the free format file settings flag. 506 free_format = False 507 free_format_data = False 508 509 # Loop over the arguments. 510 for i in range(len(self.uf_data.kargs)): 511 # Alias. 512 arg = self.uf_data.kargs[i] 513 514 # The arg description formatting. 515 desc = "The %s:" % arg['desc_short'] 516 517 # Special arg type: file selection dialog. 518 if arg['arg_type'] == 'file sel': 519 self.uf_args[arg['name']] = Selector_file(name=arg['name'], parent=self, default=arg['default'], sizer=sizer, desc=desc, wildcard=arg['wiz_filesel_wildcard'], style=arg['wiz_filesel_style'], tooltip=arg['desc'], divider=self._div_left, height_element=self.height_element, read_only=arg['wiz_read_only']) 520 521 # Special arg type: dir arg. 522 elif arg['arg_type'] == 'dir': 523 pass 524 525 # Special arg type: directory selection dialog. 526 elif arg['arg_type'] == 'dir sel': 527 self.uf_args[arg['name']] = Selector_dir(name=arg['name'], parent=self, default=arg['default'], sizer=sizer, desc=desc, style=arg['wiz_dirsel_style'], tooltip=arg['desc'], divider=self._div_left, height_element=self.height_element, read_only=arg['wiz_read_only']) 528 529 # Special arg type: free format file settings. 530 elif arg['arg_type'] == 'free format': 531 # Switch the flags. 532 free_format = True 533 if arg['name'] == 'data_col': 534 free_format_data = True 535 536 # Special arg type: functions and their arguments! 537 elif arg['arg_type'] in ['func', 'func args']: 538 pass 539 540 # Special arg type: force flags. 541 elif arg['arg_type'] in ['force flag']: 542 self.uf_args[arg['name']] = Force_true() 543 544 # Special arg type: spin IDs. 545 elif arg['arg_type'] in ['spin ID']: 546 self.uf_args[arg['name']] = Spin_id(name=arg['name'], parent=self, default=arg['default'], element_type=arg['wiz_element_type'], sizer=sizer, desc=desc, combo_choices=arg['wiz_combo_choices'], combo_data=arg['wiz_combo_data'], tooltip=arg['desc'], divider=self._div_left, height_element=self.height_element, can_be_none=arg['can_be_none']) 547 548 # Value types. 549 elif arg['py_type'] in ['float', 'int', 'num', 'str']: 550 self.uf_args[arg['name']] = Value(name=arg['name'], parent=self, default=arg['default'], element_type=arg['wiz_element_type'], value_type=arg['py_type'], min=arg['min'], max=arg['max'], sizer=sizer, desc=desc, combo_choices=arg['wiz_combo_choices'], combo_data=arg['wiz_combo_data'], tooltip=arg['desc'], divider=self._div_left, height_element=self.height_element, read_only=arg['wiz_read_only'], can_be_none=arg['can_be_none']) 551 552 # Bool type. 553 elif arg['py_type'] == 'bool': 554 self.uf_args[arg['name']] = Selector_bool(name=arg['name'], parent=self, element_type=arg['wiz_element_type'], sizer=sizer, desc=desc, tooltip=arg['desc'], default=arg['default'], divider=self._div_left, height_element=self.height_element) 555 556 # Sequence types. 557 elif arg['py_type'] in ['float_list', 'int_list', 'num_list', 'str_list', 'float_tuple', 'int_tuple', 'num_tuple', 'str_tuple', 'float_array', 'int_array', 'float_or_float_list', 'int_or_int_list', 'num_or_num_list', 'str_or_str_list', 'float_or_float_tuple', 'int_or_int_tuple', 'num_or_num_tuple', 'str_or_str_tuple', 'val_or_list']: 558 # The sequence type. 559 if arg['py_type'] in ['float_list', 'int_list', 'num_list', 'str_list', 'float_array', 'int_array', 'float_or_float_list', 'int_or_int_list', 'num_or_num_list', 'str_or_str_list', 'val_or_list']: 560 seq_type = 'list' 561 else: 562 seq_type = 'tuple' 563 564 # The value type. 565 if arg['py_type'] in ['float_list', 'num_list', 'float_tuple', 'num_tuple', 'float_array', 'float_or_float_list', 'num_or_num_list', 'float_or_float_tuple', 'num_or_num_tuple']: 566 value_type = 'float' 567 elif arg['py_type'] in ['int_list', 'int_tuple', 'int_array', 'int_or_int_list', 'int_or_int_tuple']: 568 value_type = 'int' 569 elif arg['py_type'] in ['str_list', 'str_tuple', 'str_array', 'str_or_str_list', 'str_or_str_tuple']: 570 value_type = 'str' 571 else: 572 value_type = None 573 574 # Single values. 575 single_value = False 576 if arg['py_type'] in ['float_or_float_list', 'int_or_int_list', 'num_or_num_list', 'str_or_str_list', 'float_or_float_tuple', 'int_or_int_tuple', 'num_or_num_tuple', 'str_or_str_tuple', 'val_or_list']: 577 single_value = True 578 579 # Dimensions. 580 dim = None 581 if isinstance(arg['dim'], int): 582 dim = arg['dim'] 583 584 self.uf_args[arg['name']] = Sequence(name=arg['name'], parent=self, default=arg['default'], element_type=arg['wiz_element_type'], seq_type=seq_type, value_type=value_type, dim=dim, min=arg['min'], max=arg['max'], sizer=sizer, desc=desc, combo_choices=arg['wiz_combo_choices'], combo_data=arg['wiz_combo_data'], combo_list_min=arg['wiz_combo_list_min'], tooltip=arg['desc'], single_value=single_value, divider=self._div_left, height_element=self.height_element, read_only=arg['wiz_read_only'], can_be_none=arg['can_be_none']) 585 586 # 2D sequence types. 587 elif arg['py_type'] in ['float_list_of_lists', 'int_list_of_lists', 'num_list_of_lists', 'str_list_of_lists', 'float_tuple_of_tuples', 'int_tuple_of_tuples', 'num_tuple_of_tuples', 'str_tuple_of_tuples', 'float_matrix', 'int_matrix']: 588 # The sequence type. 589 if arg['py_type'] in ['float_list_of_lists', 'int_list_of_lists', 'num_list_of_lists', 'str_list_of_lists', 'float_matrix', 'int_matrix']: 590 seq_type = 'list' 591 else: 592 seq_type = 'tuple' 593 594 # The value type. 595 if arg['py_type'] in ['float_list_of_lists', 'float_tuple_of_tuples', 'num_list_of_lists', 'num_tuple_of_tuples', 'float_matrix']: 596 value_type = 'float' 597 elif arg['py_type'] in ['int_list_of_lists', 'int_tuple_of_tuples', 'int_matrix']: 598 value_type = 'int' 599 else: 600 value_type = 'str' 601 602 self.uf_args[arg['name']] = Sequence_2D(name=arg['name'], parent=self, default=arg['default'], sizer=sizer, element_type=arg['wiz_element_type'], seq_type=seq_type, value_type=value_type, dim=arg['dim'], min=arg['min'], max=arg['max'], titles=arg['list_titles'], desc=desc, combo_choices=arg['wiz_combo_choices'], combo_data=arg['wiz_combo_data'], combo_list_min=arg['wiz_combo_list_min'], tooltip=arg['desc'], divider=self._div_left, height_element=self.height_element, read_only=arg['wiz_read_only'], can_be_none=arg['can_be_none']) 603 604 # Unknown type. 605 else: 606 raise RelaxError("The Python object type '%s' cannot be handled." % arg['py_type']) 607 608 # Add the free format element. 609 if free_format: 610 self.uf_args['free_file_format'] = Free_file_format(sizer, parent=self, data_cols=free_format_data, padding=3, spacer=0)
611 612
613 - def add_desc(self, sizer, max_y=220):
614 """Add the description to the dialog. 615 616 @param sizer: A sizer object. 617 @type sizer: wx.Sizer instance 618 @keyword max_y: The maximum height, in number of pixels, for the description. 619 @type max_y: int 620 """ 621 622 # Initialise. 623 spacing = 15 624 625 # A line with spacing. 626 sizer.AddSpacer(5) 627 sizer.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.ALL, 0) 628 sizer.AddSpacer(5) 629 630 # Create a scrolled panel. 631 panel = scrolledpanel.ScrolledPanel(self, -1, name="desc") 632 633 # A sizer for the panel. 634 panel_sizer = wx.BoxSizer(wx.VERTICAL) 635 636 # Initialise the text elements. 637 tot_y = 0 638 text_elements = [] 639 text_types = [] 640 641 # The user function name. 642 name = "The %s user function" % self.name 643 text = wx.StaticText(panel, -1, name, style=wx.TE_MULTILINE) 644 text.SetFont(font.subtitle) 645 text_elements.append(text) 646 text_types.append('title') 647 648 # The text size, then spacing after the title. 649 x, y = text.GetSizeTuple() 650 tot_y += y 651 tot_y += spacing 652 653 # The synopsis. 654 if self.uf_data.title: 655 # The text. 656 text = wx.StaticText(panel, -1, self.uf_data.title, style=wx.TE_MULTILINE) 657 658 # Formatting. 659 text.SetFont(font.normal_italic) 660 661 # The text size. 662 x, y = text.GetSizeTuple() 663 tot_y += y 664 665 # The spacing after the element. 666 tot_y += spacing 667 668 # Append the text objects. 669 text_elements.append(text) 670 text_types.append('synopsis') 671 672 # The description sections. 673 if self.uf_data.desc != None: 674 # Loop over the sections. 675 for i in range(len(self.uf_data.desc)): 676 # Alias. 677 desc = self.uf_data.desc[i] 678 679 # Skip the prompt examples. 680 if desc.get_title() == 'Prompt examples': 681 continue 682 683 # Loop over the text elements. 684 for type, element in desc.element_loop(title=True): 685 # The text version of the elements. 686 text = '' 687 if isinstance(element, str): 688 text = element 689 690 # Format the tables. 691 if type == 'table': 692 text = format_table(uf_tables.get_table(element)) 693 694 # Format the lists. 695 elif type == 'list': 696 # Loop over the list elements. 697 for j in range(len(element)): 698 text += " - %s\n" % element[j] 699 700 # Format the item lists. 701 elif type == 'item list': 702 # Loop over the list elements. 703 for j in range(len(element)): 704 # No item. 705 if element[j][0] in [None, '']: 706 text += " %s\n" % element[j][1] 707 else: 708 text += " %s: %s\n" % (element[j][0], element[j][1]) 709 710 # The text object. 711 text_obj = wx.StaticText(panel, -1, text, style=wx.TE_MULTILINE) 712 713 # Format. 714 if type == 'title': 715 text_obj.SetFont(font.subtitle) 716 elif type == 'paragraph': 717 text_obj.SetFont(font.normal) 718 elif type in ['table', 'verbatim']: 719 text_obj.SetFont(font.modern_small) 720 else: 721 text_obj.SetFont(font.normal) 722 723 # Wrap the paragraphs and lists (with spacing for scrollbars). 724 if type in ['paragraph', 'list', 'item list']: 725 text_obj.Wrap(self._main_size - 20) 726 727 # The text size. 728 x, y = text_obj.GetSizeTuple() 729 tot_y += y 730 731 # The spacing after each element (except the last). 732 tot_y += spacing 733 734 # The spacing before each section (not including the first). 735 if i != 0 and type == 'title': 736 tot_y += spacing 737 738 # Append the text objects. 739 text_elements.append(text_obj) 740 text_types.append(type) 741 742 # Some extra space for who knows what?! 743 tot_y -= spacing 744 tot_y += 20 745 746 # Set the panel size - scrolling needed. 747 if tot_y > max_y: 748 panel.SetInitialSize((self._main_size, max_y)) 749 750 # Set the panel size - no scrolling. 751 else: 752 panel.SetInitialSize((self._main_size, tot_y)) 753 754 # Add the text. 755 n = len(text_elements) 756 for i in range(n): 757 # Spacing before each section (not including the first). 758 if i > 1 and text_types[i] == 'title': 759 panel_sizer.AddSpacer(spacing) 760 761 # The text. 762 panel_sizer.Add(text_elements[i], 0, wx.ALIGN_LEFT, 0) 763 764 # Spacer after all sections (except the end). 765 if i != n - 1: 766 panel_sizer.AddSpacer(spacing) 767 768 # Set up and add the panel to the sizer. 769 panel.SetSizer(panel_sizer) 770 panel.SetAutoLayout(1) 771 panel.SetupScrolling(scroll_x=False, scroll_y=True) 772 sizer.Add(panel, 0, wx.ALL|wx.EXPAND) 773 774 # A line with spacing. 775 sizer.AddSpacer(5) 776 sizer.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.ALL, 0) 777 sizer.AddSpacer(5)
778 779
780 - def execute(self, uf, *args, **kwds):
781 """Execute the user function, either asynchronously or synchronously. 782 783 @param uf: The user function as a string. 784 @type uf: str 785 @param args: The user function arguments. 786 @type args: any arguments 787 @param kwds: The user function keyword arguments. 788 @type kwds: any keyword arguments 789 """ 790 791 # Synchronous execution. 792 if self.sync or status.gui_uf_force_sync: 793 interpreter.apply(uf, *args, **kwds) 794 795 # Asynchronous execution. 796 else: 797 interpreter.queue(uf, *args, **kwds)
798 799
800 - def on_back(self):
801 """Remove this page from the observers.""" 802 803 # Unregister this page with the 'gui_uf' observer. 804 status.observers.gui_uf.unregister(self.name)
805 806
807 - def on_display(self):
808 """Clear and update the data if needed.""" 809 810 # Register this page with the 'gui_uf' observer so that update_args() is called once the any user function completes. 811 status.observers.gui_uf.register(self.name, self.update_args) 812 813 # Update the args. 814 return self.update_args()
815 816
817 - def on_execute(self):
818 """Execute the user function.""" 819 820 # Get the argument values. 821 kargs = {} 822 for i in range(len(self.uf_data.kargs)): 823 # The argument name. 824 name = self.uf_data.kargs[i]['name'] 825 826 # Store the value. 827 kargs[name] = self.GetValue(name) 828 829 # Skip execution when a Combo_list does not have enough elements. 830 if self.uf_data.kargs[i]['wiz_combo_list_min'] != None and kargs[name] == None: 831 return 832 833 # Handle the free file format args. 834 if 'free_file_format' in self.uf_args: 835 kargs.update(self.uf_args['free_file_format'].GetValue()) 836 837 # Display the relax controller, if asked. 838 if self.uf_data.display: 839 # Get the App. 840 app = wx.GetApp() 841 842 # First show the controller. 843 app.gui.show_controller(None) 844 845 # Go to the last line. 846 app.gui.controller.log_panel.on_goto_end(None) 847 848 # The user function intro text. 849 if status.uf_intro: 850 # Convert the keys and values. 851 keys = [] 852 values = [] 853 for i in range(len(self.uf_data.kargs)): 854 keys.append(self.uf_data.kargs[i]['name']) 855 values.append(kargs[self.uf_data.kargs[i]['name']]) 856 857 # The printout. 858 print(self._intro_text(keys, values)) 859 860 # Execute the user function. 861 self.execute(self.name, **kargs) 862 863 # Bring the controller to the front. 864 if status.show_gui and self.uf_data.display: 865 wx.CallAfter(app.gui.controller.Raise)
866 867
868 - def on_next(self):
869 """Remove this page from the observers.""" 870 871 # Unregister this page with the 'gui_uf' observer. 872 status.observers.gui_uf.unregister(self.name)
873 874
875 - def update_args(self):
876 """Update all the argument ComboBox choices. 877 878 @return: The status of the update - False if a RelaxError occurs, True otherwise. 879 @rtype: bool 880 """ 881 882 # Loop over the arguments. 883 for i in range(len(self.uf_data.kargs)): 884 # The argument name. 885 name = self.uf_data.kargs[i]['name'] 886 887 # No iterator method for updating the list. 888 iterator = self.uf_data.kargs[i]['wiz_combo_iter'] 889 if iterator == None: 890 continue 891 892 # Get the new choices and data (in a safe way). 893 try: 894 choices = [] 895 data = [] 896 for vals in iterator(): 897 if arg_check.is_tuple(vals, size=2, raise_error=False): 898 choices.append(vals[0]) 899 data.append(vals[1]) 900 else: 901 choices.append(vals) 902 data.append(vals) 903 904 # Catch all RelaxErrors. 905 except AllRelaxErrors, instance: 906 # Signal the failure to the wizard. 907 self.setup_fail = True 908 909 # Display a dialog with the error. 910 gui_raise(instance) 911 912 # Return as a failure. 913 return False 914 915 # Get the current value, for setting as the default. 916 val = self.uf_args[name].GetValue() 917 918 # Update the GUI element. 919 self.UpdateChoices(name, combo_choices=choices, combo_data=data, combo_default=val) 920 921 # Successful update. 922 return True
923 924 925
926 -class Uf_storage(dict):
927 """A singleton container for holding all the GUI user functions.""" 928 929 # Class variable for storing the class instance (for the singleton). 930 _instance = None 931
932 - def __new__(self, *args, **kargs):
933 """Replacement method for implementing the singleton design pattern.""" 934 935 # First instantiation. 936 if self._instance is None: 937 # Instantiate. 938 self._instance = dict.__new__(self, *args, **kargs) 939 940 # Generate the user functions. 941 for name, data in uf_info.uf_loop(): 942 # The title. 943 title = data.title_short 944 if not title: 945 title = data.title 946 947 # Generate a new container. 948 obj = Uf_object(name, title=title, size=data.wizard_size, height_desc=data.wizard_height_desc, apply_button=data.wizard_apply_button, sync=data.gui_sync) 949 950 # Store it. 951 self._instance[name] = obj 952 953 # Already instantiated, so return the instance. 954 return self._instance
955