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