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 a set of special GUI elements to be used in the relax wizards."""
24
25
26 import wx
27 import wx.lib.mixins.listctrl
28
29
30 from graphics import fetch_icon
31 from gui.input_elements.combo_list import Combo_list
32 from gui.fonts import font
33 from gui.misc import add_border
34 from gui.string_conv import float_to_gui, gui_to_float, gui_to_int, gui_to_list, gui_to_py, gui_to_str, gui_to_tuple, int_to_gui, list_to_gui, py_to_gui, str_to_gui, tuple_to_gui
35 from lib.errors import RelaxError
36 from status import Status; status = Status()
37
38
40 """Wizard GUI element for the input of all types of Python sequence objects.
41
42 The supported Python types include:
43 - list of floats
44 - list of integers
45 - list of strings
46 - tuple of floats
47 - tuple of integers
48 - tuple of strings
49 """
50
51 - def __init__(self, name=None, default=None, parent=None, element_type='default', seq_type=None, value_type=None, dim=None, min=0, max=1000, sizer=None, desc=None, combo_choices=None, combo_data=None, combo_list_min=None, tooltip=None, divider=None, padding=0, spacer=None, height_element=27, single_value=False, read_only=False, can_be_none=False):
52 """Set up the element.
53
54 @keyword name: The name of the element to use in titles, etc.
55 @type name: str
56 @keyword default: The default value of the element.
57 @type default: sequence object
58 @keyword parent: The wizard GUI element.
59 @type parent: wx.Panel instance
60 @keyword element_type: The type of GUI element to create. If set to 'default', the wx.TextCtrl element with a button to bring up a dialog with ListCtrl will be used. If set to 'combo_list', the special gui.components.combo_list.Combo_list element will be used.
61 @type element_type: str
62 @keyword seq_type: The type of Python sequence. This should be one of 'list' or 'tuple'.
63 @type seq_type: str
64 @keyword value_type: The type of Python object that the value should be. This can be one of 'float', 'int', or 'str'.
65 @type value_type: str
66 @keyword dim: The dimensions that a list or tuple must conform to. For a 1D sequence, this can be a single value or a tuple of possible sizes. For a 2D sequence (a numpy matrix or list of lists), this must be a tuple of the fixed dimension sizes, e.g. a 3x5 matrix should be specified as (3, 5).
67 @type dim: int, tuple of int or None
68 @keyword min: For a SpinCtrl, the minimum value allowed.
69 @type min: int
70 @keyword max: For a SpinCtrl, the maximum value allowed.
71 @type max: int
72 @keyword sizer: The sizer to put the input field widget into.
73 @type sizer: wx.Sizer instance
74 @keyword desc: The text description.
75 @type desc: str
76 @keyword combo_choices: The list of choices to present to the user. This is only used if the element_type is set to 'combo'.
77 @type combo_choices: list of str
78 @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.
79 @type combo_data: list
80 @keyword combo_list_min: The minimum length for the Combo_list object.
81 @type combo_list_min: int or None
82 @keyword tooltip: The tooltip which appears on hovering over the text or input field.
83 @type tooltip: str
84 @keyword divider: The position of the divider.
85 @type divider: int
86 @keyword padding: Spacing to the left and right of the widgets.
87 @type padding: int
88 @keyword spacer: The amount of spacing to add below the field in pixels. If None, a stretchable spacer will be used.
89 @type spacer: None or int
90 @keyword height_element: The height in pixels of the GUI element.
91 @type height_element: int
92 @keyword single_value: A flag which if True will cause single input values to be treated as single values rather than a list or tuple.
93 @type single_value: bool
94 @keyword read_only: A flag which if True means that the text of the element cannot be edited.
95 @type read_only: bool
96 @keyword can_be_none: A flag which specifies if the element is allowed to have the None value.
97 @type can_be_none: bool
98 """
99
100
101 self.parent = parent
102 self.name = name
103 self.default = default
104 self.element_type = element_type
105 self.seq_type = seq_type
106 self.value_type = value_type
107 self.dim = dim
108 self.min = min
109 self.max = max
110 self.single_value = single_value
111 self.can_be_none = can_be_none
112
113
114 if value_type in ['float', 'num']:
115 self.convert_from_gui = gui_to_float
116 self.convert_to_gui = float_to_gui
117 elif value_type == 'int':
118 self.convert_from_gui = gui_to_int
119 self.convert_to_gui = int_to_gui
120 elif value_type == 'str':
121 self.convert_from_gui = gui_to_str
122 self.convert_to_gui = str_to_gui
123 else:
124 self.convert_from_gui = gui_to_py
125 self.convert_to_gui = py_to_gui
126
127
128 if seq_type == 'list':
129 self.convert_from_gui_seq = gui_to_list
130 self.convert_to_gui_seq = list_to_gui
131 elif seq_type == 'tuple':
132 self.convert_from_gui_seq = gui_to_tuple
133 self.convert_to_gui_seq = tuple_to_gui
134 else:
135 raise RelaxError("Unknown sequence type '%s'." % seq_type)
136
137
138 if self.element_type == 'default':
139
140 if read_only == None:
141 read_only = False
142
143
144 sub_sizer = wx.BoxSizer(wx.HORIZONTAL)
145
146
147 sub_sizer.AddSpacer(padding)
148
149
150 text = wx.StaticText(parent, -1, desc, style=wx.ALIGN_LEFT)
151 text.SetFont(font.normal)
152 sub_sizer.Add(text, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 0)
153
154
155 if not divider:
156 raise RelaxError("The divider position has not been supplied.")
157
158
159 x, y = text.GetSize()
160 sub_sizer.AddSpacer((divider - x, 0))
161
162
163 self._field = wx.TextCtrl(parent, -1, '')
164 self._field.SetMinSize((50, height_element))
165 self._field.SetFont(font.normal)
166 sub_sizer.Add(self._field, 1, wx.ADJUST_MINSIZE|wx.ALIGN_CENTER_VERTICAL, 0)
167
168
169 if read_only:
170 self._field.SetEditable(False)
171 colour = parent.GetBackgroundColour()
172 self._field.SetOwnBackgroundColour(colour)
173
174
175 sub_sizer.AddSpacer(5)
176
177
178 button = wx.BitmapButton(parent, -1, wx.Bitmap(fetch_icon('oxygen.actions.edit_rename', "16x16"), wx.BITMAP_TYPE_ANY))
179 button.SetMinSize((height_element, height_element))
180 button.SetToolTipString("Edit the values.")
181 sub_sizer.Add(button, 0, wx.ADJUST_MINSIZE|wx.ALIGN_CENTER_VERTICAL, 0)
182 parent.Bind(wx.EVT_BUTTON, self.open_dialog, button)
183
184
185 sub_sizer.AddSpacer(padding)
186
187
188 sizer.Add(sub_sizer, 1, wx.EXPAND|wx.ALL, 0)
189
190
191 if spacer == None:
192 sizer.AddStretchSpacer()
193 else:
194 sizer.AddSpacer(spacer)
195
196
197 if tooltip:
198 text.SetToolTipString(tooltip)
199 self._field.SetToolTipString(tooltip)
200
201
202 if self.default != None:
203 self._field.SetValue(self.convert_to_gui_seq(self.default))
204
205
206 elif self.element_type == 'combo_list':
207
208 if read_only == None:
209 read_only = False
210
211
212 self._field = Combo_list(parent, sizer, desc, value_type=value_type, min_length=combo_list_min, choices=combo_choices, data=combo_data, default=default, tooltip=tooltip, read_only=read_only, can_be_none=can_be_none)
213
214
215 else:
216 raise RelaxError("Unknown element type '%s'." % self.element_type)
217
218
220 """Special method for clearing or resetting the GUI element."""
221
222
223 if self.element_type in ['default', 'combo_list']:
224 self._field.Clear()
225
226
228 """Special method for returning the sequence values of the GUI element.
229
230 @return: The sequence of values.
231 @rtype: sequence type
232 """
233
234
235 value = self._field.GetValue()
236
237
238 if self.element_type == 'combo_list':
239
240 if value == [] or value == None:
241 return None
242
243
244 else:
245
246 value_set = False
247 if self.single_value:
248 try:
249
250 value = self.convert_from_gui(value)
251
252
253 if value == None and self.can_be_none:
254 value_set = True
255 elif self.value_type == None:
256 value_set = True
257 elif self.value_type in ['float', 'num']:
258 if isinstance(value, int) or isinstance(value, float):
259 value_set = True
260 elif self.value_type == 'int':
261 if isinstance(value, int):
262 value_set = True
263 elif self.value_type == 'str':
264 if self.seq_type == 'list' and value[0] != '[':
265 value_set = True
266 elif self.seq_type == 'tuple' and value[0] != '(':
267 value_set = True
268 except:
269 pass
270
271
272 if not value_set:
273 try:
274 value = self.convert_from_gui_seq(value)
275
276
277 except RelaxError:
278 if self.can_be_none:
279 value = None
280 elif self.seq_type == 'list':
281 value = []
282 else:
283 value = ()
284
285
286 if self.single_value:
287 if (isinstance(value, list) or isinstance(value, tuple)) and len(value) == 1:
288 value = value[0]
289
290
291 elif value != None:
292 if self.seq_type == 'list' and not isinstance(value, list):
293 value = [value]
294 elif self.seq_type == 'tuple' and not isinstance(value, tuple):
295 value = (value,)
296
297
298 if not self.single_value and len(value) == 0:
299 return None
300
301
302 return value
303
304
305 - def SetValue(self, value=None, index=None):
306 """Special method for setting the value of the GUI element.
307
308 @keyword value: The value to set.
309 @type value: value or list of values
310 @keyword index: The index of the value to set, if the full list is not given.
311 @type index: int or None
312 """
313
314
315 if self.element_type == 'combo_list':
316 self._field.SetValue(value=value, index=index)
317
318
319 else:
320
321 if self.single_value and isinstance(value, list) and len(value) == 1:
322 value = value[0]
323
324
325 self._field.SetValue(self.convert_to_gui_seq(value))
326
327
328 - def UpdateChoices(self, combo_choices=None, combo_data=None, combo_default=None):
329 """Special wizard method for updating the list of choices in a ComboBox type element.
330
331 @keyword combo_choices: The list of choices to present to the user. This is only used if the element_type is set to 'combo_list'.
332 @type combo_choices: list of str
333 @keyword combo_data: The data returned by a call to GetValue(). This is only used if the element_type is set to 'combo_list'. 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.
334 @type combo_data: list
335 @keyword combo_default: The default value of the ComboBox. This is only used if the element_type is set to 'combo_list'.
336 @type combo_default: str or None
337 """
338
339
340 if self.element_type == 'combo_list':
341 self._field.UpdateChoices(combo_choices=combo_choices, combo_data=combo_data, combo_default=combo_default)
342
343
345 """Open a special dialog for inputting a list of text values.
346
347 @param event: The wx event.
348 @type event: wx event
349 """
350
351
352 self.selection_win_show()
353
354
355 self.selection_win_data()
356
357
358 del self.sel_win
359
360
362 """Extract the data from the selection window."""
363
364
365 value = self.sel_win.GetValue()
366
367
368 if not len(value):
369 self.Clear()
370
371
372 else:
373 self.SetValue(value)
374
375
377 """Show the selection window."""
378
379
380 self.sel_win = Sequence_window(parent=self.parent, name=self.name, seq_type=self.seq_type, value_type=self.value_type, dim=self.dim)
381
382
383 self.sel_win.SetValue(self.GetValue())
384
385
386 if status.show_gui:
387 self.sel_win.ShowModal()
388 self.sel_win.Close()
389
390
391
392 -class Sequence_list_ctrl(wx.ListCtrl, wx.lib.mixins.listctrl.TextEditMixin, wx.lib.mixins.listctrl.ListCtrlAutoWidthMixin):
393 """The string list ListCtrl object."""
394
396 """Initialise the control.
397
398 @param parent: The parent window.
399 @type parent: wx.Frame instance
400 """
401
402
403 wx.ListCtrl.__init__(self, parent, -1, style=wx.BORDER_SUNKEN|wx.LC_REPORT|wx.LC_HRULES|wx.LC_VRULES)
404 wx.lib.mixins.listctrl.TextEditMixin.__init__(self)
405 wx.lib.mixins.listctrl.ListCtrlAutoWidthMixin.__init__(self)
406
407
408
410 """The Python sequence object editor window."""
411
412
413 SIZE = (600, 600)
414
415
416 BORDER = 10
417
418
419 SIZE_BUTTON = (150, 33)
420
421 - def __init__(self, parent=None, name='', seq_type='list', value_type='str', dim=None):
422 """Set up the string list editor window.
423
424 @keyword parent: The parent GUI element.
425 @type parent: wx.Window instance or None
426 @keyword name: The name of the window.
427 @type name: str
428 @keyword seq_type: The type of Python sequence. This should be one of 'list' or 'tuple'.
429 @type seq_type: str
430 @keyword value_type: The type of Python data expected in the sequence. This should be one of 'float', 'int', or 'str'.
431 @type value_type: str
432 @keyword dim: The fixed dimension that the sequence must conform to.
433 @type dim: int or None
434 """
435
436
437 self.name = name
438 self.seq_type = seq_type
439 self.value_type = value_type
440 self.dim = dim
441
442
443 if value_type in ['float', 'num']:
444 self.convert_from_gui = gui_to_float
445 self.convert_to_gui = float_to_gui
446 elif value_type == 'int':
447 self.convert_from_gui = gui_to_int
448 self.convert_to_gui = int_to_gui
449 elif value_type == 'str':
450 self.convert_from_gui = gui_to_str
451 self.convert_to_gui = str_to_gui
452 else:
453 raise RelaxError("Unknown base data type '%s'." % value_type)
454
455
456 title = "Edit the %s values." % name
457
458
459 wx.Dialog.__init__(self, parent, id=-1, title=title)
460
461
462 self.width = self.SIZE[0] - 2*self.BORDER
463
464
465 self.SetSize(self.SIZE)
466 self.Centre()
467 self.SetFont(font.normal)
468
469
470 main_sizer = wx.BoxSizer(wx.VERTICAL)
471
472
473 self.SetSizer(main_sizer)
474
475
476 sizer = add_border(main_sizer, border=self.BORDER, packing=wx.VERTICAL)
477
478
479 self.add_list(sizer)
480
481
482 sizer.AddSpacer(self.BORDER)
483
484
485 self.add_buttons(sizer)
486
487
489 """Return the values as a sequence of values.
490
491 @return: The sequence of values.
492 @rtype: sequence type
493 """
494
495
496 values = []
497
498
499 for i in range(self.sequence.GetItemCount()):
500 values.append(self.convert_from_gui(self.sequence.GetItemText(i)))
501
502
503 if self.seq_type == 'tuple':
504 values = tuple(values)
505
506
507 return values
508
509
511 """Set up the list values.
512
513 @param values: The list of values to add to the list.
514 @type values: list of str or None
515 """
516
517
518 if values == None:
519 return
520
521
522 try:
523 len(values)
524 except TypeError:
525 if self.seq_type == 'list':
526 values = [values]
527 elif self.seq_type == 'tuple':
528 values = (values,)
529
530
531 for i in range(len(values)):
532
533 if self.dim:
534 self.sequence.SetStringItem(index=i, col=0, label=self.convert_to_gui(values[i]))
535
536
537 else:
538 self.sequence.InsertStringItem(i, self.convert_to_gui(values[i]))
539
540
585
586
588 """Set up the list control.
589
590 @param sizer: A sizer object.
591 @type sizer: wx.Sizer instance
592 """
593
594
595 self.sequence = Sequence_list_ctrl(self)
596
597
598 title = "%s%s" % (self.name[0].upper(), self.name[1:])
599
600
601 self.sequence.InsertColumn(0, title)
602 self.sequence.SetColumnWidth(0, wx.LIST_AUTOSIZE)
603
604
605 sizer.Add(self.sequence, 1, wx.ALL|wx.EXPAND, 0)
606
607
608 if self.dim:
609 for i in range(self.dim):
610 self.append_row(None)
611
612
614 """Append a new row to the list.
615
616 @param event: The wx event.
617 @type event: wx event
618 """
619
620
621 next = self.sequence.GetItemCount()
622
623
624 self.sequence.InsertStringItem(next, '')
625
626
628 """Close the window.
629
630 @param event: The wx event.
631 @type event: wx event
632 """
633
634
635 self.Destroy()
636
637
639 """Remove all items from the list.
640
641 @param event: The wx event.
642 @type event: wx event
643 """
644
645
646 self.sequence.DeleteAllItems()
647