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

Source Code for Module gui.uf_objects

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