Package gui :: Package input_elements :: Module value
[hide private]
[frames] | no frames]

Source Code for Module gui.input_elements.value

  1  ############################################################################### 
  2  #                                                                             # 
  3  # Copyright (C) 2012-2013 Edward d'Auvergne                                   # 
  4  #                                                                             # 
  5  # This file is part of the program relax (http://www.nmr-relax.com).          # 
  6  #                                                                             # 
  7  # This program is free software: you can redistribute it and/or modify        # 
  8  # it under the terms of the GNU General Public License as published by        # 
  9  # the Free Software Foundation, either version 3 of the License, or           # 
 10  # (at your option) any later version.                                         # 
 11  #                                                                             # 
 12  # This program is distributed in the hope that it will be useful,             # 
 13  # but WITHOUT ANY WARRANTY; without even the implied warranty of              # 
 14  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               # 
 15  # GNU General Public License for more details.                                # 
 16  #                                                                             # 
 17  # You should have received a copy of the GNU General Public License           # 
 18  # along with this program.  If not, see <http://www.gnu.org/licenses/>.       # 
 19  #                                                                             # 
 20  ############################################################################### 
 21   
 22  # Module docstring. 
 23  """GUI element for the user input of values.""" 
 24   
 25  # Python module imports. 
 26  from copy import deepcopy 
 27  import wx 
 28   
 29  # relax module imports. 
 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   
36 -class Value:
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 # Set the default. 90 if element_type == 'default': 91 # Set the default to a SpinCtrl for integers. 92 if value_type == 'int' and not can_be_none: 93 element_type = 'spin' 94 95 # Set the default to a TextCtrl for all other types. 96 else: 97 element_type = 'text' 98 99 # Check the spinner. 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 # Store the args. 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 # The value types. 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 # Init. 127 sub_sizer = wx.BoxSizer(wx.HORIZONTAL) 128 129 # Left padding. 130 sub_sizer.AddSpacer(padding) 131 132 # The description. 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 # The divider. 138 if not divider: 139 raise RelaxError("The divider position has not been supplied.") 140 141 # Spacing. 142 x, y = text.GetSize() 143 sub_sizer.AddSpacer((divider - x, 0)) 144 145 # Initialise the text input field. 146 if self.element_type == 'text': 147 # Set up the text control. 148 self._field = wx.TextCtrl(parent, -1, '') 149 150 # Read only field. 151 if read_only: 152 # Cannot edit. 153 self._field.SetEditable(False) 154 155 # Change the colour to the background. 156 colour = parent.GetBackgroundColour() 157 self._field.SetOwnBackgroundColour(colour) 158 159 # Set the default value. 160 if self.default != None: 161 self._field.SetValue(self.convert_to_gui(self.default)) 162 163 # Initialise the spinner input field. 164 elif self.element_type == 'spin': 165 # Catch limits of None, and set to the wxSpinCtrl defaults. 166 if min == None: 167 min = 0 168 if max == None: 169 max = 100 170 171 # Set up the text control. 172 self._field = wx.SpinCtrl(parent, -1, min=min, max=max) 173 174 # Read only field (really no such thing for a spin control). 175 if read_only: 176 # Change the colour to the background. 177 colour = parent.GetBackgroundColour() 178 self._field.SetOwnBackgroundColour(colour) 179 180 # Set the default value. 181 if self.default != None: 182 self._field.SetValue(self.default) 183 184 # Initialise the combo box input field. 185 elif self.element_type == 'combo': 186 # The style. 187 style = wx.CB_DROPDOWN 188 if read_only: 189 style = style | wx.CB_READONLY 190 191 # Set up the combo box. 192 self._field = wx.ComboBox(parent, -1, '', style=style) 193 194 # Update the choices. 195 self.UpdateChoices(combo_choices=combo_choices, combo_data=combo_data, combo_default=default) 196 197 # Unknown field. 198 else: 199 raise RelaxError("Unknown element type '%s'." % self.element_type) 200 201 # Set up the input field. 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 # Right padding. 207 sub_sizer.AddSpacer(padding) 208 209 # Add to the main sizer. 210 sizer.Add(sub_sizer, 1, wx.EXPAND|wx.ALL, 0) 211 212 # Spacing below the widget. 213 if spacer == None: 214 sizer.AddStretchSpacer() 215 else: 216 sizer.AddSpacer(spacer) 217 218 # Tooltip. 219 if tooltip: 220 text.SetToolTipString(tooltip) 221 self._field.SetToolTipString(tooltip)
222 223
224 - def Clear(self):
225 """Special method for clearing or resetting the GUI element.""" 226 227 # Clear the value from a TextCtrl. 228 if self.element_type == 'text': 229 self._field.Clear() 230 231 # Clear the value from a ComboBox. 232 if self.element_type == 'combo': 233 self._field.Clear() 234 self._field.SetValue('')
235 236
237 - def GetValue(self):
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 # Convert and return the value from a TextCtrl. 245 if self.element_type == 'text': 246 # The value. 247 value = self._field.GetValue() 248 249 # Convert. 250 try: 251 value = self.convert_from_gui(value) 252 253 # Raise a clear error for user feedback. 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 # Return the integer value from a SpinCtrl. 261 if self.element_type == 'spin': 262 # The value. 263 return self._field.GetValue() 264 265 # Convert and return the value from a ComboBox. 266 if self.element_type == 'combo': 267 # An element selected from the list. 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 # A non-list value. 275 if value == None: 276 value = self.convert_from_gui(self._field.GetValue()) 277 278 # Return the value. 279 return value
280 281
282 - def SetValue(self, value):
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 # Convert and set the value for a TextCtrl. 290 if self.element_type == 'text': 291 self._field.SetValue(self.convert_to_gui(value)) 292 293 # Set the value for a SpinCtrl. 294 elif self.element_type == 'spin': 295 self._field.SetValue(value) 296 297 # Convert and set the value for a ComboBox. 298 elif self.element_type == 'combo': 299 # Loop until the proper client data is found. 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 # No value found. 308 if not found: 309 # Invalid value. 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 # Set the unknown value, and remove the selection. 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 # A TextCtrl?! 332 if self.element_type == 'text': 333 raise RelaxError("Cannot update the list of ComboBox choices as this is a TextCtrl!") 334 335 # A SpinCtrl?! 336 if self.element_type == 'spin': 337 raise RelaxError("Cannot update the list of ComboBox choices as this is a SpinCtrl!") 338 339 # Update the choices for a ComboBox. 340 if self.element_type == 'combo': 341 # Store the current selection's client data to restore at the end. 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 # First clear all data. 349 self.Clear() 350 351 # Set the data if needed. 352 if combo_data == None: 353 combo_data = deepcopy(combo_choices) 354 355 # Handle None in combo boxes by prepending a None element to the lists. 356 if self.can_be_none: 357 combo_choices.insert(0, '') 358 combo_data.insert(0, None) 359 360 # Loop over the choices and data, adding both to the end. 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 # Set the default selection. 365 if sel == None and combo_default != None: 366 # Translate if needed. 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 # Set the selection. 378 if set_sel: 379 self._field.SetStringSelection(string) 380 381 # Set the value. 382 else: 383 self._field.SetValue(string) 384 385 # Restore the selection. 386 else: 387 for j in range(self._field.GetCount()): 388 if self._field.GetClientData(j) == sel: 389 self._field.SetSelection(j)
390