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

Source Code for Module gui.uf_objects

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