1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 """GUI element for the user input of values."""
24
25
26 from copy import deepcopy
27 import wx
28
29
30 from relax_errors import RelaxError
31
32
33 from gui.errors import gui_raise
34 from gui.fonts import font
35 from gui.string_conv import float_to_gui, gui_to_float, gui_to_int, gui_to_list, gui_to_str, int_to_gui, str_to_gui
36
37
39 """GUI element for the input of all types of simple Python objects.
40
41 The supported Python types include:
42 - floats
43 - integers
44 - strings
45 """
46
47 - def __init__(self, name=None, default=None, parent=None, element_type='default', value_type=None, sizer=None, desc=None, combo_choices=None, combo_data=None, min=0, max=1000, tooltip=None, divider=None, padding=0, spacer=None, height_element=27, read_only=False, can_be_none=False):
48 """Set up the base value element.
49
50 @keyword name: The name of the element to use in titles, etc.
51 @type name: str
52 @keyword default: The default value of the element.
53 @type default: float or int or str
54 @keyword parent: The parent GUI element.
55 @type parent: wx.Panel instance
56 @keyword element_type: The type of GUI element to create. This can be set to:
57 - 'text', a wx.TextCtrl element will be used.
58 - 'combo', a wx.ComboBox element will be used.
59 - 'spin', a wx.SpinCtrl element will be used. This is only valid for integer types!
60 @type element_type: str
61 @keyword value_type: The type of Python object that the value should be. This can be one of 'float', 'int', or 'str'.
62 @type value_type: str
63 @keyword sizer: The sizer to put the input field widget into.
64 @type sizer: wx.Sizer instance
65 @keyword desc: The text description.
66 @type desc: str
67 @keyword combo_choices: The list of choices to present to the user. This is only used if the element_type is set to 'combo'.
68 @type combo_choices: list of str
69 @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.
70 @type combo_data: list
71 @keyword min: For a SpinCtrl, the minimum value allowed.
72 @type min: int
73 @keyword max: For a SpinCtrl, the maximum value allowed.
74 @type max: int
75 @keyword tooltip: The tooltip which appears on hovering over the text or input field.
76 @type tooltip: str
77 @keyword divider: The position of the divider.
78 @type divider: int
79 @keyword padding: Spacing to the left and right of the widgets.
80 @type padding: int
81 @keyword spacer: The amount of spacing to add below the field in pixels. If None, a stretchable spacer will be used.
82 @type spacer: None or int
83 @keyword height_element: The height in pixels of the GUI element.
84 @type height_element: int
85 @keyword read_only: A flag which if True means that the text of the element cannot be edited.
86 @type read_only: bool
87 @keyword can_be_none: A flag which specifies if the element is allowed to have the None value.
88 @type can_be_none: bool
89 """
90
91
92 if element_type == 'default':
93
94 if value_type == 'int' and not can_be_none:
95 element_type = 'spin'
96
97
98 else:
99 element_type = 'text'
100
101
102 if element_type == "spin" and value_type != 'int':
103 raise RelaxError("A wx.SpinCtrl element can only be used together with integers.")
104
105
106 self.name = name
107 self.default = default
108 self.element_type = element_type
109 self.can_be_none = can_be_none
110 self.read_only = read_only
111
112
113 if value_type in ['float', 'num']:
114 self.convert_from_gui = gui_to_float
115 self.convert_to_gui = float_to_gui
116 self.type_string = 'float'
117 elif value_type == 'int':
118 self.convert_from_gui = gui_to_int
119 self.convert_to_gui = int_to_gui
120 self.type_string = 'integer'
121 elif value_type == 'str':
122 self.convert_from_gui = gui_to_str
123 self.convert_to_gui = str_to_gui
124 self.type_string = 'string'
125 else:
126 raise RelaxError("Unknown value type '%s'." % value_type)
127
128
129 sub_sizer = wx.BoxSizer(wx.HORIZONTAL)
130
131
132 sub_sizer.AddSpacer(padding)
133
134
135 text = wx.StaticText(parent, -1, desc, style=wx.ALIGN_LEFT)
136 text.SetFont(font.normal)
137 sub_sizer.Add(text, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 0)
138
139
140 if not divider:
141 raise RelaxError("The divider position has not been supplied.")
142
143
144 x, y = text.GetSize()
145 sub_sizer.AddSpacer((divider - x, 0))
146
147
148 if self.element_type == 'text':
149
150 self._field = wx.TextCtrl(parent, -1, '')
151
152
153 if read_only:
154
155 self._field.SetEditable(False)
156
157
158 colour = parent.GetBackgroundColour()
159 self._field.SetOwnBackgroundColour(colour)
160
161
162 if self.default != None:
163 self._field.SetValue(self.convert_to_gui(self.default))
164
165
166 elif self.element_type == 'spin':
167
168 if min == None:
169 min = 0
170 if max == None:
171 max = 100
172
173
174 self._field = wx.SpinCtrl(parent, -1, min=min, max=max)
175
176
177 if read_only:
178
179 colour = parent.GetBackgroundColour()
180 self._field.SetOwnBackgroundColour(colour)
181
182
183 if self.default != None:
184 self._field.SetValue(self.default)
185
186
187 elif self.element_type == 'combo':
188
189 style = wx.CB_DROPDOWN
190 if read_only:
191 style = style | wx.CB_READONLY
192
193
194 self._field = wx.ComboBox(parent, -1, '', style=style)
195
196
197 self.UpdateChoices(combo_choices=combo_choices, combo_data=combo_data, combo_default=default)
198
199
200 else:
201 raise RelaxError("Unknown element type '%s'." % self.element_type)
202
203
204 self._field.SetMinSize((50, height_element))
205 self._field.SetFont(font.normal)
206 sub_sizer.Add(self._field, 1, wx.ADJUST_MINSIZE|wx.ALIGN_CENTER_VERTICAL, 0)
207
208
209 sub_sizer.AddSpacer(padding)
210
211
212 sizer.Add(sub_sizer, 1, wx.EXPAND|wx.ALL, 0)
213
214
215 if spacer == None:
216 sizer.AddStretchSpacer()
217 else:
218 sizer.AddSpacer(spacer)
219
220
221 if tooltip:
222 text.SetToolTipString(tooltip)
223 self._field.SetToolTipString(tooltip)
224
225
227 """Special method for clearing or resetting the GUI element."""
228
229
230 if self.element_type == 'text':
231 self._field.Clear()
232
233
234 if self.element_type == 'combo':
235 self._field.Clear()
236 self._field.SetValue('')
237
238
240 """Special method for returning the value of the GUI element.
241
242 @return: The string list value.
243 @rtype: list of str
244 """
245
246
247 if self.element_type == 'text':
248
249 value = self._field.GetValue()
250
251
252 try:
253 value = self.convert_from_gui(value)
254
255
256 except:
257 gui_raise(RelaxError("The value '%s' is not of the Python %s type." % (value, self.type_string)))
258 return None
259
260 return value
261
262
263 if self.element_type == 'spin':
264
265 return self._field.GetValue()
266
267
268 if self.element_type == 'combo':
269
270 sel_index = self._field.GetSelection()
271 if sel_index == wx.NOT_FOUND:
272 value = None
273 else:
274 value = self.convert_from_gui(self._field.GetClientData(sel_index))
275
276
277 if value == None:
278 value = self.convert_from_gui(self._field.GetValue())
279
280
281 return value
282
283
285 """Special method for setting the value of the GUI element.
286
287 @param value: The value to set.
288 @type value: list of str or None
289 """
290
291
292 if self.element_type == 'text':
293 self._field.SetValue(self.convert_to_gui(value))
294
295
296 elif self.element_type == 'spin':
297 self._field.SetValue(value)
298
299
300 elif self.element_type == 'combo':
301
302 found = False
303 for i in range(self._field.GetCount()):
304 if self._field.GetClientData(i) == value:
305 self._field.SetSelection(i)
306 found = True
307 break
308
309
310 if not found:
311
312 if self.read_only:
313 if value != None:
314 raise RelaxError("The Value element is read only, cannot set the value '%s'." % value)
315
316
317 else:
318 self._field.SetSelection(wx.NOT_FOUND)
319 self._field.SetValue(self.convert_to_gui(value))
320
321
322 - def UpdateChoices(self, combo_choices=None, combo_data=None, combo_default=None):
323 """Special wizard method for updating the list of choices in a ComboBox type element.
324
325 @keyword combo_choices: The list of choices to present to the user. This is only used if the element_type is set to 'combo'.
326 @type combo_choices: list of str
327 @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.
328 @type combo_data: list
329 @keyword combo_default: The default value of the ComboBox. This is only used if the element_type is set to 'combo'.
330 @type combo_default: str or None
331 """
332
333
334 if self.element_type == 'text':
335 raise RelaxError("Cannot update the list of ComboBox choices as this is a TextCtrl!")
336
337
338 if self.element_type == 'spin':
339 raise RelaxError("Cannot update the list of ComboBox choices as this is a SpinCtrl!")
340
341
342 if self.element_type == 'combo':
343
344 sel_index = self._field.GetSelection()
345 if sel_index == wx.NOT_FOUND:
346 sel = None
347 else:
348 sel = self._field.GetClientData(sel_index)
349
350
351 self.Clear()
352
353
354 if combo_data == None:
355 combo_data = deepcopy(combo_choices)
356
357
358 if self.can_be_none:
359 combo_choices.insert(0, '')
360 combo_data.insert(0, None)
361
362
363 for i in range(len(combo_choices)):
364 self._field.Insert(self.convert_to_gui(combo_choices[i]), i, combo_data[i])
365
366
367 if sel == None and combo_default != None:
368
369 if combo_default in combo_choices:
370 string = str_to_gui(str(combo_default))
371 set_sel = True
372 elif combo_default not in combo_data:
373 string = str_to_gui(str(combo_default))
374 set_sel = False
375 else:
376 string = combo_choices[combo_data.index(combo_default)]
377 set_sel = True
378
379
380 if set_sel:
381 self._field.SetStringSelection(string)
382
383
384 else:
385 self._field.SetValue(string)
386
387
388 else:
389 for j in range(self._field.GetCount()):
390 if self._field.GetClientData(j) == sel:
391 self._field.SetSelection(j)
392