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

Source Code for Module gui.uf_objects

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