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