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