--- a/LPCManager.py Sun May 20 14:57:52 2012 +0200
+++ b/LPCManager.py Fri Jun 08 12:40:31 2012 +0200
@@ -59,13 +59,9 @@
__builtin__.__dict__['_'] = wx.GetTranslation#unicode_translation
_base_folder = os.path.split(sys.path[0])[0]
-sys.path.append(os.path.join(base_folder, "beremiz"))
-_base_path = path.split(__file__)[0]
+sys.path.append(os.path.join(_base_folder, "beremiz"))
-from ProjectController import ProjectController
-from ConfigTreeNode import ConfigTreeNode
+_base_path = os.path.split(__file__)[0] from LPCconnector import LPC_connector_factory
@@ -73,14 +69,22 @@
from LPCtarget import LPC_target
-targets.targets["LPC"]={"xsd": path.join(_base_path, "LPCtarget", "XSD"),
- "code": path.join(_base_path,"LPCtarget","plc_LPC_main.c")}
-targets.toolchains["makefile"]= path.join(_base_path, "LPCtarget", "XSD_toolchain_makefile"),
+targets.targets["LPC"] = {"xsd": os.path.join(_base_path, "LPCtarget", "XSD"), + "class": lambda:LPC_target, + "code": os.path.join(_base_path,"LPCtarget","plc_LPC_main.c")} +targets.toolchains["makefile"] = os.path.join(_base_path, "LPCtarget", "XSD_toolchain_makefile")
+# helper func to get path to images +misc.opjimg = lambda imgname: os.path.join(_base_folder, "beremiz", "images", imgname+".png") +from ProjectController import ProjectController +from ConfigTreeNode import ConfigTreeNode +from ProjectNodeEditor import ProjectNodeEditor from plcopen.structures import LOCATIONDATATYPES
-from PLCControler import LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP,\
+from PLCControler import PLCControler, LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP,\ LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY
from PLCOpenEditor import IDEFrame, ProjectDialog
@@ -88,10 +92,112 @@
from canfestival import RootClass as CanOpenRootClass
from canfestival.canfestival import _SlaveCTN, _NodeListCTN, NodeManager
+ from canfestival.NetworkEditor import NetworkEditor + from canfestival.SlaveEditor import SlaveEditor +WINDOW_COLOUR = wx.Colour(240,240,240) +TITLE_COLOUR = wx.Colour(200,200,220) +CHANGED_TITLE_COLOUR = wx.Colour(220,200,220) +CHANGED_WINDOW_COLOUR = wx.Colour(255,240,240) +if wx.Platform == '__WXMSW__': + faces = { 'times': 'Times New Roman', + 'mono' : 'Courier New', + 'other': 'Comic Sans MS', + faces = { 'times': 'Times', + 'other': 'new century schoolbook', +# 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.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 + AlphaData = anImage.GetAlphaData() + 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()) + 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 + return width, height, False + def DrawLabel(self, dc, width, height, dw=0, dy=0): + if bmp != None: # if the bitmap is used + if self.bmpDisabled and not self.IsEnabled(): + if self.bmpFocus and self.hasFocus: + if self.bmpSelected and not self.up: + bw,bh = bmp.GetWidth(), bmp.GetHeight() + dw = dy = self.labelDelta + hasMask = bmp.GetMask() != None + bw = bh = 0 # no bitmap -> size is zero + dc.SetFont(self.GetFont()) + dc.SetTextForeground(self.GetForegroundColour()) + dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT)) + label = self.GetLabel() + tw, th = dc.GetTextExtent(label) # size of text + dw = dy = self.labelDelta + pos_x = (width-bw)/2+dw # adjust for bitmap and text to centre + pos_y = (height-bh-th)/2+dy + 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 + dc.DrawText(label, pos_x, pos_y) # draw the text #-------------------------------------------------------------------------------
# CANFESTIVAL CONFNODE HACK
@@ -397,6 +503,9 @@
+ class LPCSlaveEditor(SlaveEditor): class LPCCanOpenSlave(_SlaveCTN):
XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
@@ -418,6 +527,8 @@
+ EditorType = LPCSlaveEditor # TODO change netname when name change
NodeManager.__init__(self)
@@ -437,6 +548,9 @@
return str(self.BaseParams.getIEC_Channel())
+ class LPCNetworkEditor(NetworkEditor): class LPCCanOpenMaster(_NodeListCTN):
XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
@@ -450,7 +564,9 @@
+ EditorType = LPCNetworkEditor return str(self.BaseParams.getIEC_Channel())
@@ -468,10 +584,12 @@
if self.GetChildByName("Master") is None:
master = self.CTNAddChild("Master", "CanOpenNode", 0)
master.BaseParams.setEnabled(False)
+ master.CTNRequestSave() if self.GetChildByName("Slave") is None:
slave = self.CTNAddChild("Slave", "CanOpenSlave", 1)
slave.BaseParams.setEnabled(False)
#-------------------------------------------------------------------------------
@@ -498,34 +616,42 @@
[SIMULATION_MODE, TRANSFER_MODE] = range(2)
+class LPCProjectNodeEditor(ProjectNodeEditor): + ENABLE_REQUIRED = False class LPCProjectController(ProjectController):
- {"bitmap" : opjimg("Debug"),
"tooltip" : _("Simulate PLC"),
- {"bitmap" : opjimg("Run"),
"tooltip" : _("Start PLC"),
- {"bitmap" : opjimg("Stop"),
"tooltip" : _("Stop Running PLC"),
- {"bitmap" : opjimg("Build"),
"tooltip" : _("Build project into build folder"),
- {"bitmap" : opjimg("Transfer"),
+ {"bitmap" : "Transfer", "tooltip" : _("Transfer PLC"),
+ EditorType = LPCProjectNodeEditor def __init__(self, frame, logger, buildpath):
self.OrigBuildPath = buildpath
@@ -547,6 +673,19 @@
self.AbortTransferTimer = None
+ def GetProjectInfos(self): + infos = PLCControler.GetProjectInfos(self) + configurations = infos["values"].pop(-1) + for config_infos in configurations["values"]: + resources = config_infos["values"][0] + resources["values"].extend(config_infos["values"][0]["values"]) + if resources is not None: + infos["values"].append(resources) def ConfNodeLibraryFilePath(self):
if self.OrigBuildPath is not None:
return os.path.join(self.OrigBuildPath, "pous.xml")
@@ -717,6 +856,7 @@
canopen = self.CTNAddChild("CanOpen", "CanOpen", 0)
canopen.BaseParams.setEnabled(False)
+ canopen.CTNRequestSave() if self.CTNTestModified():
@@ -1084,9 +1224,6 @@
AppendMenu(parent, help='', id=wx.ID_PRINT,
kind=wx.ITEM_NORMAL, text=_(u'Print'))
- AppendMenu(parent, help='', id=wx.ID_PROPERTIES,
- kind=wx.ITEM_NORMAL, text=_(u'Properties'))
- parent.AppendSeparator()
AppendMenu(parent, help='', id=wx.ID_EXIT,
kind=wx.ITEM_NORMAL, text=_(u'Quit\tCTRL+Q'))
@@ -1095,18 +1232,20 @@
self.Bind(wx.EVT_MENU, self.OnPageSetupMenu, id=wx.ID_PAGE_SETUP)
self.Bind(wx.EVT_MENU, self.OnPreviewMenu, id=wx.ID_PREVIEW)
self.Bind(wx.EVT_MENU, self.OnPrintMenu, id=wx.ID_PRINT)
- self.Bind(wx.EVT_MENU, self.OnPropertiesMenu, id=wx.ID_PROPERTIES)
self.Bind(wx.EVT_MENU, self.OnQuitMenu, id=wx.ID_EXIT)
self.AddToMenuToolBar([(wx.ID_SAVE, "save.png", _(u'Save'), None),
(wx.ID_PRINT, "print.png", _(u'Print'), None)])
+ def _init_coll_AddMenu_Items(self, parent): + IDEFrame._init_coll_AddMenu_Items(self, parent, False) + AppendMenu(parent, help='', id=new_id, + kind=wx.ITEM_NORMAL, text=_(u'&Resource')) + self.Bind(wx.EVT_MENU, self.AddResourceMenu, id=new_id) def _init_ctrls(self, prnt):
- IDEFrame._init_ctrls(self, prnt)
- self.Bind(wx.EVT_MENU, self.OnOpenWidgetInspector, id=ID_BEREMIZINSPECTOR)
- accel = wx.AcceleratorTable([wx.AcceleratorEntry(wx.ACCEL_CTRL|wx.ACCEL_ALT, ord('I'), ID_BEREMIZINSPECTOR)])
- self.SetAcceleratorTable(accel)
+ Beremiz._init_ctrls(self, prnt) self.PLCConfig = wx.ScrolledWindow(id=ID_BEREMIZPLCCONFIG,
name='PLCConfig', parent=self.LeftNoteBook, pos=wx.Point(0, 0),
@@ -1114,16 +1253,29 @@
self.PLCConfig.SetBackgroundColour(wx.WHITE)
self.PLCConfig.Bind(wx.EVT_LEFT_DOWN, self.OnPanelLeftDown)
self.PLCConfig.Bind(wx.EVT_SIZE, self.OnMoveWindow)
+ self.MainTabs["PLCConfig"] = (self.PLCConfig, _("Topology")) self.LeftNoteBook.InsertPage(0, self.PLCConfig, _("Topology"), True)
- self.LogConsole = wx.TextCtrl(id=ID_BEREMIZLOGCONSOLE, value='',
- name='LogConsole', parent=self.BottomNoteBook, pos=wx.Point(0, 0),
- size=wx.Size(0, 0), style=wx.TE_MULTILINE|wx.TE_RICH2)
- self.LogConsole.Bind(wx.EVT_LEFT_DCLICK, self.OnLogConsoleDClick)
- self.BottomNoteBook.AddPage(self.LogConsole, _("Log Console"))
+ self.PLCConfigMainSizer = wx.FlexGridSizer(cols=1, hgap=2, rows=2, vgap=2) + self.PLCParamsSizer = wx.BoxSizer(wx.VERTICAL) + self.ConfNodeTreeSizer = wx.FlexGridSizer(cols=2, hgap=0, rows=0, vgap=2) + self.ConfNodeTreeSizer.AddGrowableCol(0) + self.ConfNodeTreeSizer.AddGrowableCol(1) - self._init_beremiz_sizers()
+ self.PLCConfigMainSizer.AddSizer(self.PLCParamsSizer, 0, border=10, flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) + self.PLCConfigMainSizer.AddSizer(self.ConfNodeTreeSizer, 0, border=10, flag=wx.BOTTOM|wx.LEFT|wx.RIGHT) + self.PLCConfigMainSizer.AddGrowableCol(0) + self.PLCConfigMainSizer.AddGrowableRow(1) + self.PLCConfig.SetSizer(self.PLCConfigMainSizer) + self.AUIManager.Update() + def __init__(self, parent, projectOpen=None, buildpath=None, ctr=None, debug=True): + self.ConfNodeInfos = {} + Beremiz.__init__(self, parent, projectOpen, buildpath, ctr, debug) def OnCloseFrame(self, event):
@@ -1144,18 +1296,16 @@
- def ShowProperties(self):
- old_values = self.Controler.GetProjectProperties()
- dialog = ProjectDialog(self ,False)
- dialog.SetValues(old_values)
- if dialog.ShowModal() == wx.ID_OK:
- new_values = dialog.GetValues()
- new_values["creationDateTime"] = old_values["creationDateTime"]
- if new_values != old_values:
- self.Controler.SetProjectProperties(None, new_values)
- self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU,
- PROJECTTREE, POUINSTANCEVARIABLESPANEL, SCALING)
+ def OnMoveWindow(self, event): + self.RefreshScrollBars() + def OnPanelLeftDown(self, event): + focused = self.FindFocus() + if isinstance(focused, TextCtrlAutoComplete): + focused.DismissListBox() def RefreshFileMenu(self):
MenuToolBar = self.Panes["MenuToolBar"]
@@ -1184,7 +1334,6 @@
project_modified = self.CTR.ProjectTestModified()
self.FileMenu.Enable(wx.ID_SAVE, project_modified)
MenuToolBar.EnableTool(wx.ID_SAVE, project_modified)
- self.FileMenu.Enable(wx.ID_PROPERTIES, True)
self.FileMenu.Enable(wx.ID_CLOSE, False)
self.FileMenu.Enable(wx.ID_PAGE_SETUP, False)
@@ -1193,8 +1342,19 @@
MenuToolBar.EnableTool(wx.ID_PRINT, False)
self.FileMenu.Enable(wx.ID_SAVE, False)
MenuToolBar.EnableTool(wx.ID_SAVE, False)
- self.FileMenu.Enable(wx.ID_PROPERTIES, False)
+ def RefreshScrollBars(self): + xstart, ystart = self.PLCConfig.GetViewStart() + window_size = self.PLCConfig.GetClientSize() + sizer = self.PLCConfig.GetSizer() + maxx, maxy = sizer.GetMinSize() + posx = max(0, min(xstart, (maxx - window_size[0]) / SCROLLBAR_UNIT)) + posy = max(0, min(ystart, (maxy - window_size[1]) / SCROLLBAR_UNIT)) + self.PLCConfig.Scroll(posx, posy) + self.PLCConfig.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT, + maxx / SCROLLBAR_UNIT, maxy / SCROLLBAR_UNIT, posx, posy) def RefreshPLCParams(self):
self.ClearSizer(self.PLCParamsSizer)
@@ -1234,6 +1394,69 @@
+ def GenerateMethodButtonSizer(self, confnode, parent, horizontal = True): + 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.FlexGridSizer(cols=len(confnode.ConfNodeMethods)) + msizer = wx.FlexGridSizer(cols=1) + for confnode_method in confnode.ConfNodeMethods: + if "method" in confnode_method and confnode_method.get("shown",True): + label = confnode_method["name"] + button = GenBitmapTextButton(id=id, parent=parent, + 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, confnode_method["method"]), id=id) + # a fancy underline on mouseover + def setFontStyle(b, s): + 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): + msizer.AddWindow(button, 0, border=0, flag=wx.ALIGN_CENTER) + def GenerateEnableButton(self, parent, sizer, confnode): + enabled = confnode.CTNEnabled() + if enabled is not None: + enablebutton_id = wx.NewId() + enablebutton = wx.lib.buttons.GenBitmapToggleButton(id=enablebutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Disabled.png')), + name='EnableButton', parent=parent, size=wx.Size(16, 16), pos=wx.Point(0, 0), style=0)#wx.NO_BORDER) + enablebutton.SetToolTipString(_("Enable/Disable this confnode")) + make_genbitmaptogglebutton_flat(enablebutton) + enablebutton.SetBitmapSelected(wx.Bitmap(Bpath( 'images', 'Enabled.png'))) + enablebutton.SetToggle(enabled) + def toggleenablebutton(event): + res = self.SetConfNodeParamsAttribute(confnode, "BaseParams.Enabled", enablebutton.GetToggle()) + enablebutton.SetToggle(res) + enablebutton.Bind(wx.EVT_BUTTON, toggleenablebutton, id=enablebutton_id) + sizer.AddWindow(enablebutton, 0, border=0, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) + sizer.AddSpacer(wx.Size(16, 16)) + def RefreshConfNodeTree(self): + self.ClearSizer(self.ConfNodeTreeSizer) + if self.CTR is not None: + for child in self.CTR.IECSortedChildren(): + self.GenerateTreeBranch(child) + if not self.ConfNodeInfos[child]["expanded"]: + self.CollapseConfNode(child) + self.PLCConfigMainSizer.Layout() + self.RefreshScrollBars() def GenerateTreeBranch(self, confnode):
leftwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1))
if confnode.CTNTestModified():
@@ -1327,7 +1550,10 @@
st.SetLabel(confnode.MandatoryParams[1].getName())
leftwindowsizer.AddWindow(st, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
- rightwindow = self.GenerateParamsPanel(confnode, bkgdclr)
+ rightwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1)) + rightwindow.SetBackgroundColour(bkgdclr) + rightwindowsizer = self.GenerateMethodButtonSizer(confnode, rightwindow, not self.ConfNodeInfos[confnode]["right_visible"]) + rightwindow.SetSizer(rightwindowsizer) self.ConfNodeTreeSizer.AddWindow(rightwindow, 0, border=0, flag=wx.GROW)
self.ConfNodeInfos[confnode]["left"] = leftwindow
@@ -1361,6 +1587,175 @@
if locations_infos["root"]["expanded"]:
self.ExpandLocation(locations_infos, "root")
+ def ExpandConfNode(self, confnode, force = False): + for child in self.ConfNodeInfos[confnode]["children"]: + self.ConfNodeInfos[child]["left"].Show() + self.ConfNodeInfos[child]["right"].Show() + if force or self.ConfNodeInfos[child]["expanded"]: + self.ExpandConfNode(child, force) + self.ConfNodeInfos[child]["expanded"] = True + locations_infos = self.ConfNodeInfos[confnode].get("locations_infos", None) + if locations_infos is not None: + if force or locations_infos["root"]["expanded"]: + self.ExpandLocation(locations_infos, "root", force) + locations_infos["root"]["expanded"] = True + def CollapseConfNode(self, confnode, force = False): + for child in self.ConfNodeInfos[confnode]["children"]: + self.ConfNodeInfos[child]["left"].Hide() + self.ConfNodeInfos[child]["right"].Hide() + self.CollapseConfNode(child, force) + self.ConfNodeInfos[child]["expanded"] = False + locations_infos = self.ConfNodeInfos[confnode].get("locations_infos", None) + if locations_infos is not None: + self.CollapseLocation(locations_infos, "root", force) + locations_infos["root"]["expanded"] = False + def ExpandLocation(self, locations_infos, group, force = False, refresh_size=True): + locations_infos[group]["expanded"] = True + if locations_infos[group]["left"] is not None: + locations_infos[group]["left"].Show() + if locations_infos[group]["right"] is not None: + locations_infos[group]["right"].Show() + elif locations_infos["root"]["left"] is not None: + locations_infos["root"]["left"].Expand(locations_infos[group]["item"]) + for child in locations_infos[group]["children"]: + self.ExpandLocation(locations_infos, child, force, False) + if locations_infos["root"]["left"] is not None and refresh_size: + self.RefreshTreeCtrlSize(locations_infos["root"]["left"]) + def CollapseLocation(self, locations_infos, group, force = False, refresh_size=True): + locations_infos[group]["expanded"] = False + if locations_infos[group]["left"] is not None: + locations_infos[group]["left"].Hide() + if locations_infos[group]["right"] is not None: + locations_infos[group]["right"].Hide() + elif locations_infos["root"]["left"] is not None: + locations_infos["root"]["left"].Collapse(locations_infos[group]["item"]) + for child in locations_infos[group]["children"]: + self.CollapseLocation(locations_infos, child, force, False) + if locations_infos["root"]["left"] is not None and refresh_size: + self.RefreshTreeCtrlSize(locations_infos["root"]["left"]) + def GenerateLocationTreeBranch(self, treectrl, root, locations_infos, parent, location): + location_name = "%s.%s" % (parent, location["name"]) + if not locations_infos.has_key(location_name): + locations_infos[location_name] = {"expanded" : False} + if location["type"] in [LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY]: + label = "%(name)s (%(location)s)" % location + elif location["location"] != "": + label = "%(location)s: %(name)s" % location + label = location["name"] + item = treectrl.AppendItem(root, label) + treectrl.SetPyData(item, location_name) + treectrl.SetItemImage(item, self.LocationImageDict[location["type"]]) + locations_infos[location_name]["item"] = item + locations_infos[location_name]["children"] = [] + infos = location.copy() + locations_infos[location_name]["infos"] = infos + for child in location["children"]: + child_name = "%s.%s" % (location_name, child["name"]) + locations_infos[location_name]["children"].append(child_name) + self.GenerateLocationTreeBranch(treectrl, item, locations_infos, location_name, child) + if locations_infos[location_name]["expanded"]: + self.ExpandLocation(locations_infos, location_name) + def GenerateLocationBeginDragFunction(self, locations_infos): + def OnLocationBeginDragFunction(event): + location_name = locations_infos["root"]["left"].GetPyData(item) + if location_name is not None: + infos = locations_infos[location_name]["infos"] + if infos["type"] in [LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY]: + data = wx.TextDataObject(str((infos["location"], "location", infos["IEC_type"], infos["var_name"], infos["description"]))) + dragSource = wx.DropSource(self) + dragSource.SetData(data) + dragSource.DoDragDrop() + return OnLocationBeginDragFunction + def RefreshTreeCtrlSize(self, treectrl): + rect = self.GetTreeCtrlItemRect(treectrl, treectrl.GetRootItem()) + treectrl.SetMinSize(wx.Size(max(rect.width, rect.x + rect.width) + 20, max(rect.height, rect.y + rect.height))) + self.PLCConfigMainSizer.Layout() + self.PLCConfig.Refresh() + wx.CallAfter(self.RefreshScrollBars) + def GetTreeCtrlItemRect(self, treectrl, item): + item_rect = treectrl.GetBoundingRect(item, True) + if item_rect is not None: + minx, miny = item_rect.x, item_rect.y + maxx, maxy = item_rect.x + item_rect.width, item_rect.y + item_rect.height + minx = miny = maxx = maxy = 0 + if treectrl.ItemHasChildren(item) and (item == treectrl.GetRootItem() or treectrl.IsExpanded(item)): + if wx.VERSION >= (2, 6, 0): + child, item_cookie = treectrl.GetFirstChild(item) + child, item_cookie = treectrl.GetFirstChild(item, 0) + child_rect = self.GetTreeCtrlItemRect(treectrl, child) + minx = min(minx, child_rect.x) + miny = min(miny, child_rect.y) + maxx = max(maxx, child_rect.x + child_rect.width) + maxy = max(maxy, child_rect.y + child_rect.height) + child, item_cookie = treectrl.GetNextChild(item, item_cookie) + return wx.Rect(minx, miny, maxx - minx, maxy - miny) + def GenerateLocationExpandCollapseFunction(self, locations_infos, expand): + def OnLocationExpandedFunction(event): + location_name = locations_infos["root"]["left"].GetPyData(item) + if location_name is not None: + locations_infos[location_name]["expanded"] = expand + self.RefreshTreeCtrlSize(locations_infos["root"]["left"]) + return OnLocationExpandedFunction + def GetButtonCallBackFunction(self, confnode, method): + """ Generate the callbackfunc for a given confnode method""" + def OnButtonClick(event): + # Disable button to prevent re-entrant call + event.GetEventObject().Disable() + getattr(confnode,method)() + event.GetEventObject().Enable() + # Trigger refresh on Idle + wx.CallAfter(self.RefreshAll) + def ClearSizer(self, sizer): + for item in sizer.GetChildren(): + item_sizer = item.GetSizer() + self.ClearSizer(item_sizer) + if isinstance(item_sizer, wx.StaticBoxSizer): + staticboxes.append(item_sizer.GetStaticBox()) + for staticbox in staticboxes: + Beremiz.RefreshAll(self) + self.RefreshPLCParams() + self.RefreshConfNodeTree() def __init__(self, port):