# Copyright (C) 2002, 2003 by Intevation GmbH # Authors: # Thomas Arendsen Hein # # This program is free software under the GPL (>=v2) # Read the file COPYING coming with the software for details. """ Scientific Parameter Controls for wxPython """ __version__ = "$Revision: 1.9 $" # $Source: /greaterrepository/sciparam/SciParam/UI/control.py,v $ # $Id: control.py,v 1.9 2003/09/29 16:39:19 thomas Exp $ from wxPython.wx import * from SciParam import DistParam _SMALLSPACE = 4 _SPACE = 10 class ParameterCtrlMixin: """Mixin class for parameter controls""" def UpdateFromParameter(self, parameter=None): """Update label, icon and unit from parameter. dialog.Layout() and dialog.topbox.SetSizeHints(dialog) have to be called by the user if necessary. """ if parameter and parameter is not self.parameter: if isinstance(parameter, self.parameter.__class__): self.parameter = parameter else: raise TypeError, ("%s.UpdateFromParameter used with wrong " "object: %r" % (self.__class__, parameter)) self.label.SetLabel(self.parameter.name + ':') size = self.label.GetBestSize() self.pargrid.SetItemMinSize(self.label, size.width, size.height) self.icon.Show(self.parameter.required) self.unit.SetLabel(self.parameter.unit) size = self.unit.GetBestSize() self.pargrid.SetItemMinSize(self.unit, size.width, size.height) self.SetValue(self.GetValue()) self.label.Enable(not self.parameter.disabled) self.icon.Enable(not self.parameter.disabled) self.unit.Enable(not self.parameter.disabled) self.Enable(not self.parameter.disabled) #FIXME: update of details box is not implemented yet. class ParameterValidator(wxPyValidator): def __init__(self, ctrl): wxPyValidator.__init__(self) self.ctrl = ctrl self.dialogexists = false EVT_CHAR(self, self.OnChar) def Clone(self): return self.__class__(self.ctrl) def Validate(self, parent): parameter = self.ctrl.parameter if parameter.disabled: return true val = self.ctrl.GetValue() if isinstance(parameter, DistParam): val = parameter.normalize(val+'/'+self.ctrl.dist_type, val) try: val = parameter.convert(val) except ValueError, why: if not self.dialogexists: self.dialogexists = true dialog = wxMessageDialog(parent, "%s contains illegal characters." % parameter.name, "Illegal value", wxOK | wxICON_ERROR) answer = dialog.ShowModal() dialog.Destroy() self.dialogexists = false self.ctrl.refocus = true return false errors = [] if not parameter.isvalid(val, errors=errors): if not self.dialogexists: self.dialogexists = true dialog = wxMessageDialog(parent, '\n'.join(errors), "Invalid value", wxOK | wxICON_ERROR) answer = dialog.ShowModal() dialog.Destroy() self.dialogexists = false self.ctrl.refocus = true return false elif self.ctrl.IsModified() and \ not parameter.isusual(val, errors=errors): if not self.dialogexists: self.dialogexists = true dialog = wxMessageDialog(parent, "%s\nDo you really want to use it?" % '\n'.join(errors), "Unusual value", wxYES_NO | wxICON_WARNING) answer = dialog.ShowModal() dialog.Destroy() self.dialogexists = false if answer == wxID_YES: # don't ask again unless the value has changed self.ctrl.DiscardEdits() return true else: self.ctrl.refocus = true return false return self.ctrl.pargrid.ValidateComment(self.ctrl) def TransferToWindow(self): self.ctrl.SetValue(str(self.ctrl.parameter)) return true def TransferFromWindow(self): if self.ctrl.parameter.disabled: return true if isinstance(self.ctrl.parameter, DistParam): self.ctrl.parameter.value.type = self.ctrl.dist_type self.ctrl.parameter.value = self.ctrl.GetValue() self.ctrl.parameter.comment = self.ctrl._comment return true def OnChar(self, event): """dummy OnChar method to make wxPython 2.2.9 happy""" event.Skip() class ParameterCtrl(wxTextCtrl, ParameterCtrlMixin): def __init__(self, parent, id, parameter, pargrid): self.validator = ParameterValidator(self) wxTextCtrl.__init__(self, parent, id, validator=self.validator) self.parent = parent self.parameter = parameter self.pargrid = pargrid self._comment = parameter.comment if isinstance(parameter, DistParam): self.dist_type = parameter.value.type if hasattr(self, 'SetMaxLength') and hasattr(parameter, 'maxlength'): self.SetMaxLength(parameter.maxlength) self.refocus = false EVT_SET_FOCUS(self, self.OnSetFocus) EVT_KILL_FOCUS(self, self.OnKillFocus) EVT_LEFT_DOWN(self, self.OnLeftDown) EVT_IDLE(self, self.OnIdle) EVT_KEY_DOWN(self, self.pargrid.CtrlOnKeyDown) def OnKillFocus(self, event): self.SetInsertionPointEnd() self.SetSelection(0, 0) if self.IsModified(): # update "unknown" and warning/error colors self.SetValue(self.GetValue()) # reset IsModified() status to true self.Replace(0, self.GetLastPosition(), self.GetValue()) event.Skip() def OnSetFocus(self, event): event.Skip() self.SetInsertionPointEnd() self.SetSelection(0, self.GetLastPosition()) self.pargrid.connect(self) def OnLeftDown(self, event): # dirty hack to solve FIXME in OnIdle if self.pargrid.parctrl is self: event.Skip() else: self.refocus = true def OnIdle(self, event): event.Skip() if self.refocus: #FIXME: set focus to current control doesn't work # if other control selected via mouse click # references for this bug: # http://lists.wxwindows.org/pipermail/wx-users/2000-May/002110.html # http://lists.wxwindows.org/pipermail/wx-users/2002-February/017738.html self.SetFocus() self.refocus = false def SetValue(self, value): if isinstance(self.parameter, DistParam): value = self.parameter.normalize(value+'/'+self.dist_type, value) try: val = self.parameter.convert(value) if not self.parameter.isvalid(val): self.SetBackgroundColour(wxNamedColour('orange red')) elif not self.parameter.isusual(val): self.SetBackgroundColour(wxNamedColour('yellow')) else: self.SetBackgroundColour(wxNamedColour('white')) if isinstance(self.parameter, DistParam): if val is None: self.dist_type = self.parameter.none else: self.dist_type = val.type self.pargrid.connect_distribution(self) value = self.parameter.string(val).split('/')[0] else: value = self.parameter.string(val) wxTextCtrl.SetValue(self, value) #FIXME: implement hook_updated for text controls? except ValueError, why: self.SetBackgroundColour(wxNamedColour('orange red')) self.Refresh() class ParameterRadioButtonValidator(wxPyValidator): def __init__(self, ctrl, radiobutton): wxPyValidator.__init__(self) self.ctrl = ctrl self.radiobutton = radiobutton self.dialogexists = false EVT_CHAR(self, self.OnChar) def Clone(self): return self.__class__(self.ctrl, self.radiobutton) def Validate(self, parent): parameter = self.ctrl.parameter if parameter.disabled: return true if self.radiobutton.GetValue(): val = self.radiobutton.GetLabel() errors = [] if not parameter.isvalid(val, errors=errors): if not self.dialogexists: self.dialogexists = true dialog = wxMessageDialog(parent, '\n'.join(errors), "Invalid choice", wxOK | wxICON_ERROR) answer = dialog.ShowModal() dialog.Destroy() self.dialogexists = false return false return self.ctrl.pargrid.ValidateComment(self.ctrl) def TransferToWindow(self): if self.radiobutton.GetLabel() == str(self.ctrl.parameter): self.radiobutton.SetValue(true) self.ctrl.currentbuttonid = self.radiobutton.GetId() else: self.radiobutton.SetValue(false) return true def TransferFromWindow(self): if self.ctrl.parameter.disabled: return true if self.radiobutton.GetValue(): self.ctrl.parameter.value = self.radiobutton.GetLabel() self.ctrl.parameter.comment = self.ctrl._comment return true def OnChar(self, event): """dummy OnChar method to make wxPython 2.2.9 happy""" event.Skip() class ParameterChoice(wxBoxSizer, ParameterCtrlMixin): def __init__(self, parent, id, parameter, pargrid): wxBoxSizer.__init__(self, wxHORIZONTAL) self.parent = parent self.parameter = parameter self.pargrid = pargrid self._comment = parameter.comment self.currentbuttonid = None self.buttonbyid = {} self.refocus = false style = wxRB_GROUP for choice in parameter.choices: rb_id = wxNewId() rb = wxRadioButton(parent, rb_id, choice, style=style) self.buttonbyid[rb_id] = rb rb.SetValidator(ParameterRadioButtonValidator(self, rb)) style = 0 EVT_RADIOBUTTON(rb, rb_id, self.OnRadioButton) EVT_SET_FOCUS(rb, self.OnSetFocus) EVT_IDLE(rb, self.OnIdle) EVT_KEY_DOWN(rb, self.pargrid.CtrlOnKeyDown) self.Add(rb, 0, wxLEFT | wxRIGHT, _SMALLSPACE) def OnSetFocus(self, event): event.Skip() self.pargrid.connect(self) def OnIdle(self, event): event.Skip() if self.refocus and self.currentbuttonid is not None: self.buttonbyid[self.currentbuttonid].SetFocus() self.refocus = false def OnRadioButton(self, event): event.Skip() self.currentbuttonid = event.GetId() for hook in self.parameter.hook_updated: hook(self.parameter, self.GetValue()) def SetValue(self, value): for id, button in self.buttonbyid.items(): if button.GetLabel() == value: self.currentbuttonid = id button.SetValue(true) else: button.SetValue(false) def GetValue(self): if self.currentbuttonid is not None: return self.buttonbyid[self.currentbuttonid].GetLabel() def Enable(self, enable=true): for rb in self.buttonbyid.values(): rb.Enable(enable) class ParameterLongChoiceValidator(wxPyValidator): def __init__(self, ctrl): wxPyValidator.__init__(self) self.ctrl = ctrl self.dialogexists = false EVT_CHAR(self, self.OnChar) def Clone(self): return self.__class__(self.ctrl) def Validate(self, parent): parameter = self.ctrl.parameter if parameter.disabled: return true val = self.ctrl.GetValue() errors = [] if not parameter.isvalid(val, errors=errors): if not self.dialogexists: self.dialogexists = true dialog = wxMessageDialog(parent, '\n'.join(errors), "Invalid choice", wxOK | wxICON_ERROR) answer = dialog.ShowModal() dialog.Destroy() self.dialogexists = false return false return self.ctrl.pargrid.ValidateComment(self.ctrl) def TransferToWindow(self): self.ctrl.SetValue(str(self.ctrl.parameter)) return true def TransferFromWindow(self): if self.ctrl.parameter.disabled: return true self.ctrl.parameter.value = self.ctrl.GetValue() self.ctrl.parameter.comment = self.ctrl._comment return true def OnChar(self, event): """dummy OnChar method to make wxPython 2.2.9 happy""" event.Skip() class ParameterLongChoice(wxChoice, ParameterCtrlMixin): def __init__(self, parent, id, parameter, pargrid): self.validator = ParameterLongChoiceValidator(self) wxChoice.__init__(self, parent, id, choices=parameter.choices) self.SetValidator(self.validator) self.parent = parent self.parameter = parameter self.pargrid = pargrid self._comment = parameter.comment self.refocus = false EVT_SET_FOCUS(self, self.OnSetFocus) EVT_CHOICE(self, self.GetId(), self.OnChoice) EVT_IDLE(self, self.OnIdle) EVT_KEY_DOWN(self, self.pargrid.CtrlOnKeyDown) def OnSetFocus(self, event): event.Skip() self.pargrid.connect(self) def OnChoice(self, event): event.Skip() self.pargrid.connect(self) for hook in self.parameter.hook_updated: hook(self.parameter, self.GetValue()) def OnIdle(self, event): event.Skip() if self.refocus: self.SetFocus() self.refocus = false def SetValue(self, value): try: self.SetStringSelection(self.parameter.convert(value)[0]) except ValueError, why: pass def GetValue(self): return self.GetStringSelection()