1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 """The module of all the objects used to hold the user function details."""
24
25
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
34 from graphics import IMAGE_PATH
35 from lib.errors import RelaxError
36
37
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
50 __mod_attr__ = [
51 'title',
52 'menu_text',
53 'gui_icon'
54 ]
55
57 """Initialise all the data."""
58
59
60 self.title = None
61 self.menu_text = None
62 self.gui_icon = None
63
64
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
75 if not name in self.__mod_attr__:
76 raise RelaxError("The object '%s' is not a modifiable attribute." % name)
77
78
79 self.__dict__[name] = value
80
81
82
84 """An empty container object."""
85
86
87
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
99 self._title = title
100
101
102 self._data = []
103 self._types = []
104
105
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
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
121 else:
122 self._data[-1].append([item, text])
123
124
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
133 if not len(self._types) or self._types[-1] != 'list':
134 self._data.append([text])
135 self._types.append('list')
136
137
138 else:
139 self._data[-1].append(text)
140
141
143 """Add a paragraph of text to the description.
144
145 @param text: The paragraph text.
146 @type text: str
147 """
148
149
150 self._data.append(text)
151 self._types.append('paragraph')
152
153
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
162 if not len(self._types) or self._types[-1] != 'prompt':
163 self._data.append([text])
164 self._types.append('prompt')
165
166
167 else:
168 self._data[-1].append(text)
169
170
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
179 if not isinstance(label, str):
180 raise RelaxError("The table label '%s' is not a valid string.")
181
182
183 self._data.append(label)
184 self._types.append('table')
185
186
188 """Add a section of verbatim text to the description.
189
190 @param text: The verbatim text.
191 @type text: str
192 """
193
194
195 self._data.append(text)
196 self._types.append('verbatim')
197
198
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
209 if title:
210 yield 'title', self._title
211
212
213 for i in range(len(self._data)):
214 yield self._types[i], self._data[i]
215
216
218 """Return the title of the section.
219
220 @return: The section title.
221 @rtype: str
222 """
223
224
225 return self._title
226
227
228
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
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
258 self.headings = None
259 self.cells = []
260 self.num_cols = 0
261
262
264 """Add a row of table headings.
265
266 @param headings: The table headings.
267 @type headings: list of str
268 """
269
270
271 self.headings = headings
272
273
274 self.num_cols = len(self.headings)
275
276
278 """Add a table row.
279
280 @param row: The table row.
281 @type row: list of str
282 """
283
284
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
291 self.cells.append(row)
292
293
294
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
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
345 """Initialise all the data."""
346
347
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
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
373 if not name in self.__mod_attr__:
374 raise RelaxError("The object '%s' is not a modifiable attribute." % name)
375
376
377 if name in ['title', 'title_short', 'backend', 'gui_icon']:
378
379 if not hasattr(self, name):
380 obj = None
381
382
383 else:
384 obj = getattr(self, name)
385
386
387 if obj != None:
388 raise RelaxError("The variable '%s' is already set to %s." % (name, repr(obj)))
389
390
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
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
563 if arg_type:
564
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
570 if arg_type not in ['free format']:
571
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
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
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
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
606 self.kargs.append({})
607 arg = self.kargs[-1]
608
609
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