1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16   
 17   
 18   
 19   
 20   
 21   
 22   
 23  """Module containing a set of special GUI elements to be used in the relax wizards.""" 
 24   
 25   
 26  import wx 
 27  import wx.lib.mixins.listctrl 
 28   
 29   
 30  import dep_check 
 31  from graphics import fetch_icon 
 32  from gui.input_elements.combo_list import Combo_list 
 33  from gui.fonts import font 
 34  from gui.misc import add_border 
 35  from gui.string_conv import float_to_gui, gui_to_float, gui_to_int, gui_to_list, gui_to_py, gui_to_str, gui_to_tuple, int_to_gui, list_to_gui, py_to_gui, str_to_gui, tuple_to_gui 
 36  from lib.check_types import is_list 
 37  from lib.errors import RelaxError 
 38  from status import Status; status = Status() 
 39   
 40   
 42      """Wizard GUI element for the input of all types of Python sequence objects. 
 43   
 44      The supported Python types include: 
 45          - list of floats 
 46          - list of integers 
 47          - list of strings 
 48          - tuple of floats 
 49          - tuple of integers 
 50          - tuple of strings 
 51      """ 
 52   
 53 -    def __init__(self, name=None, default=None, parent=None, element_type='default', seq_type=None, value_type=None, dim=None, min=0, max=1000, sizer=None, titles=None, desc=None, combo_choices=None, combo_data=None, combo_list_min=None, tooltip=None, divider=None, padding=0, spacer=None, height_element=27, single_value=False, read_only=False, can_be_none=False): 
  54          """Set up the element. 
 55   
 56          @keyword name:              The name of the element to use in titles, etc. 
 57          @type name:                 str 
 58          @keyword default:           The default value of the element. 
 59          @type default:              sequence object 
 60          @keyword parent:            The wizard GUI element. 
 61          @type parent:               wx.Panel instance 
 62          @keyword element_type:      The type of GUI element to create.  If set to 'default', the wx.TextCtrl element with a button to bring up a dialog with ListCtrl will be used.  If set to 'combo_list', the special gui.components.combo_list.Combo_list element will be used. 
 63          @type element_type:         str 
 64          @keyword seq_type:          The type of Python sequence.  This should be one of 'list' or 'tuple'. 
 65          @type seq_type:             str 
 66          @keyword value_type:        The type of Python object that the value should be.  This can be one of 'float', 'int', or 'str'. 
 67          @type value_type:           str 
 68          @keyword dim:               The dimensions that a list or tuple must conform to.  For a 1D sequence, this can be a single value or a tuple of possible sizes.  For a 2D sequence (a numpy matrix or list of lists), this must be a tuple of the fixed dimension sizes, e.g. a 3x5 matrix should be specified as (3, 5). 
 69          @type dim:                  int, tuple of int or None 
 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 sizer:             The sizer to put the input field widget into. 
 75          @type sizer:                wx.Sizer instance 
 76          @keyword titles:            The titles of each of the elements of the fixed dimension elements. 
 77          @type titles:               list of str 
 78          @keyword desc:              The text description. 
 79          @type desc:                 str 
 80          @keyword combo_choices:     The list of choices to present to the user.  This is only used if the element_type is set to 'combo'. 
 81          @type combo_choices:        list of str 
 82          @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. 
 83          @type combo_data:           list 
 84          @keyword combo_list_min:    The minimum length for the Combo_list object. 
 85          @type combo_list_min:       int or None 
 86          @keyword tooltip:           The tooltip which appears on hovering over the text or input field. 
 87          @type tooltip:              str 
 88          @keyword divider:           The position of the divider. 
 89          @type divider:              int 
 90          @keyword padding:           Spacing to the left and right of the widgets. 
 91          @type padding:              int 
 92          @keyword spacer:            The amount of spacing to add below the field in pixels.  If None, a stretchable spacer will be used. 
 93          @type spacer:               None or int 
 94          @keyword height_element:    The height in pixels of the GUI element. 
 95          @type height_element:       int 
 96          @keyword single_value:      A flag which if True will cause single input values to be treated as single values rather than a list or tuple. 
 97          @type single_value:         bool 
 98          @keyword read_only:         A flag which if True means that the text of the element cannot be edited. 
 99          @type read_only:            bool 
100          @keyword can_be_none:       A flag which specifies if the element is allowed to have the None value. 
101          @type can_be_none:          bool 
102          """ 
103   
104           
105          self.parent = parent 
106          self.name = name 
107          self.default = default 
108          self.element_type = element_type 
109          self.seq_type = seq_type 
110          self.value_type = value_type 
111          self.dim = dim 
112          self.min = min 
113          self.max = max 
114          self.titles = titles 
115          self.single_value = single_value 
116          self.can_be_none = can_be_none 
117   
118           
119          if value_type in ['float', 'num']: 
120              self.convert_from_gui = gui_to_float 
121              self.convert_to_gui =   float_to_gui 
122          elif value_type == 'int': 
123              self.convert_from_gui = gui_to_int 
124              self.convert_to_gui =   int_to_gui 
125          elif value_type == 'str': 
126              self.convert_from_gui = gui_to_str 
127              self.convert_to_gui =   str_to_gui 
128          else: 
129              self.convert_from_gui = gui_to_py 
130              self.convert_to_gui =   py_to_gui 
131   
132           
133          if seq_type == 'list': 
134              self.convert_from_gui_seq = gui_to_list 
135              self.convert_to_gui_seq =   list_to_gui 
136          elif seq_type == 'tuple': 
137              self.convert_from_gui_seq = gui_to_tuple 
138              self.convert_to_gui_seq =   tuple_to_gui 
139          else: 
140              raise RelaxError("Unknown sequence type '%s'." % seq_type) 
141   
142           
143          if self.element_type == 'default': 
144               
145              if read_only == None: 
146                  read_only = False 
147   
148               
149              sub_sizer = wx.BoxSizer(wx.HORIZONTAL) 
150   
151               
152              sub_sizer.AddSpacer(padding) 
153   
154               
155              text = wx.StaticText(parent, -1, desc, style=wx.ALIGN_LEFT) 
156              text.SetFont(font.normal) 
157              sub_sizer.Add(text, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 0) 
158   
159               
160              if not divider: 
161                  raise RelaxError("The divider position has not been supplied.") 
162   
163               
164              dc = wx.ScreenDC() 
165              dc.SetFont(font.normal) 
166              x, y = dc.GetTextExtent(desc) 
167              if dep_check.wx_classic: 
168                  sub_sizer.AddSpacer((divider - x, 0)) 
169              else: 
170                  sub_sizer.AddSpacer(int(divider - x)) 
171   
172               
173              self._field = wx.TextCtrl(parent, -1, '') 
174              self._field.SetMinSize((50, height_element)) 
175              self._field.SetFont(font.normal) 
176              sub_sizer.Add(self._field, 1, wx.ADJUST_MINSIZE|wx.ALIGN_CENTER_VERTICAL, 0) 
177   
178               
179              if read_only: 
180                  self._field.SetEditable(False) 
181                  colour = parent.GetBackgroundColour() 
182                  self._field.SetOwnBackgroundColour(colour) 
183   
184               
185              sub_sizer.AddSpacer(5) 
186   
187               
188              button = wx.BitmapButton(parent, -1, wx.Bitmap(fetch_icon('oxygen.actions.edit-rename', "16x16"), wx.BITMAP_TYPE_ANY)) 
189              button.SetMinSize((-1, height_element)) 
190              button.SetToolTip(wx.ToolTip("Edit the values.")) 
191              sub_sizer.Add(button, 0, wx.ADJUST_MINSIZE|wx.ALIGN_CENTER_VERTICAL, 0) 
192              parent.Bind(wx.EVT_BUTTON, self.open_dialog, button) 
193   
194               
195              sub_sizer.AddSpacer(padding) 
196   
197               
198              sizer.Add(sub_sizer, 1, wx.EXPAND|wx.ALL, 0) 
199   
200               
201              if spacer == None: 
202                  sizer.AddStretchSpacer() 
203              else: 
204                  sizer.AddSpacer(spacer) 
205   
206               
207              if tooltip: 
208                  text.SetToolTip(wx.ToolTip(tooltip)) 
209                  self._field.SetToolTip(wx.ToolTip(tooltip)) 
210   
211               
212              if self.default is not None: 
213                  self._field.SetValue(self.convert_to_gui_seq(self.default)) 
214   
215           
216          elif self.element_type == 'combo_list': 
217               
218              if read_only == None: 
219                  read_only = False 
220   
221               
222              if combo_list_min == None: 
223                  combo_list_min = 1 
224   
225               
226              self._field = Combo_list(parent, sizer, desc, value_type=value_type, min_length=combo_list_min, choices=combo_choices, data=combo_data, default=default, tooltip=tooltip, read_only=read_only, can_be_none=can_be_none) 
227   
228           
229          else: 
230              raise RelaxError("Unknown element type '%s'." % self.element_type) 
 231   
232   
234          """Special method for clearing or resetting the GUI element.""" 
235   
236           
237          if self.element_type in ['default', 'combo_list']: 
238              self._field.Clear() 
 239   
240   
242          """Special method for returning the sequence values of the GUI element. 
243   
244          @return:    The sequence of values. 
245          @rtype:     sequence type 
246          """ 
247   
248           
249          value = self._field.GetValue() 
250   
251           
252          if self.element_type == 'combo_list': 
253               
254              if value == [] or value == None: 
255                  return None 
256   
257           
258          else: 
259               
260              value_set = False 
261              if self.single_value: 
262                  try: 
263                       
264                      value = self.convert_from_gui(value) 
265   
266                       
267                      if value == None and self.can_be_none: 
268                          value_set = True 
269                      elif self.value_type == None: 
270                          value_set = True 
271                      elif self.value_type in ['float', 'num']: 
272                          if isinstance(value, int) or isinstance(value, float): 
273                              value_set = True 
274                      elif self.value_type == 'int': 
275                          if isinstance(value, int): 
276                              value_set = True 
277                      elif self.value_type == 'str': 
278                          if self.seq_type == 'list' and value[0] != '[': 
279                              value_set = True 
280                          elif self.seq_type == 'tuple' and value[0] != '(': 
281                              value_set = True 
282                  except: 
283                      pass 
284   
285               
286              if not value_set: 
287                  try: 
288                      value = self.convert_from_gui_seq(value) 
289   
290                   
291                  except RelaxError: 
292                      if self.can_be_none: 
293                          value = None 
294                      elif self.seq_type == 'list': 
295                          value = [] 
296                      else: 
297                          value = () 
298   
299                   
300                  except: 
301                      value = None 
302   
303           
304          if value == None: 
305              return None 
306   
307           
308          if self.single_value: 
309              if (isinstance(value, list) or isinstance(value, tuple)) and len(value) == 1: 
310                  value = value[0] 
311   
312           
313          elif value != None: 
314              if self.seq_type == 'list' and not isinstance(value, list): 
315                  value = [value] 
316              elif self.seq_type == 'tuple' and not isinstance(value, tuple): 
317                  value = (value,) 
318   
319           
320          if not self.single_value and len(value) == 0: 
321              return None 
322   
323           
324          return value 
 325   
326   
327 -    def SetValue(self, value=None, index=None): 
 328          """Special method for setting the value of the GUI element. 
329   
330          @keyword value: The value to set. 
331          @type value:    value or list of values 
332          @keyword index: The index of the value to set, if the full list is not given. 
333          @type index:    int or None 
334          """ 
335   
336           
337          if self.element_type == 'combo_list': 
338              self._field.SetValue(value=value, index=index) 
339   
340           
341          else: 
342               
343              if self.single_value and isinstance(value, list) and len(value) == 1: 
344                  value = value[0] 
345   
346               
347              self._field.SetValue(self.convert_to_gui_seq(value)) 
 348   
349   
350 -    def UpdateChoices(self, combo_choices=None, combo_data=None, combo_default=None): 
 351          """Special wizard method for updating the list of choices in a ComboBox type element. 
352   
353          @keyword combo_choices: The list of choices to present to the user.  This is only used if the element_type is set to 'combo_list'. 
354          @type combo_choices:    list of str 
355          @keyword combo_data:    The data returned by a call to GetValue().  This is only used if the element_type is set to 'combo_list'.  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. 
356          @type combo_data:       list 
357          @keyword combo_default: The default value of the ComboBox.  This is only used if the element_type is set to 'combo_list'. 
358          @type combo_default:    str or None 
359          """ 
360   
361           
362          if self.element_type == 'combo_list': 
363              self._field.UpdateChoices(combo_choices=combo_choices, combo_data=combo_data, combo_default=combo_default) 
 364   
365   
367          """Open a special dialog for inputting a list of text values. 
368   
369          @param event:   The wx event. 
370          @type event:    wx event 
371          """ 
372   
373           
374          self.selection_win_show() 
375   
376           
377          self.selection_win_data() 
378   
379           
380          self.sel_win.Destroy() 
381          del self.sel_win 
 382   
383   
385          """Extract the data from the selection window.""" 
386   
387           
388          value = self.sel_win.GetValue() 
389   
390           
391          if value == None or not len(value): 
392              self.Clear() 
393   
394           
395          else: 
396              self.SetValue(value) 
 397   
398   
400          """Show the selection window.""" 
401   
402           
403          if hasattr(self, 'sel_win'): 
404              self.sel_win.Destroy() 
405              del self.sel_win 
406   
407           
408          self.sel_win = Sequence_window(parent=self.parent, name=self.name, seq_type=self.seq_type, value_type=self.value_type, titles=self.titles, dim=self.dim) 
409   
410           
411          self.sel_win.SetValue(self.GetValue()) 
412   
413           
414          if status.show_gui: 
415              self.sel_win.ShowModal() 
416              self.sel_win.Close() 
  417   
418   
419   
420 -class Sequence_list_ctrl(wx.ListCtrl, wx.lib.mixins.listctrl.TextEditMixin, wx.lib.mixins.listctrl.ListCtrlAutoWidthMixin): 
 421      """The string list ListCtrl object.""" 
422   
424          """Initialise the control. 
425   
426          @param parent:  The parent window. 
427          @type parent:   wx.Frame instance 
428          """ 
429   
430           
431          wx.ListCtrl.__init__(self, parent, -1, style=wx.BORDER_SUNKEN|wx.LC_REPORT|wx.LC_HRULES|wx.LC_VRULES) 
432          wx.lib.mixins.listctrl.TextEditMixin.__init__(self) 
433          wx.lib.mixins.listctrl.ListCtrlAutoWidthMixin.__init__(self) 
434   
435           
436          self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.begin_label_edit) 
 437   
438   
440          """Catch edits to make the first column read only. 
441   
442          @param event:   The wx event. 
443          @type event:    wx event 
444          """ 
445   
446           
447          if event.m_col == 0: 
448              event.Veto() 
449   
450           
451          else: 
452              event.Skip() 
  453   
454   
455   
457      """The Python sequence object editor window.""" 
458   
459       
460      SIZE = (800, 600) 
461   
462       
463      BORDER = 10 
464   
465       
466      SIZE_BUTTON = (150, 33) 
467   
468 -    def __init__(self, parent=None, name='', seq_type='list', value_type='str', dim=None, titles=None): 
 469          """Set up the string list editor window. 
470   
471          @keyword parent:        The parent GUI element. 
472          @type parent:           wx.Window instance or None 
473          @keyword name:          The name of the window. 
474          @type name:             str 
475          @keyword seq_type:      The type of Python sequence.  This should be one of 'list' or 'tuple'. 
476          @type seq_type:         str 
477          @keyword value_type:    The type of Python data expected in the sequence.  This should be one of 'float', 'int', or 'str'. 
478          @type value_type:       str 
479          @keyword dim:           The fixed dimension that the sequence must conform to. 
480          @type dim:              int or None 
481          @keyword titles:        The titles of each of the elements of the fixed dimension elements. 
482          @type titles:           list of str 
483          """ 
484   
485           
486          self.name = name 
487          self.seq_type = seq_type 
488          self.value_type = value_type 
489          self.dim = dim 
490          self.titles = titles 
491   
492           
493          if value_type in ['float', 'num']: 
494              self.convert_from_gui = gui_to_float 
495              self.convert_to_gui =   float_to_gui 
496          elif value_type == 'int': 
497              self.convert_from_gui = gui_to_int 
498              self.convert_to_gui =   int_to_gui 
499          elif value_type == 'str': 
500              self.convert_from_gui = gui_to_str 
501              self.convert_to_gui =   str_to_gui 
502          else: 
503              raise RelaxError("Unknown base data type '%s'." % value_type) 
504   
505           
506          if not hasattr(self, 'variable_length'): 
507              self.variable_length = False 
508              self.offset = 0 
509              if dim == None: 
510                  self.variable_length = True 
511                  self.offset = 1 
512   
513           
514          title = "Edit the %s values." % name 
515   
516           
517          wx.Dialog.__init__(self, parent, id=-1, title=title) 
518   
519           
520          self.width = self.SIZE[0] - 2*self.BORDER 
521   
522           
523          self.SetSize(self.SIZE) 
524          if not dep_check.wx_classic and status.show_gui: 
525              self.Centre() 
526          self.SetFont(font.normal) 
527   
528           
529          main_sizer = wx.BoxSizer(wx.VERTICAL) 
530   
531           
532          self.SetSizer(main_sizer) 
533   
534           
535          sizer = add_border(main_sizer, border=self.BORDER, packing=wx.VERTICAL) 
536   
537           
538          self.add_list(sizer) 
539   
540           
541          sizer.AddSpacer(self.BORDER) 
542   
543           
544          self.add_buttons(sizer) 
545   
546           
547          if not self.sequence.GetItemCount(): 
548              self.add_element() 
 549   
550   
552          """Return the values as a sequence of values. 
553   
554          @return:    The sequence of values. 
555          @rtype:     sequence type 
556          """ 
557   
558           
559          values = [] 
560   
561           
562          for i in range(self.sequence.GetItemCount()): 
563               
564              item = self.sequence.GetItem(i, col=1) 
565              text = item.GetText() 
566   
567               
568              try: 
569                  value = self.convert_from_gui(text) 
570              except: 
571                  value = None 
572              values.append(value) 
573   
574           
575          if self.seq_type == 'tuple': 
576              values = tuple(values) 
577   
578           
579          empty = True 
580          for i in range(len(values)): 
581              if values[i] != None: 
582                  empty = False 
583                  break 
584   
585           
586          if empty: 
587              return None 
588   
589           
590          return values 
 591   
592   
594          """Set up the list values. 
595   
596          @param values:  The list of values to add to the list. 
597          @type values:   list of str or None 
598          """ 
599   
600           
601          if values == None: 
602              return 
603   
604           
605          if not self.variable_length and is_list(values) and len(values) != self.dim: 
606              return 
607   
608           
609          try: 
610              len(values) 
611          except TypeError: 
612              if self.seq_type == 'list': 
613                  values = [values] 
614              elif self.seq_type == 'tuple': 
615                  values = (values,) 
616   
617           
618          for i in range(len(values)): 
619               
620              if not self.variable_length: 
621                  if dep_check.wx_classic: 
622                      self.sequence.SetStringItem(i, 1, self.convert_to_gui(values[i])) 
623                  else: 
624                      self.sequence.SetItem(i, 1, self.convert_to_gui(values[i])) 
625   
626               
627              else: 
628                   
629                  if i != 0: 
630                       
631                      if dep_check.wx_classic: 
632                          self.sequence.InsertStringItem(i, int_to_gui(i+1)) 
633                      else: 
634                          self.sequence.InsertItem(i, int_to_gui(i+1)) 
635   
636                   
637                  if dep_check.wx_classic: 
638                      self.sequence.SetStringItem(i, 1, self.convert_to_gui(values[i])) 
639                  else: 
640                      self.sequence.SetItem(i, 1, self.convert_to_gui(values[i])) 
 641   
642   
699   
700   
724   
725   
727          """Set up the list control. 
728   
729          @param sizer:   A sizer object. 
730          @type sizer:    wx.Sizer instance 
731          """ 
732   
733           
734          self.sequence = Sequence_list_ctrl(self) 
735   
736           
737          title = "%s%s" % (self.name[0].upper(), self.name[1:]) 
738   
739           
740          if self.titles: 
741              self.sequence.InsertColumn(0, "Title") 
742              self.sequence.SetColumnWidth(0, 200) 
743          else: 
744              self.sequence.InsertColumn(0, "Number") 
745              self.sequence.SetColumnWidth(0, 70) 
746   
747           
748          self.sequence.InsertColumn(1, title) 
749          self.sequence.SetColumnWidth(1, wx.LIST_AUTOSIZE) 
750   
751           
752          sizer.Add(self.sequence, 1, wx.ALL|wx.EXPAND, 0) 
753   
754           
755          if not self.variable_length: 
756              for i in range(self.dim): 
757                   
758                  self.add_element() 
759   
760                   
761                  if self.titles: 
762                      if dep_check.wx_classic: 
763                          self.sequence.SetStringItem(i, 0, str_to_gui(self.titles[i])) 
764                      else: 
765                          self.sequence.SetItem(i, 0, str_to_gui(self.titles[i])) 
766   
767                   
768                  else: 
769                      if dep_check.wx_classic: 
770                          self.sequence.SetStringItem(i, 0, int_to_gui(i+1)) 
771                      else: 
772                          self.sequence.SetItem(i, 0, int_to_gui(i+1)) 
 773   
774   
776          """Close the window. 
777   
778          @param event:   The wx event. 
779          @type event:    wx event 
780          """ 
781   
782           
783          self.Close() 
 784   
785   
787          """Remove the last item from the list. 
788   
789          @param event:   The wx event. 
790          @type event:    wx event 
791          """ 
792   
793           
794          item = self.sequence.GetItemCount() 
795          self.sequence.DeleteItem(item-1) 
796   
797           
798          if not self.sequence.GetItemCount(): 
799              self.add_element() 
 800   
801   
803          """Remove all items from the list. 
804   
805          @param event:   The wx event. 
806          @type event:    wx event 
807          """ 
808   
809           
810          self.sequence.DeleteAllItems() 
811   
812           
813          self.add_element() 
  814