1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 """The combo list GUI element."""
24
25
26 from copy import deepcopy
27 import wx
28
29
30 from relax_errors import RelaxError
31
32
33 from gui.paths import icon_16x16
34 from gui.string_conv import float_to_gui, gui_to_float, gui_to_int, gui_to_str, int_to_gui, str_to_gui
35
36
38 """The combo list GUI element."""
39
40 - def __init__(self, parent, sizer, desc, value_type=None, n=1, min_length=1, choices=None, data=None, default=None, evt_fn=None, tooltip=None, divider=None, padding=0, spacer=None, read_only=True, can_be_none=False):
41 """Build the combo box list widget for a list of list selections.
42
43 @param parent: The parent GUI element.
44 @type parent: wx object instance
45 @param sizer: The sizer to put the combo box widget into.
46 @type sizer: wx.Sizer instance
47 @param desc: The text description.
48 @type desc: str
49 @keyword value_type: The type of Python object that the value should be. This can be one of 'float', 'int', or 'str'.
50 @type value_type: str
51 @keyword n: The number of initial entries.
52 @type n: int
53 @keyword min_length: The minimum length for the Combo_list object.
54 @type min_length: int
55 @keyword choices: The list of choices (all combo boxes will have the same list).
56 @type choices: list of str
57 @keyword 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 choices list. If not supplied, the choices list will be used for the returned data.
58 @type data: list
59 @keyword default: The default value of the ComboBox. This is only used if the element_type is set to 'combo'.
60 @type default: str or None
61 @keyword evt_fn: The event handling function.
62 @type evt_fn: func
63 @keyword tooltip: The tooltip which appears on hovering over the text or input field.
64 @type tooltip: str
65 @keyword divider: The optional position of the divider. If None, the parent class variable _div_left will be used if present.
66 @type divider: None or int
67 @keyword padding: Spacing to the left and right of the widgets.
68 @type padding: int
69 @keyword spacer: The amount of spacing to add below the field in pixels. If None, a stretchable spacer will be used.
70 @type spacer: None or int
71 @keyword read_only: A flag which if True means that text cannot be typed into the combo box widget.
72 @type read_only: bool
73 @keyword can_be_none: A flag which specifies if the element is allowed to have the None value.
74 @type can_be_none: bool
75 """
76
77
78 self._parent = parent
79 self._sizer = sizer
80 self._desc = desc
81 self._choices = choices
82 self._data = data
83 self._default = default
84 self._evt_fn = evt_fn
85 self._tooltip = tooltip
86 self._padding = padding
87 self._read_only = read_only
88 self._can_be_none = can_be_none
89 self._min_length = min_length
90
91
92 if self._data == None:
93 self._data = deepcopy(self._choices)
94
95
96 if value_type in ['float', 'num']:
97 self.convert_from_gui = gui_to_float
98 self.convert_to_gui = float_to_gui
99 self.type_string = 'float'
100 elif value_type == 'int':
101 self.convert_from_gui = gui_to_int
102 self.convert_to_gui = int_to_gui
103 self.type_string = 'integer'
104 elif value_type == 'str':
105 self.convert_from_gui = gui_to_str
106 self.convert_to_gui = str_to_gui
107 self.type_string = 'string'
108 else:
109 raise RelaxError("Unknown value type '%s'." % value_type)
110
111
112 self._main_sizer = wx.BoxSizer(wx.VERTICAL)
113 self._combo_boxes = []
114 self._sub_sizers = []
115
116
117 if n == None:
118 n = 1
119
120
121 if not divider:
122 self._divider = self._parent._div_left
123 else:
124 self._divider = divider
125
126
127 if n < min_length:
128 n = min_length
129 for i in range(n):
130 self._build_row()
131
132
133 self._sizer.Add(self._main_sizer, 0, wx.EXPAND|wx.ALL, 0)
134
135
136 if spacer == None:
137 self._sizer.AddStretchSpacer()
138 else:
139 self._sizer.AddSpacer(spacer)
140
141
142 - def _add(self, event):
143 """Add a new combo box.
144
145 @param event: The wx event.
146 @type event: wx event
147 """
148
149
150 self._build_row()
151
152
153 self._parent.Layout()
154
155
157 """Construct a row of the GUI element.
158
159 @param text: The text description of the
160 """
161
162
163 sub_sizer = wx.BoxSizer(wx.HORIZONTAL)
164 index = len(self._combo_boxes)
165
166
167 sub_sizer.AddSpacer(self._padding)
168
169
170 if index == 0:
171 text = wx.StaticText(self._parent, -1, self._desc, style=wx.ALIGN_LEFT)
172 sub_sizer.Add(text, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 0)
173
174
175 x, y = text.GetSize()
176 sub_sizer.AddSpacer((self._divider - x, 0))
177
178
179 else:
180 sub_sizer.AddSpacer((self._divider, 0))
181
182
183 style = wx.CB_DROPDOWN
184 if self._read_only:
185 style = style | wx.CB_READONLY
186 combo = wx.ComboBox(self._parent, -1, value='', style=style)
187 combo.SetMinSize((50, 27))
188 sub_sizer.Add(combo, 1, wx.ALIGN_CENTER_VERTICAL, 0)
189 self._combo_boxes.append(combo)
190
191
192 if self._choices != None:
193
194 for j in range(len(self._choices)):
195 self._combo_boxes[-1].Insert(self.convert_to_gui(self._choices[j]), j, self._data[j])
196
197
198 if self._default:
199
200 if isinstance(self._default, list):
201 if index < len(self._default):
202 self._combo_boxes[-1].SetStringSelection(self._default[index-1])
203
204
205 else:
206 self._combo_boxes[-1].SetStringSelection(self._default)
207
208
209 button = None
210 if index == 0:
211 button = wx.BitmapButton(self._parent, -1, wx.Bitmap(icon_16x16.add, wx.BITMAP_TYPE_ANY))
212 button.SetMinSize((27, 27))
213 sub_sizer.Add(button, 0, wx.ADJUST_MINSIZE|wx.ALIGN_CENTER_VERTICAL, 0)
214 self._parent.Bind(wx.EVT_BUTTON, self._add, button)
215
216
217 elif index == self._min_length:
218 button = wx.BitmapButton(self._parent, -1, wx.Bitmap(icon_16x16.remove, wx.BITMAP_TYPE_ANY))
219 button.SetMinSize((27, 27))
220 sub_sizer.Add(button, 0, wx.ADJUST_MINSIZE|wx.ALIGN_CENTER_VERTICAL, 0)
221 self._parent.Bind(wx.EVT_BUTTON, self._delete, button)
222
223
224 else:
225 sub_sizer.AddSpacer((27, 0))
226
227
228 sub_sizer.AddSpacer(self._padding)
229
230
231 self._sub_sizers.append(sub_sizer)
232 self._main_sizer.Add(sub_sizer, 1, wx.EXPAND|wx.ALL, 0)
233
234
235 if self._evt_fn:
236 self._parent.Bind(wx.EVT_COMBOBOX, self._evt_fn, combo)
237
238
239 if self._tooltip:
240 if index == 0:
241 text.SetToolTipString(self._tooltip)
242 combo.SetToolTipString(self._tooltip)
243 if index <= 1 and button != None:
244 button.SetToolTipString(self._tooltip)
245
246
248 """Add a new combo box.
249
250 @param event: The wx event.
251 @type event: wx event
252 """
253
254
255 self._combo_boxes.pop()
256
257
258 sub_sizer = self._sub_sizers.pop()
259 sub_sizer.DeleteWindows()
260 self._main_sizer.Remove(sub_sizer)
261
262
263 self._parent.Layout()
264
265
267 """Return the value represented by this GUI element.
268
269 @return: The list of choices.
270 @rtype: list
271 """
272
273
274 data = []
275 n = 0
276 for i in range(len(self._combo_boxes)):
277
278 sel_index = self._combo_boxes[i].GetSelection()
279 if sel_index == wx.NOT_FOUND:
280 val = None
281 else:
282 val = self.convert_from_gui(self._combo_boxes[i].GetClientData(sel_index))
283
284
285 if val == None:
286 val = self.convert_from_gui(self._combo_boxes[i].GetValue())
287
288 if val == None:
289 continue
290
291
292 data.append(val)
293
294
295 n += 1
296
297
298 if self._min_length != None and n < self._min_length:
299 return None
300 else:
301 return data
302
303
304 - def SetValue(self, value=None, index=None):
305 """Special method for setting the value of the GUI element.
306
307 @keyword value: The value to set.
308 @type value: value or list of values
309 @keyword index: The index of the value to set.
310 @type index: int
311 """
312
313
314 if not isinstance(value, list):
315
316 if index == None:
317 index = 0
318
319
320 if len(self._combo_boxes) <= index:
321 for i in range(len(self._combo_boxes) - index + 1):
322 self._add(None)
323
324
325 found = False
326 for j in range(self._combo_boxes[index].GetCount()):
327 if self._combo_boxes[index].GetClientData(j) == value:
328 self._combo_boxes[index].SetSelection(j)
329 found = True
330 break
331
332
333 if not found:
334
335 if self._read_only:
336 if value != None:
337 raise RelaxError("The Value element is read only, cannot set the value '%s'." % value)
338
339
340 else:
341 self._combo_boxes[index].SetSelection(wx.NOT_FOUND)
342 self._combo_boxes[index].SetValue(self.convert_to_gui(value))
343
344
345 else:
346
347 if len(self._combo_boxes) <= len(value):
348 for i in range(len(value) - len(self._combo_boxes)):
349 self._add(None)
350
351
352 for i in range(len(value)):
353
354 found = False
355 for j in range(self._combo_boxes[i].GetCount()):
356 if self._combo_boxes[i].GetClientData(j) == value[i]:
357 self._combo_boxes[i].SetSelection(j)
358 found = True
359 break
360
361
362 if not found:
363 self._combo_boxes[i].SetValue(value[i])
364
365
366 - def UpdateChoices(self, combo_choices=None, combo_data=None, combo_default=None):
367 """Special wizard method for updating the list of choices in a ComboBox type element.
368
369 @keyword combo_choices: The list of choices to present to the user. This is only used if the element_type is set to 'combo'.
370 @type combo_choices: list of str
371 @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.
372 @type combo_data: list
373 @keyword combo_default: The default value of the ComboBox. This is only used if the element_type is set to 'combo'.
374 @type combo_default: str or None
375 """
376
377
378 self._choices = combo_choices
379 self._data = combo_data
380 self._default = combo_default
381
382
383 if self._data == None:
384 self._data = deepcopy(self._choices)
385
386
387 if self._can_be_none:
388 self._choices.insert(0, '')
389 self._data.insert(0, None)
390
391
392 for i in range(len(self._combo_boxes)):
393
394 sel_index = self._combo_boxes[i].GetSelection()
395 if sel_index == wx.NOT_FOUND:
396 sel = None
397 else:
398 sel = self._combo_boxes[i].GetClientData(sel_index)
399
400
401 self._combo_boxes[i].Clear()
402
403
404 for j in range(len(self._choices)):
405 self._combo_boxes[i].Insert(self.convert_to_gui(self._choices[j]), j, self._data[j])
406
407
408 if sel == None and self._default != None:
409
410 if isinstance(self._default, list):
411
412 if len(self._default) > len(self._combo_boxes):
413 for k in range(len(self._default) - len(self._combo_boxes)):
414 self._add(None)
415
416
417 for k in range(len(self._default)):
418
419 if self._default[k] in self._choices:
420 string = self._default[k]
421 elif self._default[k] not in self._data:
422 string = self._default[k]
423 else:
424 string = self._choices[self._data.index(self._default[k])]
425
426
427 self._combo_boxes[i].SetStringSelection(string)
428
429
430 else:
431
432 if self._default in self._choices:
433 string = self._default
434 elif self._default not in self._data:
435 string = self._default
436 else:
437 string = self._choices[self._data.index(self._default)]
438
439
440 self._combo_boxes[i].SetStringSelection(string)
441
442
443 else:
444 for j in range(self._combo_boxes[i].GetCount()):
445 if self._combo_boxes[i].GetClientData(j) == sel:
446 self._combo_boxes[i].SetSelection(j)
447