diff -r d7251818be37 -r 1d1bdf6e75bf ConfTreeNodeEditor.py --- a/ConfTreeNodeEditor.py Sat Sep 08 02:12:10 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,573 +0,0 @@ - -import os -import types - -import wx -import wx.lib.buttons - -from controls import EditorPanel - -from PLCOpenEditor import TITLE, FILEMENU, PROJECTTREE, PAGETITLES - -from util.TextCtrlAutoComplete import TextCtrlAutoComplete -from util.BrowseValuesLibraryDialog import BrowseValuesLibraryDialog -from utils.BitmapLibrary import GetBitmap - -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"): - - wx.lib.statbmp.GenStaticBitmap.__init__(self, parent, ID, - GetBitmap(bitmapname), - 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): - - SHOW_BASE_PARAMS = True - SHOW_PARAMS = True - - def _init_ConfNodeEditor(self, prnt): - self.ConfNodeEditor = None - - def _init_Editor(self, parent): - self.Editor = wx.SplitterWindow(parent, - style=wx.SUNKEN_BORDER|wx.SP_3D) - self.SetNeedUpdating(True) - self.SetMinimumPaneSize(1) - - if self.SHOW_PARAMS: - self.ParamsEditor = wx.ScrolledWindow(self.Editor, - 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 - - if self.SHOW_BASE_PARAMS: - 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, 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, - flag=wx.ALIGN_CENTER_VERTICAL) - - updownsizer = wx.BoxSizer(wx.VERTICAL) - baseparamseditor_sizer.AddSizer(updownsizer, border=5, - flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL) - - self.IECCUpButton = wx.lib.buttons.GenBitmapTextButton(self.ParamsEditor, - bitmap=GetBitmap('IECCDown'), size=wx.Size(16, 16), style=wx.NO_BORDER) - self.IECCUpButton.Bind(wx.EVT_BUTTON, self.GetItemChannelChangedFunction(1), - self.IECCUpButton) - updownsizer.AddWindow(self.IECCUpButton, flag=wx.ALIGN_LEFT) - - self.IECCDownButton = wx.lib.buttons.GenBitmapButton(self.ParamsEditor, - bitmap=GetBitmap('IECCUp'), size=wx.Size(16, 16), style=wx.NO_BORDER) - self.IECCDownButton.Bind(wx.EVT_BUTTON, self.GetItemChannelChangedFunction(-1), - self.IECCDownButton) - updownsizer.AddWindow(self.IECCDownButton, flag=wx.ALIGN_LEFT) - - self.ConfNodeName = wx.TextCtrl(self.ParamsEditor, - 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.Bind(wx.EVT_TEXT, - self.GetTextCtrlCallBackFunction(self.ConfNodeName, "BaseParams.Name", True), - self.ConfNodeName) - baseparamseditor_sizer.AddWindow(self.ConfNodeName, border=5, - flag=wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) - - buttons_sizer = self.GenerateMethodButtonSizer() - baseparamseditor_sizer.AddSizer(buttons_sizer, flag=wx.ALIGN_CENTER) - - else: - self.ParamsEditorSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=1, vgap=5) - self.ParamsEditorSizer.AddGrowableCol(0) - self.ParamsEditorSizer.AddGrowableRow(0) - - self.ConfNodeParamsSizer = wx.BoxSizer(wx.VERTICAL) - self.ParamsEditorSizer.AddSizer(self.ConfNodeParamsSizer, border=5, - flag=wx.LEFT|wx.RIGHT|wx.BOTTOM) - - self.RefreshConfNodeParamsSizer() - else: - self.ParamsEditor = None - - self._init_ConfNodeEditor(self.Editor) - - if self.ConfNodeEditor is not None: - if self.ParamsEditor 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.ConfNodeEditor) - elif self.ParamsEditor is not None: - self.Editor.Initialize(self.ParamsEditor) - - def __init__(self, parent, controler, window, tagname=""): - EditorPanel.__init__(self, parent, tagname, window, controler) - - icon_name = self.Controler.GetIconName() - if icon_name is not None: - self.SetIcon(GetBitmap(icon_name)) - else: - self.SetIcon(GetBitmap("Extension")) - - 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 GetBufferState(self): - return False, False - - def Undo(self): - pass - - def Redo(self): - pass - - def RefreshView(self): - EditorPanel.RefreshView(self) - if self.ParamsEditor is not None: - if self.SHOW_BASE_PARAMS: - self.ConfNodeName.ChangeValue(self.Controler.MandatoryParams[1].getName()) - self.RefreshIECChannelControlsState() - self.RefreshConfNodeParamsSizer() - - 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): - button = GenBitmapTextButton(self.ParamsEditor, - bitmap=GetBitmap(confnode_method.get("bitmap", "Unknown")), - label=confnode_method["name"], style=wx.NO_BORDER) - button.SetFont(normal_bt_font) - button.SetToolTipString(confnode_method["tooltip"]) - if confnode_method.get("push", False): - button.Bind(wx.EVT_LEFT_DOWN, self.GetButtonCallBackFunction(confnode_method["method"], True)) - else: - button.Bind(wx.EVT_BUTTON, self.GetButtonCallBackFunction(confnode_method["method"]), button) - # 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, 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(self.ParamsEditor, - label=_(label), size=wx.Size(10, 0)) - staticboxsizer = wx.StaticBoxSizer(staticbox, wx.VERTICAL) - if first: - sizer.AddSizer(staticboxsizer, border=5, - flag=wx.GROW|wx.TOP|wx.BOTTOM) - else: - sizer.AddSizer(staticboxsizer, border=5, - flag=wx.GROW|wx.BOTTOM) - self.GenerateSizerElements(staticboxsizer, - element_infos["children"], - element_path) - else: - boxsizer = wx.FlexGridSizer(cols=3, rows=1) - boxsizer.AddGrowableCol(1) - if first: - sizer.AddSizer(boxsizer, border=5, - flag=wx.GROW|wx.ALL) - else: - sizer.AddSizer(boxsizer, border=5, - flag=wx.GROW|wx.LEFT|wx.RIGHT|wx.BOTTOM) - - staticbitmap = GenStaticBitmap(ID=-1, bitmapname=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, border=5, flag=wx.RIGHT) - - statictext = wx.StaticText(self.ParamsEditor, - label="%s:"%_(element_infos["name"])) - boxsizer.AddWindow(statictext, border=5, - flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT) - - if isinstance(element_infos["type"], types.ListType): - if isinstance(element_infos["value"], types.TupleType): - browse_boxsizer = wx.BoxSizer(wx.HORIZONTAL) - boxsizer.AddSizer(browse_boxsizer) - - textctrl = wx.TextCtrl(self.ParamsEditor, - 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) - - button = wx.Button(self.ParamsEditor, - label="...", size=wx.Size(25, 25)) - browse_boxsizer.AddWindow(button) - button.Bind(wx.EVT_BUTTON, - self.GetBrowseCallBackFunction(element_infos["name"], textctrl, element_infos["type"], - value_infos, element_path), - button) - else: - combobox = wx.ComboBox(self.ParamsEditor, - size=wx.Size(300, 28), style=wx.CB_READONLY) - boxsizer.AddWindow(combobox) - - 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(self.ParamsEditor, - label="%s - %s"%(_(name), _(value)), size=wx.Size(10, 0)) - staticboxsizer = wx.StaticBoxSizer(staticbox, wx.VERTICAL) - sizer.AddSizer(staticboxsizer, 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, combobox) - - 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(self.ParamsEditor, - size=wx.Size(300, 25), style=wx.SP_ARROW_KEYS|wx.ALIGN_RIGHT) - spinctrl.SetRange(scmin, scmax) - boxsizer.AddWindow(spinctrl) - if element_infos["value"] is not None: - spinctrl.SetValue(element_infos["value"]) - spinctrl.Bind(wx.EVT_SPINCTRL, - self.GetTextCtrlCallBackFunction(spinctrl, element_path), - spinctrl) - - else: - if element_infos["type"] == "boolean": - checkbox = wx.CheckBox(self.ParamsEditor, size=wx.Size(17, 25)) - boxsizer.AddWindow(checkbox) - if element_infos["value"] is not None: - checkbox.SetValue(element_infos["value"]) - checkbox.Bind(wx.EVT_CHECKBOX, - self.GetCheckBoxCallBackFunction(checkbox, element_path), - checkbox) - - 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(self.ParamsEditor, - size=wx.Size(300, 25), style=wx.SP_ARROW_KEYS|wx.ALIGN_RIGHT) - spinctrl.SetRange(scmin, scmax) - boxsizer.AddWindow(spinctrl) - if element_infos["value"] is not None: - spinctrl.SetValue(element_infos["value"]) - spinctrl.Bind(wx.EVT_SPINCTRL, - self.GetTextCtrlCallBackFunction(spinctrl, element_path), - spinctrl) - - else: - choices = self.ParentWindow.GetConfigEntry(element_path, [""]) - textctrl = TextCtrlAutoComplete(name=element_infos["name"], - parent=self.ParamsEditor, - appframe=self, - choices=choices, - element_path=element_path, - size=wx.Size(300, 25)) - - boxsizer.AddWindow(textctrl) - 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, push=False): - """ 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() - - if not push: - 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()) - wx.CallAfter(self.RefreshConfNodeParamsSizer) - event.Skip() - return OnChoiceContentChanged - - def GetTextCtrlCallBackFunction(self, textctrl, path, refresh=False): - def OnTextCtrlChanged(event): - res = self.SetConfNodeParamsAttribute(path, textctrl.GetValue()) - if res != textctrl.GetValue(): - textctrl.ChangeValue(res) - if refresh: - 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.ParamsEditor.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() -