--- a/LPCManager.py Sun May 20 14:57:52 2012 +0200
+++ b/LPCManager.py Fri Jun 08 12:40:31 2012 +0200
@@ -59,28 +59,32 @@
__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"))
+
+_base_path = os.path.split(__file__)[0]
+
+import connectors
+from LPCconnector import LPC_connector_factory
+connectors.connectors["LPC"]=lambda:LPC_connector_factory
+
+import targets
+from LPCtarget import LPC_target
+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
+from util import misc
+misc.opjimg = lambda imgname: os.path.join(_base_folder, "beremiz", "images", imgname+".png")
from Beremiz import *
from ProjectController import ProjectController
from ConfigTreeNode import ConfigTreeNode
-
-import connectors
-from LPCconnector import LPC_connector_factory
-connectors.connectors["LPC"]=lambda:LPC_connector_factory
-
-import targets
-from LPCtarget import LPC_target
-targets.targets["LPC"]={"xsd": path.join(_base_path, "LPCtarget", "XSD"),
- "class": LPC_target,
- "code": path.join(_base_path,"LPCtarget","plc_LPC_main.c")}
-targets.toolchains["makefile"]= path.join(_base_path, "LPCtarget", "XSD_toolchain_makefile"),
-
-from util import opjimg
+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 @@
try:
from canfestival import RootClass as CanOpenRootClass
from canfestival.canfestival import _SlaveCTN, _NodeListCTN, NodeManager
+ from canfestival.NetworkEditor import NetworkEditor
+ from canfestival.SlaveEditor import SlaveEditor
havecanfestival = True
except:
havecanfestival = False
+SCROLLBAR_UNIT = 10
+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',
+ 'helv' : 'Arial',
+ 'other': 'Comic Sans MS',
+ 'size' : 16,
+ }
+else:
+ faces = { 'times': 'Times',
+ 'mono' : 'Courier',
+ 'helv' : 'Helvetica',
+ 'other': 'new century schoolbook',
+ 'size' : 18,
+ }
+
+# 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
+
#-------------------------------------------------------------------------------
# CANFESTIVAL CONFNODE HACK
@@ -397,6 +503,9 @@
"Master_NodeId": 1,
}
+ class LPCSlaveEditor(SlaveEditor):
+ SHOW_PARAMS = False
+
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 @@
</xsd:schema>
""" % DEFAULT_SETTINGS
+ EditorType = LPCSlaveEditor
+
def __init__(self):
# TODO change netname when name change
NodeManager.__init__(self)
@@ -437,6 +548,9 @@
def GetCanDevice(self):
return str(self.BaseParams.getIEC_Channel())
+
+ class LPCNetworkEditor(NetworkEditor):
+ SHOW_PARAMS = False
class LPCCanOpenMaster(_NodeListCTN):
XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
@@ -450,7 +564,9 @@
</xsd:element>
</xsd:schema>
""" % DEFAULT_SETTINGS
-
+
+ EditorType = LPCNetworkEditor
+
def GetCanDevice(self):
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)
+ slave.CTNRequestSave()
#-------------------------------------------------------------------------------
@@ -498,34 +616,42 @@
[SIMULATION_MODE, TRANSFER_MODE] = range(2)
+class LPCProjectNodeEditor(ProjectNodeEditor):
+ SHOW_PARAMS = False
+ ENABLE_REQUIRED = False
+
class LPCProjectController(ProjectController):
- ConfNodeMethods = [
- {"bitmap" : opjimg("Debug"),
+ StatusMethods = [
+ {"bitmap" : "Debug",
"name" : _("Simulate"),
"tooltip" : _("Simulate PLC"),
"method" : "_Simulate"},
- {"bitmap" : opjimg("Run"),
+ {"bitmap" : "Run",
"name" : _("Run"),
"shown" : False,
"tooltip" : _("Start PLC"),
"method" : "_Run"},
- {"bitmap" : opjimg("Stop"),
+ {"bitmap" : "Stop",
"name" : _("Stop"),
"shown" : False,
"tooltip" : _("Stop Running PLC"),
"method" : "_Stop"},
- {"bitmap" : opjimg("Build"),
+ {"bitmap" : "Build",
"name" : _("Build"),
"tooltip" : _("Build project into build folder"),
"method" : "_Build"},
- {"bitmap" : opjimg("Transfer"),
+ {"bitmap" : "Transfer",
"name" : _("Transfer"),
"shown" : False,
"tooltip" : _("Transfer PLC"),
"method" : "_Transfer"},
]
-
+
+ ConfNodeMethods = []
+
+ 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)
+ resources = None
+ for config_infos in configurations["values"]:
+ if resources is None:
+ resources = config_infos["values"][0]
+ else:
+ resources["values"].extend(config_infos["values"][0]["values"])
+ if resources is not None:
+ infos["values"].append(resources)
+ return infos
+
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.LoadChildren()
+ canopen.CTNRequestSave()
if self.CTNTestModified():
self.SaveProject()
@@ -1084,9 +1224,6 @@
AppendMenu(parent, help='', id=wx.ID_PRINT,
kind=wx.ITEM_NORMAL, text=_(u'Print'))
parent.AppendSeparator()
- 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)
+ new_id = wx.NewId()
+ 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._init_beremiz_sizers()
-
+ 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.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):
global frame
@@ -1144,18 +1296,16 @@
event.Veto()
- 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)
- dialog.Destroy()
+ def OnMoveWindow(self, event):
+ self.GetBestSize()
+ self.RefreshScrollBars()
+ event.Skip()
+
+ def OnPanelLeftDown(self, event):
+ focused = self.FindFocus()
+ if isinstance(focused, TextCtrlAutoComplete):
+ focused.DismissListBox()
+ event.Skip()
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)
else:
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()
+ if sizer:
+ 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.Freeze()
self.ClearSizer(self.PLCParamsSizer)
@@ -1234,6 +1394,69 @@
self.RefreshScrollBars()
self.Thaw()
+ 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"])
+ if horizontal:
+ msizer = wx.FlexGridSizer(cols=len(confnode.ConfNodeMethods))
+ else:
+ msizer = wx.FlexGridSizer(cols=1)
+ for confnode_method in confnode.ConfNodeMethods:
+ if "method" in confnode_method and confnode_method.get("shown",True):
+ id = wx.NewId()
+ 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):
+ 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 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)
+ event.Skip()
+ enablebutton.Bind(wx.EVT_BUTTON, toggleenablebutton, id=enablebutton_id)
+ sizer.AddWindow(enablebutton, 0, border=0, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
+ else:
+ sizer.AddSpacer(wx.Size(16, 16))
+
+ def RefreshConfNodeTree(self):
+ self.Freeze()
+ 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()
+ self.Thaw()
+
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)
+ if 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)
+ if 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)
+ if 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)
+ if force:
+ locations_infos["root"]["expanded"] = False
+
+ def ExpandLocation(self, locations_infos, group, force = False, refresh_size=True):
+ locations_infos[group]["expanded"] = True
+ if group == "root":
+ 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"])
+ if force:
+ 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 group == "root":
+ 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"])
+ if force:
+ 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
+ else:
+ 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()
+ infos.pop("children")
+ 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):
+ item = event.GetItem()
+ 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
+ else:
+ 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)
+ else:
+ child, item_cookie = treectrl.GetFirstChild(item, 0)
+ while child.IsOk():
+ 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):
+ item = event.GetItem()
+ 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"])
+ event.Skip()
+ 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()
+ # Call
+ getattr(confnode,method)()
+ # Re-enable button
+ event.GetEventObject().Enable()
+ # Trigger refresh on Idle
+ wx.CallAfter(self.RefreshAll)
+ event.Skip()
+ return OnButtonClick
+
+ def ClearSizer(self, sizer):
+ staticboxes = []
+ for item in sizer.GetChildren():
+ if item.IsSizer():
+ item_sizer = item.GetSizer()
+ self.ClearSizer(item_sizer)
+ if isinstance(item_sizer, wx.StaticBoxSizer):
+ staticboxes.append(item_sizer.GetStaticBox())
+ sizer.Clear(True)
+ for staticbox in staticboxes:
+ staticbox.Destroy()
+
+ def RefreshAll(self):
+ Beremiz.RefreshAll(self)
+ self.RefreshPLCParams()
+ self.RefreshConfNodeTree()
+
class StdoutPseudoFile:
def __init__(self, port):