Package user_functions :: Module objects
[hide private]
[frames] | no frames]

Source Code for Module user_functions.objects

  1  ############################################################################### 
  2  #                                                                             # 
  3  # Copyright (C) 2012,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  """The module of all the objects used to hold the user function details.""" 
 24   
 25  # Python module imports. 
 26  import dep_check 
 27  if dep_check.wx_module: 
 28      from wx import DD_DEFAULT_STYLE, FileSelectorDefaultWildcardStr 
 29  else: 
 30      DD_DEFAULT_STYLE = -1 
 31      FileSelectorDefaultWildcardStr = -1 
 32   
 33  # relax module imports. 
 34  from graphics import IMAGE_PATH 
 35  from lib.errors import RelaxError 
 36   
 37   
38 -class Class_container:
39 """This class is used to process and store all of the user function class information. 40 41 @ivar title: The user function class description. 42 @type title: str 43 @ivar menu_text: The text to use for the GUI menu entry. 44 @type menu_text: str 45 @ivar gui_icon: The code for the icon to use in the GUI. 46 @type gui_icon: str or None 47 """ 48 49 # The list of modifiable objects (anything else will be rejected to prevent coding errors). 50 __mod_attr__ = [ 51 'title', 52 'menu_text', 53 'gui_icon' 54 ] 55
56 - def __init__(self):
57 """Initialise all the data.""" 58 59 # Initialise the variables for all user function classes. 60 self.title = None 61 self.menu_text = None 62 self.gui_icon = None
63 64
65 - def __setattr__(self, name, value):
66 """Override the class __setattr__ method. 67 68 @param name: The name of the attribute to modify. 69 @type name: str 70 @param value: The new value of the attribute. 71 @type value: anything 72 """ 73 74 # Test if the attribute that is trying to be set is modifiable. 75 if not name in self.__mod_attr__: 76 raise RelaxError("The object '%s' is not a modifiable attribute." % name) 77 78 # Set the attribute normally. 79 self.__dict__[name] = value
80 81 82
83 -class Container:
84 """An empty container object."""
85 86 87
88 -class Desc_container(object):
89 """A special object for holding and processing user function description information.""" 90
91 - def __init__(self, title="Description"):
92 """Set up the container. 93 94 @keyword section: The section title. 95 @type section: str 96 """ 97 98 # Store the title. 99 self._title = title 100 101 # Initialise internal storage objects. 102 self._data = [] 103 self._types = []
104 105
106 - def add_item_list_element(self, item, text):
107 """Add the element of an itemised list to the description. 108 109 @param item: The item text. 110 @type item: str 111 @param text: The itemised list element text. 112 @type text: str 113 """ 114 115 # Create a new block if needed. 116 if not len(self._types) or self._types[-1] != 'item list': 117 self._data.append([[item, text]]) 118 self._types.append('item list') 119 120 # Append the element to an existing itemised list structure. 121 else: 122 self._data[-1].append([item, text])
123 124
125 - def add_list_element(self, text):
126 """Add the element of a list to the description. 127 128 @param text: The list element text. 129 @type text: str 130 """ 131 132 # Create a new block if needed. 133 if not len(self._types) or self._types[-1] != 'list': 134 self._data.append([text]) 135 self._types.append('list') 136 137 # Append the element to an existing list structure. 138 else: 139 self._data[-1].append(text)
140 141
142 - def add_paragraph(self, text):
143 """Add a paragraph of text to the description. 144 145 @param text: The paragraph text. 146 @type text: str 147 """ 148 149 # Store the text. 150 self._data.append(text) 151 self._types.append('paragraph')
152 153
154 - def add_prompt(self, text):
155 """Add the text of a relax prompt example to the description. 156 157 @param text: The relax prompt text. 158 @type text: str 159 """ 160 161 # Create a new block if needed. 162 if not len(self._types) or self._types[-1] != 'prompt': 163 self._data.append([text]) 164 self._types.append('prompt') 165 166 # Append the example to an existing example block. 167 else: 168 self._data[-1].append(text)
169 170
171 - def add_table(self, label):
172 """Add a table to the description. 173 174 @param label: The unique label corresponding to a user_functions.objects.Table instance. 175 @type label: str 176 """ 177 178 # Check. 179 if not isinstance(label, str): 180 raise RelaxError("The table label '%s' is not a valid string.") 181 182 # Add the table. 183 self._data.append(label) 184 self._types.append('table')
185 186
187 - def add_verbatim(self, text):
188 """Add a section of verbatim text to the description. 189 190 @param text: The verbatim text. 191 @type text: str 192 """ 193 194 # Store the text. 195 self._data.append(text) 196 self._types.append('verbatim')
197 198
199 - def element_loop(self, title=False):
200 """Iterator method yielding the description elements. 201 202 @keyword title: A flag which if True will cause the title to be yielded first. 203 @type title: bool 204 @return: The element type and corresponding data. 205 @rtype: str and anything 206 """ 207 208 # The title. 209 if title: 210 yield 'title', self._title 211 212 # Loop over the elements. 213 for i in range(len(self._data)): 214 yield self._types[i], self._data[i]
215 216
217 - def get_title(self):
218 """Return the title of the section. 219 220 @return: The section title. 221 @rtype: str 222 """ 223 224 # The title. 225 return self._title
226 227 228
229 -class Table(object):
230 """A special class defining the tables used in the user function descriptions.""" 231
232 - def __init__(self, label=None, caption=None, caption_short=None, spacing=True, longtable=False):
233 """Set up the table container. 234 235 @keyword label: The unique label of the table. This is used to identify tables, and is also used in the table referencing in the LaTeX compilation of the user manual. 236 @type label: str 237 @keyword caption: The caption for the table. 238 @type caption: str 239 @keyword caption_short: The optional short caption for the table, used in the LaTeX user manual list of tables section for example. 240 @type caption_short: str 241 @keyword spacing: A flag which if True will cause empty rows to be placed between elements. 242 @type spacing: bool 243 @keyword longtable: A special LaTeX flag which if True will cause the longtables package to be used to spread a table across multiple pages. This should only be used for tables that do not fit on a single page. 244 @type longtable: bool 245 """ 246 247 # Store the args. 248 self.label = label 249 self.caption = caption 250 if caption_short: 251 self.caption_short = caption_short 252 else: 253 self.caption_short = caption 254 self.spacing = spacing 255 self.longtable = longtable 256 257 # Initialise some objects. 258 self.headings = None 259 self.cells = [] 260 self.num_cols = 0
261 262
263 - def add_headings(self, headings):
264 """Add a row of table headings. 265 266 @param headings: The table headings. 267 @type headings: list of str 268 """ 269 270 # Store the headings. 271 self.headings = headings 272 273 # The number of columns. 274 self.num_cols = len(self.headings)
275 276
277 - def add_row(self, row):
278 """Add a table row. 279 280 @param row: The table row. 281 @type row: list of str 282 """ 283 284 # Checks. 285 if self.headings == None: 286 raise RelaxError("A row cannot be added as the headings have not been set up.") 287 if len(row) != self.num_cols: 288 raise RelaxError("The number of columns in %s does not match the %s columns of the headings." % (row, self.num_cols)) 289 290 # Append the row. 291 self.cells.append(row)
292 293 294
295 -class Uf_container(object):
296 """This class is used to process and store all of the user function specific information. 297 298 @ivar title: The long title of the user function. 299 @type title: str 300 @ivar title_short: The optional short title. 301 @type title_short: str or None 302 @ivar kargs: The list of keyword argument details. 303 @type kargs: list of dict 304 @ivar backend: The user function back end. This should be a string version with full module path of the function which executes the back end. For example 'pipe_control.pipes.create'. Note, this should be importable as __import__(backend)! 305 @type backend: executable object 306 @ivar display: A flag specifying if the user function displays output to STDOUT. This is used for certain UIs to display that output. 307 @type display: str 308 @ivar desc: The multi-paragraph description defined via the Desc_container class. 309 @type desc: list of Desc_container instances 310 @ivar menu_text: The text to use for the GUI menu entry. 311 @type menu_text: str 312 @ivar gui_icon: The code for the icon to use in the GUI. 313 @type gui_icon: str or None 314 @ivar wizard_size: The size for the GUI user function wizard. This defaults to (700, 500) if not supplied. 315 @type wizard_size: tuple of int or None 316 @ivar wizard_image: The 200 pixel wide image to use for the user function wizard. This should be the path to the bitmap image. This defaults to the relax Ulysses butterfly image. 317 @type wizard_image: str 318 @ivar wizard_height_desc: The height in pixels of the description part of the wizard. 319 @type wizard_height_desc: int 320 @ivar wizard_apply_button: A flag specifying if the apply button should be shown or not. This defaults to True. 321 @type wizard_apply_button: bool 322 @ivar gui_sync: A GUI flag which if left on the default of False will cause user functions to be called in asynchronous mode. If changed to True, then synchronous operation of the user functions will occur. 323 @type gui_sync: bool 324 """ 325 326 # The list of modifiable objects (anything else will be rejected to prevent coding errors). 327 __mod_attr__ = [ 328 'title', 329 'title_short', 330 'kargs', 331 'backend', 332 'display', 333 'desc', 334 'menu_text', 335 'gui_icon', 336 'wizard_size', 337 'wizard_image', 338 'wizard_height_desc', 339 'wizard_apply_button', 340 'gui_sync', 341 ] 342 343
344 - def __init__(self):
345 """Initialise all the data.""" 346 347 # Initialise the variables for all user functions. 348 self.title = None 349 self.title_short = None 350 self.kargs = [] 351 self.backend = None 352 self.display = False 353 self.desc = [] 354 self.menu_text = '' 355 self.gui_icon = None 356 self.wizard_size = (700, 500) 357 self.wizard_image = IMAGE_PATH + "relax.gif" 358 self.wizard_height_desc = 300 359 self.wizard_apply_button = True 360 self.gui_sync = False
361 362
363 - def __setattr__(self, name, value):
364 """Override the class __setattr__ method. 365 366 @param name: The name of the attribute to modify. 367 @type name: str 368 @param value: The new value of the attribute. 369 @type value: anything 370 """ 371 372 # Test if the attribute that is trying to be set is modifiable. 373 if not name in self.__mod_attr__: 374 raise RelaxError("The object '%s' is not a modifiable attribute." % name) 375 376 # Check for duplicative modifications (to catch typo coding errors). 377 if name in ['title', 'title_short', 'backend', 'gui_icon']: 378 # No object set yet. 379 if not hasattr(self, name): 380 obj = None 381 382 # Get the current object. 383 else: 384 obj = getattr(self, name) 385 386 # Not None! 387 if obj != None: 388 raise RelaxError("The variable '%s' is already set to %s." % (name, repr(obj))) 389 390 # Set the attribute normally. 391 self.__dict__[name] = value
392 393
394 - def add_keyarg(self, name=None, default=None, basic_types=[], container_types=[], dim=(), arg_type=None, min=0, max=1000, desc_short=None, desc=None, list_titles=None, wiz_element_type='default', wiz_combo_choices=None, wiz_combo_data=None, wiz_combo_iter=None, wiz_combo_list_min=None, wiz_filesel_wildcard=FileSelectorDefaultWildcardStr, wiz_dirsel_style=DD_DEFAULT_STYLE, wiz_read_only=None, wiz_filesel_preview=True, can_be_none=False, can_be_empty=False, none_elements=False):
395 """Wrapper method for adding keyword argument information to the container. 396 397 Types 398 ===== 399 400 The basic Python data types allowed for the argument are specified via the basic_types argument. The currently supported values include: 401 402 - 'all': Special value used to deactivate type-checking. 403 - 'bool': Boolean values (True and False). 404 - 'float': Floating point numbers. 405 - 'func': Python function objects. 406 - 'int': Integer numbers. 407 - 'number': Special value allowing for any number type. 408 - 'str': String objects. 409 - 'file object': File objects (instance of file or any object with a write() method). 410 411 The 'number' value is special in that it allows for both 'int' and 'float' values. If the argument should be a higher rank object, then the container_types argument should be supplied. The allowed values currently include: 412 413 - 'all': Special value used to deactivate type-checking. 414 - 'list': Python lists. 415 - 'number array': Special value meaning both 'list' and 'numpy array'. 416 - 'numpy array': NumPy array objects. 417 - 'set': Python sets. 418 - 'tuple': Python tuples. 419 420 Here, the 'number array' is also special and allows for both 'list' and 'numpy array' containers. Note that only the basic types 'float', 'int', and 'number' are allowed with this value. 421 422 423 Rank and dimensionality 424 ======================= 425 426 To distinguish between basic Python data types and higher rank container types, as well as fixing the dimensionality of the higher rank objects, the 'dim' parameter should be supplied. This should be a tuple with elements consisting of integers or None. If multiple ranks or dimensionality are allowed, then a list of tuples can be supplied. 427 428 429 Rank 430 ---- 431 432 The number of elements of the 'dim' tuples define the rank. For example a number is rank 0, a vector is rank 1, and a matrix is rank 2. 433 434 435 Dimensionality 436 -------------- 437 438 The dimensionality, or number of elements, for each rank are fixed by supplying integers in the 'dim' tuple. If the dimensionality can be variable, the value of None should be supplied instead. 439 440 441 Examples 442 -------- 443 444 For basic Python data types, use the empty tuple: 445 446 - dim=() 447 448 For a list of basic data types of unknown length, use: 449 450 - dim=(None,) 451 452 For a numpy array of 5 elements, use: 453 454 - dim=(5,) 455 456 For a numpy 3D matrix, use: 457 458 - dim=(3,3) 459 460 For a simple string or list of string, use: 461 462 - dim=[(), (None,)] 463 464 465 Special argument types 466 ====================== 467 468 The 'arg_type' keyword enables a number of special argument types to be defined. This is used to set up special UI elements. The current set of values are: 469 470 - 'dir': This causes the argument to not be shown in certain UIs, as this indicates that the user function already has a 'file sel *' type argument and hence directory selection is redundant. 471 - 'dir sel': Indicate to certain UIs that a dir selection dialog is required. 472 - 'file sel read': Indicate to certain UIs that a file selection dialog for reading a file is required. 473 - 'file sel write': Indicate to certain UIs that a file selection dialog for writing a file is required. 474 - 'file sel multi read': The same as 'file sel read', except that multiple files can be selected simultaneously. 475 - 'file sel multi write': The same as 'file sel write', except that multiple files can be selected simultaneously. 476 - 'force flag': Used to suppress the argument in the GUI. For example when writing a file, the normal 'force' argument is meaningless in the GUI as the operating file system interface will ask if the file should be overwritten. 477 - 'free format': Used in the GUI to suppress the default argument UI element and instead show the special free file format UI element. 478 - 'func': Used in the GUI to suppress the argument as functions are not supported. 479 - 'func args': Used in the GUI to suppress the argument as functions are not supported. 480 - 'spin ID': Activate the special spin ID UI element in the GUI. 481 482 In addition, some of these values will automatically set the 'dim', 'basic_types', and 'container_types' arguments:: 483 484 _________________________________________________________________________________________ 485 | | | | | 486 | Argument | dim | basic_types | container_types | 487 |______________________|_______________|______________________________|_________________| 488 | | | | | 489 | dir | () | ['str'] | [] | 490 | dir sel | () | ['str'] | [] | 491 | file sel read | () | ['str', 'file object read'] | [] | 492 | file sel write | () | ['str', 'file object write'] | [] | 493 | file sel multi read | [(), (None,)] | ['str', 'file object read'] | ['list'] | 494 | file sel multi write | [(), (None,)] | ['str', 'file object write'] | ['list'] | 495 | force flag | () | ['bool'] | [] | 496 | func | () | ['func'] | [] | 497 | func args | (None,) | ['all'] | ['tuple'] | 498 | spin ID | () | ['str'] | [] | 499 |______________________|_______________|______________________________|_________________| 500 501 502 @keyword name: The name of the argument. 503 @type name: str 504 @keyword default: The default value of the argument. 505 @type default: anything 506 @keyword dim: The dimensions of the object to check. 507 @type dim: tuple of (int or None) or list of tuples of (int or None) 508 @keyword basic_types: The types of values are allowed for the argument. 509 @type basic_types: list of str 510 @keyword container_types: The container types allowed for the argument. 511 @type container_types: list of str 512 @keyword arg_type: A special type of argument. See the docstring for details. 513 @type arg_type: str 514 @keyword min: The minimum value allowed for integer types. This is used in the wx.SpinCtrl for example. 515 @type min: int 516 @keyword max: The maximum value allowed for integer types. This is used in the wx.SpinCtrl for example. 517 @type max: int 518 @keyword desc_short: The short human-readable description of the argument. This is used in the RelaxError messages to refer to the argument, as well as in the GUI user function page elements. 519 @type desc_short: str 520 @keyword desc: The long human-readable description of the argument. 521 @type desc: str 522 @keyword list_titles: The titles of each of the elements of the fixed length lists. This only applies to lists or list of lists. 523 @type list_titles: list of str 524 @keyword wiz_element_type: The type of GUI element to create. If left to 'default', then the currently set default element will be used. If set to 'text', a wx.TextCtrl element will be used. If set to 'combo', a wx.ComboBox element will be used. 525 @type wiz_element_type: str 526 @keyword wiz_combo_choices: The list of choices to present to the user. This is only used if the element_type is set to 'combo'. 527 @type wiz_combo_choices: list of str 528 @keyword wiz_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. 529 @type wiz_combo_data: list 530 @keyword wiz_combo_iter: An iterator method for regenerating the ComboBox choices. 531 @type wiz_combo_iter: iterator or None 532 @keyword wiz_combo_list_min: The minimum length of the Combo_list element. 533 @type wiz_combo_list_min: int or None 534 @keyword wiz_filesel_wildcard: The file selection dialog wildcard string. For example for opening PDB files, this could be "PDB files (*.pdb)|*.pdb;*.PDB". 535 @type wiz_filesel_wildcard: str or None 536 @keyword wiz_dirsel_style: The directory selection dialog style. 537 @type wiz_dirsel_style: int 538 @keyword wiz_read_only: A flag which if True means that the text of the GUI wizard page element cannot be edited. If the default of None is given, then each UI element will decide for itself what to do. 539 @type wiz_read_only: bool or None 540 @keyword wiz_filesel_preview: A flag which if True will enable the preview button in the file selection GUI wizard page element. 541 @type wiz_filesel_preview: bool 542 @keyword can_be_none: A flag which specifies if the argument is allowed to have the None value. 543 @type can_be_none: bool 544 @keyword can_be_empty: A flag which if True allows the sequence type object to be empty. 545 @type can_be_empty: bool 546 @keyword none_elements: A flag which if True allows the sequence type object to contain None elements. 547 @type none_elements: bool 548 """ 549 550 # Check that the args have been properly supplied. 551 if name == None: 552 raise RelaxError("The 'name' argument must be supplied.") 553 if desc_short == None: 554 raise RelaxError("The 'desc_short' argument must be supplied.") 555 if desc == None: 556 raise RelaxError("The 'desc' argument must be supplied.") 557 if not isinstance(basic_types, list): 558 raise RelaxError("The 'basic_types' argument must be a list.") 559 if not isinstance(container_types, list): 560 raise RelaxError("The 'container_types' argument must be a list.") 561 562 # Process the special arguments. 563 if arg_type: 564 # Check the value. 565 allowed = ['dir', 'dir sel', 'file sel read', 'file sel write', 'file sel multi read', 'file sel multi write', 'force flag', 'free format', 'func', 'func args', 'spin ID'] 566 if arg_type not in allowed: 567 raise RelaxError("The 'arg_type' argument '%s' should be one of %s." % (arg_type, allowed)) 568 569 # Overrides. 570 if arg_type not in ['free format']: 571 # Check the type and dimensionality arguments. 572 if dim != (): 573 raise RelaxError("The 'dim' argument cannot be set for the '%s' special argument." % arg_type) 574 if basic_types != []: 575 raise RelaxError("The 'basic_types' argument cannot be set for the '%s' special argument." % arg_type) 576 if container_types != []: 577 raise RelaxError("The 'container_types' argument cannot be set for the '%s' special argument." % arg_type) 578 579 # Override the dim argument. 580 if arg_type in ['func args']: 581 dim = (None,) 582 if arg_type in ['file sel multi read', 'file sel multi write']: 583 dim = [(), (None,)] 584 585 # Override the basic_types argument. 586 if arg_type in ['dir', 'dir sel', 'spin ID']: 587 basic_types = ['str'] 588 elif arg_type in ['file sel read', 'file sel multi read']: 589 basic_types = ['str', 'file object read'] 590 elif arg_type in ['file sel write', 'file sel multi write']: 591 basic_types = ['str', 'file object write'] 592 elif arg_type in ['force flag']: 593 basic_types = ['bool'] 594 elif arg_type in ['func']: 595 basic_types = ['func'] 596 elif arg_type in ['func args']: 597 basic_types = ['all'] 598 599 # Override the container_types argument. 600 if arg_type in ['file sel multi read', 'file sel multi write']: 601 container_types = ['list'] 602 elif arg_type in ['func args']: 603 container_types = ['tuple'] 604 605 # Append a new argument dictionary to the list, and alias it. 606 self.kargs.append({}) 607 arg = self.kargs[-1] 608 609 # Add the data. 610 arg['name'] = name 611 arg['default'] = default 612 arg['dim'] = dim 613 arg['basic_types'] = basic_types 614 arg['container_types'] = container_types 615 arg['arg_type'] = arg_type 616 arg['min'] = min 617 arg['max'] = max 618 arg['desc_short'] = desc_short 619 arg['desc'] = desc 620 arg['list_titles'] = list_titles 621 arg['wiz_element_type'] = wiz_element_type 622 if wiz_combo_choices == None: 623 arg['wiz_combo_choices'] = [] 624 else: 625 arg['wiz_combo_choices'] = wiz_combo_choices 626 arg['wiz_combo_data'] = wiz_combo_data 627 arg['wiz_combo_iter'] = wiz_combo_iter 628 arg['wiz_combo_list_min'] = wiz_combo_list_min 629 arg['wiz_filesel_wildcard'] = wiz_filesel_wildcard 630 arg['wiz_dirsel_style'] = wiz_dirsel_style 631 arg['wiz_read_only'] = wiz_read_only 632 arg['wiz_filesel_preview'] = wiz_filesel_preview 633 arg['can_be_none'] = can_be_none 634 arg['can_be_empty'] = can_be_empty 635 arg['none_elements'] = none_elements
636