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