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