--- a/Beremiz.py Mon May 26 14:44:03 2014 +0100
+++ b/Beremiz.py Fri Jun 06 18:30:49 2014 +0100
@@ -2,7 +2,7 @@
#This file is part of Beremiz, a Integrated Development Environment for
-#programming IEC 61131-3 automates supporting plcopen standard and CanFestival.
+#programming IEC 61131-3 automates supporting plcopen standard and CanFestival. #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
@@ -43,7 +43,7 @@
print "\nUsage of Beremiz.py :"
print "\n %s [Projectpath] [Buildpath]\n"%sys.argv[0]
opts, args = getopt.getopt(sys.argv[1:], "hu:e:", ["help", "updatecheck=", "extend="])
except getopt.GetoptError:
@@ -52,7 +52,7 @@
if o in ("-h", "--help"):
@@ -61,7 +61,7 @@
if o in ("-e", "--extend"):
@@ -74,7 +74,7 @@
if os.path.exists("BEREMIZ_DEBUG"):
__builtin__.__dict__["BMZ_DBG"] = True
@@ -83,7 +83,7 @@
app = wx.PySimpleApp(redirect=BMZ_DBG)
app.SetAppName('beremiz')
wx.InitAllImageHandlers()
bmp = wx.Image(Bpath("images", "splash.png")).ConvertToBitmap()
#splash=AdvancedSplash(None, bitmap=bmp, style=wx.SPLASH_CENTRE_ON_SCREEN, timeout=4000)
@@ -99,8 +99,8 @@
updateinfo = urllib2.urlopen(updateinfo_url,None).read()
- updateinfo = "update info unavailable."
+ updateinfo = "update info unavailable." from threading import Thread
splash.SetText(text=updateinfo)
@@ -222,15 +222,15 @@
if style is None : style=self.black_white
if style != self.black_white:
self.output.StartStyling(self.output.GetLength(), 0xff)
# Temporary deactivate read only mode on StyledTextCtrl for
- # adding text. It seems that text modifications, even
+ # adding text. It seems that text modifications, even # programmatically, are disabled in StyledTextCtrl when read
self.output.SetReadOnly(False)
self.output.AppendText(s)
self.output.SetReadOnly(True)
if style != self.black_white:
self.output.SetStyling(len(s), style)
@@ -245,7 +245,7 @@
if newtime - self.rising_timer > 1:
self.risecall(self.output)
self.rising_timer = newtime
def write_warning(self, s):
self.write(s,self.red_white)
@@ -259,11 +259,11 @@
# Temporary deactivate read only mode on StyledTextCtrl for clearing
# text. It seems that text modifications, even programmatically, are
- # disabled in StyledTextCtrl when read only is active
+ # disabled in StyledTextCtrl when read only is active self.output.SetReadOnly(False)
self.output.SetReadOnly(True)
@@ -284,13 +284,13 @@
from util.BitmapLibrary import GetBitmap
self.ConfNodeMenu = wx.Menu(title='')
self.RecentProjectsMenu = wx.Menu(title='')
IDEFrame._init_utils(self)
def _init_coll_FileMenu_Items(self, parent):
AppendMenu(parent, help='', id=wx.ID_NEW,
kind=wx.ITEM_NORMAL, text=_(u'New') + '\tCTRL+N')
@@ -316,7 +316,7 @@
AppendMenu(parent, help='', id=wx.ID_EXIT,
kind=wx.ITEM_NORMAL, text=_(u'Quit') + '\tCTRL+Q')
self.Bind(wx.EVT_MENU, self.OnNewProjectMenu, id=wx.ID_NEW)
self.Bind(wx.EVT_MENU, self.OnOpenProjectMenu, id=wx.ID_OPEN)
self.Bind(wx.EVT_MENU, self.OnSaveProjectMenu, id=wx.ID_SAVE)
@@ -327,13 +327,13 @@
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.OnQuitMenu, id=wx.ID_EXIT)
self.AddToMenuToolBar([(wx.ID_NEW, "new", _(u'New'), None),
(wx.ID_OPEN, "open", _(u'Open'), None),
(wx.ID_SAVE, "save", _(u'Save'), None),
(wx.ID_SAVEAS, "saveas", _(u'Save As...'), None),
(wx.ID_PRINT, "print", _(u'Print'), None)])
def _RecursiveAddMenuItems(self, menu, items):
for name, text, help, children in items:
@@ -342,20 +342,20 @@
menu.AppendMenu(new_id, text, new_menu)
self._RecursiveAddMenuItems(new_menu, children)
- AppendMenu(menu, help=help, id=new_id,
+ AppendMenu(menu, help=help, id=new_id, kind=wx.ITEM_NORMAL, text=text)
- self.Bind(wx.EVT_MENU, self.GetAddConfNodeFunction(name),
+ self.Bind(wx.EVT_MENU, self.GetAddConfNodeFunction(name), def _init_coll_AddMenu_Items(self, parent):
IDEFrame._init_coll_AddMenu_Items(self, parent, False)
self._RecursiveAddMenuItems(parent, GetAddMenuItems())
def _init_coll_HelpMenu_Items(self, parent):
parent.Append(help='', id=wx.ID_ABOUT,
kind=wx.ITEM_NORMAL, text=_(u'About'))
self.Bind(wx.EVT_MENU, self.OnAboutMenu, id=wx.ID_ABOUT)
def _init_coll_ConnectionStatusBar_Fields(self, parent):
@@ -364,12 +364,12 @@
parent.SetStatusText(number=2, text='')
parent.SetStatusWidths([-1, 300, 200])
def _init_ctrls(self, prnt):
IDEFrame._init_ctrls(self, prnt)
self.EditMenuSize = self.EditMenu.GetMenuItemCount()
self.Bind(wx.EVT_MENU, self.OnOpenWidgetInspector, id=inspectorID)
accels = [wx.AcceleratorEntry(wx.ACCEL_CTRL|wx.ACCEL_ALT, ord('I'), inspectorID)]
@@ -387,9 +387,9 @@
self.Bind(wx.EVT_MENU, OnMethodGen(self,method), id=newid)
accels += [wx.AcceleratorEntry(wx.ACCEL_NORMAL, shortcut,newid)]
self.SetAcceleratorTable(wx.AcceleratorTable(accels))
self.LogConsole = CustomStyledTextCtrl(
name='LogConsole', parent=self.BottomNoteBook, pos=wx.Point(0, 0),
@@ -398,32 +398,32 @@
self.LogConsole.Bind(wx.stc.EVT_STC_UPDATEUI, self.OnLogConsoleUpdateUI)
self.LogConsole.SetReadOnly(True)
self.LogConsole.SetWrapMode(wx.stc.STC_WRAP_CHAR)
# Define Log Console styles
self.LogConsole.StyleSetSpec(wx.stc.STC_STYLE_DEFAULT, "face:%(mono)s,size:%(size)d" % faces)
self.LogConsole.StyleClearAll()
self.LogConsole.StyleSetSpec(1, "face:%(mono)s,fore:#FF0000,size:%(size)d" % faces)
self.LogConsole.StyleSetSpec(2, "face:%(mono)s,fore:#FF0000,back:#FFFF00,size:%(size)d" % faces)
# Define Log Console markers
self.LogConsole.SetMarginSensitive(1, True)
self.LogConsole.SetMarginType(1, wx.stc.STC_MARGIN_SYMBOL)
self.LogConsole.MarkerDefine(0, wx.stc.STC_MARK_CIRCLE, "BLACK", "RED")
self.LogConsole.SetModEventMask(wx.stc.STC_MOD_INSERTTEXT)
self.LogConsole.Bind(wx.stc.EVT_STC_MARGINCLICK, self.OnLogConsoleMarginClick)
self.LogConsole.Bind(wx.stc.EVT_STC_MODIFIED, self.OnLogConsoleModified)
self.MainTabs["LogConsole"] = (self.LogConsole, _("Console"))
self.BottomNoteBook.AddPage(*self.MainTabs["LogConsole"])
#self.BottomNoteBook.Split(self.BottomNoteBook.GetPageIndex(self.LogConsole), wx.RIGHT)
self.LogViewer = LogViewer(self.BottomNoteBook, self)
self.MainTabs["LogViewer"] = (self.LogViewer, _("PLC Log"))
self.BottomNoteBook.AddPage(*self.MainTabs["LogViewer"])
#self.BottomNoteBook.Split(self.BottomNoteBook.GetPageIndex(self.LogViewer), wx.RIGHT)
StatusToolBar = wx.ToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize,
wx.TB_FLAT | wx.TB_NODIVIDER | wx.NO_BORDER)
StatusToolBar.SetToolBitmapSize(wx.Size(25, 25))
@@ -433,27 +433,27 @@
Name("StatusToolBar").Caption(_("Status ToolBar")).
ToolbarPane().Top().Position(1).
LeftDockable(False).RightDockable(False))
self.ConnectionStatusBar = wx.StatusBar(self, style=wx.ST_SIZEGRIP)
self._init_coll_ConnectionStatusBar_Fields(self.ConnectionStatusBar)
self.SetStatusBar(self.ConnectionStatusBar)
def __init__(self, parent, projectOpen=None, buildpath=None, ctr=None, debug=True):
IDEFrame.__init__(self, parent, debug)
self.Log = LogPseudoFile(self.LogConsole,self.SelectTab)
self.local_runtime = None
self.local_runtime_tmpdir = None
self.LastPanelSelected = None
# Define Tree item icon list
self.LocationImageList = wx.ImageList(16, 16)
self.LocationImageDict = {}
# Icons for location items
for imgname, itemtype in [
("CONFIGURATION", LOCATION_CONFNODE),
@@ -463,18 +463,18 @@
("VAR_OUTPUT", LOCATION_VAR_OUTPUT),
("VAR_LOCAL", LOCATION_VAR_MEMORY)]:
self.LocationImageDict[itemtype] = self.LocationImageList.Add(GetBitmap(imgname))
for imgname, itemtype in [
("Extension", ITEM_CONFNODE)]:
self.TreeImageDict[itemtype] = self.TreeImageList.Add(GetBitmap(imgname))
# Add beremiz's icon in top left corner of the frame
self.SetIcon(wx.Icon(Bpath("images", "brz.ico"), wx.BITMAP_TYPE_ICO))
if projectOpen is not None:
projectOpen = DecodeFileSystemPath(projectOpen, False)
if projectOpen is not None and os.path.isdir(projectOpen):
self.CTR = ProjectController(self, self.Log)
self.Controler = self.CTR
@@ -498,9 +498,9 @@
self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE)
self.DebugVariablePanel.SetDataProducer(self.CTR)
self.Bind(wx.EVT_CLOSE, self.OnCloseFrame)
self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU)
self.LogConsole.SetFocus()
@@ -534,14 +534,14 @@
cwd = self.local_runtime_tmpdir)
self.local_runtime.spin()
def KillLocalRuntime(self):
if self.local_runtime is not None:
self.local_runtime.kill(gently=False)
shutil.rmtree(self.local_runtime_tmpdir)
self.local_runtime = None
def OnOpenWidgetInspector(self, evt):
@@ -569,7 +569,7 @@
line_idx = self.LogConsole.LineFromPosition(event.GetPosition())
wx.CallAfter(self.SearchLineForError, self.LogConsole.GetLine(line_idx))
def OnLogConsoleModified(self, event):
line_idx = self.LogConsole.LineFromPosition(event.GetPosition())
line = self.LogConsole.GetLine(line_idx)
@@ -585,9 +585,9 @@
first_line, first_column, last_line, last_column, error = result.groups()
infos = self.CTR.ShowError(self.Log,
- (int(first_line), int(first_column)),
+ (int(first_line), int(first_column)), (int(last_line), int(last_column)))
## Function displaying an Error dialog in PLCOpenEditor.
# @return False if closing cancelled.
def CheckSaveBeforeClosing(self, title=_("Close Project")):
@@ -602,28 +602,28 @@
elif answer == wx.ID_CANCEL:
for idx in xrange(self.TabsOpened.GetPageCount()):
window = self.TabsOpened.GetPage(idx)
if not window.CheckSaveBeforeClosing():
def GetTabInfos(self, tab):
- if (isinstance(tab, EditorPanel) and
- not isinstance(tab, (Viewer,
+ if (isinstance(tab, EditorPanel) and + not isinstance(tab, (Viewer, return ("confnode", tab.Controler.CTNFullName(), tab.GetTagName())
- elif (isinstance(tab, TextViewer) and
+ elif (isinstance(tab, TextViewer) and (tab.Controler is None or isinstance(tab.Controler, MiniTextControler))):
return ("confnode", None, tab.GetInstancePath())
return IDEFrame.GetTabInfos(self, tab)
def LoadTab(self, notebook, page_infos):
if page_infos[0] == "confnode":
if page_infos[1] is None:
@@ -633,26 +633,26 @@
return notebook.GetPageIndex(confnode._OpenView(*page_infos[2:]))
return IDEFrame.LoadTab(self, notebook, page_infos)
def OnCloseFrame(self, event):
- for evt_type in [wx.EVT_SET_FOCUS,
+ for evt_type in [wx.EVT_SET_FOCUS, wx.stc.EVT_STC_UPDATEUI]:
self.LogConsole.Unbind(evt_type)
if self.CTR is None or self.CheckSaveBeforeClosing(_("Close Application")):
self.CTR.KillDebugThread()
def RefreshFileMenu(self):
self.RefreshRecentProjectsMenu()
MenuToolBar = self.Panes["MenuToolBar"]
selected = self.TabsOpened.GetSelection()
@@ -695,17 +695,17 @@
self.FileMenu.Enable(wx.ID_SAVEAS, False)
MenuToolBar.EnableTool(wx.ID_SAVEAS, False)
self.FileMenu.Enable(wx.ID_CLOSE_ALL, False)
def RefreshRecentProjectsMenu(self):
- recent_projects = map(DecodeFileSystemPath,
+ recent_projects = map(DecodeFileSystemPath, self.GetConfigEntry("RecentProjects", []))
self.FileMenu.Enable(ID_FILEMENURECENTPROJECTS, len(recent_projects) > 0)
for idx, projectpath in enumerate(recent_projects):
text = u'%d: %s' % (idx + 1, projectpath)
if idx < self.RecentProjectsMenu.GetMenuItemCount():
item = self.RecentProjectsMenu.FindItemByPosition(idx)
@@ -713,18 +713,18 @@
self.Disconnect(id, id, wx.EVT_BUTTON._getEvtType())
- AppendMenu(self.RecentProjectsMenu, help='', id=id,
+ AppendMenu(self.RecentProjectsMenu, help='', id=id, kind=wx.ITEM_NORMAL, text=text)
self.Bind(wx.EVT_MENU, self.GenerateOpenRecentProjectFunction(projectpath), id=id)
def GenerateOpenRecentProjectFunction(self, projectpath):
def OpenRecentProject(event):
if self.CTR is not None and not self.CheckSaveBeforeClosing():
self.OpenProject(projectpath)
def GenerateMenuRecursive(self, items, menu):
for kind, infos in items:
if isinstance(kind, ListType):
@@ -739,27 +739,27 @@
AppendMenu(menu, help='', id=id, kind=kind, text=text)
self.Bind(wx.EVT_MENU, callback, id=id)
def RefreshEditorToolBar(self):
IDEFrame.RefreshEditorToolBar(self)
self.AUIManager.GetPane("EditorToolBar").Position(2)
self.AUIManager.GetPane("StatusToolBar").Position(1)
def RefreshStatusToolBar(self):
StatusToolBar = self.Panes["StatusToolBar"]
StatusToolBar.ClearTools()
for confnode_method in self.CTR.StatusMethods:
if "method" in confnode_method and confnode_method.get("shown",True):
- StatusToolBar.AddSimpleTool(id,
- GetBitmap(confnode_method.get("bitmap", "Unknown")),
+ StatusToolBar.AddSimpleTool(id, + GetBitmap(confnode_method.get("bitmap", "Unknown")), confnode_method["tooltip"])
self.Bind(wx.EVT_MENU, self.GetMenuCallBackFunction(confnode_method["method"]), id=id)
self.AUIManager.GetPane("StatusToolBar").BestSize(StatusToolBar.GetBestSize()).Show()
@@ -767,13 +767,13 @@
self.AUIManager.GetPane("EditorToolBar").Position(2)
self.AUIManager.GetPane("StatusToolBar").Position(1)
def RefreshEditMenu(self):
IDEFrame.RefreshEditMenu(self)
if self.FindFocus() == self.LogConsole:
self.EditMenu.Enable(wx.ID_COPY, True)
self.Panes["MenuToolBar"].EnableTool(wx.ID_COPY, True)
selected = self.TabsOpened.GetSelection()
@@ -808,28 +808,28 @@
self.EditMenu.Delete(item.GetId())
self.LastPanelSelected = None
self.MenuBar.UpdateMenus()
self.RefreshStatusToolBar()
def GetMenuCallBackFunction(self, method):
""" Generate the callbackfunc for a given CTR method"""
- # Disable button to prevent re-entrant call
+ # Disable button to prevent re-entrant call event.GetEventObject().Disable()
getattr(self.CTR, method)()
event.GetEventObject().Enable()
def GetConfigEntry(self, entry_name, default):
return cPickle.loads(str(self.Config.Read(entry_name, cPickle.dumps(default))))
def ResetConnectionStatusBar(self):
for field in xrange(self.ConnectionStatusBar.GetFieldsCount()):
self.ConnectionStatusBar.SetStatusText('', field)
@@ -840,10 +840,10 @@
self.DebugVariablePanel.SetDataProducer(None)
self.ResetConnectionStatusBar()
def RefreshConfigRecentProjects(self, projectpath):
- recent_projects = map(DecodeFileSystemPath,
+ recent_projects = map(DecodeFileSystemPath, self.GetConfigEntry("RecentProjects", []))
@@ -853,24 +853,24 @@
self.Config.Write("RecentProjects", cPickle.dumps(
map(EncodeFileSystemPath, recent_projects[:MAX_RECENT_PROJECTS])))
def ResetPerspective(self):
IDEFrame.ResetPerspective(self)
self.RefreshStatusToolBar()
def OnNewProjectMenu(self, event):
if self.CTR is not None and not self.CheckSaveBeforeClosing():
defaultpath = DecodeFileSystemPath(self.Config.Read("lastopenedfolder"))
defaultpath = os.path.expanduser("~")
dialog = wx.DirDialog(self , _("Choose a project"), defaultpath)
if dialog.ShowModal() == wx.ID_OK:
projectpath = dialog.GetPath()
- self.Config.Write("lastopenedfolder",
+ self.Config.Write("lastopenedfolder", EncodeFileSystemPath(os.path.dirname(projectpath)))
@@ -892,25 +892,25 @@
self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU)
def OnOpenProjectMenu(self, event):
if self.CTR is not None and not self.CheckSaveBeforeClosing():
defaultpath = DecodeFileSystemPath(self.Config.Read("lastopenedfolder"))
defaultpath = os.path.expanduser("~")
dialog = wx.DirDialog(self , _("Choose a project"), defaultpath, style=wx.DEFAULT_DIALOG_STYLE|
if dialog.ShowModal() == wx.ID_OK:
self.OpenProject(dialog.GetPath())
def OpenProject(self, projectpath):
if os.path.isdir(projectpath):
- self.Config.Write("lastopenedfolder",
+ self.Config.Write("lastopenedfolder", EncodeFileSystemPath(os.path.dirname(projectpath)))
@@ -932,15 +932,15 @@
self.ShowErrorMessage(_("\"%s\" folder is not a valid Beremiz project\n") % projectpath)
self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU)
def OnCloseProjectMenu(self, event):
if self.CTR is not None and not self.CheckSaveBeforeClosing():
self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU)
def OnSaveProjectMenu(self, event):
selected = self.TabsOpened.GetSelection()
@@ -950,7 +950,7 @@
self._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES)
def OnSaveProjectAsMenu(self, event):
selected = self.TabsOpened.GetSelection()
@@ -960,27 +960,27 @@
self._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES)
def OnQuitMenu(self, event):
def OnAboutMenu(self, event):
OpenHtmlFrame(self,_("About Beremiz"), Bpath("doc", "about.html"), wx.Size(550, 500))
def OnProjectTreeItemBeginEdit(self, event):
selected = event.GetItem()
if self.ProjectTree.GetPyData(selected)["type"] == ITEM_CONFNODE:
IDEFrame.OnProjectTreeItemBeginEdit(self, event)
def OnProjectTreeRightUp(self, event):
item_infos = self.ProjectTree.GetPyData(item)
if item_infos["type"] == ITEM_CONFNODE:
confnode_menu = wx.Menu(title='')
confnode = item_infos["confnode"]
menu_items = confnode.GetContextualMenuItems()
@@ -998,10 +998,10 @@
AppendMenu(confnode_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Delete"))
self.Bind(wx.EVT_MENU, self.GetDeleteMenuFunction(confnode), id=new_id)
self.PopupMenu(confnode_menu)
elif item_infos["type"] == ITEM_RESOURCE:
# prevent last resource to be delted
@@ -1011,7 +1011,7 @@
IDEFrame.OnProjectTreeRightUp(self, event)
IDEFrame.OnProjectTreeRightUp(self, event)
def OnProjectTreeItemActivated(self, event):
selected = event.GetItem()
name = self.ProjectTree.GetItemText(selected)
@@ -1023,7 +1023,7 @@
IDEFrame.OnProjectTreeItemActivated(self, event)
def ProjectTreeItemSelect(self, select_item):
if select_item is not None and select_item.IsOk():
name = self.ProjectTree.GetItemText(select_item)
@@ -1034,7 +1034,7 @@
self.CTR._OpenView(onlyopened=True)
IDEFrame.ProjectTreeItemSelect(self, select_item)
def SelectProjectTreeItem(self, tagname):
if self.ProjectTree is not None:
root = self.ProjectTree.GetRootItem()
@@ -1046,31 +1046,23 @@
self.ProjectTree.SelectItem(root)
- return self.RecursiveProjectTreeItemSelection(root,
+ return self.RecursiveProjectTreeItemSelection(root, [(word, ITEM_CONFNODE) for word in tagname.split(".")])
return self.RecursiveProjectTreeItemSelection(root, [(words[2], ITEM_RESOURCE)])
elif not os.path.exists(words[0]):
IDEFrame.SelectProjectTreeItem(self, tagname)
def GetAddConfNodeFunction(self, name, confnode=None):
def AddConfNodeMenuFunction(event):
wx.CallAfter(self.AddConfNode, name, confnode)
return AddConfNodeMenuFunction
def GetDeleteMenuFunction(self, confnode):
def DeleteMenuFunction(event):
wx.CallAfter(self.DeleteConfNode, confnode)
return DeleteMenuFunction
- def AddResourceMenu(self, event):
- config_names = self.CTR.GetProjectConfigNames()
- if len(config_names) > 0:
- tagname = self.Controler.ProjectAddConfigurationResource(config_names[0])
- if tagname is not None:
- self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL)
- self.EditProjectElement(ITEM_RESOURCE, tagname)
def AddConfNode(self, ConfNodeType, confnode=None):
if self.CTR.CheckProjectPathPerm():
ConfNodeName = "%s_0" % ConfNodeType
@@ -1079,12 +1071,12 @@
self.CTR.CTNAddChild(ConfNodeName, ConfNodeType)
self._Refresh(TITLE, FILEMENU, PROJECTTREE)
def DeleteConfNode(self, confnode):
if self.CTR.CheckProjectPathPerm():
- dialog = wx.MessageDialog(self,
- _("Really delete node '%s'?") % confnode.CTNName(),
- _("Remove %s node") % confnode.CTNType,
+ dialog = wx.MessageDialog(self, + _("Really delete node '%s'?") % confnode.CTNName(), + _("Remove %s node") % confnode.CTNType, if dialog.ShowModal() == wx.ID_YES:
@@ -1124,13 +1116,13 @@
trcbck += "file : " + str(line[0][len(os.getcwd()):]) + ", "
trcbck += "line : " + str(line[1]) + ", " + "function : " + str(line[2])
trcbck_lst.append(trcbck)
cap = wx.Window_GetCapture()
- dlg = wx.SingleChoiceDialog(None,
+ dlg = wx.SingleChoiceDialog(None, An unhandled exception (bug) occured. Bug report saved at :
@@ -1142,7 +1134,7 @@
- repr(e_type) + " : " + repr(e_value),
+ repr(e_type) + " : " + repr(e_value), @@ -1165,7 +1157,7 @@
ignored_exceptions = [] # a problem with a line in a module is only reported once per session
def AddExceptHook(path, app_version='[No version]'):#, ignored_exceptions=[]):
def handle_exception(e_type, e_value, e_traceback):
traceback.print_exception(e_type, e_value, e_traceback) # this is very helpful when there's an exception in the rest of this func
last_tb = get_last_traceback(e_traceback)
@@ -1198,7 +1190,7 @@
info['self'] = format_namespace(exception_locals['self'].__dict__)
output = open(bug_report_path,'w')
@@ -1225,7 +1217,7 @@
if __name__ == '__main__':
# Install a exception handle for bug reports
AddExceptHook(os.getcwd(),updateinfo_url)
frame = Beremiz(None, projectOpen, buildpath)
--- a/IDEFrame.py Mon May 26 14:44:03 2014 +0100
+++ b/IDEFrame.py Fri Jun 06 18:30:49 2014 +0100
@@ -20,27 +20,27 @@
from util.BitmapLibrary import GetBitmap
# Define PLCOpenEditor controls id
-[ID_PLCOPENEDITOR, ID_PLCOPENEDITORLEFTNOTEBOOK,
- ID_PLCOPENEDITORBOTTOMNOTEBOOK, ID_PLCOPENEDITORRIGHTNOTEBOOK,
- ID_PLCOPENEDITORPROJECTTREE, ID_PLCOPENEDITORMAINSPLITTER,
- ID_PLCOPENEDITORSECONDSPLITTER, ID_PLCOPENEDITORTHIRDSPLITTER,
- ID_PLCOPENEDITORLIBRARYPANEL, ID_PLCOPENEDITORLIBRARYSEARCHCTRL,
- ID_PLCOPENEDITORLIBRARYTREE, ID_PLCOPENEDITORLIBRARYCOMMENT,
- ID_PLCOPENEDITORTABSOPENED, ID_PLCOPENEDITORTABSOPENED,
- ID_PLCOPENEDITOREDITORMENUTOOLBAR, ID_PLCOPENEDITOREDITORTOOLBAR,
- ID_PLCOPENEDITORPROJECTPANEL,
+[ID_PLCOPENEDITOR, ID_PLCOPENEDITORLEFTNOTEBOOK, + ID_PLCOPENEDITORBOTTOMNOTEBOOK, ID_PLCOPENEDITORRIGHTNOTEBOOK, + ID_PLCOPENEDITORPROJECTTREE, ID_PLCOPENEDITORMAINSPLITTER, + ID_PLCOPENEDITORSECONDSPLITTER, ID_PLCOPENEDITORTHIRDSPLITTER, + ID_PLCOPENEDITORLIBRARYPANEL, ID_PLCOPENEDITORLIBRARYSEARCHCTRL, + ID_PLCOPENEDITORLIBRARYTREE, ID_PLCOPENEDITORLIBRARYCOMMENT, + ID_PLCOPENEDITORTABSOPENED, ID_PLCOPENEDITORTABSOPENED, + ID_PLCOPENEDITOREDITORMENUTOOLBAR, ID_PLCOPENEDITOREDITORTOOLBAR, + ID_PLCOPENEDITORPROJECTPANEL, ] = [wx.NewId() for _init_ctrls in range(17)]
# Define PLCOpenEditor EditMenu extra items id
-[ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, ID_PLCOPENEDITOREDITMENUADDDATATYPE,
- ID_PLCOPENEDITOREDITMENUADDFUNCTION, ID_PLCOPENEDITOREDITMENUADDFUNCTIONBLOCK,
+[ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, ID_PLCOPENEDITOREDITMENUADDDATATYPE, + ID_PLCOPENEDITOREDITMENUADDFUNCTION, ID_PLCOPENEDITOREDITMENUADDFUNCTIONBLOCK, ID_PLCOPENEDITOREDITMENUADDPROGRAM, ID_PLCOPENEDITOREDITMENUADDCONFIGURATION,
ID_PLCOPENEDITOREDITMENUFINDNEXT, ID_PLCOPENEDITOREDITMENUFINDPREVIOUS,
ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, ID_PLCOPENEDITOREDITMENUADDRESOURCE
] = [wx.NewId() for _init_coll_EditMenu_Items in range(10)]
# Define PLCOpenEditor DisplayMenu extra items id
-[ID_PLCOPENEDITORDISPLAYMENURESETPERSPECTIVE,
+[ID_PLCOPENEDITORDISPLAYMENURESETPERSPECTIVE, ] = [wx.NewId() for _init_coll_DisplayMenu_Items in range(1)]
#-------------------------------------------------------------------------------
@@ -56,12 +56,12 @@
ID_PLCOPENEDITOREDITORTOOLBARBRANCH, ID_PLCOPENEDITOREDITORTOOLBARINITIALSTEP,
ID_PLCOPENEDITOREDITORTOOLBARSTEP, ID_PLCOPENEDITOREDITORTOOLBARTRANSITION,
ID_PLCOPENEDITOREDITORTOOLBARACTIONBLOCK, ID_PLCOPENEDITOREDITORTOOLBARDIVERGENCE,
- ID_PLCOPENEDITOREDITORTOOLBARJUMP, ID_PLCOPENEDITOREDITORTOOLBARMOTION,
+ ID_PLCOPENEDITOREDITORTOOLBARJUMP, ID_PLCOPENEDITOREDITORTOOLBARMOTION, ] = [wx.NewId() for _init_coll_DefaultEditorToolBar_Items in range(18)]
-# Define behaviour of each Toolbar item according to current POU body type
+# Define behaviour of each Toolbar item according to current POU body type # Informations meaning are in this order:
# - PLCOpenEditor mode where item is displayed (could be more then one)
@@ -82,77 +82,77 @@
(True, FREEDRAWING_MODE|DRIVENDRAWING_MODE,
ID_PLCOPENEDITOREDITORTOOLBARBLOCK, "OnBlockTool",
"add_block", _("Create a new block")),
- (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE,
- ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, "OnConnectionTool",
+ (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, "OnConnectionTool", "add_connection", _("Create a new connection"))],
"LD" : [(True, FREEDRAWING_MODE|DRIVENDRAWING_MODE,
ID_PLCOPENEDITOREDITORTOOLBARMOTION, "OnMotionTool",
"move", _("Move the view")),
- (True, FREEDRAWING_MODE,
- ID_PLCOPENEDITOREDITORTOOLBARCOMMENT, "OnCommentTool",
+ (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCOMMENT, "OnCommentTool", "add_comment", _("Create a new comment")),
- (True, FREEDRAWING_MODE,
- ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL, "OnPowerRailTool",
+ (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL, "OnPowerRailTool", "add_powerrail", _("Create a new power rail")),
- (False, DRIVENDRAWING_MODE,
- ID_PLCOPENEDITOREDITORTOOLBARRUNG, "OnRungTool",
+ (False, DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARRUNG, "OnRungTool", "add_rung", _("Create a new rung")),
- (True, FREEDRAWING_MODE,
- ID_PLCOPENEDITOREDITORTOOLBARCOIL, "OnCoilTool",
+ (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCOIL, "OnCoilTool", "add_coil", _("Create a new coil")),
- (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE,
- ID_PLCOPENEDITOREDITORTOOLBARCONTACT, "OnContactTool",
+ (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCONTACT, "OnContactTool", "add_contact", _("Create a new contact")),
- (False, DRIVENDRAWING_MODE,
- ID_PLCOPENEDITOREDITORTOOLBARBRANCH, "OnBranchTool",
+ (False, DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARBRANCH, "OnBranchTool", "add_branch", _("Create a new branch")),
- (True, FREEDRAWING_MODE,
- ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, "OnVariableTool",
+ (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, "OnVariableTool", "add_variable", _("Create a new variable")),
- (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE,
- ID_PLCOPENEDITOREDITORTOOLBARBLOCK, "OnBlockTool",
+ (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARBLOCK, "OnBlockTool", "add_block", _("Create a new block")),
- (True, FREEDRAWING_MODE,
- ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, "OnConnectionTool",
+ (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, "OnConnectionTool", "add_connection", _("Create a new connection"))],
"SFC" : [(True, FREEDRAWING_MODE|DRIVENDRAWING_MODE,
ID_PLCOPENEDITOREDITORTOOLBARMOTION, "OnMotionTool",
"move", _("Move the view")),
- (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE,
- ID_PLCOPENEDITOREDITORTOOLBARCOMMENT, "OnCommentTool",
+ (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCOMMENT, "OnCommentTool", "add_comment", _("Create a new comment")),
- (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE,
- ID_PLCOPENEDITOREDITORTOOLBARINITIALSTEP, "OnInitialStepTool",
+ (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARINITIALSTEP, "OnInitialStepTool", "add_initial_step", _("Create a new initial step")),
- (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE,
- ID_PLCOPENEDITOREDITORTOOLBARSTEP, "OnStepTool",
+ (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARSTEP, "OnStepTool", "add_step", _("Create a new step")),
- (True, FREEDRAWING_MODE,
- ID_PLCOPENEDITOREDITORTOOLBARTRANSITION, "OnTransitionTool",
+ (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARTRANSITION, "OnTransitionTool", "add_transition", _("Create a new transition")),
- (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE,
- ID_PLCOPENEDITOREDITORTOOLBARACTIONBLOCK, "OnActionBlockTool",
+ (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARACTIONBLOCK, "OnActionBlockTool", "add_action", _("Create a new action block")),
- (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE,
- ID_PLCOPENEDITOREDITORTOOLBARDIVERGENCE, "OnDivergenceTool",
+ (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARDIVERGENCE, "OnDivergenceTool", "add_divergence", _("Create a new divergence")),
- (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE,
- ID_PLCOPENEDITOREDITORTOOLBARJUMP, "OnJumpTool",
+ (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARJUMP, "OnJumpTool", "add_jump", _("Create a new jump")),
- (True, FREEDRAWING_MODE,
- ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, "OnVariableTool",
+ (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, "OnVariableTool", "add_variable", _("Create a new variable")),
- (True, FREEDRAWING_MODE,
- ID_PLCOPENEDITOREDITORTOOLBARBLOCK, "OnBlockTool",
+ (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARBLOCK, "OnBlockTool", "add_block", _("Create a new block")),
- (True, FREEDRAWING_MODE,
- ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, "OnConnectionTool",
+ (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, "OnConnectionTool", "add_connection", _("Create a new connection")),
- (True, FREEDRAWING_MODE,
- ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL, "OnPowerRailTool",
+ (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL, "OnPowerRailTool", "add_powerrail", _("Create a new power rail")),
- (True, FREEDRAWING_MODE,
- ID_PLCOPENEDITOREDITORTOOLBARCONTACT, "OnContactTool",
+ (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCONTACT, "OnContactTool", "add_contact", _("Create a new contact"))],
@@ -185,7 +185,7 @@
parent.Append(helpString=help, id=id, kind=kind, item=text)
-[TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE,
+[TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE, SCALING, PAGETITLES
@@ -225,33 +225,33 @@
others = [t for t in tabs if t != tab]
others.sort(lambda x,y: cmp(x["pos"][0], y["pos"][0]))
- if (other["pos"][1] == tab["pos"][1] and
+ if (other["pos"][1] == tab["pos"][1] and other["size"][1] == tab["size"][1] and
other["pos"][0] == tab["pos"][0] + tab["size"][0] + TAB_BORDER):
tab["size"] = (tab["size"][0] + other["size"][0] + TAB_BORDER, tab["size"][1])
tab["pages"].extend(other["pages"])
if tab["size"][0] == rect.width:
elif tab["pos"][1] == rect.y:
others = [t for t in tabs if t != tab]
others.sort(lambda x,y: cmp(x["pos"][1], y["pos"][1]))
- if (other["pos"][0] == tab["pos"][0] and
+ if (other["pos"][0] == tab["pos"][0] and other["size"][0] == tab["size"][0] and
other["pos"][1] == tab["pos"][1] + tab["size"][1] + TAB_BORDER):
tab["size"] = (tab["size"][0], tab["size"][1] + other["size"][1] + TAB_BORDER)
tab["pages"].extend(other["pages"])
if tab["size"][1] == rect.height:
def ComputeTabsLayout(tabs, rect):
@@ -264,27 +264,27 @@
if tab["size"][0] == rect.width:
if tab["pos"][1] == rect.y:
split = (wx.TOP, float(tab["size"][1]) / float(rect.height))
- split_rect = wx.Rect(rect.x, rect.y + tab["size"][1] + TAB_BORDER,
+ split_rect = wx.Rect(rect.x, rect.y + tab["size"][1] + TAB_BORDER, rect.width, rect.height - tab["size"][1] - TAB_BORDER)
elif tab["pos"][1] == rect.height + 1 - tab["size"][1]:
split = (wx.BOTTOM, 1.0 - float(tab["size"][1]) / float(rect.height))
- split_rect = wx.Rect(rect.x, rect.y,
+ split_rect = wx.Rect(rect.x, rect.y, rect.width, rect.height - tab["size"][1] - TAB_BORDER)
elif tab["size"][1] == rect.height:
if tab["pos"][0] == rect.x:
split = (wx.LEFT, float(tab["size"][0]) / float(rect.width))
- split_rect = wx.Rect(rect.x + tab["size"][0] + TAB_BORDER, rect.y,
+ split_rect = wx.Rect(rect.x + tab["size"][0] + TAB_BORDER, rect.y, rect.width - tab["size"][0] - TAB_BORDER, rect.height)
elif tab["pos"][0] == rect.width + 1 - tab["size"][0]:
split = (wx.RIGHT, 1.0 - float(tab["size"][0]) / float(rect.width))
- split_rect = wx.Rect(rect.x, rect.y,
+ split_rect = wx.Rect(rect.x, rect.y, rect.width - tab["size"][0] - TAB_BORDER, rect.height)
split_tab = tabs.pop(idx)
"others": ComputeTabsLayout(tabs, split_rect)}
if SimplifyTabLayout(tabs, rect):
@@ -298,7 +298,7 @@
UNEDITABLE_NAMES_DICT = dict([(_(name), name) for name in UNEDITABLE_NAMES])
class IDEFrame(wx.Frame):
# Compatibility function for wx versions < 2.6
if wx.VERSION < (2, 6, 0):
def Bind(self, event, function, id = None):
@@ -306,7 +306,7 @@
event(self, id, function)
def _init_coll_MenuBar_Menus(self, parent):
parent.Append(menu=self.FileMenu, title=_(u'&File'))
parent.Append(menu=self.EditMenu, title=_(u'&Edit'))
@@ -315,7 +315,7 @@
def _init_coll_FileMenu_Items(self, parent):
def _init_coll_AddMenu_Items(self, parent, add_config=True):
AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDDATATYPE,
kind=wx.ITEM_NORMAL, text=_(u'&Data Type'))
@@ -330,7 +330,7 @@
AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDCONFIGURATION,
kind=wx.ITEM_NORMAL, text=_(u'&Configuration'))
def _init_coll_EditMenu_Items(self, parent):
AppendMenu(parent, help='', id=wx.ID_UNDO,
kind=wx.ITEM_NORMAL, text=_(u'Undo') + '\tCTRL+Z')
@@ -371,13 +371,13 @@
self.Bind(wx.EVT_MENU, self.OnCopyMenu, id=wx.ID_COPY)
self.Bind(wx.EVT_MENU, self.OnPasteMenu, id=wx.ID_PASTE)
self.Bind(wx.EVT_MENU, self.OnFindMenu, id=wx.ID_FIND)
- self.Bind(wx.EVT_MENU, self.OnFindNextMenu,
+ self.Bind(wx.EVT_MENU, self.OnFindNextMenu, id=ID_PLCOPENEDITOREDITMENUFINDNEXT)
- self.Bind(wx.EVT_MENU, self.OnFindPreviousMenu,
+ self.Bind(wx.EVT_MENU, self.OnFindPreviousMenu, id=ID_PLCOPENEDITOREDITMENUFINDPREVIOUS)
- self.Bind(wx.EVT_MENU, self.OnSearchInProjectMenu,
+ self.Bind(wx.EVT_MENU, self.OnSearchInProjectMenu, id=ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT)
- self.Bind(wx.EVT_MENU, self.OnSearchInProjectMenu,
+ self.Bind(wx.EVT_MENU, self.OnSearchInProjectMenu, id=ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT)
self.Bind(wx.EVT_MENU, self.OnAddDataTypeMenu,
id=ID_PLCOPENEDITOREDITMENUADDDATATYPE)
@@ -387,13 +387,13 @@
id=ID_PLCOPENEDITOREDITMENUADDFUNCTIONBLOCK)
self.Bind(wx.EVT_MENU, self.GenerateAddPouFunction("program"),
id=ID_PLCOPENEDITOREDITMENUADDPROGRAM)
- self.Bind(wx.EVT_MENU, self.AddResourceMenu,
+ self.Bind(wx.EVT_MENU, self.AddResourceMenu, id=ID_PLCOPENEDITOREDITMENUADDRESOURCE)
self.Bind(wx.EVT_MENU, self.OnAddConfigurationMenu,
id=ID_PLCOPENEDITOREDITMENUADDCONFIGURATION)
self.Bind(wx.EVT_MENU, self.OnSelectAllMenu, id=wx.ID_SELECTALL)
self.Bind(wx.EVT_MENU, self.OnDeleteMenu, id=wx.ID_DELETE)
self.AddToMenuToolBar([(wx.ID_UNDO, "undo", _(u'Undo'), None),
(wx.ID_REDO, "redo", _(u'Redo'), None),
@@ -417,16 +417,16 @@
AppendMenu(zoommenu, help='', id=new_id,
kind=wx.ITEM_RADIO, text=str(int(round(value * 100))) + "%")
self.Bind(wx.EVT_MENU, self.GenerateZoomFunction(idx), id=new_id)
AppendMenu(parent, help='', id=ID_PLCOPENEDITORDISPLAYMENURESETPERSPECTIVE,
kind=wx.ITEM_NORMAL, text=_(u'Reset Perspective'))
self.Bind(wx.EVT_MENU, self.OnResetPerspective, id=ID_PLCOPENEDITORDISPLAYMENURESETPERSPECTIVE)
self.Bind(wx.EVT_MENU, self.OnRefreshMenu, id=wx.ID_REFRESH)
self.Bind(wx.EVT_MENU, self.OnClearErrorsMenu, id=wx.ID_CLEAR)
def _init_coll_HelpMenu_Items(self, parent):
@@ -437,7 +437,7 @@
self.EditMenu = wx.Menu(title='')
self.DisplayMenu = wx.Menu(title='')
self.HelpMenu = wx.Menu(title='')
self._init_coll_MenuBar_Menus(self.MenuBar)
self._init_coll_FileMenu_Items(self.FileMenu)
self._init_coll_EditMenu_Items(self.EditMenu)
@@ -450,49 +450,49 @@
style=wx.DEFAULT_FRAME_STYLE)
self.SetClientSize(wx.Size(1000, 600))
self.Bind(wx.EVT_ACTIVATE, self.OnActivated)
self.TabsImageList = wx.ImageList(31, 16)
self.TabsImageListIndexes = {}
#-----------------------------------------------------------------------
# Creating main structure
#-----------------------------------------------------------------------
self.AUIManager = wx.aui.AuiManager(self)
self.AUIManager.SetDockSizeConstraint(0.5, 0.5)
self.LeftNoteBook = wx.aui.AuiNotebook(self, ID_PLCOPENEDITORLEFTNOTEBOOK,
style=wx.aui.AUI_NB_TOP|wx.aui.AUI_NB_TAB_SPLIT|wx.aui.AUI_NB_TAB_MOVE|
wx.aui.AUI_NB_SCROLL_BUTTONS|wx.aui.AUI_NB_TAB_EXTERNAL_MOVE)
- self.LeftNoteBook.Bind(wx.aui.EVT_AUINOTEBOOK_ALLOW_DND,
+ self.LeftNoteBook.Bind(wx.aui.EVT_AUINOTEBOOK_ALLOW_DND, - self.AUIManager.AddPane(self.LeftNoteBook,
+ self.AUIManager.AddPane(self.LeftNoteBook, wx.aui.AuiPaneInfo().Name("ProjectPane").
BestSize(wx.Size(300, 500)).CloseButton(False))
self.BottomNoteBook = wx.aui.AuiNotebook(self, ID_PLCOPENEDITORBOTTOMNOTEBOOK,
style=wx.aui.AUI_NB_TOP|wx.aui.AUI_NB_TAB_SPLIT|wx.aui.AUI_NB_TAB_MOVE|
wx.aui.AUI_NB_SCROLL_BUTTONS|wx.aui.AUI_NB_TAB_EXTERNAL_MOVE)
- self.BottomNoteBook.Bind(wx.aui.EVT_AUINOTEBOOK_ALLOW_DND,
+ self.BottomNoteBook.Bind(wx.aui.EVT_AUINOTEBOOK_ALLOW_DND, - self.AUIManager.AddPane(self.BottomNoteBook,
+ self.AUIManager.AddPane(self.BottomNoteBook, wx.aui.AuiPaneInfo().Name("ResultPane").
BestSize(wx.Size(800, 300)).CloseButton(False))
self.RightNoteBook = wx.aui.AuiNotebook(self, ID_PLCOPENEDITORRIGHTNOTEBOOK,
style=wx.aui.AUI_NB_TOP|wx.aui.AUI_NB_TAB_SPLIT|wx.aui.AUI_NB_TAB_MOVE|
wx.aui.AUI_NB_SCROLL_BUTTONS|wx.aui.AUI_NB_TAB_EXTERNAL_MOVE)
- self.RightNoteBook.Bind(wx.aui.EVT_AUINOTEBOOK_ALLOW_DND,
+ self.RightNoteBook.Bind(wx.aui.EVT_AUINOTEBOOK_ALLOW_DND, - self.AUIManager.AddPane(self.RightNoteBook,
+ self.AUIManager.AddPane(self.RightNoteBook, wx.aui.AuiPaneInfo().Name("LibraryPane").
BestSize(wx.Size(250, 400)).CloseButton(False))
- self.TabsOpened = wx.aui.AuiNotebook(self, ID_PLCOPENEDITORTABSOPENED,
+ self.TabsOpened = wx.aui.AuiNotebook(self, ID_PLCOPENEDITORTABSOPENED, style=wx.aui.AUI_NB_DEFAULT_STYLE|wx.aui.AUI_NB_WINDOWLIST_BUTTON)
self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGING,
self.OnPouSelectedChanging)
@@ -502,21 +502,21 @@
self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_END_DRAG,
- self.AUIManager.AddPane(self.TabsOpened,
+ self.AUIManager.AddPane(self.TabsOpened, wx.aui.AuiPaneInfo().CentrePane().Name("TabsPane"))
#-----------------------------------------------------------------------
# Creating PLCopen Project Types Tree
#-----------------------------------------------------------------------
self.ProjectPanel = wx.SplitterWindow(id=ID_PLCOPENEDITORPROJECTPANEL,
name='ProjectPanel', parent=self.LeftNoteBook, point=wx.Point(0, 0),
size=wx.Size(0, 0), style=wx.SP_3D)
self.ProjectTree = CustomTree(id=ID_PLCOPENEDITORPROJECTTREE,
- name='ProjectTree', parent=self.ProjectPanel,
+ name='ProjectTree', parent=self.ProjectPanel, pos=wx.Point(0, 0), size=wx.Size(0, 0),
agwStyle=wx.TR_HAS_BUTTONS|wx.TR_SINGLE|wx.TR_EDIT_LABELS)
@@ -539,22 +539,22 @@
self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnProjectTreeItemActivated,
id=ID_PLCOPENEDITORPROJECTTREE)
self.ProjectTree.Bind(wx.EVT_MOTION, self.OnProjectTreeMotion)
#-----------------------------------------------------------------------
# Creating PLCopen Project POU Instance Variables Panel
#-----------------------------------------------------------------------
self.PouInstanceVariablesPanel = PouInstanceVariablesPanel(self.ProjectPanel, self, self.Controler, self.EnableDebug)
self.MainTabs["ProjectPanel"] = (self.ProjectPanel, _("Project"))
self.LeftNoteBook.AddPage(*self.MainTabs["ProjectPanel"])
self.ProjectPanel.SplitHorizontally(self.ProjectTree, self.PouInstanceVariablesPanel, 300)
#-----------------------------------------------------------------------
#-----------------------------------------------------------------------
MenuToolBar = wx.ToolBar(self, ID_PLCOPENEDITOREDITORMENUTOOLBAR, wx.DefaultPosition, wx.DefaultSize,
wx.TB_FLAT | wx.TB_NODIVIDER | wx.NO_BORDER)
MenuToolBar.SetToolBitmapSize(wx.Size(25, 25))
@@ -564,11 +564,11 @@
Name("MenuToolBar").Caption(_("Menu ToolBar")).
LeftDockable(False).RightDockable(False))
EditorToolBar = wx.ToolBar(self, ID_PLCOPENEDITOREDITORTOOLBAR, wx.DefaultPosition, wx.DefaultSize,
wx.TB_FLAT | wx.TB_NODIVIDER | wx.NO_BORDER)
EditorToolBar.SetToolBitmapSize(wx.Size(25, 25))
- EditorToolBar.AddRadioTool(ID_PLCOPENEDITOREDITORTOOLBARSELECTION,
+ EditorToolBar.AddRadioTool(ID_PLCOPENEDITOREDITORTOOLBARSELECTION, GetBitmap("select"), wx.NullBitmap, _("Select an object"))
self.Panes["EditorToolBar"] = EditorToolBar
@@ -576,39 +576,39 @@
Name("EditorToolBar").Caption(_("Editor ToolBar")).
ToolbarPane().Top().Position(1).
LeftDockable(False).RightDockable(False))
- self.Bind(wx.EVT_MENU, self.OnSelectionTool,
+ self.Bind(wx.EVT_MENU, self.OnSelectionTool, id=ID_PLCOPENEDITOREDITORTOOLBARSELECTION)
#-----------------------------------------------------------------------
#-----------------------------------------------------------------------
self.SearchResultPanel = SearchResultPanel(self.BottomNoteBook, self)
self.MainTabs["SearchResultPanel"] = (self.SearchResultPanel, _("Search"))
self.BottomNoteBook.AddPage(*self.MainTabs["SearchResultPanel"])
#-----------------------------------------------------------------------
#-----------------------------------------------------------------------
self.LibraryPanel = LibraryPanel(self, True)
self.MainTabs["LibraryPanel"] = (self.LibraryPanel, _("Library"))
self.RightNoteBook.AddPage(*self.MainTabs["LibraryPanel"])
self.SetMenuBar(self.MenuBar)
self.DebugVariablePanel = DebugVariablePanel(self.RightNoteBook, self.Controler, self)
self.MainTabs["DebugVariablePanel"] = (self.DebugVariablePanel, _("Debugger"))
self.RightNoteBook.AddPage(*self.MainTabs["DebugVariablePanel"])
self.FindDialog = FindInPouDialog(self)
## Constructor of the PLCOpenEditor class.
# @param parent The parent window.
# @param controler The controler been used by PLCOpenEditor (default: None).
@@ -618,17 +618,17 @@
self.Config = wx.ConfigBase.Get()
self.EnableDebug = enable_debug
# Define Tree item icon list
self.TreeImageList = wx.ImageList(16, 16)
for language in LANGUAGES:
self.TreeImageDict[language] = self.TreeImageList.Add(GetBitmap(language))
for imgname, itemtype in [
@@ -658,11 +658,11 @@
("RESOURCES", ITEM_RESOURCES),
("PROPERTIES", ITEM_PROPERTIES)]:
self.TreeImageDict[itemtype] = self.TreeImageList.Add(GetBitmap(imgname))
# Assign icon list to TreeCtrls
self.ProjectTree.SetImageList(self.TreeImageList)
self.PouInstanceVariablesPanel.SetTreeImageList(self.TreeImageList)
self.CurrentEditorToolBar = []
@@ -672,7 +672,7 @@
self.DrawingMode = FREEDRAWING_MODE
#self.DrawingMode = DRIVENDRAWING_MODE
# Save default perspective
for notebook, entry_name in [(self.LeftNoteBook, "leftnotebook"),
@@ -683,8 +683,8 @@
"perspective": self.AUIManager.SavePerspective(),
# Initialize Printing configuring elements
self.PrintData = wx.PrintData()
self.PrintData.SetPaperId(wx.PAPER_A4)
@@ -692,17 +692,17 @@
self.PageSetupData = wx.PageSetupDialogData(self.PrintData)
self.PageSetupData.SetMarginTopLeft(wx.Point(10, 15))
self.PageSetupData.SetMarginBottomRight(wx.Point(10, 20))
self.SetRefreshFunctions()
self.SetDeleteFunctions()
self.FindDialog.Destroy()
wx.CallAfter(self.RestoreLastState)
def OnActivated(self, event):
wx.CallAfter(self._Refresh, TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU)
@@ -724,7 +724,7 @@
return ("main", page_name)
def SaveTabLayout(self, notebook):
for child in notebook.GetChildren():
@@ -744,7 +744,7 @@
tabs.sort(lambda x, y: cmp(x["pos"], y["pos"]))
size = notebook.GetSize()
return ComputeTabsLayout(tabs, wx.Rect(1, 1, size[0] - NOTEBOOK_BORDER, size[1] - NOTEBOOK_BORDER))
def LoadTab(self, notebook, page_infos):
if page_infos[0] == "main":
infos = self.MainTabs.get(page_infos[1])
@@ -764,21 +764,21 @@
if instance_infos is not None:
return notebook.GetPageIndex(self.OpenDebugViewer(instance_infos["class"], instance_path, instance_infos["type"]))
def LoadTabLayout(self, notebook, tabs, mode="all", first_index=None):
if isinstance(tabs, ListType):
raise ValueError, "Not supported"
if tabs.has_key("split"):
self.LoadTabLayout(notebook, tabs["others"])
split_dir, split_ratio = tabs["split"]
first_index = self.LoadTabLayout(notebook, tabs["tab"], mode="first")
notebook.Split(first_index, split_dir)
self.LoadTabLayout(notebook, tabs["tab"], mode="others", first_index=first_index)
return self.LoadTab(notebook, tabs["pages"][0][0])
@@ -793,39 +793,39 @@
wx.CallAfter(notebook.SetSelection, selected)
def ResetPerspective(self):
if self.DefaultPerspective is not None:
self.AUIManager.LoadPerspective(self.DefaultPerspective["perspective"])
for notebook in [self.LeftNoteBook, self.BottomNoteBook, self.RightNoteBook]:
for idx in xrange(notebook.GetPageCount()):
notebooks = self.DefaultPerspective["notebooks"]
for notebook, entry_name in [(self.LeftNoteBook, "leftnotebook"),
(self.BottomNoteBook, "bottomnotebook"),
(self.RightNoteBook, "rightnotebook")]:
self.LoadTabLayout(notebook, notebooks.get(entry_name))
self._Refresh(EDITORTOOLBAR)
def RestoreLastState(self):
if self.Config.HasEntry("framesize"):
frame_size = cPickle.loads(str(self.Config.Read("framesize")))
self.SetClientSize(frame_size)
if not self.IsMaximized():
self.Config.Write("framesize", cPickle.dumps(self.GetClientSize()))
elif self.Config.HasEntry("framesize"):
self.Config.DeleteEntry("framesize")
#-------------------------------------------------------------------------------
@@ -840,7 +840,7 @@
EDITMENU : self.RefreshEditMenu,
DISPLAYMENU : self.RefreshDisplayMenu,
PROJECTTREE : self.RefreshProjectTree,
- POUINSTANCEVARIABLESPANEL : self.RefreshPouInstanceVariablesPanel,
+ POUINSTANCEVARIABLESPANEL : self.RefreshPouInstanceVariablesPanel, LIBRARYTREE : self.RefreshLibraryPanel,
SCALING : self.RefreshScaling,
PAGETITLES: self.RefreshPageTitles}
@@ -861,9 +861,9 @@
selected = self.TabsOpened.GetSelection()
window = self.TabsOpened.GetPage(selected)
if window.CheckSaveBeforeClosing():
# Refresh all window elements that have changed
wx.CallAfter(self._Refresh, TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU)
wx.CallAfter(self.RefreshTabCtrlEvent)
@@ -871,7 +871,7 @@
def GetCopyBuffer(self, primary_selection=False):
@@ -885,7 +885,7 @@
def SetCopyBuffer(self, text, primary_selection=False):
if primary_selection and wx.Platform == '__WXMSW__':
@@ -916,14 +916,14 @@
new_values["creationDateTime"] = old_values["creationDateTime"]
if new_values != old_values:
self.Controler.SetProjectProperties(None, new_values)
- self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU,
+ self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL, SCALING)
#-------------------------------------------------------------------------------
# Notebook Unified Functions
#-------------------------------------------------------------------------------
## Function that add a tab in Notebook, calling refresh for tab DClick event
# for wx.aui.AUINotebook.
# @param window Panel to display in tab.
@@ -931,7 +931,7 @@
def AddPage(self, window, text):
self.TabsOpened.AddPage(window, text)
self.RefreshTabCtrlEvent()
## Function that add a tab in Notebook, calling refresh for tab DClick event
# for wx.aui.AUINotebook.
# @param window Panel to display in tab.
@@ -941,16 +941,16 @@
if self.TabsOpened.GetPage(idx) == window:
self.TabsOpened.DeletePage(idx)
self.RefreshTabCtrlEvent()
- ## Function that fix difference in deleting all tabs between
+ ## Function that fix difference in deleting all tabs between # wx.Notebook and wx.aui.AUINotebook.
def DeleteAllPages(self):
for idx in xrange(self.TabsOpened.GetPageCount()):
self.TabsOpened.DeletePage(0)
self.RefreshTabCtrlEvent()
- ## Function that fix difference in setting picture on tab between
+ ## Function that fix difference in setting picture on tab between # wx.Notebook and wx.aui.AUINotebook.
# @param bitmap wx.Bitmap to define on tab.
@@ -980,12 +980,12 @@
elif answer == wx.ID_CANCEL:
for idx in xrange(self.TabsOpened.GetPageCount()):
window = self.TabsOpened.GetPage(idx)
if not window.CheckSaveBeforeClosing():
#-------------------------------------------------------------------------------
@@ -1025,7 +1025,7 @@
def OnPreviewMenu(self, event):
- selected = self.TabsOpened.GetSelection()
+ selected = self.TabsOpened.GetSelection() window = self.TabsOpened.GetPage(selected)
data = wx.PrintDialogData(self.PrintData)
@@ -1040,15 +1040,15 @@
preview_frame = wx.PreviewFrame(preview, self, _("Print preview"), style=wx.DEFAULT_FRAME_STYLE|wx.FRAME_FLOAT_ON_PARENT)
preview_frame.Initialize()
preview_canvas = preview.GetCanvas()
preview_canvas.SetMinSize(preview_canvas.GetVirtualSize())
def OnPrintMenu(self, event):
- selected = self.TabsOpened.GetSelection()
+ selected = self.TabsOpened.GetSelection() window = self.TabsOpened.GetPage(selected)
dialog_data = wx.PrintDialogData(self.PrintData)
@@ -1058,7 +1058,7 @@
margins = (self.PageSetupData.GetMarginTopLeft(), self.PageSetupData.GetMarginBottomRight())
printer = wx.Printer(dialog_data)
printout = GraphicPrintout(window, page_size, margins)
if not printer.Print(self, printout, True) and printer.GetLastError() != wx.PRINTER_CANCELLED:
self.ShowErrorMessage(_("There was a problem printing.\nPerhaps your current printer is not set correctly?"))
@@ -1087,12 +1087,12 @@
self.EditMenu.Enable(wx.ID_REDO, redo)
MenuToolBar.EnableTool(wx.ID_REDO, redo)
#self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, True)
- #self.EditMenu.Check(ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO,
+ #self.EditMenu.Check(ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, # self.Controler.IsProjectBufferEnabled())
self.EditMenu.Enable(wx.ID_FIND, selected > -1)
- self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUFINDNEXT,
+ self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUFINDNEXT, selected > -1 and self.SearchParams is not None)
- self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUFINDPREVIOUS,
+ self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUFINDPREVIOUS, selected > -1 and self.SearchParams is not None)
self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, True)
MenuToolBar.EnableTool(ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, True)
@@ -1138,7 +1138,7 @@
MenuToolBar.EnableTool(ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, False)
self.EditMenu.Enable(wx.ID_ADD, False)
self.EditMenu.Enable(wx.ID_DELETE, False)
def CloseTabsWithoutModel(self, refresh=True):
idxs = range(self.TabsOpened.GetPageCount())
@@ -1156,9 +1156,9 @@
self.Controler.LoadPrevious()
- self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE,
+ self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE, def OnRedoMenu(self, event):
selected = self.TabsOpened.GetSelection()
@@ -1166,9 +1166,9 @@
self.Controler.LoadNext()
- self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE,
+ self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE,
def OnEnableUndoRedoMenu(self, event):
self.Controler.EnableProjectBuffer(event.IsChecked())
@@ -1187,14 +1187,14 @@
control.SetSelection(0, control.GetLastPosition())
elif isinstance(control, wx.ComboBox):
control.SetMark(0, control.GetLastPosition() + 1)
def SetDeleteFunctions(self):
ITEM_DATATYPE: GetDeleteElementFunction(
- PLCControler.ProjectRemoveDataType,
+ PLCControler.ProjectRemoveDataType, check_function=self.CheckDataTypeIsUsedBeforeDeletion),
ITEM_POU: GetDeleteElementFunction(
- PLCControler.ProjectRemovePou,
+ PLCControler.ProjectRemovePou, check_function=self.CheckPouIsUsedBeforeDeletion),
ITEM_TRANSITION: GetDeleteElementFunction(
PLCControler.ProjectRemovePouTransition, ITEM_POU),
@@ -1205,7 +1205,7 @@
ITEM_RESOURCE: GetDeleteElementFunction(
PLCControler.ProjectRemoveConfigurationResource, ITEM_CONFIGURATION)
def OnDeleteMenu(self, event):
window = self.FindFocus()
if window == self.ProjectTree or window is None:
@@ -1215,7 +1215,7 @@
self.CloseTabsWithoutModel()
- self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, PROJECTTREE,
+ self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE)
elif isinstance(window, (Viewer, TextViewer)):
event = wx.KeyEvent(wx.EVT_CHAR._getEvtType())
@@ -1225,18 +1225,18 @@
def OnFindMenu(self, event):
if not self.FindDialog.IsShown():
def CloseFindInPouDialog(self):
selected = self.TabsOpened.GetSelection()
if selected == -1 and self.FindDialog.IsShown():
def OnFindNextMenu(self, event):
def OnFindPreviousMenu(self, event):
def FindInPou(self, direction, search_params=None):
if search_params is not None:
self.SearchParams = search_params
@@ -1244,7 +1244,7 @@
window = self.TabsOpened.GetPage(selected)
window.Find(direction, self.SearchParams)
def OnSearchInProjectMenu(self, event):
dialog = SearchInProjectDialog(self)
if dialog.ShowModal() == wx.ID_OK:
@@ -1253,7 +1253,7 @@
self.ClearSearchResults()
self.SearchResultPanel.SetSearchResults(criteria, result)
self.SelectTab(self.SearchResultPanel)
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
@@ -1284,7 +1284,7 @@
self.DisplayMenu.Enable(wx.ID_CLEAR, False)
self.DisplayMenu.Enable(wx.ID_ZOOM_FIT, False)
def OnRefreshMenu(self, event):
@@ -1308,14 +1308,14 @@
#-------------------------------------------------------------------------------
# Project Editor Panels Management Functions
#-------------------------------------------------------------------------------
def OnPageDragged(self, event):
wx.CallAfter(self.RefreshTabCtrlEvent)
def OnAllowNotebookDnD(self, event):
def RefreshTabCtrlEvent(self):
for child in self.TabsOpened.GetChildren():
@@ -1329,11 +1329,11 @@
self.AUIManager.RestorePane(pane)
def EnsureTabVisible(self, tab):
notebook = tab.GetParent()
notebook.SetSelection(notebook.GetPageIndex(tab))
def OnPouSelectedChanging(self, event):
selected = self.TabsOpened.GetSelection()
@@ -1341,7 +1341,7 @@
if not window.IsDebugging():
def OnPouSelectedChanged(self, event):
selected = self.TabsOpened.GetSelection()
@@ -1361,7 +1361,7 @@
wx.CallAfter(self.PouInstanceVariablesPanel.SetPouType, tagname, instance_path)
wx.CallAfter(self._Refresh, FILEMENU, EDITMENU, DISPLAYMENU, EDITORTOOLBAR)
selected = self.TabsOpened.GetSelection()
@@ -1383,13 +1383,13 @@
window = child.GetWindowFromIdx(active_page)
self._Refresh(FILEMENU, EDITMENU, DISPLAYMENU, EDITORTOOLBAR)
def RefreshEditorNames(self, old_tagname, new_tagname):
for i in xrange(self.TabsOpened.GetPageCount()):
editor = self.TabsOpened.GetPage(i)
if editor.GetTagName() == old_tagname:
editor.SetTagName(new_tagname)
def IsOpened(self, tagname):
for idx in xrange(self.TabsOpened.GetPageCount()):
if self.TabsOpened.GetPage(idx).IsViewing(tagname):
@@ -1429,7 +1429,7 @@
tagname = item_infos.get("tagname", None)
# Refresh treectrl items according to project infos
infos = self.Controler.GetProjectInfos()
root = self.ProjectTree.GetRootItem()
@@ -1437,7 +1437,7 @@
root = self.ProjectTree.AddRoot(infos["name"])
self.GenerateProjectTreeBranch(root, infos)
self.ProjectTree.Expand(root)
# Select new item corresponding to previous selected item
self.SelectProjectTreeItem(tagname)
@@ -1456,7 +1456,7 @@
self.ProjectTree.SetItemTextColour(root, highlight_colours[1])
self.ProjectTree.SetItemExtraImage(root, None)
if infos["type"] == ITEM_POU:
- self.ProjectTree.SetItemImage(root,
+ self.ProjectTree.SetItemImage(root, self.TreeImageDict[self.Controler.GetPouBodyType(infos["name"])])
self.ProjectTree.SetItemExtraImage(root, self.Controler.GetPouType(infos["name"]))
@@ -1466,8 +1466,8 @@
self.TreeImageDict[icon_name] = self.TreeImageList.Add(GetBitmap(icon_name))
self.ProjectTree.SetItemImage(root, self.TreeImageDict[icon_name])
elif self.TreeImageDict.has_key(infos["type"]):
- self.ProjectTree.SetItemImage(root, self.TreeImageDict[infos["type"]])
+ self.ProjectTree.SetItemImage(root, self.TreeImageDict[infos["type"]]) item, root_cookie = self.ProjectTree.GetFirstChild(root)
for values in infos["values"]:
if values["type"] not in ITEMS_UNEDITABLE or len(values["values"]) > 0:
@@ -1522,7 +1522,7 @@
def OnProjectTreeBeginDrag(self, event):
- selected_item = (self.SelectedItem
+ selected_item = (self.SelectedItem if self.SelectedItem is not None
if selected_item.IsOk() and self.ProjectTree.GetPyData(selected_item)["type"] == ITEM_POU:
@@ -1563,7 +1563,7 @@
self.Controler.ChangeDataTypeName(old_name, new_name)
- self.RefreshEditorNames(self.Controler.ComputeDataTypeName(old_name),
+ self.RefreshEditorNames(self.Controler.ComputeDataTypeName(old_name), self.Controler.ComputeDataTypeName(new_name))
elif item_infos["type"] == ITEM_POU:
@@ -1577,7 +1577,7 @@
self.Controler.ChangePouName(old_name, new_name)
- self.RefreshEditorNames(self.Controler.ComputePouName(old_name),
+ self.RefreshEditorNames(self.Controler.ComputePouName(old_name), self.Controler.ComputePouName(new_name))
self.RefreshLibraryPanel()
@@ -1589,7 +1589,7 @@
words = item_infos["tagname"].split("::")
self.Controler.ChangePouTransitionName(words[1], old_name, new_name)
- self.RefreshEditorNames(self.Controler.ComputePouTransitionName(words[1], old_name),
+ self.RefreshEditorNames(self.Controler.ComputePouTransitionName(words[1], old_name), self.Controler.ComputePouTransitionName(words[1], new_name))
elif item_infos["type"] == ITEM_ACTION:
@@ -1600,7 +1600,7 @@
words = item_infos["tagname"].split("::")
self.Controler.ChangePouActionName(words[1], old_name, new_name)
- self.RefreshEditorNames(self.Controler.ComputePouActionName(words[1], old_name),
+ self.RefreshEditorNames(self.Controler.ComputePouActionName(words[1], old_name), self.Controler.ComputePouActionName(words[1], new_name))
elif item_infos["type"] == ITEM_CONFIGURATION:
@@ -1619,7 +1619,7 @@
self.Controler.ChangeConfigurationName(old_name, new_name)
- self.RefreshEditorNames(self.Controler.ComputeConfigurationName(old_name),
+ self.RefreshEditorNames(self.Controler.ComputeConfigurationName(old_name), self.Controler.ComputeConfigurationName(new_name))
elif item_infos["type"] == ITEM_RESOURCE:
@@ -1639,7 +1639,7 @@
words = item_infos["tagname"].split("::")
self.Controler.ChangeConfigurationResourceName(words[1], old_name, new_name)
- self.RefreshEditorNames(self.Controler.ComputeConfigurationResourceName(words[1], old_name),
+ self.RefreshEditorNames(self.Controler.ComputeConfigurationResourceName(words[1], old_name), self.Controler.ComputeConfigurationResourceName(words[1], new_name))
@@ -1653,7 +1653,7 @@
self._Refresh(TITLE, FILEMENU, EDITMENU)
def OnProjectTreeItemActivated(self, event):
selected = event.GetItem()
name = self.ProjectTree.GetItemText(selected)
@@ -1666,7 +1666,7 @@
ITEM_TRANSITION, ITEM_ACTION]:
self.EditProjectElement(item_infos["type"], item_infos["tagname"])
def ProjectTreeItemSelect(self, select_item):
if select_item is not None and select_item.IsOk():
name = self.ProjectTree.GetItemText(select_item)
@@ -1676,14 +1676,14 @@
ITEM_TRANSITION, ITEM_ACTION]:
self.EditProjectElement(item_infos["type"], item_infos["tagname"], True)
self.PouInstanceVariablesPanel.SetPouType(item_infos["tagname"])
def OnProjectTreeLeftUp(self, event):
if self.SelectedItem is not None:
self.ProjectTree.SelectItem(self.SelectedItem)
self.ProjectTreeItemSelect(self.SelectedItem)
def OnProjectTreeMotion(self, event):
pt = wx.Point(event.GetX(), event.GetY())
@@ -1693,7 +1693,7 @@
if item != self.LastToolTipItem and self.LastToolTipItem is not None:
self.ProjectTree.SetToolTip(None)
self.LastToolTipItem = None
- if (self.LastToolTipItem != item and
+ if (self.LastToolTipItem != item and item_infos["type"] in [ITEM_POU, ITEM_TRANSITION, ITEM_ACTION]):
bodytype = self.Controler.GetEditedElementBodyType(
@@ -1708,21 +1708,21 @@
self.LastToolTipItem = item
- wx.CallAfter(self.ProjectTree.SetToolTipString,
+ wx.CallAfter(self.ProjectTree.SetToolTipString, block_type, bodytype, item_infos["name"]))
elif self.LastToolTipItem is not None:
self.ProjectTree.SetToolTip(None)
self.LastToolTipItem = None
def OnProjectTreeItemChanging(self, event):
if self.ProjectTree.GetPyData(event.GetItem())["type"] not in ITEMS_UNEDITABLE and self.SelectedItem is None:
self.SelectedItem = event.GetItem()
def EditProjectElement(self, element, tagname, onlyopened = False):
openedidx = self.IsOpened(tagname)
if openedidx is not None:
@@ -1790,30 +1790,30 @@
def OnProjectTreeRightUp(self, event):
self.ProjectTree.SelectItem(item)
self.ProjectTreeItemSelect(item)
name = self.ProjectTree.GetItemText(item)
item_infos = self.ProjectTree.GetPyData(item)
if item_infos["type"] in ITEMS_UNEDITABLE + [ITEM_PROJECT]:
if item_infos["type"] == ITEM_PROJECT:
name = UNEDITABLE_NAMES_DICT[name]
AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add DataType"))
self.Bind(wx.EVT_MENU, self.OnAddDataTypeMenu, id=new_id)
elif name in ["Functions", "Function Blocks", "Programs", "Project"]:
AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add POU"))
@@ -1830,7 +1830,7 @@
AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Configuration"))
self.Bind(wx.EVT_MENU, self.OnAddConfigurationMenu, id=new_id)
elif name == "Transitions":
@@ -1841,7 +1841,7 @@
parent = self.ProjectTree.GetItemParent(parent)
parent_type = self.ProjectTree.GetPyData(parent)["type"]
self.Bind(wx.EVT_MENU, self.GenerateAddTransitionFunction(self.ProjectTree.GetItemText(parent)), id=new_id)
@@ -1852,7 +1852,7 @@
parent = self.ProjectTree.GetItemParent(parent)
parent_type = self.ProjectTree.GetPyData(parent)["type"]
self.Bind(wx.EVT_MENU, self.GenerateAddActionFunction(self.ProjectTree.GetItemText(parent)), id=new_id)
elif name == "Resources":
@@ -1871,7 +1871,7 @@
parent_name = self.ProjectTree.GetItemText(parent)
if parent_name is not None:
self.Bind(wx.EVT_MENU, self.GenerateAddResourceFunction(parent_name), id=new_id)
if item_infos["type"] == ITEM_POU:
@@ -1883,11 +1883,11 @@
AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Action"))
self.Bind(wx.EVT_MENU, self.GenerateAddActionFunction(name), id=new_id)
AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Copy POU"))
self.Bind(wx.EVT_MENU, self.OnCopyPou, id=new_id)
pou_type = self.Controler.GetPouType(name)
if pou_type in ["function", "functionBlock"]:
change_menu = wx.Menu(title='')
@@ -1902,27 +1902,27 @@
AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Rename"))
self.Bind(wx.EVT_MENU, self.OnRenamePouMenu, id=new_id)
elif item_infos["type"] == ITEM_CONFIGURATION:
AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Resource"))
self.Bind(wx.EVT_MENU, self.GenerateAddResourceFunction(name), id=new_id)
elif item_infos["type"] in [ITEM_DATATYPE, ITEM_TRANSITION, ITEM_ACTION, ITEM_RESOURCE]:
AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Delete"))
self.Bind(wx.EVT_MENU, self.OnDeleteMenu, id=new_id)
@@ -1932,7 +1932,7 @@
def GetTreeImage(self, var_class):
return self.TreeImageDict[var_class]
def RefreshPouInstanceVariablesPanel(self):
self.PouInstanceVariablesPanel.RefreshView()
@@ -1945,11 +1945,11 @@
self.TabsOpened.GetPage(old_selected).ResetBuffer()
self.TabsOpened.SetSelection(openedidx)
elif instance_category in ITEMS_VARIABLE:
if self.Controler.IsNumType(instance_type, True):
self.AddDebugVariable(instance_path, True)
bodytype = self.Controler.GetEditedElementBodyType(instance_type, True)
@@ -1968,7 +1968,7 @@
new_window.SetKeywords(IL_KEYWORDS)
new_window.SetKeywords(ST_KEYWORDS)
if new_window is not None:
if instance_category in [ITEM_FUNCTIONBLOCK, ITEM_PROGRAM]:
pou_type = self.Controler.GetEditedElementType(instance_type, True)[1].upper()
@@ -1977,7 +1977,7 @@
icon = GetBitmap("TRANSITION", bodytype)
elif instance_category == ITEM_ACTION:
icon = GetBitmap("ACTION", bodytype)
if new_window is not None:
self.AddPage(new_window, "")
@@ -2005,19 +2005,19 @@
elif editor.IsDebugging():
editor.SubscribeAllDataConsumers()
self.DebugVariablePanel.SubscribeAllDataConsumers()
def AddDebugVariable(self, iec_path, force=False, graph=False):
self.DebugVariablePanel.InsertValue(iec_path, force=force, graph=graph)
self.EnsureTabVisible(self.DebugVariablePanel)
#-------------------------------------------------------------------------------
# Library Panel Management Function
#-------------------------------------------------------------------------------
def RefreshLibraryPanel(self):
self.LibraryPanel.RefreshTree()
#-------------------------------------------------------------------------------
# ToolBars Management Functions
#-------------------------------------------------------------------------------
@@ -2039,16 +2039,16 @@
def ResetEditorToolBar(self):
EditorToolBar = self.Panes["EditorToolBar"]
for item in self.CurrentEditorToolBar:
if wx.VERSION >= (2, 6, 0):
self.Unbind(wx.EVT_MENU, id=item)
- self.Disconnect(id=item, eventType=wx.wxEVT_COMMAND_MENU_SELECTED)
+ self.Disconnect(id=item, eventType=wx.wxEVT_COMMAND_MENU_SELECTED) EditorToolBar.DeleteTool(item)
self.AUIManager.GetPane("EditorToolBar").BestSize(EditorToolBar.GetBestSize())
@@ -2102,7 +2102,7 @@
EditorToolBar.ToggleTool(ID_PLCOPENEDITOREDITORTOOLBARSELECTION, False)
EditorToolBar.ToggleTool(ID_PLCOPENEDITOREDITORTOOLBARSELECTION, True)
def ResetToolToggle(self, id):
tool = self.Panes["EditorToolBar"].FindById(id)
@@ -2111,55 +2111,55 @@
selected = self.TabsOpened.GetSelection()
self.TabsOpened.GetPage(selected).SetMode(MODE_SELECTION)
def OnMotionTool(self, event):
selected = self.TabsOpened.GetSelection()
self.TabsOpened.GetPage(selected).SetMode(MODE_MOTION)
def OnCommentTool(self, event):
self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARCOMMENT)
selected = self.TabsOpened.GetSelection()
self.TabsOpened.GetPage(selected).SetMode(MODE_COMMENT)
def OnVariableTool(self, event):
self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARVARIABLE)
selected = self.TabsOpened.GetSelection()
self.TabsOpened.GetPage(selected).SetMode(MODE_VARIABLE)
def OnBlockTool(self, event):
self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARBLOCK)
selected = self.TabsOpened.GetSelection()
self.TabsOpened.GetPage(selected).SetMode(MODE_BLOCK)
def OnConnectionTool(self, event):
self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARCONNECTION)
selected = self.TabsOpened.GetSelection()
self.TabsOpened.GetPage(selected).SetMode(MODE_CONNECTION)
def OnPowerRailTool(self, event):
self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL)
selected = self.TabsOpened.GetSelection()
self.TabsOpened.GetPage(selected).SetMode(MODE_POWERRAIL)
def OnRungTool(self, event):
selected = self.TabsOpened.GetSelection()
self.TabsOpened.GetPage(selected).AddLadderRung()
def OnCoilTool(self, event):
self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARCOIL)
selected = self.TabsOpened.GetSelection()
self.TabsOpened.GetPage(selected).SetMode(MODE_COIL)
def OnContactTool(self, event):
if self.DrawingMode == FREEDRAWING_MODE:
self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARCONTACT)
@@ -2169,18 +2169,18 @@
self.TabsOpened.GetPage(selected).SetMode(MODE_CONTACT)
self.TabsOpened.GetPage(selected).AddLadderContact()
- def OnBranchTool(self, event):
+ def OnBranchTool(self, event): selected = self.TabsOpened.GetSelection()
self.TabsOpened.GetPage(selected).AddLadderBranch()
def OnInitialStepTool(self, event):
self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARINITIALSTEP)
selected = self.TabsOpened.GetSelection()
self.TabsOpened.GetPage(selected).SetMode(MODE_INITIALSTEP)
def OnStepTool(self, event):
if self.GetDrawingMode() == FREEDRAWING_MODE:
self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARSTEP)
@@ -2190,7 +2190,7 @@
self.TabsOpened.GetPage(selected).SetMode(MODE_STEP)
self.TabsOpened.GetPage(selected).AddStep()
def OnActionBlockTool(self, event):
if self.GetDrawingMode() == FREEDRAWING_MODE:
self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARACTIONBLOCK)
@@ -2200,13 +2200,13 @@
self.TabsOpened.GetPage(selected).SetMode(MODE_ACTION)
self.TabsOpened.GetPage(selected).AddStepAction()
def OnTransitionTool(self, event):
self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARTRANSITION)
selected = self.TabsOpened.GetSelection()
self.TabsOpened.GetPage(selected).SetMode(MODE_TRANSITION)
def OnDivergenceTool(self, event):
if self.GetDrawingMode() == FREEDRAWING_MODE:
self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARDIVERGENCE)
@@ -2216,7 +2216,7 @@
self.TabsOpened.GetPage(selected).SetMode(MODE_DIVERGENCE)
self.TabsOpened.GetPage(selected).AddDivergence()
def OnJumpTool(self, event):
if self.GetDrawingMode() == FREEDRAWING_MODE:
self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARJUMP)
@@ -2226,7 +2226,7 @@
self.TabsOpened.GetPage(selected).SetMode(MODE_JUMP)
self.TabsOpened.GetPage(selected).AddJump()
#-------------------------------------------------------------------------------
# Add Project Elements Functions
@@ -2237,7 +2237,7 @@
self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE)
self.EditProjectElement(ITEM_DATATYPE, tagname)
def GenerateAddPouFunction(self, pou_type):
dialog = PouDialog(self, pou_type)
@@ -2259,7 +2259,7 @@
dialog.SetPouNames(self.Controler.GetProjectPouNames())
dialog.SetPouElementNames(self.Controler.GetProjectPouVariableNames(pou_name))
dialog.SetValues({"transitionName": self.Controler.GenerateNewName(None, None, "transition%d")})
- if dialog.ShowModal() == wx.ID_OK:
+ if dialog.ShowModal() == wx.ID_OK: values = dialog.GetValues()
tagname = self.Controler.ProjectAddPouTransition(pou_name, values["transitionName"], values["language"])
@@ -2289,6 +2289,14 @@
self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL)
self.EditProjectElement(ITEM_CONFIGURATION, tagname)
+ def AddResourceMenu(self, event): + config_names = self.Controler.GetProjectConfigNames() + if len(config_names) > 0: + tagname = self.Controler.ProjectAddConfigurationResource(config_names[0]) + if tagname is not None: + self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL) + self.EditProjectElement(ITEM_RESOURCE, tagname) def GenerateAddResourceFunction(self, config_name):
def OnAddResourceMenu(event):
tagname = self.Controler.ProjectAddConfigurationResource(config_name)
@@ -2300,7 +2308,7 @@
def GenerateChangePouTypeFunction(self, name, new_type):
def OnChangePouTypeMenu(event):
selected = self.ProjectTree.GetSelection()
- if self.ProjectTree.GetPyData(selected)["type"] == ITEM_POU:
+ if self.ProjectTree.GetPyData(selected)["type"] == ITEM_POU: self.Controler.ProjectChangePouType(name, new_type)
self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, PROJECTTREE, LIBRARYTREE)
return OnChangePouTypeMenu
@@ -2308,7 +2316,7 @@
def OnCopyPou(self, event):
selected = self.ProjectTree.GetSelection()
pou_name = self.ProjectTree.GetItemText(selected)
pou_xml = self.Controler.GetPouXml(pou_name)
self.SetCopyBuffer(pou_xml)
@@ -2316,14 +2324,14 @@
def OnPastePou(self, event):
selected = self.ProjectTree.GetSelection()
- if self.ProjectTree.GetPyData(selected)["type"] != ITEM_PROJECT:
+ if self.ProjectTree.GetPyData(selected)["type"] != ITEM_PROJECT: pou_type = self.ProjectTree.GetItemText(selected)
pou_type = UNEDITABLE_NAMES_DICT[pou_type] # one of 'Functions', 'Function Blocks' or 'Programs'
pou_type = {'Functions': 'function', 'Function Blocks': 'functionBlock', 'Programs': 'program'}[pou_type]
pou_xml = self.GetCopyBuffer()
result = self.Controler.PastePou(pou_type, pou_xml)
@@ -2341,9 +2349,9 @@
def CheckElementIsUsedBeforeDeletion(self, check_function, title, name):
if not check_function(name):
- dialog = wx.MessageDialog(self,
- _("\"%s\" is used by one or more POUs. Do you wish to continue?") % name,
+ dialog = wx.MessageDialog(self, + _("\"%s\" is used by one or more POUs. Do you wish to continue?") % name, title, wx.YES_NO|wx.ICON_QUESTION)
answer = dialog.ShowModal()
@@ -2353,12 +2361,12 @@
return self.CheckElementIsUsedBeforeDeletion(
self.Controler.DataTypeIsUsed,
_("Remove Datatype"), name)
def CheckPouIsUsedBeforeDeletion(self, name):
return self.CheckElementIsUsedBeforeDeletion(
self.Controler.PouIsUsed,
def OnRemoveDataTypeMenu(self, event):
selected = self.ProjectTree.GetSelection()
if self.ProjectTree.GetPyData(selected)["type"] == ITEM_DATATYPE:
@@ -2370,10 +2378,10 @@
self.TabsOpened.DeletePage(idx)
self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, PROJECTTREE)
def OnRenamePouMenu(self, event):
selected = self.ProjectTree.GetSelection()
- if self.ProjectTree.GetPyData(selected)["type"] == ITEM_POU:
+ if self.ProjectTree.GetPyData(selected)["type"] == ITEM_POU: wx.CallAfter(self.ProjectTree.EditLabel, selected)
def OnRemovePouMenu(self, event):
@@ -2391,7 +2399,7 @@
def OnRemoveTransitionMenu(self, event):
selected = self.ProjectTree.GetSelection()
item_infos = self.ProjectTree.GetPyData(selected)
- if item_infos["type"] == ITEM_TRANSITION:
+ if item_infos["type"] == ITEM_TRANSITION: transition = self.ProjectTree.GetItemText(selected)
pou_name = item_infos["tagname"].split("::")[1]
self.Controler.ProjectRemovePouTransition(pou_name, transition)
@@ -2404,7 +2412,7 @@
def OnRemoveActionMenu(self, event):
selected = self.ProjectTree.GetSelection()
item_infos = self.ProjectTree.GetPyData(selected)
- if item_infos["type"] == ITEM_ACTION:
+ if item_infos["type"] == ITEM_ACTION: action = self.ProjectTree.GetItemText(selected)
pou_name = item_infos["tagname"].split("::")[1]
self.Controler.ProjectRemovePouAction(pou_name, action)
@@ -2416,7 +2424,7 @@
def OnRemoveConfigurationMenu(self, event):
selected = self.ProjectTree.GetSelection()
- if self.ProjectTree.GetPyData(selected)["type"] == ITEM_CONFIGURATION:
+ if self.ProjectTree.GetPyData(selected)["type"] == ITEM_CONFIGURATION: name = self.ProjectTree.GetItemText(selected)
self.Controler.ProjectRemoveConfiguration(name)
tagname = self.Controler.ComputeConfigurationName(name)
@@ -2494,17 +2502,17 @@
maxx, maxy = viewer.GetMaxSize()
- self.PageGrid = (UPPER_DIV(maxx, self.PageSize[0]),
+ self.PageGrid = (UPPER_DIV(maxx, self.PageSize[0]), UPPER_DIV(maxy, self.PageSize[1]))
return self.PageGrid[0] * self.PageGrid[1]
return page <= self.GetPageNumber()
page_number = self.GetPageNumber()
return (1, page_number, 1, page_number)
@@ -2520,7 +2528,7 @@
dc.SetUserScale(1.0, 1.0)
dc.printing = not self.Preview
# Get the size of the DC in pixels
ppiPrinterX, ppiPrinterY = self.GetPPIPrinter()
ppiScreenX, ppiScreenY = self.GetPPIScreen()
@@ -2528,26 +2536,26 @@
dw, dh = dc.GetSizeTuple()
Xscale = (float(dw) * float(ppiPrinterX)) / (float(pw) * 25.4)
Yscale = (float(dh) * float(ppiPrinterY)) / (float(ph) * 25.4)
fontsize = self.FontSize * Yscale
text_margin = self.TextMargin * Yscale
margin_left = self.Margins[0].x * Xscale
margin_top = self.Margins[0].y * Yscale
area_width = dw - self.Margins[1].x * Xscale - margin_left
area_height = dh - self.Margins[1].y * Yscale - margin_top
dc.SetPen(MiterPen(wx.BLACK))
- dc.SetBrush(wx.TRANSPARENT_BRUSH)
+ dc.SetBrush(wx.TRANSPARENT_BRUSH) dc.DrawRectangle(margin_left, margin_top, area_width, area_height)
dc.SetFont(wx.Font(fontsize, wx.DEFAULT, wx.NORMAL, wx.NORMAL))
dc.SetTextForeground(wx.BLACK)
block_name = " - ".join(self.Viewer.GetTagName().split("::")[1:])
text_width, text_height = dc.GetTextExtent(block_name)
dc.DrawText(block_name, margin_left, margin_top - text_height - self.TextMargin)
dc.DrawText(_("Page: %d") % page, margin_left, margin_top + area_height + self.TextMargin)
# Calculate the position on the DC for centering the graphic
posX = area_width * ((page - 1) % self.PageGrid[0])
posY = area_height * ((page - 1) / self.PageGrid[0])
@@ -2560,10 +2568,10 @@
dc.SetDeviceOrigin(-posX + margin_left, -posY + margin_top)
dc.SetClippingRegion(posX, posY, self.PageSize[0] * scale, self.PageSize[1] * scale)
dc.SetUserScale(scale, scale)
#-------------------------------------------
self.Viewer.DoDrawing(dc, True)
--- a/PLCControler.py Mon May 26 14:44:03 2014 +0100
+++ b/PLCControler.py Fri Jun 06 18:30:49 2014 +0100
@@ -2,7 +2,7 @@
#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
-#based on the plcopen standard.
+#based on the plcopen standard. #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
@@ -57,7 +57,7 @@
ITEMS_VARIABLE = [ITEM_VAR_LOCAL,
@@ -92,12 +92,12 @@
def GetUneditableNames():
- return [_("User-defined POUs"), _("Functions"), _("Function Blocks"),
- _("Programs"), _("Data Types"), _("Transitions"), _("Actions"),
+ return [_("User-defined POUs"), _("Functions"), _("Function Blocks"), + _("Programs"), _("Data Types"), _("Transitions"), _("Actions"), _("Configurations"), _("Resources"), _("Properties")]
UNEDITABLE_NAMES = GetUneditableNames()
-[USER_DEFINED_POUS, FUNCTIONS, FUNCTION_BLOCKS, PROGRAMS,
- DATA_TYPES, TRANSITIONS, ACTIONS, CONFIGURATIONS,
+[USER_DEFINED_POUS, FUNCTIONS, FUNCTION_BLOCKS, PROGRAMS, + DATA_TYPES, TRANSITIONS, ACTIONS, CONFIGURATIONS, RESOURCES, PROPERTIES] = UNEDITABLE_NAMES
#-------------------------------------------------------------------------------
@@ -133,7 +133,7 @@
_BoolValue = lambda x: x in ["true", "0"]
def _translate_args(translations, args):
- return [translate(arg[0]) if len(arg) > 0 else None
+ return [translate(arg[0]) if len(arg) > 0 else None @@ -142,7 +142,7 @@
#-------------------------------------------------------------------------------
class _VariableInfos(object):
- __slots__ = ["Name", "Class", "Option", "Location", "InitialValue",
+ __slots__ = ["Name", "Class", "Option", "Location", "InitialValue", "Edit", "Documentation", "Type", "Tree", "Number"]
def __init__(self, *args):
for attr, value in zip(self.__slots__, args):
@@ -151,39 +151,39 @@
return _VariableInfos(*[getattr(self, attr) for attr in self.__slots__])
class VariablesInfosFactory:
def __init__(self, variables):
self.Variables = variables
def SetType(self, context, *args):
if len(self.Dimensions) > 0:
return ("array", self.Type, self.Dimensions)
return (self.TreeStack.pop(-1), self.Dimensions)
def AddDimension(self, context, *args):
self.Dimensions.append(tuple(
_translate_args([_StringValue] * 2, args)))
def AddTree(self, context, *args):
self.TreeStack.append([])
def AddVarToTree(self, context, *args):
var = (args[0][0], self.Type, self.GetTree())
self.TreeStack[-1].append(var)
def AddVariable(self, context, *args):
self.Variables.append(_VariableInfos(*(_translate_args(
- [_StringValue] * 5 + [_BoolValue] + [_StringValue], args) +
+ [_StringValue] * 5 + [_BoolValue] + [_StringValue], args) + [self.GetType(), self.GetTree()])))
#-------------------------------------------------------------------------------
@@ -199,15 +199,15 @@
"program": ITEM_PROGRAM}.get(value)
if class_type is not None:
pou_type = POU_TYPES.get(value)
var_type = VAR_CLASS_INFOS.get(value)
class _VariablesTreeItemInfos(object):
@@ -219,24 +219,24 @@
return _VariableTreeItem(*[getattr(self, attr) for attr in self.__slots__])
class VariablesTreeInfosFactory:
def SetRoot(self, context, *args):
self.Root = _VariablesTreeItemInfos(
*([''] + _translate_args(
- [class_extraction, _StringValue] + [_BoolValue] * 2,
+ [class_extraction, _StringValue] + [_BoolValue] * 2, def AddVariable(self, context, *args):
if self.Root is not None:
self.Root.variables.append(_VariablesTreeItemInfos(
- [_StringValue, class_extraction, _StringValue] +
+ [_StringValue, class_extraction, _StringValue] + [_BoolValue] * 2, args) + [[]])))
#-------------------------------------------------------------------------------
@@ -244,10 +244,10 @@
#-------------------------------------------------------------------------------
class InstancesPathFactory:
def __init__(self, instances):
self.Instances = instances
def AddInstance(self, context, *args):
self.Instances.append(args[0][0])
@@ -260,13 +260,13 @@
def __init__(self, controller):
self.Controller = controller
def ConfigTagName(self, context, *args):
self.TagName = self.Controller.ComputeConfigurationName(args[0][0])
def ResourceTagName(self, context, *args):
self.TagName = self.Controller.ComputeConfigurationResourceName(args[0][0], args[1][0])
@@ -285,15 +285,15 @@
_Point = namedtuple("Point", ["x", "y"])
-_BlockInstanceInfos = namedtuple("BlockInstanceInfos",
+_BlockInstanceInfos = namedtuple("BlockInstanceInfos", ["type", "id", "x", "y", "width", "height", "specific_values", "inputs", "outputs"])
- namedtuple("BlockSpecificValues",
+ namedtuple("BlockSpecificValues", ["name", "execution_order"]),
_VariableSpecificValues = (
- namedtuple("VariableSpecificValues",
+ namedtuple("VariableSpecificValues", ["name", "value_type", "execution_order"]),
[_StringValue, _StringValue, int])
_ConnectionSpecificValues = (
@@ -305,7 +305,7 @@
_LDElementSpecificValues = (
- namedtuple("LDElementSpecificValues",
+ namedtuple("LDElementSpecificValues", ["name", "negated", "edge", "storage", "execution_order"]),
[_StringValue, _BoolValue, _StringValue, _StringValue, int])
@@ -330,7 +330,7 @@
namedtuple("StepSpecificValues", ["name", "initial", "action"]),
[_StringValue, _BoolValue, lambda x: x]),
- namedtuple("TransitionSpecificValues",
+ namedtuple("TransitionSpecificValues", ["priority", "condition_type", "condition", "connection"]),
[int, _StringValue, _StringValue, lambda x: x]),
"selectionDivergence": _DivergenceSpecificValues,
@@ -360,24 +360,24 @@
return _ActionInfos(*[getattr(self, attr) for attr in self.__slots__])
class BlockInstanceFactory:
def __init__(self, block_instances):
self.BlockInstances = block_instances
self.CurrentInstance = None
self.SpecificValues = None
self.CurrentConnection = None
def SetSpecificValues(self, context, *args):
self.SpecificValues = list(args)
self.CurrentInstance = None
self.CurrentConnection = None
def AddBlockInstance(self, context, *args):
specific_values_tuple, specific_values_translation = \
_SpecificValuesTuples.get(args[0][0], _BlockSpecificValues)
if (args[0][0] == "step" and len(self.SpecificValues) < 3 or
args[0][0] == "transition" and len(self.SpecificValues) < 4):
self.SpecificValues.append([None])
@@ -386,21 +386,21 @@
specific_values = specific_values_tuple(*_translate_args(
specific_values_translation, self.SpecificValues))
self.SpecificValues = None
self.CurrentInstance = _BlockInstanceInfos(
- *(_translate_args([_StringValue, int] + [float] * 4, args) +
+ *(_translate_args([_StringValue, int] + [float] * 4, args) + [specific_values, [], []]))
self.BlockInstances[self.CurrentInstance.id] = self.CurrentInstance
def AddInstanceConnection(self, context, *args):
connection_args = _translate_args(
[_StringValue] * 2 + [_BoolValue, _StringValue] + [float] * 2, args)
self.CurrentConnection = _InstanceConnectionInfos(
*(connection_args[1:4] + [
_Point(*connection_args[4:6]), []]))
if self.CurrentInstance is not None:
if connection_args[0] == "input":
self.CurrentInstance.inputs.append(self.CurrentConnection)
@@ -408,22 +408,22 @@
self.CurrentInstance.outputs.append(self.CurrentConnection)
self.SpecificValues.append([self.CurrentConnection])
def AddConnectionLink(self, context, *args):
self.CurrentLink = _ConnectionLinkInfos(
*(_translate_args([int, _StringValue], args) + [[]]))
self.CurrentConnection.links.append(self.CurrentLink)
def AddLinkPoint(self, context, *args):
self.CurrentLink.points.append(_Point(
*_translate_args([float] * 2, args)))
def AddAction(self, context, *args):
if len(self.SpecificValues) == 0:
self.SpecificValues.append([[]])
translated_args = _translate_args([_StringValue] * 5, args)
self.SpecificValues[0][0].append(_ActionInfos(*translated_args))
pou_block_instances_xslt = etree.parse(
os.path.join(ScriptDirectory, "plcopen", "pou_block_instances.xslt"))
@@ -461,7 +461,7 @@
# Add a new state in buffer
def Buffering(self, currentstate):
self.CurrentIndex = (self.CurrentIndex + 1) % UNDO_BUFFER_LENGTH
@@ -474,29 +474,29 @@
self.MinIndex = (self.MinIndex + 1) % UNDO_BUFFER_LENGTH
self.MinIndex = max(self.MinIndex, 0)
# Return current state of buffer
return self.Buffer[self.CurrentIndex]
# Change current state to previous in buffer and return new current state
if self.CurrentIndex != self.MinIndex:
self.CurrentIndex = (self.CurrentIndex - 1) % UNDO_BUFFER_LENGTH
return self.Buffer[self.CurrentIndex]
# Change current state to next in buffer and return new current state
if self.CurrentIndex != self.MaxIndex:
self.CurrentIndex = (self.CurrentIndex + 1) % UNDO_BUFFER_LENGTH
return self.Buffer[self.CurrentIndex]
# Return True if current state is the first in buffer
return self.CurrentIndex == self.MinIndex
# Return True if current state is the last in buffer
return self.CurrentIndex == self.MaxIndex
@@ -504,7 +504,7 @@
# Note that current state is saved
self.LastSave = self.CurrentIndex
# Return True if current state is saved
def IsCurrentSaved(self):
return self.LastSave == self.CurrentIndex
@@ -518,12 +518,12 @@
Class which controls the operations made on the plcopen model and answers to view requests
# Create a new PLCControler
# Reset PLCControler internal variables
@@ -541,7 +541,7 @@
self.TotalTypesDict = StdBlckDct.copy()
self.TotalTypes = StdBlckLst[:]
self.ProgramFilePath = ""
def GetQualifierTypes(self):
@@ -567,7 +567,7 @@
self.Project.setfileHeader(properties)
self.Project.setcontentHeader(properties)
# Initialize the project buffer
self.CreateProjectBuffer(False)
@@ -575,28 +575,28 @@
self.NextCompiledProject = self.Copy(self.Project)
self.CurrentCompiledProject = None
# Return project data type names
def GetProjectDataTypeNames(self, debug = False):
project = self.GetProject(debug)
return [datatype.getname() for datatype in project.getdataTypes()]
# Return project pou names
def GetProjectPouNames(self, debug = False):
project = self.GetProject(debug)
return [pou.getname() for pou in project.getpous()]
# Return project pou names
def GetProjectConfigNames(self, debug = False):
project = self.GetProject(debug)
return [config.getname() for config in project.getconfigurations()]
# Return project pou variable names
def GetProjectPouVariableNames(self, pou_name = None, debug = False):
@@ -610,15 +610,15 @@
for action in pou.getactionList():
variables.append(action.getname())
# Return file path if project is an open file
# Return file path if project is an open file
def GetProgramFilePath(self):
return self.ProgramFilePath
# Return file name and point out if file is up to date
if self.Project is not None:
@@ -627,7 +627,7 @@
return "~%s~"%self.FileName
# Change file path and save file name or create a default one if file path not defined
def SetFilePath(self, filepath):
@@ -636,7 +636,7 @@
self.FileName = _("Unnamed%d")%self.LastNewIndex
self.FileName = os.path.splitext(os.path.basename(filepath))[0]
# Change project properties
def SetProjectProperties(self, name = None, properties = None, buffer = True):
if self.Project is not None:
@@ -647,14 +647,14 @@
self.Project.setcontentHeader(properties)
if buffer and (name is not None or properties is not None):
def GetProjectName(self, debug=False):
project = self.GetProject(debug)
# Return project properties
def GetProjectProperties(self, debug = False):
project = self.GetProject(debug)
@@ -663,7 +663,7 @@
properties.update(project.getcontentHeader())
# Return project informations
def GetProjectInfos(self, debug = False):
project = self.GetProject(debug)
@@ -671,7 +671,7 @@
infos = {"name": project.getname(), "type": ITEM_PROJECT}
datatypes = {"name": DATA_TYPES, "type": ITEM_DATATYPES, "values":[]}
for datatype in project.getdataTypes():
- datatypes["values"].append({"name": datatype.getname(), "type": ITEM_DATATYPE,
+ datatypes["values"].append({"name": datatype.getname(), "type": ITEM_DATATYPE, "tagname": self.ComputeDataTypeName(datatype.getname()), "values": []})
pou_types = {"function": {"name": FUNCTIONS, "type": ITEM_FUNCTION, "values":[]},
"functionBlock": {"name": FUNCTION_BLOCKS, "type": ITEM_FUNCTIONBLOCK, "values":[]},
@@ -684,14 +684,14 @@
if pou.getbodyType() == "SFC":
for transition in pou.gettransitionList():
- transitions.append({"name": transition.getname(), "type": ITEM_TRANSITION,
- "tagname": self.ComputePouTransitionName(pou.getname(), transition.getname()),
+ transitions.append({"name": transition.getname(), "type": ITEM_TRANSITION, + "tagname": self.ComputePouTransitionName(pou.getname(), transition.getname()), pou_values.append({"name": TRANSITIONS, "type": ITEM_TRANSITIONS, "values": transitions})
for action in pou.getactionList():
- actions.append({"name": action.getname(), "type": ITEM_ACTION,
- "tagname": self.ComputePouActionName(pou.getname(), action.getname()),
+ actions.append({"name": action.getname(), "type": ITEM_ACTION, + "tagname": self.ComputePouActionName(pou.getname(), action.getname()), pou_values.append({"name": ACTIONS, "type": ITEM_ACTIONS, "values": actions})
if pou_type in pou_types:
@@ -700,19 +700,19 @@
configurations = {"name": CONFIGURATIONS, "type": ITEM_CONFIGURATIONS, "values": []}
for config in project.getconfigurations():
config_name = config.getname()
- config_infos = {"name": config_name, "type": ITEM_CONFIGURATION,
- "tagname": self.ComputeConfigurationName(config.getname()),
+ config_infos = {"name": config_name, "type": ITEM_CONFIGURATION, + "tagname": self.ComputeConfigurationName(config.getname()), resources = {"name": RESOURCES, "type": ITEM_RESOURCES, "values": []}
for resource in config.getresource():
resource_name = resource.getname()
- resource_infos = {"name": resource_name, "type": ITEM_RESOURCE,
- "tagname": self.ComputeConfigurationResourceName(config.getname(), resource.getname()),
+ resource_infos = {"name": resource_name, "type": ITEM_RESOURCE, + "tagname": self.ComputeConfigurationResourceName(config.getname(), resource.getname()), resources["values"].append(resource_infos)
config_infos["values"] = [resources]
configurations["values"].append(config_infos)
- infos["values"] = [datatypes, pou_types["function"], pou_types["functionBlock"],
+ infos["values"] = [datatypes, pou_types["function"], pou_types["functionBlock"], pou_types["program"], configurations]
@@ -722,17 +722,17 @@
project = self.GetProject(debug)
factory = VariablesTreeInfosFactory()
parser = etree.XMLParser()
parser.resolvers.add(LibraryResolver(self, debug))
pou_variable_xslt_tree = etree.XSLT(
os.path.join(ScriptDirectory, "plcopen", "pou_variables.xslt"),
extensions = {("pou_vars_ns", name): getattr(factory, name)
for name in ["SetRoot", "AddVariable"]})
words = tagname.split("::")
@@ -742,7 +742,7 @@
pou_variable_xslt_tree(obj)
def GetInstanceList(self, root, name, debug = False):
@@ -750,22 +750,22 @@
project = self.GetProject(debug)
factory = InstancesPathFactory(instances)
parser = etree.XMLParser()
parser.resolvers.add(LibraryResolver(self, debug))
instances_path_xslt_tree = etree.XSLT(
os.path.join(ScriptDirectory, "plcopen", "instances_path.xslt"),
("instances_ns", "AddInstance"): factory.AddInstance})
- instances_path_xslt_tree(root,
+ instances_path_xslt_tree(root, instance_type=etree.XSLT.strparam(name))
def SearchPouInstances(self, tagname, debug = False):
project = self.GetProject(debug)
@@ -781,28 +781,28 @@
for instance in self.SearchPouInstances(
self.ComputePouName(words[1]), debug)]
def GetPouInstanceTagName(self, instance_path, debug = False):
project = self.GetProject(debug)
factory = InstanceTagName(self)
parser = etree.XMLParser()
parser.resolvers.add(LibraryResolver(self, debug))
instance_tagname_xslt_tree = etree.XSLT(
os.path.join(ScriptDirectory, "plcopen", "instance_tagname.xslt"),
extensions = {("instance_tagname_ns", name): getattr(factory, name)
for name in ["ConfigTagName", "ResourceTagName",
- "PouTagName", "ActionTagName",
+ "PouTagName", "ActionTagName",
- instance_tagname_xslt_tree(project,
+ instance_tagname_xslt_tree(project, instance_path=etree.XSLT.strparam(instance_path))
return factory.GetTagName()
def GetInstanceInfos(self, instance_path, debug = False):
tagname = self.GetPouInstanceTagName(instance_path)
@@ -818,7 +818,7 @@
if var_infos.name == var_name:
# Return if data type given by name is used by another data type or pou
def DataTypeIsUsed(self, name, debug = False):
project = self.GetProject(debug)
@@ -887,11 +887,11 @@
row, col = next_row, next_col
#-------------------------------------------------------------------------------
# Project Pous management functions
#-------------------------------------------------------------------------------
# Add a Data Type to Project
def ProjectAddDataType(self, datatype_name=None):
if self.Project is not None:
@@ -902,13 +902,13 @@
return self.ComputeDataTypeName(datatype_name)
# Remove a Data Type from project
def ProjectRemoveDataType(self, datatype_name):
if self.Project is not None:
self.Project.removedataType(datatype_name)
def ProjectAddPou(self, pou_name, pou_type, body_type):
if self.Project is not None:
@@ -919,21 +919,21 @@
return self.ComputePouName(pou_name)
def ProjectChangePouType(self, name, pou_type):
if self.Project is not None:
pou = self.Project.getpou(name)
def GetPouXml(self, pou_name):
if self.Project is not None:
pou = self.Project.getpou(pou_name)
def PastePou(self, pou_type, pou_xml):
Adds the POU defined by 'pou_xml' to the current project with type 'pou_type'
@@ -944,21 +944,21 @@
return _("Couldn't paste non-POU object.")
- while self.Project.getpou(new_name):
+ while self.Project.getpou(new_name) is not None: # a POU with that name already exists.
# make a new name and test if a POU with that name exists.
# append an incrementing numeric suffix to the POU name.
new_name = "%s%d" % (name, idx)
# we've found a name that does not already exist, use it
new_pou.setname(new_name)
orig_type = new_pou.getpouType()
@@ -968,12 +968,12 @@
if orig_type == 'functionBlock' and pou_type == 'function' or \
orig_type == 'program' and pou_type in ['function', 'functionBlock']:
return _('''%s "%s" can't be pasted as a %s.''') % (orig_type, name, pou_type)
new_pou.setpouType(pou_type)
- self.Project.insertpou(-1, new_pou)
+ self.Project.insertpou(0, new_pou)
return self.ComputePouName(new_name),
# Remove a Pou from project
@@ -981,7 +981,7 @@
if self.Project is not None:
self.Project.removepou(pou_name)
# Return the name of the configuration if only one exist
def GetProjectMainConfigurationName(self):
if self.Project is not None:
@@ -990,7 +990,7 @@
if len(configurations) == 1:
return configurations[0].getname()
# Add a configuration to Project
def ProjectAddConfiguration(self, config_name=None):
if self.Project is not None:
@@ -1000,13 +1000,13 @@
return self.ComputeConfigurationName(config_name)
# Remove a configuration from project
def ProjectRemoveConfiguration(self, config_name):
if self.Project is not None:
self.Project.removeconfiguration(config_name)
# Add a resource to a configuration of the Project
def ProjectAddConfigurationResource(self, config_name, resource_name=None):
if self.Project is not None:
@@ -1016,13 +1016,13 @@
return self.ComputeConfigurationResourceName(config_name, resource_name)
# Remove a resource from a configuration of the project
def ProjectRemoveConfigurationResource(self, config_name, resource_name):
if self.Project is not None:
self.Project.removeconfigurationResource(config_name, resource_name)
# Add a Transition to a Project Pou
def ProjectAddPouTransition(self, pou_name, transition_name, transition_type):
if self.Project is not None:
@@ -1032,7 +1032,7 @@
return self.ComputePouTransitionName(pou_name, transition_name)
# Remove a Transition from a Project Pou
def ProjectRemovePouTransition(self, pou_name, transition_name):
# Search if the pou removed is currently opened
@@ -1041,7 +1041,7 @@
pou.removetransition(transition_name)
# Add an Action to a Project Pou
def ProjectAddPouAction(self, pou_name, action_name, action_type):
if self.Project is not None:
@@ -1051,7 +1051,7 @@
return self.ComputePouActionName(pou_name, action_name)
# Remove an Action from a Project Pou
def ProjectRemovePouAction(self, pou_name, action_name):
# Search if the pou removed is currently opened
@@ -1060,7 +1060,7 @@
pou.removeaction(action_name)
# Change the name of a pou
def ChangeDataTypeName(self, old_name, new_name):
if self.Project is not None:
@@ -1070,7 +1070,7 @@
datatype.setname(new_name)
self.Project.updateElementName(old_name, new_name)
# Change the name of a pou
def ChangePouName(self, old_name, new_name):
if self.Project is not None:
@@ -1080,7 +1080,7 @@
self.Project.updateElementName(old_name, new_name)
# Change the name of a pou transition
def ChangePouTransitionName(self, pou_name, old_name, new_name):
if self.Project is not None:
@@ -1092,7 +1092,7 @@
transition.setname(new_name)
pou.updateElementName(old_name, new_name)
# Change the name of a pou action
def ChangePouActionName(self, pou_name, old_name, new_name):
if self.Project is not None:
@@ -1104,7 +1104,7 @@
pou.updateElementName(old_name, new_name)
# Change the name of a pou variable
def ChangePouVariableName(self, pou_name, old_name, new_name):
if self.Project is not None:
@@ -1116,7 +1116,7 @@
if var.getname() == old_name:
# Change the name of a configuration
def ChangeConfigurationName(self, old_name, new_name):
if self.Project is not None:
@@ -1125,7 +1125,7 @@
if configuration is not None:
configuration.setname(new_name)
# Change the name of a configuration resource
def ChangeConfigurationResourceName(self, config_name, old_name, new_name):
if self.Project is not None:
@@ -1134,7 +1134,7 @@
resource.setname(new_name)
# Return the description of the pou given by its name
def GetPouDescription(self, name, debug = False):
project = self.GetProject(debug)
@@ -1144,7 +1144,7 @@
return pou.getdescription()
# Return the description of the pou given by its name
def SetPouDescription(self, name, description, debug = False):
project = self.GetProject(debug)
@@ -1154,7 +1154,7 @@
pou.setdescription(description)
# Return the type of the pou given by its name
def GetPouType(self, name, debug = False):
project = self.GetProject(debug)
@@ -1164,7 +1164,7 @@
# Return pous with SFC language
def GetSFCPous(self, debug = False):
@@ -1174,7 +1174,7 @@
if pou.getBodyType() == "SFC":
list.append(pou.getname())
# Return the body language of the pou given by its name
def GetPouBodyType(self, name, debug = False):
project = self.GetProject(debug)
@@ -1184,7 +1184,7 @@
# Return the actions of a pou
def GetPouTransitions(self, pou_name, debug = False):
@@ -1196,7 +1196,7 @@
for transition in pou.gettransitionList():
transitions.append(transition.getname())
# Return the body language of the transition given by its name
def GetTransitionBodyType(self, pou_name, pou_transition, debug = False):
project = self.GetProject(debug)
@@ -1209,7 +1209,7 @@
if transition is not None:
return transition.getbodyType()
# Return the actions of a pou
def GetPouActions(self, pou_name, debug = False):
@@ -1221,7 +1221,7 @@
for action in pou.getactionList():
actions.append(action.getname())
# Return the body language of the pou given by its name
def GetActionBodyType(self, pou_name, pou_action, debug = False):
project = self.GetProject(debug)
@@ -1233,18 +1233,18 @@
return action.getbodyType()
# Extract varlists from a list of vars
def ExtractVarLists(self, vars):
- next_type = (var.Class,
- var.Location in ["", None] or
- # When declaring globals, located
- # and not located variables are
+ next_type = (var.Class, + var.Location in ["", None] or + # When declaring globals, located + # and not located variables are # in the same declaration block
if current_type != next_type:
@@ -1264,7 +1264,7 @@
# Create variable and change its properties
tempvar = PLCOpenParser.CreateElement("variable", "varListPlain")
tempvar.setname(var.Name)
var_type = PLCOpenParser.CreateElement("type", "variable")
if isinstance(var.Type, TupleType):
if var.Type[0] == "array":
@@ -1317,28 +1317,28 @@
# Add variable to varList
current_varlist.appendvariable(tempvar)
def GetVariableDictionary(self, object_with_vars, tree=False, debug=False):
factory = VariablesInfosFactory(variables)
parser = etree.XMLParser()
parser.resolvers.add(LibraryResolver(self, debug))
variables_infos_xslt_tree = etree.XSLT(
os.path.join(ScriptDirectory, "plcopen", "variables_infos.xslt"),
extensions = {("var_infos_ns", name): getattr(factory, name)
for name in ["SetType", "AddDimension", "AddTree",
"AddVarToTree", "AddVariable"]})
variables_infos_xslt_tree(object_with_vars,
tree=etree.XSLT.strparam(str(tree)))
# Add a global var to configuration to configuration
- def AddConfigurationGlobalVar(self, config_name, var_type, var_name,
+ def AddConfigurationGlobalVar(self, config_name, var_type, var_name, location="", description=""):
if self.Project is not None:
# Found the configuration corresponding to name
@@ -1346,7 +1346,7 @@
if configuration is not None:
# Set configuration global vars
configuration.addglobalVar(
- self.GetVarTypeObject(var_type),
+ self.GetVarTypeObject(var_type), var_name, location, description)
# Replace the configuration globalvars by those given
@@ -1359,7 +1359,7 @@
configuration.setglobalVars([
varlist for vartype, varlist
in self.ExtractVarLists(vars)])
# Return the configuration globalvars
def GetConfigurationGlobalVars(self, name, debug = False):
project = self.GetProject(debug)
@@ -1369,7 +1369,7 @@
if configuration is not None:
# Extract variables defined in configuration
return self.GetVariableDictionary(configuration, debug)
# Return configuration variable names
@@ -1381,7 +1381,7 @@
if config_name is None or config_name == configuration.getname():
[var.getname() for var in reduce(
- lambda x, y: x + y, [varlist.getvariable()
+ lambda x, y: x + y, [varlist.getvariable() for varlist in configuration.globalVars],
@@ -1396,7 +1396,7 @@
varlist for vartype, varlist
in self.ExtractVarLists(vars)])
# Return the resource globalvars
def GetConfigurationResourceGlobalVars(self, config_name, name, debug = False):
project = self.GetProject(debug)
@@ -1406,11 +1406,11 @@
# Extract variables defined in configuration
return self.GetVariableDictionary(resource, debug)
# Return resource variable names
- def GetConfigurationResourceVariableNames(self,
+ def GetConfigurationResourceVariableNames(self, config_name = None, resource_name = None, debug = False):
project = self.GetProject(debug)
@@ -1421,7 +1421,7 @@
if resource_name is None or resource.getname() == resource_name:
[var.getname() for var in reduce(
- lambda x, y: x + y, [varlist.getvariable()
+ lambda x, y: x + y, [varlist.getvariable() for varlist in resource.globalVars],
@@ -1445,7 +1445,7 @@
pou.interface = PLCOpenParser.CreateElement("interface", "pou")
pou.setvars([varlist for varlist_type, varlist in self.ExtractVarLists(vars)])
# Replace the return type of the pou given by its name (only for functions)
def SetPouInterfaceReturnType(self, name, return_type):
if self.Project is not None:
@@ -1468,16 +1468,16 @@
derived_type = PLCOpenParser.CreateElement("derived", "dataType")
derived_type.setname(return_type)
return_type.setcontent(derived_type)
def UpdateProjectUsedPous(self, old_name, new_name):
if self.Project is not None:
self.Project.updateElementName(old_name, new_name)
def UpdateEditedElementUsedVariable(self, tagname, old_name, new_name):
pou = self.GetEditedElement(tagname)
pou.updateElementName(old_name, new_name)
# Return the return type of the given pou
def GetPouInterfaceReturnType(self, pou, tree=False, debug=False):
# Verify that the pou has an interface
@@ -1486,23 +1486,23 @@
return_type = pou.interface.getreturnType()
if return_type is not None:
factory = VariablesInfosFactory([])
parser = etree.XMLParser()
parser.resolvers.add(LibraryResolver(self))
return_type_infos_xslt_tree = etree.XSLT(
os.path.join(ScriptDirectory, "plcopen", "variables_infos.xslt"),
extensions = {("var_infos_ns", name): getattr(factory, name)
- for name in ["SetType", "AddDimension",
+ for name in ["SetType", "AddDimension", "AddTree", "AddVarToTree"]})
return_type_infos_xslt_tree(return_type,
tree=etree.XSLT.strparam(str(tree)))
return [factory.GetType(), factory.GetTree()]
@@ -1519,7 +1519,7 @@
BlkLst = self.TotalTypesDict.setdefault(desc["name"],[])
BlkLst.append((section["name"], desc))
# Function that clear the confnode list
def ClearConfNodeTypes(self):
@@ -1529,11 +1529,11 @@
def GetConfNodeDataTypes(self, exclude = None, only_locatables = False):
return [{"name": _("%s Data Types") % confnodetypes["name"],
for datatype in confnodetypes["types"].getdataTypes()
if not only_locatables or self.IsLocatableDataType(datatype, debug)]}
for confnodetypes in self.ConfNodeTypes]
def GetVariableLocationTree(self):
@@ -1545,7 +1545,7 @@
for var_name, var_type, var_initial in self.GetConfNodeGlobalInstances():
tempvar = PLCOpenParser.CreateElement("variable", "globalVars")
tempvar.setname(var_name)
tempvartype = PLCOpenParser.CreateElement("type", "variable")
if var_type in self.GetBaseTypes():
tempvartype.setcontent(PLCOpenParser.CreateElement(
@@ -1557,12 +1557,12 @@
tempderivedtype.setname(var_type)
tempvartype.setcontent(tempderivedtype)
tempvar.settype(tempvartype)
value = PLCOpenParser.CreateElement("initialValue", "variable")
value.setvalue(var_initial)
tempvar.setinitialValue(value)
global_vars.append(tempvar)
@@ -1592,11 +1592,11 @@
blocktype_infos = blocktype.getblockInfos()
if inputs in [None, "undefined"]:
- if inputs == tuple([var_type
+ if inputs == tuple([var_type for name, var_type, modifier in blocktype_infos["inputs"]]):
# Return Block types checking for recursion
@@ -1610,18 +1610,18 @@
if words[0] in ["P","T","A"]:
pou_type = self.GetPouType(name, debug)
- if pou_type == "function" or words[0] == "T"
+ if pou_type == "function" or words[0] == "T" else ["functionBlock", "function"])
{"name": category["name"],
"list": [block for block in category["list"]
if block["type"] in filter]}
for category in self.TotalTypes]
- blocktypes.append({"name" : USER_DEFINED_POUS,
+ blocktypes.append({"name" : USER_DEFINED_POUS, "list": [pou.getblockInfos()
for pou in project.getpous(name, filter)
len(self.GetInstanceList(pou, name, debug)) == 0)]})
@@ -1641,7 +1641,7 @@
blocktypes.extend([pou.getname()
for pou in project.getpous(name, ["functionBlock"])
len(self.GetInstanceList(pou, name, debug)) == 0)])
@@ -1672,10 +1672,10 @@
for datatype in project.getdataTypes(name)
if (not only_locatables or self.IsLocatableDataType(datatype, debug))
len(self.GetInstanceList(datatype, name, debug)) == 0)])
for category in self.GetConfNodeDataTypes(name, only_locatables):
@@ -1730,14 +1730,14 @@
def GetBaseType(self, typename, debug = False):
if TypeHierarchy.has_key(typename):
datatype = self.GetDataType(typename, debug)
basetype = self.GetDataTypeBaseType(datatype)
return self.GetBaseType(basetype, debug)
@@ -1751,19 +1751,19 @@
def IsOfType(self, typename, reference, debug = False):
if reference is None or typename == reference:
basetype = TypeHierarchy.get(typename)
return self.IsOfType(basetype, reference)
datatype = self.GetDataType(typename, debug)
basetype = self.GetDataTypeBaseType(datatype)
return self.IsOfType(basetype, reference, debug)
def IsEndType(self, typename):
return not typename.startswith("ANY")
@@ -1781,16 +1781,16 @@
if array_base_type.getLocalTag() == "derived":
return self.IsLocatableType(array_base_type.getname(), debug)
def IsLocatableType(self, typename, debug = False):
if isinstance(typename, TupleType) or self.GetBlockType(typename) is not None:
datatype = self.GetDataType(typename, debug)
return self.IsLocatableDataType(datatype)
def IsEnumeratedType(self, typename, debug = False):
if isinstance(typename, TupleType):
@@ -1822,7 +1822,7 @@
def IsNumType(self, typename, debug = False):
return self.IsOfType(typename, "ANY_NUM", debug) or\
self.IsOfType(typename, "ANY_BIT", debug)
def GetDataTypeRange(self, typename, debug = False):
range = DataTypeRange.get(typename)
@@ -1837,7 +1837,7 @@
elif basetype_content_type == "derived":
return self.GetDataTypeRange(basetype_content.getname(), debug)
def GetSubrangeBaseTypes(self, exclude, debug = False):
subrange_basetypes = DataTypeRange.keys()
@@ -1851,7 +1851,7 @@
[datatype.getname() for datatype in confnodetype["types"].getdataTypes()
if self.IsSubrangeType(datatype.getname(), exclude, debug)])
return subrange_basetypes
# Return Enumerated Values
def GetEnumeratedDataValues(self, typename = None, debug = False):
@@ -1861,7 +1861,7 @@
basetype_content = datatype_obj.baseType.getcontent()
basetype_content_type = basetype_content.getLocalTag()
if basetype_content_type == "enum":
- return [value.getname()
+ return [value.getname() for value in basetype_content.xpath(
namespaces=PLCOpenParser.NSMAP)]
@@ -1878,19 +1878,19 @@
#-------------------------------------------------------------------------------
# Project Element tag name computation functions
#-------------------------------------------------------------------------------
# Compute a data type name
def ComputeDataTypeName(self, datatype):
return "D::%s" % datatype
def ComputePouName(self, pou):
# Compute a pou transition name
def ComputePouTransitionName(self, pou, transition):
return "T::%s::%s" % (pou, transition)
# Compute a pou action name
def ComputePouActionName(self, pou, action):
return "A::%s::%s" % (pou, action)
@@ -1905,7 +1905,7 @@
def GetElementType(self, tagname):
words = tagname.split("::")
- return {"D" : ITEM_DATATYPE, "P" : ITEM_POU,
+ return {"D" : ITEM_DATATYPE, "P" : ITEM_POU, "T" : ITEM_TRANSITION, "A" : ITEM_ACTION,
"C" : ITEM_CONFIGURATION, "R" : ITEM_RESOURCE}[words[0]]
@@ -1963,7 +1963,7 @@
dimensions.append((dimension.getlower(), dimension.getupper()))
base_type = element_type.baseType.getcontent()
base_type_type = element_type.getLocalTag()
- element_infos["Type"] = ("array",
+ element_infos["Type"] = ("array", if base_type_type == "derived"
else base_type_type.upper(), dimensions)
@@ -1981,14 +1981,14 @@
infos["base_type"] = (basetype_content.getname()
if basetype_content_type == "derived"
else basetype_content_type.upper())
if datatype.initialValue is not None:
infos["initial"] = str(datatype.initialValue.getvalue())
# Change the data type informations
def SetDataTypeInfos(self, tagname, infos):
words = tagname.split("::")
@@ -2006,7 +2006,7 @@
datatype.baseType.setcontent(derived_datatype)
elif infos["type"] == "Subrange":
subrange = PLCOpenParser.CreateElement(
if infos["base_type"] in GetSubTypes("ANY_UINT")
else "subrangeSigned", "dataType")
datatype.baseType.setcontent(subrange)
@@ -2106,7 +2106,7 @@
datatype.initialValue = None
#-------------------------------------------------------------------------------
# Project opened Pous management functions
#-------------------------------------------------------------------------------
@@ -2132,7 +2132,7 @@
return project.getconfigurationResource(words[1], words[2])
# Return edited element name
def GetEditedElementName(self, tagname):
words = tagname.split("::")
@@ -2141,7 +2141,7 @@
# Return edited element name and type
def GetEditedElementType(self, tagname, debug = False):
words = tagname.split("::")
@@ -2183,14 +2183,14 @@
# Change the edited element text
def SetEditedElementText(self, tagname, text):
if self.Project is not None:
element = self.GetEditedElement(tagname)
# Return the edited element text
def GetEditedElementText(self, tagname, debug = False):
element = self.GetEditedElement(tagname, debug)
@@ -2236,13 +2236,13 @@
return element.tostring()
def GetEditedElementInstancesCopy(self, tagname, blocks_id = None, wires = None, debug = False):
element = self.GetEditedElement(tagname, debug)
- wires = dict([(wire, True)
+ wires = dict([(wire, True) if wire[0] in blocks_id and wire[1] in blocks_id])
copy_body = PLCOpenParser.CreateElement("body", "pou")
element.append(copy_body)
@@ -2257,20 +2257,20 @@
text += instance_copy.tostring()
element.remove(copy_body)
def GenerateNewName(self, tagname, name, format, start_idx=0, exclude={}, debug=False):
- names.update(dict([(varname.upper(), True)
+ names.update(dict([(varname.upper(), True) for varname in self.GetEditedElementVariables(tagname, debug)]))
words = tagname.split("::")
if words[0] in ["P","T","A"]:
element = self.GetEditedElement(tagname, debug)
if element is not None and element.getbodyType() not in ["ST", "IL"]:
for instance in element.getinstances():
- if isinstance(instance,
- (PLCOpenParser.GetElementClass("step", "sfcObjects"),
- PLCOpenParser.GetElementClass("connector", "commonObjects"),
+ if isinstance(instance, + (PLCOpenParser.GetElementClass("step", "sfcObjects"), + PLCOpenParser.GetElementClass("connector", "commonObjects"), PLCOpenParser.GetElementClass("continuation", "commonObjects"))):
names[instance.getname().upper()] = True
@@ -2290,37 +2290,37 @@
names[config.getname().upper()] = True
for resource in config.getresource():
names[resource.getname().upper()] = True
while name is None or names.get(name.upper(), False):
def PasteEditedElementInstances(self, tagname, text, new_pos, middle=False, debug=False):
element = self.GetEditedElement(tagname, debug)
element_name, element_type = self.GetEditedElementType(tagname, debug)
bodytype = element.getbodyType()
# Get edited element type scaling
project = self.GetProject(debug)
properties = project.getcontentHeader()
scaling = properties["scaling"][bodytype]
# Get ids already by all the instances in edited element
used_id = dict([(instance.getlocalId(), True) for instance in element.getinstances()])
instances, error = LoadPouInstances(text.encode("utf-8"), bodytype)
instances, error = [], ""
if error is not None or len(instances) == 0:
return _("Invalid plcopen element(s)!!!")
for instance in instances:
element.addinstance(instance)
@@ -2332,25 +2332,25 @@
if blocktype_infos["type"] != "function" and blockname is not None:
if element_type == "function":
return _("FunctionBlock \"%s\" can't be pasted in a Function!!!")%blocktype
- blockname = self.GenerateNewName(tagname,
+ blockname = self.GenerateNewName(tagname, exclude[blockname] = True
instance.setinstanceName(blockname)
self.AddEditedElementPouVar(tagname, blocktype, blockname)
elif instance_type == "step":
- stepname = self.GenerateNewName(tagname,
+ stepname = self.GenerateNewName(tagname, instance.setname(stepname)
localid = instance.getlocalId()
if not used_id.has_key(localid):
@@ -2363,7 +2363,7 @@
translate_id[localId] = idx
x, y, width, height = bbox.bounding_box()
@@ -2385,43 +2385,43 @@
new_pos = (max(30, new_pos[0]), max(30, new_pos[1]))
diff = (int(new_pos[0] - x), int(new_pos[1] - y))
for instance in instances:
connections.update(instance.updateConnectionsId(translate_id))
if getattr(instance, "setexecutionOrderId", None) is not None:
instance.setexecutionOrderId(0)
instance.translate(*diff)
return new_id, connections
def GetEditedElementInstancesInfos(self, tagname, debug = False):
element_instances = OrderedDict()
element = self.GetEditedElement(tagname, debug)
factory = BlockInstanceFactory(element_instances)
pou_block_instances_xslt_tree = etree.XSLT(
- pou_block_instances_xslt,
+ pou_block_instances_xslt, ("pou_block_instances_ns", name): getattr(factory, name)
for name in ["AddBlockInstance", "SetSpecificValues",
"AddInstanceConnection", "AddConnectionLink",
"AddLinkPoint", "AddAction"]})
pou_block_instances_xslt_tree(element)
def ClearEditedElementExecutionOrder(self, tagname):
element = self.GetEditedElement(tagname)
element.resetexecutionOrder()
def ResetEditedElementExecutionOrder(self, tagname):
element = self.GetEditedElement(tagname)
element.compileexecutionOrder()
def SetConnectionWires(self, connection, connector):
wires = connector.GetWires()
@@ -2443,7 +2443,7 @@
connection.setconnectionParameter(idx, None)
def GetVarTypeObject(self, var_type):
var_type_obj = PLCOpenParser.CreateElement("type", "variable")
if not var_type.startswith("ANY") and TypeHierarchy.get(var_type):
@@ -2455,7 +2455,7 @@
derived_type.setname(var_type)
var_type_obj.setcontent(derived_type)
def AddEditedElementPouVar(self, tagname, var_type, name,**args):
if self.Project is not None:
words = tagname.split("::")
@@ -2463,9 +2463,9 @@
pou = self.Project.getpou(words[1])
- self.GetVarTypeObject(var_type),
+ self.GetVarTypeObject(var_type),
def AddEditedElementPouExternalVar(self, tagname, var_type, name):
if self.Project is not None:
words = tagname.split("::")
@@ -2474,7 +2474,7 @@
self.GetVarTypeObject(var_type), name)
def ChangeEditedElementPouVar(self, tagname, old_type, old_name, new_type, new_name):
if self.Project is not None:
words = tagname.split("::")
@@ -2482,7 +2482,7 @@
pou = self.Project.getpou(words[1])
pou.changepouVar(old_type, old_name, new_type, new_name)
def RemoveEditedElementPouVar(self, tagname, type, name):
if self.Project is not None:
words = tagname.split("::")
@@ -2490,7 +2490,7 @@
pou = self.Project.getpou(words[1])
pou.removepouVar(type, name)
def AddEditedElementBlock(self, tagname, id, blocktype, blockname = None):
element = self.GetEditedElement(tagname)
@@ -2502,13 +2502,13 @@
block.setinstanceName(blockname)
self.AddEditedElementPouVar(tagname, blocktype, blockname)
element.addinstance(block)
def SetEditedElementBlockInfos(self, tagname, id, infos):
element = self.GetEditedElement(tagname)
block = element.getinstance(id)
old_name = block.getinstanceName()
old_type = block.gettypeName()
new_name = infos.get("name", old_name)
@@ -2571,7 +2571,7 @@
variable.addconnectionPointOut()
variable.connectionPointOut.setrelPositionXY(position.x, position.y)
def AddEditedElementVariable(self, tagname, id, var_type):
element = self.GetEditedElement(tagname)
@@ -2581,13 +2581,13 @@
INOUT: "inOutVariable"}[var_type], "fbdObjects")
element.addinstance(variable)
def SetEditedElementVariableInfos(self, tagname, id, infos):
element = self.GetEditedElement(tagname)
variable = element.getinstance(id)
for param, value in infos.items():
variable.setexpression(value)
@@ -2634,7 +2634,7 @@
CONTINUATION: "continuation"}[connection_type], "commonObjects")
connection.setlocalId(id)
element.addinstance(connection)
def SetEditedElementConnectionInfos(self, tagname, id, infos):
element = self.GetEditedElement(tagname)
@@ -2643,7 +2643,7 @@
for param, value in infos.items():
- connection.setname(value)
+ connection.setname(value) connection.setheight(value)
@@ -2668,7 +2668,7 @@
comment = PLCOpenParser.CreateElement("comment", "commonObjects")
element.addinstance(comment)
def SetEditedElementCommentInfos(self, tagname, id, infos):
element = self.GetEditedElement(tagname)
@@ -2693,7 +2693,7 @@
RIGHTRAIL: "rightPowerRail"}[powerrail_type], "ldObjects")
element.addinstance(powerrail)
def SetEditedElementPowerRailInfos(self, tagname, id, infos):
element = self.GetEditedElement(tagname)
@@ -2725,7 +2725,7 @@
powerrail.appendconnectionPointIn(connection)
connection.setrelPositionXY(position.x, position.y)
self.SetConnectionWires(connection, connector)
def AddEditedElementContact(self, tagname, id):
element = self.GetEditedElement(tagname)
@@ -2821,7 +2821,7 @@
step = PLCOpenParser.CreateElement("step", "sfcObjects")
element.addinstance(step)
def SetEditedElementStepInfos(self, tagname, id, infos):
element = self.GetEditedElement(tagname)
@@ -2864,14 +2864,14 @@
step.connectionPointOutAction.setrelPositionXY(position.x, position.y)
step.deleteconnectionPointOutAction()
def AddEditedElementTransition(self, tagname, id):
element = self.GetEditedElement(tagname)
transition = PLCOpenParser.CreateElement("transition", "sfcObjects")
transition.setlocalId(id)
element.addinstance(transition)
def SetEditedElementTransitionInfos(self, tagname, id, infos):
element = self.GetEditedElement(tagname)
@@ -2907,7 +2907,7 @@
elif infos.get("type", None) == "connection" and param == "connection" and value:
transition.setconditionContent("connection", None)
self.SetConnectionWires(transition.condition.content, value)
def AddEditedElementDivergence(self, tagname, id, divergence_type):
element = self.GetEditedElement(tagname)
@@ -2919,19 +2919,19 @@
divergence_type), "sfcObjects")
divergence.setlocalId(id)
element.addinstance(divergence)
PLCOpenParser.GetElementClass(divergence_type, "sfcObjects"))
for divergence_type in ["selectionDivergence", "simultaneousDivergence",
"selectionConvergence", "simultaneousConvergence"]]
def GetDivergenceType(self, divergence):
for divergence_type, divergence_class in self.DivergenceTypes:
if isinstance(divergence, divergence_class):
def SetEditedElementDivergenceInfos(self, tagname, id, infos):
element = self.GetEditedElement(tagname)
@@ -2975,14 +2975,14 @@
connection = PLCOpenParser.CreateElement("connectionPointOut", divergence_type)
divergence.appendconnectionPointOut(connection)
connection.setrelPositionXY(position.x, position.y)
def AddEditedElementJump(self, tagname, id):
element = self.GetEditedElement(tagname)
jump = PLCOpenParser.CreateElement("jumpStep", "sfcObjects")
element.addinstance(jump)
def SetEditedElementJumpInfos(self, tagname, id, infos):
element = self.GetEditedElement(tagname)
@@ -3005,14 +3005,14 @@
jump.addconnectionPointIn()
jump.connectionPointIn.setrelPositionXY(position.x, position.y)
self.SetConnectionWires(jump.connectionPointIn, value)
def AddEditedElementActionBlock(self, tagname, id):
element = self.GetEditedElement(tagname)
actionBlock = PLCOpenParser.CreateElement("actionBlock", "commonObjects")
actionBlock.setlocalId(id)
element.addinstance(actionBlock)
def SetEditedElementActionBlockInfos(self, tagname, id, infos):
element = self.GetEditedElement(tagname)
@@ -3035,7 +3035,7 @@
actionBlock.addconnectionPointIn()
actionBlock.connectionPointIn.setrelPositionXY(position.x, position.y)
self.SetConnectionWires(actionBlock.connectionPointIn, value)
def RemoveEditedElementInstance(self, tagname, id):
element = self.GetEditedElement(tagname)
@@ -3162,19 +3162,19 @@
self.CurrentElementEditing = None
def SaveXMLFile(self, filepath = None):
if not filepath and self.FilePath == "":
contentheader = {"modificationDateTime": datetime.datetime(*localtime()[:6])}
self.Project.setcontentHeader(contentheader)
SaveProject(self.Project, filepath)
SaveProject(self.Project, self.FilePath)
self.MarkProjectAsSaved()
self.SetFilePath(filepath)
@@ -3233,7 +3233,7 @@
self.ProjectSaved = False
if self.ProjectBuffer is not None and self.Buffering:
self.ProjectBuffer.Buffering(PLCOpenParser.Dumps(self.Project))
@@ -3245,7 +3245,7 @@
self.ProjectBuffer.CurrentSaved()
# Return if project is saved
def ProjectIsSaved(self):
if self.ProjectBuffer is not None:
@@ -3257,11 +3257,11 @@
if self.ProjectBuffer is not None:
self.Project = PLCOpenParser.Loads(self.ProjectBuffer.Previous())
if self.ProjectBuffer is not None:
self.Project = PLCOpenParser.Loads(self.ProjectBuffer.Next())
def GetBufferState(self):
if self.ProjectBuffer is not None:
first = self.ProjectBuffer.IsFirst() and not self.Buffering
--- a/ProjectController.py Mon May 26 14:44:03 2014 +0100
+++ b/ProjectController.py Fri Jun 06 18:30:49 2014 +0100
@@ -1,5 +1,5 @@
-Beremiz Project Controller
+Beremiz Project Controller @@ -63,14 +63,14 @@
class ProjectController(ConfigTreeNode, PLCControler):
- This class define Root object of the confnode tree.
+ This class define Root object of the confnode tree. - Managing project directory
- Handling PLCOpenEditor controler and view
- Loading user confnodes and instanciante them as children
# For root object, available Children Types are modules of the confnode packages.
@@ -87,15 +87,15 @@
"""+targets.GetTargetChoices()+"""
- <xsd:element name="Libraries" minOccurs="0">"""+(("""
+ </xsd:element>"""+((""" + <xsd:element name="Libraries" minOccurs="0"> """+"\n".join(['<xsd:attribute name='+
'"Enable_'+ libname + '_Library" '+
- 'type="xsd:boolean" use="optional" default="true"/>'
+ 'type="xsd:boolean" use="optional" default="true"/>' for libname,lib in features.libraries])+"""
- </xsd:complexType>""") if len(features.libraries)>0 else '<xsd:complexType/>') + """
+ </xsd:element>""") if len(features.libraries)>0 else '') + """ <xsd:attribute name="URI_location" type="xsd:string" use="optional" default=""/>
<xsd:attribute name="Disable_Extensions" type="xsd:boolean" use="optional" default="false"/>
@@ -104,7 +104,7 @@
EditorType = ProjectNodeEditor
def __init__(self, frame, logger):
PLCControler.__init__(self)
@@ -115,10 +115,10 @@
self.DebugValuesBuffers = []
self.SetAppFrame(frame, logger)
self.iec2c_path = os.path.join(base_folder, "matiec", "iec2c"+(".exe" if wx.Platform == '__WXMSW__' else ""))
self.ieclib_path = os.path.join(base_folder, "matiec", "lib")
# Setup debug information
self.IECdebug_lock = Lock()
@@ -147,7 +147,7 @@
@@ -156,7 +156,7 @@
Lib = GetClassImporter(clsname)()(self, libname, TypeStack)
TypeStack.append(Lib.GetTypes())
self.Libraries.append(Lib)
def SetAppFrame(self, frame, logger):
@@ -164,23 +164,23 @@
if self.DispatchDebugValuesTimer is not None:
self.DispatchDebugValuesTimer.Stop()
self.DispatchDebugValuesTimer = None
# Timer to pull PLC status
self.StatusTimer = wx.Timer(self.AppFrame, -1)
- self.AppFrame.Bind(wx.EVT_TIMER,
+ self.AppFrame.Bind(wx.EVT_TIMER, self.PullPLCStatusProc, self.StatusTimer)
if self._connector is not None:
frame.LogViewer.SetLogSource(self._connector)
self.StatusTimer.Start(milliseconds=500, oneShot=False)
# Timer to dispatch debug values to consumers
self.DispatchDebugValuesTimer = wx.Timer(self.AppFrame, -1)
- self.AppFrame.Bind(wx.EVT_TIMER,
+ self.AppFrame.Bind(wx.EVT_TIMER, self.DispatchDebugValuesProc, self.DispatchDebugValuesTimer)
self.RefreshConfNodesBlockLists()
def ResetAppFrame(self, logger):
@@ -188,7 +188,7 @@
self.AppFrame.Unbind(wx.EVT_TIMER, self.StatusTimer)
@@ -205,16 +205,16 @@
def GetCurrentLocation(self):
def GetCurrentName(self):
def _GetCurrentName(self):
@@ -223,10 +223,10 @@
def GetProjectName(self):
return os.path.split(self.ProjectPath)[1]
def GetDefaultTargetName(self):
if wx.Platform == '__WXMSW__':
@@ -242,15 +242,15 @@
target_name = self.GetDefaultTargetName()
target.setcontent(self.Parser.CreateElement(target_name, "TargetType"))
def GetParamsAttributes(self, path = None):
params = ConfigTreeNode.GetParamsAttributes(self, path)
if params[0]["name"] == "BeremizRoot":
for child in params[0]["children"]:
if child["name"] == "TargetType" and child["value"] == '':
- child.update(self.GetTarget().getElementInfos("TargetType"))
+ child.update(self.GetTarget().getElementInfos("TargetType"))
def SetParamsAttribute(self, path, value):
if path.startswith("BeremizRoot.TargetType.") and self.BeremizRoot.getTargetType().getcontent() is None:
self.BeremizRoot.setTargetType(self.GetTarget())
@@ -258,15 +258,15 @@
if path.startswith("BeremizRoot.Libraries."):
wx.CallAfter(self.RefreshConfNodesBlockLists)
# helper func to check project path write permission
def CheckProjectPathPerm(self, dosave=True):
if CheckPathPerm(self.ProjectPath):
if self.AppFrame is not None:
- dialog = wx.MessageDialog(self.AppFrame,
+ dialog = wx.MessageDialog(self.AppFrame, _('You must have permission to work on the project\nWork on a project copy ?'),
wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
answer = dialog.ShowModal()
@@ -277,7 +277,7 @@
self.AppFrame.RefreshPageTitles()
def _getProjectFilesPath(self, project_path=None):
if project_path is not None:
return os.path.join(project_path, "project_files")
@@ -285,7 +285,7 @@
if not os.path.exists(projectfiles_path):
os.mkdir(projectfiles_path)
def AddProjectDefaultConfiguration(self, config_name="config", res_name="resource1"):
self.ProjectAddConfiguration(config_name)
self.ProjectAddConfigurationResource(config_name, res_name)
@@ -299,7 +299,7 @@
# Verify that chosen folder is empty
if not os.path.isdir(ProjectPath) or len(os.listdir(ProjectPath)) > 0:
return _("Chosen folder isn't empty. You can't use it for a new project!")
{"projectName": _("Unnamed"),
@@ -308,7 +308,7 @@
"companyName": _("Unknown"),
"creationDateTime": datetime(*localtime()[:6])})
self.AddProjectDefaultConfiguration()
# Change XSD into class members
@@ -320,7 +320,7 @@
# this will create files base XML files
def LoadProject(self, ProjectPath, BuildPath=None):
Load a project contained in a folder
@@ -357,30 +357,30 @@
#Load and init all the children
self.RefreshConfNodesBlockLists()
if os.path.exists(self._getBuildPath()):
self.EnableMethod("_Clean", True)
if os.path.isfile(self._getIECcodepath()):
self.ShowMethod("_showIECcode", True)
self.UpdateMethodsFromPLCStatus()
def RecursiveConfNodeInfos(self, confnode):
for CTNChild in confnode.IECSortedChildren():
{"name": "%s: %s" % (CTNChild.GetFullIEC_Channel(),
"tagname": CTNChild.CTNFullName(),
"icon": CTNChild.GetIconName(),
"values": self.RecursiveConfNodeInfos(CTNChild)})
def GetProjectInfos(self):
infos = PLCControler.GetProjectInfos(self)
configurations = infos["values"].pop(-1)
@@ -394,23 +394,23 @@
infos["values"].append(resources)
infos["values"].extend(self.RecursiveConfNodeInfos(self))
def SaveProject(self, from_project_path=None):
if self.CheckProjectPathPerm(False):
if from_project_path is not None:
old_projectfiles_path = self._getProjectFilesPath(from_project_path)
if os.path.isdir(old_projectfiles_path):
- shutil.copytree(old_projectfiles_path,
+ shutil.copytree(old_projectfiles_path, self._getProjectFilesPath(self.ProjectPath))
self.SaveXMLFile(os.path.join(self.ProjectPath, 'plc.xml'))
result = self.CTNRequestSave(from_project_path)
self.logger.write_error(result)
# Ask user to choose a path with write permissions
if wx.Platform == '__WXMSW__':
@@ -444,12 +444,12 @@
for lib in self.Libraries:
- res=lib.Generate_C(buildpath,self._VariablesList,LibIECCflags)
+ res=lib.Generate_C(buildpath,self._VariablesList,LibIECCflags) LocatedCCodeAndFlags.append(res[:2])
return map(list,zip(*LocatedCCodeAndFlags))+[tuple(Extras)]
# Update PLCOpenEditor ConfNode Block types from loaded confnodes
def RefreshConfNodesBlockLists(self):
if getattr(self, "Children", None) is not None:
@@ -458,7 +458,7 @@
if self.AppFrame is not None:
self.AppFrame.RefreshLibraryPanel()
self.AppFrame.RefreshEditor()
# Update a PLCOpenEditor Pou variable location
def UpdateProjectVariableLocation(self, old_leading, new_leading):
self.Project.updateElementAddress(old_leading, new_leading)
@@ -469,13 +469,13 @@
self.AppFrame.RefreshFileMenu()
self.AppFrame.RefreshEditMenu()
wx.CallAfter(self.AppFrame.RefreshEditor)
def GetVariableLocationTree(self):
This function is meant to be overridden by confnodes.
It should returns an list of dictionaries
- IEC_type is an IEC type like BOOL/BYTE/SINT/...
- location is a string of this variable's location, like "%IX0.0.0"
@@ -483,13 +483,13 @@
for child in self.IECSortedChildren():
children.append(child.GetVariableLocationTree())
return os.path.split(__file__)[0]
def CTNPath(self, CTNName=None):
def ConfNodeXmlFilePath(self, CTNName=None):
return os.path.join(self.CTNPath(CTNName), "beremiz.xml")
@@ -515,26 +515,26 @@
# Create a build path in temp folder
self.DefaultBuildPath = os.path.join(tempfile.mkdtemp(), os.path.basename(self.ProjectPath), "build")
if not os.path.exists(self.DefaultBuildPath):
os.makedirs(self.DefaultBuildPath)
return self.DefaultBuildPath
def _getExtraFilesPath(self):
return os.path.join(self._getBuildPath(), "extra_files")
def _getIECcodepath(self):
# define name for IEC code file
return os.path.join(self._getBuildPath(), "plc.st")
def _getIECgeneratedcodepath(self):
# define name for IEC generated code file
return os.path.join(self._getBuildPath(), "generated_plc.st")
def _getIECrawcodepath(self):
# define name for IEC raw code file
return os.path.join(self.CTNPath(), "raw_plc.st")
filepath = os.path.join(self._getBuildPath(),"LOCATED_VARIABLES.h")
@@ -546,24 +546,29 @@
# This regular expression parses the lines genereated by IEC2C
LOCATED_MODEL = re.compile("__LOCATED_VAR\((?P<IEC_TYPE>[A-Z]*),(?P<NAME>[_A-Za-z0-9]*),(?P<DIR>[QMI])(?:,(?P<SIZE>[XBWDL]))?,(?P<LOC>[,0-9]*)\)")
result = LOCATED_MODEL.match(line)
resdict = result.groupdict()
# rewrite string for variadic location as a tuple of integers
resdict['LOC'] = tuple(map(int,resdict['LOC'].split(',')))
- # set located size to 'X' if not given
+ # set located size to 'X' if not given # finally store into located variable list
locations.append(resdict)
def GetConfNodeGlobalInstances(self):
return self._GlobalInstances()
def _Generate_SoftPLC(self):
+ if self._Generate_PLC_ST(): + return self._Compile_ST_to_SoftPLC() + def _Generate_PLC_ST(self): Generate SoftPLC ST/IL/SFC code out of PLCOpenEditor controller, and compile it with IEC2C
@param buildpath: path where files should be created
@@ -571,9 +576,8 @@
# Update PLCOpenEditor ConfNode Block types before generate ST code
self.RefreshConfNodesBlockLists()
self.logger.write(_("Generating SoftPLC IEC-61131 ST/IL/SFC code...\n"))
- buildpath = self._getBuildPath()
# ask PLCOpenEditor controller to write ST/IL/SFC code file
program, errors, warnings = self.GenerateProgram(self._getIECgeneratedcodepath())
@@ -599,22 +603,25 @@
plc_file = open(self._getIECcodepath(), "a")
plc_file.write(open(self._getIECgeneratedcodepath(), "r").read())
+ def _Compile_ST_to_SoftPLC(self): self.logger.write(_("Compiling IEC Program into C code...\n"))
+ buildpath = self._getBuildPath() # Now compile IEC code into many C files
- # files are listed to stdout, and errors to stderr.
+ # files are listed to stdout, and errors to stderr. status, result, err_result = ProcessLogger(
"\"%s\" -f -I \"%s\" -T \"%s\" \"%s\""%(
no_stdout=True, no_stderr=True).spin()
# parse iec2c's error message. if it contains a line number,
# then print those lines from the generated IEC file.
for err_line in err_result.split('\n'):
@@ -624,7 +631,7 @@
first_line, first_column, last_line, last_column, error = m_result.groups()
first_line, last_line = int(first_line), int(last_line)
f = open(self._getIECcodepath())
@@ -640,10 +647,10 @@
self.logger.write_warning("%04d: %s" % (i, line))
self.logger.write_error(_("Error : IEC to C compiler returned %d\n")%status)
# Now extract C files of stdout
C_files = [ fname for fname in result.splitlines() if fname[-2:]==".c" or fname[-2:]==".C" ]
# remove those that are not to be compiled because included by others
@@ -679,7 +686,7 @@
targetname = self.GetTarget().getcontent().getLocalTag()
targetclass = targets.GetBuilder(targetname)
if self._builder is None or not isinstance(self._builder,targetclass):
self._builder = targetclass(self)
@@ -703,20 +710,20 @@
# C CODE GENERATION METHODS
#######################################################################
def CTNGenerate_C(self, buildpath, locations):
- Return C code generated by iec2c compiler
+ Return C code generated by iec2c compiler when _generate_softPLC have been called
@param locations: ignored
@return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
- return ([(C_file_name, self.plcCFLAGS)
- for C_file_name in self.PLCGeneratedCFiles ],
+ return ([(C_file_name, self.plcCFLAGS) + for C_file_name in self.PLCGeneratedCFiles ], False) # do not expose retreive/publish calls
def ResetIECProgramsAndVariables(self):
Reset variable and program list that are parsed from
@@ -743,7 +750,7 @@
for line in open(csvfile,'r').xreadlines():
@@ -754,7 +761,7 @@
elif len(strippedline) > 0 and len(ListGroup) > 0:
ListGroup[-1].append(strippedline)
# first section contains programs
for line in ListGroup[0]:
# Split and Maps each field to dictionnary entries
@@ -763,7 +770,7 @@
attrs["C_path"] = '__'.join(attrs["C_path"].split(".",2)[1:])
# Push this dictionnary into result.
self._ProgramList.append(attrs)
# second section contains all variables
for line in ListGroup[1]:
@@ -776,7 +783,7 @@
parts = [config_FB] + parts[2:]
attrs["C_path"] = '.'.join(parts)
attrs["C_path"] = '__'.join(parts[1:])
attrs["C_path"] = '__'.join(parts)
@@ -788,11 +795,11 @@
IEC_path=attrs["IEC_path"]
self._IECPathToIdx[IEC_path]=(Idx, attrs["type"])
# third section contains ticktime
- self._Ticktime = int(ListGroup[2][0])
+ self._Ticktime = int(ListGroup[2][0]) self.logger.write_error(_("Cannot open/parse VARIABLES.csv!\n"))
self.logger.write_error(traceback.format_exc())
@@ -818,7 +825,7 @@
"MEM":"extern __IEC_%(type)s_p %(C_path)s;",
"OUT":"extern __IEC_%(type)s_p %(C_path)s;",
"VAR":"extern __IEC_%(type)s_t %(C_path)s;",
- "FB":"extern %(type)s %(C_path)s;"}[v["vartype"]]%v
+ "FB":"extern %(type)s %(C_path)s;"}[v["vartype"]]%v for v in self._VariablesList if v["C_path"].find('.')<0]),
"for_each_variable_do_code":"\n".join([
{"EXT":" (*fp)((void*)&(%(C_path)s),%(type)s_P_ENUM);\n",
@@ -836,9 +843,9 @@
"OUT":" return %(type)s_O_ENUM;\n",
"VAR":" return %(type)s_ENUM;\n"}[v["vartype"]]%v
for v in self._VariablesList if v["vartype"] != "FB" and v["type"] in DebugTypesSize ])}
def Generate_plc_main(self):
Use confnodes layout given in LocationCFilesAndCFLAGS to
@@ -848,7 +855,7 @@
# in retreive, publish, init, cleanup
locstrs = map(lambda x:"_".join(map(str,x)),
[loc for loc,Cfiles,DoCalls in self.LocationCFilesAndCFLAGS if loc and DoCalls])
# Generate main, based on template
if not self.BeremizRoot.getDisable_Extensions():
plc_main_code = targets.GetCode("plc_main_head") % {
@@ -882,7 +889,7 @@
plc_main_code += targets.GetCode("plc_main_tail")
Method called by user to (re)build SoftPLC and confnode tree
@@ -890,7 +897,7 @@
if self.AppFrame is not None:
self.AppFrame.ClearErrors()
self._CloseView(self._IECCodeView)
buildpath = self._getBuildPath()
# Eventually create build dir
@@ -908,21 +915,52 @@
# If IEC code gen fail, bail out.
- self.logger.write_error(_("IEC-61131-3 code generation failed !\n"))
+ self.logger.write_error(_("PLC code generation failed !\n")) # Reset variable and program list that are parsed from
# CSV file generated by IEC2C compiler.
self.ResetIECProgramsAndVariables()
+ # Collect platform specific C code + # Code and other files from extension + if not self._Generate_runtime(): + # Get current or fresh builder + builder = self.GetBuilder() + self.logger.write_error(_("Fatal : cannot get builder.\n")) + if not builder.build() : + self.logger.write_error(_("C Build failed.\n")) + self.logger.write_error(_("C Build crashed !\n")) + self.logger.write_error(traceback.format_exc()) + self.logger.write(_("Successfully built.\n")) + # Update GUI status about need for transfer + self.CompareLocalAndRemotePLC() + def _Generate_runtime(self): + buildpath = self._getBuildPath() # Generate C code and compilation params from confnode hierarchy
CTNLocationCFilesAndCFLAGS, CTNLDFLAGS, CTNExtraFiles = self._Generate_C(
self.PLCGeneratedLocatedVars)
- self.logger.write_error(_("Runtime extensions C code generation failed !\n"))
+ self.logger.write_error(_("Runtime IO extensions C code generation failed !\n")) self.logger.write_error(traceback.format_exc())
@@ -931,7 +969,7 @@
LibCFilesAndCFLAGS, LibLDFLAGS, LibExtraFiles = self.GetLibrariesCCode(buildpath)
- self.logger.write_error(_("Runtime extensions C code generation failed !\n"))
+ self.logger.write_error(_("Runtime library extensions C code generation failed !\n")) self.logger.write_error(traceback.format_exc())
@@ -939,7 +977,7 @@
self.LocationCFilesAndCFLAGS = CTNLocationCFilesAndCFLAGS + LibCFilesAndCFLAGS
self.LDFLAGS = CTNLDFLAGS + LibLDFLAGS
ExtraFiles = CTNExtraFiles + LibExtraFiles
# Get temporary directory path
extrafilespath = self._getExtraFilesPath()
@@ -953,7 +991,7 @@
open(fpath, "wb").write(fobject.read())
# Now we can forget ExtraFiles (will close files object)
# Header file for extensions
open(os.path.join(buildpath,"beremiz.h"), "w").write(targets.GetHeader())
@@ -978,32 +1016,9 @@
self.logger.write_error(traceback.format_exc())
self.logger.write(_("C code generated successfully.\n"))
- # Get current or fresh builder
- builder = self.GetBuilder()
- self.logger.write_error(_("Fatal : cannot get builder.\n"))
- if not builder.build() :
- self.logger.write_error(_("C Build failed.\n"))
- self.logger.write_error(_("C Build crashed !\n"))
- self.logger.write_error(traceback.format_exc())
- self.logger.write(_("Successfully built.\n"))
- # Update GUI status about need for transfer
- self.CompareLocalAndRemotePLC()
def ShowError(self, logger, from_location, to_location):
chunk_infos = self.GetChunkInfos(from_location, to_location)
for infos, (start_row, start_col) in chunk_infos:
@@ -1011,7 +1026,7 @@
end = (to_location[0] - start_row, to_location[1] - start_col)
if self.AppFrame is not None:
self.AppFrame.ShowError(infos, start, end)
self._OpenView("IEC code")
@@ -1019,20 +1034,20 @@
def _editIECrawcode(self):
self._OpenView("IEC raw code")
def _OpenProjectFiles(self):
self._OpenView("Project Files")
def _OpenFileEditor(self, filepath):
def _OpenView(self, name=None, onlyopened=False):
if self._IECCodeView is None:
plc_file = self._getIECcodepath()
self._IECCodeView = IECCodeViewer(self.AppFrame.TabsOpened, "", self.AppFrame, None, instancepath=name)
self._IECCodeView.SetTextSyntax("ALL")
self._IECCodeView.SetKeywords(IEC_KEYWORDS)
@@ -1043,78 +1058,78 @@
self._IECCodeView.SetText(text = text)
self._IECCodeView.SetIcon(GetBitmap("ST"))
setattr(self._IECCodeView, "_OnClose", self.OnCloseEditor)
if self._IECCodeView is not None:
self.AppFrame.EditProjectElement(self._IECCodeView, name)
elif name == "IEC raw code":
if self._IECRawCodeView is None:
controler = MiniTextControler(self._getIECrawcodepath(), self)
self._IECRawCodeView = IECCodeViewer(self.AppFrame.TabsOpened, "", self.AppFrame, controler, instancepath=name)
self._IECRawCodeView.SetTextSyntax("ALL")
self._IECRawCodeView.SetKeywords(IEC_KEYWORDS)
self._IECRawCodeView.RefreshView()
self._IECRawCodeView.SetIcon(GetBitmap("ST"))
setattr(self._IECRawCodeView, "_OnClose", self.OnCloseEditor)
if self._IECRawCodeView is not None:
self.AppFrame.EditProjectElement(self._IECRawCodeView, name)
return self._IECRawCodeView
elif name == "Project Files":
if self._ProjectFilesView is None:
self._ProjectFilesView = FileManagementPanel(self.AppFrame.TabsOpened, self, name, self._getProjectFilesPath(), True)
for extension, name, editor in features.file_editors:
if extension not in extensions:
extensions.append(extension)
- self._ProjectFilesView.SetEditableFileExtensions(extensions)
+ self._ProjectFilesView.SetEditableFileExtensions(extensions) if self._ProjectFilesView is not None:
self.AppFrame.EditProjectElement(self._ProjectFilesView, name)
return self._ProjectFilesView
elif name is not None and name.find("::") != -1:
filepath, editor_name = name.split("::")
if not self._FileEditors.has_key(filepath):
if os.path.isfile(filepath):
file_extension = os.path.splitext(filepath)[1]
editors = dict([(edit_name, edit_class)
for extension, edit_name, edit_class in features.file_editors
if extension == file_extension])
editor_name = editors.keys()[0]
- dialog = wx.SingleChoiceDialog(self.AppFrame,
- _("Select an editor:"), _("Editor selection"),
+ dialog = wx.SingleChoiceDialog(self.AppFrame, + _("Select an editor:"), _("Editor selection"), names, wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL)
if dialog.ShowModal() == wx.ID_OK:
editor_name = names[dialog.GetSelection()]
name = "::".join([filepath, editor_name])
editor = editors[editor_name]()
self._FileEditors[filepath] = editor(self.AppFrame.TabsOpened, self, name, self.AppFrame)
self._FileEditors[filepath].SetIcon(GetBitmap("FILE"))
if isinstance(self._FileEditors[filepath], DebugViewer):
self._FileEditors[filepath].SetDataProducer(self)
if self._FileEditors.has_key(filepath):
editor = self._FileEditors[filepath]
self.AppFrame.EditProjectElement(editor, editor.GetTagName())
return self._FileEditors.get(filepath)
return ConfigTreeNode._OpenView(self, self.CTNName(), onlyopened)
@@ -1147,7 +1162,7 @@
if self.AppFrame is not None:
self.AppFrame.LogViewer.SetLogCounters(log_count)
def UpdateMethodsFromPLCStatus(self):
if self._connector is not None:
@@ -1184,16 +1199,16 @@
self.AppFrame.ConnectionStatusBar.SetStatusText(
_("Connected to URI: %s") % self.BeremizRoot.getURI_location().strip(), 1)
self.AppFrame.ConnectionStatusBar.SetStatusText(_(status), 2)
def PullPLCStatusProc(self, event):
self.UpdateMethodsFromPLCStatus()
def SnapshotAndResetDebugValuesBuffers(self):
- buffers, self.DebugValuesBuffers = (self.DebugValuesBuffers,
+ buffers, self.DebugValuesBuffers = (self.DebugValuesBuffers, [list() for iec_path in self.TracedIECPath])
ticks, self.DebugTicks = self.DebugTicks, []
def RegisterDebugVarToConnector(self):
@@ -1208,10 +1223,10 @@
# This variable is not needed anymore!
IECPathsToPop.append(IECPath)
elif IECPath != "__tick__":
Idx, IEC_Type = self._IECPathToIdx.get(IECPath,(None,None))
- if IEC_Type in DebugTypesSize:
+ if IEC_Type in DebugTypesSize: Idxs.append((Idx, IEC_Type, fvalue, IECPath))
self.logger.write_warning(_("Debug: Unsupported type to debug '%s'\n")%IEC_Type)
@@ -1229,14 +1244,14 @@
self._connector.SetTraceVariablesList([])
self.SnapshotAndResetDebugValuesBuffers()
self.IECdebug_lock.release()
return self.previous_plcstate == "Started"
def ReArmDebugRegisterTimer(self):
if self.DebugTimer is not None:
# Prevent to call RegisterDebugVarToConnector when PLC is not started
# If an output location var is forced it's leads to segmentation fault in runtime
# Links between PLC located variables and real variables are not ready
@@ -1250,16 +1265,16 @@
def GetDebugIECVariableType(self, IECPath):
Idx, IEC_Type = self._IECPathToIdx.get(IECPath,(None,None))
def SubscribeDebugIECVariable(self, IECPath, callableobj, buffer_list=False, *args, **kwargs):
Dispatching use a dictionnary linking IEC variable paths
- to a WeakKeyDictionary linking
+ to a WeakKeyDictionary linking weakly referenced callables to optionnal args
if IECPath != "__tick__" and not self._IECPathToIdx.has_key(IECPath):
self.IECdebug_lock.acquire()
# If no entry exist, create a new one with a fresh WeakKeyDictionary
IECdebug_data = self.IECdebug_datas.get(IECPath, None)
@@ -1273,13 +1288,13 @@
self.IECdebug_datas[IECPath] = IECdebug_data
IECdebug_data[4] |= buffer_list
IECdebug_data[0][callableobj]=(buffer_list, args, kwargs)
self.IECdebug_lock.release()
self.ReArmDebugRegisterTimer()
def UnsubscribeDebugIECVariable(self, IECPath, callableobj):
@@ -1292,7 +1307,7 @@
IECdebug_data[4] = reduce(
- [buffer_list for buffer_list,args,kwargs
+ [buffer_list for buffer_list,args,kwargs in IECdebug_data[0].itervalues()],
self.IECdebug_lock.release()
@@ -1309,33 +1324,33 @@
def ForceDebugIECVariable(self, IECPath, fvalue):
if not self.IECdebug_datas.has_key(IECPath):
self.IECdebug_lock.acquire()
# If no entry exist, create a new one with a fresh WeakKeyDictionary
IECdebug_data = self.IECdebug_datas.get(IECPath, None)
IECdebug_data[2] = "Forced"
IECdebug_data[3] = fvalue
self.IECdebug_lock.release()
self.ReArmDebugRegisterTimer()
def ReleaseDebugIECVariable(self, IECPath):
if not self.IECdebug_datas.has_key(IECPath):
self.IECdebug_lock.acquire()
# If no entry exist, create a new one with a fresh WeakKeyDictionary
IECdebug_data = self.IECdebug_datas.get(IECPath, None)
IECdebug_data[2] = "Registered"
self.IECdebug_lock.release()
self.ReArmDebugRegisterTimer()
def CallWeakcallables(self, IECPath, function_name, *cargs):
data_tuple = self.IECdebug_datas.get(IECPath, None)
if data_tuple is not None:
@@ -1415,7 +1430,7 @@
self.CallWeakcallables(IECPath, "NewValues", debug_ticks, values)
self.CallWeakcallables("__tick__", "NewDataAvailable", debug_ticks)
delay = time.time() - start_time
next_refresh = max(REFRESH_PERIOD - delay, 0.2 * delay)
if self.DispatchDebugValuesTimer is not None and self.DebugThread is not None:
@@ -1437,7 +1452,7 @@
if self.DispatchDebugValuesTimer is not None:
self.DispatchDebugValuesTimer.Stop()
- def _connect_debug(self):
+ def _connect_debug(self): self.previous_plcstate = None
self.AppFrame.ResetGraphicViewers()
@@ -1448,7 +1463,7 @@
if self.DebugThread is None:
self.DebugThread = Thread(target=self.DebugThreadProc)
@@ -1460,7 +1475,7 @@
self.logger.write_error(_("Couldn't start PLC !\n"))
wx.CallAfter(self.UpdateMethodsFromPLCStatus)
@@ -1470,7 +1485,7 @@
# debugthread should die on his own
wx.CallAfter(self.UpdateMethodsFromPLCStatus)
def _SetConnector(self, connector, update_status=True):
@@ -1493,7 +1508,7 @@
if self._connector is not None:
self.logger.write_error(_("Already connected. Please disconnect\n"))
@@ -1512,7 +1527,7 @@
self.logger.write_error(_("Local service discovery failed!\n"))
self.logger.write_error(traceback.format_exc())
# Nothing choosed or cancel button
if uri is None or answer == wx.ID_CANCEL:
self.logger.write_error(_("Connection canceled!\n"))
@@ -1529,7 +1544,7 @@
self.AppFrame.RefreshFileMenu()
self.AppFrame.RefreshEditMenu()
self.AppFrame.RefreshPageTitles()
self._SetConnector(connectors.ConnectorFactory(uri, self))
@@ -1547,7 +1562,7 @@
self.ShowMethod("_Transfer", True)
self.CompareLocalAndRemotePLC()
# Init with actual PLC status and print it
self.UpdateMethodsFromPLCStatus()
if self.previous_plcstate is not None:
@@ -1556,7 +1571,7 @@
#self.logger.write(_("PLC is %s\n")%status)
if self.previous_plcstate in ["Started","Stopped"]:
if self.DebugAvailable() and self.GetIECProgramsAndVariables():
self.logger.write(_("Debugger ready\n"))
@@ -1590,12 +1605,12 @@
- # Get the last build PLC's
+ # Get the last build PLC's MD5 = self.GetLastBuildMD5()
- # Check if md5 file is empty : ask user to build PLC
+ # Check if md5 file is empty : ask user to build PLC self.logger.write_error(_("Failed : Must build before transfer.\n"))
@@ -1609,12 +1624,12 @@
for extrafilespath in [self._getExtraFilesPath(),
self._getProjectFilesPath()]:
- [(name, open(os.path.join(extrafilespath, name),
+ [(name, open(os.path.join(extrafilespath, name), for name in os.listdir(extrafilespath)])
builder = self.GetBuilder()
--- a/controls/VariablePanel.py Mon May 26 14:44:03 2014 +0100
+++ b/controls/VariablePanel.py Fri Jun 06 18:30:49 2014 +0100
@@ -2,7 +2,7 @@
#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
-#based on the plcopen standard.
+#based on the plcopen standard. #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
@@ -30,7 +30,7 @@
-from plcopen.structures import LOCATIONDATATYPES, TestIdentifier, IEC_KEYWORDS
+from plcopen.structures import LOCATIONDATATYPES, TestIdentifier, IEC_KEYWORDS, DefaultType from graphics.GraphicCommons import REFRESH_HIGHLIGHT_PERIOD, ERROR_HIGHLIGHT
from dialogs.ArrayTypeDialog import ArrayTypeDialog
from CustomGrid import CustomGrid
@@ -43,13 +43,7 @@
#-------------------------------------------------------------------------------
-def AppendMenu(parent, help, id, kind, text):
- if wx.VERSION >= (2, 6, 0):
- parent.Append(help=help, id=id, kind=kind, text=text)
- parent.Append(helpString=help, id=id, kind=kind, item=text)
-[TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE,
+[TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE, SCALING, PAGETITLES
@@ -73,8 +67,8 @@
def GetFilterChoiceTransfer():
- return {_("All"): _("All"), _("Interface"): _("Interface"),
- _(" Input"): _("Input"), _(" Output"): _("Output"), _(" InOut"): _("InOut"),
+ return {_("All"): _("All"), _("Interface"): _("Interface"), + _(" Input"): _("Input"), _(" Output"): _("Output"), _(" InOut"): _("InOut"), _(" External"): _("External"), _("Variables"): _("Variables"), _(" Local"): _("Local"),
_(" Temp"): _("Temp"), _("Global"): _("Global")}#, _("Access") : _("Access")}
VARIABLE_CHOICES_DICT = dict([(_(_class), _class) for _class in GetFilterChoiceTransfer().iterkeys()])
@@ -97,7 +91,7 @@
#-------------------------------------------------------------------------------
class VariableTable(CustomTable):
A custom wx.grid.Grid Table using user supplied data
@@ -105,7 +99,7 @@
# The base class must be initialized *first*
CustomTable.__init__(self, parent, data, colnames)
def GetValueByName(self, row, colname):
if row < self.GetNumberRows():
return getattr(self.data[row], colname)
@@ -113,7 +107,7 @@
def SetValueByName(self, row, colname, value):
if row < self.GetNumberRows():
setattr(self.data[row], colname, value)
def GetValue(self, row, col):
if row < self.GetNumberRows():
@@ -130,7 +124,7 @@
if colname in ["Class", "Option"]:
def SetValue(self, row, col, value):
if col < len(self.colnames):
colname = self.GetColLabelValue(col, False)
@@ -209,10 +203,10 @@
editor.SetParameters(",".join([_(choice) for choice in self.Parent.ClassList if choice not in excluded]))
elif colname != "Documentation":
grid.SetReadOnly(row, col, True)
grid.SetCellEditor(row, col, editor)
grid.SetCellRenderer(row, col, renderer)
if colname == "Location" and LOCATION_MODEL.match(self.GetValueByName(row, colname)) is None:
highlight_colours = ERROR_HIGHLIGHT
@@ -223,23 +217,23 @@
#-------------------------------------------------------------------------------
# Variable Panel Drop Target
-#-------------------------------------------------------------------------------
+#------------------------------------------------------------------------------- class VariableDropTarget(wx.TextDropTarget):
This allows dragging a variable location from somewhere to the Location
column of a variable row.
The drag source should be a TextDataObject containing a Python tuple like:
('%ID0.0.0', 'location', 'REAL')
c_ext/CFileEditor.py has an example of this (you can drag a C extension
variable to the Location column of the variable panel).
def __init__(self, parent):
wx.TextDropTarget.__init__(self)
self.ParentWindow = parent
def OnDropText(self, x, y, data):
self.ParentWindow.ParentWindow.Select()
x, y = self.ParentWindow.VariablesGrid.CalcUnscrolledPosition(x, y)
@@ -248,7 +242,7 @@
element_type = self.ParentWindow.ElementType
message = _("Invalid value \"%s\" for variable grid element")%data
@@ -267,12 +261,12 @@
variable_type = self.ParentWindow.Table.GetValueByName(row, "Type")
base_type = self.ParentWindow.Controler.GetBaseType(variable_type)
if values[2] is not None:
base_location_type = self.ParentWindow.Controler.GetBaseType(values[2])
if values[2] != variable_type and base_type != base_location_type:
message = _("Incompatible data types between \"%s\" and \"%s\"")%(values[2], variable_type)
if not location.startswith("%"):
if location[0].isdigit() and base_type != "BOOL":
@@ -282,9 +276,9 @@
elif base_type not in LOCATIONDATATYPES[location[0]]:
message = _("Incompatible size of data between \"%s\" and \"%s\"")%(location, variable_type)
- dialog = wx.SingleChoiceDialog(self.ParentWindow.ParentWindow.ParentWindow,
- _("Select a variable class:"), _("Variable class"),
- ["Input", "Output", "Memory"],
+ dialog = wx.SingleChoiceDialog(self.ParentWindow.ParentWindow.ParentWindow, + _("Select a variable class:"), _("Variable class"), + ["Input", "Output", "Memory"], wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL)
if dialog.ShowModal() == wx.ID_OK:
selected = dialog.GetSelection()
@@ -299,7 +293,7 @@
location = "%Q" + location
location = "%M" + location
self.ParentWindow.Table.SetValue(row, col, location)
self.ParentWindow.Table.ResetView(self.ParentWindow.VariablesGrid)
@@ -311,7 +305,7 @@
self.ParentWindow.Table.SetValue(row, col, values[0])
self.ParentWindow.Table.ResetView(self.ParentWindow.VariablesGrid)
self.ParentWindow.SaveValues()
- elif (element_type not in ["config", "resource", "function"] and values[1] == "Global" and
+ elif (element_type not in ["config", "resource", "function"] and values[1] == "Global" and self.ParentWindow.Filter in ["All", "Interface", "External"] or
element_type != "function" and values[1] == "location"):
if values[1] == "location":
@@ -319,11 +313,11 @@
tagname = self.ParentWindow.GetTagName()
- if var_name.upper() in [name.upper()
+ if var_name.upper() in [name.upper() for name in self.ParentWindow.Controler.\
GetProjectPouNames(self.ParentWindow.Debug)]:
message = _("\"%s\" pou already exists!")%var_name
- elif not var_name.upper() in [name.upper()
+ elif not var_name.upper() in [name.upper() for name in self.ParentWindow.Controler.\
GetEditedElementVariables(tagname, self.ParentWindow.Debug)]:
var_infos = self.ParentWindow.DefaultValue.copy()
@@ -332,9 +326,9 @@
if values[1] == "location":
if not location.startswith("%"):
- dialog = wx.SingleChoiceDialog(self.ParentWindow.ParentWindow.ParentWindow,
- _("Select a variable class:"), _("Variable class"),
- ["Input", "Output", "Memory"],
+ dialog = wx.SingleChoiceDialog(self.ParentWindow.ParentWindow.ParentWindow, + _("Select a variable class:"), _("Variable class"), + ["Input", "Output", "Memory"], wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL)
if dialog.ShowModal() == wx.ID_OK:
selected = dialog.GetSelection()
@@ -354,7 +348,7 @@
- if not var_name.upper() in [name.upper()
+ if not var_name.upper() in [name.upper() for name in self.ParentWindow.Controler.\
GetConfigurationVariableNames(configs[0])]:
self.ParentWindow.Controler.AddConfigurationGlobalVar(
@@ -372,10 +366,10 @@
self.ParentWindow.Values.append(var_infos)
self.ParentWindow.SaveValues()
self.ParentWindow.RefreshValues()
wx.CallAfter(self.ShowMessage, message)
def ShowMessage(self, message):
message = wx.MessageDialog(self.ParentWindow, message, _("Error"), wx.OK|wx.ICON_ERROR)
@@ -383,85 +377,85 @@
#-------------------------------------------------------------------------------
-#-------------------------------------------------------------------------------
+#------------------------------------------------------------------------------- class VariablePanel(wx.Panel):
def __init__(self, parent, window, controler, element_type, debug=False):
wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL)
self.MainSizer = wx.FlexGridSizer(cols=1, hgap=10, rows=2, vgap=0)
self.MainSizer.AddGrowableCol(0)
self.MainSizer.AddGrowableRow(1)
controls_sizer = wx.FlexGridSizer(cols=10, hgap=5, rows=1, vgap=5)
controls_sizer.AddGrowableCol(5)
controls_sizer.AddGrowableRow(0)
self.MainSizer.AddSizer(controls_sizer, border=5, flag=wx.GROW|wx.ALL)
self.ReturnTypeLabel = wx.StaticText(self, label=_('Return Type:'))
controls_sizer.AddWindow(self.ReturnTypeLabel, flag=wx.ALIGN_CENTER_VERTICAL)
self.ReturnType = wx.ComboBox(self,
size=wx.Size(145, -1), style=wx.CB_READONLY)
self.Bind(wx.EVT_COMBOBOX, self.OnReturnTypeChanged, self.ReturnType)
controls_sizer.AddWindow(self.ReturnType)
self.DescriptionLabel = wx.StaticText(self, label=_('Description:'))
controls_sizer.AddWindow(self.DescriptionLabel, flag=wx.ALIGN_CENTER_VERTICAL)
self.Description = wx.TextCtrl(self,
size=wx.Size(250, -1), style=wx.TE_PROCESS_ENTER)
self.Bind(wx.EVT_TEXT_ENTER, self.OnDescriptionChanged, self.Description)
self.Description.Bind(wx.EVT_KILL_FOCUS, self.OnDescriptionChanged)
- controls_sizer.AddWindow(self.Description)
+ controls_sizer.AddWindow(self.Description) class_filter_label = wx.StaticText(self, label=_('Class Filter:'))
controls_sizer.AddWindow(class_filter_label, flag=wx.ALIGN_CENTER_VERTICAL)
- self.ClassFilter = wx.ComboBox(self,
+ self.ClassFilter = wx.ComboBox(self, size=wx.Size(145, -1), style=wx.CB_READONLY)
self.Bind(wx.EVT_COMBOBOX, self.OnClassFilter, self.ClassFilter)
controls_sizer.AddWindow(self.ClassFilter)
for name, bitmap, help in [
("AddButton", "add_element", _("Add variable")),
("DeleteButton", "remove_element", _("Remove variable")),
("UpButton", "up", _("Move variable up")),
("DownButton", "down", _("Move variable down"))]:
- button = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap(bitmap),
+ button = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap(bitmap), size=wx.Size(28, 28), style=wx.NO_BORDER)
button.SetToolTipString(help)
setattr(self, name, button)
controls_sizer.AddWindow(button)
self.VariablesGrid = CustomGrid(self, style=wx.VSCROLL)
self.VariablesGrid.SetDropTarget(VariableDropTarget(self))
- self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE,
+ self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.OnVariablesGridCellChange)
- self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK,
+ self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.OnVariablesGridCellLeftClick)
- self.VariablesGrid.Bind(wx.grid.EVT_GRID_EDITOR_SHOWN,
+ self.VariablesGrid.Bind(wx.grid.EVT_GRID_EDITOR_SHOWN, self.OnVariablesGridEditorShown)
self.MainSizer.AddWindow(self.VariablesGrid, flag=wx.GROW)
self.SetSizer(self.MainSizer)
self.ParentWindow = window
self.Controler = controler
self.ElementType = element_type
self.RefreshHighlightsTimer = wx.Timer(self, -1)
- self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer,
+ self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer)
self.FilterChoiceTransfer = GetFilterChoiceTransfer()
- self.DefaultValue = _VariableInfos("", "", "", "", "", True, "", "INT", ([], []), 0)
+ self.DefaultValue = _VariableInfos("", "", "", "", "", True, "", DefaultType, ([], []), 0) if element_type in ["config", "resource"]:
self.DefaultTypes = {"All" : "Global"}
@@ -481,7 +475,7 @@
# these condense the ColAlignements list
# Num Name Class Type Loc Init Option Doc
self.ColSizes = [40, 80, 70, 80, 80, 80, 100, 80]
@@ -502,15 +496,15 @@
# these condense the ColAlignements list
# Num Name Class Type Init Option Doc
self.ColSizes = [40, 80, 70, 80, 80, 100, 160]
self.ColAlignements = [c, l, l, l, l, l, l]
self.ElementType = element_type
for choice in self.FilterChoices:
self.ClassFilter.Append(_(choice))
@@ -526,11 +520,11 @@
"Down": self.DownButton})
self.VariablesGrid.SetEditable(not self.Debug)
def _AddVariable(new_row):
row_content = self.Values[new_row - 1].copy()
result = VARIABLE_NAME_SUFFIX_MODEL.search(row_content.Name)
name = row_content.Name[:result.start(1)]
@@ -546,8 +540,8 @@
- if row_content is not None and row_content.Edit:
+ if row_content is not None and row_content.Edit: row_content = self.Values[new_row - 1].copy()
row_content = self.DefaultValue.copy()
@@ -555,10 +549,10 @@
row_content.Class = self.DefaultTypes[self.Filter]
row_content.Class = self.Filter
row_content.Name = self.Controler.GenerateNewName(
self.TagName, None, name + "%d", start_idx)
if self.Filter == "All" and len(self.Values) > 0:
self.Values.insert(new_row, row_content)
@@ -568,7 +562,7 @@
setattr(self.VariablesGrid, "_AddRow", _AddVariable)
def _DeleteVariable(row):
if self.Table.GetValueByName(row, "Edit"):
self.Values.remove(self.Table.GetRow(row))
@@ -577,7 +571,7 @@
self.ParentWindow.RefreshView(variablepanel = False)
setattr(self.VariablesGrid, "_DeleteRow", _DeleteVariable)
def _MoveVariable(row, move):
new_row = max(0, min(row + move, len(self.Values) - 1))
@@ -588,7 +582,7 @@
setattr(self.VariablesGrid, "_MoveRow", _MoveVariable)
table_length = len(self.Table.data)
@@ -603,7 +597,7 @@
self.UpButton.Enable(not self.Debug and (table_length > 0 and row > 0 and self.Filter == "All"))
self.DownButton.Enable(not self.Debug and (table_length > 0 and row < table_length - 1 and self.Filter == "All"))
setattr(self.VariablesGrid, "RefreshButtons", _RefreshButtons)
self.VariablesGrid.SetRowLabelSize(0)
for col in range(self.Table.GetNumberCols()):
attr = wx.grid.GridCellAttr()
@@ -611,29 +605,29 @@
self.VariablesGrid.SetColAttr(col, attr)
self.VariablesGrid.SetColMinimalWidth(col, self.ColSizes[col])
self.VariablesGrid.AutoSizeColumn(col, False)
self.RefreshHighlightsTimer.Stop()
def SetTagName(self, tagname):
self.BodyType = self.Controler.GetEditedElementBodyType(self.TagName)
def IsFunctionBlockType(self, name):
- if (isinstance(name, TupleType) or
+ if (isinstance(name, TupleType) or self.ElementType != "function" and self.BodyType in ["ST", "IL"]):
return self.Controler.GetBlockType(name, debug=self.Debug) is not None
self.PouNames = self.Controler.GetProjectPouNames(self.Debug)
words = self.TagName.split("::")
if self.ElementType == "config":
self.Values = self.Controler.GetConfigurationGlobalVars(words[1], self.Debug)
@@ -647,7 +641,7 @@
returnType = self.Controler.GetEditedElementInterfaceReturnType(self.TagName, debug=self.Debug)
description = self.Controler.GetPouDescription(words[1])
self.Values = self.Controler.GetEditedElementInterfaceVars(self.TagName, debug=self.Debug)
if returnType is not None:
self.ReturnType.SetStringSelection(returnType)
self.ReturnType.Enable(not self.Debug)
@@ -657,7 +651,7 @@
self.ReturnType.Enable(False)
self.ReturnTypeLabel.Hide()
if description is not None:
self.Description.SetValue(description)
self.Description.Enable(not self.Debug)
@@ -667,11 +661,11 @@
self.Description.Enable(False)
self.DescriptionLabel.Hide()
self.VariablesGrid.RefreshButtons()
def OnReturnTypeChanged(self, event):
words = self.TagName.split("::")
self.Controler.SetPouInterfaceReturnType(words[1], self.ReturnType.GetStringSelection())
@@ -679,7 +673,7 @@
self.ParentWindow.RefreshView(variablepanel = False)
self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, POUINSTANCEVARIABLESPANEL, LIBRARYTREE)
def OnDescriptionChanged(self, event):
words = self.TagName.split("::")
old_description = self.Controler.GetPouDescription(words[1])
@@ -688,7 +682,7 @@
self.Controler.SetPouDescription(words[1], new_description)
self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES, POUINSTANCEVARIABLESPANEL, LIBRARYTREE)
def OnClassFilter(self, event):
self.Filter = self.FilterChoiceTransfer[VARIABLE_CHOICES_DICT[self.ClassFilter.GetStringSelection()]]
@@ -711,7 +705,7 @@
colname = self.Table.GetColLabelValue(col, False)
value = self.Table.GetValue(row, col)
if colname == "Name" and value != "":
if not TestIdentifier(value):
message = _("\"%s\" is not a valid identifier!") % value
@@ -735,7 +729,7 @@
wx.CallAfter(self.ParentWindow.RefreshView, False)
elif colname == "Location":
wx.CallAfter(self.ParentWindow.RefreshView)
dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR)
@@ -743,64 +737,77 @@
- def OnVariablesGridEditorShown(self, event):
- row, col = event.GetRow(), event.GetCol()
- label_value = self.Table.GetColLabelValue(col, False)
- if label_value == "Type":
- type_menu = wx.Menu(title='') # the root menu
+ def BuildStdIECTypesMenu(self,type_menu): # build a submenu containing standard IEC types
base_menu = wx.Menu(title='')
for base_type in self.Controler.GetBaseTypes():
- AppendMenu(base_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=base_type)
+ base_menu.Append(help='', id=new_id, kind=wx.ITEM_NORMAL, text=base_type) self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(base_type), id=new_id)
type_menu.AppendMenu(wx.NewId(), _("Base Types"), base_menu)
+ def BuildUserTypesMenu(self,type_menu): # build a submenu containing user-defined types
datatype_menu = wx.Menu(title='')
datatypes = self.Controler.GetDataTypes(basetypes = False, confnodetypes = False)
for datatype in datatypes:
- AppendMenu(datatype_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=datatype)
+ datatype_menu.Append(help='', id=new_id, kind=wx.ITEM_NORMAL, text=datatype) self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(datatype), id=new_id)
type_menu.AppendMenu(wx.NewId(), _("User Data Types"), datatype_menu)
- for category in self.Controler.GetConfNodeDataTypes():
- if len(category["list"]) > 0:
- # build a submenu containing confnode types
- confnode_datatype_menu = wx.Menu(title='')
- for datatype in category["list"]:
- AppendMenu(confnode_datatype_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=datatype)
- self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(datatype), id=new_id)
- type_menu.AppendMenu(wx.NewId(), category["name"], confnode_datatype_menu)
+ def BuildLibsTypesMenu(self, type_menu): + for category in self.Controler.GetConfNodeDataTypes(): + if len(category["list"]) > 0: + # build a submenu containing confnode types + confnode_datatype_menu = wx.Menu(title='') + for datatype in category["list"]: + confnode_datatype_menu.Append(help='', id=new_id, kind=wx.ITEM_NORMAL, text=datatype) + self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(datatype), id=new_id) + type_menu.AppendMenu(wx.NewId(), category["name"], confnode_datatype_menu) - # build a submenu containing function block types
- bodytype = self.Controler.GetEditedElementBodyType(self.TagName)
- pouname, poutype = self.Controler.GetEditedElementType(self.TagName)
+ def BuildProjectTypesMenu(self, type_menu, classtype): + # build a submenu containing function block types + bodytype = self.Controler.GetEditedElementBodyType(self.TagName) + pouname, poutype = self.Controler.GetEditedElementType(self.TagName) + if classtype in ["Input", "Output", "InOut", "External", "Global"] or \ + poutype != "function" and bodytype in ["ST", "IL"]: + functionblock_menu = wx.Menu(title='') + fbtypes = self.Controler.GetFunctionBlockTypes(self.TagName) + for functionblock_type in fbtypes: + functionblock_menu.Append(help='', id=new_id, kind=wx.ITEM_NORMAL, text=functionblock_type) + self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(functionblock_type), id=new_id) + type_menu.AppendMenu(wx.NewId(), _("Function Block Types"), functionblock_menu) + def BuildArrayTypesMenu(self, type_menu): + type_menu.Append(help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Array")) + self.Bind(wx.EVT_MENU, self.VariableArrayTypeFunction, id=new_id) + def OnVariablesGridEditorShown(self, event): + row, col = event.GetRow(), event.GetCol() + label_value = self.Table.GetColLabelValue(col, False) + if label_value == "Type": classtype = self.Table.GetValueByName(row, "Class")
- AppendMenu(type_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Array"))
- self.Bind(wx.EVT_MENU, self.VariableArrayTypeFunction, id=new_id)
- if classtype in ["Input", "Output", "InOut", "External", "Global"] or \
- poutype != "function" and bodytype in ["ST", "IL"]:
- functionblock_menu = wx.Menu(title='')
- fbtypes = self.Controler.GetFunctionBlockTypes(self.TagName)
- for functionblock_type in fbtypes:
- AppendMenu(functionblock_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=functionblock_type)
- self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(functionblock_type), id=new_id)
+ type_menu = wx.Menu(title='') # the root menu + self.BuildStdIECTypesMenu(type_menu) + self.BuildUserTypesMenu(type_menu) - type_menu.AppendMenu(wx.NewId(), _("Function Block Types"), functionblock_menu)
+ self.BuildLibsTypesMenu(type_menu) + self.BuildProjectTypesMenu(type_menu,classtype) + self.BuildArrayTypesMenu(type_menu) rect = self.VariablesGrid.BlockToDeviceRect((row, col), (row, col))
corner_x = rect.x + rect.width
@@ -812,7 +819,7 @@
def GetVariableTypeFunction(self, base_type):
def VariableTypeFunction(event):
row = self.VariablesGrid.GetGridCursorRow()
@@ -823,11 +830,11 @@
self.Controler.BufferProject()
self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES, POUINSTANCEVARIABLESPANEL, LIBRARYTREE)
return VariableTypeFunction
def VariableArrayTypeFunction(self, event):
row = self.VariablesGrid.GetGridCursorRow()
- dialog = ArrayTypeDialog(self,
- self.Controler.GetDataTypes(self.TagName),
+ dialog = ArrayTypeDialog(self, + self.Controler.GetDataTypes(self.TagName), self.Table.GetValueByName(row, "Type"))
if dialog.ShowModal() == wx.ID_OK:
self.Table.SetValueByName(row, "Type", dialog.GetValue())
@@ -837,7 +844,7 @@
self.Controler.BufferProject()
self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES, POUINSTANCEVARIABLESPANEL, LIBRARYTREE)
def OnVariablesGridCellLeftClick(self, event):
if not self.Debug and (event.GetCol() == 0 and self.Table.GetValueByName(row, "Edit")):
@@ -849,7 +856,7 @@
for num, variable in enumerate(self.Values):
@@ -858,7 +865,7 @@
self.Table.ResetView(self.VariablesGrid)
def SaveValues(self, buffer = True):
words = self.TagName.split("::")
if self.ElementType == "config":
@@ -871,7 +878,7 @@
self.Controler.SetPouInterfaceVars(words[1], self.Values)
self.Controler.BufferProject()
- self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES, POUINSTANCEVARIABLESPANEL, LIBRARYTREE)
+ self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) #-------------------------------------------------------------------------------
# Highlights showing functions
--- a/editors/CodeFileEditor.py Mon May 26 14:44:03 2014 +0100
+++ b/editors/CodeFileEditor.py Fri Jun 06 18:30:49 2014 +0100
@@ -6,7 +6,7 @@
from plcopen.plcopen import TestTextElement
-from plcopen.structures import TestIdentifier, IEC_KEYWORDS
+from plcopen.structures import TestIdentifier, IEC_KEYWORDS, DefaultType from controls import CustomGrid, CustomTable
from editors.ConfTreeNodeEditor import ConfTreeNodeEditor
from util.BitmapLibrary import GetBitmap
@@ -14,7 +14,7 @@
from controls.VariablePanel import VARIABLE_NAME_SUFFIX_MODEL
from graphics.GraphicCommons import ERROR_HIGHLIGHT, SEARCH_RESULT_HIGHLIGHT, REFRESH_HIGHLIGHT_PERIOD
-[STC_CODE_ERROR, STC_CODE_SEARCH_RESULT,
+[STC_CODE_ERROR, STC_CODE_SEARCH_RESULT, STC_CODE_SECTION] = range(15, 18)
@@ -25,14 +25,14 @@
class CodeEditor(CustomStyledTextCtrl):
def __init__(self, parent, window, controler):
- CustomStyledTextCtrl.__init__(self, parent, -1, wx.DefaultPosition,
+ CustomStyledTextCtrl.__init__(self, parent, -1, wx.DefaultPosition,
self.SetMarginType(1, stc.STC_MARGIN_NUMBER)
self.SetMarginWidth(1, 25)
@@ -41,7 +41,7 @@
self.SetViewWhiteSpace(False)
self.SetEdgeMode(stc.STC_EDGE_BACKGROUND)
self.SetEdgeColumn(EDGE_COLUMN)
@@ -59,7 +59,7 @@
self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_BOXPLUSCONNECTED, "white", "#808080")
self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_BOXMINUSCONNECTED, "white", "#808080")
self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNER, "white", "#808080")
self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI)
self.Bind(stc.EVT_STC_MARGINCLICK, self.OnMarginClick)
self.Bind(wx.EVT_KEY_DOWN, self.OnKeyPressed)
@@ -78,33 +78,33 @@
self.StyleSetSpec(stc.STC_STYLE_CONTROLCHAR, "face:%(mono)s" % faces)
self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, "fore:#FFFFFF,back:#0000FF,bold")
self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, "fore:#000000,back:#FF0000,bold")
self.StyleSetSpec(STC_CODE_ERROR, 'fore:#FF0000,back:#FFFF00,size:%(size)d' % faces)
self.StyleSetSpec(STC_CODE_SEARCH_RESULT, 'fore:#FFFFFF,back:#FFA500,size:%(size)d' % faces)
self.StyleSetSpec(STC_CODE_SECTION, 'fore:#808080,size:%(size)d')
self.StyleSetChangeable(STC_CODE_SECTION, False)
self.SetKeyWords(0, " ".join(self.KEYWORDS))
self.Controler = controler
self.ParentWindow = window
self.DisableEvents = True
self.CurrentAction = None
self.ResetSearchResults()
self.RefreshHighlightsTimer = wx.Timer(self, -1)
self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer)
self.SectionsComments = {}
for section in self.Controler.SECTIONS_NAMES:
section_comment = " %s section " % (section)
@@ -112,11 +112,11 @@
section_comment = self.COMMENT_HEADER * (len_headers / 2) + \
self.COMMENT_HEADER * (len_headers - len_headers / 2)
self.SectionsComments[section] = {
"comment": section_comment,
for i, section in enumerate(self.Controler.SECTIONS_NAMES):
section_infos = self.SectionsComments[section]
if i + 1 < len(self.Controler.SECTIONS_NAMES):
@@ -125,24 +125,24 @@
section_infos["pattern"] = re.compile(
- section_infos["comment"] + "(.*)" +
+ section_infos["comment"] + "(.*)" +
self.SetModEventMask(wx.stc.STC_MOD_BEFOREINSERT|wx.stc.STC_MOD_BEFOREDELETE)
self.Bind(wx.stc.EVT_STC_DO_DROP, self.OnDoDrop)
self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
self.Bind(wx.stc.EVT_STC_MODIFIED, self.OnModification)
def ResetSearchResults(self):
self.SearchResults = None
self.CurrentFindHighlight = None
def OnModification(self, event):
if not self.DisableEvents:
mod_type = event.GetModificationType()
@@ -165,7 +165,7 @@
wx.CallAfter(self.RefreshModel)
wx.CallAfter(self.RefreshSectionStyling)
def OnDoDrop(self, event):
values = eval(event.GetDragText())
@@ -192,7 +192,7 @@
self.ParentWindow.RefreshFileMenu()
self.ParentWindow.RefreshEditMenu()
self.ParentWindow.RefreshPageTitles()
def StartBuffering(self):
self.Controler.StartBuffering()
if self.ParentWindow is not None:
@@ -200,7 +200,7 @@
self.ParentWindow.RefreshFileMenu()
self.ParentWindow.RefreshEditMenu()
self.ParentWindow.RefreshPageTitles()
if self.CurrentAction != None:
self.Controler.EndBuffering()
@@ -240,18 +240,18 @@
self.GotoPos(old_cursor_pos)
self.DisableEvents = False
self.RefreshSectionStyling()
def RefreshSectionStyling(self):
for line in xrange(self.GetLineCount()):
self.SetLineState(line, 0)
for section in self.Controler.SECTIONS_NAMES:
section_comments = self.SectionsComments[section]
start_pos = text.find(section_comments["comment"])
@@ -259,7 +259,7 @@
self.StartStyling(start_pos, 0xff)
self.SetStyling(end_pos - start_pos, STC_CODE_SECTION)
self.SetLineState(self.LineFromPosition(start_pos), 1)
self.StartStyling(end_pos, 0x00)
self.SetStyling(len(self.GetText()) - end_pos, stc.STC_STYLE_DEFAULT)
@@ -278,7 +278,7 @@
self.Controler.SetTextParts(parts)
self.ResetSearchResults()
def OnKeyPressed(self, event):
@@ -286,14 +286,14 @@
current_pos = self.GetCurrentPos()
selected = self.GetSelection()
text_selected = selected[0] != selected[1]
# Test if caret is before Windows like new line
if current_pos < len(text) and ord(text[current_pos]) == 13:
# Disable to type any character in section header lines
if (self.GetLineState(self.LineFromPosition(current_pos)) and
@@ -301,7 +301,7 @@
# Disable to delete line between code and header lines
elif (self.GetCurLine()[0].strip() != "" and not text_selected and
@@ -309,7 +309,7 @@
key in [wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE] and
self.GetLineState(self.LineFromPosition(min(len(text), current_pos + newline_size))))):
elif key == 32 and event.ControlDown():
pos = self.GetCurrentPos()
@@ -319,7 +319,7 @@
self.AutoCompSetIgnoreCase(False) # so this needs to match
keywords = self.KEYWORDS + [var["Name"]
for var in self.Controler.GetVariables()]
@@ -361,11 +361,11 @@
self.BraceBadLight(braceAtCaret)
self.BraceHighlight(braceAtCaret, braceOpposite)
selected_text = self.GetSelectedText()
self.ParentWindow.SetCopyBuffer(selected_text, True)
def OnMarginClick(self, event):
# fold and unfold as needed
@@ -464,11 +464,11 @@
self.DisableEvents = False
self.CmdKeyExecute(wx.stc.STC_CMD_COPY)
self.ParentWindow.RefreshEditMenu()
self.DisableEvents = True
@@ -480,21 +480,21 @@
def Find(self, direction, search_params):
if self.SearchParams != search_params:
self.ClearHighlights(SEARCH_RESULT_HIGHLIGHT)
self.SearchParams = search_params
- "raw_pattern": search_params["find_pattern"],
+ "raw_pattern": search_params["find_pattern"], "pattern": re.compile(search_params["find_pattern"]),
"case_sensitive": search_params["case_sensitive"],
"regular_expression": search_params["regular_expression"],
(start, end, SEARCH_RESULT_HIGHLIGHT)
- for start, end, text in
+ for start, end, text in TestTextElement(self.GetText(), criteria)]
self.CurrentFindHighlight = None
if len(self.SearchResults) > 0:
if self.CurrentFindHighlight is not None:
old_idx = self.SearchResults.index(self.CurrentFindHighlight)
@@ -522,9 +522,9 @@
if self.CurrentFindHighlight is not None:
self.AddHighlight(*self.CurrentFindHighlight)
self.ScrollToLine(self.CurrentFindHighlight[0][0])
if self.CurrentFindHighlight is not None:
self.RemoveHighlight(*self.CurrentFindHighlight)
@@ -557,11 +557,11 @@
def RemoveHighlight(self, start, end, highlight_type):
highlight_type = HIGHLIGHT_TYPES.get(highlight_type, None)
- if (highlight_type is not None and
+ if (highlight_type is not None and (start, end, highlight_type) in self.Highlights):
self.Highlights.remove((start, end, highlight_type))
self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True)
def ShowHighlights(self):
for start, end, highlight_type in self.Highlights:
@@ -583,14 +583,14 @@
#-------------------------------------------------------------------------------
class VariablesTable(CustomTable):
def GetValue(self, row, col):
if row < self.GetNumberRows():
return str(self.data[row].get(self.GetColLabelValue(col, False), ""))
def _updateColAttrs(self, grid):
wxGrid -> update the column attributes to add the
@@ -598,7 +598,7 @@
Otherwise default to the default renderer.
for row in range(self.GetNumberRows()):
@@ -606,7 +606,7 @@
colname = self.GetColLabelValue(col, False)
if colname in ["Name", "Initial"]:
editor = wx.grid.GridCellTextEditor()
@@ -616,49 +616,49 @@
grid.SetReadOnly(row, col, True)
grid.SetCellEditor(row, col, editor)
grid.SetCellRenderer(row, col, renderer)
grid.SetCellBackgroundColour(row, col, wx.WHITE)
self.ResizeRow(grid, row)
class VariablesEditor(wx.Panel):
def __init__(self, parent, window, controler):
wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL)
main_sizer = wx.FlexGridSizer(cols=2, hgap=0, rows=1, vgap=4)
main_sizer.AddGrowableCol(1)
main_sizer.AddGrowableRow(0)
controls_sizer = wx.BoxSizer(wx.VERTICAL)
main_sizer.AddSizer(controls_sizer, border=5, flag=wx.ALL)
for name, bitmap, help in [
("AddVariableButton", "add_element", _("Add variable")),
("DeleteVariableButton", "remove_element", _("Remove variable")),
("UpVariableButton", "up", _("Move variable up")),
("DownVariableButton", "down", _("Move variable down"))]:
- button = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap(bitmap),
+ button = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap(bitmap), size=wx.Size(28, 28), style=wx.NO_BORDER)
button.SetToolTipString(help)
setattr(self, name, button)
controls_sizer.AddWindow(button, border=5, flag=wx.BOTTOM)
self.VariablesGrid = CustomGrid(self, style=wx.VSCROLL)
self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.OnVariablesGridCellChange)
self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.OnVariablesGridCellLeftClick)
self.VariablesGrid.Bind(wx.grid.EVT_GRID_EDITOR_SHOWN, self.OnVariablesGridEditorShown)
main_sizer.AddWindow(self.VariablesGrid, flag=wx.GROW)
self.SetSizer(main_sizer)
self.ParentWindow = window
self.Controler = controler
- self.VariablesDefaultValue = {"Name" : "", "Type" : "INT", "Initial": ""}
+ self.VariablesDefaultValue = {"Name" : "", "Type" : DefaultType, "Initial": ""} self.Table = VariablesTable(self, [], ["#", "Name", "Type", "Initial"])
self.ColAlignements = [wx.ALIGN_RIGHT, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT]
self.ColSizes = [40, 200, 150, 150]
@@ -667,7 +667,7 @@
"Delete": self.DeleteVariableButton,
"Up": self.UpVariableButton,
"Down": self.DownVariableButton})
def _AddVariable(new_row):
row_content = self.Table.data[new_row - 1].copy()
@@ -691,13 +691,13 @@
setattr(self.VariablesGrid, "_AddRow", _AddVariable)
def _DeleteVariable(row):
self.Table.RemoveRow(row)
setattr(self.VariablesGrid, "_DeleteRow", _DeleteVariable)
def _MoveVariable(row, move):
new_row = self.Table.MoveRow(row, move)
@@ -705,7 +705,7 @@
setattr(self.VariablesGrid, "_MoveRow", _MoveVariable)
self.VariablesGrid.SetRowLabelSize(0)
for col in range(self.Table.GetNumberCols()):
attr = wx.grid.GridCellAttr()
@@ -717,7 +717,7 @@
self.Controler.SetVariables(self.Table.GetData())
# Buffer the last model state
self.Controler.BufferCodeFile()
@@ -730,23 +730,23 @@
self.Table.SetData(self.Controler.GetVariables())
self.Table.ResetView(self.VariablesGrid)
self.VariablesGrid.RefreshButtons()
return self.ParentWindow.GetPanelBestSize()
def OnVariablesGridCellChange(self, event):
row, col = event.GetRow(), event.GetCol()
colname = self.Table.GetColLabelValue(col, False)
value = self.Table.GetValue(row, col)
if colname == "Name" and value != "":
if not TestIdentifier(value):
message = _("\"%s\" is not a valid identifier!") % value
elif value.upper() in IEC_KEYWORDS:
message = _("\"%s\" is a keyword. It can't be used!") % value
- elif value.upper() in [var["Name"].upper()
- for var_row, var in enumerate(self.Table.data)
+ elif value.upper() in [var["Name"].upper() + for var_row, var in enumerate(self.Table.data) message = _("A variable with \"%s\" as name already exists!") % value
@@ -755,7 +755,7 @@
wx.CallAfter(self.RefreshView)
dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR)
@@ -765,7 +765,7 @@
def OnVariablesGridEditorShown(self, event):
- row, col = event.GetRow(), event.GetCol()
+ row, col = event.GetRow(), event.GetCol() if self.Table.GetColLabelValue(col, False) == "Type":
type_menu = wx.Menu(title='')
base_menu = wx.Menu(title='')
@@ -781,7 +781,7 @@
self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(datatype), id=new_id)
type_menu.AppendMenu(wx.NewId(), "User Data Types", datatype_menu)
rect = self.VariablesGrid.BlockToDeviceRect((row, col), (row, col))
self.VariablesGrid.PopupMenuXY(type_menu, rect.x + rect.width, rect.y + self.VariablesGrid.GetColLabelSize())
@@ -803,64 +803,64 @@
data_type = self.Table.GetValueByName(row, "Type")
var_name = self.Table.GetValueByName(row, "Name")
- data = wx.TextDataObject(str((var_name, "Global", data_type,
+ data = wx.TextDataObject(str((var_name, "Global", data_type, self.Controler.GetCurrentLocation())))
dragSource = wx.DropSource(self.VariablesGrid)
#-------------------------------------------------------------------------------
# CodeFileEditor Main Frame Class
#-------------------------------------------------------------------------------
class CodeFileEditor(ConfTreeNodeEditor):
def _create_CodePanel(self, prnt):
self.CodeEditorPanel = wx.SplitterWindow(prnt)
self.CodeEditorPanel.SetMinimumPaneSize(1)
- self.VariablesPanel = VariablesEditor(self.CodeEditorPanel,
+ self.VariablesPanel = VariablesEditor(self.CodeEditorPanel, self.ParentWindow, self.Controler)
if self.CODE_EDITOR is not None:
- self.CodeEditor = self.CODE_EDITOR(self.CodeEditorPanel,
+ self.CodeEditor = self.CODE_EDITOR(self.CodeEditorPanel, self.ParentWindow, self.Controler)
- self.CodeEditorPanel.SplitHorizontally(self.VariablesPanel,
+ self.CodeEditorPanel.SplitHorizontally(self.VariablesPanel, self.CodeEditorPanel.Initialize(self.VariablesPanel)
return self.CodeEditorPanel
def __init__(self, parent, controler, window):
ConfTreeNodeEditor.__init__(self, parent, controler, window)
wx.CallAfter(self.CodeEditorPanel.SetSashPosition, 150)
def GetBufferState(self):
return self.Controler.GetBufferState()
self.Controler.LoadPrevious()
self.Controler.LoadNext()
ConfTreeNodeEditor.RefreshView(self)
self.VariablesPanel.RefreshView()
self.CodeEditor.RefreshView()
def Find(self, direction, search_params):
self.CodeEditor.Find(direction, search_params)
\ No newline at end of file
--- a/editors/DataTypeEditor.py Mon May 26 14:44:03 2014 +0100
+++ b/editors/DataTypeEditor.py Fri Jun 06 18:30:49 2014 +0100
@@ -2,7 +2,7 @@
#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
-#based on the plcopen standard.
+#based on the plcopen standard. #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
@@ -29,7 +29,7 @@
-from plcopen.structures import IEC_KEYWORDS, TestIdentifier
+from plcopen.structures import IEC_KEYWORDS, TestIdentifier, DefaultType from graphics.GraphicCommons import REFRESH_HIGHLIGHT_PERIOD
from controls import CustomEditableListBox, CustomGrid, CustomTable
from dialogs import ArrayTypeDialog
@@ -59,7 +59,7 @@
#-------------------------------------------------------------------------------
class ElementsTable(CustomTable):
A custom wx.grid.Grid Table using user supplied data
@@ -67,7 +67,7 @@
# The base class must be initialized *first*
CustomTable.__init__(self, parent, data, colnames)
def GetValue(self, row, col):
if row < self.GetNumberRows():
@@ -78,17 +78,17 @@
return "ARRAY [%s] OF %s" % (",".join(map(lambda x : "..".join(x), value[2])), value[1])
def SetValue(self, row, col, value):
if col < len(self.colnames):
colname = self.GetColLabelValue(col, False)
self.old_value = self.data[row][colname]
self.data[row][colname] = value
def _updateColAttrs(self, grid):
wx.grid.Grid -> update the column attributes to add the
@@ -96,7 +96,7 @@
Otherwise default to the default renderer.
for row in range(self.GetNumberRows()):
row_highlights = self.Highlights.get(row, {})
for col in range(self.GetNumberCols()):
@@ -115,15 +115,15 @@
editor = wx.grid.GridCellTextEditor()
grid.SetReadOnly(row, col, True)
grid.SetCellEditor(row, col, editor)
grid.SetCellRenderer(row, col, renderer)
highlight_colours = row_highlights.get(colname.lower(), [(wx.WHITE, wx.BLACK)])[-1]
grid.SetCellBackgroundColour(row, col, highlight_colours[0])
grid.SetCellTextColour(row, col, highlight_colours[1])
self.ResizeRow(grid, row)
def AddHighlight(self, infos, highlight_type):
row_highlights = self.Highlights.setdefault(infos[0], {})
if infos[1] == "initial":
@@ -137,216 +137,216 @@
#-------------------------------------------------------------------------------
class DataTypeEditor(EditorPanel):
def _init_Editor(self, parent):
self.Editor = wx.Panel(parent, style=wx.SUNKEN_BORDER)
self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=10)
self.MainSizer.AddGrowableCol(0)
self.MainSizer.AddGrowableRow(1)
top_sizer = wx.BoxSizer(wx.HORIZONTAL)
- self.MainSizer.AddSizer(top_sizer, border=5,
+ self.MainSizer.AddSizer(top_sizer, border=5, flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT)
derivation_type_label = wx.StaticText(self.Editor, label=_('Derivation Type:'))
- top_sizer.AddWindow(derivation_type_label, border=5,
+ top_sizer.AddWindow(derivation_type_label, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT)
self.DerivationType = wx.ComboBox(self.Editor,
size=wx.Size(200, -1), style=wx.CB_READONLY)
self.Bind(wx.EVT_COMBOBOX, self.OnDerivationTypeChanged, self.DerivationType)
top_sizer.AddWindow(self.DerivationType, border=5, flag=wx.GROW|wx.RIGHT)
typeinfos_staticbox = wx.StaticBox(self.Editor, label=_('Type infos:'))
typeinfos_sizer = wx.StaticBoxSizer(typeinfos_staticbox, wx.HORIZONTAL)
- self.MainSizer.AddSizer(typeinfos_sizer, border=5,
+ self.MainSizer.AddSizer(typeinfos_sizer, border=5, flag=wx.GROW|wx.BOTTOM|wx.LEFT|wx.RIGHT)
# Panel for Directly derived data types
self.DirectlyPanel = wx.Panel(self.Editor, style=wx.TAB_TRAVERSAL)
typeinfos_sizer.AddWindow(self.DirectlyPanel, 1)
directly_panel_sizer = wx.BoxSizer(wx.HORIZONTAL)
- directly_basetype_label = wx.StaticText(self.DirectlyPanel,
+ directly_basetype_label = wx.StaticText(self.DirectlyPanel, - directly_panel_sizer.AddWindow(directly_basetype_label, 1, border=5,
+ directly_panel_sizer.AddWindow(directly_basetype_label, 1, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
self.DirectlyBaseType = wx.ComboBox(self.DirectlyPanel, style=wx.CB_READONLY)
self.Bind(wx.EVT_COMBOBOX, self.OnInfosChanged, self.DirectlyBaseType)
- directly_panel_sizer.AddWindow(self.DirectlyBaseType, 1, border=5,
+ directly_panel_sizer.AddWindow(self.DirectlyBaseType, 1, border=5,
directly_initialvalue_label = wx.StaticText(self.DirectlyPanel,
label=_('Initial Value:'))
- directly_panel_sizer.AddWindow(directly_initialvalue_label, 1, border=5,
+ directly_panel_sizer.AddWindow(directly_initialvalue_label, 1, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
- self.DirectlyInitialValue = wx.TextCtrl(self.DirectlyPanel,
+ self.DirectlyInitialValue = wx.TextCtrl(self.DirectlyPanel, style=wx.TE_PROCESS_ENTER|wx.TE_RICH)
self.Bind(wx.EVT_TEXT_ENTER, self.OnReturnKeyPressed, self.DirectlyInitialValue)
- directly_panel_sizer.AddWindow(self.DirectlyInitialValue, 1, border=5,
+ directly_panel_sizer.AddWindow(self.DirectlyInitialValue, 1, border=5,
self.DirectlyPanel.SetSizer(directly_panel_sizer)
# Panel for Subrange data types
self.SubrangePanel = wx.Panel(self.Editor, style=wx.TAB_TRAVERSAL)
typeinfos_sizer.AddWindow(self.SubrangePanel, 1)
subrange_panel_sizer = wx.GridSizer(cols=4, hgap=5, rows=3, vgap=0)
subrange_basetype_label = wx.StaticText(self.SubrangePanel,
- subrange_panel_sizer.AddWindow(subrange_basetype_label, 1, border=5,
+ subrange_panel_sizer.AddWindow(subrange_basetype_label, 1, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
self.SubrangeBaseType = wx.ComboBox(self.SubrangePanel, style=wx.CB_READONLY)
- self.Bind(wx.EVT_COMBOBOX, self.OnSubrangeBaseTypeChanged,
+ self.Bind(wx.EVT_COMBOBOX, self.OnSubrangeBaseTypeChanged, - subrange_panel_sizer.AddWindow(self.SubrangeBaseType, 1, border=5,
+ subrange_panel_sizer.AddWindow(self.SubrangeBaseType, 1, border=5,
subrange_initialvalue_label = wx.StaticText(self.SubrangePanel,
label=_('Initial Value:'))
- subrange_panel_sizer.AddWindow(subrange_initialvalue_label, 1, border=5,
+ subrange_panel_sizer.AddWindow(subrange_initialvalue_label, 1, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
- self.SubrangeInitialValue = wx.SpinCtrl(self.SubrangePanel,
+ self.SubrangeInitialValue = wx.SpinCtrl(self.SubrangePanel, self.Bind(wx.EVT_SPINCTRL, self.OnInfosChanged, self.SubrangeInitialValue)
- subrange_panel_sizer.AddWindow(self.SubrangeInitialValue, 1, border=5,
+ subrange_panel_sizer.AddWindow(self.SubrangeInitialValue, 1, border=5,
subrange_minimum_label = wx.StaticText(self.SubrangePanel, label=_('Minimum:'))
- subrange_panel_sizer.AddWindow(subrange_minimum_label, 1, border=5,
+ subrange_panel_sizer.AddWindow(subrange_minimum_label, 1, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
self.SubrangeMinimum = wx.SpinCtrl(self.SubrangePanel, style=wx.TAB_TRAVERSAL)
self.Bind(wx.EVT_SPINCTRL, self.OnSubrangeMinimumChanged, self.SubrangeMinimum)
- subrange_panel_sizer.AddWindow(self.SubrangeMinimum, 1, border=5,
+ subrange_panel_sizer.AddWindow(self.SubrangeMinimum, 1, border=5,
subrange_panel_sizer.AddWindow(wx.Size(0, 0), 1)
subrange_maximum_label = wx.StaticText(self.SubrangePanel,
- subrange_panel_sizer.AddWindow(subrange_maximum_label, 1, border=5,
+ subrange_panel_sizer.AddWindow(subrange_maximum_label, 1, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
self.SubrangeMaximum = wx.SpinCtrl(self.SubrangePanel, style=wx.TAB_TRAVERSAL)
self.Bind(wx.EVT_SPINCTRL, self.OnSubrangeMaximumChanged, self.SubrangeMaximum)
- subrange_panel_sizer.AddWindow(self.SubrangeMaximum, 1, border=5,
+ subrange_panel_sizer.AddWindow(self.SubrangeMaximum, 1, border=5,
self.SubrangePanel.SetSizer(subrange_panel_sizer)
# Panel for Enumerated data types
self.EnumeratedPanel = wx.Panel(self.Editor, style=wx.TAB_TRAVERSAL)
typeinfos_sizer.AddWindow(self.EnumeratedPanel, 1)
enumerated_panel_sizer = wx.BoxSizer(wx.HORIZONTAL)
- self.EnumeratedValues = CustomEditableListBox(self.EnumeratedPanel,
- label=_("Values:"), style=wx.gizmos.EL_ALLOW_NEW|
- wx.gizmos.EL_ALLOW_EDIT|
+ self.EnumeratedValues = CustomEditableListBox(self.EnumeratedPanel, + label=_("Values:"), style=wx.gizmos.EL_ALLOW_NEW| + wx.gizmos.EL_ALLOW_EDIT| wx.gizmos.EL_ALLOW_DELETE)
setattr(self.EnumeratedValues, "_OnLabelEndEdit", self.OnEnumeratedValueEndEdit)
for func in ["_OnAddButton", "_OnDelButton", "_OnUpButton", "_OnDownButton"]:
setattr(self.EnumeratedValues, func, self.OnEnumeratedValuesChanged)
- enumerated_panel_sizer.AddWindow(self.EnumeratedValues, 1, border=5,
+ enumerated_panel_sizer.AddWindow(self.EnumeratedValues, 1, border=5,
enumerated_panel_rightsizer = wx.BoxSizer(wx.HORIZONTAL)
enumerated_panel_sizer.AddSizer(enumerated_panel_rightsizer, 1)
enumerated_initialvalue_label = wx.StaticText(self.EnumeratedPanel,
label=_('Initial Value:'))
- enumerated_panel_rightsizer.AddWindow(enumerated_initialvalue_label, 1,
+ enumerated_panel_rightsizer.AddWindow(enumerated_initialvalue_label, 1, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
- self.EnumeratedInitialValue = wx.ComboBox(self.EnumeratedPanel,
+ self.EnumeratedInitialValue = wx.ComboBox(self.EnumeratedPanel, self.Bind(wx.EVT_COMBOBOX, self.OnInfosChanged, self.EnumeratedInitialValue)
- enumerated_panel_rightsizer.AddWindow(self.EnumeratedInitialValue, 1,
+ enumerated_panel_rightsizer.AddWindow(self.EnumeratedInitialValue, 1,
self.EnumeratedPanel.SetSizer(enumerated_panel_sizer)
# Panel for Array data types
self.ArrayPanel = wx.Panel(self.Editor, style=wx.TAB_TRAVERSAL)
typeinfos_sizer.AddWindow(self.ArrayPanel, 1)
array_panel_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=0)
array_panel_sizer.AddGrowableCol(0)
array_panel_sizer.AddGrowableCol(1)
array_panel_sizer.AddGrowableRow(1)
array_panel_leftSizer = wx.BoxSizer(wx.HORIZONTAL)
array_panel_sizer.AddSizer(array_panel_leftSizer, flag=wx.GROW)
array_basetype_label = wx.StaticText(self.ArrayPanel, label=_('Base Type:'))
- array_panel_leftSizer.AddWindow(array_basetype_label, 1, border=5,
+ array_panel_leftSizer.AddWindow(array_basetype_label, 1, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
self.ArrayBaseType = wx.ComboBox(self.ArrayPanel, style=wx.CB_READONLY)
self.Bind(wx.EVT_COMBOBOX, self.OnInfosChanged, self.ArrayBaseType)
- array_panel_leftSizer.AddWindow(self.ArrayBaseType, 1, border=5,
+ array_panel_leftSizer.AddWindow(self.ArrayBaseType, 1, border=5,
array_panel_rightsizer = wx.BoxSizer(wx.HORIZONTAL)
array_panel_sizer.AddSizer(array_panel_rightsizer, flag=wx.GROW)
array_initialvalue_label = wx.StaticText(self.ArrayPanel,
label=_('Initial Value:'))
- array_panel_rightsizer.AddWindow(array_initialvalue_label, 1, border=5,
+ array_panel_rightsizer.AddWindow(array_initialvalue_label, 1, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
self.ArrayInitialValue = wx.TextCtrl(self.ArrayPanel,
style=wx.TE_PROCESS_ENTER|wx.TE_RICH)
self.Bind(wx.EVT_TEXT_ENTER, self.OnReturnKeyPressed, self.ArrayInitialValue)
- array_panel_rightsizer.AddWindow(self.ArrayInitialValue, 1, border=5,
- self.ArrayDimensions = CustomEditableListBox(self.ArrayPanel,
+ array_panel_rightsizer.AddWindow(self.ArrayInitialValue, 1, border=5, + self.ArrayDimensions = CustomEditableListBox(self.ArrayPanel, label=_("Dimensions:"), style=wx.gizmos.EL_ALLOW_NEW|
wx.gizmos.EL_ALLOW_DELETE)
- for func in ["_OnLabelEndEdit", "_OnAddButton", "_OnDelButton",
+ for func in ["_OnLabelEndEdit", "_OnAddButton", "_OnDelButton", "_OnUpButton", "_OnDownButton"]:
setattr(self.ArrayDimensions, func, self.OnDimensionsChanged)
- array_panel_sizer.AddWindow(self.ArrayDimensions, 0, border=5,
+ array_panel_sizer.AddWindow(self.ArrayDimensions, 0, border=5,
self.ArrayPanel.SetSizer(array_panel_sizer)
# Panel for Structure data types
self.StructurePanel = wx.Panel(self.Editor, style=wx.TAB_TRAVERSAL)
typeinfos_sizer.AddWindow(self.StructurePanel, 1)
structure_panel_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
structure_panel_sizer.AddGrowableCol(0)
structure_panel_sizer.AddGrowableRow(1)
structure_button_sizer = wx.FlexGridSizer(cols=5, hgap=5, rows=1, vgap=0)
structure_button_sizer.AddGrowableCol(0)
structure_button_sizer.AddGrowableRow(0)
- structure_panel_sizer.AddSizer(structure_button_sizer, 0, border=5,
+ structure_panel_sizer.AddSizer(structure_button_sizer, 0, border=5,
structure_elements_label = wx.StaticText(self.StructurePanel,
structure_button_sizer.AddWindow(structure_elements_label, flag=wx.ALIGN_BOTTOM)
for name, bitmap, help in [
("StructureAddButton", "add_element", _("Add element")),
("StructureDeleteButton", "remove_element", _("Remove element")),
@@ -357,46 +357,46 @@
button.SetToolTipString(help)
setattr(self, name, button)
structure_button_sizer.AddWindow(button)
- self.StructureElementsGrid = CustomGrid(self.StructurePanel,
+ self.StructureElementsGrid = CustomGrid(self.StructurePanel, size=wx.Size(0, 150), style=wx.VSCROLL)
- self.StructureElementsGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE,
+ self.StructureElementsGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.OnStructureElementsGridCellChange)
- self.StructureElementsGrid.Bind(wx.grid.EVT_GRID_EDITOR_SHOWN,
+ self.StructureElementsGrid.Bind(wx.grid.EVT_GRID_EDITOR_SHOWN, self.OnStructureElementsGridEditorShown)
structure_panel_sizer.AddWindow(self.StructureElementsGrid, flag=wx.GROW)
self.StructurePanel.SetSizer(structure_panel_sizer)
self.Editor.SetSizer(self.MainSizer)
def __init__(self, parent, tagname, window, controler):
EditorPanel.__init__(self, parent, tagname, window, controler)
- self.StructureElementDefaultValue = {"Name" : "", "Type" : "INT", "Initial Value" : ""}
+ self.StructureElementDefaultValue = {"Name" : "", "Type" : DefaultType, "Initial Value" : ""} self.StructureElementsTable = ElementsTable(self, [], GetElementsTableColnames())
self.StructureColSizes = [40, 150, 100, 250]
self.StructureColAlignements = [wx.ALIGN_CENTER, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT]
self.StructureElementsGrid.SetTable(self.StructureElementsTable)
self.StructureElementsGrid.SetButtons({"Add": self.StructureAddButton,
"Delete": self.StructureDeleteButton,
"Up": self.StructureUpButton,
"Down": self.StructureDownButton})
def _AddStructureElement(new_row):
self.StructureElementsTable.InsertRow(new_row, self.StructureElementDefaultValue.copy())
self.StructureElementsTable.ResetView(self.StructureElementsGrid)
setattr(self.StructureElementsGrid, "_AddRow", _AddStructureElement)
def _DeleteStructureElement(row):
self.StructureElementsTable.RemoveRow(row)
self.StructureElementsTable.ResetView(self.StructureElementsGrid)
setattr(self.StructureElementsGrid, "_DeleteRow", _DeleteStructureElement)
def _MoveStructureElement(row, move):
new_row = self.StructureElementsTable.MoveRow(row, move)
@@ -404,7 +404,7 @@
self.StructureElementsTable.ResetView(self.StructureElementsGrid)
setattr(self.StructureElementsGrid, "_MoveRow", _MoveStructureElement)
self.StructureElementsGrid.SetRowLabelSize(0)
for col in range(self.StructureElementsTable.GetNumberCols()):
attr = wx.grid.GridCellAttr()
@@ -413,7 +413,7 @@
self.StructureElementsGrid.SetColMinimalWidth(col, self.StructureColSizes[col])
self.StructureElementsGrid.AutoSizeColumn(col, False)
self.StructureElementsGrid.RefreshButtons()
for datatype in GetDatatypeTypes():
self.DerivationType.Append(_(datatype))
self.SubrangePanel.Hide()
@@ -423,7 +423,7 @@
self.CurrentPanel = "Directly"
self.Initializing = False
self.HighlightControls = {
("Directly", "base"): self.DirectlyBaseType,
("Directly", "initial"): self.DirectlyInitialValue,
@@ -437,27 +437,27 @@
("Array", "base"): self.ArrayBaseType,
("Array", "range"): self.ArrayDimensions,
self.RefreshHighlightsTimer = wx.Timer(self, -1)
self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer)
self.RefreshHighlightsTimer.Stop()
def GetBufferState(self):
return self.Controler.GetBufferState()
self.Controler.LoadPrevious()
self.ParentWindow.CloseTabsWithoutModel()
self.Controler.LoadNext()
self.ParentWindow.CloseTabsWithoutModel()
return self.Controler.GetEditedElement(self.TagName) is None
self.DirectlyBaseType.Clear()
@@ -504,7 +504,7 @@
self.StructureElementsTable.ResetView(self.StructureElementsGrid)
self.StructureElementsGrid.RefreshButtons()
self.Initializing = False
def OnDerivationTypeChanged(self, event):
wx.CallAfter(self.RefreshDisplayedInfos)
wx.CallAfter(self.RefreshTypeInfos)
@@ -512,7 +512,7 @@
def OnReturnKeyPressed(self, event):
def OnInfosChanged(self, event):
@@ -563,12 +563,12 @@
def OnEnumeratedValuesChanged(self, event):
wx.CallAfter(self.RefreshEnumeratedValues)
wx.CallAfter(self.RefreshTypeInfos)
def OnStructureElementsGridCellChange(self, event):
row, col = event.GetRow(), event.GetCol()
colname = self.StructureElementsTable.GetColLabelValue(col, False)
@@ -606,34 +606,34 @@
wx.CallAfter(self.StructureElementsTable.ResetView, self.StructureElementsGrid)
def OnStructureElementsGridSelectCell(self, event):
wx.CallAfter(self.RefreshStructureButtons)
def OnStructureElementsGridEditorShown(self, event):
- row, col = event.GetRow(), event.GetCol()
+ row, col = event.GetRow(), event.GetCol() if self.StructureElementsTable.GetColLabelValue(col, False) == "Type":
type_menu = wx.Menu(title='')
base_menu = wx.Menu(title='')
for base_type in self.Controler.GetBaseTypes():
AppendMenu(base_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=base_type)
self.Bind(wx.EVT_MENU, self.GetElementTypeFunction(base_type), id=new_id)
type_menu.AppendMenu(wx.NewId(), _("Base Types"), base_menu)
datatype_menu = wx.Menu(title='')
for datatype in self.Controler.GetDataTypes(self.TagName, False):
AppendMenu(datatype_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=datatype)
self.Bind(wx.EVT_MENU, self.GetElementTypeFunction(datatype), id=new_id)
type_menu.AppendMenu(wx.NewId(), _("User Data Types"), datatype_menu)
AppendMenu(type_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Array"))
self.Bind(wx.EVT_MENU, self.ElementArrayTypeFunction, id=new_id)
## functionblock_menu = wx.Menu(title='')
## bodytype = self.Controler.GetEditedElementBodyType(self.TagName)
## pouname, poutype = self.Controler.GetEditedElementType(self.TagName)
@@ -661,8 +661,8 @@
def ElementArrayTypeFunction(self, event):
row = self.StructureElementsGrid.GetGridCursorRow()
- dialog = ArrayTypeDialog(self,
- self.Controler.GetDataTypes(self.TagName),
+ dialog = ArrayTypeDialog(self, + self.Controler.GetDataTypes(self.TagName), self.StructureElementsTable.GetValueByName(row, "Type"))
if dialog.ShowModal() == wx.ID_OK:
self.StructureElementsTable.SetValueByName(row, "Type", dialog.GetValue())
--- a/editors/TextViewer.py Mon May 26 14:44:03 2014 +0100
+++ b/editors/TextViewer.py Fri Jun 06 18:30:49 2014 +0100
@@ -2,7 +2,7 @@
#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
-#based on the plcopen standard.
+#based on the plcopen standard. #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
@@ -45,8 +45,8 @@
LETTERS.append(chr(ord('a') + i))
LETTERS.append(chr(ord('A') + i))
-[STC_PLC_WORD, STC_PLC_COMMENT, STC_PLC_NUMBER, STC_PLC_STRING,
- STC_PLC_VARIABLE, STC_PLC_PARAMETER, STC_PLC_FUNCTION, STC_PLC_JUMP,
+[STC_PLC_WORD, STC_PLC_COMMENT, STC_PLC_NUMBER, STC_PLC_STRING, + STC_PLC_VARIABLE, STC_PLC_PARAMETER, STC_PLC_FUNCTION, STC_PLC_JUMP, STC_PLC_ERROR, STC_PLC_SEARCH_RESULT] = range(10)
[SPACE, WORD, NUMBER, STRING, WSTRING, COMMENT, PRAGMA, DPRAGMA] = range(8)
@@ -70,35 +70,35 @@
return reduce(lambda x, y: x or y, map(lambda x: line.startswith(x), symbols), False)
class TextViewer(EditorPanel):
if wx.VERSION < (2, 6, 0):
def Bind(self, event, function, id = None):
event(self, id, function)
def _init_Editor(self, prnt):
- self.Editor = CustomStyledTextCtrl(id=ID_TEXTVIEWERTEXTCTRL,
+ self.Editor = CustomStyledTextCtrl(id=ID_TEXTVIEWERTEXTCTRL, parent=prnt, name="TextViewer", size=wx.Size(0, 0), style=0)
self.Editor.ParentWindow = self
self.Editor.CmdKeyAssign(ord('+'), wx.stc.STC_SCMOD_CTRL, wx.stc.STC_CMD_ZOOMIN)
self.Editor.CmdKeyAssign(ord('-'), wx.stc.STC_SCMOD_CTRL, wx.stc.STC_CMD_ZOOMOUT)
self.Editor.SetViewWhiteSpace(False)
self.Editor.SetLexer(wx.stc.STC_LEX_CONTAINER)
# Global default styles for all languages
self.Editor.StyleSetSpec(wx.stc.STC_STYLE_DEFAULT, "face:%(mono)s,size:%(size)d" % faces)
self.Editor.StyleClearAll() # Reset all to be like the default
self.Editor.StyleSetSpec(wx.stc.STC_STYLE_LINENUMBER, "back:#C0C0C0,size:%(size)d" % faces)
self.Editor.SetSelBackground(1, "#E0E0E0")
self.Editor.StyleSetSpec(STC_PLC_WORD, "fore:#00007F,bold,size:%(size)d" % faces)
self.Editor.StyleSetSpec(STC_PLC_VARIABLE, "fore:#7F0000,size:%(size)d" % faces)
@@ -110,18 +110,18 @@
self.Editor.StyleSetSpec(STC_PLC_JUMP, "fore:#FF7FFF,size:%(size)d" % faces)
self.Editor.StyleSetSpec(STC_PLC_ERROR, "fore:#FF0000,back:#FFFF00,size:%(size)d" % faces)
self.Editor.StyleSetSpec(STC_PLC_SEARCH_RESULT, "fore:#FFFFFF,back:#FFA500,size:%(size)d" % faces)
self.Editor.IndicatorSetStyle(0, wx.stc.STC_INDIC_SQUIGGLE)
if self.ParentWindow is not None and self.Controler is not None:
self.Editor.IndicatorSetForeground(0, wx.RED)
self.Editor.IndicatorSetForeground(0, wx.WHITE)
# Line numbers in the margin
self.Editor.SetMarginType(1, wx.stc.STC_MARGIN_NUMBER)
self.Editor.SetMarginWidth(1, 50)
self.Editor.MarkerDefine(wx.stc.STC_MARKNUM_FOLDEROPEN, wx.stc.STC_MARK_BOXMINUS, "white", "#808080")
self.Editor.MarkerDefine(wx.stc.STC_MARKNUM_FOLDER, wx.stc.STC_MARK_BOXPLUS, "white", "#808080")
@@ -130,11 +130,11 @@
self.Editor.MarkerDefine(wx.stc.STC_MARKNUM_FOLDEREND, wx.stc.STC_MARK_BOXPLUSCONNECTED, "white", "#808080")
self.Editor.MarkerDefine(wx.stc.STC_MARKNUM_FOLDEROPENMID, wx.stc.STC_MARK_BOXMINUSCONNECTED, "white", "#808080")
self.Editor.MarkerDefine(wx.stc.STC_MARKNUM_FOLDERMIDTAIL, wx.stc.STC_MARK_TCORNER, "white", "#808080")
self.Editor.SetTabWidth(2)
self.Editor.SetUseTabs(0)
self.Editor.SetModEventMask(wx.stc.STC_MOD_BEFOREINSERT|
wx.stc.STC_MOD_BEFOREDELETE|
wx.stc.STC_PERFORMED_USER)
@@ -147,13 +147,13 @@
self.Editor.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
self.Bind(wx.stc.EVT_STC_DO_DROP, self.OnDoDrop, id=ID_TEXTVIEWERTEXTCTRL)
self.Bind(wx.stc.EVT_STC_MODIFIED, self.OnModification, id=ID_TEXTVIEWERTEXTCTRL)
def __init__(self, parent, tagname, window, controler, debug = False, instancepath = ""):
if tagname != "" and controler is not None:
self.VARIABLE_PANEL_TYPE = controler.GetPouType(tagname.split("::")[1])
EditorPanel.__init__(self, parent, tagname, window, controler, debug)
@@ -163,62 +163,62 @@
self.DisableEvents = True
self.CurrentAction = None
self.InstancePath = instancepath
self.ResetSearchResults()
self.RefreshHighlightsTimer = wx.Timer(self, -1)
self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer)
self.RefreshHighlightsTimer.Stop()
if self.Debug or self.TagName == "":
if len(self.InstancePath) > 15:
return "..." + self.InstancePath[-12:]
return EditorPanel.GetTitle(self)
def GetInstancePath(self):
def IsViewing(self, tagname):
if self.Debug or self.TagName == "":
return self.InstancePath == tagname
return self.TagName == tagname
return self.Editor.GetText()
self.Editor.SetText(text)
def Colourise(self, start, end):
self.Editor.Colourise(start, end)
def StartStyling(self, pos, mask):
self.Editor.StartStyling(pos, mask)
def SetStyling(self, length, style):
self.Editor.SetStyling(length, style)
return self.Editor.GetCurrentPos()
def ResetSearchResults(self):
self.SearchResults = None
self.CurrentFindHighlight = None
def OnModification(self, event):
if not self.DisableEvents:
mod_type = event.GetModificationType()
@@ -239,7 +239,7 @@
self.CurrentAction = ("Delete", event.GetPosition())
wx.CallAfter(self.RefreshModel)
def OnDoDrop(self, event):
values = eval(event.GetDragText())
@@ -256,7 +256,7 @@
- if values[1] != "function":
+ if values[1] != "function": dialog = wx.TextEntryDialog(self.ParentWindow, "Block name", "Please enter a block name", "", wx.OK|wx.CANCEL|wx.CENTRE)
if dialog.ShowModal() == wx.ID_OK:
@@ -291,9 +291,9 @@
if not location.startswith("%"):
- dialog = wx.SingleChoiceDialog(self.ParentWindow,
- _("Select a variable class:"), _("Variable class"),
- ["Input", "Output", "Memory"],
+ dialog = wx.SingleChoiceDialog(self.ParentWindow, + _("Select a variable class:"), _("Variable class"), + ["Input", "Output", "Memory"], wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL)
if dialog.ShowModal() == wx.ID_OK:
selected = dialog.GetSelection()
@@ -314,13 +314,30 @@
var_type = LOCATIONDATATYPES.get(location[2], ["BOOL"])[0]
self.Controler.AddEditedElementPouVar(self.TagName,
location=location, description=values[4])
self.RefreshVariablePanel()
self.RefreshVariableTree()
event.SetDragText(var_name)
+ elif values[1] == "NamedConstant": + pou_name, pou_type = self.Controler.GetEditedElementType(self.TagName, self.Debug) + if pou_type == "program": + if var_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames(self.Debug)]: + message = _("\"%s\" pou already exists!")%var_name + if not var_name.upper() in [name.upper() for name in self.Controler.GetEditedElementVariables(self.TagName, self.Debug)]: + self.Controler.AddEditedElementPouVar(self.TagName, + description=values[4], initval=initval) + self.RefreshVariablePanel() + self.RefreshVariableTree() + event.SetDragText(var_name) elif values[1] == "Global":
if var_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames(self.Debug)]:
@@ -345,7 +362,7 @@
def SetTextSyntax(self, syntax):
if syntax in ["ST", "ALL"]:
@@ -362,16 +379,16 @@
self.BlockStartKeywords = []
self.BlockEndKeywords = []
def SetKeywords(self, keywords):
self.Keywords = [keyword.upper() for keyword in keywords]
def RefreshJumpList(self):
if self.TextSyntax != "IL":
self.Jumps = [jump.upper() for jump in LABEL_MODEL.findall(self.GetText())]
# Buffer the last model state
self.Controler.BufferProject()
@@ -379,42 +396,42 @@
self.ParentWindow.RefreshTitle()
self.ParentWindow.RefreshFileMenu()
self.ParentWindow.RefreshEditMenu()
def StartBuffering(self):
self.Controler.StartBuffering()
self.ParentWindow.RefreshTitle()
self.ParentWindow.RefreshFileMenu()
self.ParentWindow.RefreshEditMenu()
if self.CurrentAction != None:
self.Controler.EndBuffering()
self.CurrentAction = None
def GetBufferState(self):
if not self.Debug and self.TextSyntax != "ALL":
return self.Controler.GetBufferState()
if not self.Debug and self.TextSyntax != "ALL":
self.Controler.LoadPrevious()
self.ParentWindow.CloseTabsWithoutModel()
if not self.Debug and self.TextSyntax != "ALL":
self.Controler.LoadNext()
self.ParentWindow.CloseTabsWithoutModel()
if not self.Debug and self.TextSyntax != "ALL":
return self.Controler.GetEditedElement(self.TagName) is None
def RefreshView(self, variablepanel=True):
EditorPanel.RefreshView(self, variablepanel)
if self.Controler is not None:
self.DisableEvents = True
@@ -434,12 +451,12 @@
self.Editor.EmptyUndoBuffer()
self.DisableEvents = False
self.RefreshVariableTree()
self.TypeNames = [typename.upper() for typename in self.Controler.GetDataTypes(self.TagName, True, self.Debug)]
self.EnumeratedValues = [value.upper() for value in self.Controler.GetEnumeratedDataValues()]
for category in self.Controler.GetBlockTypes(self.TagName, self.Debug):
for blocktype in category["list"]:
@@ -455,13 +472,13 @@
self.Functions[blockname] = {"interface": interface,
"extensible": blocktype["extensible"]}
def RefreshVariableTree(self):
words = self.TagName.split("::")
self.Variables = self.GenerateVariableTree(
- [(variable.Name, variable.Type, variable.Tree)
+ [(variable.Name, variable.Type, variable.Tree) for variable in self.Controler.GetEditedElementInterfaceVars(
self.TagName, True, self.Debug)])
if self.Controler.GetEditedElementType(self.TagName, self.Debug)[1] == "function" or words[0] == "T" and self.TextSyntax == "IL":
@@ -470,22 +487,22 @@
self.Variables[words[-1].upper()] = self.GenerateVariableTree(var_tree)
self.Variables[words[-1].upper()] = {}
def GenerateVariableTree(self, list):
for var_name, var_type, (var_tree, var_dimension) in list:
tree[var_name.upper()] = self.GenerateVariableTree(var_tree)
def IsValidVariable(self, name, context):
return context is not None and context.get(name, None) is not None
def IsCallParameter(self, name, call):
- return (call["interface"].get(name.upper(), None) is not None or
+ return (call["interface"].get(name.upper(), None) is not None or call["extensible"] and EXTENSIBLE_PARAMETER.match(name.upper()) is not None)
def RefreshLineFolding(self, line_number):
if self.TextSyntax in ["ST", "ALL"]:
level = wx.stc.STC_FOLDLEVELBASE + self.Editor.GetLineIndentation(line_number)
@@ -497,7 +514,7 @@
level = self.Editor.GetFoldLevel(line_number - 1) & wx.stc.STC_FOLDLEVELNUMBERMASK
if level != wx.stc.STC_FOLDLEVELBASE:
- level |= wx.stc.STC_FOLDLEVELWHITEFLAG
+ level |= wx.stc.STC_FOLDLEVELWHITEFLAG elif LineStartswith(line, self.BlockStartKeywords):
level |= wx.stc.STC_FOLDLEVELHEADERFLAG
elif LineStartswith(line, self.BlockEndKeywords):
@@ -506,7 +523,7 @@
level = self.Editor.GetFoldLevel(line_number - 1) & wx.stc.STC_FOLDLEVELNUMBERMASK
self.Editor.SetFoldLevel(line_number, level)
def OnStyleNeeded(self, event):
line_number = self.Editor.LineFromPosition(self.Editor.GetEndStyled())
@@ -517,10 +534,10 @@
self.RefreshLineFolding(line_number)
end_pos = event.GetPosition()
self.StartStyling(start_pos, 0xff)
current_context = self.Variables
current_pos = last_styled_pos
@@ -642,7 +659,7 @@
elif self.IsCallParameter(word, current_call):
self.SetStyling(current_pos - last_styled_pos, STC_PLC_PARAMETER)
elif word in self.Functions:
- self.SetStyling(current_pos - last_styled_pos, STC_PLC_FUNCTION)
+ self.SetStyling(current_pos - last_styled_pos, STC_PLC_FUNCTION) elif self.TextSyntax == "IL" and word in self.Jumps:
self.SetStyling(current_pos - last_styled_pos, STC_PLC_JUMP)
elif word in self.EnumeratedValues:
@@ -670,7 +687,7 @@
if char == '[' and current_context is not None:
self.ContextStack.append(current_context.get(word, None))
current_context = self.Variables
last_styled_pos = current_pos
@@ -717,20 +734,20 @@
self.SetStyling(current_pos - start_pos, 31)
self.ShowHighlights(start_pos, end_pos)
def OnMarginClick(self, event):
if event.GetMargin() == 2:
line = self.Editor.LineFromPosition(event.GetPosition())
if self.Editor.GetFoldLevel(line) & wx.stc.STC_FOLDLEVELHEADERFLAG:
self.Editor.ToggleFold(line)
def OnUpdateUI(self, event):
selected = self.Editor.GetSelectedText()
if self.ParentWindow and selected != "":
self.ParentWindow.SetCopyBuffer(selected, True)
self.DisableEvents = True
@@ -738,12 +755,12 @@
self.DisableEvents = False
self.Editor.CmdKeyExecute(wx.stc.STC_CMD_COPY)
self.ParentWindow.RefreshEditMenu()
self.DisableEvents = True
@@ -751,28 +768,28 @@
self.DisableEvents = False
def Search(self, criteria):
return self.Controler.SearchInPou(self.TagName, criteria, self.Debug)
def Find(self, direction, search_params):
if self.SearchParams != search_params:
self.ClearHighlights(SEARCH_RESULT_HIGHLIGHT)
self.SearchParams = search_params
- "raw_pattern": search_params["find_pattern"],
+ "raw_pattern": search_params["find_pattern"], "pattern": re.compile(search_params["find_pattern"]),
"case_sensitive": search_params["case_sensitive"],
"regular_expression": search_params["regular_expression"],
(infos[1:], start, end, SEARCH_RESULT_HIGHLIGHT)
- for infos, start, end, text in
+ for infos, start, end, text in self.CurrentFindHighlight = None
if len(self.SearchResults) > 0:
if self.CurrentFindHighlight is not None:
old_idx = self.SearchResults.index(self.CurrentFindHighlight)
@@ -787,24 +804,24 @@
self.CurrentFindHighlight = self.SearchResults[0]
self.AddHighlight(*self.CurrentFindHighlight)
if self.CurrentFindHighlight is not None:
self.RemoveHighlight(*self.CurrentFindHighlight)
self.CurrentFindHighlight = None
self.Controler.SetEditedElementText(self.TagName, self.GetText())
self.ResetSearchResults()
def OnKeyDown(self, event):
if self.Controler is not None:
if self.Editor.CallTipActive():
self.Editor.CallTipCancel()
line = self.Editor.GetCurrentLine()
@@ -814,15 +831,15 @@
start_pos = self.Editor.GetLineEndPosition(line - 1) + 1
end_pos = self.GetCurrentPos()
lineText = self.Editor.GetTextRange(start_pos, end_pos).replace("\t", " ")
if key == wx.WXK_SPACE and event.ControlDown():
words = lineText.split(" ")
words = [word for i, word in enumerate(words) if word != '' or i == len(words) - 1]
if self.TextSyntax == "IL":
@@ -875,7 +892,7 @@
def ClearHighlights(self, highlight_type=None):
EditorPanel.ClearHighlights(self, highlight_type)
if highlight_type is None:
@@ -886,7 +903,7 @@
def AddHighlight(self, infos, start, end, highlight_type):
EditorPanel.AddHighlight(self, infos, start, end, highlight_type)
highlight_type = HIGHLIGHT_TYPES.get(highlight_type, None)
if infos[0] == "body" and highlight_type is not None:
self.Highlights.append((infos[1], start, end, highlight_type))
@@ -895,13 +912,13 @@
def RemoveHighlight(self, infos, start, end, highlight_type):
EditorPanel.RemoveHighlight(self, infos, start, end, highlight_type)
highlight_type = HIGHLIGHT_TYPES.get(highlight_type, None)
- if (infos[0] == "body" and highlight_type is not None and
+ if (infos[0] == "body" and highlight_type is not None and (infos[1], start, end, highlight_type) in self.Highlights):
self.Highlights.remove((infos[1], start, end, highlight_type))
self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True)
def ShowHighlights(self, start_pos, end_pos):
for indent, start, end, highlight_type in self.Highlights:
@@ -917,4 +934,4 @@
self.SetStyling(highlight_end_pos - highlight_start_pos, highlight_type)
self.StartStyling(highlight_start_pos, 0x00)
self.SetStyling(len(self.Editor.GetText()) - highlight_end_pos, wx.stc.STC_STYLE_DEFAULT)