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

Source Code for Module gui.uf_objects

   1  ############################################################################### 
   2  #                                                                             # 
   3  # Copyright (C) 2012-2014 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, Selector_file_multiple 
  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: multiple file selection dialog. 529 elif arg['arg_type'] == 'file sel multi': 530 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']) 531 532 # Special arg type: dir arg. 533 elif arg['arg_type'] == 'dir': 534 pass 535 536 # Special arg type: directory selection dialog. 537 elif arg['arg_type'] == 'dir sel': 538 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']) 539 540 # Special arg type: free format file settings. 541 elif arg['arg_type'] == 'free format': 542 # Switch the flags. 543 free_format = True 544 if arg['name'] == 'data_col': 545 free_format_data = True 546 547 # Special arg type: functions and their arguments! 548 elif arg['arg_type'] in ['func', 'func args']: 549 pass 550 551 # Special arg type: force flags. 552 elif arg['arg_type'] in ['force flag']: 553 self.uf_args[arg['name']] = Force_true() 554 555 # Special arg type: spin IDs. 556 elif arg['arg_type'] in ['spin ID']: 557 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']) 558 559 # Value types. 560 elif arg['py_type'] in ['float', 'int', 'num', 'str']: 561 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']) 562 563 # Bool type. 564 elif arg['py_type'] == 'bool': 565 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) 566 567 # Sequence types. 568 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']: 569 # The sequence type. 570 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']: 571 seq_type = 'list' 572 else: 573 seq_type = 'tuple' 574 575 # The value type. 576 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']: 577 value_type = 'float' 578 elif arg['py_type'] in ['int_list', 'int_tuple', 'int_array', 'int_or_int_list', 'int_or_int_tuple']: 579 value_type = 'int' 580 elif arg['py_type'] in ['str_list', 'str_tuple', 'str_array', 'str_or_str_list', 'str_or_str_tuple']: 581 value_type = 'str' 582 else: 583 value_type = None 584 585 # Single values. 586 single_value = False 587 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']: 588 single_value = True 589 590 # Dimensions. 591 dim = None 592 if isinstance(arg['dim'], int): 593 dim = arg['dim'] 594 595 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']) 596 597 # 2D sequence types. 598 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']: 599 # The sequence type. 600 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']: 601 seq_type = 'list' 602 else: 603 seq_type = 'tuple' 604 605 # The value type. 606 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']: 607 value_type = 'float' 608 elif arg['py_type'] in ['int_list_of_lists', 'int_tuple_of_tuples', 'int_matrix']: 609 value_type = 'int' 610 else: 611 value_type = 'str' 612 613 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']) 614 615 # Unknown type. 616 else: 617 raise RelaxError("The Python object type '%s' cannot be handled." % arg['py_type']) 618 619 # Add the free format element. 620 if free_format: 621 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)
622 623
624 - def add_desc(self, sizer, max_y=220):
625 """Add the description to the dialog. 626 627 @param sizer: A sizer object. 628 @type sizer: wx.Sizer instance 629 @keyword max_y: The maximum height, in number of pixels, for the description. 630 @type max_y: int 631 """ 632 633 # Initialise. 634 spacing = 15 635 636 # A line with spacing. 637 sizer.AddSpacer(5) 638 sizer.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.ALL, 0) 639 sizer.AddSpacer(5) 640 641 # Create a scrolled panel. 642 panel = scrolledpanel.ScrolledPanel(self, -1, name="desc") 643 644 # A sizer for the panel. 645 panel_sizer = wx.BoxSizer(wx.VERTICAL) 646 647 # Initialise the text elements. 648 tot_y = 0 649 text_elements = [] 650 text_types = [] 651 652 # The user function name. 653 name = "The %s user function" % self.name 654 text = wx.StaticText(panel, -1, name, style=wx.TE_MULTILINE) 655 text.SetFont(font.subtitle) 656 text_elements.append(text) 657 text_types.append('title') 658 659 # The text size, then spacing after the title. 660 x, y = text.GetSizeTuple() 661 tot_y += y 662 tot_y += spacing 663 664 # The synopsis. 665 if self.uf_data.title: 666 # The text. 667 text = wx.StaticText(panel, -1, self.uf_data.title, style=wx.TE_MULTILINE) 668 669 # Formatting. 670 text.SetFont(font.normal_italic) 671 672 # The text size. 673 x, y = text.GetSizeTuple() 674 tot_y += y 675 676 # The spacing after the element. 677 tot_y += spacing 678 679 # Append the text objects. 680 text_elements.append(text) 681 text_types.append('synopsis') 682 683 # The description sections. 684 if self.uf_data.desc != None: 685 # Loop over the sections. 686 for i in range(len(self.uf_data.desc)): 687 # Alias. 688 desc = self.uf_data.desc[i] 689 690 # Skip the prompt examples. 691 if desc.get_title() == 'Prompt examples': 692 continue 693 694 # Loop over the text elements. 695 for type, element in desc.element_loop(title=True): 696 # The text version of the elements. 697 text = '' 698 if isinstance(element, str): 699 text = element 700 701 # Format the tables. 702 if type == 'table': 703 text = format_table(uf_tables.get_table(element)) 704 705 # Format the lists. 706 elif type == 'list': 707 # Loop over the list elements. 708 for j in range(len(element)): 709 text += " - %s\n" % element[j] 710 711 # Format the item lists. 712 elif type == 'item list': 713 # Loop over the list elements. 714 for j in range(len(element)): 715 # No item. 716 if element[j][0] in [None, '']: 717 text += " %s\n" % element[j][1] 718 else: 719 text += " %s: %s\n" % (element[j][0], element[j][1]) 720 721 # Format prompt items. 722 elif type == 'prompt': 723 for j in range(len(element)): 724 text += "%s\n" % element[j] 725 726 # The text object. 727 text_obj = wx.StaticText(panel, -1, text, style=wx.TE_MULTILINE) 728 729 # Format. 730 if type == 'title': 731 text_obj.SetFont(font.subtitle) 732 elif type == 'paragraph': 733 text_obj.SetFont(font.normal) 734 elif type in ['table', 'verbatim', 'prompt']: 735 text_obj.SetFont(font.modern_small) 736 else: 737 text_obj.SetFont(font.normal) 738 739 # Wrap the paragraphs and lists (with spacing for scrollbars). 740 if type in ['paragraph', 'list', 'item list']: 741 text_obj.Wrap(self._main_size - 20) 742 743 # The text size. 744 x, y = text_obj.GetSizeTuple() 745 tot_y += y 746 747 # The spacing after each element (except the last). 748 tot_y += spacing 749 750 # The spacing before each section (not including the first). 751 if i != 0 and type == 'title': 752 tot_y += spacing 753 754 # Append the text objects. 755 text_elements.append(text_obj) 756 text_types.append(type) 757 758 # Some extra space for who knows what?! 759 tot_y -= spacing 760 tot_y += 20 761 762 # Set the panel size - scrolling needed. 763 if tot_y > max_y: 764 panel.SetInitialSize((self._main_size, max_y)) 765 766 # Set the panel size - no scrolling. 767 else: 768 panel.SetInitialSize((self._main_size, tot_y)) 769 770 # Add the text. 771 n = len(text_elements) 772 for i in range(n): 773 # Spacing before each section (not including the first). 774 if i > 1 and text_types[i] == 'title': 775 panel_sizer.AddSpacer(spacing) 776 777 # The text. 778 panel_sizer.Add(text_elements[i], 0, wx.ALIGN_LEFT, 0) 779 780 # Spacer after all sections (except the end). 781 if i != n - 1: 782 panel_sizer.AddSpacer(spacing) 783 784 # Set up and add the panel to the sizer. 785 panel.SetSizer(panel_sizer) 786 panel.SetAutoLayout(1) 787 panel.SetupScrolling(scroll_x=False, scroll_y=True) 788 sizer.Add(panel, 0, wx.ALL|wx.EXPAND) 789 790 # A line with spacing. 791 sizer.AddSpacer(5) 792 sizer.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.ALL, 0) 793 sizer.AddSpacer(5)
794 795
796 - def execute(self, uf, *args, **kwds):
797 """Execute the user function, either asynchronously or synchronously. 798 799 @param uf: The user function as a string. 800 @type uf: str 801 @param args: The user function arguments. 802 @type args: any arguments 803 @param kwds: The user function keyword arguments. 804 @type kwds: any keyword arguments 805 """ 806 807 # Synchronous execution. 808 if self.sync or status.gui_uf_force_sync: 809 return_status = interpreter.apply(uf, *args, **kwds) 810 return return_status 811 812 # Asynchronous execution. 813 else: 814 interpreter.queue(uf, *args, **kwds) 815 return True
816 817
818 - def on_back(self):
819 """Remove this page from the observers.""" 820 821 # Unregister this page with the 'gui_uf' observer. 822 status.observers.gui_uf.unregister(self.name)
823 824
825 - def on_display(self):
826 """Clear and update the data if needed.""" 827 828 # Register this page with the 'gui_uf' observer so that update_args() is called once the any user function completes. 829 status.observers.gui_uf.register(self.name, self.update_args, method_name='update_args') 830 831 # Update the args. 832 return self.update_args()
833 834
835 - def on_execute(self, force_exec=False):
836 """Execute the user function. 837 838 @keyword force_exec: A flag which if True will cause the execution flag to be ignored and the user function to be executed. 839 @type force_exec: bool 840 """ 841 842 # Don't execute. 843 if not force_exec and not self.execute_flag: 844 return 845 846 # Get the argument values. 847 kargs = {} 848 for i in range(len(self.uf_data.kargs)): 849 # The argument name. 850 name = self.uf_data.kargs[i]['name'] 851 852 # Store the value. 853 kargs[name] = self.GetValue(name) 854 855 # Skip execution when a Combo_list does not have enough elements. 856 if self.uf_data.kargs[i]['wiz_combo_list_min'] != None and kargs[name] == None: 857 return True 858 859 # Handle the free file format args. 860 if 'free_file_format' in self.uf_args: 861 kargs.update(self.uf_args['free_file_format'].GetValue()) 862 863 # Display the relax controller, if asked. 864 if self.uf_data.display: 865 # Get the App. 866 app = wx.GetApp() 867 868 # First show the controller. 869 app.gui.show_controller(None) 870 871 # Go to the last line. 872 app.gui.controller.log_panel.on_goto_end(None) 873 874 # The user function intro text. 875 if status.uf_intro: 876 # Convert the keys and values. 877 keys = [] 878 values = [] 879 for i in range(len(self.uf_data.kargs)): 880 keys.append(self.uf_data.kargs[i]['name']) 881 values.append(kargs[self.uf_data.kargs[i]['name']]) 882 883 # The printout. 884 print(self._intro_text(keys, values)) 885 886 # Execute the user function. 887 return_status = self.execute(self.name, **kargs) 888 889 # Bring the controller to the front. 890 if status.show_gui and self.uf_data.display: 891 wx.CallAfter(app.gui.controller.Raise) 892 893 # Return the status. 894 return return_status
895 896
897 - def on_next(self):
898 """Remove this page from the observers.""" 899 900 # Unregister this page with the 'gui_uf' observer. 901 status.observers.gui_uf.unregister(self.name)
902 903
904 - def update_args(self):
905 """Update all the argument ComboBox choices. 906 907 @return: The status of the update - False if a RelaxError occurs, True otherwise. 908 @rtype: bool 909 """ 910 911 # Loop over the arguments. 912 for i in range(len(self.uf_data.kargs)): 913 # The argument name. 914 name = self.uf_data.kargs[i]['name'] 915 916 # No iterator method for updating the list. 917 iterator = self.uf_data.kargs[i]['wiz_combo_iter'] 918 if iterator == None: 919 continue 920 921 # Get the new choices and data (in a safe way). 922 try: 923 choices = [] 924 data = [] 925 for vals in iterator(): 926 if lib.arg_check.is_tuple(vals, size=2, raise_error=False) or lib.arg_check.is_list(vals, size=2, raise_error=False): 927 choices.append(vals[0]) 928 data.append(vals[1]) 929 else: 930 choices.append(vals) 931 data.append(vals) 932 933 # Catch all RelaxErrors. 934 except AllRelaxErrors: 935 instance = sys.exc_info()[1] 936 937 # Signal the failure to the wizard. 938 self.setup_fail = True 939 940 # Display a dialog with the error. 941 gui_raise(instance) 942 943 # Return as a failure. 944 return False 945 946 # Get the current value, for setting as the default. 947 val = self.uf_args[name].GetValue() 948 949 # Update the GUI element. 950 self.UpdateChoices(name, combo_choices=choices, combo_data=data, combo_default=val) 951 952 # Successful update. 953 return True
954 955 956
957 -class Uf_storage(dict):
958 """A singleton container for holding all the GUI user functions.""" 959 960 # Class variable for storing the class instance (for the singleton). 961 _instance = None 962
963 - def __new__(self, *args, **kargs):
964 """Replacement method for implementing the singleton design pattern.""" 965 966 # First instantiation. 967 if self._instance is None: 968 # Instantiate. 969 self._instance = dict.__new__(self, *args, **kargs) 970 971 # Generate the user functions. 972 for name, data in uf_info.uf_loop(): 973 # The title. 974 title = data.title_short 975 if not title: 976 title = data.title 977 978 # Generate a new container. 979 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) 980 981 # Store it. 982 self._instance[name] = obj 983 984 # Already instantiated, so return the instance. 985 return self._instance
986 987
988 - def get_uf(self, id=0):
989 """Return the name of the user function corresponding to the given wx ID. 990 991 @keyword id: The unique wx ID number. 992 @type id: int 993 @return: The name of the user function. 994 @rtype: str 995 """ 996 997 # Loop over the elements, returning the name when a match occurs. 998 for name in self.keys(): 999 if self[name]._uf_id == id: 1000 return name
1001