1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 """Module containing the special objects for auto-generating the GUI user functions and classes."""
24
25
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
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
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
68 menu1 = wx.Menu()
69 menu2 = wx.Menu()
70
71
72 pattern = '^[a-m]'
73
74
75 class_list = []
76 uf_store = Uf_storage()
77
78
79 class_item = None
80 menu = menu1
81 menu_index = 0
82 for name, data in uf_info.uf_loop():
83
84 if search('\.', name):
85 class_name, uf_name = name.split('.')
86 else:
87 class_name = None
88
89
90 if class_name:
91 if class_name not in class_list:
92
93 if class_item != None:
94 menu.AppendItem(class_item)
95
96
97 class_data = uf_info.get_class(class_name)
98
99
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
103 sub_menu = wx.Menu()
104 class_item.SetSubMenu(sub_menu)
105
106
107 class_list.append(class_name)
108
109
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
113 else:
114
115 if class_item != None:
116 menu.AppendItem(class_item)
117 class_item = None
118
119
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
123 if menu_index == 0 and not search(pattern, name):
124 menu = menu2
125 menu_index = 1
126
127
128 parent.Bind(wx.EVT_MENU, parent.uf_call, id=uf_store[name]._uf_id)
129
130
131 if class_item != None:
132 menu.AppendItem(class_item)
133
134
135 title1 = "&User functions (a-m)"
136 title2 = "&User functions (n-z)"
137 menubar.Append(menu1, title1)
138 menubar.Append(menu2, title2)
139
140
141 return [menubar.FindMenu(title1), menubar.FindMenu(title2)]
142
143
144
146 """A special user function arg element which always returns True."""
147
149 """Initialise the object."""
150
151
152 self._value = True
153
154
156 """Simple method for returning the internal value."""
157
158
159 return self._value
160
161
163 """Internally store the value being set."""
164
165
166 self._value = value
167
168
169
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
194 if wx_wizard_sync != None:
195 self._sync = wx_wizard_sync
196
197
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
204 else:
205 self.wizard.reset()
206
207
208 if not self.page.update_args():
209 return False
210
211
212 for key in kwds:
213 self.page.SetValue(key, kwds[key])
214
215
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
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
246 self.wizard = None
247
248
249 self._uf_id = wx.NewId()
250
251
253 """Cleanly destroy the user function GUI elements."""
254
255
256 wx.Yield()
257
258
259 if hasattr(self, 'page'):
260
261 for key in self.page.uf_args:
262
263 if hasattr(self.page.uf_args[key], 'sel_win'):
264 self.page.uf_args[key].sel_win.Destroy()
265
266
267 del self.page
268
269
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
289 if sync != None:
290 self._sync = sync
291
292
293 return Uf_page(self._name, parent=wizard, height_desc=self._height_desc, sync=self._sync, execute=execute)
294
295
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
306 if parent == None:
307 app = wx.GetApp()
308 parent = app.gui
309
310
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
314 self.page = self.create_page(self.wizard, sync=self._sync)
315
316
317 if not self.page.update_args():
318 return False
319
320
321 self.wizard.add_page(self.page, apply_button=self._apply_button)
322
323
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
347 self.name = name
348 self.sync = sync
349 self.execute_flag = execute
350
351
352 self.uf_args = {}
353
354
355 wx.Yield()
356
357
358 wx.BeginBusyCursor()
359
360
361 self.uf_data = uf_info.get_uf(name)
362
363
364 self.image_path = self.uf_data.wizard_image
365
366
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
373 super(Uf_page, self).__init__(parent, height_desc=height_desc)
374
375
376 if wx.IsBusy():
377 wx.EndBusyCursor()
378
379
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
390 stripped_text = strip_lead(text)
391
392
393 while True:
394 if stripped_text[0] == "\n":
395 stripped_text = stripped_text[1:]
396 else:
397 break
398
399
400 while True:
401 if stripped_text[-1] == "\n":
402 stripped_text = stripped_text[:-1]
403 else:
404 break
405
406
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
424 text = ""
425
426
427 if prompt:
428 text += status.ps3
429
430
431 text += "%s(" % self.name
432
433
434 for i in range(len(keys)):
435
436 if i >= 1:
437 text += ", "
438
439
440 text += "%s=%s" % (keys[i], repr(values[i]))
441
442
443 text += ")"
444
445
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
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
470 if key not in self.uf_args:
471 return None
472
473
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
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
493 if arg == None:
494 raise RelaxError("The key '%s' is unknown." % key)
495
496
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
501 elif arg['arg_type'] in ['func', 'func args']:
502 pass
503
504
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
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
534 free_format = False
535 free_format_data = False
536
537
538 for i in range(len(self.uf_data.kargs)):
539
540 arg = self.uf_data.kargs[i]
541
542
543 desc = "The %s:" % arg['desc_short']
544
545
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
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
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
577 elif arg['arg_type'] == 'dir':
578 pass
579
580
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
585 elif arg['arg_type'] == 'free format':
586
587 free_format = True
588 if arg['name'] == 'data_col':
589 free_format_data = True
590
591
592 elif arg['arg_type'] in ['func', 'func args']:
593 pass
594
595
596 elif arg['arg_type'] in ['force flag']:
597 self.uf_args[arg['name']] = Force_true()
598
599
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
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
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
617 elif len(dim) == 1:
618
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
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
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
641 elif len(dim) == 2:
642
643 if 'list' in arg['container_types']:
644 seq_type = 'list'
645 else:
646 seq_type = 'tuple'
647
648
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
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
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
679 spacing = 15
680
681
682 sizer.AddSpacer(5)
683 sizer.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.ALL, 0)
684 sizer.AddSpacer(5)
685
686
687 panel = scrolledpanel.ScrolledPanel(self, -1, name="desc")
688
689
690 panel_sizer = wx.BoxSizer(wx.VERTICAL)
691
692
693 tot_y = 0
694 text_elements = []
695 text_types = []
696
697
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
705 x, y = text.GetSizeTuple()
706 tot_y += y
707 tot_y += spacing
708
709
710 if self.uf_data.title:
711
712 text = wx.StaticText(panel, -1, self.uf_data.title, style=wx.TE_MULTILINE)
713
714
715 text.SetFont(font.normal_italic)
716
717
718 x, y = text.GetSizeTuple()
719 tot_y += y
720
721
722 tot_y += spacing
723
724
725 text_elements.append(text)
726 text_types.append('synopsis')
727
728
729 if self.uf_data.desc != None:
730
731 for i in range(len(self.uf_data.desc)):
732
733 desc = self.uf_data.desc[i]
734
735
736 if desc.get_title() == 'Prompt examples':
737 continue
738
739
740 for type, element in desc.element_loop(title=True):
741
742 text = ''
743 if isinstance(element, str):
744 text = element
745
746
747 if type == 'table':
748 text = format_table(uf_tables.get_table(element))
749
750
751 elif type == 'list':
752
753 for j in range(len(element)):
754 text += " - %s\n" % element[j]
755
756
757 text = text[:-1]
758
759
760 elif type == 'item list':
761
762 for j in range(len(element)):
763
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
770 text = text[:-1]
771
772
773 elif type == 'prompt':
774 for j in range(len(element)):
775 text += "%s\n" % element[j]
776
777
778 text = text[:-1]
779
780
781 text_obj = wx.StaticText(panel, -1, text, style=wx.TE_MULTILINE)
782
783
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
794 if type in ['paragraph', 'list', 'item list']:
795 text_obj.Wrap(self._main_size - 20)
796
797
798 x, y = text_obj.GetSizeTuple()
799 tot_y += y
800
801
802 tot_y += spacing
803
804
805 if i != 0 and type == 'title':
806 tot_y += spacing
807
808
809 text_elements.append(text_obj)
810 text_types.append(type)
811
812
813 tot_y -= spacing
814 tot_y += 20
815
816
817 if tot_y > max_y:
818 panel.SetInitialSize((self._main_size, max_y))
819
820
821 else:
822 panel.SetInitialSize((self._main_size, tot_y))
823
824
825 n = len(text_elements)
826 for i in range(n):
827
828 if i > 1 and text_types[i] == 'title':
829 panel_sizer.AddSpacer(spacing)
830
831
832 panel_sizer.Add(text_elements[i], 0, wx.ALIGN_LEFT, 0)
833
834
835 if i != n - 1:
836 panel_sizer.AddSpacer(spacing)
837
838
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
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
862 if self.sync or status.gui_uf_force_sync:
863 return_status = interpreter.apply(uf, *args, **kwds)
864 return return_status
865
866
867 else:
868 interpreter.queue(uf, *args, **kwds)
869 return True
870
871
873 """Remove this page from the observers."""
874
875
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
883 status.observers.gui_uf.register(self.name, self.update_args, method_name='update_args')
884
885
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
897 if not force_exec and not self.execute_flag:
898 return
899
900
901 kargs = {}
902 for i in range(len(self.uf_data.kargs)):
903
904 name = self.uf_data.kargs[i]['name']
905
906
907 kargs[name] = self.GetValue(name)
908
909
910 if self.uf_data.kargs[i]['wiz_combo_list_min'] != None and kargs[name] == None:
911 return True
912
913
914 if 'free_file_format' in self.uf_args:
915 kargs.update(self.uf_args['free_file_format'].GetValue())
916
917
918 if self.uf_data.display:
919
920 app = wx.GetApp()
921
922
923 app.gui.show_controller(None)
924
925
926 app.gui.controller.log_panel.on_goto_end(None)
927
928
929 if status.uf_intro:
930
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
938 print(self._intro_text(keys, values))
939
940
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
947 gui_raise(sys.exc_info()[1])
948
949
950 return False
951
952
953 return_status = self.execute(self.name, **kargs)
954
955
956 if status.show_gui and self.uf_data.display:
957 wx.CallAfter(app.gui.controller.Raise)
958
959
960 return return_status
961
962
964 """Remove this page from the observers."""
965
966
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
978 for i in range(len(self.uf_data.kargs)):
979
980 name = self.uf_data.kargs[i]['name']
981
982
983 iterator = self.uf_data.kargs[i]['wiz_combo_iter']
984 if iterator == None:
985 continue
986
987
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
1000 except AllRelaxErrors:
1001 instance = sys.exc_info()[1]
1002
1003
1004 self.setup_fail = True
1005
1006
1007 gui_raise(instance)
1008
1009
1010 return False
1011
1012
1013 val = self.uf_args[name].GetValue()
1014
1015
1016 self.UpdateChoices(name, combo_choices=choices, combo_data=data, combo_default=val)
1017
1018
1019 return True
1020
1021
1022
1024 """A singleton container for holding all the GUI user functions."""
1025
1026
1027 _instance = None
1028
1029 - def __new__(self, *args, **kargs):
1052
1053
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
1064 for name in self:
1065 if self[name]._uf_id == id:
1066 return name
1067