# 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. """ Details box for Scientific Parameter Controls for wxPython """ __version__ = "$Revision: 1.8 $" # $Source: /greaterrepository/sciparam/SciParam/UI/detailsbox.py,v $ # $Id: detailsbox.py,v 1.8 2003/09/23 09:21:47 thomas Exp $ from wxPython.wx import * from SciParam import ChoiceParam, FloatParam, DistParam, Distribution from control import _SPACE, _SMALLSPACE from pargrid import ParameterGrid DETAILS_KEY = 'D' # must be uppercase DISTRIBUTION_CHOICE = false class ParameterDistributionDialog(wxDialog): """input of distribution parameters like mean value and std. deviation""" def __init__(self, dialog, parctrl): wxDialog.__init__(self, dialog, -1, parctrl.parameter.name) self.parctrl = parctrl self.dist_type = parctrl.dist_type dist_type_ctrl = [ChoiceParam('Distribution', choices=parctrl.parameter.value.types, value=self.dist_type, long=1, hook_updated=self.OnTypeUpdated)] descriptives_map = parctrl.parameter.value.descriptives_map max_descriptives = max(map(len, descriptives_map.values())) self.desc_params = {} for dist_type in parctrl.parameter.value.types: desc_params = [] for label in descriptives_map[dist_type]: desc_params.append(FloatParam(label, unit=parctrl.parameter.unit, notunknown=1)) while len(desc_params) < max_descriptives: desc_params.append(FloatParam('not used', disabled=true)) self.desc_params[dist_type] = desc_params parts = parctrl.GetValue().split(';') for param in self.desc_params[self.dist_type]: if parts: try: param.value = parts.pop(0) except ValueError, why: pass self.pargrid = ParameterGrid(self, None, [dist_type_ctrl + self.desc_params[self.dist_type]]) self.box = wxBoxSizer(wxVERTICAL) self.box.Add(self.pargrid, 0, wxTOP | wxLEFT | wxRIGHT | wxGROW, _SPACE) # box with buttons buttonbox = wxBoxSizer(wxHORIZONTAL) button = wxButton(self, wxID_OK, ' OK ') button.SetDefault() buttonbox.Add(button, 0, wxLEFT | wxRIGHT | wxALIGN_BOTTOM, _SPACE) button = wxButton(self, wxID_CANCEL, ' Cancel ') buttonbox.Add(button, 0, wxLEFT | wxRIGHT | wxALIGN_BOTTOM, _SPACE) self.box.Add(buttonbox, 1, wxALL | wxALIGN_CENTER, _SPACE) self.SetAutoLayout(true) self.SetSizer(self.box) self.box.Fit(self) self.box.SetSizeHints(self) self.CenterOnParent() self.dialogexists = false EVT_BUTTON(self, wxID_OK, self.OnOK) def OnTypeUpdated(self, parameter, value): if value != self.dist_type: for ctrl, param in zip(self.pargrid.controls[1:], self.desc_params[value]): ctrl.UpdateFromParameter(param) self.Layout() self.box.SetSizeHints(self) self.dist_type = value def DistValidate(self): parameter = self.parctrl.parameter try: self.dist = Distribution(None, self.dist_type) self.dist.descriptives = tuple( [parctrl.GetValue() for parctrl in self.pargrid.controls[1:] if not parctrl.parameter.disabled]) except ValueError, why: if not self.dialogexists: self.dialogexists = true dialog = wxMessageDialog(self, str(why), "Illegal value", wxOK | wxICON_ERROR) answer = dialog.ShowModal() dialog.Destroy() self.dialogexists = false return false errors = [] if not parameter.isvalid(self.dist, errors=errors): if not self.dialogexists: self.dialogexists = true dialog = wxMessageDialog(self, '\n'.join(errors), "Invalid value", wxOK | wxICON_ERROR) answer = dialog.ShowModal() dialog.Destroy() self.dialogexists = false return false elif not parameter.isusual(self.dist, errors=errors): if not self.dialogexists: self.dialogexists = true dialog = wxMessageDialog(self, "%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: return true return false return true def OnOK(self, event): if self.Validate() and self.DistValidate() \ and self.TransferDataFromWindow(): if self.IsModal(): self.EndModal(event.GetId()) else: self.SetReturnCode(event.GetId()) self.Show(false) class DistChoice(wxChoice): def __init__(self, parent): wxChoice.__init__(self, parent, -1, choices=['not applicable']+Distribution.types) self.connect(None) EVT_CHOICE(self, self.GetId(), self.OnChoice) def connect(self, parctrl): self.parctrl = parctrl if parctrl and isinstance(parctrl.parameter, DistParam): self.Clear() for choice in parctrl.parameter.value.types: self.Append(choice) self.SetStringSelection(parctrl.dist_type) self.Enable(true) else: self.Enable(false) self.Clear() self.Append('not applicable') self.SetSelection(0) def OnChoice(self, event): if self.parctrl: self.parctrl.SetValue(self.parctrl.GetValue() + '/' + self.GetStringSelection()) class ParameterDetailsBox(wxStaticBox): def __init__(self, parent, id, dialog): wxStaticBox.__init__(self, parent, id, 'Details (Alt-%s toggles input focus)' % DETAILS_KEY) self.parent = parent self.dialog = dialog self.sizer = wxStaticBoxSizer(self, wxVERTICAL) self.parctrl = None self.dialogexists = false parameters = self.dialog.get_parameters() ## Name and Description self.name_desc = wxStaticText(parent, -1, 'Parameter:', style=wxST_NO_AUTORESIZE) font = wxFont(10, wxSWISS, wxNORMAL, wxBOLD, false) self.name_desc.SetFont(font) self.sizer.Add(self.name_desc, 0, wxBOTTOM, _SMALLSPACE) minsize = self.name_desc.GetSize() for par in parameters: height = 0 for line in self.get_name_desc(par).splitlines(): size = self.name_desc.GetFullTextExtent(line) minsize.SetWidth(max(minsize.width, size[0])) height += size[1] + size[2] minsize.SetHeight(max(minsize.height, height)) self.sizer.SetItemMinSize(self.name_desc, minsize.width, minsize.height) ## Range self.range = wxStaticText(parent, -1, 'Range:') self.sizer.Add(self.range, 0, wxBOTTOM, _SMALLSPACE) minsize = self.range.GetSize() for par in parameters: size = self.range.GetFullTextExtent(str(par.range)) minsize.SetWidth(max(minsize.width, size[0])) minsize.SetHeight(max(minsize.height, size[1] + size[2])) self.sizer.SetItemMinSize(self.range, minsize.width, minsize.height) ## Comment if dialog.show_comment: comment = wxStaticText(self.parent, -1, 'Comment on the value you entered:') self.comment = wxTextCtrl(self.parent, -1, size=wxSize(-1, 75), style=wxTE_MULTILINE) EVT_KILL_FOCUS(self.comment, self.OnKillFocus) EVT_KEY_DOWN(self.comment, self.OnKeyDown) EVT_CHAR(self.comment, self.CommentOnChar) hbox = wxBoxSizer(wxHORIZONTAL) ## Distribution if DISTRIBUTION_CHOICE: self.dist = wxStaticText(parent, -1, 'Distribution:', style=wxST_NO_AUTORESIZE) hbox.Add(self.dist, 0, wxALIGN_CENTER_VERTICAL) self.distChoice = DistChoice(parent) EVT_KEY_DOWN(self.distChoice, self.OnKeyDown) hbox.Add(self.distChoice, 0, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, _SMALLSPACE) else: self.dist = wxStaticText(parent, -1, self.get_dist(None)[0], style=wxST_NO_AUTORESIZE) hbox.Add(self.dist, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, _SMALLSPACE) minsize = self.dist.GetSize() for dist_type in Distribution.types: size = self.dist.GetFullTextExtent(self.get_dist(dist_type)[0]) minsize.SetWidth(max(minsize.width, size[0])) minsize.SetHeight(max(minsize.height, size[1] + size[2])) hbox.SetItemMinSize(self.dist, minsize.width, minsize.height) self.distButton = wxButton(parent, -1, 'Edit Distribution') self.distButton.Enable(false) hbox.Add(self.distButton, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, _SPACE) EVT_BUTTON(parent, self.distButton.GetId(), self.OnDistButton) EVT_KEY_DOWN(self.distButton, self.OnKeyDown) ## Default self.default = wxStaticText(parent, -1, self.get_default('')[0], style=wxALIGN_RIGHT | wxST_NO_AUTORESIZE) hbox.Add(self.default, 1, wxLEFT | wxALIGN_CENTER_VERTICAL, _SPACE) minsize = self.default.GetSize() always_default = true for par in parameters: if par.default is None: if always_default: always_default = false default = self.get_default(None)[0] else: default = None else: default = self.get_default(par.default)[0] if default: size = self.default.GetFullTextExtent(default) minsize.SetWidth(max(minsize.width, size[0])) minsize.SetHeight(max(minsize.height, size[1] + size[2])) hbox.SetItemMinSize(self.default, minsize.width, minsize.height) self.defaultButton = wxButton(parent, -1, 'Apply Default') self.defaultButton.Enable(false) hbox.Add(self.defaultButton, 0, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, _SMALLSPACE) EVT_BUTTON(parent, self.defaultButton.GetId(), self.OnDefaultButton) EVT_KEY_DOWN(self.defaultButton, self.OnKeyDown) # end of hbox self.sizer.Add(hbox, 0, wxBOTTOM | wxGROW, _SMALLSPACE) if dialog.show_comment: self.sizer.Add(comment, 0, wxTOP, _SPACE) self.sizer.Add(self.comment, 1, wxTOP | wxGROW, _SMALLSPACE) def get_name_desc(self, parameter): if parameter.description: return "%s: %s" % (parameter.name, parameter.description) else: return parameter.name def get_dist(self, dist_type): if dist_type is None: return "Distribution: not applicable", false else: return "Distribution: %s" % dist_type, true def get_default(self, default): if default is None: return "Default: not available", false else: return "Default: %s" % default, true def connect(self, parctrl=None): if parctrl is self.parctrl: return if self.parctrl and self.dialog.show_comment: self.parctrl._comment = self.comment.GetValue() if parctrl: self.parctrl = parctrl self.name_desc.SetLabel(self.get_name_desc(parctrl.parameter)) self.range.SetLabel(parctrl.parameter.range()) self.connect_distribution(parctrl) ## Default default = self.get_default(parctrl.parameter.default) self.default.SetLabel(default[0]) self.defaultButton.Enable(default[1]) if self.dialog.show_comment: self.comment.SetValue(parctrl._comment) def connect_distribution(self, parctrl): if parctrl and isinstance(parctrl.parameter, DistParam): dist_type = self.get_dist(parctrl.dist_type) else: dist_type = self.get_dist(None) if DISTRIBUTION_CHOICE: self.distChoice.connect(parctrl) else: self.dist.SetLabel(dist_type[0]) self.distButton.Enable(dist_type[1]) def ValidateComment(self, ctrl): if self.dialog.show_comment: length = len(ctrl._comment) max_length = self.dialog.comment_length if max_length and not self.dialogexists and length > max_length: self.dialogexists = true dialog = wxMessageDialog(self.dialog, "The comment for '%s' is too long.\n" "Maximum length is %d characters." % (ctrl.parameter.name, max_length), "Comment too long", wxOK | wxICON_ERROR) dialog.ShowModal() dialog.Destroy() self.dialogexists = false ctrl.pargrid.connect(ctrl) self.comment.SetFocus() return false return true def OnKillFocus(self, event): self.connect() event.Skip() def OnDistButton(self, event): dialog = ParameterDistributionDialog(self.dialog, self.parctrl) if dialog.ShowModal() == wxID_OK: self.parctrl.SetValue(str(dialog.dist)) dialog.Destroy() self.parctrl.refocus = true event.Skip() def OnDefaultButton(self, event): self.parctrl.SetValue(str(self.parctrl.parameter.default)) self.parctrl.refocus = true event.Skip() def OnKeyDown(self, event): """Catch details key from details box""" if event.AltDown() and event.GetKeyCode() == ord(DETAILS_KEY): self.parctrl.refocus = true else: event.Skip() def CtrlOnKeyDown(self, event): """Catch details key from parameter controls""" if event.AltDown() and event.GetKeyCode() == ord(DETAILS_KEY): if self.dialog.show_comment: self.comment.SetFocus() elif DISTRIBUTION_CHOICE and self.distChoice.IsEnabled(): self.distChoice.SetFocus() elif self.distButton.IsEnabled(): self.distButton.SetFocus() elif self.defaultButton.IsEnabled(): self.defaultButton.SetFocus() else: event.Skip() def CommentOnChar(self, event): # Try to determine whether the key event would add a character # to the text in the control and forbid it if it would become # too long. This is difficult as we have to second guess how the # edit control would handle the event. key = event.GetKeyCode() selection = self.comment.GetSelection() if not self.dialog.comment_length \ or key < 32 or key > 255 or key == WXK_DELETE \ or selection[1] > selection[0] \ or len(self.comment.GetValue()) < self.dialog.comment_length: event.Skip()