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