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