diff -r 7b421e080636 -r 413946c04c87 ConfTreeNodeEditor.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ConfTreeNodeEditor.py Mon May 21 10:04:27 2012 +0200 @@ -0,0 +1,536 @@ + +import os +import types + +import wx +import wx.lib.buttons + +from controls import EditorPanel + +from PLCOpenEditor import TITLE, FILEMENU, PROJECTTREE, PAGETITLES + +from util import opjimg +from util.TextCtrlAutoComplete import TextCtrlAutoComplete +from util.BrowseValuesLibraryDialog import BrowseValuesLibraryDialog + +if wx.Platform == '__WXMSW__': + faces = { 'times': 'Times New Roman', + 'mono' : 'Courier New', + 'helv' : 'Arial', + 'other': 'Comic Sans MS', + 'size' : 16, + } +else: + faces = { 'times': 'Times', + 'mono' : 'Courier', + 'helv' : 'Helvetica', + 'other': 'new century schoolbook', + 'size' : 18, + } + +SCROLLBAR_UNIT = 10 +WINDOW_COLOUR = wx.Colour(240,240,240) + +CWD = os.path.split(os.path.realpath(__file__))[0] + +def Bpath(*args): + return os.path.join(CWD,*args) + +# Some helpers to tweak GenBitmapTextButtons +# TODO: declare customized classes instead. +gen_mini_GetBackgroundBrush = lambda obj:lambda dc: wx.Brush(obj.GetParent().GetBackgroundColour(), wx.SOLID) +gen_textbutton_GetLabelSize = lambda obj:lambda:(wx.lib.buttons.GenButton._GetLabelSize(obj)[:-1] + (False,)) + +def make_genbitmaptogglebutton_flat(button): + button.GetBackgroundBrush = gen_mini_GetBackgroundBrush(button) + button.labelDelta = 0 + button.SetBezelWidth(0) + button.SetUseFocusIndicator(False) + +# Patch wx.lib.imageutils so that gray is supported on alpha images +import wx.lib.imageutils +from wx.lib.imageutils import grayOut as old_grayOut +def grayOut(anImage): + if anImage.HasAlpha(): + AlphaData = anImage.GetAlphaData() + else : + AlphaData = None + + old_grayOut(anImage) + + if AlphaData is not None: + anImage.SetAlphaData(AlphaData) + +wx.lib.imageutils.grayOut = grayOut + +class GenBitmapTextButton(wx.lib.buttons.GenBitmapTextButton): + def _GetLabelSize(self): + """ used internally """ + w, h = self.GetTextExtent(self.GetLabel()) + if not self.bmpLabel: + return w, h, False # if there isn't a bitmap use the size of the text + + w_bmp = self.bmpLabel.GetWidth()+2 + h_bmp = self.bmpLabel.GetHeight()+2 + height = h + h_bmp + if w_bmp > w: + width = w_bmp + else: + width = w + return width, height, False + + def DrawLabel(self, dc, width, height, dw=0, dy=0): + bmp = self.bmpLabel + if bmp != None: # if the bitmap is used + if self.bmpDisabled and not self.IsEnabled(): + bmp = self.bmpDisabled + if self.bmpFocus and self.hasFocus: + bmp = self.bmpFocus + if self.bmpSelected and not self.up: + bmp = self.bmpSelected + bw,bh = bmp.GetWidth(), bmp.GetHeight() + if not self.up: + dw = dy = self.labelDelta + hasMask = bmp.GetMask() != None + else: + bw = bh = 0 # no bitmap -> size is zero + + dc.SetFont(self.GetFont()) + if self.IsEnabled(): + dc.SetTextForeground(self.GetForegroundColour()) + else: + dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT)) + + label = self.GetLabel() + tw, th = dc.GetTextExtent(label) # size of text + if not self.up: + dw = dy = self.labelDelta + + pos_x = (width-bw)/2+dw # adjust for bitmap and text to centre + pos_y = (height-bh-th)/2+dy + if bmp !=None: + dc.DrawBitmap(bmp, pos_x, pos_y, hasMask) # draw bitmap if available + pos_x = (width-tw)/2+dw # adjust for bitmap and text to centre + pos_y += bh + 2 + + dc.DrawText(label, pos_x, pos_y) # draw the text + + +class GenStaticBitmap(wx.lib.statbmp.GenStaticBitmap): + """ Customized GenStaticBitmap, fix transparency redraw bug on wx2.8/win32, + and accept image name as __init__ parameter, fail silently if file do not exist""" + def __init__(self, parent, ID, bitmapname, + pos = wx.DefaultPosition, size = wx.DefaultSize, + style = 0, + name = "genstatbmp"): + + bitmappath = Bpath( "images", bitmapname) + if os.path.isfile(bitmappath): + bitmap = wx.Bitmap(bitmappath) + else: + bitmap = None + wx.lib.statbmp.GenStaticBitmap.__init__(self, parent, ID, bitmap, + pos, size, + style, + name) + + def OnPaint(self, event): + dc = wx.PaintDC(self) + colour = self.GetParent().GetBackgroundColour() + dc.SetPen(wx.Pen(colour)) + dc.SetBrush(wx.Brush(colour )) + dc.DrawRectangle(0, 0, *dc.GetSizeTuple()) + if self._bitmap: + dc.DrawBitmap(self._bitmap, 0, 0, True) + +class ConfTreeNodeEditor(EditorPanel): + + HAS_BASE_PARAMS = True + + def _init_ConfNodeEditor(self, prnt): + self.ConfNodeEditor = None + + def _init_Editor(self, prnt): + self.Editor = wx.SplitterWindow(id=self.ID, name='EditorSplitter', parent=prnt, + size=wx.Size(0, 0), style=wx.SUNKEN_BORDER|wx.SP_3D) + self.SetNeedUpdating(True) + self.SetMinimumPaneSize(1) + + self.ParamsEditor = wx.ScrolledWindow(self.Editor, -1, size=wx.Size(-1, -1), + style=wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER|wx.HSCROLL|wx.VSCROLL) + self.ParamsEditor.SetBackgroundColour(WINDOW_COLOUR) + self.ParamsEditor.Bind(wx.EVT_SIZE, self.OnWindowResize) + self.ParamsEditor.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel) + + # Variable allowing disabling of ParamsEditor scroll when Popup shown + self.ScrollingEnabled = True + + self.ParamsEditorSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=5) + self.ParamsEditorSizer.AddGrowableCol(0) + self.ParamsEditorSizer.AddGrowableRow(1) + + self.ParamsEditor.SetSizer(self.ParamsEditorSizer) + + baseparamseditor_sizer = wx.BoxSizer(wx.HORIZONTAL) + self.ParamsEditorSizer.AddSizer(baseparamseditor_sizer, 0, border=5, + flag=wx.GROW|wx.LEFT|wx.RIGHT|wx.TOP) + + self.FullIECChannel = wx.StaticText(self.ParamsEditor, -1) + self.FullIECChannel.SetFont( + wx.Font(faces["size"], wx.DEFAULT, wx.NORMAL, + wx.BOLD, faceName = faces["helv"])) + baseparamseditor_sizer.AddWindow(self.FullIECChannel, 0, border=0, flag=wx.ALIGN_CENTER_VERTICAL) + + updownsizer = wx.BoxSizer(wx.VERTICAL) + baseparamseditor_sizer.AddSizer(updownsizer, 0, border=5, + flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL) + + ieccdownbutton_id = wx.NewId() + self.IECCDownButton = wx.lib.buttons.GenBitmapButton( + id=ieccdownbutton_id, bitmap=wx.Bitmap(opjimg('IECCDown')), + name='IECDownButton', parent=self.ParamsEditor, pos=wx.Point(0, 0), + size=wx.Size(16, 16), style=wx.NO_BORDER) + self.IECCDownButton.Bind(wx.EVT_BUTTON, self.GetItemChannelChangedFunction(-1), + id=ieccdownbutton_id) + updownsizer.AddWindow(self.IECCDownButton, 0, border=0, flag=wx.ALIGN_LEFT) + + ieccupbutton_id = wx.NewId() + self.IECCUpButton = wx.lib.buttons.GenBitmapTextButton( + id=ieccupbutton_id, bitmap=wx.Bitmap(opjimg('IECCUp')), + name='IECUpButton', parent=self.ParamsEditor, pos=wx.Point(0, 0), + size=wx.Size(16, 16), style=wx.NO_BORDER) + self.IECCUpButton.Bind(wx.EVT_BUTTON, self.GetItemChannelChangedFunction(1), + id=ieccupbutton_id) + updownsizer.AddWindow(self.IECCUpButton, 0, border=0, flag=wx.ALIGN_LEFT) + + self.RefreshIECChannelControlsState() + + confnodename_id = wx.NewId() + self.ConfNodeName = wx.TextCtrl( + self.ParamsEditor, confnodename_id, + size=wx.Size(150, 25), style=wx.NO_BORDER) + self.ConfNodeName.SetFont( + wx.Font(faces["size"] * 0.75, wx.DEFAULT, wx.NORMAL, + wx.BOLD, faceName = faces["helv"])) + self.ConfNodeName.ChangeValue(self.Controler.MandatoryParams[1].getName()) + self.ConfNodeName.Bind(wx.EVT_TEXT, + self.GetTextCtrlCallBackFunction(self.ConfNodeName, "BaseParams.Name"), + id=confnodename_id) + baseparamseditor_sizer.AddWindow(self.ConfNodeName, 0, border=5, flag=wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) + + buttons_sizer = self.GenerateMethodButtonSizer() + baseparamseditor_sizer.AddSizer(buttons_sizer, 0, border=0, flag=wx.ALIGN_CENTER) + + self.ConfNodeParamsSizer = wx.BoxSizer(wx.VERTICAL) + self.ParamsEditorSizer.AddSizer(self.ConfNodeParamsSizer, 0, border=5, + flag=wx.LEFT|wx.RIGHT|wx.BOTTOM) + + self.RefreshConfNodeParamsSizer() + + self._init_ConfNodeEditor(self.Editor) + + if self.ConfNodeEditor is not None: + min_size = self.ParamsEditorSizer.GetMinSize() + self.Editor.SplitHorizontally(self.ParamsEditor, + self.ConfNodeEditor, + min(min_size.height, 200)) + else: + self.Editor.Initialize(self.ParamsEditor) + + def __init__(self, parent, tagname, controler, window): + EditorPanel.__init__(self, parent, tagname, window, controler) + + self.SetIcon(wx.Bitmap(self.Controler.GetIconPath(), wx.BITMAP_TYPE_PNG)) + + def __del__(self): + self.Controler.OnCloseEditor(self) + + def GetTagName(self): + return self.Controler.CTNFullName() + + def GetTitle(self): + fullname = self.Controler.CTNFullName() + if self.Controler.CTNTestModified(): + return "~%s~" % fullname + return fullname + + def HasNoModel(self): + return False + + def EnableScrolling(self, enable): + self.ScrollingEnabled = enable + + def RefreshIECChannelControlsState(self): + self.FullIECChannel.SetLabel(self.Controler.GetFullIEC_Channel()) + self.IECCDownButton.Enable(self.Controler.BaseParams.getIEC_Channel() > 0) + + def RefreshConfNodeParamsSizer(self): + self.Freeze() + self.ConfNodeParamsSizer.Clear(True) + + confnode_infos = self.Controler.GetParamsAttributes() + if len(confnode_infos) > 0: + self.GenerateSizerElements(self.ConfNodeParamsSizer, confnode_infos, None, False) + + self.ParamsEditorSizer.Layout() + self.Thaw() + + def GenerateMethodButtonSizer(self): + normal_bt_font=wx.Font(faces["size"] / 3, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = faces["helv"]) + mouseover_bt_font=wx.Font(faces["size"] / 3, wx.DEFAULT, wx.NORMAL, wx.NORMAL, underline=True, faceName = faces["helv"]) + + msizer = wx.BoxSizer(wx.HORIZONTAL) + + for confnode_method in self.Controler.ConfNodeMethods: + if "method" in confnode_method and confnode_method.get("shown",True): + id = wx.NewId() + label = confnode_method["name"] + button = GenBitmapTextButton(id=id, parent=self.ParamsEditor, + bitmap=wx.Bitmap(Bpath("images", "%s.png"%confnode_method.get("bitmap", "Unknown"))), label=label, + name=label, pos=wx.DefaultPosition, style=wx.NO_BORDER) + button.SetFont(normal_bt_font) + button.SetToolTipString(confnode_method["tooltip"]) + button.Bind(wx.EVT_BUTTON, self.GetButtonCallBackFunction(confnode_method["method"]), id=id) + # a fancy underline on mouseover + def setFontStyle(b, s): + def fn(event): + b.SetFont(s) + b.Refresh() + event.Skip() + return fn + button.Bind(wx.EVT_ENTER_WINDOW, setFontStyle(button, mouseover_bt_font)) + button.Bind(wx.EVT_LEAVE_WINDOW, setFontStyle(button, normal_bt_font)) + #hack to force size to mini + if not confnode_method.get("enabled",True): + button.Disable() + msizer.AddWindow(button, 0, border=0, flag=wx.ALIGN_CENTER) + return msizer + + def GenerateSizerElements(self, sizer, elements, path, clean = True): + if clean: + sizer.Clear(True) + first = True + for element_infos in elements: + if path: + element_path = "%s.%s"%(path, element_infos["name"]) + else: + element_path = element_infos["name"] + if element_infos["type"] == "element": + label = element_infos["name"] + staticbox = wx.StaticBox(id=-1, label=_(label), + name='%s_staticbox'%element_infos["name"], parent=self.ParamsEditor, + pos=wx.Point(0, 0), size=wx.Size(10, 0), style=0) + staticboxsizer = wx.StaticBoxSizer(staticbox, wx.VERTICAL) + if first: + sizer.AddSizer(staticboxsizer, 0, border=0, flag=wx.GROW|wx.TOP) + else: + sizer.AddSizer(staticboxsizer, 0, border=0, flag=wx.GROW) + self.GenerateSizerElements(staticboxsizer, element_infos["children"], element_path) + else: + boxsizer = wx.FlexGridSizer(cols=3, rows=1) + boxsizer.AddGrowableCol(1) + if first: + sizer.AddSizer(boxsizer, 0, border=5, flag=wx.GROW|wx.ALL) + else: + sizer.AddSizer(boxsizer, 0, border=5, flag=wx.GROW|wx.LEFT|wx.RIGHT|wx.BOTTOM) + staticbitmap = GenStaticBitmap(ID=-1, bitmapname="%s.png"%element_infos["name"], + name="%s_bitmap"%element_infos["name"], parent=self.ParamsEditor, + pos=wx.Point(0, 0), size=wx.Size(24, 24), style=0) + boxsizer.AddWindow(staticbitmap, 0, border=5, flag=wx.RIGHT) + label = element_infos["name"] + statictext = wx.StaticText(id=-1, label="%s:"%_(label), + name="%s_label"%element_infos["name"], parent=self.ParamsEditor, + pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) + boxsizer.AddWindow(statictext, 0, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT) + id = wx.NewId() + if isinstance(element_infos["type"], types.ListType): + if isinstance(element_infos["value"], types.TupleType): + browse_boxsizer = wx.BoxSizer(wx.HORIZONTAL) + boxsizer.AddSizer(browse_boxsizer, 0, border=0, flag=0) + + textctrl = wx.TextCtrl(id=id, name=element_infos["name"], parent=self.ParamsEditor, + pos=wx.Point(0, 0), size=wx.Size(275, 25), style=wx.TE_READONLY) + if element_infos["value"] is not None: + textctrl.SetValue(element_infos["value"][0]) + value_infos = element_infos["value"][1] + else: + value_infos = None + browse_boxsizer.AddWindow(textctrl, 0, border=0, flag=0) + button_id = wx.NewId() + button = wx.Button(id=button_id, name="browse_%s" % element_infos["name"], parent=self.ParamsEditor, + label="...", pos=wx.Point(0, 0), size=wx.Size(25, 25)) + browse_boxsizer.AddWindow(button, 0, border=0, flag=0) + button.Bind(wx.EVT_BUTTON, + self.GetBrowseCallBackFunction(element_infos["name"], textctrl, element_infos["type"], + value_infos, element_path), + id=button_id) + else: + combobox = wx.ComboBox(id=id, name=element_infos["name"], parent=self.ParamsEditor, + pos=wx.Point(0, 0), size=wx.Size(300, 28), style=wx.CB_READONLY) + boxsizer.AddWindow(combobox, 0, border=0, flag=0) + if element_infos["use"] == "optional": + combobox.Append("") + if len(element_infos["type"]) > 0 and isinstance(element_infos["type"][0], types.TupleType): + for choice, xsdclass in element_infos["type"]: + combobox.Append(choice) + name = element_infos["name"] + value = element_infos["value"] + staticbox = wx.StaticBox(id=-1, label="%s - %s"%(_(name), _(value)), + name='%s_staticbox'%element_infos["name"], parent=self.ParamsEditor, + pos=wx.Point(0, 0), size=wx.Size(10, 0), style=0) + staticboxsizer = wx.StaticBoxSizer(staticbox, wx.VERTICAL) + sizer.AddSizer(staticboxsizer, 0, border=5, flag=wx.GROW|wx.BOTTOM) + self.GenerateSizerElements(staticboxsizer, element_infos["children"], element_path) + callback = self.GetChoiceContentCallBackFunction(combobox, staticboxsizer, element_path) + else: + for choice in element_infos["type"]: + combobox.Append(choice) + callback = self.GetChoiceCallBackFunction(combobox, element_path) + if element_infos["value"] is None: + combobox.SetStringSelection("") + else: + combobox.SetStringSelection(element_infos["value"]) + combobox.Bind(wx.EVT_COMBOBOX, callback, id=id) + elif isinstance(element_infos["type"], types.DictType): + scmin = -(2**31) + scmax = 2**31-1 + if "min" in element_infos["type"]: + scmin = element_infos["type"]["min"] + if "max" in element_infos["type"]: + scmax = element_infos["type"]["max"] + spinctrl = wx.SpinCtrl(id=id, name=element_infos["name"], parent=self.ParamsEditor, + pos=wx.Point(0, 0), size=wx.Size(300, 25), style=wx.SP_ARROW_KEYS|wx.ALIGN_RIGHT) + spinctrl.SetRange(scmin,scmax) + boxsizer.AddWindow(spinctrl, 0, border=0, flag=0) + if element_infos["value"] is not None: + spinctrl.SetValue(element_infos["value"]) + spinctrl.Bind(wx.EVT_SPINCTRL, self.GetTextCtrlCallBackFunction(spinctrl, element_path), id=id) + else: + if element_infos["type"] == "boolean": + checkbox = wx.CheckBox(id=id, name=element_infos["name"], parent=self.ParamsEditor, + pos=wx.Point(0, 0), size=wx.Size(17, 25), style=0) + boxsizer.AddWindow(checkbox, 0, border=0, flag=0) + if element_infos["value"] is not None: + checkbox.SetValue(element_infos["value"]) + checkbox.Bind(wx.EVT_CHECKBOX, self.GetCheckBoxCallBackFunction(checkbox, element_path), id=id) + elif element_infos["type"] in ["unsignedLong", "long","integer"]: + if element_infos["type"].startswith("unsigned"): + scmin = 0 + else: + scmin = -(2**31) + scmax = 2**31-1 + spinctrl = wx.SpinCtrl(id=id, name=element_infos["name"], parent=self.ParamsEditor, + pos=wx.Point(0, 0), size=wx.Size(300, 25), style=wx.SP_ARROW_KEYS|wx.ALIGN_RIGHT) + spinctrl.SetRange(scmin, scmax) + boxsizer.AddWindow(spinctrl, 0, border=0, flag=0) + if element_infos["value"] is not None: + spinctrl.SetValue(element_infos["value"]) + spinctrl.Bind(wx.EVT_SPINCTRL, self.GetTextCtrlCallBackFunction(spinctrl, element_path), id=id) + else: + choices = self.ParentWindow.GetConfigEntry(element_path, [""]) + textctrl = TextCtrlAutoComplete(id=id, + name=element_infos["name"], + parent=self.ParamsEditor, + appframe=self, + choices=choices, + element_path=element_path, + pos=wx.Point(0, 0), + size=wx.Size(300, 25), + style=0) + + boxsizer.AddWindow(textctrl, 0, border=0, flag=0) + if element_infos["value"] is not None: + textctrl.ChangeValue(str(element_infos["value"])) + textctrl.Bind(wx.EVT_TEXT, self.GetTextCtrlCallBackFunction(textctrl, element_path)) + first = False + + + def GetItemChannelChangedFunction(self, dir): + def OnConfNodeTreeItemChannelChanged(event): + confnode_IECChannel = self.Controler.BaseParams.getIEC_Channel() + res = self.SetConfNodeParamsAttribute("BaseParams.IEC_Channel", confnode_IECChannel + dir) + wx.CallAfter(self.RefreshIECChannelControlsState) + wx.CallAfter(self.ParentWindow._Refresh, TITLE, FILEMENU, PROJECTTREE) + wx.CallAfter(self.ParentWindow.SelectProjectTreeItem, self.GetTagName()) + event.Skip() + return OnConfNodeTreeItemChannelChanged + + def SetConfNodeParamsAttribute(self, *args, **kwargs): + res, StructChanged = self.Controler.SetParamsAttribute(*args, **kwargs) + if StructChanged: + wx.CallAfter(self.RefreshConfNodeParamsSizer) + wx.CallAfter(self.ParentWindow._Refresh, TITLE, FILEMENU) + return res + + def GetButtonCallBackFunction(self, method): + """ Generate the callbackfunc for a given confnode method""" + def OnButtonClick(event): + # Disable button to prevent re-entrant call + event.GetEventObject().Disable() + # Call + getattr(self.Controler,method)() + # Re-enable button + event.GetEventObject().Enable() + + event.Skip() + return OnButtonClick + + def GetChoiceCallBackFunction(self, choicectrl, path): + def OnChoiceChanged(event): + res = self.SetConfNodeParamsAttribute(path, choicectrl.GetStringSelection()) + choicectrl.SetStringSelection(res) + event.Skip() + return OnChoiceChanged + + def GetChoiceContentCallBackFunction(self, choicectrl, staticboxsizer, path): + def OnChoiceContentChanged(event): + res = self.SetConfNodeParamsAttribute(path, choicectrl.GetStringSelection()) + event.Skip() + return OnChoiceContentChanged + + def GetTextCtrlCallBackFunction(self, textctrl, path): + def OnTextCtrlChanged(event): + res = self.SetConfNodeParamsAttribute(path, textctrl.GetValue()) + if res != textctrl.GetValue(): + textctrl.ChangeValue(res) + if textctrl == self.ConfNodeName: + wx.CallAfter(self.ParentWindow._Refresh, TITLE, FILEMENU, PROJECTTREE, PAGETITLES) + wx.CallAfter(self.ParentWindow.SelectProjectTreeItem, self.GetTagName()) + event.Skip() + return OnTextCtrlChanged + + def GetCheckBoxCallBackFunction(self, chkbx, path): + def OnCheckBoxChanged(event): + res = self.SetConfNodeParamsAttribute(path, chkbx.IsChecked()) + chkbx.SetValue(res) + event.Skip() + return OnCheckBoxChanged + + def GetBrowseCallBackFunction(self, name, textctrl, library, value_infos, path): + infos = [value_infos] + def OnBrowseButton(event): + dialog = BrowseValuesLibraryDialog(self, name, library, infos[0]) + if dialog.ShowModal() == wx.ID_OK: + value, value_infos = self.SetConfNodeParamsAttribute(path, dialog.GetValueInfos()) + textctrl.ChangeValue(value) + infos[0] = value_infos + dialog.Destroy() + event.Skip() + return OnBrowseButton + + def OnWindowResize(self, event): + self.GetBestSize() + xstart, ystart = self.ParamsEditor.GetViewStart() + window_size = self.ParamsEditor.GetClientSize() + maxx, maxy = self.ParamsEditorSizer.GetMinSize() + posx = max(0, min(xstart, (maxx - window_size[0]) / SCROLLBAR_UNIT)) + posy = max(0, min(ystart, (maxy - window_size[1]) / SCROLLBAR_UNIT)) + self.ParamsEditor.Scroll(posx, posy) + self.ParamsEditor.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT, + maxx / SCROLLBAR_UNIT, maxy / SCROLLBAR_UNIT, posx, posy) + event.Skip() + + def OnMouseWheel(self, event): + if self.ScrollingEnabled: + event.Skip() +