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