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