# HG changeset patch # User Andrey Skvortsov # Date 1503346978 -10800 # Node ID 64beb9e9c749be9f5c9daa7e865451658a79d33d # Parent 31e63e25b4ccf7e0fb153e84f98fe9fa99d06679# Parent 3311eea28d5660ee63bd5b150266ebdab38a4d12 fix codestyle issues according to PEP8 diff -r 31e63e25b4cc -r 64beb9e9c749 Beremiz.py --- a/Beremiz.py Mon Aug 21 20:17:19 2017 +0000 +++ b/Beremiz.py Mon Aug 21 23:22:58 2017 +0300 @@ -23,12 +23,14 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -import os, sys, getopt +import os +import sys +import getopt import time import __builtin__ import util.paths as paths + class BeremizIDELauncher: def __init__(self): self.updateinfo_url = None @@ -40,7 +42,7 @@ self.splashPath = self.Bpath("images", "splash.png") def Bpath(self, *args): - return os.path.join(self.app_dir,*args) + return os.path.join(self.app_dir, *args) def ShowSplashScreen(self): from wx.lib.agw.advancedsplash import AdvancedSplash @@ -53,21 +55,20 @@ self.splash.Show() self.splash.ProcessEvent(wx.PaintEvent()) else: - for i in range(0,30): + for i in range(0, 30): wx.Yield() - time.sleep(0.01); - + time.sleep(0.01) def Usage(self): print "Usage:" - print "%s [Options] [Projectpath] [Buildpath]"%sys.argv[0] + print "%s [Options] [Projectpath] [Buildpath]" % sys.argv[0] print "" print "Supported options:" print "-h --help Print this help" print "-u --updatecheck URL Retrieve update information by checking URL" print "-e --extend PathToExtension Extend IDE functionality by loading at start additional extensions" print "" - print "" + print "" def SetCmdOptions(self): self.shortCmdOpts = "hu:e:" @@ -108,7 +109,7 @@ def CreateApplication(self): if os.path.exists("BEREMIZ_DEBUG"): __builtin__.__dict__["BMZ_DBG"] = True - else : + else: __builtin__.__dict__["BMZ_DBG"] = False global wxversion, wx @@ -157,10 +158,10 @@ def updateinfoproc(): global updateinfo - try : + try: import urllib2 - updateinfo = urllib2.urlopen(self.updateinfo_url,None).read() - except : + updateinfo = urllib2.urlopen(self.updateinfo_url, None).read() + except Exception: updateinfo = _("update info unavailable.") from threading import Thread @@ -178,7 +179,7 @@ import version import tempfile logpath = tempfile.gettempdir()+os.sep+'Beremiz' - BeremizIDE.AddExceptHook(logpath,version.app_version) + BeremizIDE.AddExceptHook(logpath, version.app_version) def ShowUI(self): self.frame = BeremizIDE.Beremiz(None, self.projectOpen, self.buildpath) @@ -190,6 +191,7 @@ self.ProcessCommandLineArgs() self.CreateApplication() + if __name__ == '__main__': beremiz = BeremizIDELauncher() beremiz.Start() diff -r 31e63e25b4cc -r 64beb9e9c749 BeremizIDE.py --- a/BeremizIDE.py Mon Aug 21 20:17:19 2017 +0000 +++ b/BeremizIDE.py Mon Aug 21 23:22:58 2017 +0300 @@ -24,26 +24,32 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import os, sys +import os +import sys import tempfile import shutil import random import time import version + +from types import ListType + +import wx.lib.buttons +import wx.lib.statbmp +import wx.stc +import cPickle +import types +import time +import re +import platform +import time +import traceback +import commands +import threading +from threading import Lock, Timer, currentThread +from time import time as gettime + import util.paths as paths -from types import ListType - -beremiz_dir = paths.AbsDir(__file__) - -def Bpath(*args): - return os.path.join(beremiz_dir,*args) - - - -import wx.lib.buttons, wx.lib.statbmp, wx.stc -import cPickle -import types, time, re, platform, time, traceback, commands - from docutil import OpenHtmlFrame from editors.EditorPanel import EditorPanel from editors.Viewer import Viewer @@ -57,27 +63,63 @@ from controls import EnhancedStatusBar as esb from dialogs.AboutDialog import ShowAboutDialog -from PLCControler import LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY, ITEM_PROJECT, ITEM_RESOURCE +from PLCControler import \ + LOCATION_CONFNODE, \ + LOCATION_MODULE, \ + LOCATION_GROUP, \ + LOCATION_VAR_INPUT, \ + LOCATION_VAR_OUTPUT, \ + LOCATION_VAR_MEMORY, \ + ITEM_PROJECT, \ + ITEM_RESOURCE + from ProjectController import ProjectController, GetAddMenuItems, MATIEC_ERROR_MODEL, ITEM_CONFNODE +from IDEFrame import \ + TITLE,\ + EDITORTOOLBAR,\ + FILEMENU,\ + EDITMENU,\ + DISPLAYMENU,\ + PROJECTTREE,\ + POUINSTANCEVARIABLESPANEL,\ + LIBRARYTREE,\ + SCALING,\ + PAGETITLES,\ + IDEFrame, \ + AppendMenu,\ + EncodeFileSystemPath, \ + DecodeFileSystemPath + +from util.BitmapLibrary import GetBitmap + + +beremiz_dir = paths.AbsDir(__file__) + + +def Bpath(*args): + return os.path.join(beremiz_dir, *args) + MAX_RECENT_PROJECTS = 9 + if wx.Platform == '__WXMSW__': faces = { - 'mono' : 'Courier New', - 'size' : 8, + 'mono': 'Courier New', + 'size': 8, } else: faces = { - 'mono' : 'Courier', - 'size' : 10, + 'mono': 'Courier', + 'size': 10, } -from threading import Lock,Timer,currentThread + MainThread = currentThread().ident REFRESH_PERIOD = 0.1 -from time import time as gettime + + class LogPseudoFile: """ Base class for file like objects to facilitate StdOut for the Shell.""" def __init__(self, output, risecall): @@ -96,15 +138,15 @@ self.LastRefreshTime = gettime() self.LastRefreshTimer = None - def write(self, s, style = None): + def write(self, s, style=None): if self.lock.acquire(): - self.stack.append((s,style)) + self.stack.append((s, style)) self.lock.release() current_time = gettime() self.TimerAccessLock.acquire() if self.LastRefreshTimer: self.LastRefreshTimer.cancel() - self.LastRefreshTimer=None + self.LastRefreshTimer = None self.TimerAccessLock.release() if current_time - self.LastRefreshTime > REFRESH_PERIOD and self.RefreshLock.acquire(False): self._should_write() @@ -133,11 +175,12 @@ self.YieldLock.release() def _write(self): - if self.output : + if self.output: self.output.Freeze() self.lock.acquire() for s, style in self.stack: - if style is None : style=self.black_white + if style is None: + style = self.black_white if style != self.black_white: self.output.StartStyling(self.output.GetLength(), 0xff) @@ -159,7 +202,7 @@ self.LastRefreshTime = gettime() try: self.RefreshLock.release() - except: + except Exception: pass newtime = time.time() if newtime - self.rising_timer > 1: @@ -167,10 +210,10 @@ self.rising_timer = newtime def write_warning(self, s): - self.write(s,self.red_white) + self.write(s, self.red_white) def write_error(self, s): - self.write(s,self.red_yellow) + self.write(s, self.red_yellow) def writeyield(self, s): self.write(s) @@ -187,21 +230,9 @@ def isatty(self): return False + ID_FILEMENURECENTPROJECTS = wx.NewId() -from IDEFrame import TITLE,\ - EDITORTOOLBAR,\ - FILEMENU,\ - EDITMENU,\ - DISPLAYMENU,\ - PROJECTTREE,\ - POUINSTANCEVARIABLESPANEL,\ - LIBRARYTREE,\ - SCALING,\ - PAGETITLES,\ - IDEFrame, AppendMenu,\ - EncodeFileSystemPath, DecodeFileSystemPath -from util.BitmapLibrary import GetBitmap class Beremiz(IDEFrame): @@ -213,29 +244,29 @@ def _init_coll_FileMenu_Items(self, parent): AppendMenu(parent, help='', id=wx.ID_NEW, - kind=wx.ITEM_NORMAL, text=_(u'New') + '\tCTRL+N') + kind=wx.ITEM_NORMAL, text=_(u'New') + '\tCTRL+N') AppendMenu(parent, help='', id=wx.ID_OPEN, - kind=wx.ITEM_NORMAL, text=_(u'Open') + '\tCTRL+O') + kind=wx.ITEM_NORMAL, text=_(u'Open') + '\tCTRL+O') parent.AppendMenu(ID_FILEMENURECENTPROJECTS, _("&Recent Projects"), self.RecentProjectsMenu) parent.AppendSeparator() AppendMenu(parent, help='', id=wx.ID_SAVE, - kind=wx.ITEM_NORMAL, text=_(u'Save') + '\tCTRL+S') + kind=wx.ITEM_NORMAL, text=_(u'Save') + '\tCTRL+S') AppendMenu(parent, help='', id=wx.ID_SAVEAS, - kind=wx.ITEM_NORMAL, text=_(u'Save as') + '\tCTRL+SHIFT+S') + kind=wx.ITEM_NORMAL, text=_(u'Save as') + '\tCTRL+SHIFT+S') AppendMenu(parent, help='', id=wx.ID_CLOSE, - kind=wx.ITEM_NORMAL, text=_(u'Close Tab') + '\tCTRL+W') + kind=wx.ITEM_NORMAL, text=_(u'Close Tab') + '\tCTRL+W') AppendMenu(parent, help='', id=wx.ID_CLOSE_ALL, - kind=wx.ITEM_NORMAL, text=_(u'Close Project') + '\tCTRL+SHIFT+W') + kind=wx.ITEM_NORMAL, text=_(u'Close Project') + '\tCTRL+SHIFT+W') parent.AppendSeparator() AppendMenu(parent, help='', id=wx.ID_PAGE_SETUP, - kind=wx.ITEM_NORMAL, text=_(u'Page Setup') + '\tCTRL+ALT+P') + kind=wx.ITEM_NORMAL, text=_(u'Page Setup') + '\tCTRL+ALT+P') AppendMenu(parent, help='', id=wx.ID_PREVIEW, - kind=wx.ITEM_NORMAL, text=_(u'Preview') + '\tCTRL+SHIFT+P') + kind=wx.ITEM_NORMAL, text=_(u'Preview') + '\tCTRL+SHIFT+P') AppendMenu(parent, help='', id=wx.ID_PRINT, - kind=wx.ITEM_NORMAL, text=_(u'Print') + '\tCTRL+P') + kind=wx.ITEM_NORMAL, text=_(u'Print') + '\tCTRL+P') parent.AppendSeparator() AppendMenu(parent, help='', id=wx.ID_EXIT, - kind=wx.ITEM_NORMAL, text=_(u'Quit') + '\tCTRL+Q') + 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) @@ -263,7 +294,7 @@ self._RecursiveAddMenuItems(new_menu, children) else: AppendMenu(menu, help=help, id=new_id, - kind=wx.ITEM_NORMAL, text=text) + kind=wx.ITEM_NORMAL, text=text) self.Bind(wx.EVT_MENU, self.GetAddConfNodeFunction(name), id=new_id) @@ -272,15 +303,18 @@ self._RecursiveAddMenuItems(parent, GetAddMenuItems()) def _init_coll_HelpMenu_Items(self, parent): - handler=lambda event: { - wx.MessageBox(version.GetCommunityHelpMsg(), _(u'Community support'), wx.OK | wx.ICON_INFORMATION) - } + def handler(event): + return wx.MessageBox( + version.GetCommunityHelpMsg(), + _(u'Community support'), + wx.OK | wx.ICON_INFORMATION) + id = wx.NewId() - parent.Append(help='', id=id, kind=wx.ITEM_NORMAL, text=_(u'Community support')) + parent.Append(help='', id=id, kind=wx.ITEM_NORMAL, text=_(u'Community support')) self.Bind(wx.EVT_MENU, handler, id=id) - + parent.Append(help='', id=wx.ID_ABOUT, - kind=wx.ITEM_NORMAL, text=_(u'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): @@ -299,26 +333,26 @@ inspectorID = wx.NewId() self.Bind(wx.EVT_MENU, self.OnOpenWidgetInspector, id=inspectorID) - accels = [wx.AcceleratorEntry(wx.ACCEL_CTRL|wx.ACCEL_ALT, ord('I'), inspectorID)] + accels = [wx.AcceleratorEntry(wx.ACCEL_CTRL | wx.ACCEL_ALT, ord('I'), inspectorID)] keyID = wx.NewId() self.Bind(wx.EVT_MENU, self.SwitchFullScrMode, id=keyID) accels += [wx.AcceleratorEntry(wx.ACCEL_NORMAL, wx.WXK_F12, keyID)] - for method,shortcut in [("Stop", wx.WXK_F4), - ("Run", wx.WXK_F5), - ("Transfer", wx.WXK_F6), - ("Connect", wx.WXK_F7), - ("Build", wx.WXK_F11)]: - def OnMethodGen(obj,meth): + for method, shortcut in [("Stop", wx.WXK_F4), + ("Run", wx.WXK_F5), + ("Transfer", wx.WXK_F6), + ("Connect", wx.WXK_F7), + ("Build", wx.WXK_F11)]: + def OnMethodGen(obj, meth): def OnMethod(evt): if obj.CTR is not None: - obj.CTR.CallMethod('_'+meth) + obj.CTR.CallMethod('_'+meth) wx.CallAfter(self.RefreshStatusToolBar) return OnMethod newid = wx.NewId() - self.Bind(wx.EVT_MENU, OnMethodGen(self,method), id=newid) - accels += [wx.AcceleratorEntry(wx.ACCEL_NORMAL, shortcut,newid)] + self.Bind(wx.EVT_MENU, OnMethodGen(self, method), id=newid) + accels += [wx.AcceleratorEntry(wx.ACCEL_NORMAL, shortcut, newid)] self.SetAcceleratorTable(wx.AcceleratorTable(accels)) @@ -349,29 +383,29 @@ self.MainTabs["LogConsole"] = (self.LogConsole, _("Console")) self.BottomNoteBook.AddPage(*self.MainTabs["LogConsole"]) - #self.BottomNoteBook.Split(self.BottomNoteBook.GetPageIndex(self.LogConsole), wx.RIGHT) + # 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) + # 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) + wx.TB_FLAT | wx.TB_NODIVIDER | wx.NO_BORDER) StatusToolBar.SetToolBitmapSize(wx.Size(25, 25)) StatusToolBar.Realize() self.Panes["StatusToolBar"] = StatusToolBar self.AUIManager.AddPane(StatusToolBar, wx.aui.AuiPaneInfo(). - Name("StatusToolBar").Caption(_("Status ToolBar")). - ToolbarPane().Top().Position(1). - LeftDockable(False).RightDockable(False)) + Name("StatusToolBar").Caption(_("Status ToolBar")). + ToolbarPane().Top().Position(1). + LeftDockable(False).RightDockable(False)) self.AUIManager.Update() self.ConnectionStatusBar = esb.EnhancedStatusBar(self, style=wx.ST_SIZEGRIP) self._init_coll_ConnectionStatusBar_Fields(self.ConnectionStatusBar) - self.ProgressStatusBar = wx.Gauge(self.ConnectionStatusBar, -1, range = 100) - self.ConnectionStatusBar.AddWidget(self.ProgressStatusBar, esb.ESB_EXACT_FIT, esb.ESB_EXACT_FIT, 2) + self.ProgressStatusBar = wx.Gauge(self.ConnectionStatusBar, -1, range=100) + self.ConnectionStatusBar.AddWidget(self.ProgressStatusBar, esb.ESB_EXACT_FIT, esb.ESB_EXACT_FIT, 2) self.ProgressStatusBar.Hide() self.SetStatusBar(self.ConnectionStatusBar) @@ -383,15 +417,14 @@ # commands invoked by build process by default are # found here. os.environ["PATH"] = os.getcwd()+';'+os.environ["PATH"] - - + def __init__(self, parent, projectOpen=None, buildpath=None, ctr=None, debug=True): # Add beremiz's icon in top left corner of the frame self.icon = wx.Icon(Bpath("images", "brz.ico"), wx.BITMAP_TYPE_ICO) self.__init_execute_path() - + IDEFrame.__init__(self, parent, debug) - self.Log = LogPseudoFile(self.LogConsole,self.SelectTab) + self.Log = LogPseudoFile(self.LogConsole, self.SelectTab) self.local_runtime = None self.runtime_port = None @@ -405,17 +438,17 @@ # Icons for location items for imgname, itemtype in [ - ("CONFIGURATION", LOCATION_CONFNODE), - ("RESOURCE", LOCATION_MODULE), - ("PROGRAM", LOCATION_GROUP), - ("VAR_INPUT", LOCATION_VAR_INPUT), - ("VAR_OUTPUT", LOCATION_VAR_OUTPUT), - ("VAR_LOCAL", LOCATION_VAR_MEMORY)]: + ("CONFIGURATION", LOCATION_CONFNODE), + ("RESOURCE", LOCATION_MODULE), + ("PROGRAM", LOCATION_GROUP), + ("VAR_INPUT", LOCATION_VAR_INPUT), + ("VAR_OUTPUT", LOCATION_VAR_OUTPUT), + ("VAR_LOCAL", LOCATION_VAR_MEMORY)]: self.LocationImageDict[itemtype] = self.LocationImageList.Add(GetBitmap(imgname)) # Icons for other items for imgname, itemtype in [ - ("Extension", ITEM_CONFNODE)]: + ("Extension", ITEM_CONFNODE)]: self.TreeImageDict[itemtype] = self.TreeImageList.Add(GetBitmap(imgname)) if projectOpen is not None: @@ -461,23 +494,24 @@ else: self.SetTitle(name) - def StartLocalRuntime(self, taskbaricon = True): + def StartLocalRuntime(self, taskbaricon=True): if (self.local_runtime is None) or (self.local_runtime.exitcode is not None): # create temporary directory for runtime working directory self.local_runtime_tmpdir = tempfile.mkdtemp() # choose an arbitrary random port for runtime self.runtime_port = int(random.random() * 1000) + 61131 # launch local runtime - self.local_runtime = ProcessLogger(self.Log, - "\"%s\" \"%s\" -p %s -i localhost %s %s"%( + self.local_runtime = ProcessLogger( + self.Log, + "\"%s\" \"%s\" -p %s -i localhost %s %s" % ( sys.executable, Bpath("Beremiz_service.py"), self.runtime_port, - {False : "-x 0", True :"-x 1"}[taskbaricon], + {False: "-x 0", True: "-x 1"}[taskbaricon], self.local_runtime_tmpdir), no_gui=False, - timeout=500, keyword = self.local_runtime_tmpdir, - cwd = self.local_runtime_tmpdir) + timeout=500, keyword=self.local_runtime_tmpdir, + cwd=self.local_runtime_tmpdir) self.local_runtime.spin() return self.runtime_port @@ -531,17 +565,19 @@ if result is not None: first_line, first_column, last_line, last_column, error = result.groups() infos = self.CTR.ShowError(self.Log, - (int(first_line), int(first_column)), - (int(last_line), int(last_column))) - - ## Function displaying an Error dialog in PLCOpenEditor. - # @return False if closing cancelled. + (int(first_line), int(first_column)), + (int(last_line), int(last_column))) + def CheckSaveBeforeClosing(self, title=_("Close Project")): + """Function displaying an Error dialog in PLCOpenEditor. + + :returns: False if closing cancelled. + """ if self.CTR.ProjectTestModified(): dialog = wx.MessageDialog(self, _("There are changes, do you want to save?"), title, - wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION) + wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION) answer = dialog.ShowModal() dialog.Destroy() if answer == wx.ID_YES: @@ -584,6 +620,7 @@ # Twisted reactor needs to be stopped only before quit, # since it cannot be restarted ToDoBeforeQuit = [] + def AddToDoBeforeQuit(self, Thing): self.ToDoBeforeQuit.append(Thing) @@ -599,7 +636,7 @@ self.SaveLastState() - for Thing in self.ToDoBeforeQuit : + for Thing in self.ToDoBeforeQuit: Thing() self.ToDoBeforeQuit = [] @@ -657,7 +694,7 @@ try: recent_projects = map(DecodeFileSystemPath, self.GetConfigEntry("RecentProjects", [])) - except: + except Exception: recent_projects = [] while self.RecentProjectsMenu.GetMenuItemCount() > len(recent_projects): @@ -715,10 +752,10 @@ if self.CTR is not None: for confnode_method in self.CTR.StatusMethods: - if "method" in confnode_method and confnode_method.get("shown",True): + if "method" in confnode_method and confnode_method.get("shown", True): id = wx.NewId() - 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) @@ -807,7 +844,7 @@ try: recent_projects = map(DecodeFileSystemPath, self.GetConfigEntry("RecentProjects", [])) - except: + except Exception: recent_projects = [] if projectpath in recent_projects: recent_projects.remove(projectpath) @@ -827,10 +864,10 @@ try: defaultpath = DecodeFileSystemPath(self.Config.Read("lastopenedfolder")) - except: + except Exception: defaultpath = os.path.expanduser("~") - dialog = wx.DirDialog(self , _("Choose an empty directory for new project"), defaultpath) + dialog = wx.DirDialog(self, _("Choose an empty directory for new project"), defaultpath) if dialog.ShowModal() == wx.ID_OK: projectpath = dialog.GetPath() self.Config.Write("lastopenedfolder", @@ -863,11 +900,11 @@ try: defaultpath = DecodeFileSystemPath(self.Config.Read("lastopenedfolder")) - except: + except Exception: defaultpath = os.path.expanduser("~") - dialog = wx.DirDialog(self , _("Choose a project"), defaultpath, style=wx.DEFAULT_DIALOG_STYLE| - wx.RESIZE_BORDER) + dialog = wx.DirDialog(self, _("Choose a project"), defaultpath, + style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) if dialog.ShowModal() == wx.ID_OK: self.OpenProject(dialog.GetPath()) dialog.Destroy() @@ -932,7 +969,7 @@ self.Close() def OnAboutMenu(self, event): - info = version.GetAboutDialogInfo() + info = version.GetAboutDialogInfo() ShowAboutDialog(self, info) def OnProjectTreeItemBeginEdit(self, event): @@ -1015,8 +1052,9 @@ self.ProjectTree.SelectItem(root) self.ResetSelectedItem() else: - return self.RecursiveProjectTreeItemSelection(root, - [(word, ITEM_CONFNODE) for word in tagname.split(".")]) + return self.RecursiveProjectTreeItemSelection( + root, + [(word, ITEM_CONFNODE) for word in tagname.split(".")]) elif words[0] == "R": return self.RecursiveProjectTreeItemSelection(root, [(words[2], ITEM_RESOURCE)]) elif not os.path.exists(words[0]): @@ -1043,19 +1081,20 @@ def DeleteConfNode(self, confnode): if self.CTR.CheckProjectPathPerm(): - dialog = wx.MessageDialog(self, + dialog = wx.MessageDialog( + self, _("Really delete node '%s'?") % confnode.CTNName(), _("Remove %s node") % confnode.CTNType, - wx.YES_NO|wx.NO_DEFAULT) + wx.YES_NO | wx.NO_DEFAULT) if dialog.ShowModal() == wx.ID_YES: confnode.CTNRemove() del confnode self._Refresh(TITLE, FILEMENU, PROJECTTREE) dialog.Destroy() -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Highlights showing functions -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- def ShowHighlight(self, infos, start, end, highlight_type): config_name = self.Controler.GetProjectMainConfigurationName() @@ -1068,16 +1107,17 @@ else: IDEFrame.ShowHighlight(self, infos, start, end, highlight_type) -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Exception Handler -#------------------------------------------------------------------------------- -import threading, traceback +# ------------------------------------------------------------------------------- Max_Traceback_List_Size = 20 + def Display_Exception_Dialog(e_type, e_value, e_tb, bug_report_path): trcbck_lst = [] - for i,line in enumerate(traceback.extract_tb(e_tb)): + for i, line in enumerate(traceback.extract_tb(e_tb)): trcbck = " " + str(i+1) + ". " if line[0].find(os.getcwd()) == -1: trcbck += "file : " + str(line[0]) + ", " @@ -1091,7 +1131,8 @@ if cap: cap.ReleaseMouse() - dlg = wx.SingleChoiceDialog(None, + dlg = wx.SingleChoiceDialog( + None, _(""" An unhandled exception (bug) occured. Bug report saved at : (%s) @@ -1113,6 +1154,7 @@ return res + def get_last_traceback(tb): while tb.tb_next: tb = tb.tb_next @@ -1123,17 +1165,18 @@ return '\n'.join(['%s%s: %s' % (indent, k, repr(v)[:10000]) for k, v in d.iteritems()]) -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 save_bug_report(e_type, e_value, e_traceback, bug_report_path,date): +ignored_exceptions = [] # a problem with a line in a module is only reported once per session + + +def AddExceptHook(path, app_version='[No version]'): + + def save_bug_report(e_type, e_value, e_traceback, bug_report_path, date): info = { - 'app-title': wx.GetApp().GetAppName(), # app_title + 'app-title': wx.GetApp().GetAppName(), 'app-version': app_version, 'wx-version': wx.VERSION_STRING, 'wx-platform': wx.Platform, - 'python-version': platform.python_version(), # sys.version.split()[0], + 'python-version': platform.python_version(), 'platform': platform.platform(), 'e-type': e_type, 'e-value': e_value, @@ -1148,7 +1191,7 @@ if 'self' in exception_locals: try: info['self'] = format_namespace(exception_locals['self'].__dict__) - except: + except Exception: pass if not os.path.exists(path): os.mkdir(path) @@ -1160,7 +1203,7 @@ output.close() 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 + 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) ex = (last_tb.tb_frame.f_code.co_filename, last_tb.tb_frame.f_lineno) if ex not in ignored_exceptions: @@ -1169,19 +1212,21 @@ bug_report_path = path + os.sep + "bug_report_" + time.strftime("%Y_%m_%d__%H-%M-%S") + ".txt" save_bug_report(e_type, e_value, e_traceback, bug_report_path, date) Display_Exception_Dialog(e_type, e_value, e_traceback, bug_report_path) - #sys.excepthook = lambda *args: wx.CallAfter(handle_exception, *args) + # sys.excepthook = lambda *args: wx.CallAfter(handle_exception, *args) sys.excepthook = handle_exception init_old = threading.Thread.__init__ + def init(self, *args, **kwargs): init_old(self, *args, **kwargs) run_old = self.run + def run_with_except_hook(*args, **kw): try: run_old(*args, **kw) except (KeyboardInterrupt, SystemExit): raise - except: + except Exception: sys.excepthook(*sys.exc_info()) self.run = run_with_except_hook threading.Thread.__init__ = init diff -r 31e63e25b4cc -r 64beb9e9c749 Beremiz_service.py --- a/Beremiz_service.py Mon Aug 21 20:17:19 2017 +0000 +++ b/Beremiz_service.py Mon Aug 21 23:22:58 2017 +0300 @@ -23,8 +23,18 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import os, sys, getopt +import os +import sys +import getopt +import threading from threading import Thread +import traceback +import __builtin__ +import Pyro.core as pyro + +from runtime import PLCObject, PLCprint, ServicePublisher +import util.paths as paths + def usage(): print """ @@ -42,13 +52,14 @@ -e - python extension (absolute path .py) working_dir - directory where are stored PLC files -"""%sys.argv[0] +""" % sys.argv[0] + try: opts, argv = getopt.getopt(sys.argv[1:], "i:p:n:x:t:a:w:c:e:h") except getopt.GetoptError, err: # print help information and exit: - print str(err) # will print something like "option -a not recognized" + print str(err) # will print something like "option -a not recognized" usage() sys.exit(2) @@ -64,7 +75,7 @@ enabletwisted = True havetwisted = False -extensions=[] +extensions = [] for o, a in opts: if o == "-h": @@ -97,7 +108,7 @@ usage() sys.exit() -import util.paths as paths + beremiz_dir = paths.AbsDir(__file__) if len(argv) > 1: @@ -108,21 +119,22 @@ os.chdir(WorkingDir) elif len(argv) == 0: WorkingDir = os.getcwd() - argv=[WorkingDir] - -import __builtin__ + argv = [WorkingDir] + if __name__ == '__main__': __builtin__.__dict__['_'] = lambda x: x + def Bpath(*args): - return os.path.join(beremiz_dir,*args) + return os.path.join(beremiz_dir, *args) + def SetupI18n(): # Import module for internationalization import gettext # Get folder containing translation files - localedir = os.path.join(beremiz_dir,"locale") + localedir = os.path.join(beremiz_dir, "locale") # Get the default language langid = wx.LANGUAGE_DEFAULT # Define translation domain (name of translation files) @@ -138,12 +150,10 @@ # Define locale domain loc.AddCatalog(domain) - import locale global default_locale default_locale = locale.getdefaultlocale()[1] - # sys.stdout.encoding = default_locale # if Beremiz_service is started from Beremiz IDE # sys.stdout.encoding is None (that means 'ascii' encoding'). @@ -156,13 +166,14 @@ __builtin__.__dict__['_'] = unicode_translation # __builtin__.__dict__['_'] = wx.GetTranslation + if enablewx: try: import wxversion wxversion.select(['2.8', '3.0']) import wx havewx = True - except: + except ImportError: print "Wx unavailable !" havewx = False @@ -186,15 +197,14 @@ class ParamsEntryDialog(wx.TextEntryDialog): if wx.VERSION < (2, 6, 0): - def Bind(self, event, function, id = None): + def Bind(self, event, function, id=None): if id is not None: event(self, id, function) else: event(self, function) - - def __init__(self, parent, message, caption = _("Please enter text"), defaultValue = "", - style = wx.OK|wx.CANCEL|wx.CENTRE, pos = wx.DefaultPosition): + def __init__(self, parent, message, caption=_("Please enter text"), defaultValue="", + style=wx.OK | wx.CANCEL | wx.CENTRE, pos=wx.DefaultPosition): wx.TextEntryDialog.__init__(self, parent, message, caption, defaultValue, style, pos) self.Tests = [] @@ -207,10 +217,10 @@ def OnOK(self, event): value = self.GetValue() - texts = {"value" : value} + texts = {"value": value} for function, message in self.Tests: if not function(value): - message = wx.MessageDialog(self, message%texts, _("Error"), wx.OK|wx.ICON_ERROR) + message = wx.MessageDialog(self, message % texts, _("Error"), wx.OK | wx.ICON_ERROR) message.ShowModal() message.Destroy() return @@ -262,7 +272,7 @@ menu = wx.Menu() menu.Append(self.TBMENU_START, _("Start PLC")) menu.Append(self.TBMENU_STOP, _("Stop PLC")) - if self.level==1: + if self.level == 1: menu.AppendSeparator() menu.Append(self.TBMENU_CHANGE_NAME, _("Change Name")) menu.Append(self.TBMENU_CHANGE_INTERFACE, _("Change IP of interface to bind")) @@ -285,13 +295,13 @@ elif "wxGTK" in wx.PlatformInfo: img = img.Scale(22, 22) # wxMac can be any size upto 128x128, so leave the source img alone.... - icon = wx.IconFromBitmap(img.ConvertToBitmap() ) + icon = wx.IconFromBitmap(img.ConvertToBitmap()) return icon def OnTaskBarStartPLC(self, evt): if self.pyroserver.plcobj is not None: plcstatus = self.pyroserver.plcobj.GetPLCstatus()[0] - if plcstatus is "Stopped": + if plcstatus is "Stopped": self.pyroserver.plcobj.StartPLC() else: print _("PLC is empty or already started.") @@ -306,17 +316,17 @@ def OnTaskBarChangeInterface(self, evt): ip_addr = self.pyroserver.ip_addr ip_addr = '' if ip_addr is None else ip_addr - dlg = ParamsEntryDialog(None, _("Enter the IP of the interface to bind"), defaultValue=ip_addr) + dlg = ParamsEntryDialog(None, _("Enter the IP of the interface to bind"), defaultValue=ip_addr) dlg.SetTests([(re.compile('\d{1,3}(?:\.\d{1,3}){3}$').match, _("IP is not valid!")), - ( lambda x :len([x for x in x.split(".") if 0 <= int(x) <= 255]) == 4, _("IP is not valid!")) - ]) + (lambda x:len([x for x in x.split(".") if 0 <= int(x) <= 255]) == 4, + _("IP is not valid!"))]) if dlg.ShowModal() == wx.ID_OK: self.pyroserver.ip_addr = dlg.GetValue() self.pyroserver.Stop() def OnTaskBarChangePort(self, evt): dlg = ParamsEntryDialog(None, _("Enter a port number "), defaultValue=str(self.pyroserver.port)) - dlg.SetTests([(UnicodeType.isdigit, _("Port number must be an integer!")), (lambda port : 0 <= int(port) <= 65535 , _("Port number must be 0 <= port <= 65535!"))]) + dlg.SetTests([(UnicodeType.isdigit, _("Port number must be an integer!")), (lambda port: 0 <= int(port) <= 65535, _("Port number must be 0 <= port <= 65535!"))]) if dlg.ShowModal() == wx.ID_OK: self.pyroserver.port = int(dlg.GetValue()) self.pyroserver.Stop() @@ -331,14 +341,14 @@ servicename = self.pyroserver.servicename servicename = '' if servicename is None else servicename dlg = ParamsEntryDialog(None, _("Enter a name "), defaultValue=servicename) - dlg.SetTests([(lambda name : len(name) is not 0 , _("Name must not be null!"))]) + dlg.SetTests([(lambda name: len(name) is not 0, _("Name must not be null!"))]) if dlg.ShowModal() == wx.ID_OK: self.pyroserver.servicename = dlg.GetValue() self.pyroserver.Restart() def _LiveShellLocals(self): if self.pyroserver.plcobj is not None: - return {"locals":self.pyroserver.plcobj.python_runtime_vars} + return {"locals": self.pyroserver.plcobj.python_runtime_vars} else: return {} @@ -363,7 +373,7 @@ wx.CallAfter(wx.GetApp().ExitMainLoop) def UpdateIcon(self, plcstatus): - if plcstatus is "Started" : + if plcstatus is "Started": currenticon = self.MakeIcon(starticon) elif plcstatus is "Stopped": currenticon = self.MakeIcon(stopicon) @@ -371,19 +381,19 @@ currenticon = self.MakeIcon(defaulticon) self.SetIcon(currenticon, "Beremiz Service") -from runtime import PLCObject, PLCprint, ServicePublisher -import Pyro.core as pyro if not os.path.isdir(WorkingDir): os.mkdir(WorkingDir) + def default_evaluator(tocall, *args, **kwargs): try: - res=(tocall(*args,**kwargs), None) + res = (tocall(*args, **kwargs), None) except Exception: - res=(None, sys.exc_info()) + res = (None, sys.exc_info()) return res + class Server(): def __init__(self, servicename, ip_addr, port, workdir, argv, autostart=False, @@ -419,11 +429,11 @@ def Start(self): pyro.initServer() - self.daemon=pyro.Daemon(host=self.ip_addr, port=self.port) + self.daemon = pyro.Daemon(host=self.ip_addr, port=self.port) self.plcobj = PLCObject(self.workdir, self.daemon, self.argv, self.statuschange, self.evaluator, self.pyruntimevars) - uri = self.daemon.connect(self.plcobj,"PLCObject") + uri = self.daemon.connect(self.plcobj, "PLCObject") print _("Pyro port :"), self.port print _("Pyro object's uri :"), uri @@ -435,17 +445,17 @@ # Configure and publish service # Not publish service if localhost in address params - if (self.servicename is not None and - self.ip_addr is not None and - self.ip_addr != "localhost" and - self.ip_addr != "127.0.0.1"): + if self.servicename is not None and \ + self.ip_addr is not None and \ + self.ip_addr != "localhost" and \ + self.ip_addr != "127.0.0.1": print _("Publishing service on local network") self.servicepublisher = ServicePublisher.ServicePublisher() self.servicepublisher.RegisterService(self.servicename, self.ip_addr, self.port) self.plcobj.AutoLoad() if self.plcobj.GetPLCstatus()[0] != "Empty": - if self.autostart : + if self.autostart: self.plcobj.StartPLC() self.plcobj.StatusChange() @@ -462,6 +472,7 @@ self.servicepublisher = None self.daemon.shutdown(True) + if enabletwisted: import warnings with warnings.catch_warnings(): @@ -474,7 +485,7 @@ from twisted.internet import reactor havetwisted = True - except: + except ImportError: print _("Twisted unavailable.") havetwisted = False @@ -492,12 +503,12 @@ main_thread = currentThread() def statuschangeTskBar(status): - wx.CallAfter(taskbar_instance.UpdateIcon,status) + wx.CallAfter(taskbar_instance.UpdateIcon, status) statuschange.append(statuschangeTskBar) def wx_evaluator(obj, *args, **kwargs): - tocall,args,kwargs = obj.call + tocall, args, kwargs = obj.call obj.res = default_evaluator(tocall, *args, **kwargs) wx_eval_lock.release() @@ -507,8 +518,8 @@ # avoid dead lock if called from the wx mainloop return default_evaluator(tocall, *args, **kwargs) else: - o=type('',(object,),dict(call=(tocall, args, kwargs), res=None)) - wx.CallAfter(wx_evaluator,o) + o = type('', (object,), dict(call=(tocall, args, kwargs), res=None)) + wx.CallAfter(wx_evaluator, o) wx_eval_lock.acquire() return o.res @@ -524,32 +535,40 @@ # Exception hooks s -import threading, traceback + + def LogException(*exp): if pyroserver.plcobj is not None: - pyroserver.plcobj.LogMessage(0,'\n'.join(traceback.format_exception(*exp))) + pyroserver.plcobj.LogMessage(0, '\n'.join(traceback.format_exception(*exp))) else: traceback.print_exception(*exp) + sys.excepthook = LogException + + def installThreadExcepthook(): init_old = threading.Thread.__init__ + def init(self, *args, **kwargs): init_old(self, *args, **kwargs) run_old = self.run + def run_with_except_hook(*args, **kw): try: run_old(*args, **kw) except (KeyboardInterrupt, SystemExit): raise - except: + except Exception: sys.excepthook(*sys.exc_info()) self.run = run_with_except_hook threading.Thread.__init__ = init + + installThreadExcepthook() if havetwisted: - if webport is not None : + if webport is not None: try: import runtime.NevowServer as NS except Exception, e: @@ -557,7 +576,7 @@ webport = None NS.WorkingDir = WorkingDir - if wampconf is not None : + if wampconf is not None: try: import runtime.WampClient as WC except Exception, e: @@ -571,7 +590,7 @@ execfile(extfilename, locals()) if havetwisted: - if webport is not None : + if webport is not None: try: website = NS.RegisterWebsite(webport) pyruntimevars["website"] = website @@ -579,7 +598,7 @@ except Exception, e: print _("Nevow Web service failed. "), e - if wampconf is not None : + if wampconf is not None: try: WC.RegisterWampClient(wampconf) pyruntimevars["wampsession"] = WC.GetSession @@ -589,7 +608,7 @@ if havetwisted or havewx: - pyro_thread=Thread(target=pyroserver.Loop) + pyro_thread = Thread(target=pyroserver.Loop) pyro_thread.start() if havetwisted: @@ -597,9 +616,9 @@ elif havewx: app.MainLoop() else: - try : + try: pyroserver.Loop() - except KeyboardInterrupt,e: + except KeyboardInterrupt, e: pass pyroserver.Quit() sys.exit(0) diff -r 31e63e25b4cc -r 64beb9e9c749 CodeFileTreeNode.py --- a/CodeFileTreeNode.py Mon Aug 21 20:17:19 2017 +0000 +++ b/CodeFileTreeNode.py Mon Aug 21 23:22:58 2017 +0300 @@ -23,7 +23,9 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import os, re, traceback +import os +import re +import traceback from copy import deepcopy from lxml import etree @@ -80,11 +82,12 @@ SECTION_TAG_ELEMENT = "" + class CodeFile: - + CODEFILE_NAME = "CodeFile" SECTIONS_NAMES = [] - + def __init__(self): sections_str = {"codefile_name": self.CODEFILE_NAME} if "includes" in self.SECTIONS_NAMES: @@ -94,34 +97,34 @@ sections_str["sections"] = "\n".join( [SECTION_TAG_ELEMENT % name for name in self.SECTIONS_NAMES if name != "includes"]) - + self.CodeFileParser = GenerateParserFromXSDstring( CODEFILE_XSD % sections_str) self.CodeFileVariables = etree.XPath("variables/variable") - + filepath = self.CodeFileName() - + if os.path.isfile(filepath): xmlfile = open(filepath, 'r') codefile_xml = xmlfile.read() xmlfile.close() - + codefile_xml = codefile_xml.replace( - '<%s>' % self.CODEFILE_NAME, + '<%s>' % self.CODEFILE_NAME, '<%s xmlns:xhtml="http://www.w3.org/1999/xhtml">' % self.CODEFILE_NAME) for cre, repl in [ - (re.compile("(?)(?:)(?!)"), "]]>")]: + (re.compile("(?)(?:)(?!)"), "]]>")]: codefile_xml = cre.sub(repl, codefile_xml) - + try: self.CodeFile, error = self.CodeFileParser.LoadXMLString(codefile_xml) if error is not None: (fname, lnum, src) = ((self.CODEFILE_NAME,) + error) - self.GetCTRoot().logger.write_warning(XSDSchemaErrorMessage.format(a1 = fname, a2 = lnum, a3 = src)) + self.GetCTRoot().logger.write_warning(XSDSchemaErrorMessage.format(a1=fname, a2=lnum, a3=src)) self.CreateCodeFileBuffer(True) except Exception, exc: - msg = _("Couldn't load confnode parameters {a1} :\n {a2}").format(a1 = CTNName, a2 = unicode(exc)) + msg = _("Couldn't load confnode parameters {a1} :\n {a2}").format(a1=CTNName, a2=unicode(exc)) self.GetCTRoot().logger.write_error(msg) self.GetCTRoot().logger.write_error(traceback.format_exc()) else: @@ -132,13 +135,13 @@ def GetBaseTypes(self): return self.GetCTRoot().GetBaseTypes() - def GetDataTypes(self, basetypes = False): + def GetDataTypes(self, basetypes=False): return self.GetCTRoot().GetDataTypes(basetypes=basetypes) def GenerateNewName(self, format, start_idx): return self.GetCTRoot().GenerateNewName( None, None, format, start_idx, - dict([(var.getname().upper(), True) + dict([(var.getname().upper(), True) for var in self.CodeFile.variables.getvariable()])) def SetVariables(self, variables): @@ -152,17 +155,18 @@ variable.setonchange(var["OnChange"]) variable.setopts(var["Options"]) self.CodeFile.variables.appendvariable(variable) - + def GetVariables(self): datas = [] for var in self.CodeFileVariables(self.CodeFile): - datas.append({"Name" : var.getname(), - "Type" : var.gettype(), - "Initial" : var.getinitial(), - "Description" : var.getdesc(), - "OnChange" : var.getonchange(), - "Options" : var.getopts(), - }) + datas.append({ + "Name": var.getname(), + "Type": var.gettype(), + "Initial": var.getinitial(), + "Description": var.getdesc(), + "OnChange": var.getonchange(), + "Options": var.getopts(), + }) return datas def SetTextParts(self, parts): @@ -170,42 +174,42 @@ section_code = parts.get(section) if section_code is not None: getattr(self.CodeFile, section).setanyText(section_code) - + def GetTextParts(self): return dict([(section, getattr(self.CodeFile, section).getanyText()) for section in self.SECTIONS_NAMES]) - + def CTNTestModified(self): - return self.ChangesToSave or not self.CodeFileIsSaved() + return self.ChangesToSave or not self.CodeFileIsSaved() def OnCTNSave(self, from_project_path=None): filepath = self.CodeFileName() - - xmlfile = open(filepath,"w") + + xmlfile = open(filepath, "w") xmlfile.write(etree.tostring( - self.CodeFile, - pretty_print=True, - xml_declaration=True, + self.CodeFile, + pretty_print=True, + xml_declaration=True, encoding='utf-8')) xmlfile.close() - + self.MarkCodeFileAsSaved() return True def CTNGlobalInstances(self): variables = self.CodeFileVariables(self.CodeFile) - ret = [(variable.getname(), - variable.gettype(), - variable.getinitial()) - for variable in variables] + ret = [(variable.getname(), + variable.gettype(), + variable.getinitial()) + for variable in variables] ret.extend([("On"+variable.getname()+"Change", "python_poll", "") - for variable in variables - if variable.getonchange()]) + for variable in variables + if variable.getonchange()]) return ret -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Current Buffering Management Functions -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- """ Return a copy of the codefile model @@ -219,31 +223,30 @@ def BufferCodeFile(self): self.CodeFileBuffer.Buffering(self.CodeFileParser.Dumps(self.CodeFile)) - + def StartBuffering(self): self.Buffering = True - + def EndBuffering(self): if self.Buffering: self.CodeFileBuffer.Buffering(self.CodeFileParser.Dumps(self.CodeFile)) self.Buffering = False - + def MarkCodeFileAsSaved(self): self.EndBuffering() self.CodeFileBuffer.CurrentSaved() - + def CodeFileIsSaved(self): return self.CodeFileBuffer.IsCurrentSaved() and not self.Buffering - + def LoadPrevious(self): self.EndBuffering() self.CodeFile = self.CodeFileParser.Loads(self.CodeFileBuffer.Previous()) - + def LoadNext(self): self.CodeFile = self.CodeFileParser.Loads(self.CodeFileBuffer.Next()) - + def GetBufferState(self): first = self.CodeFileBuffer.IsFirst() and not self.Buffering last = self.CodeFileBuffer.IsLast() return not first, not last - diff -r 31e63e25b4cc -r 64beb9e9c749 ConfigTreeNode.py --- a/ConfigTreeNode.py Mon Aug 21 20:17:19 2017 +0000 +++ b/ConfigTreeNode.py Mon Aug 21 23:22:58 2017 +0300 @@ -31,7 +31,9 @@ - ... TODO : document """ -import os,traceback,types +import os +import traceback +import types import shutil from lxml import etree @@ -55,6 +57,7 @@ NameTypeSeparator = '@' XSDSchemaErrorMessage = _("{a1} XML file doesn't follow XSD schema at line {a2}:\n{a3}") + class ConfigTreeNode: """ This class is the one that define confnodes. @@ -67,7 +70,7 @@ LibraryControler = None EditorType = ConfTreeNodeEditor IconPath = None - + def _AddParamsMembers(self): self.CTNParams = None if self.XSD: @@ -78,7 +81,7 @@ setattr(self, name, obj) def __init__(self): - # Create BaseParam + # Create BaseParam self.BaseParams = _BaseParamsParser.CreateRoot() self.MandatoryParams = ("BaseParams", self.BaseParams) self._AddParamsMembers() @@ -86,39 +89,39 @@ self._View = None # copy ConfNodeMethods so that it can be later customized self.ConfNodeMethods = [dic.copy() for dic in self.ConfNodeMethods] - + def ConfNodeBaseXmlFilePath(self, CTNName=None): return os.path.join(self.CTNPath(CTNName), "baseconfnode.xml") - + def ConfNodeXmlFilePath(self, CTNName=None): return os.path.join(self.CTNPath(CTNName), "confnode.xml") def ConfNodePath(self): return os.path.join(self.CTNParent.ConfNodePath(), self.CTNType) - def CTNPath(self,CTNName=None,project_path=None): + def CTNPath(self, CTNName=None, project_path=None): if not CTNName: CTNName = self.CTNName() if not project_path: project_path = self.CTNParent.CTNPath() return os.path.join(project_path, CTNName + NameTypeSeparator + self.CTNType) - + def CTNName(self): return self.BaseParams.getName() - + def CTNEnabled(self): return self.BaseParams.getEnabled() - + def CTNFullName(self): parent = self.CTNParent.CTNFullName() if parent != "": return parent + "." + self.CTNName() return self.BaseParams.getName() - + def GetIconName(self): return None - + def CTNTestModified(self): return self.ChangesToSave @@ -134,15 +137,15 @@ return True return False - + def RemoteExec(self, script, **kwargs): return self.CTNParent.RemoteExec(script, **kwargs) - + def OnCTNSave(self, from_project_path=None): - #Default, do nothing and return success + """Default, do nothing and return success""" return True - def GetParamsAttributes(self, path = None): + def GetParamsAttributes(self, path=None): if path: parts = path.split(".", 1) if self.MandatoryParams and parts[0] == self.MandatoryParams[0]: @@ -154,7 +157,7 @@ if self.CTNParams: params.append(self.CTNParams[1].getElementInfos(self.CTNParams[0])) return params - + def SetParamsAttribute(self, path, value): self.ChangesToSave = True # Filter IEC_Channel and Name, that have specific behavior @@ -169,7 +172,7 @@ res = self.FindNewName(value) self.CTNRequestSave() return res, True - + parts = path.split(".", 1) if self.MandatoryParams and parts[0] == self.MandatoryParams[0]: self.MandatoryParams[1].setElementValue(parts[1], value) @@ -189,32 +192,32 @@ if not os.path.isdir(ctnpath): # Create it os.mkdir(ctnpath) - + # generate XML for base XML parameters controller of the confnode if self.MandatoryParams: - BaseXMLFile = open(self.ConfNodeBaseXmlFilePath(),'w') + BaseXMLFile = open(self.ConfNodeBaseXmlFilePath(), 'w') BaseXMLFile.write(etree.tostring( - self.MandatoryParams[1], - pretty_print=True, - xml_declaration=True, + self.MandatoryParams[1], + pretty_print=True, + xml_declaration=True, encoding='utf-8')) BaseXMLFile.close() - + # generate XML for XML parameters controller of the confnode if self.CTNParams: - XMLFile = open(self.ConfNodeXmlFilePath(),'w') + XMLFile = open(self.ConfNodeXmlFilePath(), 'w') XMLFile.write(etree.tostring( - self.CTNParams[1], - pretty_print=True, - xml_declaration=True, + self.CTNParams[1], + pretty_print=True, + xml_declaration=True, encoding='utf-8')) XMLFile.close() - + # Call the confnode specific OnCTNSave method result = self.OnCTNSave(from_project_path) if not result: - return _("Error while saving \"%s\"\n")%self.CTNPath() - + return _("Error while saving \"%s\"\n") % self.CTNPath() + # mark confnode as saved self.ChangesToSave = False # go through all children and do the same @@ -226,7 +229,7 @@ if result: return result return None - + def CTNImport(self, src_CTNPath): shutil.copytree(src_CTNPath, self.CTNPath) return True @@ -236,13 +239,13 @@ @return: [(instance_name, instance_type),...] """ return [] - + def _GlobalInstances(self): instances = self.CTNGlobalInstances() for CTNChild in self.IECSortedChildren(): instances.extend(CTNChild._GlobalInstances()) return instances - + def CTNGenerate_C(self, buildpath, locations): """ Generate C code @@ -255,9 +258,9 @@ }, ...] @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND """ - self.GetCTRoot().logger.write_warning(".".join(map(lambda x:str(x), self.GetCurrentLocation())) + " -> Nothing to do\n") - return [],"",False - + self.GetCTRoot().logger.write_warning(".".join(map(lambda x: str(x), self.GetCurrentLocation())) + " -> Nothing to do\n") + return [], "", False + def _Generate_C(self, buildpath, locations): # Generate confnodes [(Cfiles, CFLAGS)], LDFLAGS, DoCalls, extra_files # extra_files = [(fname,fobject), ...] @@ -273,46 +276,46 @@ # confnode asks for some LDFLAGS if CTNLDFLAGS: # LDFLAGS can be either string - if type(CTNLDFLAGS)==type(str()): - LDFLAGS=[CTNLDFLAGS] - #or list of strings - elif type(CTNLDFLAGS)==type(list()): - LDFLAGS=CTNLDFLAGS[:] + if isinstance(CTNLDFLAGS, str): + LDFLAGS = [CTNLDFLAGS] + # or list of strings + elif isinstance(CTNLDFLAGS, list): + LDFLAGS = CTNLDFLAGS[:] else: - LDFLAGS=[] - + LDFLAGS = [] + # recurse through all children, and stack their results for CTNChild in self.IECSortedChildren(): new_location = CTNChild.GetCurrentLocation() # How deep are we in the tree ? - depth=len(new_location) + depth = len(new_location) _LocationCFilesAndCFLAGS, _LDFLAGS, _extra_files = \ CTNChild._Generate_C( - #keep the same path + # keep the same path buildpath, # filter locations that start with current IEC location - [loc for loc in locations if loc["LOC"][0:depth] == new_location ]) + [loc for loc in locations if loc["LOC"][0:depth] == new_location]) # stack the result LocationCFilesAndCFLAGS += _LocationCFilesAndCFLAGS LDFLAGS += _LDFLAGS extra_files += _extra_files - + return LocationCFilesAndCFLAGS, LDFLAGS, extra_files def IterChildren(self): for CTNType, Children in self.Children.items(): for CTNInstance in Children: yield CTNInstance - + def IECSortedChildren(self): # reorder children by IEC_channels - ordered = [(chld.BaseParams.getIEC_Channel(),chld) for chld in self.IterChildren()] + ordered = [(chld.BaseParams.getIEC_Channel(), chld) for chld in self.IterChildren()] if ordered: ordered.sort() return zip(*ordered)[1] else: return [] - + def _GetChildBySomething(self, something, toks): for CTNInstance in self.IterChildren(): # if match component of the name @@ -320,7 +323,7 @@ # if Name have other components if len(toks) >= 2: # Recurse in order to find the latest object - return CTNInstance._GetChildBySomething( something, toks[1:]) + return CTNInstance._GetChildBySomething(something, toks[1:]) # No sub name -> found return CTNInstance # Not found @@ -338,7 +341,7 @@ return self._GetChildBySomething("IEC_Channel", Location) else: return self - + def GetCurrentLocation(self): """ @return: Tupple containing confnode IEC location of current confnode : %I0.0.4.5 => (0,0,4,5) @@ -349,13 +352,13 @@ """ @return: String "ParentParentName.ParentName.Name" """ - return self.CTNParent._GetCurrentName() + self.BaseParams.getName() + return self.CTNParent._GetCurrentName() + self.BaseParams.getName() def _GetCurrentName(self): """ @return: String "ParentParentName.ParentName.Name." """ - return self.CTNParent._GetCurrentName() + self.BaseParams.getName() + "." + return self.CTNParent._GetCurrentName() + self.BaseParams.getName() + "." def GetCTRoot(self): return self.CTNParent.GetCTRoot() @@ -372,7 +375,7 @@ 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" ''' @@ -392,9 +395,9 @@ # Get Current Name CurrentName = self.BaseParams.getName() # Do nothing if no change - #if CurrentName == DesiredName: return CurrentName + # if CurrentName == DesiredName: return CurrentName # Build a list of used Name out of parent's Children - AllNames=[] + AllNames = [] for CTNInstance in self.CTNParent.IterChildren(): if CTNInstance != self: AllNames.append(CTNInstance.BaseParams.getName()) @@ -407,9 +410,9 @@ BaseDesiredName = DesiredName suffix = 1 while res in AllNames: - res = "%s_%d"%(BaseDesiredName, suffix) + res = "%s_%d" % (BaseDesiredName, suffix) suffix += 1 - + # Get old path oldname = self.CTNPath() # Check previous confnode existance @@ -421,12 +424,12 @@ shutil.move(oldname, self.CTNPath()) # warn user he has two left hands if DesiredName != res: - msg = _("A child named \"{a1}\" already exists -> \"{a2}\"\n").format(a1 = DesiredName, a2 = res) + msg = _("A child named \"{a1}\" already exists -> \"{a2}\"\n").format(a1=DesiredName, a2=res) self.GetCTRoot().logger.write_warning(msg) return res def GetAllChannels(self): - AllChannels=[] + AllChannels = [] for CTNInstance in self.CTNParent.IterChildren(): if CTNInstance != self: AllChannels.append(CTNInstance.BaseParams.getIEC_Channel()) @@ -441,20 +444,20 @@ # Get Current IEC channel CurrentChannel = self.BaseParams.getIEC_Channel() # Do nothing if no change - #if CurrentChannel == DesiredChannel: return CurrentChannel + # if CurrentChannel == DesiredChannel: return CurrentChannel # Build a list of used Channels out of parent's Children AllChannels = self.GetAllChannels() - + # Now, try to guess the nearest available channel res = DesiredChannel - while res in AllChannels: # While channel not free - if res < CurrentChannel: # Want to go down ? - res -= 1 # Test for n-1 - if res < 0 : - self.GetCTRoot().logger.write_warning(_("Cannot find lower free IEC channel than %d\n")%CurrentChannel) - return CurrentChannel # Can't go bellow 0, do nothing - else : # Want to go up ? - res += 1 # Test for n-1 + while res in AllChannels: # While channel not free + if res < CurrentChannel: # Want to go down ? + res -= 1 # Test for n-1 + if res < 0: + self.GetCTRoot().logger.write_warning(_("Cannot find lower free IEC channel than %d\n") % CurrentChannel) + return CurrentChannel # Can't go bellow 0, do nothing + else: # Want to go up ? + res += 1 # Test for n-1 # Finally set IEC Channel self.BaseParams.setIEC_Channel(res) return res @@ -466,14 +469,14 @@ if self.EditorType is not None: app_frame = self.GetCTRoot().AppFrame if self._View is None and not onlyopened: - + self._View = self.EditorType(app_frame.TabsOpened, self, app_frame) - + if self._View is not None: if name is None: name = self.CTNFullName() app_frame.EditProjectElement(self._View, name) - + return self._View return None @@ -508,7 +511,7 @@ def CTNRemove(self): # Fetch the confnode - #CTNInstance = self.GetChildByName(CTNName) + # CTNInstance = self.GetChildByName(CTNName) # Ask to his parent to remove it self.CTNParent._doRemoveChild(self) @@ -521,29 +524,31 @@ # reorganize self.CTNChildrenTypes tuples from (name, CTNClass, Help) # to ( name, (CTNClass, Help)), an make a dict transpose = zip(*self.CTNChildrenTypes) - CTNChildrenTypes = dict(zip(transpose[0],zip(transpose[1],transpose[2]))) + CTNChildrenTypes = dict(zip(transpose[0], zip(transpose[1], transpose[2]))) # Check that adding this confnode is allowed try: CTNClass, CTNHelp = CTNChildrenTypes[CTNType] except KeyError: - raise Exception, _("Cannot create child {a1} of type {a2} ").format(a1 = CTNName, a2 = CTNType) - + raise Exception(_("Cannot create child {a1} of type {a2} "). + format(a1=CTNName, a2=CTNType)) + # if CTNClass is a class factory, call it. (prevent unneeded imports) - if type(CTNClass) == types.FunctionType: + if isinstance(CTNClass, types.FunctionType): CTNClass = CTNClass() - + # Eventualy Initialize child instance list for this class of confnode ChildrenWithSameClass = self.Children.setdefault(CTNType, list()) # Check count if getattr(CTNClass, "CTNMaxCount", None) and len(ChildrenWithSameClass) >= CTNClass.CTNMaxCount: - msg = _("Max count ({a1}) reached for this confnode of type {a2} ").format(a1 = CTNClass.CTNMaxCount, a2 = CTNType) - raise Exception, msg - + raise Exception( + _("Max count ({a1}) reached for this confnode of type {a2} "). + format(a1=CTNClass.CTNMaxCount, a2=CTNType)) + # create the final class, derived of provided confnode and template class FinalCTNClass(CTNClass, ConfigTreeNode): """ ConfNode class is derivated into FinalCTNClass before being instanciated - This way __init__ is overloaded to ensure ConfigTreeNode.__init__ is called + This way __init__ is overloaded to ensure ConfigTreeNode.__init__ is called before CTNClass.__init__, and to do the file related stuff. """ def __init__(_self): @@ -558,25 +563,25 @@ # check name is unique NewCTNName = _self.FindNewName(CTNName) # If dir have already be made, and file exist - if os.path.isdir(_self.CTNPath(NewCTNName)): #and os.path.isfile(_self.ConfNodeXmlFilePath(CTNName)): - #Load the confnode.xml file into parameters members + if os.path.isdir(_self.CTNPath(NewCTNName)): # and os.path.isfile(_self.ConfNodeXmlFilePath(CTNName)): + # Load the confnode.xml file into parameters members _self.LoadXMLParams(NewCTNName) # Basic check. Better to fail immediately. if (_self.BaseParams.getName() != NewCTNName): - msg = _("Project tree layout do not match confnode.xml {a1}!={a2} ").\ - format(a1 = NewCTNName, a2 = _self.BaseParams.getName()) - raise Exception, msg + raise Exception( + _("Project tree layout do not match confnode.xml {a1}!={a2} "). + format(a1=NewCTNName, a2=_self.BaseParams.getName())) # Now, self.CTNPath() should be OK - + # Check that IEC_Channel is not already in use. _self.FindNewIEC_Channel(_self.BaseParams.getIEC_Channel()) # Call the confnode real __init__ if getattr(CTNClass, "__init__", None): CTNClass.__init__(_self) - #Load and init all the children + # Load and init all the children _self.LoadChildren() - #just loaded, nothing to saved + # just loaded, nothing to saved _self.ChangesToSave = False else: # If confnode do not have corresponding file/dirs - they will be created on Save @@ -587,31 +592,31 @@ if getattr(CTNClass, "__init__", None): CTNClass.__init__(_self) _self.CTNRequestSave() - #just created, must be saved + # just created, must be saved _self.ChangesToSave = True - + def _getBuildPath(_self): return self._getBuildPath() - + # Create the object out of the resulting class newConfNodeOpj = FinalCTNClass() # Store it in CTNgedChils ChildrenWithSameClass.append(newConfNodeOpj) - + return newConfNodeOpj - + def ClearChildren(self): for child in self.IterChildren(): child.ClearChildren() self.Children = {} - - def LoadXMLParams(self, CTNName = None): + + def LoadXMLParams(self, CTNName=None): methode_name = os.path.join(self.CTNPath(CTNName), "methods.py") if os.path.isfile(methode_name): execfile(methode_name) - + ConfNodeName = CTNName if CTNName is not None else self.CTNName() - + # Get the base xml tree if self.MandatoryParams: try: @@ -619,14 +624,14 @@ self.BaseParams, error = _BaseParamsParser.LoadXMLString(basexmlfile.read()) if error is not None: (fname, lnum, src) = ((ConfNodeName + " BaseParams",) + error) - self.GetCTRoot().logger.write_warning(XSDSchemaErrorMessage.format(a1 = fname, a2 = lnum, a3 = src)) + self.GetCTRoot().logger.write_warning(XSDSchemaErrorMessage.format(a1=fname, a2=lnum, a3=src)) self.MandatoryParams = ("BaseParams", self.BaseParams) basexmlfile.close() except Exception, exc: - msg = _("Couldn't load confnode base parameters {a1} :\n {a2}").format(a1 = ConfNodeName, a2 = unicode(exc)) + msg = _("Couldn't load confnode base parameters {a1} :\n {a2}").format(a1=ConfNodeName, a2=unicode(exc)) self.GetCTRoot().logger.write_error(msg) self.GetCTRoot().logger.write_error(traceback.format_exc()) - + # Get the xml tree if self.CTNParams: try: @@ -634,16 +639,16 @@ obj, error = self.Parser.LoadXMLString(xmlfile.read()) if error is not None: (fname, lnum, src) = ((ConfNodeName,) + error) - self.GetCTRoot().logger.write_warning(XSDSchemaErrorMessage.format(a1 = fname, a2 = lnum, a3 = src)) + self.GetCTRoot().logger.write_warning(XSDSchemaErrorMessage.format(a1=fname, a2=lnum, a3=src)) name = obj.getLocalTag() setattr(self, name, obj) self.CTNParams = (name, obj) xmlfile.close() except Exception, exc: - msg = _("Couldn't load confnode parameters {a1} :\n {a2}").format(a1 = ConfNodeName, a2 = unicode(exc)) + msg = _("Couldn't load confnode parameters {a1} :\n {a2}").format(a1=ConfNodeName, a2=unicode(exc)) self.GetCTRoot().logger.write_error(msg) self.GetCTRoot().logger.write_error(traceback.format_exc()) - + def LoadChildren(self): # Iterate over all CTNName@CTNType in confnode directory, and try to open them for CTNDir in os.listdir(self.CTNPath()): @@ -653,7 +658,6 @@ try: self.CTNAddChild(pname, ptype) except Exception, exc: - msg = _("Could not add child \"{a1}\", type {a2} :\n{a3}\n").format(a1 = pname, a2 = ptype, a3 = unicode(exc)) + msg = _("Could not add child \"{a1}\", type {a2} :\n{a3}\n").format(a1=pname, a2=ptype, a3=unicode(exc)) self.GetCTRoot().logger.write_error(msg) self.GetCTRoot().logger.write_error(traceback.format_exc()) - diff -r 31e63e25b4cc -r 64beb9e9c749 IDEFrame.py --- a/IDEFrame.py Mon Aug 21 20:17:19 2017 +0000 +++ b/IDEFrame.py Mon Aug 21 23:22:58 2017 +0300 @@ -22,11 +22,14 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import os, sys +import os +import sys import cPickle from types import TupleType - -import wx, wx.grid +import base64 + +import wx +import wx.grid import wx.aui from editors.EditorPanel import EditorPanel @@ -43,48 +46,51 @@ 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_PLCOPENEDITOREDITMENUADDPROGRAM, ID_PLCOPENEDITOREDITMENUADDCONFIGURATION, - ID_PLCOPENEDITOREDITMENUFINDNEXT, ID_PLCOPENEDITOREDITMENUFINDPREVIOUS, - ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, ID_PLCOPENEDITOREDITMENUADDRESOURCE +[ + 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_PLCOPENEDITORDISPLAYMENUSWITCHPERSPECTIVE, +[ + ID_PLCOPENEDITORDISPLAYMENURESETPERSPECTIVE, + ID_PLCOPENEDITORDISPLAYMENUSWITCHPERSPECTIVE, ] = [wx.NewId() for _init_coll_DisplayMenu_Items in range(2)] -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # EditorToolBar definitions -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Define PLCOpenEditor Toolbar items id -[ID_PLCOPENEDITOREDITORTOOLBARSELECTION, ID_PLCOPENEDITOREDITORTOOLBARCOMMENT, - ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, ID_PLCOPENEDITOREDITORTOOLBARBLOCK, - ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, ID_PLCOPENEDITOREDITORTOOLBARWIRE, - ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL, ID_PLCOPENEDITOREDITORTOOLBARRUNG, - ID_PLCOPENEDITOREDITORTOOLBARCOIL, ID_PLCOPENEDITOREDITORTOOLBARCONTACT, - ID_PLCOPENEDITOREDITORTOOLBARBRANCH, ID_PLCOPENEDITOREDITORTOOLBARINITIALSTEP, - ID_PLCOPENEDITOREDITORTOOLBARSTEP, ID_PLCOPENEDITOREDITORTOOLBARTRANSITION, - ID_PLCOPENEDITOREDITORTOOLBARACTIONBLOCK, ID_PLCOPENEDITOREDITORTOOLBARDIVERGENCE, - ID_PLCOPENEDITOREDITORTOOLBARJUMP, ID_PLCOPENEDITOREDITORTOOLBARMOTION, +[ + ID_PLCOPENEDITOREDITORTOOLBARSELECTION, ID_PLCOPENEDITOREDITORTOOLBARCOMMENT, + ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, ID_PLCOPENEDITOREDITORTOOLBARBLOCK, + ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, ID_PLCOPENEDITOREDITORTOOLBARWIRE, + ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL, ID_PLCOPENEDITOREDITORTOOLBARRUNG, + ID_PLCOPENEDITOREDITORTOOLBARCOIL, ID_PLCOPENEDITOREDITORTOOLBARCONTACT, + ID_PLCOPENEDITOREDITORTOOLBARBRANCH, ID_PLCOPENEDITOREDITORTOOLBARINITIALSTEP, + ID_PLCOPENEDITOREDITORTOOLBARSTEP, ID_PLCOPENEDITOREDITORTOOLBARTRANSITION, + ID_PLCOPENEDITOREDITORTOOLBARACTIONBLOCK, ID_PLCOPENEDITOREDITORTOOLBARDIVERGENCE, + 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 # Informations meaning are in this order: # - Item is toggled @@ -94,102 +100,101 @@ # - Item icon filename # - Item tooltip text EditorToolBarItems = { - "FBD" : [(True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARMOTION, "OnMotionTool", - "move", _("Move the view")), - (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARCOMMENT, "OnCommentTool", - "add_comment", _("Create a new comment")), - (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, "OnVariableTool", - "add_variable", _("Create a new variable")), - (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARBLOCK, "OnBlockTool", - "add_block", _("Create a new block")), - (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", - "add_comment", _("Create a new comment")), - (True, FREEDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL, "OnPowerRailTool", - "add_powerrail", _("Create a new power rail")), - (False, DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARRUNG, "OnRungTool", - "add_rung", _("Create a new rung")), - (True, FREEDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARCOIL, "OnCoilTool", - "add_coil", _("Create a new coil")), - (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARCONTACT, "OnContactTool", - "add_contact", _("Create a new contact")), - (False, DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARBRANCH, "OnBranchTool", - "add_branch", _("Create a new branch")), - (True, FREEDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, "OnVariableTool", - "add_variable", _("Create a new variable")), - (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARBLOCK, "OnBlockTool", - "add_block", _("Create a new block")), - (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", - "add_comment", _("Create a new comment")), - (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARINITIALSTEP, "OnInitialStepTool", - "add_initial_step", _("Create a new initial step")), - (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARSTEP, "OnStepTool", - "add_step", _("Create a new step")), - (True, FREEDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARTRANSITION, "OnTransitionTool", - "add_transition", _("Create a new transition")), - (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARACTIONBLOCK, "OnActionBlockTool", - "add_action", _("Create a new action block")), - (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARDIVERGENCE, "OnDivergenceTool", - "add_divergence", _("Create a new divergence")), - (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARJUMP, "OnJumpTool", - "add_jump", _("Create a new jump")), - (True, FREEDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, "OnVariableTool", - "add_variable", _("Create a new variable")), - (True, FREEDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARBLOCK, "OnBlockTool", - "add_block", _("Create a new block")), - (True, FREEDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, "OnConnectionTool", - "add_connection", _("Create a new connection")), - (True, FREEDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL, "OnPowerRailTool", - "add_powerrail", _("Create a new power rail")), - (True, FREEDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARCONTACT, "OnContactTool", - "add_contact", _("Create a new contact"))], - "ST" : [], - "IL" : [], - "debug": [(True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARMOTION, "OnMotionTool", - "move", _("Move the view"))], + "FBD": [(True, FREEDRAWING_MODE | DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARMOTION, "OnMotionTool", + "move", _("Move the view")), + (True, FREEDRAWING_MODE | DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCOMMENT, "OnCommentTool", + "add_comment", _("Create a new comment")), + (True, FREEDRAWING_MODE | DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, "OnVariableTool", + "add_variable", _("Create a new variable")), + (True, FREEDRAWING_MODE | DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARBLOCK, "OnBlockTool", + "add_block", _("Create a new block")), + (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", + "add_comment", _("Create a new comment")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL, "OnPowerRailTool", + "add_powerrail", _("Create a new power rail")), + (False, DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARRUNG, "OnRungTool", + "add_rung", _("Create a new rung")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCOIL, "OnCoilTool", + "add_coil", _("Create a new coil")), + (False, FREEDRAWING_MODE | DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCONTACT, "OnContactTool", + "add_contact", _("Create a new contact")), + (False, DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARBRANCH, "OnBranchTool", + "add_branch", _("Create a new branch")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, "OnVariableTool", + "add_variable", _("Create a new variable")), + (False, FREEDRAWING_MODE | DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARBLOCK, "OnBlockTool", + "add_block", _("Create a new block")), + (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", + "add_comment", _("Create a new comment")), + (True, FREEDRAWING_MODE | DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARINITIALSTEP, "OnInitialStepTool", + "add_initial_step", _("Create a new initial step")), + (False, FREEDRAWING_MODE | DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARSTEP, "OnStepTool", + "add_step", _("Create a new step")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARTRANSITION, "OnTransitionTool", + "add_transition", _("Create a new transition")), + (False, FREEDRAWING_MODE | DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARACTIONBLOCK, "OnActionBlockTool", + "add_action", _("Create a new action block")), + (False, FREEDRAWING_MODE | DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARDIVERGENCE, "OnDivergenceTool", + "add_divergence", _("Create a new divergence")), + (False, FREEDRAWING_MODE | DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARJUMP, "OnJumpTool", + "add_jump", _("Create a new jump")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, "OnVariableTool", + "add_variable", _("Create a new variable")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARBLOCK, "OnBlockTool", + "add_block", _("Create a new block")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, "OnConnectionTool", + "add_connection", _("Create a new connection")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL, "OnPowerRailTool", + "add_powerrail", _("Create a new power rail")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCONTACT, "OnContactTool", + "add_contact", _("Create a new contact"))], + "ST": [], + "IL": [], + "debug": [(True, FREEDRAWING_MODE | DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARMOTION, "OnMotionTool", + "move", _("Move the view"))], } -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Helper Functions -#------------------------------------------------------------------------------- - -import base64 +# ------------------------------------------------------------------------------- + def EncodeFileSystemPath(path, use_base64=True): path = path.encode(sys.getfilesystemencoding()) @@ -197,22 +202,29 @@ return base64.encodestring(path) return path + def DecodeFileSystemPath(path, is_base64=True): if is_base64: path = base64.decodestring(path) return unicode(path, sys.getfilesystemencoding()) -# Compatibility function for wx versions < 2.6 + def AppendMenu(parent, help, id, kind, text): + """ + Compatibility function for wx versions < 2.6 + """ if wx.VERSION >= (2, 6, 0): parent.Append(help=help, id=id, kind=kind, text=text) else: parent.Append(helpString=help, id=id, kind=kind, item=text) -[TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE, - POUINSTANCEVARIABLESPANEL, LIBRARYTREE, SCALING, PAGETITLES + +[ + TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE, + POUINSTANCEVARIABLESPANEL, LIBRARYTREE, SCALING, PAGETITLES ] = range(10) + def GetShortcutKeyCallbackFunction(viewer_function): def ShortcutKeyFunction(self, event): control = self.FindFocus() @@ -224,6 +236,7 @@ control.ProcessEvent(event) return ShortcutKeyFunction + def GetDeleteElementFunction(remove_function, parent_type=None, check_function=None): def DeleteElementFunction(self, selected): name = self.ProjectTree.GetItemText(selected) @@ -236,6 +249,7 @@ remove_function(self.Controler, name) return DeleteElementFunction + if wx.Platform == '__WXMSW__': TAB_BORDER = 6 NOTEBOOK_BORDER = 6 @@ -243,15 +257,16 @@ TAB_BORDER = 7 NOTEBOOK_BORDER = 2 + def SimplifyTabLayout(tabs, rect): for tab in tabs: if tab["pos"][0] == rect.x: others = [t for t in tabs if t != tab] - others.sort(lambda x,y: cmp(x["pos"][0], y["pos"][0])) + others.sort(lambda x, y: cmp(x["pos"][0], y["pos"][0])) for other in others: - 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): + 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"]) @@ -262,11 +277,11 @@ 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])) + others.sort(lambda x, y: cmp(x["pos"][1], y["pos"][1])) for other in others: - 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): + 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"]) @@ -276,6 +291,7 @@ return True return False + def ComputeTabsLayout(tabs, rect): if len(tabs) == 0: return tabs @@ -284,7 +300,7 @@ split = None for idx, tab in enumerate(tabs): if len(tab["pages"]) == 0: - raise ValueError, "Not possible" + raise ValueError("Not possible") if tab["size"][0] == rect.width: if tab["pos"][1] == rect.y: split = (wx.TOP, float(tab["size"][1]) / float(rect.height)) @@ -305,7 +321,7 @@ split_rect = wx.Rect(rect.x, rect.y, rect.width - tab["size"][0] - TAB_BORDER, rect.height) break - if split != None: + if split is not None: split_tab = tabs.pop(idx) return {"split": split, "tab": split_tab, @@ -315,17 +331,15 @@ return ComputeTabsLayout(tabs, rect) return tabs -#------------------------------------------------------------------------------- -# IDEFrame Base Class -#------------------------------------------------------------------------------- UNEDITABLE_NAMES_DICT = dict([(_(name), name) for name in UNEDITABLE_NAMES]) + class IDEFrame(wx.Frame): - + """IDEFrame Base Class""" # Compatibility function for wx versions < 2.6 if wx.VERSION < (2, 6, 0): - def Bind(self, event, function, id = None): + def Bind(self, event, function, id=None): if id is not None: event(self, id, function) else: @@ -342,79 +356,79 @@ def _init_coll_AddMenu_Items(self, parent, add_config=True): AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDDATATYPE, - kind=wx.ITEM_NORMAL, text=_(u'&Data Type')) + kind=wx.ITEM_NORMAL, text=_(u'&Data Type')) AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDFUNCTION, - kind=wx.ITEM_NORMAL, text=_(u'&Function')) + kind=wx.ITEM_NORMAL, text=_(u'&Function')) AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDFUNCTIONBLOCK, - kind=wx.ITEM_NORMAL, text=_(u'Function &Block')) + kind=wx.ITEM_NORMAL, text=_(u'Function &Block')) AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDPROGRAM, - kind=wx.ITEM_NORMAL, text=_(u'&Program')) + kind=wx.ITEM_NORMAL, text=_(u'&Program')) AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDRESOURCE, - kind=wx.ITEM_NORMAL, text=_(u'&Resource')) + kind=wx.ITEM_NORMAL, text=_(u'&Resource')) if add_config: AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDCONFIGURATION, - kind=wx.ITEM_NORMAL, text=_(u'&Configuration')) + 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') + kind=wx.ITEM_NORMAL, text=_(u'Undo') + '\tCTRL+Z') AppendMenu(parent, help='', id=wx.ID_REDO, - kind=wx.ITEM_NORMAL, text=_(u'Redo') + '\tCTRL+Y') - #AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, + kind=wx.ITEM_NORMAL, text=_(u'Redo') + '\tCTRL+Y') + # AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, # kind=wx.ITEM_CHECK, text=_(u'Enable Undo/Redo')) - enable_undo_redo = _(u'Enable Undo/Redo') # Keeping text in translations for possible menu reactivation + enable_undo_redo = _(u'Enable Undo/Redo') # Keeping text in translations for possible menu reactivation parent.AppendSeparator() AppendMenu(parent, help='', id=wx.ID_CUT, - kind=wx.ITEM_NORMAL, text=_(u'Cut') + '\tCTRL+X') + kind=wx.ITEM_NORMAL, text=_(u'Cut') + '\tCTRL+X') AppendMenu(parent, help='', id=wx.ID_COPY, - kind=wx.ITEM_NORMAL, text=_(u'Copy') + '\tCTRL+C') + kind=wx.ITEM_NORMAL, text=_(u'Copy') + '\tCTRL+C') AppendMenu(parent, help='', id=wx.ID_PASTE, - kind=wx.ITEM_NORMAL, text=_(u'Paste') + '\tCTRL+V') + kind=wx.ITEM_NORMAL, text=_(u'Paste') + '\tCTRL+V') parent.AppendSeparator() AppendMenu(parent, help='', id=wx.ID_FIND, - kind=wx.ITEM_NORMAL, text=_(u'Find') + '\tCTRL+F') + kind=wx.ITEM_NORMAL, text=_(u'Find') + '\tCTRL+F') AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUFINDNEXT, - kind=wx.ITEM_NORMAL, text=_(u'Find Next') + '\tCTRL+K') + kind=wx.ITEM_NORMAL, text=_(u'Find Next') + '\tCTRL+K') AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUFINDPREVIOUS, - kind=wx.ITEM_NORMAL, text=_(u'Find Previous') + '\tCTRL+SHIFT+K') + kind=wx.ITEM_NORMAL, text=_(u'Find Previous') + '\tCTRL+SHIFT+K') parent.AppendSeparator() AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, - kind=wx.ITEM_NORMAL, text=_(u'Search in Project') + '\tCTRL+SHIFT+F') + kind=wx.ITEM_NORMAL, text=_(u'Search in Project') + '\tCTRL+SHIFT+F') parent.AppendSeparator() add_menu = wx.Menu(title='') self._init_coll_AddMenu_Items(add_menu) parent.AppendMenu(wx.ID_ADD, _(u"&Add Element"), add_menu) AppendMenu(parent, help='', id=wx.ID_SELECTALL, - kind=wx.ITEM_NORMAL, text=_(u'Select All') + '\tCTRL+A') + kind=wx.ITEM_NORMAL, text=_(u'Select All') + '\tCTRL+A') AppendMenu(parent, help='', id=wx.ID_DELETE, - kind=wx.ITEM_NORMAL, text=_(u'&Delete')) + kind=wx.ITEM_NORMAL, text=_(u'&Delete')) self.Bind(wx.EVT_MENU, self.OnUndoMenu, id=wx.ID_UNDO) self.Bind(wx.EVT_MENU, self.OnRedoMenu, id=wx.ID_REDO) - #self.Bind(wx.EVT_MENU, self.OnEnableUndoRedoMenu, id=ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO) + # self.Bind(wx.EVT_MENU, self.OnEnableUndoRedoMenu, id=ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO) self.Bind(wx.EVT_MENU, self.OnCutMenu, id=wx.ID_CUT) 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, - id=ID_PLCOPENEDITOREDITMENUFINDNEXT) + id=ID_PLCOPENEDITOREDITMENUFINDNEXT) self.Bind(wx.EVT_MENU, self.OnFindPreviousMenu, - id=ID_PLCOPENEDITOREDITMENUFINDPREVIOUS) + id=ID_PLCOPENEDITOREDITMENUFINDPREVIOUS) self.Bind(wx.EVT_MENU, self.OnSearchInProjectMenu, - id=ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT) + id=ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT) self.Bind(wx.EVT_MENU, self.OnSearchInProjectMenu, - id=ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT) + id=ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT) self.Bind(wx.EVT_MENU, self.OnAddDataTypeMenu, - id=ID_PLCOPENEDITOREDITMENUADDDATATYPE) + id=ID_PLCOPENEDITOREDITMENUADDDATATYPE) self.Bind(wx.EVT_MENU, self.GenerateAddPouFunction("function"), - id=ID_PLCOPENEDITOREDITMENUADDFUNCTION) + id=ID_PLCOPENEDITOREDITMENUADDFUNCTION) self.Bind(wx.EVT_MENU, self.GenerateAddPouFunction("functionBlock"), - id=ID_PLCOPENEDITOREDITMENUADDFUNCTIONBLOCK) + id=ID_PLCOPENEDITOREDITMENUADDFUNCTIONBLOCK) self.Bind(wx.EVT_MENU, self.GenerateAddPouFunction("program"), - id=ID_PLCOPENEDITOREDITMENUADDPROGRAM) + id=ID_PLCOPENEDITOREDITMENUADDPROGRAM) self.Bind(wx.EVT_MENU, self.AddResourceMenu, - id=ID_PLCOPENEDITOREDITMENUADDRESOURCE) + id=ID_PLCOPENEDITOREDITMENUADDRESOURCE) self.Bind(wx.EVT_MENU, self.OnAddConfigurationMenu, - id=ID_PLCOPENEDITOREDITMENUADDCONFIGURATION) + 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) @@ -429,17 +443,17 @@ def _init_coll_DisplayMenu_Items(self, parent): AppendMenu(parent, help='', id=wx.ID_REFRESH, - kind=wx.ITEM_NORMAL, text=_(u'Refresh') + '\tCTRL+R') + kind=wx.ITEM_NORMAL, text=_(u'Refresh') + '\tCTRL+R') if self.EnableDebug: AppendMenu(parent, help='', id=wx.ID_CLEAR, - kind=wx.ITEM_NORMAL, text=_(u'Clear Errors') + '\tCTRL+K') + kind=wx.ITEM_NORMAL, text=_(u'Clear Errors') + '\tCTRL+K') parent.AppendSeparator() zoommenu = wx.Menu(title='') parent.AppendMenu(wx.ID_ZOOM_FIT, _("Zoom"), zoommenu) for idx, value in enumerate(ZOOM_FACTORS): new_id = wx.NewId() AppendMenu(zoommenu, help='', id=new_id, - kind=wx.ITEM_RADIO, text=str(int(round(value * 100))) + "%") + kind=wx.ITEM_RADIO, text=str(int(round(value * 100))) + "%") self.Bind(wx.EVT_MENU, self.GenerateZoomFunction(idx), id=new_id) parent.AppendSeparator() @@ -448,7 +462,7 @@ self.Bind(wx.EVT_MENU, self.SwitchFullScrMode, id=ID_PLCOPENEDITORDISPLAYMENUSWITCHPERSPECTIVE) AppendMenu(parent, help='', id=ID_PLCOPENEDITORDISPLAYMENURESETPERSPECTIVE, - kind=wx.ITEM_NORMAL, text=_(u'Reset Perspective')) + 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) @@ -474,14 +488,14 @@ def _init_icon(self, parent): if self.icon: - self.SetIcon(self.icon) + self.SetIcon(self.icon) elif parent and parent.icon: - self.SetIcon(parent.icon) - + self.SetIcon(parent.icon) + def _init_ctrls(self, prnt): wx.Frame.__init__(self, id=ID_PLCOPENEDITOR, name='IDEFrame', - parent=prnt, pos=wx.DefaultPosition, size=wx.Size(1000, 600), - style=wx.DEFAULT_FRAME_STYLE) + parent=prnt, pos=wx.DefaultPosition, size=wx.Size(1000, 600), + style=wx.DEFAULT_FRAME_STYLE) self._init_icon(prnt) self.SetClientSize(wx.Size(1000, 600)) self.Bind(wx.EVT_ACTIVATE, self.OnActivated) @@ -489,95 +503,113 @@ 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.Panes = {} - 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 = 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.OnAllowNotebookDnD) - self.AUIManager.AddPane(self.LeftNoteBook, - wx.aui.AuiPaneInfo().Name("ProjectPane"). - Left().Layer(1). - 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.OnAllowNotebookDnD) + self.AUIManager.AddPane( + self.LeftNoteBook, + wx.aui.AuiPaneInfo().Name("ProjectPane").Left().Layer(1). + 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.OnAllowNotebookDnD) - self.AUIManager.AddPane(self.BottomNoteBook, - wx.aui.AuiPaneInfo().Name("ResultPane"). - Bottom().Layer(0). - 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.OnAllowNotebookDnD) + self.AUIManager.AddPane( + self.BottomNoteBook, + wx.aui.AuiPaneInfo().Name("ResultPane").Bottom().Layer(0). + 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.OnAllowNotebookDnD) - self.AUIManager.AddPane(self.RightNoteBook, - wx.aui.AuiPaneInfo().Name("LibraryPane"). - Right().Layer(0). - BestSize(wx.Size(250, 400)).CloseButton(False)) - - self.TabsOpened = wx.aui.AuiNotebook(self, ID_PLCOPENEDITORTABSOPENED, - style=wx.aui.AUI_NB_DEFAULT_STYLE|wx.aui.AUI_NB_WINDOWLIST_BUTTON) + self.OnAllowNotebookDnD) + self.AUIManager.AddPane( + self.RightNoteBook, + wx.aui.AuiPaneInfo().Name("LibraryPane").Right().Layer(0). + BestSize(wx.Size(250, 400)).CloseButton(False)) + + 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) + self.OnPouSelectedChanging) self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, - self.OnPouSelectedChanged) + self.OnPouSelectedChanged) self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, - self.OnPageClose) + self.OnPageClose) self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_END_DRAG, - self.OnPageDragged) + self.OnPageDragged) self.AUIManager.AddPane(self.TabsOpened, - wx.aui.AuiPaneInfo().CentrePane().Name("TabsPane")) - - #----------------------------------------------------------------------- + wx.aui.AuiPaneInfo().CentrePane().Name("TabsPane")) + + # ----------------------------------------------------------------------- # Creating PLCopen Project Types Tree - #----------------------------------------------------------------------- + # ----------------------------------------------------------------------- self.MainTabs = {} - 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.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, - pos=wx.Point(0, 0), size=wx.Size(0, 0), - style=wx.SUNKEN_BORDER, - agwStyle=wx.TR_HAS_BUTTONS|wx.TR_SINGLE|wx.TR_EDIT_LABELS) + name='ProjectTree', + parent=self.ProjectPanel, + pos=wx.Point(0, 0), size=wx.Size(0, 0), + style=wx.SUNKEN_BORDER, + agwStyle=(wx.TR_HAS_BUTTONS | + wx.TR_SINGLE | + wx.TR_EDIT_LABELS)) self.ProjectTree.SetBackgroundBitmap(GetBitmap("custom_tree_background"), - wx.ALIGN_RIGHT|wx.ALIGN_BOTTOM) + wx.ALIGN_RIGHT | wx.ALIGN_BOTTOM) add_menu = wx.Menu() self._init_coll_AddMenu_Items(add_menu) self.ProjectTree.SetAddMenu(add_menu) self.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.OnProjectTreeRightUp, - id=ID_PLCOPENEDITORPROJECTTREE) + id=ID_PLCOPENEDITORPROJECTTREE) self.ProjectTree.Bind(wx.EVT_LEFT_UP, self.OnProjectTreeLeftUp) self.Bind(wx.EVT_TREE_SEL_CHANGING, self.OnProjectTreeItemChanging, - id=ID_PLCOPENEDITORPROJECTTREE) + id=ID_PLCOPENEDITORPROJECTTREE) self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnProjectTreeBeginDrag, - id=ID_PLCOPENEDITORPROJECTTREE) + id=ID_PLCOPENEDITORPROJECTTREE) self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.OnProjectTreeItemBeginEdit, - id=ID_PLCOPENEDITORPROJECTTREE) + id=ID_PLCOPENEDITORPROJECTTREE) self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnProjectTreeItemEndEdit, - id=ID_PLCOPENEDITORPROJECTTREE) + id=ID_PLCOPENEDITORPROJECTTREE) self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnProjectTreeItemActivated, - id=ID_PLCOPENEDITORPROJECTTREE) + 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) @@ -586,46 +618,50 @@ self.ProjectPanel.SplitHorizontally(self.ProjectTree, self.PouInstanceVariablesPanel, 300) - #----------------------------------------------------------------------- + # ----------------------------------------------------------------------- # Creating Tool Bar - #----------------------------------------------------------------------- - - MenuToolBar = wx.ToolBar(self, ID_PLCOPENEDITOREDITORMENUTOOLBAR, wx.DefaultPosition, wx.DefaultSize, - wx.TB_FLAT | wx.TB_NODIVIDER | wx.NO_BORDER) + # ----------------------------------------------------------------------- + + 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)) MenuToolBar.Realize() self.Panes["MenuToolBar"] = MenuToolBar self.AUIManager.AddPane(MenuToolBar, wx.aui.AuiPaneInfo(). - Name("MenuToolBar").Caption(_("Menu ToolBar")). - ToolbarPane().Top(). - LeftDockable(False).RightDockable(False)) - - EditorToolBar = wx.ToolBar(self, ID_PLCOPENEDITOREDITORTOOLBAR, wx.DefaultPosition, wx.DefaultSize, - wx.TB_FLAT | wx.TB_NODIVIDER | wx.NO_BORDER) + Name("MenuToolBar").Caption(_("Menu ToolBar")). + ToolbarPane().Top(). + 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, - GetBitmap("select"), wx.NullBitmap, _("Select an object")) + GetBitmap("select"), + wx.NullBitmap, + _("Select an object")) EditorToolBar.Realize() self.Panes["EditorToolBar"] = EditorToolBar self.AUIManager.AddPane(EditorToolBar, wx.aui.AuiPaneInfo(). - Name("EditorToolBar").Caption(_("Editor ToolBar")). - ToolbarPane().Top().Position(1). - LeftDockable(False).RightDockable(False)) + Name("EditorToolBar").Caption(_("Editor ToolBar")). + ToolbarPane().Top().Position(1). + LeftDockable(False).RightDockable(False)) self.Bind(wx.EVT_MENU, self.OnSelectionTool, - id=ID_PLCOPENEDITOREDITORTOOLBARSELECTION) - - #----------------------------------------------------------------------- + id=ID_PLCOPENEDITOREDITORTOOLBARSELECTION) + + # ----------------------------------------------------------------------- # Creating Search Panel - #----------------------------------------------------------------------- + # ----------------------------------------------------------------------- self.SearchResultPanel = SearchResultPanel(self.BottomNoteBook, self) self.MainTabs["SearchResultPanel"] = (self.SearchResultPanel, _("Search")) self.BottomNoteBook.AddPage(*self.MainTabs["SearchResultPanel"]) - #----------------------------------------------------------------------- + # ----------------------------------------------------------------------- # Creating Library Panel - #----------------------------------------------------------------------- + # ----------------------------------------------------------------------- self.LibraryPanel = LibraryPanel(self, True) self.MainTabs["LibraryPanel"] = (self.LibraryPanel, _("Library")) @@ -641,13 +677,7 @@ self.AUIManager.Update() - - ## Constructor of the PLCOpenEditor class. - # @param parent The parent window. - # @param controler The controler been used by PLCOpenEditor (default: None). - # @param fileOpen The filepath to open if no controler defined (default: None). - # @param debug The filepath to open if no controler defined (default: False). - def __init__(self, parent, enable_debug = False): + def __init__(self, parent, enable_debug=False): self.Controler = None self.Config = wx.ConfigBase.Get() self.EnableDebug = enable_debug @@ -664,32 +694,32 @@ # Icons for other items for imgname, itemtype in [ - #editables - ("PROJECT", ITEM_PROJECT), - #("POU", ITEM_POU), - #("VARIABLE", ITEM_VARIABLE), - ("TRANSITION", ITEM_TRANSITION), - ("ACTION", ITEM_ACTION), - ("CONFIGURATION", ITEM_CONFIGURATION), - ("RESOURCE", ITEM_RESOURCE), - ("DATATYPE", ITEM_DATATYPE), - # uneditables - ("DATATYPES", ITEM_DATATYPES), - ("FUNCTION", ITEM_FUNCTION), - ("FUNCTIONBLOCK", ITEM_FUNCTIONBLOCK), - ("PROGRAM", ITEM_PROGRAM), - ("VAR_LOCAL", ITEM_VAR_LOCAL), - ("VAR_LOCAL", ITEM_VAR_GLOBAL), - ("VAR_LOCAL", ITEM_VAR_EXTERNAL), - ("VAR_LOCAL", ITEM_VAR_TEMP), - ("VAR_INPUT", ITEM_VAR_INPUT), - ("VAR_OUTPUT", ITEM_VAR_OUTPUT), - ("VAR_INOUT", ITEM_VAR_INOUT), - ("TRANSITIONS", ITEM_TRANSITIONS), - ("ACTIONS", ITEM_ACTIONS), - ("CONFIGURATIONS", ITEM_CONFIGURATIONS), - ("RESOURCES", ITEM_RESOURCES), - ("PROPERTIES", ITEM_PROPERTIES)]: + # editables + ("PROJECT", ITEM_PROJECT), + # ("POU", ITEM_POU), + # ("VARIABLE", ITEM_VARIABLE), + ("TRANSITION", ITEM_TRANSITION), + ("ACTION", ITEM_ACTION), + ("CONFIGURATION", ITEM_CONFIGURATION), + ("RESOURCE", ITEM_RESOURCE), + ("DATATYPE", ITEM_DATATYPE), + # uneditables + ("DATATYPES", ITEM_DATATYPES), + ("FUNCTION", ITEM_FUNCTION), + ("FUNCTIONBLOCK", ITEM_FUNCTIONBLOCK), + ("PROGRAM", ITEM_PROGRAM), + ("VAR_LOCAL", ITEM_VAR_LOCAL), + ("VAR_LOCAL", ITEM_VAR_GLOBAL), + ("VAR_LOCAL", ITEM_VAR_EXTERNAL), + ("VAR_LOCAL", ITEM_VAR_TEMP), + ("VAR_INPUT", ITEM_VAR_INPUT), + ("VAR_OUTPUT", ITEM_VAR_OUTPUT), + ("VAR_INOUT", ITEM_VAR_INOUT), + ("TRANSITIONS", ITEM_TRANSITIONS), + ("ACTIONS", ITEM_ACTIONS), + ("CONFIGURATIONS", ITEM_CONFIGURATIONS), + ("RESOURCES", ITEM_RESOURCES), + ("PROPERTIES", ITEM_PROPERTIES)]: self.TreeImageDict[itemtype] = self.TreeImageList.Add(GetBitmap(imgname)) # Assign icon list to TreeCtrls @@ -703,7 +733,7 @@ self.SearchParams = None self.Highlights = {} self.DrawingMode = FREEDRAWING_MODE - #self.DrawingMode = DRIVENDRAWING_MODE + # self.DrawingMode = DRIVENDRAWING_MODE self.AuiTabCtrl = [] # Save default perspective @@ -717,7 +747,6 @@ "notebooks": notebooks, } - # Initialize Printing configuring elements self.PrintData = wx.PrintData() self.PrintData.SetPaperId(wx.PAPER_A4) @@ -754,9 +783,9 @@ notebook.SetSelection(idx) return -#------------------------------------------------------------------------------- -# Saving and restoring frame organization functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Saving and restoring frame organization functions + # ------------------------------------------------------------------------------- def GetTabInfos(self, tab): for page_name, (page_ref, page_title) in self.MainTabs.iteritems(): @@ -774,7 +803,7 @@ tab_size = child.GetSize() for page_idx in xrange(child.GetPageCount()): page = child.GetWindowFromIdx(page_idx) - if not tab.has_key("size"): + if "size" not in tab: tab["size"] = (tab_size[0], tab_size[1] + page.GetSize()[1]) tab_infos = self.GetTabInfos(page) if tab_infos is not None: @@ -808,9 +837,9 @@ if isinstance(tabs, ListType): if len(tabs) == 0: return - raise ValueError, "Not supported" - - if tabs.has_key("split"): + raise ValueError("Not supported") + + if "split" in tabs: self.LoadTabLayout(notebook, tabs["others"]) split_dir, split_ratio = tabs["split"] @@ -867,26 +896,28 @@ self.Config.Flush() -#------------------------------------------------------------------------------- -# General Functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # General Functions + # ------------------------------------------------------------------------------- def SetRefreshFunctions(self): self.RefreshFunctions = { - TITLE : self.RefreshTitle, - EDITORTOOLBAR : self.RefreshEditorToolBar, - FILEMENU : self.RefreshFileMenu, - EDITMENU : self.RefreshEditMenu, - DISPLAYMENU : self.RefreshDisplayMenu, - PROJECTTREE : self.RefreshProjectTree, - POUINSTANCEVARIABLESPANEL : self.RefreshPouInstanceVariablesPanel, - LIBRARYTREE : self.RefreshLibraryPanel, - SCALING : self.RefreshScaling, + TITLE: self.RefreshTitle, + EDITORTOOLBAR: self.RefreshEditorToolBar, + FILEMENU: self.RefreshFileMenu, + EDITMENU: self.RefreshEditMenu, + DISPLAYMENU: self.RefreshDisplayMenu, + PROJECTTREE: self.RefreshProjectTree, + POUINSTANCEVARIABLESPANEL: self.RefreshPouInstanceVariablesPanel, + LIBRARYTREE: self.RefreshLibraryPanel, + SCALING: self.RefreshScaling, PAGETITLES: self.RefreshPageTitles} - ## Call PLCOpenEditor refresh functions. - # @param elements List of elements to refresh. def _Refresh(self, *elements): + """Call Editor refresh functions. + + :param elements: List of elements to refresh. + """ try: for element in elements: self.RefreshFunctions[element]() @@ -894,9 +925,11 @@ # ignore exceptions caused by refresh while quitting pass - ## Callback function when AUINotebook Page closed with CloseButton - # @param event AUINotebook Event. def OnPageClose(self, event): + """Callback function when AUINotebook Page closed with CloseButton + + :param event: AUINotebook Event. + """ selected = self.TabsOpened.GetSelection() if selected > -1: window = self.TabsOpened.GetPage(selected) @@ -911,7 +944,6 @@ else: event.Veto() - def GetCopyBuffer(self, primary_selection=False): data = None if primary_selection and wx.Platform == '__WXMSW__': @@ -959,22 +991,20 @@ PROJECTTREE, POUINSTANCEVARIABLESPANEL, SCALING) dialog.Destroy() -#------------------------------------------------------------------------------- -# 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. - # @param text title for the tab ctrl. + # ------------------------------------------------------------------------------- + # Notebook Unified Functions + # ------------------------------------------------------------------------------- + def AddPage(self, window, text): + """Function that add a tab in Notebook, calling refresh for tab DClick event + for wx.aui.AUINotebook. + + :param window: Panel to display in tab. + :param text: title for the tab ctrl. + """ 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. - # @param text title for the tab ctrl. def DeletePage(self, window): for idx in xrange(self.TabsOpened.GetPageCount()): if self.TabsOpened.GetPage(idx) == window: @@ -982,37 +1012,44 @@ self.RefreshTabCtrlEvent() return - ## Function that fix difference in deleting all tabs between - # wx.Notebook and wx.aui.AUINotebook. def DeleteAllPages(self): + """Function that fix difference in deleting all tabs between + wx.Notebook and wx.aui.AUINotebook. + """ for idx in xrange(self.TabsOpened.GetPageCount()): self.TabsOpened.DeletePage(0) self.RefreshTabCtrlEvent() - ## Function that fix difference in setting picture on tab between - # wx.Notebook and wx.aui.AUINotebook. - # @param idx Tab index. - # @param bitmap wx.Bitmap to define on tab. - # @return True if operation succeeded def SetPageBitmap(self, idx, bitmap): + """Function that fix difference in setting picture on tab between + wx.Notebook and wx.aui.AUINotebook. + + :param idx: Tab index. + :param bitmap: wx.Bitmap to define on tab. + :returns: True if operation succeeded + """ return self.TabsOpened.SetPageBitmap(idx, bitmap) -#------------------------------------------------------------------------------- -# Dialog Message Functions -#------------------------------------------------------------------------------- - - ## Function displaying an Error dialog in PLCOpenEditor. - # @param message The message to display. + # ------------------------------------------------------------------------------- + # Dialog Message Functions + # ------------------------------------------------------------------------------- + def ShowErrorMessage(self, message): - dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) + """Function displaying an Error dialog in editor. + + :param message: The message to display. + """ + dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() - ## Function displaying an Error dialog in PLCOpenEditor. - # @return False if closing cancelled. def CheckSaveBeforeClosing(self, title=_("Close Project")): + """Function displaying an question dialog if project is not saved" + + :returns: False if closing cancelled. + """ if not self.Controler.ProjectIsSaved(): - dialog = wx.MessageDialog(self, _("There are changes, do you want to save?"), title, wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION) + dialog = wx.MessageDialog(self, _("There are changes, do you want to save?"), title, wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION) answer = dialog.ShowModal() dialog.Destroy() if answer == wx.ID_YES: @@ -1027,9 +1064,9 @@ return True -#------------------------------------------------------------------------------- -# File Menu Functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # File Menu Functions + # ------------------------------------------------------------------------------- def RefreshFileMenu(self): pass @@ -1076,7 +1113,7 @@ preview = wx.PrintPreview(printout, printout2, data) if preview.Ok(): - preview_frame = wx.PreviewFrame(preview, self, _("Print preview"), style=wx.DEFAULT_FRAME_STYLE|wx.FRAME_FLOAT_ON_PARENT) + preview_frame = wx.PreviewFrame(preview, self, _("Print preview"), style=wx.DEFAULT_FRAME_STYLE | wx.FRAME_FLOAT_ON_PARENT) preview_frame.Initialize() @@ -1108,9 +1145,9 @@ def OnQuitMenu(self, event): self.Close() -#------------------------------------------------------------------------------- -# Edit Menu Functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Edit Menu Functions + # ------------------------------------------------------------------------------- def RefreshEditMenu(self): MenuToolBar = self.Panes["MenuToolBar"] @@ -1125,14 +1162,14 @@ MenuToolBar.EnableTool(wx.ID_UNDO, undo) 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.Enable(ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, True) + # self.EditMenu.Check(ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, # self.Controler.IsProjectBufferEnabled()) self.EditMenu.Enable(wx.ID_FIND, selected > -1) self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUFINDNEXT, - selected > -1 and self.SearchParams is not None) + selected > -1 and self.SearchParams is not None) self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUFINDPREVIOUS, - selected > -1 and self.SearchParams is not None) + selected > -1 and self.SearchParams is not None) self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, True) MenuToolBar.EnableTool(ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, True) self.EditMenu.Enable(wx.ID_ADD, True) @@ -1162,7 +1199,7 @@ MenuToolBar.EnableTool(wx.ID_UNDO, False) self.EditMenu.Enable(wx.ID_REDO, False) MenuToolBar.EnableTool(wx.ID_REDO, False) - #self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, False) + # self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, False) self.EditMenu.Enable(wx.ID_CUT, False) MenuToolBar.EnableTool(wx.ID_CUT, False) self.EditMenu.Enable(wx.ID_COPY, False) @@ -1295,9 +1332,9 @@ self.SearchResultPanel.SetSearchResults(criteria, result) self.SelectTab(self.SearchResultPanel) -#------------------------------------------------------------------------------- -# Display Menu Functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Display Menu Functions + # ------------------------------------------------------------------------------- def RefreshDisplayMenu(self): if self.Controler is not None: @@ -1346,9 +1383,9 @@ def OnResetPerspective(self, event): self.ResetPerspective() -#------------------------------------------------------------------------------- -# Project Editor Panels Management Functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Project Editor Panels Management Functions + # ------------------------------------------------------------------------------- def OnPageDragged(self, event): wx.CallAfter(self.RefreshTabCtrlEvent) @@ -1453,7 +1490,7 @@ event.Skip() return OnTabsOpenedDClick - def SwitchFullScrMode(self,evt): + def SwitchFullScrMode(self, evt): pane = self.AUIManager.GetPane(self.TabsOpened) if pane.IsMaximized(): self.AUIManager.RestorePane(pane) @@ -1461,9 +1498,9 @@ self.AUIManager.MaximizePane(pane) self.AUIManager.Update() -#------------------------------------------------------------------------------- -# Types Tree Management Functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Types Tree Management Functions + # ------------------------------------------------------------------------------- def RefreshProjectTree(self): # Extract current selected item tagname @@ -1499,16 +1536,16 @@ self.ProjectTree.SetItemTextColour(root, highlight_colours[1]) self.ProjectTree.SetItemExtraImage(root, None) if infos["type"] == ITEM_POU: - self.ProjectTree.SetItemImage(root, - self.TreeImageDict[self.Controler.GetPouBodyType(infos["name"])]) + self.ProjectTree.SetItemImage( + root, self.TreeImageDict[self.Controler.GetPouBodyType(infos["name"])]) if item_alone: self.ProjectTree.SetItemExtraImage(root, self.Controler.GetPouType(infos["name"])) - elif infos.has_key("icon") and infos["icon"] is not None: + elif "icon" in infos and infos["icon"] is not None: icon_name = infos["icon"] - if not self.TreeImageDict.has_key(icon_name): + if icon_name not in self.TreeImageDict: 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"]): + elif infos["type"] in self.TreeImageDict: self.ProjectTree.SetItemImage(root, self.TreeImageDict[infos["type"]]) item, root_cookie = self.ProjectTree.GetFirstChild(root) @@ -1539,8 +1576,8 @@ root = self.ProjectTree.GetRootItem() if root is not None and root.IsOk(): words = tagname.split("::") - result = self.RecursiveProjectTreeItemSelection(root, - zip(words[1:], self.TagNamePartsItemTypes.get(words[0], []))) + result = self.RecursiveProjectTreeItemSelection( + root, zip(words[1:], self.TagNamePartsItemTypes.get(words[0], []))) return result def RecursiveProjectTreeItemSelection(self, root, items): @@ -1591,18 +1628,18 @@ new_name = event.GetLabel() if new_name != "": if not TestIdentifier(new_name): - message = _("\"%s\" is not a valid identifier!")%new_name + message = _("\"%s\" is not a valid identifier!") % new_name elif new_name.upper() in IEC_KEYWORDS: - message = _("\"%s\" is a keyword. It can't be used!")%new_name + message = _("\"%s\" is a keyword. It can't be used!") % new_name else: item = event.GetItem() old_name = self.ProjectTree.GetItemText(item) item_infos = self.ProjectTree.GetPyData(item) if item_infos["type"] == ITEM_PROJECT: - self.Controler.SetProjectProperties(name = new_name) + self.Controler.SetProjectProperties(name=new_name) elif item_infos["type"] == ITEM_DATATYPE: if new_name.upper() in [name.upper() for name in self.Controler.GetProjectDataTypeNames() if name != old_name]: - message = _("\"%s\" data type already exists!")%new_name + message = _("\"%s\" data type already exists!") % new_name abort = True if not abort: self.Controler.ChangeDataTypeName(old_name, new_name) @@ -1611,10 +1648,10 @@ self.RefreshPageTitles() elif item_infos["type"] == ITEM_POU: if new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames() if name != old_name]: - message = _("\"%s\" pou already exists!")%new_name + message = _("\"%s\" pou already exists!") % new_name abort = True elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariableNames()]: - messageDialog = wx.MessageDialog(self, _("A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?")%new_name, _("Error"), wx.YES_NO|wx.ICON_QUESTION) + messageDialog = wx.MessageDialog(self, _("A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?") % new_name, _("Error"), wx.YES_NO | wx.ICON_QUESTION) if messageDialog.ShowModal() == wx.ID_NO: abort = True messageDialog.Destroy() @@ -1626,11 +1663,11 @@ self.RefreshPageTitles() elif item_infos["type"] == ITEM_TRANSITION: pou_item = self.ProjectTree.GetItemParent(event.GetItem()) - pou_name = self.ProjectTree.GetItemText(pou_item) + pou_name = self.ProjectTree.GetItemText(pou_item) if new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames()]: - message = _("A POU named \"%s\" already exists!")%new_name + message = _("A POU named \"%s\" already exists!") % new_name elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariableNames(pou_name) if name != old_name]: - message = _("A variable with \"%s\" as name already exists in this pou!")%new_name + message = _("A variable with \"%s\" as name already exists in this pou!") % new_name else: words = item_infos["tagname"].split("::") self.Controler.ChangePouTransitionName(words[1], old_name, new_name) @@ -1641,9 +1678,9 @@ pou_item = self.ProjectTree.GetItemParent(event.GetItem()) pou_name = self.ProjectTree.GetItemText(pou_item) if new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames()]: - message = _("A POU named \"%s\" already exists!")%new_name + message = _("A POU named \"%s\" already exists!") % new_name elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariableNames(pou_name) if name != old_name]: - message = _("A variable with \"%s\" as name already exists in this pou!")%new_name + message = _("A variable with \"%s\" as name already exists in this pou!") % new_name else: words = item_infos["tagname"].split("::") self.Controler.ChangePouActionName(words[1], old_name, new_name) @@ -1652,15 +1689,15 @@ self.RefreshPageTitles() elif item_infos["type"] == ITEM_CONFIGURATION: if new_name.upper() in [name.upper() for name in self.Controler.GetProjectConfigNames() if name != old_name]: - message = _("\"%s\" config already exists!")%new_name + message = _("\"%s\" config already exists!") % new_name abort = True elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames()]: - messageDialog = wx.MessageDialog(self, _("There is a POU named \"%s\". This could cause a conflict. Do you wish to continue?")%new_name, _("Error"), wx.YES_NO|wx.ICON_QUESTION) + messageDialog = wx.MessageDialog(self, _("There is a POU named \"%s\". This could cause a conflict. Do you wish to continue?") % new_name, _("Error"), wx.YES_NO | wx.ICON_QUESTION) if messageDialog.ShowModal() == wx.ID_NO: abort = True messageDialog.Destroy() elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariableNames()]: - messageDialog = wx.MessageDialog(self, _("A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?")%new_name, _("Error"), wx.YES_NO|wx.ICON_QUESTION) + messageDialog = wx.MessageDialog(self, _("A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?") % new_name, _("Error"), wx.YES_NO | wx.ICON_QUESTION) if messageDialog.ShowModal() == wx.ID_NO: abort = True messageDialog.Destroy() @@ -1671,15 +1708,15 @@ self.RefreshPageTitles() elif item_infos["type"] == ITEM_RESOURCE: if new_name.upper() in [name.upper() for name in self.Controler.GetProjectConfigNames()]: - message = _("\"%s\" config already exists!")%new_name + message = _("\"%s\" config already exists!") % new_name abort = True elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames()]: - messageDialog = wx.MessageDialog(self, _("There is a POU named \"%s\". This could cause a conflict. Do you wish to continue?")%new_name, _("Error"), wx.YES_NO|wx.ICON_QUESTION) + messageDialog = wx.MessageDialog(self, _("There is a POU named \"%s\". This could cause a conflict. Do you wish to continue?") % new_name, _("Error"), wx.YES_NO | wx.ICON_QUESTION) if messageDialog.ShowModal() == wx.ID_NO: abort = True messageDialog.Destroy() elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariableNames()]: - messageDialog = wx.MessageDialog(self, _("A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?")%new_name, _("Error"), wx.YES_NO|wx.ICON_QUESTION) + messageDialog = wx.MessageDialog(self, _("A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?") % new_name, _("Error"), wx.YES_NO | wx.ICON_QUESTION) if messageDialog.ShowModal() == wx.ID_NO: abort = True messageDialog.Destroy() @@ -1707,8 +1744,8 @@ self.EditProjectSettings() else: if item_infos["type"] in [ITEM_DATATYPE, ITEM_POU, - ITEM_CONFIGURATION, ITEM_RESOURCE, - ITEM_TRANSITION, ITEM_ACTION]: + ITEM_CONFIGURATION, ITEM_RESOURCE, + ITEM_TRANSITION, ITEM_ACTION]: self.EditProjectElement(item_infos["type"], item_infos["tagname"]) event.Skip() @@ -1738,8 +1775,8 @@ if item != self.LastToolTipItem and self.LastToolTipItem is not None: self.ProjectTree.SetToolTip(None) self.LastToolTipItem = None - if (self.LastToolTipItem != item and - item_infos["type"] in [ITEM_POU, ITEM_TRANSITION, ITEM_ACTION]): + if self.LastToolTipItem != item and \ + item_infos["type"] in [ITEM_POU, ITEM_TRANSITION, ITEM_ACTION]: bodytype = self.Controler.GetEditedElementBodyType( item_infos["tagname"]) if item_infos["type"] == ITEM_POU: @@ -1754,8 +1791,8 @@ block_type = "Action" self.LastToolTipItem = item wx.CallAfter(self.ProjectTree.SetToolTipString, - "%s : %s : %s" % ( - block_type, bodytype, item_infos["name"])) + "%s : %s : %s" % ( + block_type, bodytype, item_infos["name"])) elif self.LastToolTipItem is not None: self.ProjectTree.SetToolTip(None) self.LastToolTipItem = None @@ -1768,7 +1805,7 @@ else: event.Skip() - def EditProjectElement(self, element, tagname, onlyopened = False): + def EditProjectElement(self, element, tagname, onlyopened=False): openedidx = self.IsOpened(tagname) if openedidx is not None: old_selected = self.TabsOpened.GetSelection() @@ -1862,7 +1899,7 @@ if name != "Project": new_id = wx.NewId() AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add POU")) - self.Bind(wx.EVT_MENU, self.GenerateAddPouFunction({"Functions" : "function", "Function Blocks" : "functionBlock", "Programs" : "program"}[name]), id=new_id) + self.Bind(wx.EVT_MENU, self.GenerateAddPouFunction({"Functions": "function", "Function Blocks": "functionBlock", "Programs": "program"}[name]), id=new_id) new_id = wx.NewId() AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Paste POU")) @@ -1970,10 +2007,9 @@ event.Skip() - -#------------------------------------------------------------------------------- -# Instances Tree Management Functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Instances Tree Management Functions + # ------------------------------------------------------------------------------- def GetTreeImage(self, var_class): return self.TreeImageDict[var_class] @@ -2056,16 +2092,16 @@ self.DebugVariablePanel.InsertValue(iec_path, force=force, graph=graph) self.EnsureTabVisible(self.DebugVariablePanel) -#------------------------------------------------------------------------------- -# Library Panel Management Function -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Library Panel Management Function + # ------------------------------------------------------------------------------- def RefreshLibraryPanel(self): self.LibraryPanel.RefreshTree() -#------------------------------------------------------------------------------- -# ToolBars Management Functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # ToolBars Management Functions + # ------------------------------------------------------------------------------- def AddToMenuToolBar(self, items): MenuToolBar = self.Panes["MenuToolBar"] @@ -2133,10 +2169,9 @@ self.CurrentMenu = menu self.ResetCurrentMode() - -#------------------------------------------------------------------------------- -# EditorToolBar Items Functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # EditorToolBar Items Functions + # ------------------------------------------------------------------------------- def ResetCurrentMode(self): selected = self.TabsOpened.GetSelection() @@ -2272,10 +2307,9 @@ else: self.TabsOpened.GetPage(selected).AddJump() - -#------------------------------------------------------------------------------- -# Add Project Elements Functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Add Project Elements Functions + # ------------------------------------------------------------------------------- def OnAddNewProject(self, event): # Asks user to create main program after creating new project @@ -2290,7 +2324,7 @@ self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE) self.EditProjectElement(ITEM_DATATYPE, tagname) - def GenerateAddPouFunction(self, pou_type, type_readonly = False): + def GenerateAddPouFunction(self, pou_type, type_readonly=False): def OnAddPouMenu(event): dialog = PouDialog(self, pou_type, type_readonly) dialog.SetPouNames(self.Controler.GetProjectPouNames()) @@ -2383,7 +2417,7 @@ 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 = UNEDITABLE_NAMES_DICT[pou_type] # one of 'Functions', 'Function Blocks' or 'Programs' pou_type = {'Functions': 'function', 'Function Blocks': 'functionBlock', 'Programs': 'program'}[pou_type] else: pou_type = None @@ -2398,17 +2432,18 @@ self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, PROJECTTREE, LIBRARYTREE) self.EditProjectElement(ITEM_POU, result[0]) -#------------------------------------------------------------------------------- -# Remove Project Elements Functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Remove Project Elements Functions + # ------------------------------------------------------------------------------- def CheckElementIsUsedBeforeDeletion(self, check_function, title, name): if not check_function(name): return True - dialog = wx.MessageDialog(self, + 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) + title, wx.YES_NO | wx.ICON_QUESTION) answer = dialog.ShowModal() dialog.Destroy() return answer == wx.ID_YES @@ -2502,9 +2537,9 @@ self.TabsOpened.DeletePage(idx) self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL) -#------------------------------------------------------------------------------- -# Highlights showing functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Highlights showing functions + # ------------------------------------------------------------------------------- def ShowHighlight(self, infos, start, end, highlight_type): self.SelectProjectTreeItem(infos[0]) @@ -2541,14 +2576,17 @@ def ClearSearchResults(self): self.ClearHighlights(SEARCH_RESULT_HIGHLIGHT) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Viewer Printout -#------------------------------------------------------------------------------- - -UPPER_DIV = lambda x, y: (x / y) + {True : 0, False : 1}[(x % y) == 0] +# ------------------------------------------------------------------------------- + + +def UPPER_DIV(x, y): + return (x / y) + {True: 0, False: 1}[(x % y) == 0] + class GraphicPrintout(wx.Printout): - def __init__(self, viewer, page_size, margins, preview = False): + def __init__(self, viewer, page_size, margins, preview=False): wx.Printout.__init__(self) self.Viewer = viewer self.PageSize = page_size @@ -2625,9 +2663,6 @@ dc.SetClippingRegion(posX, posY, self.PageSize[0] * scale, self.PageSize[1] * scale) dc.SetUserScale(scale, scale) - #------------------------------------------- - self.Viewer.DoDrawing(dc, True) return True - diff -r 31e63e25b4cc -r 64beb9e9c749 NativeLib.py --- a/NativeLib.py Mon Aug 21 20:17:19 2017 +0000 +++ b/NativeLib.py Mon Aug 21 23:22:58 2017 +0300 @@ -27,7 +27,7 @@ import util.paths as paths from POULibrary import POULibrary + class NativeLibrary(POULibrary): def GetLibraryPath(self): - return paths.AbsNeighbourFile(__file__, "NativeLib.xml") - + return paths.AbsNeighbourFile(__file__, "NativeLib.xml") diff -r 31e63e25b4cc -r 64beb9e9c749 PLCControler.py --- a/PLCControler.py Mon Aug 21 20:17:19 2017 +0000 +++ b/PLCControler.py Mon Aug 21 23:22:58 2017 +0300 @@ -27,47 +27,52 @@ from types import StringType, UnicodeType, TupleType from lxml import etree from copy import deepcopy -import os,sys,re +import os +import sys +import re import datetime import util.paths as paths from time import localtime from collections import OrderedDict, namedtuple - +from util.TranslationCatalogs import NoTranslate from plcopen import * from graphics.GraphicCommons import * from PLCGenerator import * duration_model = re.compile("(?:([0-9]{1,2})h)?(?:([0-9]{1,2})m(?!s))?(?:([0-9]{1,2})s)?(?:([0-9]{1,3}(?:\.[0-9]*)?)ms)?") -ITEMS_EDITABLE = [ITEM_PROJECT, - ITEM_POU, - ITEM_VARIABLE, - ITEM_TRANSITION, - ITEM_ACTION, - ITEM_CONFIGURATION, - ITEM_RESOURCE, - ITEM_DATATYPE - ] = range(8) - -ITEMS_UNEDITABLE = [ITEM_DATATYPES, - ITEM_FUNCTION, - ITEM_FUNCTIONBLOCK, - ITEM_PROGRAM, - ITEM_TRANSITIONS, - ITEM_ACTIONS, - ITEM_CONFIGURATIONS, - ITEM_RESOURCES, - ITEM_PROPERTIES - ] = range(8, 17) - -ITEMS_VARIABLE = [ITEM_VAR_LOCAL, - ITEM_VAR_GLOBAL, - ITEM_VAR_EXTERNAL, - ITEM_VAR_TEMP, - ITEM_VAR_INPUT, - ITEM_VAR_OUTPUT, - ITEM_VAR_INOUT - ] = range(17, 24) +ITEMS_EDITABLE = [ + ITEM_PROJECT, + ITEM_POU, + ITEM_VARIABLE, + ITEM_TRANSITION, + ITEM_ACTION, + ITEM_CONFIGURATION, + ITEM_RESOURCE, + ITEM_DATATYPE +] = range(8) + +ITEMS_UNEDITABLE = [ + ITEM_DATATYPES, + ITEM_FUNCTION, + ITEM_FUNCTIONBLOCK, + ITEM_PROGRAM, + ITEM_TRANSITIONS, + ITEM_ACTIONS, + ITEM_CONFIGURATIONS, + ITEM_RESOURCES, + ITEM_PROPERTIES +] = range(8, 17) + +ITEMS_VARIABLE = [ + ITEM_VAR_LOCAL, + ITEM_VAR_GLOBAL, + ITEM_VAR_EXTERNAL, + ITEM_VAR_TEMP, + ITEM_VAR_INPUT, + ITEM_VAR_OUTPUT, + ITEM_VAR_INOUT +] = range(17, 24) VAR_CLASS_INFOS = { "Local": ("localVars", ITEM_VAR_LOCAL), @@ -78,10 +83,11 @@ "Output": ("outputVars", ITEM_VAR_OUTPUT), "InOut": ("inOutVars", ITEM_VAR_INOUT)} -POU_TYPES = {"program": ITEM_PROGRAM, - "functionBlock": ITEM_FUNCTIONBLOCK, - "function": ITEM_FUNCTION, - } +POU_TYPES = { + "program": ITEM_PROGRAM, + "functionBlock": ITEM_FUNCTIONBLOCK, + "function": ITEM_FUNCTION, +} LOCATIONS_ITEMS = [LOCATION_CONFNODE, LOCATION_MODULE, @@ -92,21 +98,22 @@ ScriptDirectory = paths.AbsDir(__file__) + def GetUneditableNames(): - _ = lambda x:x + _ = NoTranslate 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, RESOURCES, PROPERTIES] = UNEDITABLE_NAMES -#------------------------------------------------------------------------------- -# Helper object for loading library in xslt stylesheets -#------------------------------------------------------------------------------- class LibraryResolver(etree.Resolver): + """Helper object for loading library in xslt stylesheets""" def __init__(self, controller, debug=False): self.Controller = controller @@ -126,32 +133,42 @@ lib_el.append(deepcopy(ctn["types"])) return self.resolve_string(etree.tostring(lib_el), context) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Helpers functions for translating list of arguments # from xslt to valid arguments -#------------------------------------------------------------------------------- - -_StringValue = lambda x: x -_BoolValue = lambda x: x in ["true", "0"] +# ------------------------------------------------------------------------------- + + +def _StringValue(x): + return x + + +def _BoolValue(x): + return x in ["true", "0"] + def _translate_args(translations, args): return [translate(arg[0]) if len(arg) > 0 else None for translate, arg in zip(translations, args)] -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Helpers object for generating pou var list -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + class _VariableInfos(object): __slots__ = ["Name", "Class", "Option", "Location", "InitialValue", "Edit", "Documentation", "Type", "Tree", "Number"] + def __init__(self, *args): for attr, value in zip(self.__slots__, args): setattr(self, attr, value if value is not None else "") + def copy(self): return _VariableInfos(*[getattr(self, attr) for attr in self.__slots__]) + class VariablesInfosFactory: def __init__(self, variables): @@ -188,9 +205,10 @@ [_StringValue] * 5 + [_BoolValue] + [_StringValue], args) + [self.GetType(), self.GetTree()]))) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Helpers object for generating pou variable instance list -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + def class_extraction(value): class_type = { @@ -212,14 +230,18 @@ return None + class _VariablesTreeItemInfos(object): __slots__ = ["name", "var_class", "type", "edit", "debug", "variables"] + def __init__(self, *args): for attr, value in zip(self.__slots__, args): setattr(self, attr, value if value is not None else "") + def copy(self): return _VariableTreeItem(*[getattr(self, attr) for attr in self.__slots__]) + class VariablesTreeInfosFactory: def __init__(self): @@ -241,23 +263,18 @@ [_StringValue, class_extraction, _StringValue] + [_BoolValue] * 2, args) + [[]]))) -#------------------------------------------------------------------------------- -# Helpers object for generating instances path list -#------------------------------------------------------------------------------- class InstancesPathFactory: - + """Helpers object for generating instances path list""" def __init__(self, instances): self.Instances = instances def AddInstance(self, context, *args): self.Instances.append(args[0][0]) -#------------------------------------------------------------------------------- -# Helpers object for generating instance tagname -#------------------------------------------------------------------------------- class InstanceTagName: + """Helpers object for generating instance tagname""" def __init__(self, controller): self.Controller = controller @@ -281,13 +298,16 @@ def TransitionTagName(self, context, *args): self.TagName = self.Controller.ComputePouTransitionName(args[0][0], args[0][1]) -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Helpers object for generating pou block instances list -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + _Point = namedtuple("Point", ["x", "y"]) -_BlockInstanceInfos = namedtuple("BlockInstanceInfos", +_BlockInstanceInfos = namedtuple( + "BlockInstanceInfos", ["type", "id", "x", "y", "width", "height", "specific_values", "inputs", "outputs"]) _BlockSpecificValues = ( @@ -347,20 +367,26 @@ [lambda x: x]), } -_InstanceConnectionInfos = namedtuple("InstanceConnectionInfos", +_InstanceConnectionInfos = namedtuple( + "InstanceConnectionInfos", ["name", "negated", "edge", "position", "links"]) -_ConnectionLinkInfos = namedtuple("ConnectionLinkInfos", +_ConnectionLinkInfos = namedtuple( + "ConnectionLinkInfos", ["refLocalId", "formalParameter", "points"]) + class _ActionInfos(object): __slots__ = ["qualifier", "type", "value", "duration", "indicator"] + def __init__(self, *args): for attr, value in zip(self.__slots__, args): setattr(self, attr, value if value is not None else "") + def copy(self): return _ActionInfos(*[getattr(self, attr) for attr in self.__slots__]) + class BlockInstanceFactory: def __init__(self, block_instances): @@ -380,8 +406,8 @@ 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): + if args[0][0] == "step" and len(self.SpecificValues) < 3 or \ + args[0][0] == "transition" and len(self.SpecificValues) < 4: self.SpecificValues.append([None]) elif args[0][0] == "actionBlock" and len(self.SpecificValues) < 1: self.SpecificValues.append([[]]) @@ -426,23 +452,25 @@ 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")) -#------------------------------------------------------------------------------- -# Undo Buffer for PLCOpenEditor -#------------------------------------------------------------------------------- # Length of the buffer UNDO_BUFFER_LENGTH = 20 -""" -Class implementing a buffer of changes made on the current editing model -""" + class UndoBuffer: - - # Constructor initialising buffer - def __init__(self, currentstate, issaved = False): + """ + Undo Buffer for PLCOpenEditor + Class implementing a buffer of changes made on the current editing model + """ + + def __init__(self, currentstate, issaved=False): + """ + Constructor initialising buffer + """ self.Buffer = [] self.CurrentIndex = -1 self.MinIndex = -1 @@ -464,8 +492,10 @@ else: self.LastSave = -1 - # Add a new state in buffer def Buffering(self, currentstate): + """ + Add a new state in buffer + """ self.CurrentIndex = (self.CurrentIndex + 1) % UNDO_BUFFER_LENGTH self.Buffer[self.CurrentIndex] = currentstate # Actualising buffer limits @@ -477,8 +507,10 @@ self.MinIndex = (self.MinIndex + 1) % UNDO_BUFFER_LENGTH self.MinIndex = max(self.MinIndex, 0) - # Return current state of buffer def Current(self): + """ + Return current state of buffer + """ return self.Buffer[self.CurrentIndex] # Change current state to previous in buffer and return new current state @@ -512,14 +544,11 @@ return self.LastSave == self.CurrentIndex -#------------------------------------------------------------------------------- -# Controler for PLCOpenEditor -#------------------------------------------------------------------------------- - -""" -Class which controls the operations made on the plcopen model and answers to view requests -""" class PLCControler: + """ + Controler for PLCOpenEditor + Class which controls the operations made on the plcopen model and answers to view requests + """ # Create a new PLCControler def __init__(self): @@ -547,15 +576,15 @@ def GetQualifierTypes(self): return QualifierList - def GetProject(self, debug = False): + def GetProject(self, debug=False): if debug and self.CurrentCompiledProject is not None: return self.CurrentCompiledProject else: return self.Project -#------------------------------------------------------------------------------- -# Project management functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Project management functions + # ------------------------------------------------------------------------------- # Return if a project is opened def HasOpenedProject(self): @@ -579,28 +608,28 @@ self.Buffering = False # Return project data type names - def GetProjectDataTypeNames(self, debug = False): + def GetProjectDataTypeNames(self, debug=False): project = self.GetProject(debug) if project is not None: return [datatype.getname() for datatype in project.getdataTypes()] return [] # Return project pou names - def GetProjectPouNames(self, debug = False): + def GetProjectPouNames(self, debug=False): project = self.GetProject(debug) if project is not None: return [pou.getname() for pou in project.getpous()] return [] # Return project pou names - def GetProjectConfigNames(self, debug = False): + def GetProjectConfigNames(self, debug=False): project = self.GetProject(debug) if project is not None: return [config.getname() for config in project.getconfigurations()] return [] # Return project pou variable names - def GetProjectPouVariableNames(self, pou_name = None, debug = False): + def GetProjectPouVariableNames(self, pou_name=None, debug=False): variables = [] project = self.GetProject(debug) if project is not None: @@ -627,7 +656,7 @@ if self.ProjectIsSaved(): return self.FileName else: - return "~%s~"%self.FileName + return "~%s~" % self.FileName return "" # Change file path and save file name or create a default one if file path not defined @@ -635,12 +664,12 @@ self.FilePath = filepath if filepath == "": self.LastNewIndex += 1 - self.FileName = _("Unnamed%d")%self.LastNewIndex + self.FileName = _("Unnamed%d") % self.LastNewIndex else: self.FileName = os.path.splitext(os.path.basename(filepath))[0] # Change project properties - def SetProjectProperties(self, name = None, properties = None, buffer = True): + def SetProjectProperties(self, name=None, properties=None, buffer=True): if self.Project is not None: if name is not None: self.Project.setname(name) @@ -658,7 +687,7 @@ return None # Return project properties - def GetProjectProperties(self, debug = False): + def GetProjectProperties(self, debug=False): project = self.GetProject(debug) if project is not None: properties = project.getfileHeader() @@ -667,17 +696,34 @@ return None # Return project informations - def GetProjectInfos(self, debug = False): + def GetProjectInfos(self, debug=False): project = self.GetProject(debug) if project is not None: infos = {"name": project.getname(), "type": ITEM_PROJECT} - datatypes = {"name": DATA_TYPES, "type": ITEM_DATATYPES, "values":[]} + datatypes = {"name": DATA_TYPES, "type": ITEM_DATATYPES, "values": []} for datatype in project.getdataTypes(): - 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":[]}, - "program": {"name": PROGRAMS, "type": ITEM_PROGRAM, "values":[]}} + 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": [] + }, + "program": { + "name": PROGRAMS, + "type": ITEM_PROGRAM, + "values": [] + } + } for pou in project.getpous(): pou_type = pou.getpouType() pou_infos = {"name": pou.getname(), "type": ITEM_POU, @@ -686,13 +732,17 @@ if pou.getbodyType() == "SFC": transitions = [] for transition in pou.gettransitionList(): - transitions.append({"name": transition.getname(), "type": ITEM_TRANSITION, + transitions.append({ + "name": transition.getname(), + "type": ITEM_TRANSITION, "tagname": self.ComputePouTransitionName(pou.getname(), transition.getname()), "values": []}) pou_values.append({"name": TRANSITIONS, "type": ITEM_TRANSITIONS, "values": transitions}) actions = [] for action in pou.getactionList(): - actions.append({"name": action.getname(), "type": ITEM_ACTION, + actions.append({ + "name": action.getname(), + "type": ITEM_ACTION, "tagname": self.ComputePouActionName(pou.getname(), action.getname()), "values": []}) pou_values.append({"name": ACTIONS, "type": ITEM_ACTIONS, "values": actions}) @@ -702,13 +752,17 @@ configurations = {"name": CONFIGURATIONS, "type": ITEM_CONFIGURATIONS, "values": []} for config in project.getconfigurations(): config_name = config.getname() - config_infos = {"name": config_name, "type": ITEM_CONFIGURATION, + config_infos = { + "name": config_name, + "type": ITEM_CONFIGURATION, "tagname": self.ComputeConfigurationName(config.getname()), "values": []} resources = {"name": RESOURCES, "type": ITEM_RESOURCES, "values": []} for resource in config.getresource(): resource_name = resource.getname() - resource_infos = {"name": resource_name, "type": ITEM_RESOURCE, + resource_infos = { + "name": resource_name, + "type": ITEM_RESOURCE, "tagname": self.ComputeConfigurationResourceName(config.getname(), resource.getname()), "values": []} resources["values"].append(resource_infos) @@ -719,7 +773,7 @@ return infos return None - def GetPouVariables(self, tagname, debug = False): + def GetPouVariables(self, tagname, debug=False): pou_type = None project = self.GetProject(debug) if project is not None: @@ -732,8 +786,8 @@ etree.parse( os.path.join(ScriptDirectory, "plcopen", "pou_variables.xslt"), parser), - extensions = {("pou_vars_ns", name): getattr(factory, name) - for name in ["SetRoot", "AddVariable"]}) + extensions={("pou_vars_ns", name): getattr(factory, name) + for name in ["SetRoot", "AddVariable"]}) obj = None words = tagname.split("::") @@ -747,7 +801,7 @@ return None - def GetInstanceList(self, root, name, debug = False): + def GetInstanceList(self, root, name, debug=False): instances = [] project = self.GetProject(debug) if project is not None: @@ -760,15 +814,15 @@ etree.parse( os.path.join(ScriptDirectory, "plcopen", "instances_path.xslt"), parser), - extensions = { + extensions={ ("instances_ns", "AddInstance"): factory.AddInstance}) - instances_path_xslt_tree(root, - instance_type=etree.XSLT.strparam(name)) + instances_path_xslt_tree( + root, instance_type=etree.XSLT.strparam(name)) return instances - def SearchPouInstances(self, tagname, debug = False): + def SearchPouInstances(self, tagname, debug=False): project = self.GetProject(debug) if project is not None: words = tagname.split("::") @@ -784,7 +838,7 @@ self.ComputePouName(words[1]), debug)] return [] - def GetPouInstanceTagName(self, instance_path, debug = False): + def GetPouInstanceTagName(self, instance_path, debug=False): project = self.GetProject(debug) factory = InstanceTagName(self) @@ -795,17 +849,19 @@ etree.parse( os.path.join(ScriptDirectory, "plcopen", "instance_tagname.xslt"), parser), - extensions = {("instance_tagname_ns", name): getattr(factory, name) - for name in ["ConfigTagName", "ResourceTagName", - "PouTagName", "ActionTagName", - "TransitionTagName"]}) - - instance_tagname_xslt_tree(project, - instance_path=etree.XSLT.strparam(instance_path)) + extensions={("instance_tagname_ns", name): getattr(factory, name) + for name in ["ConfigTagName", + "ResourceTagName", + "PouTagName", + "ActionTagName", + "TransitionTagName"]}) + + instance_tagname_xslt_tree( + project, instance_path=etree.XSLT.strparam(instance_path)) return factory.GetTagName() - def GetInstanceInfos(self, instance_path, debug = False): + def GetInstanceInfos(self, instance_path, debug=False): tagname = self.GetPouInstanceTagName(instance_path) if tagname is not None: infos = self.GetPouVariables(tagname, debug) @@ -822,21 +878,21 @@ return None # Return if data type given by name is used by another data type or pou - def DataTypeIsUsed(self, name, debug = False): + def DataTypeIsUsed(self, name, debug=False): project = self.GetProject(debug) if project is not None: return len(self.GetInstanceList(project, name, debug)) > 0 return False # Return if pou given by name is used by another pou - def PouIsUsed(self, name, debug = False): + def PouIsUsed(self, name, debug=False): project = self.GetProject(debug) if project is not None: return len(self.GetInstanceList(project, name, debug)) > 0 return False # Return if pou given by name is directly or undirectly used by the reference pou - def PouIsUsedBy(self, name, reference, debug = False): + def PouIsUsedBy(self, name, reference, debug=False): pou_infos = self.GetPou(reference, debug) if pou_infos is not None: return len(self.GetInstanceList(pou_infos, name, debug)) > 0 @@ -890,9 +946,9 @@ row, col = next_row, next_col return infos -#------------------------------------------------------------------------------- -# Project Pous management functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Project Pous management functions + # ------------------------------------------------------------------------------- # Add a Data Type to Project def ProjectAddDataType(self, datatype_name=None): @@ -942,7 +998,7 @@ ''' try: new_pou, error = LoadPou(pou_xml) - except: + except Exception: error = "" if error is not None: return _("Couldn't paste non-POU object.") @@ -969,7 +1025,7 @@ # programs cannot be pasted as functions or function blocks if orig_type == 'functionBlock' and pou_type == 'function' or \ orig_type == 'program' and pou_type in ['function', 'functionBlock']: - msg = _('''{a1} "{a2}" can't be pasted as a {a3}.''').format(a1 = orig_type, a2 = name, a3 = pou_type) + msg = _('''{a1} "{a2}" can't be pasted as a {a3}.''').format(a1=orig_type, a2=name, a3=pou_type) return msg new_pou.setpouType(pou_type) @@ -1139,7 +1195,7 @@ self.BufferProject() # Return the description of the pou given by its name - def GetPouDescription(self, name, debug = False): + def GetPouDescription(self, name, debug=False): project = self.GetProject(debug) if project is not None: # Found the pou correponding to name and return its type @@ -1149,7 +1205,7 @@ return "" # Return the description of the pou given by its name - def SetPouDescription(self, name, description, debug = False): + def SetPouDescription(self, name, description, debug=False): project = self.GetProject(debug) if project is not None: # Found the pou correponding to name and return its type @@ -1159,7 +1215,7 @@ self.BufferProject() # Return the type of the pou given by its name - def GetPouType(self, name, debug = False): + def GetPouType(self, name, debug=False): project = self.GetProject(debug) if project is not None: # Found the pou correponding to name and return its type @@ -1169,7 +1225,7 @@ return None # Return pous with SFC language - def GetSFCPous(self, debug = False): + def GetSFCPous(self, debug=False): list = [] project = self.GetProject(debug) if project is not None: @@ -1179,7 +1235,7 @@ return list # Return the body language of the pou given by its name - def GetPouBodyType(self, name, debug = False): + def GetPouBodyType(self, name, debug=False): project = self.GetProject(debug) if project is not None: # Found the pou correponding to name and return its body language @@ -1189,7 +1245,7 @@ return None # Return the actions of a pou - def GetPouTransitions(self, pou_name, debug = False): + def GetPouTransitions(self, pou_name, debug=False): transitions = [] project = self.GetProject(debug) if project is not None: @@ -1201,7 +1257,7 @@ return transitions # Return the body language of the transition given by its name - def GetTransitionBodyType(self, pou_name, pou_transition, debug = False): + def GetTransitionBodyType(self, pou_name, pou_transition, debug=False): project = self.GetProject(debug) if project is not None: # Found the pou correponding to name @@ -1214,7 +1270,7 @@ return None # Return the actions of a pou - def GetPouActions(self, pou_name, debug = False): + def GetPouActions(self, pou_name, debug=False): actions = [] project = self.GetProject(debug) if project is not None: @@ -1226,7 +1282,7 @@ return actions # Return the body language of the pou given by its name - def GetActionBodyType(self, pou_name, pou_action, debug = False): + def GetActionBodyType(self, pou_name, pou_action, debug=False): project = self.GetProject(debug) if project is not None: # Found the pou correponding to name and return its body language @@ -1332,17 +1388,17 @@ etree.parse( os.path.join(ScriptDirectory, "plcopen", "variables_infos.xslt"), parser), - 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))) + 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))) return variables # Add a global var to configuration to configuration def AddConfigurationGlobalVar(self, config_name, var_type, var_name, - location="", description=""): + location="", description=""): if self.Project is not None: # Found the configuration corresponding to name configuration = self.Project.getconfiguration(config_name) @@ -1364,7 +1420,7 @@ in self.ExtractVarLists(vars)]) # Return the configuration globalvars - def GetConfigurationGlobalVars(self, name, debug = False): + def GetConfigurationGlobalVars(self, name, debug=False): project = self.GetProject(debug) if project is not None: # Found the configuration corresponding to name @@ -1376,7 +1432,7 @@ return [] # Return configuration variable names - def GetConfigurationVariableNames(self, config_name = None, debug = False): + def GetConfigurationVariableNames(self, config_name=None, debug=False): variables = [] project = self.GetProject(debug) if project is not None: @@ -1384,7 +1440,8 @@ if config_name is None or config_name == configuration.getname(): variables.extend( [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], [])]) return variables @@ -1401,7 +1458,7 @@ in self.ExtractVarLists(vars)]) # Return the resource globalvars - def GetConfigurationResourceGlobalVars(self, config_name, name, debug = False): + def GetConfigurationResourceGlobalVars(self, config_name, name, debug=False): project = self.GetProject(debug) if project is not None: # Found the resource corresponding to name @@ -1413,8 +1470,8 @@ return [] # Return resource variable names - def GetConfigurationResourceVariableNames(self, - config_name = None, resource_name = None, debug = False): + def GetConfigurationResourceVariableNames( + self, config_name=None, resource_name=None, debug=False): variables = [] project = self.GetProject(debug) if project is not None: @@ -1424,13 +1481,14 @@ if resource_name is None or resource.getname() == resource_name: variables.extend( [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], [])]) return variables # Return the interface for the given pou - def GetPouInterfaceVars(self, pou, tree=False, debug = False): + def GetPouInterfaceVars(self, pou, tree=False, debug=False): interface = pou.interface # Verify that the pou has an interface if interface is not None: @@ -1497,11 +1555,11 @@ etree.parse( os.path.join(ScriptDirectory, "plcopen", "variables_infos.xslt"), parser), - extensions = {("var_infos_ns", name): getattr(factory, name) - for name in ["SetType", "AddDimension", - "AddTree", "AddVarToTree"]}) - return_type_infos_xslt_tree(return_type, - tree=etree.XSLT.strparam(str(tree))) + extensions={("var_infos_ns", name): getattr(factory, name) + for name in ["SetType", "AddDimension", + "AddTree", "AddVarToTree"]}) + return_type_infos_xslt_tree( + return_type, tree=etree.XSLT.strparam(str(tree))) if tree: return [factory.GetType(), factory.GetTree()] return factory.GetType() @@ -1516,11 +1574,11 @@ addedcat = [{"name": _("%s POUs") % confnodetypes["name"], "list": [pou.getblockInfos() for pou in confnodetypes["types"].getpous()]} - for confnodetypes in typeslist] + for confnodetypes in typeslist] self.TotalTypes.extend(addedcat) for cat in addedcat: for desc in cat["list"]: - BlkLst = self.TotalTypesDict.setdefault(desc["name"],[]) + BlkLst = self.TotalTypesDict.setdefault(desc["name"], []) BlkLst.append((section["name"], desc)) # Function that clear the confnode list @@ -1529,7 +1587,7 @@ self.TotalTypesDict = StdBlckDct.copy() self.TotalTypes = StdBlckLst[:] - def GetConfNodeDataTypes(self, exclude = None, only_locatables = False): + def GetConfNodeDataTypes(self, exclude=None, only_locatables=False): return [{"name": _("%s Data Types") % confnodetypes["name"], "list": [ datatype.getname() @@ -1570,9 +1628,9 @@ return global_vars # Function that returns the block definition associated to the block type given - def GetBlockType(self, typename, inputs = None, debug = False): + def GetBlockType(self, typename, inputs=None, debug=False): result_blocktype = None - for sectioname, blocktype in self.TotalTypesDict.get(typename,[]): + for sectioname, blocktype in self.TotalTypesDict.get(typename, []): if inputs is not None and inputs != "undefined": block_inputs = tuple([var_type for name, var_type, modifier in blocktype["inputs"]]) if reduce(lambda x, y: x and y, map(lambda x: x[0] == "ANY" or self.IsOfType(*x), zip(inputs, block_inputs)), True): @@ -1597,20 +1655,20 @@ return blocktype_infos if inputs == tuple([var_type - for name, var_type, modifier in blocktype_infos["inputs"]]): + for name, var_type, modifier in blocktype_infos["inputs"]]): return blocktype_infos return None # Return Block types checking for recursion - def GetBlockTypes(self, tagname = "", debug = False): + def GetBlockTypes(self, tagname="", debug=False): typename = None words = tagname.split("::") name = None project = self.GetProject(debug) if project is not None: pou_type = None - if words[0] in ["P","T","A"]: + if words[0] in ["P", "T", "A"]: name = words[1] pou_type = self.GetPouType(name, debug) filter = (["function"] @@ -1621,35 +1679,38 @@ "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) if (name is None or - len(self.GetInstanceList(pou, name, debug)) == 0)]}) + len(self.GetInstanceList(pou, name, debug)) == 0)] + }) return blocktypes return self.TotalTypes # Return Function Block types checking for recursion - def GetFunctionBlockTypes(self, tagname = "", debug = False): + def GetFunctionBlockTypes(self, tagname="", debug=False): project = self.GetProject(debug) words = tagname.split("::") name = None - if project is not None and words[0] in ["P","T","A"]: + if project is not None and words[0] in ["P", "T", "A"]: name = words[1] blocktypes = [] for blocks in self.TotalTypesDict.itervalues(): - for sectioname,block in blocks: + for sectioname, block in blocks: if block["type"] == "functionBlock": blocktypes.append(block["name"]) if project is not None: - blocktypes.extend([pou.getname() + blocktypes.extend([ + pou.getname() for pou in project.getpous(name, ["functionBlock"]) if (name is None or len(self.GetInstanceList(pou, name, debug)) == 0)]) return blocktypes # Return Block types checking for recursion - def GetBlockResource(self, debug = False): + def GetBlockResource(self, debug=False): blocktypes = [] for category in StdBlckLst[:-1]: for blocktype in category["list"]: @@ -1663,7 +1724,7 @@ return blocktypes # Return Data Types checking for recursion - def GetDataTypes(self, tagname = "", basetypes = True, confnodetypes = True, only_locatables = False, debug = False): + def GetDataTypes(self, tagname="", basetypes=True, confnodetypes=True, only_locatables=False, debug=False): if basetypes: datatypes = self.GetBaseTypes() else: @@ -1677,16 +1738,16 @@ datatypes.extend([ datatype.getname() for datatype in project.getdataTypes(name) - if (not only_locatables or self.IsLocatableDataType(datatype, debug)) + if ((not only_locatables or self.IsLocatableDataType(datatype, debug)) and (name is None or - len(self.GetInstanceList(datatype, name, debug)) == 0)]) + len(self.GetInstanceList(datatype, name, debug)) == 0))]) if confnodetypes: for category in self.GetConfNodeDataTypes(name, only_locatables): datatypes.extend(category["list"]) return datatypes # Return Data Type Object - def GetPou(self, typename, debug = False): + def GetPou(self, typename, debug=False): project = self.GetProject(debug) if project is not None: result = project.getpou(typename) @@ -1702,9 +1763,8 @@ return result return None - # Return Data Type Object - def GetDataType(self, typename, debug = False): + def GetDataType(self, typename, debug=False): project = self.GetProject(debug) if project is not None: result = project.getdataType(typename) @@ -1730,8 +1790,8 @@ return None # Return Base Type of given possible derived type - def GetBaseType(self, typename, debug = False): - if TypeHierarchy.has_key(typename): + def GetBaseType(self, typename, debug=False): + if typename in TypeHierarchy: return typename datatype = self.GetDataType(typename, debug) @@ -1749,9 +1809,9 @@ TypeHierarchy_list has a rough order to it (e.g. SINT, INT, DINT, ...), which makes it easy for a user to find a type in a menu. ''' - return [x for x,y in TypeHierarchy_list if not x.startswith("ANY")] - - def IsOfType(self, typename, reference, debug = False): + return [x for x, y in TypeHierarchy_list if not x.startswith("ANY")] + + def IsOfType(self, typename, reference, debug=False): if reference is None or typename == reference: return True @@ -1772,7 +1832,7 @@ return not typename.startswith("ANY") return True - def IsLocatableDataType(self, datatype, debug = False): + def IsLocatableDataType(self, datatype, debug=False): basetype_content = datatype.baseType.getcontent() basetype_content_type = basetype_content.getLocalTag() if basetype_content_type in ["enum", "struct"]: @@ -1785,20 +1845,20 @@ return self.IsLocatableType(array_base_type.getname(), debug) return True - def IsLocatableType(self, typename, debug = False): + def IsLocatableType(self, typename, debug=False): if isinstance(typename, TupleType) or self.GetBlockType(typename) is not None: return False # the size of these types is implementation dependend if typename in ["TIME", "DATE", "DT", "TOD"]: return False - + datatype = self.GetDataType(typename, debug) if datatype is not None: return self.IsLocatableDataType(datatype) return True - def IsEnumeratedType(self, typename, debug = False): + def IsEnumeratedType(self, typename, debug=False): if isinstance(typename, TupleType): typename = typename[1] datatype = self.GetDataType(typename, debug) @@ -1810,7 +1870,7 @@ return basetype_content_type == "enum" return False - def IsSubrangeType(self, typename, exclude=None, debug = False): + def IsSubrangeType(self, typename, exclude=None, debug=False): if typename == exclude: return False if isinstance(typename, TupleType): @@ -1826,11 +1886,11 @@ self.GetDataTypeBaseType(datatype), exclude) return False - def IsNumType(self, typename, debug = False): + 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): + def GetDataTypeRange(self, typename, debug=False): range = DataTypeRange.get(typename) if range is not None: return range @@ -1846,7 +1906,7 @@ return None # Return Subrange types - def GetSubrangeBaseTypes(self, exclude, debug = False): + def GetSubrangeBaseTypes(self, exclude, debug=False): subrange_basetypes = DataTypeRange.keys() project = self.GetProject(debug) if project is not None: @@ -1860,7 +1920,7 @@ return subrange_basetypes # Return Enumerated Values - def GetEnumeratedDataValues(self, typename = None, debug = False): + def GetEnumeratedDataValues(self, typename=None, debug=False): values = [] if typename is not None: datatype_obj = self.GetDataType(typename, debug) @@ -1882,9 +1942,9 @@ values.extend(confnodetype["types"].GetEnumeratedDataTypeValues()) return values -#------------------------------------------------------------------------------- -# Project Element tag name computation functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Project Element tag name computation functions + # ------------------------------------------------------------------------------- # Compute a data type name def ComputeDataTypeName(self, datatype): @@ -1912,16 +1972,21 @@ def GetElementType(self, tagname): words = tagname.split("::") - return {"D" : ITEM_DATATYPE, "P" : ITEM_POU, - "T" : ITEM_TRANSITION, "A" : ITEM_ACTION, - "C" : ITEM_CONFIGURATION, "R" : ITEM_RESOURCE}[words[0]] - -#------------------------------------------------------------------------------- -# Project opened Data types management functions -#------------------------------------------------------------------------------- + return { + "D": ITEM_DATATYPE, + "P": ITEM_POU, + "T": ITEM_TRANSITION, + "A": ITEM_ACTION, + "C": ITEM_CONFIGURATION, + "R": ITEM_RESOURCE + }[words[0]] + + # ------------------------------------------------------------------------------- + # Project opened Data types management functions + # ------------------------------------------------------------------------------- # Return the data type informations - def GetDataTypeInfos(self, tagname, debug = False): + def GetDataTypeInfos(self, tagname, debug=False): project = self.GetProject(debug) if project is not None: words = tagname.split("::") @@ -1939,8 +2004,8 @@ base_type = basetype_content.baseType.getcontent() base_type_type = base_type.getLocalTag() infos["base_type"] = (base_type.getname() - if base_type_type == "derived" - else base_type_type) + if base_type_type == "derived" + else base_type_type) elif basetype_content_type == "enum": infos["type"] = "Enumerated" infos["values"] = [] @@ -1954,8 +2019,8 @@ base_type = basetype_content.baseType.getcontent() base_type_type = base_type.getLocalTag() infos["base_type"] = (base_type.getname() - if base_type_type == "derived" - else base_type_type.upper()) + if base_type_type == "derived" + else base_type_type.upper()) elif basetype_content_type == "struct": infos["type"] = "Structure" infos["elements"] = [] @@ -1971,9 +2036,10 @@ base_type = element_type.baseType.getcontent() base_type_type = base_type.getLocalTag() element_infos["Type"] = ("array", - base_type.getname() - if base_type_type == "derived" - else base_type_type.upper(), dimensions) + base_type.getname() + if base_type_type == "derived" + else base_type_type.upper(), + dimensions) elif element_type_type == "derived": element_infos["Type"] = element_type.getname() else: @@ -1986,8 +2052,8 @@ else: infos["type"] = "Directly" infos["base_type"] = (basetype_content.getname() - if basetype_content_type == "derived" - else basetype_content_type.upper()) + if basetype_content_type == "derived" + else basetype_content_type.upper()) if datatype.initialValue is not None: infos["initial"] = datatype.initialValue.getvalue() @@ -2116,12 +2182,12 @@ datatype.initialValue = None self.BufferProject() -#------------------------------------------------------------------------------- -# Project opened Pous management functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Project opened Pous management functions + # ------------------------------------------------------------------------------- # Return edited element - def GetEditedElement(self, tagname, debug = False): + def GetEditedElement(self, tagname, debug=False): project = self.GetProject(debug) if project is not None: words = tagname.split("::") @@ -2145,21 +2211,21 @@ # Return edited element name def GetEditedElementName(self, tagname): words = tagname.split("::") - if words[0] in ["P","C","D"]: + if words[0] in ["P", "C", "D"]: return words[1] else: return words[2] return None # Return edited element name and type - def GetEditedElementType(self, tagname, debug = False): + def GetEditedElementType(self, tagname, debug=False): words = tagname.split("::") - if words[0] in ["P","T","A"]: + if words[0] in ["P", "T", "A"]: return words[1], self.GetPouType(words[1], debug) return None, None # Return language in which edited element is written - def GetEditedElementBodyType(self, tagname, debug = False): + def GetEditedElementBodyType(self, tagname, debug=False): words = tagname.split("::") if words[0] == "P": return self.GetPouBodyType(words[1], debug) @@ -2170,9 +2236,9 @@ return None # Return the edited element variables - def GetEditedElementInterfaceVars(self, tagname, tree=False, debug = False): + def GetEditedElementInterfaceVars(self, tagname, tree=False, debug=False): words = tagname.split("::") - if words[0] in ["P","T","A"]: + if words[0] in ["P", "T", "A"]: project = self.GetProject(debug) if project is not None: pou = project.getpou(words[1]) @@ -2181,7 +2247,7 @@ return [] # Return the edited element return type - def GetEditedElementInterfaceReturnType(self, tagname, tree=False, debug = False): + def GetEditedElementInterfaceReturnType(self, tagname, tree=False, debug=False): words = tagname.split("::") if words[0] == "P": project = self.GetProject(debug) @@ -2201,14 +2267,14 @@ element.settext(text) # Return the edited element text - def GetEditedElementText(self, tagname, debug = False): + def GetEditedElementText(self, tagname, debug=False): element = self.GetEditedElement(tagname, debug) if element is not None: return element.gettext() return "" # Return the edited element transitions - def GetEditedElementTransitions(self, tagname, debug = False): + def GetEditedElementTransitions(self, tagname, debug=False): pou = self.GetEditedElement(tagname, debug) if pou is not None and pou.getbodyType() == "SFC": transitions = [] @@ -2218,7 +2284,7 @@ return [] # Return edited element transitions - def GetEditedElementActions(self, tagname, debug = False): + def GetEditedElementActions(self, tagname, debug=False): pou = self.GetEditedElement(tagname, debug) if pou is not None and pou.getbodyType() == "SFC": actions = [] @@ -2228,9 +2294,9 @@ return [] # Return the names of the pou elements - def GetEditedElementVariables(self, tagname, debug = False): + def GetEditedElementVariables(self, tagname, debug=False): words = tagname.split("::") - if words[0] in ["P","T","A"]: + if words[0] in ["P", "T", "A"]: return self.GetProjectPouVariableNames(words[1], debug) elif words[0] in ["C", "R"]: names = self.GetConfigurationVariableNames(words[1], debug) @@ -2240,13 +2306,13 @@ return names return [] - def GetEditedElementCopy(self, tagname, debug = False): + def GetEditedElementCopy(self, tagname, debug=False): element = self.GetEditedElement(tagname, debug) if element is not None: return element.tostring() return "" - def GetEditedElementInstancesCopy(self, tagname, blocks_id = None, wires = None, debug = False): + def GetEditedElementInstancesCopy(self, tagname, blocks_id=None, wires=None, debug=False): element = self.GetEditedElement(tagname, debug) text = "" if element is not None: @@ -2273,14 +2339,15 @@ names.update(dict([(varname.upper(), True) for varname in self.GetEditedElementVariables(tagname, debug)])) words = tagname.split("::") - if words[0] in ["P","T","A"]: + 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"), - PLCOpenParser.GetElementClass("continuation", "commonObjects"))): + if isinstance( + instance, + (PLCOpenParser.GetElementClass("step", "sfcObjects"), + PLCOpenParser.GetElementClass("connector", "commonObjects"), + PLCOpenParser.GetElementClass("continuation", "commonObjects"))): names[instance.getname().upper()] = True else: project = self.GetProject(debug) @@ -2302,7 +2369,7 @@ i = start_idx while name is None or names.get(name.upper(), False): - name = (format%i) + name = (format % i) i += 1 return name @@ -2325,7 +2392,7 @@ try: instances, error = LoadPouInstances(text, bodytype) - except: + except Exception: instances, error = [], "" if error is not None or len(instances) == 0: return _("Invalid plcopen element(s)!!!") @@ -2340,10 +2407,10 @@ blockname = instance.getinstanceName() 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 + return _("FunctionBlock \"%s\" can't be pasted in a Function!!!") % blocktype blockname = self.GenerateNewName(tagname, blockname, - "%s%%d"%blocktype, + "%s%%d" % blocktype, debug=debug) exclude[blockname] = True instance.setinstanceName(blockname) @@ -2357,7 +2424,7 @@ exclude[stepname] = True instance.setname(stepname) localid = instance.getlocalId() - if not used_id.has_key(localid): + if localid not in used_id: new_id[localid] = True idx = 1 @@ -2366,8 +2433,8 @@ for instance in instances: localId = instance.getlocalId() bbox.union(instance.getBoundingBox()) - if used_id.has_key(localId): - while used_id.has_key(idx) or new_id.has_key(idx): + if localId in used_id: + while (idx in used_id) or (idx in new_id): idx += 1 new_id[idx] = True instance.setlocalId(idx) @@ -2404,7 +2471,7 @@ return new_id, connections - def GetEditedElementInstancesInfos(self, tagname, debug = False): + def GetEditedElementInstancesInfos(self, tagname, debug=False): element_instances = OrderedDict() element = self.GetEditedElement(tagname, debug) if element is not None: @@ -2412,7 +2479,7 @@ pou_block_instances_xslt_tree = etree.XSLT( pou_block_instances_xslt, - extensions = { + extensions={ ("pou_block_instances_ns", name): getattr(factory, name) for name in ["AddBlockInstance", "SetSpecificValues", "AddInstanceConnection", "AddConnectionLink", @@ -2440,7 +2507,7 @@ result = wire.GetConnectedInfos(-1) else: result = wire.GetConnectedInfos(0) - if result != None: + if result is not None: refLocalId, formalParameter = result connections = connection.getconnections() if connections is None or len(connection.getconnections()) <= idx: @@ -2465,7 +2532,7 @@ var_type_obj.setcontent(derived_type) return var_type_obj - def AddEditedElementPouVar(self, tagname, var_type, name,**args): + def AddEditedElementPouVar(self, tagname, var_type, name, **args): if self.Project is not None: words = tagname.split("::") if words[0] in ['P', 'T', 'A']: @@ -2500,7 +2567,7 @@ if pou is not None: pou.removepouVar(type, name) - def AddEditedElementBlock(self, tagname, id, blocktype, blockname = None): + def AddEditedElementBlock(self, tagname, id, blocktype, blockname=None): element = self.GetEditedElement(tagname) if element is not None: block = PLCOpenParser.CreateElement("block", "fbdObjects") @@ -3053,7 +3120,7 @@ self.RemoveEditedElementPouVar(tagname, instance.gettypeName(), instance.getinstanceName()) element.removeinstance(id) - def GetEditedResourceVariables(self, tagname, debug = False): + def GetEditedResourceVariables(self, tagname, debug=False): varlist = [] words = tagname.split("::") for var in self.GetConfigurationGlobalVars(words[1], debug): @@ -3076,17 +3143,17 @@ new_task.setname(task["Name"]) if task["Triggering"] == "Interrupt": new_task.setsingle(task["Single"]) -## result = duration_model.match(task["Interval"]).groups() -## if reduce(lambda x, y: x or y != None, result): -## values = [] -## for value in result[:-1]: -## if value != None: -## values.append(int(value)) -## else: -## values.append(0) -## if result[-1] is not None: -## values.append(int(float(result[-1]) * 1000)) -## new_task.setinterval(datetime.time(*values)) +# result = duration_model.match(task["Interval"]).groups() +# if reduce(lambda x, y: x or y != None, result): +# values = [] +# for value in result[:-1]: +# if value != None: +# values.append(int(value)) +# else: +# values.append(0) +# if result[-1] is not None: +# values.append(int(float(result[-1]) * 1000)) +# new_task.setinterval(datetime.time(*values)) if task["Triggering"] == "Cyclic": new_task.setinterval(task["Interval"]) new_task.setpriority(int(task["Priority"])) @@ -3103,7 +3170,7 @@ new_instance.setname(instance["Name"]) new_instance.settypeName(instance["Type"]) - def GetEditedResourceInfos(self, tagname, debug = False): + def GetEditedResourceInfos(self, tagname, debug=False): resource = self.GetEditedElement(tagname, debug) if resource is not None: tasks = resource.gettask() @@ -3120,19 +3187,19 @@ new_task["Single"] = "" interval = task.getinterval() if interval is not None: -## text = "" -## if interval.hour != 0: -## text += "%dh"%interval.hour -## if interval.minute != 0: -## text += "%dm"%interval.minute -## if interval.second != 0: -## text += "%ds"%interval.second -## if interval.microsecond != 0: -## if interval.microsecond % 1000 != 0: -## text += "%.3fms"%(float(interval.microsecond) / 1000) -## else: -## text += "%dms"%(interval.microsecond / 1000) -## new_task["Interval"] = text + # text = "" + # if interval.hour != 0: + # text += "%dh"%interval.hour + # if interval.minute != 0: + # text += "%dm"%interval.minute + # if interval.second != 0: + # text += "%ds"%interval.second + # if interval.microsecond != 0: + # if interval.microsecond % 1000 != 0: + # text += "%.3fms"%(float(interval.microsecond) / 1000) + # else: + # text += "%dms"%(interval.microsecond / 1000) + # new_task["Interval"] = text new_task["Interval"] = interval else: new_task["Interval"] = "" @@ -3172,7 +3239,7 @@ self.CurrentElementEditing = None return error - def SaveXMLFile(self, filepath = None): + def SaveXMLFile(self, filepath=None): if not filepath and self.FilePath == "": return False else: @@ -3189,9 +3256,9 @@ self.SetFilePath(filepath) return True -#------------------------------------------------------------------------------- -# Search in Current Project Functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Search in Current Project Functions + # ------------------------------------------------------------------------------- def SearchInProject(self, criteria): return self.Project.Search(criteria) @@ -3209,14 +3276,12 @@ return search_results return [] -#------------------------------------------------------------------------------- -# Current Buffering Management Functions -#------------------------------------------------------------------------------- - - """ - Return a copy of the project - """ + # ------------------------------------------------------------------------------- + # Current Buffering Management Functions + # ------------------------------------------------------------------------------- + def Copy(self, model): + """Return a copy of the project""" return deepcopy(model) def CreateProjectBuffer(self, saved): diff -r 31e63e25b4cc -r 64beb9e9c749 PLCGenerator.py --- a/PLCGenerator.py Mon Aug 21 20:17:19 2017 +0000 +++ b/PLCGenerator.py Mon Aug 21 23:22:58 2017 +0300 @@ -29,14 +29,14 @@ # Dictionary associating PLCOpen variable categories to the corresponding # IEC 61131-3 variable categories -varTypeNames = {"localVars" : "VAR", "tempVars" : "VAR_TEMP", "inputVars" : "VAR_INPUT", - "outputVars" : "VAR_OUTPUT", "inOutVars" : "VAR_IN_OUT", "externalVars" : "VAR_EXTERNAL", - "globalVars" : "VAR_GLOBAL", "accessVars" : "VAR_ACCESS"} +varTypeNames = {"localVars": "VAR", "tempVars": "VAR_TEMP", "inputVars": "VAR_INPUT", + "outputVars": "VAR_OUTPUT", "inOutVars": "VAR_IN_OUT", "externalVars": "VAR_EXTERNAL", + "globalVars": "VAR_GLOBAL", "accessVars": "VAR_ACCESS"} # Dictionary associating PLCOpen POU categories to the corresponding # IEC 61131-3 POU categories -pouTypeNames = {"function" : "FUNCTION", "functionBlock" : "FUNCTION_BLOCK", "program" : "PROGRAM"} +pouTypeNames = {"function": "FUNCTION", "functionBlock": "FUNCTION_BLOCK", "program": "PROGRAM"} errorVarTypes = { @@ -45,8 +45,9 @@ "VAR_INOUT": "var_inout", } -# Helper function for reindenting text + def ReIndentText(text, nb_spaces): + """ Helper function for reindenting text """ compute = "" lines = text.splitlines() if len(lines) > 0: @@ -62,11 +63,12 @@ indent += " " for line in lines: if line != "": - compute += "%s%s\n"%(indent, line) + compute += "%s%s\n" % (indent, line) else: compute += "\n" return compute + def SortInstances(a, b): ax, ay = int(a.getx()), int(a.gety()) bx, by = int(b.getx()), int(b.gety()) @@ -75,25 +77,26 @@ else: return cmp(ay, by) -# Helper for emulate join on element list + def JoinList(separator, mylist): - if len(mylist) > 0 : + """ Helper for emulate join on element list """ + if len(mylist) > 0: return reduce(lambda x, y: x + separator + y, mylist) - else : + else: return mylist -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Specific exception for PLC generating errors -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- class PLCGenException(Exception): pass -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Generator of PLC program -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- class ProgramGenerator: @@ -114,9 +117,9 @@ def ComputeValue(self, value, var_type): base_type = self.Controler.GetBaseType(var_type) if base_type == "STRING" and not value.startswith("'") and not value.endswith("'"): - return "'%s'"%value + return "'%s'" % value elif base_type == "WSTRING" and not value.startswith('"') and not value.endswith('"'): - return "\"%s\""%value + return "\"%s\"" % value return value # Generate a data type from its name @@ -154,16 +157,16 @@ max_value = basetype_content.range.getupper() datatype_def += [(basetype_name, (tagname, "base")), (" (", ()), - ("%s"%min_value, (tagname, "lower")), + ("%s" % min_value, (tagname, "lower")), ("..", ()), - ("%s"%max_value, (tagname, "upper")), - (")",())] + ("%s" % max_value, (tagname, "upper")), + (")", ())] # Data type is an enumerated type elif basetype_content_type == "enum": values = [[(value.getname(), (tagname, "value", i))] for i, value in enumerate( basetype_content.xpath("ppx:values/ppx:value", - namespaces=PLCOpenParser.NSMAP))] + namespaces=PLCOpenParser.NSMAP))] datatype_def += [("(", ())] datatype_def += JoinList([(", ", ())], values) datatype_def += [(")", ())] @@ -178,13 +181,13 @@ # Array derived directly from an elementary type else: basetype_name = base_type_type.upper() - dimensions = [[("%s"%dimension.getlower(), (tagname, "range", i, "lower")), + dimensions = [[("%s" % dimension.getlower(), (tagname, "range", i, "lower")), ("..", ()), - ("%s"%dimension.getupper(), (tagname, "range", i, "upper"))] + ("%s" % dimension.getupper(), (tagname, "range", i, "upper"))] for i, dimension in enumerate(basetype_content.getdimension())] datatype_def += [("ARRAY [", ())] datatype_def += JoinList([(",", ())], dimensions) - datatype_def += [("] OF " , ()), + datatype_def += [("] OF ", ()), (basetype_name, (tagname, "base"))] # Data type is a structure elif basetype_content_type == "struct": @@ -245,18 +248,18 @@ pou = self.Project.getpou(pou_name) pou_type = pou.getpouType() # Verify that POU type exists - if pouTypeNames.has_key(pou_type): + if pou_type in pouTypeNames: # Create a POU program generator pou_program = PouProgramGenerator(self, pou.getname(), pouTypeNames[pou_type], self.Errors, self.Warnings) program = pou_program.GenerateProgram(pou) self.Program += program else: - raise PLCGenException, _("Undefined pou type \"%s\"")%pou_type + raise PLCGenException(_("Undefined pou type \"%s\"") % pou_type) # Generate a POU defined and used in text def GeneratePouProgramInText(self, text): for pou_name in self.PouComputed.keys(): - model = re.compile("(?:^|[^0-9^A-Z])%s(?:$|[^0-9^A-Z])"%pou_name.upper()) + model = re.compile("(?:^|[^0-9^A-Z])%s(?:$|[^0-9^A-Z])" % pou_name.upper()) if model.search(text) is not None: self.GeneratePouProgram(pou_name) @@ -394,11 +397,11 @@ # Single argument if exists if single is not None: if len(single) == 0: - msg = _("Source signal has to be defined for single task '{a1}' in resource '{a2}.{a3}'.").\ - format(a1 = task.getname(), a2 = config_name, a3 = resource.getname()) - raise PLCGenException, msg - - if single[0]=='[' and single[-1]==']' : + raise PLCGenException( + _("Source signal has to be defined for single task '{a1}' in resource '{a2}.{a3}'."). + format(a1=task.getname(), a2=config_name, a3=resource.getname())) + + if single[0] == '[' and single[-1] == ']': SNGLKW = "MULTI" else: SNGLKW = "SINGLE" @@ -411,19 +414,19 @@ resrce += [("INTERVAL := ", ()), (interval, (tagname, "task", task_number, "interval")), (",", ())] -## resrce += [("INTERVAL := t#", ())] -## if interval.hour != 0: -## resrce += [("%dh"%interval.hour, (tagname, "task", task_number, "interval", "hour"))] -## if interval.minute != 0: -## resrce += [("%dm"%interval.minute, (tagname, "task", task_number, "interval", "minute"))] -## if interval.second != 0: -## resrce += [("%ds"%interval.second, (tagname, "task", task_number, "interval", "second"))] -## if interval.microsecond != 0: -## resrce += [("%dms"%(interval.microsecond / 1000), (tagname, "task", task_number, "interval", "millisecond"))] -## resrce += [(",", ())] +# resrce += [("INTERVAL := t#", ())] +# if interval.hour != 0: +# resrce += [("%dh"%interval.hour, (tagname, "task", task_number, "interval", "hour"))] +# if interval.minute != 0: +# resrce += [("%dm"%interval.minute, (tagname, "task", task_number, "interval", "minute"))] +# if interval.second != 0: +# resrce += [("%ds"%interval.second, (tagname, "task", task_number, "interval", "second"))] +# if interval.microsecond != 0: +# resrce += [("%dms"%(interval.microsecond / 1000), (tagname, "task", task_number, "interval", "millisecond"))] +# resrce += [(",", ())] # Priority argument resrce += [("PRIORITY := ", ()), - ("%d"%task.getpriority(), (tagname, "task", task_number, "priority")), + ("%d" % task.getpriority(), (tagname, "task", task_number, "priority")), (");\n", ())] task_number += 1 instance_number = 0 @@ -441,10 +444,10 @@ # Generate any program assign to no task for instance in resource.getpouInstance(): resrce += [(" PROGRAM ", ()), - (instance.getname(), (tagname, "instance", instance_number, "name")), - (" : ", ()), - (instance.gettypeName(), (tagname, "instance", instance_number, "type")), - (";\n", ())] + (instance.getname(), (tagname, "instance", instance_number, "name")), + (" : ", ()), + (instance.gettypeName(), (tagname, "instance", instance_number, "type")), + (";\n", ())] instance_number += 1 resrce += [(" END_RESOURCE\n", ())] return resrce @@ -477,9 +480,9 @@ return self.Program -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Generator of POU programs -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- [ConnectorClass, ContinuationClass, ActionBlockClass] = [ PLCOpenParser.GetElementClass(instance_name, "commonObjects") @@ -494,12 +497,14 @@ SelectionConvergenceClass, SelectionDivergenceClass, SimultaneousConvergenceClass, SimultaneousDivergenceClass] = [ PLCOpenParser.GetElementClass(instance_name, "sfcObjects") - for instance_name in ["step", "transition", "jumpStep", - "selectionConvergence", "selectionDivergence", - "simultaneousConvergence", "simultaneousDivergence"]] + for instance_name in [ + "step", "transition", "jumpStep", + "selectionConvergence", "selectionDivergence", + "simultaneousConvergence", "simultaneousDivergence"]] TransitionObjClass = PLCOpenParser.GetElementClass("transition", "transitions") ActionObjClass = PLCOpenParser.GetElementClass("action", "actions") + class PouProgramGenerator: # Create a new POU program generator @@ -517,7 +522,7 @@ self.ComputedConnectors = {} self.ConnectionTypes = {} self.RelatedConnections = [] - self.SFCNetworks = {"Steps":{}, "Transitions":{}, "Actions":{}} + self.SFCNetworks = {"Steps": {}, "Transitions": {}, "Actions": {}} self.SFCComputedBlocks = [] self.ActionNumber = 0 self.Program = [] @@ -590,7 +595,7 @@ parameter = link.getformalParameter() instance = body.getcontentInstance(link.getrefLocalId()) if isinstance(instance, (InVariableClass, InOutVariableClass, - ContinuationClass, ContactClass, CoilClass)): + ContinuationClass, ContactClass, CoilClass)): return instance.connectionPointOut elif isinstance(instance, BlockClass): outputvariables = instance.outputVariables.getvariable() @@ -691,14 +696,15 @@ self.Interface.append((varTypeNames[varlist_type], option, True, located)) LITERAL_TYPES = { - "T": "TIME", - "D": "DATE", + "T": "TIME", + "D": "DATE", "TOD": "TIME_OF_DAY", - "DT": "DATE_AND_TIME", - "2": None, - "8": None, - "16": None, + "DT": "DATE_AND_TIME", + "2": None, + "8": None, + "16": None, } + def ComputeConnectionTypes(self, pou): body = pou.getbody() if isinstance(body, ListType): @@ -712,8 +718,7 @@ InOutVariableClass)): expression = instance.getexpression() var_type = self.GetVariableType(expression) - if (isinstance(pou, TransitionObjClass) - and expression == pou.getname()): + if isinstance(pou, TransitionObjClass) and expression == pou.getname(): var_type = "BOOL" elif (not isinstance(pou, (TransitionObjClass, ActionObjClass)) and pou.getpouType() == "function" and expression == pou.getname()): @@ -740,7 +745,7 @@ if isinstance(instance, (OutVariableClass, InOutVariableClass)): self.ConnectionTypes[instance.connectionPointIn] = var_type connected = self.GetConnectedConnector(instance.connectionPointIn, body) - if connected is not None and not self.ConnectionTypes.has_key(connected): + if connected is not None and connected not in self.ConnectionTypes: for related in self.ExtractRelatedConnections(connected): self.ConnectionTypes[related] = var_type elif isinstance(instance, (ContactClass, CoilClass)): @@ -749,7 +754,7 @@ self.ConnectionTypes[instance.connectionPointIn] = "BOOL" for link in instance.connectionPointIn.getconnections(): connected = self.GetLinkedConnector(link, body) - if connected is not None and not self.ConnectionTypes.has_key(connected): + if connected is not None and connected not in self.ConnectionTypes: for related in self.ExtractRelatedConnections(connected): self.ConnectionTypes[related] = "BOOL" elif isinstance(instance, LeftPowerRailClass): @@ -761,7 +766,7 @@ self.ConnectionTypes[connection] = "BOOL" for link in connection.getconnections(): connected = self.GetLinkedConnector(link, body) - if connected is not None and not self.ConnectionTypes.has_key(connected): + if connected is not None and connected not in self.ConnectionTypes: for related in self.ExtractRelatedConnections(connected): self.ConnectionTypes[related] = "BOOL" elif isinstance(instance, TransitionClass): @@ -770,10 +775,12 @@ self.ConnectionTypes[content["value"]] = "BOOL" connections = content["value"].getconnections() if not connections: - raise PLCGenException, _("SFC transition in POU \"%s\" must be connected.") % self.Name - for link in connections: + raise PLCGenException( + _("SFC transition in POU \"%s\" must be connected.") % self.Name) + + for link in connections: connected = self.GetLinkedConnector(link, body) - if connected is not None and not self.ConnectionTypes.has_key(connected): + if connected is not None and connected not in self.ConnectionTypes: for related in self.ExtractRelatedConnections(connected): self.ConnectionTypes[related] = "BOOL" elif isinstance(instance, ContinuationClass): @@ -783,8 +790,9 @@ for element in body.getcontentInstances(): if isinstance(element, ConnectorClass) and element.getname() == name: if connector is not None: - msg = _("More than one connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU").format(a1 = name, a2 = self.Name) - raise PLCGenException, msg + raise PLCGenException( + _("More than one connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU"). + format(a1=name, a2=self.Name)) connector = element if connector is not None: undefined = [instance.connectionPointOut, connector.connectionPointIn] @@ -793,7 +801,7 @@ undefined.append(connected) related = [] for connection in undefined: - if self.ConnectionTypes.has_key(connection): + if connection in self.ConnectionTypes: var_type = self.ConnectionTypes[connection] else: related.extend(self.ExtractRelatedConnections(connection)) @@ -803,8 +811,10 @@ for connection in related: self.ConnectionTypes[connection] = var_type else: - msg = _("No connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU").format(a1 = name, a2 = self.Name) - raise PLCGenException, msg + raise PLCGenException( + _("No connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU"). + format(a1=name, a2=self.Name)) + elif isinstance(instance, BlockClass): block_infos = self.GetBlockType(instance.gettypeName(), "undefined") if block_infos is not None: @@ -826,7 +836,8 @@ if block_infos is not None: self.ComputeBlockInputTypes(instance, block_infos, body) else: - raise PLCGenException, _("No informations found for \"%s\" block")%(instance.gettypeName()) + raise PLCGenException( + _("No informations found for \"%s\" block") % (instance.gettypeName())) if body_type == "SFC": previous_tagname = self.TagName for action in pou.getactionList(): @@ -848,10 +859,10 @@ for oname, otype, oqualifier in block_infos["outputs"]: if output_name == oname: if otype.startswith("ANY"): - if not undefined.has_key(otype): + if otype not in undefined: undefined[otype] = [] undefined[otype].append(variable.connectionPointOut) - elif not self.ConnectionTypes.has_key(variable.connectionPointOut): + elif variable.connectionPointOut not in self.ConnectionTypes: for connection in self.ExtractRelatedConnections(variable.connectionPointOut): self.ConnectionTypes[connection] = otype for variable in instance.inputVariables.getvariable(): @@ -864,14 +875,14 @@ if input_name == iname: connected = self.GetConnectedConnector(variable.connectionPointIn, body) if itype.startswith("ANY"): - if not undefined.has_key(itype): + if itype not in undefined: undefined[itype] = [] undefined[itype].append(variable.connectionPointIn) if connected is not None: undefined[itype].append(connected) else: self.ConnectionTypes[variable.connectionPointIn] = itype - if connected is not None and not self.ConnectionTypes.has_key(connected): + if connected is not None and connected not in self.ConnectionTypes: for connection in self.ExtractRelatedConnections(connected): self.ConnectionTypes[connection] = itype for var_type, connections in undefined.items(): @@ -894,7 +905,7 @@ body = body[0] body_content = body.getcontent() body_type = body_content.getLocalTag() - if body_type in ["IL","ST"]: + if body_type in ["IL", "ST"]: text = body_content.getanyText() self.ParentGenerator.GeneratePouProgramInText(text.upper()) self.Program = [(ReIndentText(text, len(self.CurrentIndent)), @@ -912,7 +923,7 @@ self.GenerateSFCJump(instance, pou) if len(self.InitialSteps) > 0 and len(self.SFCComputedBlocks) > 0: action_name = "COMPUTE_FUNCTION_BLOCKS" - action_infos = {"qualifier" : "S", "content" : action_name} + action_infos = {"qualifier": "S", "content": action_name} self.SFCNetworks["Steps"][self.InitialSteps[0]]["actions"].append(action_infos) self.SFCNetworks["Actions"][action_name] = (self.SFCComputedBlocks, ()) self.Program = [] @@ -920,7 +931,7 @@ for initialstep in self.InitialSteps: self.ComputeSFCStep(initialstep) else: - otherInstances = {"outVariables&coils" : [], "blocks" : [], "connectors" : []} + otherInstances = {"outVariables&coils": [], "blocks": [], "connectors": []} orderedInstances = [] for instance in body.getcontentInstances(): if isinstance(instance, (OutVariableClass, InOutVariableClass, BlockClass)): @@ -958,11 +969,13 @@ if block_infos is None: block_infos = self.GetBlockType(block_type) if block_infos is None: - raise PLCGenException, _("Undefined block type \"{a1}\" in \"{a2}\" POU").format(a1 = block_type, a2 = self.Name) + raise PLCGenException( + _("Undefined block type \"{a1}\" in \"{a2}\" POU"). + format(a1=block_type, a2=self.Name)) try: self.GenerateBlock(instance, block_infos, body, None) except ValueError, e: - raise PLCGenException, e.message + raise PLCGenException(e.message) elif isinstance(instance, ConnectorClass): connector = instance.getname() if self.ComputedConnectors.get(connector, None): @@ -1031,7 +1044,7 @@ [(input_name, None) for input_name in input_names]) for variable in input_variables: parameter = variable.getformalParameter() - if input_connected.has_key(parameter): + if parameter in input_connected: input_connected[parameter] = variable if input_connected["EN"] is None: input_connected.pop("EN") @@ -1053,7 +1066,7 @@ if connections is not None: if parameter != "EN": one_input_connected = True - if inout_variables.has_key(parameter): + if parameter in inout_variables: expression = self.ComputeExpression(body, connections, executionOrderId > 0, True) if expression is not None: inout_variables[parameter] = value @@ -1073,11 +1086,11 @@ if one_input_connected: for i, variable in enumerate(output_variables): parameter = variable.getformalParameter() - if not inout_variables.has_key(parameter) and parameter in output_names + ["", "ENO"]: + if parameter not in inout_variables and parameter in output_names + ["", "ENO"]: if variable.getformalParameter() == "": - variable_name = "%s%d"%(type, block.getlocalId()) + variable_name = "%s%d" % (type, block.getlocalId()) else: - variable_name = "%s%d_%s"%(type, block.getlocalId(), parameter) + variable_name = "%s%d_%s" % (type, block.getlocalId(), parameter) if self.Interface[-1][0] != "VAR" or self.Interface[-1][1] is not None or self.Interface[-1][2]: self.Interface.append(("VAR", None, False, [])) if variable.connectionPointOut in self.ConnectionTypes: @@ -1086,7 +1099,7 @@ self.Interface[-1][3].append(("ANY", variable_name, None, None)) if len(output_variables) > 1 and parameter not in ["", "OUT"]: vars.append([(parameter, (self.TagName, "block", block.getlocalId(), "output", i)), - (" => %s"%variable_name, ())]) + (" => %s" % variable_name, ())]) else: output_info = (self.TagName, "block", block.getlocalId(), "output", i) output_name = variable_name @@ -1098,7 +1111,7 @@ self.Program += JoinList([(", ", ())], vars) self.Program += [(");\n", ())] else: - msg = _("\"{a1}\" function cancelled in \"{a2}\" POU: No input connected").format(a1 = type, a2 = self.TagName.split("::")[-1]) + msg = _("\"{a1}\" function cancelled in \"{a2}\" POU: No input connected").format(a1=type, a2=self.TagName.split("::")[-1]) self.Warnings.append(msg) elif block_infos["type"] == "functionBlock": if not self.ComputedBlocks.get(block, False) and not order: @@ -1116,7 +1129,7 @@ input_info = (self.TagName, "block", block.getlocalId(), "input", input_idx) connections = variable.connectionPointIn.getconnections() if connections is not None: - expression = self.ComputeExpression(body, connections, executionOrderId > 0, inout_variables.has_key(parameter)) + expression = self.ComputeExpression(body, connections, executionOrderId > 0, parameter in inout_variables) if expression is not None: vars.append([(parameter, input_info), (" := ", ())] + self.ExtractModifier(variable, expression, input_info)) @@ -1145,9 +1158,9 @@ else: for i, variable in enumerate(output_variables): blockPointx, blockPointy = variable.connectionPointOut.getrelPositionXY() - if (connectionPoint is None or - block.getx() + blockPointx == connectionPoint.getx() and - block.gety() + blockPointy == connectionPoint.gety()): + if connectionPoint is None or \ + block.getx() + blockPointx == connectionPoint.getx() and \ + block.gety() + blockPointy == connectionPoint.gety(): output_variable = variable output_parameter = variable.getformalParameter() output_idx = i @@ -1155,21 +1168,21 @@ if output_variable is not None: if block_infos["type"] == "function": output_info = (self.TagName, "block", block.getlocalId(), "output", output_idx) - if inout_variables.has_key(output_parameter): + if output_parameter in inout_variables: output_value = inout_variables[output_parameter] else: if output_parameter == "": - output_name = "%s%d"%(type, block.getlocalId()) + output_name = "%s%d" % (type, block.getlocalId()) else: - output_name = "%s%d_%s"%(type, block.getlocalId(), output_parameter) + output_name = "%s%d_%s" % (type, block.getlocalId(), output_parameter) output_value = [(output_name, output_info)] return self.ExtractModifier(output_variable, output_value, output_info) if block_infos["type"] == "functionBlock": output_info = (self.TagName, "block", block.getlocalId(), "output", output_idx) - output_name = self.ExtractModifier(output_variable, [("%s.%s"%(name, output_parameter), output_info)], output_info) + output_name = self.ExtractModifier(output_variable, [("%s.%s" % (name, output_parameter), output_info)], output_info) if to_inout: - variable_name = "%s_%s"%(name, output_parameter) + variable_name = "%s_%s" % (name, output_parameter) if not self.IsAlreadyDefined(variable_name): if self.Interface[-1][0] != "VAR" or self.Interface[-1][1] is not None or self.Interface[-1][2]: self.Interface.append(("VAR", None, False, [])) @@ -1179,7 +1192,7 @@ else: self.Interface[-1][3].append(("ANY", variable_name, None, None)) self.Program += [(self.CurrentIndent, ()), - ("%s := "%variable_name, ())] + ("%s := " % variable_name, ())] self.Program += output_name self.Program += [(";\n", ())] return [(variable_name, ())] @@ -1188,14 +1201,14 @@ if output_parameter is None: output_parameter = "" if name: - blockname = "{a1}({a2})".format(a1 = name, a2 = type) + blockname = "{a1}({a2})".format(a1=name, a2=type) else: blockname = type - msg = _("No output {a1} variable found in block {a2} in POU {a3}. Connection must be broken").\ - format(a1 = output_parameter, a2 = blockname, a3 = self.Name) - raise ValueError, msg - - def GeneratePaths(self, connections, body, order = False, to_inout = False): + raise ValueError( + _("No output {a1} variable found in block {a2} in POU {a3}. Connection must be broken"). + format(a1=output_parameter, a2=blockname, a3=self.Name)) + + def GeneratePaths(self, connections, body, order=False, to_inout=False): paths = [] for connection in connections: localId = connection.getrefLocalId() @@ -1211,24 +1224,26 @@ if block_infos is None: block_infos = self.GetBlockType(block_type) if block_infos is None: - msg = _("Undefined block type \"{a1}\" in \"{a2}\" POU").format(a1 = block_type, a2 = self.Name) - raise PLCGenException, msg + raise PLCGenException( + _("Undefined block type \"{a1}\" in \"{a2}\" POU"). + format(a1=block_type, a2=self.Name)) try: paths.append(str(self.GenerateBlock(next, block_infos, body, connection, order, to_inout))) except ValueError, e: - raise PLCGenException, e.message + raise PLCGenException(e.message) elif isinstance(next, ContinuationClass): name = next.getname() computed_value = self.ComputedConnectors.get(name, None) - if computed_value != None: + if computed_value is not None: paths.append(str(computed_value)) else: connector = None for instance in body.getcontentInstances(): if isinstance(instance, ConnectorClass) and instance.getname() == name: if connector is not None: - msg = _("More than one connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU").format(a1 = name, a2 = self.Name) - raise PLCGenException, msg + raise PLCGenException( + _("More than one connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU"). + format(a1=name, a2=self.Name)) connector = instance if connector is not None: connections = connector.connectionPointIn.getconnections() @@ -1238,8 +1253,9 @@ self.ComputedConnectors[name] = expression paths.append(str(expression)) else: - msg = _("No connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU").format(a1 = name, a2 = self.Name) - raise PLCGenException, msg + raise PLCGenException( + _("No connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU"). + format(a1=name, a2=self.Name)) elif isinstance(next, ContactClass): contact_info = (self.TagName, "contact", next.getlocalId()) variable = str(self.ExtractModifier(next, [(next.getvariable(), contact_info + ("reference",))], contact_info)) @@ -1260,7 +1276,7 @@ paths.append(str(self.GeneratePaths(next.connectionPointIn.getconnections(), body, order))) return paths - def ComputePaths(self, paths, first = False): + def ComputePaths(self, paths, first=False): if type(paths) == TupleType: if None in paths: return [("TRUE", ())] @@ -1278,7 +1294,7 @@ else: return eval(paths) - def ComputeExpression(self, body, connections, order = False, to_inout = False): + def ComputeExpression(self, body, connections, order=False, to_inout=False): paths = self.GeneratePaths(connections, body, order, to_inout) if len(paths) == 0: return None @@ -1315,15 +1331,15 @@ if self.Interface[-1][0] != "VAR" or self.Interface[-1][1] is not None or self.Interface[-1][2]: self.Interface.append(("VAR", None, False, [])) i = 1 - name = "%s%d"%(edge, i) + name = "%s%d" % (edge, i) while self.IsAlreadyDefined(name): i += 1 - name = "%s%d"%(edge, i) + name = "%s%d" % (edge, i) self.Interface[-1][3].append((edge, name, None, None)) self.Program += [(self.CurrentIndent, ()), (name, var_info), ("(CLK := ", ())] self.Program += expression self.Program += [(");\n", ())] - return [("%s.Q"%name, var_info)] + return [("%s.Q" % name, var_info)] def ExtractDivergenceInput(self, divergence, pou): connectionPointIn = divergence.getconnectionPointIn() @@ -1354,10 +1370,10 @@ if step_name not in self.SFCNetworks["Steps"].keys(): if step.getinitialStep(): self.InitialSteps.append(step_name) - step_infos = {"id" : step.getlocalId(), - "initial" : step.getinitialStep(), - "transitions" : [], - "actions" : []} + step_infos = {"id": step.getlocalId(), + "initial": step.getinitialStep(), + "transitions": [], + "actions": []} self.SFCNetworks["Steps"][step_name] = step_infos if step.connectionPointIn is not None: instances = [] @@ -1389,8 +1405,10 @@ jump_target = jump.gettargetName() if not pou.hasstep(jump_target): pname = pou.getname() - msg = _("SFC jump in pou \"{a1}\" refers to non-existent SFC step \"{a2}\"").format( a1 = pname, a2 = jump_target) - raise PLCGenException, msg + raise PLCGenException( + _("SFC jump in pou \"{a1}\" refers to non-existent SFC step \"{a2}\""). + format(a1=pname, a2=jump_target)) + if jump.connectionPointIn is not None: instances = [] connections = jump.connectionPointIn.getconnections() @@ -1430,10 +1448,10 @@ if step_name in self.SFCNetworks["Steps"].keys(): actions = actionBlock.getactions() for i, action in enumerate(actions): - action_infos = {"id" : actionBlock.getlocalId(), - "qualifier" : action["qualifier"], - "content" : action["value"], - "num" : i} + action_infos = {"id": actionBlock.getlocalId(), + "qualifier": action["qualifier"], + "content": action["value"], + "num": i} if "duration" in action: action_infos["duration"] = action["duration"] if "indicator" in action: @@ -1441,9 +1459,12 @@ if action["type"] == "reference": self.GenerateSFCAction(action["value"], pou) else: - action_name = "%s_INLINE%d"%(step_name.upper(), self.GetActionNumber()) - self.SFCNetworks["Actions"][action_name] = ([(self.CurrentIndent, ()), - (action["value"], (self.TagName, "action_block", action_infos["id"], "action", i, "inline")), + action_name = "%s_INLINE%d" % (step_name.upper(), self.GetActionNumber()) + self.SFCNetworks["Actions"][action_name] = ([ + (self.CurrentIndent, ()), + (action["value"], ( + self.TagName, "action_block", action_infos["id"], + "action", i, "inline")), ("\n", ())], ()) action_infos["content"] = action_name self.SFCNetworks["Steps"][step_name]["actions"].append(action_infos) @@ -1480,15 +1501,15 @@ steps.extend(self.ExtractConvergenceInputs(step, pou)) elif isinstance(instance, SimultaneousConvergenceClass): steps.extend(self.ExtractConvergenceInputs(instance, pou)) - transition_infos = {"id" : transition.getlocalId(), + transition_infos = {"id": transition.getlocalId(), "priority": transition.getpriority(), - "from": [], - "to" : [], - "content": []} + "from": [], + "to": [], + "content": []} self.SFCNetworks["Transitions"][transition] = transition_infos transitionValues = transition.getconditionContent() if transitionValues["type"] == "inline": - transition_infos["content"] = [("\n%s:= "%self.CurrentIndent, ()), + transition_infos["content"] = [("\n%s:= " % self.CurrentIndent, ()), (transitionValues["value"], (self.TagName, "transition", transition.getlocalId(), "inline")), (";\n", ())] elif transitionValues["type"] == "reference": @@ -1505,17 +1526,19 @@ (ReIndentText(transitionBody.getcontent().getanyText(), len(self.CurrentIndent)), (self.TagName, "body", len(self.CurrentIndent)))] else: for instance in transitionBody.getcontentInstances(): - if isinstance(instance, OutVariableClass) and instance.getexpression() == transitionValues["value"]\ - or isinstance(instance, CoilClass) and instance.getvariable() == transitionValues["value"]: + if isinstance(instance, OutVariableClass) and instance.getexpression() == transitionValues["value"] or \ + isinstance(instance, CoilClass) and instance.getvariable() == transitionValues["value"]: connections = instance.connectionPointIn.getconnections() if connections is not None: expression = self.ComputeExpression(transitionBody, connections) if expression is not None: - transition_infos["content"] = [("\n%s:= "%self.CurrentIndent, ())] + expression + [(";\n", ())] + transition_infos["content"] = [("\n%s:= " % self.CurrentIndent, ())] + expression + [(";\n", ())] self.SFCComputedBlocks += self.Program self.Program = [] - if not transition_infos.has_key("content"): - raise PLCGenException, _("Transition \"%s\" body must contain an output variable or coil referring to its name") % transitionValues["value"] + if "content" not in transition_infos: + raise PLCGenException( + _("Transition \"%s\" body must contain an output variable or coil referring to its name") + % transitionValues["value"]) self.TagName = previous_tagname elif transitionValues["type"] == "connection": body = pou.getbody() @@ -1525,7 +1548,7 @@ if connections is not None: expression = self.ComputeExpression(body, connections) if expression is not None: - transition_infos["content"] = [("\n%s:= "%self.CurrentIndent, ())] + expression + [(";\n", ())] + transition_infos["content"] = [("\n%s:= " % self.CurrentIndent, ())] + expression + [(";\n", ())] self.SFCComputedBlocks += self.Program self.Program = [] for step in steps: @@ -1564,7 +1587,7 @@ (action_infos["indicator"], action_info + ("indicator",))] self.Program += [(");\n", ())] self.IndentLeft() - self.Program += [("%sEND_STEP\n\n"%self.CurrentIndent, ())] + self.Program += [("%sEND_STEP\n\n" % self.CurrentIndent, ())] for action in actions: self.ComputeSFCAction(action) for transition in step_infos["transitions"]: @@ -1573,19 +1596,19 @@ def ComputeSFCAction(self, action_name): if action_name in self.SFCNetworks["Actions"].keys(): action_content, action_info = self.SFCNetworks["Actions"].pop(action_name) - self.Program += [("%sACTION "%self.CurrentIndent, ()), + self.Program += [("%sACTION " % self.CurrentIndent, ()), (action_name, action_info), (":\n", ())] self.Program += action_content - self.Program += [("%sEND_ACTION\n\n"%self.CurrentIndent, ())] + self.Program += [("%sEND_ACTION\n\n" % self.CurrentIndent, ())] def ComputeSFCTransition(self, transition): if transition in self.SFCNetworks["Transitions"].keys(): transition_infos = self.SFCNetworks["Transitions"].pop(transition) - self.Program += [("%sTRANSITION"%self.CurrentIndent, ())] - if transition_infos["priority"] != None: + self.Program += [("%sTRANSITION" % self.CurrentIndent, ())] + if transition_infos["priority"] is not None: self.Program += [(" (PRIORITY := ", ()), - ("%d"%transition_infos["priority"], (self.TagName, "transition", transition_infos["id"], "priority")), + ("%d" % transition_infos["priority"], (self.TagName, "transition", transition_infos["id"], "priority")), (")", ())] self.Program += [(" FROM ", ())] if len(transition_infos["from"]) > 1: @@ -1595,9 +1618,9 @@ elif len(transition_infos["from"]) == 1: self.Program += transition_infos["from"][0] else: - msg = _("Transition with content \"{a1}\" not connected to a previous step in \"{a2}\" POU").\ - format(a1 = transition_infos["content"], a2 = self.Name) - raise PLCGenException, msg + raise PLCGenException( + _("Transition with content \"{a1}\" not connected to a previous step in \"{a2}\" POU"). + format(a1=transition_infos["content"], a2=self.Name)) self.Program += [(" TO ", ())] if len(transition_infos["to"]) > 1: self.Program += [("(", ())] @@ -1606,11 +1629,11 @@ elif len(transition_infos["to"]) == 1: self.Program += transition_infos["to"][0] else: - msg = _("Transition with content \"{a1}\" not connected to a next step in \"{a2}\" POU").\ - format(a1 = transition_infos["content"], a2 = self.Name) - raise PLCGenException, msg + raise PLCGenException( + _("Transition with content \"{a1}\" not connected to a next step in \"{a2}\" POU"). + format(a1=transition_infos["content"], a2=self.Name)) self.Program += transition_infos["content"] - self.Program += [("%sEND_TRANSITION\n\n"%self.CurrentIndent, ())] + self.Program += [("%sEND_TRANSITION\n\n" % self.CurrentIndent, ())] for [(step_name, step_infos)] in transition_infos["to"]: self.ComputeSFCStep(step_name) @@ -1619,35 +1642,35 @@ self.ComputeConnectionTypes(pou) self.ComputeProgram(pou) - program = [("%s "%self.Type, ()), + program = [("%s " % self.Type, ()), (self.Name, (self.TagName, "name"))] if self.ReturnType is not None: program += [(" : ", ()), (self.ReturnType, (self.TagName, "return"))] program += [("\n", ())] if len(self.Interface) == 0: - raise PLCGenException, _("No variable defined in \"%s\" POU")%self.Name - if len(self.Program) == 0 : - raise PLCGenException, _("No body defined in \"%s\" POU")%self.Name + raise PLCGenException(_("No variable defined in \"%s\" POU") % self.Name) + if len(self.Program) == 0: + raise PLCGenException(_("No body defined in \"%s\" POU") % self.Name) var_number = 0 for list_type, option, located, variables in self.Interface: variable_type = errorVarTypes.get(list_type, "var_local") - program += [(" %s"%list_type, ())] + program += [(" %s" % list_type, ())] if option is not None: - program += [(" %s"%option, (self.TagName, variable_type, (var_number, var_number + len(variables)), option.lower()))] + program += [(" %s" % option, (self.TagName, variable_type, (var_number, var_number + len(variables)), option.lower()))] program += [("\n", ())] for var_type, var_name, var_address, var_initial in variables: program += [(" ", ())] if var_name: program += [(var_name, (self.TagName, variable_type, var_number, "name")), (" ", ())] - if var_address != None: + if var_address is not None: program += [("AT ", ()), (var_address, (self.TagName, variable_type, var_number, "location")), (" ", ())] program += [(": ", ()), (var_type, (self.TagName, variable_type, var_number, "type"))] - if var_initial != None: + if var_initial is not None: program += [(" := ", ()), (self.ParentGenerator.ComputeValue(var_initial, var_type), (self.TagName, variable_type, var_number, "initial value"))] program += [(";\n", ())] @@ -1655,11 +1678,11 @@ program += [(" END_VAR\n", ())] program += [("\n", ())] program += self.Program - program += [("END_%s\n\n"%self.Type, ())] + program += [("END_%s\n\n" % self.Type, ())] return program + def GenerateCurrentProgram(controler, project, errors, warnings): generator = ProgramGenerator(controler, project, errors, warnings) generator.GenerateProgram() return generator.GetGeneratedProgram() - diff -r 31e63e25b4cc -r 64beb9e9c749 PLCOpenEditor.py --- a/PLCOpenEditor.py Mon Aug 21 20:17:19 2017 +0000 +++ b/PLCOpenEditor.py Mon Aug 21 23:22:58 2017 +0300 @@ -24,10 +24,35 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. import wx -import os, sys, platform, time, traceback, getopt +import os +import sys +import platform +import time +import traceback +import getopt import version import util.paths as paths +from docutil import * +from IDEFrame import IDEFrame, AppendMenu +from IDEFrame import \ + TITLE, \ + EDITORTOOLBAR, \ + FILEMENU, \ + EDITMENU, \ + DISPLAYMENU, \ + PROJECTTREE, \ + POUINSTANCEVARIABLESPANEL, \ + LIBRARYTREE, \ + PAGETITLES + +from IDEFrame import EncodeFileSystemPath, DecodeFileSystemPath +from editors.Viewer import Viewer +from PLCControler import PLCControler +from dialogs import ProjectDialog +from dialogs.AboutDialog import ShowAboutDialog + + beremiz_dir = paths.AbsDir(__file__) __version__ = "$Revision: 1.130 $" @@ -37,7 +62,7 @@ # command line def usage(): print "\nUsage of PLCOpenEditor.py :" - print "\n %s [Filepath]\n"%sys.argv[0] + print "\n %s [Filepath]\n" % sys.argv[0] # Parse options given to PLCOpenEditor in command line try: @@ -68,32 +93,25 @@ else: app = wx.PySimpleApp() - from util.misc import InstallLocalRessources InstallLocalRessources(beremiz_dir) -from docutil import * -from IDEFrame import IDEFrame, AppendMenu -from IDEFrame import TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE, PAGETITLES -from IDEFrame import EncodeFileSystemPath, DecodeFileSystemPath -from editors.Viewer import Viewer -from PLCControler import PLCControler -from dialogs import ProjectDialog -from dialogs.AboutDialog import ShowAboutDialog - -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # PLCOpenEditor Main Class -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Define PLCOpenEditor FileMenu extra items id -[ID_PLCOPENEDITORFILEMENUGENERATE, +[ + ID_PLCOPENEDITORFILEMENUGENERATE, ] = [wx.NewId() for _init_coll_FileMenu_Items in range(1)] + class PLCOpenEditor(IDEFrame): # Compatibility function for wx versions < 2.6 if wx.VERSION < (2, 6, 0): - def Bind(self, event, function, id = None): + def Bind(self, event, function, id=None): if id is not None: event(self, id, function) else: @@ -101,33 +119,33 @@ def _init_coll_FileMenu_Items(self, parent): AppendMenu(parent, help='', id=wx.ID_NEW, - kind=wx.ITEM_NORMAL, text=_(u'New') +'\tCTRL+N') + kind=wx.ITEM_NORMAL, text=_(u'New') + '\tCTRL+N') AppendMenu(parent, help='', id=wx.ID_OPEN, - kind=wx.ITEM_NORMAL, text=_(u'Open') + '\tCTRL+O') + kind=wx.ITEM_NORMAL, text=_(u'Open') + '\tCTRL+O') AppendMenu(parent, help='', id=wx.ID_CLOSE, - kind=wx.ITEM_NORMAL, text=_(u'Close Tab') + '\tCTRL+W') + kind=wx.ITEM_NORMAL, text=_(u'Close Tab') + '\tCTRL+W') AppendMenu(parent, help='', id=wx.ID_CLOSE_ALL, - kind=wx.ITEM_NORMAL, text=_(u'Close Project') + '\tCTRL+SHIFT+W') + kind=wx.ITEM_NORMAL, text=_(u'Close Project') + '\tCTRL+SHIFT+W') parent.AppendSeparator() AppendMenu(parent, help='', id=wx.ID_SAVE, - kind=wx.ITEM_NORMAL, text=_(u'Save') + '\tCTRL+S') + kind=wx.ITEM_NORMAL, text=_(u'Save') + '\tCTRL+S') AppendMenu(parent, help='', id=wx.ID_SAVEAS, - kind=wx.ITEM_NORMAL, text=_(u'Save As...') + '\tCTRL+SHIFT+S') + kind=wx.ITEM_NORMAL, text=_(u'Save As...') + '\tCTRL+SHIFT+S') AppendMenu(parent, help='', id=ID_PLCOPENEDITORFILEMENUGENERATE, - kind=wx.ITEM_NORMAL, text=_(u'Generate Program') + '\tCTRL+G') + kind=wx.ITEM_NORMAL, text=_(u'Generate Program') + '\tCTRL+G') parent.AppendSeparator() AppendMenu(parent, help='', id=wx.ID_PAGE_SETUP, - kind=wx.ITEM_NORMAL, text=_(u'Page Setup') + '\tCTRL+ALT+P') + kind=wx.ITEM_NORMAL, text=_(u'Page Setup') + '\tCTRL+ALT+P') AppendMenu(parent, help='', id=wx.ID_PREVIEW, - kind=wx.ITEM_NORMAL, text=_(u'Preview') + '\tCTRL+SHIFT+P') + kind=wx.ITEM_NORMAL, text=_(u'Preview') + '\tCTRL+SHIFT+P') AppendMenu(parent, help='', id=wx.ID_PRINT, - kind=wx.ITEM_NORMAL, text=_(u'Print') + '\tCTRL+P') + kind=wx.ITEM_NORMAL, text=_(u'Print') + '\tCTRL+P') parent.AppendSeparator() AppendMenu(parent, help='', id=wx.ID_PROPERTIES, - kind=wx.ITEM_NORMAL, text=_(u'&Properties')) + kind=wx.ITEM_NORMAL, text=_(u'&Properties')) parent.AppendSeparator() AppendMenu(parent, help='', id=wx.ID_EXIT, - kind=wx.ITEM_NORMAL, text=_(u'Quit') + '\tCTRL+Q') + 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) @@ -136,7 +154,7 @@ self.Bind(wx.EVT_MENU, self.OnSaveProjectMenu, id=wx.ID_SAVE) self.Bind(wx.EVT_MENU, self.OnSaveProjectAsMenu, id=wx.ID_SAVEAS) self.Bind(wx.EVT_MENU, self.OnGenerateProgramMenu, - id=ID_PLCOPENEDITORFILEMENUGENERATE) + id=ID_PLCOPENEDITORFILEMENUGENERATE) self.Bind(wx.EVT_MENU, self.OnPageSetupMenu, id=wx.ID_PAGE_SETUP) self.Bind(wx.EVT_MENU, self.OnPreviewMenu, id=wx.ID_PREVIEW) self.Bind(wx.EVT_MENU, self.OnPrintMenu, id=wx.ID_PRINT) @@ -151,31 +169,34 @@ def _init_coll_HelpMenu_Items(self, parent): AppendMenu(parent, help='', id=wx.ID_HELP, - kind=wx.ITEM_NORMAL, text=_(u'PLCOpenEditor') + '\tF1') - #AppendMenu(parent, help='', id=wx.ID_HELP_CONTENTS, + kind=wx.ITEM_NORMAL, text=_(u'PLCOpenEditor') + '\tF1') + # AppendMenu(parent, help='', id=wx.ID_HELP_CONTENTS, # kind=wx.ITEM_NORMAL, text=u'PLCOpen\tF2') - #AppendMenu(parent, help='', id=wx.ID_HELP_CONTEXT, + # AppendMenu(parent, help='', id=wx.ID_HELP_CONTEXT, # kind=wx.ITEM_NORMAL, text=u'IEC 61131-3\tF3') - - handler=lambda event: { - wx.MessageBox(version.GetCommunityHelpMsg(), _(u'Community support'), wx.OK | wx.ICON_INFORMATION) - } + + def handler(event): + return wx.MessageBox( + version.GetCommunityHelpMsg(), + _(u'Community support'), + wx.OK | wx.ICON_INFORMATION) + id = wx.NewId() - parent.Append(help='', id=id, kind=wx.ITEM_NORMAL, text=_(u'Community support')) + parent.Append(help='', id=id, kind=wx.ITEM_NORMAL, text=_(u'Community support')) self.Bind(wx.EVT_MENU, handler, id=id) - + AppendMenu(parent, help='', id=wx.ID_ABOUT, - kind=wx.ITEM_NORMAL, text=_(u'About')) + kind=wx.ITEM_NORMAL, text=_(u'About')) self.Bind(wx.EVT_MENU, self.OnPLCOpenEditorMenu, id=wx.ID_HELP) - #self.Bind(wx.EVT_MENU, self.OnPLCOpenMenu, id=wx.ID_HELP_CONTENTS) + # self.Bind(wx.EVT_MENU, self.OnPLCOpenMenu, id=wx.ID_HELP_CONTENTS) self.Bind(wx.EVT_MENU, self.OnAboutMenu, id=wx.ID_ABOUT) - ## Constructor of the PLCOpenEditor class. - # @param parent The parent window. - # @param controler The controler been used by PLCOpenEditor (default: None). - # @param fileOpen The filepath to open if no controler defined (default: None). - # @param debug The filepath to open if no controler defined (default: False). - def __init__(self, parent, fileOpen = None): + def __init__(self, parent, fileOpen=None): + """ Constructor of the PLCOpenEditor class. + + :param parent: The parent window. + :param fileOpen: The filepath to open if no controler defined (default: None). + """ self.icon = wx.Icon(os.path.join(beremiz_dir, "images", "poe.ico"), wx.BITMAP_TYPE_ICO) IDEFrame.__init__(self, parent) @@ -203,7 +224,7 @@ if result is not None: (num, line) = result - self.ShowErrorMessage(_("PLC syntax error at line {a1}:\n{a2}").format(a1 = num, a2 = line)) + self.ShowErrorMessage(_("PLC syntax error at line {a1}:\n{a2}").format(a1=num, a2=line)) def OnCloseFrame(self, event): if self.Controler is None or self.CheckSaveBeforeClosing(_("Close Application")): @@ -218,13 +239,13 @@ def RefreshTitle(self): name = _("PLCOpenEditor") if self.Controler is not None: - self.SetTitle("%s - %s"%(name, self.Controler.GetFilename())) + self.SetTitle("%s - %s" % (name, self.Controler.GetFilename())) else: self.SetTitle(name) -#------------------------------------------------------------------------------- -# File Menu Functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # File Menu Functions + # ------------------------------------------------------------------------------- def RefreshFileMenu(self): MenuToolBar = self.Panes["MenuToolBar"] @@ -316,7 +337,7 @@ if result is not None: (num, line) = result - self.ShowErrorMessage(_("PLC syntax error at line {a1}:\n{a2}").format(a1 = num, a2 = line)) + self.ShowErrorMessage(_("PLC syntax error at line {a1}:\n{a2}").format(a1=num, a2=line)) def OnCloseProjectMenu(self, event): if not self.CheckSaveBeforeClosing(): @@ -331,7 +352,7 @@ self.SaveProjectAs() def OnGenerateProgramMenu(self, event): - dialog = wx.FileDialog(self, _("Choose a file"), os.getcwd(), self.Controler.GetProgramFilePath(), _("ST files (*.st)|*.st|All files|*.*"), wx.SAVE|wx.CHANGE_DIR) + dialog = wx.FileDialog(self, _("Choose a file"), os.getcwd(), self.Controler.GetProgramFilePath(), _("ST files (*.st)|*.st|All files|*.*"), wx.SAVE | wx.CHANGE_DIR) if dialog.ShowModal() == wx.ID_OK: filepath = dialog.GetPath() message_text = "" @@ -341,14 +362,14 @@ message_text += "".join([_("warning: %s\n") % warning for warning in warnings]) if len(errors) > 0: message_text += "".join([_("error: %s\n") % error for error in errors]) - message_text += _("Can't generate program to file %s!")%filepath + message_text += _("Can't generate program to file %s!") % filepath header, icon = _("Error"), wx.ICON_ERROR else: message_text += _("Program was successfully generated!") else: - message_text += _("\"%s\" is not a valid folder!")%os.path.dirname(filepath) + message_text += _("\"%s\" is not a valid folder!") % os.path.dirname(filepath) header, icon = _("Error"), wx.ICON_ERROR - message = wx.MessageDialog(self, message_text, header, wx.OK|icon) + message = wx.MessageDialog(self, message_text, header, wx.OK | icon) message.ShowModal() message.Destroy() dialog.Destroy() @@ -379,28 +400,30 @@ if filepath != "": directory, filename = os.path.split(filepath) else: - directory, filename = os.getcwd(), "%(projectName)s.xml"%self.Controler.GetProjectProperties() - dialog = wx.FileDialog(self, _("Choose a file"), directory, filename, _("PLCOpen files (*.xml)|*.xml|All files|*.*"), wx.SAVE|wx.OVERWRITE_PROMPT) + directory, filename = os.getcwd(), "%(projectName)s.xml" % self.Controler.GetProjectProperties() + dialog = wx.FileDialog(self, _("Choose a file"), directory, filename, _("PLCOpen files (*.xml)|*.xml|All files|*.*"), wx.SAVE | wx.OVERWRITE_PROMPT) if dialog.ShowModal() == wx.ID_OK: filepath = dialog.GetPath() if os.path.isdir(os.path.dirname(filepath)): result = self.Controler.SaveXMLFile(filepath) if not result: - self.ShowErrorMessage(_("Can't save project to file %s!")%filepath) + self.ShowErrorMessage(_("Can't save project to file %s!") % filepath) else: - self.ShowErrorMessage(_("\"%s\" is not a valid folder!")%os.path.dirname(filepath)) + self.ShowErrorMessage(_("\"%s\" is not a valid folder!") % os.path.dirname(filepath)) self._Refresh(TITLE, FILEMENU, PAGETITLES) dialog.Destroy() -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Exception Handler -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + Max_Traceback_List_Size = 20 -def Display_Exception_Dialog(e_type,e_value,e_tb): + +def Display_Exception_Dialog(e_type, e_value, e_tb): trcbck_lst = [] - for i,line in enumerate(traceback.extract_tb(e_tb)): + for i, line in enumerate(traceback.extract_tb(e_tb)): trcbck = " " + str(i+1) + _(". ") if line[0].find(os.getcwd()) == -1: trcbck += _("file : ") + str(line[0]) + _(", ") @@ -414,7 +437,8 @@ if cap: cap.ReleaseMouse() - dlg = wx.SingleChoiceDialog(None, + dlg = wx.SingleChoiceDialog( + None, _(""" An unhandled exception (bug) occured. Bug report saved at : (%s) @@ -436,11 +460,13 @@ return res + def Display_Error_Dialog(e_value): - message = wx.MessageDialog(None, str(e_value), _("Error"), wx.OK|wx.ICON_ERROR) + message = wx.MessageDialog(None, str(e_value), _("Error"), wx.OK | wx.ICON_ERROR) message.ShowModal() message.Destroy() + def get_last_traceback(tb): while tb.tb_next: tb = tb.tb_next @@ -451,49 +477,51 @@ return '\n'.join(['%s%s: %s' % (indent, k, repr(v)[:10000]) for k, v in d.iteritems()]) -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=[]): +ignored_exceptions = [] # a problem with a line in a module is only reported once per session + + +def AddExceptHook(path, app_version='[No version]'): 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 + 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) ex = (last_tb.tb_frame.f_code.co_filename, last_tb.tb_frame.f_lineno) if str(e_value).startswith("!!!"): Display_Error_Dialog(e_value) elif ex not in ignored_exceptions: - result = Display_Exception_Dialog(e_type,e_value,e_traceback) + result = Display_Exception_Dialog(e_type, e_value, e_traceback) if result: ignored_exceptions.append(ex) info = { - 'app-title' : wx.GetApp().GetAppName(), # app_title - 'app-version' : app_version, - 'wx-version' : wx.VERSION_STRING, - 'wx-platform' : wx.Platform, - 'python-version' : platform.python_version(), #sys.version.split()[0], - 'platform' : platform.platform(), - 'e-type' : e_type, - 'e-value' : e_value, - 'date' : time.ctime(), - 'cwd' : os.getcwd(), + 'app-title': wx.GetApp().GetAppName(), + 'app-version': app_version, + 'wx-version': wx.VERSION_STRING, + 'wx-platform': wx.Platform, + 'python-version': platform.python_version(), + 'platform': platform.platform(), + 'e-type': e_type, + 'e-value': e_value, + 'date': time.ctime(), + 'cwd': os.getcwd(), } if e_traceback: info['traceback'] = ''.join(traceback.format_tb(e_traceback)) + '%s: %s' % (e_type, e_value) last_tb = get_last_traceback(e_traceback) - exception_locals = last_tb.tb_frame.f_locals # the locals at the level of the stack trace where the exception actually occurred + exception_locals = last_tb.tb_frame.f_locals # the locals at the level of the stack trace where the exception actually occurred info['locals'] = format_namespace(exception_locals) if 'self' in exception_locals: info['self'] = format_namespace(exception_locals['self'].__dict__) - output = open(path+os.sep+"bug_report_"+time.strftime("%Y_%m_%d__%H-%M-%S")+".txt",'w') + output = open(path+os.sep+"bug_report_"+time.strftime("%Y_%m_%d__%H-%M-%S")+".txt", 'w') lst = info.keys() lst.sort() for a in lst: output.write(a+":\n"+str(info[a])+"\n\n") - #sys.excepthook = lambda *args: wx.CallAfter(handle_exception, *args) + # sys.excepthook = lambda *args: wx.CallAfter(handle_exception, *args) sys.excepthook = handle_exception + if __name__ == '__main__': if wx.VERSION < (3, 0, 0): wx.InitAllImageHandlers() @@ -505,4 +533,3 @@ frame.Show() app.MainLoop() - diff -r 31e63e25b4cc -r 64beb9e9c749 POULibrary.py --- a/POULibrary.py Mon Aug 21 20:17:19 2017 +0000 +++ b/POULibrary.py Mon Aug 21 23:22:58 2017 +0300 @@ -24,6 +24,7 @@ from weakref import ref + class POULibrary: def __init__(self, CTR, LibName, TypeStack): from PLCControler import PLCControler @@ -33,21 +34,21 @@ self.LibraryControler.OpenXMLFile(self.GetLibraryPath()) self.LibraryControler.ClearConfNodeTypes() self.LibraryControler.AddConfNodeTypesList(TypeStack) - self.program = None; + self.program = None def GetSTCode(self): if not self.program: self.program = self.LibraryControler.GenerateProgram()[0]+"\n" - return self.program + return self.program def GetName(self): return self.LibName def GetCTR(self): return self.CTR() - + def GetTypes(self): - return {"name" : self.GetName(), "types": self.LibraryControler.Project} + return {"name": self.GetName(), "types": self.LibraryControler.Project} def GetLibraryPath(self): raise Exception("Not implemented") diff -r 31e63e25b4cc -r 64beb9e9c749 ProjectController.py --- a/ProjectController.py Mon Aug 21 20:17:19 2017 +0000 +++ b/ProjectController.py Mon Aug 21 23:22:58 2017 +0300 @@ -26,12 +26,15 @@ """ Beremiz Project Controller """ -import os,sys,traceback +import os +import sys +import traceback import time import features import shutil import wx -import re, tempfile +import re +import tempfile from math import ceil from types import ListType from threading import Timer, Lock, Thread @@ -64,18 +67,20 @@ ITEM_CONFNODE = 25 + def ExtractChildrenTypesFromCatalog(catalog): children_types = [] - for n,d,h,c in catalog: + for n, d, h, c in catalog: if isinstance(c, ListType): children_types.extend(ExtractChildrenTypesFromCatalog(c)) else: children_types.append((n, GetClassImporter(c), d)) return children_types + def ExtractMenuItemsFromCatalog(catalog): menu_items = [] - for n,d,h,c in catalog: + for n, d, h, c in catalog: if isinstance(c, ListType): children = ExtractMenuItemsFromCatalog(c) else: @@ -83,63 +88,66 @@ menu_items.append((n, d, h, children)) return menu_items + def GetAddMenuItems(): return ExtractMenuItemsFromCatalog(features.catalog) + class Iec2CSettings(): def __init__(self): self.iec2c = None self.iec2c_buildopts = None - self.ieclib_path = self.findLibPath() + self.ieclib_path = self.findLibPath() self.ieclib_c_path = self.findLibCPath() def findObject(self, paths, test): - path=None + path = None for p in paths: if test(p): path = p - break + break return path - + def findCmd(self): - cmd="iec2c"+(".exe" if wx.Platform == '__WXMSW__' else "") - paths=[ + cmd = "iec2c"+(".exe" if wx.Platform == '__WXMSW__' else "") + paths = [ os.path.join(base_folder, "matiec") ] - path = self.findObject(paths, lambda p:os.path.isfile(os.path.join(p, cmd))) + path = self.findObject(paths, lambda p: os.path.isfile(os.path.join(p, cmd))) # otherwise use iec2c from PATH if path is not None: - cmd=os.path.join(path, cmd) + cmd = os.path.join(path, cmd) return cmd - + def findLibPath(self): - paths=[ + paths = [ os.path.join(base_folder, "matiec", "lib"), "/usr/lib/matiec" ] - path = self.findObject(paths, lambda p:os.path.isfile(os.path.join(p, "ieclib.txt"))) + path = self.findObject(paths, lambda p: os.path.isfile(os.path.join(p, "ieclib.txt"))) return path - + def findLibCPath(self): - path=None - paths=[ + path = None + paths = [ os.path.join(self.ieclib_path, "C"), self.ieclib_path] - path = self.findObject(paths, lambda p:os.path.isfile(os.path.join(p, "iec_types.h"))) + path = self.findObject(paths, lambda p: os.path.isfile(os.path.join(p, "iec_types.h"))) return path def findSupportedOptions(self): - buildcmd = "\"%s\" -h"%(self.getCmd()) - options =["-f", "-l", "-p"] + buildcmd = "\"%s\" -h" % (self.getCmd()) + options = ["-f", "-l", "-p"] buildopt = "" try: # Invoke compiler. Output files are listed to stdout, errors to stderr status, result, err_result = ProcessLogger(None, buildcmd, - no_stdout=True, no_stderr=True).spin() - except Exception,e: + no_stdout=True, + no_stderr=True).spin() + except Exception, e: return buildopt for opt in options: @@ -165,8 +173,10 @@ self.ieclib_c_path = self.findLibCPath() return self.ieclib_c_path + iec2c_cfg = Iec2CSettings() + class ProjectController(ConfigTreeNode, PLCControler): """ This class define Root object of the confnode tree. @@ -196,12 +206,12 @@ """+((""" - """+"\n".join(['' - for libname,lib in features.libraries])+""" + for libname, lib in features.libraries])+""" - """) if len(features.libraries)>0 else '') + """ + """) if len(features.libraries) > 0 else '') + """ @@ -226,7 +236,7 @@ self.IECdebug_datas = {} self.IECdebug_lock = Lock() - self.DebugTimer=None + self.DebugTimer = None self.ResetIECProgramsAndVariables() # In both new or load scenario, no need to save @@ -253,8 +263,8 @@ def LoadLibraries(self): self.Libraries = [] - TypeStack=[] - for libname,clsname in features.libraries: + TypeStack = [] + for libname, clsname in features.libraries: if self.BeremizRoot.Libraries is None or getattr(self.BeremizRoot.Libraries, "Enable_"+libname+"_Library"): Lib = GetClassImporter(clsname)()(self, libname, TypeStack) TypeStack.append(Lib.GetTypes()) @@ -273,7 +283,8 @@ # Timer to pull PLC status self.StatusTimer = wx.Timer(self.AppFrame, -1) self.AppFrame.Bind(wx.EVT_TIMER, - self.PullPLCStatusProc, self.StatusTimer) + self.PullPLCStatusProc, + self.StatusTimer) if self._connector is not None: frame.LogViewer.SetLogSource(self._connector) @@ -282,7 +293,8 @@ # Timer to dispatch debug values to consumers self.DispatchDebugValuesTimer = wx.Timer(self.AppFrame, -1) self.AppFrame.Bind(wx.EVT_TIMER, - self.DispatchDebugValuesProc, self.DispatchDebugValuesTimer) + self.DispatchDebugValuesProc, + self.DispatchDebugValuesTimer) self.RefreshConfNodesBlockLists() @@ -298,7 +310,7 @@ return "Project" def CTNTestModified(self): - return self.ChangesToSave or not self.ProjectIsSaved() + return self.ChangesToSave or not self.ProjectIsSaved() def CTNFullName(self): return "" @@ -346,7 +358,7 @@ target.setcontent(self.Parser.CreateElement(target_name, "TargetType")) return target - def GetParamsAttributes(self, path = None): + def GetParamsAttributes(self, path=None): params = ConfigTreeNode.GetParamsAttributes(self, path) if params[0]["name"] == "BeremizRoot": for child in params[0]["children"]: @@ -367,10 +379,11 @@ if CheckPathPerm(self.ProjectPath): return True if self.AppFrame is not None: - dialog = wx.MessageDialog(self.AppFrame, - _('You must have permission to work on the project\nWork on a project copy ?'), - _('Error'), - wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION) + dialog = wx.MessageDialog( + self.AppFrame, + _('You must have permission to work on the project\nWork on a project copy ?'), + _('Error'), + wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION) answer = dialog.ShowModal() dialog.Destroy() if answer == wx.ID_YES: @@ -452,7 +465,7 @@ if error is not None: if self.Project is not None: (fname_err, lnum, src) = (("PLC",) + error) - self.logger.write_warning(XSDSchemaErrorMessage.format(a1 = fname_err, a2 = lnum, a3 = src)) + self.logger.write_warning(XSDSchemaErrorMessage.format(a1=fname_err, a2=lnum, a3=src)) else: return error, False if len(self.GetProjectConfigNames()) == 0: @@ -465,11 +478,11 @@ self._setBuildPath(BuildPath) # If dir have already be made, and file exist if os.path.isdir(self.CTNPath()) and os.path.isfile(self.ConfNodeXmlFilePath()): - #Load the confnode.xml file into parameters members + # Load the confnode.xml file into parameters members result = self.LoadXMLParams() if result: return result, False - #Load and init all the children + # Load and init all the children self.LoadChildren() self.RefreshConfNodesBlockLists() self.UpdateButtons() @@ -539,7 +552,7 @@ path = os.getenv("USERPROFILE") else: path = os.getenv("HOME") - dirdialog = wx.DirDialog(self.AppFrame , _("Choose a directory to save project"), path, wx.DD_NEW_DIR_BUTTON) + dirdialog = wx.DirDialog(self.AppFrame, _("Choose a directory to save project"), path, wx.DD_NEW_DIR_BUTTON) answer = dirdialog.ShowModal() dirdialog.Destroy() if answer == wx.ID_OK: @@ -554,24 +567,24 @@ def GetLibrariesTypes(self): self.LoadLibraries() - return [ lib.GetTypes() for lib in self.Libraries ] + return [lib.GetTypes() for lib in self.Libraries] def GetLibrariesSTCode(self): - return "\n".join([ lib.GetSTCode() for lib in self.Libraries ]) + return "\n".join([lib.GetSTCode() for lib in self.Libraries]) def GetLibrariesCCode(self, buildpath): - if len(self.Libraries)==0: - return [],[],() + if len(self.Libraries) == 0: + return [], [], () self.GetIECProgramsAndVariables() - LibIECCflags = '"-I%s" -Wno-unused-function'%os.path.abspath(self.GetIECLibPath()) - LocatedCCodeAndFlags=[] - Extras=[] + LibIECCflags = '"-I%s" -Wno-unused-function' % os.path.abspath(self.GetIECLibPath()) + LocatedCCodeAndFlags = [] + Extras = [] 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]) - if len(res)>2: + if len(res) > 2: Extras.extend(res[2:]) - return map(list,zip(*LocatedCCodeAndFlags))+[tuple(Extras)] + return map(list, zip(*LocatedCCodeAndFlags))+[tuple(Extras)] # Update PLCOpenEditor ConfNode Block types from loaded confnodes def RefreshConfNodesBlockLists(self): @@ -660,10 +673,10 @@ def GetLocations(self): locations = [] - filepath = os.path.join(self._getBuildPath(),"LOCATED_VARIABLES.h") + filepath = os.path.join(self._getBuildPath(), "LOCATED_VARIABLES.h") if os.path.isfile(filepath): # IEC2C compiler generate a list of located variables : LOCATED_VARIABLES.h - location_file = open(os.path.join(self._getBuildPath(),"LOCATED_VARIABLES.h")) + location_file = open(os.path.join(self._getBuildPath(), "LOCATED_VARIABLES.h")) # each line of LOCATED_VARIABLES.h declares a located variable lines = [line.strip() for line in location_file.readlines()] # This regular expression parses the lines genereated by IEC2C @@ -675,7 +688,7 @@ # Get the resulting dict resdict = result.groupdict() # rewrite string for variadic location as a tuple of integers - resdict['LOC'] = tuple(map(int,resdict['LOC'].split(','))) + resdict['LOC'] = tuple(map(int, resdict['LOC'].split(','))) # set located size to 'X' if not given if not resdict['SIZE']: resdict['SIZE'] = 'X' @@ -706,10 +719,10 @@ if len(warnings) > 0: self.logger.write_warning(_("Warnings in ST/IL/SFC code generator :\n")) for warning in warnings: - self.logger.write_warning("%s\n"%warning) + self.logger.write_warning("%s\n" % warning) if len(errors) > 0: # Failed ! - self.logger.write_error(_("Error in ST/IL/SFC code generator :\n%s\n")%errors[0]) + self.logger.write_error(_("Error in ST/IL/SFC code generator :\n%s\n") % errors[0]) return False plc_file = open(self._getIECcodepath(), "w") # Add ST Library from confnodes @@ -728,12 +741,10 @@ plc_file.close() return True - - def _Compile_ST_to_SoftPLC(self): self.logger.write(_("Compiling IEC Program into C code...\n")) buildpath = self._getBuildPath() - buildcmd = "\"%s\" %s -I \"%s\" -T \"%s\" \"%s\""%( + buildcmd = "\"%s\" %s -I \"%s\" -T \"%s\" \"%s\"" % ( iec2c_cfg.getCmd(), iec2c_cfg.getOptions(), iec2c_cfg.getLibPath(), @@ -743,8 +754,8 @@ try: # Invoke compiler. Output files are listed to stdout, errors to stderr status, result, err_result = ProcessLogger(self.logger, buildcmd, - no_stdout=True, no_stderr=True).spin() - except Exception,e: + no_stdout=True, no_stderr=True).spin() + except Exception, e: self.logger.write_error(buildcmd + "\n") self.logger.write_error(repr(e) + "\n") return False @@ -773,31 +784,33 @@ if first_line <= i <= last_line: if last_section is not None: self.logger.write_warning("In section: " + last_section) - last_section = None # only write section once + last_section = None # only write section once self.logger.write_warning("%04d: %s" % (i, line)) f.close() - self.logger.write_error(_("Error : IEC to C compiler returned %d\n")%status) + self.logger.write_error(_("Error : IEC to C compiler returned %d\n") % status) return False # Now extract C files of stdout - C_files = [ fname for fname in result.splitlines() if fname[-2:]==".c" or fname[-2:]==".C" ] + 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 C_files.remove("POUS.c") if not C_files: self.logger.write_error(_("Error : At least one configuration and one resource must be declared in PLC !\n")) return False # transform those base names to full names with path - C_files = map(lambda filename:os.path.join(buildpath, filename), C_files) + C_files = map(lambda filename: os.path.join(buildpath, filename), C_files) # prepend beremiz include to configuration header - H_files = [ fname for fname in result.splitlines() if fname[-2:]==".h" or fname[-2:]==".H" ] + H_files = [fname for fname in result.splitlines() if fname[-2:] == ".h" or fname[-2:] == ".H"] H_files.remove("LOCATED_VARIABLES.h") - H_files = map(lambda filename:os.path.join(buildpath, filename), H_files) + H_files = map(lambda filename: os.path.join(buildpath, filename), H_files) for H_file in H_files: - with file(H_file, 'r') as original: data = original.read() - with file(H_file, 'w') as modified: modified.write('#include "beremiz.h"\n' + data) + with file(H_file, 'r') as original: + data = original.read() + with file(H_file, 'w') as modified: + modified.write('#include "beremiz.h"\n' + data) self.logger.write(_("Extracting Located Variables...\n")) # Keep track of generated located variables for later use by self._Generate_C @@ -805,7 +818,7 @@ # Keep track of generated C files for later use by self.CTNGenerate_C self.PLCGeneratedCFiles = C_files # compute CFLAGS for plc - self.plcCFLAGS = '"-I%s" -Wno-unused-function'%iec2c_cfg.getLibCPath() + self.plcCFLAGS = '"-I%s" -Wno-unused-function' % iec2c_cfg.getLibCPath() return True def GetBuilder(self): @@ -817,19 +830,19 @@ targetclass = targets.GetBuilder(targetname) # if target already - if self._builder is None or not isinstance(self._builder,targetclass): + if self._builder is None or not isinstance(self._builder, targetclass): # Get classname instance self._builder = targetclass(self) return self._builder def ResetBuildMD5(self): - builder=self.GetBuilder() + builder = self.GetBuilder() if builder is not None: builder.ResetBinaryCodeMD5() self.EnableMethod("_Transfer", False) def GetLastBuildMD5(self): - builder=self.GetBuilder() + builder = self.GetBuilder() if builder is not None: return builder.GetBinaryCodeMD5() else: @@ -850,9 +863,9 @@ """ return ([(C_file_name, self.plcCFLAGS) - for C_file_name in self.PLCGeneratedCFiles ], - "", # no ldflags - False) # do not expose retreive/publish calls + for C_file_name in self.PLCGeneratedCFiles], + "", # no ldflags + False) # do not expose retreive/publish calls def ResetIECProgramsAndVariables(self): """ @@ -875,7 +888,7 @@ """ if self._ProgramList is None or self._VariablesList is None: try: - csvfile = os.path.join(self._getBuildPath(),"VARIABLES.csv") + csvfile = os.path.join(self._getBuildPath(), "VARIABLES.csv") # describes CSV columns ProgramsListAttributeName = ["num", "C_path", "type"] VariablesListAttributeName = ["num", "vartype", "IEC_path", "C_path", "type"] @@ -886,7 +899,7 @@ # Separate sections ListGroup = [] - for line in open(csvfile,'r').xreadlines(): + for line in open(csvfile, 'r').xreadlines(): strippedline = line.strip() if strippedline.startswith("//"): # Start new section @@ -898,9 +911,9 @@ # first section contains programs for line in ListGroup[0]: # Split and Maps each field to dictionnary entries - attrs = dict(zip(ProgramsListAttributeName,line.strip().split(';'))) + attrs = dict(zip(ProgramsListAttributeName, line.strip().split(';'))) # Truncate "C_path" to remove conf an resources names - attrs["C_path"] = '__'.join(attrs["C_path"].split(".",2)[1:]) + attrs["C_path"] = '__'.join(attrs["C_path"].split(".", 2)[1:]) # Push this dictionnary into result. self._ProgramList.append(attrs) @@ -909,9 +922,9 @@ Idx = 0 for line in ListGroup[1]: # Split and Maps each field to dictionnary entries - attrs = dict(zip(VariablesListAttributeName,line.strip().split(';'))) + attrs = dict(zip(VariablesListAttributeName, line.strip().split(';'))) # Truncate "C_path" to remove conf an resources names - parts = attrs["C_path"].split(".",2) + parts = attrs["C_path"].split(".", 2) if len(parts) > 2: config_FB = config_FBs.get(tuple(parts[:2])) if config_FB: @@ -927,19 +940,19 @@ # Push this dictionnary into result. self._DbgVariablesList.append(attrs) # Fill in IEC<->C translation dicts - IEC_path=attrs["IEC_path"] - self._IECPathToIdx[IEC_path]=(Idx, attrs["type"]) + IEC_path = attrs["IEC_path"] + self._IECPathToIdx[IEC_path] = (Idx, attrs["type"]) # Ignores numbers given in CSV file # Idx=int(attrs["num"]) # Count variables only, ignore FBs - Idx+=1 + Idx += 1 self._VariablesList.append(attrs) # third section contains ticktime if len(ListGroup) > 2: self._Ticktime = int(ListGroup[2][0]) - except Exception,e: + except Exception, e: self.logger.write_error(_("Cannot open/parse VARIABLES.csv!\n")) self.logger.write_error(traceback.format_exc()) self.ResetIECProgramsAndVariables() @@ -956,31 +969,35 @@ # prepare debug code variable_decl_array = [] bofs = 0 - for v in self._DbgVariablesList : + for v in self._DbgVariablesList: sz = DebugTypesSize.get(v["type"], 0) variable_decl_array += [ - "{&(%(C_path)s), "%v+ - {"EXT":"%(type)s_P_ENUM", - "IN":"%(type)s_P_ENUM", - "MEM":"%(type)s_O_ENUM", - "OUT":"%(type)s_O_ENUM", - "VAR":"%(type)s_ENUM"}[v["vartype"]]%v + - "}"] + "{&(%(C_path)s), " % v + + { + "EXT": "%(type)s_P_ENUM", + "IN": "%(type)s_P_ENUM", + "MEM": "%(type)s_O_ENUM", + "OUT": "%(type)s_O_ENUM", + "VAR": "%(type)s_ENUM" + }[v["vartype"]] % v + + "}"] bofs += sz debug_code = targets.GetCode("plc_debug.c") % { - "buffer_size":bofs, - "programs_declarations": - "\n".join(["extern %(type)s %(C_path)s;"%p for p in self._ProgramList]), - "extern_variables_declarations":"\n".join([ - {"EXT":"extern __IEC_%(type)s_p %(C_path)s;", - "IN":"extern __IEC_%(type)s_p %(C_path)s;", - "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 - for v in self._VariablesList if v["C_path"].find('.')<0]), - "variable_decl_array": ",\n".join(variable_decl_array) - } + "buffer_size": bofs, + "programs_declarations": "\n".join(["extern %(type)s %(C_path)s;" % + p for p in self._ProgramList]), + "extern_variables_declarations": "\n".join([ + { + "EXT": "extern __IEC_%(type)s_p %(C_path)s;", + "IN": "extern __IEC_%(type)s_p %(C_path)s;", + "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 + for v in self._VariablesList if v["C_path"].find('.') < 0]), + "variable_decl_array": ",\n".join(variable_decl_array) + } return debug_code @@ -991,43 +1008,43 @@ """ # filter location that are related to code that will be called # 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]) + 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.c") % { - "calls_prototypes":"\n".join([( - "int __init_%(s)s(int argc,char **argv);\n"+ - "void __cleanup_%(s)s(void);\n"+ - "void __retrieve_%(s)s(void);\n"+ - "void __publish_%(s)s(void);")%{'s':locstr} for locstr in locstrs]), - "retrieve_calls":"\n ".join([ - "__retrieve_%s();"%locstr for locstr in locstrs]), - "publish_calls":"\n ".join([ #Call publish in reverse order - "__publish_%s();"%locstrs[i-1] for i in xrange(len(locstrs), 0, -1)]), - "init_calls":"\n ".join([ - "init_level=%d; "%(i+1)+ - "if((res = __init_%s(argc,argv))){"%locstr + - #"printf(\"%s\"); "%locstr + #for debug - "return res;}" for i,locstr in enumerate(locstrs)]), - "cleanup_calls":"\n ".join([ - "if(init_level >= %d) "%i+ - "__cleanup_%s();"%locstrs[i-1] for i in xrange(len(locstrs), 0, -1)]) + "calls_prototypes": "\n".join([( + "int __init_%(s)s(int argc,char **argv);\n" + + "void __cleanup_%(s)s(void);\n" + + "void __retrieve_%(s)s(void);\n" + + "void __publish_%(s)s(void);") % {'s': locstr} for locstr in locstrs]), + "retrieve_calls": "\n ".join([ + "__retrieve_%s();" % locstr for locstr in locstrs]), + "publish_calls": "\n ".join([ # Call publish in reverse order + "__publish_%s();" % locstrs[i-1] for i in xrange(len(locstrs), 0, -1)]), + "init_calls": "\n ".join([ + "init_level=%d; " % (i+1) + + "if((res = __init_%s(argc,argv))){" % locstr + + # "printf(\"%s\"); "%locstr + #for debug + "return res;}" for i, locstr in enumerate(locstrs)]), + "cleanup_calls": "\n ".join([ + "if(init_level >= %d) " % i + + "__cleanup_%s();" % locstrs[i-1] for i in xrange(len(locstrs), 0, -1)]) } else: plc_main_code = targets.GetCode("plc_main_head.c") % { - "calls_prototypes":"\n", - "retrieve_calls":"\n", - "publish_calls":"\n", - "init_calls":"\n", - "cleanup_calls":"\n" - } + "calls_prototypes": "\n", + "retrieve_calls": "\n", + "publish_calls": "\n", + "init_calls": "\n", + "cleanup_calls": "\n" + } plc_main_code += targets.GetTargetCode(self.GetTarget().getcontent().getLocalTag()) plc_main_code += targets.GetCode("plc_main_tail.c") return plc_main_code - def _Build(self): """ Method called by user to (re)build SoftPLC and confnode tree @@ -1073,7 +1090,7 @@ # Build try: - if not builder.build() : + if not builder.build(): self.logger.write_error(_("C Build failed.\n")) return False except Exception, exc: @@ -1110,7 +1127,7 @@ self.ResetBuildMD5() return False - self.LocationCFilesAndCFLAGS = LibCFilesAndCFLAGS + CTNLocationCFilesAndCFLAGS + self.LocationCFilesAndCFLAGS = LibCFilesAndCFLAGS + CTNLocationCFilesAndCFLAGS self.LDFLAGS = CTNLDFLAGS + LibLDFLAGS ExtraFiles = CTNExtraFiles + LibExtraFiles @@ -1122,14 +1139,14 @@ # Recreate directory os.mkdir(extrafilespath) # Then write the files - for fname,fobject in ExtraFiles: - fpath = os.path.join(extrafilespath,fname) + for fname, fobject in ExtraFiles: + fpath = os.path.join(extrafilespath, fname) open(fpath, "wb").write(fobject.read()) # Now we can forget ExtraFiles (will close files object) del ExtraFiles # Header file for extensions - open(os.path.join(buildpath,"beremiz.h"), "w").write(targets.GetHeader()) + open(os.path.join(buildpath, "beremiz.h"), "w").write(targets.GetHeader()) # Template based part of C code generation # files are stacked at the beginning, as files of confnode tree root @@ -1137,16 +1154,16 @@ # debugger code (self.Generate_plc_debugger, "plc_debugger.c", "Debugger"), # init/cleanup/retrieve/publish, run and align code - (self.Generate_plc_main,"plc_main.c","Common runtime")]: + (self.Generate_plc_main, "plc_main.c", "Common runtime")]: try: # Do generate code = generator() if code is None: - raise - code_path = os.path.join(buildpath,filename) + raise + code_path = os.path.join(buildpath, filename) open(code_path, "w").write(code) # Insert this file as first file to be compiled at root confnode - self.LocationCFilesAndCFLAGS[0][1].insert(0,(code_path, self.plcCFLAGS)) + self.LocationCFilesAndCFLAGS[0][1].insert(0, (code_path, self.plcCFLAGS)) except Exception, exc: self.logger.write_error(name+_(" generation failed !\n")) self.logger.write_error(traceback.format_exc()) @@ -1158,30 +1175,34 @@ 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: - row = 1 if from_location[0] < start_row else (from_location[0] - start_row) + row = 1 if from_location[0] < start_row else (from_location[0] - start_row) col = 1 if (start_row != from_location[0]) else (from_location[1] - start_col) start = (row, col) - row = 1 if to_location[0] < start_row else (to_location[0] - start_row) + row = 1 if to_location[0] < start_row else (to_location[0] - start_row) col = 1 if (start_row != to_location[0]) else (to_location[1] - start_col) end = (row, col) - + if self.AppFrame is not None: self.AppFrame.ShowError(infos, start, end) _IECCodeView = None + def _showIECcode(self): self._OpenView("IEC code") _IECRawCodeView = None + def _editIECrawcode(self): self._OpenView("IEC raw code") _ProjectFilesView = None + def _OpenProjectFiles(self): self._OpenView("Project Files") _FileEditors = {} + def _OpenFileEditor(self, filepath): self._OpenView(filepath) @@ -1195,10 +1216,10 @@ self._IECCodeView.SetKeywords(IEC_KEYWORDS) try: text = file(plc_file).read() - except: + except Exception: text = '(* No IEC code have been generated at that time ! *)' - self._IECCodeView.SetText(text = text) - self._IECCodeView.Editor.SetReadOnly(True) + self._IECCodeView.SetText(text=text) + self._IECCodeView.Editor.SetReadOnly(True) self._IECCodeView.SetIcon(GetBitmap("ST")) setattr(self._IECCodeView, "_OnClose", self.OnCloseEditor) @@ -1240,7 +1261,7 @@ elif name is not None and name.find("::") != -1: filepath, editor_name = name.split("::") - if not self._FileEditors.has_key(filepath): + if filepath not in self._FileEditors: if os.path.isfile(filepath): file_extension = os.path.splitext(filepath)[1] @@ -1253,9 +1274,12 @@ editor_name = editors.keys()[0] elif len(editors) > 0: names = editors.keys() - dialog = wx.SingleChoiceDialog(self.AppFrame, - _("Select an editor:"), _("Editor selection"), - names, wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL) + 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()] dialog.Destroy() @@ -1269,7 +1293,7 @@ if isinstance(self._FileEditors[filepath], DebugViewer): self._FileEditors[filepath].SetDataProducer(self) - if self._FileEditors.has_key(filepath): + if filepath in self._FileEditors: editor = self._FileEditors[filepath] self.AppFrame.EditProjectElement(editor, editor.GetTagName()) @@ -1305,11 +1329,10 @@ self.ShowMethod("_showIECcode", os.path.isfile(self._getIECcodepath())) if self.AppFrame is not None and not self.UpdateMethodsFromPLCStatus(): self.AppFrame.RefreshStatusToolBar() - + def UpdateButtons(self): wx.CallAfter(self._UpdateButtons) - def UpdatePLCLog(self, log_count): if log_count: if self.AppFrame is not None: @@ -1328,19 +1351,19 @@ status = "Disconnected" if(self.previous_plcstate != status): for args in { - "Started" : [("_Run", False), - ("_Stop", True)], - "Stopped" : [("_Run", True), - ("_Stop", False)], - "Empty" : [("_Run", False), - ("_Stop", False)], - "Broken" : [], - "Disconnected" :[("_Run", False), - ("_Stop", False), - ("_Transfer", False), - ("_Connect", True), - ("_Disconnect", False)], - }.get(status,[]): + "Started": [("_Run", False), + ("_Stop", True)], + "Stopped": [("_Run", True), + ("_Stop", False)], + "Empty": [("_Run", False), + ("_Stop", False)], + "Broken": [], + "Disconnected": [("_Run", False), + ("_Stop", False), + ("_Transfer", False), + ("_Connect", True), + ("_Disconnect", False)], + }.get(status, []): self.ShowMethod(*args) self.previous_plcstate = status if self.AppFrame is not None: @@ -1364,8 +1387,8 @@ "Disconnected": _("Disconnected") } return msgs.get(status, status) - - def ShowPLCProgress(self, status = "", progress = 0): + + def ShowPLCProgress(self, status="", progress=0): self.AppFrame.ProgressStatusBar.Show() self.AppFrame.ConnectionStatusBar.SetStatusText(self.GetTextStatus(status), 1) self.AppFrame.ProgressStatusBar.SetValue(progress) @@ -1376,25 +1399,25 @@ self.previous_plcstate = "" self.AppFrame.ProgressStatusBar.Hide() self.UpdateMethodsFromPLCStatus() - + def PullPLCStatusProc(self, event): self.UpdateMethodsFromPLCStatus() def SnapshotAndResetDebugValuesBuffers(self): buffers, self.DebugValuesBuffers = (self.DebugValuesBuffers, - [list() for n in xrange(len(self.TracedIECPath))]) + [list() for n in xrange(len(self.TracedIECPath))]) ticks, self.DebugTicks = self.DebugTicks, [] return ticks, buffers def RegisterDebugVarToConnector(self): - self.DebugTimer=None + self.DebugTimer = None Idxs = [] self.TracedIECPath = [] self.TracedIECTypes = [] if self._connector is not None: self.IECdebug_lock.acquire() IECPathsToPop = [] - for IECPath,data_tuple in self.IECdebug_datas.iteritems(): + for IECPath, data_tuple in self.IECdebug_datas.iteritems(): WeakCallableDict, data_log, status, fvalue, buffer_list = data_tuple if len(WeakCallableDict) == 0: # Callable Dict is empty. @@ -1402,14 +1425,14 @@ IECPathsToPop.append(IECPath) elif IECPath != "__tick__": # Convert - Idx, IEC_Type = self._IECPathToIdx.get(IECPath,(None,None)) + Idx, IEC_Type = self._IECPathToIdx.get(IECPath, (None, None)) if Idx is not None: if IEC_Type in DebugTypesSize: Idxs.append((Idx, IEC_Type, fvalue, IECPath)) else: - self.logger.write_warning(_("Debug: Unsupported type to debug '%s'\n")%IEC_Type) + self.logger.write_warning(_("Debug: Unsupported type to debug '%s'\n") % IEC_Type) else: - self.logger.write_warning(_("Debug: Unknown variable '%s'\n")%IECPath) + self.logger.write_warning(_("Debug: Unknown variable '%s'\n") % IECPath) for IECPathToPop in IECPathsToPop: self.IECdebug_datas.pop(IECPathToPop) @@ -1438,12 +1461,12 @@ if self.IsPLCStarted(): # Timer to prevent rapid-fire when registering many variables # use wx.CallAfter use keep using same thread. TODO : use wx.Timer instead - self.DebugTimer=Timer(0.5,wx.CallAfter,args = [self.RegisterDebugVarToConnector]) + self.DebugTimer = Timer(0.5, wx.CallAfter, args=[self.RegisterDebugVarToConnector]) # Rearm anti-rapid-fire timer self.DebugTimer.start() def GetDebugIECVariableType(self, IECPath): - Idx, IEC_Type = self._IECPathToIdx.get(IECPath,(None,None)) + Idx, IEC_Type = self._IECPathToIdx.get(IECPath, (None, None)) return IEC_Type def SubscribeDebugIECVariable(self, IECPath, callableobj, buffer_list=False): @@ -1452,24 +1475,24 @@ to a WeakKeyDictionary linking weakly referenced callables """ - if IECPath != "__tick__" and not self._IECPathToIdx.has_key(IECPath): + if IECPath != "__tick__" and IECPath not in self._IECPathToIdx: return None self.IECdebug_lock.acquire() # If no entry exist, create a new one with a fresh WeakKeyDictionary IECdebug_data = self.IECdebug_datas.get(IECPath, None) if IECdebug_data is None: - IECdebug_data = [ - WeakKeyDictionary(), # Callables - [], # Data storage [(tick, data),...] - "Registered", # Variable status + IECdebug_data = [ + WeakKeyDictionary(), # Callables + [], # Data storage [(tick, data),...] + "Registered", # Variable status None, buffer_list] # Forced value self.IECdebug_datas[IECPath] = IECdebug_data else: IECdebug_data[4] |= buffer_list - IECdebug_data[0][callableobj]=buffer_list + IECdebug_data[0][callableobj] = buffer_list self.IECdebug_lock.release() @@ -1481,12 +1504,12 @@ self.IECdebug_lock.acquire() IECdebug_data = self.IECdebug_datas.get(IECPath, None) if IECdebug_data is not None: - IECdebug_data[0].pop(callableobj,None) + IECdebug_data[0].pop(callableobj, None) if len(IECdebug_data[0]) == 0: self.IECdebug_datas.pop(IECPath) else: IECdebug_data[4] = reduce( - lambda x, y: x|y, + lambda x, y: x | y, IECdebug_data[0].itervalues(), False) self.IECdebug_lock.release() @@ -1501,7 +1524,7 @@ self.ReArmDebugRegisterTimer() def ForceDebugIECVariable(self, IECPath, fvalue): - if not self.IECdebug_datas.has_key(IECPath): + if IECPath not in self.IECdebug_datas: return self.IECdebug_lock.acquire() @@ -1516,7 +1539,7 @@ self.ReArmDebugRegisterTimer() def ReleaseDebugIECVariable(self, IECPath): - if not self.IECdebug_datas.has_key(IECPath): + if IECPath not in self.IECdebug_datas: return self.IECdebug_lock.acquire() @@ -1534,8 +1557,8 @@ data_tuple = self.IECdebug_datas.get(IECPath, None) if data_tuple is not None: WeakCallableDict, data_log, status, fvalue, buffer_list = data_tuple - #data_log.append((debug_tick, value)) - for weakcallable,buffer_list in WeakCallableDict.iteritems(): + # data_log.append((debug_tick, value)) + for weakcallable, buffer_list in WeakCallableDict.iteritems(): function = getattr(weakcallable, function_name, None) if function is not None: if buffer_list: @@ -1560,20 +1583,19 @@ while (not self.debug_break) and (self._connector is not None): plc_status, Traces = self._connector.GetTraceVariables() debug_getvar_retry += 1 - #print [dict.keys() for IECPath, (dict, log, status, fvalue) in self.IECdebug_datas.items()] - if plc_status == "Started" : + # print [dict.keys() for IECPath, (dict, log, status, fvalue) in self.IECdebug_datas.items()] + if plc_status == "Started": if len(Traces) > 0: Failed = False self.IECdebug_lock.acquire() - for debug_tick, debug_buff in Traces : + for debug_tick, debug_buff in Traces: debug_vars = UnpackDebugBuffer(debug_buff, self.TracedIECTypes) - if (debug_vars is not None and - len(debug_vars) == len(self.TracedIECPath)): + if debug_vars is not None and len(debug_vars) == len(self.TracedIECPath): for IECPath, values_buffer, value in izip( self.TracedIECPath, self.DebugValuesBuffers, debug_vars): - IECdebug_data = self.IECdebug_datas.get(IECPath, None) #FIXME get + IECdebug_data = self.IECdebug_datas.get(IECPath, None) # FIXME get if IECdebug_data is not None and value is not None: forced = IECdebug_data[2:4] == ["Forced", value] if not IECdebug_data[4] and len(values_buffer) > 0: @@ -1659,7 +1681,7 @@ self.logger.write_error(_("Couldn't stop PLC !\n")) # debugthread should die on his own - #self.KillDebugThread() + # self.KillDebugThread() wx.CallAfter(self.UpdateMethodsFromPLCStatus) @@ -1686,10 +1708,7 @@ return # Get connector uri - uri = self.\ - BeremizRoot.\ - getURI_location().\ - strip() + uri = self.BeremizRoot.getURI_location().strip() # if uri is empty launch discovery dialog if uri == "": @@ -1699,7 +1718,7 @@ answer = dialog.ShowModal() uri = dialog.GetURI() dialog.Destroy() - except: + except Exception: self.logger.write_error(_("Local service discovery failed!\n")) self.logger.write_error(traceback.format_exc()) uri = None @@ -1709,9 +1728,7 @@ self.logger.write_error(_("Connection canceled!\n")) return else: - self.\ - BeremizRoot.\ - setURI_location(uri) + self.BeremizRoot.setURI_location(uri) self.ChangesToSave = True if self._View is not None: self._View.RefreshView() @@ -1725,13 +1742,13 @@ try: self._SetConnector(connectors.ConnectorFactory(uri, self)) except Exception, msg: - self.logger.write_error(_("Exception while connecting %s!\n")%uri) + self.logger.write_error(_("Exception while connecting %s!\n") % uri) self.logger.write_error(traceback.format_exc()) # Did connection success ? if self._connector is None: # Oups. - self.logger.write_error(_("Connection failed to %s!\n")%uri) + self.logger.write_error(_("Connection failed to %s!\n") % uri) else: self.ShowMethod("_Connect", False) self.ShowMethod("_Disconnect", True) @@ -1746,9 +1763,9 @@ else: status = "" - #self.logger.write(_("PLC is %s\n")%status) - - if self.previous_plcstate in ["Started","Stopped"]: + # 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")) self._connect_debug() @@ -1763,22 +1780,21 @@ # Check remote target PLC correspondance to that md5 if MD5 is not None: if not self._connector.MatchMD5(MD5): -# self.logger.write_warning( -# _("Latest build does not match with target, please transfer.\n")) + # self.logger.write_warning( + # _("Latest build does not match with target, please transfer.\n")) self.EnableMethod("_Transfer", True) else: -# self.logger.write( -# _("Latest build matches target, no transfer needed.\n")) + # self.logger.write( + # _("Latest build matches target, no transfer needed.\n")) self.EnableMethod("_Transfer", True) # warns controller that program match self.ProgramTransferred() - #self.EnableMethod("_Transfer", False) + # self.EnableMethod("_Transfer", False) else: -# self.logger.write_warning( -# _("Cannot compare latest build to target. Please build.\n")) + # self.logger.write_warning( + # _("Cannot compare latest build to target. Please build.\n")) self.EnableMethod("_Transfer", False) - def _Disconnect(self): self._SetConnector(None) @@ -1787,7 +1803,7 @@ MD5 = self.GetLastBuildMD5() # Check if md5 file is empty : ask user to build PLC - if MD5 is None : + if MD5 is None: self.logger.write_error(_("Failed : Must build before transfer.\n")) return False @@ -1803,14 +1819,14 @@ extrafiles.extend( [(name, open(os.path.join(extrafilespath, name), - 'rb').read()) \ + 'rb').read()) for name in os.listdir(extrafilespath)]) # Send PLC on target builder = self.GetBuilder() if builder is not None: data = builder.GetBinaryCode() - if data is not None : + if data is not None: if self._connector.NewPLC(MD5, data, extrafiles) and self.GetIECProgramsAndVariables(): self.UnsubscribeAllDebugIECVariable() self.ProgramTransferred() @@ -1818,83 +1834,102 @@ self.AppFrame.CloseObsoleteDebugTabs() self.AppFrame.RefreshPouInstanceVariablesPanel() self.logger.write(_("Transfer completed successfully.\n")) - self.AppFrame.LogViewer.ResetLogCounters(); + self.AppFrame.LogViewer.ResetLogCounters() else: self.logger.write_error(_("Transfer failed\n")) - self.HidePLCProgress() + self.HidePLCProgress() else: self.logger.write_error(_("No PLC to transfer (did build succeed ?)\n")) wx.CallAfter(self.UpdateMethodsFromPLCStatus) StatusMethods = [ - {"bitmap" : "Build", - "name" : _("Build"), - "tooltip" : _("Build project into build folder"), - "method" : "_Build"}, - {"bitmap" : "Clean", - "name" : _("Clean"), - "enabled" : False, - "tooltip" : _("Clean project build folder"), - "method" : "_Clean"}, - {"bitmap" : "Run", - "name" : _("Run"), - "shown" : False, - "tooltip" : _("Start PLC"), - "method" : "_Run"}, - {"bitmap" : "Stop", - "name" : _("Stop"), - "shown" : False, - "tooltip" : _("Stop Running PLC"), - "method" : "_Stop"}, - {"bitmap" : "Connect", - "name" : _("Connect"), - "tooltip" : _("Connect to the target PLC"), - "method" : "_Connect"}, - {"bitmap" : "Transfer", - "name" : _("Transfer"), - "shown" : False, - "tooltip" : _("Transfer PLC"), - "method" : "_Transfer"}, - {"bitmap" : "Disconnect", - "name" : _("Disconnect"), - "shown" : False, - "tooltip" : _("Disconnect from PLC"), - "method" : "_Disconnect"}, - {"bitmap" : "ShowIECcode", - "name" : _("Show code"), - "shown" : False, - "tooltip" : _("Show IEC code generated by PLCGenerator"), - "method" : "_showIECcode"}, + { + "bitmap": "Build", + "name": _("Build"), + "tooltip": _("Build project into build folder"), + "method": "_Build" + }, + { + "bitmap": "Clean", + "name": _("Clean"), + "tooltip": _("Clean project build folder"), + "method": "_Clean", + "enabled": False, + }, + { + "bitmap": "Run", + "name": _("Run"), + "tooltip": _("Start PLC"), + "method": "_Run", + "shown": False, + }, + { + "bitmap": "Stop", + "name": _("Stop"), + "tooltip": _("Stop Running PLC"), + "method": "_Stop", + "shown": False, + }, + { + "bitmap": "Connect", + "name": _("Connect"), + "tooltip": _("Connect to the target PLC"), + "method": "_Connect" + }, + { + "bitmap": "Transfer", + "name": _("Transfer"), + "tooltip": _("Transfer PLC"), + "method": "_Transfer", + "shown": False, + }, + { + "bitmap": "Disconnect", + "name": _("Disconnect"), + "tooltip": _("Disconnect from PLC"), + "method": "_Disconnect", + "shown": False, + }, + { + "bitmap": "ShowIECcode", + "name": _("Show code"), + "tooltip": _("Show IEC code generated by PLCGenerator"), + "method": "_showIECcode", + "shown": False, + }, ] ConfNodeMethods = [ - {"bitmap" : "editIECrawcode", - "name" : _("Raw IEC code"), - "tooltip" : _("Edit raw IEC code added to code generated by PLCGenerator"), - "method" : "_editIECrawcode"}, - {"bitmap" : "ManageFolder", - "name" : _("Project Files"), - "tooltip" : _("Open a file explorer to manage project files"), - "method" : "_OpenProjectFiles"}, + { + "bitmap": "editIECrawcode", + "name": _("Raw IEC code"), + "tooltip": _("Edit raw IEC code added to code generated by PLCGenerator"), + "method": "_editIECrawcode" + }, + { + "bitmap": "ManageFolder", + "name": _("Project Files"), + "tooltip": _("Open a file explorer to manage project files"), + "method": "_OpenProjectFiles" + }, ] - def EnableMethod(self, method, value): for d in self.StatusMethods: - if d["method"]==method: - d["enabled"]=value + if d["method"] == method: + d["enabled"] = value return True return False def ShowMethod(self, method, value): for d in self.StatusMethods: - if d["method"]==method: - d["shown"]=value + if d["method"] == method: + d["shown"] = value return True return False def CallMethod(self, method): for d in self.StatusMethods: - if d["method"]==method and d.get("enabled", True) and d.get("shown", True): + if d["method"] == method and d.get("enabled", True) and d.get("shown", True): getattr(self, method)() diff -r 31e63e25b4cc -r 64beb9e9c749 c_ext/CFileEditor.py --- a/c_ext/CFileEditor.py Mon Aug 21 20:17:19 2017 +0000 +++ b/c_ext/CFileEditor.py Mon Aug 21 23:22:58 2017 +0300 @@ -27,22 +27,23 @@ from controls.CustomStyledTextCtrl import faces from editors.CodeFileEditor import CodeFileEditor, CodeEditor + class CppEditor(CodeEditor): - KEYWORDS = ["asm", "auto", "bool", "break", "case", "catch", "char", "class", - "const", "const_cast", "continue", "default", "delete", "do", "double", - "dynamic_cast", "else", "enum", "explicit", "export", "extern", "false", - "float", "for", "friend", "goto", "if", "inline", "int", "long", "mutable", - "namespace", "new", "operator", "private", "protected", "public", "register", - "reinterpret_cast", "return", "short", "signed", "sizeof", "static", - "static_cast", "struct", "switch", "template", "this", "throw", "true", "try", - "typedef", "typeid", "typename", "union", "unsigned", "using", "virtual", - "void", "volatile", "wchar_t", "while"] + KEYWORDS = ["asm", "auto", "bool", "break", "case", "catch", "char", "class", + "const", "const_cast", "continue", "default", "delete", "do", "double", + "dynamic_cast", "else", "enum", "explicit", "export", "extern", "false", + "float", "for", "friend", "goto", "if", "inline", "int", "long", "mutable", + "namespace", "new", "operator", "private", "protected", "public", "register", + "reinterpret_cast", "return", "short", "signed", "sizeof", "static", + "static_cast", "struct", "switch", "template", "this", "throw", "true", "try", + "typedef", "typeid", "typename", "union", "unsigned", "using", "virtual", + "void", "volatile", "wchar_t", "while"] COMMENT_HEADER = "/" - + def SetCodeLexer(self): self.SetLexer(stc.STC_LEX_CPP) - + self.StyleSetSpec(stc.STC_C_COMMENT, 'fore:#408060,size:%(size)d' % faces) self.StyleSetSpec(stc.STC_C_COMMENTLINE, 'fore:#408060,size:%(size)d' % faces) self.StyleSetSpec(stc.STC_C_COMMENTDOC, 'fore:#408060,size:%(size)d' % faces) @@ -53,15 +54,12 @@ self.StyleSetSpec(stc.STC_C_OPERATOR, 'bold,size:%(size)d' % faces) self.StyleSetSpec(stc.STC_C_STRINGEOL, 'back:#FFD5FF,size:%(size)d' % faces) -#------------------------------------------------------------------------------- -# CFileEditor Main Frame Class -#------------------------------------------------------------------------------- class CFileEditor(CodeFileEditor): - + """ + CFileEditor Main Frame Class + """ + CONFNODEEDITOR_TABS = [ (_("C code"), "_create_CodePanel")] CODE_EDITOR = CppEditor - - - diff -r 31e63e25b4cc -r 64beb9e9c749 c_ext/c_ext.py --- a/c_ext/c_ext.py Mon Aug 21 20:17:19 2017 +0000 +++ b/c_ext/c_ext.py Mon Aug 21 23:22:58 2017 +0300 @@ -27,6 +27,7 @@ from CFileEditor import CFileEditor from CodeFileTreeNode import CodeFile + class CFile(CodeFile): XSD = """ @@ -47,13 +48,13 @@ "retrieveFunction", "publishFunction"] EditorType = CFileEditor - + def GetIconName(self): return "Cfile" def CodeFileName(self): return os.path.join(self.CTNPath(), "cfile.xml") - + def CTNGenerate_C(self, buildpath, locations): """ Generate C code @@ -70,17 +71,17 @@ current_location = self.GetCurrentLocation() # define a unique name for the generated C file location_str = "_".join(map(str, current_location)) - + text = "/* Code generated by Beremiz c_ext confnode */\n\n" text += "#include \n\n" - + # Adding includes text += "/* User includes */\n" text += self.CodeFile.includes.getanyText().strip() text += "\n" - + text += '#include "iec_types_all.h"\n\n' - + # Adding variables config = self.GetCTRoot().GetProjectConfigNames()[0] text += "/* User variables reference */\n" @@ -93,36 +94,35 @@ text += "extern %(type)s %(global)s;\n" % var_infos text += "#define %(name)s %(global)s.value\n" % var_infos text += "\n" - + # Adding user global variables and routines text += "/* User internal user variables and routines */\n" text += self.CodeFile.globals.getanyText().strip() text += "\n" - + # Adding Beremiz confnode functions text += "/* Beremiz confnode functions */\n" - text += "int __init_%s(int argc,char **argv)\n{\n"%location_str + text += "int __init_%s(int argc,char **argv)\n{\n" % location_str text += self.CodeFile.initFunction.getanyText().strip() text += " return 0;\n}\n\n" - - text += "void __cleanup_%s(void)\n{\n"%location_str + + text += "void __cleanup_%s(void)\n{\n" % location_str text += self.CodeFile.cleanUpFunction.getanyText().strip() text += "\n}\n\n" - - text += "void __retrieve_%s(void)\n{\n"%location_str + + text += "void __retrieve_%s(void)\n{\n" % location_str text += self.CodeFile.retrieveFunction.getanyText().strip() text += "\n}\n\n" - - text += "void __publish_%s(void)\n{\n"%location_str + + text += "void __publish_%s(void)\n{\n" % location_str text += self.CodeFile.publishFunction.getanyText().strip() text += "\n}\n\n" - - Gen_Cfile_path = os.path.join(buildpath, "CFile_%s.c"%location_str) - cfile = open(Gen_Cfile_path,'w') + + Gen_Cfile_path = os.path.join(buildpath, "CFile_%s.c" % location_str) + cfile = open(Gen_Cfile_path, 'w') cfile.write(text) cfile.close() - - matiec_CFLAGS = '"-I%s"'%os.path.abspath(self.GetCTRoot().GetIECLibPath()) - - return [(Gen_Cfile_path, str(self.CExtension.getCFLAGS() + matiec_CFLAGS))],str(self.CExtension.getLDFLAGS()),True + matiec_CFLAGS = '"-I%s"' % os.path.abspath(self.GetCTRoot().GetIECLibPath()) + + return [(Gen_Cfile_path, str(self.CExtension.getCFLAGS() + matiec_CFLAGS))], str(self.CExtension.getLDFLAGS()), True diff -r 31e63e25b4cc -r 64beb9e9c749 canfestival/NetworkEditor.py --- a/canfestival/NetworkEditor.py Mon Aug 21 20:17:19 2017 +0000 +++ b/canfestival/NetworkEditor.py Mon Aug 21 23:22:58 2017 +0300 @@ -28,55 +28,62 @@ from networkeditortemplate import NetworkEditorTemplate from editors.ConfTreeNodeEditor import ConfTreeNodeEditor -[ID_NETWORKEDITOR, +[ + ID_NETWORKEDITOR, ] = [wx.NewId() for _init_ctrls in range(1)] -[ID_NETWORKEDITORCONFNODEMENUADDSLAVE, ID_NETWORKEDITORCONFNODEMENUREMOVESLAVE, - ID_NETWORKEDITORCONFNODEMENUMASTER, +[ + ID_NETWORKEDITORCONFNODEMENUADDSLAVE, + ID_NETWORKEDITORCONFNODEMENUREMOVESLAVE, + ID_NETWORKEDITORCONFNODEMENUMASTER, ] = [wx.NewId() for _init_coll_ConfNodeMenu_Items in range(3)] -[ID_NETWORKEDITORMASTERMENUNODEINFOS, ID_NETWORKEDITORMASTERMENUDS301PROFILE, - ID_NETWORKEDITORMASTERMENUDS302PROFILE, ID_NETWORKEDITORMASTERMENUDSOTHERPROFILE, - ID_NETWORKEDITORMASTERMENUADD, +[ + ID_NETWORKEDITORMASTERMENUNODEINFOS, ID_NETWORKEDITORMASTERMENUDS301PROFILE, + ID_NETWORKEDITORMASTERMENUDS302PROFILE, ID_NETWORKEDITORMASTERMENUDSOTHERPROFILE, + ID_NETWORKEDITORMASTERMENUADD, ] = [wx.NewId() for _init_coll_MasterMenu_Items in range(5)] -[ID_NETWORKEDITORADDMENUSDOSERVER, ID_NETWORKEDITORADDMENUSDOCLIENT, - ID_NETWORKEDITORADDMENUPDOTRANSMIT, ID_NETWORKEDITORADDMENUPDORECEIVE, - ID_NETWORKEDITORADDMENUMAPVARIABLE, ID_NETWORKEDITORADDMENUUSERTYPE, +[ + ID_NETWORKEDITORADDMENUSDOSERVER, ID_NETWORKEDITORADDMENUSDOCLIENT, + ID_NETWORKEDITORADDMENUPDOTRANSMIT, ID_NETWORKEDITORADDMENUPDORECEIVE, + ID_NETWORKEDITORADDMENUMAPVARIABLE, ID_NETWORKEDITORADDMENUUSERTYPE, ] = [wx.NewId() for _init_coll_AddMenu_Items in range(6)] + class NetworkEditor(ConfTreeNodeEditor, NetworkEditorTemplate): - + ID = ID_NETWORKEDITOR CONFNODEEDITOR_TABS = [ (_("CANOpen network"), "_create_NetworkEditor")] - + def _create_NetworkEditor(self, prnt): - self.NetworkEditor = wx.Panel(id=-1, parent=prnt, pos=wx.Point(0, 0), - size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL) - + self.NetworkEditor = wx.Panel( + id=-1, parent=prnt, pos=wx.Point(0, 0), + size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL) + NetworkEditorTemplate._init_ctrls(self, self.NetworkEditor) - + main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=1, vgap=0) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(0) - - main_sizer.AddWindow(self.NetworkNodes, 0, border=5, flag=wx.GROW|wx.ALL) - + + main_sizer.AddWindow(self.NetworkNodes, 0, border=5, flag=wx.GROW | wx.ALL) + self.NetworkEditor.SetSizer(main_sizer) - + return self.NetworkEditor - + def __init__(self, parent, controler, window): ConfTreeNodeEditor.__init__(self, parent, controler, window) NetworkEditorTemplate.__init__(self, controler, window, False) - + self.RefreshNetworkNodes() self.RefreshBufferState() - + def __del__(self): self.Controler.OnCloseEditor(self) - + def GetConfNodeMenuItems(self): add_menu = [(wx.ITEM_NORMAL, (_('SDO Server'), ID_NETWORKEDITORADDMENUSDOSERVER, '', self.OnAddSDOServerMenu)), (wx.ITEM_NORMAL, (_('SDO Client'), ID_NETWORKEDITORADDMENUSDOCLIENT, '', self.OnAddSDOClientMenu)), @@ -84,7 +91,7 @@ (wx.ITEM_NORMAL, (_('PDO Receive'), ID_NETWORKEDITORADDMENUPDORECEIVE, '', self.OnAddPDOReceiveMenu)), (wx.ITEM_NORMAL, (_('Map Variable'), ID_NETWORKEDITORADDMENUMAPVARIABLE, '', self.OnAddMapVariableMenu)), (wx.ITEM_NORMAL, (_('User Type'), ID_NETWORKEDITORADDMENUUSERTYPE, '', self.OnAddUserTypeMenu))] - + profile = self.Manager.GetCurrentProfileName() if profile not in ("None", "DS-301"): other_profile_text = _("%s Profile") % profile @@ -93,35 +100,35 @@ add_menu.append((wx.ITEM_NORMAL, (text, wx.NewId(), '', self.GetProfileCallBack(text)))) else: other_profile_text = _('Other Profile') - + master_menu = [(wx.ITEM_NORMAL, (_('DS-301 Profile'), ID_NETWORKEDITORMASTERMENUDS301PROFILE, '', self.OnCommunicationMenu)), (wx.ITEM_NORMAL, (_('DS-302 Profile'), ID_NETWORKEDITORMASTERMENUDS302PROFILE, '', self.OnOtherCommunicationMenu)), (wx.ITEM_NORMAL, (other_profile_text, ID_NETWORKEDITORMASTERMENUDSOTHERPROFILE, '', self.OnEditProfileMenu)), (wx.ITEM_SEPARATOR, None), (add_menu, (_('Add'), ID_NETWORKEDITORMASTERMENUADD))] - + return [(wx.ITEM_NORMAL, (_('Add slave'), ID_NETWORKEDITORCONFNODEMENUADDSLAVE, '', self.OnAddSlaveMenu)), (wx.ITEM_NORMAL, (_('Remove slave'), ID_NETWORKEDITORCONFNODEMENUREMOVESLAVE, '', self.OnRemoveSlaveMenu)), (wx.ITEM_SEPARATOR, None), (master_menu, (_('Master'), ID_NETWORKEDITORCONFNODEMENUMASTER))] - + def RefreshMainMenu(self): pass - + def RefreshConfNodeMenu(self, confnode_menu): confnode_menu.Enable(ID_NETWORKEDITORCONFNODEMENUMASTER, self.NetworkNodes.GetSelection() == 0) - + def RefreshView(self): ConfTreeNodeEditor.RefreshView(self) self.RefreshCurrentIndexList() - + def RefreshBufferState(self): NetworkEditorTemplate.RefreshBufferState(self) self.ParentWindow.RefreshTitle() self.ParentWindow.RefreshFileMenu() self.ParentWindow.RefreshEditMenu() self.ParentWindow.RefreshPageTitles() - + def OnNodeSelectedChanged(self, event): NetworkEditorTemplate.OnNodeSelectedChanged(self, event) wx.CallAfter(self.ParentWindow.RefreshEditMenu) diff -r 31e63e25b4cc -r 64beb9e9c749 canfestival/SlaveEditor.py --- a/canfestival/SlaveEditor.py Mon Aug 21 20:17:19 2017 +0000 +++ b/canfestival/SlaveEditor.py Mon Aug 21 23:22:58 2017 +0300 @@ -28,33 +28,36 @@ from nodeeditortemplate import NodeEditorTemplate from editors.ConfTreeNodeEditor import ConfTreeNodeEditor -[ID_SLAVEEDITORCONFNODEMENUNODEINFOS, ID_SLAVEEDITORCONFNODEMENUDS301PROFILE, - ID_SLAVEEDITORCONFNODEMENUDS302PROFILE, ID_SLAVEEDITORCONFNODEMENUDSOTHERPROFILE, - ID_SLAVEEDITORCONFNODEMENUADD, +[ + ID_SLAVEEDITORCONFNODEMENUNODEINFOS, ID_SLAVEEDITORCONFNODEMENUDS301PROFILE, + ID_SLAVEEDITORCONFNODEMENUDS302PROFILE, ID_SLAVEEDITORCONFNODEMENUDSOTHERPROFILE, + ID_SLAVEEDITORCONFNODEMENUADD, ] = [wx.NewId() for _init_coll_ConfNodeMenu_Items in range(5)] -[ID_SLAVEEDITORADDMENUSDOSERVER, ID_SLAVEEDITORADDMENUSDOCLIENT, - ID_SLAVEEDITORADDMENUPDOTRANSMIT, ID_SLAVEEDITORADDMENUPDORECEIVE, - ID_SLAVEEDITORADDMENUMAPVARIABLE, ID_SLAVEEDITORADDMENUUSERTYPE, +[ + ID_SLAVEEDITORADDMENUSDOSERVER, ID_SLAVEEDITORADDMENUSDOCLIENT, + ID_SLAVEEDITORADDMENUPDOTRANSMIT, ID_SLAVEEDITORADDMENUPDORECEIVE, + ID_SLAVEEDITORADDMENUMAPVARIABLE, ID_SLAVEEDITORADDMENUUSERTYPE, ] = [wx.NewId() for _init_coll_AddMenu_Items in range(6)] + class SlaveEditor(ConfTreeNodeEditor, NodeEditorTemplate): - + CONFNODEEDITOR_TABS = [ (_("CANOpen slave"), "_create_SlaveNodeEditor")] - + def _create_SlaveNodeEditor(self, prnt): self.SlaveNodeEditor = EditingPanel(prnt, self, self.Controler, self.Editable) return self.SlaveNodeEditor - + def __init__(self, parent, controler, window, editable=True): self.Editable = editable ConfTreeNodeEditor.__init__(self, parent, controler, window) NodeEditorTemplate.__init__(self, controler, window, False) - + def __del__(self): self.Controler.OnCloseEditor(self) - + def GetConfNodeMenuItems(self): if self.Editable: add_menu = [(wx.ITEM_NORMAL, (_('SDO Server'), ID_SLAVEEDITORADDMENUSDOSERVER, '', self.OnAddSDOServerMenu)), @@ -63,7 +66,7 @@ (wx.ITEM_NORMAL, (_('PDO Receive'), ID_SLAVEEDITORADDMENUPDORECEIVE, '', self.OnAddPDOReceiveMenu)), (wx.ITEM_NORMAL, (_('Map Variable'), ID_SLAVEEDITORADDMENUMAPVARIABLE, '', self.OnAddMapVariableMenu)), (wx.ITEM_NORMAL, (_('User Type'), ID_SLAVEEDITORADDMENUUSERTYPE, '', self.OnAddUserTypeMenu))] - + profile = self.Controler.GetCurrentProfileName() if profile not in ("None", "DS-301"): other_profile_text = _("%s Profile") % profile @@ -72,14 +75,14 @@ add_menu.append((wx.ITEM_NORMAL, (text, wx.NewId(), '', self.GetProfileCallBack(text)))) else: other_profile_text = _('Other Profile') - + return [(wx.ITEM_NORMAL, (_('DS-301 Profile'), ID_SLAVEEDITORCONFNODEMENUDS301PROFILE, '', self.OnCommunicationMenu)), (wx.ITEM_NORMAL, (_('DS-302 Profile'), ID_SLAVEEDITORCONFNODEMENUDS302PROFILE, '', self.OnOtherCommunicationMenu)), (wx.ITEM_NORMAL, (other_profile_text, ID_SLAVEEDITORCONFNODEMENUDSOTHERPROFILE, '', self.OnEditProfileMenu)), (wx.ITEM_SEPARATOR, None), (add_menu, (_('Add'), ID_SLAVEEDITORCONFNODEMENUADD))] return [] - + def RefreshConfNodeMenu(self, confnode_menu): if self.Editable: confnode_menu.Enable(ID_SLAVEEDITORCONFNODEMENUDSOTHERPROFILE, False) @@ -90,37 +93,37 @@ def RefreshCurrentIndexList(self): self.RefreshView() - + def RefreshBufferState(self): self.ParentWindow.RefreshTitle() self.ParentWindow.RefreshFileMenu() self.ParentWindow.RefreshEditMenu() self.ParentWindow.RefreshPageTitles() + class MasterViewer(SlaveEditor): SHOW_BASE_PARAMS = False SHOW_PARAMS = False - + def __init__(self, parent, controler, window, tagname): SlaveEditor.__init__(self, parent, controler, window, False) - + self.TagName = tagname - + def GetTagName(self): return self.TagName - + def GetCurrentNodeId(self): return None - + def GetInstancePath(self): return self.Controler.CTNFullName() + ".generated_master" - + def GetTitle(self): return self.GetInstancePath() - + def IsViewing(self, tagname): return self.GetInstancePath() == tagname def RefreshView(self): self.SlaveNodeEditor.RefreshIndexList() - diff -r 31e63e25b4cc -r 64beb9e9c749 canfestival/canfestival.py --- a/canfestival/canfestival.py Mon Aug 21 20:17:19 2017 +0000 +++ b/canfestival/canfestival.py Mon Aug 21 23:22:58 2017 +0300 @@ -23,37 +23,54 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import os, sys, shutil +import os +import sys +import shutil +import wx +from gnosis.xml.pickle import * +from gnosis.xml.pickle.util import setParanoia + import util.paths as paths - -base_folder = paths.AbsParentDir(__file__, 2) -CanFestivalPath = os.path.join(base_folder, "CanFestival-3") -sys.path.append(os.path.join(CanFestivalPath, "objdictgen")) - -import wx - -from nodelist import NodeList +from util.TranslationCatalogs import AddCatalog +from ConfigTreeNode import ConfigTreeNode +from PLCControler import \ + LOCATION_CONFNODE, \ + LOCATION_MODULE, \ + LOCATION_GROUP, \ + LOCATION_VAR_INPUT, \ + LOCATION_VAR_OUTPUT, \ + LOCATION_VAR_MEMORY + +try: + from nodelist import NodeList +except ImportError: + base_folder = paths.AbsParentDir(__file__, 2) + CanFestivalPath = os.path.join(base_folder, "CanFestival-3") + sys.path.append(os.path.join(CanFestivalPath, "objdictgen")) + + from nodelist import NodeList + + from nodemanager import NodeManager -import config_utils, gen_cfile, eds_utils +import config_utils +import gen_cfile +import eds_utils import canfestival_config as local_canfestival_config -from ConfigTreeNode import ConfigTreeNode + from commondialogs import CreateNodeDialog from subindextable import IECTypeConversion, SizeConversion - -from PLCControler import LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY from SlaveEditor import SlaveEditor, MasterViewer from NetworkEditor import NetworkEditor -from gnosis.xml.pickle import * -from gnosis.xml.pickle.util import setParanoia + +AddCatalog(os.path.join(CanFestivalPath, "objdictgen", "locale")) setParanoia(0) -from util.TranslationCatalogs import AddCatalog -AddCatalog(os.path.join(CanFestivalPath, "objdictgen", "locale")) - -#-------------------------------------------------- + +# -------------------------------------------------- # Location Tree Helper -#-------------------------------------------------- +# -------------------------------------------------- + def GetSlaveLocationTree(slave_node, current_location, name): entries = [] @@ -67,19 +84,19 @@ "size": size, "IEC_type": IECTypeConversion.get(typeinfos["name"]), "var_name": "%s_%4.4x_%2.2x" % ("_".join(name.split()), index, subindex), - "location": "%s%s"%(SizeConversion[size], ".".join(map(str, current_location + - (index, subindex)))), + "location": "%s%s" % (SizeConversion[size], ".".join(map(str, current_location + + (index, subindex)))), "description": "", "children": []}) - return {"name": name, - "type": LOCATION_CONFNODE, - "location": ".".join([str(i) for i in current_location]) + ".x", - "children": entries - } - -#-------------------------------------------------- + return {"name": name, + "type": LOCATION_CONFNODE, + "location": ".".join([str(i) for i in current_location]) + ".x", + "children": entries} + +# -------------------------------------------------- # SLAVE -#-------------------------------------------------- +# -------------------------------------------------- + class _SlaveCTN(NodeManager): XSD = """ @@ -102,7 +119,7 @@ """ - + EditorType = SlaveEditor IconPath = os.path.join(CanFestivalPath, "objdictgen", "networkedit.png") @@ -122,21 +139,21 @@ profile, filepath = dialog.GetProfile() NMT = dialog.GetNMTManagement() options = dialog.GetOptions() - self.CreateNewNode(name, # Name - will be changed at build time - id, # NodeID - will be changed at build time - "slave", # Type - description,# description - profile, # profile - filepath, # prfile filepath - NMT, # NMT - options) # options + self.CreateNewNode(name, # Name - will be changed at build time + id, # NodeID - will be changed at build time + "slave", # Type + description, # description + profile, # profile + filepath, # prfile filepath + NMT, # NMT + options) # options else: self.CreateNewNode("SlaveNode", # Name - will be changed at build time 0x00, # NodeID - will be changed at build time "slave", # Type - "", # description + "", # description "None", # profile - "", # prfile filepath + "", # prfile filepath "heartbeat", # NMT []) # options dialog.Destroy() @@ -156,48 +173,50 @@ if self._View is not None: self._View.SetBusId(self.GetCurrentLocation()) return self._View - + def _ExportSlave(self): - dialog = wx.FileDialog(self.GetCTRoot().AppFrame, - _("Choose a file"), - os.path.expanduser("~"), - "%s.eds" % self.CTNName(), + dialog = wx.FileDialog(self.GetCTRoot().AppFrame, + _("Choose a file"), + os.path.expanduser("~"), + "%s.eds" % self.CTNName(), _("EDS files (*.eds)|*.eds|All files|*.*"), - wx.SAVE|wx.OVERWRITE_PROMPT) + wx.SAVE | wx.OVERWRITE_PROMPT) if dialog.ShowModal() == wx.ID_OK: result = eds_utils.GenerateEDSFile(dialog.GetPath(), self.GetCurrentNodeCopy()) if result: self.GetCTRoot().logger.write_error(_("Error: Export slave failed\n")) - dialog.Destroy() - + dialog.Destroy() + ConfNodeMethods = [ - {"bitmap" : "ExportSlave", - "name" : _("Export slave"), - "tooltip" : _("Export CanOpen slave to EDS file"), - "method" : "_ExportSlave"}, + { + "bitmap": "ExportSlave", + "name": _("Export slave"), + "tooltip": _("Export CanOpen slave to EDS file"), + "method": "_ExportSlave" + }, ] - + def CTNTestModified(self): return self.ChangesToSave or self.OneFileHasChanged() - + def OnCTNSave(self, from_project_path=None): return self.SaveCurrentInFile(self.GetSlaveODPath()) def SetParamsAttribute(self, path, value): result = ConfigTreeNode.SetParamsAttribute(self, path, value) - + # Filter IEC_Channel and Name, that have specific behavior if path == "BaseParams.IEC_Channel" and self._View is not None: self._View.SetBusId(self.GetCurrentLocation()) - + return result - + def GetVariableLocationTree(self): current_location = self.GetCurrentLocation() - return GetSlaveLocationTree(self.CurrentNode, - self.GetCurrentLocation(), + return GetSlaveLocationTree(self.CurrentNode, + self.GetCurrentLocation(), self.BaseParams.getName()) - + def CTNGenerate_C(self, buildpath, locations): """ Generate C code @@ -214,75 +233,78 @@ current_location = self.GetCurrentLocation() # define a unique name for the generated C file prefix = "_".join(map(str, current_location)) - Gen_OD_path = os.path.join(buildpath, "OD_%s.c"%prefix ) + Gen_OD_path = os.path.join(buildpath, "OD_%s.c" % prefix) # Create a new copy of the model slave = self.GetCurrentNodeCopy() - slave.SetNodeName("OD_%s"%prefix) + slave.SetNodeName("OD_%s" % prefix) # allow access to local OD from Slave PLC pointers = config_utils.LocalODPointers(locations, current_location, slave) res = gen_cfile.GenerateFile(Gen_OD_path, slave, pointers) - if res : - raise Exception, res - res = eds_utils.GenerateEDSFile(os.path.join(buildpath, "Slave_%s.eds"%prefix), slave) - if res : - raise Exception, res - return [(Gen_OD_path,local_canfestival_config.getCFLAGS(CanFestivalPath))],"",False + if res: + raise Exception(res) + res = eds_utils.GenerateEDSFile(os.path.join(buildpath, "Slave_%s.eds" % prefix), slave) + if res: + raise Exception(res) + return [(Gen_OD_path, local_canfestival_config.getCFLAGS(CanFestivalPath))], "", False def LoadPrevious(self): self.LoadCurrentPrevious() - + def LoadNext(self): self.LoadCurrentNext() - + def GetBufferState(self): return self.GetCurrentBufferState() -#-------------------------------------------------- +# -------------------------------------------------- # MASTER -#-------------------------------------------------- +# -------------------------------------------------- + class MiniNodeManager(NodeManager): - + def __init__(self, parent, filepath, fullname): NodeManager.__init__(self) - + self.OpenFileInCurrent(filepath) - + self.Parent = parent self.Fullname = fullname - + def GetIconName(self): return None - + def OnCloseEditor(self, view): self.Parent.OnCloseEditor(view) - + def CTNFullName(self): return self.Fullname - + def CTNTestModified(self): return False - + def GetBufferState(self): return self.GetCurrentBufferState() - + ConfNodeMethods = [] + class _NodeManager(NodeManager): def __init__(self, parent, *args, **kwargs): NodeManager.__init__(self, *args, **kwargs) self.Parent = parent - + def __del__(self): self.Parent = None - + def GetCurrentNodeName(self): return self.Parent.CTNName() - + def GetCurrentNodeID(self): return self.Parent.CanFestivalNode.getNodeId() - + + class _NodeListCTN(NodeList): XSD = """ @@ -295,20 +317,20 @@ - """ - + """ + EditorType = NetworkEditor IconPath = os.path.join(CanFestivalPath, "objdictgen", "networkedit.png") - + def __init__(self): manager = _NodeManager(self) NodeList.__init__(self, manager) self.LoadProject(self.CTNPath()) self.SetNetworkName(self.BaseParams.getName()) - + def GetCanDevice(self): return self.CanFestivalNode.getCAN_Device() - + def SetParamsAttribute(self, path, value): if path == "CanFestivalNode.NodeId": nodeid = self.CanFestivalNode.getNodeId() @@ -319,10 +341,10 @@ value += dir if value < 0: value = nodeid - + value, refresh = ConfigTreeNode.SetParamsAttribute(self, path, value) refresh_network = False - + # Filter IEC_Channel and Name, that have specific behavior if path == "BaseParams.IEC_Channel" and self._View is not None: self._View.SetBusId(self.GetCurrentLocation()) @@ -331,31 +353,35 @@ refresh_network = True elif path == "CanFestivalNode.NodeId": refresh_network = True - + if refresh_network and self._View is not None: wx.CallAfter(self._View.RefreshBufferState) return value, refresh - + def GetVariableLocationTree(self): current_location = self.GetCurrentLocation() nodeindexes = self.SlaveNodes.keys() nodeindexes.sort() - return {"name": self.BaseParams.getName(), - "type": LOCATION_CONFNODE, - "location": self.GetFullIEC_Channel(), - "children": [GetSlaveLocationTree(self.Manager.GetCurrentNodeCopy(), - current_location, - _("Local entries"))] + - [GetSlaveLocationTree(self.SlaveNodes[nodeid]["Node"], - current_location + (nodeid,), - self.SlaveNodes[nodeid]["Name"]) - for nodeid in nodeindexes] + children = [] + children += [GetSlaveLocationTree(self.Manager.GetCurrentNodeCopy(), + current_location, + _("Local entries"))] + children += [GetSlaveLocationTree(self.SlaveNodes[nodeid]["Node"], + current_location + (nodeid,), + self.SlaveNodes[nodeid]["Name"]) for nodeid in nodeindexes] + + return { + "name": self.BaseParams.getName(), + "type": LOCATION_CONFNODE, + "location": self.GetFullIEC_Channel(), + "children": children } - + _GeneratedMasterView = None + def _ShowGeneratedMaster(self): self._OpenView("Generated master") - + def _OpenView(self, name=None, onlyopened=False): if name == "Generated master": app_frame = self.GetCTRoot().AppFrame @@ -365,37 +391,39 @@ if not os.path.exists(buildpath): self.GetCTRoot().logger.write_error(_("Error: No PLC built\n")) return - + masterpath = os.path.join(buildpath, "MasterGenerated.od") if not os.path.exists(masterpath): self.GetCTRoot().logger.write_error(_("Error: No Master generated\n")) return - + manager = MiniNodeManager(self, masterpath, self.CTNFullName()) self._GeneratedMasterView = MasterViewer(app_frame.TabsOpened, manager, app_frame, name) - + if self._GeneratedMasterView is not None: app_frame.EditProjectElement(self._GeneratedMasterView, self._GeneratedMasterView.GetInstancePath()) - + return self._GeneratedMasterView else: ConfigTreeNode._OpenView(self, name, onlyopened) if self._View is not None: self._View.SetBusId(self.GetCurrentLocation()) return self._View - + ConfNodeMethods = [ - {"bitmap" : "ShowMaster", - "name" : _("Show Master"), - "tooltip" : _("Show Master generated by config_utils"), - "method" : "_ShowGeneratedMaster"} + { + "bitmap": "ShowMaster", + "name": _("Show Master"), + "tooltip": _("Show Master generated by config_utils"), + "method": "_ShowGeneratedMaster" + } ] - + def OnCloseEditor(self, view): ConfigTreeNode.OnCloseEditor(self, view) if self._GeneratedMasterView == view: self._GeneratedMasterView = None - + def OnCTNClose(self): ConfigTreeNode.OnCTNClose(self) self._CloseView(self._GeneratedMasterView) @@ -403,11 +431,11 @@ def CTNTestModified(self): return self.ChangesToSave or self.HasChanged() - + def OnCTNSave(self, from_project_path=None): self.SetRoot(self.CTNPath()) if from_project_path is not None: - shutil.copytree(self.GetEDSFolder(from_project_path), + shutil.copytree(self.GetEDSFolder(from_project_path), self.GetEDSFolder()) return self.SaveProject() is None @@ -428,32 +456,33 @@ current_location = self.GetCurrentLocation() # define a unique name for the generated C file prefix = "_".join(map(str, current_location)) - Gen_OD_path = os.path.join(buildpath, "OD_%s.c"%prefix ) + Gen_OD_path = os.path.join(buildpath, "OD_%s.c" % prefix) # Create a new copy of the model with DCF loaded with PDO mappings for desired location try: - master, pointers = config_utils.GenerateConciseDCF(locations, current_location, self, self.CanFestivalNode.getSync_TPDOs(),"OD_%s"%prefix) + master, pointers = config_utils.GenerateConciseDCF(locations, current_location, self, self.CanFestivalNode.getSync_TPDOs(), "OD_%s" % prefix) except config_utils.PDOmappingException, e: - raise Exception, e.message + raise Exception(e.message) # Do generate C file. res = gen_cfile.GenerateFile(Gen_OD_path, master, pointers) - if res : - raise Exception, res - + if res: + raise Exception(res) + file = open(os.path.join(buildpath, "MasterGenerated.od"), "w") dump(master, file) file.close() - - return [(Gen_OD_path,local_canfestival_config.getCFLAGS(CanFestivalPath))],"",False - + + return [(Gen_OD_path, local_canfestival_config.getCFLAGS(CanFestivalPath))], "", False + def LoadPrevious(self): self.Manager.LoadCurrentPrevious() - + def LoadNext(self): self.Manager.LoadCurrentNext() - + def GetBufferState(self): return self.Manager.GetCurrentBufferState() - + + class RootClass: XSD = """ @@ -464,29 +493,30 @@ """ - - CTNChildrenTypes = [("CanOpenNode",_NodeListCTN, "CanOpen Master"), - ("CanOpenSlave",_SlaveCTN, "CanOpen Slave")] - def GetParamsAttributes(self, path = None): - infos = ConfigTreeNode.GetParamsAttributes(self, path = path) + + CTNChildrenTypes = [("CanOpenNode", _NodeListCTN, "CanOpen Master"), + ("CanOpenSlave", _SlaveCTN, "CanOpen Slave")] + + def GetParamsAttributes(self, path=None): + infos = ConfigTreeNode.GetParamsAttributes(self, path=path) for element in infos: if element["name"] == "CanFestivalInstance": for child in element["children"]: if child["name"] == "CAN_Driver": child["type"] = local_canfestival_config.DLL_LIST return infos - + def GetCanDriver(self): res = self.CanFestivalInstance.getCAN_Driver() - if not res : + if not res: return "" return res - + def CTNGenerate_C(self, buildpath, locations): can_driver = self.GetCanDriver() if can_driver is not None: can_drivers = local_canfestival_config.DLL_LIST - if can_driver not in can_drivers : + if can_driver not in can_drivers: can_driver = can_drivers[0] can_drv_ext = self.GetCTRoot().GetBuilder().extension can_drv_prefix = self.GetCTRoot().GetBuilder().dlopen_prefix @@ -494,106 +524,104 @@ else: can_driver_name = "" - - format_dict = {"locstr" : "_".join(map(str,self.GetCurrentLocation())), - "candriver" : can_driver_name, - "nodes_includes" : "", - "board_decls" : "", - "nodes_init" : "", - "nodes_open" : "", - "nodes_stop" : "", - "nodes_close" : "", - "nodes_send_sync" : "", - "nodes_proceed_sync" : "", - "slavebootups" : "", - "slavebootup_register" : "", - "post_sync" : "", - "post_sync_register" : "", - "pre_op" : "", - "pre_op_register" : "", + format_dict = {"locstr": "_".join(map(str, self.GetCurrentLocation())), + "candriver": can_driver_name, + "nodes_includes": "", + "board_decls": "", + "nodes_init": "", + "nodes_open": "", + "nodes_stop": "", + "nodes_close": "", + "nodes_send_sync": "", + "nodes_proceed_sync": "", + "slavebootups": "", + "slavebootup_register": "", + "post_sync": "", + "post_sync_register": "", + "pre_op": "", + "pre_op_register": "", } for child in self.IECSortedChildren(): - childlocstr = "_".join(map(str,child.GetCurrentLocation())) + childlocstr = "_".join(map(str, child.GetCurrentLocation())) nodename = "OD_%s" % childlocstr - + # Try to get Slave Node child_data = getattr(child, "CanFestivalSlaveNode", None) if child_data is None: # Not a slave -> master child_data = getattr(child, "CanFestivalNode") # Apply sync setting - format_dict["nodes_init"] += 'NODE_MASTER_INIT(%s, %s)\n '%( + format_dict["nodes_init"] += 'NODE_MASTER_INIT(%s, %s)\n ' % ( nodename, child_data.getNodeId()) if child_data.getSync_TPDOs(): - format_dict["nodes_send_sync"] += 'NODE_SEND_SYNC(%s)\n '%(nodename) - format_dict["nodes_proceed_sync"] += 'NODE_PROCEED_SYNC(%s)\n '%(nodename) + format_dict["nodes_send_sync"] += 'NODE_SEND_SYNC(%s)\n ' % (nodename) + format_dict["nodes_proceed_sync"] += 'NODE_PROCEED_SYNC(%s)\n ' % (nodename) # initialize and declare node boot status variables for post_SlaveBootup lookup SlaveIDs = child.GetSlaveIDs() if len(SlaveIDs) == 0: # define post_SlaveBootup lookup functions format_dict["slavebootups"] += ( - "static void %s_post_SlaveBootup(CO_Data* d, UNS8 nodeId){}\n"%(nodename)) + "static void %s_post_SlaveBootup(CO_Data* d, UNS8 nodeId){}\n" % (nodename)) else: format_dict["slavebootups"] += ( - "static void %s_post_SlaveBootup(CO_Data* d, UNS8 nodeId){\n"%(nodename)+ - " check_and_start_node(d, nodeId);\n"+ + "static void %s_post_SlaveBootup(CO_Data* d, UNS8 nodeId){\n" % (nodename) + + " check_and_start_node(d, nodeId);\n" + "}\n") # register previously declared func as post_SlaveBootup callback for that node format_dict["slavebootup_register"] += ( - "%s_Data.post_SlaveBootup = %s_post_SlaveBootup;\n"%(nodename,nodename)) + "%s_Data.post_SlaveBootup = %s_post_SlaveBootup;\n" % (nodename, nodename)) format_dict["pre_op"] += ( - "static void %s_preOperational(CO_Data* d){\n "%(nodename)+ - "".join([" masterSendNMTstateChange(d, %d, NMT_Reset_Comunication);\n"%NdId for NdId in SlaveIDs])+ + "static void %s_preOperational(CO_Data* d){\n " % (nodename) + + "".join([" masterSendNMTstateChange(d, %d, NMT_Reset_Comunication);\n" % NdId for NdId in SlaveIDs]) + "}\n") format_dict["pre_op_register"] += ( - "%s_Data.preOperational = %s_preOperational;\n"%(nodename,nodename)) + "%s_Data.preOperational = %s_preOperational;\n" % (nodename, nodename)) else: # Slave node align = child_data.getSync_Align() - align_ratio=child_data.getSync_Align_Ratio() + align_ratio = child_data.getSync_Align_Ratio() if align > 0: format_dict["post_sync"] += ( - "static int %s_CalCount = 0;\n"%(nodename)+ - "static void %s_post_sync(CO_Data* d){\n"%(nodename)+ - " if(%s_CalCount < %d){\n"%(nodename, align)+ - " %s_CalCount++;\n"%(nodename)+ - " align_tick(-1);\n"+ - " }else{\n"+ - " align_tick(%d);\n"%(align_ratio)+ - " }\n"+ + "static int %s_CalCount = 0;\n" % (nodename) + + "static void %s_post_sync(CO_Data* d){\n" % (nodename) + + " if(%s_CalCount < %d){\n" % (nodename, align) + + " %s_CalCount++;\n" % (nodename) + + " align_tick(-1);\n" + + " }else{\n" + + " align_tick(%d);\n" % (align_ratio) + + " }\n" + "}\n") format_dict["post_sync_register"] += ( - "%s_Data.post_sync = %s_post_sync;\n"%(nodename,nodename)) - format_dict["nodes_init"] += 'NODE_SLAVE_INIT(%s, %s)\n '%( + "%s_Data.post_sync = %s_post_sync;\n" % (nodename, nodename)) + format_dict["nodes_init"] += 'NODE_SLAVE_INIT(%s, %s)\n ' % ( nodename, child_data.getNodeId()) - + # Include generated OD headers - format_dict["nodes_includes"] += '#include "%s.h"\n'%(nodename) + format_dict["nodes_includes"] += '#include "%s.h"\n' % (nodename) # Declare CAN channels according user filled config - format_dict["board_decls"] += 'BOARD_DECL(%s, "%s", "%s")\n'%( + format_dict["board_decls"] += 'BOARD_DECL(%s, "%s", "%s")\n' % ( nodename, child.GetCanDevice(), child_data.getCAN_Baudrate()) - format_dict["nodes_open"] += 'NODE_OPEN(%s)\n '%(nodename) - format_dict["nodes_close"] += 'NODE_CLOSE(%s)\n '%(nodename) - format_dict["nodes_stop"] += 'NODE_STOP(%s)\n '%(nodename) - - filename = paths.AbsNeighbourFile(__file__,"cf_runtime.c") + format_dict["nodes_open"] += 'NODE_OPEN(%s)\n ' % (nodename) + format_dict["nodes_close"] += 'NODE_CLOSE(%s)\n ' % (nodename) + format_dict["nodes_stop"] += 'NODE_STOP(%s)\n ' % (nodename) + + filename = paths.AbsNeighbourFile(__file__, "cf_runtime.c") cf_main = open(filename).read() % format_dict - cf_main_path = os.path.join(buildpath, "CF_%(locstr)s.c"%format_dict) - f = open(cf_main_path,'w') + cf_main_path = os.path.join(buildpath, "CF_%(locstr)s.c" % format_dict) + f = open(cf_main_path, 'w') f.write(cf_main) f.close() - res = [(cf_main_path, local_canfestival_config.getCFLAGS(CanFestivalPath))],local_canfestival_config.getLDFLAGS(CanFestivalPath), True - + res = [(cf_main_path, local_canfestival_config.getCFLAGS(CanFestivalPath))], local_canfestival_config.getLDFLAGS(CanFestivalPath), True + if can_driver is not None: - can_driver_path = os.path.join(CanFestivalPath,"drivers",can_driver,can_driver_name) + can_driver_path = os.path.join(CanFestivalPath, "drivers", can_driver, can_driver_name) if os.path.exists(can_driver_path): - res += ((can_driver_name, file(can_driver_path,"rb")),) + res += ((can_driver_name, file(can_driver_path, "rb")),) return res - diff -r 31e63e25b4cc -r 64beb9e9c749 canfestival/config_utils.py --- a/canfestival/config_utils.py Mon Aug 21 20:17:19 2017 +0000 +++ b/canfestival/config_utils.py Mon Aug 21 23:22:58 2017 +0300 @@ -25,30 +25,46 @@ from types import * # Translation between IEC types and Can Open types -IECToCOType = {"BOOL":0x01, "SINT":0x02, "INT":0x03,"DINT":0x04,"LINT":0x10, - "USINT":0x05,"UINT":0x06,"UDINT":0x07,"ULINT":0x1B,"REAL":0x08, - "LREAL":0x11,"STRING":0x09,"BYTE":0x05,"WORD":0x06,"DWORD":0x07, - "LWORD":0x1B,"WSTRING":0x0B} - -# Constants for PDO types +IECToCOType = { + "BOOL": 0x01, + "SINT": 0x02, + "INT": 0x03, + "DINT": 0x04, + "LINT": 0x10, + "USINT": 0x05, + "UINT": 0x06, + "UDINT": 0x07, + "ULINT": 0x1B, + "REAL": 0x08, + "LREAL": 0x11, + "STRING": 0x09, + "BYTE": 0x05, + "WORD": 0x06, + "DWORD": 0x07, + "LWORD": 0x1B, + "WSTRING": 0x0B +} + +# Constants for PDO types RPDO = 1 TPDO = 2 -SlavePDOType = {"I" : TPDO, "Q" : RPDO} -InvertPDOType = {RPDO : TPDO, TPDO : RPDO} -PDOTypeBaseIndex = {RPDO : 0x1400, TPDO : 0x1800} -PDOTypeBaseCobId = {RPDO : 0x200, TPDO : 0x180} +SlavePDOType = {"I": TPDO, "Q": RPDO} +InvertPDOType = {RPDO: TPDO, TPDO: RPDO} +PDOTypeBaseIndex = {RPDO: 0x1400, TPDO: 0x1800} +PDOTypeBaseCobId = {RPDO: 0x200, TPDO: 0x180} VariableIncrement = 0x100 -VariableStartIndex = {TPDO : 0x2000, RPDO : 0x4000} -VariableDirText = {TPDO : "__I", RPDO : "__Q"} -VariableTypeOffset = dict(zip(["","X","B","W","D","L"], range(6))) +VariableStartIndex = {TPDO: 0x2000, RPDO: 0x4000} +VariableDirText = {TPDO: "__I", RPDO: "__Q"} +VariableTypeOffset = dict(zip(["", "X", "B", "W", "D", "L"], range(6))) TrashVariables = [(1, 0x01), (8, 0x05), (16, 0x06), (32, 0x07), (64, 0x1B)] -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Specific exception for PDO mapping errors -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + class PDOmappingException(Exception): pass @@ -61,22 +77,22 @@ @param size: number of bytes generated @return: a string containing the value converted """ - + data = ("%" + str(size * 2) + "." + str(size * 2) + "X") % value list_car = [data[i:i+2] for i in xrange(0, len(data), 2)] list_car.reverse() return "".join([chr(int(car, 16)) for car in list_car]) -def GetNodePDOIndexes(node, type, parameters = False): +def GetNodePDOIndexes(node, type, parameters=False): """ Find the PDO indexes of a node - @param node: node + @param node: node @param type: type of PDO searched (RPDO or TPDO or both) @param parameters: indicate which indexes are expected (PDO paramaters : True or PDO mappings : False) @return: a list of indexes found """ - + indexes = [] if type & RPDO: indexes.extend([idx for idx in node.GetIndexes() if 0x1400 <= idx <= 0x15FF]) @@ -91,17 +107,17 @@ def SearchNodePDOMapping(loc_infos, node): """ Find the PDO indexes of a node - @param node: node + @param node: node @param type: type of PDO searched (RPDO or TPDO or both) @param parameters: indicate which indexes are expected (PDO paramaters : True or PDO mappings : False) @return: a list of indexes found """ - + model = (loc_infos["index"] << 16) + (loc_infos["subindex"] << 8) - + for PDOidx in GetNodePDOIndexes(node, loc_infos["pdotype"]): values = node.GetEntry(PDOidx) - if values != None: + if values is not None: for subindex, mapping in enumerate(values): if subindex != 0 and mapping & 0xFFFFFF00 == model: return PDOidx, subindex @@ -115,10 +131,10 @@ @param cobid: PDO generated COB ID @param transmittype : PDO transmit type @param pdomapping: list of PDO mappings - @return: a tuple of value and number of parameters to add to DCF - """ - - dcfdata=[] + @return: a tuple of value and number of parameters to add to DCF + """ + + dcfdata = [] # Create entry for RPDO or TPDO parameters and Disable PDO # ---- INDEX ----- --- SUBINDEX ---- ----- SIZE ------ ------ DATA ------ dcfdata += [LE_to_BE(idx, 2) + LE_to_BE(0x01, 1) + LE_to_BE(0x04, 4) + LE_to_BE(0x80000000 + cobid, 4)] @@ -137,6 +153,7 @@ dcfdata += [LE_to_BE(idx, 2) + LE_to_BE(0x01, 1) + LE_to_BE(0x04, 4) + LE_to_BE(cobid, 4)] return "".join(dcfdata), len(dcfdata) + class ConciseDCFGenerator: def __init__(self, nodelist, nodename): @@ -152,7 +169,7 @@ self.TrashVariables = {} # Dictionary of pointed variables self.PointedVariables = {} - + self.NodeList = nodelist self.Manager = self.NodeList.Manager self.MasterNode = self.Manager.GetCurrentNodeCopy() @@ -161,41 +178,40 @@ def GetPointedVariables(self): return self.PointedVariables - + def RemoveUsedNodeCobId(self, node): """ Remove all PDO COB ID used by the given node from the list of available COB ID @param node: node @return: a tuple of number of RPDO and TPDO for the node """ - + # Get list of all node TPDO and RPDO indexes nodeRpdoIndexes = GetNodePDOIndexes(node, RPDO, True) nodeTpdoIndexes = GetNodePDOIndexes(node, TPDO, True) - + # Mark all the COB ID of the node already mapped PDO as not available for PdoIdx in nodeRpdoIndexes + nodeTpdoIndexes: pdo_cobid = node.GetEntry(PdoIdx, 0x01) # Extract COB ID, if PDO isn't active - if pdo_cobid > 0x600 : + if pdo_cobid > 0x600: pdo_cobid -= 0x80000000 # Remove COB ID from the list of available COB ID if pdo_cobid in self.ListCobIDAvailable: self.ListCobIDAvailable.remove(pdo_cobid) - + return len(nodeRpdoIndexes), len(nodeTpdoIndexes) - def PrepareMasterNode(self): """ Add mandatory entries for DCF generation into MasterNode. """ - + # Adding DCF entry into Master node if not self.MasterNode.IsEntry(0x1F22): self.MasterNode.AddEntry(0x1F22, 1, "") self.Manager.AddSubentriesToCurrent(0x1F22, 127, self.MasterNode) - + # Adding trash mappable variables for unused mapped datas idxTrashVariables = 0x2000 + self.MasterNode.GetNodeID() # Add an entry for storing unexpected all variable @@ -206,40 +222,39 @@ self.Manager.SetCurrentEntry(idxTrashVariables, subidx + 1, typeidx, "type", None, self.MasterNode) # Store the mapping value for this entry self.TrashVariables[size] = (idxTrashVariables << 16) + ((subidx + 1) << 8) + size - + RPDOnumber, TPDOnumber = self.RemoveUsedNodeCobId(self.MasterNode) - + # Store the indexes of the first RPDO and TPDO available for MasterNode - self.CurrentPDOParamsIdx = {RPDO : 0x1400 + RPDOnumber, TPDO : 0x1800 + TPDOnumber} + self.CurrentPDOParamsIdx = {RPDO: 0x1400 + RPDOnumber, TPDO: 0x1800 + TPDOnumber} # Prepare MasterNode with all nodelist slaves for idx, (nodeid, nodeinfos) in enumerate(self.NodeList.SlaveNodes.items()): node = nodeinfos["Node"] node.SetNodeID(nodeid) - + RPDOnumber, TPDOnumber = self.RemoveUsedNodeCobId(node) - + # Get Slave's default SDO server parameters - RSDO_cobid = node.GetEntry(0x1200,0x01) + RSDO_cobid = node.GetEntry(0x1200, 0x01) if not RSDO_cobid: RSDO_cobid = 0x600 + nodeid - TSDO_cobid = node.GetEntry(0x1200,0x02) + TSDO_cobid = node.GetEntry(0x1200, 0x02) if not TSDO_cobid: TSDO_cobid = 0x580 + nodeid - + # Configure Master's SDO parameters entries self.Manager.ManageEntriesOfCurrent([0x1280 + idx], [], self.MasterNode) self.MasterNode.SetEntry(0x1280 + idx, 0x01, RSDO_cobid) self.MasterNode.SetEntry(0x1280 + idx, 0x02, TSDO_cobid) - self.MasterNode.SetEntry(0x1280 + idx, 0x03, nodeid) - - + self.MasterNode.SetEntry(0x1280 + idx, 0x03, nodeid) + def GetMasterNode(self): """ Return MasterNode. """ return self.MasterNode - + def AddParamsToDCF(self, nodeid, data, nbparams): """ Add entry to DCF, for the requested nodeID @@ -249,19 +264,19 @@ """ # Get current DCF for slave nodeDCF = self.MasterNode.GetEntry(0x1F22, nodeid) - + # Extract data and number of params in current DCF - if nodeDCF != None and nodeDCF != '': + if nodeDCF is not None and nodeDCF != '': tmpnbparams = [i for i in nodeDCF[:4]] tmpnbparams.reverse() - nbparams += int(''.join(["%2.2x"%ord(i) for i in tmpnbparams]), 16) + nbparams += int(''.join(["%2.2x" % ord(i) for i in tmpnbparams]), 16) data = nodeDCF[4:] + data - + # Build new DCF dcf = LE_to_BE(nbparams, 0x04) + data # Set new DCF for slave self.MasterNode.SetEntry(0x1F22, nodeid, dcf) - + def GetEmptyPDO(self, nodeid, pdotype, start_index=None): """ Search a not configured PDO for a slave @@ -275,12 +290,12 @@ index = PDOTypeBaseIndex[pdotype] else: index = start_index - + # Search for all PDO possible index until find a configurable PDO # starting from start_index while index < PDOTypeBaseIndex[pdotype] + 0x200: values = self.NodeList.GetSlaveNodeEntry(nodeid, index + 0x200) - if values != None and values[0] > 0: + if values is not None and values[0] > 0: # Check that all subindex upper than 0 equal 0 => configurable PDO if reduce(lambda x, y: x and y, map(lambda x: x == 0, values[1:]), True): cobid = self.NodeList.GetSlaveNodeEntry(nodeid, index, 1) @@ -296,7 +311,7 @@ return index, cobid, values[0] index += 1 return None - + def AddPDOMapping(self, nodeid, pdotype, pdoindex, pdocobid, pdomapping, sync_TPDOs): """ Record a new mapping request for a slave, and add related slave config to the DCF @@ -305,16 +320,18 @@ @param pdomapping: list od variables to map with PDO """ # Add an entry to MasterMapping - self.MasterMapping[pdocobid] = {"type" : InvertPDOType[pdotype], - "mapping" : [None] + [(loc_infos["type"], name) for name, loc_infos in pdomapping]} - + self.MasterMapping[pdocobid] = { + "type": InvertPDOType[pdotype], + "mapping": [None] + [(loc_infos["type"], name) for name, loc_infos in pdomapping] + } + # Return the data to add to DCF if sync_TPDOs: return GeneratePDOMappingDCF(pdoindex, pdocobid, 0x01, pdomapping) else: return GeneratePDOMappingDCF(pdoindex, pdocobid, 0xFF, pdomapping) return 0, "" - + def GenerateDCF(self, locations, current_location, sync_TPDOs): """ Generate Concise DCF of MasterNode for the locations list given @@ -322,87 +339,101 @@ @param current_location: tuple of the located prefixes not to be considered @param sync_TPDOs: indicate if TPDO must be synchronous """ - - #------------------------------------------------------------------------------- + + # ------------------------------------------------------------------------------- # Verify that locations correspond to real slave variables - #------------------------------------------------------------------------------- - + # ------------------------------------------------------------------------------- + # Get list of locations check if exists and mappables -> put them in IECLocations for location in locations: COlocationtype = IECToCOType[location["IEC_TYPE"]] name = location["NAME"] if name in self.IECLocations: if self.IECLocations[name]["type"] != COlocationtype: - raise PDOmappingException, _("Type conflict for location \"%s\"") % name + raise PDOmappingException(_("Type conflict for location \"%s\"") % name) else: # Get only the part of the location that concern this node loc = location["LOC"][len(current_location):] # loc correspond to (ID, INDEX, SUBINDEX [,BIT]) if len(loc) not in (2, 3, 4): - raise PDOmappingException, _("Bad location size : %s") % str(loc) + raise PDOmappingException(_("Bad location size : %s") % str(loc)) elif len(loc) == 2: continue - + direction = location["DIR"] - + sizelocation = location["SIZE"] - + # Extract and check nodeid nodeid, index, subindex = loc[:3] - + # Check Id is in slave node list if nodeid not in self.NodeList.SlaveNodes.keys(): - raise PDOmappingException, _("Non existing node ID : {a1} (variable {a2})").format(a1 = nodeid, a2 = name) - + raise PDOmappingException( + _("Non existing node ID : {a1} (variable {a2})"). + format(a1=nodeid, a2=name)) + # Get the model for this node (made from EDS) node = self.NodeList.SlaveNodes[nodeid]["Node"] - + # Extract and check index and subindex if not node.IsEntry(index, subindex): msg = _("No such index/subindex ({a1},{a2}) in ID : {a3} (variable {a4})").\ - format(a1 = "%x" % index, a2 ="%x" % subindex, a3 = nodeid, a4 = name) - raise PDOmappingException, msg - + format(a1="%x" % index, a2="%x" % subindex, a3=nodeid, a4=name) + raise PDOmappingException(msg) + # Get the entry info subentry_infos = node.GetSubentryInfos(index, subindex) - + # If a PDO mappable if subentry_infos and subentry_infos["pdo"]: if sizelocation == "X" and len(loc) > 3: numbit = loc[3] elif sizelocation != "X" and len(loc) > 3: - msg = _("Cannot set bit offset for non bool '{a1}' variable (ID:{a2},Idx:{a3},sIdx:{a4}))").\ - format(a1 = name, a2 = nodeid, a3 = "%x" % index, a4 = "%x" % subindex) - raise PDOmappingException, msg + raise PDOmappingException( + _("Cannot set bit offset for non bool '{a1}' variable (ID:{a2},Idx:{a3},sIdx:{a4}))"). + format(a1=name, a2=nodeid, a3="%x" % index, a4="%x" % subindex)) else: numbit = None - + if location["IEC_TYPE"] != "BOOL" and subentry_infos["type"] != COlocationtype: - raise PDOmappingException, _("Invalid type \"{a1}\"-> {a2} != {a3} for location \"{a4}\"").\ - format(a1 = location["IEC_TYPE"], a2 = COlocationtype, a3 = subentry_infos["type"] , a4 = name) - + raise PDOmappingException( + _("Invalid type \"{a1}\"-> {a2} != {a3} for location \"{a4}\""). + format(a1=location["IEC_TYPE"], + a2=COlocationtype, + a3=subentry_infos["type"], + a4=name)) + typeinfos = node.GetEntryInfos(COlocationtype) - self.IECLocations[name] = {"type":COlocationtype, "pdotype":SlavePDOType[direction], - "nodeid": nodeid, "index": index,"subindex": subindex, - "bit": numbit, "size": typeinfos["size"], "sizelocation": sizelocation} + self.IECLocations[name] = { + "type": COlocationtype, + "pdotype": SlavePDOType[direction], + "nodeid": nodeid, + "index": index, + "subindex": subindex, + "bit": numbit, + "size": typeinfos["size"], + "sizelocation": sizelocation + } else: - raise PDOmappingException, _("Not PDO mappable variable : '{a1}' (ID:{a2},Idx:{a3},sIdx:{a4}))").\ - format(a1 = name, a2 = nodeid, a3 = "%x" % index, a4 = "%x" % subindex) - - #------------------------------------------------------------------------------- + raise PDOmappingException( + _("Not PDO mappable variable : '{a1}' (ID:{a2},Idx:{a3},sIdx:{a4}))"). + format(a1=name, a2=nodeid, a3="%x" % index, a4="%x" % subindex)) + + # ------------------------------------------------------------------------------- # Search for locations already mapped - #------------------------------------------------------------------------------- - + # ------------------------------------------------------------------------------- + for name, locationinfos in self.IECLocations.items(): node = self.NodeList.SlaveNodes[locationinfos["nodeid"]]["Node"] - + # Search if slave has a PDO mapping this locations result = SearchNodePDOMapping(locationinfos, node) - if result != None: + if result is not None: index, subindex = result # Get COB ID of the PDO cobid = self.NodeList.GetSlaveNodeEntry(locationinfos["nodeid"], index - 0x200, 1) - + # Add PDO to MasterMapping if cobid not in self.MasterMapping.keys(): # Verify that PDO transmit type is conform to sync_TPDOs @@ -414,18 +445,18 @@ else: # Change TransmitType to ASYCHRONE data, nbparams = GeneratePDOMappingDCF(index - 0x200, cobid, 0xFF, []) - - # Add entry to slave dcf to change transmit type of + + # Add entry to slave dcf to change transmit type of self.AddParamsToDCF(locationinfos["nodeid"], data, nbparams) - + mapping = [None] values = node.GetEntry(index) # Store the size of each entry mapped in PDO for value in values[1:]: if value != 0: mapping.append(value % 0x100) - self.MasterMapping[cobid] = {"type" : InvertPDOType[locationinfos["pdotype"]], "mapping" : mapping} - + self.MasterMapping[cobid] = {"type": InvertPDOType[locationinfos["pdotype"]], "mapping": mapping} + # Indicate that this PDO entry must be saved if locationinfos["bit"] is not None: if not isinstance(self.MasterMapping[cobid]["mapping"][subindex], ListType): @@ -434,24 +465,24 @@ self.MasterMapping[cobid]["mapping"][subindex][locationinfos["bit"]] = (locationinfos["type"], name) else: self.MasterMapping[cobid]["mapping"][subindex] = (locationinfos["type"], name) - + else: # Add location to those that haven't been mapped yet if locationinfos["nodeid"] not in self.LocationsNotMapped.keys(): - self.LocationsNotMapped[locationinfos["nodeid"]] = {TPDO : [], RPDO : []} + self.LocationsNotMapped[locationinfos["nodeid"]] = {TPDO: [], RPDO: []} self.LocationsNotMapped[locationinfos["nodeid"]][locationinfos["pdotype"]].append((name, locationinfos)) - - #------------------------------------------------------------------------------- + + # ------------------------------------------------------------------------------- # Build concise DCF for the others locations - #------------------------------------------------------------------------------- - + # ------------------------------------------------------------------------------- + for nodeid, locations in self.LocationsNotMapped.items(): node = self.NodeList.SlaveNodes[nodeid]["Node"] - + # Initialize number of params and data to add to node DCF nbparams = 0 dataparams = "" - + # Generate the best PDO mapping for each type of PDO for pdotype in (TPDO, RPDO): if len(locations[pdotype]) > 0: @@ -459,7 +490,8 @@ pdomapping = [] result = self.GetEmptyPDO(nodeid, pdotype) if result is None: - raise PDOmappingException, _("Unable to define PDO mapping for node %02x") % nodeid + raise PDOmappingException( + _("Unable to define PDO mapping for node %02x") % nodeid) pdoindex, pdocobid, pdonbparams = result for name, loc_infos in locations[pdotype]: pdosize += loc_infos["size"] @@ -473,7 +505,8 @@ pdomapping = [(name, loc_infos)] result = self.GetEmptyPDO(nodeid, pdotype, pdoindex + 1) if result is None: - raise PDOmappingException, _("Unable to define PDO mapping for node %02x") % nodeid + raise PDOmappingException( + _("Unable to define PDO mapping for node %02x") % nodeid) pdoindex, pdocobid, pdonbparams = result else: pdomapping.append((name, loc_infos)) @@ -483,78 +516,79 @@ data, nbaddedparams = self.AddPDOMapping(nodeid, pdotype, pdoindex, pdocobid, pdomapping, sync_TPDOs) dataparams += data nbparams += nbaddedparams - + # Add number of params and data to node DCF self.AddParamsToDCF(nodeid, dataparams, nbparams) - - #------------------------------------------------------------------------------- + + # ------------------------------------------------------------------------------- # Master Node Configuration - #------------------------------------------------------------------------------- - + # ------------------------------------------------------------------------------- + # Generate Master's Configuration from informations stored in MasterMapping for cobid, pdo_infos in self.MasterMapping.items(): # Get next PDO index in MasterNode for this PDO type current_idx = self.CurrentPDOParamsIdx[pdo_infos["type"]] - + # Search if there is already a PDO in MasterNode with this cob id for idx in GetNodePDOIndexes(self.MasterNode, pdo_infos["type"], True): if self.MasterNode.GetEntry(idx, 1) == cobid: current_idx = idx - + # Add a PDO to MasterNode if not PDO have been found if current_idx == self.CurrentPDOParamsIdx[pdo_infos["type"]]: addinglist = [current_idx, current_idx + 0x200] self.Manager.ManageEntriesOfCurrent(addinglist, [], self.MasterNode) self.MasterNode.SetEntry(current_idx, 0x01, cobid) - + # Increment the number of PDO for this PDO type self.CurrentPDOParamsIdx[pdo_infos["type"]] += 1 - + # Change the transmit type of the PDO if sync_TPDOs: self.MasterNode.SetEntry(current_idx, 0x02, 0x01) else: self.MasterNode.SetEntry(current_idx, 0x02, 0xFF) - + mapping = [] for item in pdo_infos["mapping"]: if isinstance(item, ListType): mapping.extend(item) else: mapping.append(item) - + # Add some subentries to PDO mapping if there is not enough if len(mapping) > 1: self.Manager.AddSubentriesToCurrent(current_idx + 0x200, len(mapping) - 1, self.MasterNode) - + # Generate MasterNode's PDO mapping for subindex, variable in enumerate(mapping): if subindex == 0: continue new_index = False - + if isinstance(variable, (IntType, LongType)): # If variable is an integer then variable is unexpected self.MasterNode.SetEntry(current_idx + 0x200, subindex, self.TrashVariables[variable]) else: typeidx, varname = variable variable_infos = self.IECLocations[varname] - + # Calculate base index for storing variable - mapvariableidx = VariableStartIndex[variable_infos["pdotype"]] + \ - VariableTypeOffset[variable_infos["sizelocation"]] * VariableIncrement + \ - variable_infos["nodeid"] - + mapvariableidx = \ + VariableStartIndex[variable_infos["pdotype"]] + \ + VariableTypeOffset[variable_infos["sizelocation"]] * VariableIncrement + \ + variable_infos["nodeid"] + # Generate entry name - indexname = "%s%s%s_%d"%(VariableDirText[variable_infos["pdotype"]], - variable_infos["sizelocation"], - '_'.join(map(str,current_location)), - variable_infos["nodeid"]) - - # Search for an entry that has an empty subindex + indexname = "%s%s%s_%d" % (VariableDirText[variable_infos["pdotype"]], + variable_infos["sizelocation"], + '_'.join(map(str, current_location)), + variable_infos["nodeid"]) + + # Search for an entry that has an empty subindex while mapvariableidx < VariableStartIndex[variable_infos["pdotype"]] + 0x2000: # Entry doesn't exist - if not self.MasterNode.IsEntry(mapvariableidx): + if not self.MasterNode.IsEntry(mapvariableidx): # Add entry to MasterNode self.Manager.AddMapVariableToCurrent(mapvariableidx, "beremiz"+indexname, 3, 1, self.MasterNode) new_index = True @@ -567,30 +601,31 @@ mapvariableidx += 8 * VariableIncrement else: break - + # Verify that a not full entry has been found if mapvariableidx < VariableStartIndex[variable_infos["pdotype"]] + 0x2000: # Generate subentry name - if variable_infos["bit"] != None: - subindexname = "%(index)d_%(subindex)d_%(bit)d"%variable_infos + if variable_infos["bit"] is not None: + subindexname = "%(index)d_%(subindex)d_%(bit)d" % variable_infos else: - subindexname = "%(index)d_%(subindex)d"%variable_infos + subindexname = "%(index)d_%(subindex)d" % variable_infos # If entry have just been created, no subentry have to be added if not new_index: self.Manager.AddSubentriesToCurrent(mapvariableidx, 1, self.MasterNode) nbsubentries += 1 # Add informations to the new subentry created - self.MasterNode.SetMappingEntry(mapvariableidx, nbsubentries, values = {"name" : subindexname}) - self.MasterNode.SetMappingEntry(mapvariableidx, nbsubentries, values = {"type" : typeidx}) - + self.MasterNode.SetMappingEntry(mapvariableidx, nbsubentries, values={"name": subindexname}) + self.MasterNode.SetMappingEntry(mapvariableidx, nbsubentries, values={"type": typeidx}) + # Set value of the PDO mapping typeinfos = self.Manager.GetEntryInfos(typeidx) - if typeinfos != None: + if typeinfos is not None: value = (mapvariableidx << 16) + ((nbsubentries) << 8) + typeinfos["size"] self.MasterNode.SetEntry(current_idx + 0x200, subindex, value) - + # Add variable to pointed variables - self.PointedVariables[(mapvariableidx, nbsubentries)] = "%s_%s"%(indexname, subindexname) + self.PointedVariables[(mapvariableidx, nbsubentries)] = "%s_%s" % (indexname, subindexname) + def GenerateConciseDCF(locations, current_location, nodelist, sync_TPDOs, nodename): """ @@ -605,13 +640,14 @@ @param nodelist: CanFestival network editor model @return: a modified copy of the given CanFestival network editor model """ - + dcfgenerator = ConciseDCFGenerator(nodelist, nodename) dcfgenerator.GenerateDCF(locations, current_location, sync_TPDOs) - masternode,pointers = dcfgenerator.GetMasterNode(), dcfgenerator.GetPointedVariables() + masternode, pointers = dcfgenerator.GetMasterNode(), dcfgenerator.GetPointedVariables() # allow access to local OD from Master PLC pointers.update(LocalODPointers(locations, current_location, masternode)) - return masternode,pointers + return masternode, pointers + def LocalODPointers(locations, current_location, slave): IECLocations = {} @@ -621,36 +657,44 @@ name = location["NAME"] if name in IECLocations: if IECLocations[name] != COlocationtype: - raise PDOmappingException, _("Type conflict for location \"%s\"") % name + raise PDOmappingException(_("Type conflict for location \"%s\"") % name) else: # Get only the part of the location that concern this node loc = location["LOC"][len(current_location):] # loc correspond to (ID, INDEX, SUBINDEX [,BIT]) if len(loc) not in (2, 3, 4): - raise PDOmappingException, _("Bad location size : %s") % str(loc) + raise PDOmappingException(_("Bad location size : %s") % str(loc)) elif len(loc) != 2: continue - + # Extract and check nodeid index, subindex = loc[:2] - + # Extract and check index and subindex if not slave.IsEntry(index, subindex): - raise PDOmappingException, _("No such index/subindex ({a1},{a2}) (variable {a3})").\ - format(a1 = "%x" % index, a2 = "%x" % subindex, a3 = name) - + raise PDOmappingException( + _("No such index/subindex ({a1},{a2}) (variable {a3})"). + format(a1="%x" % index, a2="%x" % subindex, a3=name)) + # Get the entry info - subentry_infos = slave.GetSubentryInfos(index, subindex) + subentry_infos = slave.GetSubentryInfos(index, subindex) if subentry_infos["type"] != COlocationtype: - raise PDOmappingException, _("Invalid type \"{a1}\"-> {a2} != {a3} for location \"{a4}\"").\ - format( a1 = location["IEC_TYPE"], a2 = COlocationtype, a3 = subentry_infos["type"] , a4 = name) - + raise PDOmappingException( + _("Invalid type \"{a1}\"-> {a2} != {a3} for location \"{a4}\""). + format(a1=location["IEC_TYPE"], + a2=COlocationtype, + a3=subentry_infos["type"], + a4=name)) + IECLocations[name] = COlocationtype pointers[(index, subindex)] = name return pointers - + + if __name__ == "__main__": - import os, sys, getopt + import os + import sys + import getopt def usage(): print """ @@ -666,14 +710,14 @@ Reset the reference result of config_utils test. Use with caution. Be sure that config_utils is currently working properly. -"""%sys.argv[0] - +""" % sys.argv[0] + # Boolean that indicate if reference result must be redefined reset = False # Extract command options try: - opts, args = getopt.getopt(sys.argv[1:], "hr", ["help","reset"]) + opts, args = getopt.getopt(sys.argv[1:], "hr", ["help", "reset"]) except getopt.GetoptError: # print help information and exit: usage() @@ -693,54 +737,56 @@ base_folder = os.path.split(base_folder)[0] # Add CanFestival folder to search pathes sys.path.append(os.path.join(base_folder, "CanFestival-3", "objdictgen")) - + from nodemanager import * from nodelist import * - + # Open the test nodelist contained into test_config folder manager = NodeManager() nodelist = NodeList(manager) result = nodelist.LoadProject("test_config") - + # List of locations, we try to map for test - locations = [{"IEC_TYPE":"BYTE","NAME":"__IB0_1_64_24576_1","DIR":"I","SIZE":"B","LOC":(0,1,64,24576,1)}, - {"IEC_TYPE":"INT","NAME":"__IW0_1_64_25601_2","DIR":"I","SIZE":"W","LOC":(0,1,64,25601,2)}, - {"IEC_TYPE":"INT","NAME":"__IW0_1_64_25601_3","DIR":"I","SIZE":"W","LOC":(0,1,64,25601,3)}, - {"IEC_TYPE":"INT","NAME":"__QW0_1_64_25617_2","DIR":"Q","SIZE":"W","LOC":(0,1,64,25617,1)}, - {"IEC_TYPE":"BYTE","NAME":"__IB0_1_64_24578_1","DIR":"I","SIZE":"B","LOC":(0,1,64,24578,1)}, - {"IEC_TYPE":"UDINT","NAME":"__ID0_1_64_25638_1","DIR":"I","SIZE":"D","LOC":(0,1,64,25638,1)}, - {"IEC_TYPE":"UDINT","NAME":"__ID0_1_64_25638_2","DIR":"I","SIZE":"D","LOC":(0,1,64,25638,2)}, - {"IEC_TYPE":"UDINT","NAME":"__ID0_1_64_25638_3","DIR":"I","SIZE":"D","LOC":(0,1,64,25638,3)}, - {"IEC_TYPE":"UDINT","NAME":"__ID0_1_64_25638_4","DIR":"I","SIZE":"D","LOC":(0,1,64,25638,4)}, - {"IEC_TYPE":"UDINT","NAME":"__ID0_1_4096_0","DIR":"I","SIZE":"D","LOC":(0,1,4096,0)}] - + locations = [ + {"IEC_TYPE": "BYTE", "NAME": "__IB0_1_64_24576_1", "DIR": "I", "SIZE": "B", "LOC": (0, 1, 64, 24576, 1)}, + {"IEC_TYPE": "INT", "NAME": "__IW0_1_64_25601_2", "DIR": "I", "SIZE": "W", "LOC": (0, 1, 64, 25601, 2)}, + {"IEC_TYPE": "INT", "NAME": "__IW0_1_64_25601_3", "DIR": "I", "SIZE": "W", "LOC": (0, 1, 64, 25601, 3)}, + {"IEC_TYPE": "INT", "NAME": "__QW0_1_64_25617_2", "DIR": "Q", "SIZE": "W", "LOC": (0, 1, 64, 25617, 1)}, + {"IEC_TYPE": "BYTE", "NAME": "__IB0_1_64_24578_1", "DIR": "I", "SIZE": "B", "LOC": (0, 1, 64, 24578, 1)}, + {"IEC_TYPE": "UDINT", "NAME": "__ID0_1_64_25638_1", "DIR": "I", "SIZE": "D", "LOC": (0, 1, 64, 25638, 1)}, + {"IEC_TYPE": "UDINT", "NAME": "__ID0_1_64_25638_2", "DIR": "I", "SIZE": "D", "LOC": (0, 1, 64, 25638, 2)}, + {"IEC_TYPE": "UDINT", "NAME": "__ID0_1_64_25638_3", "DIR": "I", "SIZE": "D", "LOC": (0, 1, 64, 25638, 3)}, + {"IEC_TYPE": "UDINT", "NAME": "__ID0_1_64_25638_4", "DIR": "I", "SIZE": "D", "LOC": (0, 1, 64, 25638, 4)}, + {"IEC_TYPE": "UDINT", "NAME": "__ID0_1_4096_0", "DIR": "I", "SIZE": "D", "LOC": (0, 1, 4096, 0)} + ] + # Generate MasterNode configuration try: masternode, pointedvariables = GenerateConciseDCF(locations, (0, 1), nodelist, True, "TestNode") except ValueError, message: - print "%s\nTest Failed!"%message + print "%s\nTest Failed!" % message sys.exit() - + import pprint - # Get Text corresponding to MasterNode + # Get Text corresponding to MasterNode result_node = masternode.PrintString() result_vars = pprint.pformat(pointedvariables) result = result_node + "\n********POINTERS*********\n" + result_vars + "\n" - + # If reset has been choosen if reset: # Write Text into reference result file testfile = open("test_config/result.txt", "w") testfile.write(result) testfile.close() - + print "Reset Successful!" else: import os - + testfile = open("test_config/result_tmp.txt", "w") testfile.write(result) testfile.close() - + os.system("diff test_config/result.txt test_config/result_tmp.txt") os.remove("test_config/result_tmp.txt") diff -r 31e63e25b4cc -r 64beb9e9c749 connectors/PYRO/__init__.py --- a/connectors/PYRO/__init__.py Mon Aug 21 20:17:19 2017 +0000 +++ b/connectors/PYRO/__init__.py Mon Aug 21 23:22:58 2017 +0300 @@ -1,199 +1,202 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# This file is part of Beremiz, a Integrated Development Environment for -# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. -# -# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD -# -# See COPYING file for copyrights details. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -import Pyro -import Pyro.core -import Pyro.util -from Pyro.errors import PyroError -import traceback -from time import sleep -import copy -import socket -service_type = '_PYRO._tcp.local.' -import os.path -# this module attribute contains a list of DNS-SD (Zeroconf) service types -# supported by this connector confnode. -# -# for connectors that do not support DNS-SD, this attribute can be omitted -# or set to an empty list. - -def PYRO_connector_factory(uri, confnodesroot): - """ - This returns the connector to Pyro style PLCobject - """ - confnodesroot.logger.write(_("PYRO connecting to URI : %s\n") % uri) - - servicetype, location = uri.split("://") - if servicetype == "PYROS": - schemename = "PYROLOCSSL" - # Protect against name->IP substitution in Pyro3 - Pyro.config.PYRO_DNS_URI = True - # Beware Pyro lib need str path, not unicode - # don't rely on PYRO_STORAGE ! see documentation - Pyro.config.PYROSSL_CERTDIR = os.path.abspath(str(confnodesroot.ProjectPath) + '/certs') - if not os.path.exists(Pyro.config.PYROSSL_CERTDIR): - confnodesroot.logger.write_error( - 'Error : the directory %s is missing for SSL certificates (certs_dir).' - 'Please fix it in your project.\n' % Pyro.config.PYROSSL_CERTDIR) - return None - else: - confnodesroot.logger.write(_("PYRO using certificates in '%s' \n") - % (Pyro.config.PYROSSL_CERTDIR)) - Pyro.config.PYROSSL_CERT = "client.crt" - Pyro.config.PYROSSL_KEY = "client.key" - # Ugly Monkey Patching - def _gettimeout(self): - return self.timeout - - def _settimeout(self, timeout): - self.timeout = timeout - from M2Crypto.SSL import Connection - Connection.timeout = None - Connection.gettimeout = _gettimeout - Connection.settimeout = _settimeout - # M2Crypto.SSL.Checker.WrongHost: Peer certificate commonName does not - # match host, expected 127.0.0.1, got server - Connection.clientPostConnectionCheck = None - else: - schemename = "PYROLOC" - if location.find(service_type) != -1: - try: - from util.Zeroconf import Zeroconf - r = Zeroconf() - i = r.getServiceInfo(service_type, location) - if i is None: - raise Exception("'%s' not found" % location) - ip = str(socket.inet_ntoa(i.getAddress())) - port = str(i.getPort()) - newlocation = ip + ':' + port - confnodesroot.logger.write(_("'{a1}' is located at {a2}\n").format(a1 = location, a2 = newlocation)) - location = newlocation - r.close() - except Exception, msg: - confnodesroot.logger.write_error(_("MDNS resolution failure for '%s'\n") % location) - confnodesroot.logger.write_error(traceback.format_exc()) - return None - - # Try to get the proxy object - try: - RemotePLCObjectProxy = Pyro.core.getAttrProxyForURI(schemename + "://" + location + "/PLCObject") - except Exception, msg: - confnodesroot.logger.write_error(_("Connection to '%s' failed.\n") % location) - confnodesroot.logger.write_error(traceback.format_exc()) - return None - - def PyroCatcher(func, default=None): - """ - A function that catch a Pyro exceptions, write error to logger - and return default value when it happen - """ - def catcher_func(*args, **kwargs): - try: - return func(*args, **kwargs) - except Pyro.errors.ConnectionClosedError, e: - confnodesroot.logger.write_error(_("Connection lost!\n")) - confnodesroot._SetConnector(None) - except Pyro.errors.ProtocolError, e: - confnodesroot.logger.write_error(_("Pyro exception: %s\n") % e) - except Exception, e: - # confnodesroot.logger.write_error(traceback.format_exc()) - errmess = ''.join(Pyro.util.getPyroTraceback(e)) - confnodesroot.logger.write_error(errmess + "\n") - print errmess - confnodesroot._SetConnector(None) - return default - return catcher_func - - # Check connection is effective. - # lambda is for getattr of GetPLCstatus to happen inside catcher - if PyroCatcher(lambda: RemotePLCObjectProxy.GetPLCstatus())() is None: - confnodesroot.logger.write_error(_("Cannot get PLC status - connection failed.\n")) - return None - - class PyroProxyProxy(object): - """ - A proxy proxy class to handle Beremiz Pyro interface specific behavior. - And to put Pyro exception catcher in between caller and Pyro proxy - """ - def __init__(self): - # for safe use in from debug thread, must create a copy - self.RemotePLCObjectProxyCopy = None - - def GetPyroProxy(self): - """ - This func returns the real Pyro Proxy. - Use this if you musn't keep reference to it. - """ - return RemotePLCObjectProxy - - def _PyroStartPLC(self, *args, **kwargs): - """ - confnodesroot._connector.GetPyroProxy() is used - rather than RemotePLCObjectProxy because - object is recreated meanwhile, - so we must not keep ref to it here - """ - current_status, log_count = confnodesroot._connector.GetPyroProxy().GetPLCstatus() - if current_status == "Dirty": - """ - Some bad libs with static symbols may polute PLC - ask runtime to suicide and come back again - """ - confnodesroot.logger.write(_("Force runtime reload\n")) - confnodesroot._connector.GetPyroProxy().ForceReload() - confnodesroot._Disconnect() - # let remote PLC time to resurect.(freeze app) - sleep(0.5) - confnodesroot._Connect() - self.RemotePLCObjectProxyCopy = copy.copy(confnodesroot._connector.GetPyroProxy()) - return confnodesroot._connector.GetPyroProxy().StartPLC(*args, **kwargs) - StartPLC = PyroCatcher(_PyroStartPLC, False) - - def _PyroGetTraceVariables(self): - """ - for safe use in from debug thread, must use the copy - """ - if self.RemotePLCObjectProxyCopy is None: - self.RemotePLCObjectProxyCopy = copy.copy(confnodesroot._connector.GetPyroProxy()) - return self.RemotePLCObjectProxyCopy.GetTraceVariables() - GetTraceVariables = PyroCatcher(_PyroGetTraceVariables, ("Broken", None)) - - def _PyroGetPLCstatus(self): - return RemotePLCObjectProxy.GetPLCstatus() - GetPLCstatus = PyroCatcher(_PyroGetPLCstatus, ("Broken", None)) - - def _PyroRemoteExec(self, script, **kwargs): - return RemotePLCObjectProxy.RemoteExec(script, **kwargs) - RemoteExec = PyroCatcher(_PyroRemoteExec, (-1, "RemoteExec script failed!")) - - def __getattr__(self, attrName): - member = self.__dict__.get(attrName, None) - if member is None: - def my_local_func(*args, **kwargs): - return RemotePLCObjectProxy.__getattr__(attrName)(*args, **kwargs) - member = PyroCatcher(my_local_func, None) - self.__dict__[attrName] = member - return member - - return PyroProxyProxy() +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# This file is part of Beremiz, a Integrated Development Environment for +# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. +# +# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# +# See COPYING file for copyrights details. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +import Pyro +import Pyro.core +import Pyro.util +from Pyro.errors import PyroError +import traceback +from time import sleep +import copy +import socket +import os.path + +service_type = '_PYRO._tcp.local.' +# this module attribute contains a list of DNS-SD (Zeroconf) service types +# supported by this connector confnode. +# +# for connectors that do not support DNS-SD, this attribute can be omitted +# or set to an empty list. + + +def PYRO_connector_factory(uri, confnodesroot): + """ + This returns the connector to Pyro style PLCobject + """ + confnodesroot.logger.write(_("PYRO connecting to URI : %s\n") % uri) + + servicetype, location = uri.split("://") + if servicetype == "PYROS": + schemename = "PYROLOCSSL" + # Protect against name->IP substitution in Pyro3 + Pyro.config.PYRO_DNS_URI = True + # Beware Pyro lib need str path, not unicode + # don't rely on PYRO_STORAGE ! see documentation + Pyro.config.PYROSSL_CERTDIR = os.path.abspath(str(confnodesroot.ProjectPath) + '/certs') + if not os.path.exists(Pyro.config.PYROSSL_CERTDIR): + confnodesroot.logger.write_error( + 'Error : the directory %s is missing for SSL certificates (certs_dir).' + 'Please fix it in your project.\n' % Pyro.config.PYROSSL_CERTDIR) + return None + else: + confnodesroot.logger.write(_("PYRO using certificates in '%s' \n") + % (Pyro.config.PYROSSL_CERTDIR)) + Pyro.config.PYROSSL_CERT = "client.crt" + Pyro.config.PYROSSL_KEY = "client.key" + + # Ugly Monkey Patching + def _gettimeout(self): + return self.timeout + + def _settimeout(self, timeout): + self.timeout = timeout + from M2Crypto.SSL import Connection + Connection.timeout = None + Connection.gettimeout = _gettimeout + Connection.settimeout = _settimeout + # M2Crypto.SSL.Checker.WrongHost: Peer certificate commonName does not + # match host, expected 127.0.0.1, got server + Connection.clientPostConnectionCheck = None + else: + schemename = "PYROLOC" + if location.find(service_type) != -1: + try: + from util.Zeroconf import Zeroconf + r = Zeroconf() + i = r.getServiceInfo(service_type, location) + if i is None: + raise Exception("'%s' not found" % location) + ip = str(socket.inet_ntoa(i.getAddress())) + port = str(i.getPort()) + newlocation = ip + ':' + port + confnodesroot.logger.write(_("'{a1}' is located at {a2}\n").format(a1=location, a2=newlocation)) + location = newlocation + r.close() + except Exception, msg: + confnodesroot.logger.write_error(_("MDNS resolution failure for '%s'\n") % location) + confnodesroot.logger.write_error(traceback.format_exc()) + return None + + # Try to get the proxy object + try: + RemotePLCObjectProxy = Pyro.core.getAttrProxyForURI(schemename + "://" + location + "/PLCObject") + except Exception, msg: + confnodesroot.logger.write_error(_("Connection to '%s' failed.\n") % location) + confnodesroot.logger.write_error(traceback.format_exc()) + return None + + def PyroCatcher(func, default=None): + """ + A function that catch a Pyro exceptions, write error to logger + and return default value when it happen + """ + def catcher_func(*args, **kwargs): + try: + return func(*args, **kwargs) + except Pyro.errors.ConnectionClosedError, e: + confnodesroot.logger.write_error(_("Connection lost!\n")) + confnodesroot._SetConnector(None) + except Pyro.errors.ProtocolError, e: + confnodesroot.logger.write_error(_("Pyro exception: %s\n") % e) + except Exception, e: + # confnodesroot.logger.write_error(traceback.format_exc()) + errmess = ''.join(Pyro.util.getPyroTraceback(e)) + confnodesroot.logger.write_error(errmess + "\n") + print errmess + confnodesroot._SetConnector(None) + return default + return catcher_func + + # Check connection is effective. + # lambda is for getattr of GetPLCstatus to happen inside catcher + if PyroCatcher(lambda: RemotePLCObjectProxy.GetPLCstatus())() is None: + confnodesroot.logger.write_error(_("Cannot get PLC status - connection failed.\n")) + return None + + class PyroProxyProxy(object): + """ + A proxy proxy class to handle Beremiz Pyro interface specific behavior. + And to put Pyro exception catcher in between caller and Pyro proxy + """ + def __init__(self): + # for safe use in from debug thread, must create a copy + self.RemotePLCObjectProxyCopy = None + + def GetPyroProxy(self): + """ + This func returns the real Pyro Proxy. + Use this if you musn't keep reference to it. + """ + return RemotePLCObjectProxy + + def _PyroStartPLC(self, *args, **kwargs): + """ + confnodesroot._connector.GetPyroProxy() is used + rather than RemotePLCObjectProxy because + object is recreated meanwhile, + so we must not keep ref to it here + """ + current_status, log_count = confnodesroot._connector.GetPyroProxy().GetPLCstatus() + if current_status == "Dirty": + """ + Some bad libs with static symbols may polute PLC + ask runtime to suicide and come back again + """ + confnodesroot.logger.write(_("Force runtime reload\n")) + confnodesroot._connector.GetPyroProxy().ForceReload() + confnodesroot._Disconnect() + # let remote PLC time to resurect.(freeze app) + sleep(0.5) + confnodesroot._Connect() + self.RemotePLCObjectProxyCopy = copy.copy(confnodesroot._connector.GetPyroProxy()) + return confnodesroot._connector.GetPyroProxy().StartPLC(*args, **kwargs) + StartPLC = PyroCatcher(_PyroStartPLC, False) + + def _PyroGetTraceVariables(self): + """ + for safe use in from debug thread, must use the copy + """ + if self.RemotePLCObjectProxyCopy is None: + self.RemotePLCObjectProxyCopy = copy.copy(confnodesroot._connector.GetPyroProxy()) + return self.RemotePLCObjectProxyCopy.GetTraceVariables() + GetTraceVariables = PyroCatcher(_PyroGetTraceVariables, ("Broken", None)) + + def _PyroGetPLCstatus(self): + return RemotePLCObjectProxy.GetPLCstatus() + GetPLCstatus = PyroCatcher(_PyroGetPLCstatus, ("Broken", None)) + + def _PyroRemoteExec(self, script, **kwargs): + return RemotePLCObjectProxy.RemoteExec(script, **kwargs) + RemoteExec = PyroCatcher(_PyroRemoteExec, (-1, "RemoteExec script failed!")) + + def __getattr__(self, attrName): + member = self.__dict__.get(attrName, None) + if member is None: + def my_local_func(*args, **kwargs): + return RemotePLCObjectProxy.__getattr__(attrName)(*args, **kwargs) + member = PyroCatcher(my_local_func, None) + self.__dict__[attrName] = member + return member + + return PyroProxyProxy() diff -r 31e63e25b4cc -r 64beb9e9c749 connectors/WAMP/__init__.py --- a/connectors/WAMP/__init__.py Mon Aug 21 20:17:19 2017 +0000 +++ b/connectors/WAMP/__init__.py Mon Aug 21 23:22:58 2017 +0300 @@ -22,8 +22,9 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import sys, traceback, atexit -#from twisted.python import log +import sys +import traceback +import atexit from twisted.internet import reactor, threads from autobahn.twisted import wamp from autobahn.twisted.websocket import WampWebSocketClientFactory, connectWS @@ -36,6 +37,7 @@ _WampConnection = None _WampSessionEvent = Event() + class WampSession(wamp.ApplicationSession): def onJoin(self, details): global _WampSession, _WampSessionEvent @@ -49,10 +51,14 @@ _WampSession = None print 'WAMP session left' -PLCObjDefaults = { "StartPLC": False, - "GetTraceVariables" : ("Broken",None), - "GetPLCstatus" : ("Broken",None), - "RemoteExec" : (-1, "RemoteExec script failed!")} + +PLCObjDefaults = { + "StartPLC": False, + "GetTraceVariables": ("Broken", None), + "GetPLCstatus": ("Broken", None), + "RemoteExec": (-1, "RemoteExec script failed!") +} + def WAMP_connector_factory(uri, confnodesroot): """ @@ -61,37 +67,38 @@ """ servicetype, location = uri.split("://") urlpath, realm, ID = location.split('#') - urlprefix = {"WAMP":"ws", - "WAMPS":"wss"}[servicetype] + urlprefix = {"WAMP": "ws", + "WAMPS": "wss"}[servicetype] url = urlprefix+"://"+urlpath def RegisterWampClient(): - ## start logging to console + # start logging to console # log.startLogging(sys.stdout) # create a WAMP application session factory component_config = types.ComponentConfig( - realm = realm, - extra = {"ID":ID}) + realm=realm, + extra={"ID": ID}) session_factory = wamp.ApplicationSessionFactory( - config = component_config) + config=component_config) session_factory.session = WampSession # create a WAMP-over-WebSocket transport client factory transport_factory = WampWebSocketClientFactory( session_factory, - url = url, - serializers = [MsgPackSerializer()], - debug = False, - debug_wamp = False) + url=url, + serializers=[MsgPackSerializer()], + debug=False, + debug_wamp=False) # start the client from a Twisted endpoint conn = connectWS(transport_factory) - confnodesroot.logger.write(_("WAMP connecting to URL : %s\n")%url) + confnodesroot.logger.write(_("WAMP connecting to URL : %s\n") % url) return conn AddToDoBeforeQuit = confnodesroot.AppFrame.AddToDoBeforeQuit + def ThreadProc(): global _WampConnection _WampConnection = RegisterWampClient() @@ -99,22 +106,23 @@ reactor.run(installSignalHandlers=False) def WampSessionProcMapper(funcname): - wampfuncname = '.'.join((ID,funcname)) - def catcher_func(*args,**kwargs): + wampfuncname = '.'.join((ID, funcname)) + + def catcher_func(*args, **kwargs): global _WampSession - if _WampSession is not None : + if _WampSession is not None: try: return threads.blockingCallFromThread( reactor, _WampSession.call, wampfuncname, - *args,**kwargs) + *args, **kwargs) except TransportLost, e: confnodesroot.logger.write_error(_("Connection lost!\n")) confnodesroot._SetConnector(None) - except Exception,e: + except Exception, e: errmess = traceback.format_exc() confnodesroot.logger.write_error(errmess+"\n") print errmess - #confnodesroot._SetConnector(None) + # confnodesroot._SetConnector(None) return PLCObjDefaults.get(funcname) return catcher_func @@ -128,7 +136,7 @@ reactor, RegisterWampClient) if not _WampSessionEvent.wait(5): _WampConnection = stopConnecting() - raise Exception, _("WAMP connection timeout") + raise Exception(_("WAMP connection timeout")) def __del__(self): global _WampConnection @@ -144,13 +152,9 @@ return member # Try to get the proxy object - try : + try: return WampPLCObjectProxy() except Exception, msg: - confnodesroot.logger.write_error(_("WAMP connection to '%s' failed.\n")%location) + confnodesroot.logger.write_error(_("WAMP connection to '%s' failed.\n") % location) confnodesroot.logger.write_error(traceback.format_exc()) return None - - - - diff -r 31e63e25b4cc -r 64beb9e9c749 connectors/__init__.py --- a/connectors/__init__.py Mon Aug 21 20:17:19 2017 +0000 +++ b/connectors/__init__.py Mon Aug 21 23:22:58 2017 +0300 @@ -1,66 +1,68 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# This file is part of Beremiz, a Integrated Development Environment for -# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. -# -# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD -# Copyright (C) 2017: Andrey Skvortsov -# -# See COPYING file for copyrights details. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -# Package initialisation - -from os import listdir, path -import util.paths as paths - -_base_path = paths.AbsDir(__file__) - - -def _GetLocalConnectorClassFactory(name): - return lambda: getattr(__import__(name, globals(), locals()), name + "_connector_factory") - -connectors = {name:_GetLocalConnectorClassFactory(name) - for name in listdir(_base_path) - if path.isdir(path.join(_base_path, name)) - and not name.startswith("__")} - - -def ConnectorFactory(uri, confnodesroot): - """ - Return a connector corresponding to the URI - or None if cannot connect to URI - """ - servicetype = uri.split("://")[0].upper() - if servicetype == "LOCAL": - # Local is special case - # pyro connection to local runtime - # started on demand, listening on random port - servicetype = "PYRO" - runtime_port = confnodesroot.AppFrame.StartLocalRuntime( - taskbaricon=True) - uri = "PYROLOC://127.0.0.1:" + str(runtime_port) - elif servicetype in connectors: - pass - elif servicetype[-1] == 'S' and servicetype[:-1] in connectors: - servicetype = servicetype[:-1] - else: - return None - - # import module according to uri type - connectorclass = connectors[servicetype]() - return connectorclass(uri, confnodesroot) +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# This file is part of Beremiz, a Integrated Development Environment for +# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. +# +# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov +# +# See COPYING file for copyrights details. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# Package initialisation + +from os import listdir, path +import util.paths as paths + +_base_path = paths.AbsDir(__file__) + + +def _GetLocalConnectorClassFactory(name): + return lambda: getattr(__import__(name, globals(), locals()), name + "_connector_factory") + + +connectors = {name: + _GetLocalConnectorClassFactory(name) + for name in listdir(_base_path) + if (path.isdir(path.join(_base_path, name)) + and not name.startswith("__"))} + + +def ConnectorFactory(uri, confnodesroot): + """ + Return a connector corresponding to the URI + or None if cannot connect to URI + """ + servicetype = uri.split("://")[0].upper() + if servicetype == "LOCAL": + # Local is special case + # pyro connection to local runtime + # started on demand, listening on random port + servicetype = "PYRO" + runtime_port = confnodesroot.AppFrame.StartLocalRuntime( + taskbaricon=True) + uri = "PYROLOC://127.0.0.1:" + str(runtime_port) + elif servicetype in connectors: + pass + elif servicetype[-1] == 'S' and servicetype[:-1] in connectors: + servicetype = servicetype[:-1] + else: + return None + + # import module according to uri type + connectorclass = connectors[servicetype]() + return connectorclass(uri, confnodesroot) diff -r 31e63e25b4cc -r 64beb9e9c749 controls/CustomEditableListBox.py --- a/controls/CustomEditableListBox.py Mon Aug 21 20:17:19 2017 +0000 +++ b/controls/CustomEditableListBox.py Mon Aug 21 23:22:58 2017 +0300 @@ -25,16 +25,17 @@ import wx import wx.gizmos + class CustomEditableListBox(wx.gizmos.EditableListBox): - + def __init__(self, *args, **kwargs): wx.gizmos.EditableListBox.__init__(self, *args, **kwargs) - + listbox = self.GetListCtrl() listbox.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) listbox.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnLabelBeginEdit) listbox.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.OnLabelEndEdit) - + for button, tooltip, call_function in [ (self.GetEditButton(), _("Edit item"), "_OnEditButton"), (self.GetNewButton(), _("New item"), "_OnNewButton"), @@ -43,13 +44,13 @@ (self.GetDownButton(), _("Move down"), "_OnDownButton")]: button.SetToolTipString(tooltip) button.Bind(wx.EVT_BUTTON, self.GetButtonPressedFunction(call_function)) - + self.Editing = False - + def EnsureCurrentItemVisible(self): listctrl = self.GetListCtrl() listctrl.EnsureVisible(listctrl.GetFocusedItem()) - + def OnLabelBeginEdit(self, event): self.Editing = True func = getattr(self, "_OnLabelBeginEdit", None) @@ -57,7 +58,7 @@ func(event) else: event.Skip() - + def OnLabelEndEdit(self, event): self.Editing = False func = getattr(self, "_OnLabelEndEdit", None) @@ -65,7 +66,7 @@ func(event) else: event.Skip() - + def GetButtonPressedFunction(self, call_function): def OnButtonPressed(event): if wx.Platform != '__WXMSW__' or not self.Editing: @@ -77,7 +78,7 @@ wx.CallAfter(self.EnsureCurrentItemVisible) event.Skip() return OnButtonPressed - + def OnKeyDown(self, event): button = None keycode = event.GetKeyCode() diff -r 31e63e25b4cc -r 64beb9e9c749 controls/CustomGrid.py --- a/controls/CustomGrid.py Mon Aug 21 20:17:19 2017 +0000 +++ b/controls/CustomGrid.py Mon Aug 21 23:22:58 2017 +0300 @@ -25,46 +25,47 @@ import wx import wx.grid + class CustomGrid(wx.grid.Grid): - + def __init__(self, *args, **kwargs): wx.grid.Grid.__init__(self, *args, **kwargs) - + self.Editable = True - + self.AddButton = None self.DeleteButton = None self.UpButton = None self.DownButton = None - + self.SetFont(wx.Font(12, wx.DEFAULT, wx.NORMAL, wx.NORMAL, False, 'Sans')) self.SetLabelFont(wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.NORMAL, False, 'Sans')) self.SetSelectionBackground(wx.WHITE) self.SetSelectionForeground(wx.BLACK) self.DisableDragRowSize() - + self.Bind(wx.grid.EVT_GRID_SELECT_CELL, self.OnSelectCell) self.Bind(wx.grid.EVT_GRID_EDITOR_HIDDEN, self.OnEditorHidden) self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) - + def SetFocus(self): if self: wx.grid.Grid.SetFocus(self) - + def SetDefaultValue(self, default_value): self.DefaultValue = default_value - + def SetEditable(self, editable=True): self.Editable = editable self.RefreshButtons() - + def SetButtons(self, buttons): for name in ["Add", "Delete", "Up", "Down"]: button = buttons.get(name, None) setattr(self, "%sButton" % name, button) if button is not None: button.Bind(wx.EVT_BUTTON, getattr(self, "On%sButton" % name)) - + def RefreshButtons(self): if self: rows = self.Table.GetNumberRows() @@ -77,7 +78,7 @@ self.UpButton.Enable(self.Editable and row > 0) if self.DownButton is not None: self.DownButton.Enable(self.Editable and 0 <= row < rows - 1) - + def CloseEditControl(self): row, col = self.GetGridCursorRow(), self.GetGridCursorCol() if row != -1 and col != -1: @@ -119,27 +120,27 @@ self.Table.ResetView(self) if new_row != row: self.SetSelectedCell(new_row, col) - + def SetSelectedCell(self, row, col): self.SetGridCursor(row, col) self.MakeCellVisible(row, col) self.RefreshButtons() - + def OnAddButton(self, event): self.AddRow() self.SetFocus() event.Skip() - + def OnDeleteButton(self, event): self.DeleteRow() self.SetFocus() event.Skip() - + def OnUpButton(self, event): self.MoveRow(self.GetGridCursorRow(), -1) self.SetFocus() event.Skip() - + def OnDownButton(self, event): self.MoveRow(self.GetGridCursorRow(), 1) self.SetFocus() diff -r 31e63e25b4cc -r 64beb9e9c749 controls/CustomStyledTextCtrl.py --- a/controls/CustomStyledTextCtrl.py Mon Aug 21 20:17:19 2017 +0000 +++ b/controls/CustomStyledTextCtrl.py Mon Aug 21 23:22:58 2017 +0300 @@ -26,19 +26,21 @@ import wx.stc if wx.Platform == '__WXMSW__': - faces = { 'times': 'Times New Roman', - 'mono' : 'Courier New', - 'helv' : 'Arial', - 'other': 'Comic Sans MS', - 'size' : 10, - } + faces = { + 'times': 'Times New Roman', + 'mono': 'Courier New', + 'helv': 'Arial', + 'other': 'Comic Sans MS', + 'size': 10, + } else: - faces = { 'times': 'Times', - 'mono' : 'Courier', - 'helv' : 'Helvetica', - 'other': 'new century schoolbook', - 'size' : 12, - } + faces = { + 'times': 'Times', + 'mono': 'Courier', + 'helv': 'Helvetica', + 'other': 'new century schoolbook', + 'size': 12, + } NAVIGATION_KEYS = [ wx.WXK_END, @@ -58,6 +60,7 @@ wx.WXK_NUMPAD_PAGEDOWN, wx.WXK_NUMPAD_END] + def GetCursorPos(old, new): if old == "": return 0 @@ -81,13 +84,14 @@ else: return None + class CustomStyledTextCtrl(wx.stc.StyledTextCtrl): - + def __init__(self, *args, **kwargs): wx.stc.StyledTextCtrl.__init__(self, *args, **kwargs) - + self.Bind(wx.EVT_MOTION, self.OnMotion) - + def OnMotion(self, event): if wx.Platform == '__WXMSW__': if not event.Dragging(): diff -r 31e63e25b4cc -r 64beb9e9c749 controls/CustomTable.py --- a/controls/CustomTable.py Mon Aug 21 20:17:19 2017 +0000 +++ b/controls/CustomTable.py Mon Aug 21 23:22:58 2017 +0300 @@ -30,8 +30,9 @@ else: ROW_HEIGHT = 28 + class CustomTable(wx.grid.PyGridTableBase): - + """ A custom wx.grid.Grid Table using user supplied data """ @@ -47,10 +48,10 @@ # see if the table has changed size self._rows = self.GetNumberRows() self._cols = self.GetNumberCols() - + def GetNumberCols(self): return len(self.colnames) - + def GetNumberRows(self): return len(self.data) @@ -66,11 +67,11 @@ def GetValue(self, row, col): if row < self.GetNumberRows(): return self.data[row].get(self.GetColLabelValue(col, False), "") - + def SetValue(self, row, col, value): if col < len(self.colnames): self.data[row][self.GetColLabelValue(col, False)] = value - + def GetValueByName(self, row, colname): if row < self.GetNumberRows(): return self.data[row].get(colname) @@ -91,10 +92,10 @@ (self._cols, self.GetNumberCols(), wx.grid.GRIDTABLE_NOTIFY_COLS_DELETED, wx.grid.GRIDTABLE_NOTIFY_COLS_APPENDED), ]: if new < current: - msg = wx.grid.GridTableMessage(self,delmsg,new,current-new) + msg = wx.grid.GridTableMessage(self, delmsg, new, current-new) grid.ProcessTableMessage(msg) elif new > current: - msg = wx.grid.GridTableMessage(self,addmsg,new-current) + msg = wx.grid.GridTableMessage(self, addmsg, new-current) grid.ProcessTableMessage(msg) self.UpdateValues(grid) grid.EndBatch() @@ -125,33 +126,33 @@ row_highlights = self.Highlights.get(row, {}) for col in range(self.GetNumberCols()): colname = self.GetColLabelValue(col, False) - + grid.SetReadOnly(row, col, True) grid.SetCellEditor(row, col, None) grid.SetCellRenderer(row, col, None) - + 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 ResizeRow(self, grid, row): if grid.GetRowSize(row) < ROW_HEIGHT: grid.SetRowMinimalHeight(row, ROW_HEIGHT) grid.AutoSizeRow(row, False) - + def SetData(self, data): self.data = data - + def GetData(self): return self.data - + def GetCurrentIndex(self): return self.CurrentIndex - + def SetCurrentIndex(self, index): self.CurrentIndex = index - + def AppendRow(self, row_content): self.data.append(row_content) diff -r 31e63e25b4cc -r 64beb9e9c749 controls/CustomToolTip.py --- a/controls/CustomToolTip.py Mon Aug 21 20:17:19 2017 +0000 +++ b/controls/CustomToolTip.py Mon Aug 21 23:22:58 2017 +0300 @@ -26,45 +26,45 @@ from controls.CustomStyledTextCtrl import faces -TOOLTIP_MAX_CHARACTERS = 30 # Maximum number of characters by line in ToolTip -TOOLTIP_MAX_LINE = 5 # Maximum number of line in ToolTip -TOOLTIP_WAIT_PERIOD = 0.5 # Wait period before displaying tooltip in second +TOOLTIP_MAX_CHARACTERS = 30 # Maximum number of characters by line in ToolTip +TOOLTIP_MAX_LINE = 5 # Maximum number of line in ToolTip +TOOLTIP_WAIT_PERIOD = 0.5 # Wait period before displaying tooltip in second -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Custom ToolTip -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -""" -Class that implements a custom tool tip -""" class CustomToolTip(wx.PopupWindow): - + """ + Class that implements a custom tool tip + """ + def __init__(self, parent, tip, restricted=True): """ Constructor @param parent: Parent window @param tip: Tip text (may be multiline) - @param restricted: Tool tip must follow size restriction in line and + @param restricted: Tool tip must follow size restriction in line and characters number defined (default True) """ wx.PopupWindow.__init__(self, parent) - + self.Restricted = restricted - + self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM) self.SetTip(tip) - + # Initialize text font style self.Font = wx.Font( - faces["size"], - wx.SWISS, - wx.NORMAL, - wx.NORMAL, - faceName = faces["mono"]) - + faces["size"], + wx.SWISS, + wx.NORMAL, + wx.NORMAL, + faceName=faces["mono"]) + self.Bind(wx.EVT_PAINT, self.OnPaint) - + def SetFont(self, font): """ Set tool tip text font style @@ -72,7 +72,7 @@ """ self.Font = font self.RefreshTip() - + def SetTip(self, tip): """ Set tool tip text @@ -87,8 +87,7 @@ new_line = words[0] for word in words[1:]: # Add word to line - if len(new_line + " " + word) <= \ - TOOLTIP_MAX_CHARACTERS: + if len(new_line + " " + word) <= TOOLTIP_MAX_CHARACTERS: new_line += " " + word # Create new line else: @@ -97,24 +96,24 @@ self.Tip.append(new_line) else: self.Tip.append(line) - + # Restrict number of lines if len(self.Tip) > TOOLTIP_MAX_LINE: self.Tip = self.Tip[:TOOLTIP_MAX_LINE] - + # Add ... to the end of last line to indicate that tool tip # text is too long if len(self.Tip[-1]) < TOOLTIP_MAX_CHARACTERS - 3: self.Tip[-1] += "..." else: - self.Tip[-1] = self.Tip[-1]\ - [:TOOLTIP_MAX_CHARACTERS - 3] + "..." + self.Tip[-1] = self.Tip[-1][:TOOLTIP_MAX_CHARACTERS - 3] + \ + "..." else: self.Tip = tip.splitlines() - + # Prevent to call wx method in non-wx threads wx.CallAfter(self.RefreshTip) - + def SetToolTipPosition(self, pos): """ Set tool tip position @@ -122,32 +121,32 @@ """ # Get screen size to prevent tool tip to go out of the screen screen_width, screen_height = wx.GetDisplaySize() - + # Calculate position of tool tip to stay in screen limits tip_width, tip_height = self.GetToolTipSize() self.SetPosition(wx.Point( max(0, min(pos.x, screen_width - tip_width)), max(0, min(pos.y, screen_height - tip_height)))) - + def GetToolTipSize(self): """ Get tool tip size according to tip text and restriction @return: wx.Size(tool_tip_width, tool_tip_height) """ max_width = max_height = 0 - + # Create a memory DC for calculating text extent dc = wx.MemoryDC() dc.SetFont(self.Font) - + # Compute max tip text size for line in self.Tip: w, h = dc.GetTextExtent(line) max_width = max(max_width, w) max_height += h - + return wx.Size(max_width + 4, max_height + 4) - + def RefreshTip(self): """ Refresh tip on screen @@ -156,10 +155,10 @@ if self: # Refresh tool tip size and position self.SetClientSize(self.GetToolTipSize()) - + # Redraw tool tip self.Refresh() - + def OnPaint(self, event): """ Callback for Paint Event @@ -168,26 +167,26 @@ # Get buffered paint DC for tool tip dc = wx.AutoBufferedPaintDC(self) dc.Clear() - + # Set DC drawing style dc.SetPen(wx.BLACK_PEN) dc.SetBrush(wx.Brush(wx.Colour(255, 238, 170))) dc.SetFont(self.Font) - + # Draw Tool tip dc.BeginDrawing() tip_width, tip_height = self.GetToolTipSize() - + # Draw background rectangle dc.DrawRectangle(0, 0, tip_width, tip_height) - + # Draw tool tip text line_offset = 0 for line in self.Tip: dc.DrawText(line, 2, line_offset + 2) line_width, line_height = dc.GetTextExtent(line) line_offset += line_height - + dc.EndDrawing() - + event.Skip() diff -r 31e63e25b4cc -r 64beb9e9c749 controls/CustomTree.py --- a/controls/CustomTree.py Mon Aug 21 20:17:19 2017 +0000 +++ b/controls/CustomTree.py Mon Aug 21 23:22:58 2017 +0300 @@ -30,44 +30,51 @@ # Customize CustomTreeItem for adding icon on item left CT.GenericTreeItem._ExtraImage = None + def SetExtraImage(self, image): self._type = (1 if image is not None else 0) self._ExtraImage = image + CT.GenericTreeItem.SetExtraImage = SetExtraImage _DefaultGetCurrentCheckedImage = CT.GenericTreeItem.GetCurrentCheckedImage + + def GetCurrentCheckedImage(self): if self._ExtraImage is not None: return self._ExtraImage return _DefaultGetCurrentCheckedImage(self) + + CT.GenericTreeItem.GetCurrentCheckedImage = GetCurrentCheckedImage + class CustomTree(CT.CustomTreeCtrl): - + def __init__(self, *args, **kwargs): CT.CustomTreeCtrl.__init__(self, *args, **kwargs) - + self.BackgroundBitmap = None - self.BackgroundAlign = wx.ALIGN_LEFT|wx.ALIGN_TOP - + self.BackgroundAlign = wx.ALIGN_LEFT | wx.ALIGN_TOP + self.AddMenu = None self.Enabled = False - + self.Bind(wx.EVT_SCROLLWIN, self.OnScroll) self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) - + def SetBackgroundBitmap(self, bitmap, align): self.BackgroundBitmap = bitmap self.BackgroundAlign = align - + def SetImageListCheck(self, sizex, sizey, imglist=None): CT.CustomTreeCtrl.SetImageListCheck(self, sizex, sizey, imglist=None) - + self.ExtraImages = {} for image in ["function", "functionBlock", "program"]: self.ExtraImages[image] = self._imageListCheck.Add(GetBitmap(image.upper())) - + def SetItemExtraImage(self, item, bitmap): dc = wx.ClientDC(self) image = self.ExtraImages.get(bitmap) @@ -76,45 +83,45 @@ else: item.SetExtraImage(None) self.CalculateSize(item, dc) - self.RefreshLine(item) - + self.RefreshLine(item) + def SetAddMenu(self, add_menu): self.AddMenu = add_menu - + def Enable(self, enabled): self.Enabled = enabled - + def GetBitmapRect(self): client_size = self.GetClientSize() bitmap_size = self.BackgroundBitmap.GetSize() - + if self.BackgroundAlign & wx.ALIGN_RIGHT: x = client_size[0] - bitmap_size[0] elif self.BackgroundAlign & wx.ALIGN_CENTER_HORIZONTAL: x = (client_size[0] - bitmap_size[0]) / 2 else: x = 0 - + if self.BackgroundAlign & wx.ALIGN_BOTTOM: y = client_size[1] - bitmap_size[1] elif self.BackgroundAlign & wx.ALIGN_CENTER_VERTICAL: y = (client_size[1] - bitmap_size[1]) / 2 else: y = 0 - + return wx.Rect(x, y, bitmap_size[0], bitmap_size[1]) - + def OnLeftUp(self, event): if self.Enabled: pos = event.GetPosition() item, flags = self.HitTest(pos) - + bitmap_rect = self.GetBitmapRect() - if (bitmap_rect.InsideXY(pos.x, pos.y) or - flags & wx.TREE_HITTEST_NOWHERE) and self.AddMenu is not None: + if ((bitmap_rect.InsideXY(pos.x, pos.y) or + flags & wx.TREE_HITTEST_NOWHERE) and self.AddMenu is not None): wx.CallAfter(self.PopupMenuXY, self.AddMenu, pos.x, pos.y) event.Skip() - + def OnEraseBackground(self, event): dc = event.GetDC() @@ -122,12 +129,12 @@ dc = wx.ClientDC(self) rect = self.GetUpdateRegion().GetBox() dc.SetClippingRect(rect) - + dc.Clear() - + bitmap_rect = self.GetBitmapRect() dc.DrawBitmap(self.BackgroundBitmap, bitmap_rect.x, bitmap_rect.y) - + def OnScroll(self, event): wx.CallAfter(self.Refresh) event.Skip() diff -r 31e63e25b4cc -r 64beb9e9c749 controls/DebugVariablePanel/DebugVariableGraphicViewer.py --- a/controls/DebugVariablePanel/DebugVariableGraphicViewer.py Mon Aug 21 20:17:19 2017 +0000 +++ b/controls/DebugVariablePanel/DebugVariableGraphicViewer.py Mon Aug 21 23:22:58 2017 +0300 @@ -53,19 +53,20 @@ # Canvas height [SIZE_MINI, SIZE_MIDDLE, SIZE_MAXI] = [0, 100, 200] -CANVAS_BORDER = (20., 10.) # Border height on at bottom and top of graph -CANVAS_PADDING = 8.5 # Border inside graph where no label is drawn -VALUE_LABEL_HEIGHT = 17. # Height of variable label in graph -AXES_LABEL_HEIGHT = 12.75 # Height of variable value in graph +CANVAS_BORDER = (20., 10.) # Border height on at bottom and top of graph +CANVAS_PADDING = 8.5 # Border inside graph where no label is drawn +VALUE_LABEL_HEIGHT = 17. # Height of variable label in graph +AXES_LABEL_HEIGHT = 12.75 # Height of variable value in graph # Colors used cyclically for graph curves COLOR_CYCLE = ['r', 'b', 'g', 'm', 'y', 'k'] # Color for graph cursor CURSOR_COLOR = '#800080' -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Debug Variable Graphic Viewer Helpers -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + def merge_ranges(ranges): """ @@ -83,37 +84,37 @@ min_value = range_min elif range_min is not None: min_value = min(min_value, range_min) - + # Update maximal range value if max_value is None: max_value = range_max elif range_min is not None: max_value = max(max_value, range_max) - + # Calculate range center and width if at least one valid range is defined if min_value is not None and max_value is not None: center = (min_value + max_value) / 2. range_size = max(1.0, max_value - min_value) - + # Set default center and with if no valid range is defined else: center = 0.5 range_size = 1.0 - + # Return range expended from 10 % return center - range_size * 0.55, center + range_size * 0.55 -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Debug Variable Graphic Viewer Drop Target -#------------------------------------------------------------------------------- - -""" -Class that implements a custom drop target class for Debug Variable Graphic -Viewer -""" +# ------------------------------------------------------------------------------- + class DebugVariableGraphicDropTarget(wx.TextDropTarget): - + """ + Class that implements a custom drop target class for Debug Variable Graphic + Viewer + """ + def __init__(self, parent, window): """ Constructor @@ -123,7 +124,7 @@ wx.TextDropTarget.__init__(self) self.ParentControl = parent self.ParentWindow = window - + def __del__(self): """ Destructor @@ -132,7 +133,7 @@ # Panel self.ParentControl = None self.ParentWindow = None - + def OnDragOver(self, x, y, d): """ Function called when mouse is dragged over Drop Target @@ -142,9 +143,9 @@ """ # Signal parent that mouse is dragged over self.ParentControl.OnMouseDragging(x, y) - + return wx.TextDropTarget.OnDragOver(self, x, y, d) - + def OnDropText(self, x, y, data): """ Function called when mouse is released in Drop Target @@ -154,65 +155,65 @@ """ # Signal Debug Variable Panel to reset highlight self.ParentWindow.ResetHighlight() - + message = None - + # Check that data is valid regarding DebugVariablePanel try: values = eval(data) if not isinstance(values, TupleType): raise ValueError - except: - message = _("Invalid value \"%s\" for debug variable")%data + except Exception: + message = _("Invalid value \"%s\" for debug variable") % data values = None - + # Display message if data is invalid if message is not None: wx.CallAfter(self.ShowMessage, message) - + # Data contain a reference to a variable to debug elif values[1] == "debug": target_idx = self.ParentControl.GetIndex() - + # If mouse is dropped in graph canvas bounding box and graph is # not 3D canvas, graphs will be merged rect = self.ParentControl.GetAxesBoundingBox() if not self.ParentControl.Is3DCanvas() and rect.InsideXY(x, y): # Default merge type is parallel merge_type = GRAPH_PARALLEL - + # If mouse is dropped in left part of graph canvas, graph # wall be merged orthogonally - merge_rect = wx.Rect(rect.x, rect.y, + merge_rect = wx.Rect(rect.x, rect.y, rect.width / 2., rect.height) if merge_rect.InsideXY(x, y): merge_type = GRAPH_ORTHOGONAL - + # Merge graphs - wx.CallAfter(self.ParentWindow.MergeGraphs, - values[0], target_idx, + wx.CallAfter(self.ParentWindow.MergeGraphs, + values[0], target_idx, merge_type, force=True) - + else: width, height = self.ParentControl.GetSize() - + # Get Before which Viewer the variable has to be moved or added # according to the position of mouse in Viewer. if y > height / 2: target_idx += 1 - + # Drag'n Drop is an internal is an internal move inside Debug - # Variable Panel + # Variable Panel if len(values) > 2 and values[2] == "move": - self.ParentWindow.MoveValue(values[0], + self.ParentWindow.MoveValue(values[0], target_idx) - + # Drag'n Drop was initiated by another control of Beremiz else: - self.ParentWindow.InsertValue(values[0], - target_idx, + self.ParentWindow.InsertValue(values[0], + target_idx, force=True) - + def OnLeave(self): """ Function called when mouse is leave Drop Target @@ -220,30 +221,30 @@ # Signal Debug Variable Panel to reset highlight self.ParentWindow.ResetHighlight() return wx.TextDropTarget.OnLeave(self) - + def ShowMessage(self, message): """ Show error message in Error Dialog @param message: Error message to display """ - dialog = wx.MessageDialog(self.ParentWindow, - message, - _("Error"), - wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self.ParentWindow, + message, + _("Error"), + wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Debug Variable Graphic Viewer Class -#------------------------------------------------------------------------------- - -""" -Class that implements a Viewer that display variable values as a graphs -""" +# ------------------------------------------------------------------------------- + class DebugVariableGraphicViewer(DebugVariableViewer, FigureCanvas): - + """ + Class that implements a Viewer that display variable values as a graphs + """ + def __init__(self, parent, window, items, graph_type): """ Constructor @@ -253,36 +254,36 @@ @param graph_type: Graph display type (Parallel or orthogonal) """ DebugVariableViewer.__init__(self, window, items) - + self.GraphType = graph_type # Graph type display self.CursorTick = None # Tick of the graph cursor - + # Mouse position when start dragging self.MouseStartPos = None # Tick when moving tick start self.StartCursorTick = None # Canvas size when starting to resize canvas - self.CanvasStartSize = None - + self.CanvasStartSize = None + # List of current displayed contextual buttons self.ContextualButtons = [] # Reference to item for which contextual buttons was displayed self.ContextualButtonsItem = None - + # Flag indicating that zoom fit current displayed data range or whole # data range if False self.ZoomFit = False - + # Create figure for drawing graphs self.Figure = matplotlib.figure.Figure(facecolor='w') # Defined border around figure in canvas - self.Figure.subplotpars.update(top=0.95, left=0.1, + self.Figure.subplotpars.update(top=0.95, left=0.1, bottom=0.1, right=0.95) - + FigureCanvas.__init__(self, parent, -1, self.Figure) self.SetWindowStyle(wx.WANTS_CHARS) self.SetBackgroundColour(wx.WHITE) - + # Bind wx events self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDClick) self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) @@ -290,42 +291,42 @@ self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave) self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) self.Bind(wx.EVT_SIZE, self.OnResize) - + # Set canvas min size canvas_size = self.GetCanvasMinSize() self.SetMinSize(canvas_size) - + # Define Viewer drop target self.SetDropTarget(DebugVariableGraphicDropTarget(self, window)) - + # Connect matplotlib events self.mpl_connect('button_press_event', self.OnCanvasButtonPressed) self.mpl_connect('motion_notify_event', self.OnCanvasMotion) self.mpl_connect('button_release_event', self.OnCanvasButtonReleased) self.mpl_connect('scroll_event', self.OnCanvasScroll) - + # Add buttons for zooming on current displayed data range self.Buttons.append( GraphButton(0, 0, "fit_graph", self.OnZoomFitButton)) - + # Add buttons for changing canvas size with predefined height for size, bitmap in zip( [SIZE_MINI, SIZE_MIDDLE, SIZE_MAXI], ["minimize_graph", "middle_graph", "maximize_graph"]): self.Buttons.append( - GraphButton(0, 0, bitmap, + GraphButton(0, 0, bitmap, self.GetOnChangeSizeButton(size))) - + # Add buttons for exporting graph values to clipboard and close graph for bitmap, callback in [ ("export_graph_mini", self.OnExportGraphButton), ("delete_graph", self.OnCloseButton)]: self.Buttons.append(GraphButton(0, 0, bitmap, callback)) - + # Update graphs elements self.ResetGraphics() self.RefreshLabelsPosition(canvas_size.height) - + def AddItem(self, item): """ Add an item to the list of items displayed by Viewer @@ -333,14 +334,14 @@ """ DebugVariableViewer.AddItem(self, item) self.ResetGraphics() - + def RemoveItem(self, item): """ Remove an item from the list of items displayed by Viewer @param item: Item to remove from the list """ DebugVariableViewer.RemoveItem(self, item) - + # If list of items is not empty if not self.ItemsIsEmpty(): # Return to parallel graph if there is only one item @@ -348,51 +349,51 @@ if len(self.Items) == 1: self.GraphType = GRAPH_PARALLEL self.ResetGraphics() - + def SetCursorTick(self, cursor_tick): """ Set cursor tick @param cursor_tick: Cursor tick """ self.CursorTick = cursor_tick - + def SetZoomFit(self, zoom_fit): """ Set flag indicating that zoom fit current displayed data range @param zoom_fit: Flag for zoom fit (False: zoom fit whole data range) """ - # Flag is different from the actual one + # Flag is different from the actual one if zoom_fit != self.ZoomFit: # Save new flag value self.ZoomFit = zoom_fit - + # Update button for zoom fit bitmap self.Buttons[0].SetBitmap("full_graph" if zoom_fit else "fit_graph") - + # Refresh canvas self.RefreshViewer() - + def SubscribeAllDataConsumers(self): """ Function that unsubscribe and remove every item that store values of a variable that doesn't exist in PLC anymore """ DebugVariableViewer.SubscribeAllDataConsumers(self) - + # Graph still have data to display if not self.ItemsIsEmpty(): # Reset flag indicating that zoom fit current displayed data range self.SetZoomFit(False) - + self.ResetGraphics() - + def Is3DCanvas(self): """ Return if Viewer is a 3D canvas @return: True if Viewer is a 3D canvas """ return self.GraphType == GRAPH_ORTHOGONAL and len(self.Items) == 3 - + def GetButtons(self): """ Return list of buttons defined in Viewer @@ -400,7 +401,7 @@ """ # Add contextual buttons to default buttons return self.Buttons + self.ContextualButtons - + def PopupContextualButtons(self, item, rect, direction=wx.RIGHT): """ Show contextual menu for item aside a label of this item defined @@ -412,19 +413,19 @@ # Return immediately if contextual menu for item is already shown if self.ContextualButtonsItem == item: return - + # Close already shown contextual menu self.DismissContextualButtons() - + # Save item for which contextual menu is shown self.ContextualButtonsItem = item - + # If item variable is forced, add button for release variable to # contextual menu if self.ContextualButtonsItem.IsForced(): self.ContextualButtons.append( GraphButton(0, 0, "release", self.OnReleaseItemButton)) - + # Add other buttons to contextual menu for bitmap, callback in [ ("force", self.OnForceItemButton), @@ -432,13 +433,13 @@ ("delete_graph", self.OnRemoveItemButton)]: self.ContextualButtons.append( GraphButton(0, 0, bitmap, callback)) - + # If buttons are shown at left side or upper side of rect, positions # will be set in reverse order buttons = self.ContextualButtons[:] if direction in [wx.TOP, wx.LEFT]: - buttons.reverse() - + buttons.reverse() + # Set contextual menu buttons position aside rect depending on # direction given offset = 0 @@ -446,22 +447,22 @@ w, h = button.GetSize() if direction in [wx.LEFT, wx.RIGHT]: x = rect.x + (- w - offset - if direction == wx.LEFT - else rect.width + offset) + if direction == wx.LEFT + else rect.width + offset) y = rect.y + (rect.height - h) / 2 offset += w else: - x = rect.x + (rect.width - w ) / 2 + x = rect.x + (rect.width - w) / 2 y = rect.y + (- h - offset if direction == wx.TOP else rect.height + offset) offset += h button.SetPosition(x, y) button.Show() - + # Refresh canvas self.ParentWindow.ForceRefresh() - + def DismissContextualButtons(self): """ Close current shown contextual menu @@ -469,14 +470,14 @@ # Return immediately if no contextual menu is shown if self.ContextualButtonsItem is None: return - + # Reset variables corresponding to contextual menu self.ContextualButtonsItem = None self.ContextualButtons = [] - + # Refresh canvas self.ParentWindow.ForceRefresh() - + def IsOverContextualButton(self, x, y): """ Return if point is over one contextual button of Viewer @@ -488,7 +489,7 @@ if button.HitTest(x, y): return button return None - + def ExportGraph(self, item=None): """ Export item(s) data to clipboard in CSV format @@ -497,17 +498,17 @@ """ self.ParentWindow.CopyDataToClipboard( [(item, [entry for entry in item.GetData()]) - for item in (self.Items - if item is None + for item in (self.Items + if item is None else [item])]) - + def OnZoomFitButton(self): """ Function called when Viewer Zoom Fit button is pressed """ # Toggle zoom fit flag value self.SetZoomFit(not self.ZoomFit) - + def GetOnChangeSizeButton(self, height): """ Function that generate callback function for change Viewer height to @@ -518,32 +519,32 @@ def OnChangeSizeButton(): self.SetCanvasHeight(height) return OnChangeSizeButton - + def OnExportGraphButton(self): """ Function called when Viewer Export button is pressed """ # Export data of every item in Viewer self.ExportGraph() - + def OnForceItemButton(self): """ Function called when contextual menu Force button is pressed """ - # Open dialog for forcing item variable value + # Open dialog for forcing item variable value self.ForceValue(self.ContextualButtonsItem) # Close contextual menu self.DismissContextualButtons() - + def OnReleaseItemButton(self): """ Function called when contextual menu Release button is pressed """ - # Release item variable value + # Release item variable value self.ReleaseValue(self.ContextualButtonsItem) # Close contextual menu self.DismissContextualButtons() - + def OnExportItemGraphButton(self): """ Function called when contextual menu Export button is pressed @@ -552,17 +553,17 @@ self.ExportGraph(self.ContextualButtonsItem) # Close contextual menu self.DismissContextualButtons() - - def OnRemoveItemButton(self): + + def OnRemoveItemButton(self): """ Function called when contextual menu Remove button is pressed """ # Remove item from Viewer - wx.CallAfter(self.ParentWindow.DeleteValue, self, + wx.CallAfter(self.ParentWindow.DeleteValue, self, self.ContextualButtonsItem) # Close contextual menu self.DismissContextualButtons() - + def HandleCursorMove(self, event): """ Update Cursor position according to mouse position and graph type @@ -571,7 +572,7 @@ start_tick, end_tick = self.ParentWindow.GetRange() cursor_tick = None items = self.ItemsDict.values() - + # Graph is orthogonal if self.GraphType == GRAPH_ORTHOGONAL: # Extract items data displayed in canvas figure @@ -579,31 +580,31 @@ end_tick = max(end_tick, start_tick) x_data = items[0].GetData(start_tick, end_tick) y_data = items[1].GetData(start_tick, end_tick) - + # Search for the nearest point from mouse position if len(x_data) > 0 and len(y_data) > 0: - length = min(len(x_data), len(y_data)) - d = numpy.sqrt((x_data[:length,1]-event.xdata) ** 2 + \ - (y_data[:length,1]-event.ydata) ** 2) - + length = min(len(x_data), len(y_data)) + d = numpy.sqrt((x_data[:length, 1]-event.xdata) ** 2 + + (y_data[:length, 1]-event.ydata) ** 2) + # Set cursor tick to the tick of this point cursor_tick = x_data[numpy.argmin(d), 0] - + # Graph is parallel else: # Extract items tick data = items[0].GetData(start_tick, end_tick) - + # Search for point that tick is the nearest from mouse X position # and set cursor tick to the tick of this point if len(data) > 0: cursor_tick = data[numpy.argmin( - numpy.abs(data[:,0] - event.xdata)), 0] - + numpy.abs(data[:, 0] - event.xdata)), 0] + # Update cursor tick if cursor_tick is not None: self.ParentWindow.SetCursorTick(cursor_tick) - + def OnCanvasButtonPressed(self, event): """ Function called when a button of mouse is pressed @@ -613,18 +614,18 @@ # comparing to wx width, height = self.GetSize() x, y = event.x, height - event.y - + # Return immediately if mouse is over a button if self.IsOverButton(x, y): - return - + return + # Mouse was clicked inside graph figure if event.inaxes == self.Axes: - + # Find if it was on an item label item_idx = None # Check every label paired with corresponding item - for i, t in ([pair for pair in enumerate(self.AxesLabels)] + + for i, t in ([pair for pair in enumerate(self.AxesLabels)] + [pair for pair in enumerate(self.Labels)]): # Get label bounding box (x0, y0), (x1, y1) = t.get_window_extent().get_points() @@ -633,46 +634,46 @@ if rect.InsideXY(x, y): item_idx = i break - + # If an item label have been clicked if item_idx is not None: # Hide buttons and contextual buttons self.ShowButtons(False) self.DismissContextualButtons() - + # Start a drag'n drop from mouse position in wx coordinate of # parent xw, yw = self.GetPosition() - self.ParentWindow.StartDragNDrop(self, - self.ItemsDict.values()[item_idx], - x + xw, y + yw, # Current mouse position - x + xw, y + yw) # Mouse position when button was clicked - + self.ParentWindow.StartDragNDrop( + self, self.ItemsDict.values()[item_idx], + x + xw, y + yw, # Current mouse position + x + xw, y + yw) # Mouse position when button was clicked + # Don't handle mouse button if canvas is 3D and let matplotlib do # the default behavior (rotate 3D axes) elif not self.Is3DCanvas(): # Save mouse position when clicked self.MouseStartPos = wx.Point(x, y) - + # Mouse button was left button, start moving cursor if event.button == 1: # Save current tick in case a drag'n drop is initiate to # restore it self.StartCursorTick = self.CursorTick - + self.HandleCursorMove(event) - + # Mouse button is middle button and graph is parallel, start # moving graph along X coordinate (tick) elif event.button == 2 and self.GraphType == GRAPH_PARALLEL: self.StartCursorTick = self.ParentWindow.GetRange()[0] - + # Mouse was clicked outside graph figure and over resize highlight with # left button, start resizing Viewer elif event.button == 1 and event.y <= 5: self.MouseStartPos = wx.Point(x, y) self.CanvasStartSize = height - + def OnCanvasButtonReleased(self, event): """ Function called when a button of mouse is released @@ -685,57 +686,57 @@ item = self.ParentWindow.DraggingAxesPanel.ItemsDict.values()[0] # Give mouse position in wx coordinate of parent self.ParentWindow.StopDragNDrop(item.GetVariable(), - xw + event.x, yw + height - event.y) - + xw + event.x, yw + height - event.y) + else: # Reset any move in progress self.MouseStartPos = None self.CanvasStartSize = None - + # Handle button under mouse if it exist width, height = self.GetSize() self.HandleButton(event.x, height - event.y) - + def OnCanvasMotion(self, event): """ Function called when a button of mouse is moved over Viewer @param event: Mouse event """ width, height = self.GetSize() - + # If a drag'n drop is in progress, move canvas dragged if self.ParentWindow.IsDragging(): xw, yw = self.GetPosition() # Give mouse position in wx coordinate of parent self.ParentWindow.MoveDragNDrop( - xw + event.x, + xw + event.x, yw + height - event.y) - - # If a Viewer resize is in progress, change Viewer size + + # If a Viewer resize is in progress, change Viewer size elif event.button == 1 and self.CanvasStartSize is not None: width, height = self.GetSize() self.SetCanvasHeight( self.CanvasStartSize + height - event.y - self.MouseStartPos.y) - + # If no button is pressed, show or hide contextual buttons or resize # highlight elif event.button is None: # Compute direction for items label according graph type - if self.GraphType == GRAPH_PARALLEL: # Graph is parallel + if self.GraphType == GRAPH_PARALLEL: # Graph is parallel directions = [wx.RIGHT] * len(self.AxesLabels) + \ [wx.LEFT] * len(self.Labels) - elif len(self.AxesLabels) > 0: # Graph is orthogonal in 2D - directions = [wx.RIGHT, wx.TOP, # Directions for AxesLabels - wx.LEFT, wx.BOTTOM] # Directions for Labels - else: # Graph is orthogonal in 3D + elif len(self.AxesLabels) > 0: # Graph is orthogonal in 2D + directions = [wx.RIGHT, wx.TOP, # Directions for AxesLabels + wx.LEFT, wx.BOTTOM] # Directions for Labels + else: # Graph is orthogonal in 3D directions = [wx.LEFT] * len(self.Labels) - + # Find if mouse is over an item label item_idx = None menu_direction = None for (i, t), dir in zip( - [pair for pair in enumerate(self.AxesLabels)] + - [pair for pair in enumerate(self.Labels)], + [pair for pair in enumerate(self.AxesLabels)] + + [pair for pair in enumerate(self.Labels)], directions): # Check every label paired with corresponding item (x0, y0), (x1, y1) = t.get_window_extent().get_points() @@ -745,19 +746,19 @@ item_idx = i menu_direction = dir break - - # If mouse is over an item label, + + # If mouse is over an item label, if item_idx is not None: self.PopupContextualButtons( - self.ItemsDict.values()[item_idx], + self.ItemsDict.values()[item_idx], rect, menu_direction) return - + # If mouse isn't over a contextual menu, hide the current shown one - # if it exists + # if it exists if self.IsOverContextualButton(event.x, height - event.y) is None: self.DismissContextualButtons() - + # Update resize highlight if event.y <= 5: if self.SetHighlight(HIGHLIGHT_RESIZE): @@ -767,46 +768,47 @@ if self.SetHighlight(HIGHLIGHT_NONE): self.SetCursor(wx.NullCursor) self.ParentWindow.ForceRefresh() - - # Handle buttons if canvas is not 3D + + # Handle buttons if canvas is not 3D elif not self.Is3DCanvas(): - + # If left button is pressed if event.button == 1: - + # Mouse is inside graph figure if event.inaxes == self.Axes: - + # If a cursor move is in progress, update cursor position if self.MouseStartPos is not None: self.HandleCursorMove(event) - + # Mouse is outside graph figure, cursor move is in progress and # there is only one item in Viewer, start a drag'n drop elif self.MouseStartPos is not None and len(self.Items) == 1: xw, yw = self.GetPosition() self.ParentWindow.SetCursorTick(self.StartCursorTick) - self.ParentWindow.StartDragNDrop(self, - self.ItemsDict.values()[0], + self.ParentWindow.StartDragNDrop( + self, self.ItemsDict.values()[0], # Current mouse position event.x + xw, height - event.y + yw, # Mouse position when button was clicked self.MouseStartPos.x + xw, self.MouseStartPos.y + yw) - + # If middle button is pressed and moving graph along X coordinate # is in progress - elif event.button == 2 and self.GraphType == GRAPH_PARALLEL and \ - self.MouseStartPos is not None: + elif (event.button == 2 and + self.GraphType == GRAPH_PARALLEL and + self.MouseStartPos is not None): start_tick, end_tick = self.ParentWindow.GetRange() rect = self.GetAxesBoundingBox() - + # Move graph along X coordinate self.ParentWindow.SetCanvasPosition( - self.StartCursorTick + + self.StartCursorTick + (self.MouseStartPos.x - event.x) * (end_tick - start_tick) / rect.width) - + def OnCanvasScroll(self, event): """ Function called when a wheel mouse is use in Viewer @@ -815,7 +817,7 @@ # Change X range of graphs if mouse is in canvas figure and ctrl is # pressed if event.inaxes is not None and event.guiEvent.ControlDown(): - + # Calculate position of fixed tick point according to graph type # and mouse position if self.GraphType == GRAPH_ORTHOGONAL: @@ -824,10 +826,10 @@ else: tick = event.xdata self.ParentWindow.ChangeRange(int(-event.step) / 3, tick) - + # Vetoing event to prevent parent panel to be scrolled self.ParentWindow.VetoScrollEvent = True - + def OnLeftDClick(self, event): """ Function called when a left mouse button is double clicked @@ -841,17 +843,17 @@ self.ParentWindow.SetCursorTick(self.StartCursorTick) # Toggle to text Viewer(s) self.ParentWindow.ToggleViewerType(self) - + else: event.Skip() - + # Cursor tick move for each arrow key KEY_CURSOR_INCREMENT = { wx.WXK_LEFT: -1, wx.WXK_RIGHT: 1, wx.WXK_UP: 10, wx.WXK_DOWN: -10} - + def OnKeyDown(self, event): """ Function called when key is pressed @@ -863,7 +865,7 @@ if move is not None: self.ParentWindow.MoveCursorTick(move) event.Skip() - + def OnLeave(self, event): """ Function called when mouse leave Viewer @@ -876,7 +878,7 @@ DebugVariableViewer.OnLeave(self, event) else: event.Skip() - + def GetCanvasMinSize(self): """ Return the minimum size of Viewer so that all items label can be @@ -885,10 +887,10 @@ """ # The minimum height take in account the height of all items, padding # inside figure and border around figure - return wx.Size(200, - CANVAS_BORDER[0] + CANVAS_BORDER[1] + - 2 * CANVAS_PADDING + VALUE_LABEL_HEIGHT * len(self.Items)) - + return wx.Size(200, + CANVAS_BORDER[0] + CANVAS_BORDER[1] + + 2 * CANVAS_PADDING + VALUE_LABEL_HEIGHT * len(self.Items)) + def SetCanvasHeight(self, height): """ Set Viewer size checking that it respects Viewer minimum size @@ -899,7 +901,7 @@ self.SetMinSize(wx.Size(min_width, height)) self.RefreshLabelsPosition(height) self.ParentWindow.RefreshGraphicsSizer() - + def GetAxesBoundingBox(self, parent_coordinate=False): """ Return figure bounding box in wx coordinate @@ -911,15 +913,15 @@ ax, ay, aw, ah = self.figure.gca().get_position().bounds bbox = wx.Rect(ax * width, height - (ay + ah) * height - 1, aw * width + 2, ah * height + 1) - + # If parent_coordinate, add Viewer position in parent if parent_coordinate: xw, yw = self.GetPosition() bbox.x += xw bbox.y += yw - + return bbox - + def RefreshHighlight(self, x, y): """ Refresh Viewer highlight according to mouse position @@ -927,7 +929,7 @@ @param y: Y coordinate of mouse pointer """ width, height = self.GetSize() - + # Mouse is over Viewer figure and graph is not 3D bbox = self.GetAxesBoundingBox() if bbox.InsideXY(x, y) and not self.Is3DCanvas(): @@ -935,28 +937,28 @@ # Mouse is over Viewer left part of figure if rect.InsideXY(x, y): self.SetHighlight(HIGHLIGHT_LEFT) - + # Mouse is over Viewer right part of figure else: self.SetHighlight(HIGHLIGHT_RIGHT) - + # Mouse is over upper part of Viewer elif y < height / 2: # Viewer is upper one in Debug Variable Panel, show highlight if self.ParentWindow.IsViewerFirst(self): self.SetHighlight(HIGHLIGHT_BEFORE) - + # Viewer is not the upper one, show highlight in previous one # It prevents highlight to move when mouse leave one Viewer to # another else: self.SetHighlight(HIGHLIGHT_NONE) self.ParentWindow.HighlightPreviousViewer(self) - + # Mouse is over lower part of Viewer else: self.SetHighlight(HIGHLIGHT_AFTER) - + def OnAxesMotion(self, event): """ Function overriding default function called when mouse is dragged for @@ -969,13 +971,14 @@ if current_time - self.LastMotionTime > REFRESH_PERIOD: self.LastMotionTime = current_time Axes3D._on_move(self.Axes, event) - + def GetAddTextFunction(self): """ Return function for adding text in figure according to graph type @return: Function adding text to figure """ text_func = (self.Axes.text2D if self.Is3DCanvas() else self.Axes.text) + def AddText(*args, **kwargs): args = [0, 0, ""] kwargs["transform"] = self.Axes.transAxes @@ -984,61 +987,61 @@ def SetAxesColor(self, color): if LooseVersion(matplotlib.__version__) >= LooseVersion("1.5.0"): - self.Axes.set_prop_cycle(cycler('color',color)) + self.Axes.set_prop_cycle(cycler('color', color)) else: self.Axes.set_color_cycle(color) - + def ResetGraphics(self): """ Reset figure and graphical elements displayed in it - Called any time list of items or graph type change + Called any time list of items or graph type change """ # Clear figure from any axes defined self.Figure.clear() - + # Add 3D projection if graph is in 3D if self.Is3DCanvas(): self.Axes = self.Figure.gca(projection='3d') self.SetAxesColor(['b']) - - # Override function to prevent too much refresh when graph is + + # Override function to prevent too much refresh when graph is # rotated self.LastMotionTime = gettime() setattr(self.Axes, "_on_move", self.OnAxesMotion) - + # Init graph mouse event so that graph can be rotated self.Axes.mouse_init() - + # Set size of Z axis labels self.Axes.tick_params(axis='z', labelsize='small') - + else: self.Axes = self.Figure.gca() self.SetAxesColor(COLOR_CYCLE) - + # Set size of X and Y axis labels self.Axes.tick_params(axis='x', labelsize='small') self.Axes.tick_params(axis='y', labelsize='small') - + # Init variables storing graphical elements added to figure - self.Plots = [] # List of curves - self.VLine = None # Vertical line for cursor - self.HLine = None # Horizontal line for cursor (only orthogonal 2D) - self.AxesLabels = [] # List of items variable path text label - self.Labels = [] # List of items text label - - # Get function to add a text in figure according to graph type + self.Plots = [] # List of curves + self.VLine = None # Vertical line for cursor + self.HLine = None # Horizontal line for cursor (only orthogonal 2D) + self.AxesLabels = [] # List of items variable path text label + self.Labels = [] # List of items text label + + # Get function to add a text in figure according to graph type add_text_func = self.GetAddTextFunction() - + # Graph type is parallel or orthogonal in 3D if self.GraphType == GRAPH_PARALLEL or self.Is3DCanvas(): num_item = len(self.Items) for idx in xrange(num_item): - + # Get color from color cycle (black if only one item) - color = ('k' if num_item == 1 - else COLOR_CYCLE[idx % len(COLOR_CYCLE)]) - + color = ('k' if num_item == 1 else + COLOR_CYCLE[idx % len(COLOR_CYCLE)]) + # In 3D graph items variable label are not displayed as text # in figure, but as axis title if not self.Is3DCanvas(): @@ -1046,12 +1049,12 @@ self.AxesLabels.append( add_text_func(size='small', color=color, verticalalignment='top')) - + # Items variable labels are in figure lower right corner self.Labels.append( - add_text_func(size='large', color=color, + add_text_func(size='large', color=color, horizontalalignment='right')) - + # Graph type is orthogonal in 2D else: # X coordinate labels are in figure lower side @@ -1059,7 +1062,7 @@ self.Labels.append( add_text_func(size='large', horizontalalignment='right')) - + # Y coordinate labels are vertical and in figure left side self.AxesLabels.append( add_text_func(size='small', rotation='vertical', @@ -1067,11 +1070,11 @@ self.Labels.append( add_text_func(size='large', rotation='vertical', verticalalignment='top')) - + # Refresh position of labels according to Viewer size width, height = self.GetSize() self.RefreshLabelsPosition(height) - + def RefreshLabelsPosition(self, height): """ Function called when mouse leave Viewer @@ -1083,34 +1086,34 @@ # expressed border and text position in pixel on screen and apply the # ratio calculated hereafter to get border and text position in # matplotlib coordinate - canvas_ratio = 1. / height # Divide by canvas height in pixel + canvas_ratio = 1. / height # Divide by canvas height in pixel graph_ratio = 1. / ( (1.0 - (CANVAS_BORDER[0] + CANVAS_BORDER[1]) * canvas_ratio) - * height) # Divide by figure height in pixel - + * height) # Divide by figure height in pixel + # Update position of figure (keeping up and bottom border the same # size) self.Figure.subplotpars.update( - top= 1.0 - CANVAS_BORDER[1] * canvas_ratio, - bottom= CANVAS_BORDER[0] * canvas_ratio) - + top=1.0 - CANVAS_BORDER[1] * canvas_ratio, + bottom=CANVAS_BORDER[0] * canvas_ratio) + # Update position of items labels if self.GraphType == GRAPH_PARALLEL or self.Is3DCanvas(): num_item = len(self.Items) for idx in xrange(num_item): - + # In 3D graph items variable label are not displayed if not self.Is3DCanvas(): # Items variable labels are in figure upper left corner self.AxesLabels[idx].set_position( - (0.05, - 1.0 - (CANVAS_PADDING + + (0.05, + 1.0 - (CANVAS_PADDING + AXES_LABEL_HEIGHT * idx) * graph_ratio)) - + # Items variable labels are in figure lower right corner self.Labels[idx].set_position( - (0.95, - CANVAS_PADDING * graph_ratio + + (0.95, + CANVAS_PADDING * graph_ratio + (num_item - idx - 1) * VALUE_LABEL_HEIGHT * graph_ratio)) else: # X coordinate labels are in figure lower side @@ -1118,16 +1121,16 @@ (0.1, CANVAS_PADDING * graph_ratio)) self.Labels[0].set_position( (0.95, CANVAS_PADDING * graph_ratio)) - + # Y coordinate labels are vertical and in figure left side self.AxesLabels[1].set_position( (0.05, 2 * CANVAS_PADDING * graph_ratio)) self.Labels[1].set_position( (0.05, 1.0 - CANVAS_PADDING * graph_ratio)) - + # Update subplots self.Figure.subplots_adjust() - + def RefreshViewer(self, refresh_graphics=True): """ Function called to refresh displayed by matplotlib canvas @@ -1138,55 +1141,55 @@ if refresh_graphics: # Get tick range of values to display start_tick, end_tick = self.ParentWindow.GetRange() - + # Graph is parallel - if self.GraphType == GRAPH_PARALLEL: + if self.GraphType == GRAPH_PARALLEL: # Init list of data range for each variable displayed ranges = [] - + # Get data and range for each variable displayed for idx, item in enumerate(self.Items): data, min_value, max_value = item.GetDataAndValueRange( start_tick, end_tick, not self.ZoomFit) - + # Check that data is not empty if data is not None: # Add variable range to list of variable data range ranges.append((min_value, max_value)) - + # Add plot to canvas if not yet created if len(self.Plots) <= idx: self.Plots.append( self.Axes.plot(data[:, 0], data[:, 1])[0]) - + # Set data to already created plot in canvas else: self.Plots[idx].set_data(data[:, 0], data[:, 1]) - + # Get X and Y axis ranges x_min, x_max = start_tick, end_tick y_min, y_max = merge_ranges(ranges) - + # Display cursor in canvas if a cursor tick is defined and it is # include in values tick range - if (self.CursorTick is not None and - start_tick <= self.CursorTick <= end_tick): - + if self.CursorTick is not None and \ + start_tick <= self.CursorTick <= end_tick: + # Define a vertical line to display cursor position if no # line is already defined if self.VLine is None: - self.VLine = self.Axes.axvline(self.CursorTick, + self.VLine = self.Axes.axvline(self.CursorTick, color=CURSOR_COLOR) - + # Set value of vertical line if already defined else: self.VLine.set_xdata((self.CursorTick, self.CursorTick)) self.VLine.set_visible(True) - + # Hide vertical line if cursor tick is not defined or reset elif self.VLine is not None: self.VLine.set_visible(False) - + # Graph is orthogonal else: # Update tick range, removing ticks that don't have a value for @@ -1194,76 +1197,75 @@ start_tick = max(start_tick, self.GetItemsMinCommonTick()) end_tick = max(end_tick, start_tick) items = self.ItemsDict.values() - + # Get data and range for first variable (X coordinate) x_data, x_min, x_max = items[0].GetDataAndValueRange( start_tick, end_tick, not self.ZoomFit) # Get data and range for second variable (Y coordinate) y_data, y_min, y_max = items[1].GetDataAndValueRange( start_tick, end_tick, not self.ZoomFit) - + # Normalize X and Y coordinates value range x_min, x_max = merge_ranges([(x_min, x_max)]) y_min, y_max = merge_ranges([(y_min, y_max)]) - - # Get X and Y coordinates for cursor if cursor tick is defined + + # Get X and Y coordinates for cursor if cursor tick is defined if self.CursorTick is not None: x_cursor, x_forced = items[0].GetValue( self.CursorTick, raw=True) y_cursor, y_forced = items[1].GetValue( self.CursorTick, raw=True) - + # Get common data length so that each value has an x and y # coordinate length = (min(len(x_data), len(y_data)) if x_data is not None and y_data is not None else 0) - - # Graph is orthogonal 2D + + # Graph is orthogonal 2D if len(self.Items) < 3: - + # Check that x and y data are not empty if x_data is not None and y_data is not None: - + # Add plot to canvas if not yet created if len(self.Plots) == 0: self.Plots.append( - self.Axes.plot(x_data[:, 1][:length], + self.Axes.plot(x_data[:, 1][:length], y_data[:, 1][:length])[0]) - + # Set data to already created plot in canvas else: self.Plots[0].set_data( - x_data[:, 1][:length], + x_data[:, 1][:length], y_data[:, 1][:length]) - + # Display cursor in canvas if a cursor tick is defined and it is # include in values tick range - if (self.CursorTick is not None and - start_tick <= self.CursorTick <= end_tick): - + if self.CursorTick is not None and \ + start_tick <= self.CursorTick <= end_tick: + # Define a vertical line to display cursor x coordinate # if no line is already defined if self.VLine is None: - self.VLine = self.Axes.axvline(x_cursor, + self.VLine = self.Axes.axvline(x_cursor, color=CURSOR_COLOR) # Set value of vertical line if already defined else: self.VLine.set_xdata((x_cursor, x_cursor)) - - + # Define a horizontal line to display cursor y # coordinate if no line is already defined if self.HLine is None: - self.HLine = self.Axes.axhline(y_cursor, + self.HLine = self.Axes.axhline(y_cursor, color=CURSOR_COLOR) # Set value of horizontal line if already defined else: self.HLine.set_ydata((y_cursor, y_cursor)) - + self.VLine.set_visible(True) self.HLine.set_visible(True) - + # Hide vertical and horizontal line if cursor tick is not # defined or reset else: @@ -1271,42 +1273,43 @@ self.VLine.set_visible(False) if self.HLine is not None: self.HLine.set_visible(False) - + # Graph is orthogonal 3D else: # Remove all plots already defined in 3D canvas while len(self.Axes.lines) > 0: self.Axes.lines.pop() - + # Get data and range for third variable (Z coordinate) z_data, z_min, z_max = items[2].GetDataAndValueRange( start_tick, end_tick, not self.ZoomFit) - + # Normalize Z coordinate value range z_min, z_max = merge_ranges([(z_min, z_max)]) - + # Check that x, y and z data are not empty - if (x_data is not None and y_data is not None and - z_data is not None): - + if x_data is not None and \ + y_data is not None and \ + z_data is not None: + # Get common data length so that each value has an x, y # and z coordinate length = min(length, len(z_data)) - + # Add plot to canvas self.Axes.plot(x_data[:, 1][:length], y_data[:, 1][:length], - zs = z_data[:, 1][:length]) - + zs=z_data[:, 1][:length]) + # Display cursor in canvas if a cursor tick is defined and # it is include in values tick range - if (self.CursorTick is not None and - start_tick <= self.CursorTick <= end_tick): - + if self.CursorTick is not None and \ + start_tick <= self.CursorTick <= end_tick: + # Get Z coordinate for cursor z_cursor, z_forced = items[2].GetValue( self.CursorTick, raw=True) - + # Add 3 lines parallel to x, y and z axis to display # cursor position in 3D for kwargs in [{"xs": numpy.array([x_min, x_max])}, @@ -1319,14 +1322,14 @@ kwargs.setdefault(param, value) kwargs["color"] = CURSOR_COLOR self.Axes.plot(**kwargs) - + # Set Z axis limits self.Axes.set_zlim(z_min, z_max) - + # Set X and Y axis limits self.Axes.set_xlim(x_min, x_max) self.Axes.set_ylim(y_min, y_max) - + # Get value and forced flag for each variable displayed in graph # If cursor tick is not defined get value and flag of last received # or get value and flag of variable at cursor tick @@ -1335,34 +1338,34 @@ if self.CursorTick is not None else (item.GetValue(), item.IsForced())) for item in self.Items]) - + # Get path of each variable displayed simplified using panel variable # name mask - labels = [item.GetVariable(self.ParentWindow.GetVariableNameMask()) + labels = [item.GetVariable(self.ParentWindow.GetVariableNameMask()) for item in self.Items] - - # Get style for each variable according to + + # Get style for each variable according to styles = map(lambda x: {True: 'italic', False: 'normal'}[x], forced) - + # Graph is orthogonal 3D, set variables path as 3D axis label if self.Is3DCanvas(): - for idx, label_func in enumerate([self.Axes.set_xlabel, + for idx, label_func in enumerate([self.Axes.set_xlabel, self.Axes.set_ylabel, self.Axes.set_zlabel]): label_func(labels[idx], fontdict={'size': 'small', 'color': COLOR_CYCLE[idx]}) - + # Graph is not orthogonal 3D, set variables path in axes labels else: for label, text in zip(self.AxesLabels, labels): label.set_text(text) - + # Set value label text and style according to value and forced flag for # each variable displayed for label, value, style in zip(self.Labels, values, styles): label.set_text(value) label.set_style(style) - + # Refresh figure self.draw() @@ -1372,51 +1375,49 @@ """ # Render figure using agg FigureCanvasAgg.draw(self) - + # Get bitmap of figure rendered self.bitmap = _convert_agg_to_wx_bitmap(self.get_renderer(), None) - if wx.VERSION < (3, 0, 0): + if wx.VERSION < (3, 0, 0): self.bitmap.UseAlpha() - + # Create DC for rendering graphics in bitmap destDC = wx.MemoryDC() destDC.SelectObject(self.bitmap) - + # Get Graphics Context for DC, for anti-aliased and transparent # rendering destGC = wx.GCDC(destDC) - + destGC.BeginDrawing() - + # Get canvas size and figure bounding box in canvas width, height = self.GetSize() bbox = self.GetAxesBoundingBox() - + # If highlight to display is resize, draw thick grey line at bottom - # side of canvas + # side of canvas if self.Highlight == HIGHLIGHT_RESIZE: destGC.SetPen(HIGHLIGHT_RESIZE_PEN) destGC.SetBrush(HIGHLIGHT_RESIZE_BRUSH) destGC.DrawRectangle(0, height - 5, width, 5) - + # If highlight to display is merging graph, draw 50% transparent blue # rectangle on left or right part of figure depending on highlight type elif self.Highlight in [HIGHLIGHT_LEFT, HIGHLIGHT_RIGHT]: destGC.SetPen(HIGHLIGHT_DROP_PEN) destGC.SetBrush(HIGHLIGHT_DROP_BRUSH) - - x_offset = (bbox.width / 2 + + x_offset = (bbox.width / 2 if self.Highlight == HIGHLIGHT_RIGHT else 0) - destGC.DrawRectangle(bbox.x + x_offset, bbox.y, + destGC.DrawRectangle(bbox.x + x_offset, bbox.y, bbox.width / 2, bbox.height) - + # Draw other Viewer common elements self.DrawCommonElements(destGC, self.GetButtons()) - + destGC.EndDrawing() - + self._isDrawn = True self.gui_repaint(drawDC=drawDC) - - diff -r 31e63e25b4cc -r 64beb9e9c749 controls/DebugVariablePanel/DebugVariableItem.py --- a/controls/DebugVariablePanel/DebugVariableItem.py Mon Aug 21 20:17:19 2017 +0000 +++ b/controls/DebugVariablePanel/DebugVariableItem.py Mon Aug 21 23:22:58 2017 +0300 @@ -27,24 +27,24 @@ from graphics.DebugDataConsumer import DebugDataConsumer, TYPE_TRANSLATOR -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Constant for calculate CRC for string variables -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- STRING_CRC_SIZE = 8 STRING_CRC_MASK = 2 ** STRING_CRC_SIZE - 1 -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Debug Variable Item Class -#------------------------------------------------------------------------------- - -""" -Class that implements an element that consumes debug values for PLC variable and -stores received values for displaying them in graphic panel or table -""" +# ------------------------------------------------------------------------------- + class DebugVariableItem(DebugDataConsumer): - + """ + Class that implements an element that consumes debug values for PLC variable and + stores received values for displaying them in graphic panel or table + """ + def __init__(self, parent, variable, store_data=False): """ Constructor @@ -52,24 +52,24 @@ @param variable: Path of variable to debug """ DebugDataConsumer.__init__(self) - + self.Parent = parent self.Variable = variable self.StoreData = store_data - + # Get Variable data type self.RefreshVariableType() - + def __del__(self): """ Destructor """ # Reset reference to debug variable panel self.Parent = None - + def SetVariable(self, variable): """ - Set path of variable + Set path of variable @param variable: Path of variable to debug """ if self.Parent is not None and self.Variable != variable: @@ -77,46 +77,46 @@ self.Variable = variable # Get Variable data type self.RefreshVariableType() - + # Refresh debug variable panel self.Parent.RefreshView() - + def GetVariable(self, mask=None): """ - Return path of variable + Return path of variable @param mask: Mask to apply to variable path [var_name, '*',...] @return: String containing masked variable path """ # Apply mask to variable name if mask is not None: # '#' correspond to parts that are different between all items - + # Extract variable path parts parts = self.Variable.split('.') # Adjust mask size to size of variable path mask = mask + ['*'] * max(0, len(parts) - len(mask)) - + # Store previous mask last = None # Init masked variable path variable = "" - + for m, p in zip(mask, parts): # Part is not masked, add part prefixed with '.' is previous # wasn't masked if m == '*': variable += ('.' if last == '*' else '') + p - + # Part is mask, add '..' if first or previous wasn't masked elif last is None or last == '*': variable += '..' - + last = m - + return variable - + return self.Variable - + def RefreshVariableType(self): """ Get and store variable data type @@ -124,14 +124,14 @@ self.VariableType = self.Parent.GetDataType(self.Variable) # Reset data stored self.ResetData() - + def GetVariableType(self): """ Return variable data type @return: Variable data type """ return self.VariableType - + def GetData(self, start_tick=None, end_tick=None): """ Return data stored contained in given range @@ -142,7 +142,7 @@ # Return immediately if data empty or none if self.Data is None or len(self.Data) == 0: return self.Data - + # Find nearest data outside given range indexes start_idx = (self.GetNearestData(start_tick, -1) if start_tick is not None @@ -150,28 +150,27 @@ end_idx = (self.GetNearestData(end_tick, 1) if end_tick is not None else len(self.Data)) - + # Return data between indexes return self.Data[start_idx:end_idx] - + def GetRawValue(self, index): """ Return raw value at given index for string variables @param index: Variable value index @return: Variable data type """ - if (self.VariableType in ["STRING", "WSTRING"] and - index < len(self.RawData)): + if self.VariableType in ["STRING", "WSTRING"] and index < len(self.RawData): return self.RawData[index][0] return "" - + def GetValueRange(self): """ Return variable value range @return: (minimum_value, maximum_value) """ return self.MinValue, self.MaxValue - + def GetDataAndValueRange(self, start_tick, end_tick, full_range=True): """ Return variable data and value range for a given tick range @@ -179,16 +178,16 @@ @param end_tick: end tick of given range (default None, last data) @param full_range: Value range is calculated on whole data (False: only calculated on data in given range) - @return: (numpy.array([(tick, value, forced),...]), + @return: (numpy.array([(tick, value, forced),...]), min_value, max_value) """ # Get data in given tick range data = self.GetData(start_tick, end_tick) - + # Value range is calculated on whole data if full_range: return data, self.MinValue, self.MaxValue - + # Check that data in given range is not empty values = data[:, 1] if len(values) > 0: @@ -196,10 +195,10 @@ return (data, data[numpy.argmin(values), 1], data[numpy.argmax(values), 1]) - + # Return default values return data, None, None - + def ResetData(self): """ Reset data stored when store data option enabled @@ -207,31 +206,31 @@ if self.StoreData and self.IsNumVariable(): # Init table storing data self.Data = numpy.array([]).reshape(0, 3) - + # Init table storing raw data if variable is strin self.RawData = ([] if self.VariableType in ["STRING", "WSTRING"] else None) - + # Init Value range variables self.MinValue = None self.MaxValue = None - + else: self.Data = None - + # Init variable value self.Value = "" - + def IsNumVariable(self): """ Return if variable data type is numeric. String variables are considered as numeric (string CRC) @return: True if data type is numeric """ - return (self.Parent.IsNumType(self.VariableType) or + return (self.Parent.IsNumType(self.VariableType) or self.VariableType in ["STRING", "WSTRING"]) - + def NewValues(self, ticks, values): """ Function called by debug thread when a new debug value is available @@ -240,24 +239,24 @@ @param forced: Forced flag, True if value is forced (default: False) """ DebugDataConsumer.NewValues(self, ticks[-1], values[-1], raw=None) - + if self.Data is not None: - + if self.VariableType in ["STRING", "WSTRING"]: last_raw_data = (self.RawData[-1] if len(self.RawData) > 0 else None) last_raw_data_idx = len(self.RawData) - 1 - + data_values = [] for tick, (value, forced) in zip(ticks, values): # Translate forced flag to float for storing in Data table forced_value = float(forced) - + # String data value is CRC num_value = (binascii.crc32(value) & STRING_CRC_MASK if self.VariableType in ["STRING", "WSTRING"] else float(value)) - + # Update variable range values self.MinValue = (min(self.MinValue, num_value) if self.MinValue is not None @@ -265,10 +264,10 @@ self.MaxValue = (max(self.MaxValue, num_value) if self.MaxValue is not None else num_value) - + # In the case of string variables, we store raw string value and # forced flag in raw data table. Only changes in this two values - # are stored. Index to the corresponding raw value is stored in + # are stored. Index to the corresponding raw value is stored in # data third column if self.VariableType in ["STRING", "WSTRING"]: raw_data = (value, forced_value) @@ -277,20 +276,20 @@ last_raw_data = raw_data self.RawData.append(raw_data) extra_value = last_raw_data_idx - + # In other case, data third column is forced flag else: extra_value = forced_value - + data_values.append( [float(tick), num_value, extra_value]) - + # Add New data to stored data table self.Data = numpy.append(self.Data, data_values, axis=0) - + # Signal to debug variable panel to refresh self.Parent.HasNewData = True - + def SetForced(self, forced): """ Update Forced flag @@ -299,29 +298,27 @@ # Store forced flag if self.Forced != forced: self.Forced = forced - + # Signal to debug variable panel to refresh self.Parent.HasNewData = True - + def SetValue(self, value): """ Update value. @param value: New value """ # Remove quote and double quote surrounding string value to get raw value - if (self.VariableType == "STRING" and - value.startswith("'") and value.endswith("'") or - self.VariableType == "WSTRING" and - value.startswith('"') and value.endswith('"')): + if self.VariableType == "STRING" and value.startswith("'") and value.endswith("'") or \ + self.VariableType == "WSTRING" and value.startswith('"') and value.endswith('"'): value = value[1:-1] - + # Store variable value if self.Value != value: self.Value = value - + # Signal to debug variable panel to refresh self.Parent.HasNewData = True - + def GetValue(self, tick=None, raw=False): """ Return current value or value and forced flag for tick given @@ -329,26 +326,27 @@ """ # If tick given and stored data option enabled if tick is not None and self.Data is not None: - + # Return current value and forced flag if data empty if len(self.Data) == 0: return self.Value, self.IsForced() - + # Get index of nearest data from tick given idx = self.GetNearestData(tick, 0) - + # Get value and forced flag at given index - value, forced = self.RawData[int(self.Data[idx, 2])] \ - if self.VariableType in ["STRING", "WSTRING"] \ - else self.Data[idx, 1:3] - + value, forced = \ + self.RawData[int(self.Data[idx, 2])] \ + if self.VariableType in ["STRING", "WSTRING"] \ + else self.Data[idx, 1:3] + # Get raw value if asked if not raw: value = TYPE_TRANSLATOR.get( self.VariableType, str)(value) - + return value, forced - + # Return raw value if asked if not raw and self.VariableType in ["STRING", "WSTRING"]: return TYPE_TRANSLATOR.get( @@ -368,16 +366,16 @@ # Return immediately if data is empty if self.Data is None: return None - + # Extract data ticks ticks = self.Data[:, 0] - + # Get nearest data from tick idx = numpy.argmin(abs(ticks - tick)) - + # Adjust data index according to constraint - if (adjust < 0 and ticks[idx] > tick and idx > 0 or - adjust > 0 and ticks[idx] < tick and idx < len(ticks)): + if adjust < 0 and ticks[idx] > tick and idx > 0 or \ + adjust > 0 and ticks[idx] < tick and idx < len(ticks): idx += adjust - + return idx diff -r 31e63e25b4cc -r 64beb9e9c749 controls/DebugVariablePanel/DebugVariablePanel.py --- a/controls/DebugVariablePanel/DebugVariablePanel.py Mon Aug 21 20:17:19 2017 +0000 +++ b/controls/DebugVariablePanel/DebugVariablePanel.py Mon Aug 21 23:22:58 2017 +0300 @@ -30,10 +30,11 @@ import wx.lib.buttons import matplotlib -matplotlib.use('WX') +matplotlib.use('WX') # noqa import matplotlib.pyplot from matplotlib.backends.backend_wxagg import _convert_agg_to_wx_bitmap + from editors.DebugViewer import DebugViewer from util.BitmapLibrary import GetBitmap @@ -41,11 +42,12 @@ from DebugVariableTextViewer import DebugVariableTextViewer from DebugVariableGraphicViewer import * -MILLISECOND = 1000000 # Number of nanosecond in a millisecond -SECOND = 1000 * MILLISECOND # Number of nanosecond in a second -MINUTE = 60 * SECOND # Number of nanosecond in a minute -HOUR = 60 * MINUTE # Number of nanosecond in a hour -DAY = 24 * HOUR # Number of nanosecond in a day + +MILLISECOND = 1000000 # Number of nanosecond in a millisecond +SECOND = 1000 * MILLISECOND # Number of nanosecond in a second +MINUTE = 60 * SECOND # Number of nanosecond in a minute +HOUR = 60 * MINUTE # Number of nanosecond in a hour +DAY = 24 * HOUR # Number of nanosecond in a day # List of values possible for graph range # Format is [(time_in_plain_text, value_in_nanosecond),...] @@ -58,33 +60,35 @@ # Scrollbar increment in pixel SCROLLBAR_UNIT = 10 + def compute_mask(x, y): return [(xp if xp == yp else "*") for xp, yp in zip(x, y)] + def NextTick(variables): next_tick = None for item, data in variables: if len(data) == 0: continue - + next_tick = (data[0][0] if next_tick is None else min(next_tick, data[0][0])) - + return next_tick -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Debug Variable Graphic Panel Drop Target -#------------------------------------------------------------------------------- - -""" -Class that implements a custom drop target class for Debug Variable Graphic -Panel -""" +# ------------------------------------------------------------------------------- + class DebugVariableDropTarget(wx.TextDropTarget): - + """ + Class that implements a custom drop target class for Debug Variable Graphic + Panel + """ + def __init__(self, window): """ Constructor @@ -92,14 +96,14 @@ """ wx.TextDropTarget.__init__(self) self.ParentWindow = window - + def __del__(self): """ Destructor """ # Remove reference to Debug Variable Panel self.ParentWindow = None - + def OnDragOver(self, x, y, d): """ Function called when mouse is dragged over Drop Target @@ -107,10 +111,10 @@ @param y: Y coordinate of mouse pointer @param d: Suggested default for return value """ - # Signal Debug Variable Panel to refresh highlight giving mouse position + # Signal Debug Variable Panel to refresh highlight giving mouse position self.ParentWindow.RefreshHighlight(x, y) return wx.TextDropTarget.OnDragOver(self, x, y, d) - + def OnDropText(self, x, y, data): """ Function called when mouse is released in Drop Target @@ -120,34 +124,34 @@ """ # Signal Debug Variable Panel to reset highlight self.ParentWindow.ResetHighlight() - + message = None - + # Check that data is valid regarding DebugVariablePanel try: values = eval(data) if not isinstance(values, TupleType): raise ValueError - except: - message = _("Invalid value \"%s\" for debug variable")%data + except Exception: + message = _("Invalid value \"%s\" for debug variable") % data values = None - + # Display message if data is invalid if message is not None: wx.CallAfter(self.ShowMessage, message) - + # Data contain a reference to a variable to debug elif values[1] == "debug": - + # Drag'n Drop is an internal is an internal move inside Debug - # Variable Panel + # Variable Panel if len(values) > 2 and values[2] == "move": self.ParentWindow.MoveValue(values[0]) - + # Drag'n Drop was initiated by another control of Beremiz else: self.ParentWindow.InsertValue(values[0], force=True) - + def OnLeave(self): """ Function called when mouse is leave Drop Target @@ -155,30 +159,25 @@ # Signal Debug Variable Panel to reset highlight self.ParentWindow.ResetHighlight() return wx.TextDropTarget.OnLeave(self) - + def ShowMessage(self, message): """ Show error message in Error Dialog @param message: Error message to display """ - dialog = wx.MessageDialog(self.ParentWindow, - message, - _("Error"), - wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self.ParentWindow, + message, + _("Error"), + wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() -#------------------------------------------------------------------------------- -# Debug Variable Graphic Panel Class -#------------------------------------------------------------------------------- - -""" -Class that implements a Viewer that display variable values as a graphs -""" - class DebugVariablePanel(wx.Panel, DebugViewer): - + """ + Class that implements a Viewer that display variable values as a graphs + """ + def __init__(self, parent, producer, window): """ Constructor @@ -187,49 +186,50 @@ consumers @param window: Reference to Beremiz frame """ - wx.Panel.__init__(self, parent, style=wx.SP_3D|wx.TAB_TRAVERSAL) - + wx.Panel.__init__(self, parent, style=wx.SP_3D | wx.TAB_TRAVERSAL) + # Save Reference to Beremiz frame self.ParentWindow = window - + # Variable storing flag indicating that variable displayed in table # received new value and then table need to be refreshed self.HasNewData = False - + # Variable storing flag indicating that refresh has been forced, and # that next time refresh is possible, it will be done even if no new # data is available self.Force = False - + self.SetBackgroundColour(wx.WHITE) - + main_sizer = wx.BoxSizer(wx.VERTICAL) - - self.Ticks = numpy.array([]) # List of tick received - self.StartTick = 0 # Tick starting range of data displayed - self.Fixed = False # Flag that range of data is fixed - self.CursorTick = None # Tick of cursor for displaying values - + + self.Ticks = numpy.array([]) # List of tick received + self.StartTick = 0 # Tick starting range of data displayed + self.Fixed = False # Flag that range of data is fixed + self.CursorTick = None # Tick of cursor for displaying values + self.DraggingAxesPanel = None self.DraggingAxesBoundingBox = None self.DraggingAxesMousePos = None self.VetoScrollEvent = False - + self.VariableNameMask = [] - + self.GraphicPanels = [] - + graphics_button_sizer = wx.BoxSizer(wx.HORIZONTAL) - main_sizer.AddSizer(graphics_button_sizer, border=5, flag=wx.GROW|wx.ALL) - + main_sizer.AddSizer(graphics_button_sizer, border=5, flag=wx.GROW | wx.ALL) + range_label = wx.StaticText(self, label=_('Range:')) graphics_button_sizer.AddWindow(range_label, flag=wx.ALIGN_CENTER_VERTICAL) - + self.CanvasRange = wx.ComboBox(self, style=wx.CB_READONLY) self.Bind(wx.EVT_COMBOBOX, self.OnRangeChanged, self.CanvasRange) - graphics_button_sizer.AddWindow(self.CanvasRange, 1, - border=5, flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL) - + graphics_button_sizer.AddWindow(self.CanvasRange, 1, + border=5, + flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) + self.CanvasRange.Clear() default_range_idx = 0 for idx, (text, value) in enumerate(RANGE_VALUES): @@ -237,62 +237,62 @@ if text == "1s": default_range_idx = idx self.CanvasRange.SetSelection(default_range_idx) - + for name, bitmap, help in [ - ("CurrentButton", "current", _("Go to current value")), - ("ExportGraphButton", "export_graph", _("Export graph values to clipboard"))]: - button = wx.lib.buttons.GenBitmapButton(self, - bitmap=GetBitmap(bitmap), - size=wx.Size(28, 28), style=wx.NO_BORDER) + ("CurrentButton", "current", _("Go to current value")), + ("ExportGraphButton", "export_graph", _("Export graph values to clipboard"))]: + 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) self.Bind(wx.EVT_BUTTON, getattr(self, "On" + name), button) graphics_button_sizer.AddWindow(button, border=5, flag=wx.LEFT) - - self.CanvasPosition = wx.ScrollBar(self, - size=wx.Size(0, 16), style=wx.SB_HORIZONTAL) - self.CanvasPosition.Bind(wx.EVT_SCROLL_THUMBTRACK, - self.OnPositionChanging, self.CanvasPosition) - self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEUP, - self.OnPositionChanging, self.CanvasPosition) - self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEDOWN, - self.OnPositionChanging, self.CanvasPosition) - self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEUP, - self.OnPositionChanging, self.CanvasPosition) - self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEDOWN, - self.OnPositionChanging, self.CanvasPosition) - main_sizer.AddWindow(self.CanvasPosition, border=5, flag=wx.GROW|wx.LEFT|wx.RIGHT|wx.BOTTOM) - + + self.CanvasPosition = wx.ScrollBar( + self, size=wx.Size(0, 16), style=wx.SB_HORIZONTAL) + self.CanvasPosition.Bind(wx.EVT_SCROLL_THUMBTRACK, + self.OnPositionChanging, self.CanvasPosition) + self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEUP, + self.OnPositionChanging, self.CanvasPosition) + self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEDOWN, + self.OnPositionChanging, self.CanvasPosition) + self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEUP, + self.OnPositionChanging, self.CanvasPosition) + self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEDOWN, + self.OnPositionChanging, self.CanvasPosition) + main_sizer.AddWindow(self.CanvasPosition, border=5, flag=wx.GROW | wx.LEFT | wx.RIGHT | wx.BOTTOM) + self.TickSizer = wx.BoxSizer(wx.HORIZONTAL) - main_sizer.AddSizer(self.TickSizer, border=5, flag=wx.ALL|wx.GROW) - + main_sizer.AddSizer(self.TickSizer, border=5, flag=wx.ALL | wx.GROW) + self.TickLabel = wx.StaticText(self) self.TickSizer.AddWindow(self.TickLabel, border=5, flag=wx.RIGHT) - - self.MaskLabel = wx.TextCtrl(self, style=wx.TE_READONLY|wx.TE_CENTER|wx.NO_BORDER) - self.TickSizer.AddWindow(self.MaskLabel, 1, border=5, flag=wx.RIGHT|wx.GROW) - + + self.MaskLabel = wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_CENTER | wx.NO_BORDER) + self.TickSizer.AddWindow(self.MaskLabel, 1, border=5, flag=wx.RIGHT | wx.GROW) + self.TickTimeLabel = wx.StaticText(self) self.TickSizer.AddWindow(self.TickTimeLabel) - - self.GraphicsWindow = wx.ScrolledWindow(self, style=wx.HSCROLL|wx.VSCROLL) + + self.GraphicsWindow = wx.ScrolledWindow(self, style=wx.HSCROLL | wx.VSCROLL) self.GraphicsWindow.SetBackgroundColour(wx.WHITE) self.GraphicsWindow.SetDropTarget(DebugVariableDropTarget(self)) self.GraphicsWindow.Bind(wx.EVT_ERASE_BACKGROUND, self.OnGraphicsWindowEraseBackground) self.GraphicsWindow.Bind(wx.EVT_PAINT, self.OnGraphicsWindowPaint) self.GraphicsWindow.Bind(wx.EVT_SIZE, self.OnGraphicsWindowResize) self.GraphicsWindow.Bind(wx.EVT_MOUSEWHEEL, self.OnGraphicsWindowMouseWheel) - + main_sizer.AddWindow(self.GraphicsWindow, 1, flag=wx.GROW) - + self.GraphicsSizer = wx.BoxSizer(wx.VERTICAL) self.GraphicsWindow.SetSizer(self.GraphicsSizer) - + DebugViewer.__init__(self, producer, True) - + self.SetSizer(main_sizer) self.SetTickTime() - + def SetTickTime(self, ticktime=0): """ Set Ticktime for calculate data range according to time range selected @@ -300,26 +300,26 @@ """ # Save ticktime self.Ticktime = ticktime - + # Set ticktime to millisecond if undefined if self.Ticktime == 0: self.Ticktime = MILLISECOND - + # Calculate range to apply to data self.CurrentRange = RANGE_VALUES[ self.CanvasRange.GetSelection()][1] / self.Ticktime - + def SetDataProducer(self, producer): """ Set Data Producer @param producer: Data Producer """ DebugViewer.SetDataProducer(self, producer) - + # Set ticktime if data producer is available if self.DataProducer is not None: self.SetTickTime(self.DataProducer.GetTicktime()) - + def RefreshNewData(self): """ Called to refresh Panel according to values received by variables @@ -329,50 +329,50 @@ if self.HasNewData or self.Force: self.HasNewData = False self.RefreshView() - + DebugViewer.RefreshNewData(self) - + def NewDataAvailable(self, ticks): """ Called by DataProducer for each tick captured or by panel to refresh graphs @param tick: PLC tick captured - All other parameters are passed to refresh function + All other parameters are passed to refresh function """ # If tick given if ticks is not None: tick = ticks[-1] - + # Save tick as start tick for range if data is still empty if len(self.Ticks) == 0: self.StartTick = ticks[0] - + # Add tick to list of ticks received self.Ticks = numpy.append(self.Ticks, ticks) - + # Update start tick for range if range follow ticks received if not self.Fixed or tick < self.StartTick + self.CurrentRange: self.StartTick = max(self.StartTick, tick - self.CurrentRange) - + # Force refresh if graph is fixed because range of data received # is too small to fill data range selected if self.Fixed and \ self.Ticks[-1] - self.Ticks[0] < self.CurrentRange: self.Force = True - + self.HasNewData = False self.RefreshView() - + else: DebugViewer.NewDataAvailable(self, ticks) - + def ForceRefresh(self): """ Called to force refresh of graphs """ self.Force = True wx.CallAfter(self.NewDataAvailable, None) - + def SetCursorTick(self, cursor_tick): """ Set Cursor for displaying values of items at a tick given @@ -381,37 +381,36 @@ # Save cursor tick self.CursorTick = cursor_tick self.Fixed = cursor_tick is not None - self.UpdateCursorTick() - + self.UpdateCursorTick() + def MoveCursorTick(self, move): if self.CursorTick is not None: - cursor_tick = max(self.Ticks[0], - min(self.CursorTick + move, - self.Ticks[-1])) + cursor_tick = max(self.Ticks[0], + min(self.CursorTick + move, self.Ticks[-1])) cursor_tick_idx = numpy.argmin(numpy.abs(self.Ticks - cursor_tick)) if self.Ticks[cursor_tick_idx] == self.CursorTick: - cursor_tick_idx = max(0, - min(cursor_tick_idx + abs(move) / move, - len(self.Ticks) - 1)) + cursor_tick_idx = max(0, + min(cursor_tick_idx + abs(move) / move, + len(self.Ticks) - 1)) self.CursorTick = self.Ticks[cursor_tick_idx] - self.StartTick = max(self.Ticks[ - numpy.argmin(numpy.abs(self.Ticks - - self.CursorTick + self.CurrentRange))], - min(self.StartTick, self.CursorTick)) + self.StartTick = max( + self.Ticks[numpy.argmin( + numpy.abs(self.Ticks - self.CursorTick + self.CurrentRange))], + min(self.StartTick, self.CursorTick)) self.RefreshCanvasPosition() - self.UpdateCursorTick() - + self.UpdateCursorTick() + def ResetCursorTick(self): self.CursorTick = None self.Fixed = False self.UpdateCursorTick() - + def UpdateCursorTick(self): for panel in self.GraphicPanels: if isinstance(panel, DebugVariableGraphicViewer): panel.SetCursorTick(self.CursorTick) self.ForceRefresh() - + def StartDragNDrop(self, panel, item, x_mouse, y_mouse, x_mouse_start, y_mouse_start): if len(panel.GetItems()) > 1: self.DraggingAxesPanel = DebugVariableGraphicViewer(self.GraphicsWindow, self, [item], GRAPH_PARALLEL) @@ -424,23 +423,23 @@ self.DraggingAxesPanel = panel self.DraggingAxesBoundingBox = panel.GetAxesBoundingBox(parent_coordinate=True) self.DraggingAxesMousePos = wx.Point( - x_mouse_start - self.DraggingAxesBoundingBox.x, + x_mouse_start - self.DraggingAxesBoundingBox.x, y_mouse_start - self.DraggingAxesBoundingBox.y) self.MoveDragNDrop(x_mouse, y_mouse) - + def MoveDragNDrop(self, x_mouse, y_mouse): self.DraggingAxesBoundingBox.x = x_mouse - self.DraggingAxesMousePos.x self.DraggingAxesBoundingBox.y = y_mouse - self.DraggingAxesMousePos.y self.RefreshHighlight(x_mouse, y_mouse) - + def RefreshHighlight(self, x_mouse, y_mouse): for idx, panel in enumerate(self.GraphicPanels): x, y = panel.GetPosition() width, height = panel.GetSize() rect = wx.Rect(x, y, width, height) - if (rect.InsideXY(x_mouse, y_mouse) or - idx == 0 and y_mouse < 0 or - idx == len(self.GraphicPanels) - 1 and y_mouse > panel.GetPosition()[1]): + if rect.InsideXY(x_mouse, y_mouse) or \ + idx == 0 and y_mouse < 0 or \ + idx == len(self.GraphicPanels) - 1 and y_mouse > panel.GetPosition()[1]: panel.RefreshHighlight(x_mouse - x, y_mouse - y) else: panel.SetHighlight(HIGHLIGHT_NONE) @@ -448,7 +447,7 @@ self.RefreshView() else: self.ForceRefresh() - + def ResetHighlight(self): for panel in self.GraphicPanels: panel.SetHighlight(HIGHLIGHT_NONE) @@ -456,10 +455,10 @@ self.RefreshView() else: self.ForceRefresh() - + def IsDragging(self): return self.DraggingAxesPanel is not None - + def GetDraggingAxesClippingRegion(self, panel): x, y = panel.GetPosition() width, height = panel.GetSize() @@ -468,12 +467,12 @@ bbox.x -= x bbox.y -= y return bbox - + def GetDraggingAxesPosition(self, panel): x, y = panel.GetPosition() return wx.Point(self.DraggingAxesBoundingBox.x - x, self.DraggingAxesBoundingBox.y - y) - + def StopDragNDrop(self, variable, x_mouse, y_mouse): if self.DraggingAxesPanel not in self.GraphicPanels: self.DraggingAxesPanel.Destroy() @@ -504,25 +503,25 @@ idx += 1 wx.CallAfter(self.MoveValue, variable, idx, True) self.ForceRefresh() - return + return width, height = self.GraphicsWindow.GetVirtualSize() rect = wx.Rect(0, 0, width, height) if rect.InsideXY(x_mouse, y_mouse): wx.CallAfter(self.MoveValue, variable, len(self.GraphicPanels), True) self.ForceRefresh() - + def RefreshGraphicsSizer(self): self.GraphicsSizer.Clear() - + for panel in self.GraphicPanels: self.GraphicsSizer.AddWindow(panel, flag=wx.GROW) - + self.GraphicsSizer.Layout() self.RefreshGraphicsWindowScrollbars() - + def RefreshView(self): self.RefreshCanvasPosition() - + width, height = self.GraphicsWindow.GetVirtualSize() bitmap = wx.EmptyBitmap(width, height) dc = wx.BufferedDC(wx.ClientDC(self.GraphicsWindow), bitmap) @@ -531,22 +530,22 @@ if self.DraggingAxesPanel is not None: destBBox = self.DraggingAxesBoundingBox srcBBox = self.DraggingAxesPanel.GetAxesBoundingBox() - + srcBmp = _convert_agg_to_wx_bitmap(self.DraggingAxesPanel.get_renderer(), None) srcDC = wx.MemoryDC() srcDC.SelectObject(srcBmp) - - dc.Blit(destBBox.x, destBBox.y, - int(destBBox.width), int(destBBox.height), + + dc.Blit(destBBox.x, destBBox.y, + int(destBBox.width), int(destBBox.height), srcDC, srcBBox.x, srcBBox.y) dc.EndDrawing() - + if not self.Fixed or self.Force: self.Force = False refresh_graphics = True else: refresh_graphics = False - + if self.DraggingAxesPanel is not None and self.DraggingAxesPanel not in self.GraphicPanels: self.DraggingAxesPanel.RefreshViewer(refresh_graphics) for panel in self.GraphicPanels: @@ -554,7 +553,7 @@ panel.RefreshViewer(refresh_graphics) else: panel.RefreshViewer() - + if self.CursorTick is not None: tick = self.CursorTick elif len(self.Ticks) > 0: @@ -570,27 +569,27 @@ ((tick_duration % DAY) / HOUR, _("%dh")), ((tick_duration % HOUR) / MINUTE, _("%dm")), ((tick_duration % MINUTE) / SECOND, _("%ds"))]: - + if value > 0 or not_null: duration += format % value not_null = True - - duration += _("%03gms") % (float(tick_duration % SECOND) / MILLISECOND) + + duration += _("%03gms") % (float(tick_duration % SECOND) / MILLISECOND) self.TickTimeLabel.SetLabel("t: %s" % duration) else: self.TickLabel.SetLabel("") self.TickTimeLabel.SetLabel("") self.TickSizer.Layout() - + def SubscribeAllDataConsumers(self): DebugViewer.SubscribeAllDataConsumers(self) - + if self.DataProducer is not None: if self.DataProducer is not None: self.SetTickTime(self.DataProducer.GetTicktime()) - + self.ResetCursorTick() - + for panel in self.GraphicPanels[:]: panel.SubscribeAllDataConsumers() if panel.ItemsIsEmpty(): @@ -598,28 +597,28 @@ panel.ReleaseMouse() self.GraphicPanels.remove(panel) panel.Destroy() - + self.ResetVariableNameMask() self.RefreshGraphicsSizer() self.ForceRefresh() - + def ResetView(self): self.UnsubscribeAllDataConsumers() - + self.Fixed = False for panel in self.GraphicPanels: panel.Destroy() self.GraphicPanels = [] self.ResetVariableNameMask() self.RefreshGraphicsSizer() - + def SetCanvasPosition(self, tick): tick = max(self.Ticks[0], min(tick, self.Ticks[-1] - self.CurrentRange)) self.StartTick = self.Ticks[numpy.argmin(numpy.abs(self.Ticks - tick))] self.Fixed = True self.RefreshCanvasPosition() self.ForceRefresh() - + def RefreshCanvasPosition(self): if len(self.Ticks) > 0: pos = int(self.StartTick - self.Ticks[0]) @@ -628,7 +627,7 @@ pos = 0 range = 0 self.CanvasPosition.SetScrollbar(pos, self.CurrentRange, range, self.CurrentRange) - + def ChangeRange(self, dir, tick=None): current_range = self.CurrentRange current_range_idx = self.CanvasRange.GetSelection() @@ -644,7 +643,7 @@ self.StartTick = self.Ticks[numpy.argmin(numpy.abs(self.Ticks - new_start_tick))] self.Fixed = new_start_tick < self.Ticks[-1] - self.CurrentRange self.ForceRefresh() - + def RefreshRange(self): if len(self.Ticks) > 0: if self.Fixed and self.Ticks[-1] - self.Ticks[0] < self.CurrentRange: @@ -654,7 +653,7 @@ else: self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange) self.ForceRefresh() - + def OnRangeChanged(self, event): try: self.CurrentRange = RANGE_VALUES[self.CanvasRange.GetSelection()][1] / self.Ticktime @@ -662,13 +661,13 @@ self.CanvasRange.SetValue(str(self.CurrentRange)) wx.CallAfter(self.RefreshRange) event.Skip() - + def OnCurrentButton(self, event): if len(self.Ticks) > 0: self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange) self.ResetCursorTick() event.Skip() - + def CopyDataToClipboard(self, variables): text = "tick;%s;\n" % ";".join([item.GetVariable() for item, data in variables]) next_tick = NextTick(variables) @@ -693,7 +692,7 @@ text += "%d;%s;\n" % (next_tick, ";".join(values)) next_tick = NextTick(variables) self.ParentWindow.SetCopyBuffer(text) - + def OnExportGraphButton(self, event): items = reduce(lambda x, y: x + y, [panel.GetItems() for panel in self.GraphicPanels], @@ -703,25 +702,25 @@ if item.IsNumVariable()] wx.CallAfter(self.CopyDataToClipboard, variables) event.Skip() - + def OnPositionChanging(self, event): if len(self.Ticks) > 0: self.StartTick = self.Ticks[0] + event.GetPosition() self.Fixed = True self.ForceRefresh() event.Skip() - + def GetRange(self): return self.StartTick, self.StartTick + self.CurrentRange - + def GetViewerIndex(self, viewer): if viewer in self.GraphicPanels: return self.GraphicPanels.index(viewer) return None - + def IsViewerFirst(self, viewer): return viewer == self.GraphicPanels[0] - + def HighlightPreviousViewer(self, viewer): if self.IsViewerFirst(viewer): return @@ -729,25 +728,25 @@ if idx is None: return self.GraphicPanels[idx-1].SetHighlight(HIGHLIGHT_AFTER) - + def ResetVariableNameMask(self): items = [] for panel in self.GraphicPanels: items.extend(panel.GetItems()) if len(items) > 1: - self.VariableNameMask = reduce(compute_mask, - [item.GetVariable().split('.') for item in items]) + self.VariableNameMask = reduce( + compute_mask, [item.GetVariable().split('.') for item in items]) elif len(items) > 0: self.VariableNameMask = items[0].GetVariable().split('.')[:-1] + ['*'] else: self.VariableNameMask = [] self.MaskLabel.ChangeValue(".".join(self.VariableNameMask)) self.MaskLabel.SetInsertionPoint(self.MaskLabel.GetLastPosition()) - + def GetVariableNameMask(self): return self.VariableNameMask - - def InsertValue(self, iec_path, idx = None, force=False, graph=False): + + def InsertValue(self, iec_path, idx=None, force=False, graph=False): for panel in self.GraphicPanels: if panel.GetItem(iec_path) is not None: if graph and isinstance(panel, DebugVariableTextViewer): @@ -758,7 +757,7 @@ item = DebugVariableItem(self, iec_path, True) result = self.AddDataConsumer(iec_path.upper(), item, True) if result is not None or force: - + self.Freeze() if item.IsNumVariable() and graph: panel = DebugVariableGraphicViewer(self.GraphicsWindow, self, [item], GRAPH_PARALLEL) @@ -774,8 +773,8 @@ self.RefreshGraphicsSizer() self.Thaw() self.ForceRefresh() - - def MoveValue(self, iec_path, idx = None, graph=False): + + def MoveValue(self, iec_path, idx=None, graph=False): if idx is None: idx = len(self.GraphicPanels) source_panel = None @@ -787,9 +786,9 @@ break if source_panel is not None: source_panel_idx = self.GraphicPanels.index(source_panel) - + if (len(source_panel.GetItems()) == 1): - + if source_panel_idx < idx: self.GraphicPanels.insert(idx, source_panel) self.GraphicPanels.pop(source_panel_idx) @@ -798,7 +797,7 @@ self.GraphicPanels.insert(idx, source_panel) else: return - + else: source_panel.RemoveItem(item) source_size = source_panel.GetSize() @@ -807,22 +806,22 @@ panel.SetCanvasHeight(source_size.height) if self.CursorTick is not None: panel.SetCursorTick(self.CursorTick) - + else: panel = DebugVariableTextViewer(self.GraphicsWindow, self, [item]) - + self.GraphicPanels.insert(idx, panel) - + if source_panel.ItemsIsEmpty(): if source_panel.HasCapture(): source_panel.ReleaseMouse() source_panel.Destroy() self.GraphicPanels.remove(source_panel) - + self.ResetVariableNameMask() self.RefreshGraphicsSizer() self.ForceRefresh() - + def MergeGraphs(self, source, target_idx, merge_type, force=False): source_item = None source_panel = None @@ -846,11 +845,11 @@ graph_type = target_panel.GraphType if target_panel != source_panel: if (merge_type == GRAPH_PARALLEL and graph_type != merge_type or - merge_type == GRAPH_ORTHOGONAL and + merge_type == GRAPH_ORTHOGONAL and (graph_type == GRAPH_PARALLEL and len(target_panel.Items) > 1 or graph_type == GRAPH_ORTHOGONAL and len(target_panel.Items) >= 3)): return - + if source_panel is not None: source_panel.RemoveItem(source_item) if source_panel.ItemsIsEmpty(): @@ -862,7 +861,7 @@ target_panel.RemoveItem(source_item) else: target_panel = None - + if target_panel is not None: target_panel.AddItem(source_item) target_panel.GraphType = merge_type @@ -874,15 +873,15 @@ else: target_panel.SetCanvasHeight(size.height) target_panel.ResetGraphics() - + self.ResetVariableNameMask() self.RefreshGraphicsSizer() self.ForceRefresh() - + def DeleteValue(self, source_panel, item=None): source_idx = self.GetViewerIndex(source_panel) if source_idx is not None: - + if item is None: source_panel.ClearItems() source_panel.Destroy() @@ -900,7 +899,7 @@ self.Fixed = False self.ResetCursorTick() self.ForceRefresh() - + def ToggleViewerType(self, panel): panel_idx = self.GetViewerIndex(panel) if panel_idx is not None: @@ -916,7 +915,7 @@ panel.Destroy() self.RefreshGraphicsSizer() self.ForceRefresh() - + def ResetGraphicsValues(self): self.Ticks = numpy.array([]) self.StartTick = 0 @@ -931,23 +930,24 @@ posx = max(0, min(xstart, (vwidth - window_size[0]) / SCROLLBAR_UNIT)) posy = max(0, min(ystart, (vheight - window_size[1]) / SCROLLBAR_UNIT)) self.GraphicsWindow.Scroll(posx, posy) - self.GraphicsWindow.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT, - vwidth / SCROLLBAR_UNIT, vheight / SCROLLBAR_UNIT, posx, posy) - + self.GraphicsWindow.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT, + vwidth / SCROLLBAR_UNIT, vheight / SCROLLBAR_UNIT, + posx, posy) + def OnGraphicsWindowEraseBackground(self, event): pass - + def OnGraphicsWindowPaint(self, event): self.RefreshView() event.Skip() - + def OnGraphicsWindowResize(self, event): size = self.GetSize() for panel in self.GraphicPanels: panel_size = panel.GetSize() - if (isinstance(panel, DebugVariableGraphicViewer) and - panel.GraphType == GRAPH_ORTHOGONAL and - panel_size.width == panel_size.height): + if isinstance(panel, DebugVariableGraphicViewer) and \ + panel.GraphType == GRAPH_ORTHOGONAL and \ + panel_size.width == panel_size.height: panel.SetCanvasHeight(size.width) self.RefreshGraphicsWindowScrollbars() self.GraphicsSizer.Layout() diff -r 31e63e25b4cc -r 64beb9e9c749 controls/DebugVariablePanel/DebugVariableTextViewer.py --- a/controls/DebugVariablePanel/DebugVariableTextViewer.py Mon Aug 21 20:17:19 2017 +0000 +++ b/controls/DebugVariablePanel/DebugVariableTextViewer.py Mon Aug 21 23:22:58 2017 +0300 @@ -30,16 +30,16 @@ from DebugVariableViewer import DebugVariableViewer from GraphButton import GraphButton -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Debug Variable Text Viewer Drop Target -#------------------------------------------------------------------------------- - -""" -Class that implements a custom drop target class for Debug Variable Text Viewer -""" +# ------------------------------------------------------------------------------- + class DebugVariableTextDropTarget(wx.TextDropTarget): - + """ + Class that implements a custom drop target class for Debug Variable Text Viewer + """ + def __init__(self, parent, window): """ Constructor @@ -49,7 +49,7 @@ wx.TextDropTarget.__init__(self) self.ParentControl = parent self.ParentWindow = window - + def __del__(self): """ Destructor @@ -58,7 +58,7 @@ # Panel self.ParentControl = None self.ParentWindow = None - + def OnDragOver(self, x, y, d): """ Function called when mouse is dragged over Drop Target @@ -68,9 +68,9 @@ """ # Signal parent that mouse is dragged over self.ParentControl.OnMouseDragging(x, y) - + return wx.TextDropTarget.OnDragOver(self, x, y, d) - + def OnDropText(self, x, y, data): """ Function called when mouse is released in Drop Target @@ -80,76 +80,75 @@ """ # Signal Debug Variable Panel to reset highlight self.ParentWindow.ResetHighlight() - + message = None - + # Check that data is valid regarding DebugVariablePanel try: values = eval(data) if not isinstance(values, TupleType): raise ValueError - except: + except Exception: message = _("Invalid value \"%s\" for debug variable") % data values = None - + # Display message if data is invalid if message is not None: wx.CallAfter(self.ShowMessage, message) - + # Data contain a reference to a variable to debug elif values[1] == "debug": - + # Get Before which Viewer the variable has to be moved or added # according to the position of mouse in Viewer. width, height = self.ParentControl.GetSize() target_idx = self.ParentControl.GetIndex() if y > height / 2: target_idx += 1 - + # Drag'n Drop is an internal is an internal move inside Debug - # Variable Panel + # Variable Panel if len(values) > 2 and values[2] == "move": - self.ParentWindow.MoveValue(values[0], + self.ParentWindow.MoveValue(values[0], target_idx) - + # Drag'n Drop was initiated by another control of Beremiz else: - self.ParentWindow.InsertValue(values[0], - target_idx, + self.ParentWindow.InsertValue(values[0], + target_idx, force=True) - + def OnLeave(self): """ Function called when mouse is leave Drop Target """ # Signal Debug Variable Panel to reset highlight self.ParentWindow.ResetHighlight() - + return wx.TextDropTarget.OnLeave(self) - + def ShowMessage(self, message): """ Show error message in Error Dialog @param message: Error message to display """ - dialog = wx.MessageDialog(self.ParentWindow, - message, - _("Error"), - wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self.ParentWindow, + message, + _("Error"), + wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Debug Variable Text Viewer Class -#------------------------------------------------------------------------------- - -""" -Class that implements a Viewer that display variable values as a text -""" +# ------------------------------------------------------------------------------- class DebugVariableTextViewer(DebugVariableViewer, wx.Panel): - + """ + Class that implements a Viewer that display variable values as a text + """ + def __init__(self, parent, window, items=[]): """ Constructor @@ -158,13 +157,13 @@ @param items: List of DebugVariableItem displayed by Viewer """ DebugVariableViewer.__init__(self, window, items) - + wx.Panel.__init__(self, parent) # Set panel background colour self.SetBackgroundColour(wx.WHITE) # Define panel drop target self.SetDropTarget(DebugVariableTextDropTarget(self, window)) - + # Bind events self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) @@ -174,16 +173,16 @@ self.Bind(wx.EVT_SIZE, self.OnResize) self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) self.Bind(wx.EVT_PAINT, self.OnPaint) - + # Define panel min size for parent sizer layout self.SetMinSize(wx.Size(0, 25)) - + # Add buttons to Viewer for bitmap, callback in [("force", self.OnForceButton), ("release", self.OnReleaseButton), ("delete_graph", self.OnCloseButton)]: self.Buttons.append(GraphButton(0, 0, bitmap, callback)) - + def RefreshViewer(self): """ Method that refresh the content displayed by Viewer @@ -193,24 +192,24 @@ bitmap = wx.EmptyBitmap(width, height) dc = wx.BufferedDC(wx.ClientDC(self), bitmap) dc.Clear() - + # Get Graphics Context for DC, for anti-aliased and transparent # rendering gc = wx.GCDC(dc) - + gc.BeginDrawing() - + # Get first item item = self.ItemsDict.values()[0] - + # Get item variable path masked according Debug Variable Panel mask item_path = item.GetVariable( self.ParentWindow.GetVariableNameMask()) - + # Draw item variable path at Viewer left side w, h = gc.GetTextExtent(item_path) gc.DrawText(item_path, 20, (height - h) / 2) - + # Update 'Release' button state and text color according to item forced # flag value item_forced = item.IsForced() @@ -218,17 +217,17 @@ self.RefreshButtonsPosition() if item_forced: gc.SetTextForeground(wx.BLUE) - + # Draw item current value at right side of Viewer item_value = item.GetValue() w, h = gc.GetTextExtent(item_value) gc.DrawText(item_value, width - 40 - w, (height - h) / 2) - + # Draw other Viewer common elements self.DrawCommonElements(gc) - + gc.EndDrawing() - + def OnLeftDown(self, event): """ Function called when mouse left button is pressed @@ -236,15 +235,15 @@ """ # Get first item item = self.ItemsDict.values()[0] - + # Calculate item path bounding box width, height = self.GetSize() item_path = item.GetVariable( self.ParentWindow.GetVariableNameMask()) w, h = self.GetTextExtent(item_path) - + # Test if mouse has been pressed in this bounding box. In that case - # start a move drag'n drop of item variable + # start a move drag'n drop of item variable x, y = event.GetPosition() item_path_bbox = wx.Rect(20, (height - h) / 2, w, h) if item_path_bbox.InsideXY(x, y): @@ -253,11 +252,11 @@ dragSource = wx.DropSource(self) dragSource.SetData(data) dragSource.DoDragDrop() - + # In other case handle event normally else: event.Skip() - + def OnLeftUp(self, event): """ Function called when mouse left button is released @@ -267,7 +266,7 @@ x, y = event.GetPosition() wx.CallAfter(self.HandleButton, x, y) event.Skip() - + def OnLeftDClick(self, event): """ Function called when mouse left button is double clicked @@ -276,7 +275,7 @@ # Only numeric variables can be toggled to graph canvas if self.ItemsDict.values()[0].IsNumVariable(): self.ParentWindow.ToggleViewerType(self) - + def OnPaint(self, event): """ Function called when redrawing Viewer content is needed diff -r 31e63e25b4cc -r 64beb9e9c749 controls/DebugVariablePanel/DebugVariableViewer.py --- a/controls/DebugVariablePanel/DebugVariableViewer.py Mon Aug 21 20:17:19 2017 +0000 +++ b/controls/DebugVariablePanel/DebugVariableViewer.py Mon Aug 21 23:22:58 2017 +0300 @@ -46,17 +46,17 @@ HIGHLIGHT_RESIZE_PEN = wx.Pen(wx.Colour(200, 200, 200)) HIGHLIGHT_RESIZE_BRUSH = wx.Brush(wx.Colour(200, 200, 200)) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Base Debug Variable Viewer Class -#------------------------------------------------------------------------------- - -""" -Class that implements a generic viewer that display a list of variable values -This class has to be inherited to effectively display variable values -""" +# ------------------------------------------------------------------------------- + class DebugVariableViewer: - + """ + Class that implements a generic viewer that display a list of variable values + This class has to be inherited to effectively display variable values + """ + def __init__(self, window, items=[]): """ Constructor @@ -67,26 +67,26 @@ self.ItemsDict = OrderedDict([(item.GetVariable(), item) for item in items]) self.Items = self.ItemsDict.viewvalues() - + # Variable storing current highlight displayed in Viewer self.Highlight = HIGHLIGHT_NONE # List of buttons self.Buttons = [] - + def __del__(self): """ Destructor """ # Remove reference to Debug Variable Panel self.ParentWindow = None - + def GetIndex(self): """ Return position of Viewer in Debug Variable Panel @return: Position of Viewer """ return self.ParentWindow.GetViewerIndex(self) - + def GetItem(self, variable): """ Return item storing values of a variable @@ -94,28 +94,28 @@ @return: Item storing values of this variable """ return self.ItemsDict.get(variable, None) - + def GetItems(self): """ Return items displayed by Viewer @return: List of items displayed in Viewer """ return self.ItemsDict.values() - + def AddItem(self, item): """ Add an item to the list of items displayed by Viewer @param item: Item to add to the list """ self.ItemsDict[item.GetVariable()] = item - + def RemoveItem(self, item): """ Remove an item from the list of items displayed by Viewer @param item: Item to remove from the list """ self.ItemsDict.pop(item.GetVariable(), None) - + def ClearItems(self): """ Clear list of items displayed by Viewer @@ -123,17 +123,17 @@ # Unsubscribe every items of the list for item in self.Items: self.ParentWindow.RemoveDataConsumer(item) - + # Clear list self.ItemsDict.clear() - + def ItemsIsEmpty(self): """ Return if list of items displayed by Viewer is empty @return: True if list is empty """ return len(self.Items) == 0 - + def SubscribeAllDataConsumers(self): """ Function that unsubscribe and remove every item that store values of @@ -141,7 +141,7 @@ """ for item in self.ItemsDict.values()[:]: iec_path = item.GetVariable() - + # Check that variablepath exist in PLC if self.ParentWindow.GetDataType(iec_path) is None: # If not, unsubscribe and remove it @@ -151,30 +151,30 @@ # If it exist, resubscribe and refresh data type self.ParentWindow.AddDataConsumer(iec_path.upper(), item, True) item.RefreshVariableType() - + def ResetItemsData(self): """ Reset data stored in every items displayed in Viewer """ for item in self.Items: item.ResetData() - + def GetItemsMinCommonTick(self): """ Return the minimum tick common to all iems displayed in Viewer @return: Minimum common tick between items """ - return reduce(max, [item.GetData()[0, 0] + return reduce(max, [item.GetData()[0, 0] for item in self.Items if len(item.GetData()) > 0], 0) - + def RefreshViewer(self): """ Method that refresh the content displayed by Viewer Need to be overridden by inherited classes """ pass - + def SetHighlight(self, highlight): """ Set Highlight type displayed in Viewer @@ -183,17 +183,17 @@ # Return immediately if highlight don't change if self.Highlight == highlight: return False - + self.Highlight = highlight return True - + def GetButtons(self): """ Return list of buttons defined in Viewer @return: List of buttons """ return self.Buttons - + def IsOverButton(self, x, y): """ Return if point is over one button of Viewer @@ -205,7 +205,7 @@ if button.HitTest(x, y): return button return None - + def HandleButton(self, x, y): """ Search for the button under point and if found execute associated @@ -217,10 +217,10 @@ button = self.IsOverButton(x, y) if button is None: return False - + button.ProcessCallback() return True - + def ShowButtons(self, show): """ Set display state of buttons in Viewer @@ -229,27 +229,27 @@ # Change display of every buttons for button in self.Buttons: button.Show(show) - + # Refresh button positions self.RefreshButtonsPosition() self.RefreshViewer() - + def RefreshButtonsPosition(self): """ Function that refresh buttons position in Viewer """ # Get Viewer size width, height = self.GetSize() - + # Buttons are align right so we calculate buttons positions in # reverse order buttons = self.Buttons[:] buttons.reverse() - + # Position offset on x coordinate x_offset = 0 for button in buttons: - # Buttons are stacked right, removing those that are not active + # Buttons are stacked right, removing those that are not active if button.IsEnabled(): # Update button position according to button width and offset # on x coordinate @@ -257,7 +257,7 @@ button.SetPosition(width - 5 - w - x_offset, 5) # Update offset on x coordinate x_offset += w + 2 - + def DrawCommonElements(self, dc, buttons=None): """ Function that draw common graphics for every Viewers @@ -268,26 +268,26 @@ """ # Get Viewer size width, height = self.GetSize() - + # Set dc styling for drop before or drop after highlight dc.SetPen(HIGHLIGHT_DROP_PEN) dc.SetBrush(HIGHLIGHT_DROP_BRUSH) - + # Draw line at upper side of Viewer if highlight is drop before if self.Highlight == HIGHLIGHT_BEFORE: dc.DrawLine(0, 1, width - 1, 1) - + # Draw line at lower side of Viewer if highlight is drop before elif self.Highlight == HIGHLIGHT_AFTER: dc.DrawLine(0, height - 1, width - 1, height - 1) - + # If no specific buttons are defined, get default buttons if buttons is None: buttons = self.Buttons # Draw buttons for button in buttons: button.Draw(dc) - + # If graph dragging is processing if self.ParentWindow.IsDragging(): destBBox = self.ParentWindow.GetDraggingAxesClippingRegion(self) @@ -295,55 +295,55 @@ if destBBox.width > 0 and destBBox.height > 0: srcPanel = self.ParentWindow.DraggingAxesPanel srcBBox = srcPanel.GetAxesBoundingBox() - + srcX = srcBBox.x - (srcPos.x if destBBox.x == 0 else 0) srcY = srcBBox.y - (srcPos.y if destBBox.y == 0 else 0) - + srcBmp = _convert_agg_to_wx_bitmap( srcPanel.get_renderer(), None) srcDC = wx.MemoryDC() srcDC.SelectObject(srcBmp) - - dc.Blit(destBBox.x, destBBox.y, - int(destBBox.width), int(destBBox.height), + + dc.Blit(destBBox.x, destBBox.y, + int(destBBox.width), int(destBBox.height), srcDC, srcX, srcY) - + def OnEnter(self, event): """ Function called when entering Viewer - @param event: wx.MouseEvent + @param event: wx.MouseEvent """ # Display buttons self.ShowButtons(True) event.Skip() - + def OnLeave(self, event): """ Function called when leaving Viewer - @param event: wx.MouseEvent + @param event: wx.MouseEvent """ # Hide buttons self.ShowButtons(False) event.Skip() - + def OnCloseButton(self): """ Function called when Close button is pressed """ wx.CallAfter(self.ParentWindow.DeleteValue, self) - + def OnForceButton(self): """ Function called when Force button is pressed """ self.ForceValue(self.ItemsDict.values()[0]) - + def OnReleaseButton(self): """ Function called when Release button is pressed """ self.ReleaseValue(self.ItemsDict.values()[0]) - + def OnMouseDragging(self, x, y): """ Function called when mouse is dragged over Viewer @@ -354,7 +354,7 @@ # Refresh highlight in Debug Variable Panel (highlight can be displayed # in another Viewer self.ParentWindow.RefreshHighlight(x + xw, y + yw) - + def RefreshHighlight(self, x, y): """ Function called by Debug Variable Panel asking Viewer to refresh @@ -364,22 +364,22 @@ """ # Get Viewer size width, height = self.GetSize() - + # Mouse is in the first half of Viewer if y < height / 2: # If Viewer is the upper one, draw drop before highlight if self.ParentWindow.IsViewerFirst(self): self.SetHighlight(HIGHLIGHT_BEFORE) - + # Else draw drop after highlight in previous Viewer else: self.SetHighlight(HIGHLIGHT_NONE) self.ParentWindow.HighlightPreviousViewer(self) - - # Mouse is in the second half of Viewer, draw drop after highlight + + # Mouse is in the second half of Viewer, draw drop after highlight else: self.SetHighlight(HIGHLIGHT_AFTER) - + def OnEraseBackground(self, event): """ Function called when Viewer background is going to be erase @@ -387,7 +387,7 @@ """ # Prevent flicker on Windows pass - + def OnResize(self, event): """ Function called when Viewer size changed @@ -397,7 +397,7 @@ self.RefreshButtonsPosition() self.ParentWindow.ForceRefresh() event.Skip() - + def ForceValue(self, item): """ Force value of item given @@ -409,13 +409,13 @@ # Return immediately if not found if iec_type is None: return - + # Open a dialog to enter varaible forced value dialog = ForceVariableDialog(self, iec_type, str(item.GetValue())) if dialog.ShowModal() == wx.ID_OK: self.ParentWindow.ForceDataValue(iec_path.upper(), dialog.GetValue()) - + def ReleaseValue(self, item): """ Release value of item given diff -r 31e63e25b4cc -r 64beb9e9c749 controls/DebugVariablePanel/GraphButton.py --- a/controls/DebugVariablePanel/GraphButton.py Mon Aug 21 20:17:19 2017 +0000 +++ b/controls/DebugVariablePanel/GraphButton.py Mon Aug 21 23:22:58 2017 +0300 @@ -26,16 +26,16 @@ from util.BitmapLibrary import GetBitmap -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Custom button for Graphic Viewer Class -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -""" -Class that implements a custom button for graphic Viewer -""" class GraphButton(): - + """ + Class that implements a custom button for graphic Viewer + """ + def __init__(self, x, y, bitmap, callback): """ Constructor @@ -48,21 +48,21 @@ self.SetPosition(x, y) # Set button bitmap self.SetBitmap(bitmap) - + # By default button is hide and enabled self.Shown = False self.Enabled = True - + # Save reference to callback function self.Callback = callback - + def __del__(self): """ Destructor """ # Remove reference to callback function self.callback = None - + def SetBitmap(self, bitmap): """ Set bitmap to use for button @@ -70,7 +70,7 @@ """ # Get wx.Bitmap object corresponding to bitmap self.Bitmap = GetBitmap(bitmap) - + def GetSize(self): """ Return size of button @@ -78,7 +78,7 @@ """ # Button size is size of bitmap return self.Bitmap.GetSize() - + def SetPosition(self, x, y): """ Set button position @@ -86,7 +86,7 @@ @param y: Y coordinate of Button in Graphic Viewer """ self.Position = wx.Point(x, y) - + def Show(self, show=True): """ Mark if button to be displayed in Graphic Viewer @@ -94,20 +94,20 @@ (default True) """ self.Shown = show - + def Hide(self): """ Hide button from Graphic Viewer """ self.Show(False) - + def IsShown(self): """ Return if button is displayed in Graphic Viewer @return: True if button is displayed in Graphic Viewer """ return self.Shown - + def Enable(self, enable=True): """ Mark if button is active in Graphic Viewer @@ -115,44 +115,44 @@ (default True) """ self.Enabled = enable - + def Disable(self): """ Deactivate button in Graphic Viewer """ self.Enabled = False - + def IsEnabled(self): """ Return if button is active in Graphic Viewer @return: True if button is active in Graphic Viewer """ return self.Enabled - + def HitTest(self, x, y): """ Test if point is inside button @param x: X coordinate of point @param y: Y coordinate of point @return: True if button is active and displayed and point is inside - button + button """ # Return immediately if button is hidden or inactive if not (self.IsShown() and self.IsEnabled()): return False - + # Test if point is inside button w, h = self.Bitmap.GetSize() rect = wx.Rect(self.Position.x, self.Position.y, w, h) return rect.InsideXY(x, y) - + def ProcessCallback(self): """ Call callback function if defined """ if self.Callback is not None: self.Callback() - + def Draw(self, dc): """ Draw button in Graphic Viewer diff -r 31e63e25b4cc -r 64beb9e9c749 controls/DurationCellEditor.py --- a/controls/DurationCellEditor.py Mon Aug 21 20:17:19 2017 +0000 +++ b/controls/DurationCellEditor.py Mon Aug 21 23:22:58 2017 +0300 @@ -26,40 +26,41 @@ from dialogs.DurationEditorDialog import DurationEditorDialog + class DurationCellControl(wx.PyControl): - + ''' Custom cell editor control with a text box and a button that launches the DurationEditorDialog. ''' def __init__(self, parent): wx.Control.__init__(self, parent) - + main_sizer = wx.FlexGridSizer(cols=2, hgap=0, rows=1, vgap=0) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(0) - + # create location text control - self.Duration = wx.TextCtrl(self, size=wx.Size(0, -1), - style=wx.TE_PROCESS_ENTER) + self.Duration = wx.TextCtrl(self, size=wx.Size(0, -1), + style=wx.TE_PROCESS_ENTER) self.Duration.Bind(wx.EVT_KEY_DOWN, self.OnDurationChar) main_sizer.AddWindow(self.Duration, flag=wx.GROW) - + # create browse button self.EditButton = wx.Button(self, label='...', size=wx.Size(30, -1)) self.Bind(wx.EVT_BUTTON, self.OnEditButtonClick, self.EditButton) main_sizer.AddWindow(self.EditButton, flag=wx.GROW) - + self.Bind(wx.EVT_SIZE, self.OnSize) - + self.SetSizer(main_sizer) - + self.Default = None - + def SetValue(self, value): self.Default = value self.Duration.SetValue(value) - + def GetValue(self): return self.Duration.GetValue() @@ -90,23 +91,24 @@ def SetInsertionPoint(self, i): self.Duration.SetInsertionPoint(i) - + def SetFocus(self): self.Duration.SetFocus() + class DurationCellEditor(wx.grid.PyGridCellEditor): ''' Grid cell editor that uses DurationCellControl to display an edit button. ''' def __init__(self, table, colname): wx.grid.PyGridCellEditor.__init__(self) - + self.Table = table self.Colname = colname - + def __del__(self): self.CellControl = None - + def Create(self, parent, id, evt_handler): self.CellControl = DurationCellControl(parent) self.SetControl(self.CellControl) @@ -131,13 +133,13 @@ return self.EndEditInternal(row, col, grid, oldval) else: def EndEdit(self, row, col, grid): - oldval = self.Table.GetValueByName(row, self.Colname) - return self.EndEditInternal(row, col, grid, oldval) + oldval = self.Table.GetValueByName(row, self.Colname) + return self.EndEditInternal(row, col, grid, oldval) def SetSize(self, rect): self.CellControl.SetDimensions(rect.x + 1, rect.y, - rect.width, rect.height, - wx.SIZE_ALLOW_MINUS_ONE) + rect.width, rect.height, + wx.SIZE_ALLOW_MINUS_ONE) def Clone(self): return DurationCellEditor(self.Table) diff -r 31e63e25b4cc -r 64beb9e9c749 controls/EnhancedStatusBar.py --- a/controls/EnhancedStatusBar.py Mon Aug 21 20:17:19 2017 +0000 +++ b/controls/EnhancedStatusBar.py Mon Aug 21 23:22:58 2017 +0300 @@ -1,248 +1,246 @@ -# --------------------------------------------------------------------------- # -# ENHANCEDSTATUSBAR wxPython IMPLEMENTATION -# Python Code By: -# -# Andrea Gavana, @ 31 May 2005 -# Nitro, @ 21 September 2005 -# Latest Revision: 21 September 2005, 19.57.20 GMT+2 -# Latest Revision before Latest Revision: 21 September 2005, 18.29.35 GMT+2 -# Latest Revision before Latest Revision before Latest Revision: 31 May 2005, 23.17 CET -# -# -# TODO List/Caveats -# -# 1. Some Requests/Features To Add? -# -# -# For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please -# Write To Me At: -# -# -# andrea.gavana@agip.it -# andrea_gavan@tin.it -# -# Or, Obviously, To The wxPython Mailing List!!! -# -# -# licensed under wxWidgets License (GPL compatible) -# End Of Comments -# --------------------------------------------------------------------------- # - -""" Description: - -EnhancedStatusBar Is A Slight Modification (Actually A Subclassing) Of wx.StatusBar. -It Allows You To Add Almost Any Widget You Like To The wx.StatusBar Of Your Main -Frame Application And Also To Layout Them Properly. - - -What It Can Do: - -1) Almost All The Functionalities Of wx.StatusBar Are Still Present; -2) You Can Add Different Kind Of Widgets Into Every Field Of The EnhancedStatusBar; -3) The AddWidget() Methods Accepts 2 Layout Inputs: - - horizontalalignment: This Specify The Horizontal Alignment For Your Widget, - And Can Be ESB_EXACT_FIT, ESB_ALIGN_CENTER_HORIZONTAL, ESB_ALIGN_LEFT And - ESB_ALIGN_RIGHT; - - varticalalignment: This Specify The Vertical Alignment For Your Widget, - And Can Be ESB_EXACT_FIT, ESB_ALIGN_CENTER_VERTICAL, ESB_ALIGN_BOTTOM And - ESB_ALIGN_LEFT; - -EnhancedStatusBar Is Freeware And Distributed Under The wxPython License. - -Latest Revision: 21 September 2005, 19.57.20 GMT+2 -Latest Revision before Latest Revision: 21 September 2005, 18.29.35 GMT+2 -Latest Revision before Latest Revision before Latest Revision: 31 May 2005, 23.17 CET - -""" - -import wx - -# Horizontal Alignment Constants -ESB_ALIGN_CENTER_VERTICAL = 1 -ESB_ALIGN_TOP = 2 -ESB_ALIGN_BOTTOM = 3 - -# Vertical Alignment Constants -ESB_ALIGN_CENTER_HORIZONTAL = 11 -ESB_ALIGN_LEFT = 12 -ESB_ALIGN_RIGHT = 13 - -# Exact Fit (Either Horizontal Or Vertical Or Both) Constant -ESB_EXACT_FIT = 20 - - -# --------------------------------------------------------------- -# Class EnhancedStatusBar -# --------------------------------------------------------------- -# This Is The Main Class Implementation. See The Demo For Details -# --------------------------------------------------------------- -class EnhancedStatusBarItem(object): - def __init__(self, widget, pos, horizontalalignment=ESB_ALIGN_CENTER_HORIZONTAL, verticalalignment=ESB_ALIGN_CENTER_VERTICAL): - self.__dict__.update( locals() ) - -class EnhancedStatusBar(wx.StatusBar): - - def __init__(self, parent, id=wx.ID_ANY, style=wx.ST_SIZEGRIP, - name="EnhancedStatusBar"): - """Default Class Constructor. - - EnhancedStatusBar.__init__(self, parent, id=wx.ID_ANY, - style=wx.ST_SIZEGRIP, - name="EnhancedStatusBar") - """ - - wx.StatusBar.__init__(self, parent, id, style, name) - - self._items = {} - self._curPos = 0 - self._parent = parent - - wx.EVT_SIZE(self, self.OnSize) - wx.CallAfter(self.OnSize, None) - - - def OnSize(self, event): - """Handles The wx.EVT_SIZE Events For The StatusBar. - - Actually, All The Calculations Linked To HorizontalAlignment And - VerticalAlignment Are Done In This Function.""" - - for pos, item in self._items.items(): - widget, horizontalalignment, verticalalignment = item.widget, item.horizontalalignment, item.verticalalignment - - rect = self.GetFieldRect(pos) - widgetpos = widget.GetPosition() - widgetsize = widget.GetSize() - - rect = self.GetFieldRect(pos) - - if horizontalalignment == ESB_EXACT_FIT: - - if verticalalignment == ESB_EXACT_FIT: - """ 1 September 2015 Fix fit align """ - widget.SetSize((rect.width-4, rect.height-4)) - widget.SetPosition((rect.x+2, rect.y+2)) - elif verticalalignment == ESB_ALIGN_CENTER_VERTICAL: - if widgetsize[1] < rect.width - 1: - diffs = (rect.height - widgetsize[1])/2 - widget.SetSize((rect.width-2, widgetsize[1])) - widget.SetPosition((rect.x-1, rect.y+diffs)) - else: - widget.SetSize((rect.width-2, widgetsize[1])) - widget.SetPosition((rect.x-1, rect.y-1)) - elif verticalalignment == ESB_ALIGN_TOP: - widget.SetSize((rect.width-2, widgetsize[1])) - widget.SetPosition((rect.x-1, rect.y)) - elif verticalalignment == ESB_ALIGN_BOTTOM: - widget.SetSize((rect.width-2, widgetsize[1])) - widget.SetPosition((rect.x-1, rect.height-widgetsize[1])) - - elif horizontalalignment == ESB_ALIGN_LEFT: - - xpos = rect.x - 1 - if verticalalignment == ESB_EXACT_FIT: - widget.SetSize((widgetsize[0], rect.height-2)) - widget.SetPosition((xpos, rect.y-1)) - elif verticalalignment == ESB_ALIGN_CENTER_VERTICAL: - if widgetsize[1] < rect.height - 1: - diffs = (rect.height - widgetsize[1])/2 - widget.SetPosition((xpos, rect.y+diffs)) - else: - widget.SetSize((widgetsize[0], rect.height-2)) - widget.SetPosition((xpos, rect.y-1)) - elif verticalalignment == ESB_ALIGN_TOP: - widget.SetPosition((xpos, rect.y)) - elif verticalalignment == ESB_ALIGN_BOTTOM: - widget.SetPosition((xpos, rect.height-widgetsize[1])) - - elif horizontalalignment == ESB_ALIGN_RIGHT: - - xpos = rect.x + rect.width - widgetsize[0] - 1 - if verticalalignment == ESB_EXACT_FIT: - widget.SetSize((widgetsize[0], rect.height-2)) - widget.SetPosition((xpos, rect.y-1)) - elif verticalalignment == ESB_ALIGN_CENTER_VERTICAL: - if widgetsize[1] < rect.height - 1: - diffs = (rect.height - widgetsize[1])/2 - widget.SetPosition((xpos, rect.y+diffs)) - else: - widget.SetSize((widgetsize[0], rect.height-2)) - widget.SetPosition((xpos, rect.y-1)) - elif verticalalignment == ESB_ALIGN_TOP: - widget.SetPosition((xpos, rect.y)) - elif verticalalignment == ESB_ALIGN_BOTTOM: - widget.SetPosition((xpos, rect.height-widgetsize[1])) - - elif horizontalalignment == ESB_ALIGN_CENTER_HORIZONTAL: - - xpos = rect.x + (rect.width - widgetsize[0])/2 - 1 - if verticalalignment == ESB_EXACT_FIT: - widget.SetSize((widgetsize[0], rect.height)) - widget.SetPosition((xpos, rect.y)) - elif verticalalignment == ESB_ALIGN_CENTER_VERTICAL: - if widgetsize[1] < rect.height - 1: - diffs = (rect.height - widgetsize[1])/2 - widget.SetPosition((xpos, rect.y+diffs)) - else: - widget.SetSize((widgetsize[0], rect.height-1)) - widget.SetPosition((xpos, rect.y+1)) - elif verticalalignment == ESB_ALIGN_TOP: - widget.SetPosition((xpos, rect.y)) - elif verticalalignment == ESB_ALIGN_BOTTOM: - widget.SetPosition((xpos, rect.height-widgetsize[1])) - - - if event is not None: - event.Skip() - - - def AddWidget(self, widget, horizontalalignment=ESB_ALIGN_CENTER_HORIZONTAL, - verticalalignment=ESB_ALIGN_CENTER_VERTICAL, pos = -1): - """Add A Widget To The EnhancedStatusBar. - - Parameters: - - - horizontalalignment: This Can Be One Of: - a) ESB_EXACT_FIT: The Widget Will Fit Horizontally The StatusBar Field Width; - b) ESB_ALIGN_CENTER_HORIZONTAL: The Widget Will Be Centered Horizontally In - The StatusBar Field; - c) ESB_ALIGN_LEFT: The Widget Will Be Left Aligned In The StatusBar Field; - d) ESB_ALIGN_RIGHT: The Widget Will Be Right Aligned In The StatusBar Field; - - - verticalalignment: - a) ESB_EXACT_FIT: The Widget Will Fit Vertically The StatusBar Field Height; - b) ESB_ALIGN_CENTER_VERTICAL: The Widget Will Be Centered Vertically In - The StatusBar Field; - c) ESB_ALIGN_BOTTOM: The Widget Will Be Bottom Aligned In The StatusBar Field; - d) ESB_ALIGN_TOP: The Widget Will Be TOP Aligned In The StatusBar Field; - - """ - - if pos == -1: - pos = self._curPos - self._curPos += 1 - - if self.GetFieldsCount() <= pos: - raise "\nERROR: EnhancedStatusBar has a max of %d items, you tried to set item #%d" % (self.GetFieldsCount(), pos) - - if horizontalalignment not in [ESB_ALIGN_CENTER_HORIZONTAL, ESB_EXACT_FIT, - ESB_ALIGN_LEFT, ESB_ALIGN_RIGHT]: - raise '\nERROR: Parameter "horizontalalignment" Should Be One Of '\ - '"ESB_ALIGN_CENTER_HORIZONTAL", "ESB_ALIGN_LEFT", "ESB_ALIGN_RIGHT"' \ - '"ESB_EXACT_FIT"' - - if verticalalignment not in [ESB_ALIGN_CENTER_VERTICAL, ESB_EXACT_FIT, - ESB_ALIGN_TOP, ESB_ALIGN_BOTTOM]: - raise '\nERROR: Parameter "verticalalignment" Should Be One Of '\ - '"ESB_ALIGN_CENTER_VERTICAL", "ESB_ALIGN_TOP", "ESB_ALIGN_BOTTOM"' \ - '"ESB_EXACT_FIT"' - - - try: - self.RemoveChild(self._items[pos].widget) - self._items[pos].widget.Destroy() - except KeyError: pass - - self._items[pos] = EnhancedStatusBarItem(widget, pos, horizontalalignment, verticalalignment) - - wx.CallAfter(self.OnSize, None) +# --------------------------------------------------------------------------- # +# ENHANCEDSTATUSBAR wxPython IMPLEMENTATION +# Python Code By: +# +# Andrea Gavana, @ 31 May 2005 +# Nitro, @ 21 September 2005 +# Latest Revision: 21 September 2005, 19.57.20 GMT+2 +# Latest Revision before Latest Revision: 21 September 2005, 18.29.35 GMT+2 +# Latest Revision before Latest Revision before Latest Revision: 31 May 2005, 23.17 CET +# +# +# TODO List/Caveats +# +# 1. Some Requests/Features To Add? +# +# +# For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please +# Write To Me At: +# +# +# andrea.gavana@agip.it +# andrea_gavan@tin.it +# +# Or, Obviously, To The wxPython Mailing List!!! +# +# +# licensed under wxWidgets License (GPL compatible) +# End Of Comments +# --------------------------------------------------------------------------- # + +""" Description: + +EnhancedStatusBar Is A Slight Modification (Actually A Subclassing) Of wx.StatusBar. +It Allows You To Add Almost Any Widget You Like To The wx.StatusBar Of Your Main +Frame Application And Also To Layout Them Properly. + + +What It Can Do: + +1) Almost All The Functionalities Of wx.StatusBar Are Still Present; +2) You Can Add Different Kind Of Widgets Into Every Field Of The EnhancedStatusBar; +3) The AddWidget() Methods Accepts 2 Layout Inputs: + - horizontalalignment: This Specify The Horizontal Alignment For Your Widget, + And Can Be ESB_EXACT_FIT, ESB_ALIGN_CENTER_HORIZONTAL, ESB_ALIGN_LEFT And + ESB_ALIGN_RIGHT; + - varticalalignment: This Specify The Vertical Alignment For Your Widget, + And Can Be ESB_EXACT_FIT, ESB_ALIGN_CENTER_VERTICAL, ESB_ALIGN_BOTTOM And + ESB_ALIGN_LEFT; + +EnhancedStatusBar Is Freeware And Distributed Under The wxPython License. + +Latest Revision: 21 September 2005, 19.57.20 GMT+2 +Latest Revision before Latest Revision: 21 September 2005, 18.29.35 GMT+2 +Latest Revision before Latest Revision before Latest Revision: 31 May 2005, 23.17 CET + +""" + +import wx + +# Horizontal Alignment Constants +ESB_ALIGN_CENTER_VERTICAL = 1 +ESB_ALIGN_TOP = 2 +ESB_ALIGN_BOTTOM = 3 + +# Vertical Alignment Constants +ESB_ALIGN_CENTER_HORIZONTAL = 11 +ESB_ALIGN_LEFT = 12 +ESB_ALIGN_RIGHT = 13 + +# Exact Fit (Either Horizontal Or Vertical Or Both) Constant +ESB_EXACT_FIT = 20 + + +# --------------------------------------------------------------- +# Class EnhancedStatusBar +# --------------------------------------------------------------- +# This Is The Main Class Implementation. See The Demo For Details +# --------------------------------------------------------------- +class EnhancedStatusBarItem(object): + def __init__(self, widget, pos, horizontalalignment=ESB_ALIGN_CENTER_HORIZONTAL, verticalalignment=ESB_ALIGN_CENTER_VERTICAL): + self.__dict__.update(locals()) + + +class EnhancedStatusBar(wx.StatusBar): + + def __init__(self, parent, id=wx.ID_ANY, style=wx.ST_SIZEGRIP, + name="EnhancedStatusBar"): + """Default Class Constructor. + + EnhancedStatusBar.__init__(self, parent, id=wx.ID_ANY, + style=wx.ST_SIZEGRIP, + name="EnhancedStatusBar") + """ + + wx.StatusBar.__init__(self, parent, id, style, name) + + self._items = {} + self._curPos = 0 + self._parent = parent + + wx.EVT_SIZE(self, self.OnSize) + wx.CallAfter(self.OnSize, None) + + def OnSize(self, event): + """Handles The wx.EVT_SIZE Events For The StatusBar. + + Actually, All The Calculations Linked To HorizontalAlignment And + VerticalAlignment Are Done In This Function.""" + + for pos, item in self._items.items(): + widget, horizontalalignment, verticalalignment = item.widget, item.horizontalalignment, item.verticalalignment + + rect = self.GetFieldRect(pos) + widgetpos = widget.GetPosition() + widgetsize = widget.GetSize() + + rect = self.GetFieldRect(pos) + + if horizontalalignment == ESB_EXACT_FIT: + + if verticalalignment == ESB_EXACT_FIT: + """ 1 September 2015 Fix fit align """ + widget.SetSize((rect.width-4, rect.height-4)) + widget.SetPosition((rect.x+2, rect.y+2)) + elif verticalalignment == ESB_ALIGN_CENTER_VERTICAL: + if widgetsize[1] < rect.width - 1: + diffs = (rect.height - widgetsize[1])/2 + widget.SetSize((rect.width-2, widgetsize[1])) + widget.SetPosition((rect.x-1, rect.y+diffs)) + else: + widget.SetSize((rect.width-2, widgetsize[1])) + widget.SetPosition((rect.x-1, rect.y-1)) + elif verticalalignment == ESB_ALIGN_TOP: + widget.SetSize((rect.width-2, widgetsize[1])) + widget.SetPosition((rect.x-1, rect.y)) + elif verticalalignment == ESB_ALIGN_BOTTOM: + widget.SetSize((rect.width-2, widgetsize[1])) + widget.SetPosition((rect.x-1, rect.height-widgetsize[1])) + + elif horizontalalignment == ESB_ALIGN_LEFT: + + xpos = rect.x - 1 + if verticalalignment == ESB_EXACT_FIT: + widget.SetSize((widgetsize[0], rect.height-2)) + widget.SetPosition((xpos, rect.y-1)) + elif verticalalignment == ESB_ALIGN_CENTER_VERTICAL: + if widgetsize[1] < rect.height - 1: + diffs = (rect.height - widgetsize[1])/2 + widget.SetPosition((xpos, rect.y+diffs)) + else: + widget.SetSize((widgetsize[0], rect.height-2)) + widget.SetPosition((xpos, rect.y-1)) + elif verticalalignment == ESB_ALIGN_TOP: + widget.SetPosition((xpos, rect.y)) + elif verticalalignment == ESB_ALIGN_BOTTOM: + widget.SetPosition((xpos, rect.height-widgetsize[1])) + + elif horizontalalignment == ESB_ALIGN_RIGHT: + + xpos = rect.x + rect.width - widgetsize[0] - 1 + if verticalalignment == ESB_EXACT_FIT: + widget.SetSize((widgetsize[0], rect.height-2)) + widget.SetPosition((xpos, rect.y-1)) + elif verticalalignment == ESB_ALIGN_CENTER_VERTICAL: + if widgetsize[1] < rect.height - 1: + diffs = (rect.height - widgetsize[1])/2 + widget.SetPosition((xpos, rect.y+diffs)) + else: + widget.SetSize((widgetsize[0], rect.height-2)) + widget.SetPosition((xpos, rect.y-1)) + elif verticalalignment == ESB_ALIGN_TOP: + widget.SetPosition((xpos, rect.y)) + elif verticalalignment == ESB_ALIGN_BOTTOM: + widget.SetPosition((xpos, rect.height-widgetsize[1])) + + elif horizontalalignment == ESB_ALIGN_CENTER_HORIZONTAL: + + xpos = rect.x + (rect.width - widgetsize[0])/2 - 1 + if verticalalignment == ESB_EXACT_FIT: + widget.SetSize((widgetsize[0], rect.height)) + widget.SetPosition((xpos, rect.y)) + elif verticalalignment == ESB_ALIGN_CENTER_VERTICAL: + if widgetsize[1] < rect.height - 1: + diffs = (rect.height - widgetsize[1])/2 + widget.SetPosition((xpos, rect.y+diffs)) + else: + widget.SetSize((widgetsize[0], rect.height-1)) + widget.SetPosition((xpos, rect.y+1)) + elif verticalalignment == ESB_ALIGN_TOP: + widget.SetPosition((xpos, rect.y)) + elif verticalalignment == ESB_ALIGN_BOTTOM: + widget.SetPosition((xpos, rect.height-widgetsize[1])) + + if event is not None: + event.Skip() + + def AddWidget(self, widget, horizontalalignment=ESB_ALIGN_CENTER_HORIZONTAL, + verticalalignment=ESB_ALIGN_CENTER_VERTICAL, pos=-1): + """Add A Widget To The EnhancedStatusBar. + + Parameters: + + - horizontalalignment: This Can Be One Of: + a) ESB_EXACT_FIT: The Widget Will Fit Horizontally The StatusBar Field Width; + b) ESB_ALIGN_CENTER_HORIZONTAL: The Widget Will Be Centered Horizontally In + The StatusBar Field; + c) ESB_ALIGN_LEFT: The Widget Will Be Left Aligned In The StatusBar Field; + d) ESB_ALIGN_RIGHT: The Widget Will Be Right Aligned In The StatusBar Field; + + - verticalalignment: + a) ESB_EXACT_FIT: The Widget Will Fit Vertically The StatusBar Field Height; + b) ESB_ALIGN_CENTER_VERTICAL: The Widget Will Be Centered Vertically In + The StatusBar Field; + c) ESB_ALIGN_BOTTOM: The Widget Will Be Bottom Aligned In The StatusBar Field; + d) ESB_ALIGN_TOP: The Widget Will Be TOP Aligned In The StatusBar Field; + + """ + + if pos == -1: + pos = self._curPos + self._curPos += 1 + + if self.GetFieldsCount() <= pos: + raise "\nERROR: EnhancedStatusBar has a max of %d items, you tried to set item #%d" % (self.GetFieldsCount(), pos) + + if horizontalalignment not in [ESB_ALIGN_CENTER_HORIZONTAL, ESB_EXACT_FIT, + ESB_ALIGN_LEFT, ESB_ALIGN_RIGHT]: + raise '\nERROR: Parameter "horizontalalignment" Should Be One Of '\ + '"ESB_ALIGN_CENTER_HORIZONTAL", "ESB_ALIGN_LEFT", "ESB_ALIGN_RIGHT"' \ + '"ESB_EXACT_FIT"' + + if verticalalignment not in [ESB_ALIGN_CENTER_VERTICAL, ESB_EXACT_FIT, + ESB_ALIGN_TOP, ESB_ALIGN_BOTTOM]: + raise '\nERROR: Parameter "verticalalignment" Should Be One Of '\ + '"ESB_ALIGN_CENTER_VERTICAL", "ESB_ALIGN_TOP", "ESB_ALIGN_BOTTOM"' \ + '"ESB_EXACT_FIT"' + + try: + self.RemoveChild(self._items[pos].widget) + self._items[pos].widget.Destroy() + except KeyError: + pass + + self._items[pos] = EnhancedStatusBarItem(widget, pos, horizontalalignment, verticalalignment) + + wx.CallAfter(self.OnSize, None) diff -r 31e63e25b4cc -r 64beb9e9c749 controls/FolderTree.py --- a/controls/FolderTree.py Mon Aug 21 20:17:19 2017 +0000 +++ b/controls/FolderTree.py Mon Aug 21 23:22:58 2017 +0300 @@ -30,6 +30,7 @@ DRIVE, FOLDER, FILE = range(3) + def sort_folder(x, y): if x[1] == y[1]: return cmp(x[0], y[0]) @@ -38,6 +39,7 @@ else: return 1 + def splitpath(path): head, tail = os.path.split(path) if head == "": @@ -46,20 +48,21 @@ return splitpath(head) return splitpath(head) + [tail] + class FolderTree(wx.Panel): - + def __init__(self, parent, folder, filter=None, editable=True): wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL) - + main_sizer = wx.BoxSizer(wx.VERTICAL) - - self.Tree = wx.TreeCtrl(self, - style=wx.TR_HAS_BUTTONS| - wx.TR_SINGLE| - wx.SUNKEN_BORDER| - wx.TR_HIDE_ROOT| - wx.TR_LINES_AT_ROOT| - wx.TR_EDIT_LABELS) + + self.Tree = wx.TreeCtrl(self, + style=(wx.TR_HAS_BUTTONS | + wx.TR_SINGLE | + wx.SUNKEN_BORDER | + wx.TR_HIDE_ROOT | + wx.TR_LINES_AT_ROOT | + wx.TR_EDIT_LABELS)) if wx.Platform == '__WXMSW__': self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnTreeItemExpanded, self.Tree) self.Tree.Bind(wx.EVT_LEFT_DOWN, self.OnTreeLeftDown) @@ -69,19 +72,19 @@ self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.OnTreeBeginLabelEdit, self.Tree) self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnTreeEndLabelEdit, self.Tree) main_sizer.AddWindow(self.Tree, 1, flag=wx.GROW) - + if filter is not None: self.Filter = wx.ComboBox(self, style=wx.CB_READONLY) self.Bind(wx.EVT_COMBOBOX, self.OnFilterChanged, self.Filter) main_sizer.AddWindow(self.Filter, flag=wx.GROW) else: self.Filter = None - + self.SetSizer(main_sizer) - + self.Folder = folder self.Editable = editable - + self.TreeImageList = wx.ImageList(16, 16) self.TreeImageDict = {} for item_type, bitmap in [(DRIVE, "tree_drive"), @@ -89,7 +92,7 @@ (FILE, "tree_file")]: self.TreeImageDict[item_type] = self.TreeImageList.Add(GetBitmap(bitmap)) self.Tree.SetImageList(self.TreeImageList) - + self.Filters = {} if self.Filter is not None: filter_parts = filter.split("|") @@ -101,11 +104,11 @@ self.Filter.Append(filter_parts[idx]) if idx == 0: self.Filter.SetStringSelection(filter_parts[idx]) - + self.CurrentFilter = self.Filters[self.Filter.GetStringSelection()] else: self.CurrentFilter = "" - + def _GetFolderChildren(self, folderpath, recursive=True): items = [] if wx.Platform == '__WXMSW__' and folderpath == "/": @@ -116,7 +119,7 @@ else: try: files = os.listdir(folderpath) - except: + except Exception: return [] for filename in files: if not filename.startswith("."): @@ -127,25 +130,25 @@ else: children = 0 items.append((filename, FOLDER, children)) - elif (self.CurrentFilter == "" or + elif (self.CurrentFilter == "" or os.path.splitext(filename)[1] == self.CurrentFilter): items.append((filename, FILE, None)) if recursive: items.sort(sort_folder) return items - + def SetFilter(self, filter): self.CurrentFilter = filter - + def GetTreeCtrl(self): return self.Tree - + def RefreshTree(self): root = self.Tree.GetRootItem() if not root.IsOk(): root = self.Tree.AddRoot("") self.GenerateTreeBranch(root, self.Folder) - + def GenerateTreeBranch(self, root, folderpath): item, item_cookie = self.Tree.GetFirstChild(root) for idx, (filename, item_type, children) in enumerate(self._GetFolderChildren(folderpath)): @@ -172,18 +175,18 @@ def ExpandItem(self, item): self.GenerateTreeBranch(item, self.GetPath(item)) self.Tree.Expand(item) - + def OnTreeItemActivated(self, event): self.ExpandItem(event.GetItem()) event.Skip() - + def OnTreeLeftDown(self, event): item, flags = self.Tree.HitTest(event.GetPosition()) if flags & wx.TREE_HITTEST_ONITEMBUTTON and not self.Tree.IsExpanded(item): self.ExpandItem(item) else: event.Skip() - + def OnTreeItemExpanded(self, event): item = event.GetItem() self.GenerateTreeBranch(item, self.GetPath(item)) @@ -201,7 +204,7 @@ event.Skip() else: event.Veto() - + def OnTreeEndLabelEdit(self, event): new_name = event.GetLabel() if new_name != "": @@ -212,20 +215,20 @@ os.rename(old_filepath, new_filepath) event.Skip() else: - message = wx.MessageDialog(self, - _("File '%s' already exists!") % new_name, - _("Error"), wx.OK|wx.ICON_ERROR) + message = wx.MessageDialog(self, + _("File '%s' already exists!") % new_name, + _("Error"), wx.OK | wx.ICON_ERROR) message.ShowModal() message.Destroy() event.Veto() else: event.Skip() - + def OnFilterChanged(self, event): self.CurrentFilter = self.Filters[self.Filter.GetStringSelection()] self.RefreshTree() event.Skip() - + def _SelectItem(self, root, parts): if len(parts) == 0: self.Tree.SelectItem(root) @@ -233,22 +236,22 @@ item, item_cookie = self.Tree.GetFirstChild(root) while item.IsOk(): if self.Tree.GetItemText(item) == parts[0]: - if (self.Tree.ItemHasChildren(item) and - not self.Tree.IsExpanded(item)): + if self.Tree.ItemHasChildren(item) and \ + not self.Tree.IsExpanded(item): self.Tree.Expand(item) wx.CallAfter(self._SelectItem, item, parts[1:]) else: self._SelectItem(item, parts[1:]) return item, item_cookie = self.Tree.GetNextChild(root, item_cookie) - + def SetPath(self, path): if path.startswith(self.Folder): root = self.Tree.GetRootItem() if root.IsOk(): relative_path = path.replace(os.path.join(self.Folder, ""), "") self._SelectItem(root, splitpath(relative_path)) - + def GetPath(self, item=None): if item is None: item = self.Tree.GetSelection() diff -r 31e63e25b4cc -r 64beb9e9c749 controls/LibraryPanel.py --- a/controls/LibraryPanel.py Mon Aug 21 20:17:19 2017 +0000 +++ b/controls/LibraryPanel.py Mon Aug 21 23:22:58 2017 +0300 @@ -24,25 +24,27 @@ import wx -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Helpers -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + [CATEGORY, BLOCK] = range(2) -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Library Panel -#------------------------------------------------------------------------------- - -""" -Class that implements a panel displaying a tree containing an hierarchical list -of functions and function blocks available in project an a search control for -quickly find one functions or function blocks in this list and a text control -displaying informations about selected functions or function blocks -""" +# ------------------------------------------------------------------------------- + class LibraryPanel(wx.Panel): - + """ + Class that implements a panel displaying a tree containing an hierarchical list + of functions and function blocks available in project an a search control for + quickly find one functions or function blocks in this list and a text control + displaying informations about selected functions or function blocks + """ + def __init__(self, parent, enable_drag=False): """ Constructor @@ -51,19 +53,19 @@ be drag'n drop from LibraryPanel (default: False) """ wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL) - + # Define LibraryPanel main sizer main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(1) - + # Add SearchCtrl to main sizer self.SearchCtrl = wx.SearchCtrl(self) # Add a button with a magnifying glass, essentially to show that this # control is for searching in tree self.SearchCtrl.ShowSearchButton(True) self.Bind(wx.EVT_TEXT, self.OnSearchCtrlChanged, self.SearchCtrl) - self.Bind(wx.EVT_SEARCHCTRL_SEARCH_BTN, + self.Bind(wx.EVT_SEARCHCTRL_SEARCH_BTN, self.OnSearchButtonClick, self.SearchCtrl) # Bind keyboard event on SearchCtrl text control to catch UP and DOWN # for search previous and next occurrence @@ -74,56 +76,56 @@ search_textctrl.Bind(wx.EVT_CHAR, self.OnKeyDown) main_sizer.AddWindow(self.SearchCtrl, flag=wx.GROW) - + # Add Splitter window for tree and block comment to main sizer splitter_window = wx.SplitterWindow(self) splitter_window.SetSashGravity(1.0) main_sizer.AddWindow(splitter_window, flag=wx.GROW) - + # Add TreeCtrl for functions and function blocks library in splitter # window self.Tree = wx.TreeCtrl(splitter_window, - size=wx.Size(0, 0), - style=wx.TR_HAS_BUTTONS| - wx.TR_SINGLE| - wx.SUNKEN_BORDER| - wx.TR_HIDE_ROOT| - wx.TR_LINES_AT_ROOT) + size=wx.Size(0, 0), + style=(wx.TR_HAS_BUTTONS | + wx.TR_SINGLE | + wx.SUNKEN_BORDER | + wx.TR_HIDE_ROOT | + wx.TR_LINES_AT_ROOT)) self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnTreeItemSelected, self.Tree) self.Tree.Bind(wx.EVT_CHAR, self.OnKeyDown) # If drag'n drop is enabled, bind event generated when a drag begins on # tree to start a drag'n drop if enable_drag: self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnTreeBeginDrag, self.Tree) - + # Add TextCtrl for function and function block informations - self.Comment = wx.TextCtrl(splitter_window, size=wx.Size(0, 80), - style=wx.TE_READONLY|wx.TE_MULTILINE) - + self.Comment = wx.TextCtrl(splitter_window, size=wx.Size(0, 80), + style=wx.TE_READONLY | wx.TE_MULTILINE) + splitter_window.SplitHorizontally(self.Tree, self.Comment, -80) - + self.SetSizer(main_sizer) - + # Reference to the project controller self.Controller = None - + # Variable storing functions and function blocks library to display self.BlockList = None - + def __del__(self): """ Destructor """ # Remove reference to project controller self.Controller = None - + def SetController(self, controller): """ Set reference to project controller @param controller: Reference to project controller """ self.Controller = controller - + def SetBlockList(self, blocklist): """ Set function and function block library to display in TreeCtrl @@ -133,15 +135,15 @@ self.BlockList = blocklist # Refresh TreeCtrl values self.RefreshTree() - + def SetFocus(self): """ Called to give focus to LibraryPanel - Override wx.Window SetFocus method + Override wx.Window SetFocus method """ # Give focus to SearchCtrl self.SearchCtrl.SetFocus() - + def ResetTree(self): """ Reset LibraryPanel values displayed in controls @@ -150,7 +152,7 @@ self.SearchCtrl.SetValue("") self.Tree.DeleteAllItems() self.Comment.SetValue("") - + def RefreshTree(self): """ Refresh LibraryPanel values displayed in controls @@ -160,35 +162,35 @@ if blocktypes is None and self.Controller is not None: # Get library from project controller if not defined blocktypes = self.Controller.GetBlockTypes() - + # Refresh TreeCtrl values if a library is defined if blocktypes is not None: # List that will contain tree items to be deleted when TreeCtrl # will be refreshed items_to_delete = [] - + # Get current selected item for selected it when values refreshed selected_item = self.Tree.GetSelection() selected_pydata = (self.Tree.GetPyData(selected_item) - if selected_item.IsOk() and - selected_item != self.Tree.GetRootItem() + if (selected_item.IsOk() and + selected_item != self.Tree.GetRootItem()) else None) # Don't save selected item if it is a category selected_infos = ((self.Tree.GetItemText(selected_item), selected_pydata["inputs"]) - if selected_pydata is not None and - selected_pydata["type"] == BLOCK - else (None, None)) - + if (selected_pydata is not None and + selected_pydata["type"] == BLOCK) + else (None, None)) + # Get TreeCtrl root item (hidden) root = self.Tree.GetRootItem() if not root.IsOk(): # Create root if not present root = self.Tree.AddRoot("") - + # Iterate over functions and function blocks library categories and # add a tree item to root item for each of them - + # Get first child under root item category_item, root_cookie = self.Tree.GetFirstChild(root) for category in blocktypes: @@ -196,11 +198,11 @@ # extracting translated strings for gettext to consider "name" # to be translated category_name = category["name"] - + # Tree item already exists, set item label if category_item.IsOk(): self.Tree.SetItemText(category_item, _(category_name)) - + # Tree item doesn't exist, add new one to root else: category_item = self.Tree.AppendItem(root, _(category_name)) @@ -209,24 +211,24 @@ if wx.Platform != '__WXMSW__': category_item, root_cookie = \ self.Tree.GetNextChild(root, root_cookie) - - # Set data associated to tree item (only save that item is a + + # Set data associated to tree item (only save that item is a # category) - self.Tree.SetPyData(category_item, {"type" : CATEGORY}) - + self.Tree.SetPyData(category_item, {"type": CATEGORY}) + # Iterate over functions and function blocks defined in library - # category add a tree item to category tree item for each of + # category add a tree item to category tree item for each of # them - + # Get first child under category tree item blocktype_item, category_cookie = \ self.Tree.GetFirstChild(category_item) for blocktype in category["list"]: - + # Tree item already exists, set item label if blocktype_item.IsOk(): self.Tree.SetItemText(blocktype_item, blocktype["name"]) - + # Tree item doesn't exist, add new one to category item else: blocktype_item = self.Tree.AppendItem( @@ -234,58 +236,58 @@ # See comment when adding category if wx.Platform != '__WXMSW__': blocktype_item, category_cookie = \ - self.Tree.GetNextChild(category_item, + self.Tree.GetNextChild(category_item, category_cookie) - + # Define data to associate to block tree item comment = blocktype["comment"] - block_data = {"type" : BLOCK, - "block_type" : blocktype["type"], - "inputs" : tuple([type - for name, type, modifier - in blocktype["inputs"]]), - "extension" : (len(blocktype["inputs"]) - if blocktype["extensible"] - else None), - "comment": _(comment) + - blocktype.get("usage", "")} + block_data = { + "type": BLOCK, + "block_type": blocktype["type"], + "inputs": tuple([type + for name, type, modifier + in blocktype["inputs"]]), + "extension": (len(blocktype["inputs"]) + if blocktype["extensible"] else None), + "comment": _(comment) + blocktype.get("usage", "") + } self.Tree.SetPyData(blocktype_item, block_data) - + # Select block tree item in tree if it corresponds to # previously selected one - if selected_infos == (blocktype["name"], + if selected_infos == (blocktype["name"], blocktype["inputs"]): self.Tree.SelectItem(blocktype_item) - + # Update TextCtrl value self.Comment.SetValue(block_data["comment"]) - + # Get next block tree item under category tree item blocktype_item, category_cookie = \ self.Tree.GetNextChild(category_item, category_cookie) - + # Add every remaining tree item under category tree item after # updating all block items to the list of items to delete while blocktype_item.IsOk(): items_to_delete.append(blocktype_item) blocktype_item, category_cookie = \ self.Tree.GetNextChild(category_item, category_cookie) - + # Get next category tree item under root item category_item, root_cookie = \ self.Tree.GetNextChild(root, root_cookie) - - # Add every remaining tree item under root item after updating all + + # Add every remaining tree item under root item after updating all # category items to the list of items to delete while category_item.IsOk(): items_to_delete.append(category_item) category_item, root_cookie = \ self.Tree.GetNextChild(root, root_cookie) - + # Remove all items in list of items to delete from TreeCtrl for item in items_to_delete: self.Tree.Delete(item) - + def GetSelectedBlock(self): """ Get selected block informations @@ -295,20 +297,20 @@ # Get selected item associated data in tree selected_item = self.Tree.GetSelection() selected_pydata = (self.Tree.GetPyData(selected_item) - if selected_item.IsOk() and - selected_item != self.Tree.GetRootItem() + if (selected_item.IsOk() and + selected_item != self.Tree.GetRootItem()) else None) - + # Return value is None if selected tree item is root or a category - return ({"type": self.Tree.GetItemText(selected_item), + return ({"type": self.Tree.GetItemText(selected_item), "inputs": selected_pydata["inputs"]} - if selected_pydata is not None and - selected_pydata["type"] == BLOCK + if (selected_pydata is not None and + selected_pydata["type"] == BLOCK) else None) - + def SelectTreeItem(self, name, inputs): """ - Select Tree item corresponding to block informations given + Select Tree item corresponding to block informations given @param name: Block type name @param inputs: List of block inputs type [input_type,...] """ @@ -318,8 +320,8 @@ # Select tree item found self.Tree.SelectItem(item) self.Tree.EnsureVisible(item) - - def FindTreeItem(self, item, name, inputs = None): + + def FindTreeItem(self, item, name, inputs=None): """ Find Tree item corresponding to block informations given Function is recursive @@ -330,12 +332,12 @@ # Return immediately if item isn't valid if not item.IsOk(): return None - + # Get data associated to item to test item_pydata = self.Tree.GetPyData(item) if item_pydata is not None and item_pydata["type"] == BLOCK: # Only test item corresponding to block - + # Test if block inputs type are the same than those given type_inputs = item_pydata.get("inputs", None) type_extension = item_pydata.get("extension", None) @@ -343,7 +345,7 @@ same_inputs = reduce( lambda x, y: x and y, map( - lambda x: x[0]==x[1] or x[0]=='ANY' or x[1]=='ANY', + lambda x: x[0] == x[1] or x[0] == 'ANY' or x[1] == 'ANY', zip(type_inputs, (inputs[:type_extension] if type_extension is not None @@ -351,11 +353,11 @@ True) else: same_inputs = True - + # Return item if block data corresponds to informations given if self.Tree.GetItemText(item) == name and same_inputs: return item - + # Test item children if item doesn't correspond child, child_cookie = self.Tree.GetFirstChild(item) while child.IsOk(): @@ -363,9 +365,9 @@ if result: return result child, child_cookie = self.Tree.GetNextChild(item, child_cookie) - + return None - + def SearchInTree(self, value, mode="first"): """ Search in Tree and select item that name contains string given @@ -378,13 +380,13 @@ root = self.Tree.GetRootItem() if not root.IsOk(): return False - + # Set function to navigate in Tree item sibling according to search - # mode defined + # mode defined sibling_function = (self.Tree.GetPrevSibling if mode == "previous" else self.Tree.GetNextSibling) - + # Get current selected item (for next and previous mode) item = self.Tree.GetSelection() if not item.IsOk() or mode == "first": @@ -392,29 +394,29 @@ selected = None else: selected = item - + # Navigate through tree items until one matching found or reach tree # starting or ending while item.IsOk(): - + # Get item data to get item type item_pydata = self.Tree.GetPyData(item) - + # Item is a block category if (item == root) or item_pydata["type"] == CATEGORY: - - # Get category first or last child according to search mode + + # Get category first or last child according to search mode # defined child = (self.Tree.GetLastChild(item) if mode == "previous" else self.Tree.GetFirstChild(item)[0]) - + # If category has no child, go to sibling category item = (child if child.IsOk() else sibling_function(item)) - + # Item is a block else: - + # Extract item block name name = self.Tree.GetItemText(item) # Test if block name contains string given @@ -428,17 +430,17 @@ self.Tree.SelectItem(item) self.Tree.EnsureVisible(item) return True - + # Go to next item sibling if block not found next = sibling_function(item) - + # If category has no other child, go to next category sibling item = (next if next.IsOk() else sibling_function(self.Tree.GetItemParent(item))) - + return False - + def OnSearchCtrlChanged(self, event): """ Called when SearchCtrl text control value changed @@ -447,7 +449,7 @@ # Search for block containing SearchCtrl value in 'first' mode self.SearchInTree(self.SearchCtrl.GetValue()) event.Skip() - + def OnSearchButtonClick(self, event): """ Called when SearchCtrl search button was clicked @@ -456,7 +458,7 @@ # Search for block containing SearchCtrl value in 'next' mode self.SearchInTree(self.SearchCtrl.GetValue(), "next") event.Skip() - + def OnTreeItemSelected(self, event): """ Called when tree item is selected @@ -468,13 +470,13 @@ item_pydata["comment"] if item_pydata is not None and item_pydata["type"] == BLOCK else "") - + # Call extra function defined when tree item is selected if getattr(self, "_OnTreeItemSelected", None) is not None: self._OnTreeItemSelected(event) - + event.Skip() - + def OnTreeBeginDrag(self, event): """ Called when a drag is started in tree @@ -482,19 +484,19 @@ """ selected_item = event.GetItem() item_pydata = self.Tree.GetPyData(selected_item) - + # Item dragged is a block if item_pydata is not None and item_pydata["type"] == BLOCK: # Start a drag'n drop data = wx.TextDataObject(str( - (self.Tree.GetItemText(selected_item), - item_pydata["block_type"], - "", + (self.Tree.GetItemText(selected_item), + item_pydata["block_type"], + "", item_pydata["inputs"]))) dragSource = wx.DropSource(self.Tree) dragSource.SetData(data) dragSource.DoDragDrop() - + def OnKeyDown(self, event): """ Called when key is pressed in SearchCtrl text control @@ -503,17 +505,17 @@ # Get event keycode and value in SearchCtrl keycode = event.GetKeyCode() search_value = self.SearchCtrl.GetValue() - + # Up key was pressed and SearchCtrl isn't empty, search for block in - # 'previous' mode + # 'previous' mode if keycode == wx.WXK_UP and search_value != "": self.SearchInTree(search_value, "previous") - + # Down key was pressed and SearchCtrl isn't empty, search for block in - # 'next' mode + # 'next' mode elif keycode == wx.WXK_DOWN and search_value != "": self.SearchInTree(search_value, "next") - + # Handle key normally else: event.Skip() diff -r 31e63e25b4cc -r 64beb9e9c749 controls/LocationCellEditor.py --- a/controls/LocationCellEditor.py Mon Aug 21 20:17:19 2017 +0000 +++ b/controls/LocationCellEditor.py Mon Aug 21 23:22:58 2017 +0300 @@ -26,32 +26,33 @@ from dialogs.BrowseLocationsDialog import BrowseLocationsDialog + class LocationCellControl(wx.PyControl): - + ''' Custom cell editor control with a text box and a button that launches the BrowseLocationsDialog. ''' def __init__(self, parent): wx.Control.__init__(self, parent) - + main_sizer = wx.FlexGridSizer(cols=2, hgap=0, rows=1, vgap=0) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(0) - + # create location text control - self.Location = wx.TextCtrl(self, size=wx.Size(0, -1), - style=wx.TE_PROCESS_ENTER) + self.Location = wx.TextCtrl(self, size=wx.Size(0, -1), + style=wx.TE_PROCESS_ENTER) self.Location.Bind(wx.EVT_KEY_DOWN, self.OnLocationChar) main_sizer.AddWindow(self.Location, flag=wx.GROW) - + # create browse button self.BrowseButton = wx.Button(self, label='...', size=wx.Size(30, -1)) self.BrowseButton.Bind(wx.EVT_BUTTON, self.OnBrowseButtonClick) main_sizer.AddWindow(self.BrowseButton, flag=wx.GROW) - + self.Bind(wx.EVT_SIZE, self.OnSize) - + self.SetSizer(main_sizer) self.Controller = None @@ -73,7 +74,7 @@ def SetValue(self, value): self.Default = value self.Location.SetValue(value) - + def GetValue(self): return self.Location.GetValue() @@ -88,15 +89,17 @@ else: infos = None dialog.Destroy() - + if infos is not None: location = infos["location"] # set the location if not infos["location"].startswith("%"): - dialog = wx.SingleChoiceDialog(self, - _("Select a variable class:"), _("Variable class"), - [_("Input"), _("Output"), _("Memory")], - wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL) + dialog = wx.SingleChoiceDialog( + self, + _("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() else: @@ -111,7 +114,7 @@ location = "%Q" + location else: location = "%M" + location - + self.Location.SetValue(location) self.VarType = infos["IEC_type"] @@ -129,24 +132,25 @@ def SetInsertionPoint(self, i): self.Location.SetInsertionPoint(i) - + def SetFocus(self): self.Location.SetFocus() + class LocationCellEditor(wx.grid.PyGridCellEditor): ''' Grid cell editor that uses LocationCellControl to display a browse button. ''' def __init__(self, table, controller): wx.grid.PyGridCellEditor.__init__(self) - + self.Table = table self.Controller = controller def __del__(self): self.CellControl = None self.Controller = None - + def Create(self, parent, id, evt_handler): self.CellControl = LocationCellControl(parent) self.SetControl(self.CellControl) @@ -169,20 +173,19 @@ self.Table.SetValueByName(row, 'Type', self.CellControl.GetVarType()) self.CellControl.Disable() return changed - + if wx.VERSION >= (3, 0, 0): def EndEdit(self, row, col, grid, oldval): return self.EndEditInternal(row, col, grid, oldval) else: def EndEdit(self, row, col, grid): - old_loc = self.Table.GetValueByName(row, 'Location') + old_loc = self.Table.GetValueByName(row, 'Location') return self.EndEditInternal(row, col, grid, old_loc) - + def SetSize(self, rect): self.CellControl.SetDimensions(rect.x + 1, rect.y, - rect.width, rect.height, - wx.SIZE_ALLOW_MINUS_ONE) + rect.width, rect.height, + wx.SIZE_ALLOW_MINUS_ONE) def Clone(self): return LocationCellEditor(self.Table, self.Controller) - diff -r 31e63e25b4cc -r 64beb9e9c749 controls/LogViewer.py --- a/controls/LogViewer.py Mon Aug 21 20:17:19 2017 +0000 +++ b/controls/LogViewer.py Mon Aug 21 23:22:58 2017 +0300 @@ -37,6 +37,7 @@ THUMB_SIZE_RATIO = 1. / 8. + def ArrowPoints(direction, width, height, xoffset, yoffset): if direction == wx.TOP: return [wx.Point(xoffset + 1, yoffset + height - 2), @@ -47,6 +48,7 @@ wx.Point(xoffset + width / 2, yoffset - 2), wx.Point(xoffset + width - 1, yoffset - height + 1)] + class LogScrollBar(wx.Panel): def __init__(self, parent, size): @@ -58,7 +60,7 @@ self.Bind(wx.EVT_PAINT, self.OnPaint) self.Bind(wx.EVT_SIZE, self.OnResize) - self.ThumbPosition = 0. # -1 <= ThumbPosition <= 1 + self.ThumbPosition = 0. # -1 <= ThumbPosition <= 1 self.ThumbScrollingStartPos = None def GetRangeRect(self): @@ -177,8 +179,10 @@ dc.EndDrawing() event.Skip() + BUTTON_SIZE = (70, 15) + class LogButton(): def __init__(self, label, callback): @@ -217,12 +221,14 @@ w, h = dc.GetTextExtent(self.Label) dc.DrawText(self.Label, - self.Position.x + (self.Size.width - w) / 2, - self.Position.y + (self.Size.height - h) / 2) + self.Position.x + (self.Size.width - w) / 2, + self.Position.y + (self.Size.height - h) / 2) + DATE_INFO_SIZE = 10 MESSAGE_INFO_SIZE = 18 + class LogMessage: def __init__(self, tv_sec, tv_nsec, level, level_bitmap, msg): @@ -271,6 +277,7 @@ return DATE_INFO_SIZE + MESSAGE_INFO_SIZE return MESSAGE_INFO_SIZE + SECOND = 1 MINUTE = 60 * SECOND HOUR = 60 * MINUTE @@ -281,10 +288,11 @@ (_("1m"), MINUTE), (_("1s"), SECOND)] + class LogViewer(DebugViewer, wx.Panel): def __init__(self, parent, window): - wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER) + wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER) DebugViewer.__init__(self, None, False, False) main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=5) @@ -292,7 +300,7 @@ main_sizer.AddGrowableRow(1) filter_sizer = wx.BoxSizer(wx.HORIZONTAL) - main_sizer.AddSizer(filter_sizer, border=5, flag=wx.TOP|wx.LEFT|wx.RIGHT|wx.GROW) + main_sizer.AddSizer(filter_sizer, border=5, flag=wx.TOP | wx.LEFT | wx.RIGHT | wx.GROW) self.MessageFilter = wx.ComboBox(self, style=wx.CB_READONLY) self.MessageFilter.Append(_("All")) @@ -301,20 +309,20 @@ for level in levels: self.MessageFilter.Append(_(level)) self.Bind(wx.EVT_COMBOBOX, self.OnMessageFilterChanged, self.MessageFilter) - filter_sizer.AddWindow(self.MessageFilter, 1, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) + filter_sizer.AddWindow(self.MessageFilter, 1, border=5, flag=wx.RIGHT | wx.ALIGN_CENTER_VERTICAL) self.SearchMessage = wx.SearchCtrl(self, style=wx.TE_PROCESS_ENTER) self.SearchMessage.ShowSearchButton(True) self.SearchMessage.ShowCancelButton(True) self.Bind(wx.EVT_TEXT_ENTER, self.OnSearchMessageChanged, self.SearchMessage) self.Bind(wx.EVT_SEARCHCTRL_SEARCH_BTN, - self.OnSearchMessageSearchButtonClick, self.SearchMessage) + self.OnSearchMessageSearchButtonClick, self.SearchMessage) self.Bind(wx.EVT_SEARCHCTRL_CANCEL_BTN, - self.OnSearchMessageCancelButtonClick, self.SearchMessage) - filter_sizer.AddWindow(self.SearchMessage, 3, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) + self.OnSearchMessageCancelButtonClick, self.SearchMessage) + filter_sizer.AddWindow(self.SearchMessage, 3, border=5, flag=wx.RIGHT | wx.ALIGN_CENTER_VERTICAL) self.CleanButton = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap("Clean"), - size=wx.Size(28, 28), style=wx.NO_BORDER) + size=wx.Size(28, 28), style=wx.NO_BORDER) self.CleanButton.SetToolTipString(_("Clean log messages")) self.Bind(wx.EVT_BUTTON, self.OnCleanButton, self.CleanButton) filter_sizer.AddWindow(self.CleanButton) @@ -322,7 +330,7 @@ message_panel_sizer = wx.FlexGridSizer(cols=2, hgap=0, rows=1, vgap=0) message_panel_sizer.AddGrowableCol(0) message_panel_sizer.AddGrowableRow(0) - main_sizer.AddSizer(message_panel_sizer, border=5, flag=wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.GROW) + main_sizer.AddSizer(message_panel_sizer, border=5, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.GROW) self.MessagePanel = wx.Panel(self) if wx.Platform == '__WXMSW__': @@ -403,7 +411,7 @@ def ResetLogCounters(self): self.previous_log_count = [None]*LogLevelsCount - + def SetLogCounters(self, log_count): new_messages = [] for level, count, prev in zip(xrange(LogLevelsCount), log_count, self.previous_log_count): @@ -413,7 +421,7 @@ oldest_message = (-1, None) else: dump_end = prev - 1 - for msgidx in xrange(count-1, dump_end,-1): + for msgidx in xrange(count-1, dump_end, -1): new_message = self.GetLogMessageFromSource(msgidx, level) if new_message is None: if prev is None: @@ -553,13 +561,13 @@ self.MessageScrollBar.RefreshThumbPosition() def IsPLCLogEmpty(self): - empty=True + empty = True for level, prev in zip(xrange(LogLevelsCount), self.previous_log_count): if prev is not None: - empty=False + empty = False break return empty - + def IsMessagePanelTop(self, message_idx=None): if message_idx is None: message_idx = self.CurrentMessage diff -r 31e63e25b4cc -r 64beb9e9c749 controls/PouInstanceVariablesPanel.py --- a/controls/PouInstanceVariablesPanel.py Mon Aug 21 20:17:19 2017 +0000 +++ b/controls/PouInstanceVariablesPanel.py Mon Aug 21 23:22:58 2017 +0300 @@ -28,15 +28,32 @@ import wx.lib.agw.customtreectrl as CT import wx.lib.buttons +from PLCControler import \ + ITEMS_VARIABLE, \ + ITEM_CONFIGURATION, \ + ITEM_RESOURCE, \ + ITEM_POU, \ + ITEM_TRANSITION, \ + ITEM_ACTION + +from util.BitmapLibrary import GetBitmap + + # Customize CustomTreeItem for adding icon on item right CT.GenericTreeItem._rightimages = [] + def SetRightImages(self, images): self._rightimages = images + + CT.GenericTreeItem.SetRightImages = SetRightImages + def GetRightImages(self): return self._rightimages + + CT.GenericTreeItem.GetRightImages = GetRightImages @@ -59,14 +76,14 @@ w, h = self.GetClientSize() total_h = self.GetLineHeight(item) r_image_w, r_image_h = self._imageListRight.GetSize(rightimages[0]) - + bbox_width = (r_image_w + 4) * len(rightimages) + 4 bbox_height = r_image_h + 8 bbox_x = w - bbox_width bbox_y = item.GetY() + ((total_h > r_image_h) and [(total_h-r_image_h)/2] or [0])[0] - + return wx.Rect(bbox_x, bbox_y, bbox_width, bbox_height) - + return None def IsOverItemRightImage(self, item, point): @@ -75,30 +92,30 @@ point = self.CalcUnscrolledPosition(point) r_image_w, r_image_h = self._imageListRight.GetSize(rightimages[0]) images_bbx = self.GetItemRightImagesBBox(item) - + rect = wx.Rect(images_bbx.x + 4, images_bbx.y + 4, r_image_w, r_image_h) for r_image in rightimages: if rect.Inside(point): return r_image rect.x += r_image_w + 4 - + return None - + def PaintItem(self, item, dc, level, align): CT.CustomTreeCtrl.PaintItem(self, item, dc, level, align) - + rightimages = item.GetRightImages() if len(rightimages) > 0: images_bbx = self.GetItemRightImagesBBox(item) r_image_w, r_image_h = self._imageListRight.GetSize(rightimages[0]) - + dc.SetBrush(wx.TRANSPARENT_BRUSH) dc.SetPen(wx.TRANSPARENT_PEN) - + bg_width = (r_image_w + 4) * len(rightimages) + 4 bg_height = r_image_h + 8 - dc.DrawRectangle(images_bbx.x, images_bbx.y, + dc.DrawRectangle(images_bbx.x, images_bbx.y, images_bbx.width, images_bbx.height) x_pos = images_bbx.x + 4 for r_image in rightimages: @@ -106,98 +123,98 @@ r_image, dc, x_pos, images_bbx.y + 4, wx.IMAGELIST_DRAW_TRANSPARENT) x_pos += r_image_w + 4 - + + _ButtonCallbacks = namedtuple("ButtonCallbacks", ["leftdown", "dclick"]) -from PLCControler import ITEMS_VARIABLE, ITEM_CONFIGURATION, ITEM_RESOURCE, ITEM_POU, ITEM_TRANSITION, ITEM_ACTION -from util.BitmapLibrary import GetBitmap class PouInstanceVariablesPanel(wx.Panel): def __init__(self, parent, window, controller, debug): - wx.Panel.__init__(self, name='PouInstanceTreePanel', - parent=parent, pos=wx.Point(0, 0), - size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL) - - self.ParentButton = wx.lib.buttons.GenBitmapButton(self, - bitmap=GetBitmap("top"), size=wx.Size(28, 28), style=wx.NO_BORDER) + wx.Panel.__init__(self, name='PouInstanceTreePanel', + parent=parent, pos=wx.Point(0, 0), + size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL) + + self.ParentButton = wx.lib.buttons.GenBitmapButton( + self, bitmap=GetBitmap("top"), size=wx.Size(28, 28), style=wx.NO_BORDER) self.ParentButton.SetToolTipString(_("Parent instance")) - self.Bind(wx.EVT_BUTTON, self.OnParentButtonClick, - self.ParentButton) - + self.Bind(wx.EVT_BUTTON, self.OnParentButtonClick, + self.ParentButton) + self.InstanceChoice = wx.ComboBox(self, size=wx.Size(0, 0), style=wx.CB_READONLY) self.Bind(wx.EVT_COMBOBOX, self.OnInstanceChoiceChanged, - self.InstanceChoice) - - self.DebugButton = wx.lib.buttons.GenBitmapButton(self, - bitmap=GetBitmap("debug_instance"), size=wx.Size(28, 28), style=wx.NO_BORDER) + self.InstanceChoice) + + self.DebugButton = wx.lib.buttons.GenBitmapButton( + self, bitmap=GetBitmap("debug_instance"), size=wx.Size(28, 28), style=wx.NO_BORDER) self.DebugButton.SetToolTipString(_("Debug instance")) - self.Bind(wx.EVT_BUTTON, self.OnDebugButtonClick, - self.DebugButton) - - self.VariablesList = CustomTreeCtrlWithRightImage(self, - style=wx.SUNKEN_BORDER, - agwStyle=CT.TR_NO_BUTTONS| - CT.TR_SINGLE| - CT.TR_HAS_VARIABLE_ROW_HEIGHT| - CT.TR_HIDE_ROOT| - CT.TR_NO_LINES| - getattr(CT, "TR_ALIGN_WINDOWS_RIGHT", CT.TR_ALIGN_WINDOWS)) + self.Bind(wx.EVT_BUTTON, self.OnDebugButtonClick, + self.DebugButton) + + self.VariablesList = CustomTreeCtrlWithRightImage( + self, + style=wx.SUNKEN_BORDER, + agwStyle=(CT.TR_NO_BUTTONS | + CT.TR_SINGLE | + CT.TR_HAS_VARIABLE_ROW_HEIGHT | + CT.TR_HIDE_ROOT | + CT.TR_NO_LINES | + getattr(CT, "TR_ALIGN_WINDOWS_RIGHT", CT.TR_ALIGN_WINDOWS))) self.VariablesList.SetIndent(0) self.VariablesList.SetSpacing(5) - self.VariablesList.DoSelectItem = lambda *x,**y:True + self.VariablesList.DoSelectItem = lambda *x, **y: True self.VariablesList.Bind(CT.EVT_TREE_ITEM_ACTIVATED, - self.OnVariablesListItemActivated) + self.OnVariablesListItemActivated) self.VariablesList.Bind(wx.EVT_LEFT_DOWN, self.OnVariablesListLeftDown) self.VariablesList.Bind(wx.EVT_KEY_DOWN, self.OnVariablesListKeyDown) - + self.TreeRightImageList = wx.ImageList(24, 24) self.EditImage = self.TreeRightImageList.Add(GetBitmap("edit")) self.DebugInstanceImage = self.TreeRightImageList.Add(GetBitmap("debug_instance")) self.VariablesList.SetRightImageList(self.TreeRightImageList) - + self.ButtonCallBacks = { self.EditImage: _ButtonCallbacks( self.EditButtonCallback, None), self.DebugInstanceImage: _ButtonCallbacks( self.DebugButtonCallback, self.DebugButtonDClickCallback)} - + buttons_sizer = wx.FlexGridSizer(cols=3, hgap=0, rows=1, vgap=0) buttons_sizer.AddWindow(self.ParentButton) buttons_sizer.AddWindow(self.InstanceChoice, flag=wx.GROW) buttons_sizer.AddWindow(self.DebugButton) buttons_sizer.AddGrowableCol(1) buttons_sizer.AddGrowableRow(0) - + main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0) main_sizer.AddSizer(buttons_sizer, flag=wx.GROW) main_sizer.AddWindow(self.VariablesList, flag=wx.GROW) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(1) - + self.SetSizer(main_sizer) - + self.ParentWindow = window self.Controller = controller self.Debug = debug if not self.Debug: self.DebugButton.Hide() - + self.PouTagName = None self.PouInfos = None self.PouInstance = None - + def __del__(self): self.Controller = None - + def SetTreeImageList(self, tree_image_list): self.VariablesList.SetImageList(tree_image_list) - + def SetController(self, controller): self.Controller = controller - + self.RefreshView() - + def SetPouType(self, tagname, pou_instance=None): if self.Controller is not None: if tagname == "Project": @@ -206,7 +223,7 @@ tagname = self.Controller.ComputeConfigurationName(config_name) if pou_instance is not None: self.PouInstance = pou_instance - + if self.PouTagName != tagname: self.PouTagName = tagname self.RefreshView() @@ -214,20 +231,20 @@ self.RefreshInstanceChoice() else: self.RefreshView() - + def ResetView(self): self.Controller = None - + self.PouTagName = None self.PouInfos = None self.PouInstance = None - + self.RefreshView() - + def RefreshView(self): self.Freeze() self.VariablesList.DeleteAllItems() - + if self.Controller is not None and self.PouTagName is not None: if self.PouTagName.split('::')[0] in ['A', 'T']: self.PouInfos = self.Controller.GetPouVariables('P::%s' % self.PouTagName.split('::')[1], self.Debug) @@ -244,24 +261,24 @@ text = "%s (%s)" % (var_infos.name, var_infos.type) else: text = var_infos.name - + right_images = [] if var_infos.edit: right_images.append(self.EditImage) - + if var_infos.debug and self.Debug: right_images.append(self.DebugInstanceImage) - + item = self.VariablesList.AppendItem(root, text) item.SetRightImages(right_images) self.VariablesList.SetItemImage(item, self.ParentWindow.GetTreeImage(var_infos.var_class)) self.VariablesList.SetPyData(item, var_infos) - + self.RefreshInstanceChoice() self.RefreshButtons() - + self.Thaw() - + def RefreshInstanceChoice(self): self.InstanceChoice.Clear() self.InstanceChoice.SetValue("") @@ -279,12 +296,12 @@ else: self.PouInstance = None self.InstanceChoice.SetValue(_("Select an instance")) - + def RefreshButtons(self): enabled = self.InstanceChoice.GetSelection() != -1 self.ParentButton.Enable(enabled and self.PouInfos.var_class != ITEM_CONFIGURATION) self.DebugButton.Enable(enabled and self.PouInfos.debug and self.Debug) - + root = self.VariablesList.GetRootItem() if root is not None and root.IsOk(): item, item_cookie = self.VariablesList.GetFirstChild(root) @@ -295,12 +312,12 @@ if child.GetName() != "edit": child.Enable(enabled) item, item_cookie = self.VariablesList.GetNextChild(root, item_cookie) - + def EditButtonCallback(self, infos): var_class = infos.var_class if var_class == ITEM_RESOURCE: tagname = self.Controller.ComputeConfigurationResourceName( - self.InstanceChoice.GetStringSelection(), + self.InstanceChoice.GetStringSelection(), infos.name) elif var_class == ITEM_TRANSITION: tagname = self.Controller.ComputePouTransitionName( @@ -314,7 +331,7 @@ var_class = ITEM_POU tagname = self.Controller.ComputePouName(infos.type) self.ParentWindow.EditProjectElement(var_class, tagname) - + def DebugButtonCallback(self, infos): if self.InstanceChoice.GetSelection() != -1: var_class = infos.var_class @@ -344,16 +361,16 @@ var_class, var_path, self.Controller.ComputePouName(infos.type)) - + def DebugButtonDClickCallback(self, infos): if self.InstanceChoice.GetSelection() != -1: if infos.var_class in ITEMS_VARIABLE: self.ParentWindow.AddDebugVariable( - "%s.%s" % (self.InstanceChoice.GetStringSelection(), - infos.name), + "%s.%s" % (self.InstanceChoice.GetStringSelection(), + infos.name), force=True, graph=True) - + def ShowInstanceChoicePopup(self): self.InstanceChoice.SetFocusFromKbd() size = self.InstanceChoice.GetSize() @@ -361,10 +378,10 @@ event.x = size.width / 2 event.y = size.height / 2 event.SetEventObject(self.InstanceChoice) - #event = wx.KeyEvent(wx.EVT_KEY_DOWN._getEvtType()) - #event.m_keyCode = wx.WXK_SPACE + # event = wx.KeyEvent(wx.EVT_KEY_DOWN._getEvtType()) + # event.m_keyCode = wx.WXK_SPACE self.InstanceChoice.GetEventHandler().ProcessEvent(event) - + def OnParentButtonClick(self, event): if self.InstanceChoice.GetSelection() != -1: parent_path = self.InstanceChoice.GetStringSelection().rsplit(".", 1)[0] @@ -373,11 +390,11 @@ wx.CallAfter(self.SetPouType, tagname, parent_path) wx.CallAfter(self.ParentWindow.SelectProjectTreeItem, tagname) event.Skip() - + def OnInstanceChoiceChanged(self, event): self.RefreshButtons() event.Skip() - + def OnDebugButtonClick(self, event): if self.InstanceChoice.GetSelection() != -1: self.ParentWindow.OpenDebugViewer( @@ -385,26 +402,26 @@ self.InstanceChoice.GetStringSelection(), self.PouTagName) event.Skip() - + def OnVariablesListItemActivated(self, event): selected_item = event.GetItem() if selected_item is not None and selected_item.IsOk(): item_infos = self.VariablesList.GetPyData(selected_item) if item_infos is not None: - + item_button = self.VariablesList.IsOverItemRightImage( selected_item, event.GetPoint()) if item_button is not None: callback = self.ButtonCallBacks[item_button].dclick if callback is not None: callback(item_infos) - + elif item_infos.var_class not in ITEMS_VARIABLE: instance_path = self.InstanceChoice.GetStringSelection() if item_infos.var_class == ITEM_RESOURCE: if instance_path != "": tagname = self.Controller.ComputeConfigurationResourceName( - instance_path, + instance_path, item_infos.name) else: tagname = None @@ -424,7 +441,7 @@ self.SetPouType(tagname, item_path) self.ParentWindow.SelectProjectTreeItem(tagname) event.Skip() - + def OnVariablesListLeftDown(self, event): if self.InstanceChoice.GetSelection() == -1: wx.CallAfter(self.ShowInstanceChoicePopup) @@ -434,15 +451,15 @@ if item is not None: item_infos = self.VariablesList.GetPyData(item) if item_infos is not None: - + item_button = self.VariablesList.IsOverItemRightImage( item, event.GetPosition()) if item_button is not None: callback = self.ButtonCallBacks[item_button].leftdown if callback is not None: callback(item_infos) - - elif (flags & CT.TREE_HITTEST_ONITEMLABEL and + + elif (flags & CT.TREE_HITTEST_ONITEMLABEL and item_infos.var_class in ITEMS_VARIABLE): self.ParentWindow.EnsureTabVisible( self.ParentWindow.DebugVariablePanel) @@ -457,4 +474,3 @@ keycode = event.GetKeyCode() if keycode != wx.WXK_LEFT: event.Skip() - diff -r 31e63e25b4cc -r 64beb9e9c749 controls/ProjectPropertiesPanel.py --- a/controls/ProjectPropertiesPanel.py Mon Aug 21 20:17:19 2017 +0000 +++ b/controls/ProjectPropertiesPanel.py Mon Aug 21 23:22:58 2017 +0300 @@ -25,22 +25,25 @@ import wx -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Helpers -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- REQUIRED_PARAMS = ["projectName", "productName", "productVersion", "companyName"] -[TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE, - POUINSTANCEVARIABLESPANEL, LIBRARYTREE, SCALING, PAGETITLES +[ + TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE, + POUINSTANCEVARIABLESPANEL, LIBRARYTREE, SCALING, PAGETITLES ] = range(10) -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Project Properties Panel -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + class ProjectPropertiesPanel(wx.Notebook): - + def AddSizerParams(self, parent, sizer, params): for idx, (name, label) in enumerate(params): border = 0 @@ -48,18 +51,18 @@ border |= wx.TOP elif idx == len(params) - 1: border |= wx.BOTTOM - + st = wx.StaticText(parent, label=label) - sizer.AddWindow(st, border=10, - flag=wx.ALIGN_CENTER_VERTICAL|border|wx.LEFT) - + sizer.AddWindow(st, border=10, + flag=wx.ALIGN_CENTER_VERTICAL | border | wx.LEFT) + tc = wx.TextCtrl(parent, style=wx.TE_PROCESS_ENTER) setattr(self, name, tc) callback = self.GetTextCtrlChangedFunction(tc, name) self.Bind(wx.EVT_TEXT_ENTER, callback, tc) tc.Bind(wx.EVT_KILL_FOCUS, callback) - sizer.AddWindow(tc, border=10, - flag=wx.GROW|border|wx.RIGHT) + sizer.AddWindow(tc, border=10, + flag=wx.GROW | border | wx.RIGHT) def __init__(self, parent, controller=None, window=None, enable_required=True): wx.Notebook.__init__(self, parent) @@ -67,36 +70,36 @@ self.Controller = controller self.ParentWindow = window self.Values = None - + # Project Panel elements self.ProjectPanel = wx.Panel(self, style=wx.TAB_TRAVERSAL) projectpanel_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=5, vgap=15) projectpanel_sizer.AddGrowableCol(1) self.ProjectPanel.SetSizer(projectpanel_sizer) - + self.AddSizerParams(self.ProjectPanel, projectpanel_sizer, - [("projectName", _('Project Name (required):')), - ("projectVersion", _('Project Version (optional):')), - ("productName", _('Product Name (required):')), - ("productVersion", _('Product Version (required):')), - ("productRelease", _('Product Release (optional):'))]) - + [("projectName", _('Project Name (required):')), + ("projectVersion", _('Project Version (optional):')), + ("productName", _('Product Name (required):')), + ("productVersion", _('Product Version (required):')), + ("productRelease", _('Product Release (optional):'))]) + self.AddPage(self.ProjectPanel, _("Project")) - + # Author Panel elements self.AuthorPanel = wx.Panel(self, style=wx.TAB_TRAVERSAL) authorpanel_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=4, vgap=15) authorpanel_sizer.AddGrowableCol(1) self.AuthorPanel.SetSizer(authorpanel_sizer) - + self.AddSizerParams(self.AuthorPanel, authorpanel_sizer, - [("companyName", _('Company Name (required):')), - ("companyURL", _('Company URL (optional):')), - ("authorName", _('Author Name (optional):')), - ("organization", _('Organization (optional):'))]) - + [("companyName", _('Company Name (required):')), + ("companyURL", _('Company URL (optional):')), + ("authorName", _('Author Name (optional):')), + ("organization", _('Organization (optional):'))]) + self.AddPage(self.AuthorPanel, _("Author")) # Graphics Panel elements @@ -106,47 +109,48 @@ graphicpanel_sizer.AddGrowableCol(0) graphicpanel_sizer.AddGrowableRow(3) self.GraphicsPanel.SetSizer(graphicpanel_sizer) - + pageSize_st = wx.StaticText(self.GraphicsPanel, - label=_('Page Size (optional):')) - graphicpanel_sizer.AddWindow(pageSize_st, border=10, - flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP|wx.LEFT|wx.RIGHT) - + label=_('Page Size (optional):')) + graphicpanel_sizer.AddWindow( + pageSize_st, border=10, + flag=wx.ALIGN_CENTER_VERTICAL | wx.TOP | wx.LEFT | wx.RIGHT) + pageSize_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=5) pageSize_sizer.AddGrowableCol(1) - graphicpanel_sizer.AddSizer(pageSize_sizer, border=10, - flag=wx.GROW|wx.LEFT|wx.RIGHT) - + graphicpanel_sizer.AddSizer(pageSize_sizer, border=10, + flag=wx.GROW | wx.LEFT | wx.RIGHT) + for name, label in [('PageWidth', _('Width:')), ('PageHeight', _('Height:'))]: st = wx.StaticText(self.GraphicsPanel, label=label) - pageSize_sizer.AddWindow(st, border=12, - flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT) - - sp = wx.SpinCtrl(self.GraphicsPanel, - min=0, max=2**16, style=wx.TE_PROCESS_ENTER) + pageSize_sizer.AddWindow(st, border=12, + flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT) + + sp = wx.SpinCtrl(self.GraphicsPanel, + min=0, max=2**16, style=wx.TE_PROCESS_ENTER) setattr(self, name, sp) callback = self.GetPageSizeChangedFunction(sp, name) self.Bind(wx.EVT_TEXT_ENTER, callback, sp) sp.Bind(wx.EVT_KILL_FOCUS, callback) pageSize_sizer.AddWindow(sp, flag=wx.GROW) - + scaling_st = wx.StaticText(self.GraphicsPanel, - label=_('Grid Resolution:')) - graphicpanel_sizer.AddWindow(scaling_st, border=10, - flag=wx.GROW|wx.LEFT|wx.RIGHT) - + label=_('Grid Resolution:')) + graphicpanel_sizer.AddWindow(scaling_st, border=10, + flag=wx.GROW | wx.LEFT | wx.RIGHT) + scaling_nb = wx.Notebook(self.GraphicsPanel) - graphicpanel_sizer.AddWindow(scaling_nb, border=10, - flag=wx.GROW|wx.BOTTOM|wx.LEFT|wx.RIGHT) - + graphicpanel_sizer.AddWindow(scaling_nb, border=10, + flag=wx.GROW | wx.BOTTOM | wx.LEFT | wx.RIGHT) + self.Scalings = {} - for language, translation in [("FBD",_("FBD")), ("LD",_("LD")), ("SFC",_("SFC"))]: + for language, translation in [("FBD", _("FBD")), ("LD", _("LD")), ("SFC", _("SFC"))]: scaling_panel = wx.Panel(scaling_nb, style=wx.TAB_TRAVERSAL) scalingpanel_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=5) scalingpanel_sizer.AddGrowableCol(1) scaling_panel.SetSizer(scalingpanel_sizer) - + scaling_controls = [] for idx, (name, label) in enumerate([('XScale', _('Horizontal:')), ('YScale', _('Vertical:'))]): @@ -154,67 +158,69 @@ border = wx.TOP else: border = wx.BOTTOM - + st = wx.StaticText(scaling_panel, label=label) - scalingpanel_sizer.AddWindow(st, border=10, - flag=wx.ALIGN_CENTER_VERTICAL|border|wx.LEFT) - - sp = wx.SpinCtrl(scaling_panel, - min=0, max=2**16, style=wx.TE_PROCESS_ENTER) + scalingpanel_sizer.AddWindow( + st, border=10, + flag=wx.ALIGN_CENTER_VERTICAL | border | wx.LEFT) + + sp = wx.SpinCtrl(scaling_panel, + min=0, max=2**16, style=wx.TE_PROCESS_ENTER) scaling_controls.append(sp) callback = self.GetScalingChangedFunction(sp, language, name) self.Bind(wx.EVT_TEXT_ENTER, callback, sp) sp.Bind(wx.EVT_KILL_FOCUS, callback) - scalingpanel_sizer.AddWindow(sp, border=10, - flag=wx.GROW|border|wx.RIGHT) - + scalingpanel_sizer.AddWindow(sp, border=10, + flag=wx.GROW | border | wx.RIGHT) + self.Scalings[language] = scaling_controls scaling_nb.AddPage(scaling_panel, translation) - + self.AddPage(self.GraphicsPanel, _("Graphics")) # Miscellaneous Panel elements - self.MiscellaneousPanel = wx.Panel(id=-1, parent=self, - name='MiscellaneousPanel', pos=wx.Point(0, 0), - size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL) + self.MiscellaneousPanel = wx.Panel( + id=-1, parent=self, name='MiscellaneousPanel', pos=wx.Point(0, 0), + size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL) miscellaneouspanel_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=15) miscellaneouspanel_sizer.AddGrowableCol(1) miscellaneouspanel_sizer.AddGrowableRow(1) self.MiscellaneousPanel.SetSizer(miscellaneouspanel_sizer) - + language_label = wx.StaticText(self.MiscellaneousPanel, - label=_('Language (optional):')) - miscellaneouspanel_sizer.AddWindow(language_label, border=10, - flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP|wx.LEFT) - - self.Language = wx.ComboBox(self.MiscellaneousPanel, - style=wx.CB_READONLY) + label=_('Language (optional):')) + miscellaneouspanel_sizer.AddWindow(language_label, border=10, + flag=wx.ALIGN_CENTER_VERTICAL | wx.TOP | wx.LEFT) + + self.Language = wx.ComboBox(self.MiscellaneousPanel, + style=wx.CB_READONLY) self.Bind(wx.EVT_COMBOBOX, self.OnLanguageChanged, self.Language) - miscellaneouspanel_sizer.AddWindow(self.Language, border=10, - flag=wx.GROW|wx.TOP|wx.RIGHT) - - description_label = wx.StaticText(self.MiscellaneousPanel, - label=_('Content Description (optional):')) - miscellaneouspanel_sizer.AddWindow(description_label, border=10, - flag=wx.BOTTOM|wx.LEFT) - - self.ContentDescription = wx.TextCtrl(self.MiscellaneousPanel, - size=wx.Size(240,150), style=wx.TE_MULTILINE|wx.TE_PROCESS_ENTER) - self.Bind(wx.EVT_TEXT_ENTER, self.OnContentDescriptionChanged, - self.ContentDescription) - self.ContentDescription.Bind(wx.EVT_KILL_FOCUS, - self.OnContentDescriptionChanged) - miscellaneouspanel_sizer.AddWindow(self.ContentDescription, border=10, - flag=wx.GROW|wx.BOTTOM|wx.RIGHT) - + miscellaneouspanel_sizer.AddWindow(self.Language, border=10, + flag=wx.GROW | wx.TOP | wx.RIGHT) + + description_label = wx.StaticText( + self.MiscellaneousPanel, label=_('Content Description (optional):')) + miscellaneouspanel_sizer.AddWindow(description_label, border=10, + flag=wx.BOTTOM | wx.LEFT) + + self.ContentDescription = wx.TextCtrl( + self.MiscellaneousPanel, size=wx.Size(240, 150), + style=wx.TE_MULTILINE | wx.TE_PROCESS_ENTER) + self.Bind(wx.EVT_TEXT_ENTER, self.OnContentDescriptionChanged, + self.ContentDescription) + self.ContentDescription.Bind(wx.EVT_KILL_FOCUS, + self.OnContentDescriptionChanged) + miscellaneouspanel_sizer.AddWindow(self.ContentDescription, border=10, + flag=wx.GROW | wx.BOTTOM | wx.RIGHT) + self.AddPage(self.MiscellaneousPanel, _("Miscellaneous")) - + for param in REQUIRED_PARAMS: getattr(self, param).Enable(enable_required) - + languages = ["", "en-US", "fr-FR", "zh-CN", "ru-RU"] - + for language in languages: self.Language.Append(language) @@ -241,13 +247,13 @@ tc = getattr(self, item, None) if tc is not None: tc.SetValue(value) - + def GetValues(self): values = {} for param in ["projectName", "projectVersion", "productName", "productVersion", "productRelease", "companyName", - "companyURL", "authorName", + "companyURL", "authorName", "organization"]: value = getattr(self, param).GetValue() if param in REQUIRED_PARAMS or value != "": @@ -270,7 +276,7 @@ values["scaling"][language] = (self.Scalings[language][0].GetValue(), self.Scalings[language][1].GetValue()) return values - + def GetTextCtrlChangedFunction(self, textctrl, name): def TextCtrlChangedFunction(event): if self.Controller is not None and self.Values is not None: @@ -281,7 +287,7 @@ if old_value != new_value: self.Controller.SetProjectProperties(properties={name: new_value}) self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, - PROJECTTREE, PAGETITLES) + PROJECTTREE, PAGETITLES) wx.CallAfter(self.RefreshView) event.Skip() return TextCtrlChangedFunction @@ -300,11 +306,11 @@ if old_value != new_value: self.Controller.SetProjectProperties(properties={"pageSize": new_value}) self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, - PAGETITLES, SCALING) + PAGETITLES, SCALING) wx.CallAfter(self.RefreshView) event.Skip() return PageSizeChangedFunction - + def GetScalingChangedFunction(self, spinctrl, language, name): def ScalingChangedFunction(event): if self.Controller is not None: @@ -320,11 +326,11 @@ if old_value != new_value: self.Controller.SetProjectProperties(properties={"scaling": {language: new_value}}) self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, - PAGETITLES, SCALING) + PAGETITLES, SCALING) wx.CallAfter(self.RefreshView) event.Skip() return ScalingChangedFunction - + def OnLanguageChanged(self, event): if self.Controller is not None: if self.Values is not None: @@ -339,7 +345,7 @@ self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES) wx.CallAfter(self.RefreshView) event.Skip() - + def OnContentDescriptionChanged(self, event): if self.Controller is not None: if self.Values is not None: diff -r 31e63e25b4cc -r 64beb9e9c749 controls/SearchResultPanel.py --- a/controls/SearchResultPanel.py Mon Aug 21 20:17:19 2017 +0000 +++ b/controls/SearchResultPanel.py Mon Aug 21 23:22:58 2017 +0300 @@ -31,6 +31,7 @@ from PLCControler import * from util.BitmapLibrary import GetBitmap + def GenerateName(infos): if infos[0] in ["input", "output", "value"]: return "%s %d:" % (infos[0], infos[1]) @@ -40,18 +41,22 @@ return "element %d %s" % (infos[1], infos[2]) return "%s:" % infos[0] -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Search Result Panel -#------------------------------------------------------------------------------- - -[ID_SEARCHRESULTPANEL, ID_SEARCHRESULTPANELHEADERLABEL, - ID_SEARCHRESULTPANELSEARCHRESULTSTREE, ID_SEARCHRESULTPANELRESETBUTTON, +# ------------------------------------------------------------------------------- + + +[ + ID_SEARCHRESULTPANEL, ID_SEARCHRESULTPANELHEADERLABEL, + ID_SEARCHRESULTPANELSEARCHRESULTSTREE, ID_SEARCHRESULTPANELRESETBUTTON, ] = [wx.NewId() for _init_ctrls in range(4)] + class SearchResultPanel(wx.Panel): if wx.VERSION < (2, 6, 0): - def Bind(self, event, function, id = None): + def Bind(self, event, function, id=None): if id is not None: event(self, id, function) else: @@ -60,109 +65,110 @@ def _init_coll_MainSizer_Items(self, parent): parent.AddSizer(self.HeaderSizer, 0, border=0, flag=wx.GROW) parent.AddWindow(self.SearchResultsTree, 1, border=0, flag=wx.GROW) - + def _init_coll_MainSizer_Growables(self, parent): parent.AddGrowableCol(0) parent.AddGrowableRow(1) def _init_coll_HeaderSizer_Items(self, parent): - parent.AddWindow(self.HeaderLabel, 1, border=5, flag=wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) + parent.AddWindow(self.HeaderLabel, 1, border=5, flag=wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL) parent.AddWindow(self.ResetButton, 0, border=0, flag=0) - + def _init_coll_HeaderSizer_Growables(self, parent): parent.AddGrowableCol(0) - + def _init_sizers(self): self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0) self.HeaderSizer = wx.BoxSizer(wx.HORIZONTAL) - + self._init_coll_MainSizer_Items(self.MainSizer) self._init_coll_MainSizer_Growables(self.MainSizer) self._init_coll_HeaderSizer_Items(self.HeaderSizer) - + self.SetSizer(self.MainSizer) def _init_ctrls(self, prnt): wx.Panel.__init__(self, id=ID_SEARCHRESULTPANEL, - name='SearchResultPanel', parent=prnt, pos=wx.Point(0, 0), - size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL) + name='SearchResultPanel', parent=prnt, pos=wx.Point(0, 0), + size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL) self.HeaderLabel = wx.StaticText(id=ID_SEARCHRESULTPANELHEADERLABEL, - name='HeaderLabel', parent=self, - pos=wx.Point(0, 0), size=wx.Size(0, 17), style=0) - - search_results_tree_style = CT.TR_HAS_BUTTONS|CT.TR_NO_LINES|CT.TR_HAS_VARIABLE_ROW_HEIGHT + name='HeaderLabel', parent=self, + pos=wx.Point(0, 0), size=wx.Size(0, 17), style=0) + + search_results_tree_style = CT.TR_HAS_BUTTONS | CT.TR_NO_LINES | CT.TR_HAS_VARIABLE_ROW_HEIGHT self.SearchResultsTree = CT.CustomTreeCtrl(id=ID_SEARCHRESULTPANELSEARCHRESULTSTREE, - name="SearchResultsTree", parent=self, - pos=wx.Point(0, 0), style=search_results_tree_style) + name="SearchResultsTree", parent=self, + pos=wx.Point(0, 0), style=search_results_tree_style) if wx.VERSION >= (2, 8, 11): self.SearchResultsTree.SetAGWWindowStyleFlag(search_results_tree_style) self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnSearchResultsTreeItemActivated, - id=ID_SEARCHRESULTPANELSEARCHRESULTSTREE) - - self.ResetButton = wx.lib.buttons.GenBitmapButton(self, - bitmap=GetBitmap("reset"), size=wx.Size(28, 28), style=wx.NO_BORDER) + id=ID_SEARCHRESULTPANELSEARCHRESULTSTREE) + + self.ResetButton = wx.lib.buttons.GenBitmapButton( + self, bitmap=GetBitmap("reset"), + size=wx.Size(28, 28), style=wx.NO_BORDER) self.ResetButton.SetToolTipString(_("Reset search result")) self.Bind(wx.EVT_BUTTON, self.OnResetButton, self.ResetButton) - + self._init_sizers() def __init__(self, parent, window): self.ParentWindow = window - + self._init_ctrls(parent) - + # Define Tree item icon list self.TreeImageList = wx.ImageList(16, 16) self.TreeImageDict = {} - + # Icons for other items for imgname, itemtype in [ - #editables - ("PROJECT", ITEM_PROJECT), - ("TRANSITION", ITEM_TRANSITION), - ("ACTION", ITEM_ACTION), - ("CONFIGURATION", ITEM_CONFIGURATION), - ("RESOURCE", ITEM_RESOURCE), - ("DATATYPE", ITEM_DATATYPE), - ("ACTION", "action_block"), - ("IL", "IL"), - ("ST", "ST")]: + # editables + ("PROJECT", ITEM_PROJECT), + ("TRANSITION", ITEM_TRANSITION), + ("ACTION", ITEM_ACTION), + ("CONFIGURATION", ITEM_CONFIGURATION), + ("RESOURCE", ITEM_RESOURCE), + ("DATATYPE", ITEM_DATATYPE), + ("ACTION", "action_block"), + ("IL", "IL"), + ("ST", "ST")]: self.TreeImageDict[itemtype] = self.TreeImageList.Add(GetBitmap(imgname)) - + for itemtype in ["function", "functionBlock", "program", "comment", "block", "io_variable", "connector", "contact", "coil", - "step", "transition", "jump", - "var_local", "var_input", + "step", "transition", "jump", + "var_local", "var_input", "var_inout", "var_output"]: self.TreeImageDict[itemtype] = self.TreeImageList.Add(GetBitmap(itemtype.upper())) - + # Assign icon list to TreeCtrl self.SearchResultsTree.SetImageList(self.TreeImageList) - + self.ResetSearchResults() def SetSearchResults(self, criteria, search_results): self.Criteria = criteria self.SearchResults = {} self.ElementsOrder = [] - + for infos, start, end, text in search_results: if infos[0] not in self.ElementsOrder: self.ElementsOrder.append(infos[0]) - + results = self.SearchResults.setdefault(infos[0], []) results.append((infos, start, end, text)) - + self.RefreshView() - + def ResetSearchResults(self): self.Criteria = None self.ElementsOrder = [] self.SearchResults = {} self.RefreshView() - + def RefreshView(self): self.SearchResultsTree.DeleteAllItems() if self.Criteria is None: @@ -170,29 +176,30 @@ self.ResetButton.Enable(False) else: matches_number = 0 - search_results_tree_infos = {"name": _("Project '%s':") % self.ParentWindow.Controler.GetProjectName(), - "type": ITEM_PROJECT, - "data": None, - "text": None, - "matches": None, - } + search_results_tree_infos = { + "name": _("Project '%s':") % self.ParentWindow.Controler.GetProjectName(), + "type": ITEM_PROJECT, + "data": None, + "text": None, + "matches": None, + } search_results_tree_children = search_results_tree_infos.setdefault("children", []) for tagname in self.ElementsOrder: results = self.SearchResults.get(tagname, []) matches_number += len(results) - + words = tagname.split("::") - + element_type = self.ParentWindow.Controler.GetElementType(tagname) if element_type == ITEM_POU: element_type = self.ParentWindow.Controler.GetPouType(words[1]) - + element_infos = {"name": words[-1], "type": element_type, "data": tagname, "text": None, "matches": len(results)} - + children = element_infos.setdefault("children", []) for infos, start, end, text in results: if infos[1] == "name" or element_type == ITEM_DATATYPE: @@ -215,15 +222,16 @@ child_type = self.ParentWindow.Controler.GetPouBodyType(words[1]) else: child_name = GenerateName(infos[3:]) - child_infos = {"name": child_name, - "type": child_type, - "data": (infos, start, end ,None), - "text": text, - "matches": 1, - "children": [], - } + child_infos = { + "name": child_name, + "type": child_type, + "data": (infos, start, end, None), + "text": text, + "matches": 1, + "children": [], + } children.append(child_infos) - + if len(words) > 2: for _element_infos in search_results_tree_children: if _element_infos["name"] == words[1]: @@ -234,41 +242,41 @@ search_results_tree_children.append(element_infos) else: search_results_tree_children.append(element_infos) - + if matches_number < 2: header_format = _("'{a1}' - {a2} match in project") else: header_format = _("'{a1}' - {a2} matches in project") - - self.HeaderLabel.SetLabel(header_format.format(a1 = self.Criteria["find_pattern"], a2 = matches_number)) + + self.HeaderLabel.SetLabel(header_format.format(a1=self.Criteria["find_pattern"], a2=matches_number)) self.ResetButton.Enable(True) - + if matches_number > 0: root = self.SearchResultsTree.GetRootItem() if root is None: root = self.SearchResultsTree.AddRoot(search_results_tree_infos["name"]) self.GenerateSearchResultsTreeBranch(root, search_results_tree_infos) self.SearchResultsTree.Expand(root) - + def GetTextCtrlClickFunction(self, item): def OnTextCtrlClick(event): self.SearchResultsTree.SelectItem(item) event.Skip() return OnTextCtrlClick - + def GetTextCtrlDClickFunction(self, item): def OnTextCtrlDClick(event): self.ShowSearchResults(item) event.Skip() return OnTextCtrlDClick - + def GenerateSearchResultsTreeBranch(self, root, infos): to_delete = [] if infos["name"] == "body": item_name = "%d:" % infos["data"][1][0] else: item_name = infos["name"] - + self.SearchResultsTree.SetItemText(root, item_name) self.SearchResultsTree.SetPyData(root, infos["data"]) self.SearchResultsTree.SetItemBackgroundColour(root, wx.WHITE) @@ -278,7 +286,7 @@ self.SearchResultsTree.SetItemImage(root, self.TreeImageDict[self.ParentWindow.Controler.GetPouType(infos["name"])]) else: self.SearchResultsTree.SetItemImage(root, self.TreeImageDict[infos["type"]]) - + text = None if infos["text"] is not None: text = infos["text"] @@ -291,13 +299,13 @@ text = _("(%d matches)") % infos["matches"] start_idx, end_idx = 0, len(text) style = wx.TextAttr(wx.Colour(0, 127, 174)) - + if text is not None: - text_ctrl_style = wx.BORDER_NONE|wx.TE_READONLY|wx.TE_RICH2 + text_ctrl_style = wx.BORDER_NONE | wx.TE_READONLY | wx.TE_RICH2 if wx.Platform != '__WXMSW__' or len(text.splitlines()) > 1: text_ctrl_style |= wx.TE_MULTILINE - text_ctrl = wx.TextCtrl(id=-1, parent=self.SearchResultsTree, pos=wx.Point(0, 0), - value=text, style=text_ctrl_style) + text_ctrl = wx.TextCtrl(id=-1, parent=self.SearchResultsTree, pos=wx.Point(0, 0), + value=text, style=text_ctrl_style) width, height = text_ctrl.GetTextExtent(text) text_ctrl.SetClientSize(wx.Size(width + 1, height)) text_ctrl.SetBackgroundColour(self.SearchResultsTree.GetBackgroundColour()) @@ -306,7 +314,7 @@ text_ctrl.SetInsertionPoint(0) text_ctrl.SetStyle(start_idx, end_idx, style) self.SearchResultsTree.SetItemWindow(root, text_ctrl) - + if wx.VERSION >= (2, 6, 0): item, root_cookie = self.SearchResultsTree.GetFirstChild(root) else: @@ -317,7 +325,7 @@ item, root_cookie = self.SearchResultsTree.GetNextChild(root, root_cookie) self.GenerateSearchResultsTreeBranch(item, child) item, root_cookie = self.SearchResultsTree.GetNextChild(root, root_cookie) - + def ShowSearchResults(self, item): data = self.SearchResultsTree.GetPyData(item) if isinstance(data, TupleType): @@ -327,11 +335,11 @@ self.ParentWindow.ClearHighlights(SEARCH_RESULT_HIGHLIGHT) for infos, start, end, text in search_results: self.ParentWindow.ShowSearchResult(infos, start, end) - + def OnSearchResultsTreeItemActivated(self, event): self.ShowSearchResults(event.GetItem()) event.Skip() - + def OnResetButton(self, event): self.ResetSearchResults() self.ParentWindow.ClearSearchResults() diff -r 31e63e25b4cc -r 64beb9e9c749 controls/TextCtrlAutoComplete.py --- a/controls/TextCtrlAutoComplete.py Mon Aug 21 20:17:19 2017 +0000 +++ b/controls/TextCtrlAutoComplete.py Mon Aug 21 23:22:58 2017 +0300 @@ -34,34 +34,36 @@ LISTBOX_BORDER_HEIGHT = 4 LISTBOX_INTERVAL_HEIGHT = 6 + class PopupWithListbox(wx.PopupWindow): - + def __init__(self, parent, choices=[]): wx.PopupWindow.__init__(self, parent, wx.BORDER_SIMPLE) - - self.ListBox = wx.ListBox(self, -1, style=wx.LB_HSCROLL|wx.LB_SINGLE|wx.LB_SORT) - + + self.ListBox = wx.ListBox(self, -1, style=wx.LB_HSCROLL | wx.LB_SINGLE | wx.LB_SORT) + self.SetChoices(choices) - + self.ListBox.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) self.ListBox.Bind(wx.EVT_MOTION, self.OnMotion) - + def SetChoices(self, choices): max_text_width = 0 max_text_height = 0 - + self.ListBox.Clear() for choice in choices: self.ListBox.Append(choice) w, h = self.ListBox.GetTextExtent(choice) max_text_width = max(max_text_width, w) max_text_height = max(max_text_height, h) - + itemcount = min(len(choices), MAX_ITEM_SHOWN) width = self.Parent.GetSize()[0] - height = max_text_height * itemcount + \ - LISTBOX_INTERVAL_HEIGHT * max(0, itemcount - 1) + \ - 2 * LISTBOX_BORDER_HEIGHT + height = \ + max_text_height * itemcount + \ + LISTBOX_INTERVAL_HEIGHT * max(0, itemcount - 1) + \ + 2 * LISTBOX_BORDER_HEIGHT if max_text_width + 10 > width: height += 15 size = wx.Size(width, height) @@ -69,7 +71,7 @@ size.width -= 2 self.ListBox.SetSize(size) self.SetClientSize(size) - + def MoveSelection(self, direction): selected = self.ListBox.GetSelection() if selected == wx.NOT_FOUND: @@ -82,10 +84,10 @@ if selected == self.ListBox.GetCount(): selected = wx.NOT_FOUND self.ListBox.SetSelection(selected) - + def GetSelection(self): return self.ListBox.GetStringSelection() - + def OnLeftDown(self, event): selected = self.ListBox.HitTest(wx.Point(event.GetX(), event.GetY())) parent_size = self.Parent.GetSize() @@ -99,16 +101,17 @@ else: wx.CallAfter(self.Parent.DismissListBox) event.Skip() - + def OnMotion(self, event): self.ListBox.SetSelection( self.ListBox.HitTest(wx.Point(event.GetX(), event.GetY()))) event.Skip() - + + class TextCtrlAutoComplete(wx.TextCtrl): - def __init__ (self, parent, choices=None, dropDownClick=True, - element_path=None, **therest): + def __init__(self, parent, choices=None, dropDownClick=True, + element_path=None, **therest): """ Constructor works just like wx.TextCtrl except you can pass in a list of choices. You can also change the choice list at any time @@ -118,21 +121,21 @@ therest['style'] = wx.TE_PROCESS_ENTER | therest.get('style', 0) wx.TextCtrl.__init__(self, parent, **therest) - - #Some variables + + # Some variables self._dropDownClick = dropDownClick self._lastinsertionpoint = None self._hasfocus = False - + self._screenheight = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y) self.element_path = element_path - + self.listbox = None - + self.SetChoices(choices) - #gp = self - #while ( gp != None ) : + # gp = self + # while ( gp != None ) : # gp.Bind ( wx.EVT_MOVE , self.onControlChanged, gp ) # gp.Bind ( wx.EVT_SIZE , self.onControlChanged, gp ) # gp = gp.GetParent() @@ -142,7 +145,7 @@ self.Bind(wx.EVT_TEXT, self.OnEnteredText) self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) - #If need drop down on left click + # If need drop down on left click if dropDownClick: self.Bind(wx.EVT_LEFT_DOWN, self.OnClickToggleDown) self.Bind(wx.EVT_LEFT_UP, self.OnClickToggleUp) @@ -201,14 +204,14 @@ self.DismissListBox() self._hasfocus = False event.Skip() - + def SetChoices(self, choices): self._choices = choices self.RefreshListBoxChoices() - + def GetChoices(self): return self._choices - + def SetValueFromSelected(self, selected): """ Sets the wx.TextCtrl value from the selected wx.ListCtrl item. @@ -217,7 +220,7 @@ if selected != "": self.SetValue(selected) self.DismissListBox() - + def RefreshListBoxChoices(self): if self.listbox is not None: text = self.GetValue() @@ -227,7 +230,7 @@ def PopupListBox(self): if self.listbox is None: self.listbox = PopupWithListbox(self) - + # Show the popup right below or above the button # depending on available screen space... pos = self.ClientToScreen((0, 0)) @@ -236,9 +239,9 @@ pos.x -= 2 pos.y -= 2 self.listbox.Position(pos, (0, sz[1])) - + self.RefreshListBoxChoices() - + self.listbox.Show() def DismissListBox(self): diff -r 31e63e25b4cc -r 64beb9e9c749 controls/VariablePanel.py --- a/controls/VariablePanel.py Mon Aug 21 20:17:19 2017 +0000 +++ b/controls/VariablePanel.py Mon Aug 21 23:22:58 2017 +0300 @@ -1,984 +1,1019 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# This file is part of Beremiz, a Integrated Development Environment for -# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. -# -# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD -# -# See COPYING file for copyrights details. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -import os -import re -from types import TupleType, StringType, UnicodeType - -import wx -import wx.grid -import wx.lib.buttons - -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 -from CustomTable import CustomTable -from LocationCellEditor import LocationCellEditor -from util.BitmapLibrary import GetBitmap -from PLCControler import _VariableInfos - -#------------------------------------------------------------------------------- -# Helpers -#------------------------------------------------------------------------------- - -[TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE, - POUINSTANCEVARIABLESPANEL, LIBRARYTREE, SCALING, PAGETITLES -] = range(10) - -def GetVariableTableColnames(location): - _ = lambda x : x - if location: - return ["#", _("Name"), _("Class"), _("Type"), _("Location"), _("Initial Value"), _("Option"), _("Documentation")] - return ["#", _("Name"), _("Class"), _("Type"), _("Initial Value"), _("Option"), _("Documentation")] - -def GetOptions(constant=True, retain=True, non_retain=True): - _ = lambda x : x - options = [""] - if constant: - options.append(_("Constant")) - if retain: - options.append(_("Retain")) - if non_retain: - options.append(_("Non-Retain")) - return options -OPTIONS_DICT = dict([(_(option), option) for option in GetOptions()]) - -def GetFilterChoiceTransfer(): - _ = lambda x : x - 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()]) -VARIABLE_CLASSES_DICT = dict([(_(_class), _class) for _class in GetFilterChoiceTransfer().itervalues()]) - -CheckOptionForClass = {"Local": lambda x: x, - "Temp": lambda x: "", - "Input": lambda x: {"Retain": "Retain", "Non-Retain": "Non-Retain"}.get(x, ""), - "InOut": lambda x: "", - "Output": lambda x: {"Retain": "Retain", "Non-Retain": "Non-Retain"}.get(x, ""), - "Global": lambda x: {"Constant": "Constant", "Retain": "Retain"}.get(x, ""), - "External": lambda x: {"Constant": "Constant"}.get(x, "") - } - -LOCATION_MODEL = re.compile("((?:%[IQM](?:\*|(?:[XBWLD]?[0-9]+(?:\.[0-9]+)*)))?)$") -VARIABLE_NAME_SUFFIX_MODEL = re.compile("([0-9]*)$") - -#------------------------------------------------------------------------------- -# Variables Panel Table -#------------------------------------------------------------------------------- - -class VariableTable(CustomTable): - - """ - A custom wx.grid.Grid Table using user supplied data - """ - def __init__(self, parent, data, colnames): - # The base class must be initialized *first* - CustomTable.__init__(self, parent, data, colnames) - self.old_value = None - - def GetValueByName(self, row, colname): - if row < self.GetNumberRows(): - return getattr(self.data[row], colname) - - 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(): - if col == 0: - return self.data[row].Number - colname = self.GetColLabelValue(col, False) - if colname == "Initial Value": - colname = "InitialValue" - value = getattr(self.data[row], colname, "") - if colname == "Type" and isinstance(value, TupleType): - if value[0] == "array": - return "ARRAY [%s] OF %s" % (",".join(map(lambda x : "..".join(x), value[2])), value[1]) - if not isinstance(value, (StringType, UnicodeType)): - value = str(value) - if colname in ["Class", "Option"]: - return _(value) - return value - - def SetValue(self, row, col, value): - if col < len(self.colnames): - colname = self.GetColLabelValue(col, False) - if colname == "Name": - self.old_value = getattr(self.data[row], colname) - elif colname == "Class": - value = VARIABLE_CLASSES_DICT[value] - self.SetValueByName(row, "Option", CheckOptionForClass[value](self.GetValueByName(row, "Option"))) - if value == "External": - self.SetValueByName(row, "InitialValue", "") - elif colname == "Option": - value = OPTIONS_DICT[value] - elif colname == "Initial Value": - colname = "InitialValue" - setattr(self.data[row], colname, value) - - def GetOldValue(self): - return self.old_value - - def _GetRowEdit(self, row): - row_edit = self.GetValueByName(row, "Edit") - var_type = self.Parent.GetTagName() - bodytype = self.Parent.Controler.GetEditedElementBodyType(var_type) - if bodytype in ["ST", "IL"]: - row_edit = True; - return row_edit - - def _updateColAttrs(self, grid): - """ - wx.grid.Grid -> update the column attributes to add the - appropriate renderer given the column name. - - Otherwise default to the default renderer. - """ - for row in range(self.GetNumberRows()): - var_class = self.GetValueByName(row, "Class") - var_type = self.GetValueByName(row, "Type") - row_highlights = self.Highlights.get(row, {}) - for col in range(self.GetNumberCols()): - editor = None - renderer = None - colname = self.GetColLabelValue(col, False) - if self.Parent.Debug: - grid.SetReadOnly(row, col, True) - else: - if colname == "Option": - options = GetOptions(constant = var_class in ["Local", "External", "Global"], - retain = self.Parent.ElementType != "function" and var_class in ["Local", "Input", "Output", "Global"], - non_retain = self.Parent.ElementType != "function" and var_class in ["Local", "Input", "Output"]) - if len(options) > 1: - editor = wx.grid.GridCellChoiceEditor() - editor.SetParameters(",".join(map(_, options))) - else: - grid.SetReadOnly(row, col, True) - elif col != 0 and self._GetRowEdit(row): - grid.SetReadOnly(row, col, False) - if colname == "Name": - editor = wx.grid.GridCellTextEditor() - renderer = wx.grid.GridCellStringRenderer() - elif colname == "Initial Value": - if var_class not in ["External", "InOut"]: - if self.Parent.Controler.IsEnumeratedType(var_type): - editor = wx.grid.GridCellChoiceEditor() - editor.SetParameters(",".join([""] + self.Parent.Controler.GetEnumeratedDataValues(var_type))) - else: - editor = wx.grid.GridCellTextEditor() - renderer = wx.grid.GridCellStringRenderer() - else: - grid.SetReadOnly(row, col, True) - elif colname == "Location": - if var_class in ["Local", "Global"] and self.Parent.Controler.IsLocatableType(var_type): - editor = LocationCellEditor(self, self.Parent.Controler) - renderer = wx.grid.GridCellStringRenderer() - else: - grid.SetReadOnly(row, col, True) - elif colname == "Class": - if len(self.Parent.ClassList) == 1: - grid.SetReadOnly(row, col, True) - else: - editor = wx.grid.GridCellChoiceEditor() - excluded = [] - if self.Parent.IsFunctionBlockType(var_type): - excluded.extend(["Local","Temp"]) - 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 - else: - 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) - -#------------------------------------------------------------------------------- -# 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) - col = self.ParentWindow.VariablesGrid.XToCol(x) - row = self.ParentWindow.VariablesGrid.YToRow(y) - message = None - element_type = self.ParentWindow.ElementType - try: - values = eval(data) - except: - message = _("Invalid value \"%s\" for variable grid element")%data - values = None - if not isinstance(values, TupleType): - message = _("Invalid value \"%s\" for variable grid element")%data - values = None - if values is not None: - if col != wx.NOT_FOUND and row != wx.NOT_FOUND: - colname = self.ParentWindow.Table.GetColLabelValue(col, False) - if colname == "Location" and values[1] == "location": - if not self.ParentWindow.Table.GetValueByName(row, "Edit"): - message = _("Can't give a location to a function block instance") - elif self.ParentWindow.Table.GetValueByName(row, "Class") not in ["Local", "Global"]: - message = _("Can only give a location to local or global variables") - else: - location = values[0] - 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 \"{a1}\" and \"{a2}\"").\ - format(a1 = values[2], a2 = variable_type) - - if message is None: - if not location.startswith("%"): - if location[0].isdigit() and base_type != "BOOL": - message = _("Incompatible size of data between \"%s\" and \"BOOL\"")%location - elif location[0] not in LOCATIONDATATYPES: - message = _("Unrecognized data size \"%s\"")%location[0] - elif base_type not in LOCATIONDATATYPES[location[0]]: - message = _("Incompatible size of data between \"{a1}\" and \"{a2}\"").\ - format(a1 = location, a2 = variable_type) - else: - 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() - else: - selected = None - dialog.Destroy() - if selected is None: - return - if selected == 0: - location = "%I" + location - elif selected == 1: - location = "%Q" + location - else: - location = "%M" + location - - if message is None: - self.ParentWindow.Table.SetValue(row, col, location) - self.ParentWindow.Table.ResetView(self.ParentWindow.VariablesGrid) - self.ParentWindow.SaveValues() - elif colname == "Initial Value" and values[1] == "Constant": - if not self.ParentWindow.Table.GetValueByName(row, "Edit"): - message = _("Can't set an initial value to a function block instance") - else: - 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 - self.ParentWindow.Filter in ["All", "Interface", "External"] or - element_type != "function" and values[1] in ["location", "NamedConstant"]): - if values[1] in ["location","NamedConstant"]: - var_name = values[3] - else: - var_name = values[0] - tagname = self.ParentWindow.GetTagName() - dlg = wx.TextEntryDialog( - self.ParentWindow.ParentWindow.ParentWindow, - _("Confirm or change variable name"), - _('Variable Drop'), var_name) - dlg.SetValue(var_name) - var_name = dlg.GetValue() if dlg.ShowModal() == wx.ID_OK else None - dlg.Destroy() - if var_name is None: - return - elif 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() - for name in self.ParentWindow.Controler.\ - GetEditedElementVariables(tagname, self.ParentWindow.Debug)]: - var_infos = self.ParentWindow.DefaultValue.copy() - var_infos.Name = var_name - var_infos.Type = values[2] - var_infos.Documentation = values[4] - if values[1] == "location": - location = values[0] - if not location.startswith("%"): - 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() - else: - selected = None - dialog.Destroy() - if selected is None: - return - if selected == 0: - location = "%I" + location - elif selected == 1: - location = "%Q" + location - else: - location = "%M" + location - if element_type == "functionBlock": - configs = self.ParentWindow.Controler.GetProjectConfigNames( - self.ParentWindow.Debug) - if len(configs) == 0: - return - if not var_name.upper() in [name.upper() - for name in self.ParentWindow.Controler.\ - GetConfigurationVariableNames(configs[0])]: - self.ParentWindow.Controler.AddConfigurationGlobalVar( - configs[0], values[2], var_name, location, "") - var_infos.Class = "External" - else: - if element_type == "program": - var_infos.Class = "Local" - else: - var_infos.Class = "Global" - var_infos.Location = location - elif values[1] == "NamedConstant": - if element_type in ["functionBlock","program"]: - var_infos.Class = "Local" - var_infos.InitialValue = values[0] - else : - return - else: - var_infos.Class = "External" - var_infos.Number = len(self.ParentWindow.Values) - self.ParentWindow.Values.append(var_infos) - self.ParentWindow.SaveValues() - self.ParentWindow.RefreshValues() - else: - message = _("\"%s\" element for this pou already exists!")%var_name - - if message is not None: - wx.CallAfter(self.ShowMessage, message) - - def ShowMessage(self, message): - message = wx.MessageDialog(self.ParentWindow, message, _("Error"), wx.OK|wx.ICON_ERROR) - message.ShowModal() - message.Destroy() - -#------------------------------------------------------------------------------- -# Variable Panel -#------------------------------------------------------------------------------- - -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) - - 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, - 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), - 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 | wx.HSCROLL) - self.VariablesGrid.SetDropTarget(VariableDropTarget(self)) - 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) - self.MainSizer.AddWindow(self.VariablesGrid, flag=wx.GROW) - - self.SetSizer(self.MainSizer) - - self.ParentWindow = window - self.Controler = controler - self.ElementType = element_type - self.Debug = debug - - self.RefreshHighlightsTimer = wx.Timer(self, -1) - self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, - self.RefreshHighlightsTimer) - - self.Filter = "All" - self.FilterChoices = [] - self.FilterChoiceTransfer = GetFilterChoiceTransfer() - - self.DefaultValue = _VariableInfos("", "", "", "", "", True, "", DefaultType, ([], []), 0) - - if element_type in ["config", "resource"]: - self.DefaultTypes = {"All" : "Global"} - else: - self.DefaultTypes = {"All" : "Local", "Interface" : "Input", "Variables" : "Local"} - - if element_type in ["config", "resource"] \ - or element_type in ["program", "transition", "action"]: - # this is an element that can have located variables - self.Table = VariableTable(self, [], GetVariableTableColnames(True)) - - if element_type in ["config", "resource"]: - self.FilterChoices = ["All", "Global"]#,"Access"] - else: - self.FilterChoices = ["All", - "Interface", " Input", " Output", " InOut", " External", - "Variables", " Local", " Temp"]#,"Access"] - - # these condense the ColAlignements list - l = wx.ALIGN_LEFT - c = wx.ALIGN_CENTER - - # Num Name Class Type Loc Init Option Doc - self.ColSizes = [40, 80, 100, 80, 110, 120, 100, 160] - self.ColAlignements = [c, l, l, l, l, l, l, l] - self.ColFixedSizeFlag=[True,False, True, False, True, True, True, False] - - else: - # this is an element that cannot have located variables - self.Table = VariableTable(self, [], GetVariableTableColnames(False)) - - if element_type == "function": - self.FilterChoices = ["All", - "Interface", " Input", " Output", " InOut", - "Variables", " Local"] - else: - self.FilterChoices = ["All", - "Interface", " Input", " Output", " InOut", " External", - "Variables", " Local", " Temp"] - - # these condense the ColAlignements list - l = wx.ALIGN_LEFT - c = wx.ALIGN_CENTER - - # Num Name Class Type Init Option Doc - self.ColSizes = [40, 80, 100, 80, 120, 100, 160] - self.ColAlignements = [c, l, l, l, l, l, l] - self.ColFixedSizeFlag=[True,False, True, False, True, True, False] - - self.PanelWidthMin = sum(self.ColSizes) - - self.ElementType = element_type - self.BodyType = None - - for choice in self.FilterChoices: - self.ClassFilter.Append(_(choice)) - - reverse_transfer = {} - for filter, choice in self.FilterChoiceTransfer.items(): - reverse_transfer[choice] = filter - self.ClassFilter.SetStringSelection(_(reverse_transfer[self.Filter])) - self.RefreshTypeList() - - self.VariablesGrid.SetTable(self.Table) - self.VariablesGrid.SetButtons({"Add": self.AddButton, - "Delete": self.DeleteButton, - "Up": self.UpButton, - "Down": self.DownButton}) - self.VariablesGrid.SetEditable(not self.Debug) - - def _AddVariable(new_row): - if new_row > 0: - row_content = self.Values[new_row - 1].copy() - - result = VARIABLE_NAME_SUFFIX_MODEL.search(row_content.Name) - if result is not None: - name = row_content.Name[:result.start(1)] - suffix = result.group(1) - if suffix != "": - start_idx = int(suffix) - else: - start_idx = 0 - else: - name = row_content.Name - start_idx = 0 - else: - row_content = None - start_idx = 0 - name = "LocalVar" - - if row_content is not None and row_content.Edit: - row_content = self.Values[new_row - 1].copy() - else: - row_content = self.DefaultValue.copy() - if self.Filter in self.DefaultTypes: - row_content.Class = self.DefaultTypes[self.Filter] - else: - 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) - else: - self.Values.append(row_content) - new_row = self.Table.GetNumberRows() - self.SaveValues() - if self.ElementType == "resource": - self.ParentWindow.RefreshView(variablepanel = False) - self.RefreshValues() - return new_row - setattr(self.VariablesGrid, "_AddRow", _AddVariable) - - def _DeleteVariable(row): - if _GetRowEdit(row): - self.Values.remove(self.Table.GetRow(row)) - self.SaveValues() - if self.ElementType == "resource": - self.ParentWindow.RefreshView(variablepanel = False) - self.RefreshValues() - setattr(self.VariablesGrid, "_DeleteRow", _DeleteVariable) - - def _MoveVariable(row, move): - if self.Filter == "All": - new_row = max(0, min(row + move, len(self.Values) - 1)) - if new_row != row: - self.Values.insert(new_row, self.Values.pop(row)) - self.SaveValues() - self.RefreshValues() - return new_row - return row - setattr(self.VariablesGrid, "_MoveRow", _MoveVariable) - - def _GetRowEdit(row): - row_edit = False - if self: - row_edit = self.Table.GetValueByName(row, "Edit") - bodytype = self.Controler.GetEditedElementBodyType(self.TagName) - row_edit = row_edit or (bodytype in ["ST", "IL"]) - return row_edit - - def _RefreshButtons(): - if self: - table_length = len(self.Table.data) - row_class = None - row_edit = True - row = 0 - if table_length > 0: - row = self.VariablesGrid.GetGridCursorRow() - row_edit = _GetRowEdit(row) - self.AddButton.Enable(not self.Debug) - self.DeleteButton.Enable(not self.Debug and (table_length > 0 and row_edit)) - 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) - - panel_width = window.Parent.ScreenRect.Width - 35 - if panel_width > self.PanelWidthMin: - stretch_cols_width = panel_width - stretch_cols_sum = 0 - for col in range(len(self.ColFixedSizeFlag)): - if self.ColFixedSizeFlag[col]: - stretch_cols_width -= self.ColSizes[col] - else: - stretch_cols_sum += self.ColSizes[col] - - self.VariablesGrid.SetRowLabelSize(0) - for col in range(self.Table.GetNumberCols()): - attr = wx.grid.GridCellAttr() - attr.SetAlignment(self.ColAlignements[col], wx.ALIGN_CENTRE) - self.VariablesGrid.SetColAttr(col, attr) - self.VariablesGrid.SetColMinimalWidth(col, self.ColSizes[col]) - if (panel_width > self.PanelWidthMin) and not self.ColFixedSizeFlag[col]: - self.VariablesGrid.SetColSize(col, int((float(self.ColSizes[col])/stretch_cols_sum)*stretch_cols_width)) - else: - self.VariablesGrid.SetColSize(col, self.ColSizes[col]) - - def __del__(self): - self.RefreshHighlightsTimer.Stop() - - def SetTagName(self, tagname): - self.TagName = tagname - self.BodyType = self.Controler.GetEditedElementBodyType(self.TagName) - - def GetTagName(self): - return self.TagName - - def IsFunctionBlockType(self, name): - if (isinstance(name, TupleType) or - self.ElementType != "function" and self.BodyType in ["ST", "IL"]): - return False - else: - return self.Controler.GetBlockType(name, debug=self.Debug) is not None - - def RefreshView(self): - self.PouNames = self.Controler.GetProjectPouNames(self.Debug) - returnType = None - description = None - - words = self.TagName.split("::") - if self.ElementType == "config": - self.Values = self.Controler.GetConfigurationGlobalVars(words[1], self.Debug) - elif self.ElementType == "resource": - self.Values = self.Controler.GetConfigurationResourceGlobalVars(words[1], words[2], self.Debug) - else: - if self.ElementType == "function": - self.ReturnType.Clear() - for data_type in self.Controler.GetDataTypes(self.TagName, debug=self.Debug): - self.ReturnType.Append(data_type) - 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) - self.ReturnTypeLabel.Show() - self.ReturnType.Show() - else: - self.ReturnType.Enable(False) - self.ReturnTypeLabel.Hide() - self.ReturnType.Hide() - - if description is not None: - self.Description.SetValue(description) - self.Description.Enable(not self.Debug) - self.DescriptionLabel.Show() - self.Description.Show() - else: - self.Description.Enable(False) - self.DescriptionLabel.Hide() - self.Description.Hide() - - self.RefreshValues() - self.VariablesGrid.RefreshButtons() - self.MainSizer.Layout() - - def OnReturnTypeChanged(self, event): - words = self.TagName.split("::") - self.Controler.SetPouInterfaceReturnType(words[1], self.ReturnType.GetStringSelection()) - self.Controler.BufferProject() - self.ParentWindow.RefreshView(variablepanel = False) - self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) - event.Skip() - - def OnDescriptionChanged(self, event): - words = self.TagName.split("::") - old_description = self.Controler.GetPouDescription(words[1]) - new_description = self.Description.GetValue() - if new_description != old_description: - self.Controler.SetPouDescription(words[1], new_description) - self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) - event.Skip() - - def OnClassFilter(self, event): - self.Filter = self.FilterChoiceTransfer[VARIABLE_CHOICES_DICT[self.ClassFilter.GetStringSelection()]] - self.RefreshTypeList() - self.RefreshValues() - self.VariablesGrid.RefreshButtons() - event.Skip() - - def RefreshTypeList(self): - if self.Filter == "All": - self.ClassList = [self.FilterChoiceTransfer[choice] for choice in self.FilterChoices if self.FilterChoiceTransfer[choice] not in ["All","Interface","Variables"]] - elif self.Filter == "Interface": - self.ClassList = ["Input","Output","InOut","External"] - elif self.Filter == "Variables": - self.ClassList = ["Local","Temp"] - else: - self.ClassList = [self.Filter] - - def ShowErrorMessage(self, message): - dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) - dialog.ShowModal() - dialog.Destroy() - - def OnVariablesGridCellChange(self, event): - row, col = event.GetRow(), event.GetCol() - colname = self.Table.GetColLabelValue(col, False) - value = self.Table.GetValue(row, col) - message = None - - 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 self.PouNames: - message = _("A POU named \"%s\" already exists!") % value - elif value.upper() in [var.Name.upper() for var in self.Values if var != self.Table.data[row]]: - message = _("A variable with \"%s\" as name already exists in this pou!") % value - else: - self.SaveValues(False) - old_value = self.Table.GetOldValue() - if old_value != "": - self.Controler.UpdateEditedElementUsedVariable(self.TagName, old_value, value) - self.Controler.BufferProject() - wx.CallAfter(self.ParentWindow.RefreshView, False) - self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) - else: - self.SaveValues() - if colname == "Class": - self.ClearLocation(row, col, value) - wx.CallAfter(self.ParentWindow.RefreshView) - elif colname == "Location": - wx.CallAfter(self.ParentWindow.RefreshView) - - if message is not None: - wx.CallAfter(self.ShowErrorMessage, message) - event.Veto() - else: - event.Skip() - - def ClearLocation(self, row, col, value): - if self.Values[row].Location != '': - if self.Table.GetColLabelValue(col, False) == 'Class' and value not in ["Local", "Global"] or \ - self.Table.GetColLabelValue(col, False) == 'Type' and not self.Controler.IsLocatableType(value): - self.Values[row].Location = '' - self.RefreshValues() - self.SaveValues() - - def BuildStdIECTypesMenu(self,type_menu): - # build a submenu containing standard IEC types - base_menu = wx.Menu(title='') - for base_type in self.Controler.GetBaseTypes(): - new_id = wx.NewId() - 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: - new_id = wx.NewId() - 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) - - 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"]: - new_id = wx.NewId() - 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) - - 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: - new_id = wx.NewId() - 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): - new_id = wx.NewId() - 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": - old_value = self.Values[row].Type - classtype = self.Table.GetValueByName(row, "Class") - type_menu = wx.Menu(title='') # the root menu - - self.BuildStdIECTypesMenu(type_menu) - - self.BuildUserTypesMenu(type_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 - corner_y = rect.y + self.VariablesGrid.GetColLabelSize() - - # pop up this new menu - self.VariablesGrid.PopupMenuXY(type_menu, corner_x, corner_y) - type_menu.Destroy() - event.Veto() - value = self.Values[row].Type - if old_value != value: - self.ClearLocation(row, col, value) - else: - event.Skip() - - def GetVariableTypeFunction(self, base_type): - def VariableTypeFunction(event): - row = self.VariablesGrid.GetGridCursorRow() - self.Table.SetValueByName(row, "Type", base_type) - self.Table.ResetView(self.VariablesGrid) - self.SaveValues(False) - self.ParentWindow.RefreshView(variablepanel = False) - 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), - self.Table.GetValueByName(row, "Type")) - if dialog.ShowModal() == wx.ID_OK: - self.Table.SetValueByName(row, "Type", dialog.GetValue()) - self.Table.ResetView(self.VariablesGrid) - self.SaveValues(False) - self.ParentWindow.RefreshView(variablepanel = False) - self.Controler.BufferProject() - self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) - dialog.Destroy() - - def OnVariablesGridCellLeftClick(self, event): - row = event.GetRow() - if not self.Debug and (event.GetCol() == 0 and self.Table.GetValueByName(row, "Edit")): - var_name = self.Table.GetValueByName(row, "Name") - var_class = self.Table.GetValueByName(row, "Class") - var_type = self.Table.GetValueByName(row, "Type") - data = wx.TextDataObject(str((var_name, var_class, var_type, self.TagName))) - dragSource = wx.DropSource(self.VariablesGrid) - dragSource.SetData(data) - dragSource.DoDragDrop() - event.Skip() - - def RefreshValues(self): - data = [] - for num, variable in enumerate(self.Values): - if variable.Class in self.ClassList: - variable.Number = num + 1 - data.append(variable) - self.Table.SetData(data) - self.Table.ResetView(self.VariablesGrid) - - def SaveValues(self, buffer = True): - words = self.TagName.split("::") - if self.ElementType == "config": - self.Controler.SetConfigurationGlobalVars(words[1], self.Values) - elif self.ElementType == "resource": - self.Controler.SetConfigurationResourceGlobalVars(words[1], words[2], self.Values) - else: - if self.ReturnType.IsEnabled(): - self.Controler.SetPouInterfaceReturnType(words[1], self.ReturnType.GetStringSelection()) - self.Controler.SetPouInterfaceVars(words[1], self.Values) - if buffer: - self.Controler.BufferProject() - self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) - -#------------------------------------------------------------------------------- -# Highlights showing functions -#------------------------------------------------------------------------------- - - def OnRefreshHighlightsTimer(self, event): - self.Table.ResetView(self.VariablesGrid) - event.Skip() - - def AddVariableHighlight(self, infos, highlight_type): - if isinstance(infos[0], TupleType): - for i in xrange(*infos[0]): - self.Table.AddHighlight((i,) + infos[1:], highlight_type) - cell_visible = infos[0][0] - else: - self.Table.AddHighlight(infos, highlight_type) - cell_visible = infos[0] - colnames = [colname.lower() for colname in self.Table.colnames] - self.VariablesGrid.MakeCellVisible(cell_visible, colnames.index(infos[1])) - self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True) - - def RemoveVariableHighlight(self, infos, highlight_type): - if isinstance(infos[0], TupleType): - for i in xrange(*infos[0]): - self.Table.RemoveHighlight((i,) + infos[1:], highlight_type) - else: - self.Table.RemoveHighlight(infos, highlight_type) - self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True) - - def ClearHighlights(self, highlight_type=None): - self.Table.ClearHighlights(highlight_type) - self.Table.ResetView(self.VariablesGrid) +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# This file is part of Beremiz, a Integrated Development Environment for +# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. +# +# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# +# See COPYING file for copyrights details. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +import os +import re +from types import TupleType, StringType, UnicodeType + +import wx +import wx.grid +import wx.lib.buttons + +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 +from CustomTable import CustomTable +from LocationCellEditor import LocationCellEditor +from util.BitmapLibrary import GetBitmap +from PLCControler import _VariableInfos +from util.TranslationCatalogs import NoTranslate + + +# ------------------------------------------------------------------------------- +# Helpers +# ------------------------------------------------------------------------------- + + +[ + TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE, + POUINSTANCEVARIABLESPANEL, LIBRARYTREE, SCALING, PAGETITLES +] = range(10) + + +def GetVariableTableColnames(location): + _ = NoTranslate + cols = ["#", + _("Name"), + _("Class"), + _("Type"), + _("Location"), + _("Initial Value"), + _("Option"), + _("Documentation")] + if not location: + del cols[4] # remove 'Location' column + return cols + + +def GetOptions(constant=True, retain=True, non_retain=True): + _ = NoTranslate + options = [""] + if constant: + options.append(_("Constant")) + if retain: + options.append(_("Retain")) + if non_retain: + options.append(_("Non-Retain")) + return options + + +OPTIONS_DICT = dict([(_(option), option) for option in GetOptions()]) + + +def GetFilterChoiceTransfer(): + _ = NoTranslate + 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()]) +VARIABLE_CLASSES_DICT = dict([(_(_class), _class) for _class in GetFilterChoiceTransfer().itervalues()]) + +CheckOptionForClass = { + "Local": lambda x: x, + "Temp": lambda x: "", + "Input": lambda x: {"Retain": "Retain", "Non-Retain": "Non-Retain"}.get(x, ""), + "InOut": lambda x: "", + "Output": lambda x: {"Retain": "Retain", "Non-Retain": "Non-Retain"}.get(x, ""), + "Global": lambda x: {"Constant": "Constant", "Retain": "Retain"}.get(x, ""), + "External": lambda x: {"Constant": "Constant"}.get(x, "") +} + +LOCATION_MODEL = re.compile("((?:%[IQM](?:\*|(?:[XBWLD]?[0-9]+(?:\.[0-9]+)*)))?)$") +VARIABLE_NAME_SUFFIX_MODEL = re.compile("([0-9]*)$") + + +# ------------------------------------------------------------------------------- +# Variables Panel Table +# ------------------------------------------------------------------------------- + + +class VariableTable(CustomTable): + + """ + A custom wx.grid.Grid Table using user supplied data + """ + def __init__(self, parent, data, colnames): + # The base class must be initialized *first* + CustomTable.__init__(self, parent, data, colnames) + self.old_value = None + + def GetValueByName(self, row, colname): + if row < self.GetNumberRows(): + return getattr(self.data[row], colname) + + 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(): + if col == 0: + return self.data[row].Number + colname = self.GetColLabelValue(col, False) + if colname == "Initial Value": + colname = "InitialValue" + value = getattr(self.data[row], colname, "") + if colname == "Type" and isinstance(value, TupleType): + if value[0] == "array": + return "ARRAY [%s] OF %s" % (",".join(map(lambda x: "..".join(x), value[2])), value[1]) + if not isinstance(value, (StringType, UnicodeType)): + value = str(value) + if colname in ["Class", "Option"]: + return _(value) + return value + + def SetValue(self, row, col, value): + if col < len(self.colnames): + colname = self.GetColLabelValue(col, False) + if colname == "Name": + self.old_value = getattr(self.data[row], colname) + elif colname == "Class": + value = VARIABLE_CLASSES_DICT[value] + self.SetValueByName(row, "Option", CheckOptionForClass[value](self.GetValueByName(row, "Option"))) + if value == "External": + self.SetValueByName(row, "InitialValue", "") + elif colname == "Option": + value = OPTIONS_DICT[value] + elif colname == "Initial Value": + colname = "InitialValue" + setattr(self.data[row], colname, value) + + def GetOldValue(self): + return self.old_value + + def _GetRowEdit(self, row): + row_edit = self.GetValueByName(row, "Edit") + var_type = self.Parent.GetTagName() + bodytype = self.Parent.Controler.GetEditedElementBodyType(var_type) + if bodytype in ["ST", "IL"]: + row_edit = True + return row_edit + + def _updateColAttrs(self, grid): + """ + wx.grid.Grid -> update the column attributes to add the + appropriate renderer given the column name. + + Otherwise default to the default renderer. + """ + for row in range(self.GetNumberRows()): + var_class = self.GetValueByName(row, "Class") + var_type = self.GetValueByName(row, "Type") + row_highlights = self.Highlights.get(row, {}) + for col in range(self.GetNumberCols()): + editor = None + renderer = None + colname = self.GetColLabelValue(col, False) + if self.Parent.Debug: + grid.SetReadOnly(row, col, True) + else: + if colname == "Option": + options = GetOptions(constant=var_class in ["Local", "External", "Global"], + retain=self.Parent.ElementType != "function" and var_class in ["Local", "Input", "Output", "Global"], + non_retain=self.Parent.ElementType != "function" and var_class in ["Local", "Input", "Output"]) + if len(options) > 1: + editor = wx.grid.GridCellChoiceEditor() + editor.SetParameters(",".join(map(_, options))) + else: + grid.SetReadOnly(row, col, True) + elif col != 0 and self._GetRowEdit(row): + grid.SetReadOnly(row, col, False) + if colname == "Name": + editor = wx.grid.GridCellTextEditor() + renderer = wx.grid.GridCellStringRenderer() + elif colname == "Initial Value": + if var_class not in ["External", "InOut"]: + if self.Parent.Controler.IsEnumeratedType(var_type): + editor = wx.grid.GridCellChoiceEditor() + editor.SetParameters(",".join([""] + self.Parent.Controler.GetEnumeratedDataValues(var_type))) + else: + editor = wx.grid.GridCellTextEditor() + renderer = wx.grid.GridCellStringRenderer() + else: + grid.SetReadOnly(row, col, True) + elif colname == "Location": + if var_class in ["Local", "Global"] and self.Parent.Controler.IsLocatableType(var_type): + editor = LocationCellEditor(self, self.Parent.Controler) + renderer = wx.grid.GridCellStringRenderer() + else: + grid.SetReadOnly(row, col, True) + elif colname == "Class": + if len(self.Parent.ClassList) == 1: + grid.SetReadOnly(row, col, True) + else: + editor = wx.grid.GridCellChoiceEditor() + excluded = [] + if self.Parent.IsFunctionBlockType(var_type): + excluded.extend(["Local", "Temp"]) + 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 + else: + 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) + + +# ------------------------------------------------------------------------------- +# 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) + col = self.ParentWindow.VariablesGrid.XToCol(x) + row = self.ParentWindow.VariablesGrid.YToRow(y) + message = None + element_type = self.ParentWindow.ElementType + try: + values = eval(data) + except Exception: + message = _("Invalid value \"%s\" for variable grid element") % data + values = None + if not isinstance(values, TupleType): + message = _("Invalid value \"%s\" for variable grid element") % data + values = None + if values is not None: + if col != wx.NOT_FOUND and row != wx.NOT_FOUND: + colname = self.ParentWindow.Table.GetColLabelValue(col, False) + if colname == "Location" and values[1] == "location": + if not self.ParentWindow.Table.GetValueByName(row, "Edit"): + message = _("Can't give a location to a function block instance") + elif self.ParentWindow.Table.GetValueByName(row, "Class") not in ["Local", "Global"]: + message = _("Can only give a location to local or global variables") + else: + location = values[0] + 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 \"{a1}\" and \"{a2}\"").\ + format(a1=values[2], a2=variable_type) + + if message is None: + if not location.startswith("%"): + if location[0].isdigit() and base_type != "BOOL": + message = _("Incompatible size of data between \"%s\" and \"BOOL\"") % location + elif location[0] not in LOCATIONDATATYPES: + message = _("Unrecognized data size \"%s\"") % location[0] + elif base_type not in LOCATIONDATATYPES[location[0]]: + message = _("Incompatible size of data between \"{a1}\" and \"{a2}\"").\ + format(a1=location, a2=variable_type) + else: + 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() + else: + selected = None + dialog.Destroy() + if selected is None: + return + if selected == 0: + location = "%I" + location + elif selected == 1: + location = "%Q" + location + else: + location = "%M" + location + + if message is None: + self.ParentWindow.Table.SetValue(row, col, location) + self.ParentWindow.Table.ResetView(self.ParentWindow.VariablesGrid) + self.ParentWindow.SaveValues() + elif colname == "Initial Value" and values[1] == "Constant": + if not self.ParentWindow.Table.GetValueByName(row, "Edit"): + message = _("Can't set an initial value to a function block instance") + else: + 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 + self.ParentWindow.Filter in ["All", "Interface", "External"] or + element_type != "function" and values[1] in ["location", "NamedConstant"]): + if values[1] in ["location", "NamedConstant"]: + var_name = values[3] + else: + var_name = values[0] + tagname = self.ParentWindow.GetTagName() + dlg = wx.TextEntryDialog( + self.ParentWindow.ParentWindow.ParentWindow, + _("Confirm or change variable name"), + _('Variable Drop'), var_name) + dlg.SetValue(var_name) + var_name = dlg.GetValue() if dlg.ShowModal() == wx.ID_OK else None + dlg.Destroy() + if var_name is None: + return + elif 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() + for name in self.ParentWindow.Controler. + GetEditedElementVariables(tagname, self.ParentWindow.Debug)]: + var_infos = self.ParentWindow.DefaultValue.copy() + var_infos.Name = var_name + var_infos.Type = values[2] + var_infos.Documentation = values[4] + if values[1] == "location": + location = values[0] + if not location.startswith("%"): + 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() + else: + selected = None + dialog.Destroy() + if selected is None: + return + if selected == 0: + location = "%I" + location + elif selected == 1: + location = "%Q" + location + else: + location = "%M" + location + if element_type == "functionBlock": + configs = self.ParentWindow.Controler.GetProjectConfigNames( + self.ParentWindow.Debug) + if len(configs) == 0: + return + if not var_name.upper() in [ + name.upper() for name in + self.ParentWindow.Controler.GetConfigurationVariableNames(configs[0])]: + self.ParentWindow.Controler.AddConfigurationGlobalVar( + configs[0], values[2], var_name, location, "") + var_infos.Class = "External" + else: + if element_type == "program": + var_infos.Class = "Local" + else: + var_infos.Class = "Global" + var_infos.Location = location + elif values[1] == "NamedConstant": + if element_type in ["functionBlock", "program"]: + var_infos.Class = "Local" + var_infos.InitialValue = values[0] + else: + return + else: + var_infos.Class = "External" + var_infos.Number = len(self.ParentWindow.Values) + self.ParentWindow.Values.append(var_infos) + self.ParentWindow.SaveValues() + self.ParentWindow.RefreshValues() + else: + message = _("\"%s\" element for this pou already exists!") % var_name + + if message is not None: + wx.CallAfter(self.ShowMessage, message) + + def ShowMessage(self, message): + message = wx.MessageDialog(self.ParentWindow, message, _("Error"), wx.OK | wx.ICON_ERROR) + message.ShowModal() + message.Destroy() + + +# ------------------------------------------------------------------------------- +# Variable Panel +# ------------------------------------------------------------------------------- + + +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) + + 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, + 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), + 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 | wx.HSCROLL) + self.VariablesGrid.SetDropTarget(VariableDropTarget(self)) + 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) + self.MainSizer.AddWindow(self.VariablesGrid, flag=wx.GROW) + + self.SetSizer(self.MainSizer) + + self.ParentWindow = window + self.Controler = controler + self.ElementType = element_type + self.Debug = debug + + self.RefreshHighlightsTimer = wx.Timer(self, -1) + self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, + self.RefreshHighlightsTimer) + + self.Filter = "All" + self.FilterChoices = [] + self.FilterChoiceTransfer = GetFilterChoiceTransfer() + + self.DefaultValue = _VariableInfos("", "", "", "", "", True, "", DefaultType, ([], []), 0) + + if element_type in ["config", "resource"]: + self.DefaultTypes = {"All": "Global"} + else: + self.DefaultTypes = {"All": "Local", "Interface": "Input", "Variables": "Local"} + + if element_type in ["config", "resource"] \ + or element_type in ["program", "transition", "action"]: + # this is an element that can have located variables + self.Table = VariableTable(self, [], GetVariableTableColnames(True)) + + if element_type in ["config", "resource"]: + self.FilterChoices = ["All", "Global"] # ,"Access"] + else: + self.FilterChoices = ["All", + "Interface", " Input", " Output", " InOut", " External", + "Variables", " Local", " Temp"] # ,"Access"] + + # these condense the ColAlignements list + left = wx.ALIGN_LEFT + center = wx.ALIGN_CENTER + + # Num Name Class Type Loc Init Option Doc + self.ColSettings = { + "size": [40, 80, 100, 80, 110, 120, 100, 160], + "alignement": [center, left, left, left, left, left, left, left], + "fixed_size": [True, False, True, False, True, True, True, False], + } + + else: + # this is an element that cannot have located variables + self.Table = VariableTable(self, [], GetVariableTableColnames(False)) + + if element_type == "function": + self.FilterChoices = ["All", + "Interface", " Input", " Output", " InOut", + "Variables", " Local"] + else: + self.FilterChoices = ["All", + "Interface", " Input", " Output", " InOut", " External", + "Variables", " Local", " Temp"] + + # these condense the alignements list + left = wx.ALIGN_LEFT + center = wx.ALIGN_CENTER + + # Num Name Class Type Init Option Doc + self.ColSettings = { + "size": [40, 80, 100, 80, 120, 100, 160], + "alignement": [center, left, left, left, left, left, left], + "fixed_size": [True, False, True, False, True, True, False], + } + + self.PanelWidthMin = sum(self.ColSettings["size"]) + + self.ElementType = element_type + self.BodyType = None + + for choice in self.FilterChoices: + self.ClassFilter.Append(_(choice)) + + reverse_transfer = {} + for filter, choice in self.FilterChoiceTransfer.items(): + reverse_transfer[choice] = filter + self.ClassFilter.SetStringSelection(_(reverse_transfer[self.Filter])) + self.RefreshTypeList() + + self.VariablesGrid.SetTable(self.Table) + self.VariablesGrid.SetButtons({"Add": self.AddButton, + "Delete": self.DeleteButton, + "Up": self.UpButton, + "Down": self.DownButton}) + self.VariablesGrid.SetEditable(not self.Debug) + + def _AddVariable(new_row): + if new_row > 0: + row_content = self.Values[new_row - 1].copy() + + result = VARIABLE_NAME_SUFFIX_MODEL.search(row_content.Name) + if result is not None: + name = row_content.Name[:result.start(1)] + suffix = result.group(1) + if suffix != "": + start_idx = int(suffix) + else: + start_idx = 0 + else: + name = row_content.Name + start_idx = 0 + else: + row_content = None + start_idx = 0 + name = "LocalVar" + + if row_content is not None and row_content.Edit: + row_content = self.Values[new_row - 1].copy() + else: + row_content = self.DefaultValue.copy() + if self.Filter in self.DefaultTypes: + row_content.Class = self.DefaultTypes[self.Filter] + else: + 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) + else: + self.Values.append(row_content) + new_row = self.Table.GetNumberRows() + self.SaveValues() + if self.ElementType == "resource": + self.ParentWindow.RefreshView(variablepanel=False) + self.RefreshValues() + return new_row + setattr(self.VariablesGrid, "_AddRow", _AddVariable) + + def _DeleteVariable(row): + if _GetRowEdit(row): + self.Values.remove(self.Table.GetRow(row)) + self.SaveValues() + if self.ElementType == "resource": + self.ParentWindow.RefreshView(variablepanel=False) + self.RefreshValues() + setattr(self.VariablesGrid, "_DeleteRow", _DeleteVariable) + + def _MoveVariable(row, move): + if self.Filter == "All": + new_row = max(0, min(row + move, len(self.Values) - 1)) + if new_row != row: + self.Values.insert(new_row, self.Values.pop(row)) + self.SaveValues() + self.RefreshValues() + return new_row + return row + setattr(self.VariablesGrid, "_MoveRow", _MoveVariable) + + def _GetRowEdit(row): + row_edit = False + if self: + row_edit = self.Table.GetValueByName(row, "Edit") + bodytype = self.Controler.GetEditedElementBodyType(self.TagName) + row_edit = row_edit or (bodytype in ["ST", "IL"]) + return row_edit + + def _RefreshButtons(): + if self: + table_length = len(self.Table.data) + row_class = None + row_edit = True + row = 0 + if table_length > 0: + row = self.VariablesGrid.GetGridCursorRow() + row_edit = _GetRowEdit(row) + self.AddButton.Enable(not self.Debug) + self.DeleteButton.Enable(not self.Debug and (table_length > 0 and row_edit)) + 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) + + panel_width = window.Parent.ScreenRect.Width - 35 + if panel_width > self.PanelWidthMin: + stretch_cols_width = panel_width + stretch_cols_sum = 0 + for col in range(len(self.ColSettings["fixed_size"])): + if self.ColSettings["fixed_size"][col]: + stretch_cols_width -= self.ColSettings["size"][col] + else: + stretch_cols_sum += self.ColSettings["size"][col] + + self.VariablesGrid.SetRowLabelSize(0) + for col in range(self.Table.GetNumberCols()): + attr = wx.grid.GridCellAttr() + attr.SetAlignment(self.ColSettings["alignement"][col], wx.ALIGN_CENTRE) + self.VariablesGrid.SetColAttr(col, attr) + self.VariablesGrid.SetColMinimalWidth(col, self.ColSettings["size"][col]) + if (panel_width > self.PanelWidthMin) and not self.ColSettings["fixed_size"][col]: + self.VariablesGrid.SetColSize(col, int((float(self.ColSettings["size"][col])/stretch_cols_sum)*stretch_cols_width)) + else: + self.VariablesGrid.SetColSize(col, self.ColSettings["size"][col]) + + def __del__(self): + self.RefreshHighlightsTimer.Stop() + + def SetTagName(self, tagname): + self.TagName = tagname + self.BodyType = self.Controler.GetEditedElementBodyType(self.TagName) + + def GetTagName(self): + return self.TagName + + def IsFunctionBlockType(self, name): + if isinstance(name, TupleType) or \ + self.ElementType != "function" and self.BodyType in ["ST", "IL"]: + return False + else: + return self.Controler.GetBlockType(name, debug=self.Debug) is not None + + def RefreshView(self): + self.PouNames = self.Controler.GetProjectPouNames(self.Debug) + returnType = None + description = None + + words = self.TagName.split("::") + if self.ElementType == "config": + self.Values = self.Controler.GetConfigurationGlobalVars(words[1], self.Debug) + elif self.ElementType == "resource": + self.Values = self.Controler.GetConfigurationResourceGlobalVars(words[1], words[2], self.Debug) + else: + if self.ElementType == "function": + self.ReturnType.Clear() + for data_type in self.Controler.GetDataTypes(self.TagName, debug=self.Debug): + self.ReturnType.Append(data_type) + 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) + self.ReturnTypeLabel.Show() + self.ReturnType.Show() + else: + self.ReturnType.Enable(False) + self.ReturnTypeLabel.Hide() + self.ReturnType.Hide() + + if description is not None: + self.Description.SetValue(description) + self.Description.Enable(not self.Debug) + self.DescriptionLabel.Show() + self.Description.Show() + else: + self.Description.Enable(False) + self.DescriptionLabel.Hide() + self.Description.Hide() + + self.RefreshValues() + self.VariablesGrid.RefreshButtons() + self.MainSizer.Layout() + + def OnReturnTypeChanged(self, event): + words = self.TagName.split("::") + self.Controler.SetPouInterfaceReturnType(words[1], self.ReturnType.GetStringSelection()) + self.Controler.BufferProject() + self.ParentWindow.RefreshView(variablepanel=False) + self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) + event.Skip() + + def OnDescriptionChanged(self, event): + words = self.TagName.split("::") + old_description = self.Controler.GetPouDescription(words[1]) + new_description = self.Description.GetValue() + if new_description != old_description: + self.Controler.SetPouDescription(words[1], new_description) + self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) + event.Skip() + + def OnClassFilter(self, event): + self.Filter = self.FilterChoiceTransfer[VARIABLE_CHOICES_DICT[self.ClassFilter.GetStringSelection()]] + self.RefreshTypeList() + self.RefreshValues() + self.VariablesGrid.RefreshButtons() + event.Skip() + + def RefreshTypeList(self): + if self.Filter == "All": + self.ClassList = [self.FilterChoiceTransfer[choice] for choice in self.FilterChoices if self.FilterChoiceTransfer[choice] not in ["All", "Interface", "Variables"]] + elif self.Filter == "Interface": + self.ClassList = ["Input", "Output", "InOut", "External"] + elif self.Filter == "Variables": + self.ClassList = ["Local", "Temp"] + else: + self.ClassList = [self.Filter] + + def ShowErrorMessage(self, message): + dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) + dialog.ShowModal() + dialog.Destroy() + + def OnVariablesGridCellChange(self, event): + row, col = event.GetRow(), event.GetCol() + colname = self.Table.GetColLabelValue(col, False) + value = self.Table.GetValue(row, col) + message = None + + 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 self.PouNames: + message = _("A POU named \"%s\" already exists!") % value + elif value.upper() in [var.Name.upper() for var in self.Values if var != self.Table.data[row]]: + message = _("A variable with \"%s\" as name already exists in this pou!") % value + else: + self.SaveValues(False) + old_value = self.Table.GetOldValue() + if old_value != "": + self.Controler.UpdateEditedElementUsedVariable(self.TagName, old_value, value) + self.Controler.BufferProject() + wx.CallAfter(self.ParentWindow.RefreshView, False) + self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) + else: + self.SaveValues() + if colname == "Class": + self.ClearLocation(row, col, value) + wx.CallAfter(self.ParentWindow.RefreshView) + elif colname == "Location": + wx.CallAfter(self.ParentWindow.RefreshView) + + if message is not None: + wx.CallAfter(self.ShowErrorMessage, message) + event.Veto() + else: + event.Skip() + + def ClearLocation(self, row, col, value): + if self.Values[row].Location != '': + if self.Table.GetColLabelValue(col, False) == 'Class' and value not in ["Local", "Global"] or \ + self.Table.GetColLabelValue(col, False) == 'Type' and not self.Controler.IsLocatableType(value): + self.Values[row].Location = '' + self.RefreshValues() + self.SaveValues() + + def BuildStdIECTypesMenu(self, type_menu): + # build a submenu containing standard IEC types + base_menu = wx.Menu(title='') + for base_type in self.Controler.GetBaseTypes(): + new_id = wx.NewId() + 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: + new_id = wx.NewId() + 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) + + 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"]: + new_id = wx.NewId() + 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) + + 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: + new_id = wx.NewId() + 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): + new_id = wx.NewId() + 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": + old_value = self.Values[row].Type + classtype = self.Table.GetValueByName(row, "Class") + type_menu = wx.Menu(title='') # the root menu + + self.BuildStdIECTypesMenu(type_menu) + + self.BuildUserTypesMenu(type_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 + corner_y = rect.y + self.VariablesGrid.GetColLabelSize() + + # pop up this new menu + self.VariablesGrid.PopupMenuXY(type_menu, corner_x, corner_y) + type_menu.Destroy() + event.Veto() + value = self.Values[row].Type + if old_value != value: + self.ClearLocation(row, col, value) + else: + event.Skip() + + def GetVariableTypeFunction(self, base_type): + def VariableTypeFunction(event): + row = self.VariablesGrid.GetGridCursorRow() + self.Table.SetValueByName(row, "Type", base_type) + self.Table.ResetView(self.VariablesGrid) + self.SaveValues(False) + self.ParentWindow.RefreshView(variablepanel=False) + 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), + self.Table.GetValueByName(row, "Type")) + if dialog.ShowModal() == wx.ID_OK: + self.Table.SetValueByName(row, "Type", dialog.GetValue()) + self.Table.ResetView(self.VariablesGrid) + self.SaveValues(False) + self.ParentWindow.RefreshView(variablepanel=False) + self.Controler.BufferProject() + self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) + dialog.Destroy() + + def OnVariablesGridCellLeftClick(self, event): + row = event.GetRow() + if not self.Debug and (event.GetCol() == 0 and self.Table.GetValueByName(row, "Edit")): + var_name = self.Table.GetValueByName(row, "Name") + var_class = self.Table.GetValueByName(row, "Class") + var_type = self.Table.GetValueByName(row, "Type") + data = wx.TextDataObject(str((var_name, var_class, var_type, self.TagName))) + dragSource = wx.DropSource(self.VariablesGrid) + dragSource.SetData(data) + dragSource.DoDragDrop() + event.Skip() + + def RefreshValues(self): + data = [] + for num, variable in enumerate(self.Values): + if variable.Class in self.ClassList: + variable.Number = num + 1 + data.append(variable) + self.Table.SetData(data) + self.Table.ResetView(self.VariablesGrid) + + def SaveValues(self, buffer=True): + words = self.TagName.split("::") + if self.ElementType == "config": + self.Controler.SetConfigurationGlobalVars(words[1], self.Values) + elif self.ElementType == "resource": + self.Controler.SetConfigurationResourceGlobalVars(words[1], words[2], self.Values) + else: + if self.ReturnType.IsEnabled(): + self.Controler.SetPouInterfaceReturnType(words[1], self.ReturnType.GetStringSelection()) + self.Controler.SetPouInterfaceVars(words[1], self.Values) + if buffer: + self.Controler.BufferProject() + self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) + + # ------------------------------------------------------------------------------- + # Highlights showing functions + # ------------------------------------------------------------------------------- + + def OnRefreshHighlightsTimer(self, event): + self.Table.ResetView(self.VariablesGrid) + event.Skip() + + def AddVariableHighlight(self, infos, highlight_type): + if isinstance(infos[0], TupleType): + for i in xrange(*infos[0]): + self.Table.AddHighlight((i,) + infos[1:], highlight_type) + cell_visible = infos[0][0] + else: + self.Table.AddHighlight(infos, highlight_type) + cell_visible = infos[0] + colnames = [colname.lower() for colname in self.Table.colnames] + self.VariablesGrid.MakeCellVisible(cell_visible, colnames.index(infos[1])) + self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True) + + def RemoveVariableHighlight(self, infos, highlight_type): + if isinstance(infos[0], TupleType): + for i in xrange(*infos[0]): + self.Table.RemoveHighlight((i,) + infos[1:], highlight_type) + else: + self.Table.RemoveHighlight(infos, highlight_type) + self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True) + + def ClearHighlights(self, highlight_type=None): + self.Table.ClearHighlights(highlight_type) + self.Table.ResetView(self.VariablesGrid) diff -r 31e63e25b4cc -r 64beb9e9c749 dialogs/AboutDialog.py --- a/dialogs/AboutDialog.py Mon Aug 21 20:17:19 2017 +0000 +++ b/dialogs/AboutDialog.py Mon Aug 21 23:22:58 2017 +0300 @@ -37,8 +37,6 @@ from wx.lib.agw.hyperlink import HyperLinkCtrl -#---------------------------------------------------------------------- - class AboutDialog(wx.Dialog): """ A replacement About Dialog for Windows, as it uses a generic frame that @@ -106,8 +104,6 @@ CreditsDialog(self, self.info) -#---------------------------------------------------------------------- - class CreditsDialog(wx.Dialog): def __init__(self, parent, info): wx.Dialog.__init__(self, parent, title=_("Credits"), size=(475, 320), @@ -144,8 +140,6 @@ close.Bind(wx.EVT_BUTTON, lambda evt: self.Destroy()) -#---------------------------------------------------------------------- - class LicenseDialog(wx.Dialog): def __init__(self, parent, info): wx.Dialog.__init__(self, parent, title=_("License"), size=(500, 400), @@ -173,7 +167,6 @@ close.Bind(wx.EVT_BUTTON, lambda evt: self.Destroy()) -#---------------------------------------------------------------------- def ShowAboutDialog(parent, info): if os.name == "nt": diff -r 31e63e25b4cc -r 64beb9e9c749 dialogs/ActionBlockDialog.py --- a/dialogs/ActionBlockDialog.py Mon Aug 21 20:17:19 2017 +0000 +++ b/dialogs/ActionBlockDialog.py Mon Aug 21 23:22:58 2017 +0300 @@ -29,25 +29,28 @@ from controls import CustomGrid, CustomTable from util.BitmapLibrary import GetBitmap from PLCControler import _ActionInfos - -#------------------------------------------------------------------------------- +from util.TranslationCatalogs import NoTranslate +# ------------------------------------------------------------------------------- # Helpers -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + def GetActionTableColnames(): - _ = lambda x: x + _ = NoTranslate return [_("Qualifier"), _("Duration"), _("Type"), _("Value"), _("Indicator")] + def GetTypeList(): - _ = lambda x: x + _ = NoTranslate return [_("Action"), _("Variable"), _("Inline")] -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Action Table -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + class ActionTable(CustomTable): - + def GetValue(self, row, col): if row < self.GetNumberRows(): colname = self.GetColLabelValue(col, False) @@ -55,7 +58,7 @@ if colname == "Type": return _(value) return value - + def SetValue(self, row, col, value): if col < len(self.colnames): colname = self.GetColLabelValue(col, False) @@ -64,7 +67,7 @@ elif colname == "Qualifier" and not self.Parent.DurationList[value]: self.data[row].duration = "" setattr(self.data[row], colname.lower(), value) - + def _updateColAttrs(self, grid): """ wx.Grid -> update the column attributes to add the @@ -72,7 +75,7 @@ Otherwise default to the default renderer. """ - + for row in range(self.GetNumberRows()): for col in range(self.GetNumberCols()): editor = None @@ -103,69 +106,71 @@ elif colname == "Indicator": editor = wx.grid.GridCellChoiceEditor() editor.SetParameters(self.Parent.VariableList) - + grid.SetCellEditor(row, col, editor) grid.SetCellRenderer(row, col, renderer) grid.SetReadOnly(row, col, readonly) - + grid.SetCellBackgroundColour(row, col, wx.WHITE) self.ResizeRow(grid, row) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Action Block Dialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + class ActionBlockDialog(wx.Dialog): - + def __init__(self, parent): wx.Dialog.__init__(self, parent, title=_('Edit action block properties')) - + main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=10) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(1) - + top_sizer = wx.FlexGridSizer(cols=5, hgap=5, rows=1, vgap=0) top_sizer.AddGrowableCol(0) top_sizer.AddGrowableRow(0) main_sizer.AddSizer(top_sizer, border=20, - flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) - + flag=wx.GROW | wx.TOP | wx.LEFT | wx.RIGHT) + actions_label = wx.StaticText(self, label=_('Actions:')) top_sizer.AddWindow(actions_label, flag=wx.ALIGN_BOTTOM) - + for name, bitmap, help in [ ("AddButton", "add_element", _("Add action")), ("DeleteButton", "remove_element", _("Remove action")), ("UpButton", "up", _("Move action up")), ("DownButton", "down", _("Move action down"))]: - button = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap(bitmap), - size=wx.Size(28, 28), style=wx.NO_BORDER) + 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) top_sizer.AddWindow(button) - + self.ActionsGrid = CustomGrid(self, size=wx.Size(-1, 250), style=wx.VSCROLL) self.ActionsGrid.DisableDragGridSize() self.ActionsGrid.EnableScrolling(False, True) - self.ActionsGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, + self.ActionsGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.OnActionsGridCellChange) main_sizer.AddSizer(self.ActionsGrid, border=20, - flag=wx.GROW|wx.LEFT|wx.RIGHT) - - button_sizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE) + flag=wx.GROW | wx.LEFT | wx.RIGHT) + + button_sizer = self.CreateButtonSizer(wx.OK | wx.CANCEL | wx.CENTRE) self.Bind(wx.EVT_BUTTON, self.OnOK, button_sizer.GetAffirmativeButton()) - main_sizer.AddSizer(button_sizer, border=20, - flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) - + main_sizer.AddSizer(button_sizer, border=20, + flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) + self.SetSizer(main_sizer) - + self.Table = ActionTable(self, [], GetActionTableColnames()) - typelist = GetTypeList() - self.TypeList = ",".join(map(_,typelist)) + typelist = GetTypeList() + self.TypeList = ",".join(map(_, typelist)) self.TranslateType = dict([(_(value), value) for value in typelist]) self.ColSizes = [60, 90, 130, 200, 50] self.ColAlignements = [wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT] - + self.ActionsGrid.SetTable(self.Table) self.ActionsGrid.SetDefaultValue(_ActionInfos("N", "Action", "", "", "")) self.ActionsGrid.SetButtons({"Add": self.AddButton, @@ -173,19 +178,19 @@ "Up": self.UpButton, "Down": self.DownButton}) self.ActionsGrid.SetRowLabelSize(0) - + for col in range(self.Table.GetNumberCols()): attr = wx.grid.GridCellAttr() attr.SetAlignment(self.ColAlignements[col], wx.ALIGN_CENTRE) self.ActionsGrid.SetColAttr(col, attr) self.ActionsGrid.SetColMinimalWidth(col, self.ColSizes[col]) self.ActionsGrid.AutoSizeColumn(col, False) - + self.Table.ResetView(self.ActionsGrid) self.ActionsGrid.SetFocus() self.ActionsGrid.RefreshButtons() self.Fit() - + def OnOK(self, event): self.ActionsGrid.CloseEditControl() self.EndModal(wx.ID_OK) @@ -193,14 +198,14 @@ def OnActionsGridCellChange(self, event): wx.CallAfter(self.Table.ResetView, self.ActionsGrid) event.Skip() - + def SetQualifierList(self, list): self.QualifierList = ",".join(list) self.DurationList = list def SetVariableList(self, list): self.VariableList = "," + ",".join([variable.Name for variable in list]) - + def SetActionList(self, list): self.ActionList = "," + ",".join(list) @@ -218,7 +223,7 @@ if len(actions) > 0: self.ActionsGrid.SetGridCursor(0, 0) self.ActionsGrid.RefreshButtons() - + def GetValues(self): actions = self.Table.GetData() for action in actions: diff -r 31e63e25b4cc -r 64beb9e9c749 dialogs/ArrayTypeDialog.py --- a/dialogs/ArrayTypeDialog.py Mon Aug 21 20:17:19 2017 +0000 +++ b/dialogs/ArrayTypeDialog.py Mon Aug 21 23:22:58 2017 +0300 @@ -28,67 +28,68 @@ from controls import CustomEditableListBox -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Helpers -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- DIMENSION_MODEL = re.compile("([0-9]+)\.\.([0-9]+)$") -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Array Type Dialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + class ArrayTypeDialog(wx.Dialog): - + def __init__(self, parent, datatypes, infos): wx.Dialog.__init__(self, parent, title=_('Edit array type properties')) - + main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=10) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(1) - + top_sizer = wx.BoxSizer(wx.HORIZONTAL) - main_sizer.AddSizer(top_sizer, border=20, - flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) - + main_sizer.AddSizer(top_sizer, border=20, + flag=wx.GROW | wx.TOP | wx.LEFT | wx.RIGHT) + basetype_label = wx.StaticText(self, label=_('Base Type:')) top_sizer.AddWindow(basetype_label, 1, flag=wx.ALIGN_BOTTOM) - + self.BaseType = wx.ComboBox(self, style=wx.CB_READONLY) top_sizer.AddWindow(self.BaseType, 1, flag=wx.GROW) - - self.Dimensions = CustomEditableListBox(self, label=_("Dimensions:"), - style=wx.gizmos.EL_ALLOW_NEW| - wx.gizmos.EL_ALLOW_EDIT| - wx.gizmos.EL_ALLOW_DELETE) - for func in ["_OnLabelEndEdit", - "_OnAddButton", - "_OnDelButton", - "_OnUpButton", + + self.Dimensions = CustomEditableListBox(self, label=_("Dimensions:"), + style=(wx.gizmos.EL_ALLOW_NEW | + wx.gizmos.EL_ALLOW_EDIT | + wx.gizmos.EL_ALLOW_DELETE)) + for func in ["_OnLabelEndEdit", + "_OnAddButton", + "_OnDelButton", + "_OnUpButton", "_OnDownButton"]: setattr(self.Dimensions, func, self.OnDimensionsChanged) - main_sizer.AddSizer(self.Dimensions, border=20, - flag=wx.GROW|wx.LEFT|wx.RIGHT) - - button_sizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE) + main_sizer.AddSizer(self.Dimensions, border=20, + flag=wx.GROW | wx.LEFT | wx.RIGHT) + + button_sizer = self.CreateButtonSizer(wx.OK | wx.CANCEL | wx.CENTRE) self.Bind(wx.EVT_BUTTON, self.OnOK, button_sizer.GetAffirmativeButton()) - main_sizer.AddSizer(button_sizer, border=20, - flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) - + main_sizer.AddSizer(button_sizer, border=20, + flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) + self.SetSizer(main_sizer) - + for datatype in datatypes: self.BaseType.Append(datatype) - + if isinstance(infos, TupleType) and infos[0] == "array": self.BaseType.SetStringSelection(infos[1]) - self.Dimensions.SetStrings(map(lambda x : "..".join(x), infos[2])) + self.Dimensions.SetStrings(map(lambda x: "..".join(x), infos[2])) elif infos in datatypes: self.BaseType.SetStringSelection(infos) - + self.BaseType.SetFocus() self.Fit() - + def GetDimensions(self): message = None dimensions_list = [] @@ -99,28 +100,28 @@ for dimensions in dimension_strings: result = DIMENSION_MODEL.match(dimensions) if result is None: - message = _("\"%s\" value isn't a valid array dimension!")%dimensions + message = _("\"%s\" value isn't a valid array dimension!") % dimensions break bounds = result.groups() if int(bounds[0]) >= int(bounds[1]): - message = _("\"%s\" value isn't a valid array dimension!\nRight value must be greater than left value.")%dimensions + message = _("\"%s\" value isn't a valid array dimension!\nRight value must be greater than left value.") % dimensions break dimensions_list.append(bounds) if message is not None: - dlg = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) + dlg = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() return None return dimensions_list - + def OnDimensionsChanged(self, event): wx.CallAfter(self.GetDimensions) event.Skip() - + def OnOK(self, event): if self.GetDimensions() is not None: self.EndModal(wx.ID_OK) - + def GetValue(self): return "array", self.BaseType.GetStringSelection(), self.GetDimensions() diff -r 31e63e25b4cc -r 64beb9e9c749 dialogs/BlockPreviewDialog.py --- a/dialogs/BlockPreviewDialog.py Mon Aug 21 20:17:19 2017 +0000 +++ b/dialogs/BlockPreviewDialog.py Mon Aug 21 23:22:58 2017 +0300 @@ -28,17 +28,17 @@ from plcopen.structures import TestIdentifier, IEC_KEYWORDS from graphics.GraphicCommons import FREEDRAWING_MODE -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Dialog with preview for graphic block -#------------------------------------------------------------------------------- - -""" -Class that implements a generic dialog containing a preview panel for displaying -graphic created by dialog -""" +# ------------------------------------------------------------------------------- + class BlockPreviewDialog(wx.Dialog): - + """ + Class that implements a generic dialog containing a preview panel for displaying + graphic created by dialog + """ + def __init__(self, parent, controller, tagname, title): """ Constructor @@ -48,53 +48,54 @@ @param title: Title of dialog frame """ wx.Dialog.__init__(self, parent, title=title) - + # Save reference to self.Controller = controller self.TagName = tagname - + # Label for preview self.PreviewLabel = wx.StaticText(self, label=_('Preview:')) - + # Create Preview panel self.Preview = wx.Panel(self, style=wx.SIMPLE_BORDER) self.Preview.SetBackgroundColour(wx.WHITE) - + # Add function to preview panel so that it answers to graphic elements # like Viewer - setattr(self.Preview, "GetDrawingMode", lambda:FREEDRAWING_MODE) - setattr(self.Preview, "GetScaling", lambda:None) + setattr(self.Preview, "GetDrawingMode", lambda: FREEDRAWING_MODE) + setattr(self.Preview, "GetScaling", lambda: None) setattr(self.Preview, "GetBlockType", controller.GetBlockType) setattr(self.Preview, "IsOfType", controller.IsOfType) - + # Bind paint event on Preview panel self.Preview.Bind(wx.EVT_PAINT, self.OnPaint) - + # Add default dialog buttons sizer - self.ButtonSizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE) - self.Bind(wx.EVT_BUTTON, self.OnOK, + self.ButtonSizer = self.CreateButtonSizer(wx.OK | wx.CANCEL | wx.CENTRE) + self.Bind(wx.EVT_BUTTON, self.OnOK, self.ButtonSizer.GetAffirmativeButton()) - + self.Element = None # Graphic element to display in preview self.MinElementSize = None # Graphic element minimal size - + # Variable containing the graphic element name when dialog is opened self.DefaultElementName = None self.Fit() - + # List of variables defined in POU {var_name: (var_class, var_type),...} self.VariableList = {} - + def __del__(self): """ Destructor """ # Remove reference to project controller self.Controller = None - - def _init_sizers(self, main_rows, main_growable_row, - left_rows, left_growable_row, - right_rows, right_growable_row): + + def _init_sizers(self, + main_rows, main_growable_row, + left_rows, left_growable_row, + right_rows, right_growable_row): """ Initialize common sizers @param main_rows: Number of rows in main sizer @@ -108,44 +109,44 @@ None if no row is growable """ # Create dialog main sizer - self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, + self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=main_rows, vgap=10) self.MainSizer.AddGrowableCol(0) if main_growable_row is not None: self.MainSizer.AddGrowableRow(main_growable_row) - + # Create a sizer for dividing parameters in two columns self.ColumnSizer = wx.BoxSizer(wx.HORIZONTAL) - self.MainSizer.AddSizer(self.ColumnSizer, border=20, - flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) - + self.MainSizer.AddSizer(self.ColumnSizer, border=20, + flag=wx.GROW | wx.TOP | wx.LEFT | wx.RIGHT) + # Create a sizer for left column - self.LeftGridSizer = wx.FlexGridSizer(cols=1, hgap=0, + self.LeftGridSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=left_rows, vgap=5) self.LeftGridSizer.AddGrowableCol(0) if left_growable_row is not None: self.LeftGridSizer.AddGrowableRow(left_growable_row) - self.ColumnSizer.AddSizer(self.LeftGridSizer, 1, border=5, - flag=wx.GROW|wx.RIGHT|wx.EXPAND) - + self.ColumnSizer.AddSizer(self.LeftGridSizer, 1, border=5, + flag=wx.GROW | wx.RIGHT | wx.EXPAND) + # Create a sizer for right column - self.RightGridSizer = wx.FlexGridSizer(cols=1, hgap=0, + self.RightGridSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=right_rows, vgap=0) self.RightGridSizer.AddGrowableCol(0) if right_growable_row is not None: self.RightGridSizer.AddGrowableRow(right_growable_row) - self.ColumnSizer.AddSizer(self.RightGridSizer, 1, border=5, - flag=wx.GROW|wx.LEFT) - + self.ColumnSizer.AddSizer(self.RightGridSizer, 1, border=5, + flag=wx.GROW | wx.LEFT) + self.SetSizer(self.MainSizer) - + def SetMinElementSize(self, size): """ Define minimal graphic element size @param size: Tuple containing minimal size (width, height) """ self.MinElementSize = size - + def GetMinElementSize(self): """ Get minimal graphic element size @@ -155,16 +156,16 @@ """ if self.Element is None: return None - + return self.Element.GetMinSize() - + def SetPreviewFont(self, font): """ Set font of Preview panel @param font: wx.Font object containing font style """ self.Preview.SetFont(font) - + def RefreshVariableList(self): """ Extract list of variables defined in POU @@ -175,20 +176,20 @@ for var in self.Controller.GetEditedElementInterfaceVars( self.TagName) if var.Edit} - - # Add POU name to variable list if POU is a function + + # Add POU name to variable list if POU is a function returntype = self.Controller.GetEditedElementInterfaceReturnType( self.TagName) if returntype is not None: self.VariableList[ self.Controller.GetEditedElementName(self.TagName)] = \ ("Output", returntype) - + # Add POU name if POU is a transition words = self.TagName.split("::") if words[0] == "T": self.VariableList[words[2]] = ("Output", "BOOL") - + def TestElementName(self, element_name): """ Test displayed graphic element name @@ -198,47 +199,46 @@ message_format = None # Get graphic element name in upper case uppercase_element_name = element_name.upper() - + # Test if graphic element name is a valid identifier if not TestIdentifier(element_name): message_format = _("\"%s\" is not a valid identifier!") - + # Test that graphic element name isn't a keyword elif uppercase_element_name in IEC_KEYWORDS: message_format = _("\"%s\" is a keyword. It can't be used!") - + # Test that graphic element name isn't a POU name elif uppercase_element_name in self.Controller.GetProjectPouNames(): message_format = _("\"%s\" pou already exists!") - + # Test that graphic element name isn't already used in POU by a variable # or another graphic element - elif ((self.DefaultElementName is None or - self.DefaultElementName.upper() != uppercase_element_name) and - uppercase_element_name in self.Controller.\ - GetEditedElementVariables(self.TagName)): + elif ((self.DefaultElementName is None or + self.DefaultElementName.upper() != uppercase_element_name) and + uppercase_element_name in self.Controller.GetEditedElementVariables(self.TagName)): message_format = _("\"%s\" element for this pou already exists!") - + # If an error have been identify, show error message dialog if message_format is not None: self.ShowErrorMessage(message_format % element_name) # Test failed return False - + # Test succeed return True - + def ShowErrorMessage(self, message): """ Show an error message dialog over this dialog @param message: Error message to display """ - dialog = wx.MessageDialog(self, message, - _("Error"), - wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self, message, + _("Error"), + wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() - + def OnOK(self, event): """ Called when dialog OK button is pressed @@ -248,7 +248,7 @@ """ # Close dialog self.EndModal(wx.ID_OK) - + def RefreshPreview(self): """ Refresh preview panel of graphic element @@ -258,42 +258,42 @@ dc = wx.ClientDC(self.Preview) dc.SetFont(self.Preview.GetFont()) dc.Clear() - + # Return immediately if no graphic element defined if self.Element is None: return - + # Calculate block size according to graphic element min size due to its # parameters and graphic element min size defined min_width, min_height = self.GetMinElementSize() width = max(self.MinElementSize[0], min_width) height = max(self.MinElementSize[1], min_height) self.Element.SetSize(width, height) - + # Get element position and bounding box to center in preview posx, posy = self.Element.GetPosition() bbox = self.Element.GetBoundingBox() - + # Get Preview panel size client_size = self.Preview.GetClientSize() - + # If graphic element is too big to be displayed in preview panel, # calculate preview panel scale so that graphic element fit inside - scale = (max(float(bbox.width) / client_size.width, - float(bbox.height) / client_size.height) * 1.1 - if bbox.width * 1.1 > client_size.width or - bbox.height * 1.1 > client_size.height - else 1.0) + k = 1.1 if (bbox.width * 1.1 > client_size.width or + bbox.height * 1.1 > client_size.height) \ + else 1.0 + scale = (max(float(bbox.width) / client_size.width, + float(bbox.height) / client_size.height) * k) dc.SetUserScale(1.0 / scale, 1.0 / scale) - + # Center graphic element in preview panel x = int(client_size.width * scale - bbox.width) / 2 + posx - bbox.x y = int(client_size.height * scale - bbox.height) / 2 + posy - bbox.y self.Element.SetPosition(x, y) - + # Draw graphic element self.Element.Draw(dc) - + def OnPaint(self, event): """ Called when Preview panel need to be redraw @@ -301,4 +301,3 @@ """ self.RefreshPreview() event.Skip() - diff -r 31e63e25b4cc -r 64beb9e9c749 dialogs/BrowseLocationsDialog.py --- a/dialogs/BrowseLocationsDialog.py Mon Aug 21 20:17:19 2017 +0000 +++ b/dialogs/BrowseLocationsDialog.py Mon Aug 21 23:22:58 2017 +0300 @@ -30,111 +30,122 @@ from plcopen.structures import LOCATIONDATATYPES from PLCControler import LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY from util.BitmapLibrary import GetBitmap - -#------------------------------------------------------------------------------- +from util.TranslationCatalogs import NoTranslate + +# ------------------------------------------------------------------------------- # Helpers -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + def GetDirFilterChoiceOptions(): - _ = lambda x : x - return [(_("All"), [LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY]), - (_("Input"), [LOCATION_VAR_INPUT]), - (_("Output"), [LOCATION_VAR_OUTPUT]), + _ = NoTranslate + return [(_("All"), [LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY]), + (_("Input"), [LOCATION_VAR_INPUT]), + (_("Output"), [LOCATION_VAR_OUTPUT]), (_("Memory"), [LOCATION_VAR_MEMORY])] + + DIRFILTERCHOICE_OPTIONS = dict([(_(option), filter) for option, filter in GetDirFilterChoiceOptions()]) + def GetTypeFilterChoiceOptions(): - _ = lambda x : x - return [_("All"), - _("Type and derivated"), + _ = NoTranslate + return [_("All"), + _("Type and derivated"), _("Type strict")] + # turn LOCATIONDATATYPES inside-out LOCATION_SIZES = {} for size, types in LOCATIONDATATYPES.iteritems(): for type in types: LOCATION_SIZES[type] = size -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Browse Locations Dialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + class BrowseLocationsDialog(wx.Dialog): - + def __init__(self, parent, var_type, controller): wx.Dialog.__init__(self, parent, title=_('Browse Locations'), - style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER) - + style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) + main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=10) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(1) - + locations_label = wx.StaticText(self, label=_('Locations available:')) - main_sizer.AddWindow(locations_label, border=20, - flag=wx.TOP|wx.LEFT|wx.RIGHT|wx.GROW) - - self.LocationsTree = wx.TreeCtrl(self, - style=wx.TR_HAS_BUTTONS|wx.TR_SINGLE|wx.SUNKEN_BORDER|wx.TR_HIDE_ROOT|wx.TR_LINES_AT_ROOT) + main_sizer.AddWindow(locations_label, border=20, + flag=wx.TOP | wx.LEFT | wx.RIGHT | wx.GROW) + + self.LocationsTree = wx.TreeCtrl(self, + style=(wx.TR_HAS_BUTTONS | + wx.TR_SINGLE | + wx.SUNKEN_BORDER | + wx.TR_HIDE_ROOT | + wx.TR_LINES_AT_ROOT)) self.LocationsTree.SetInitialSize(wx.Size(-1, 300)) - self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnLocationsTreeItemActivated, + self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnLocationsTreeItemActivated, self.LocationsTree) - main_sizer.AddWindow(self.LocationsTree, border=20, - flag=wx.LEFT|wx.RIGHT|wx.GROW) - + main_sizer.AddWindow(self.LocationsTree, border=20, + flag=wx.LEFT | wx.RIGHT | wx.GROW) + button_gridsizer = wx.FlexGridSizer(cols=5, hgap=5, rows=1, vgap=0) button_gridsizer.AddGrowableCol(1) button_gridsizer.AddGrowableCol(3) button_gridsizer.AddGrowableRow(0) - main_sizer.AddSizer(button_gridsizer, border=20, - flag=wx.BOTTOM|wx.LEFT|wx.RIGHT|wx.GROW) - + main_sizer.AddSizer(button_gridsizer, border=20, + flag=wx.BOTTOM | wx.LEFT | wx.RIGHT | wx.GROW) + direction_label = wx.StaticText(self, label=_('Direction:')) button_gridsizer.AddWindow(direction_label, - flag=wx.ALIGN_CENTER_VERTICAL) - + flag=wx.ALIGN_CENTER_VERTICAL) + self.DirFilterChoice = wx.ComboBox(self, style=wx.CB_READONLY) self.Bind(wx.EVT_COMBOBOX, self.OnFilterChoice, self.DirFilterChoice) button_gridsizer.AddWindow(self.DirFilterChoice, - flag=wx.GROW|wx.ALIGN_CENTER_VERTICAL) - + flag=wx.GROW | wx.ALIGN_CENTER_VERTICAL) + filter_label = wx.StaticText(self, label=_('Type:')) button_gridsizer.AddWindow(filter_label, - flag=wx.ALIGN_CENTER_VERTICAL) - + flag=wx.ALIGN_CENTER_VERTICAL) + self.TypeFilterChoice = wx.ComboBox(self, style=wx.CB_READONLY) self.Bind(wx.EVT_COMBOBOX, self.OnFilterChoice, self.TypeFilterChoice) button_gridsizer.AddWindow(self.TypeFilterChoice, - flag=wx.GROW|wx.ALIGN_CENTER_VERTICAL) - - button_sizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE) + flag=wx.GROW | wx.ALIGN_CENTER_VERTICAL) + + button_sizer = self.CreateButtonSizer(wx.OK | wx.CANCEL | wx.CENTRE) self.Bind(wx.EVT_BUTTON, self.OnOK, button_sizer.GetAffirmativeButton()) button_gridsizer.AddSizer(button_sizer, flag=wx.ALIGN_RIGHT) - + self.SetSizer(main_sizer) - + self.Controller = controller self.VarType = var_type self.BaseVarType = self.Controller.GetBaseType(self.VarType) self.VarTypeSize = LOCATION_SIZES[self.BaseVarType] self.Locations = self.Controller.GetVariableLocationTree() - + # Define Tree item icon list self.TreeImageList = wx.ImageList(16, 16) self.TreeImageDict = {} - + # Icons for items for imgname, itemtype in [ - ("CONFIGURATION", LOCATION_CONFNODE), - ("RESOURCE", LOCATION_MODULE), - ("PROGRAM", LOCATION_GROUP), - ("VAR_INPUT", LOCATION_VAR_INPUT), - ("VAR_OUTPUT", LOCATION_VAR_OUTPUT), - ("VAR_LOCAL", LOCATION_VAR_MEMORY)]: - self.TreeImageDict[itemtype]=self.TreeImageList.Add(GetBitmap(imgname)) - + ("CONFIGURATION", LOCATION_CONFNODE), + ("RESOURCE", LOCATION_MODULE), + ("PROGRAM", LOCATION_GROUP), + ("VAR_INPUT", LOCATION_VAR_INPUT), + ("VAR_OUTPUT", LOCATION_VAR_OUTPUT), + ("VAR_LOCAL", LOCATION_VAR_MEMORY)]: + self.TreeImageDict[itemtype] = self.TreeImageList.Add(GetBitmap(imgname)) + # Assign icon list to TreeCtrls self.LocationsTree.SetImageList(self.TreeImageList) - + # Set a options for the choice for option, filter in GetDirFilterChoiceOptions(): self.DirFilterChoice.Append(_(option)) @@ -143,34 +154,34 @@ self.TypeFilterChoice.Append(_(option)) self.TypeFilterChoice.SetStringSelection(_("All")) self.RefreshFilters() - + self.RefreshLocationsTree() self.Fit() - + def RefreshFilters(self): self.DirFilter = DIRFILTERCHOICE_OPTIONS[self.DirFilterChoice.GetStringSelection()] self.TypeFilter = self.TypeFilterChoice.GetSelection() - + def RefreshLocationsTree(self): root = self.LocationsTree.GetRootItem() if not root.IsOk(): root = self.LocationsTree.AddRoot("") self.GenerateLocationsTreeBranch(root, self.Locations) - + def FilterType(self, location_type, location_size): if self.TypeFilter == 0: return True - + if location_size != self.VarTypeSize: return False - + if self.TypeFilter == 1: return self.Controller.IsOfType(location_type, self.BaseVarType) elif self.TypeFilter == 2: return location_type == self.VarType - + return True - + def GenerateLocationsTreeBranch(self, root, locations): to_delete = [] item, root_cookie = self.LocationsTree.GetFirstChild(root) @@ -194,28 +205,28 @@ item, root_cookie = self.LocationsTree.GetNextChild(root, root_cookie) for item in to_delete: self.LocationsTree.Delete(item) - + def OnLocationsTreeItemActivated(self, event): infos = self.LocationsTree.GetPyData(event.GetItem()) if infos["type"] not in [LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP]: wx.CallAfter(self.EndModal, wx.ID_OK) event.Skip() - + def OnFilterChoice(self, event): self.RefreshFilters() self.RefreshLocationsTree() - + def GetValues(self): selected = self.LocationsTree.GetSelection() return self.LocationsTree.GetPyData(selected) - + def OnOK(self, event): selected = self.LocationsTree.GetSelection() var_infos = None if selected.IsOk(): var_infos = self.LocationsTree.GetPyData(selected) if var_infos is None or var_infos["type"] in [LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP]: - dialog = wx.MessageDialog(self, _("A location must be selected!"), _("Error"), wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self, _("A location must be selected!"), _("Error"), wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() else: diff -r 31e63e25b4cc -r 64beb9e9c749 dialogs/BrowseValuesLibraryDialog.py --- a/dialogs/BrowseValuesLibraryDialog.py Mon Aug 21 20:17:19 2017 +0000 +++ b/dialogs/BrowseValuesLibraryDialog.py Mon Aug 21 23:22:58 2017 +0300 @@ -29,38 +29,38 @@ class BrowseValuesLibraryDialog(wx.Dialog): """ Modal dialog that helps in selecting predefined XML attributes sets out of hierarchically organized list - """ + """ def __init__(self, parent, name, library, default=None): wx.Dialog.__init__(self, - name='BrowseValueDialog', parent=parent, - style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER, - title=_('Browse %s values library') % name) + name='BrowseValueDialog', parent=parent, + style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, + title=_('Browse %s values library') % name) self.staticText1 = wx.StaticText( label=_('Choose a value for %s:') % name, name='staticText1', parent=self, pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) - + self.ValuesLibrary = wx.TreeCtrl( name='ValuesLibrary', parent=self, pos=wx.Point(0, 0), - size=wx.Size(400, 200), style=wx.TR_HAS_BUTTONS|wx.TR_SINGLE|wx.SUNKEN_BORDER|wx.TR_HIDE_ROOT|wx.TR_LINES_AT_ROOT) - - self.ButtonSizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE) + size=wx.Size(400, 200), style=wx.TR_HAS_BUTTONS | wx.TR_SINGLE | wx.SUNKEN_BORDER | wx.TR_HIDE_ROOT | wx.TR_LINES_AT_ROOT) + + self.ButtonSizer = self.CreateButtonSizer(wx.OK | wx.CANCEL | wx.CENTRE) self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.ButtonSizer.GetAffirmativeButton().GetId()) - + self.flexGridSizer1 = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=10) - - self.flexGridSizer1.AddWindow(self.staticText1, 0, border=20, flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) - self.flexGridSizer1.AddWindow(self.ValuesLibrary, 0, border=20, flag=wx.GROW|wx.LEFT|wx.RIGHT) - self.flexGridSizer1.AddSizer(self.ButtonSizer, 0, border=20, flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) - + + self.flexGridSizer1.AddWindow(self.staticText1, 0, border=20, flag=wx.GROW | wx.TOP | wx.LEFT | wx.RIGHT) + self.flexGridSizer1.AddWindow(self.ValuesLibrary, 0, border=20, flag=wx.GROW | wx.LEFT | wx.RIGHT) + self.flexGridSizer1.AddSizer(self.ButtonSizer, 0, border=20, flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) + self.flexGridSizer1.AddGrowableCol(0) self.flexGridSizer1.AddGrowableRow(1) - + self.SetSizer(self.flexGridSizer1) self.Fit() - + root = self.ValuesLibrary.AddRoot("") self.GenerateValuesLibraryBranch(root, library, default) @@ -80,9 +80,8 @@ def OnOK(self, event): selected = self.ValuesLibrary.GetSelection() if not selected.IsOk() or self.ValuesLibrary.GetPyData(selected) is None: - message = wx.MessageDialog(self, _("No valid value selected!"), _("Error"), wx.OK|wx.ICON_ERROR) + message = wx.MessageDialog(self, _("No valid value selected!"), _("Error"), wx.OK | wx.ICON_ERROR) message.ShowModal() message.Destroy() else: self.EndModal(wx.ID_OK) - diff -r 31e63e25b4cc -r 64beb9e9c749 dialogs/ConnectionDialog.py --- a/dialogs/ConnectionDialog.py Mon Aug 21 20:17:19 2017 +0000 +++ b/dialogs/ConnectionDialog.py Mon Aug 21 23:22:58 2017 +0300 @@ -29,17 +29,17 @@ from graphics.FBD_Objects import FBD_Connector from BlockPreviewDialog import BlockPreviewDialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Set Connection Parameters Dialog -#------------------------------------------------------------------------------- - -""" -Class that implements a dialog for defining parameters of a connection graphic -element -""" +# ------------------------------------------------------------------------------- + class ConnectionDialog(BlockPreviewDialog): - + """ + Class that implements a dialog for defining parameters of a connection graphic + element + """ + def __init__(self, parent, controller, tagname, apply_button=False): """ Constructor @@ -49,49 +49,50 @@ @param apply_button: Enable button for applying connector modification to all connector having the same name in POU (default: False) """ - BlockPreviewDialog.__init__(self, parent, controller, tagname, - title=_('Connection Properties')) - + BlockPreviewDialog.__init__(self, parent, controller, tagname, + title=_('Connection Properties')) + # Init common sizers self._init_sizers(2, 0, 7, 1, 0, None) - + # Create label for connection type type_label = wx.StaticText(self, label=_('Type:')) self.LeftGridSizer.AddWindow(type_label, flag=wx.GROW) - + # Create radio buttons for selecting connection type self.TypeRadioButtons = {} first = True for type, label in [(CONNECTOR, _('Connector')), (CONTINUATION, _('Continuation'))]: - radio_button = wx.RadioButton(self, label=label, - style=(wx.RB_GROUP if first else 0)) + radio_button = wx.RadioButton(self, label=label, + style=(wx.RB_GROUP if first else 0)) radio_button.SetValue(first) self.Bind(wx.EVT_RADIOBUTTON, self.OnTypeChanged, radio_button) self.LeftGridSizer.AddWindow(radio_button, flag=wx.GROW) self.TypeRadioButtons[type] = radio_button first = False - + # Create label for connection name name_label = wx.StaticText(self, label=_('Name:')) self.LeftGridSizer.AddWindow(name_label, flag=wx.GROW) - + # Create text control for defining connection name self.ConnectionName = wx.TextCtrl(self) - self.ConnectionName.SetMinSize(wx.Size(200,-1)) + self.ConnectionName.SetMinSize(wx.Size(200, -1)) self.Bind(wx.EVT_TEXT, self.OnNameChanged, self.ConnectionName) self.LeftGridSizer.AddWindow(self.ConnectionName, flag=wx.GROW) - + # Add preview panel and associated label to sizers - self.Preview.SetMinSize(wx.Size(-1,100)) + self.Preview.SetMinSize(wx.Size(-1, 100)) self.LeftGridSizer.AddWindow(self.PreviewLabel, flag=wx.GROW) self.LeftGridSizer.AddWindow(self.Preview, flag=wx.GROW) - + # Add buttons sizer to sizers - self.MainSizer.AddSizer(self.ButtonSizer, border=20, - flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) + self.MainSizer.AddSizer( + self.ButtonSizer, border=20, + flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) self.ColumnSizer.RemoveSizer(self.RightGridSizer) - + # Add button for applying connection name modification to all connection # of POU if apply_button: @@ -105,10 +106,10 @@ controller.GenerateNewName( tagname, None, "Connection%d", 0)) self.Fit() - + # Connector radio button is default control having keyboard focus self.TypeRadioButtons[CONNECTOR].SetFocus() - + def GetConnectionType(self): """ Return type selected for connection @@ -117,7 +118,7 @@ return (CONNECTOR if self.TypeRadioButtons[CONNECTOR].GetValue() else CONTINUATION) - + def SetValues(self, values): """ Set default connection parameters @@ -125,18 +126,18 @@ """ # For each parameters defined, set corresponding control value for name, value in values.items(): - + # Parameter is connection type if name == "type": self.TypeRadioButtons[value].SetValue(True) - + # Parameter is connection name elif name == "name": self.ConnectionName.SetValue(value) - + # Refresh preview panel self.RefreshPreview() - + def GetValues(self): """ Return connection parameters defined in dialog @@ -154,23 +155,23 @@ @return: True if connection name is valid """ message = None - + # Get connection name typed by user connection_name = self.ConnectionName.GetValue() - + # Test that a name have been defined if connection_name == "": message = _("Form isn't complete. Name must be filled!") - + # If an error have been identify, show error message dialog if message is not None: self.ShowErrorMessage(message) # Test failed return False - + # Return result of element name test return self.TestElementName(connection_name) - + def OnOK(self, event): """ Called when dialog OK button is pressed @@ -206,17 +207,16 @@ """ self.RefreshPreview() event.Skip() - + def RefreshPreview(self): """ Refresh preview panel of graphic element Override BlockPreviewDialog function """ # Set graphic element displayed, creating a FBD connection element - self.Element = FBD_Connector(self.Preview, - self.GetConnectionType(), - self.ConnectionName.GetValue()) - + self.Element = FBD_Connector(self.Preview, + self.GetConnectionType(), + self.ConnectionName.GetValue()) + # Call BlockPreviewDialog function BlockPreviewDialog.RefreshPreview(self) - diff -r 31e63e25b4cc -r 64beb9e9c749 dialogs/DiscoveryDialog.py --- a/dialogs/DiscoveryDialog.py Mon Aug 21 20:17:19 2017 +0000 +++ b/dialogs/DiscoveryDialog.py Mon Aug 21 23:22:58 2017 +0300 @@ -25,70 +25,77 @@ import socket import wx -import wx.lib.mixins.listctrl as listmix +import wx.lib.mixins.listctrl as listmix from util.Zeroconf import * import connectors service_type = '_PYRO._tcp.local.' + class AutoWidthListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin): def __init__(self, parent, id, name, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0): wx.ListCtrl.__init__(self, parent, id, pos, size, style, name=name) listmix.ListCtrlAutoWidthMixin.__init__(self) -[ID_DISCOVERYDIALOG, ID_DISCOVERYDIALOGSTATICTEXT1, - ID_DISCOVERYDIALOGSERVICESLIST, ID_DISCOVERYDIALOGREFRESHBUTTON, - ID_DISCOVERYDIALOGLOCALBUTTON, ID_DISCOVERYDIALOGIPBUTTON, + +[ + ID_DISCOVERYDIALOG, ID_DISCOVERYDIALOGSTATICTEXT1, + ID_DISCOVERYDIALOGSERVICESLIST, ID_DISCOVERYDIALOGREFRESHBUTTON, + ID_DISCOVERYDIALOGLOCALBUTTON, ID_DISCOVERYDIALOGIPBUTTON, ] = [wx.NewId() for _init_ctrls in range(6)] + class DiscoveryDialog(wx.Dialog, listmix.ColumnSorterMixin): - + def _init_coll_MainSizer_Items(self, parent): - parent.AddWindow(self.staticText1, 0, border=20, flag=wx.TOP|wx.LEFT|wx.RIGHT|wx.GROW) - parent.AddWindow(self.ServicesList, 0, border=20, flag=wx.LEFT|wx.RIGHT|wx.GROW) - parent.AddSizer(self.ButtonGridSizer, 0, border=20, flag=wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.GROW) - + parent.AddWindow(self.staticText1, 0, border=20, flag=wx.TOP | wx.LEFT | wx.RIGHT | wx.GROW) + parent.AddWindow(self.ServicesList, 0, border=20, flag=wx.LEFT | wx.RIGHT | wx.GROW) + parent.AddSizer(self.ButtonGridSizer, 0, border=20, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.GROW) + def _init_coll_MainSizer_Growables(self, parent): parent.AddGrowableCol(0) parent.AddGrowableRow(1) - + def _init_coll_ButtonGridSizer_Items(self, parent): parent.AddWindow(self.RefreshButton, 0, border=0, flag=0) parent.AddWindow(self.LocalButton, 0, border=0, flag=0) parent.AddWindow(self.IpButton, 0, border=0, flag=0) parent.AddSizer(self.ButtonSizer, 0, border=0, flag=0) - + def _init_coll_ButtonGridSizer_Growables(self, parent): parent.AddGrowableCol(0) parent.AddGrowableCol(1) parent.AddGrowableRow(0) - + def _init_sizers(self): self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=10) self.ButtonGridSizer = wx.FlexGridSizer(cols=4, hgap=5, rows=1, vgap=0) - + self._init_coll_MainSizer_Items(self.MainSizer) self._init_coll_MainSizer_Growables(self.MainSizer) self._init_coll_ButtonGridSizer_Items(self.ButtonGridSizer) self._init_coll_ButtonGridSizer_Growables(self.ButtonGridSizer) - + self.SetSizer(self.MainSizer) - + def _init_ctrls(self, prnt): - wx.Dialog.__init__(self, id=ID_DISCOVERYDIALOG, - name='DiscoveryDialog', parent=prnt, style=wx.DEFAULT_DIALOG_STYLE, - title=_('Service Discovery')) - - self.staticText1 = wx.StaticText(id=ID_DISCOVERYDIALOGSTATICTEXT1, - label=_('Services available:'), name='staticText1', parent=self, - pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) - + wx.Dialog.__init__( + self, id=ID_DISCOVERYDIALOG, + name='DiscoveryDialog', parent=prnt, style=wx.DEFAULT_DIALOG_STYLE, + title=_('Service Discovery')) + + self.staticText1 = wx.StaticText( + id=ID_DISCOVERYDIALOGSTATICTEXT1, + label=_('Services available:'), name='staticText1', parent=self, + pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) + # Set up list control - self.ServicesList = AutoWidthListCtrl(id=ID_DISCOVERYDIALOGSERVICESLIST, - name='ServicesList', parent=self, pos=wx.Point(0, 0), size=wx.Size(0, 0), - style=wx.LC_REPORT|wx.LC_EDIT_LABELS|wx.LC_SORT_ASCENDING|wx.LC_SINGLE_SEL) + self.ServicesList = AutoWidthListCtrl( + id=ID_DISCOVERYDIALOGSERVICESLIST, + name='ServicesList', parent=self, pos=wx.Point(0, 0), size=wx.Size(0, 0), + style=wx.LC_REPORT | wx.LC_EDIT_LABELS | wx.LC_SORT_ASCENDING | wx.LC_SINGLE_SEL) self.ServicesList.InsertColumn(0, _('NAME')) self.ServicesList.InsertColumn(1, _('TYPE')) self.ServicesList.InsertColumn(2, _('IP')) @@ -100,48 +107,53 @@ self.ServicesList.SetInitialSize(wx.Size(-1, 300)) self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected, id=ID_DISCOVERYDIALOGSERVICESLIST) self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated, id=ID_DISCOVERYDIALOGSERVICESLIST) - + listmix.ColumnSorterMixin.__init__(self, 4) - - self.RefreshButton = wx.Button(id=ID_DISCOVERYDIALOGREFRESHBUTTON, - label=_('Refresh'), name='RefreshButton', parent=self, - pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) + + self.RefreshButton = wx.Button( + id=ID_DISCOVERYDIALOGREFRESHBUTTON, + label=_('Refresh'), name='RefreshButton', parent=self, + pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) self.Bind(wx.EVT_BUTTON, self.OnRefreshButton, id=ID_DISCOVERYDIALOGREFRESHBUTTON) - - self.LocalButton = wx.Button(id=ID_DISCOVERYDIALOGLOCALBUTTON, - label=_('Local'), name='LocalButton', parent=self, - pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) + + self.LocalButton = wx.Button( + id=ID_DISCOVERYDIALOGLOCALBUTTON, + label=_('Local'), name='LocalButton', parent=self, + pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) self.Bind(wx.EVT_BUTTON, self.OnLocalButton, id=ID_DISCOVERYDIALOGLOCALBUTTON) - self.IpButton = wx.Button(id=ID_DISCOVERYDIALOGIPBUTTON, - label=_('Add IP'), name='IpButton', parent=self, - pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) + self.IpButton = wx.Button( + id=ID_DISCOVERYDIALOGIPBUTTON, + label=_('Add IP'), name='IpButton', parent=self, + pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) self.Bind(wx.EVT_BUTTON, self.OnIpButton, id=ID_DISCOVERYDIALOGIPBUTTON) - - self.ButtonSizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTER) - + + self.ButtonSizer = self.CreateButtonSizer(wx.OK | wx.CANCEL | wx.CENTER) + self._init_sizers() self.Fit() - + def __init__(self, parent): self._init_ctrls(parent) - + self.itemDataMap = {} self.nextItemId = 0 - + self.URI = None self.Browser = None - + self.ZeroConfInstance = Zeroconf() self.RefreshList() - self.LatestSelection=None - + self.LatestSelection = None + def __del__(self): - if self.Browser is not None : self.Browser.cancel() + if self.Browser is not None: + self.Browser.cancel() self.ZeroConfInstance.close() - + def RefreshList(self): - if self.Browser is not None : self.Browser.cancel() + if self.Browser is not None: + self.Browser.cancel() self.Browser = ServiceBrowser(self.ZeroConfInstance, service_type, self) def OnRefreshButton(self, event): @@ -154,9 +166,11 @@ event.Skip() def OnIpButton(self, event): + def GetColText(col): + return self.getColumnText(self.LatestSelection, col) + if self.LatestSelection is not None: - l = lambda col : self.getColumnText(self.LatestSelection,col) - self.URI = "%s://%s:%s"%tuple(map(l,(1,2,3))) + self.URI = "%s://%s:%s" % tuple(map(GetColText, (1, 2, 3))) self.EndModal(wx.ID_OK) event.Skip() @@ -181,27 +195,26 @@ # connect_type = self.getColumnText(idx, 1) # connect_address = self.getColumnText(idx, 2) # connect_port = self.getColumnText(idx, 3) -# +# # self.URI = "%s://%s:%s"%(connect_type, connect_address, connect_port) def SetURI(self, idx): self.LatestSelection = idx - svcname = self.getColumnText(idx, 0) + svcname = self.getColumnText(idx, 0) connect_type = self.getColumnText(idx, 1) - self.URI = "%s://%s"%(connect_type, svcname + '.' + service_type) - + self.URI = "%s://%s" % (connect_type, svcname + '.' + service_type) + def GetURI(self): return self.URI - + def removeService(self, zeroconf, _type, name): wx.CallAfter(self._removeService, name) - def _removeService(self, name): ''' called when a service with the desired type goes offline. ''' - + # loop through the list items looking for the service that went offline for idx in xrange(self.ServicesList.GetItemCount()): # this is the unique identifier assigned to the item @@ -213,7 +226,7 @@ if item_name == name: self.ServicesList.DeleteItem(idx) break - + def addService(self, zeroconf, _type, name): wx.CallAfter(self._addService, _type, name) @@ -223,10 +236,10 @@ ''' info = self.ZeroConfInstance.getServiceInfo(_type, name) - svcname = name.split(".")[0] + svcname = name.split(".")[0] typename = _type.split(".")[0][1:] - ip = str(socket.inet_ntoa(info.getAddress())) - port = info.getPort() + ip = str(socket.inet_ntoa(info.getAddress())) + port = info.getPort() num_items = self.ServicesList.GetItemCount() @@ -240,13 +253,12 @@ # we assign every list item a unique id (that won't change when items # are added or removed) self.ServicesList.SetItemData(new_item, self.nextItemId) - + # the value of each column has to be stored in the itemDataMap # so that ColumnSorterMixin knows how to sort the column. # "name" is included at the end so that self.removeService # can access it. - self.itemDataMap[self.nextItemId] = [ svcname, typename, ip, port, name ] + self.itemDataMap[self.nextItemId] = [svcname, typename, ip, port, name] self.nextItemId += 1 - diff -r 31e63e25b4cc -r 64beb9e9c749 dialogs/DurationEditorDialog.py --- a/dialogs/DurationEditorDialog.py Mon Aug 21 20:17:19 2017 +0000 +++ b/dialogs/DurationEditorDialog.py Mon Aug 21 23:22:58 2017 +0300 @@ -27,9 +27,9 @@ import wx -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Helpers -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- MICROSECONDS = 0.001 MILLISECONDS = 1 @@ -49,51 +49,52 @@ ("Microseconds", _('Microseconds:')), ] -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Edit Duration Value Dialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + class DurationEditorDialog(wx.Dialog): def __init__(self, parent): wx.Dialog.__init__(self, parent, title=_('Edit Duration')) - + main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=10) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(0) - + controls_sizer = wx.FlexGridSizer(cols=len(CONTROLS), hgap=10, rows=2, vgap=10) - main_sizer.AddSizer(controls_sizer, border=20, - flag=wx.TOP|wx.LEFT|wx.RIGHT|wx.GROW) - + main_sizer.AddSizer(controls_sizer, border=20, + flag=wx.TOP | wx.LEFT | wx.RIGHT | wx.GROW) + controls = [] for i, (name, label) in enumerate(CONTROLS): controls_sizer.AddGrowableCol(i) - + st = wx.StaticText(self, label=label) txtctrl = wx.TextCtrl(self, value='0', style=wx.TE_PROCESS_ENTER) - self.Bind(wx.EVT_TEXT_ENTER, - self.GetControlValueTestFunction(txtctrl), + self.Bind(wx.EVT_TEXT_ENTER, + self.GetControlValueTestFunction(txtctrl), txtctrl) setattr(self, name, txtctrl) - + controls.append((st, txtctrl)) - + for st, txtctrl in controls: controls_sizer.AddWindow(st, flag=wx.GROW) - + for st, txtctrl in controls: controls_sizer.AddWindow(txtctrl, flag=wx.GROW) - - button_sizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE) + + button_sizer = self.CreateButtonSizer(wx.OK | wx.CANCEL | wx.CENTRE) self.Bind(wx.EVT_BUTTON, self.OnOK, button_sizer.GetAffirmativeButton()) - main_sizer.AddSizer(button_sizer, border=20, - flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) - + main_sizer.AddSizer(button_sizer, border=20, + flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) + self.SetSizer(main_sizer) self.Fit() self.Days.SetFocus() - + def SetDuration(self, value): result = IEC_TIME_MODEL.match(value.upper()) if result is not None: @@ -112,13 +113,13 @@ else: self.Milliseconds.SetValue("0") self.Microseconds.SetValue("0") - + def GetControlValueTestFunction(self, control): def OnValueChanged(event): try: value = float(control.GetValue()) except ValueError, e: - message = wx.MessageDialog(self, _("Invalid value!\nYou must fill a numeric value."), _("Error"), wx.OK|wx.ICON_ERROR) + message = wx.MessageDialog(self, _("Invalid value!\nYou must fill a numeric value."), _("Error"), wx.OK | wx.ICON_ERROR) message.ShowModal() message.Destroy() event.Skip() @@ -130,29 +131,29 @@ for control, factor in [(self.Days, DAY), (self.Hours, HOUR), (self.Minutes, MINUTE), (self.Seconds, SECOND), (self.Milliseconds, MILLISECONDS), (self.Microseconds, MICROSECONDS)]: - + milliseconds += float(control.GetValue()) * factor - + not_null = False duration = "T#" - for value, format in [(int(milliseconds) / DAY, "%dd"), - ((int(milliseconds) % DAY) / HOUR, "%dh"), - ((int(milliseconds) % HOUR) / MINUTE, "%dm"), - ((int(milliseconds) % MINUTE) / SECOND, "%ds")]: - + for value, format in [((int(milliseconds) / DAY), "%dd"), + ((int(milliseconds) % DAY) / HOUR, "%dh"), + ((int(milliseconds) % HOUR) / MINUTE, "%dm"), + ((int(milliseconds) % MINUTE) / SECOND, "%ds")]: + if value > 0 or not_null: duration += format % value not_null = True - + duration += "%gms" % (milliseconds % SECOND) return duration - + def OnOK(self, event): self.OnCloseDialog() def OnCloseDialog(self): errors = [] - for control, name in [(self.Days, _("days")), (self.Hours, _("hours")), + for control, name in [(self.Days, _("days")), (self.Hours, _("hours")), (self.Minutes, _("minutes")), (self.Seconds, _("seconds")), (self.Milliseconds, _("milliseconds"))]: try: @@ -164,7 +165,7 @@ message = _("Field %s hasn't a valid value!") % errors[0] else: message = _("Fields %s haven't a valid value!") % ",".join(errors) - dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() else: diff -r 31e63e25b4cc -r 64beb9e9c749 dialogs/FBDBlockDialog.py --- a/dialogs/FBDBlockDialog.py Mon Aug 21 20:17:19 2017 +0000 +++ b/dialogs/FBDBlockDialog.py Mon Aug 21 23:22:58 2017 +0300 @@ -31,24 +31,25 @@ from controls.LibraryPanel import LibraryPanel from BlockPreviewDialog import BlockPreviewDialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Helpers -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + def GetBlockTypeDefaultNameModel(blocktype): return re.compile("%s[0-9]+" % blocktype if blocktype is not None else ".*") -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Set Block Parameters Dialog -#------------------------------------------------------------------------------- - -""" -Class that implements a dialog for defining parameters of a FBD block graphic -element -""" +# ------------------------------------------------------------------------------- + class FBDBlockDialog(BlockPreviewDialog): - + """ + Class that implements a dialog for defining parameters of a FBD block graphic + element + """ + def __init__(self, parent, controller, tagname): """ Constructor @@ -57,84 +58,84 @@ @param tagname: Tagname of project POU edited """ BlockPreviewDialog.__init__(self, parent, controller, tagname, - title=_('Block Properties')) - + title=_('Block Properties')) + # Init common sizers self._init_sizers(2, 0, 1, 0, 3, 2) - + # Create static box around library panel type_staticbox = wx.StaticBox(self, label=_('Type:')) left_staticboxsizer = wx.StaticBoxSizer(type_staticbox, wx.VERTICAL) self.LeftGridSizer.AddSizer(left_staticboxsizer, border=5, flag=wx.GROW) - + # Create Library panel and add it to static box self.LibraryPanel = LibraryPanel(self) self.LibraryPanel.SetInitialSize(wx.Size(-1, 400)) - + # Set function to call when selection in Library panel changed - setattr(self.LibraryPanel, "_OnTreeItemSelected", - self.OnLibraryTreeItemSelected) - left_staticboxsizer.AddWindow(self.LibraryPanel, 1, border=5, - flag=wx.GROW|wx.TOP) - + setattr(self.LibraryPanel, "_OnTreeItemSelected", + self.OnLibraryTreeItemSelected) + left_staticboxsizer.AddWindow(self.LibraryPanel, 1, border=5, + flag=wx.GROW | wx.TOP) + # Create sizer for other block parameters top_right_gridsizer = wx.FlexGridSizer(cols=2, hgap=0, rows=4, vgap=5) top_right_gridsizer.AddGrowableCol(1) self.RightGridSizer.AddSizer(top_right_gridsizer, flag=wx.GROW) - + # Create label for block name name_label = wx.StaticText(self, label=_('Name:')) - top_right_gridsizer.AddWindow(name_label, - flag=wx.ALIGN_CENTER_VERTICAL) - + top_right_gridsizer.AddWindow(name_label, + flag=wx.ALIGN_CENTER_VERTICAL) + # Create text control for defining block name self.BlockName = wx.TextCtrl(self) self.Bind(wx.EVT_TEXT, self.OnNameChanged, self.BlockName) top_right_gridsizer.AddWindow(self.BlockName, flag=wx.GROW) - + # Create label for extended block input number inputs_label = wx.StaticText(self, label=_('Inputs:')) - top_right_gridsizer.AddWindow(inputs_label, - flag=wx.ALIGN_CENTER_VERTICAL) - + top_right_gridsizer.AddWindow(inputs_label, + flag=wx.ALIGN_CENTER_VERTICAL) + # Create spin control for defining extended block input number self.Inputs = wx.SpinCtrl(self, min=2, max=20, - style=wx.SP_ARROW_KEYS) + style=wx.SP_ARROW_KEYS) self.Bind(wx.EVT_SPINCTRL, self.OnInputsChanged, self.Inputs) top_right_gridsizer.AddWindow(self.Inputs, flag=wx.GROW) - + # Create label for block execution order - execution_order_label = wx.StaticText(self, - label=_('Execution Order:')) - top_right_gridsizer.AddWindow(execution_order_label, - flag=wx.ALIGN_CENTER_VERTICAL) - + execution_order_label = wx.StaticText(self, + label=_('Execution Order:')) + top_right_gridsizer.AddWindow(execution_order_label, + flag=wx.ALIGN_CENTER_VERTICAL) + # Create spin control for defining block execution order self.ExecutionOrder = wx.SpinCtrl(self, min=0, style=wx.SP_ARROW_KEYS) - self.Bind(wx.EVT_SPINCTRL, self.OnExecutionOrderChanged, + self.Bind(wx.EVT_SPINCTRL, self.OnExecutionOrderChanged, self.ExecutionOrder) top_right_gridsizer.AddWindow(self.ExecutionOrder, flag=wx.GROW) - + # Create label for block execution control - execution_control_label = wx.StaticText(self, - label=_('Execution Control:')) - top_right_gridsizer.AddWindow(execution_control_label, - flag=wx.ALIGN_CENTER_VERTICAL) - + execution_control_label = wx.StaticText(self, + label=_('Execution Control:')) + top_right_gridsizer.AddWindow(execution_control_label, + flag=wx.ALIGN_CENTER_VERTICAL) + # Create check box to enable block execution control self.ExecutionControl = wx.CheckBox(self) - self.Bind(wx.EVT_CHECKBOX, self.OnExecutionOrderChanged, + self.Bind(wx.EVT_CHECKBOX, self.OnExecutionOrderChanged, self.ExecutionControl) top_right_gridsizer.AddWindow(self.ExecutionControl, flag=wx.GROW) - + # Add preview panel and associated label to sizers self.RightGridSizer.AddWindow(self.PreviewLabel, flag=wx.GROW) self.RightGridSizer.AddWindow(self.Preview, flag=wx.GROW) - + # Add buttons sizer to sizers - self.MainSizer.AddSizer(self.ButtonSizer, border=20, - flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) - + self.MainSizer.AddSizer(self.ButtonSizer, border=20, + flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) + # Dictionary containing correspondence between parameter exchanged and # control to fill with parameter value self.ParamsControl = { @@ -142,20 +143,20 @@ "executionOrder": self.ExecutionOrder, "executionControl": self.ExecutionControl } - + # Init controls value and sensibility self.BlockName.SetValue("") self.BlockName.Enable(False) self.Inputs.Enable(False) - + # Variable containing last name typed self.CurrentBlockName = None - + # Refresh Library panel values self.LibraryPanel.SetBlockList(controller.GetBlockTypes(tagname)) self.Fit() self.LibraryPanel.SetFocus() - + def SetValues(self, values): """ Set default block parameters @@ -163,38 +164,38 @@ """ # Extract block type defined in parameters blocktype = values.get("type", None) - - # Select block type in library panel + + # Select block type in library panel if blocktype is not None: - self.LibraryPanel.SelectTreeItem(blocktype, + self.LibraryPanel.SelectTreeItem(blocktype, values.get("inputs", None)) - + # Define regular expression for determine if block name is block # default name default_name_model = GetBlockTypeDefaultNameModel(blocktype) - + # For each parameters defined, set corresponding control value for name, value in values.items(): - + # Parameter is block name if name == "name": if value != "": # Set default graphic element name for testing self.DefaultElementName = value - + # Test if block name is type default block name and save # block name if not (name have been typed by user) if default_name_model.match(value) is None: self.CurrentBlockName = value - + self.BlockName.ChangeValue(value) - + # Set value of other controls else: control = self.ParamsControl.get(name, None) if control is not None: control.SetValue(value) - + # Refresh preview panel self.RefreshPreview() @@ -211,7 +212,7 @@ name: control.GetValue() for name, control in self.ParamsControl.iteritems()}) return values - + def OnOK(self, event): """ Called when dialog OK button is pressed @@ -219,31 +220,31 @@ @param event: wx.Event from OK button """ message = None - + # Get block type selected selected = self.LibraryPanel.GetSelectedBlock() - + # Get block type name and if block is a function block block_name = self.BlockName.GetValue() name_enabled = self.BlockName.IsEnabled() - + # Test that a type has been selected for block if selected is None: message = _("Form isn't complete. Valid block type must be selected!") - + # Test, if block is a function block, that a name have been defined elif name_enabled and block_name == "": message = _("Form isn't complete. Name must be filled!") - + # Show error message if an error is detected if message is not None: self.ShowErrorMessage(message) - + # Test block name validity if necessary elif not name_enabled or self.TestElementName(block_name): # Call BlockPreviewDialog function BlockPreviewDialog.OnOK(self, event) - + def OnLibraryTreeItemSelected(self, event): """ Called when block type selected in library panel @@ -251,12 +252,12 @@ """ # Get type selected in library panel values = self.LibraryPanel.GetSelectedBlock() - + # Get block type informations - blocktype = (self.Controller.GetBlockType(values["type"], + blocktype = (self.Controller.GetBlockType(values["type"], values["inputs"]) if values is not None else None) - + # Set input number spin control according to block type informations if blocktype is not None: self.Inputs.SetValue(len(blocktype["inputs"])) @@ -264,12 +265,12 @@ else: self.Inputs.SetValue(2) self.Inputs.Enable(False) - + # Update block name with default value if block type is a function and # current block name wasn't typed by user if blocktype is not None and blocktype["type"] != "function": self.BlockName.Enable(True) - + if self.CurrentBlockName is None: # Generate new block name according to block type, taking # default element name if it was already a default name for this @@ -277,21 +278,21 @@ default_name_model = GetBlockTypeDefaultNameModel(values["type"]) block_name = ( self.DefaultElementName - if (self.DefaultElementName is not None and + if (self.DefaultElementName is not None and default_name_model.match(self.DefaultElementName)) else self.Controller.GenerateNewName( self.TagName, None, values["type"]+"%d", 0)) else: block_name = self.CurrentBlockName - + self.BlockName.ChangeValue(block_name) else: self.BlockName.Enable(False) self.BlockName.ChangeValue("") - + # Refresh preview panel self.RefreshPreview() - + def OnNameChanged(self, event): """ Called when block name value changed @@ -302,7 +303,7 @@ self.CurrentBlockName = self.BlockName.GetValue() self.RefreshPreview() event.Skip() - + def OnInputsChanged(self, event): """ Called when block inputs number changed @@ -311,7 +312,7 @@ if self.Inputs.IsEnabled(): self.RefreshPreview() event.Skip() - + def OnExecutionOrderChanged(self, event): """ Called when block execution order value changed @@ -319,7 +320,7 @@ """ self.RefreshPreview() event.Skip() - + def OnExecutionControlChanged(self, event): """ Called when block execution control value changed @@ -327,7 +328,7 @@ """ self.RefreshPreview() event.Skip() - + def RefreshPreview(self): """ Refresh preview panel of graphic element @@ -335,22 +336,21 @@ """ # Get type selected in library panel values = self.LibraryPanel.GetSelectedBlock() - + # If a block type is selected in library panel if values is not None: # Set graphic element displayed, creating a FBD block element - self.Element = FBD_Block(self.Preview, values["type"], - (self.BlockName.GetValue() - if self.BlockName.IsEnabled() - else ""), - extension = self.Inputs.GetValue(), - inputs = values["inputs"], - executionControl = self.ExecutionControl.GetValue(), - executionOrder = self.ExecutionOrder.GetValue()) - + self.Element = FBD_Block( + self.Preview, values["type"], + (self.BlockName.GetValue() if self.BlockName.IsEnabled() else ""), + extension=self.Inputs.GetValue(), + inputs=values["inputs"], + executionControl=self.ExecutionControl.GetValue(), + executionOrder=self.ExecutionOrder.GetValue()) + # Reset graphic element displayed else: - self.Element = None - + self.Element = None + # Call BlockPreviewDialog function BlockPreviewDialog.RefreshPreview(self) diff -r 31e63e25b4cc -r 64beb9e9c749 dialogs/FBDVariableDialog.py --- a/dialogs/FBDVariableDialog.py Mon Aug 21 20:17:19 2017 +0000 +++ b/dialogs/FBDVariableDialog.py Mon Aug 21 23:22:58 2017 +0300 @@ -29,28 +29,31 @@ from graphics.FBD_Objects import FBD_Variable from BlockPreviewDialog import BlockPreviewDialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Helpers -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Dictionaries containing correspondence between variable block class and string # to be shown in Class combo box in both sense -VARIABLE_CLASSES_DICT = {INPUT : _("Input"), - INOUT : _("InOut"), - OUTPUT : _("Output")} +VARIABLE_CLASSES_DICT = { + INPUT: _("Input"), + INOUT: _("InOut"), + OUTPUT: _("Output") +} + VARIABLE_CLASSES_DICT_REVERSE = dict( [(value, key) for key, value in VARIABLE_CLASSES_DICT.iteritems()]) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Set Variable Parameters Dialog -#------------------------------------------------------------------------------- - -""" -Class that implements a dialog for defining parameters of a FBD variable graphic -element -""" +# ------------------------------------------------------------------------------- + class FBDVariableDialog(BlockPreviewDialog): + """ + Class that implements a dialog for defining parameters of a FBD variable graphic + element + """ def __init__(self, parent, controller, tagname, exclude_input=False): """ @@ -61,70 +64,71 @@ @param exclude_input: Exclude input from variable class selection """ BlockPreviewDialog.__init__(self, parent, controller, tagname, - title=_('Variable Properties')) - + title=_('Variable Properties')) + # Init common sizers self._init_sizers(4, 2, 4, None, 3, 2) - + # Create label for variable class class_label = wx.StaticText(self, label=_('Class:')) self.LeftGridSizer.AddWindow(class_label, flag=wx.GROW) - + # Create a combo box for defining variable class self.Class = wx.ComboBox(self, style=wx.CB_READONLY) self.Bind(wx.EVT_COMBOBOX, self.OnClassChanged, self.Class) self.LeftGridSizer.AddWindow(self.Class, flag=wx.GROW) - + # Create label for variable execution order - execution_order_label = wx.StaticText(self, - label=_('Execution Order:')) + execution_order_label = wx.StaticText(self, + label=_('Execution Order:')) self.LeftGridSizer.AddWindow(execution_order_label, flag=wx.GROW) - + # Create spin control for defining variable execution order self.ExecutionOrder = wx.SpinCtrl(self, min=0, style=wx.SP_ARROW_KEYS) - self.Bind(wx.EVT_SPINCTRL, self.OnExecutionOrderChanged, + self.Bind(wx.EVT_SPINCTRL, self.OnExecutionOrderChanged, self.ExecutionOrder) self.LeftGridSizer.AddWindow(self.ExecutionOrder, flag=wx.GROW) - + # Create label for variable expression name_label = wx.StaticText(self, label=_('Expression:')) - self.RightGridSizer.AddWindow(name_label, border=5, - flag=wx.GROW|wx.BOTTOM) - + self.RightGridSizer.AddWindow(name_label, border=5, + flag=wx.GROW | wx.BOTTOM) + # Create text control for defining variable expression self.Expression = wx.TextCtrl(self) self.Bind(wx.EVT_TEXT, self.OnExpressionChanged, self.Expression) self.RightGridSizer.AddWindow(self.Expression, flag=wx.GROW) - + # Create a list box to selected variable expression in the list of # variables defined in POU - self.VariableName = wx.ListBox(self, size=wx.Size(-1,120), - style=wx.LB_SINGLE|wx.LB_SORT) + self.VariableName = wx.ListBox(self, size=wx.Size(-1, 120), + style=wx.LB_SINGLE | wx.LB_SORT) self.Bind(wx.EVT_LISTBOX, self.OnNameChanged, self.VariableName) - self.RightGridSizer.AddWindow(self.VariableName, border=4, flag=wx.GROW|wx.TOP) - + self.RightGridSizer.AddWindow(self.VariableName, border=4, flag=wx.GROW | wx.TOP) + # Add preview panel and associated label to sizers self.MainSizer.AddWindow(self.PreviewLabel, border=20, - flag=wx.GROW|wx.LEFT|wx.RIGHT) + flag=wx.GROW | wx.LEFT | wx.RIGHT) self.MainSizer.AddWindow(self.Preview, border=20, - flag=wx.GROW|wx.LEFT|wx.RIGHT) - + flag=wx.GROW | wx.LEFT | wx.RIGHT) + # Add buttons sizer to sizers - self.MainSizer.AddSizer(self.ButtonSizer, border=20, - flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) - + self.MainSizer.AddSizer( + self.ButtonSizer, border=20, + flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) + # Set options that can be selected in class combo box for var_class, choice in VARIABLE_CLASSES_DICT.iteritems(): if not exclude_input or var_class != INPUT: self.Class.Append(choice) self.Class.SetSelection(0) - + # Extract list of variables defined in POU self.RefreshVariableList() - + # Refresh values in name list box self.RefreshNameList() - + self.Preview.SetInitialSize(wx.Size(-1, 60)) self.Fit() @@ -138,32 +142,31 @@ # Get variable class to select POU variable applicable var_class = VARIABLE_CLASSES_DICT_REVERSE[ self.Class.GetStringSelection()] - + # Refresh names in name list box by selecting variables in POU variables # list that can be applied to variable class self.VariableName.Clear() for name, (var_type, value_type) in self.VariableList.iteritems(): if var_type != "Input" or var_class == INPUT: self.VariableName.Append(name) - + # Get variable expression and select corresponding value in name list # box if it exists selected = self.Expression.GetValue() - if (selected != "" and - self.VariableName.FindString(selected) != wx.NOT_FOUND): + if selected != "" and self.VariableName.FindString(selected) != wx.NOT_FOUND: self.VariableName.SetStringSelection(selected) else: self.VariableName.SetSelection(wx.NOT_FOUND) - + # Disable name list box if no name present inside self.VariableName.Enable(self.VariableName.GetCount() > 0) - + def SetValues(self, values): """ Set default variable parameters @param values: Variable parameters values """ - + # Get class parameter value var_class = values.get("class", None) if var_class is not None: @@ -171,10 +174,10 @@ self.Class.SetStringSelection(VARIABLE_CLASSES_DICT[var_class]) # Refresh names in name list box according to var class self.RefreshNameList() - + # For each parameters defined, set corresponding control value for name, value in values.items(): - + # Parameter is variable expression if name == "expression": # Set expression text control value @@ -184,15 +187,15 @@ self.VariableName.SetStringSelection(value) else: self.VariableName.SetSelection(wx.NOT_FOUND) - + # Parameter is variable execution order elif name == "executionOrder": self.ExecutionOrder.SetValue(value) - + # Refresh preview panel self.RefreshPreview() self.Fit() - + def GetValues(self): """ Return block parameters defined in dialog @@ -215,16 +218,16 @@ @param event: wx.Event from OK button """ message = None - + # Test that an expression have been selected or typed by user value = self.Expression.GetValue() if value == "": message = _("At least a variable or an expression must be selected!") - + # Show error message if an error is detected if message is not None: self.ShowErrorMessage(message) - + else: # Call BlockPreviewDialog function BlockPreviewDialog.OnOK(self, event) @@ -236,7 +239,7 @@ """ # Refresh name list box values self.RefreshNameList() - + self.RefreshPreview() event.Skip() @@ -249,10 +252,10 @@ # list box if value selected is valid if self.VariableName.GetSelection() != wx.NOT_FOUND: self.Expression.ChangeValue(self.VariableName.GetStringSelection()) - - self.RefreshPreview() - event.Skip() - + + self.RefreshPreview() + event.Skip() + def OnExpressionChanged(self, event): """ Called when expression text control is changed by user @@ -261,10 +264,10 @@ # Select the corresponding value in name list box if it exists self.VariableName.SetSelection( self.VariableName.FindString(self.Expression.GetValue())) - - self.RefreshPreview() - event.Skip() - + + self.RefreshPreview() + event.Skip() + def OnExecutionOrderChanged(self, event): """ Called when block execution control value changed @@ -272,7 +275,7 @@ """ self.RefreshPreview() event.Skip() - + def RefreshPreview(self): """ Refresh preview panel of graphic element @@ -280,16 +283,14 @@ """ # Get expression value to put in FBD variable element name = self.Expression.GetValue() - + # Set graphic element displayed, creating a FBD variable element - self.Element = FBD_Variable(self.Preview, - VARIABLE_CLASSES_DICT_REVERSE[ - self.Class.GetStringSelection()], - name, - self.VariableList.get(name, ("", ""))[1], - executionOrder = self.ExecutionOrder.GetValue()) - + self.Element = FBD_Variable( + self.Preview, + VARIABLE_CLASSES_DICT_REVERSE[self.Class.GetStringSelection()], + name, + self.VariableList.get(name, ("", ""))[1], + executionOrder=self.ExecutionOrder.GetValue()) + # Call BlockPreviewDialog function BlockPreviewDialog.RefreshPreview(self) - - diff -r 31e63e25b4cc -r 64beb9e9c749 dialogs/FindInPouDialog.py --- a/dialogs/FindInPouDialog.py Mon Aug 21 20:17:19 2017 +0000 +++ b/dialogs/FindInPouDialog.py Mon Aug 21 23:22:58 2017 +0300 @@ -26,86 +26,87 @@ import wx from plcopen.plcopen import * + class FindInPouDialog(wx.Dialog): def _init_icon(self, parent): if parent and parent.icon: self.SetIcon(parent.icon) - def __init__(self, parent): - wx.Dialog.__init__(self, parent, title=_("Find"), - style=wx.CAPTION|wx.CLOSE_BOX|wx.CLIP_CHILDREN|wx.RESIZE_BORDER) - + wx.Dialog.__init__( + self, parent, title=_("Find"), + style=wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN | wx.RESIZE_BORDER) + self._init_icon(parent) panel = wx.Panel(self, style=wx.TAB_TRAVERSAL) - + main_sizer = wx.FlexGridSizer(cols=1, hgap=5, rows=2, vgap=5) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(0) - + controls_sizer = wx.BoxSizer(wx.VERTICAL) - main_sizer.AddSizer(controls_sizer, border=20, - flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) - + main_sizer.AddSizer(controls_sizer, border=20, + flag=wx.GROW | wx.TOP | wx.LEFT | wx.RIGHT) + patterns_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=1, vgap=5) patterns_sizer.AddGrowableCol(1) - controls_sizer.AddSizer(patterns_sizer, border=5, flag=wx.GROW|wx.BOTTOM) - + controls_sizer.AddSizer(patterns_sizer, border=5, flag=wx.GROW | wx.BOTTOM) + find_label = wx.StaticText(panel, label=_("Find:")) patterns_sizer.AddWindow(find_label, flag=wx.ALIGN_CENTER_VERTICAL) - + self.FindPattern = wx.TextCtrl(panel) self.Bind(wx.EVT_TEXT, self.OnFindPatternChanged, self.FindPattern) self.Bind(wx.EVT_CHAR_HOOK, self.OnEscapeKey) patterns_sizer.AddWindow(self.FindPattern, flag=wx.GROW) - + params_sizer = wx.BoxSizer(wx.HORIZONTAL) - controls_sizer.AddSizer(params_sizer, border=5, flag=wx.GROW|wx.BOTTOM) - + controls_sizer.AddSizer(params_sizer, border=5, flag=wx.GROW | wx.BOTTOM) + direction_staticbox = wx.StaticBox(panel, label=_("Direction")) direction_staticboxsizer = wx.StaticBoxSizer( direction_staticbox, wx.VERTICAL) - params_sizer.AddSizer(direction_staticboxsizer, 1, border=5, - flag=wx.GROW|wx.RIGHT) - - self.Forward = wx.RadioButton(panel, label=_("Forward"), - style=wx.RB_GROUP) - direction_staticboxsizer.AddWindow(self.Forward, border=5, - flag=wx.ALL|wx.GROW) - + params_sizer.AddSizer(direction_staticboxsizer, 1, border=5, + flag=wx.GROW | wx.RIGHT) + + self.Forward = wx.RadioButton(panel, label=_("Forward"), + style=wx.RB_GROUP) + direction_staticboxsizer.AddWindow(self.Forward, border=5, + flag=wx.ALL | wx.GROW) + self.Backward = wx.RadioButton(panel, label=_("Backward")) - direction_staticboxsizer.AddWindow(self.Backward, border=5, - flag=wx.ALL|wx.GROW) - + direction_staticboxsizer.AddWindow(self.Backward, border=5, + flag=wx.ALL | wx.GROW) + options_staticbox = wx.StaticBox(panel, label=_("Options")) options_staticboxsizer = wx.StaticBoxSizer( options_staticbox, wx.VERTICAL) params_sizer.AddSizer(options_staticboxsizer, 1, flag=wx.GROW) - + self.CaseSensitive = wx.CheckBox(panel, label=_("Case sensitive")) self.CaseSensitive.SetValue(True) - options_staticboxsizer.AddWindow(self.CaseSensitive, border=5, - flag=wx.ALL|wx.GROW) - + options_staticboxsizer.AddWindow(self.CaseSensitive, border=5, + flag=wx.ALL | wx.GROW) + self.WrapSearch = wx.CheckBox(panel, label=_("Wrap search")) self.WrapSearch.SetValue(True) - options_staticboxsizer.AddWindow(self.WrapSearch, border=5, - flag=wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.GROW) - + options_staticboxsizer.AddWindow(self.WrapSearch, border=5, + flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.GROW) + self.RegularExpressions = wx.CheckBox(panel, label=_("Regular expressions")) - options_staticboxsizer.AddWindow(self.RegularExpressions, border=5, - flag=wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.GROW) - + options_staticboxsizer.AddWindow(self.RegularExpressions, border=5, + flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.GROW) + buttons_sizer = wx.BoxSizer(wx.HORIZONTAL) - main_sizer.AddSizer(buttons_sizer, border=20, - flag=wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.ALIGN_RIGHT) - + main_sizer.AddSizer(buttons_sizer, border=20, + flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.ALIGN_RIGHT) + self.FindButton = wx.Button(panel, label=_("Find")) self.FindButton.SetDefault() self.Bind(wx.EVT_BUTTON, self.OnFindButton, self.FindButton) buttons_sizer.AddWindow(self.FindButton, border=5, flag=wx.RIGHT) - + self.CloseButton = wx.Button(panel, label=_("Close")) self.Bind(wx.EVT_BUTTON, self.OnCloseButton, self.CloseButton) buttons_sizer.AddWindow(self.CloseButton) @@ -113,32 +114,31 @@ # set the longest message here, to use it length to calculate # optimal size of dialog window self.RegExpSyntaxErrMsg = _("Syntax error in regular expression of pattern to search!") - self.StatusLabel = wx.StaticText(panel, label= self.RegExpSyntaxErrMsg) + self.StatusLabel = wx.StaticText(panel, label=self.RegExpSyntaxErrMsg) controls_sizer.AddWindow(self.StatusLabel, flag=wx.ALIGN_CENTER_VERTICAL) - + panel.SetSizer(main_sizer) main_sizer.Fit(self) # clear message after dialog size calculation self.SetStatusText("") - self.ParentWindow = parent - + self.Bind(wx.EVT_CLOSE, self.OnCloseFrame) self.infosPrev = {} self.criteria = {} self.FindPattern.SetFocus() self.RefreshButtonsState() - + def RefreshButtonsState(self): find_pattern = self.FindPattern.GetValue() self.FindButton.Enable(find_pattern != "") - + def OnCloseFrame(self, event): self.Hide() event.Veto() - + def OnCloseButton(self, event): self.Hide() event.Skip() @@ -157,7 +157,7 @@ def SetStatusText(self, msg): self.StatusLabel.SetLabel(msg) self.Layout() - + def OnFindButton(self, event): infos = { "find_pattern": self.FindPattern.GetValue(), @@ -172,12 +172,12 @@ try: self.criteria = infos CompilePattern(self.criteria) - except: + except Exception: self.criteria.clear() message = self.RegExpSyntaxErrMsg self.SetStatusText(message) if len(self.criteria) > 0: wx.CallAfter(self.ParentWindow.FindInPou, - {True: 1, False:-1}[self.Forward.GetValue()], - self.criteria) + {True: 1, False: -1}[self.Forward.GetValue()], + self.criteria) event.Skip() diff -r 31e63e25b4cc -r 64beb9e9c749 dialogs/ForceVariableDialog.py --- a/dialogs/ForceVariableDialog.py Mon Aug 21 20:17:19 2017 +0000 +++ b/dialogs/ForceVariableDialog.py Mon Aug 21 23:22:58 2017 +0300 @@ -26,26 +26,29 @@ import wx -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Helpers -#------------------------------------------------------------------------------- - -LOCATIONDATATYPES = {"X" : ["BOOL"], - "B" : ["SINT", "USINT", "BYTE", "STRING"], - "W" : ["INT", "UINT", "WORD", "WSTRING"], - "D" : ["DINT", "UDINT", "REAL", "DWORD"], - "L" : ["LINT", "ULINT", "LREAL", "LWORD"]} +# ------------------------------------------------------------------------------- + +LOCATIONDATATYPES = {"X": ["BOOL"], + "B": ["SINT", "USINT", "BYTE", "STRING"], + "W": ["INT", "UINT", "WORD", "WSTRING"], + "D": ["DINT", "UDINT", "REAL", "DWORD"], + "L": ["LINT", "ULINT", "LREAL", "LWORD"]} + def gen_get_function(f): def get_function(v): try: return f(v) - except: + except Exception: return None return get_function + def gen_get_string(delimiter): STRING_MODEL = re.compile("%(delimiter)s([^%(delimiter)s]*)%(delimiter)s$" % {"delimiter": delimiter}) + def get_string(v): result = STRING_MODEL.match(v) if result is not None: @@ -53,6 +56,7 @@ return None return get_string + getinteger = gen_get_function(int) getfloat = gen_get_function(float) getstring = gen_get_string("'") @@ -68,7 +72,8 @@ IEC_DATETIME_MODEL = re.compile("(?:(?:DT|DATE_AND_TIME)#)?([0-9]{4})-([0-9]{2})-([0-9]{2})-([0-9]{2}):([0-9]{2}):([0-9]{2}(?:\.[0-9]+)?)$") IEC_TIMEOFDAY_MODEL = re.compile("(?:(?:TOD|TIME_OF_DAY)#)?([0-9]{2}):([0-9]{2}):([0-9]{2}(?:\.[0-9]+)?)$") -def gettime(v): + +def gettime(v): result = IEC_TIME_MODEL.match(v.upper()) if result is not None: negative, days, hours, minutes, seconds, milliseconds = result.groups() @@ -87,9 +92,10 @@ if negative is not None: microseconds = -microseconds return datetime.timedelta(microseconds=microseconds) - - else: - return None + + else: + return None + def getdate(v): result = IEC_DATE_MODEL.match(v.upper()) @@ -101,8 +107,9 @@ return None base_date = datetime.datetime(1970, 1, 1) return date - base_date - else: - return None + else: + return None + def getdatetime(v): result = IEC_DATETIME_MODEL.match(v.upper()) @@ -114,8 +121,9 @@ return None base_date = datetime.datetime(1970, 1, 1) return date - base_date - else: - return None + else: + return None + def gettimeofday(v): result = IEC_TIMEOFDAY_MODEL.match(v.upper()) @@ -130,6 +138,7 @@ else: return None + GetTypeValue = {"BOOL": lambda x: {"TRUE": True, "FALSE": False, "0": False, "1": True}.get(x.upper(), None), "SINT": getinteger, "INT": getinteger, @@ -152,38 +161,43 @@ "DT": getdatetime, "TOD": gettimeofday} -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Force Variable Dialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + class ForceVariableDialog(wx.TextEntryDialog): def __init__(self, parent, iec_type, defaultValue=""): - wx.TextEntryDialog.__init__(self, parent, message = _("Forcing Variable Value"), - caption = _("Please enter value for a \"%s\" variable:") % iec_type, defaultValue = defaultValue, - style = wx.OK|wx.CANCEL|wx.CENTRE, pos = wx.DefaultPosition) - - self.IEC_Type = iec_type - - self.Bind(wx.EVT_BUTTON, self.OnOK, - self.GetSizer().GetItem(2).GetSizer().GetItem(1).GetSizer().GetAffirmativeButton()) - self.ValueTextCtrl=self.GetSizer().GetItem(1).GetWindow() + wx.TextEntryDialog.__init__( + self, parent, + message=_("Forcing Variable Value"), + caption=_("Please enter value for a \"%s\" variable:") % iec_type, + defaultValue=defaultValue, + style=wx.OK | wx.CANCEL | wx.CENTRE, pos=wx.DefaultPosition) + + self.IEC_Type = iec_type + + self.Bind(wx.EVT_BUTTON, self.OnOK, + self.GetSizer().GetItem(2).GetSizer().GetItem(1). + GetSizer().GetAffirmativeButton()) + self.ValueTextCtrl = self.GetSizer().GetItem(1).GetWindow() if self.IEC_Type == "BOOL": self.ToggleButton = wx.ToggleButton(self, label=_("Toggle value")) - value=GetTypeValue[self.IEC_Type](defaultValue) + value = GetTypeValue[self.IEC_Type](defaultValue) if value is not None: self.ToggleButton.SetValue(value) - border=self.GetSizer().GetItem(1).GetBorder() + border = self.GetSizer().GetItem(1).GetBorder() self.GetSizer().Insert(before=2, item=self.ToggleButton, border=border, - flag=wx.LEFT|wx.RIGHT|wx.EXPAND) + flag=wx.LEFT | wx.RIGHT | wx.EXPAND) self.Bind(wx.EVT_TOGGLEBUTTON, self.ToggleBoolValue, self.ToggleButton) self.Fit() def ToggleBoolValue(self, event): - value=self.ToggleButton.GetValue() + value = self.ToggleButton.GetValue() self.ValueTextCtrl.SetValue(unicode(value)) def OnOK(self, event): @@ -192,9 +206,9 @@ if value == "": message = _("You must type a value!") elif GetTypeValue[self.IEC_Type](value) is None: - message = _("Invalid value \"{a1}\" for \"{a2}\" variable!").format(a1 = value, a2 = self.IEC_Type) + message = _("Invalid value \"{a1}\" for \"{a2}\" variable!").format(a1=value, a2=self.IEC_Type) if message is not None: - dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() else: diff -r 31e63e25b4cc -r 64beb9e9c749 dialogs/LDElementDialog.py --- a/dialogs/LDElementDialog.py Mon Aug 21 20:17:19 2017 +0000 +++ b/dialogs/LDElementDialog.py Mon Aug 21 23:22:58 2017 +0300 @@ -31,17 +31,17 @@ from graphics.LD_Objects import LD_Contact, LD_Coil from BlockPreviewDialog import BlockPreviewDialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Set Ladder Element Parmeters Dialog -#------------------------------------------------------------------------------- - -""" -Class that implements a dialog for defining parameters of a LD contact or coil -graphic element -""" +# ------------------------------------------------------------------------------- + class LDElementDialog(BlockPreviewDialog): - + """ + Class that implements a dialog for defining parameters of a LD contact or coil + graphic element + """ + def __init__(self, parent, controller, tagname, type): """ Constructor @@ -50,80 +50,81 @@ @param tagname: Tagname of project POU edited @param type: Type of LD element ('contact or 'coil') """ - BlockPreviewDialog.__init__(self, parent, controller, tagname, - title=(_("Edit Contact Values") - if type == "contact" - else _("Edit Coil Values"))) - + BlockPreviewDialog.__init__(self, parent, controller, tagname, + title=(_("Edit Contact Values") + if type == "contact" + else _("Edit Coil Values"))) + # Init common sizers - self._init_sizers(2, 0, - (7 if type == "contact" else 9), None, 2, 1) - + self._init_sizers(2, 0, (7 if type == "contact" else 9), + None, 2, 1) + # Create label for LD element modifier modifier_label = wx.StaticText(self, label=_('Modifier:')) - self.LeftGridSizer.AddWindow(modifier_label, border=5, - flag=wx.GROW|wx.BOTTOM) - + self.LeftGridSizer.AddWindow(modifier_label, border=5, + flag=wx.GROW | wx.BOTTOM) + # Create radio buttons for selecting LD element modifier self.ModifierRadioButtons = {} first = True - element_modifiers = ([CONTACT_NORMAL, CONTACT_REVERSE, + element_modifiers = ([CONTACT_NORMAL, CONTACT_REVERSE, CONTACT_RISING, CONTACT_FALLING] if type == "contact" else [COIL_NORMAL, COIL_REVERSE, COIL_SET, COIL_RESET, COIL_RISING, COIL_FALLING]) - modifiers_label = [_("Normal"), _("Negated")] + \ - ([_("Set"), _("Reset")] if type == "coil" else []) + \ - [_("Rising Edge"), _("Falling Edge")] - + modifiers_label = \ + [_("Normal"), _("Negated")] + \ + ([_("Set"), _("Reset")] if type == "coil" else []) + \ + [_("Rising Edge"), _("Falling Edge")] + for modifier, label in zip(element_modifiers, modifiers_label): - radio_button = wx.RadioButton(self, label=label, - style=(wx.RB_GROUP if first else 0)) + radio_button = wx.RadioButton(self, label=label, + style=(wx.RB_GROUP if first else 0)) radio_button.SetValue(first) self.Bind(wx.EVT_RADIOBUTTON, self.OnModifierChanged, radio_button) self.LeftGridSizer.AddWindow(radio_button, flag=wx.GROW) self.ModifierRadioButtons[modifier] = radio_button first = False - + # Create label for LD element variable element_variable_label = wx.StaticText(self, label=_('Variable:')) self.LeftGridSizer.AddWindow(element_variable_label, border=5, - flag=wx.GROW|wx.TOP) - + flag=wx.GROW | wx.TOP) + # Create a combo box for defining LD element variable self.ElementVariable = wx.ComboBox(self, style=wx.CB_SORT) - self.Bind(wx.EVT_COMBOBOX, self.OnVariableChanged, + self.Bind(wx.EVT_COMBOBOX, self.OnVariableChanged, self.ElementVariable) - self.Bind(wx.EVT_TEXT, self.OnVariableChanged, - self.ElementVariable) + self.Bind(wx.EVT_TEXT, self.OnVariableChanged, + self.ElementVariable) self.LeftGridSizer.AddWindow(self.ElementVariable, border=5, - flag=wx.GROW|wx.TOP) - + flag=wx.GROW | wx.TOP) + # Add preview panel and associated label to sizers self.RightGridSizer.AddWindow(self.PreviewLabel, flag=wx.GROW) self.RightGridSizer.AddWindow(self.Preview, flag=wx.GROW) - + # Add buttons sizer to sizers - self.MainSizer.AddSizer(self.ButtonSizer, border=20, - flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) - + self.MainSizer.AddSizer(self.ButtonSizer, border=20, + flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) + # Save LD element class self.ElementClass = (LD_Contact if type == "contact" else LD_Coil) - + # Extract list of variables defined in POU self.RefreshVariableList() - + # Set values in ElementVariable for name, (var_type, value_type) in self.VariableList.iteritems(): # Only select BOOL variable and avoid input for coil if (type == "contact" or var_type != "Input") and \ value_type == "BOOL": self.ElementVariable.Append(name) - + self.Fit() # Normal radio button is default control having keyboard focus self.ModifierRadioButtons[element_modifiers[0]].SetFocus() - + def GetElementModifier(self): """ Return modifier selected for LD element @@ -143,15 +144,15 @@ """ # For each parameters defined, set corresponding control value for name, value in values.items(): - + # Parameter is LD element variable if name == "variable": self.ElementVariable.SetValue(value) - + # Set value of other controls elif name == "modifier": self.ModifierRadioButtons[value].SetValue(True) - + # Refresh preview panel self.RefreshPreview() @@ -188,20 +189,19 @@ Override BlockPreviewDialog function """ value = self.ElementVariable.GetValue() - + # Set graphic element displayed, creating a LD element self.Element = self.ElementClass( - self.Preview, + self.Preview, self.GetElementModifier(), value) button = self.ButtonSizer.GetAffirmativeButton() button.Enable(value != "") - + # Call BlockPreviewDialog function BlockPreviewDialog.RefreshPreview(self) - + def OnOK(self, event): if self.ElementVariable.GetValue() != "": self.EndModal(wx.ID_OK) - diff -r 31e63e25b4cc -r 64beb9e9c749 dialogs/LDPowerRailDialog.py --- a/dialogs/LDPowerRailDialog.py Mon Aug 21 20:17:19 2017 +0000 +++ b/dialogs/LDPowerRailDialog.py Mon Aug 21 23:22:58 2017 +0300 @@ -29,17 +29,16 @@ from graphics.LD_Objects import LD_PowerRail from BlockPreviewDialog import BlockPreviewDialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Set Ladder Power Rail Parameters Dialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -""" -Class that implements a dialog for defining parameters of a power rail graphic -element -""" class LDPowerRailDialog(BlockPreviewDialog): - + """ + Class that implements a dialog for defining parameters of a power rail graphic + element + """ def __init__(self, parent, controller, tagname): """ Constructor @@ -48,51 +47,52 @@ @param tagname: Tagname of project POU edited """ BlockPreviewDialog.__init__(self, parent, controller, tagname, - title=_('Power Rail Properties')) - + title=_('Power Rail Properties')) + # Init common sizers self._init_sizers(2, 0, 5, None, 2, 1) - + # Create label for connection type type_label = wx.StaticText(self, label=_('Type:')) self.LeftGridSizer.AddWindow(type_label, flag=wx.GROW) - + # Create radio buttons for selecting power rail type self.TypeRadioButtons = {} first = True for type, label in [(LEFTRAIL, _('Left PowerRail')), (RIGHTRAIL, _('Right PowerRail'))]: - radio_button = wx.RadioButton(self, label=label, - style=(wx.RB_GROUP if first else 0)) + radio_button = wx.RadioButton(self, label=label, + style=(wx.RB_GROUP if first else 0)) radio_button.SetValue(first) self.Bind(wx.EVT_RADIOBUTTON, self.OnTypeChanged, radio_button) self.LeftGridSizer.AddWindow(radio_button, flag=wx.GROW) self.TypeRadioButtons[type] = radio_button first = False - + # Create label for power rail pin number pin_number_label = wx.StaticText(self, label=_('Pin number:')) self.LeftGridSizer.AddWindow(pin_number_label, flag=wx.GROW) - + # Create spin control for defining power rail pin number self.PinNumber = wx.SpinCtrl(self, min=1, max=50, - style=wx.SP_ARROW_KEYS) + style=wx.SP_ARROW_KEYS) self.PinNumber.SetValue(1) self.Bind(wx.EVT_SPINCTRL, self.OnPinNumberChanged, self.PinNumber) self.LeftGridSizer.AddWindow(self.PinNumber, flag=wx.GROW) - + # Add preview panel and associated label to sizers self.RightGridSizer.AddWindow(self.PreviewLabel, flag=wx.GROW) self.RightGridSizer.AddWindow(self.Preview, flag=wx.GROW) - + # Add buttons sizer to sizers - self.MainSizer.AddSizer(self.ButtonSizer, border=20, - flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) + self.MainSizer.AddSizer( + self.ButtonSizer, border=20, + flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) self.Fit() - + # Left Power Rail radio button is default control having keyboard focus self.TypeRadioButtons[LEFTRAIL].SetFocus() - + def GetMinElementSize(self): """ Get minimal graphic element size @@ -100,7 +100,7 @@ element defined """ return self.Element.GetMinSize(True) - + def GetPowerRailType(self): """ Return type selected for power rail @@ -109,7 +109,7 @@ return (LEFTRAIL if self.TypeRadioButtons[LEFTRAIL].GetValue() else RIGHTRAIL) - + def SetValues(self, values): """ Set default power rail parameters @@ -117,11 +117,11 @@ """ # For each parameters defined, set corresponding control value for name, value in values.items(): - + # Parameter is power rail type if name == "type": self.TypeRadioButtons[value].SetValue(True) - + # Parameter is power rail pin number elif name == "pin_number": self.PinNumber.SetValue(value) @@ -158,11 +158,11 @@ Refresh preview panel of graphic element Override BlockPreviewDialog function """ - + # Set graphic element displayed, creating a power rail element - self.Element = LD_PowerRail(self.Preview, - self.GetPowerRailType(), - connectors = self.PinNumber.GetValue()) - + self.Element = LD_PowerRail(self.Preview, + self.GetPowerRailType(), + connectors=self.PinNumber.GetValue()) + # Call BlockPreviewDialog function BlockPreviewDialog.RefreshPreview(self) diff -r 31e63e25b4cc -r 64beb9e9c749 dialogs/PouActionDialog.py --- a/dialogs/PouActionDialog.py Mon Aug 21 20:17:19 2017 +0000 +++ b/dialogs/PouActionDialog.py Mon Aug 21 23:22:58 2017 +0300 @@ -26,55 +26,60 @@ import wx from plcopen.structures import TestIdentifier, IEC_KEYWORDS +from util.TranslationCatalogs import NoTranslate + def GetActionLanguages(): - _ = lambda x : x + _ = NoTranslate return [_("IL"), _("ST"), _("LD"), _("FBD")] + + ACTION_LANGUAGES_DICT = dict([(_(language), language) for language in GetActionLanguages()]) + class PouActionDialog(wx.Dialog): - + def __init__(self, parent): wx.Dialog.__init__(self, parent, title=_('Create a new action')) - + main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=10) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(0) - + infos_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=3, vgap=15) infos_sizer.AddGrowableCol(1) - main_sizer.AddSizer(infos_sizer, border=20, - flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) - + main_sizer.AddSizer(infos_sizer, border=20, + flag=wx.GROW | wx.TOP | wx.LEFT | wx.RIGHT) + actionname_label = wx.StaticText(self, label=_('Action Name:')) - infos_sizer.AddWindow(actionname_label, border=4, - flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP) - - self.ActionName = wx.TextCtrl(self, size=wx.Size(180,-1)) + infos_sizer.AddWindow(actionname_label, border=4, + flag=wx.ALIGN_CENTER_VERTICAL | wx.TOP) + + self.ActionName = wx.TextCtrl(self, size=wx.Size(180, -1)) infos_sizer.AddWindow(self.ActionName, flag=wx.GROW) - + language_label = wx.StaticText(self, label=_('Language:')) - infos_sizer.AddWindow(language_label, border=4, - flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP) - + infos_sizer.AddWindow(language_label, border=4, + flag=wx.ALIGN_CENTER_VERTICAL | wx.TOP) + self.Language = wx.ComboBox(self, style=wx.CB_READONLY) infos_sizer.AddWindow(self.Language, flag=wx.GROW) - - button_sizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE) - self.Bind(wx.EVT_BUTTON, self.OnOK, - button_sizer.GetAffirmativeButton()) - main_sizer.AddSizer(button_sizer, border=20, - flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) - + + button_sizer = self.CreateButtonSizer(wx.OK | wx.CANCEL | wx.CENTRE) + self.Bind(wx.EVT_BUTTON, self.OnOK, + button_sizer.GetAffirmativeButton()) + main_sizer.AddSizer(button_sizer, border=20, + flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) + self.SetSizer(main_sizer) - + for option in GetActionLanguages(): self.Language.Append(_(option)) - + self.Fit() self.PouNames = [] self.PouElementNames = [] - + def OnOK(self, event): error = [] action_name = self.ActionName.GetValue() @@ -89,9 +94,9 @@ if i == 0: text += item elif i == len(error) - 1: - text += _(" and %s")%item + text += _(" and %s") % item else: - text += _(", %s")%item + text += _(", %s") % item message = _("Form isn't complete. %s must be filled!") % text elif not TestIdentifier(action_name): message = _("\"%s\" is not a valid identifier!") % action_name @@ -102,28 +107,27 @@ elif action_name.upper() in self.PouElementNames: message = _("\"%s\" element for this pou already exists!") % action_name if message is not None: - dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() else: self.EndModal(wx.ID_OK) - + def SetPouNames(self, pou_names): self.PouNames = [pou_name.upper() for pou_name in pou_names] - + def SetPouElementNames(self, element_names): self.PouElementNames = [element_name.upper() for element_name in element_names] - + def SetValues(self, values): for item, value in values.items(): if item == "actionName": self.ActionName.SetValue(value) elif item == "language": self.Language.SetStringSelection(_(value)) - + def GetValues(self): values = {} values["actionName"] = self.ActionName.GetValue() values["language"] = ACTION_LANGUAGES_DICT[self.Language.GetStringSelection()] return values - diff -r 31e63e25b4cc -r 64beb9e9c749 dialogs/PouDialog.py --- a/dialogs/PouDialog.py Mon Aug 21 20:17:19 2017 +0000 +++ b/dialogs/PouDialog.py Mon Aug 21 23:22:58 2017 +0300 @@ -26,16 +26,22 @@ import wx from plcopen.structures import TestIdentifier, IEC_KEYWORDS +from util.TranslationCatalogs import NoTranslate + def GetPouTypes(): - _ = lambda x : x + _ = NoTranslate return [_("function"), _("functionBlock"), _("program")] + + POU_TYPES_DICT = dict([(_(pou_type), pou_type) for pou_type in GetPouTypes()]) + def GetPouLanguages(): - _ = lambda x : x + _ = NoTranslate return [_("IL"), _("ST"), _("LD"), _("FBD"), _("SFC")] + class PouDialog(wx.Dialog): POU_LANGUAGES = GetPouLanguages() @@ -43,8 +49,8 @@ def __init__(self, parent, pou_type=None, type_readonly=False): wx.Dialog.__init__(self, id=-1, parent=parent, - name='PouDialog', title=_('Create a new POU'), - style=wx.DEFAULT_DIALOG_STYLE) + name='PouDialog', title=_('Create a new POU'), + style=wx.DEFAULT_DIALOG_STYLE) main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=10) main_sizer.AddGrowableCol(0) @@ -53,18 +59,18 @@ infos_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=3, vgap=15) infos_sizer.AddGrowableCol(1) main_sizer.AddSizer(infos_sizer, border=20, - flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) + flag=wx.GROW | wx.TOP | wx.LEFT | wx.RIGHT) pouname_label = wx.StaticText(self, label=_('POU Name:')) infos_sizer.AddWindow(pouname_label, border=4, - flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP) + flag=wx.ALIGN_CENTER_VERTICAL | wx.TOP) self.PouName = wx.TextCtrl(self) infos_sizer.AddWindow(self.PouName, flag=wx.GROW) poutype_label = wx.StaticText(self, label=_('POU Type:')) infos_sizer.AddWindow(poutype_label, border=4, - flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP) + flag=wx.ALIGN_CENTER_VERTICAL | wx.TOP) self.PouType = wx.ComboBox(self, style=wx.CB_READONLY) self.Bind(wx.EVT_COMBOBOX, self.OnTypeChanged, self.PouType) @@ -72,15 +78,15 @@ language_label = wx.StaticText(self, label=_('Language:')) infos_sizer.AddWindow(language_label, border=4, - flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP) + flag=wx.ALIGN_CENTER_VERTICAL | wx.TOP) self.Language = wx.ComboBox(self, style=wx.CB_READONLY) infos_sizer.AddWindow(self.Language, flag=wx.GROW) - button_sizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE) + button_sizer = self.CreateButtonSizer(wx.OK | wx.CANCEL | wx.CENTRE) self.Bind(wx.EVT_BUTTON, self.OnOK, button_sizer.GetAffirmativeButton()) main_sizer.AddSizer(button_sizer, border=20, - flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) + flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) self.SetSizer(main_sizer) @@ -111,9 +117,9 @@ if i == 0: text += item elif i == len(error) - 1: - text += _(" and %s")%item + text += _(" and %s") % item else: - text += _(", %s")%item + text += _(", %s") % item message = _("Form isn't complete. %s must be filled!") % text elif not TestIdentifier(pou_name): message = _("\"%s\" is not a valid identifier!") % pou_name @@ -126,13 +132,13 @@ question = True if message is not None: if question: - dialog = wx.MessageDialog(self, message, _("Warning"), wx.YES_NO|wx.ICON_EXCLAMATION) + dialog = wx.MessageDialog(self, message, _("Warning"), wx.YES_NO | wx.ICON_EXCLAMATION) result = dialog.ShowModal() dialog.Destroy() if result == wx.ID_YES: self.EndModal(wx.ID_OK) else: - dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() else: diff -r 31e63e25b4cc -r 64beb9e9c749 dialogs/PouNameDialog.py --- a/dialogs/PouNameDialog.py Mon Aug 21 20:17:19 2017 +0000 +++ b/dialogs/PouNameDialog.py Mon Aug 21 23:22:58 2017 +0300 @@ -24,21 +24,22 @@ import wx -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # POU Name Dialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + class PouNameDialog(wx.TextEntryDialog): - def __init__(self, parent, message, caption = "Please enter text", defaultValue = "", - style = wx.OK|wx.CANCEL|wx.CENTRE, pos = wx.DefaultPosition): + def __init__(self, parent, message, caption="Please enter text", defaultValue="", + style=wx.OK | wx.CANCEL | wx.CENTRE, pos=wx.DefaultPosition): wx.TextEntryDialog.__init__(self, parent, message, caption, defaultValue, style, pos) - + self.PouNames = [] - - self.Bind(wx.EVT_BUTTON, self.OnOK, - self.GetSizer().GetItem(2).GetSizer().GetItem(1).GetSizer().GetAffirmativeButton()) - + + self.Bind(wx.EVT_BUTTON, self.OnOK, + self.GetSizer().GetItem(2).GetSizer().GetItem(1).GetSizer().GetAffirmativeButton()) + def OnOK(self, event): message = None step_name = self.GetSizer().GetItem(1).GetWindow().GetValue() @@ -51,7 +52,7 @@ elif step_name.upper() in self.PouNames: message = _("A POU named \"%s\" already exists!") % step_name if message is not None: - dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() else: @@ -60,4 +61,3 @@ def SetPouNames(self, pou_names): self.PouNames = [pou_name.upper() for pou_name in pou_names] - diff -r 31e63e25b4cc -r 64beb9e9c749 dialogs/PouTransitionDialog.py --- a/dialogs/PouTransitionDialog.py Mon Aug 21 20:17:19 2017 +0000 +++ b/dialogs/PouTransitionDialog.py Mon Aug 21 23:22:58 2017 +0300 @@ -26,57 +26,62 @@ import wx from plcopen.structures import TestIdentifier, IEC_KEYWORDS +from util.TranslationCatalogs import NoTranslate -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # POU Transition Dialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + def GetTransitionLanguages(): - _ = lambda x : x + _ = NoTranslate return [_("IL"), _("ST"), _("LD"), _("FBD")] + + TRANSITION_LANGUAGES_DICT = dict([(_(language), language) for language in GetTransitionLanguages()]) + class PouTransitionDialog(wx.Dialog): - + def __init__(self, parent): wx.Dialog.__init__(self, parent, title=_('Create a new transition')) - + main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=10) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(0) - + infos_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=3, vgap=10) infos_sizer.AddGrowableCol(1) - main_sizer.AddSizer(infos_sizer, border=20, - flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) - + main_sizer.AddSizer(infos_sizer, border=20, + flag=wx.GROW | wx.TOP | wx.LEFT | wx.RIGHT) + transitionname_label = wx.StaticText(self, label=_('Transition Name:')) - infos_sizer.AddWindow(transitionname_label, border=4, - flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP) + infos_sizer.AddWindow(transitionname_label, border=4, + flag=wx.ALIGN_CENTER_VERTICAL | wx.TOP) - self.TransitionName = wx.TextCtrl(self, size=wx.Size(180,-1)) + self.TransitionName = wx.TextCtrl(self, size=wx.Size(180, -1)) infos_sizer.AddWindow(self.TransitionName, flag=wx.GROW) language_label = wx.StaticText(self, label=_('Language:')) - infos_sizer.AddWindow(language_label, border=4, - flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP) - + infos_sizer.AddWindow(language_label, border=4, + flag=wx.ALIGN_CENTER_VERTICAL | wx.TOP) + self.Language = wx.ComboBox(self, style=wx.CB_READONLY) infos_sizer.AddWindow(self.Language, flag=wx.GROW) - button_sizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE) + button_sizer = self.CreateButtonSizer(wx.OK | wx.CANCEL | wx.CENTRE) self.Bind(wx.EVT_BUTTON, self.OnOK, button_sizer.GetAffirmativeButton()) - main_sizer.AddSizer(button_sizer, border=20, flag=wx.ALIGN_RIGHT|wx.BOTTOM) - + main_sizer.AddSizer(button_sizer, border=20, flag=wx.ALIGN_RIGHT | wx.BOTTOM) + self.SetSizer(main_sizer) - + for language in GetTransitionLanguages(): self.Language.Append(_(language)) - + self.Fit() self.PouNames = [] self.PouElementNames = [] - + def OnOK(self, event): error = [] transition_name = self.TransitionName.GetValue() @@ -91,9 +96,9 @@ if i == 0: text += item elif i == len(error) - 1: - text += _(" and %s")%item + text += _(" and %s") % item else: - text += _(", %s")%item + text += _(", %s") % item message = _("Form isn't complete. %s must be filled!") % text elif not TestIdentifier(transition_name): message = _("\"%s\" is not a valid identifier!") % transition_name @@ -104,25 +109,25 @@ elif transition_name.upper() in self.PouElementNames: message = _("\"%s\" element for this pou already exists!") % transition_name if message is not None: - dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() else: self.EndModal(wx.ID_OK) - + def SetPouNames(self, pou_names): self.PouNames = [pou_name.upper() for pou_name in pou_names] - + def SetPouElementNames(self, pou_names): self.PouElementNames = [pou_name.upper() for pou_name in pou_names] - + def SetValues(self, values): for item, value in values.items(): if item == "transitionName": self.TransitionName.SetValue(value) elif item == "language": self.Language.SetSelection(_(value)) - + def GetValues(self): values = {} values["transitionName"] = self.TransitionName.GetValue() diff -r 31e63e25b4cc -r 64beb9e9c749 dialogs/ProjectDialog.py --- a/dialogs/ProjectDialog.py Mon Aug 21 20:17:19 2017 +0000 +++ b/dialogs/ProjectDialog.py Mon Aug 21 23:22:58 2017 +0300 @@ -27,30 +27,33 @@ from controls.ProjectPropertiesPanel import ProjectPropertiesPanel + class ProjectDialog(wx.Dialog): - + def __init__(self, parent, enable_required=True): - wx.Dialog.__init__(self, parent, title=_('Project properties'), - style=wx.DEFAULT_DIALOG_STYLE) - + wx.Dialog.__init__(self, parent, title=_('Project properties'), + style=wx.DEFAULT_DIALOG_STYLE) + main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=10) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(0) - - self.ProjectProperties = ProjectPropertiesPanel(self, - enable_required=enable_required) + + self.ProjectProperties = ProjectPropertiesPanel( + self, + enable_required=enable_required) + main_sizer.AddWindow(self.ProjectProperties, flag=wx.GROW) - - self.ButtonSizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE) - self.Bind(wx.EVT_BUTTON, self.OnOK, + + self.ButtonSizer = self.CreateButtonSizer(wx.OK | wx.CANCEL | wx.CENTRE) + self.Bind(wx.EVT_BUTTON, self.OnOK, self.ButtonSizer.GetAffirmativeButton()) - main_sizer.AddSizer(self.ButtonSizer, border=20, - flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) - + main_sizer.AddSizer(self.ButtonSizer, border=20, + flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) + self.SetSizer(main_sizer) self.ProjectProperties.Fit() self.Fit() - + def OnOK(self, event): values = self.ProjectProperties.GetValues() error = [] @@ -66,12 +69,13 @@ if i == 0: text += item elif i == len(error) - 1: - text += _(" and %s")%item + text += _(" and %s") % item else: - text += ", %s"%item - dialog = wx.MessageDialog(self, - _("Form isn't complete. %s must be filled!") % text, - _("Error"), wx.OK|wx.ICON_ERROR) + text += ", %s" % item + dialog = wx.MessageDialog( + self, + _("Form isn't complete. %s must be filled!") % text, + _("Error"), wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() else: @@ -79,6 +83,6 @@ def SetValues(self, values): self.ProjectProperties.SetValues(values) - + def GetValues(self): return self.ProjectProperties.GetValues() diff -r 31e63e25b4cc -r 64beb9e9c749 dialogs/SFCDivergenceDialog.py --- a/dialogs/SFCDivergenceDialog.py Mon Aug 21 20:17:19 2017 +0000 +++ b/dialogs/SFCDivergenceDialog.py Mon Aug 21 23:22:58 2017 +0300 @@ -30,18 +30,18 @@ from graphics.SFC_Objects import SFC_Divergence from BlockPreviewDialog import BlockPreviewDialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Create New Divergence Dialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -""" -Class that implements a dialog for defining parameters for creating a new -divergence graphic element -""" class SFCDivergenceDialog(BlockPreviewDialog): - - def __init__(self, parent, controller, tagname, poss_div_types = None): + """ + Class that implements a dialog for defining parameters for creating a new + divergence graphic element + """ + + def __init__(self, parent, controller, tagname, poss_div_types=None): """ Constructor @param parent: Parent wx.Window of dialog for modal @@ -50,15 +50,15 @@ @param poss_div_types: Types of divergence that will be available in the dialog window """ BlockPreviewDialog.__init__(self, parent, controller, tagname, - title=_('Create a new divergence or convergence')) - + title=_('Create a new divergence or convergence')) + # Init common sizers self._init_sizers(2, 0, 7, None, 2, 1) - + # Create label for divergence type type_label = wx.StaticText(self, label=_('Type:')) self.LeftGridSizer.AddWindow(type_label, flag=wx.GROW) - + # Create radio buttons for selecting divergence type divergence_buttons = [ (SELECTION_DIVERGENCE, _('Selection Divergence')), @@ -67,47 +67,49 @@ (SIMULTANEOUS_CONVERGENCE, _('Simultaneous Convergence'))] poss_div_btns = [] if poss_div_types is not None: - for val in poss_div_types: - poss_div_btns.append(divergence_buttons[val]) + for val in poss_div_types: + poss_div_btns.append(divergence_buttons[val]) else: poss_div_btns = divergence_buttons self.TypeRadioButtons = {} first = True focusbtn = None for type, label in poss_div_btns: - radio_button = wx.RadioButton(self, label=label, - style=(wx.RB_GROUP if first else 0)) + radio_button = wx.RadioButton(self, label=label, + style=(wx.RB_GROUP if first else 0)) radio_button.SetValue(first) self.Bind(wx.EVT_RADIOBUTTON, self.OnTypeChanged, radio_button) self.LeftGridSizer.AddWindow(radio_button, flag=wx.GROW) self.TypeRadioButtons[type] = radio_button - if first: focusbtn = type + if first: + focusbtn = type first = False # Create label for number of divergence sequences - sequences_label = wx.StaticText(self, - label=_('Number of sequences:')) + sequences_label = wx.StaticText(self, + label=_('Number of sequences:')) self.LeftGridSizer.AddWindow(sequences_label, flag=wx.GROW) - + # Create spin control for defining number of divergence sequences self.Sequences = wx.SpinCtrl(self, min=2, max=20, initial=2) self.Bind(wx.EVT_SPINCTRL, self.OnSequencesChanged, self.Sequences) self.LeftGridSizer.AddWindow(self.Sequences, flag=wx.GROW) - + # Add preview panel and associated label to sizers self.RightGridSizer.AddWindow(self.PreviewLabel, flag=wx.GROW) self.RightGridSizer.AddWindow(self.Preview, flag=wx.GROW) - + # Add buttons sizer to sizers - self.MainSizer.AddSizer(self.ButtonSizer, border=20, - flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) - + self.MainSizer.AddSizer( + self.ButtonSizer, border=20, + flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) + self.Fit() # Selection divergence radio button is default control having keyboard # focus self.TypeRadioButtons[focusbtn].SetFocus() - + def GetMinElementSize(self): """ Get minimal graphic element size @@ -115,7 +117,7 @@ element defined """ return self.Element.GetMinSize(True) - + def GetDivergenceType(self): """ Return type selected for SFC divergence @@ -127,7 +129,7 @@ if control.GetValue(): return type return None - + def GetValues(self): """ Set default SFC divergence parameters @@ -151,17 +153,16 @@ """ self.RefreshPreview() event.Skip() - + def RefreshPreview(self): """ Refresh preview panel of graphic element Override BlockPreviewDialog function """ # Set graphic element displayed, creating a SFC divergence - self.Element = SFC_Divergence(self.Preview, - self.GetDivergenceType(), + self.Element = SFC_Divergence(self.Preview, + self.GetDivergenceType(), self.Sequences.GetValue()) - + # Call BlockPreviewDialog function BlockPreviewDialog.RefreshPreview(self) - diff -r 31e63e25b4cc -r 64beb9e9c749 dialogs/SFCStepDialog.py --- a/dialogs/SFCStepDialog.py Mon Aug 21 20:17:19 2017 +0000 +++ b/dialogs/SFCStepDialog.py Mon Aug 21 23:22:58 2017 +0300 @@ -28,17 +28,17 @@ from graphics.SFC_Objects import SFC_Step from BlockPreviewDialog import BlockPreviewDialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Set SFC Step Parameters Dialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -""" -Class that implements a dialog for defining parameters of a SFC step graphic -element -""" class SFCStepDialog(BlockPreviewDialog): - + """ + Class that implements a dialog for defining parameters of a SFC step graphic + element + """ + def __init__(self, parent, controller, tagname, initial=False): """ Constructor @@ -47,25 +47,25 @@ @param tagname: Tagname of project POU edited @param initial: True if step is initial (default: False) """ - BlockPreviewDialog.__init__(self,parent, controller, tagname, - title=_('Edit Step')) - + BlockPreviewDialog.__init__(self, parent, controller, tagname, + title=_('Edit Step')) + # Init common sizers self._init_sizers(2, 0, 6, None, 2, 1) - + # Create label for SFC step name name_label = wx.StaticText(self, label=_('Name:')) self.LeftGridSizer.AddWindow(name_label, flag=wx.GROW) - + # Create text control for defining SFC step name self.StepName = wx.TextCtrl(self) self.Bind(wx.EVT_TEXT, self.OnNameChanged, self.StepName) self.LeftGridSizer.AddWindow(self.StepName, flag=wx.GROW) - + # Create label for SFC step connectors connectors_label = wx.StaticText(self, label=_('Connectors:')) self.LeftGridSizer.AddWindow(connectors_label, flag=wx.GROW) - + # Create check boxes for defining connectors available on SFC step self.ConnectorsCheckBox = {} for name, label in [("input", _("Input")), @@ -77,27 +77,28 @@ self.Bind(wx.EVT_CHECKBOX, self.OnConnectorsChanged, check_box) self.LeftGridSizer.AddWindow(check_box, flag=wx.GROW) self.ConnectorsCheckBox[name] = check_box - + # Add preview panel and associated label to sizers self.RightGridSizer.AddWindow(self.PreviewLabel, flag=wx.GROW) self.RightGridSizer.AddWindow(self.Preview, flag=wx.GROW) - + # Add buttons sizer to sizers - self.MainSizer.AddSizer(self.ButtonSizer, border=20, - flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) - + self.MainSizer.AddSizer( + self.ButtonSizer, border=20, + flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) + # Save flag that indicates that step is initial self.Initial = initial - + # Set default name for step self.StepName.ChangeValue(controller.GenerateNewName( tagname, None, "Step%d", 0)) self.Fit() - + # Step name text control is default control having keyboard focus self.StepName.SetFocus() - + def SetValues(self, values): """ Set default block parameters @@ -105,20 +106,20 @@ """ # For each parameters defined, set corresponding control value for name, value in values.items(): - + # Parameter is step name if name == "name": self.StepName.ChangeValue(value) - + # Set value of other controls else: control = self.ConnectorsCheckBox.get(name, None) if control is not None: control.SetValue(value) - + # Refresh preview panel self.RefreshPreview() - + def GetValues(self): """ Return step parameters defined in dialog @@ -130,7 +131,7 @@ for name, control in self.ConnectorsCheckBox.iteritems()}) values["width"], values["height"] = self.Element.GetSize() return values - + def OnOK(self, event): """ Called when dialog OK button is pressed @@ -138,23 +139,23 @@ @param event: wx.Event from OK button """ message = None - + # Get step name typed by user step_name = self.StepName.GetValue() - + # Test that a name have been defined if step_name == "": message = _("Form isn't complete. Name must be filled!") - + # If an error have been identify, show error message dialog if message is not None: self.ShowErrorMessage(message) - + # Test step name validity elif self.TestElementName(step_name): # Call BlockPreviewDialog function BlockPreviewDialog.OnOK(self, event) - + def OnConnectorsChanged(self, event): """ Called when a step connector value changed @@ -170,23 +171,23 @@ """ self.RefreshPreview() event.Skip() - + def RefreshPreview(self): """ Refresh preview panel of graphic element Override BlockPreviewDialog function """ # Set graphic element displayed, creating a SFC step element - self.Element = SFC_Step(self.Preview, - self.StepName.GetValue(), + self.Element = SFC_Step(self.Preview, + self.StepName.GetValue(), self.Initial) - + # Update connectors of SFC step element according to check boxes value for name, control in self.ConnectorsCheckBox.iteritems(): if control.IsChecked(): getattr(self.Element, "Add" + name.capitalize())() else: getattr(self.Element, "Remove" + name.capitalize())() - + # Call BlockPreviewDialog function BlockPreviewDialog.RefreshPreview(self) diff -r 31e63e25b4cc -r 64beb9e9c749 dialogs/SFCStepNameDialog.py --- a/dialogs/SFCStepNameDialog.py Mon Aug 21 20:17:19 2017 +0000 +++ b/dialogs/SFCStepNameDialog.py Mon Aug 21 23:22:58 2017 +0300 @@ -24,23 +24,24 @@ import wx -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Edit Step Name Dialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + class SFCStepNameDialog(wx.TextEntryDialog): - def __init__(self, parent, message, caption = "Please enter text", defaultValue = "", - style = wx.OK|wx.CANCEL|wx.CENTRE, pos = wx.DefaultPosition): + def __init__(self, parent, message, caption="Please enter text", defaultValue="", + style=wx.OK | wx.CANCEL | wx.CENTRE, pos=wx.DefaultPosition): wx.TextEntryDialog.__init__(self, parent, message, caption, defaultValue, style, pos) - + self.PouNames = [] self.Variables = [] self.StepNames = [] - - self.Bind(wx.EVT_BUTTON, self.OnOK, - self.GetSizer().GetItem(2).GetSizer().GetItem(1).GetSizer().GetAffirmativeButton()) - + + self.Bind(wx.EVT_BUTTON, self.OnOK, + self.GetSizer().GetItem(2).GetSizer().GetItem(1).GetSizer().GetAffirmativeButton()) + def OnOK(self, event): message = None step_name = self.GetSizer().GetItem(1).GetWindow().GetValue() @@ -57,7 +58,7 @@ elif step_name.upper() in self.StepNames: message = _("\"%s\" step already exists!") % step_name if message is not None: - dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() else: diff -r 31e63e25b4cc -r 64beb9e9c749 dialogs/SFCTransitionDialog.py --- a/dialogs/SFCTransitionDialog.py Mon Aug 21 20:17:19 2017 +0000 +++ b/dialogs/SFCTransitionDialog.py Mon Aug 21 23:22:58 2017 +0300 @@ -28,17 +28,17 @@ from graphics.SFC_Objects import SFC_Transition from BlockPreviewDialog import BlockPreviewDialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Set Transition Parameters Dialog -#------------------------------------------------------------------------------- - -""" -Class that implements a dialog for defining parameters of a transition graphic -element -""" +# ------------------------------------------------------------------------------- + class SFCTransitionDialog(BlockPreviewDialog): - + """ + Class that implements a dialog for defining parameters of a transition graphic + element + """ + def __init__(self, parent, controller, tagname, connection=True): """ Constructor @@ -49,34 +49,34 @@ connection (default: True) """ BlockPreviewDialog.__init__(self, parent, controller, tagname, - title=_('Edit transition')) - + title=_('Edit transition')) + # Init common sizers self._init_sizers(2, 0, 8, None, 2, 1) - + # Create label for transition type type_label = wx.StaticText(self, label=_('Type:')) self.LeftGridSizer.AddWindow(type_label, flag=wx.GROW) - + # Create combo box for selecting reference value reference = wx.ComboBox(self, style=wx.CB_READONLY) reference.Append("") for transition in controller.GetEditedElementTransitions(tagname): reference.Append(transition) self.Bind(wx.EVT_COMBOBOX, self.OnReferenceChanged, reference) - + # Create Text control for defining inline value inline = wx.TextCtrl(self) self.Bind(wx.EVT_TEXT, self.OnInlineChanged, inline) - + # Create radio buttons for selecting power rail type self.TypeRadioButtons = {} first = True for type, label, control in [('reference', _('Reference'), reference), ('inline', _('Inline'), inline), ('connection', _('Connection'), None)]: - radio_button = wx.RadioButton(self, label=label, - style=(wx.RB_GROUP if first else 0)) + radio_button = wx.RadioButton(self, label=label, + style=(wx.RB_GROUP if first else 0)) radio_button.SetValue(first) self.Bind(wx.EVT_RADIOBUTTON, self.OnTypeChanged, radio_button) self.LeftGridSizer.AddWindow(radio_button, flag=wx.GROW) @@ -85,29 +85,30 @@ self.LeftGridSizer.AddWindow(control, flag=wx.GROW) self.TypeRadioButtons[type] = (radio_button, control) first = False - + # Create label for transition priority priority_label = wx.StaticText(self, label=_('Priority:')) self.LeftGridSizer.AddWindow(priority_label, flag=wx.GROW) - + # Create spin control for defining priority value self.Priority = wx.SpinCtrl(self, min=0, style=wx.SP_ARROW_KEYS) self.Bind(wx.EVT_TEXT, self.OnPriorityChanged, self.Priority) self.LeftGridSizer.AddWindow(self.Priority, flag=wx.GROW) - + # Add preview panel and associated label to sizers self.RightGridSizer.AddWindow(self.PreviewLabel, flag=wx.GROW) self.RightGridSizer.AddWindow(self.Preview, flag=wx.GROW) - + # Add buttons sizer to sizers - self.MainSizer.AddSizer(self.ButtonSizer, border=20, - flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) + self.MainSizer.AddSizer( + self.ButtonSizer, border=20, + flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) self.Fit() - + # Reference radio button is default control having keyboard focus self.TypeRadioButtons["reference"][0].SetFocus() - + def GetTransitionType(self): """ Return type selected for SFC transition and associated value @@ -124,7 +125,7 @@ else: return type, None return None, None - + def SetValues(self, values): """ Set default SFC transition parameters @@ -132,14 +133,14 @@ """ # Extract transition value according to type type_value = values.get("value", None) - + # For each parameters defined, set corresponding control value for name, value in values.items(): - + # Parameter is SFC transition priority if name == "priority": self.Priority.SetValue(values["priority"]) - + # Parameter is SFC transition type elif name == "type": for type, (radio, control) in self.TypeRadioButtons.iteritems(): @@ -152,20 +153,20 @@ control.SetStringSelection(type_value) elif isinstance(control, wx.TextCtrl): control.ChangeValue(type_value) - + # Refresh preview panel self.RefreshPreview() - + def GetValues(self): """ Return SFC transition parameters defined in dialog @return: {parameter_name: parameter_value,...} """ - values = {"priority" : self.Priority.GetValue()} + values = {"priority": self.Priority.GetValue()} values["type"], values["value"] = self.GetTransitionType() values["width"], values["height"] = self.Element.GetSize() return values - + def OnOK(self, event): """ Called when dialog OK button is pressed @@ -173,18 +174,18 @@ @param event: wx.Event from OK button """ message = None - + # Get transition type and value associated type, value = self.GetTransitionType() - + # Test that value associated to type is defined if type != "connection" and value == "": message = _("Form isn't complete. %s must be filled!") % type - + # Show error message if an error is detected if message is not None: self.ShowErrorMessage(message) - + else: # Call BlockPreviewDialog function BlockPreviewDialog.OnOK(self, event) @@ -198,7 +199,7 @@ for type, (radio, control) in self.TypeRadioButtons.iteritems(): if control is not None: control.Enable(radio.GetValue()) - + # Refresh preview panel self.RefreshPreview() event.Skip() @@ -236,6 +237,6 @@ self.Element = SFC_Transition(self.Preview) self.Element.SetType(*self.GetTransitionType()) self.Element.SetPriority(self.Priority.GetValue()) - + # Call BlockPreviewDialog function BlockPreviewDialog.RefreshPreview(self) diff -r 31e63e25b4cc -r 64beb9e9c749 dialogs/SearchInProjectDialog.py --- a/dialogs/SearchInProjectDialog.py Mon Aug 21 20:17:19 2017 +0000 +++ b/dialogs/SearchInProjectDialog.py Mon Aug 21 23:22:58 2017 +0300 @@ -24,72 +24,75 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. import re +import wx from plcopen.plcopen import * -import wx +from util.TranslationCatalogs import NoTranslate -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Search In Project Dialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + def GetElementsChoices(): - _ = lambda x: x - return [("datatype", _("Data Type")), - ("function", _("Function")), - ("functionBlock", _("Function Block")), - ("program", _("Program")), + _ = NoTranslate + return [("datatype", _("Data Type")), + ("function", _("Function")), + ("functionBlock", _("Function Block")), + ("program", _("Program")), ("configuration", _("Configuration"))] + class SearchInProjectDialog(wx.Dialog): - + def __init__(self, parent): wx.Dialog.__init__(self, parent, title=_('Search in Project')) - + main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=10) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(1) - + pattern_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=5) pattern_sizer.AddGrowableCol(0) - main_sizer.AddSizer(pattern_sizer, border=20, - flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) - + main_sizer.AddSizer(pattern_sizer, border=20, + flag=wx.GROW | wx.TOP | wx.LEFT | wx.RIGHT) + pattern_label = wx.StaticText(self, label=_('Pattern to search:')) pattern_sizer.AddWindow(pattern_label, flag=wx.ALIGN_BOTTOM) - + self.CaseSensitive = wx.CheckBox(self, label=_('Case sensitive')) pattern_sizer.AddWindow(self.CaseSensitive, flag=wx.GROW) - - self.Pattern = wx.TextCtrl(self, size=wx.Size(250,-1)) + + self.Pattern = wx.TextCtrl(self, size=wx.Size(250, -1)) self.Bind(wx.EVT_TEXT, self.FindPatternChanged, self.Pattern) pattern_sizer.AddWindow(self.Pattern, flag=wx.GROW) self.Bind(wx.EVT_CHAR_HOOK, self.OnEscapeKey) self.RegularExpression = wx.CheckBox(self, label=_('Regular expression')) pattern_sizer.AddWindow(self.RegularExpression, flag=wx.GROW) - + scope_staticbox = wx.StaticBox(self, label=_('Scope')) scope_sizer = wx.StaticBoxSizer(scope_staticbox, wx.HORIZONTAL) - main_sizer.AddSizer(scope_sizer, border=20, - flag=wx.GROW|wx.LEFT|wx.RIGHT) - + main_sizer.AddSizer(scope_sizer, border=20, + flag=wx.GROW | wx.LEFT | wx.RIGHT) + scope_selection_sizer = wx.BoxSizer(wx.VERTICAL) - scope_sizer.AddSizer(scope_selection_sizer, 1, border=5, - flag=wx.GROW|wx.TOP|wx.LEFT|wx.BOTTOM) - + scope_sizer.AddSizer(scope_selection_sizer, 1, border=5, + flag=wx.GROW | wx.TOP | wx.LEFT | wx.BOTTOM) + self.WholeProject = wx.RadioButton(self, label=_('Whole Project'), style=wx.RB_GROUP) self.WholeProject.SetValue(True) self.Bind(wx.EVT_RADIOBUTTON, self.OnScopeChanged, self.WholeProject) - scope_selection_sizer.AddWindow(self.WholeProject, border=5, - flag=wx.GROW|wx.BOTTOM) - + scope_selection_sizer.AddWindow(self.WholeProject, border=5, + flag=wx.GROW | wx.BOTTOM) + self.OnlyElements = wx.RadioButton(self, label=_('Only Elements')) self.Bind(wx.EVT_RADIOBUTTON, self.OnScopeChanged, self.OnlyElements) self.OnlyElements.SetValue(False) scope_selection_sizer.AddWindow(self.OnlyElements, flag=wx.GROW) - + self.ElementsList = wx.CheckListBox(self) self.ElementsList.Enable(False) - scope_sizer.AddWindow(self.ElementsList, 1, border=5, - flag=wx.GROW|wx.TOP|wx.RIGHT|wx.BOTTOM) + scope_sizer.AddWindow(self.ElementsList, 1, border=5, + flag=wx.GROW | wx.TOP | wx.RIGHT | wx.BOTTOM) buttons_sizer = wx.BoxSizer(wx.HORIZONTAL) main_sizer.AddSizer(buttons_sizer, border=20, @@ -103,9 +106,9 @@ self.CloseButton = wx.Button(self, label=_("Close")) self.Bind(wx.EVT_BUTTON, self.OnCloseButton, self.CloseButton) buttons_sizer.AddWindow(self.CloseButton) - + self.SetSizer(main_sizer) - + for name, label in GetElementsChoices(): self.ElementsList.Append(_(label)) @@ -139,7 +142,7 @@ self.OnCloseButton(event) else: event.Skip() - + def OnFindButton(self, event): message = None infos = { @@ -160,12 +163,12 @@ self.criteria = infos CompilePattern(self.criteria) self.infosPrev = infos - except: + except Exception: self.criteria.clear() message = _("Syntax error in regular expression of pattern to search!") - + if message is not None: - dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() else: diff -r 31e63e25b4cc -r 64beb9e9c749 doc/conf.py --- a/doc/conf.py Mon Aug 21 20:17:19 2017 +0000 +++ b/doc/conf.py Mon Aug 21 23:22:58 2017 +0300 @@ -11,17 +11,18 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys, os +import sys +import os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) +# sys.path.insert(0, os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +# needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. @@ -34,7 +35,7 @@ source_suffix = '.rst' # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' @@ -54,40 +55,40 @@ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None gettext_compact = False -locale_dirs = [ "locale/" ] +locale_dirs = ["locale/"] # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- @@ -99,26 +100,26 @@ # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, @@ -127,44 +128,44 @@ # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'Beremizdoc' @@ -173,14 +174,14 @@ # -- Options for LaTeX output -------------------------------------------------- latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', + # The paper size ('letterpaper' or 'a4paper'). + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples @@ -192,23 +193,23 @@ # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # -- Options for manual page output -------------------------------------------- @@ -221,7 +222,7 @@ ] # If true, show URL addresses after external links. -#man_show_urls = False +# man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ @@ -236,10 +237,10 @@ ] # Documents to append as an appendix to all manuals. -#texinfo_appendices = [] +# texinfo_appendices = [] # If false, no module index is generated. -#texinfo_domain_indices = True +# texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' +# texinfo_show_urls = 'footnote' diff -r 31e63e25b4cc -r 64beb9e9c749 docutil/dochtml.py --- a/docutil/dochtml.py Mon Aug 21 20:17:19 2017 +0000 +++ b/docutil/dochtml.py Mon Aug 21 23:22:58 2017 +0300 @@ -22,10 +22,14 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import wx, os, wx.html, subprocess +import os +import subprocess +import wx +import wx.html HtmlFrameOpened = [] + def OpenHtmlFrame(self, title, file, size): if title not in HtmlFrameOpened: HtmlFrameOpened.append(title) @@ -35,15 +39,18 @@ window.SetClientSize(size) window.Show() + [ID_HTMLFRAME, ID_HTMLFRAMEHTMLCONTENT] = [wx.NewId() for _init_ctrls in range(2)] EVT_HTML_URL_CLICK = wx.NewId() + class HtmlWindowUrlClick(wx.PyEvent): def __init__(self, linkinfo): wx.PyEvent.__init__(self) self.SetEventType(EVT_HTML_URL_CLICK) self.linkinfo = (linkinfo.GetHref(), linkinfo.GetTarget()) - + + class UrlClickHtmlWindow(wx.html.HtmlWindow): """ HTML window that generates and OnLinkClicked event. @@ -51,47 +58,48 @@ """ def OnLinkClicked(self, linkinfo): wx.PostEvent(self, HtmlWindowUrlClick(linkinfo)) - + def Bind(self, event, handler, source=None, id=wx.ID_ANY, id2=wx.ID_ANY): if event == HtmlWindowUrlClick: self.Connect(-1, -1, EVT_HTML_URL_CLICK, handler) else: wx.html.HtmlWindow.Bind(event, handler, source=source, id=id, id2=id2) + class HtmlFrame(wx.Frame): def _init_ctrls(self, prnt): wx.Frame.__init__(self, id=ID_HTMLFRAME, name='HtmlFrame', - parent=prnt, pos=wx.Point(320, 231), size=wx.Size(853, 616), - style=wx.DEFAULT_FRAME_STYLE, title='') + parent=prnt, pos=wx.Point(320, 231), size=wx.Size(853, 616), + style=wx.DEFAULT_FRAME_STYLE, title='') self.SetIcon(prnt.icon) self.Bind(wx.EVT_CLOSE, self.OnCloseFrame) - + self.HtmlContent = UrlClickHtmlWindow(id=ID_HTMLFRAMEHTMLCONTENT, - name='HtmlContent', parent=self, pos=wx.Point(0, 0), - size=wx.Size(-1, -1), style=wx.html.HW_SCROLLBAR_AUTO|wx.html.HW_NO_SELECTION) + name='HtmlContent', parent=self, pos=wx.Point(0, 0), + size=wx.Size(-1, -1), style=wx.html.HW_SCROLLBAR_AUTO | wx.html.HW_NO_SELECTION) self.HtmlContent.Bind(HtmlWindowUrlClick, self.OnLinkClick) def __init__(self, parent, opened): self._init_ctrls(parent) self.HtmlFrameOpened = opened - + def SetHtmlCode(self, htmlcode): self.HtmlContent.SetPage(htmlcode) - + def SetHtmlPage(self, htmlpage): self.HtmlContent.LoadPage(htmlpage) - + def OnCloseFrame(self, event): self.HtmlFrameOpened.remove(self.GetTitle()) event.Skip() - + def OnLinkClick(self, event): url = event.linkinfo[0] try: if wx.Platform == '__WXMSW__': import webbrowser webbrowser.open(url) - elif subprocess.call("firefox %s"%url, shell=True) != 0: + elif subprocess.call("firefox %s" % url, shell=True) != 0: wx.MessageBox("""Firefox browser not found.\nPlease point your browser at :\n%s""" % url) except ImportError: wx.MessageBox('Please point your browser at: %s' % url) diff -r 31e63e25b4cc -r 64beb9e9c749 docutil/docpdf.py --- a/docutil/docpdf.py Mon Aug 21 20:17:19 2017 +0000 +++ b/docutil/docpdf.py Mon Aug 21 23:22:58 2017 +0300 @@ -22,10 +22,13 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import wx, os +import os +import wx + readerexepath = None - + + def get_acroversion(): " Return version of Adobe Acrobat executable or None" import _winreg @@ -39,38 +42,41 @@ try: res = _winreg.QueryValue(_winreg.HKEY_LOCAL_MACHINE, 'Software\\Adobe\\%s\\%s\\InstallPath' % (key, numver)) return res - except: + except Exception: pass return None -def open_win_pdf(readerexepath, pdffile, pagenum = None): - if pagenum != None : - os.spawnl(os.P_DETACH, readerexepath, "AcroRd32.exe", "/A", "page=%d=OpenActions" % pagenum, '"%s"'%pdffile) + +def open_win_pdf(readerexepath, pdffile, pagenum=None): + if pagenum is not None: + os.spawnl(os.P_DETACH, readerexepath, "AcroRd32.exe", "/A", "page=%d=OpenActions" % pagenum, '"%s"' % pdffile) else: - os.spawnl(os.P_DETACH, readerexepath, "AcroRd32.exe", '"%s"'%pdffile) + os.spawnl(os.P_DETACH, readerexepath, "AcroRd32.exe", '"%s"' % pdffile) -def open_lin_pdf(readerexepath, pdffile, pagenum = None): - if pagenum == None : - os.system("%s -remote DS301 %s &"%(readerexepath, pdffile)) + +def open_lin_pdf(readerexepath, pdffile, pagenum=None): + if pagenum is None: + os.system("%s -remote DS301 %s &" % (readerexepath, pdffile)) else: - print "Open pdf %s at page %d"%(pdffile, pagenum) - os.system("%s -remote DS301 %s %d &"%(readerexepath, pdffile, pagenum)) + print "Open pdf %s at page %d" % (pdffile, pagenum) + os.system("%s -remote DS301 %s %d &" % (readerexepath, pdffile, pagenum)) -def open_pdf(pdffile, pagenum = None): - if wx.Platform == '__WXMSW__' : + +def open_pdf(pdffile, pagenum=None): + if wx.Platform == '__WXMSW__': try: readerpath = get_acroversion() - except: + except Exception: wx.MessageBox("Acrobat Reader is not found or installed !") return None - + readerexepath = os.path.join(readerpath, "AcroRd32.exe") if(os.path.isfile(readerexepath)): open_win_pdf(readerexepath, pdffile, pagenum) else: return None else: - readerexepath = os.path.join("/usr/bin","xpdf") + readerexepath = os.path.join("/usr/bin", "xpdf") if(os.path.isfile(readerexepath)): open_lin_pdf(readerexepath, pdffile, pagenum) else: diff -r 31e63e25b4cc -r 64beb9e9c749 docutil/docsvg.py --- a/docutil/docsvg.py Mon Aug 21 20:17:19 2017 +0000 +++ b/docutil/docsvg.py Mon Aug 21 23:22:58 2017 +0300 @@ -22,41 +22,46 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import wx, os, subprocess +import wx +import os +import subprocess + def get_inkscape_path(): """ Return the Inkscape path """ import _winreg svgexepath = _winreg.QueryValue(_winreg.HKEY_LOCAL_MACHINE, - 'Software\\Classes\\svgfile\\shell\\Inkscape\\command') + 'Software\\Classes\\svgfile\\shell\\Inkscape\\command') svgexepath = svgexepath.replace('"%1"', '') return svgexepath.replace('"', '') + def open_win_svg(svgexepath, svgfile): """ Open Inkscape on Windows platform """ popenargs = [svgexepath] - if svgfile is not None : + if svgfile is not None: popenargs.append(svgfile) subprocess.Popen(popenargs).pid + def open_lin_svg(svgexepath, svgfile): """ Open Inkscape on Linux platform """ if os.path.isfile("/usr/bin/inkscape"): - os.system("%s %s &"%(svgexepath , svgfile)) - + os.system("%s %s &" % (svgexepath, svgfile)) + + def open_svg(svgfile): """ Generic function to open SVG file """ - if wx.Platform == '__WXMSW__' : + if wx.Platform == '__WXMSW__': try: open_win_svg(get_inkscape_path(), svgfile) - except: + except Exception: wx.MessageBox("Inkscape is not found or installed !") return None else: - svgexepath = os.path.join("/usr/bin","inkscape") + svgexepath = os.path.join("/usr/bin", "inkscape") if(os.path.isfile(svgexepath)): open_lin_svg(svgexepath, svgfile) else: wx.MessageBox("Inkscape is not found or installed !") return None - diff -r 31e63e25b4cc -r 64beb9e9c749 editors/CodeFileEditor.py --- a/editors/CodeFileEditor.py Mon Aug 21 20:17:19 2017 +0000 +++ b/editors/CodeFileEditor.py Mon Aug 21 23:22:58 2017 +0300 @@ -37,6 +37,7 @@ from controls.CustomStyledTextCtrl import CustomStyledTextCtrl, faces, GetCursorPos, NAVIGATION_KEYS from controls.VariablePanel import VARIABLE_NAME_SUFFIX_MODEL from graphics.GraphicCommons import ERROR_HIGHLIGHT, SEARCH_RESULT_HIGHLIGHT, REFRESH_HIGHLIGHT_PERIOD +from util.TranslationCatalogs import NoTranslate [STC_CODE_ERROR, STC_CODE_SEARCH_RESULT, STC_CODE_SECTION] = range(15, 18) @@ -48,6 +49,7 @@ EDGE_COLUMN = 80 + class CodeEditor(CustomStyledTextCtrl): KEYWORDS = [] @@ -55,14 +57,14 @@ def __init__(self, parent, window, controler): CustomStyledTextCtrl.__init__(self, parent, -1, wx.DefaultPosition, - wx.Size(-1, 300), 0) + wx.Size(-1, 300), 0) self.SetMarginType(1, stc.STC_MARGIN_NUMBER) self.SetMarginWidth(1, 25) self.SetProperty("fold", "1") self.SetProperty("tab.timmy.whinge.level", "1") - self.SetMargins(0,0) + self.SetMargins(0, 0) self.SetViewWhiteSpace(False) @@ -133,9 +135,10 @@ for section in self.Controler.SECTIONS_NAMES: section_comment = " %s section " % (section) len_headers = EDGE_COLUMN - len(section_comment) - section_comment = self.COMMENT_HEADER * (len_headers / 2) + \ - section_comment + \ - self.COMMENT_HEADER * (len_headers - len_headers / 2) + section_comment = \ + self.COMMENT_HEADER * (len_headers / 2) + \ + section_comment + \ + self.COMMENT_HEADER * (len_headers - len_headers / 2) self.SectionsComments[section] = { "comment": section_comment, @@ -152,7 +155,7 @@ section_infos["comment"] + "(.*)" + section_end, re.DOTALL) - self.SetModEventMask(wx.stc.STC_MOD_BEFOREINSERT|wx.stc.STC_MOD_BEFOREDELETE) + 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) @@ -170,17 +173,17 @@ def OnModification(self, event): if not self.DisableEvents: mod_type = event.GetModificationType() - if not (mod_type&wx.stc.STC_PERFORMED_UNDO or mod_type&wx.stc.STC_PERFORMED_REDO): - if mod_type&wx.stc.STC_MOD_BEFOREINSERT: - if self.CurrentAction == None: + if not (mod_type & wx.stc.STC_PERFORMED_UNDO or mod_type & wx.stc.STC_PERFORMED_REDO): + if mod_type & wx.stc.STC_MOD_BEFOREINSERT: + if self.CurrentAction is None: self.StartBuffering() elif self.CurrentAction[0] != "Add" or self.CurrentAction[1] != event.GetPosition() - 1: self.Controler.EndBuffering() self.StartBuffering() self.CurrentAction = ("Add", event.GetPosition()) wx.CallAfter(self.RefreshModel) - elif mod_type&wx.stc.STC_MOD_BEFOREDELETE: - if self.CurrentAction == None: + elif mod_type & wx.stc.STC_MOD_BEFOREDELETE: + if self.CurrentAction is None: self.StartBuffering() elif self.CurrentAction[0] != "Delete" or self.CurrentAction[1] != event.GetPosition() + 1: self.Controler.EndBuffering() @@ -193,7 +196,7 @@ def OnDoDrop(self, event): try: values = eval(event.GetDragText()) - except: + except Exception: values = event.GetDragText() if isinstance(values, tuple): message = None @@ -226,7 +229,7 @@ self.ParentWindow.RefreshPageTitles() def ResetBuffer(self): - if self.CurrentAction != None: + if self.CurrentAction is not None: self.Controler.EndBuffering() self.CurrentAction = None @@ -258,7 +261,7 @@ self.SetText(new_text) new_cursor_pos = GetCursorPos(old_text, new_text) self.LineScroll(column, line) - if new_cursor_pos != None: + if new_cursor_pos is not None: self.GotoPos(new_cursor_pos) else: self.GotoPos(old_cursor_pos) @@ -381,7 +384,7 @@ if braceAtCaret >= 0: braceOpposite = self.BraceMatch(braceAtCaret) - if braceAtCaret != -1 and braceOpposite == -1: + if braceAtCaret != -1 and braceOpposite == -1: self.BraceBadLight(braceAtCaret) else: self.BraceHighlight(braceAtCaret, braceOpposite) @@ -443,8 +446,6 @@ lineNum = lineNum + 1 - - def Expand(self, line, doExpand, force=False, visLevels=0, level=-1): lastChild = self.GetLastChild(line, level) line = line + 1 @@ -548,9 +549,9 @@ self.RemoveHighlight(*self.CurrentFindHighlight) self.CurrentFindHighlight = None -#------------------------------------------------------------------------------- -# Highlights showing functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Highlights showing functions + # ------------------------------------------------------------------------------- def OnRefreshHighlightsTimer(self, event): self.RefreshView(True) @@ -575,8 +576,8 @@ def RemoveHighlight(self, start, end, highlight_type): highlight_type = HIGHLIGHT_TYPES.get(highlight_type, None) - if (highlight_type is not None and - (start, end, highlight_type) in self.Highlights): + 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) @@ -596,9 +597,9 @@ self.SetStyling(len(self.GetText()) - highlight_end_pos, stc.STC_STYLE_DEFAULT) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Helper for VariablesGrid values -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- class VariablesTable(CustomTable): @@ -660,7 +661,7 @@ ("UpVariableButton", "up", _("Move variable up")), ("DownVariableButton", "down", _("Move variable down"))]: button = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap(bitmap), - size=wx.Size(28, 28), style=wx.NO_BORDER) + 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) @@ -676,8 +677,14 @@ self.ParentWindow = window self.Controler = controler - self.VariablesDefaultValue = {"Name" : "", "Type" : DefaultType, "Initial": "", - "Description":"", "OnChange":"", "Options":""} + self.VariablesDefaultValue = { + "Name": "", + "Type": DefaultType, + "Initial": "", + "Description": "", + "OnChange": "", + "Options": "" + } self.Table = VariablesTable(self, [], self.GetVariableTableColnames()) self.ColAlignements = [wx.ALIGN_RIGHT] + \ [wx.ALIGN_LEFT]*(len(self.VariablesDefaultValue)) @@ -735,8 +742,14 @@ self.Table.ResetView(self.VariablesGrid) def GetVariableTableColnames(self): - _ = lambda x : x - return ["#", _("Name"),_("Type"), _("Initial"), _("Description"), _("OnChange"), _("Options")] + _ = NoTranslate + return ["#", + _("Name"), + _("Type"), + _("Initial"), + _("Description"), + _("OnChange"), + _("Options")] def RefreshModel(self): self.Controler.SetVariables(self.Table.GetData()) @@ -759,7 +772,7 @@ return self.ParentWindow.GetPanelBestSize() def ShowErrorMessage(self, message): - dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() @@ -831,7 +844,7 @@ data_type = self.Table.GetValueByName(row, "Type") var_name = self.Table.GetValueByName(row, "Name") data = wx.TextDataObject(str((var_name, "Global", data_type, - self.Controler.GetCurrentLocation()))) + self.Controler.GetCurrentLocation()))) dragSource = wx.DropSource(self.VariablesGrid) dragSource.SetData(data) dragSource.DoDragDrop() @@ -839,9 +852,9 @@ event.Skip() -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # CodeFileEditor Main Frame Class -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- class CodeFileEditor(ConfTreeNodeEditor): @@ -853,14 +866,15 @@ self.CodeEditorPanel.SetMinimumPaneSize(1) self.VariablesPanel = VariablesEditor(self.CodeEditorPanel, - self.ParentWindow, self.Controler) + self.ParentWindow, + self.Controler) if self.CODE_EDITOR is not None: self.CodeEditor = self.CODE_EDITOR(self.CodeEditorPanel, - self.ParentWindow, self.Controler) + self.ParentWindow, self.Controler) self.CodeEditorPanel.SplitHorizontally(self.VariablesPanel, - self.CodeEditor, 150) + self.CodeEditor, 150) else: self.CodeEditorPanel.Initialize(self.VariablesPanel) @@ -890,4 +904,3 @@ def Find(self, direction, search_params): self.CodeEditor.Find(direction, search_params) - diff -r 31e63e25b4cc -r 64beb9e9c749 editors/ConfTreeNodeEditor.py --- a/editors/ConfTreeNodeEditor.py Mon Aug 21 20:17:19 2017 +0000 +++ b/editors/ConfTreeNodeEditor.py Mon Aug 21 23:22:58 2017 +0300 @@ -37,22 +37,25 @@ from util.BitmapLibrary import GetBitmap if wx.Platform == '__WXMSW__': - faces = { 'times': 'Times New Roman', - 'mono' : 'Courier New', - 'helv' : 'Arial', - 'other': 'Comic Sans MS', - 'size' : 16, - } + faces = { + 'times': 'Times New Roman', + 'mono': 'Courier New', + 'helv': 'Arial', + 'other': 'Comic Sans MS', + 'size': 16, + } else: - faces = { 'times': 'Times', - 'mono' : 'Courier', - 'helv' : 'Helvetica', - 'other': 'new century schoolbook', - 'size' : 18, - } + faces = { + 'times': 'Times', + 'mono': 'Courier', + 'helv': 'Helvetica', + 'other': 'new century schoolbook', + 'size': 18, + } SCROLLBAR_UNIT = 10 + class GenBitmapTextButton(wx.lib.buttons.GenBitmapTextButton): def _GetLabelSize(self): """ used internally """ @@ -71,17 +74,17 @@ def DrawLabel(self, dc, width, height, dw=0, dy=0): bmp = self.bmpLabel - if bmp != None: # if the bitmap is used + if bmp is not None: # if the bitmap is used if self.bmpDisabled and not self.IsEnabled(): bmp = self.bmpDisabled if self.bmpFocus and self.hasFocus: bmp = self.bmpFocus if self.bmpSelected and not self.up: bmp = self.bmpSelected - bw,bh = bmp.GetWidth(), bmp.GetHeight() + bw, bh = bmp.GetWidth(), bmp.GetHeight() if not self.up: dw = dy = self.labelDelta - hasMask = bmp.GetMask() != None + hasMask = bmp.GetMask() is not None else: bw = bh = 0 # no bitmap -> size is zero @@ -98,8 +101,8 @@ pos_x = (width-bw)/2+dw # adjust for bitmap and text to centre pos_y = (height-bh-th)/2+dy - if bmp !=None: - dc.DrawBitmap(bmp, pos_x, pos_y, hasMask) # draw bitmap if available + if bmp is not None: + dc.DrawBitmap(bmp, pos_x, pos_y, hasMask) # draw bitmap if available pos_x = (width-tw)/2+dw # adjust for bitmap and text to centre pos_y += bh + 2 @@ -110,19 +113,20 @@ """ Customized GenStaticBitmap, fix transparency redraw bug on wx2.8/win32, and accept image name as __init__ parameter, fail silently if file do not exist""" def __init__(self, parent, ID, bitmapname, - pos = wx.DefaultPosition, size = wx.DefaultSize, - style = 0, - name = "genstatbmp"): + pos=wx.DefaultPosition, size=wx.DefaultSize, + style=0, + name="genstatbmp"): bitmap = GetBitmap(bitmapname) if bitmap is None: bitmap = wx.EmptyBitmap(0, 0) wx.StaticBitmap.__init__(self, parent, ID, - bitmap, - pos, size, - style, - name) + bitmap, + pos, size, + style, + name) + class ConfTreeNodeEditor(EditorPanel): @@ -137,48 +141,54 @@ if tabs_num > 1 or self.SHOW_BASE_PARAMS: self.Editor = wx.Panel(parent, - style=wx.SUNKEN_BORDER|wx.SP_3D) + style=wx.SUNKEN_BORDER | wx.SP_3D) self.MainSizer = wx.BoxSizer(wx.VERTICAL) if self.SHOW_BASE_PARAMS: baseparamseditor_sizer = wx.BoxSizer(wx.HORIZONTAL) self.MainSizer.AddSizer(baseparamseditor_sizer, border=5, - flag=wx.GROW|wx.ALL) + flag=wx.GROW | wx.ALL) self.FullIECChannel = wx.StaticText(self.Editor, -1) self.FullIECChannel.SetFont( wx.Font(faces["size"], wx.DEFAULT, wx.NORMAL, - wx.BOLD, faceName = faces["helv"])) + wx.BOLD, faceName=faces["helv"])) baseparamseditor_sizer.AddWindow(self.FullIECChannel, - flag=wx.ALIGN_CENTER_VERTICAL) + flag=wx.ALIGN_CENTER_VERTICAL) updownsizer = wx.BoxSizer(wx.VERTICAL) baseparamseditor_sizer.AddSizer(updownsizer, border=5, - flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL) - - self.IECCUpButton = wx.lib.buttons.GenBitmapTextButton(self.Editor, - bitmap=GetBitmap('IECCDown'), size=wx.Size(16, 16), style=wx.NO_BORDER) + flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) + + self.IECCUpButton = wx.lib.buttons.GenBitmapTextButton( + self.Editor, + bitmap=GetBitmap('IECCDown'), + size=wx.Size(16, 16), + style=wx.NO_BORDER) self.IECCUpButton.Bind(wx.EVT_BUTTON, self.GetItemChannelChangedFunction(1), - self.IECCUpButton) + self.IECCUpButton) updownsizer.AddWindow(self.IECCUpButton, flag=wx.ALIGN_LEFT) - self.IECCDownButton = wx.lib.buttons.GenBitmapButton(self.Editor, - bitmap=GetBitmap('IECCUp'), size=wx.Size(16, 16), style=wx.NO_BORDER) + self.IECCDownButton = wx.lib.buttons.GenBitmapButton( + self.Editor, bitmap=GetBitmap('IECCUp'), + size=wx.Size(16, 16), style=wx.NO_BORDER) self.IECCDownButton.Bind(wx.EVT_BUTTON, self.GetItemChannelChangedFunction(-1), - self.IECCDownButton) + self.IECCDownButton) updownsizer.AddWindow(self.IECCDownButton, flag=wx.ALIGN_LEFT) self.ConfNodeName = wx.TextCtrl(self.Editor, - size=wx.Size(150, 25)) + size=wx.Size(150, 25)) self.ConfNodeName.SetFont( wx.Font(faces["size"] * 0.75, wx.DEFAULT, wx.NORMAL, - wx.BOLD, faceName = faces["helv"])) - self.ConfNodeName.Bind(wx.EVT_TEXT, - self.GetTextCtrlCallBackFunction(self.ConfNodeName, "BaseParams.Name", True), - self.ConfNodeName) - baseparamseditor_sizer.AddWindow(self.ConfNodeName, border=5, - flag=wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) + wx.BOLD, faceName=faces["helv"])) + self.ConfNodeName.Bind( + wx.EVT_TEXT, + self.GetTextCtrlCallBackFunction(self.ConfNodeName, "BaseParams.Name", True), + self.ConfNodeName) + baseparamseditor_sizer.AddWindow( + self.ConfNodeName, border=5, + flag=wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL) buttons_sizer = self.GenerateMethodButtonSizer() baseparamseditor_sizer.AddSizer(buttons_sizer, flag=wx.ALIGN_CENTER) @@ -207,11 +217,11 @@ if self.SHOW_PARAMS and len(self.Controler.GetParamsAttributes()) > 0: - panel_style = wx.TAB_TRAVERSAL|wx.HSCROLL|wx.VSCROLL + panel_style = wx.TAB_TRAVERSAL | wx.HSCROLL | wx.VSCROLL if self.ConfNodeNoteBook is None and parent != self.Editor: panel_style |= wx.SUNKEN_BORDER self.ParamsEditor = wx.ScrolledWindow(parent, - style=panel_style) + style=panel_style) self.ParamsEditor.Bind(wx.EVT_SIZE, self.OnParamsEditorResize) self.ParamsEditor.Bind(wx.EVT_SCROLLWIN, self.OnParamsEditorScroll) @@ -222,7 +232,7 @@ self.ConfNodeParamsSizer = wx.BoxSizer(wx.VERTICAL) self.ParamsEditorSizer.AddSizer(self.ConfNodeParamsSizer, border=5, - flag=wx.LEFT|wx.RIGHT|wx.BOTTOM) + flag=wx.LEFT | wx.RIGHT | wx.BOTTOM) self.RefreshConfNodeParamsSizer() @@ -294,16 +304,17 @@ self.Thaw() def GenerateMethodButtonSizer(self): - normal_bt_font=wx.Font(faces["size"] / 3, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = faces["helv"]) - mouseover_bt_font=wx.Font(faces["size"] / 3, wx.DEFAULT, wx.NORMAL, wx.NORMAL, underline=True, faceName = faces["helv"]) + normal_bt_font = wx.Font(faces["size"] / 3, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName=faces["helv"]) + mouseover_bt_font = wx.Font(faces["size"] / 3, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName=faces["helv"], underline=True) msizer = wx.BoxSizer(wx.HORIZONTAL) for confnode_method in self.Controler.ConfNodeMethods: - if "method" in confnode_method and confnode_method.get("shown",True): + if "method" in confnode_method and confnode_method.get("shown", True): button = GenBitmapTextButton(self.Editor, - bitmap=GetBitmap(confnode_method.get("bitmap", "Unknown")), - label=confnode_method["name"], style=wx.NO_BORDER) + bitmap=GetBitmap(confnode_method.get("bitmap", "Unknown")), + label=confnode_method["name"], + style=wx.NO_BORDER) button.SetFont(normal_bt_font) button.SetToolTipString(confnode_method["tooltip"]) if confnode_method.get("push", False): @@ -311,6 +322,7 @@ else: button.Bind(wx.EVT_BUTTON, self.GetButtonCallBackFunction(confnode_method["method"]), button) # a fancy underline on mouseover + def setFontStyle(b, s): def fn(event): b.SetFont(s) @@ -319,19 +331,19 @@ return fn button.Bind(wx.EVT_ENTER_WINDOW, setFontStyle(button, mouseover_bt_font)) button.Bind(wx.EVT_LEAVE_WINDOW, setFontStyle(button, normal_bt_font)) - #hack to force size to mini - if not confnode_method.get("enabled",True): + # hack to force size to mini + if not confnode_method.get("enabled", True): button.Disable() msizer.AddWindow(button, flag=wx.ALIGN_CENTER) return msizer - def GenerateSizerElements(self, sizer, elements, path, clean = True): + def GenerateSizerElements(self, sizer, elements, path, clean=True): if clean: sizer.Clear(True) first = True for element_infos in elements: if path: - element_path = "%s.%s"%(path, element_infos["name"]) + element_path = "%s.%s" % (path, element_infos["name"]) else: element_path = element_infos["name"] if element_infos["type"] == "element": @@ -341,7 +353,7 @@ if value is not None: label += " - %s" % _(value) staticbox = wx.StaticBox(self.ParamsEditor, - label=_(label), size=wx.Size(10, 0)) + label=_(label), size=wx.Size(10, 0)) staticboxsizer = wx.StaticBoxSizer(staticbox, wx.VERTICAL) flags = (wx.GROW | wx.BOTTOM | wx.LEFT | wx.RIGHT) if first: @@ -357,15 +369,16 @@ if first: flags |= wx.TOP sizer.AddSizer(boxsizer, border=5, flag=flags) - staticbitmap = GenStaticBitmap(ID=-1, bitmapname=element_infos["name"], - name="%s_bitmap"%element_infos["name"], parent=self.ParamsEditor, + staticbitmap = GenStaticBitmap( + ID=-1, bitmapname=element_infos["name"], + name="%s_bitmap" % element_infos["name"], parent=self.ParamsEditor, pos=wx.Point(0, 0), size=wx.Size(24, 24), style=0) boxsizer.AddWindow(staticbitmap, border=5, flag=wx.RIGHT) statictext = wx.StaticText(self.ParamsEditor, - label="%s:"%_(element_infos["name"])) + label="%s:" % _(element_infos["name"])) boxsizer.AddWindow(statictext, border=5, - flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT) + flag=wx.ALIGN_CENTER_VERTICAL | wx.RIGHT) if isinstance(element_infos["type"], types.ListType): if isinstance(element_infos["value"], types.TupleType): @@ -373,7 +386,7 @@ boxsizer.AddSizer(browse_boxsizer) textctrl = wx.TextCtrl(self.ParamsEditor, - size=wx.Size(275, -1), style=wx.TE_READONLY) + size=wx.Size(275, -1), style=wx.TE_READONLY) if element_infos["value"] is not None: textctrl.SetValue(element_infos["value"][0]) value_infos = element_infos["value"][1] @@ -389,7 +402,7 @@ button) else: combobox = wx.ComboBox(self.ParamsEditor, - size=wx.Size(300, -1), style=wx.CB_READONLY) + size=wx.Size(300, -1), style=wx.CB_READONLY) boxsizer.AddWindow(combobox) if element_infos["use"] == "optional": @@ -401,7 +414,8 @@ value = element_infos["value"] staticbox = wx.StaticBox(self.ParamsEditor, - label="%s - %s"%(_(name), _(value)), size=wx.Size(10, 0)) + label="%s - %s" % (_(name), _(value)), + size=wx.Size(10, 0)) staticboxsizer = wx.StaticBoxSizer(staticbox, wx.VERTICAL) sizer.AddSizer(staticboxsizer, border=5, flag=wx.GROW | wx.BOTTOM | wx.LEFT | wx.RIGHT) self.GenerateSizerElements(staticboxsizer, element_infos["children"], element_path) @@ -424,7 +438,8 @@ if "max" in element_infos["type"]: scmax = element_infos["type"]["max"] spinctrl = wx.SpinCtrl(self.ParamsEditor, - size=wx.Size(300, -1), style=wx.SP_ARROW_KEYS|wx.ALIGN_RIGHT) + size=wx.Size(300, -1), + style=wx.SP_ARROW_KEYS | wx.ALIGN_RIGHT) spinctrl.SetRange(scmin, scmax) boxsizer.AddWindow(spinctrl) if element_infos["value"] is not None: @@ -443,14 +458,15 @@ self.GetCheckBoxCallBackFunction(checkbox, element_path), checkbox) - elif element_infos["type"] in ["unsignedLong", "long","integer"]: + elif element_infos["type"] in ["unsignedLong", "long", "integer"]: if element_infos["type"].startswith("unsigned"): scmin = 0 else: scmin = -(2**31) scmax = 2**31-1 spinctrl = wx.SpinCtrl(self.ParamsEditor, - size=wx.Size(300, -1), style=wx.SP_ARROW_KEYS|wx.ALIGN_RIGHT) + size=wx.Size(300, -1), + style=wx.SP_ARROW_KEYS | wx.ALIGN_RIGHT) spinctrl.SetRange(scmin, scmax) boxsizer.AddWindow(spinctrl) if element_infos["value"] is not None: @@ -477,7 +493,6 @@ sizer.Layout() self.RefreshScrollbars() - def GetItemChannelChangedFunction(self, dir): def OnConfNodeTreeItemChannelChanged(event): confnode_IECChannel = self.Controler.BaseParams.getIEC_Channel() @@ -500,7 +515,7 @@ # Disable button to prevent re-entrant call event.GetEventObject().Disable() # Call - getattr(self.Controler,method)() + getattr(self.Controler, method)() # Re-enable button event.GetEventObject().Enable() @@ -547,6 +562,7 @@ def GetBrowseCallBackFunction(self, name, textctrl, library, value_infos, path): infos = [value_infos] + def OnBrowseButton(event): dialog = BrowseValuesLibraryDialog(self, name, library, infos[0]) if dialog.ShowModal() == wx.ID_OK: @@ -566,7 +582,8 @@ posy = max(0, min(ystart, (maxy - window_size[1]) / SCROLLBAR_UNIT)) self.ParamsEditor.Scroll(posx, posy) self.ParamsEditor.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT, - maxx / SCROLLBAR_UNIT, maxy / SCROLLBAR_UNIT, posx, posy) + maxx / SCROLLBAR_UNIT, maxy / SCROLLBAR_UNIT, + posx, posy) def OnParamsEditorResize(self, event): self.RefreshScrollbars() @@ -578,4 +595,3 @@ control.DismissListBox() self.Refresh() event.Skip() - diff -r 31e63e25b4cc -r 64beb9e9c749 editors/DataTypeEditor.py --- a/editors/DataTypeEditor.py Mon Aug 21 20:17:19 2017 +0000 +++ b/editors/DataTypeEditor.py Mon Aug 21 23:22:58 2017 +0300 @@ -35,28 +35,35 @@ from dialogs import ArrayTypeDialog from EditorPanel import EditorPanel from util.BitmapLibrary import GetBitmap - -#------------------------------------------------------------------------------- +from util.TranslationCatalogs import NoTranslate + +# ------------------------------------------------------------------------------- # Helpers -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- DIMENSION_MODEL = re.compile("([0-9]+)\.\.([0-9]+)$") + def AppendMenu(parent, help, id, kind, text): parent.Append(help=help, id=id, kind=kind, text=text) + def GetElementsTableColnames(): - _ = lambda x : x + _ = NoTranslate return ["#", _("Name"), _("Type"), _("Initial Value")] + def GetDatatypeTypes(): - _ = lambda x : x + _ = NoTranslate return [_("Directly"), _("Subrange"), _("Enumerated"), _("Array"), _("Structure")] + + DATATYPE_TYPES_DICT = dict([(_(datatype), datatype) for datatype in GetDatatypeTypes()]) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Structure Elements Table -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + class ElementsTable(CustomTable): @@ -74,10 +81,10 @@ return row + 1 colname = self.GetColLabelValue(col, False) value = self.data[row].get(colname, "") - + if colname == "Type" and isinstance(value, TupleType): if value[0] == "array": - return "ARRAY [%s] OF %s" % (",".join(map(lambda x : "..".join(x), value[2])), value[1]) + return "ARRAY [%s] OF %s" % (",".join(map(lambda x: "..".join(x), value[2])), value[1]) return value def SetValue(self, row, col, value): @@ -133,9 +140,10 @@ col_highlights = row_highlights.setdefault(infos[1], []) col_highlights.append(highlight_type) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Datatype Editor class -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + class DataTypeEditor(EditorPanel): @@ -148,21 +156,21 @@ top_sizer = wx.BoxSizer(wx.HORIZONTAL) self.MainSizer.AddSizer(top_sizer, border=5, - flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) + 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, - flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT) + flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT) self.DerivationType = wx.ComboBox(self.Editor, - size=wx.Size(200, -1), style=wx.CB_READONLY) + 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) + 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, - flag=wx.GROW|wx.BOTTOM|wx.LEFT|wx.RIGHT) + flag=wx.GROW | wx.BOTTOM | wx.LEFT | wx.RIGHT) # Panel for Directly derived data types @@ -172,25 +180,25 @@ directly_panel_sizer = wx.BoxSizer(wx.HORIZONTAL) directly_basetype_label = wx.StaticText(self.DirectlyPanel, - label=_('Base Type:')) + label=_('Base Type:')) directly_panel_sizer.AddWindow(directly_basetype_label, 1, border=5, - flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) + 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, - flag=wx.GROW|wx.ALL) + flag=wx.GROW | wx.ALL) directly_initialvalue_label = wx.StaticText(self.DirectlyPanel, - label=_('Initial Value:')) + label=_('Initial Value:')) directly_panel_sizer.AddWindow(directly_initialvalue_label, 1, border=5, - flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) + flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL) self.DirectlyInitialValue = wx.TextCtrl(self.DirectlyPanel, - style=wx.TE_PROCESS_ENTER|wx.TE_RICH) + 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, - flag=wx.ALL) + flag=wx.ALL) self.DirectlyPanel.SetSizer(directly_panel_sizer) @@ -202,49 +210,49 @@ subrange_panel_sizer = wx.GridSizer(cols=4, hgap=5, rows=3, vgap=0) subrange_basetype_label = wx.StaticText(self.SubrangePanel, - label=_('Base Type:')) + label=_('Base Type:')) subrange_panel_sizer.AddWindow(subrange_basetype_label, 1, border=5, - flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) + 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.SubrangeBaseType) + self.SubrangeBaseType) subrange_panel_sizer.AddWindow(self.SubrangeBaseType, 1, border=5, - flag=wx.GROW|wx.ALL) + flag=wx.GROW | wx.ALL) subrange_initialvalue_label = wx.StaticText(self.SubrangePanel, - label=_('Initial Value:')) + label=_('Initial Value:')) subrange_panel_sizer.AddWindow(subrange_initialvalue_label, 1, border=5, - flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) + flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL) self.SubrangeInitialValue = wx.SpinCtrl(self.SubrangePanel, - style=wx.TAB_TRAVERSAL) + style=wx.TAB_TRAVERSAL) self.Bind(wx.EVT_SPINCTRL, self.OnInfosChanged, self.SubrangeInitialValue) subrange_panel_sizer.AddWindow(self.SubrangeInitialValue, 1, border=5, - flag=wx.GROW|wx.ALL) + flag=wx.GROW | wx.ALL) subrange_minimum_label = wx.StaticText(self.SubrangePanel, label=_('Minimum:')) subrange_panel_sizer.AddWindow(subrange_minimum_label, 1, border=5, - flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) + 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, - flag=wx.GROW|wx.ALL) + flag=wx.GROW | wx.ALL) for i in xrange(2): subrange_panel_sizer.AddWindow(wx.Size(0, 0), 1) subrange_maximum_label = wx.StaticText(self.SubrangePanel, - label=_('Maximum:')) + label=_('Maximum:')) subrange_panel_sizer.AddWindow(subrange_maximum_label, 1, border=5, - flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) + 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, - flag=wx.GROW|wx.ALL) + flag=wx.GROW | wx.ALL) self.SubrangePanel.SetSizer(subrange_panel_sizer) @@ -255,29 +263,32 @@ 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| - wx.gizmos.EL_ALLOW_DELETE) + 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, - flag=wx.GROW|wx.ALL) + flag=wx.GROW | wx.ALL) 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:')) + label=_('Initial Value:')) enumerated_panel_rightsizer.AddWindow(enumerated_initialvalue_label, 1, - border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) + border=5, + flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL) self.EnumeratedInitialValue = wx.ComboBox(self.EnumeratedPanel, - style=wx.CB_READONLY) + style=wx.CB_READONLY) self.Bind(wx.EVT_COMBOBOX, self.OnInfosChanged, self.EnumeratedInitialValue) enumerated_panel_rightsizer.AddWindow(self.EnumeratedInitialValue, 1, - border=5, flag=wx.ALL) + border=5, flag=wx.ALL) self.EnumeratedPanel.SetSizer(enumerated_panel_sizer) @@ -296,36 +307,38 @@ array_basetype_label = wx.StaticText(self.ArrayPanel, label=_('Base Type:')) array_panel_leftSizer.AddWindow(array_basetype_label, 1, border=5, - flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) + 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, - flag=wx.GROW|wx.ALL) + flag=wx.GROW | wx.ALL) 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:')) + label=_('Initial Value:')) array_panel_rightsizer.AddWindow(array_initialvalue_label, 1, border=5, - flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) + flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL) self.ArrayInitialValue = wx.TextCtrl(self.ArrayPanel, - style=wx.TE_PROCESS_ENTER|wx.TE_RICH) + 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, - flag=wx.ALL) - - self.ArrayDimensions = CustomEditableListBox(self.ArrayPanel, - label=_("Dimensions:"), style=wx.gizmos.EL_ALLOW_NEW| - wx.gizmos.EL_ALLOW_EDIT| - wx.gizmos.EL_ALLOW_DELETE) + flag=wx.ALL) + + self.ArrayDimensions = CustomEditableListBox( + self.ArrayPanel, + label=_("Dimensions:"), + style=(wx.gizmos.EL_ALLOW_NEW | + wx.gizmos.EL_ALLOW_EDIT | + wx.gizmos.EL_ALLOW_DELETE)) for func in ["_OnLabelEndEdit", "_OnAddButton", "_OnDelButton", "_OnUpButton", "_OnDownButton"]: setattr(self.ArrayDimensions, func, self.OnDimensionsChanged) array_panel_sizer.AddWindow(self.ArrayDimensions, 0, border=5, - flag=wx.GROW|wx.ALL) + flag=wx.GROW | wx.ALL) self.ArrayPanel.SetSizer(array_panel_sizer) @@ -342,10 +355,10 @@ structure_button_sizer.AddGrowableCol(0) structure_button_sizer.AddGrowableRow(0) structure_panel_sizer.AddSizer(structure_button_sizer, 0, border=5, - flag=wx.ALL|wx.GROW) + flag=wx.ALL | wx.GROW) structure_elements_label = wx.StaticText(self.StructurePanel, - label=_('Elements :')) + label=_('Elements :')) structure_button_sizer.AddWindow(structure_elements_label, flag=wx.ALIGN_BOTTOM) for name, bitmap, help in [ @@ -354,17 +367,19 @@ ("StructureUpButton", "up", _("Move element up")), ("StructureDownButton", "down", _("Move element down"))]: button = wx.lib.buttons.GenBitmapButton(self.StructurePanel, - bitmap=GetBitmap(bitmap), size=wx.Size(28, 28), style=wx.NO_BORDER) + bitmap=GetBitmap(bitmap), + size=wx.Size(28, 28), + style=wx.NO_BORDER) button.SetToolTipString(help) setattr(self, name, button) structure_button_sizer.AddWindow(button) self.StructureElementsGrid = CustomGrid(self.StructurePanel, - size=wx.Size(0, 150), style=wx.VSCROLL) + size=wx.Size(0, 150), style=wx.VSCROLL) self.StructureElementsGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, - self.OnStructureElementsGridCellChange) + self.OnStructureElementsGridCellChange) self.StructureElementsGrid.Bind(wx.grid.EVT_GRID_EDITOR_SHOWN, - self.OnStructureElementsGridEditorShown) + self.OnStructureElementsGridEditorShown) structure_panel_sizer.AddWindow(self.StructureElementsGrid, flag=wx.GROW) self.StructurePanel.SetSizer(structure_panel_sizer) @@ -374,7 +389,7 @@ def __init__(self, parent, tagname, window, controler): EditorPanel.__init__(self, parent, tagname, window, controler) - self.StructureElementDefaultValue = {"Name" : "", "Type" : DefaultType, "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] @@ -496,7 +511,7 @@ self.EnumeratedInitialValue.SetStringSelection(type_infos["initial"]) elif type_infos["type"] == "Array": self.ArrayBaseType.SetStringSelection(type_infos["base_type"]) - self.ArrayDimensions.SetStrings(map(lambda x : "..".join(x), type_infos["dimensions"])) + self.ArrayDimensions.SetStrings(map(lambda x: "..".join(x), type_infos["dimensions"])) self.ArrayInitialValue.SetValue(type_infos["initial"]) elif type_infos["type"] == "Structure": self.StructureElementsTable.SetData(type_infos["elements"]) @@ -547,12 +562,12 @@ index = event.GetIndex() if index >= len(values) or values[index].upper() != text.upper(): if text.upper() in [value.upper() for value in values]: - message = wx.MessageDialog(self, _("\"%s\" value already defined!")%text, _("Error"), wx.OK|wx.ICON_ERROR) + message = wx.MessageDialog(self, _("\"%s\" value already defined!") % text, _("Error"), wx.OK | wx.ICON_ERROR) message.ShowModal() message.Destroy() event.Veto() elif text.upper() in IEC_KEYWORDS: - message = wx.MessageDialog(self, _("\"%s\" is a keyword. It can't be used!")%text, _("Error"), wx.OK|wx.ICON_ERROR) + message = wx.MessageDialog(self, _("\"%s\" is a keyword. It can't be used!") % text, _("Error"), wx.OK | wx.ICON_ERROR) message.ShowModal() message.Destroy() else: @@ -571,10 +586,10 @@ event.Skip() def ShowErrorMessage(self, message): - dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() - + def OnStructureElementsGridCellChange(self, event): row, col = event.GetRow(), event.GetCol() colname = self.StructureElementsTable.GetColLabelValue(col, False) @@ -582,20 +597,20 @@ if colname == "Name": message = None if not TestIdentifier(value): - message = _("\"%s\" is not a valid identifier!")%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 self.PouNames: -## message = _("A pou with \"%s\" as name exists!")%value + message = _("\"%s\" is a keyword. It can't be used!") % value +# elif value.upper() in self.PouNames: +# message = _("A pou with \"%s\" as name exists!")%value elif value.upper() in [var["Name"].upper() for idx, var in enumerate(self.StructureElementsTable.GetData()) if idx != row]: - message = _("An element named \"%s\" already exists in this structure!")%value + message = _("An element named \"%s\" already exists in this structure!") % value else: self.RefreshTypeInfos() wx.CallAfter(self.StructureElementsTable.ResetView, self.StructureElementsGrid) -## old_value = self.Table.GetOldValue() -## if old_value != "": -## self.Controler.UpdateEditedElementUsedVariable(self.TagName, old_value, value) -## self.Controler.BufferProject() +# old_value = self.Table.GetOldValue() +# if old_value != "": +# self.Controler.UpdateEditedElementUsedVariable(self.TagName, old_value, value) +# self.Controler.BufferProject() event.Skip() if message is not None: @@ -633,15 +648,15 @@ 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) -## if classtype in ["Input","Output","InOut","External","Global"] or poutype != "function" and bodytype in ["ST", "IL"]: -## for functionblock_type in self.Controler.GetFunctionBlockTypes(self.TagName): -## new_id = wx.NewId() -## 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.AppendMenu(wx.NewId(), _("Function Block Types"), functionblock_menu) +# functionblock_menu = wx.Menu(title='') +# 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"]: +# for functionblock_type in self.Controler.GetFunctionBlockTypes(self.TagName): +# new_id = wx.NewId() +# 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.AppendMenu(wx.NewId(), _("Function Block Types"), functionblock_menu) rect = self.StructureElementsGrid.BlockToDeviceRect((row, col), (row, col)) self.StructureElementsGrid.PopupMenuXY(type_menu, rect.x + rect.width, rect.y + self.StructureElementsGrid.GetColLabelSize()) @@ -718,7 +733,7 @@ def RefreshTypeInfos(self): selected = DATATYPE_TYPES_DICT[self.DerivationType.GetStringSelection()] - infos = {"type" : selected} + infos = {"type": selected} if selected == "Directly": infos["base_type"] = self.DirectlyBaseType.GetStringSelection() infos["initial"] = self.DirectlyInitialValue.GetValue() @@ -740,14 +755,14 @@ for dimensions in self.ArrayDimensions.GetStrings(): result = DIMENSION_MODEL.match(dimensions) if result is None: - message = wx.MessageDialog(self, _("\"%s\" value isn't a valid array dimension!")%dimensions, _("Error"), wx.OK|wx.ICON_ERROR) + message = wx.MessageDialog(self, _("\"%s\" value isn't a valid array dimension!") % dimensions, _("Error"), wx.OK | wx.ICON_ERROR) message.ShowModal() message.Destroy() self.RefreshView() return bounds = result.groups() if int(bounds[0]) >= int(bounds[1]): - message = wx.MessageDialog(self, _("\"%s\" value isn't a valid array dimension!\nRight value must be greater than left value.")%dimensions, _("Error"), wx.OK|wx.ICON_ERROR) + message = wx.MessageDialog(self, _("\"%s\" value isn't a valid array dimension!\nRight value must be greater than left value.") % dimensions, _("Error"), wx.OK | wx.ICON_ERROR) message.ShowModal() message.Destroy() self.RefreshView() @@ -762,9 +777,9 @@ self.ParentWindow.RefreshFileMenu() self.ParentWindow.RefreshEditMenu() -#------------------------------------------------------------------------------- -# Highlights showing functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Highlights showing functions + # ------------------------------------------------------------------------------- def OnRefreshHighlightsTimer(self, event): self.RefreshView() @@ -790,7 +805,7 @@ self.StructureElementsTable.ClearHighlights(highlight_type) self.RefreshView() - def AddHighlight(self, infos, start, end ,highlight_type): + def AddHighlight(self, infos, start, end, highlight_type): self.Highlights.append((infos, start, end, highlight_type)) self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True) @@ -812,4 +827,3 @@ listctrl.SetItemBackgroundColour(infos[1], highlight_type[0]) listctrl.SetItemTextColour(infos[1], highlight_type[1]) listctrl.Select(listctrl.FocusedItem, False) - diff -r 31e63e25b4cc -r 64beb9e9c749 editors/DebugViewer.py --- a/editors/DebugViewer.py Mon Aug 21 20:17:19 2017 +0000 +++ b/editors/DebugViewer.py Mon Aug 21 23:22:58 2017 +0300 @@ -27,20 +27,20 @@ import wx -REFRESH_PERIOD = 0.1 # Minimum time between 2 refresh -DEBUG_REFRESH_LOCK = Lock() # Common refresh lock for all debug viewers - -#------------------------------------------------------------------------------- +REFRESH_PERIOD = 0.1 # Minimum time between 2 refresh +DEBUG_REFRESH_LOCK = Lock() # Common refresh lock for all debug viewers + +# ------------------------------------------------------------------------------- # Debug Viewer Class -#------------------------------------------------------------------------------- - -""" -Class that implements common behavior of every viewers able to display debug -values -""" +# ------------------------------------------------------------------------------- + class DebugViewer: - + """ + Class that implements common behavior of every viewers able to display debug + values + """ + def __init__(self, producer, debug, subscribe_tick=True): """ Constructor @@ -52,49 +52,49 @@ """ self.Debug = debug self.SubscribeTick = subscribe_tick - + # Flag indicating that consumer value update inhibited # (DebugViewer is refreshing) self.Inhibited = False - + # List of data consumers subscribed to DataProducer self.DataConsumers = {} - + # Time stamp indicating when last refresh have been initiated self.LastRefreshTime = gettime() # Flag indicating that DebugViewer has acquire common debug lock self.HasAcquiredLock = False # Lock for access to the two preceding variable self.AccessLock = Lock() - + # Timer to refresh Debug Viewer one last time in the case that a new # value have been received during refresh was inhibited and no one # after refresh was activated self.LastRefreshTimer = None # Lock for access to the timer self.TimerAccessLock = Lock() - + # Set DataProducer and subscribe tick if needed self.SetDataProducer(producer) - + def __del__(self): """ Destructor """ # Unsubscribe all data consumers self.UnsubscribeAllDataConsumers() - + # Delete reference to DataProducer self.DataProducer = None - + # Stop last refresh timer if self.LastRefreshTimer is not None: self.LastRefreshTimer.cancel() - + # Release Common debug lock if DebugViewer has acquired it if self.HasAcquiredLock: DEBUG_REFRESH_LOCK.release() - + def SetDataProducer(self, producer): """ Set Data Producer @@ -103,25 +103,25 @@ # In the case that tick need to be subscribed and DebugViewer is # debugging if self.SubscribeTick and self.Debug: - + # Subscribe tick to new data producer if producer is not None: producer.SubscribeDebugIECVariable("__tick__", self, True) - + # Unsubscribe tick from old data producer if getattr(self, "DataProducer", None) is not None: self.DataProducer.UnsubscribeDebugIECVariable("__tick__", self) - + # Save new data producer self.DataProducer = producer - + def IsDebugging(self): """ Get flag indicating if Debug Viewer is debugging @return: Debugging flag """ return self.Debug - + def Inhibit(self, inhibit): """ Set consumer value update inhibit flag @@ -130,10 +130,10 @@ # Inhibit every data consumers in list for consumer, iec_path in self.DataConsumers.iteritems(): consumer.Inhibit(inhibit) - + # Save inhibit flag self.Inhibited = inhibit - + def AddDataConsumer(self, iec_path, consumer, buffer_list=False): """ Subscribe data consumer to DataProducer @@ -145,19 +145,19 @@ # Return immediately if no DataProducer defined if self.DataProducer is None: return None - + # Subscribe data consumer to DataProducer result = self.DataProducer.SubscribeDebugIECVariable( iec_path, consumer, buffer_list) if result is not None and consumer != self: - + # Store data consumer if successfully subscribed and inform # consumer of variable data type self.DataConsumers[consumer] = iec_path consumer.SetDataType(self.GetDataType(iec_path)) - + return result - + def RemoveDataConsumer(self, consumer): """ Unsubscribe data consumer from DataProducer @@ -165,12 +165,12 @@ """ # Remove consumer from data consumer list iec_path = self.DataConsumers.pop(consumer, None) - + # Unsubscribe consumer from DataProducer if iec_path is not None: self.DataProducer.UnsubscribeDebugIECVariable( iec_path, consumer) - + def SubscribeAllDataConsumers(self): """ Called to Subscribe all data consumers contained in DebugViewer. @@ -179,24 +179,24 @@ # Subscribe tick if needed if self.SubscribeTick and self.Debug and self.DataProducer is not None: self.DataProducer.SubscribeDebugIECVariable("__tick__", self, True) - + def UnsubscribeAllDataConsumers(self, tick=True): """ Called to Unsubscribe all data consumers. """ if self.DataProducer is not None: - + # Unscribe tick if needed if self.SubscribeTick and tick and self.Debug: self.DataProducer.UnsubscribeDebugIECVariable("__tick__", self) - + # Unsubscribe all data consumers in list for consumer, iec_path in self.DataConsumers.iteritems(): self.DataProducer.UnsubscribeDebugIECVariable( iec_path, consumer) - + self.DataConsumers = {} - + def GetDataType(self, iec_path): """ Return variable data type. @@ -204,20 +204,20 @@ @return: variable data type (None if not found) """ if self.DataProducer is not None: - + # Search for variable informations in project compilation files data_type = self.DataProducer.GetDebugIECVariableType( iec_path.upper()) if data_type is not None: return data_type - + # Search for variable informations in project data infos = self.DataProducer.GetInstanceInfos(iec_path) if infos is not None: return infos.type - + return None - + def IsNumType(self, data_type): """ Indicate if data type given is a numeric data type @@ -226,9 +226,9 @@ """ if self.DataProducer is not None: return self.DataProducer.IsNumType(data_type) - + return False - + def ForceDataValue(self, iec_path, value): """ Force PLC variable value @@ -237,7 +237,7 @@ """ if self.DataProducer is not None: self.DataProducer.ForceDebugIECVariable(iec_path, value) - + def ReleaseDataValue(self, iec_path): """ Release PLC variable value @@ -245,40 +245,40 @@ """ if self.DataProducer is not None: self.DataProducer.ReleaseDebugIECVariable(iec_path) - + def NewDataAvailable(self, ticks): """ Called by DataProducer for each tick captured @param tick: PLC tick captured - All other parameters are passed to refresh function + All other parameters are passed to refresh function """ # Stop last refresh timer self.TimerAccessLock.acquire() if self.LastRefreshTimer is not None: self.LastRefreshTimer.cancel() - self.LastRefreshTimer=None + self.LastRefreshTimer = None self.TimerAccessLock.release() - + # Only try to refresh DebugViewer if it is visible on screen and not # already refreshing if self.IsShown() and not self.Inhibited: - + # Try to get acquire common refresh lock if minimum period between # two refresh has expired if gettime() - self.LastRefreshTime > REFRESH_PERIOD and \ DEBUG_REFRESH_LOCK.acquire(False): self.StartRefreshing() - + # If common lock wasn't acquired for any reason, restart last # refresh timer else: self.StartLastRefreshTimer() - + # In the case that DebugViewer isn't visible on screen and has already # acquired common refresh lock, reset DebugViewer elif not self.IsShown() and self.HasAcquiredLock: DebugViewer.RefreshNewData(self) - + def ShouldRefresh(self): """ Callback function called when last refresh timer expired @@ -286,15 +286,15 @@ """ # Cancel if DebugViewer is not visible on screen if self and self.IsShown(): - + # Try to acquire common refresh lock if DEBUG_REFRESH_LOCK.acquire(False): self.StartRefreshing() - + # Restart last refresh timer if common refresh lock acquired failed else: self.StartLastRefreshTimer() - + def StartRefreshing(self): """ Called to initiate a refresh of DebugViewer @@ -306,13 +306,13 @@ self.HasAcquiredLock = True self.LastRefreshTime = gettime() self.AccessLock.release() - + # Inhibit data consumer value update self.Inhibit(True) - + # Initiate DebugViewer refresh wx.CallAfter(self.RefreshNewData) - + def StartLastRefreshTimer(self): """ Called to start last refresh timer for the minimum time between 2 @@ -324,19 +324,19 @@ REFRESH_PERIOD, self.ShouldRefresh) self.LastRefreshTimer.start() self.TimerAccessLock.release() - + def RefreshNewData(self): """ Called to refresh DebugViewer according to values received by data consumers May be overridden by inherited classes Can receive any parameters depending on what is needed by inherited - class + class """ if self: # Activate data consumer value update self.Inhibit(False) - + # Release common refresh lock if acquired and update # last refresh time self.AccessLock.acquire() diff -r 31e63e25b4cc -r 64beb9e9c749 editors/EditorPanel.py --- a/editors/EditorPanel.py Mon Aug 21 20:17:19 2017 +0000 +++ b/editors/EditorPanel.py Mon Aug 21 23:22:58 2017 +0300 @@ -26,67 +26,68 @@ from controls import VariablePanel + class EditorPanel(wx.SplitterWindow): - + VARIABLE_PANEL_TYPE = None - + def _init_Editor(self, prnt): self.Editor = None - + def _init_MenuItems(self): self.MenuItems = [] - + def _init_ctrls(self, parent): wx.SplitterWindow.__init__(self, parent, - style=wx.SUNKEN_BORDER|wx.SP_3D) + style=wx.SUNKEN_BORDER | wx.SP_3D) self.SetMinimumPaneSize(1) - + self._init_MenuItems() - + if self.VARIABLE_PANEL_TYPE is not None: self.VariableEditor = VariablePanel(self, self, self.Controler, self.VARIABLE_PANEL_TYPE, self.Debug) self.VariableEditor.SetTagName(self.TagName) else: self.VariableEditor = None - + self._init_Editor(self) - + if self.Editor is not None and self.VariableEditor is not None: self.SplitHorizontally(self.VariableEditor, self.Editor, 200) elif self.VariableEditor is not None: self.Initialize(self.VariableEditor) elif self.Editor is not None: self.Initialize(self.Editor) - + def __init__(self, parent, tagname, window, controler, debug=False): self.ParentWindow = window self.Controler = controler self.TagName = tagname self.Icon = None self.Debug = debug - + self._init_ctrls(parent) - + def SetTagName(self, tagname): self.TagName = tagname if self.VARIABLE_PANEL_TYPE is not None: self.VariableEditor.SetTagName(tagname) - + def GetTagName(self): return self.TagName - + def Select(self): self.ParentWindow.EditProjectElement(None, self.GetTagName(), True) - + def GetTitle(self): return ".".join(self.TagName.split("::")[1:]) - + def GetIcon(self): return self.Icon - + def SetIcon(self, icon): self.Icon = icon - + def IsViewing(self, tagname): return self.GetTagName() == tagname @@ -98,54 +99,54 @@ def ResetBuffer(self): pass - + def IsModified(self): return False - + def CheckSaveBeforeClosing(self): return True - + def Save(self): pass - + def SaveAs(self): pass - + def GetBufferState(self): if self.Controler is not None: return self.Controler.GetBufferState() return False, False - + def Undo(self): if self.Controler is not None: self.Controler.LoadPrevious() self.RefreshView() - + def Redo(self): if self.Controler is not None: self.Controler.LoadNext() self.RefreshView() - + def Find(self, direction, search_params): pass - + def HasNoModel(self): return False - + def RefreshView(self, variablepanel=True): if variablepanel: self.RefreshVariablePanel() - + def RefreshVariablePanel(self): if self.VariableEditor is not None: self.VariableEditor.RefreshView() - + def GetConfNodeMenuItems(self): return self.MenuItems - + def RefreshConfNodeMenu(self, confnode_menu): pass - + def _Refresh(self, *args): self.ParentWindow._Refresh(*args) @@ -159,7 +160,7 @@ def RemoveHighlight(self, infos, start, end, highlight_type): if self.VariableEditor is not None and infos[0] in ["var_local", "var_input", "var_output", "var_inout"]: self.VariableEditor.RemoveVariableHighlight(infos[1:], highlight_type) - + def ClearHighlights(self, highlight_type=None): if self.VariableEditor is not None: self.VariableEditor.ClearHighlights(highlight_type) diff -r 31e63e25b4cc -r 64beb9e9c749 editors/FileManagementPanel.py --- a/editors/FileManagementPanel.py Mon Aug 21 20:17:19 2017 +0000 +++ b/editors/FileManagementPanel.py Mon Aug 21 23:22:58 2017 +0300 @@ -34,39 +34,41 @@ FILTER = _("All files (*.*)|*.*|CSV files (*.csv)|*.csv") + class FileManagementPanel(EditorPanel): - + def _init_Editor(self, parent): self.Editor = wx.Panel(parent) - + main_sizer = wx.BoxSizer(wx.HORIZONTAL) - + left_sizer = wx.BoxSizer(wx.VERTICAL) - main_sizer.AddSizer(left_sizer, 1, border=5, flag=wx.GROW|wx.ALL) - + main_sizer.AddSizer(left_sizer, 1, border=5, flag=wx.GROW | wx.ALL) + managed_dir_label = wx.StaticText(self.Editor, label=_(self.TagName) + ":") - left_sizer.AddWindow(managed_dir_label, border=5, flag=wx.GROW|wx.BOTTOM) - + left_sizer.AddWindow(managed_dir_label, border=5, flag=wx.GROW | wx.BOTTOM) + self.ManagedDir = FolderTree(self.Editor, self.Folder, FILTER) left_sizer.AddWindow(self.ManagedDir, 1, flag=wx.GROW) - + managed_treectrl = self.ManagedDir.GetTreeCtrl() self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnTreeItemChanged, managed_treectrl) if self.EnableDragNDrop: self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnTreeBeginDrag, managed_treectrl) - + button_sizer = wx.BoxSizer(wx.VERTICAL) - main_sizer.AddSizer(button_sizer, border=5, - flag=wx.ALL|wx.ALIGN_CENTER_VERTICAL) - + main_sizer.AddSizer(button_sizer, border=5, + flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL) + for idx, (name, bitmap, help) in enumerate([ ("DeleteButton", "remove_element", _("Remove file from left folder")), ("LeftCopyButton", "LeftCopy", _("Copy file from right folder to left")), ("RightCopyButton", "RightCopy", _("Copy file from left folder to right")), ("EditButton", "edit", _("Edit file"))]): - button = wx.lib.buttons.GenBitmapButton(self.Editor, - bitmap=GetBitmap(bitmap), - size=wx.Size(28, 28), style=wx.NO_BORDER) + button = wx.lib.buttons.GenBitmapButton( + self.Editor, + bitmap=GetBitmap(bitmap), + size=wx.Size(28, 28), style=wx.NO_BORDER) button.SetToolTipString(help) setattr(self, name, button) if idx > 0: @@ -75,62 +77,62 @@ flag = 0 self.Bind(wx.EVT_BUTTON, getattr(self, "On" + name), button) button_sizer.AddWindow(button, border=20, flag=flag) - + right_sizer = wx.BoxSizer(wx.VERTICAL) - main_sizer.AddSizer(right_sizer, 1, border=5, flag=wx.GROW|wx.ALL) - + main_sizer.AddSizer(right_sizer, 1, border=5, flag=wx.GROW | wx.ALL) + if wx.Platform == '__WXMSW__': system_dir_label = wx.StaticText(self.Editor, label=_("My Computer:")) else: system_dir_label = wx.StaticText(self.Editor, label=_("Home Directory:")) - right_sizer.AddWindow(system_dir_label, border=5, flag=wx.GROW|wx.BOTTOM) - + right_sizer.AddWindow(system_dir_label, border=5, flag=wx.GROW | wx.BOTTOM) + self.SystemDir = FolderTree(self.Editor, self.HomeDirectory, FILTER, False) right_sizer.AddWindow(self.SystemDir, 1, flag=wx.GROW) - + system_treectrl = self.SystemDir.GetTreeCtrl() self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnTreeItemChanged, system_treectrl) - + self.Editor.SetSizer(main_sizer) - + def __init__(self, parent, controler, name, folder, enable_dragndrop=False): self.Folder = os.path.realpath(folder) self.EnableDragNDrop = enable_dragndrop - + if wx.Platform == '__WXMSW__': self.HomeDirectory = "/" else: self.HomeDirectory = os.path.expanduser("~") - + EditorPanel.__init__(self, parent, name, None, None) - + self.Controler = controler - + self.EditableFileExtensions = [] self.EditButton.Hide() - + self.SetIcon(GetBitmap("FOLDER")) - + def __del__(self): self.Controler.OnCloseEditor(self) - + def GetTitle(self): return _(self.TagName) - + def SetEditableFileExtensions(self, extensions): self.EditableFileExtensions = extensions if len(self.EditableFileExtensions) > 0: self.EditButton.Show() - + def RefreshView(self): self.ManagedDir.RefreshTree() self.SystemDir.RefreshTree() self.RefreshButtonsState() - + def RefreshButtonsState(self): managed_filepath = self.ManagedDir.GetPath() system_filepath = self.SystemDir.GetPath() - + self.DeleteButton.Enable(os.path.isfile(managed_filepath)) self.LeftCopyButton.Enable(os.path.isfile(system_filepath)) self.RightCopyButton.Enable(os.path.isfile(managed_filepath)) @@ -138,22 +140,23 @@ self.EditButton.Enable( os.path.isfile(managed_filepath) and os.path.splitext(managed_filepath)[1] in self.EditableFileExtensions) - + def OnTreeItemChanged(self, event): self.RefreshButtonsState() event.Skip() - + def OnDeleteButton(self, event): filepath = self.ManagedDir.GetPath() if os.path.isfile(filepath): folder, filename = os.path.split(filepath) - - dialog = wx.MessageDialog(self, - _("Do you really want to delete the file '%s'?") % filename, - _("Delete File"), wx.YES_NO|wx.ICON_QUESTION) + + dialog = wx.MessageDialog(self, + _("Do you really want to delete the file '%s'?") % filename, + _("Delete File"), + wx.YES_NO | wx.ICON_QUESTION) remove = dialog.ShowModal() == wx.ID_YES dialog.Destroy() - + if remove: os.remove(filepath) self.ManagedDir.RefreshTree() @@ -161,11 +164,11 @@ def OnEditButton(self, event): filepath = self.ManagedDir.GetPath() - if (os.path.isfile(filepath) and - os.path.splitext(filepath)[1] in self.EditableFileExtensions): + if os.path.isfile(filepath) and \ + os.path.splitext(filepath)[1] in self.EditableFileExtensions: self.Controler._OpenView(filepath + "::") event.Skip() - + def CopyFile(self, src, dst): if os.path.isfile(src): src_folder, src_filename = os.path.split(src) @@ -173,17 +176,18 @@ dst_folder, dst_filename = os.path.split(dst) else: dst_folder = dst - + dst_filepath = os.path.join(dst_folder, src_filename) if os.path.isfile(dst_filepath): - dialog = wx.MessageDialog(self, - _("The file '%s' already exist.\nDo you want to replace it?") % src_filename, - _("Replace File"), wx.YES_NO|wx.ICON_QUESTION) + dialog = wx.MessageDialog( + self, + _("The file '%s' already exist.\nDo you want to replace it?") % src_filename, + _("Replace File"), wx.YES_NO | wx.ICON_QUESTION) copy = dialog.ShowModal() == wx.ID_YES dialog.Destroy() else: copy = True - + if copy: shutil.copyfile(src, dst_filepath) return dst_filepath @@ -202,7 +206,7 @@ self.SystemDir.RefreshTree() self.SystemDir.SetPath(filepath) event.Skip() - + def OnTreeBeginDrag(self, event): filepath = self.ManagedDir.GetPath() if os.path.isfile(filepath): @@ -211,4 +215,3 @@ dragSource = wx.DropSource(self) dragSource.SetData(data) dragSource.DoDragDrop() - diff -r 31e63e25b4cc -r 64beb9e9c749 editors/IECCodeViewer.py --- a/editors/IECCodeViewer.py Mon Aug 21 20:17:19 2017 +0000 +++ b/editors/IECCodeViewer.py Mon Aug 21 23:22:58 2017 +0300 @@ -25,16 +25,17 @@ from editors.TextViewer import TextViewer from plcopen.plcopen import TestTextElement + class IECCodeViewer(TextViewer): - + def __del__(self): TextViewer.__del__(self) if getattr(self, "_OnClose"): self._OnClose(self) - + def Paste(self): if self.Controler is not None: TextViewer.Paste(self) - + def Search(self, criteria): return [((self.TagName, "body", 0),) + result for result in TestTextElement(self.Editor.GetText(), criteria)] diff -r 31e63e25b4cc -r 64beb9e9c749 editors/LDViewer.py --- a/editors/LDViewer.py Mon Aug 21 20:17:19 2017 +0000 +++ b/editors/LDViewer.py Mon Aug 21 23:22:58 2017 +0300 @@ -28,6 +28,7 @@ from Viewer import * + def ExtractNextBlocks(block, block_list): current_list = [block] while len(current_list) > 0: @@ -49,7 +50,8 @@ block_list.append(next) next_list.append(next) current_list = next_list - + + def CalcBranchSize(elements, stops): branch_size = 0 stop_list = stops @@ -58,19 +60,19 @@ element_tree = {} for element in elements: if element not in element_tree: - element_tree[element] = {"parents":["start"], "children":[], "weight":None} + element_tree[element] = {"parents": ["start"], "children": [], "weight": None} GenerateTree(element, element_tree, stop_list) elif element_tree[element]: element_tree[element]["parents"].append("start") - remove_stops = {"start":[], "stop":[]} + remove_stops = {"start": [], "stop": []} for element, values in element_tree.items(): if "stop" in values["children"]: removed = [] for child in values["children"]: if child != "stop": -## if child in elements: -## RemoveElement(child, element_tree) -## removed.append(child) + # if child in elements: + # RemoveElement(child, element_tree) + # removed.append(child) if "start" in element_tree[child]["parents"]: if element not in remove_stops["stop"]: remove_stops["stop"].append(element) @@ -89,16 +91,17 @@ branch_size += values["weight"] else: return 1 - #print branch_size return branch_size + def RemoveElement(remove, element_tree): if remove in element_tree and element_tree[remove]: for child in element_tree[remove]["children"]: if child != "stop": RemoveElement(child, element_tree) element_tree.pop(remove) -## element_tree[remove] = None +# element_tree[remove] = None + def GenerateTree(element, element_tree, stop_list): if element in element_tree: @@ -115,20 +118,21 @@ for wire, handle in connector.GetWires(): next = wire.EndConnected.GetParentBlock() if isinstance(next, LD_PowerRail) and next.GetType() == LEFTRAIL or next in stop_list: -## for remove in element_tree[element]["children"]: -## RemoveElement(remove, element_tree) -## element_tree[element]["children"] = ["stop"] + # for remove in element_tree[element]["children"]: + # RemoveElement(remove, element_tree) + # element_tree[element]["children"] = ["stop"] element_tree[element]["children"].append("stop") -## elif element_tree[element]["children"] == ["stop"]: -## element_tree[next] = None + # elif element_tree[element]["children"] == ["stop"]: + # element_tree[next] = None elif next not in element_tree or element_tree[next]: element_tree[element]["children"].append(next) if next in element_tree: element_tree[next]["parents"].append(element) else: - element_tree[next] = {"parents":[element], "children":[], "weight":None} + element_tree[next] = {"parents": [element], "children": [], "weight": None} GenerateTree(next, element_tree, stop_list) + def CalcWeight(element, element_tree): weight = 0 parts = None @@ -156,26 +160,25 @@ element_tree[element]["weight"] = max(1, weight / parts) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Ladder Diagram Graphic elements Viewer class -#------------------------------------------------------------------------------- - - -""" -Class derived from Viewer class that implements a Viewer of Ladder Diagram -""" +# ------------------------------------------------------------------------------- + class LD_Viewer(Viewer): - - def __init__(self, parent, tagname, window, controler, debug = False, instancepath = ""): + """ + Class derived from Viewer class that implements a Viewer of Ladder Diagram + """ + + def __init__(self, parent, tagname, window, controler, debug=False, instancepath=""): Viewer.__init__(self, parent, tagname, window, controler, debug, instancepath) self.Rungs = [] self.RungComments = [] self.CurrentLanguage = "LD" -#------------------------------------------------------------------------------- -# Refresh functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Refresh functions + # ------------------------------------------------------------------------------- def ResetView(self): self.Rungs = [] @@ -194,7 +197,7 @@ self.RungComments.insert(i, None) else: self.RungComments.insert(i, None) - + def loadInstance(self, instance, ids, selection): Viewer.loadInstance(self, instance, ids, selection) if self.GetDrawingMode() != FREEDRAWING_MODE: @@ -212,7 +215,10 @@ if rung not in rungs: rungs.append(rung) if len(rungs) > 1: - raise ValueError, _("Ladder element with id %d is on more than one rung.")%instance["id"] + raise ValueError( + _("Ladder element with id %d is on more than one rung.") + % instance["id"]) + element = self.FindElementById(instance["id"]) element_connectors = element.GetConnectors() self.Rungs[rungs[0]].SelectElement(element) @@ -228,9 +234,12 @@ if rung not in rungs: rungs.append(rung) if len(rungs) > 1: - raise ValueError, _("Ladder element with id %d is on more than one rung.")%instance["id"] + raise ValueError( + _("Ladder element with id %d is on more than one rung.") + % instance["id"]) + element = self.FindElementById(instance["id"]) - element_connectors = element.GetConnectors() + element_connectors = element.GetConnectors() self.Rungs[rungs[0]].SelectElement(element) for wire, num in element_connectors["inputs"][0].GetWires(): self.Rungs[rungs[0]].SelectElement(wire) @@ -240,7 +249,7 @@ pos = element.GetPosition() i = 0 inserted = False - while i < len(self.RungComments) and not inserted: + while i < len(self.RungComments) and not inserted: ipos = self.RungComments[i].GetPosition() if pos[1] < ipos[1]: self.RungComments.insert(i, element) @@ -248,10 +257,10 @@ i += 1 if not inserted: self.RungComments.append(element) - -#------------------------------------------------------------------------------- -# Search Element functions -#------------------------------------------------------------------------------- + + # ------------------------------------------------------------------------------- + # Search Element functions + # ------------------------------------------------------------------------------- def FindRung(self, element): for i, rung in enumerate(self.Rungs): @@ -259,10 +268,10 @@ return i return None - def FindElement(self, event, exclude_group = False, connectors = True): + def FindElement(self, event, exclude_group=False, connectors=True): if self.GetDrawingMode() == FREEDRAWING_MODE: return Viewer.FindElement(self, event, exclude_group, connectors) - + dc = self.GetLogicalDC() pos = event.GetLogicalPosition(dc) if self.SelectedElement and not isinstance(self.SelectedElement, (Graphic_Group, Wire)): @@ -286,16 +295,16 @@ def SearchElements(self, bbox): if self.GetDrawingMode() == FREEDRAWING_MODE: return Viewer.SearchElements(self, bbox) - + elements = [] for element in self.Blocks.values() + self.Comments.values(): if element.IsInSelection(bbox): elements.append(element) return elements -#------------------------------------------------------------------------------- -# Mouse event functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Mouse event functions + # ------------------------------------------------------------------------------- def OnViewerLeftDown(self, event): if self.GetDrawingMode() == FREEDRAWING_MODE: @@ -345,7 +354,7 @@ self.SelectedElement.SetElements(elements) self.SelectedElement.SetSelected(True) elif self.Mode == MODE_SELECTION and self.SelectedElement: - dc = self.GetLogicalDC() + dc = self.GetLogicalDC() if not isinstance(self.SelectedElement, Graphic_Group): if self.IsWire(self.SelectedElement): result = self.SelectedElement.TestSegment(event.GetLogicalPosition(dc), True) @@ -383,9 +392,9 @@ wx.CallAfter(self.SetCurrentCursor, 0) event.Skip() -#------------------------------------------------------------------------------- -# Keyboard event functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Keyboard event functions + # ------------------------------------------------------------------------------- def OnChar(self, event): if self.GetDrawingMode() == FREEDRAWING_MODE: @@ -434,9 +443,9 @@ else: event.Skip() -#------------------------------------------------------------------------------- -# Model adding functions from Drop Target -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Model adding functions from Drop Target + # ------------------------------------------------------------------------------- def AddVariableBlock(self, x, y, scaling, var_class, var_name, var_type): if var_type == "BOOL": @@ -474,9 +483,9 @@ else: Viewer.AddVariableBlock(self, x, y, scaling, var_class, var_name, var_type) -#------------------------------------------------------------------------------- -# Adding element functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Adding element functions + # ------------------------------------------------------------------------------- def AddLadderRung(self): dialog = LDElementDialog(self.ParentWindow, self.Controler, "coil") @@ -491,7 +500,7 @@ if returntype == "BOOL": varlist.append(self.Controler.GetEditedElementName(self.TagName)) dialog.SetVariables(varlist) - dialog.SetValues({"name":"","type":COIL_NORMAL}) + dialog.SetValues({"name": "", "type": COIL_NORMAL}) if dialog.ShowModal() == wx.ID_OK: values = dialog.GetValues() startx, starty = LD_OFFSET[0], 0 @@ -500,7 +509,7 @@ starty = bbox.y + bbox.height starty += LD_OFFSET[1] rung = Graphic_Group(self) - + # Create comment id = self.GetNewId() comment = Comment(self, _("Comment"), id) @@ -511,7 +520,7 @@ self.Controler.AddEditedElementComment(self.TagName, id) self.RefreshCommentModel(comment) starty += LD_COMMENT_DEFAULTSIZE[1] + LD_OFFSET[1] - + # Create LeftPowerRail id = self.GetNewId() leftpowerrail = LD_PowerRail(self, LEFTRAIL, id) @@ -521,7 +530,7 @@ rung.SelectElement(leftpowerrail) self.Controler.AddEditedElementPowerRail(self.TagName, id, LEFTRAIL) self.RefreshPowerRailModel(leftpowerrail) - + # Create Coil id = self.GetNewId() coil = LD_Coil(self, values["type"], values["name"], id) @@ -530,7 +539,7 @@ self.AddBlock(coil) rung.SelectElement(coil) self.Controler.AddEditedElementCoil(self.TagName, id) - + # Create Wire between LeftPowerRail and Coil wire = Wire(self) start_connector = coil_connectors["inputs"][0] @@ -541,7 +550,7 @@ wire.ConnectEndPoint(None, end_connector) self.AddWire(wire) rung.SelectElement(wire) - + # Create RightPowerRail id = self.GetNewId() rightpowerrail = LD_PowerRail(self, RIGHTRAIL, id) @@ -550,7 +559,7 @@ self.AddBlock(rightpowerrail) rung.SelectElement(rightpowerrail) self.Controler.AddEditedElementPowerRail(self.TagName, id, RIGHTRAIL) - + # Create Wire between LeftPowerRail and Coil wire = Wire(self) start_connector = rightpowerrail_connectors["inputs"][0] @@ -574,7 +583,7 @@ left_element = self.SelectedElement.EndConnected if not isinstance(left_element.GetParentBlock(), LD_Coil): wires.append(self.SelectedElement) - elif self.SelectedElement and isinstance(self.SelectedElement,Graphic_Group): + elif self.SelectedElement and isinstance(self.SelectedElement, Graphic_Group): if False not in [self.IsWire(element) for element in self.SelectedElement.GetElements()]: for element in self.SelectedElement.GetElements(): wires.append(element) @@ -588,7 +597,7 @@ if var.Class != "Output" and var.Type == "BOOL": varlist.append(var.Name) dialog.SetVariables(varlist) - dialog.SetValues({"name":"","type":CONTACT_NORMAL}) + dialog.SetValues({"name": "", "type": CONTACT_NORMAL}) if dialog.ShowModal() == wx.ID_OK: values = dialog.GetValues() points = wires[0].GetSelectedSegmentPoints() @@ -663,7 +672,7 @@ self.RefreshVisibleElements() self.Refresh(False) else: - message = wx.MessageDialog(self, _("You must select the wire where a contact should be added!"), _("Error"), wx.OK|wx.ICON_ERROR) + message = wx.MessageDialog(self, _("You must select the wire where a contact should be added!"), _("Error"), wx.OK | wx.ICON_ERROR) message.ShowModal() message.Destroy() @@ -683,7 +692,7 @@ right_index = [] for block in blocks: connectors = block.GetConnectors() - block_infos = {"lefts":[],"rights":[]} + block_infos = {"lefts": [], "rights": []} block_infos.update(connectors) for connector in block_infos["inputs"]: for wire, handle in connector.GetWires(): @@ -778,7 +787,7 @@ if left_powerrail: powerrail = left_elements[0].GetParentBlock() index = 0 - for left_element in left_elements: + for left_element in left_elements: index = max(index, powerrail.GetConnectorIndex(left_element)) powerrail.InsertConnector(index + 1) powerrail.RefreshModel() @@ -805,17 +814,17 @@ if returntype == "BOOL": varlist.append(self.Controler.GetEditedElementName(self.TagName)) dialog.SetVariables(varlist) - dialog.SetValues({"name":"","type":COIL_NORMAL}) + dialog.SetValues({"name": "", "type": COIL_NORMAL}) if dialog.ShowModal() == wx.ID_OK: values = dialog.GetValues() powerrail = right_elements[0].GetParentBlock() index = 0 - for right_element in right_elements: + for right_element in right_elements: index = max(index, powerrail.GetConnectorIndex(right_element)) powerrail.InsertConnector(index + 1) powerrail.RefreshModel() connectors = powerrail.GetConnectors() - + # Create Coil id = self.GetNewId() coil = LD_Coil(self, values["type"], values["name"], id) @@ -825,7 +834,7 @@ rung.SelectElement(coil) self.Controler.AddEditedElementCoil(self.TagName, id) coil_connectors = coil.GetConnectors() - + # Create Wire between LeftPowerRail and Coil wire = Wire(self) connectors["inputs"][index + 1].Connect((wire, 0), False) @@ -835,7 +844,7 @@ self.AddWire(wire) rung.SelectElement(wire) left_elements.reverse() - + for i, left_element in enumerate(left_elements): # Create Wire between LeftPowerRail and Coil new_wire = Wire(self) @@ -844,7 +853,7 @@ left_element.InsertConnect(left_index[i] + 1, (new_wire, -1), False) new_wire.ConnectStartPoint(None, coil_connectors["inputs"][0]) new_wire.ConnectEndPoint(None, left_element) - + self.RefreshPosition(coil) else: left_elements.reverse() @@ -879,22 +888,22 @@ self.RefreshVisibleElements() self.Refresh(False) else: - message = wx.MessageDialog(self, _("The group of block must be coherent!"), _("Error"), wx.OK|wx.ICON_ERROR) + message = wx.MessageDialog(self, _("The group of block must be coherent!"), _("Error"), wx.OK | wx.ICON_ERROR) message.ShowModal() message.Destroy() else: - message = wx.MessageDialog(self, _("You must select the block or group of blocks around which a branch should be added!"), _("Error"), wx.OK|wx.ICON_ERROR) + message = wx.MessageDialog(self, _("You must select the block or group of blocks around which a branch should be added!"), _("Error"), wx.OK | wx.ICON_ERROR) message.ShowModal() message.Destroy() def AddLadderBlock(self): - message = wx.MessageDialog(self, _("This option isn't available yet!"), _("Warning"), wx.OK|wx.ICON_EXCLAMATION) + message = wx.MessageDialog(self, _("This option isn't available yet!"), _("Warning"), wx.OK | wx.ICON_EXCLAMATION) message.ShowModal() message.Destroy() -#------------------------------------------------------------------------------- -# Delete element functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Delete element functions + # ------------------------------------------------------------------------------- def DeleteContact(self, contact): if self.GetDrawingMode() == FREEDRAWING_MODE: @@ -1080,16 +1089,16 @@ self.RefreshRungs(new_bbox.height - old_bbox.height, rungindex + 1) self.SelectedElement = None -#------------------------------------------------------------------------------- -# Refresh element position functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Refresh element position functions + # ------------------------------------------------------------------------------- def RefreshPosition(self, element, recursive=True): # If element is LeftPowerRail, no need to update position if isinstance(element, LD_PowerRail) and element.GetType() == LEFTRAIL: element.RefreshModel() return - + # Extract max position of the elements connected to input connectors = element.GetConnectors() position = element.GetPosition() @@ -1103,7 +1112,7 @@ pos = leftblock.GetPosition() size = leftblock.GetSize() maxx = max(maxx, pos[0] + size[0]) - + # Refresh position of element if isinstance(element, LD_Coil): interval = LD_WIRECOIL_SIZE @@ -1114,13 +1123,13 @@ movex = maxx + interval - position[0] element.Move(movex, 0) position = element.GetPosition() - + # Extract blocks connected to inputs blocks = [] for i, connector in enumerate(connectors["inputs"]): for j, (wire, handle) in enumerate(connector.GetWires()): blocks.append(wire.EndConnected.GetParentBlock()) - + for i, connector in enumerate(connectors["inputs"]): startpoint = connector.GetPosition(False) previous_blocks = [] @@ -1168,13 +1177,13 @@ previous_blocks.append(block) blocks.remove(block) ExtractNextBlocks(block, block_list) - + element.RefreshModel(False) if recursive: for connector in connectors["outputs"]: for wire, handle in connector.GetWires(): self.RefreshPosition(wire.StartConnected.GetParentBlock()) - + def RefreshRungs(self, movey, fromidx): if movey != 0: for i in xrange(fromidx, len(self.Rungs)): @@ -1185,11 +1194,10 @@ if self.IsBlock(element): self.RefreshPosition(element) -#------------------------------------------------------------------------------- -# Edit element content functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Edit element content functions + # ------------------------------------------------------------------------------- def EditPowerRailContent(self, powerrail): if self.GetDrawingMode() == FREEDRAWING_MODE: Viewer.EditPowerRailContent(self, powerrail) - diff -r 31e63e25b4cc -r 64beb9e9c749 editors/ProjectNodeEditor.py --- a/editors/ProjectNodeEditor.py Mon Aug 21 20:17:19 2017 +0000 +++ b/editors/ProjectNodeEditor.py Mon Aug 21 23:22:58 2017 +0300 @@ -28,54 +28,55 @@ from EditorPanel import EditorPanel from ConfTreeNodeEditor import ConfTreeNodeEditor + class ProjectNodeEditor(ConfTreeNodeEditor): - + SHOW_BASE_PARAMS = False ENABLE_REQUIRED = True CONFNODEEDITOR_TABS = [ (_("Config variables"), "_create_VariablePanel"), (_("Project properties"), "_create_ProjectPropertiesPanel")] - + def _create_VariablePanel(self, prnt): self.VariableEditorPanel = VariablePanel(prnt, self, self.Controler, "config", self.Debug) self.VariableEditorPanel.SetTagName(self.TagName) - + return self.VariableEditorPanel - + def _create_ProjectPropertiesPanel(self, prnt): self.ProjectProperties = ProjectPropertiesPanel(prnt, self.Controler, self.ParentWindow, self.ENABLE_REQUIRED) - + return self.ProjectProperties - + def __init__(self, parent, controler, window): configuration = controler.GetProjectMainConfigurationName() if configuration is not None: tagname = controler.ComputeConfigurationName(configuration) else: tagname = "" - + ConfTreeNodeEditor.__init__(self, parent, controler, window, tagname) - + buttons_sizer = self.GenerateMethodButtonSizer() self.MainSizer.InsertSizer(0, buttons_sizer, 0, border=5, flag=wx.ALL) self.MainSizer.Layout() - + self.VariableEditor = self.VariableEditorPanel def GetTagName(self): return self.Controler.CTNName() - + def SetTagName(self, tagname): self.TagName = tagname if self.VariableEditor is not None: self.VariableEditor.SetTagName(tagname) - + def GetTitle(self): fullname = _(self.Controler.CTNName()) if self.Controler.CTNTestModified(): return "~%s~" % fullname return fullname - + def RefreshView(self, variablepanel=True): ConfTreeNodeEditor.RefreshView(self) self.VariableEditorPanel.RefreshView() @@ -83,12 +84,11 @@ def GetBufferState(self): return self.Controler.GetBufferState() - + def Undo(self): self.Controler.LoadPrevious() self.ParentWindow.CloseTabsWithoutModel() - + def Redo(self): self.Controler.LoadNext() self.ParentWindow.CloseTabsWithoutModel() - diff -r 31e63e25b4cc -r 64beb9e9c749 editors/ResourceEditor.py --- a/editors/ResourceEditor.py Mon Aug 21 20:17:19 2017 +0000 +++ b/editors/ResourceEditor.py Mon Aug 21 23:22:58 2017 +0300 @@ -32,13 +32,19 @@ from EditorPanel import EditorPanel from util.BitmapLibrary import GetBitmap from plcopen.structures import LOCATIONDATATYPES, TestIdentifier, IEC_KEYWORDS, DefaultType -#------------------------------------------------------------------------------- +from util.TranslationCatalogs import NoTranslate + + +# ------------------------------------------------------------------------------- # Configuration Editor class -#------------------------------------------------------------------------------- - -[ID_CONFIGURATIONEDITOR, +# ------------------------------------------------------------------------------- + + +[ + ID_CONFIGURATIONEDITOR, ] = [wx.NewId() for _init_ctrls in range(1)] + class ConfigurationEditor(EditorPanel): ID = ID_CONFIGURATIONEDITOR @@ -59,29 +65,36 @@ return self.Controler.GetEditedElement(self.TagName) is None -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Resource Editor class -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- def GetTasksTableColnames(): - _ = lambda x : x + _ = NoTranslate return [_("Name"), _("Triggering"), _("Single"), _("Interval"), _("Priority")] + def GetTaskTriggeringOptions(): - _ = lambda x : x + _ = NoTranslate return [_("Interrupt"), _("Cyclic")] + + TASKTRIGGERINGOPTIONS_DICT = dict([(_(option), option) for option in GetTaskTriggeringOptions()]) -SingleCellEditor = lambda *x : wx.grid.GridCellChoiceEditor() + +def SingleCellEditor(*x): + return wx.grid.GridCellChoiceEditor() + def CheckSingle(single, varlist): return single in varlist def GetInstancesTableColnames(): - _ = lambda x : x + _ = NoTranslate return [_("Name"), _("Type"), _("Task")] + class ResourceTable(CustomTable): """ @@ -154,12 +167,12 @@ if interval != "" and IEC_TIME_MODEL.match(interval.upper()) is None: error = True elif colname == "Single": - editor = SingleCellEditor(self,colname) + editor = SingleCellEditor(self, colname) editor.SetParameters(self.Parent.VariableList) if self.GetValueByName(row, "Triggering") != "Interrupt": grid.SetReadOnly(row, col, True) single = self.GetValueByName(row, colname) - if single != "" and not CheckSingle(single,self.Parent.VariableList): + if single != "" and not CheckSingle(single, self.Parent.VariableList): error = True elif colname == "Triggering": editor = wx.grid.GridCellChoiceEditor() @@ -185,10 +198,9 @@ grid.SetCellTextColour(row, col, highlight_colours[1]) self.ResizeRow(grid, row) - -#------------------------------------------------------------------------------- -# Highlights showing functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Highlights showing functions + # ------------------------------------------------------------------------------- def AddHighlight(self, infos, highlight_type): row_highlights = self.Highlights.setdefault(infos[0], {}) @@ -208,13 +220,12 @@ row_highlights.pop(col) - class ResourceEditor(EditorPanel): VARIABLE_PANEL_TYPE = "resource" def _init_Editor(self, parent): - self.Editor = wx.Panel(parent, style=wx.SUNKEN_BORDER|wx.TAB_TRAVERSAL) + self.Editor = wx.Panel(parent, style=wx.SUNKEN_BORDER | wx.TAB_TRAVERSAL) main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=5) main_sizer.AddGrowableCol(0) @@ -225,7 +236,7 @@ tasks_sizer.AddGrowableCol(0) tasks_sizer.AddGrowableRow(1) main_sizer.AddSizer(tasks_sizer, border=5, - flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) + flag=wx.GROW | wx.TOP | wx.LEFT | wx.RIGHT) tasks_buttons_sizer = wx.FlexGridSizer(cols=5, hgap=5, rows=1, vgap=0) tasks_buttons_sizer.AddGrowableCol(0) @@ -241,7 +252,9 @@ ("UpTaskButton", "up", _("Move task up")), ("DownTaskButton", "down", _("Move task down"))]: button = wx.lib.buttons.GenBitmapButton(self.Editor, - bitmap=GetBitmap(bitmap), size=wx.Size(28, 28), style=wx.NO_BORDER) + bitmap=GetBitmap(bitmap), + size=wx.Size(28, 28), + style=wx.NO_BORDER) button.SetToolTipString(help) setattr(self, name, button) tasks_buttons_sizer.AddWindow(button) @@ -254,7 +267,7 @@ instances_sizer.AddGrowableCol(0) instances_sizer.AddGrowableRow(1) main_sizer.AddSizer(instances_sizer, border=5, - flag=wx.GROW|wx.BOTTOM|wx.LEFT|wx.RIGHT) + flag=wx.GROW | wx.BOTTOM | wx.LEFT | wx.RIGHT) instances_buttons_sizer = wx.FlexGridSizer(cols=5, hgap=5, rows=1, vgap=0) instances_buttons_sizer.AddGrowableCol(0) @@ -269,15 +282,15 @@ ("DeleteInstanceButton", "remove_element", _("Remove instance")), ("UpInstanceButton", "up", _("Move instance up")), ("DownInstanceButton", "down", _("Move instance down"))]: - button = wx.lib.buttons.GenBitmapButton(self.Editor, - bitmap=GetBitmap(bitmap), size=wx.Size(28, 28), style=wx.NO_BORDER) + button = wx.lib.buttons.GenBitmapButton( + self.Editor, bitmap=GetBitmap(bitmap), + size=wx.Size(28, 28), style=wx.NO_BORDER) button.SetToolTipString(help) setattr(self, name, button) instances_buttons_sizer.AddWindow(button) self.InstancesGrid = CustomGrid(self.Editor, style=wx.VSCROLL) - self.InstancesGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, - self.OnInstancesGridCellChange) + self.InstancesGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.OnInstancesGridCellChange) instances_sizer.AddWindow(self.InstancesGrid, flag=wx.GROW) self.Editor.SetSizer(main_sizer) @@ -288,7 +301,7 @@ self.RefreshHighlightsTimer = wx.Timer(self, -1) self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer) - self.TasksDefaultValue = {"Name" : "", "Triggering" : "", "Single" : "", "Interval" : "", "Priority" : 0} + self.TasksDefaultValue = {"Name": "", "Triggering": "", "Single": "", "Interval": "", "Priority": 0} self.TasksTable = ResourceTable(self, [], GetTasksTableColnames()) self.TasksTable.SetColAlignements([wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_RIGHT, wx.ALIGN_RIGHT]) self.TasksTable.SetColSizes([200, 100, 100, 150, 100]) @@ -323,7 +336,7 @@ self.TasksTable.ResetView(self.TasksGrid) self.TasksGrid.RefreshButtons() - self.InstancesDefaultValue = {"Name" : "", "Type" : "", "Task" : ""} + self.InstancesDefaultValue = {"Name": "", "Type": "", "Task": ""} self.InstancesTable = ResourceTable(self, [], GetInstancesTableColnames()) self.InstancesTable.SetColAlignements([wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT]) self.InstancesTable.SetColSizes([200, 150, 150]) @@ -362,9 +375,11 @@ rows = self.InstancesTable.GetNumberRows() row = self.InstancesGrid.GetGridCursorRow() self.DeleteInstanceButton.Enable(rows > 0) - self.UpInstanceButton.Enable(row > 0 and + self.UpInstanceButton.Enable( + row > 0 and self.InstancesTable.GetValueByName(row, "Task") == self.InstancesTable.GetValueByName(row - 1, "Task")) - self.DownInstanceButton.Enable(0 <= row < rows - 1 and + self.DownInstanceButton.Enable( + 0 <= row < rows - 1 and self.InstancesTable.GetValueByName(row, "Task") == self.InstancesTable.GetValueByName(row + 1, "Task")) setattr(self.InstancesGrid, "RefreshButtons", _RefreshInstanceButtons) @@ -381,17 +396,17 @@ self.TypeList = "" blocktypes = self.Controler.GetBlockResource() for blocktype in blocktypes: - self.TypeList += ",%s"%blocktype + self.TypeList += ",%s" % blocktype def RefreshTaskList(self): self.TaskList = "" for row in xrange(self.TasksTable.GetNumberRows()): - self.TaskList += ",%s"%self.TasksTable.GetValueByName(row, "Name") + self.TaskList += ",%s" % self.TasksTable.GetValueByName(row, "Name") def RefreshVariableList(self): self.VariableList = "" for variable in self.Controler.GetEditedResourceVariables(self.TagName): - self.VariableList += ",%s"%variable + self.VariableList += ",%s" % variable def RefreshModel(self): self.Controler.SetEditedResourceInfos(self.TagName, self.TasksTable.GetData(), self.InstancesTable.GetData()) @@ -433,7 +448,7 @@ self.InstancesGrid.RefreshButtons() def ShowErrorMessage(self, message): - dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() @@ -447,7 +462,7 @@ 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 i, var in enumerate(self.TasksTable.data) if i!=row]: + elif value.upper() in [var["Name"].upper() for i, var in enumerate(self.TasksTable.data) if i != row]: message = _("A task with the same name already exists!") if message is not None: event.Veto() @@ -482,7 +497,7 @@ 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 i ,var in enumerate(self.InstancesTable.data) if i!=row]: + elif value.upper() in [var["Name"].upper() for i, var in enumerate(self.InstancesTable.data) if i != row]: message = _("An instance with the same name already exists!") if message is not None: event.Veto() @@ -494,9 +509,9 @@ self.InstancesGrid.RefreshButtons() event.Skip() -#------------------------------------------------------------------------------- -# Highlights showing functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Highlights showing functions + # ------------------------------------------------------------------------------- def OnRefreshHighlightsTimer(self, event): self.RefreshView() diff -r 31e63e25b4cc -r 64beb9e9c749 editors/SFCViewer.py --- a/editors/SFCViewer.py Mon Aug 21 20:17:19 2017 +0000 +++ b/editors/SFCViewer.py Mon Aug 21 23:22:58 2017 +0300 @@ -35,7 +35,7 @@ class SFC_Viewer(Viewer): - + SFC_StandardRules = { # The key of this dict is a block that user try to connect, # and the value is a list of blocks, that can be connected with the current block @@ -61,7 +61,7 @@ SELECTION_DIVERGENCE: [("SFC_Transition", SOUTH)], SELECTION_CONVERGENCE: [("SFC_Step", SOUTH), - ("SFC_Jump", SOUTH)], + ("SFC_Jump", SOUTH)], SIMULTANEOUS_DIVERGENCE: [("SFC_Step", SOUTH)], @@ -82,10 +82,10 @@ "LD_Coil": [("SFC_Transition", WEST)] } - def __init__(self, parent, tagname, window, controler, debug = False, instancepath = ""): + def __init__(self, parent, tagname, window, controler, debug=False, instancepath=""): Viewer.__init__(self, parent, tagname, window, controler, debug, instancepath) self.CurrentLanguage = "SFC" - + def ConnectConnectors(self, start, end): startpoint = [start.GetPosition(False), start.GetDirection()] endpoint = [end.GetPosition(False), end.GetDirection()] @@ -96,8 +96,8 @@ wire.ConnectStartPoint(None, start) wire.ConnectEndPoint(None, end) return wire - - def CreateTransition(self, connector, next = None): + + def CreateTransition(self, connector, next=None): previous = connector.GetParentBlock() id = self.GetNewId() transition = SFC_Transition(self, "reference", "", 0, id) @@ -124,7 +124,7 @@ next_block.RefreshPosition() transition.RefreshOutputModel(True) return transition - + def RemoveTransition(self, transition): connectors = transition.GetConnectors() input_wires = connectors["input"].GetWires() @@ -146,8 +146,8 @@ self.Controler.RemoveEditedElementInstance(self.TagName, transition.GetId()) wire = self.ConnectConnectors(next, previous) return wire - - def CreateStep(self, name, connector, next = None): + + def CreateStep(self, name, connector, next=None): previous = connector.GetParentBlock() id = self.GetNewId() step = SFC_Step(self, name, False, id) @@ -213,9 +213,9 @@ else: return None -#------------------------------------------------------------------------------- -# Mouse event functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Mouse event functions + # ------------------------------------------------------------------------------- def OnViewerLeftDown(self, event): if self.GetDrawingMode() == FREEDRAWING_MODE: @@ -289,7 +289,7 @@ self.SelectedElement.RefreshModel() self.SelectedElement.SetSelected(True) event.Skip() - + def OnViewerRightUp(self, event): if self.GetDrawingMode() == FREEDRAWING_MODE: Viewer.OnViewerRightUp(self, event) @@ -307,7 +307,7 @@ self.SelectedElement.Refresh() wx.CallAfter(self.SetCurrentCursor, 0) event.Skip() - + def OnViewerLeftDClick(self, event): if self.GetDrawingMode() == FREEDRAWING_MODE: Viewer.OnViewerLeftDClick(self, event) @@ -315,7 +315,7 @@ self.SelectedElement.OnLeftDClick(event, self.GetLogicalDC(), self.Scaling) self.Refresh(False) event.Skip() - + def OnViewerMotion(self, event): if self.GetDrawingMode() == FREEDRAWING_MODE: Viewer.OnViewerMotion(self, event) @@ -341,9 +341,9 @@ return blockName # This method check the IEC 61131-3 compatibility between two SFC blocks - def BlockCompatibility(self,startblock = None, endblock = None, direction = None): - if startblock!= None and endblock != None and (isinstance(startblock,SFC_Objects)\ - or isinstance(endblock,SFC_Objects)): + def BlockCompatibility(self, startblock=None, endblock=None, direction=None): + if startblock is not None and endblock is not None and \ + (isinstance(startblock, SFC_Objects) or isinstance(endblock, SFC_Objects)): # Full "SFC_StandardRules" table would be symmetrical and # to avoid duplicate records and minimize the table only upper part is defined. if (direction == SOUTH or direction == EAST): @@ -356,9 +356,9 @@ return False return True -#------------------------------------------------------------------------------- -# Keyboard event functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Keyboard event functions + # ------------------------------------------------------------------------------- def OnChar(self, event): if self.GetDrawingMode() == FREEDRAWING_MODE: @@ -424,13 +424,13 @@ self.RefreshRect(self.GetScrolledRect(self.SelectedElement.GetRedrawRect(0, scaling[1])), False) else: event.Skip() - -#------------------------------------------------------------------------------- -# Adding element functions -#------------------------------------------------------------------------------- + + # ------------------------------------------------------------------------------- + # Adding element functions + # ------------------------------------------------------------------------------- def AddInitialStep(self, pos): - dialog = SFCStepNameDialog(self.ParentWindow, _("Please enter step name"), _("Add a new initial step"), "", wx.OK|wx.CANCEL) + dialog = SFCStepNameDialog(self.ParentWindow, _("Please enter step name"), _("Add a new initial step"), "", wx.OK | wx.CANCEL) dialog.SetPouNames(self.Controler.GetProjectPouNames(self.Debug)) dialog.SetVariables(self.Controler.GetEditedElementInterfaceVars(self.TagName, debug=self.Debug)) dialog.SetStepNames([block.GetName() for block in self.Blocks if isinstance(block, SFC_Step)]) @@ -452,7 +452,7 @@ def AddStep(self): if self.SelectedElement in self.Wires or isinstance(self.SelectedElement, SFC_Step): - dialog = SFCStepNameDialog(self.ParentWindow, _("Add a new step"), _("Please enter step name"), "", wx.OK|wx.CANCEL) + dialog = SFCStepNameDialog(self.ParentWindow, _("Add a new step"), _("Please enter step name"), "", wx.OK | wx.CANCEL) dialog.SetPouNames(self.Controler.GetProjectPouNames(self.Debug)) dialog.SetVariables(self.Controler.GetEditedElementInterfaceVars(self.TagName, debug=self.Debug)) dialog.SetStepNames([block.GetName() for block in self.Blocks if isinstance(block, SFC_Step)]) @@ -503,7 +503,7 @@ self.RefreshScrollBars() self.Refresh(False) dialog.Destroy() - + def AddStepAction(self): if isinstance(self.SelectedElement, SFC_Step): connectors = self.SelectedElement.GetConnectors() @@ -532,9 +532,9 @@ self.RefreshScrollBars() self.Refresh(False) dialog.Destroy() - + def AddDivergence(self): - if self.SelectedElement in self.Wires or isinstance(self.SelectedElement, Graphic_Group) or isinstance(self.SelectedElement, SFC_Step): + if self.SelectedElement in self.Wires or isinstance(self.SelectedElement, Graphic_Group) or isinstance(self.SelectedElement, SFC_Step): dialog = SFCDivergenceDialog(self.ParentWindow) dialog.SetPreviewFont(self.GetFont()) if dialog.ShowModal() == wx.ID_OK: @@ -729,7 +729,7 @@ self.RefreshScrollBars() self.Refresh(False) dialog.Destroy() - + def AddDivergenceBranch(self, divergence): if isinstance(divergence, SFC_Divergence): if self.GetDrawingMode() == FREEDRAWING_MODE: @@ -750,7 +750,7 @@ self.RefreshBuffer() self.RefreshScrollBars() self.Refresh(False) - + def RemoveDivergenceBranch(self, divergence): if isinstance(divergence, SFC_Divergence): if self.GetDrawingMode() == FREEDRAWING_MODE: @@ -759,16 +759,18 @@ self.RefreshBuffer() self.RefreshScrollBars() self.Refresh(False) - + def AddJump(self): if isinstance(self.SelectedElement, SFC_Step) and not self.SelectedElement.Output: choices = [] for block in self.Blocks: if isinstance(block, SFC_Step): choices.append(block.GetName()) - dialog = wx.SingleChoiceDialog(self.ParentWindow, - _("Add a new jump"), _("Please choose a target"), - choices, wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL) + dialog = wx.SingleChoiceDialog(self.ParentWindow, + _("Add a new jump"), + _("Please choose a target"), + choices, + wx.DEFAULT_DIALOG_STYLE | wx.OK | wx.CANCEL) if dialog.ShowModal() == wx.ID_OK: value = dialog.GetStringSelection() self.SelectedElement.AddOutput() @@ -796,7 +798,7 @@ if self.GetDrawingMode() == FREEDRAWING_MODE: Viewer.EditStepContent(self, step) else: - dialog = SFCStepNameDialog(self.ParentWindow, _("Edit step name"), _("Please enter step name"), step.GetName(), wx.OK|wx.CANCEL) + dialog = SFCStepNameDialog(self.ParentWindow, _("Edit step name"), _("Please enter step name"), step.GetName(), wx.OK | wx.CANCEL) dialog.SetPouNames(self.Controler.GetProjectPouNames(self.Debug)) dialog.SetVariables(self.Controler.GetEditedElementInterfaceVars(self.TagName, debug=self.Debug)) dialog.SetStepNames([block.GetName() for block in self.Blocks if isinstance(block, SFC_Step) and block.GetName() != step.GetName()]) @@ -812,9 +814,9 @@ self.Refresh(False) dialog.Destroy() -#------------------------------------------------------------------------------- -# Delete element functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Delete element functions + # ------------------------------------------------------------------------------- def DeleteStep(self, step): if self.GetDrawingMode() == FREEDRAWING_MODE: @@ -884,7 +886,7 @@ self.DeleteDivergence(previous_block) else: self.RefreshDivergenceModel(previous_block) - + def DeleteTransition(self, transition): if self.GetDrawingMode() == FREEDRAWING_MODE: Viewer.DeleteTransition(self, transition) @@ -919,7 +921,7 @@ self.DeleteDivergence(next_block) else: self.RefreshDivergenceModel(next_block) - + def DeleteDivergence(self, divergence): if self.GetDrawingMode() == FREEDRAWING_MODE: Viewer.DeleteDivergence(self, divergence) @@ -979,8 +981,8 @@ next_pos = next.GetPosition(False) wire_size = GetWireSize(previous_block) previous_block.RefreshOutputPosition((0, previous_pos.y + wire_size - next_pos.y)) - wire.SetPoints([wx.Point(previous_pos.x, previous_pos.y + wire_size), - wx.Point(previous_pos.x, previous_pos.y)]) + wire.SetPoints([wx.Point(previous_pos.x, previous_pos.y + wire_size), + wx.Point(previous_pos.x, previous_pos.y)]) if isinstance(next_block, SFC_Divergence): next_block.RefreshPosition() previous_block.RefreshOutputModel(True) @@ -1009,12 +1011,12 @@ next_pos = next.GetPosition(False) wire_size = GetWireSize(previous_block) previous_block.RefreshOutputPosition((previous_pos.x - next_pos.x, previous_pos.y + wire_size - next_pos.y)) - wire.SetPoints([wx.Point(previous_pos.x, previous_pos.y + wire_size), - wx.Point(previous_pos.x, previous_pos.y)]) + wire.SetPoints([wx.Point(previous_pos.x, previous_pos.y + wire_size), + wx.Point(previous_pos.x, previous_pos.y)]) if isinstance(next_block, SFC_Divergence): next_block.RefreshPosition() previous_block.RefreshOutputModel(True) - + def DeleteJump(self, jump): if self.GetDrawingMode() == FREEDRAWING_MODE: Viewer.DeleteJump(self, jump) @@ -1049,7 +1051,7 @@ self.DeleteDivergence(previous_block) else: previous_block.RefreshModel() - + def DeleteActionBlock(self, actionblock): if self.GetDrawingMode() == FREEDRAWING_MODE: Viewer.DeleteActionBlock(self, actionblock) @@ -1069,14 +1071,14 @@ self.RefreshStepModel(step) step.RefreshOutputPosition() step.RefreshOutputModel(True) - + def DeleteWire(self, wire): if self.GetDrawingMode() == FREEDRAWING_MODE: Viewer.DeleteWire(self, wire) -#------------------------------------------------------------------------------- -# Model update functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Model update functions + # ------------------------------------------------------------------------------- def RefreshBlockModel(self, block): blockid = block.GetId() @@ -1087,4 +1089,3 @@ infos["width"], infos["height"] = block.GetSize() infos["connectors"] = block.GetConnectors() self.Controler.SetEditedElementBlockInfos(self.TagName, blockid, infos) - diff -r 31e63e25b4cc -r 64beb9e9c749 editors/TextViewer.py --- a/editors/TextViewer.py Mon Aug 21 20:17:19 2017 +0000 +++ b/editors/TextViewer.py Mon Aug 21 23:22:58 2017 +0300 @@ -33,9 +33,9 @@ from EditorPanel import EditorPanel from controls.CustomStyledTextCtrl import CustomStyledTextCtrl, faces, GetCursorPos, NAVIGATION_KEYS -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Textual programs Viewer class -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- NEWLINE = "\n" @@ -51,15 +51,16 @@ STC_PLC_EMPTY] = range(11) [SPACE, WORD, NUMBER, STRING, WSTRING, COMMENT, PRAGMA, DPRAGMA] = range(8) -[ID_TEXTVIEWER, ID_TEXTVIEWERTEXTCTRL, +[ + ID_TEXTVIEWER, ID_TEXTVIEWERTEXTCTRL, ] = [wx.NewId() for _init_ctrls in range(2)] re_texts = {} re_texts["letter"] = "[A-Za-z]" re_texts["digit"] = "[0-9]" -re_texts["identifier"] = "((?:%(letter)s|(?:_(?:%(letter)s|%(digit)s)))(?:_?(?:%(letter)s|%(digit)s))*)"%re_texts +re_texts["identifier"] = "((?:%(letter)s|(?:_(?:%(letter)s|%(digit)s)))(?:_?(?:%(letter)s|%(digit)s))*)" % re_texts IDENTIFIER_MODEL = re.compile(re_texts["identifier"]) -LABEL_MODEL = re.compile("[ \t\n]%(identifier)s:[ \t\n]"%re_texts) +LABEL_MODEL = re.compile("[ \t\n]%(identifier)s:[ \t\n]" % re_texts) EXTENSIBLE_PARAMETER = re.compile("IN[1-9][0-9]*$") HIGHLIGHT_TYPES = { @@ -67,15 +68,17 @@ SEARCH_RESULT_HIGHLIGHT: STC_PLC_SEARCH_RESULT, } + def LineStartswith(line, symbols): return reduce(lambda x, y: x or y, map(lambda x: line.startswith(x), symbols), False) + class TextViewer(EditorPanel): ID = ID_TEXTVIEWER if wx.VERSION < (2, 6, 0): - def Bind(self, event, function, id = None): + def Bind(self, event, function, id=None): if id is not None: event(self, id, function) else: @@ -83,7 +86,7 @@ def _init_Editor(self, prnt): self.Editor = CustomStyledTextCtrl(id=ID_TEXTVIEWERTEXTCTRL, - parent=prnt, name="TextViewer", size=wx.Size(0, 0), style=0) + 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) @@ -136,8 +139,8 @@ self.Editor.SetTabWidth(2) self.Editor.SetUseTabs(0) - self.Editor.SetModEventMask(wx.stc.STC_MOD_BEFOREINSERT| - wx.stc.STC_MOD_BEFOREDELETE| + self.Editor.SetModEventMask(wx.stc.STC_MOD_BEFOREINSERT | + wx.stc.STC_MOD_BEFOREDELETE | wx.stc.STC_PERFORMED_USER) self.Bind(wx.stc.EVT_STC_STYLENEEDED, self.OnStyleNeeded, id=ID_TEXTVIEWERTEXTCTRL) @@ -149,7 +152,7 @@ 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 = ""): + 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]) @@ -223,16 +226,16 @@ def OnModification(self, event): if not self.DisableEvents: mod_type = event.GetModificationType() - if mod_type&wx.stc.STC_MOD_BEFOREINSERT: - if self.CurrentAction == None: + if mod_type & wx.stc.STC_MOD_BEFOREINSERT: + if self.CurrentAction is None: self.StartBuffering() elif self.CurrentAction[0] != "Add" or self.CurrentAction[1] != event.GetPosition() - 1: self.Controler.EndBuffering() self.StartBuffering() self.CurrentAction = ("Add", event.GetPosition()) wx.CallAfter(self.RefreshModel) - elif mod_type&wx.stc.STC_MOD_BEFOREDELETE: - if self.CurrentAction == None: + elif mod_type & wx.stc.STC_MOD_BEFOREDELETE: + if self.CurrentAction is None: self.StartBuffering() elif self.CurrentAction[0] != "Delete" or self.CurrentAction[1] != event.GetPosition() + 1: self.Controler.EndBuffering() @@ -244,7 +247,7 @@ def OnDoDrop(self, event): try: values = eval(event.GetDragText()) - except: + except Exception: values = event.GetDragText() if isinstance(values, tuple): message = None @@ -259,25 +262,25 @@ blockinputs = None if values[1] != "function": if blockname == "": - dialog = wx.TextEntryDialog(self.ParentWindow, _("Block name"), _("Please enter a block name"), "", wx.OK|wx.CANCEL|wx.CENTRE) + dialog = wx.TextEntryDialog(self.ParentWindow, _("Block name"), _("Please enter a block name"), "", wx.OK | wx.CANCEL | wx.CENTRE) if dialog.ShowModal() == wx.ID_OK: blockname = dialog.GetValue() else: - event.SetDragText("") + event.SetDragText("") return dialog.Destroy() if blockname.upper() in [name.upper() for name in self.Controler.GetProjectPouNames(self.Debug)]: - message = _("\"%s\" pou already exists!")%blockname + message = _("\"%s\" pou already exists!") % blockname elif blockname.upper() in [name.upper() for name in self.Controler.GetEditedElementVariables(self.TagName, self.Debug)]: - message = _("\"%s\" element for this pou already exists!")%blockname + message = _("\"%s\" element for this pou already exists!") % blockname else: self.Controler.AddEditedElementPouVar(self.TagName, values[0], blockname) self.RefreshVariablePanel() self.RefreshVariableTree() blockinfo = self.Controler.GetBlockType(blocktype, blockinputs, self.Debug) hint = ',\n '.join( - [ " " + fctdecl[0]+" := (*"+fctdecl[1]+"*)" for fctdecl in blockinfo["inputs"]] + - [ " " + fctdecl[0]+" => (*"+fctdecl[1]+"*)" for fctdecl in blockinfo["outputs"]]) + [" " + fctdecl[0]+" := (*"+fctdecl[1]+"*)" for fctdecl in blockinfo["inputs"]] + + [" " + fctdecl[0]+" => (*"+fctdecl[1]+"*)" for fctdecl in blockinfo["outputs"]]) if values[1] == "function": event.SetDragText(blocktype+"(\n "+hint+")") else: @@ -296,16 +299,18 @@ if var_name is None: return elif var_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames(self.Debug)]: - message = _("\"%s\" pou already exists!")%var_name + message = _("\"%s\" pou already exists!") % var_name elif var_name.upper() in [name.upper() for name in self.Controler.GetEditedElementVariables(self.TagName, self.Debug)]: - message = _("\"%s\" element for this pou already exists!")%var_name + message = _("\"%s\" element for this pou already exists!") % var_name else: location = values[0] if not location.startswith("%"): - dialog = wx.SingleChoiceDialog(self.ParentWindow, - _("Select a variable class:"), _("Variable class"), - [_("Input"), _("Output"), _("Memory")], - wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL) + 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() else: @@ -324,7 +329,8 @@ var_type = values[2] else: var_type = LOCATIONDATATYPES.get(location[2], ["BOOL"])[0] - self.Controler.AddEditedElementPouVar(self.TagName, + self.Controler.AddEditedElementPouVar( + self.TagName, var_type, var_name, location=location, description=values[4]) self.RefreshVariablePanel() @@ -347,7 +353,7 @@ if var_name is None: return elif var_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames(self.Debug)]: - message = _("\"%s\" pou already exists!")%var_name + message = _("\"%s\" pou already exists!") % var_name else: var_type = values[2] if not var_name.upper() in [name.upper() for name in self.Controler.GetEditedElementVariables(self.TagName, self.Debug)]: @@ -370,7 +376,7 @@ if var_name is None: return elif var_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames(self.Debug)]: - message = _("\"%s\" pou already exists!")%var_name + message = _("\"%s\" pou already exists!") % var_name else: if not var_name.upper() in [name.upper() for name in self.Controler.GetEditedElementVariables(self.TagName, self.Debug)]: self.Controler.AddEditedElementPouExternalVar(self.TagName, values[2], var_name) @@ -386,7 +392,7 @@ else: message = _("Variable don't belong to this POU!") if message is not None: - dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() event.SetDragText("") @@ -434,7 +440,7 @@ self.ParentWindow.RefreshEditMenu() def ResetBuffer(self): - if self.CurrentAction != None: + if self.CurrentAction is not None: self.Controler.EndBuffering() self.CurrentAction = None @@ -473,7 +479,7 @@ self.SetText(new_text) new_cursor_pos = GetCursorPos(old_text, new_text) self.Editor.LineScroll(column, line) - if new_cursor_pos != None: + if new_cursor_pos is not None: self.Editor.GotoPos(new_cursor_pos) else: self.Editor.GotoPos(old_cursor_pos) @@ -493,9 +499,9 @@ if blocktype["type"] == "function" and blockname not in self.Keywords and blockname not in self.Variables.keys(): interface = dict([(name, {}) for name, type, modifier in blocktype["inputs"] + blocktype["outputs"] if name != '']) for param in ["EN", "ENO"]: - if not interface.has_key(param): + if param not in interface: interface[param] = {} - if self.Functions.has_key(blockname): + if blockname in self.Functions: self.Functions[blockname]["interface"].update(interface) self.Functions[blockname]["extensible"] |= blocktype["extensible"] else: @@ -543,7 +549,7 @@ else: 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): @@ -632,7 +638,7 @@ if len(self.CallStack) > 0: current_call = self.CallStack.pop() else: - current_call = None + current_call = None elif state == PRAGMA: if line.endswith("}"): self.SetStyling(current_pos - last_styled_pos, STC_PLC_EMPTY) @@ -909,9 +915,9 @@ self.Editor.AutoCompCancel() event.Skip() -#------------------------------------------------------------------------------- -# Highlights showing functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Highlights showing functions + # ------------------------------------------------------------------------------- def OnRefreshHighlightsTimer(self, event): self.RefreshView() @@ -941,8 +947,8 @@ 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 - (infos[1], start, end, highlight_type) in self.Highlights): + 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) @@ -961,4 +967,3 @@ 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) - diff -r 31e63e25b4cc -r 64beb9e9c749 editors/Viewer.py --- a/editors/Viewer.py Mon Aug 21 20:17:19 2017 +0000 +++ b/editors/Viewer.py Mon Aug 21 23:22:58 2017 +0300 @@ -46,9 +46,10 @@ CURSORS = None SFC_Objects = (SFC_Step, SFC_ActionBlock, SFC_Transition, SFC_Divergence, SFC_Jump) + def ResetCursors(): global CURSORS - if CURSORS == None: + if CURSORS is None: CURSORS = [wx.NullCursor, wx.StockCursor(wx.CURSOR_HAND), wx.StockCursor(wx.CURSOR_SIZENWSE), @@ -56,26 +57,30 @@ wx.StockCursor(wx.CURSOR_SIZEWE), wx.StockCursor(wx.CURSOR_SIZENS)] + def AppendMenu(parent, help, id, kind, text): if wx.VERSION >= (2, 6, 0): parent.Append(help=help, id=id, kind=kind, text=text) else: parent.Append(helpString=help, id=id, kind=kind, item=text) + if wx.Platform == '__WXMSW__': - faces = { 'times': 'Times New Roman', - 'mono' : 'Courier New', - 'helv' : 'Arial', - 'other': 'Comic Sans MS', - 'size' : 10, - } + faces = { + 'times': 'Times New Roman', + 'mono': 'Courier New', + 'helv': 'Arial', + 'other': 'Comic Sans MS', + 'size': 10, + } else: - faces = { 'times': 'Times', - 'mono' : 'Courier', - 'helv' : 'Helvetica', - 'other': 'new century schoolbook', - 'size' : 12, - } + faces = { + 'times': 'Times', + 'mono': 'Courier', + 'helv': 'Helvetica', + 'other': 'new century schoolbook', + 'size': 12, + } if wx.Platform == '__WXMSW__': MAX_ZOOMIN = 4 @@ -83,49 +88,67 @@ MAX_ZOOMIN = 7 ZOOM_FACTORS = [math.sqrt(2) ** x for x in xrange(-6, MAX_ZOOMIN)] + def GetVariableCreationFunction(variable_type): def variableCreationFunction(viewer, id, specific_values): - return FBD_Variable(viewer, variable_type, - specific_values.name, - specific_values.value_type, - id, - specific_values.execution_order) + return FBD_Variable(viewer, + variable_type, + specific_values.name, + specific_values.value_type, + id, + specific_values.execution_order) return variableCreationFunction + def GetConnectorCreationFunction(connector_type): def connectorCreationFunction(viewer, id, specific_values): - return FBD_Connector(viewer, connector_type, - specific_values.name, id) + return FBD_Connector(viewer, + connector_type, + specific_values.name, + id) return connectorCreationFunction + def commentCreationFunction(viewer, id, specific_values): return Comment(viewer, specific_values.content, id) + def GetPowerRailCreationFunction(powerrail_type): def powerRailCreationFunction(viewer, id, specific_values): - return LD_PowerRail(viewer, powerrail_type, id, - specific_values.connectors) + return LD_PowerRail(viewer, + powerrail_type, + id, + specific_values.connectors) return powerRailCreationFunction -NEGATED_VALUE = lambda x: x if x is not None else False -MODIFIER_VALUE = lambda x: x if x is not None else 'none' + +def NEGATED_VALUE(x): + return x if x is not None else False + + +def MODIFIER_VALUE(x): + return x if x is not None else 'none' + CONTACT_TYPES = {(True, "none"): CONTACT_REVERSE, (False, "rising"): CONTACT_RISING, (False, "falling"): CONTACT_FALLING} + def contactCreationFunction(viewer, id, specific_values): contact_type = CONTACT_TYPES.get((NEGATED_VALUE(specific_values.negated), MODIFIER_VALUE(specific_values.edge)), CONTACT_NORMAL) return LD_Contact(viewer, contact_type, specific_values.name, id) + COIL_TYPES = {(True, "none", "none"): COIL_REVERSE, (False, "none", "set"): COIL_SET, (False, "none", "reset"): COIL_RESET, (False, "rising", "none"): COIL_RISING, (False, "falling", "none"): COIL_FALLING} + def coilCreationFunction(viewer, id, specific_values): coil_type = COIL_TYPES.get((NEGATED_VALUE(specific_values.negated), MODIFIER_VALUE(specific_values.edge), @@ -133,36 +156,47 @@ COIL_NORMAL) return LD_Coil(viewer, coil_type, specific_values.name, id) + def stepCreationFunction(viewer, id, specific_values): - step = SFC_Step(viewer, specific_values.name, - specific_values.initial, id) + step = SFC_Step(viewer, + specific_values.name, + specific_values.initial, + id) if specific_values.action is not None: step.AddAction() connector = step.GetActionConnector() connector.SetPosition(wx.Point(*specific_values.action.position)) return step + def transitionCreationFunction(viewer, id, specific_values): - transition = SFC_Transition(viewer, specific_values.condition_type, - specific_values.condition, - specific_values.priority, id) + transition = SFC_Transition(viewer, + specific_values.condition_type, + specific_values.condition, + specific_values.priority, + id) return transition + divergence_types = [SELECTION_DIVERGENCE, SELECTION_CONVERGENCE, SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE] + def GetDivergenceCreationFunction(divergence_type): def divergenceCreationFunction(viewer, id, specific_values): return SFC_Divergence(viewer, divergence_type, - specific_values.connectors, id) + specific_values.connectors, id) return divergenceCreationFunction + def jumpCreationFunction(viewer, id, specific_values): return SFC_Jump(viewer, specific_values.target, id) + def actionBlockCreationFunction(viewer, id, specific_values): return SFC_ActionBlock(viewer, specific_values.actions, id) + ElementCreationFunctions = { "input": GetVariableCreationFunction(INPUT), "output": GetVariableCreationFunction(OUTPUT), @@ -184,6 +218,7 @@ "actionBlock": actionBlockCreationFunction, } + def sort_blocks(block_infos1, block_infos2): x1, y1 = block_infos1[0].GetPosition() x2, y2 = block_infos2[0].GetPosition() @@ -192,24 +227,27 @@ else: return cmp(y1, y2) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Graphic elements Viewer base class -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + # ID Constants for alignment menu items -[ID_VIEWERALIGNMENTMENUITEMS0, ID_VIEWERALIGNMENTMENUITEMS1, - ID_VIEWERALIGNMENTMENUITEMS2, ID_VIEWERALIGNMENTMENUITEMS4, - ID_VIEWERALIGNMENTMENUITEMS5, ID_VIEWERALIGNMENTMENUITEMS6, +[ + ID_VIEWERALIGNMENTMENUITEMS0, ID_VIEWERALIGNMENTMENUITEMS1, + ID_VIEWERALIGNMENTMENUITEMS2, ID_VIEWERALIGNMENTMENUITEMS4, + ID_VIEWERALIGNMENTMENUITEMS5, ID_VIEWERALIGNMENTMENUITEMS6, ] = [wx.NewId() for _init_coll_AlignmentMenu_Items in range(6)] # ID Constants for contextual menu items -[ID_VIEWERCONTEXTUALMENUITEMS0, ID_VIEWERCONTEXTUALMENUITEMS1, - ID_VIEWERCONTEXTUALMENUITEMS2, ID_VIEWERCONTEXTUALMENUITEMS3, - ID_VIEWERCONTEXTUALMENUITEMS5, ID_VIEWERCONTEXTUALMENUITEMS6, - ID_VIEWERCONTEXTUALMENUITEMS8, ID_VIEWERCONTEXTUALMENUITEMS9, - ID_VIEWERCONTEXTUALMENUITEMS11, ID_VIEWERCONTEXTUALMENUITEMS12, - ID_VIEWERCONTEXTUALMENUITEMS14, ID_VIEWERCONTEXTUALMENUITEMS16, - ID_VIEWERCONTEXTUALMENUITEMS17, +[ + ID_VIEWERCONTEXTUALMENUITEMS0, ID_VIEWERCONTEXTUALMENUITEMS1, + ID_VIEWERCONTEXTUALMENUITEMS2, ID_VIEWERCONTEXTUALMENUITEMS3, + ID_VIEWERCONTEXTUALMENUITEMS5, ID_VIEWERCONTEXTUALMENUITEMS6, + ID_VIEWERCONTEXTUALMENUITEMS8, ID_VIEWERCONTEXTUALMENUITEMS9, + ID_VIEWERCONTEXTUALMENUITEMS11, ID_VIEWERCONTEXTUALMENUITEMS12, + ID_VIEWERCONTEXTUALMENUITEMS14, ID_VIEWERCONTEXTUALMENUITEMS16, + ID_VIEWERCONTEXTUALMENUITEMS17, ] = [wx.NewId() for _init_coll_ContextualMenu_Items in range(13)] @@ -230,11 +268,11 @@ message = None try: values = eval(data) - except: - message = _("Invalid value \"%s\" for viewer block")%data + except Exception: + message = _("Invalid value \"%s\" for viewer block") % data values = None if not isinstance(values, TupleType): - message = _("Invalid value \"%s\" for viewer block")%data + message = _("Invalid value \"%s\" for viewer block") % data values = None if values is not None: if values[1] == "debug": @@ -244,11 +282,11 @@ elif values[1] in ["function", "functionBlock"]: words = tagname.split("::") if pou_name == values[0]: - message = _("\"%s\" can't use itself!")%pou_name + message = _("\"%s\" can't use itself!") % pou_name elif pou_type == "function" and values[1] != "function": message = _("Function Blocks can't be used in Functions!") elif self.ParentWindow.Controler.PouIsUsedBy(pou_name, values[0], self.ParentWindow.Debug): - message = _("\"{a1}\" is already used by \"{a2}\"!").format(a1 = pou_name, a2 = values[0]) + message = _("\"{a1}\" is already used by \"{a2}\"!").format(a1=pou_name, a2=values[0]) else: blockname = values[2] if len(values) > 3: @@ -258,12 +296,12 @@ if values[1] != "function" and blockname == "": blockname = self.ParentWindow.GenerateNewName(blocktype=values[0]) if blockname.upper() in [name.upper() for name in self.ParentWindow.Controler.GetProjectPouNames(self.ParentWindow.Debug)]: - message = _("\"%s\" pou already exists!")%blockname + message = _("\"%s\" pou already exists!") % blockname elif blockname.upper() in [name.upper() for name in self.ParentWindow.Controler.GetEditedElementVariables(tagname, self.ParentWindow.Debug)]: - message = _("\"%s\" element for this pou already exists!")%blockname + message = _("\"%s\" element for this pou already exists!") % blockname else: id = self.ParentWindow.GetNewId() - block = FBD_Block(self.ParentWindow, values[0], blockname, id, inputs = blockinputs) + block = FBD_Block(self.ParentWindow, values[0], blockname, id, inputs=blockinputs) width, height = block.GetMinSize() if scaling is not None: x = round(float(x) / float(scaling[0])) * scaling[0] @@ -285,10 +323,12 @@ if pou_type == "program": location = values[0] if not location.startswith("%"): - dialog = wx.SingleChoiceDialog(self.ParentWindow.ParentWindow, - _("Select a variable class:"), _("Variable class"), - [_("Input"), _("Output"), _("Memory")], - wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL) + dialog = wx.SingleChoiceDialog( + self.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() else: @@ -313,7 +353,7 @@ if var_name is None: return elif var_name.upper() in [name.upper() for name in self.ParentWindow.Controler.GetProjectPouNames(self.ParentWindow.Debug)]: - message = _("\"%s\" pou already exists!")%var_name + message = _("\"%s\" pou already exists!") % var_name elif not var_name.upper() in [name.upper() for name in self.ParentWindow.Controler.GetEditedElementVariables(tagname, self.ParentWindow.Debug)]: if location[1] == "Q": var_class = OUTPUT @@ -328,7 +368,7 @@ self.ParentWindow.ParentWindow.RefreshPouInstanceVariablesPanel() self.ParentWindow.AddVariableBlock(x, y, scaling, var_class, var_name, var_type) else: - message = _("\"%s\" element for this pou already exists!")%var_name + message = _("\"%s\" element for this pou already exists!") % var_name elif values[1] == "NamedConstant": if pou_type == "program": initval = values[0] @@ -343,7 +383,7 @@ if var_name is None: return elif var_name.upper() in [name.upper() for name in self.ParentWindow.Controler.GetProjectPouNames(self.ParentWindow.Debug)]: - message = _("\"%s\" pou already exists!")%var_name + message = _("\"%s\" pou already exists!") % var_name elif not var_name.upper() in [name.upper() for name in self.ParentWindow.Controler.GetEditedElementVariables(tagname, self.ParentWindow.Debug)]: var_class = INPUT var_type = values[2] @@ -352,7 +392,7 @@ self.ParentWindow.ParentWindow.RefreshPouInstanceVariablesPanel() self.ParentWindow.AddVariableBlock(x, y, scaling, var_class, var_name, var_type) else: - message = _("\"%s\" element for this pou already exists!")%var_name + message = _("\"%s\" element for this pou already exists!") % var_name elif values[1] == "Global": var_name = values[0] dlg = wx.TextEntryDialog( @@ -365,14 +405,14 @@ if var_name is None: return elif var_name.upper() in [name.upper() for name in self.ParentWindow.Controler.GetProjectPouNames(self.ParentWindow.Debug)]: - message = _("\"%s\" pou already exists!")%var_name + message = _("\"%s\" pou already exists!") % var_name elif not var_name.upper() in [name.upper() for name in self.ParentWindow.Controler.GetEditedElementVariables(tagname, self.ParentWindow.Debug)]: self.ParentWindow.Controler.AddEditedElementPouExternalVar(tagname, values[2], var_name) self.ParentWindow.RefreshVariablePanel() self.ParentWindow.ParentWindow.RefreshPouInstanceVariablesPanel() self.ParentWindow.AddVariableBlock(x, y, scaling, INPUT, var_name, values[2]) else: - message = _("\"%s\" element for this pou already exists!")%var_name + message = _("\"%s\" element for this pou already exists!") % var_name elif values[1] == "Constant": self.ParentWindow.AddVariableBlock(x, y, scaling, INPUT, values[0], None) elif values[3] == tagname: @@ -421,15 +461,14 @@ return AddVariableFunction def ShowMessage(self, message): - message = wx.MessageDialog(self.ParentWindow, message, _("Error"), wx.OK|wx.ICON_ERROR) + message = wx.MessageDialog(self.ParentWindow, message, _("Error"), wx.OK | wx.ICON_ERROR) message.ShowModal() message.Destroy() - class DebugInstanceName(DebugDataConsumer): VALUE_TRANSLATION = {True: _("Active"), False: _("Inactive")} - + def __init__(self, parent): DebugDataConsumer.__init__(self) self.Parent = parent @@ -437,7 +476,7 @@ self.ActionState = None self.x_offset = 2 self.y_offset = 2 - + def SetValue(self, value): self.ActionState = value if self.ActionState != self.ActionLastState: @@ -448,7 +487,7 @@ return _("Debug: %s") % self.Parent.InstancePath def GetRedrawRect(self): - x, y = self.Parent.CalcUnscrolledPosition(self.x_offset, self.y_offset) + x, y = self.Parent.CalcUnscrolledPosition(self.x_offset, self.y_offset) dc = self.Parent.GetLogicalDC() ipw, iph = dc.GetTextExtent(self.GetInstanceName()) vw, vh = 0, 0 @@ -462,13 +501,13 @@ scalex, scaley = dc.GetUserScale() dc.SetUserScale(1, 1) x, y = self.Parent.CalcUnscrolledPosition(self.x_offset, self.y_offset) - + text = self.GetInstanceName() if self.ActionState is not None: text += " (" dc.DrawText(text, x, y) - tw, th = dc.GetTextExtent(text) + tw, th = dc.GetTextExtent(text) if self.ActionState is not None: text = self.VALUE_TRANSLATION[self.ActionState] @@ -478,20 +517,20 @@ if self.ActionState: dc.SetTextForeground(wx.BLACK) tw = tw + dc.GetTextExtent(text)[0] - - text = ")" + + text = ")" dc.DrawText(text, x + tw, y) dc.SetUserScale(scalex, scaley) -""" -Class that implements a Viewer based on a wx.ScrolledWindow for drawing and -manipulating graphic elements -""" class Viewer(EditorPanel, DebugViewer): + """ + Class that implements a Viewer based on a wx.ScrolledWindow for drawing and + manipulating graphic elements + """ if wx.VERSION < (2, 6, 0): - def Bind(self, event, function, id = None): + def Bind(self, event, function, id=None): if id is not None: event(self, id, function) else: @@ -535,8 +574,9 @@ # Add Alignment Menu items to the given menu def AddAlignmentMenuItems(self, menu): - [ID_ALIGN_LEFT, ID_ALIGN_CENTER, ID_ALIGN_RIGHT, - ID_ALIGN_TOP, ID_ALIGN_MIDDLE, ID_ALIGN_BOTTOM, + [ + ID_ALIGN_LEFT, ID_ALIGN_CENTER, ID_ALIGN_RIGHT, + ID_ALIGN_TOP, ID_ALIGN_MIDDLE, ID_ALIGN_BOTTOM, ] = [wx.NewId() for i in xrange(6)] # Create menu items @@ -587,7 +627,8 @@ None]) if self.CurrentLanguage != "FBD": - [ID_ADD_POWER_RAIL, ID_ADD_CONTACT, ID_ADD_COIL, + [ + ID_ADD_POWER_RAIL, ID_ADD_CONTACT, ID_ADD_COIL, ] = [wx.NewId() for i in xrange(3)] # Create menu items @@ -602,8 +643,9 @@ menu.AppendSeparator() if self.CurrentLanguage == "SFC": - [ID_ADD_INITIAL_STEP, ID_ADD_STEP, ID_ADD_TRANSITION, - ID_ADD_ACTION_BLOCK, ID_ADD_DIVERGENCE, ID_ADD_JUMP, + [ + ID_ADD_INITIAL_STEP, ID_ADD_STEP, ID_ADD_TRANSITION, + ID_ADD_ACTION_BLOCK, ID_ADD_DIVERGENCE, ID_ADD_JUMP, ] = [wx.NewId() for i in xrange(6)] # Create menu items @@ -662,12 +704,12 @@ def _init_Editor(self, prnt): self.Editor = wx.ScrolledWindow(prnt, name="Viewer", - pos=wx.Point(0, 0), size=wx.Size(0, 0), - style=wx.HSCROLL | wx.VSCROLL) + pos=wx.Point(0, 0), size=wx.Size(0, 0), + style=wx.HSCROLL | wx.VSCROLL) self.Editor.ParentWindow = self # Create a new Viewer - def __init__(self, parent, tagname, window, controler, debug = False, instancepath = ""): + def __init__(self, parent, tagname, window, controler, debug=False, instancepath=""): self.VARIABLE_PANEL_TYPE = controler.GetPouType(tagname.split("::")[1]) EditorPanel.__init__(self, parent, tagname, window, controler, debug) @@ -675,7 +717,7 @@ # Adding a rubberband to Viewer self.rubberBand = RubberBand(viewer=self) - self.Editor.SetBackgroundColour(wx.Colour(255,255,255)) + self.Editor.SetBackgroundColour(wx.Colour(255, 255, 255)) self.Editor.SetBackgroundStyle(wx.BG_STYLE_CUSTOM) self.ResetView() self.LastClientSize = None @@ -722,17 +764,17 @@ self.ElementRefreshList_lock = Lock() dc = wx.ClientDC(self.Editor) - font = wx.Font(faces["size"], wx.SWISS, wx.NORMAL, wx.NORMAL, faceName = faces["mono"]) + font = wx.Font(faces["size"], wx.SWISS, wx.NORMAL, wx.NORMAL, faceName=faces["mono"]) dc.SetFont(font) width, height = dc.GetTextExtent("ABCDEFGHIJKLMNOPQRSTUVWXYZ") while width > 260: faces["size"] -= 1 - font = wx.Font(faces["size"], wx.SWISS, wx.NORMAL, wx.NORMAL, faceName = faces["mono"]) + font = wx.Font(faces["size"], wx.SWISS, wx.NORMAL, wx.NORMAL, faceName=faces["mono"]) dc.SetFont(font) width, height = dc.GetTextExtent("ABCDEFGHIJKLMNOPQRSTUVWXYZ") self.SetFont(font) self.MiniTextDC = wx.MemoryDC() - self.MiniTextDC.SetFont(wx.Font(faces["size"] * 0.75, wx.SWISS, wx.NORMAL, wx.NORMAL, faceName = faces["helv"])) + self.MiniTextDC.SetFont(wx.Font(faces["size"] * 0.75, wx.SWISS, wx.NORMAL, wx.NORMAL, faceName=faces["helv"])) self.CurrentScale = None self.SetScale(ZOOM_FACTORS.index(1.0), False) @@ -776,7 +818,7 @@ def GetScrolledRect(self, rect): rect.x, rect.y = self.Editor.CalcScrolledPosition(int(rect.x * self.ViewScale[0]), - int(rect.y * self.ViewScale[1])) + int(rect.y * self.ViewScale[1])) rect.width = int(rect.width * self.ViewScale[0]) + 2 rect.height = int(rect.height * self.ViewScale[1]) + 2 return rect @@ -899,9 +941,9 @@ def GetMiniFont(self): return self.MiniTextDC.GetFont() -#------------------------------------------------------------------------------- -# Element management functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Element management functions + # ------------------------------------------------------------------------------- def AddBlock(self, block): self.Blocks[block.GetId()] = block @@ -955,7 +997,7 @@ block.GetName() == name: blocks.append(block) return blocks - + def GetConnectorByName(self, name): for block in self.Blocks.itervalues(): if isinstance(block, FBD_Connector) and\ @@ -964,7 +1006,7 @@ return block return None - def RefreshVisibleElements(self, xp = None, yp = None): + def RefreshVisibleElements(self, xp=None, yp=None): x, y = self.Editor.CalcUnscrolledPosition(0, 0) if xp is not None: x = xp * self.Editor.GetScrollPixelsPerUnit()[0] @@ -991,14 +1033,14 @@ blockname = block.GetName() connectorname = element.GetName() if blockname != "": - iec_path = "%s.%s.%s"%(instance_path, blockname, connectorname) + iec_path = "%s.%s.%s" % (instance_path, blockname, connectorname) else: if connectorname == "": - iec_path = "%s.%s%d"%(instance_path, block.GetType(), block.GetId()) + iec_path = "%s.%s%d" % (instance_path, block.GetType(), block.GetId()) else: - iec_path = "%s.%s%d_%s"%(instance_path, block.GetType(), block.GetId(), connectorname) + iec_path = "%s.%s%d_%s" % (instance_path, block.GetType(), block.GetId(), connectorname) elif isinstance(block, FBD_Variable): - iec_path = "%s.%s"%(instance_path, block.GetName()) + iec_path = "%s.%s" % (instance_path, block.GetName()) elif isinstance(block, FBD_Connector): connection = self.GetConnectorByName(block.GetName()) if connection is not None: @@ -1006,14 +1048,14 @@ if len(connector.Wires) == 1: iec_path = self.GetElementIECPath(connector.Wires[0][0]) elif isinstance(element, LD_Contact): - iec_path = "%s.%s"%(instance_path, element.GetName()) + iec_path = "%s.%s" % (instance_path, element.GetName()) elif isinstance(element, SFC_Step): - iec_path = "%s.%s.X"%(instance_path, element.GetName()) + iec_path = "%s.%s.X" % (instance_path, element.GetName()) elif isinstance(element, SFC_Transition): connectors = element.GetConnectors() previous_steps = self.GetPreviousSteps(connectors["inputs"]) next_steps = self.GetNextSteps(connectors["outputs"]) - iec_path = "%s.%s->%s"%(instance_path, ",".join(previous_steps), ",".join(next_steps)) + iec_path = "%s.%s->%s" % (instance_path, ",".join(previous_steps), ",".join(next_steps)) return iec_path def GetWireModifier(self, wire): @@ -1042,9 +1084,9 @@ element.SetSize(width, height) element.RefreshModel() -#------------------------------------------------------------------------------- -# Reset functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Reset functions + # ------------------------------------------------------------------------------- # Resets Viewer lists def ResetView(self): @@ -1162,11 +1204,9 @@ self.RefreshVisibleElements() self.Editor.Refresh(False) - -#------------------------------------------------------------------------------- -# Refresh functions -#------------------------------------------------------------------------------- - + # ------------------------------------------------------------------------------- + # Refresh functions + # ------------------------------------------------------------------------------- def ElementNeedRefresh(self, element): self.ElementRefreshList_lock.acquire() @@ -1208,13 +1248,12 @@ self.ResetBuffer() instance = {} # List of ids of already loaded blocks - instances = self.Controler.GetEditedElementInstancesInfos(self.TagName, debug = self.Debug) + instances = self.Controler.GetEditedElementInstancesInfos(self.TagName, debug=self.Debug) # Load Blocks until they are all loaded while len(instances) > 0: self.loadInstance(instances.popitem(0)[1], instances, selection) - if (selection is not None and - isinstance(self.SelectedElement, Graphic_Group)): + if selection is not None and isinstance(self.SelectedElement, Graphic_Group): self.SelectedElement.RefreshWireExclusion() self.SelectedElement.RefreshBoundingBox() @@ -1222,7 +1261,7 @@ if self.TagName.split("::")[0] == "A" and self.Debug: self.AddDataConsumer("%s.Q" % self.InstancePath.upper(), self.InstanceName) - + for wire in self.Wires: if not wire.IsConnectedCompatible(): wire.SetValid(False) @@ -1302,7 +1341,8 @@ maxy = max(maxy, extent.y + extent.height) maxx = int(maxx * self.ViewScale[0]) maxy = int(maxy * self.ViewScale[1]) - self.Editor.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT, + self.Editor.SetScrollbars( + SCROLLBAR_UNIT, SCROLLBAR_UNIT, round(maxx / SCROLLBAR_UNIT) + width_incr, round(maxy / SCROLLBAR_UNIT) + height_incr, xstart, ystart, True) @@ -1345,7 +1385,7 @@ def loadInstance(self, instance, remaining_instances, selection): self.current_id = max(self.current_id, instance.id) creation_function = ElementCreationFunctions.get(instance.type, None) - connectors = {"inputs" : [], "outputs" : []} + connectors = {"inputs": [], "outputs": []} specific_values = instance.specific_values if creation_function is not None: element = creation_function(self, instance.id, specific_values) @@ -1384,10 +1424,11 @@ connectors["outputs"].pop(0) executionControl = True block_name = specific_values.name if specific_values.name is not None else "" - element = FBD_Block(self, instance.type, block_name, - instance.id, len(connectors["inputs"]), - connectors=connectors, executionControl=executionControl, - executionOrder=specific_values.execution_order) + element = FBD_Block( + self, instance.type, block_name, + instance.id, len(connectors["inputs"]), + connectors=connectors, executionControl=executionControl, + executionOrder=specific_values.execution_order) if isinstance(element, Comment): self.AddComment(element) else: @@ -1399,7 +1440,7 @@ connector_pos = wx.Point(*output_connector.position) if isinstance(element, FBD_Block): connector = element.GetConnector(connector_pos, - output_name = output_connector.name) + output_name=output_connector.name) elif i < len(connectors["outputs"]): connector = connectors["outputs"][i] else: @@ -1415,7 +1456,7 @@ connector_pos = wx.Point(*input_connector.position) if isinstance(element, FBD_Block): connector = element.GetConnector(connector_pos, - input_name = input_connector.name) + input_name=input_connector.name) elif i < len(connectors["inputs"]): connector = connectors["inputs"][i] else: @@ -1461,19 +1502,18 @@ wire = Wire(self) wire.SetPoints(points) else: - wire = Wire(self, - [wx.Point(*start_connector.GetPosition()), - start_connector.GetDirection()], - [wx.Point(*end_connector.GetPosition()), - end_connector.GetDirection()]) + wire = Wire( + self, + [wx.Point(*start_connector.GetPosition()), start_connector.GetDirection()], + [wx.Point(*end_connector.GetPosition()), end_connector.GetDirection()]) start_connector.Wires.append((wire, 0)) end_connector.Wires.append((wire, -1)) wire.StartConnected = start_connector wire.EndConnected = end_connector connected.RefreshConnectors() self.AddWire(wire) - if selection is not None and (\ - selection[1].get((id, refLocalId), False) or \ + if selection is not None and ( + selection[1].get((id, refLocalId), False) or selection[1].get((refLocalId, id), False)): self.SelectInGroup(wire) else: @@ -1487,12 +1527,12 @@ def IsEndType(self, type): return self.Controler.IsEndType(type) - def GetBlockType(self, type, inputs = None): + def GetBlockType(self, type, inputs=None): return self.Controler.GetBlockType(type, inputs, self.Debug) -#------------------------------------------------------------------------------- -# Search Element functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Search Element functions + # ------------------------------------------------------------------------------- def FindBlock(self, event): dc = self.GetLogicalDC() @@ -1510,7 +1550,7 @@ return wire return None - def FindElement(self, event, exclude_group = False, connectors = True): + def FindElement(self, event, exclude_group=False, connectors=True): dc = self.GetLogicalDC() pos = event.GetLogicalPosition(dc) if self.SelectedElement and not (exclude_group and isinstance(self.SelectedElement, Graphic_Group)): @@ -1521,12 +1561,12 @@ return element return None - def FindBlockConnector(self, pos, direction = None, exclude = None): + def FindBlockConnector(self, pos, direction=None, exclude=None): result, error = self.FindBlockConnectorWithError(pos, direction, exclude) return result - def FindBlockConnectorWithError(self, pos, direction = None, exclude = None): - error = False + def FindBlockConnectorWithError(self, pos, direction=None, exclude=None): + error = False startblock = None for block in self.Blocks.itervalues(): connector = block.TestConnector(pos, direction, exclude) @@ -1539,7 +1579,7 @@ error = True return connector, error return None, error - + def FindElementById(self, id): block = self.Blocks.get(id, None) if block is not None: @@ -1563,12 +1603,13 @@ self.SelectedElement.SetElements(self.GetElements()) self.SelectedElement.SetSelected(True) -#------------------------------------------------------------------------------- -# Popup menu functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Popup menu functions + # ------------------------------------------------------------------------------- def GetForceVariableMenuFunction(self, iec_path, element): iec_type = self.GetDataType(iec_path) + def ForceVariableFunction(event): if iec_type is not None: dialog = ForceVariableDialog(self.ParentWindow, iec_type, str(element.GetValue())) @@ -1611,7 +1652,7 @@ self.Editor.PopupMenu(menu) menu.Destroy() - def PopupBlockMenu(self, connector = None): + def PopupBlockMenu(self, connector=None): menu = wx.Menu(title='') if connector is not None and connector.IsCompatible("BOOL"): self.AddBlockPinMenuItems(menu, connector) @@ -1662,7 +1703,8 @@ if self.SelectedElement.GetStartConnected() in connected else self.SelectedElement.GetStartConnected()) - self.AddWireMenuItems(menu, delete, + self.AddWireMenuItems( + menu, delete, start_connector.GetDirection() == EAST and not isinstance(start_connector.GetParentBlock(), SFC_Step)) @@ -1695,9 +1737,9 @@ self.Editor.PopupMenu(menu) menu.Destroy() -#------------------------------------------------------------------------------- -# Menu items functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Menu items functions + # ------------------------------------------------------------------------------- def OnAlignLeftMenu(self, event): if self.SelectedElement is not None and isinstance(self.SelectedElement, Graphic_Group): @@ -1771,9 +1813,7 @@ def OnReplaceWireMenu(self, event): # Check that selected element is a wire before applying replace - if (self.SelectedElement is not None and - self.IsWire(self.SelectedElement)): - + if self.SelectedElement is not None and self.IsWire(self.SelectedElement): # Get wire redraw bbox to erase it from screen wire = self.SelectedElement redraw_rect = wire.GetRedrawRect() @@ -1844,8 +1884,8 @@ # Add Wire to Viewer and connect it to blocks new_wire = Wire(self, - [wx.Point(*start_point), connector.GetDirection()], - [wx.Point(*end_point), end_connector.GetDirection()]) + [wx.Point(*start_point), connector.GetDirection()], + [wx.Point(*end_point), end_connector.GetDirection()]) self.AddWire(new_wire) connector.Connect((new_wire, 0), False) end_connector.Connect((new_wire, -1), False) @@ -1883,7 +1923,7 @@ def OnEditBlockMenu(self, event): if self.SelectedElement is not None: - self.ParentWindow.EditProjectElement(ITEM_POU, "P::%s"%self.SelectedElement.GetType()) + self.ParentWindow.EditProjectElement(ITEM_POU, "P::%s" % self.SelectedElement.GetType()) def OnAdjustBlockSizeMenu(self, event): if self.SelectedElement is not None: @@ -1916,6 +1956,7 @@ def GetAddToWireMenuCallBack(self, func, *args): args += (self.SelectedElement,) + def AddToWireMenuCallBack(event): func(wx.Rect(0, 0, 0, 0), *args) return AddToWireMenuCallBack @@ -1925,16 +1966,16 @@ wx.CallAfter(func) return ClipboardCallback -#------------------------------------------------------------------------------- -# Mouse event functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Mouse event functions + # ------------------------------------------------------------------------------- def OnViewerMouseEvent(self, event): self.ResetBuffer() if event.Leaving() and self.ToolTipElement is not None: self.ToolTipElement.DestroyToolTip() elif (not event.Entering() and - gettime() - self.LastToolTipCheckTime > REFRESH_PERIOD): + gettime() - self.LastToolTipCheckTime > REFRESH_PERIOD): self.LastToolTipCheckTime = gettime() element = None if not event.Leaving() and not event.LeftUp() and not event.LeftDClick(): @@ -1991,7 +2032,7 @@ if element is None or element.TestHandle(event) == (0, 0): connector = self.FindBlockConnector(pos, self.SelectedElement.GetConnectionDirection()) if connector is not None: - event.Dragging = lambda : True + event.Dragging = lambda: True self.SelectedElement.OnMotion(event, dc, self.Scaling) if self.SelectedElement.EndConnected is not None: self.SelectedElement.ResetPoints() @@ -2130,8 +2171,8 @@ # Popup contextual menu menu = wx.Menu() self.AddMenuItems(menu, - [(wx.NewId(), wx.ITEM_NORMAL, text, '', callback) - for text, callback in items]) + [(wx.NewId(), wx.ITEM_NORMAL, text, '', callback) + for text, callback in items]) self.PopupMenu(menu) self.SelectedElement.StartConnected.HighlightParentBlock(False) @@ -2237,16 +2278,18 @@ "functionBlock": ITEM_FUNCTIONBLOCK, }.get(self.Controler.GetPouType(instance_type)) if pou_type is not None and instance_type in self.Controler.GetProjectPouNames(self.Debug): - self.ParentWindow.OpenDebugViewer(pou_type, - "%s.%s"%(self.GetInstancePath(True), self.SelectedElement.GetName()), + self.ParentWindow.OpenDebugViewer( + pou_type, + "%s.%s" % (self.GetInstancePath(True), self.SelectedElement.GetName()), self.Controler.ComputePouName(instance_type)) else: iec_path = self.GetElementIECPath(self.SelectedElement) if iec_path is not None: if isinstance(self.SelectedElement, Wire): if self.SelectedElement.EndConnected is not None: - self.ParentWindow.OpenDebugViewer(ITEM_VAR_LOCAL, iec_path, - self.SelectedElement.EndConnected.GetType()) + self.ParentWindow.OpenDebugViewer( + ITEM_VAR_LOCAL, iec_path, + self.SelectedElement.EndConnected.GetType()) else: self.ParentWindow.OpenDebugViewer(ITEM_VAR_LOCAL, iec_path, "BOOL") elif event.ControlDown() and not event.ShiftDown(): @@ -2256,8 +2299,9 @@ else: instance_type = None if instance_type in self.Controler.GetProjectPouNames(self.Debug): - self.ParentWindow.EditProjectElement(ITEM_POU, - self.Controler.ComputePouName(instance_type)) + self.ParentWindow.EditProjectElement( + ITEM_POU, + self.Controler.ComputePouName(instance_type)) else: self.SelectedElement.OnLeftDClick(event, self.GetLogicalDC(), self.Scaling) elif event.ControlDown() and event.ShiftDown(): @@ -2290,8 +2334,7 @@ self.RefreshScrollBars() self.RefreshVisibleElements() else: - if (not event.Dragging() and - gettime() - self.LastHighlightCheckTime > REFRESH_PERIOD): + if not event.Dragging() and (gettime() - self.LastHighlightCheckTime) > REFRESH_PERIOD: self.LastHighlightCheckTime = gettime() highlighted = self.FindElement(event, connectors=False) if self.HighlightedElement is not None and self.HighlightedElement != highlighted: @@ -2308,8 +2351,8 @@ elif not self.Debug and self.Mode == MODE_SELECTION and self.SelectedElement is not None: if self.DrawingWire: connector, errorHighlight = self.FindBlockConnectorWithError(pos, self.SelectedElement.GetConnectionDirection(), self.SelectedElement.EndConnected) - self.SelectedElement.ErrHighlight = errorHighlight; - if not connector or self.SelectedElement.EndConnected == None: + self.SelectedElement.ErrHighlight = errorHighlight + if not connector or self.SelectedElement.EndConnected is None: self.SelectedElement.ResetPoints() movex, movey = self.SelectedElement.OnMotion(event, dc, self.Scaling) self.SelectedElement.GeneratePoints() @@ -2371,11 +2414,11 @@ elif position.y > window_size[1] - SCROLL_ZONE: move_window.y = 1 if move_window.x != 0 or move_window.y != 0: - self.RefreshVisibleElements(xp = xstart + move_window.x, yp = ystart + move_window.y) + self.RefreshVisibleElements(xp=xstart + move_window.x, yp=ystart + move_window.y) self.Scroll(xstart + move_window.x, ystart + move_window.y) self.RefreshScrollBars(move_window.x, move_window.y) - def BlockCompatibility(self, startblock=None, endblock=None, direction = None): + def BlockCompatibility(self, startblock=None, endblock=None, direction=None): return True def GetPopupMenuItems(self): @@ -2423,9 +2466,9 @@ (_(u'Variable'), self.GetAddToWireMenuCallBack(self.AddNewVariable, True))) return items -#------------------------------------------------------------------------------- -# Keyboard event functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Keyboard event functions + # ------------------------------------------------------------------------------- ARROW_KEY_MOVE = { wx.WXK_LEFT: (-1, 0), @@ -2453,7 +2496,7 @@ self.RefreshRect(self.GetScrolledRect(rect), False) elif not self.Debug and keycode == wx.WXK_RETURN and self.SelectedElement is not None: self.SelectedElement.OnLeftDClick(event, self.GetLogicalDC(), self.Scaling) - elif self.ARROW_KEY_MOVE.has_key(keycode): + elif keycode in self.ARROW_KEY_MOVE: move = self.ARROW_KEY_MOVE[keycode] if event.ControlDown() and event.ShiftDown(): self.Scroll({-1: 0, 0: xpos, 1: xmax}[move[0]], @@ -2501,9 +2544,9 @@ else: event.Skip() -#------------------------------------------------------------------------------- -# Model adding functions from Drop Target -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Model adding functions from Drop Target + # ------------------------------------------------------------------------------- def AddVariableBlock(self, x, y, scaling, var_class, var_name, var_type): id = self.GetNewId() @@ -2524,9 +2567,9 @@ self.RefreshVisibleElements() self.Editor.Refresh(False) -#------------------------------------------------------------------------------- -# Model adding functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Model adding functions + # ------------------------------------------------------------------------------- def GetScaledSize(self, width, height): if self.Scaling is not None: @@ -2571,10 +2614,11 @@ id = self.GetNewId() values = dialog.GetValues() values.setdefault("name", "") - block = FBD_Block(self, values["type"], values["name"], id, - values["extension"], values["inputs"], - executionControl = values["executionControl"], - executionOrder = values["executionOrder"]) + block = FBD_Block( + self, values["type"], values["name"], id, + values["extension"], values["inputs"], + executionControl=values["executionControl"], + executionOrder=values["executionOrder"]) self.Controler.AddEditedElementBlock(self.TagName, id, values["type"], values.get("name", None)) connector = None if wire is not None: @@ -2585,7 +2629,7 @@ break self.AddNewElement(block, bbox, wire, connector) self.RefreshVariablePanel() - self.ParentWindow.RefreshPouInstanceVariablesPanel() + self.ParentWindow.RefreshPouInstanceVariablesPanel() dialog.Destroy() def AddNewVariable(self, bbox, exclude_input=False, wire=None): @@ -2625,7 +2669,7 @@ dialog = wx.TextEntryDialog(self.ParentWindow, _("Edit comment"), _("Please enter comment text"), - "", wx.OK|wx.CANCEL|wx.TE_MULTILINE) + "", wx.OK | wx.CANCEL | wx.TE_MULTILINE) dialog.SetClientSize(wx.Size(400, 200)) if dialog.ShowModal() == wx.ID_OK: value = dialog.GetValue() @@ -2633,7 +2677,7 @@ comment = Comment(self, value, id) comment.SetPosition(bbox.x, bbox.y) min_width, min_height = comment.GetMinSize() - comment.SetSize(*self.GetScaledSize(max(min_width,bbox.width),max(min_height,bbox.height))) + comment.SetSize(*self.GetScaledSize(max(min_width, bbox.width), max(min_height, bbox.height))) self.AddComment(comment) self.Controler.AddEditedElementComment(self.TagName, id) self.RefreshCommentModel(comment) @@ -2689,11 +2733,11 @@ def AddNewStep(self, bbox, initial=False, wire=None): if wire is not None: values = { - "name": self.Controler.GenerateNewName( - self.TagName, None, "Step%d", 0), - "input": True, + "name": self.Controler.GenerateNewName(self.TagName, None, "Step%d", 0), + "input": True, "output": True, - "action":False} + "action": False + } else: dialog = SFCStepDialog(self.ParentWindow, self.Controler, self.TagName, initial) dialog.SetPreviewFont(self.GetFont()) @@ -2736,7 +2780,7 @@ connector = transition.GetConnectors()["inputs"][0] self.AddNewElement(transition, bbox, wire, connector) - def AddNewDivergence(self, bbox, poss_div_types = None, wire=None): + def AddNewDivergence(self, bbox, poss_div_types=None, wire=None): dialog = SFCDivergenceDialog(self.ParentWindow, self.Controler, self.TagName, poss_div_types) dialog.SetPreviewFont(self.GetFont()) dialog.SetMinElementSize((bbox.width, bbox.height)) @@ -2754,8 +2798,10 @@ if isinstance(block, SFC_Step): choices.append(block.GetName()) dialog = wx.SingleChoiceDialog(self.ParentWindow, - _("Add a new jump"), _("Please choose a target"), - choices, wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL) + _("Add a new jump"), + _("Please choose a target"), + choices, + wx.DEFAULT_DIALOG_STYLE | wx.OK | wx.CANCEL) if dialog.ShowModal() == wx.ID_OK: id = self.GetNewId() jump = SFC_Jump(self, dialog.GetStringSelection(), id) @@ -2775,20 +2821,22 @@ self.AddNewElement(actionblock, bbox, wire) dialog.Destroy() -#------------------------------------------------------------------------------- -# Edit element content functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Edit element content functions + # ------------------------------------------------------------------------------- def EditBlockContent(self, block): dialog = FBDBlockDialog(self.ParentWindow, self.Controler, self.TagName) dialog.SetPreviewFont(self.GetFont()) dialog.SetMinElementSize(block.GetSize()) - old_values = {"name" : block.GetName(), - "type" : block.GetType(), - "extension" : block.GetExtension(), - "inputs" : block.GetInputTypes(), - "executionControl" : block.GetExecutionControl(), - "executionOrder" : block.GetExecutionOrder()} + old_values = { + "name": block.GetName(), + "type": block.GetType(), + "extension": block.GetExtension(), + "inputs": block.GetInputTypes(), + "executionControl": block.GetExecutionControl(), + "executionOrder": block.GetExecutionOrder() + } dialog.SetValues(old_values) if dialog.ShowModal() == wx.ID_OK: new_values = dialog.GetValues() @@ -2798,7 +2846,7 @@ else: block.SetName("") block.SetSize(*self.GetScaledSize(new_values["width"], new_values["height"])) - block.SetType(new_values["type"], new_values["extension"], executionControl = new_values["executionControl"]) + block.SetType(new_values["type"], new_values["extension"], executionControl=new_values["executionControl"]) block.SetExecutionOrder(new_values["executionOrder"]) rect = rect.Union(block.GetRedrawRect()) self.RefreshBlockModel(block) @@ -2817,8 +2865,11 @@ dialog = FBDVariableDialog(self.ParentWindow, self.Controler, self.TagName) dialog.SetPreviewFont(self.GetFont()) dialog.SetMinElementSize(variable.GetSize()) - old_values = {"expression" : variable.GetName(), "class" : variable.GetType(), - "executionOrder" : variable.GetExecutionOrder()} + old_values = { + "expression": variable.GetName(), + "class": variable.GetType(), + "executionOrder": variable.GetExecutionOrder() + } dialog.SetValues(old_values) if dialog.ShowModal() == wx.ID_OK: new_values = dialog.GetValues() @@ -2846,7 +2897,7 @@ dialog = ConnectionDialog(self.ParentWindow, self.Controler, self.TagName, True) dialog.SetPreviewFont(self.GetFont()) dialog.SetMinElementSize(connection.GetSize()) - values = {"name" : connection.GetName(), "type" : connection.GetType()} + values = {"name": connection.GetName(), "type": connection.GetType()} dialog.SetValues(values) result = dialog.ShowModal() dialog.Destroy() @@ -2878,8 +2929,8 @@ dialog = LDElementDialog(self.ParentWindow, self.Controler, self.TagName, "contact") dialog.SetPreviewFont(self.GetFont()) dialog.SetMinElementSize(contact.GetSize()) - dialog.SetValues({"variable" : contact.GetName(), - "modifier" : contact.GetType()}) + dialog.SetValues({"variable": contact.GetName(), + "modifier": contact.GetType()}) if dialog.ShowModal() == wx.ID_OK: values = dialog.GetValues() rect = contact.GetRedrawRect(1, 1) @@ -2898,8 +2949,8 @@ dialog = LDElementDialog(self.ParentWindow, self.Controler, self.TagName, "coil") dialog.SetPreviewFont(self.GetFont()) dialog.SetMinElementSize(coil.GetSize()) - dialog.SetValues({"variable" : coil.GetName(), - "modifier" : coil.GetType()}) + dialog.SetValues({"variable": coil.GetName(), + "modifier": coil.GetType()}) if dialog.ShowModal() == wx.ID_OK: values = dialog.GetValues() rect = coil.GetRedrawRect(1, 1) @@ -2946,10 +2997,10 @@ dialog.SetMinElementSize(step.GetSize()) connectors = step.GetConnectors() dialog.SetValues({ - "name" : step.GetName(), + "name": step.GetName(), "input": len(connectors["inputs"]) > 0, "output": len(connectors["outputs"]) > 0, - "action": step.GetActionConnector() != None}) + "action": step.GetActionConnector() is not None}) if dialog.ShowModal() == wx.ID_OK: values = dialog.GetValues() rect = step.GetRedrawRect(1, 1) @@ -2966,7 +3017,7 @@ rect = rect.Union(block.GetRedrawRect()) block.Refresh(rect) step.SetName(new_name) - + if values["input"]: step.AddInput() else: @@ -2991,11 +3042,15 @@ dialog = SFCTransitionDialog(self.ParentWindow, self.Controler, self.TagName, self.GetDrawingMode() == FREEDRAWING_MODE) dialog.SetPreviewFont(self.GetFont()) dialog.SetMinElementSize(transition.GetSize()) - dialog.SetValues({"type":transition.GetType(),"value":transition.GetCondition(), "priority":transition.GetPriority()}) + dialog.SetValues({ + "type": transition.GetType(), + "value": transition.GetCondition(), + "priority": transition.GetPriority() + }) if dialog.ShowModal() == wx.ID_OK: values = dialog.GetValues() rect = transition.GetRedrawRect(1, 1) - transition.SetType(values["type"],values["value"]) + transition.SetType(values["type"], values["value"]) transition.SetPriority(values["priority"]) rect = rect.Union(transition.GetRedrawRect()) self.RefreshTransitionModel(transition) @@ -3011,8 +3066,10 @@ if isinstance(block, SFC_Step): choices.append(block.GetName()) dialog = wx.SingleChoiceDialog(self.ParentWindow, - _("Edit jump target"), _("Please choose a target"), - choices, wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL) + _("Edit jump target"), + _("Please choose a target"), + choices, + wx.DEFAULT_DIALOG_STYLE | wx.OK | wx.CANCEL) try: indx = choices.index(jump.GetTarget()) dialog.SetSelection(indx) @@ -3055,7 +3112,7 @@ _("Edit comment"), _("Please enter comment text"), comment.GetContent(), - wx.OK|wx.CANCEL|wx.TE_MULTILINE) + wx.OK | wx.CANCEL | wx.TE_MULTILINE) width, height = comment.GetSize() dialogSize = wx.Size(max(width + 30, 400), max(height + 60, 200)) dialog.SetClientSize(dialogSize) @@ -3072,9 +3129,9 @@ comment.Refresh(rect) dialog.Destroy() -#------------------------------------------------------------------------------- -# Model update functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Model update functions + # ------------------------------------------------------------------------------- def RefreshBlockModel(self, block): blockid = block.GetId() @@ -3221,11 +3278,9 @@ infos["connector"] = actionblock.GetConnector() self.Controler.SetEditedElementActionBlockInfos(self.TagName, actionblockid, infos) - -#------------------------------------------------------------------------------- -# Model delete functions -#------------------------------------------------------------------------------- - + # ------------------------------------------------------------------------------- + # Model delete functions + # ------------------------------------------------------------------------------- def DeleteBlock(self, block): elements = [] @@ -3372,10 +3427,9 @@ self.RemoveBlock(actionblock) self.Controler.RemoveEditedElementInstance(self.TagName, actionblock.GetId()) - -#------------------------------------------------------------------------------- -# Editing functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Editing functions + # ------------------------------------------------------------------------------- def Cut(self): if not self.Debug and (self.IsBlock(self.SelectedElement) or self.IsComment(self.SelectedElement) or isinstance(self.SelectedElement, Graphic_Group)): @@ -3418,7 +3472,7 @@ self.RefreshVariablePanel() self.ParentWindow.RefreshPouInstanceVariablesPanel() else: - message = wx.MessageDialog(self.Editor, result, "Error", wx.OK|wx.ICON_ERROR) + message = wx.MessageDialog(self.Editor, result, "Error", wx.OK | wx.ICON_ERROR) message.ShowModal() message.Destroy() @@ -3506,9 +3560,9 @@ self.Controler.AddEditedElementActionBlock(self.TagName, block.GetId()) self.RefreshActionBlockModel(block) -#------------------------------------------------------------------------------- -# Find and Replace functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Find and Replace functions + # ------------------------------------------------------------------------------- def Find(self, direction, search_params): if self.SearchParams != search_params: @@ -3549,9 +3603,9 @@ self.RemoveHighlight(*self.CurrentFindHighlight) self.CurrentFindHighlight = None -#------------------------------------------------------------------------------- -# Highlights showing functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Highlights showing functions + # ------------------------------------------------------------------------------- def OnRefreshHighlightsTimer(self, event): self.RefreshView() @@ -3590,9 +3644,9 @@ if block is not None: block.AddHighlight(infos[2:], start, end, highlight_type) -#------------------------------------------------------------------------------- -# Drawing functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Drawing functions + # ------------------------------------------------------------------------------- def OnScrollWindow(self, event): if self.Editor.HasCapture() and self.StartMousePos is not None: @@ -3602,9 +3656,9 @@ self.Editor.Freeze() wx.CallAfter(self.Editor.Thaw) elif event.GetOrientation() == wx.HORIZONTAL: - self.RefreshVisibleElements(xp = event.GetPosition()) + self.RefreshVisibleElements(xp=event.GetPosition()) else: - self.RefreshVisibleElements(yp = event.GetPosition()) + self.RefreshVisibleElements(yp=event.GetPosition()) # Handle scroll in debug to fully redraw area and ensuring # instance path is fully draw without flickering @@ -3627,16 +3681,16 @@ if event.ShiftDown(): x, y = self.GetViewStart() xp = max(0, min(x - rotation * 3, self.Editor.GetVirtualSize()[0] / self.Editor.GetScrollPixelsPerUnit()[0])) - self.RefreshVisibleElements(xp = xp) + self.RefreshVisibleElements(xp=xp) self.Scroll(xp, y) elif event.ControlDown(): dc = self.GetLogicalDC() - self.SetScale(self.CurrentScale + rotation, mouse_event = event) + self.SetScale(self.CurrentScale + rotation, mouse_event=event) self.ParentWindow.RefreshDisplayMenu() else: x, y = self.GetViewStart() yp = max(0, min(y - rotation * 3, self.Editor.GetVirtualSize()[1] / self.Editor.GetScrollPixelsPerUnit()[1])) - self.RefreshVisibleElements(yp = yp) + self.RefreshVisibleElements(yp=yp) self.Scroll(x, yp) def OnMoveWindow(self, event): @@ -3647,7 +3701,7 @@ self.RefreshVisibleElements() event.Skip() - def DoDrawing(self, dc, printing = False): + def DoDrawing(self, dc, printing=False): if printing: if getattr(dc, "printing", False): font = wx.Font(self.GetFont().GetPointSize(), wx.MODERN, wx.NORMAL, wx.NORMAL) @@ -3684,11 +3738,11 @@ comment.Draw(dc) for wire in self.Wires.iterkeys(): if wire != self.SelectedElement and (wire.IsVisible() or printing): - if not self.Debug or wire.GetValue() != True: + if not self.Debug or not wire.GetValue(): wire.Draw(dc) if self.Debug: for wire in self.Wires.iterkeys(): - if wire != self.SelectedElement and (wire.IsVisible() or printing) and wire.GetValue() == True: + if wire != self.SelectedElement and (wire.IsVisible() or printing) and wire.GetValue(): wire.Draw(dc) for block in self.Blocks.itervalues(): if block != self.SelectedElement and (block.IsVisible() or printing): @@ -3711,5 +3765,3 @@ if self.Debug: DebugViewer.RefreshNewData(self) event.Skip() - - diff -r 31e63e25b4cc -r 64beb9e9c749 graphics/DebugDataConsumer.py --- a/graphics/DebugDataConsumer.py Mon Aug 21 20:17:19 2017 +0000 +++ b/graphics/DebugDataConsumer.py Mon Aug 21 23:22:58 2017 +0300 @@ -24,9 +24,10 @@ import datetime -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Date and Time conversion function -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- SECOND = 1000000 # Number of microseconds in one second MINUTE = 60 * SECOND # Number of microseconds in one minute @@ -36,6 +37,7 @@ # Date corresponding to Epoch (1970 January the first) DATE_ORIGIN = datetime.datetime(1970, 1, 1) + def get_microseconds(value): """ Function converting time duration expressed in day, second and microseconds @@ -43,10 +45,11 @@ @param value: Time duration to convert @return: Time duration expressed in microsecond """ - return float(value.days * DAY + \ - value.seconds * SECOND + \ + return float(value.days * DAY + + value.seconds * SECOND + value.microseconds) - return + return + def generate_time(value): """ @@ -56,40 +59,41 @@ @return: IEC 61131 TIME literal """ microseconds = get_microseconds(value) - + # Get absolute microseconds value and save if it was negative negative = microseconds < 0 microseconds = abs(microseconds) - + # TIME literal prefix data = "T#" if negative: data += "-" - + # In TIME literal format, it isn't mandatory to indicate null values # if no greater non-null values are available. This variable is used to # inhibit formatting until a non-null value is found not_null = False - + for val, format in [ - (int(microseconds) / DAY, "%dd"), # Days - ((int(microseconds) % DAY) / HOUR, "%dh"), # Hours - ((int(microseconds) % HOUR) / MINUTE, "%dm"), # Minutes - ((int(microseconds) % MINUTE) / SECOND, "%ds")]: # Seconds - - # Add value to TIME literal if value is non-null or another non-null + (int(microseconds) / DAY, "%dd"), # Days + ((int(microseconds) % DAY) / HOUR, "%dh"), # Hours + ((int(microseconds) % HOUR) / MINUTE, "%dm"), # Minutes + ((int(microseconds) % MINUTE) / SECOND, "%ds")]: # Seconds + + # Add value to TIME literal if value is non-null or another non-null # value have already be found if val > 0 or not_null: data += format % val - + # Update non-null variable not_null = True - - # In any case microseconds have to be added to TIME literal + + # In any case microseconds have to be added to TIME literal data += "%gms" % (microseconds % SECOND / 1000.) - + return data + def generate_date(value): """ Function converting time duration expressed in day, second and microseconds @@ -99,6 +103,7 @@ """ return (DATE_ORIGIN + value).strftime("DATE#%Y-%m-%d") + def generate_datetime(value): """ Function converting time duration expressed in day, second and microseconds @@ -108,6 +113,7 @@ """ return (DATE_ORIGIN + value).strftime("DT#%Y-%m-%d-%H:%M:%S.%f") + def generate_timeofday(value): """ Function converting time duration expressed in day, second and microseconds @@ -116,22 +122,23 @@ @return: IEC 61131 TIME_OF_DAY literal """ microseconds = get_microseconds(value) - + # TIME_OF_DAY literal prefix data = "TOD#" - + for val, format in [ (int(microseconds) / HOUR, "%2.2d:"), # Hours ((int(microseconds) % HOUR) / MINUTE, "%2.2d:"), # Minutes ((int(microseconds) % MINUTE) / SECOND, "%2.2d."), # Seconds (microseconds % SECOND, "%6.6d")]: # Microseconds - + # Add value to TIME_OF_DAY literal data += format % val - + return data -# Dictionary of translation functions from value send by debugger to IEC + +# Dictionary of translation functions from value send by debugger to IEC # literal stored by type TYPE_TRANSLATOR = { "TIME": generate_time, @@ -143,18 +150,19 @@ "REAL": lambda v: "%.6g" % v, "LREAL": lambda v: "%.6g" % v} -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Debug Data Consumer Class -#------------------------------------------------------------------------------- - -""" -Class that implements an element that consumes debug values -Value update can be inhibited during the time the associated Debug Viewer is -refreshing -""" +# ------------------------------------------------------------------------------- + class DebugDataConsumer: - + """ + Class that implements an element that consumes debug values + Value update can be inhibited during the time the associated Debug Viewer is + refreshing + """ + def __init__(self): """ Constructor @@ -162,17 +170,17 @@ # Debug value and forced flag self.Value = None self.Forced = False - + # Store debug value and forced flag when value update is inhibited self.LastValue = None self.LastForced = False - + # Value IEC data type self.DataType = None - + # Flag that value update is inhibited self.Inhibited = False - + def Inhibit(self, inhibit): """ Set flag to inhibit or activate value update @@ -180,23 +188,23 @@ """ # Save inhibit flag self.Inhibited = inhibit - + # When reactivated update value and forced flag with stored values if not inhibit and self.LastValue is not None: self.SetForced(self.LastForced) self.SetValue(self.LastValue) - + # Reset stored values self.LastValue = None self.LastForced = False - + def SetDataType(self, data_type): """ Set value IEC data type @param data_type: Value IEC data type """ self.DataType = data_type - + def NewValues(self, tick, values, raw="BOOL"): """ Function called by debug thread when a new debug value is available @@ -206,21 +214,21 @@ @param raw: Data type of values not translated (default: 'BOOL') """ value, forced = values - + # Translate value to IEC literal if self.DataType != raw: value = TYPE_TRANSLATOR.get(self.DataType, str)(value) - + # Store value and forced flag when value update is inhibited if self.Inhibited: self.LastValue = value self.LastForced = forced - + # Update value and forced flag in any other case else: self.SetForced(forced) self.SetValue(value) - + def SetValue(self, value): """ Update value. @@ -228,14 +236,14 @@ @param value: New value """ self.Value = value - + def GetValue(self): """ Return current value @return: Current value """ return self.Value - + def SetForced(self, forced): """ Update Forced flag. @@ -243,7 +251,7 @@ @param forced: New forced flag """ self.Forced = forced - + def IsForced(self): """ Indicate if current value is forced diff -r 31e63e25b4cc -r 64beb9e9c749 graphics/FBD_Objects.py --- a/graphics/FBD_Objects.py Mon Aug 21 20:17:19 2017 +0000 +++ b/graphics/FBD_Objects.py Mon Aug 21 23:22:58 2017 +0300 @@ -27,21 +27,23 @@ from graphics.GraphicCommons import * from plcopen.structures import * -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Function Block Diagram Block -#------------------------------------------------------------------------------- - -""" -Class that implements the graphic representation of a function block -""" +# ------------------------------------------------------------------------------- + def TestConnectorName(name, block_type): return name in ["OUT", "MN", "MX"] or name.startswith("IN") and (block_type, name) != ("EXPT", "IN2") + class FBD_Block(Graphic_Element): - + """ + Class that implements the graphic representation of a function block + """ + # Create a new block - def __init__(self, parent, type, name, id = None, extension = 0, inputs = None, connectors = {}, executionControl = False, executionOrder = 0): + def __init__(self, parent, type, name, id=None, extension=0, inputs=None, connectors={}, executionControl=False, executionOrder=0): Graphic_Element.__init__(self, parent) self.Type = None self.Description = None @@ -56,9 +58,9 @@ self.Pen = MiterPen(wx.BLACK) self.SetType(type, extension, inputs, connectors, executionControl) self.Highlights = {} - + # Make a clone of this FBD_Block - def Clone(self, parent, id = None, name = "", pos = None): + def Clone(self, parent, id=None, name="", pos=None): if self.Name != "" and name == "": name = self.Name block = FBD_Block(parent, self.Type, name, id, self.Extension) @@ -70,10 +72,10 @@ block.Inputs = [input.Clone(block) for input in self.Inputs] block.Outputs = [output.Clone(block) for output in self.Outputs] return block - + def GetConnectorTranslation(self, element): return dict(zip(self.Inputs + self.Outputs, element.Inputs + element.Outputs)) - + def Flush(self): for input in self.Inputs: input.Flush() @@ -81,9 +83,9 @@ for output in self.Outputs: output.Flush() self.Outputs = [] - + # Returns the RedrawRect - def GetRedrawRect(self, movex = 0, movey = 0): + def GetRedrawRect(self, movex=0, movey=0): rect = Graphic_Element.GetRedrawRect(self, movex, movey) if movex != 0 or movey != 0: for input in self.Inputs: @@ -93,26 +95,26 @@ if output.IsConnected(): rect = rect.Union(output.GetConnectedRedrawRect(movex, movey)) return rect - + # Delete this block by calling the appropriate method def Delete(self): self.Parent.DeleteBlock(self) - + # Unconnect all inputs and outputs def Clean(self): for input in self.Inputs: - input.UnConnect(delete = True) + input.UnConnect(delete=True) for output in self.Outputs: - output.UnConnect(delete = True) - + output.UnConnect(delete=True) + # Refresh the size of text for name def RefreshNameSize(self): self.NameSize = self.Parent.GetTextExtent(self.Name) - + # Refresh the size of text for execution order def RefreshExecutionOrderSize(self): self.ExecutionOrderSize = self.Parent.GetTextExtent(str(self.ExecutionOrder)) - + # Returns if the point given is in the bounding box def HitTest(self, pt, connectors=True): if self.Name != "": @@ -121,7 +123,7 @@ test_text = False test_block = self.GetBlockBoundingBox(connectors).InsideXY(pt.x, pt.y) return test_text or test_block - + # Returns the bounding box of the name outside the block def GetTextBoundingBox(self): # Calculate the size of the name outside the block @@ -130,7 +132,7 @@ self.Pos.y - (text_height + 2), text_width, text_height) - + # Returns the bounding box of function block without name outside def GetBlockBoundingBox(self, connectors=True): bbx_x, bbx_y = self.Pos.x, self.Pos.y @@ -143,13 +145,13 @@ bbx_width = max(bbx_width, bbx_width + self.Pos.x + self.ExecutionOrderSize[0] - bbx_x - self.Size[0]) bbx_height = bbx_height + (self.ExecutionOrderSize[1] + 2) return wx.Rect(bbx_x, bbx_y, bbx_width + 1, bbx_height + 1) - + # Refresh the block bounding box def RefreshBoundingBox(self): self.BoundingBox = self.GetBlockBoundingBox() if self.Name != "": self.BoundingBox.Union(self.GetTextBoundingBox()) - + # Refresh the positions of the block connectors def RefreshConnectors(self): scaling = self.Parent.GetScaling() @@ -170,16 +172,16 @@ self.Outputs[i].SetPosition(wx.Point(self.Size[0], ypos)) position += linesize self.RefreshConnected() - + # Refresh the positions of wires connected to inputs and outputs - def RefreshConnected(self, exclude = []): + def RefreshConnected(self, exclude=[]): for input in self.Inputs: input.MoveConnected(exclude) for output in self.Outputs: output.MoveConnected(exclude) - - # Returns the block connector that starts with the point given if it exists - def GetConnector(self, position, output_name = None, input_name = None): + + # Returns the block connector that starts with the point given if it exists + def GetConnector(self, position, output_name=None, input_name=None): if input_name is not None: # Test each input connector for input in self.Inputs: @@ -193,14 +195,14 @@ if input_name is None and output_name is None: return self.FindNearestConnector(position, self.Inputs + self.Outputs) return None - + def GetInputTypes(self): return tuple([input.GetType(True) for input in self.Inputs if input.GetName() != "EN"]) - + def SetOutputValues(self, values): for output in self.Outputs: output.SetValue(values.get(ouput.getName(), None)) - + def GetConnectionResultType(self, connector, connectortype): if not TestConnectorName(connector.GetName(), self.Type): return connectortype @@ -216,13 +218,13 @@ if resulttype is None or outputtype is not None and self.IsOfType(outputtype, resulttype): resulttype = outputtype return resulttype - + # Returns all the block connectors def GetConnectors(self): - return {"inputs" : self.Inputs, "outputs" : self.Outputs} - + return {"inputs": self.Inputs, "outputs": self.Outputs} + # Test if point given is on one of the block connectors - def TestConnector(self, pt, direction = None, exclude = True): + def TestConnector(self, pt, direction=None, exclude=True): # Test each input connector for input in self.Inputs: if input.TestPoint(pt, direction, exclude): @@ -232,10 +234,10 @@ if output.TestPoint(pt, direction, exclude): return output return None - + # Changes the block type - def SetType(self, type, extension, inputs = None, connectors = {}, executionControl = False): - if type != self.Type or self.Extension != extension or executionControl != self.ExecutionControl: + def SetType(self, type, extension, inputs=None, connectors={}, executionControl=False): + if type != self.Type or self.Extension != extension or executionControl != self.ExecutionControl: if type != self.Type: self.Type = type self.TypeSize = self.Parent.GetTextExtent(self.Type) @@ -252,7 +254,7 @@ start = int(inputs[-1][0].replace("IN", "")) for i in xrange(self.Extension - len(blocktype["inputs"])): start += 1 - inputs.append(("IN%d"%start, inputs[-1][1], inputs[-1][2])) + inputs.append(("IN%d" % start, inputs[-1][1], inputs[-1][2])) comment = blocktype["comment"] self.Description = _(comment) + blocktype.get("usage", "") else: @@ -261,14 +263,14 @@ outputs = connectors.get("outputs", []) self.Description = None if self.ExecutionControl: - inputs.insert(0, ("EN","BOOL","none")) - outputs.insert(0, ("ENO","BOOL","none")) + inputs.insert(0, ("EN", "BOOL", "none")) + outputs.insert(0, ("ENO", "BOOL", "none")) self.Pen = MiterPen(self.Colour) - + # Extract the inputs properties and create or modify the corresponding connector input_connectors = [] for input_name, input_type, input_modifier in inputs: - connector = Connector(self, input_name, input_type, wx.Point(0, 0), WEST, onlyone = True) + connector = Connector(self, input_name, input_type, wx.Point(0, 0), WEST, onlyone=True) if input_modifier == "negated": connector.SetNegated(True) elif input_modifier != "none": @@ -282,9 +284,9 @@ break input_connectors.append(connector) for input in self.Inputs: - input.UnConnect(delete = True) + input.UnConnect(delete=True) self.Inputs = input_connectors - + # Extract the outputs properties and create or modify the corresponding connector output_connectors = [] for output_name, output_type, output_modifier in outputs: @@ -302,49 +304,49 @@ break output_connectors.append(connector) for output in self.Outputs: - output.UnConnect(delete = True) + output.UnConnect(delete=True) self.Outputs = output_connectors - + self.RefreshMinSize() self.RefreshConnectors() for output in self.Outputs: output.RefreshWires() self.RefreshBoundingBox() - + # Returns the block type def GetType(self): return self.Type - + # Changes the block name def SetName(self, name): self.Name = name self.RefreshNameSize() - + # Returs the block name def GetName(self): return self.Name - + # Changes the extension name def SetExtension(self, extension): self.Extension = extension - + # Returs the extension name def GetExtension(self): return self.Extension - + # Changes the execution order def SetExecutionOrder(self, executionOrder): self.ExecutionOrder = executionOrder self.RefreshExecutionOrderSize() - + # Returs the execution order def GetExecutionOrder(self): return self.ExecutionOrder - + # Returs the execution order def GetExecutionControl(self): return self.ExecutionControl - + # Refresh the block minimum size def RefreshMinSize(self): # Calculate the inputs maximum width @@ -360,42 +362,42 @@ width = max(self.TypeSize[0] + 10, max_input + max_output + 15) height = (max(len(self.Inputs), len(self.Outputs)) + 1) * BLOCK_LINE_SIZE self.MinSize = width, height - + # Returns the block minimum size def GetMinSize(self): return self.MinSize - + # Changes the negated property of the connector handled def SetConnectorNegated(self, negated): handle_type, handle = self.Handle if handle_type == HANDLE_CONNECTOR: handle.SetNegated(negated) self.RefreshModel(False) - + # Changes the edge property of the connector handled def SetConnectorEdge(self, edge): handle_type, handle = self.Handle if handle_type == HANDLE_CONNECTOR: handle.SetEdge(edge) self.RefreshModel(False) - -## # Method called when a Motion event have been generated -## def OnMotion(self, event, dc, scaling): -## if not event.Dragging(): -## pos = event.GetLogicalPosition(dc) -## for input in self.Inputs: -## rect = input.GetRedrawRect() -## if rect.InsideXY(pos.x, pos.y): -## print "Find input" -## tip = wx.TipWindow(self.Parent, "Test") -## tip.SetBoundingRect(rect) -## return Graphic_Element.OnMotion(self, event, dc, scaling) - + +# # Method called when a Motion event have been generated +# def OnMotion(self, event, dc, scaling): +# if not event.Dragging(): +# pos = event.GetLogicalPosition(dc) +# for input in self.Inputs: +# rect = input.GetRedrawRect() +# if rect.InsideXY(pos.x, pos.y): +# print "Find input" +# tip = wx.TipWindow(self.Parent, "Test") +# tip.SetBoundingRect(rect) +# return Graphic_Element.OnMotion(self, event, dc, scaling) + # Method called when a LeftDClick event have been generated def OnLeftDClick(self, event, dc, scaling): # Edit the block properties self.Parent.EditBlockContent(self) - + # Method called when a RightUp event have been generated def OnRightUp(self, event, dc, scaling): pos = GetScaledEventPosition(event, dc, scaling) @@ -406,7 +408,7 @@ self.Parent.PopupBlockMenu(connector) else: self.Parent.PopupBlockMenu() - + # Refreshes the block model def RefreshModel(self, move=True): self.Parent.RefreshBlockModel(self) @@ -414,12 +416,12 @@ if move: for output in self.Outputs: output.RefreshWires() - + def GetToolTipValue(self): return self.Description - + # Adds an highlight to the block - def AddHighlight(self, infos, start, end ,highlight_type): + def AddHighlight(self, infos, start, end, highlight_type): if infos[0] in ["type", "name"] and start[0] == 0 and end[0] == 0: highlights = self.Highlights.setdefault(infos[0], []) AddHighlight(highlights, (start, end, highlight_type)) @@ -427,7 +429,7 @@ self.Inputs[infos[1]].AddHighlight(infos[2:], start, end, highlight_type) elif infos[0] == "output" and infos[1] < len(self.Outputs): self.Outputs[infos[1]].AddHighlight(infos[2:], start, end, highlight_type) - + # Removes an highlight from the block def RemoveHighlight(self, infos, start, end, highlight_type): if infos[0] in ["type", "name"]: @@ -438,7 +440,7 @@ self.Inputs[infos[1]].RemoveHighlight(infos[2:], start, end, highlight_type) elif infos[0] == "output" and infos[1] < len(self.Outputs): self.Outputs[infos[1]].RemoveHighlight(infos[2:], start, end, highlight_type) - + # Removes all the highlights of one particular type from the block def ClearHighlight(self, highlight_type=None): if highlight_type is None: @@ -453,14 +455,14 @@ input.ClearHighlights(highlight_type) for output in self.Outputs: output.ClearHighlights(highlight_type) - + # Draws block def Draw(self, dc): Graphic_Element.Draw(self, dc) dc.SetPen(self.Pen) dc.SetBrush(wx.WHITE_BRUSH) dc.SetTextForeground(self.Colour) - + if getattr(dc, "printing", False): name_size = dc.GetTextExtent(self.Name) type_size = dc.GetTextExtent(self.Type) @@ -469,7 +471,7 @@ name_size = self.NameSize type_size = self.TypeSize executionorder_size = self.ExecutionOrderSize - + # Draw a rectangle with the block size dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1) # Draw block name and block type @@ -487,25 +489,25 @@ if self.ExecutionOrder != 0: # Draw block execution order dc.DrawText(str(self.ExecutionOrder), self.Pos.x + self.Size[0] - executionorder_size[0], - self.Pos.y + self.Size[1] + 2) - + self.Pos.y + self.Size[1] + 2) + if not getattr(dc, "printing", False): DrawHighlightedText(dc, self.Name, self.Highlights.get("name", []), name_pos[0], name_pos[1]) DrawHighlightedText(dc, self.Type, self.Highlights.get("type", []), type_pos[0], type_pos[1]) - - -#------------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------------- # Function Block Diagram Variable -#------------------------------------------------------------------------------- - -""" -Class that implements the graphic representation of a variable -""" +# ------------------------------------------------------------------------------- + class FBD_Variable(Graphic_Element): + """ + Class that implements the graphic representation of a variable + """ # Create a new variable - def __init__(self, parent, type, name, value_type, id = None, executionOrder = 0): + def __init__(self, parent, type, name, value_type, id=None, executionOrder=0): Graphic_Element.__init__(self, parent) self.Type = None self.ValueType = None @@ -516,9 +518,9 @@ self.Output = None self.SetType(type, value_type) self.Highlights = [] - + # Make a clone of this FBD_Variable - def Clone(self, parent, id = None, pos = None): + def Clone(self, parent, id=None, pos=None): variable = FBD_Variable(parent, self.Type, self.Name, self.ValueType, id) variable.SetSize(self.Size[0], self.Size[1]) if pos is not None: @@ -530,7 +532,7 @@ if self.Output: variable.Output = self.Output.Clone(variable) return variable - + def GetConnectorTranslation(self, element): connectors = {} if self.Input is not None: @@ -538,7 +540,7 @@ if self.Output is not None: connectors[self.Output] = element.Output return connectors - + def Flush(self): if self.Input is not None: self.Input.Flush() @@ -546,9 +548,9 @@ if self.Output is not None: self.Output.Flush() self.Output = None - + # Returns the RedrawRect - def GetRedrawRect(self, movex = 0, movey = 0): + def GetRedrawRect(self, movex=0, movey=0): rect = Graphic_Element.GetRedrawRect(self, movex, movey) if movex != 0 or movey != 0: if self.Input and self.Input.IsConnected(): @@ -556,26 +558,26 @@ if self.Output and self.Output.IsConnected(): rect = rect.Union(self.Output.GetConnectedRedrawRect(movex, movey)) return rect - + # Unconnect connector def Clean(self): if self.Input: - self.Input.UnConnect(delete = True) + self.Input.UnConnect(delete=True) if self.Output: - self.Output.UnConnect(delete = True) - + self.Output.UnConnect(delete=True) + # Delete this variable by calling the appropriate method def Delete(self): self.Parent.DeleteVariable(self) - + # Refresh the size of text for name def RefreshNameSize(self): self.NameSize = self.Parent.GetTextExtent(self.Name) - + # Refresh the size of text for execution order def RefreshExecutionOrderSize(self): self.ExecutionOrderSize = self.Parent.GetTextExtent(str(self.ExecutionOrder)) - + # Refresh the variable bounding box def RefreshBoundingBox(self): if self.Type in (OUTPUT, INOUT): @@ -594,7 +596,7 @@ bbx_width = max(bbx_width, bbx_width + self.Pos.x + self.ExecutionOrderSize[0] - bbx_x - self.Size[0]) bbx_height = bbx_height + (self.ExecutionOrderSize[1] + 2) self.BoundingBox = wx.Rect(bbx_x, self.Pos.y, bbx_width + 1, bbx_height + 1) - + # Refresh the position of the variable connector def RefreshConnectors(self): scaling = self.Parent.GetScaling() @@ -607,28 +609,28 @@ if self.Output: self.Output.SetPosition(wx.Point(self.Size[0], position)) self.RefreshConnected() - + # Refresh the position of wires connected to connector - def RefreshConnected(self, exclude = []): + def RefreshConnected(self, exclude=[]): if self.Input: self.Input.MoveConnected(exclude) if self.Output: self.Output.MoveConnected(exclude) - + # Test if point given is on the variable connector - def TestConnector(self, pt, direction = None, exclude=True): + def TestConnector(self, pt, direction=None, exclude=True): if self.Input and self.Input.TestPoint(pt, direction, exclude): return self.Input if self.Output and self.Output.TestPoint(pt, direction, exclude): return self.Output return None - - # Returns the block connector that starts with the point given if it exists - def GetConnector(self, position, name = None): + + # Returns the block connector that starts with the point given if it exists + def GetConnector(self, position, name=None): # if a name is given if name is not None: # Test input and output connector if they exists - #if self.Input and name == self.Input.GetName(): + # if self.Input and name == self.Input.GetName(): # return self.Input if self.Output and name == self.Output.GetName(): return self.Output @@ -640,8 +642,8 @@ if self.Output: connectors.append(self.Output) return self.FindNearestConnector(position, connectors) - - # Returns all the block connectors + + # Returns all the block connectors def GetConnectors(self): connectors = {"inputs": [], "outputs": []} if self.Input: @@ -649,14 +651,14 @@ if self.Output: connectors["outputs"].append(self.Output) return connectors - + # Changes the negated property of the variable connector if handled def SetConnectorNegated(self, negated): handle_type, handle = self.Handle if handle_type == HANDLE_CONNECTOR: handle.SetNegated(negated) self.RefreshModel(False) - + # Changes the variable type def SetType(self, type, value_type): if type != self.Type: @@ -664,15 +666,15 @@ # Create an input or output connector according to variable type if self.Type != INPUT: if self.Input is None: - self.Input = Connector(self, "", value_type, wx.Point(0, 0), WEST, onlyone = True) + self.Input = Connector(self, "", value_type, wx.Point(0, 0), WEST, onlyone=True) elif self.Input: - self.Input.UnConnect(delete = True) + self.Input.UnConnect(delete=True) self.Input = None if self.Type != OUTPUT: if self.Output is None: self.Output = Connector(self, "", value_type, wx.Point(0, 0), EAST) elif self.Output: - self.Output.UnConnect(delete = True) + self.Output.UnConnect(delete=True) self.Output = None self.RefreshConnectors() self.RefreshBoundingBox() @@ -680,38 +682,38 @@ if self.Input: self.Input.SetType(value_type) if self.Output: - self.Output.SetType(value_type) - + self.Output.SetType(value_type) + # Returns the variable type def GetType(self): return self.Type - + # Returns the variable value type def GetValueType(self): return self.ValueType - + # Changes the variable name def SetName(self, name): self.Name = name self.RefreshNameSize() - + # Returns the variable name def GetName(self): return self.Name - + # Changes the execution order def SetExecutionOrder(self, executionOrder): self.ExecutionOrder = executionOrder self.RefreshExecutionOrderSize() - + # Returs the execution order def GetExecutionOrder(self): return self.ExecutionOrder - + # Returns the variable minimum size def GetMinSize(self): return self.NameSize[0] + 10, self.NameSize[1] + 10 - + # Set size of the variable to the minimum size def SetBestSize(self, scaling): if self.Type == INPUT: @@ -720,22 +722,22 @@ return Graphic_Element.SetBestSize(self, scaling, x_factor=0.) else: return Graphic_Element.SetBestSize(self, scaling) - + # Method called when a LeftDClick event have been generated def OnLeftDClick(self, event, dc, scaling): if event.ControlDown(): - # Change variable type + # Change variable type types = [INPUT, OUTPUT, INOUT] - self.Parent.ChangeVariableType(self, - types[(types.index(self.Type) + 1) % len(types)]) + self.Parent.ChangeVariableType( + self, types[(types.index(self.Type) + 1) % len(types)]) else: # Edit the variable properties self.Parent.EditVariableContent(self) - + # Method called when a RightUp event have been generated def OnRightUp(self, event, dc, scaling): self.Parent.PopupVariableMenu() - + # Refreshes the variable model def RefreshModel(self, move=True): self.Parent.RefreshVariableModel(self) @@ -744,35 +746,35 @@ if move and self.Type != OUTPUT: if self.Output: self.Output.RefreshWires() - + # Adds an highlight to the variable def AddHighlight(self, infos, start, end, highlight_type): if infos[0] == "expression" and start[0] == 0 and end[0] == 0: AddHighlight(self.Highlights, (start, end, highlight_type)) - + # Removes an highlight from the variable def RemoveHighlight(self, infos, start, end, highlight_type): if infos[0] == "expression": RemoveHighlight(self.Highlights, (start, end, highlight_type)) - + # Removes all the highlights of one particular type from the variable def ClearHighlight(self, highlight_type=None): ClearHighlights(self.Highlights, highlight_type) - + # Draws variable def Draw(self, dc): Graphic_Element.Draw(self, dc) dc.SetPen(MiterPen(wx.BLACK)) dc.SetBrush(wx.WHITE_BRUSH) - + if getattr(dc, "printing", False): name_size = dc.GetTextExtent(self.Name) executionorder_size = dc.GetTextExtent(str(self.ExecutionOrder)) else: name_size = self.NameSize executionorder_size = self.ExecutionOrderSize - - text_pos = (self.Pos.x + (self.Size[0] - name_size[0]) / 2, + + text_pos = (self.Pos.x + (self.Size[0] - name_size[0]) / 2, self.Pos.y + (self.Size[1] - name_size[1]) / 2) # Draw a rectangle with the variable size dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1) @@ -781,27 +783,28 @@ # Draw connectors if self.Input: self.Input.Draw(dc) - if self.Output: + if self.Output: self.Output.Draw(dc) if self.ExecutionOrder != 0: # Draw variable execution order dc.DrawText(str(self.ExecutionOrder), self.Pos.x + self.Size[0] - executionorder_size[0], - self.Pos.y + self.Size[1] + 2) + self.Pos.y + self.Size[1] + 2) if not getattr(dc, "printing", False): DrawHighlightedText(dc, self.Name, self.Highlights, text_pos[0], text_pos[1]) - -#------------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------------- # Function Block Diagram Connector -#------------------------------------------------------------------------------- - -""" -Class that implements the graphic representation of a connection -""" +# ------------------------------------------------------------------------------- + class FBD_Connector(Graphic_Element): + """ + Class that implements the graphic representation of a connection + """ # Create a new connection - def __init__(self, parent, type, name, id = None): + def __init__(self, parent, type, name, id=None): Graphic_Element.__init__(self, parent) self.Type = type self.Id = id @@ -811,27 +814,27 @@ self.Highlights = [] # Create an input or output connector according to connection type if self.Type == CONNECTOR: - self.Connector = Connector(self, "", "ANY", wx.Point(0, 0), WEST, onlyone = True) + self.Connector = Connector(self, "", "ANY", wx.Point(0, 0), WEST, onlyone=True) else: self.Connector = Connector(self, "", "ANY", wx.Point(0, 0), EAST) self.RefreshConnectors() self.RefreshNameSize() - + def Flush(self): if self.Connector: self.Connector.Flush() self.Connector = None - + # Returns the RedrawRect - def GetRedrawRect(self, movex = 0, movey = 0): + def GetRedrawRect(self, movex=0, movey=0): rect = Graphic_Element.GetRedrawRect(self, movex, movey) if movex != 0 or movey != 0: if self.Connector and self.Connector.IsConnected(): rect = rect.Union(self.Connector.GetConnectedRedrawRect(movex, movey)) return rect - + # Make a clone of this FBD_Connector - def Clone(self, parent, id = None, pos = None): + def Clone(self, parent, id=None, pos=None): connection = FBD_Connector(parent, self.Type, self.Name, id) connection.SetSize(self.Size[0], self.Size[1]) if pos is not None: @@ -840,23 +843,23 @@ connection.SetPosition(self.Pos.x, self.Pos.y) connection.Connector = self.Connector.Clone(connection) return connection - + def GetConnectorTranslation(self, element): - return {self.Connector : element.Connector} + return {self.Connector: element.Connector} # Unconnect connector def Clean(self): if self.Connector: - self.Connector.UnConnect(delete = True) - + self.Connector.UnConnect(delete=True) + # Delete this connection by calling the appropriate method def Delete(self): self.Parent.DeleteConnection(self) - + # Refresh the size of text for name def RefreshNameSize(self): self.NameSize = self.Parent.GetTextExtent(self.Name) - + # Refresh the connection bounding box def RefreshBoundingBox(self): if self.Type == CONNECTOR: @@ -865,7 +868,7 @@ bbx_x = self.Pos.x bbx_width = self.Size[0] + CONNECTOR_SIZE self.BoundingBox = wx.Rect(bbx_x, self.Pos.y, bbx_width, self.Size[1]) - + # Refresh the position of the connection connector def RefreshConnectors(self): scaling = self.Parent.GetScaling() @@ -878,23 +881,23 @@ else: self.Connector.SetPosition(wx.Point(self.Size[0], position)) self.RefreshConnected() - + # Refresh the position of wires connected to connector - def RefreshConnected(self, exclude = []): + def RefreshConnected(self, exclude=[]): if self.Connector: self.Connector.MoveConnected(exclude) - + # Test if point given is on the connection connector - def TestConnector(self, pt, direction = None, exclude=True): + def TestConnector(self, pt, direction=None, exclude=True): if self.Connector and self.Connector.TestPoint(pt, direction, exclude): return self.Connector return None - + # Returns the connection connector - def GetConnector(self, position = None, name = None): + def GetConnector(self, position=None, name=None): return self.Connector - - # Returns all the block connectors + + # Returns all the block connectors def GetConnectors(self): connectors = {"inputs": [], "outputs": []} if self.Type == CONNECTOR: @@ -918,50 +921,50 @@ self.Clean() # Create an input or output connector according to connection type if self.Type == CONNECTOR: - self.Connector = Connector(self, "", "ANY", wx.Point(0, 0), WEST, onlyone = True) + self.Connector = Connector(self, "", "ANY", wx.Point(0, 0), WEST, onlyone=True) else: self.Connector = Connector(self, "", "ANY", wx.Point(0, 0), EAST) self.RefreshConnectors() self.RefreshBoundingBox() - + # Returns the connection type def GetType(self): return self.Type - + def GetConnectionResultType(self, connector, connectortype): if self.Type == CONTINUATION: connector = self.Parent.GetConnectorByName(self.Name) if connector is not None: return connector.Connector.GetConnectedType() return connectortype - + # Changes the connection name def SetName(self, name): self.Name = name self.RefreshNameSize() - + # Returns the connection name def GetName(self): return self.Name - + # Set size of the variable to the minimum size def SetBestSize(self, scaling): if self.Type == CONTINUATION: return Graphic_Element.SetBestSize(self, scaling, x_factor=1.) else: return Graphic_Element.SetBestSize(self, scaling, x_factor=0.) - + # Returns the connection minimum size def GetMinSize(self): text_width, text_height = self.NameSize if text_height % 2 == 1: text_height += 1 return text_width + text_height + 20, text_height + 10 - + # Method called when a LeftDClick event have been generated def OnLeftDClick(self, event, dc, scaling): if event.ControlDown(): - # Change connection type + # Change connection type if self.Type == CONNECTOR: self.Parent.ChangeConnectionType(self, CONTINUATION) else: @@ -969,12 +972,12 @@ else: # Edit the connection properties self.Parent.EditConnectionContent(self) - + # Method called when a RightUp event have been generated def OnRightUp(self, event, dc, scaling): # Popup the default menu self.Parent.PopupConnectionMenu() - + # Refreshes the connection model def RefreshModel(self, move=True): self.Parent.RefreshConnectionModel(self) @@ -983,51 +986,50 @@ if move and self.Type == CONTINUATION: if self.Connector: self.Connector.RefreshWires() - + # Adds an highlight to the connection def AddHighlight(self, infos, start, end, highlight_type): if infos[0] == "name" and start[0] == 0 and end[0] == 0: AddHighlight(self.Highlights, (start, end, highlight_type)) - + # Removes an highlight from the connection def RemoveHighlight(self, infos, start, end, highlight_type): if infos[0] == "name": RemoveHighlight(self.Highlights, (start, end, highlight_type)) - + # Removes all the highlights of one particular type from the connection def ClearHighlight(self, highlight_type=None): ClearHighlights(self.Highlights, highlight_type) - + # Draws connection def Draw(self, dc): Graphic_Element.Draw(self, dc) dc.SetPen(MiterPen(wx.BLACK)) dc.SetBrush(wx.WHITE_BRUSH) - + if getattr(dc, "printing", False): name_size = dc.GetTextExtent(self.Name) else: name_size = self.NameSize - + # Draw a rectangle with the connection size with arrows inside dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1) arrowsize = min(self.Size[1] / 2, (self.Size[0] - name_size[0] - 10) / 2) - dc.DrawLine(self.Pos.x, self.Pos.y, self.Pos.x + arrowsize, - self.Pos.y + self.Size[1] / 2) - dc.DrawLine(self.Pos.x + arrowsize, self.Pos.y + self.Size[1] / 2, - self.Pos.x, self.Pos.y + self.Size[1]) - dc.DrawLine(self.Pos.x + self.Size[0] - arrowsize, self.Pos.y, - self.Pos.x + self.Size[0], self.Pos.y + self.Size[1] / 2) - dc.DrawLine(self.Pos.x + self.Size[0], self.Pos.y + self.Size[1] / 2, - self.Pos.x + self.Size[0] - arrowsize, self.Pos.y + self.Size[1]) + dc.DrawLine(self.Pos.x, self.Pos.y, self.Pos.x + arrowsize, + self.Pos.y + self.Size[1] / 2) + dc.DrawLine(self.Pos.x + arrowsize, self.Pos.y + self.Size[1] / 2, + self.Pos.x, self.Pos.y + self.Size[1]) + dc.DrawLine(self.Pos.x + self.Size[0] - arrowsize, self.Pos.y, + self.Pos.x + self.Size[0], self.Pos.y + self.Size[1] / 2) + dc.DrawLine(self.Pos.x + self.Size[0], self.Pos.y + self.Size[1] / 2, + self.Pos.x + self.Size[0] - arrowsize, self.Pos.y + self.Size[1]) # Draw connection name - text_pos = (self.Pos.x + (self.Size[0] - name_size[0]) / 2, + text_pos = (self.Pos.x + (self.Size[0] - name_size[0]) / 2, self.Pos.y + (self.Size[1] - name_size[1]) / 2) dc.DrawText(self.Name, text_pos[0], text_pos[1]) # Draw connector if self.Connector: self.Connector.Draw(dc) - + if not getattr(dc, "printing", False): DrawHighlightedText(dc, self.Name, self.Highlights, text_pos[0], text_pos[1]) - diff -r 31e63e25b4cc -r 64beb9e9c749 graphics/GraphicCommons.py --- a/graphics/GraphicCommons.py Mon Aug 21 20:17:19 2017 +0000 +++ b/graphics/GraphicCommons.py Mon Aug 21 23:22:58 2017 +0300 @@ -26,14 +26,14 @@ from math import * from types import * import datetime -from threading import Lock,Timer +from threading import Lock, Timer from graphics.ToolTipProducer import ToolTipProducer from graphics.DebugDataConsumer import DebugDataConsumer -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Common constants -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- """ Definition of constants for dimensions of graphic elements @@ -78,17 +78,17 @@ [HANDLE_MOVE, HANDLE_RESIZE, HANDLE_POINT, HANDLE_SEGMENT, HANDLE_CONNECTOR] = range(5) # List of value for resize handle that are valid -VALID_HANDLES = [(1,1), (1,2), (1,3), (2,3), (3,3), (3,2), (3,1), (2,1)] +VALID_HANDLES = [(1, 1), (1, 2), (1, 3), (2, 3), (3, 3), (3, 2), (3, 1), (2, 1)] # Contants for defining the direction of a connector -[EAST, NORTH, WEST, SOUTH] = [(1,0), (0,-1), (-1,0), (0,1)] - -# Contants for defining which mode is selected for each view -[MODE_SELECTION, MODE_BLOCK, MODE_VARIABLE, MODE_CONNECTION, MODE_COMMENT, - MODE_COIL, MODE_CONTACT, MODE_POWERRAIL, MODE_INITIALSTEP, MODE_STEP, +[EAST, NORTH, WEST, SOUTH] = [(1, 0), (0, -1), (-1, 0), (0, 1)] + +# Contants for defining which mode is selected for each view +[MODE_SELECTION, MODE_BLOCK, MODE_VARIABLE, MODE_CONNECTION, MODE_COMMENT, + MODE_COIL, MODE_CONTACT, MODE_POWERRAIL, MODE_INITIALSTEP, MODE_STEP, MODE_TRANSITION, MODE_DIVERGENCE, MODE_JUMP, MODE_ACTION, MODE_MOTION] = range(15) -# Contants for defining alignment types for graphic group +# Contants for defining alignment types for graphic group [ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT, ALIGN_TOP, ALIGN_MIDDLE, ALIGN_BOTTOM] = range(6) # Contants for defining which drawing mode is selected for app @@ -105,16 +105,17 @@ REFRESH_HIGHLIGHT_PERIOD = 0.1 HANDLE_CURSORS = { - (1, 1) : 2, - (3, 3) : 2, - (1, 3) : 3, - (3, 1) : 3, - (1, 2) : 4, - (3, 2) : 4, - (2, 1) : 5, - (2, 3) : 5 + (1, 1): 2, + (3, 3): 2, + (1, 3): 3, + (3, 1): 3, + (1, 2): 4, + (3, 2): 4, + (2, 1): 5, + (2, 3): 5 } + def round_scaling(x, n, constraint=0): fraction = float(x) / float(n) if constraint == -1: @@ -122,26 +123,36 @@ else: xround = round(fraction) if constraint == 1 and xround < fraction: - xround += 1 + xround += 1 return int(xround * n) + """ Basic vector operations for calculate wire points """ -# Create a vector from two points and define if vector must be normal -def vector(p1, p2, normal = True): + +def vector(p1, p2, normal=True): + """ + Create a vector from two points and define if vector must be normal + """ vector = (p2.x - p1.x, p2.y - p1.y) if normal: return normalize(vector) return vector -# Calculate the norm of a given vector + def norm(v): + """ + Calculate the norm of a given vector + """ return sqrt(v[0] * v[0] + v[1] * v[1]) -# Normalize a given vector + def normalize(v): + """ + Normalize a given vector + """ v_norm = norm(v) # Verifie if it is not a null vector if v_norm > 0: @@ -149,24 +160,32 @@ else: return v -# Calculate the scalar product of two vectors + def is_null_vector(v): + """ + Calculate the scalar product of two vectors + """ return v == (0, 0) -# Calculate the scalar product of two vectors + def add_vectors(v1, v2): + """ + Calculate the scalar product of two vectors + """ return (v1[0] + v2[0], v1[1] + v2[1]) -# Calculate the scalar product of two vectors + def product(v1, v2): + """ + Calculate the scalar product of two vectors + """ return v1[0] * v2[0] + v1[1] * v2[1] -""" -Function that calculates the nearest point of the grid defined by scaling for the given point -""" - def GetScaledEventPosition(event, dc, scaling): + """ + Function that calculates the nearest point of the grid defined by scaling for the given point + """ pos = event.GetLogicalPosition(dc) if scaling: pos.x = round(float(pos.x) / float(scaling[0])) * scaling[0] @@ -174,11 +193,10 @@ return pos -""" -Function that choose a direction during the wire points generation -""" - def DirectionChoice(v_base, v_target, dir_target): + """ + Function that choose a direction during the wire points generation + """ dir_product = product(v_base, v_target) if dir_product < 0: return (-v_base[0], -v_base[1]) @@ -186,31 +204,37 @@ return dir_target return v_base + def MiterPen(colour, width=1, style=wx.SOLID): pen = wx.Pen(colour, width, style) pen.SetJoin(wx.JOIN_MITER) pen.SetCap(wx.CAP_PROJECTING) return pen -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Helpers for highlighting text -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + def AddHighlight(highlights, infos): RemoveHighlight(highlights, infos) highlights.append(infos) + def RemoveHighlight(highlights, infos): if infos in highlights: highlights.remove(infos) return True return False + def ClearHighlight(highlights, highlight_type=None): if highlight_type is not None: return [highlight for highlight in highlights if highlight[2] != highlight_type] return [] + def DrawHighlightedText(dc, text, highlights, x, y): current_pen = dc.GetPen() dc.SetPen(wx.TRANSPARENT_PEN) @@ -224,26 +248,27 @@ dc.DrawText(part, x + offset_width, y) dc.SetPen(current_pen) dc.SetTextForeground(wx.BLACK) - -#------------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------------- # Graphic element base class -#------------------------------------------------------------------------------- - -""" -Class that implements a generic graphic element -""" +# ------------------------------------------------------------------------------- + class Graphic_Element(ToolTipProducer): - + """ + Class that implements a generic graphic element + """ + # Create a new graphic element - def __init__(self, parent, id = None): + def __init__(self, parent, id=None): ToolTipProducer.__init__(self, parent) self.Parent = parent self.Id = id self.oldPos = None self.StartPos = None self.CurrentDrag = None - self.Handle = (None,None) + self.Handle = (None, None) self.Dragging = False self.Selected = False self.Highlighted = False @@ -251,22 +276,22 @@ self.Size = wx.Size(0, 0) self.BoundingBox = wx.Rect(0, 0, 0, 0) self.Visible = False - + def GetDefinition(self): return [self.Id], [] - + def TestVisible(self, screen): self.Visible = self.Selected or self.GetRedrawRect().Intersects(screen) - + def IsVisible(self): return self.Visible - + def SpreadCurrent(self): pass - + def GetConnectorTranslation(self, element): return {} - + def FindNearestConnector(self, position, connectors): distances = [] for connector in connectors: @@ -278,20 +303,20 @@ if len(distances) > 0: return distances[0][1] return None - + def IsOfType(self, type, reference): return self.Parent.IsOfType(type, reference) - + def IsEndType(self, type): return self.Parent.IsEndType(type) - + def GetDragging(self): return self.Dragging - + # Make a clone of this element def Clone(self, parent): return Graphic_Element(parent, self.Id) - + # Changes the block position def SetPosition(self, x, y): self.Pos.x = x @@ -302,7 +327,7 @@ # Returns the block position def GetPosition(self): return self.Pos.x, self.Pos.y - + # Changes the element size def SetSize(self, width, height): self.Size.SetWidth(width) @@ -313,11 +338,11 @@ # Returns the element size def GetSize(self): return self.Size.GetWidth(), self.Size.GetHeight() - + # Returns the minimum element size def GetMinSize(self): return 0, 0 - + # Set size of the element to the minimum size def SetBestSize(self, scaling, x_factor=0.5, y_factor=0.5): width, height = self.GetSize() @@ -336,31 +361,31 @@ height = round_scaling(height, scaling[1], 1) self.SetSize(width, height) return self.Pos.x - posx, self.Pos.y - posy - + # Refresh the element Bounding Box def RefreshBoundingBox(self): self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0], self.Size[1]) - + # Refresh the element connectors position def RefreshConnectors(self): pass - + # Refresh the position of wires connected to element inputs and outputs def RefreshConnected(self): pass - + # Change the parent def SetParent(self, parent): self.Parent = parent - + # Override this method for defining the method to call for deleting this element def Delete(self): pass - + # Returns the Id def GetId(self): return self.Id - + # Returns if the point given is in the bounding box def HitTest(self, pt, connectors=True): if connectors: @@ -368,21 +393,21 @@ else: rect = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0], self.Size[1]) return rect.InsideXY(pt.x, pt.y) - + # Returns if the point given is in the bounding box def IsInSelection(self, rect): return rect.InsideXY(self.BoundingBox.x, self.BoundingBox.y) and rect.InsideXY(self.BoundingBox.x + self.BoundingBox.width, self.BoundingBox.y + self.BoundingBox.height) - + # Override this method for refreshing the bounding box def RefreshBoundingBox(self): pass - + # Returns the bounding box def GetBoundingBox(self): return self.BoundingBox - + # Returns the RedrawRect - def GetRedrawRect(self, movex = 0, movey = 0): + def GetRedrawRect(self, movex=0, movey=0): scalex, scaley = self.Parent.GetViewScale() rect = wx.Rect() rect.x = self.BoundingBox.x - int(HANDLE_SIZE / scalex) - 3 - abs(movex) @@ -390,42 +415,42 @@ rect.width = self.BoundingBox.width + 2 * (int(HANDLE_SIZE / scalex) + abs(movex) + 1) + 4 rect.height = self.BoundingBox.height + 2 * (int(HANDLE_SIZE / scaley) + abs(movey) + 1) + 4 return rect - - def Refresh(self, rect = None): + + def Refresh(self, rect=None): if self.Visible: if rect is not None: self.Parent.RefreshRect(self.Parent.GetScrolledRect(rect), False) else: self.Parent.RefreshRect(self.Parent.GetScrolledRect(self.GetRedrawRect()), False) - + # Change the variable that indicates if this element is selected def SetSelected(self, selected): self.Selected = selected self.Refresh() - + # Change the variable that indicates if this element is highlighted def SetHighlighted(self, highlighted): self.Highlighted = highlighted self.Refresh() - + # Test if the point is on a handle of this element def TestHandle(self, event): dc = self.Parent.GetLogicalDC() scalex, scaley = dc.GetUserScale() pos = event.GetPosition() pt = wx.Point(*self.Parent.CalcUnscrolledPosition(pos.x, pos.y)) - + left = (self.BoundingBox.x - 2) * scalex - HANDLE_SIZE center = (self.BoundingBox.x + self.BoundingBox.width / 2) * scalex - HANDLE_SIZE / 2 right = (self.BoundingBox.x + self.BoundingBox.width + 2) * scalex - + top = (self.BoundingBox.y - 2) * scaley - HANDLE_SIZE middle = (self.BoundingBox.y + self.BoundingBox.height / 2) * scaley - HANDLE_SIZE / 2 bottom = (self.BoundingBox.y + self.BoundingBox.height + 2) * scaley - + extern_rect = wx.Rect(left, top, right + HANDLE_SIZE - left, bottom + HANDLE_SIZE - top) intern_rect = wx.Rect(left + HANDLE_SIZE, top + HANDLE_SIZE, right - left - HANDLE_SIZE, bottom - top - HANDLE_SIZE) - + # Verify that this element is selected if self.Selected and extern_rect.InsideXY(pt.x, pt.y) and not intern_rect.InsideXY(pt.x, pt.y): # Find if point is on a handle horizontally @@ -450,7 +475,7 @@ if (handle_x, handle_y) in VALID_HANDLES: return handle_x, handle_y return 0, 0 - + # Method called when a LeftDown event have been generated def OnLeftDown(self, event, dc, scaling): pos = event.GetLogicalPosition(dc) @@ -469,7 +494,7 @@ self.oldPos = GetScaledEventPosition(event, dc, scaling) self.StartPos = wx.Point(self.Pos.x, self.Pos.y) self.CurrentDrag = wx.Point(0, 0) - + # Method called when a LeftUp event have been generated def OnLeftUp(self, event, dc, scaling): # If a dragging have been initiated @@ -498,7 +523,7 @@ # Method called when a LeftDClick event have been generated def OnLeftDClick(self, event, dc, scaling): pass - + # Method called when a Motion event have been generated def OnMotion(self, event, dc, scaling): # If the cursor is dragging and the element have been clicked @@ -532,19 +557,19 @@ return 0, 0 # Moves the element - def Move(self, dx, dy, exclude = []): + def Move(self, dx, dy, exclude=[]): self.Pos.x += max(-self.BoundingBox.x, dx) self.Pos.y += max(-self.BoundingBox.y, dy) self.RefreshConnected(exclude) self.RefreshBoundingBox() - + # Resizes the element from position and size given def Resize(self, x, y, width, height): self.Move(x, y) self.SetSize(width, height) - + # Refreshes the element state according to move defined and handle selected - def ProcessDragging(self, movex, movey, event, scaling, width_fac = 1, height_fac = 1): + def ProcessDragging(self, movex, movey, event, scaling, width_fac=1, height_fac=1): handle_type, handle = self.Handle # If it is a resize handle, calculate the values from resizing if handle_type == HANDLE_RESIZE: @@ -618,27 +643,27 @@ movey = self.StartPos.y - self.Pos.y else: movex = self.StartPos.x - self.Pos.x - movey = self.StartPos.y + self.CurrentDrag.y - self.Pos.y + movey = self.StartPos.y + self.CurrentDrag.y - self.Pos.y self.Move(movex, movey) return movex, movey return 0, 0 - + # Override this method for defining the method to call for adding an highlight to this element def AddHighlight(self, infos, start, end, highlight_type): pass - + # Override this method for defining the method to call for removing an highlight from this element def RemoveHighlight(self, infos, start, end, highlight_type): pass - + # Override this method for defining the method to call for removing all the highlights of one particular type from this element def ClearHighlight(self, highlight_type=None): pass - + # Override this method for defining the method to call for refreshing the model of this element def RefreshModel(self, move=True): pass - + # Draws the highlightment of this element if it is highlighted (can be overwritten) def DrawHighlightment(self, dc): scalex, scaley = dc.GetUserScale() @@ -646,13 +671,13 @@ dc.SetPen(MiterPen(HIGHLIGHTCOLOR)) dc.SetBrush(wx.Brush(HIGHLIGHTCOLOR)) dc.SetLogicalFunction(wx.AND) - dc.DrawRectangle(int(round((self.Pos.x - 1) * scalex)) - 2, - int(round((self.Pos.y - 1) * scaley)) - 2, - int(round((self.Size.width + 3) * scalex)) + 5, + dc.DrawRectangle(int(round((self.Pos.x - 1) * scalex)) - 2, + int(round((self.Pos.y - 1) * scaley)) - 2, + int(round((self.Size.width + 3) * scalex)) + 5, int(round((self.Size.height + 3) * scaley)) + 5) dc.SetLogicalFunction(wx.COPY) dc.SetUserScale(scalex, scaley) - + # Draws the handles of this element if it is selected def Draw(self, dc): if not getattr(dc, "printing", False): @@ -663,55 +688,55 @@ dc.SetUserScale(1, 1) dc.SetPen(MiterPen(wx.BLACK)) dc.SetBrush(wx.BLACK_BRUSH) - + left = (self.BoundingBox.x - 2) * scalex - HANDLE_SIZE center = (self.BoundingBox.x + self.BoundingBox.width / 2) * scalex - HANDLE_SIZE / 2 right = (self.BoundingBox.x + self.BoundingBox.width + 2) * scalex - + top = (self.BoundingBox.y - 2) * scaley - HANDLE_SIZE middle = (self.BoundingBox.y + self.BoundingBox.height / 2) * scaley - HANDLE_SIZE / 2 bottom = (self.BoundingBox.y + self.BoundingBox.height + 2) * scaley - + for x, y in [(left, top), (center, top), (right, top), (left, middle), (right, middle), (left, bottom), (center, bottom), (right, bottom)]: dc.DrawRectangle(x, y, HANDLE_SIZE, HANDLE_SIZE) - + dc.SetUserScale(scalex, scaley) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Group of graphic elements -#------------------------------------------------------------------------------- - -""" -Class that implements a group of graphic elements -""" +# ------------------------------------------------------------------------------- + class Graphic_Group(Graphic_Element): - + """ + Class that implements a group of graphic elements + """ + # Create a new group of graphic elements def __init__(self, parent): Graphic_Element.__init__(self, parent) self.Elements = [] self.RefreshWireExclusion() self.RefreshBoundingBox() - + # Destructor def __del__(self): self.Elements = [] - + def GetDefinition(self): - blocks = [] + blocks = [] wires = [] for element in self.Elements: block, wire = element.GetDefinition() blocks.extend(block) wires.extend(wire) return blocks, wires - + # Make a clone of this element - def Clone(self, parent, pos = None): + def Clone(self, parent, pos=None): group = Graphic_Group(parent) connectors = {} exclude_names = {} @@ -729,9 +754,9 @@ if parent.IsNamedElement(element): name = parent.GenerateNewName(element, exclude_names) exclude_names[name.upper()] = True - new_element = element.Clone(parent, newid, name, pos = new_pos) + new_element = element.Clone(parent, newid, name, pos=new_pos) else: - new_element = element.Clone(parent, newid, pos = new_pos) + new_element = element.Clone(parent, newid, pos=new_pos) new_element.SetBestSize(parent.Scaling) else: new_element = element.Clone(parent) @@ -751,20 +776,20 @@ if not isinstance(element, Wire): parent.AddBlockInModel(element) return group - + def CanAddBlocks(self, parent): valid = True for element in self.Elements: if not isinstance(element, Wire): valid &= parent.CanAddElement(element) return valid - + def IsVisible(self): for element in self.Elements: if element.IsVisible(): return True return False - + # Refresh the list of wire excluded def RefreshWireExclusion(self): self.WireExcluded = [] @@ -774,9 +799,9 @@ endblock = element.EndConnected.GetParentBlock() if startblock in self.Elements and endblock in self.Elements: self.WireExcluded.append(element) - + # Returns the RedrawRect - def GetRedrawRect(self, movex = 0, movey = 0): + def GetRedrawRect(self, movex=0, movey=0): rect = None for element in self.Elements: if rect is None: @@ -784,41 +809,41 @@ else: rect = rect.Union(element.GetRedrawRect(movex, movey)) return rect - + # Clean this group of elements def Clean(self): # Clean all the elements of the group for element in self.Elements: element.Clean() - + # Delete this group of elements def Delete(self): # Delete all the elements of the group for element in self.Elements: element.Delete() self.WireExcluded = [] - + # Returns if the point given is in the bounding box of one of the elements of this group def HitTest(self, pt, connectors=True): result = False for element in self.Elements: result |= element.HitTest(pt, connectors) return result - + # Returns if the element given is in this group def IsElementIn(self, element): return element in self.Elements - + # Change the elements of the group def SetElements(self, elements): self.Elements = elements self.RefreshWireExclusion() self.RefreshBoundingBox() - + # Returns the elements of the group def GetElements(self): return self.Elements - + # Align the group elements def AlignElements(self, horizontally, vertically): minx = self.BoundingBox.x + self.BoundingBox.width @@ -854,11 +879,11 @@ element.Move(movex, movey) element.RefreshModel() self.RefreshBoundingBox() - + # Add the given element to the group of elements def AddElement(self, element): self.Elements.append(element) - + # Remove or select the given element if it is or not in the group def SelectElement(self, element): if element in self.Elements: @@ -867,7 +892,7 @@ self.Elements.append(element) self.RefreshWireExclusion() self.RefreshBoundingBox() - + # Move this group of elements def Move(self, movex, movey): movex = max(-self.BoundingBox.x, movex) @@ -879,7 +904,7 @@ elif element in self.WireExcluded: element.Move(movex, movey, True) self.RefreshBoundingBox() - + # Refreshes the bounding box of this group of elements def RefreshBoundingBox(self): if len(self.Elements) > 0: @@ -902,7 +927,7 @@ # Forbids to change the group position def SetPosition(x, y): pass - + # Returns the position of this group def GetPosition(self, exclude_wires=False): if exclude_wires: @@ -919,15 +944,15 @@ return 0, 0 return posx, posy return self.BoundingBox.x, self.BoundingBox.y - + # Forbids to change the group size def SetSize(width, height): pass - + # Returns the size of this group def GetSize(self): return self.BoundingBox.width, self.BoundingBox.height - + # Set size of the group elements to their minimum size def SetBestSize(self, scaling): max_movex = max_movey = 0 @@ -936,7 +961,7 @@ max_movex = max(max_movex, movex) max_movey = max(max_movey, movey) return max_movex, max_movey - + # Refreshes the group elements to move defined and handle selected def ProcessDragging(self, movex, movey, event, scaling): handle_type, handle = self.Handle @@ -960,17 +985,17 @@ self.Move(movex, movey) return movex, movey return 0, 0 - + # Change the variable that indicates if this element is highlighted def SetHighlighted(self, highlighted): for element in self.Elements: element.SetHighlighted(highlighted) - + def HighlightPoint(self, pos): for element in self.Elements: if isinstance(element, Wire): element.HighlightPoint(pos) - + # Method called when a LeftDown event have been generated def OnLeftDown(self, event, dc, scaling): Graphic_Element.OnLeftDown(self, event, dc, scaling) @@ -998,18 +1023,19 @@ for element in self.Elements: element.Draw(dc) -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Connector for all types of blocks -#------------------------------------------------------------------------------- - -""" -Class that implements a connector for any type of block -""" +# ------------------------------------------------------------------------------- + class Connector(DebugDataConsumer, ToolTipProducer): - + """ + Class that implements a connector for any type of block + """ + # Create a new connector - def __init__(self, parent, name, type, position, direction, negated = False, edge = "none", onlyone = False): + def __init__(self, parent, name, type, position, direction, negated=False, edge="none", onlyone=False): DebugDataConsumer.__init__(self) ToolTipProducer.__init__(self, parent.Parent) self.ParentBlock = parent @@ -1033,15 +1059,15 @@ self.Selected = False self.Highlights = [] self.RefreshNameSize() - + def Flush(self): self.ParentBlock = None for wire, handle in self.Wires: wire.Flush() self.Wires = [] - + # Returns the RedrawRect - def GetRedrawRect(self, movex = 0, movey = 0): + def GetRedrawRect(self, movex=0, movey=0): parent_pos = self.ParentBlock.GetPosition() x = min(parent_pos[0] + self.Pos.x, parent_pos[0] + self.Pos.x + self.Direction[0] * CONNECTOR_SIZE) y = min(parent_pos[1] + self.Pos.y, parent_pos[1] + self.Pos.y + self.Direction[1] * CONNECTOR_SIZE) @@ -1060,43 +1086,44 @@ if self.Edge == "rising" and self.Direction[1] == 1: y -= 5 height += 5 - rect = wx.Rect(x - abs(movex), y - abs(movey), width + 2 * abs(movex), height + 2 * abs(movey)) + rect = wx.Rect(x - abs(movex), y - abs(movey), width + 2 * abs(movex), height + 2 * abs(movey)) if self.ValueSize is None and isinstance(self.ComputedValue, (StringType, UnicodeType)): self.ValueSize = self.ParentBlock.Parent.GetMiniTextExtent(self.ComputedValue) if self.ValueSize is not None: width, height = self.ValueSize - rect = rect.Union(wx.Rect( - parent_pos[0] + self.Pos.x + CONNECTOR_SIZE * self.Direction[0] + \ - width * (self.Direction[0] - 1) / 2, - parent_pos[1] + self.Pos.y + CONNECTOR_SIZE * self.Direction[1] + \ - height * (self.Direction[1] - 1), + rect = rect.Union( + wx.Rect( + parent_pos[0] + self.Pos.x + CONNECTOR_SIZE * self.Direction[0] + + width * (self.Direction[0] - 1) / 2, + parent_pos[1] + self.Pos.y + CONNECTOR_SIZE * self.Direction[1] + + height * (self.Direction[1] - 1), width, height)) return rect - + # Change the connector selection def SetSelected(self, selected): self.Selected = selected - + # Make a clone of the connector - def Clone(self, parent = None): + def Clone(self, parent=None): if parent is None: parent = self.ParentBlock return Connector(parent, self.Name, self.Type, wx.Point(self.Pos[0], self.Pos[1]), - self.Direction, self.Negated) - + self.Direction, self.Negated) + # Returns the connector parent block def GetParentBlock(self): return self.ParentBlock - + # Returns the connector type - def GetType(self, raw = False): + def GetType(self, raw=False): if self.ParentBlock.IsEndType(self.Type) or raw: return self.Type elif (self.Negated or self.Edge != "none") and self.ParentBlock.IsOfType("BOOL", self.Type): return "BOOL" else: return self.ParentBlock.GetConnectionResultType(self, self.Type) - + # Returns the connector type def GetConnectedType(self): if self.ParentBlock.IsEndType(self.Type): @@ -1104,7 +1131,7 @@ elif len(self.Wires) == 1: return self.Wires[0][0].GetOtherConnectedType(self.Wires[0][1]) return self.Type - + # Returns the connector type def GetConnectedRedrawRect(self, movex, movey): rect = None @@ -1114,22 +1141,22 @@ else: rect = rect.Union(wire.GetRedrawRect()) return rect - + # Returns if connector type is compatible with type given def IsCompatible(self, type): reference = self.GetType() return self.ParentBlock.IsOfType(type, reference) or self.ParentBlock.IsOfType(reference, type) - + # Changes the connector name def SetType(self, type): self.Type = type for wire, handle in self.Wires: wire.SetValid(wire.IsConnectedCompatible()) - + # Returns the connector name def GetName(self): return self.Name - + # Changes the connector name def SetName(self, name): self.Name = name @@ -1140,15 +1167,15 @@ self.Forced = forced if self.Visible: self.Parent.ElementNeedRefresh(self) - + def GetComputedValue(self): if self.Value is not None and self.Value != "undefined" and not isinstance(self.Value, BooleanType): return self.Value return None - + def GetToolTipValue(self): return self.GetComputedValue() - + def SetValue(self, value): if self.Value != value: self.Value = value @@ -1161,7 +1188,7 @@ self.ValueSize = None if self.ParentBlock.Visible: self.ParentBlock.Parent.ElementNeedRefresh(self) - + def RefreshForced(self): self.Forced = False for wire, handle in self.Wires: @@ -1169,12 +1196,12 @@ def RefreshValue(self): self.Value = self.ReceivingCurrent() - + def RefreshValid(self): self.Valid = True for wire, handle in self.Wires: self.Valid &= wire.GetValid() - + def ReceivingCurrent(self): current = False for wire, handle in self.Wires: @@ -1184,36 +1211,36 @@ elif value == "undefined": current = "undefined" return current - + def SpreadCurrent(self, spreading): for wire, handle in self.Wires: wire.SetValue(spreading) - + # Changes the connector name size def RefreshNameSize(self): if self.Name != "": self.NameSize = self.ParentBlock.Parent.GetTextExtent(self.Name) else: self.NameSize = 0, 0 - + # Returns the connector name size def GetNameSize(self): return self.NameSize - + # Returns the wires connected to the connector def GetWires(self): return self.Wires - + # Returns the parent block Id def GetBlockId(self): return self.ParentBlock.GetId() - + # Returns the connector relative position def GetRelPosition(self): return self.Pos - + # Returns the connector absolute position - def GetPosition(self, size = True): + def GetPosition(self, size=True): parent_pos = self.ParentBlock.GetPosition() # If the position of the end of the connector is asked if size: @@ -1223,25 +1250,25 @@ x = parent_pos[0] + self.Pos.x y = parent_pos[1] + self.Pos.y return wx.Point(x, y) - + # Change the connector relative position def SetPosition(self, pos): self.Pos = pos - + # Returns the connector direction def GetDirection(self): return self.Direction - + # Change the connector direction def SetDirection(self, direction): self.Direction = direction - + # Connect a wire to this connector at the last place - def Connect(self, wire, refresh = True): + def Connect(self, wire, refresh=True): self.InsertConnect(len(self.Wires), wire, refresh) - + # Connect a wire to this connector at the place given - def InsertConnect(self, idx, wire, refresh = True): + def InsertConnect(self, idx, wire, refresh=True): if wire not in self.Wires: self.Wires.insert(idx, wire) if wire[1] == 0: @@ -1250,16 +1277,16 @@ wire[0].ConnectEndPoint(None, self) if refresh: self.ParentBlock.RefreshModel(False) - + # Returns the index of the wire given in the list of connected def GetWireIndex(self, wire): for i, (tmp_wire, handle) in enumerate(self.Wires): if tmp_wire == wire: return i return None - + # Unconnect a wire or all wires connected to the connector - def UnConnect(self, wire = None, unconnect = True, delete = False): + def UnConnect(self, wire=None, unconnect=True, delete=False): i = 0 found = False while i < len(self.Wires) and not found: @@ -1281,13 +1308,13 @@ if not delete: self.RefreshValid() self.ParentBlock.RefreshModel(False) - + # Returns if connector has one or more wire connected def IsConnected(self): return len(self.Wires) > 0 - + # Move the wires connected - def MoveConnected(self, exclude = []): + def MoveConnected(self, exclude=[]): if len(self.Wires) > 0: # Calculate the new position of the end point parent_pos = self.ParentBlock.GetPosition() @@ -1300,21 +1327,21 @@ wire.MoveStartPoint(wx.Point(x, y)) else: wire.MoveEndPoint(wx.Point(x, y)) - + # Refreshes the model of all the wires connected def RefreshWires(self): for wire in self.Wires: wire[0].RefreshModel() - + # Refreshes the parent block model def RefreshParentBlock(self): self.ParentBlock.RefreshModel(False) - + # Highlight the parent block def HighlightParentBlock(self, highlight): self.ParentBlock.SetHighlighted(highlight) self.ParentBlock.Refresh() - + # Returns all the blocks connected to this connector def GetConnectedBlocks(self): blocks = [] @@ -1330,44 +1357,44 @@ if block not in blocks: blocks.append(block) return blocks - + # Returns the connector negated property def IsNegated(self): return self.Negated - + # Changes the connector negated property def SetNegated(self, negated): if self.ParentBlock.IsOfType("BOOL", self.Type): self.Negated = negated self.Edge = "none" - + # Returns the connector edge property def GetEdge(self): return self.Edge - + # Changes the connector edge property def SetEdge(self, edge): if self.ParentBlock.IsOfType("BOOL", self.Type): - self.Edge = edge + self.Edge = edge self.Negated = False - + # assume that pointer is already inside of this connector def ConnectionAvailable(self, direction=None, exclude=True): wire_nums = len(self.Wires) - - connector_free = (wire_nums<= 0) + + connector_free = (wire_nums <= 0) connector_max_used = ((wire_nums > 0) and self.OneConnected) if (self.Parent.CurrentLanguage in ["SFC", "LD"]) and (self.Type == "BOOL"): - connector_max_used = False; + connector_max_used = False # connector is available for new connection - connect = connector_free or not connector_max_used + connect = connector_free or not connector_max_used return connect, connector_max_used - + # Tests if the point given is near from the end point of this connector def TestPoint(self, pt, direction=None, exclude=True): - inside = False; - check_point = (not exclude) and (direction is None or self.Direction == direction); + inside = False + check_point = (not exclude) and (direction is None or self.Direction == direction) if check_point: # Calculate a square around the end point of this connector @@ -1377,10 +1404,10 @@ width = ANCHOR_DISTANCE * 2 + abs(self.Direction[0]) * CONNECTOR_SIZE height = ANCHOR_DISTANCE * 2 + abs(self.Direction[1]) * CONNECTOR_SIZE rect = wx.Rect(x, y, width, height) - inside = rect.InsideXY(pt.x, pt.y); - + inside = rect.InsideXY(pt.x, pt.y) + return inside - + # Draws the highlightment of this element if it is highlighted def DrawHighlightment(self, dc): scalex, scaley = dc.GetUserScale() @@ -1393,7 +1420,7 @@ parent_pos = self.ParentBlock.GetPosition() posx = parent_pos[0] + self.Pos.x posy = parent_pos[1] + self.Pos.y - xstart = parent_pos[0] + self.Pos.x + xstart = parent_pos[0] + self.Pos.x ystart = parent_pos[1] + self.Pos.y if self.Direction[0] < 0: xstart += 1 @@ -1401,18 +1428,18 @@ ystart += 1 xend = xstart + CONNECTOR_SIZE * self.Direction[0] yend = ystart + CONNECTOR_SIZE * self.Direction[1] - dc.DrawLine(round((xstart + self.Direction[0]) * scalex), round((ystart + self.Direction[1]) * scaley), + dc.DrawLine(round((xstart + self.Direction[0]) * scalex), round((ystart + self.Direction[1]) * scaley), round(xend * scalex), round(yend * scaley)) dc.SetLogicalFunction(wx.COPY) dc.SetUserScale(scalex, scaley) - + # Adds an highlight to the connector def AddHighlight(self, infos, start, end, highlight_type): if highlight_type == ERROR_HIGHLIGHT: for wire, handle in self.Wires: wire.SetValid(False) AddHighlight(self.Highlights, (start, end, highlight_type)) - + # Removes an highlight from the connector def RemoveHighlight(self, infos, start, end, highlight_type): error = False @@ -1425,7 +1452,7 @@ if not error: for wire, handle in self.Wires: wire.SetValid(wire.IsConnectedCompatible()) - + # Removes all the highlights of one particular type from the connector def ClearHighlight(self, highlight_type=None): error = False @@ -1441,13 +1468,13 @@ if not error: for wire, handle in self.Wires: wire.SetValid(wire.IsConnectedCompatible()) - + # Draws the connector def Draw(self, dc): if self.Selected: dc.SetPen(MiterPen(wx.BLUE, 3)) dc.SetBrush(wx.WHITE_BRUSH) - #elif len(self.Highlights) > 0: + # elif len(self.Highlights) > 0: # dc.SetPen(MiterPen(self.Highlights[-1][1])) # dc.SetBrush(wx.Brush(self.Highlights[-1][0])) else: @@ -1466,19 +1493,19 @@ dc.SetPen(MiterPen(wx.BLACK)) dc.SetBrush(wx.WHITE_BRUSH) parent_pos = self.ParentBlock.GetPosition() - + if getattr(dc, "printing", False): name_size = dc.GetTextExtent(self.Name) else: name_size = self.NameSize - + if self.Negated: # If connector is negated, draw a circle xcenter = parent_pos[0] + self.Pos.x + (CONNECTOR_SIZE * self.Direction[0]) / 2 ycenter = parent_pos[1] + self.Pos.y + (CONNECTOR_SIZE * self.Direction[1]) / 2 dc.DrawCircle(xcenter, ycenter, CONNECTOR_SIZE / 2) else: - xstart = parent_pos[0] + self.Pos.x + xstart = parent_pos[0] + self.Pos.x ystart = parent_pos[1] + self.Pos.y if self.Edge == "rising": # If connector has a rising edge, draw a right arrow @@ -1524,26 +1551,27 @@ self.ValueSize = self.ParentBlock.Parent.GetMiniTextExtent(self.ComputedValue) if self.ValueSize is not None: width, height = self.ValueSize - dc.DrawText(self.ComputedValue, - parent_pos[0] + self.Pos.x + CONNECTOR_SIZE * self.Direction[0] + \ - width * (self.Direction[0] - 1) / 2, - parent_pos[1] + self.Pos.y + CONNECTOR_SIZE * self.Direction[1] + \ - height * (self.Direction[1] - 1)) + dc.DrawText(self.ComputedValue, + parent_pos[0] + self.Pos.x + CONNECTOR_SIZE * self.Direction[0] + + width * (self.Direction[0] - 1) / 2, + parent_pos[1] + self.Pos.y + CONNECTOR_SIZE * self.Direction[1] + + height * (self.Direction[1] - 1)) dc.SetFont(self.ParentBlock.Parent.GetFont()) dc.SetTextForeground(wx.BLACK) -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Common Wire Element -#------------------------------------------------------------------------------- - -""" -Class that implements a wire for connecting two blocks -""" +# ------------------------------------------------------------------------------- + class Wire(Graphic_Element, DebugDataConsumer): - + """ + Class that implements a wire for connecting two blocks + """ + # Create a new wire - def __init__(self, parent, start = None, end = None): + def __init__(self, parent, start=None, end=None): Graphic_Element.__init__(self, parent) DebugDataConsumer.__init__(self) self.StartPoint = start @@ -1568,20 +1596,20 @@ self.ComputingType = False self.Font = parent.GetMiniFont() self.ErrHighlight = False - + def GetDefinition(self): if self.StartConnected is not None and self.EndConnected is not None: startblock = self.StartConnected.GetParentBlock() endblock = self.EndConnected.GetParentBlock() return [], [(startblock.GetId(), endblock.GetId())] return [], [] - + def Flush(self): self.StartConnected = None self.EndConnected = None - + # Returns the RedrawRect - def GetRedrawRect(self, movex = 0, movey = 0): + def GetRedrawRect(self, movex=0, movey=0): rect = Graphic_Element.GetRedrawRect(self, movex, movey) if self.StartConnected: rect = rect.Union(self.StartConnected.GetRedrawRect(movex, movey)) @@ -1607,8 +1635,8 @@ y = self.Points[middle].y - height rect = rect.Union(wx.Rect(x, y, width, height)) return rect - - def Clone(self, parent, connectors = {}, dx = 0, dy = 0): + + def Clone(self, parent, connectors={}, dx=0, dy=0): start_connector = connectors.get(self.StartConnected, None) end_connector = connectors.get(self.EndConnected, None) if start_connector is not None and end_connector is not None: @@ -1620,18 +1648,18 @@ wire.ConnectEndPoint(end_connector.GetPosition(), end_connector) return wire return None - + # Forbids to change the wire position def SetPosition(x, y): pass - + # Forbids to change the wire size def SetSize(width, height): pass - + # Forbids to et size of the group elements to their minimum size pass - + # Moves and Resizes the element for fitting scaling def SetBestSize(self, scaling): if scaling is not None: @@ -1664,11 +1692,11 @@ point.y += movey return movex_max, movey_max return 0, 0 - + # Returns connector to which start point is connected def GetStartConnected(self): return self.StartConnected - + # Returns connector to which start point is connected def GetStartConnectedType(self): if self.StartConnected and not self.ComputingType: @@ -1677,11 +1705,11 @@ self.ComputingType = False return computed_type return None - + # Returns connector to which end point is connected def GetEndConnected(self): return self.EndConnected - + # Returns connector to which end point is connected def GetEndConnectedType(self): if self.EndConnected and not self.ComputingType: @@ -1690,7 +1718,7 @@ self.ComputingType = False return computed_type return None - + def GetConnectionDirection(self): if self.StartConnected is None and self.EndConnected is None: return None @@ -1707,26 +1735,26 @@ else: return (-self.StartPoint[1][0], -self.StartPoint[1][1]) return None - + def GetOtherConnected(self, connector): if self.StartConnected == connector: return self.EndConnected else: return self.StartConnected - + def GetOtherConnectedType(self, handle): if handle == 0: return self.GetEndConnectedType() else: return self.GetStartConnectedType() - + def IsConnectedCompatible(self): if self.StartConnected: return self.StartConnected.IsCompatible(self.GetEndConnectedType()) elif self.EndConnected: return True return False - + def SetForced(self, forced): if self.Forced != forced: self.Forced = forced @@ -1741,7 +1769,7 @@ if self.Value is not None and self.Value != "undefined" and not isinstance(self.Value, BooleanType): return self.Value return None - + def GetToolTipValue(self): return self.GetComputedValue() @@ -1773,25 +1801,25 @@ if isinstance(value, BooleanType) and self.StartConnected is not None: block = self.StartConnected.GetParentBlock() block.SpreadCurrent() - + # Unconnect the start and end points def Clean(self): if self.StartConnected: self.UnConnectStartPoint() if self.EndConnected: self.UnConnectEndPoint() - + # Delete this wire by calling the corresponding method def Delete(self): self.Parent.DeleteWire(self) - + # Select a segment and not the whole wire. It's useful for Ladder Diagram def SetSelectedSegment(self, segment): # The last segment is indicated if segment == -1: segment = len(self.Segments) - 1 # The selected segment is reinitialised - if segment == None: + if segment is None: if self.StartConnected: self.StartConnected.SetSelected(False) if self.EndConnected: @@ -1814,17 +1842,17 @@ self.EndConnected.SetSelected(True) self.SelectedSegment = segment self.Refresh() - + def SetValid(self, valid): self.Valid = valid if self.StartConnected: self.StartConnected.RefreshValid() if self.EndConnected: self.EndConnected.RefreshValid() - + def GetValid(self): return self.Valid - + # Reinitialize the wire points def ResetPoints(self): if self.StartPoint and self.EndPoint: @@ -1833,7 +1861,7 @@ else: self.Points = [] self.Segments = [] - + # Refresh the wire bounding box def RefreshBoundingBox(self): if len(self.Points) > 0: @@ -1862,7 +1890,7 @@ self.Pos.x, self.Pos.y = minx, miny self.Size = wx.Size(maxx - minx, maxy - miny) self.BoundingBox = wx.Rect(minbbxx, minbbxy, maxbbxx - minbbxx + 1, maxbbxy - minbbxy + 1) - + # Refresh the realpoints that permits to keep the proportionality in wire during resizing def RefreshRealPoints(self): if len(self.Points) > 0: @@ -1870,8 +1898,8 @@ # Calculate float relative position of each point with the minimum point for point in self.Points: self.RealPoints.append([float(point.x - self.Pos.x), float(point.y - self.Pos.y)]) - - # Returns the wire minimum size + + # Returns the wire minimum size def GetMinSize(self): width = 1 height = 1 @@ -1893,7 +1921,7 @@ width = MIN_SEGMENT_SIZE height = MIN_SEGMENT_SIZE return width + 1, height + 1 - + # Returns if the point given is on one of the wire segments def HitTest(self, pt, connectors=True): test = False @@ -1903,7 +1931,7 @@ x1 = self.Points[i].x - self.Segments[0][0] * CONNECTOR_SIZE y1 = self.Points[i].y - self.Segments[0][1] * CONNECTOR_SIZE else: - x1, y1 = self.Points[i].x, self.Points[i].y + x1, y1 = self.Points[i].x, self.Points[i].y if i == len(self.Points) - 2 and self.EndConnected is not None: x2 = self.Points[i + 1].x + self.Segments[-1][0] * CONNECTOR_SIZE y2 = self.Points[i + 1].y + self.Segments[-1][1] * CONNECTOR_SIZE @@ -1911,25 +1939,25 @@ x2, y2 = self.Points[i + 1].x, self.Points[i + 1].y # Calculate a rectangle around the segment rect = wx.Rect(min(x1, x2) - ANCHOR_DISTANCE, min(y1, y2) - ANCHOR_DISTANCE, - abs(x1 - x2) + 2 * ANCHOR_DISTANCE, abs(y1 - y2) + 2 * ANCHOR_DISTANCE) - test |= rect.InsideXY(pt.x, pt.y) + abs(x1 - x2) + 2 * ANCHOR_DISTANCE, abs(y1 - y2) + 2 * ANCHOR_DISTANCE) + test |= rect.InsideXY(pt.x, pt.y) return test - - # Returns the wire start or end point if the point given is on one of them + + # Returns the wire start or end point if the point given is on one of them def TestPoint(self, pt): # Test the wire start point rect = wx.Rect(self.Points[0].x - ANCHOR_DISTANCE, self.Points[0].y - ANCHOR_DISTANCE, - 2 * ANCHOR_DISTANCE, 2 * ANCHOR_DISTANCE) + 2 * ANCHOR_DISTANCE, 2 * ANCHOR_DISTANCE) if rect.InsideXY(pt.x, pt.y): return 0 # Test the wire end point if len(self.Points) > 1: rect = wx.Rect(self.Points[-1].x - ANCHOR_DISTANCE, self.Points[-1].y - ANCHOR_DISTANCE, - 2 * ANCHOR_DISTANCE, 2 * ANCHOR_DISTANCE) + 2 * ANCHOR_DISTANCE, 2 * ANCHOR_DISTANCE) if rect.InsideXY(pt.x, pt.y): return -1 return None - + # Returns the wire segment if the point given is on it def TestSegment(self, pt, all=False): for i in xrange(len(self.Segments)): @@ -1939,11 +1967,11 @@ x2, y2 = self.Points[i + 1].x, self.Points[i + 1].y # Calculate a rectangle around the segment rect = wx.Rect(min(x1, x2) - ANCHOR_DISTANCE, min(y1, y2) - ANCHOR_DISTANCE, - abs(x1 - x2) + 2 * ANCHOR_DISTANCE, abs(y1 - y2) + 2 * ANCHOR_DISTANCE) + abs(x1 - x2) + 2 * ANCHOR_DISTANCE, abs(y1 - y2) + 2 * ANCHOR_DISTANCE) if rect.InsideXY(pt.x, pt.y): return i, self.Segments[i] return None - + # Define the wire points def SetPoints(self, points, verify=True): if len(points) > 1: @@ -1952,10 +1980,10 @@ self.StartPoint = [None, vector(self.Points[0], self.Points[1])] self.EndPoint = [None, vector(self.Points[-1], self.Points[-2])] # Calculate the start and end points - self.StartPoint[0] = wx.Point(self.Points[0].x + CONNECTOR_SIZE * self.StartPoint[1][0], - self.Points[0].y + CONNECTOR_SIZE * self.StartPoint[1][1]) - self.EndPoint[0] = wx.Point(self.Points[-1].x + CONNECTOR_SIZE * self.EndPoint[1][0], - self.Points[-1].y + CONNECTOR_SIZE * self.EndPoint[1][1]) + self.StartPoint[0] = wx.Point(self.Points[0].x + CONNECTOR_SIZE * self.StartPoint[1][0], + self.Points[0].y + CONNECTOR_SIZE * self.StartPoint[1][1]) + self.EndPoint[0] = wx.Point(self.Points[-1].x + CONNECTOR_SIZE * self.EndPoint[1][0], + self.Points[-1].y + CONNECTOR_SIZE * self.EndPoint[1][1]) self.Points[0] = self.StartPoint[0] self.Points[-1] = self.EndPoint[0] # Calculate the segments directions @@ -1979,37 +2007,37 @@ i += 1 self.RefreshBoundingBox() self.RefreshRealPoints() - + # Returns the position of the point indicated def GetPoint(self, index): if index < len(self.Points): return self.Points[index].x, self.Points[index].y return None - + # Returns a list of the position of all wire points - def GetPoints(self, invert = False): + def GetPoints(self, invert=False): points = self.VerifyPoints() - points[0] = wx.Point(points[0].x - CONNECTOR_SIZE * self.StartPoint[1][0], - points[0].y - CONNECTOR_SIZE * self.StartPoint[1][1]) - points[-1] = wx.Point(points[-1].x - CONNECTOR_SIZE * self.EndPoint[1][0], - points[-1].y - CONNECTOR_SIZE * self.EndPoint[1][1]) + points[0] = wx.Point(points[0].x - CONNECTOR_SIZE * self.StartPoint[1][0], + points[0].y - CONNECTOR_SIZE * self.StartPoint[1][1]) + points[-1] = wx.Point(points[-1].x - CONNECTOR_SIZE * self.EndPoint[1][0], + points[-1].y - CONNECTOR_SIZE * self.EndPoint[1][1]) # An inversion of the list is asked if invert: points.reverse() return points - + # Returns the position of the two selected segment points def GetSelectedSegmentPoints(self): - if self.SelectedSegment != None and len(self.Points) > 1: + if self.SelectedSegment is not None and len(self.Points) > 1: return self.Points[self.SelectedSegment:self.SelectedSegment + 2] return [] - + # Returns if the selected segment is the first and/or the last of the wire def GetSelectedSegmentConnections(self): - if self.SelectedSegment != None and len(self.Points) > 1: + if self.SelectedSegment is not None and len(self.Points) > 1: return self.SelectedSegment == 0, self.SelectedSegment == len(self.Segments) - 1 return (True, True) - + # Returns the connectors on which the wire is connected def GetConnected(self): connected = [] @@ -2018,7 +2046,7 @@ if self.EndConnected and self.EndPoint[1] == WEST: connected.append(self.EndConnected) return connected - + # Returns the id of the block connected to the first or the last wire point def GetConnectedInfos(self, index): if index == 0 and self.StartConnected: @@ -2026,15 +2054,15 @@ elif index == -1 and self.EndConnected: return self.EndConnected.GetBlockId(), self.EndConnected.GetName() return None - + # Update the wire points position by keeping at most possible the current positions - def GeneratePoints(self, realpoints = True): + def GeneratePoints(self, realpoints=True): i = 0 # Calculate the start enad end points with the minimum segment size in the right direction end = wx.Point(self.EndPoint[0].x + self.EndPoint[1][0] * MIN_SEGMENT_SIZE, - self.EndPoint[0].y + self.EndPoint[1][1] * MIN_SEGMENT_SIZE) - start = wx.Point(self.StartPoint[0].x + self.StartPoint[1][0] * MIN_SEGMENT_SIZE, - self.StartPoint[0].y + self.StartPoint[1][1] * MIN_SEGMENT_SIZE) + self.EndPoint[0].y + self.EndPoint[1][1] * MIN_SEGMENT_SIZE) + start = wx.Point(self.StartPoint[0].x + self.StartPoint[1][0] * MIN_SEGMENT_SIZE, + self.StartPoint[0].y + self.StartPoint[1][1] * MIN_SEGMENT_SIZE) # Evaluate the point till it's the last while i < len(self.Points) - 1: # The next point is the last @@ -2046,15 +2074,17 @@ # If the end point is not in the start direction, a point is added if v_end != self.Segments[0] or v_end == self.EndPoint[1]: self.Points.insert(1, wx.Point(start.x, start.y)) - self.Segments.insert(1, DirectionChoice((self.Segments[0][1], - self.Segments[0][0]), v_end, self.EndPoint[1])) + self.Segments.insert(1, DirectionChoice( + (self.Segments[0][1], + self.Segments[0][0]), v_end, self.EndPoint[1])) # The current point is the second elif i == 1: # The previous direction and the target direction are mainly opposed, a point is added if product(v_end, self.Segments[0]) < 0: self.Points.insert(2, wx.Point(self.Points[1].x, self.Points[1].y)) - self.Segments.insert(2, DirectionChoice((self.Segments[1][1], - self.Segments[1][0]), v_end, self.EndPoint[1])) + self.Segments.insert(2, DirectionChoice( + (self.Segments[1][1], + self.Segments[1][0]), v_end, self.EndPoint[1])) # The previous direction and the end direction are the same or they are # perpendiculars and the end direction points towards current segment elif product(self.Segments[0], self.EndPoint[1]) >= 0 and product(self.Segments[1], self.EndPoint[1]) <= 0: @@ -2066,8 +2096,9 @@ # If the previous direction and the end direction are the same, a point is added if product(self.Segments[0], self.EndPoint[1]) > 0: self.Points.insert(2, wx.Point(self.Points[1].x, self.Points[1].y)) - self.Segments.insert(2, DirectionChoice((self.Segments[1][1], - self.Segments[1][0]), v_end, self.EndPoint[1])) + self.Segments.insert(2, DirectionChoice( + (self.Segments[1][1], + self.Segments[1][0]), v_end, self.EndPoint[1])) else: # Current point is positioned in the middle of start point # and end point on the current direction and a point is added @@ -2076,8 +2107,9 @@ if self.Segments[0][1] != 0: self.Points[1].y = (end.y + start.y) / 2 self.Points.insert(2, wx.Point(self.Points[1].x, self.Points[1].y)) - self.Segments.insert(2, DirectionChoice((self.Segments[1][1], - self.Segments[1][0]), v_end, self.EndPoint[1])) + self.Segments.insert(2, DirectionChoice( + (self.Segments[1][1], + self.Segments[1][0]), v_end, self.EndPoint[1])) else: # The previous direction and the end direction are perpendiculars if product(self.Segments[i - 1], self.EndPoint[1]) == 0: @@ -2093,7 +2125,7 @@ self.Segments[i - 1] = (-self.Segments[i - 1][0], -self.Segments[i - 1][1]) else: test = True - # If the current point is the third, test if the second + # If the current point is the third, test if the second # point can be aligned with the end point if i == 2: test_point = wx.Point(self.Points[1].x, self.Points[1].y) @@ -2119,8 +2151,12 @@ if self.Segments[1][1] != 0: self.Points[2].y = (self.Points[1].y + end.y) / 2 self.Points.insert(3, wx.Point(self.Points[2].x, self.Points[2].y)) - self.Segments.insert(3, DirectionChoice((self.Segments[2][1], - self.Segments[2][0]), v_end, self.EndPoint[1])) + self.Segments.insert( + 3, + DirectionChoice((self.Segments[2][1], + self.Segments[2][0]), + v_end, + self.EndPoint[1])) else: # Current point is aligned with end point if self.Segments[i - 1][0] != 0: @@ -2140,16 +2176,22 @@ self.Points[i].y = (end.y + self.Points[i - 1].y) / 2 # A point is added self.Points.insert(i + 1, wx.Point(self.Points[i].x, self.Points[i].y)) - self.Segments.insert(i + 1, DirectionChoice((self.Segments[i][1], - self.Segments[i][0]), v_end, self.EndPoint[1])) + self.Segments.insert( + i + 1, + DirectionChoice((self.Segments[i][1], + self.Segments[i][0]), v_end, self.EndPoint[1])) else: # Current point is the first, and second is not mainly in the first direction if i == 0 and product(vector(start, self.Points[1]), self.Segments[0]) < 0: - # If first and second directions aren't perpendiculars, a point is added + # If first and second directions aren't perpendiculars, a point is added if product(self.Segments[0], self.Segments[1]) != 0: self.Points.insert(1, wx.Point(start.x, start.y)) - self.Segments.insert(1, DirectionChoice((self.Segments[0][1], - self.Segments[0][0]), vector(start, self.Points[1]), self.Segments[1])) + self.Segments.insert( + 1, + DirectionChoice((self.Segments[0][1], + self.Segments[0][0]), + vector(start, self.Points[1]), + self.Segments[1])) else: self.Points[1].x, self.Points[1].y = start.x, start.y else: @@ -2165,7 +2207,7 @@ self.RefreshBoundingBox() if realpoints: self.RefreshRealPoints() - + # Verify that two consecutive points haven't the same position def VerifyPoints(self): points = [point for point in self.Points] @@ -2185,9 +2227,9 @@ self.RefreshBoundingBox() self.RefreshRealPoints() return points - + # Moves all the wire points except the first and the last if they are connected - def Move(self, dx, dy, endpoints = False): + def Move(self, dx, dy, endpoints=False): for i, point in enumerate(self.Points): if endpoints or not (i == 0 and self.StartConnected) and not (i == len(self.Points) - 1 and self.EndConnected): point.x += dx @@ -2195,7 +2237,7 @@ self.StartPoint[0] = self.Points[0] self.EndPoint[0] = self.Points[-1] self.GeneratePoints() - + # Resize the wire from position and size given def Resize(self, x, y, width, height): if len(self.Points) > 1: @@ -2212,9 +2254,9 @@ else: dir = (0, 0) pointx = max(-dir[0] * MIN_SEGMENT_SIZE, min(int(round(point[0] * width / float(max(lastwidth, 1)))), - width - dir[0] * MIN_SEGMENT_SIZE)) + width - dir[0] * MIN_SEGMENT_SIZE)) pointy = max(-dir[1] * MIN_SEGMENT_SIZE, min(int(round(point[1] * height / float(max(lastheight, 1)))), - height - dir[1] * MIN_SEGMENT_SIZE)) + height - dir[1] * MIN_SEGMENT_SIZE)) self.Points[i] = wx.Point(minx + x + pointx, miny + y + pointy) self.StartPoint[0] = self.Points[0] self.EndPoint[0] = self.Points[-1] @@ -2246,33 +2288,35 @@ dir = self.EndPoint[1] else: dir = (0, 0) - realpointx = max(-dir[0] * MIN_SEGMENT_SIZE, min(int(round(point[0])), - width - dir[0] * MIN_SEGMENT_SIZE)) - realpointy = max(-dir[1] * MIN_SEGMENT_SIZE, min(int(round(point[1])), - height - dir[1] * MIN_SEGMENT_SIZE)) + realpointx = max(-dir[0] * MIN_SEGMENT_SIZE, + min(int(round(point[0])), + width - dir[0] * MIN_SEGMENT_SIZE)) + realpointy = max(-dir[1] * MIN_SEGMENT_SIZE, + min(int(round(point[1])), + height - dir[1] * MIN_SEGMENT_SIZE)) self.Points[i] = wx.Point(minx + x + realpointx, miny + y + realpointy) self.StartPoint[0] = self.Points[0] self.EndPoint[0] = self.Points[-1] self.GeneratePoints(False) - + # Moves the wire start point and update the wire points def MoveStartPoint(self, point): if len(self.Points) > 1: self.StartPoint[0] = point self.Points[0] = point self.GeneratePoints() - + # Changes the wire start direction and update the wire points def SetStartPointDirection(self, dir): if len(self.Points) > 1: self.StartPoint[1] = dir self.Segments[0] = dir self.GeneratePoints() - + # Rotates the wire start direction by an angle of 90 degrees anticlockwise def RotateStartPoint(self): self.SetStartPointDirection((self.StartPoint[1][1], -self.StartPoint[1][0])) - + # Connects wire start point to the connector given and moves wire start point # to given point def ConnectStartPoint(self, point, connector): @@ -2280,17 +2324,17 @@ self.MoveStartPoint(point) self.StartConnected = connector self.RefreshBoundingBox() - + # Unconnects wire start point - def UnConnectStartPoint(self, delete = False): + def UnConnectStartPoint(self, delete=False): if delete: self.StartConnected = None self.Delete() elif self.StartConnected: - self.StartConnected.UnConnect(self, unconnect = False) + self.StartConnected.UnConnect(self, unconnect=False) self.StartConnected = None self.RefreshBoundingBox() - + # Moves the wire end point and update the wire points def MoveEndPoint(self, point): if len(self.Points) > 1: @@ -2303,7 +2347,7 @@ if len(self.Points) > 1: self.EndPoint[1] = dir self.GeneratePoints() - + # Rotates the wire end direction by an angle of 90 degrees anticlockwise def RotateEndPoint(self): self.SetEndPointDirection((self.EndPoint[1][1], -self.EndPoint[1][0])) @@ -2315,17 +2359,17 @@ self.MoveEndPoint(point) self.EndConnected = connector self.RefreshBoundingBox() - + # Unconnects wire end point - def UnConnectEndPoint(self, delete = False): + def UnConnectEndPoint(self, delete=False): if delete: self.EndConnected = None self.Delete() elif self.EndConnected: - self.EndConnected.UnConnect(self, unconnect = False) + self.EndConnected.UnConnect(self, unconnect=False) self.EndConnected = None self.RefreshBoundingBox() - + # Moves the wire segment given by its index def MoveSegment(self, idx, movex, movey, scaling): if 0 < idx < len(self.Segments) - 1: @@ -2356,7 +2400,7 @@ if start_y != self.Points[idx].y: return 0, self.Points[idx].y - start_y return 0, 0 - + # Adds two points in the middle of the handled segment def AddSegment(self): handle_type, handle = self.Handle @@ -2391,7 +2435,7 @@ self.Points.insert(segment + 4, wx.Point(p2x, p2y)) self.Segments.insert(segment + 4, dir) self.GeneratePoints() - + # Delete the handled segment by removing the two segment points def DeleteSegment(self): handle_type, handle = self.Handle @@ -2402,19 +2446,19 @@ self.Segments.pop(segment) self.GeneratePoints() self.RefreshModel() - + # Method called when a LeftDown event have been generated def OnLeftDown(self, event, dc, scaling): pos = GetScaledEventPosition(event, dc, scaling) # Test if a point have been handled - #result = self.TestPoint(pos) - #if result != None: + # result = self.TestPoint(pos) + # if result != None: # self.Handle = (HANDLE_POINT, result) # wx.CallAfter(self.Parent.SetCurrentCursor, 1) - #else: + # else: # Test if a segment have been handled result = self.TestSegment(pos) - if result != None: + if result is not None: if result[1] in (NORTH, SOUTH): wx.CallAfter(self.Parent.SetCurrentCursor, 4) elif result[1] in (EAST, WEST): @@ -2424,20 +2468,20 @@ else: Graphic_Element.OnLeftDown(self, event, dc, scaling) self.oldPos = pos - + # Method called when a RightUp event has been generated def OnRightUp(self, event, dc, scaling): pos = GetScaledEventPosition(event, dc, scaling) # Test if a segment has been handled result = self.TestSegment(pos, True) - if result != None: + if result is not None: self.Handle = (HANDLE_SEGMENT, result) # Popup the menu with special items for a wire self.Parent.PopupWireMenu(0 < result[0] < len(self.Segments) - 1) else: # Execute the default method for a graphic element Graphic_Element.OnRightUp(self, event, dc, scaling) - + # Method called when a LeftDClick event has been generated def OnLeftDClick(self, event, dc, scaling): rect = self.GetRedrawRect() @@ -2492,7 +2536,7 @@ self.Parent.RefreshBuffer() rect.Union(self.GetRedrawRect()) self.Parent.RefreshRect(self.Parent.GetScrolledRect(rect), False) - + # Method called when a Motion event has been generated def OnMotion(self, event, dc, scaling): pos = GetScaledEventPosition(event, dc, scaling) @@ -2511,7 +2555,7 @@ else: # Execute the default method for a graphic element return Graphic_Element.OnMotion(self, event, dc, scaling) - + # Refreshes the wire state according to move defined and handle selected def ProcessDragging(self, movex, movey, event, scaling): handle_type, handle = self.Handle @@ -2572,14 +2616,14 @@ # Execute the default method for a graphic element else: return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling) - + # Refreshes the wire model def RefreshModel(self, move=True): if self.StartConnected and self.StartPoint[1] in [WEST, NORTH]: self.StartConnected.RefreshParentBlock() if self.EndConnected and self.EndPoint[1] in [WEST, NORTH]: self.EndConnected.RefreshParentBlock() - + # Change the variable that indicates if this element is highlighted def SetHighlighted(self, highlighted): self.Highlighted = highlighted @@ -2587,7 +2631,7 @@ self.OverStart = False self.OverEnd = False self.Refresh() - + def HighlightPoint(self, pos): refresh = False start, end = self.OverStart, self.OverEnd @@ -2595,20 +2639,20 @@ self.OverEnd = False # Test if a point has been handled result = self.TestPoint(pos) - if result != None: + if result is not None: if result == 0 and self.StartConnected is not None: self.OverStart = True elif result != 0 and self.EndConnected is not None: self.OverEnd = True if start != self.OverStart or end != self.OverEnd: self.Refresh() - + # Draws the highlightment of this element if it is highlighted def DrawHighlightment(self, dc): scalex, scaley = dc.GetUserScale() dc.SetUserScale(1, 1) # If user trying to connect wire with wrong input, highlight will become red. - if self.ErrHighlight == True and not (self.EndConnected): + if self.ErrHighlight and not (self.EndConnected): highlightcolor = wx.RED else: highlightcolor = HIGHLIGHTCOLOR @@ -2617,31 +2661,31 @@ dc.SetLogicalFunction(wx.AND) # Draw the start and end points if they are not connected or the mouse is over them if len(self.Points) > 0 and (not self.StartConnected or self.OverStart): - dc.DrawCircle(round(self.Points[0].x * scalex), - round(self.Points[0].y * scaley), + dc.DrawCircle(round(self.Points[0].x * scalex), + round(self.Points[0].y * scaley), (POINT_RADIUS + 1) * scalex + 2) if len(self.Points) > 1 and (not self.EndConnected or self.OverEnd): dc.DrawCircle(self.Points[-1].x * scalex, self.Points[-1].y * scaley, (POINT_RADIUS + 1) * scalex + 2) # Draw the wire lines and the last point (it seems that DrawLines stop before the last point) if len(self.Points) > 1: - points = [wx.Point(round((self.Points[0].x - self.Segments[0][0]) * scalex), + points = [wx.Point(round((self.Points[0].x - self.Segments[0][0]) * scalex), round((self.Points[0].y - self.Segments[0][1]) * scaley))] points.extend([wx.Point(round(point.x * scalex), round(point.y * scaley)) for point in self.Points[1:-1]]) - points.append(wx.Point(round((self.Points[-1].x + self.Segments[-1][0]) * scalex), + points.append(wx.Point(round((self.Points[-1].x + self.Segments[-1][0]) * scalex), round((self.Points[-1].y + self.Segments[-1][1]) * scaley))) else: points = [] dc.DrawLines(points) dc.SetLogicalFunction(wx.COPY) dc.SetUserScale(scalex, scaley) - + if self.StartConnected is not None: self.StartConnected.DrawHighlightment(dc) self.StartConnected.Draw(dc) if self.EndConnected is not None: self.EndConnected.DrawHighlightment(dc) self.EndConnected.Draw(dc) - + # Draws the wire lines and points def Draw(self, dc): Graphic_Element.Draw(self, dc) @@ -2712,9 +2756,10 @@ dc.SetTextForeground(wx.BLACK) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Graphic comment element -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + def FilterHighlightsByRow(highlights, row, length): _highlights = [] @@ -2727,6 +2772,7 @@ _highlights.append((start, end, highlight_type)) return _highlights + def FilterHighlightsByColumn(highlights, start_col, end_col): _highlights = [] for start, end, highlight_type in highlights: @@ -2736,41 +2782,41 @@ _highlights.append((start, end, highlight_type)) return _highlights -""" -Class that implements a comment -""" class Comment(Graphic_Element): + """ + Class that implements a comment + """ # Create a new comment - def __init__(self, parent, content, id = None): + def __init__(self, parent, content, id=None): Graphic_Element.__init__(self, parent) self.Id = id self.Content = content self.Pos = wx.Point(0, 0) self.Size = wx.Size(0, 0) self.Highlights = [] - + # Make a clone of this comment - def Clone(self, parent, id = None, pos = None): + def Clone(self, parent, id=None, pos=None): comment = Comment(parent, self.Content, id) if pos is not None: comment.SetPosition(pos.x, pos.y) comment.SetSize(self.Size[0], self.Size[1]) return comment - + # Method for keeping compatibility with others def Clean(self): pass - + # Delete this comment by calling the corresponding method def Delete(self): self.Parent.DeleteComment(self) - + # Refresh the comment bounding box def RefreshBoundingBox(self): self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1) - + # Changes the comment size def SetSize(self, width, height): self.Size.SetWidth(width) @@ -2780,7 +2826,7 @@ # Returns the comment size def GetSize(self): return self.Size.GetWidth(), self.Size.GetHeight() - + # Returns the comment minimum size def GetMinSize(self): dc = wx.ClientDC(self.Parent) @@ -2793,7 +2839,7 @@ min_width = max(min_width, wordwidth) min_height = max(min_height, wordheight) return min_width + 20, min_height + 20 - + # Changes the comment position def SetPosition(self, x, y): self.Pos.x = x @@ -2815,51 +2861,51 @@ # Returns the comment position def GetPosition(self): return self.Pos.x, self.Pos.y - + # Moves the comment - def Move(self, dx, dy, connected = True): + def Move(self, dx, dy, connected=True): self.Pos.x += dx self.Pos.y += dy self.RefreshBoundingBox() - + # Resizes the comment with the position and the size given def Resize(self, x, y, width, height): self.Move(x, y) self.SetSize(width, height) - + # Method called when a RightUp event have been generated def OnRightUp(self, event, dc, scaling): # Popup the default menu self.Parent.PopupDefaultMenu() - + # Refreshes the wire state according to move defined and handle selected def ProcessDragging(self, movex, movey, event, scaling): if self.Parent.GetDrawingMode() != FREEDRAWING_MODE and self.Parent.CurrentLanguage == "LD": movex = movey = 0 return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling) - + # Refreshes the comment model def RefreshModel(self, move=True): self.Parent.RefreshCommentModel(self) - + # Method called when a LeftDClick event have been generated def OnLeftDClick(self, event, dc, scaling): # Edit the comment content self.Parent.EditCommentContent(self) - + # Adds an highlight to the comment def AddHighlight(self, infos, start, end, highlight_type): if infos[0] == "content": AddHighlight(self.Highlights, (start, end, highlight_type)) - + # Removes an highlight from the comment def RemoveHighlight(self, infos, start, end, highlight_type): RemoveHighlight(self.Highlights, (start, end, highlight_type)) - + # Removes all the highlights of one particular type from the comment def ClearHighlight(self, highlight_type=None): self.Highlights = ClearHighlights(self.Highlights, highlight_type) - + # Draws the highlightment of this element if it is highlighted def DrawHighlightment(self, dc): scalex, scaley = dc.GetUserScale() @@ -2867,29 +2913,29 @@ dc.SetPen(MiterPen(HIGHLIGHTCOLOR)) dc.SetBrush(wx.Brush(HIGHLIGHTCOLOR)) dc.SetLogicalFunction(wx.AND) - + left = (self.Pos.x - 1) * scalex - 2 right = (self.Pos.x + self.Size[0] + 1) * scalex + 2 top = (self.Pos.y - 1) * scaley - 2 bottom = (self.Pos.y + self.Size[1] + 1) * scaley + 2 angle_top = (self.Pos.x + self.Size[0] - 9) * scalex + 2 angle_right = (self.Pos.y + 9) * scaley - 2 - + polygon = [wx.Point(left, top), wx.Point(angle_top, top), wx.Point(right, angle_right), wx.Point(right, bottom), wx.Point(left, bottom)] dc.DrawPolygon(polygon) - + dc.SetLogicalFunction(wx.COPY) dc.SetUserScale(scalex, scaley) - + # Draws the comment and its content def Draw(self, dc): Graphic_Element.Draw(self, dc) dc.SetPen(MiterPen(wx.BLACK)) dc.SetBrush(wx.WHITE_BRUSH) # Draws the comment shape - polygon = [wx.Point(self.Pos.x, self.Pos.y), + polygon = [wx.Point(self.Pos.x, self.Pos.y), wx.Point(self.Pos.x + self.Size[0] - 10, self.Pos.y), wx.Point(self.Pos.x + self.Size[0], self.Pos.y + 10), wx.Point(self.Pos.x + self.Size[0], self.Pos.y + self.Size[1]), @@ -2951,4 +2997,3 @@ y += wordheight + 5 if y + wordheight > self.Pos.y + self.Size[1] - 10: break - diff -r 31e63e25b4cc -r 64beb9e9c749 graphics/LD_Objects.py --- a/graphics/LD_Objects.py Mon Aug 21 20:17:19 2017 +0000 +++ b/graphics/LD_Objects.py Mon Aug 21 23:22:58 2017 +0300 @@ -28,16 +28,17 @@ from graphics.DebugDataConsumer import DebugDataConsumer from plcopen.structures import * -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Ladder Diagram PowerRail -#------------------------------------------------------------------------------- - -""" -Class that implements the graphic representation of a power rail -""" +# ------------------------------------------------------------------------------- + class LD_PowerRail(Graphic_Element): - + """ + Class that implements the graphic representation of a power rail + """ + # Create a new power rail def __init__(self, parent, type, id=None, connectors=1): Graphic_Element.__init__(self, parent) @@ -47,14 +48,14 @@ self.Id = id self.Extensions = [LD_LINE_SIZE / 2, LD_LINE_SIZE / 2] self.SetType(type, connectors) - + def Flush(self): for connector in self.Connectors: connector.Flush() self.Connectors = [] - + # Make a clone of this LD_PowerRail - def Clone(self, parent, id = None, pos = None): + def Clone(self, parent, id=None, pos=None): powerrail = LD_PowerRail(parent, self.Type, id) powerrail.SetSize(self.Size[0], self.Size[1]) if pos is not None: @@ -65,13 +66,13 @@ for connector in self.Connectors: powerrail.Connectors.append(connector.Clone(powerrail)) return powerrail - + def GetConnectorTranslation(self, element): return dict(zip([connector for connector in self.Connectors], [connector for connector in element.Connectors])) - + # Returns the RedrawRect - def GetRedrawRect(self, movex = 0, movey = 0): + def GetRedrawRect(self, movex=0, movey=0): rect = Graphic_Element.GetRedrawRect(self, movex, movey) for connector in self.Connectors: rect = rect.Union(connector.GetRedrawRect(movex, movey)) @@ -80,7 +81,7 @@ if connector.IsConnected(): rect = rect.Union(connector.GetConnectedRedrawRect(movex, movey)) return rect - + # Forbids to change the power rail size def SetSize(self, width, height): if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: @@ -88,47 +89,47 @@ else: Graphic_Element.SetSize(self, LD_POWERRAIL_WIDTH, height) self.RefreshConnectors() - + # Forbids to select a power rail def HitTest(self, pt, connectors=True): if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: - return Graphic_Element.HitTest(self, pt, connectors) or self.TestConnector(pt, exclude=False) != None + return Graphic_Element.HitTest(self, pt, connectors) or self.TestConnector(pt, exclude=False) is not None return False - + # Forbids to select a power rail def IsInSelection(self, rect): if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: return Graphic_Element.IsInSelection(self, rect) return False - + # Deletes this power rail by calling the appropriate method def Delete(self): self.Parent.DeletePowerRail(self) - + # Unconnect all connectors def Clean(self): for connector in self.Connectors: - connector.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) - + connector.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + # Refresh the power rail bounding box def RefreshBoundingBox(self): self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1) - + # Refresh the power rail size def RefreshSize(self): self.Size = wx.Size(LD_POWERRAIL_WIDTH, max(LD_LINE_SIZE * len(self.Connectors), self.Size[1])) self.RefreshBoundingBox() - + # Returns the block minimum size def GetMinSize(self, default=False): height = (LD_LINE_SIZE * (len(self.Connectors) - 1) if default else 0) return LD_POWERRAIL_WIDTH, height + self.Extensions[0] + self.Extensions[1] - + # Add a connector or a blank to this power rail at the last place def AddConnector(self): self.InsertConnector(len(self.Connectors)) - + # Add a connector or a blank to this power rail at the place given def InsertConnector(self, idx): if self.Type == LEFTRAIL: @@ -138,7 +139,7 @@ self.Connectors.insert(idx, connector) self.RefreshSize() self.RefreshConnectors() - + # Moves the divergence connector given def MoveConnector(self, connector, movey): position = connector.GetRelPosition() @@ -163,19 +164,19 @@ self.Size[1] = max(maxy + self.Extensions[1], self.Size[1]) connector.MoveConnected() self.RefreshBoundingBox() - + # Returns the index in connectors list for the connector given def GetConnectorIndex(self, connector): if connector in self.Connectors: return self.Connectors.index(connector) return None - + # Delete the connector or blank from connectors list at the index given def DeleteConnector(self, idx): self.Connectors.pop(idx) self.RefreshConnectors() self.RefreshSize() - + # Refresh the positions of the power rail connectors def RefreshConnectors(self): scaling = self.Parent.GetScaling() @@ -193,14 +194,14 @@ elif self.Type == RIGHTRAIL: connector.SetPosition(wx.Point(0, position)) self.RefreshConnected() - + # Refresh the position of wires connected to power rail - def RefreshConnected(self, exclude = []): + def RefreshConnected(self, exclude=[]): for connector in self.Connectors: connector.MoveConnected(exclude) - - # Returns the power rail connector that starts with the point given if it exists - def GetConnector(self, position, name = None): + + # Returns the power rail connector that starts with the point given if it exists + def GetConnector(self, position, name=None): # if a name is given if name is not None: # Test each connector if it exists @@ -208,22 +209,22 @@ if name == connector.GetName(): return connector return self.FindNearestConnector(position, [connector for connector in self.Connectors if connector is not None]) - - # Returns all the power rail connectors + + # Returns all the power rail connectors def GetConnectors(self): connectors = [connector for connector in self.Connectors if connector] if self.Type == LEFTRAIL: return {"inputs": [], "outputs": connectors} else: return {"inputs": connectors, "outputs": []} - + # Test if point given is on one of the power rail connectors - def TestConnector(self, pt, direction = None, exclude = True): + def TestConnector(self, pt, direction=None, exclude=True): for connector in self.Connectors: if connector.TestPoint(pt, direction, exclude): return connector return None - + # Returns the power rail type def SetType(self, type, connectors): if type != self.Type or len(self.Connectors) != connectors: @@ -235,11 +236,11 @@ for connector in xrange(connectors): self.AddConnector() self.RefreshSize() - + # Returns the power rail type def GetType(self): return self.Type - + # Method called when a LeftDown event have been generated def OnLeftDown(self, event, dc, scaling): self.RealConnectors = [] @@ -249,16 +250,16 @@ position = connector.GetRelPosition() self.RealConnectors.append(max(0., min(float(position.y - self.Extensions[0]) / float(height), 1.))) elif len(self.Connectors) > 1: - self.RealConnectors = map(lambda x : x * 1 / (len(self.Connectors) - 1), xrange(len(self.Connectors))) + self.RealConnectors = map(lambda x: x * 1 / (len(self.Connectors) - 1), xrange(len(self.Connectors))) else: self.RealConnectors = [0.5] Graphic_Element.OnLeftDown(self, event, dc, scaling) - + # Method called when a LeftUp event have been generated def OnLeftUp(self, event, dc, scaling): Graphic_Element.OnLeftUp(self, event, dc, scaling) self.RealConnectors = None - + # Method called when a LeftDown event have been generated def OnRightDown(self, event, dc, scaling): pos = GetScaledEventPosition(event, dc, scaling) @@ -272,12 +273,12 @@ self.oldPos = GetScaledEventPosition(event, dc, scaling) else: Graphic_Element.OnRightDown(self, event, dc, scaling) - + # Method called when a LeftDClick event have been generated def OnLeftDClick(self, event, dc, scaling): # Edit the powerrail properties self.Parent.EditPowerRailContent(self) - + # Method called when a RightUp event have been generated def OnRightUp(self, event, dc, scaling): handle_type, handle = self.Handle @@ -292,7 +293,7 @@ Graphic_Element.OnRightUp(self, event, dc, scaling) else: self.Parent.PopupDefaultMenu() - + def Resize(self, x, y, width, height): self.Move(x, y) if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: @@ -314,16 +315,16 @@ elif self.Parent.GetDrawingMode() == FREEDRAWING_MODE: return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling) return 0, 0 - + # Refreshes the power rail model def RefreshModel(self, move=True): self.Parent.RefreshPowerRailModel(self) - # If power rail has moved and power rail is of type LEFT, refresh the model + # If power rail has moved and power rail is of type LEFT, refresh the model # of wires connected to connectors if move and self.Type == LEFTRAIL: for connector in self.Connectors: connector.RefreshWires() - + # Draws power rail def Draw(self, dc): Graphic_Element.Draw(self, dc) @@ -337,20 +338,20 @@ # Draw connectors for connector in self.Connectors: connector.Draw(dc) - - -#------------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------------- # Ladder Diagram Contact -#------------------------------------------------------------------------------- - -""" -Class that implements the graphic representation of a contact -""" +# ------------------------------------------------------------------------------- + class LD_Contact(Graphic_Element, DebugDataConsumer): - + """ + Class that implements the graphic representation of a contact + """ + # Create a new contact - def __init__(self, parent, type, name, id = None): + def __init__(self, parent, type, name, id=None): Graphic_Element.__init__(self, parent) DebugDataConsumer.__init__(self) self.Type = type @@ -365,7 +366,7 @@ self.PreviousSpreading = False self.RefreshNameSize() self.RefreshTypeSize() - + def Flush(self): if self.Input is not None: self.Input.Flush() @@ -373,13 +374,13 @@ if self.Output is not None: self.Output.Flush() self.Output = None - + def SetForced(self, forced): if self.Forced != forced: self.Forced = forced if self.Visible: self.Parent.ElementNeedRefresh(self) - + def SetValue(self, value): if self.Type == CONTACT_RISING: refresh = self.Value and not self.PreviousValue @@ -393,7 +394,7 @@ if self.Visible: self.Parent.ElementNeedRefresh(self) self.SpreadCurrent() - + def SpreadCurrent(self): if self.Parent.Debug: if self.Value is None: @@ -414,9 +415,9 @@ elif not spreading and self.PreviousSpreading: self.Output.SpreadCurrent(False) self.PreviousSpreading = spreading - + # Make a clone of this LD_Contact - def Clone(self, parent, id = None, pos = None): + def Clone(self, parent, id=None, pos=None): contact = LD_Contact(parent, self.Type, self.Name, id) contact.SetSize(self.Size[0], self.Size[1]) if pos is not None: @@ -426,12 +427,12 @@ contact.Input = self.Input.Clone(contact) contact.Output = self.Output.Clone(contact) return contact - + def GetConnectorTranslation(self, element): - return {self.Input : element.Input, self.Output : element.Output} - + return {self.Input: element.Input, self.Output: element.Output} + # Returns the RedrawRect - def GetRedrawRect(self, movex = 0, movey = 0): + def GetRedrawRect(self, movex=0, movey=0): rect = Graphic_Element.GetRedrawRect(self, movex, movey) rect = rect.Union(self.Input.GetRedrawRect(movex, movey)) rect = rect.Union(self.Output.GetRedrawRect(movex, movey)) @@ -445,30 +446,30 @@ def ProcessDragging(self, movex, movey, event, scaling): if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: movex = movey = 0 - return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling, height_fac = 2) - + return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling, height_fac=2) + # Forbids to change the contact size def SetSize(self, width, height): if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: Graphic_Element.SetSize(self, width, height) self.RefreshConnectors() - + # Delete this contact by calling the appropriate method def Delete(self): self.Parent.DeleteContact(self) - + # Unconnect input and output def Clean(self): - self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) - self.Output.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) - + self.Input.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + self.Output.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + # Refresh the size of text for name def RefreshNameSize(self): if self.Name != "": self.NameSize = self.Parent.GetTextExtent(self.Name) else: self.NameSize = 0, 0 - + # Refresh the size of text for type def RefreshTypeSize(self): typetext = "" @@ -482,7 +483,7 @@ self.TypeSize = self.Parent.GetTextExtent(typetext) else: self.TypeSize = 0, 0 - + # Refresh the contact bounding box def RefreshBoundingBox(self): # Calculate the size of the name outside the contact @@ -499,33 +500,33 @@ bbx_y = self.Pos.y bbx_height = self.Size[1] self.BoundingBox = wx.Rect(bbx_x, bbx_y, bbx_width + 1, bbx_height + 1) - + # Returns the block minimum size def GetMinSize(self): return LD_ELEMENT_SIZE - + # Refresh the position of wire connected to contact - def RefreshConnected(self, exclude = []): + def RefreshConnected(self, exclude=[]): self.Input.MoveConnected(exclude) self.Output.MoveConnected(exclude) - - # Returns the contact connector that starts with the point given if it exists - def GetConnector(self, position, name = None): + + # Returns the contact connector that starts with the point given if it exists + def GetConnector(self, position, name=None): # if a name is given if name is not None: # Test input and output connector - #if name == self.Input.GetName(): + # if name == self.Input.GetName(): # return self.Input if name == self.Output.GetName(): return self.Output return self.FindNearestConnector(position, [self.Input, self.Output]) - - # Returns input and output contact connectors + + # Returns input and output contact connectors def GetConnectors(self): return {"inputs": [self.Input], "outputs": [self.Output]} - + # Test if point given is on contact input or output connector - def TestConnector(self, pt, direction = None, exclude=True): + def TestConnector(self, pt, direction=None, exclude=True): # Test input connector if self.Input.TestPoint(pt, direction, exclude): return self.Input @@ -561,24 +562,24 @@ # Returns the contact type def GetType(self): return self.Type - + # Method called when a LeftDClick event have been generated def OnLeftDClick(self, event, dc, scaling): # Edit the contact properties self.Parent.EditContactContent(self) - + # Method called when a RightUp event have been generated def OnRightUp(self, event, dc, scaling): # Popup the default menu self.Parent.PopupDefaultMenu() - + # Refreshes the contact model def RefreshModel(self, move=True): self.Parent.RefreshContactModel(self) # If contact has moved, refresh the model of wires connected to output if move: self.Output.RefreshWires() - + # Draws the highlightment of this element if it is highlighted def DrawHighlightment(self, dc): scalex, scaley = dc.GetUserScale() @@ -592,12 +593,12 @@ top = (self.Pos.y - 1) * scaley - 2 width = 4 * scalex + 5 height = (self.Size[1] + 3) * scaley + 5 - + dc.DrawRectangle(left_left, top, width, height) dc.DrawRectangle(right_left, top, width, height) dc.SetLogicalFunction(wx.COPY) dc.SetUserScale(scalex, scaley) - + # Adds an highlight to the connection def AddHighlight(self, infos, start, end, highlight_type): highlights = self.Highlights.setdefault(infos[0], []) @@ -606,13 +607,13 @@ AddHighlight(highlights, (start, end, highlight_type)) else: AddHighlight(highlights, ((0, 0), (0, 1), highlight_type)) - + # Removes an highlight from the connection def RemoveHighlight(self, infos, start, end, highlight_type): highlights = self.Highlights.get(infos[0], []) if RemoveHighlight(highlights, (start, end, highlight_type)) and len(highlights) == 0: self.Highlights.pop(infos[0]) - + # Removes all the highlights of one particular type from the connection def ClearHighlight(self, highlight_type=None): if highlight_type is None: @@ -623,11 +624,11 @@ highlights = ClearHighlights(highlight, highlight_type) if len(highlights) == 0: self.Highlights.pop(name) - + # Draws contact def Draw(self, dc): Graphic_Element.Draw(self, dc) - if self.Value is not None: + if self.Value is not None: if self.Type == CONTACT_NORMAL and self.Value or \ self.Type == CONTACT_REVERSE and not self.Value or \ self.Type == CONTACT_RISING and self.Value and not self.PreviousValue or \ @@ -643,7 +644,7 @@ else: dc.SetPen(MiterPen(wx.BLACK)) dc.SetBrush(wx.BLACK_BRUSH) - + # Compiling contact type modifier symbol typetext = "" if self.Type == CONTACT_REVERSE: @@ -652,7 +653,7 @@ typetext = "P" elif self.Type == CONTACT_FALLING: typetext = "N" - + if getattr(dc, "printing", False): name_size = dc.GetTextExtent(self.Name) if typetext != "": @@ -661,7 +662,7 @@ name_size = self.NameSize if typetext != "": type_size = self.TypeSize - + # Draw two rectangles for representing the contact dc.DrawRectangle(self.Pos.x, self.Pos.y, 2, self.Size[1] + 1) dc.DrawRectangle(self.Pos.x + self.Size[0] - 1, self.Pos.y, 2, self.Size[1] + 1) @@ -677,7 +678,7 @@ # Draw input and output connectors self.Input.Draw(dc) self.Output.Draw(dc) - + if not getattr(dc, "printing", False): for name, highlights in self.Highlights.iteritems(): if name == "reference": @@ -685,18 +686,19 @@ elif typetext != "": DrawHighlightedText(dc, typetext, highlights, type_pos[0], type_pos[1]) -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Ladder Diagram Coil -#------------------------------------------------------------------------------- - -""" -Class that implements the graphic representation of a coil -""" +# ------------------------------------------------------------------------------- + class LD_Coil(Graphic_Element): - + """ + Class that implements the graphic representation of a coil + """ + # Create a new coil - def __init__(self, parent, type, name, id = None): + def __init__(self, parent, type, name, id=None): Graphic_Element.__init__(self, parent) self.Type = type self.Name = name @@ -710,7 +712,7 @@ self.PreviousValue = False self.RefreshNameSize() self.RefreshTypeSize() - + def Flush(self): if self.Input is not None: self.Input.Flush() @@ -718,7 +720,7 @@ if self.Output is not None: self.Output.Flush() self.Output = None - + def SpreadCurrent(self): if self.Parent.Debug: self.PreviousValue = self.Value @@ -729,9 +731,9 @@ self.Output.SpreadCurrent(False) if self.Value != self.PreviousValue and self.Visible: self.Parent.ElementNeedRefresh(self) - + # Make a clone of this LD_Coil - def Clone(self, parent, id = None, pos = None): + def Clone(self, parent, id=None, pos=None): coil = LD_Coil(parent, self.Type, self.Name, id) coil.SetSize(self.Size[0], self.Size[1]) if pos is not None: @@ -741,12 +743,12 @@ coil.Input = self.Input.Clone(coil) coil.Output = self.Output.Clone(coil) return coil - + def GetConnectorTranslation(self, element): - return {self.Input : element.Input, self.Output : element.Output} - + return {self.Input: element.Input, self.Output: element.Output} + # Returns the RedrawRect - def GetRedrawRect(self, movex = 0, movey = 0): + def GetRedrawRect(self, movex=0, movey=0): rect = Graphic_Element.GetRedrawRect(self, movex, movey) rect = rect.Union(self.Input.GetRedrawRect(movex, movey)) rect = rect.Union(self.Output.GetRedrawRect(movex, movey)) @@ -756,34 +758,34 @@ if self.Output.IsConnected(): rect = rect.Union(self.Output.GetConnectedRedrawRect(movex, movey)) return rect - + def ProcessDragging(self, movex, movey, event, scaling): if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: movex = movey = 0 - return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling, height_fac = 2) - + return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling, height_fac=2) + # Forbids to change the Coil size def SetSize(self, width, height): if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: Graphic_Element.SetSize(self, width, height) self.RefreshConnectors() - + # Delete this coil by calling the appropriate method def Delete(self): self.Parent.DeleteCoil(self) - + # Unconnect input and output def Clean(self): - self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) - self.Output.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) - + self.Input.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + self.Output.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + # Refresh the size of text for name def RefreshNameSize(self): if self.Name != "": self.NameSize = self.Parent.GetTextExtent(self.Name) else: self.NameSize = 0, 0 - + # Refresh the size of text for type def RefreshTypeSize(self): typetext = "" @@ -801,7 +803,7 @@ self.TypeSize = self.Parent.GetTextExtent(typetext) else: self.TypeSize = 0, 0 - + # Refresh the coil bounding box def RefreshBoundingBox(self): # Calculate the size of the name outside the coil @@ -818,33 +820,33 @@ bbx_y = self.Pos.y bbx_height = self.Size[1] self.BoundingBox = wx.Rect(bbx_x, bbx_y, bbx_width + 1, bbx_height + 1) - + # Returns the block minimum size def GetMinSize(self): return LD_ELEMENT_SIZE - + # Refresh the position of wire connected to coil - def RefreshConnected(self, exclude = []): + def RefreshConnected(self, exclude=[]): self.Input.MoveConnected(exclude) self.Output.MoveConnected(exclude) - - # Returns the coil connector that starts with the point given if it exists - def GetConnector(self, position, name = None): + + # Returns the coil connector that starts with the point given if it exists + def GetConnector(self, position, name=None): # if a name is given if name is not None: # Test input and output connector - #if self.Input and name == self.Input.GetName(): + # if self.Input and name == self.Input.GetName(): # return self.Input if self.Output and name == self.Output.GetName(): return self.Output return self.FindNearestConnector(position, [self.Input, self.Output]) - - # Returns input and output coil connectors + + # Returns input and output coil connectors def GetConnectors(self): return {"inputs": [self.Input], "outputs": [self.Output]} - + # Test if point given is on coil input or output connector - def TestConnector(self, pt, direction = None, exclude=True): + def TestConnector(self, pt, direction=None, exclude=True): # Test input connector if self.Input.TestPoint(pt, direction, exclude): return self.Input @@ -852,7 +854,7 @@ if self.Output.TestPoint(pt, direction, exclude): return self.Output return None - + # Refresh the positions of the block connectors def RefreshConnectors(self): scaling = self.Parent.GetScaling() @@ -862,7 +864,7 @@ self.Input.SetPosition(wx.Point(0, position)) self.Output.SetPosition(wx.Point(self.Size[0], position)) self.RefreshConnected() - + # Changes the coil name def SetName(self, name): self.Name = name @@ -871,33 +873,33 @@ # Returns the coil name def GetName(self): return self.Name - + # Changes the coil type def SetType(self, type): self.Type = type self.RefreshTypeSize() - + # Returns the coil type def GetType(self): return self.Type - + # Method called when a LeftDClick event have been generated def OnLeftDClick(self, event, dc, scaling): # Edit the coil properties self.Parent.EditCoilContent(self) - + # Method called when a RightUp event have been generated def OnRightUp(self, event, dc, scaling): # Popup the default menu self.Parent.PopupDefaultMenu() - + # Refreshes the coil model def RefreshModel(self, move=True): self.Parent.RefreshCoilModel(self) # If coil has moved, refresh the model of wires connected to output if move: self.Output.RefreshWires() - + # Draws the highlightment of this element if it is highlighted def DrawHighlightment(self, dc): scalex, scaley = dc.GetUserScale() @@ -906,19 +908,19 @@ dc.SetBrush(wx.TRANSPARENT_BRUSH) dc.SetLogicalFunction(wx.AND) # Draw a two circle arcs for representing the coil - dc.DrawEllipticArc(round(self.Pos.x * scalex), - round((self.Pos.y - int(self.Size[1] * (sqrt(2) - 1.) / 2.) + 1) * scaley), - round(self.Size[0] * scalex), + dc.DrawEllipticArc(round(self.Pos.x * scalex), + round((self.Pos.y - int(self.Size[1] * (sqrt(2) - 1.) / 2.) + 1) * scaley), + round(self.Size[0] * scalex), round((int(self.Size[1] * sqrt(2)) - 1) * scaley), 135, 225) - dc.DrawEllipticArc(round(self.Pos.x * scalex), - round((self.Pos.y - int(self.Size[1] * (sqrt(2) - 1.) / 2.) + 1) * scaley), - round(self.Size[0] * scalex), + dc.DrawEllipticArc(round(self.Pos.x * scalex), + round((self.Pos.y - int(self.Size[1] * (sqrt(2) - 1.) / 2.) + 1) * scaley), + round(self.Size[0] * scalex), round((int(self.Size[1] * sqrt(2)) - 1) * scaley), -45, 45) dc.SetLogicalFunction(wx.COPY) dc.SetUserScale(scalex, scaley) - + # Adds an highlight to the connection def AddHighlight(self, infos, start, end, highlight_type): highlights = self.Highlights.setdefault(infos[0], []) @@ -927,13 +929,13 @@ AddHighlight(highlights, (start, end, highlight_type)) else: AddHighlight(highlights, ((0, 0), (0, 1), highlight_type)) - + # Removes an highlight from the connection def RemoveHighlight(self, infos, start, end, highlight_type): highlights = self.Highlights.get(infos[0], []) if RemoveHighlight(highlights, (start, end, highlight_type)) and len(highlights) == 0: self.Highlights.pop(infos[0]) - + # Removes all the highlights of one particular type from the connection def ClearHighlight(self, highlight_type=None): if highlight_type is None: @@ -944,7 +946,7 @@ highlights = ClearHighlights(highlight, highlight_type) if len(highlights) == 0: self.Highlights.pop(name) - + # Draws coil def Draw(self, dc): Graphic_Element.Draw(self, dc) @@ -953,8 +955,8 @@ else: dc.SetPen(MiterPen(wx.BLACK, 2, wx.SOLID)) dc.SetBrush(wx.TRANSPARENT_BRUSH) - - # Compiling coil type modifier symbol + + # Compiling coil type modifier symbol typetext = "" if self.Type == COIL_REVERSE: typetext = "/" @@ -966,7 +968,7 @@ typetext = "P" elif self.Type == COIL_FALLING: typetext = "N" - + if getattr(dc, "printing", False) and not isinstance(dc, wx.PostScriptDC): # Draw an clipped ellipse for representing the coil clipping_box = dc.GetClippingBox() @@ -992,7 +994,7 @@ name_size = self.NameSize if typetext != "": type_size = self.TypeSize - + # Draw coil name name_pos = (self.Pos.x + (self.Size[0] - name_size[0]) / 2, self.Pos.y - (name_size[1] + 2)) @@ -1012,5 +1014,3 @@ DrawHighlightedText(dc, self.Name, highlights, name_pos[0], name_pos[1]) elif typetext != "": DrawHighlightedText(dc, typetext, highlights, type_pos[0], type_pos[1]) - - diff -r 31e63e25b4cc -r 64beb9e9c749 graphics/RubberBand.py --- a/graphics/RubberBand.py Mon Aug 21 20:17:19 2017 +0000 +++ b/graphics/RubberBand.py Mon Aug 21 23:22:58 2017 +0300 @@ -26,28 +26,29 @@ from graphics.GraphicCommons import GetScaledEventPosition -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Viewer RubberBand -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -""" -Class that implements a rubberband for graphic Viewers -""" class RubberBand: - + """ + Class that implements a rubberband for graphic Viewers + """ + def __init__(self, viewer): """ Constructor @param viewer: Viewer on which rubberband must be drawn """ self.Viewer = viewer - + # wx.Panel on which rubberband will be drawn self.DrawingSurface = viewer.Editor - + self.Reset() - + def Reset(self): """ Initialize internal attributes of rubberband @@ -55,14 +56,14 @@ self.StartPoint = None self.CurrentBBox = None self.LastBBox = None - + def IsShown(self): """ Indicate if rubberband is drawn on viewer @return: True if rubberband is drawn """ - return self.CurrentBBox != None - + return self.CurrentBBox is not None + def GetCurrentExtent(self): """ Return the rubberband bounding box @@ -73,7 +74,7 @@ if self.IsShown(): return self.CurrentBBox return self.LastBBox - + def OnLeftDown(self, event, dc, scaling): """ Called when left mouse is pressed on Viewer. Starts to edit a new @@ -85,16 +86,16 @@ # Save the point where mouse was pressed in Viewer unit, position may # be modified by scroll and zoom applied on viewer self.StartPoint = GetScaledEventPosition(event, dc, scaling) - + # Initialize rubberband bounding box self.CurrentBBox = wx.Rect(self.StartPoint.x, self.StartPoint.y, 0, 0) - + # Change viewer mouse cursor to reflect a rubberband bounding box is # edited self.DrawingSurface.SetCursor(wx.StockCursor(wx.CURSOR_CROSS)) - + self.Redraw() - + def OnMotion(self, event, dc, scaling): """ Called when mouse is dragging over Viewer. Update the current edited @@ -106,19 +107,19 @@ # Get mouse position in Viewer unit, position may be modified by scroll # and zoom applied on viewer pos = GetScaledEventPosition(event, dc, scaling) - + # Save the last bounding box drawn for erasing it later self.LastBBox = wx.Rect(0, 0, 0, 0) self.LastBBox.Union(self.CurrentBBox) - - # Calculate new position and size of the box + + # Calculate new position and size of the box self.CurrentBBox.x = min(pos.x, self.StartPoint.x) self.CurrentBBox.y = min(pos.y, self.StartPoint.y) self.CurrentBBox.width = abs(pos.x - self.StartPoint.x) + 1 self.CurrentBBox.height = abs(pos.y - self.StartPoint.y) + 1 - + self.Redraw() - + def OnLeftUp(self, event, dc, scaling): """ Called when mouse is release from Viewer. Erase the current edited @@ -129,16 +130,16 @@ """ # Change viewer mouse cursor to default self.DrawingSurface.SetCursor(wx.NullCursor) - + # Save the last edited bounding box self.LastBBox = self.CurrentBBox self.CurrentBBox = None - + self.Redraw() - + def DrawBoundingBoxes(self, bboxes, dc=None): """ - Draw a list of bounding box on Viewer in the order given using XOR + Draw a list of bounding box on Viewer in the order given using XOR logical function @param bboxes: List of bounding boxes to draw on viewer @param dc: Device Context of Viewer (default None) @@ -146,38 +147,38 @@ # Get viewer Device Context if not given if dc is None: dc = self.Viewer.GetLogicalDC() - + # Save current viewer scale factors before resetting them in order to # avoid rubberband pen to be scaled scalex, scaley = dc.GetUserScale() dc.SetUserScale(1, 1) - + # Set DC drawing style dc.SetPen(wx.Pen(wx.WHITE, style=wx.DOT)) dc.SetBrush(wx.TRANSPARENT_BRUSH) dc.SetLogicalFunction(wx.XOR) - + # Draw the bounding boxes using viewer scale factor for bbox in bboxes: if bbox is not None: dc.DrawRectangle( - bbox.x * scalex, bbox.y * scaley, + bbox.x * scalex, bbox.y * scaley, bbox.width * scalex, bbox.height * scaley) - + dc.SetLogicalFunction(wx.COPY) - + # Restore Viewer scale factor dc.SetUserScale(scalex, scaley) - - def Redraw(self, dc = None): + + def Redraw(self, dc=None): """ Redraw rubberband on Viewer @param dc: Device Context of Viewer (default None) """ # Erase last bbox and draw current bbox self.DrawBoundingBoxes([self.LastBBox, self.CurrentBBox], dc) - - def Erase(self, dc = None): + + def Erase(self, dc=None): """ Erase rubberband from Viewer @param dc: Device Context of Viewer (default None) @@ -185,7 +186,7 @@ # Erase last bbox self.DrawBoundingBoxes([self.LastBBox], dc) - def Draw(self, dc = None): + def Draw(self, dc=None): """ Draw rubberband on Viewer @param dc: Device Context of Viewer (default None) diff -r 31e63e25b4cc -r 64beb9e9c749 graphics/SFC_Objects.py --- a/graphics/SFC_Objects.py Mon Aug 21 20:17:19 2017 +0000 +++ b/graphics/SFC_Objects.py Mon Aug 21 23:22:58 2017 +0300 @@ -28,24 +28,25 @@ from graphics.DebugDataConsumer import DebugDataConsumer from plcopen.structures import * + def GetWireSize(block): if isinstance(block, SFC_Step): return SFC_WIRE_MIN_SIZE + block.GetActionExtraLineNumber() * SFC_ACTION_MIN_SIZE[1] else: return SFC_WIRE_MIN_SIZE -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Sequencial Function Chart Step -#------------------------------------------------------------------------------- - -""" -Class that implements the graphic representation of a step -""" +# ------------------------------------------------------------------------------- + class SFC_Step(Graphic_Element, DebugDataConsumer): - + """ + Class that implements the graphic representation of a step + """ + # Create a new step - def __init__(self, parent, name, initial = False, id = None): + def __init__(self, parent, name, initial=False, id=None): Graphic_Element.__init__(self, parent) DebugDataConsumer.__init__(self) self.SetName(name) @@ -62,7 +63,7 @@ self.Action = None self.PreviousValue = None self.PreviousSpreading = False - + def Flush(self): if self.Input is not None: self.Input.Flush() @@ -73,13 +74,13 @@ if self.Action is not None: self.Action.Flush() self.Action = None - + def SetForced(self, forced): if self.Forced != forced: self.Forced = forced if self.Visible: self.Parent.ElementNeedRefresh(self) - + def SetValue(self, value): self.PreviousValue = self.Value self.Value = value @@ -87,7 +88,7 @@ if self.Visible: self.Parent.ElementNeedRefresh(self) self.SpreadCurrent() - + def SpreadCurrent(self): if self.Parent.Debug: spreading = self.Value @@ -102,9 +103,9 @@ if self.Action is not None: self.Action.SpreadCurrent(False) self.PreviousSpreading = spreading - + # Make a clone of this SFC_Step - def Clone(self, parent, id = None, name = "Step", pos = None): + def Clone(self, parent, id=None, name="Step", pos=None): step = SFC_Step(parent, name, self.Initial, id) step.SetSize(self.Size[0], self.Size[1]) if pos is not None: @@ -118,7 +119,7 @@ if self.Action: step.Action = self.Action.Clone(step) return step - + def GetConnectorTranslation(self, element): connectors = {} if self.Input is not None: @@ -128,9 +129,9 @@ if self.Action is not None: connectors[self.Action] = element.Action return connectors - + # Returns the RedrawRect - def GetRedrawRect(self, movex = 0, movey = 0): + def GetRedrawRect(self, movex=0, movey=0): rect = Graphic_Element.GetRedrawRect(self, movex, movey) if self.Input: rect = rect.Union(self.Input.GetRedrawRect(movex, movey)) @@ -146,63 +147,63 @@ if self.Action and self.Action.IsConnected(): rect = rect.Union(self.Action.GetConnectedRedrawRect(movex, movey)) return rect - + # Delete this step by calling the appropriate method def Delete(self): self.Parent.DeleteStep(self) - + # Unconnect input and output def Clean(self): if self.Input: - self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + self.Input.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) if self.Output: - self.Output.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + self.Output.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) if self.Action: - self.Action.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) - + self.Action.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + # Refresh the size of text for name def RefreshNameSize(self): self.NameSize = self.Parent.GetTextExtent(self.Name) - + # Add output connector to step def AddInput(self): if not self.Input: self.Input = Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH) self.RefreshBoundingBox() - + # Remove output connector from step def RemoveInput(self): if self.Input: - self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + self.Input.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) self.Input = None self.RefreshBoundingBox() - + # Add output connector to step def AddOutput(self): if not self.Output: - self.Output = Connector(self, "", None, wx.Point(self.Size[0] / 2, self.Size[1]), SOUTH, onlyone = True) + self.Output = Connector(self, "", None, wx.Point(self.Size[0] / 2, self.Size[1]), SOUTH, onlyone=True) self.RefreshBoundingBox() - + # Remove output connector from step def RemoveOutput(self): if self.Output: - self.Output.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + self.Output.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) self.Output = None self.RefreshBoundingBox() - + # Add action connector to step def AddAction(self): if not self.Action: - self.Action = Connector(self, "", None, wx.Point(self.Size[0], self.Size[1] / 2), EAST, onlyone = True) + self.Action = Connector(self, "", None, wx.Point(self.Size[0], self.Size[1] / 2), EAST, onlyone=True) self.RefreshBoundingBox() - + # Remove action connector from step def RemoveAction(self): if self.Action: - self.Action.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + self.Action.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) self.Action = None self.RefreshBoundingBox() - + # Refresh the step bounding box def RefreshBoundingBox(self): # Calculate the bounding box size @@ -220,9 +221,9 @@ bbx_height = self.Size[1] + CONNECTOR_SIZE if self.Output: bbx_height += CONNECTOR_SIZE - #self.BoundingBox = wx.Rect(self.Pos.x, bbx_y, bbx_width + 1, bbx_height + 1) + # self.BoundingBox = wx.Rect(self.Pos.x, bbx_y, bbx_width + 1, bbx_height + 1) self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1) - + # Refresh the positions of the step connectors def RefreshConnectors(self): scaling = self.Parent.GetScaling() @@ -241,22 +242,22 @@ if self.Action: self.Action.SetPosition(wx.Point(self.Size[0], vertical_pos)) self.RefreshConnected() - + # Refresh the position of wires connected to step - def RefreshConnected(self, exclude = []): + def RefreshConnected(self, exclude=[]): if self.Input: self.Input.MoveConnected(exclude) if self.Output: self.Output.MoveConnected(exclude) if self.Action: self.Action.MoveConnected(exclude) - - # Returns the step connector that starts with the point given if it exists - def GetConnector(self, position, name = None): + + # Returns the step connector that starts with the point given if it exists + def GetConnector(self, position, name=None): # if a name is given if name is not None: # Test input, output and action connector if they exists - #if self.Input and name == self.Input.GetName(): + # if self.Input and name == self.Input.GetName(): # return self.Input if self.Output and name == self.Output.GetName(): return self.Output @@ -273,12 +274,12 @@ if self.Action: connectors.append(self.Action) return self.FindNearestConnector(position, connectors) - - # Returns action step connector + + # Returns action step connector def GetActionConnector(self): return self.Action - - # Returns input and output step connectors + + # Returns input and output step connectors def GetConnectors(self): connectors = {"inputs": [], "outputs": []} if self.Input: @@ -286,9 +287,9 @@ if self.Output: connectors["outputs"].append(self.Output) return connectors - + # Test if point given is on step input or output connector - def TestConnector(self, pt, direction = None, exclude=True): + def TestConnector(self, pt, direction=None, exclude=True): # Test input connector if it exists if self.Input and self.Input.TestPoint(pt, direction, exclude): return self.Input @@ -312,7 +313,7 @@ # Returns the step initial property def GetInitial(self): return self.Initial - + # Returns the connector connected to input def GetPreviousConnector(self): if self.Input: @@ -320,7 +321,7 @@ if len(wires) == 1: return wires[0][0].GetOtherConnected(self.Input) return None - + # Returns the connector connected to output def GetNextConnector(self): if self.Output: @@ -328,7 +329,7 @@ if len(wires) == 1: return wires[0][0].GetOtherConnected(self.Output) return None - + # Returns the connector connected to action def GetActionConnected(self): if self.Action: @@ -336,7 +337,7 @@ if len(wires) == 1: return wires[0][0].GetOtherConnected(self.Action) return None - + # Returns the number of action line def GetActionExtraLineNumber(self): if self.Action: @@ -346,7 +347,7 @@ action_block = wires[0][0].GetOtherConnected(self.Action).GetParentBlock() return max(0, action_block.GetLineNumber() - 1) return 0 - + # Returns the step minimum size def GetMinSize(self): text_width, text_height = self.Parent.GetTextExtent(self.Name) @@ -354,7 +355,7 @@ return text_width + 14, text_height + 14 else: return text_width + 10, text_height + 10 - + # Updates the step size def UpdateSize(self, width, height): diffx = self.Size.GetWidth() / 2 - width / 2 @@ -365,7 +366,7 @@ self.RefreshConnected() else: self.RefreshOutputPosition((0, diffy)) - + # Align input element with this step def RefreshInputPosition(self): if self.Input: @@ -382,9 +383,9 @@ input_block.MoveActionBlock((diffx, 0)) input_block.Move(diffx, 0) input_block.RefreshInputPosition() - + # Align output element with this step - def RefreshOutputPosition(self, move = None): + def RefreshOutputPosition(self, move=None): if self.Output: wires = self.Output.GetWires() if len(wires) != 1: @@ -400,7 +401,7 @@ if isinstance(output_block, SFC_Step): output_block.MoveActionBlock((diffx, diffy)) wires[0][0].SetPoints([wx.Point(current_pos.x, current_pos.y + wire_size), - wx.Point(current_pos.x, current_pos.y)]) + wx.Point(current_pos.x, current_pos.y)]) if not isinstance(output_block, SFC_Divergence) or output_block.GetConnectors()["inputs"].index(output) == 0: output_block.Move(diffx, diffy, self.Parent.Wires) output_block.RefreshOutputPosition((diffx, diffy)) @@ -422,7 +423,7 @@ output_block.MoveActionBlock((diffx, 0)) output_block.Move(diffx, 0) output_block.RefreshOutputPosition() - + # Refresh action element with this step def MoveActionBlock(self, move): if self.Action: @@ -432,24 +433,24 @@ action_block = wires[0][0].GetOtherConnected(self.Action).GetParentBlock() action_block.Move(move[0], move[1], self.Parent.Wires) wires[0][0].Move(move[0], move[1], True) - + # Resize the divergence from position and size given def Resize(self, x, y, width, height): if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: self.UpdateSize(width, height) else: Graphic_Element.Resize(self, x, y, width, height) - + # Method called when a LeftDClick event have been generated def OnLeftDClick(self, event, dc, scaling): # Edit the step properties self.Parent.EditStepContent(self) - + # Method called when a RightUp event have been generated def OnRightUp(self, event, dc, scaling): # Popup the menu with special items for a step self.Parent.PopupDefaultMenu() - + # Refreshes the step state according to move defined and handle selected def ProcessDragging(self, movex, movey, event, scaling): handle_type, handle = self.Handle @@ -477,17 +478,17 @@ return movex, 0 else: return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling) - + # Refresh input element model def RefreshInputModel(self): if self.Input: input = self.GetPreviousConnector() - if input: + if input: input_block = input.GetParentBlock() input_block.RefreshModel(False) if not isinstance(input_block, SFC_Divergence): input_block.RefreshInputModel() - + # Refresh output element model def RefreshOutputModel(self, move=False): if self.Output: @@ -497,7 +498,7 @@ output_block.RefreshModel(False) if not isinstance(output_block, SFC_Divergence) or move: output_block.RefreshOutputModel(move) - + # Refreshes the step model def RefreshModel(self, move=True): self.Parent.RefreshStepModel(self) @@ -513,21 +514,21 @@ self.RefreshOutputModel(self.Initial) elif self.Output: self.Output.RefreshWires() - + # Adds an highlight to the connection def AddHighlight(self, infos, start, end, highlight_type): if infos[0] == "name" and start[0] == 0 and end[0] == 0: AddHighlight(self.Highlights, (start, end, highlight_type)) - + # Removes an highlight from the connection def RemoveHighlight(self, infos, start, end, highlight_type): if infos[0] == "name": RemoveHighlight(self.Highlights, (start, end, highlight_type)) - + # Removes all the highlights of one particular type from the connection def ClearHighlight(self, highlight_type=None): ClearHighlights(self.Highlights, highlight_type) - + # Draws step def Draw(self, dc): Graphic_Element.Draw(self, dc) @@ -541,12 +542,12 @@ else: dc.SetPen(MiterPen(wx.BLACK)) dc.SetBrush(wx.WHITE_BRUSH) - + if getattr(dc, "printing", False): name_size = dc.GetTextExtent(self.Name) else: name_size = self.NameSize - + # Draw two rectangles for representing the step dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1) if self.Initial: @@ -562,23 +563,23 @@ self.Output.Draw(dc) if self.Action: self.Action.Draw(dc) - + if not getattr(dc, "printing", False): DrawHighlightedText(dc, self.Name, self.Highlights, name_pos[0], name_pos[1]) - - -#------------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------------- # Sequencial Function Chart Transition -#------------------------------------------------------------------------------- - -""" -Class that implements the graphic representation of a transition -""" +# ------------------------------------------------------------------------------- + class SFC_Transition(Graphic_Element, DebugDataConsumer): - + """ + Class that implements the graphic representation of a transition + """ + # Create a new transition - def __init__(self, parent, type = "reference", condition = None, priority = 0, id = None): + def __init__(self, parent, type="reference", condition=None, priority=0, id=None): Graphic_Element.__init__(self, parent) DebugDataConsumer.__init__(self) self.Type = None @@ -586,14 +587,14 @@ self.Priority = 0 self.Size = wx.Size(SFC_TRANSITION_SIZE[0], SFC_TRANSITION_SIZE[1]) # Create an input and output connector - self.Input = Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH, onlyone = True) - self.Output = Connector(self, "", None, wx.Point(self.Size[0] / 2, self.Size[1]), SOUTH, onlyone = True) + self.Input = Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH, onlyone=True) + self.Output = Connector(self, "", None, wx.Point(self.Size[0] / 2, self.Size[1]), SOUTH, onlyone=True) self.SetType(type, condition) self.SetPriority(priority) self.Highlights = {} self.PreviousValue = None self.PreviousSpreading = False - + def Flush(self): if self.Input is not None: self.Input.Flush() @@ -604,13 +605,13 @@ if self.Type == "connection" and self.Condition is not None: self.Condition.Flush() self.Condition = None - + def SetForced(self, forced): if self.Forced != forced: self.Forced = forced if self.Visible: self.Parent.ElementNeedRefresh(self) - + def SetValue(self, value): self.PreviousValue = self.Value self.Value = value @@ -618,7 +619,7 @@ if self.Visible: self.Parent.ElementNeedRefresh(self) self.SpreadCurrent() - + def SpreadCurrent(self): if self.Parent.Debug: if self.Value is None: @@ -629,9 +630,9 @@ elif not spreading and self.PreviousSpreading: self.Output.SpreadCurrent(False) self.PreviousSpreading = spreading - + # Make a clone of this SFC_Transition - def Clone(self, parent, id = None, pos = None): + def Clone(self, parent, id=None, pos=None): transition = SFC_Transition(parent, self.Type, self.Condition, self.Priority, id) transition.SetSize(self.Size[0], self.Size[1]) if pos is not None: @@ -643,15 +644,15 @@ if self.Type == "connection": transition.Condition = self.Condition.Clone(transition) return transition - + def GetConnectorTranslation(self, element): - connectors = {self.Input : element.Input, self.Output : element.Output} + connectors = {self.Input: element.Input, self.Output: element.Output} if self.Type == "connection" and self.Condition is not None: connectors[self.Condition] = element.Condition return connectors - + # Returns the RedrawRect - def GetRedrawRect(self, movex = 0, movey = 0): + def GetRedrawRect(self, movex=0, movey=0): rect = Graphic_Element.GetRedrawRect(self, movex, movey) if self.Input: rect = rect.Union(self.Input.GetRedrawRect(movex, movey)) @@ -665,17 +666,17 @@ if self.Type == "connection" and self.Condition.IsConnected(): rect = rect.Union(self.Condition.GetConnectedRedrawRect(movex, movey)) return rect - + # Forbids to change the transition size def SetSize(self, width, height): if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: Graphic_Element.SetSize(self, width, height) - + # Forbids to resize the transition def Resize(self, x, y, width, height): if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: Graphic_Element.Resize(self, x, y, width, height) - + # Refresh the size of text for name def RefreshConditionSize(self): if self.Type != "connection": @@ -683,7 +684,7 @@ self.ConditionSize = self.Parent.GetTextExtent(self.Condition) else: self.ConditionSize = self.Parent.GetTextExtent("Transition") - + # Refresh the size of text for name def RefreshPrioritySize(self): if self.Priority != "": @@ -694,14 +695,14 @@ # Delete this transition by calling the appropriate method def Delete(self): self.Parent.DeleteTransition(self) - + # Unconnect input and output def Clean(self): - self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) - self.Output.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + self.Input.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + self.Output.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) if self.Type == "connection": - self.Condition.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) - + self.Condition.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + # Returns if the point given is in the bounding box def HitTest(self, pt, connectors=True): if self.Type != "connection": @@ -715,7 +716,7 @@ else: test_text = False return test_text or Graphic_Element.HitTest(self, pt, connectors) - + # Refresh the transition bounding box def RefreshBoundingBox(self): bbx_x, bbx_y, bbx_width, bbx_height = self.Pos.x, self.Pos.y, self.Size[0], self.Size[1] @@ -733,21 +734,21 @@ bbx_y = min(bbx_y, self.Pos.y - max(0, (text_height - self.Size[1]) / 2)) bbx_height = max(bbx_height, self.Pos.y - bbx_y + (self.Size[1] + text_height) / 2) self.BoundingBox = wx.Rect(bbx_x, bbx_y, bbx_width + 1, bbx_height + 1) - + # Returns the connector connected to input def GetPreviousConnector(self): wires = self.Input.GetWires() if len(wires) == 1: return wires[0][0].GetOtherConnected(self.Input) return None - + # Returns the connector connected to output def GetNextConnector(self): wires = self.Output.GetWires() if len(wires) == 1: return wires[0][0].GetOtherConnected(self.Output) return None - + # Refresh the positions of the transition connectors def RefreshConnectors(self): scaling = self.Parent.GetScaling() @@ -763,20 +764,20 @@ if self.Type == "connection": self.Condition.SetPosition(wx.Point(0, vertical_pos)) self.RefreshConnected() - + # Refresh the position of the wires connected to transition - def RefreshConnected(self, exclude = []): + def RefreshConnected(self, exclude=[]): self.Input.MoveConnected(exclude) self.Output.MoveConnected(exclude) if self.Type == "connection": self.Condition.MoveConnected(exclude) - - # Returns the transition connector that starts with the point given if it exists - def GetConnector(self, position, name = None): + + # Returns the transition connector that starts with the point given if it exists + def GetConnector(self, position, name=None): # if a name is given if name is not None: # Test input and output connector - #if name == self.Input.GetName(): + # if name == self.Input.GetName(): # return self.Input if name == self.Output.GetName(): return self.Output @@ -786,19 +787,19 @@ if self.Type == "connection": connectors.append(self.Condition) return self.FindNearestConnector(position, connectors) - + # Returns the transition condition connector def GetConditionConnector(self): if self.Type == "connection": return self.Condition return None - + # Returns input and output transition connectors def GetConnectors(self): return {"inputs": [self.Input], "outputs": [self.Output]} - + # Test if point given is on transition input or output connector - def TestConnector(self, pt, direction = None, exclude=True): + def TestConnector(self, pt, direction=None, exclude=True): # Test input connector if self.Input.TestPoint(pt, direction, exclude): return self.Input @@ -811,25 +812,25 @@ return None # Changes the transition type - def SetType(self, type, condition = None): + def SetType(self, type, condition=None): if self.Type != type: if self.Type == "connection": - self.Condition.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + self.Condition.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) self.Type = type if type == "connection": self.Condition = Connector(self, "", "BOOL", wx.Point(0, self.Size[1] / 2), WEST) else: - if condition == None: + if condition is None: condition = "" self.Condition = condition self.RefreshConditionSize() elif self.Type != "connection": - if condition == None: + if condition is None: condition = "" self.Condition = condition self.RefreshConditionSize() self.RefreshBoundingBox() - + # Returns the transition type def GetType(self): return self.Type @@ -839,7 +840,7 @@ self.Priority = priority self.RefreshPrioritySize() self.RefreshBoundingBox() - + # Returns the transition type def GetPriority(self): return self.Priority @@ -849,11 +850,11 @@ if self.Type != "connection": return self.Condition return None - + # Returns the transition minimum size def GetMinSize(self): return SFC_TRANSITION_SIZE - + # Align input element with this step def RefreshInputPosition(self): wires = self.Input.GetWires() @@ -870,9 +871,9 @@ input_block.MoveActionBlock((diffx, 0)) input_block.Move(diffx, 0) input_block.RefreshInputPosition() - + # Align output element with this step - def RefreshOutputPosition(self, move = None): + def RefreshOutputPosition(self, move=None): wires = self.Output.GetWires() if len(wires) != 1: return @@ -902,12 +903,12 @@ def OnLeftDClick(self, event, dc, scaling): # Edit the transition properties self.Parent.EditTransitionContent(self) - + # Method called when a RightUp event have been generated def OnRightUp(self, event, dc, scaling): # Popup the menu with special items for a step self.Parent.PopupDefaultMenu() - + # Refreshes the transition state according to move defined and handle selected def ProcessDragging(self, movex, movey, event, scaling): if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: @@ -919,8 +920,8 @@ self.RefreshOutputPosition() return movex, 0 else: - return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling, width_fac = 2, height_fac = 2) - + return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling, width_fac=2, height_fac=2) + # Refresh input element model def RefreshInputModel(self): if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: @@ -930,7 +931,7 @@ input_block.RefreshModel(False) if not isinstance(input_block, SFC_Divergence): input_block.RefreshInputModel() - + # Refresh output element model def RefreshOutputModel(self, move=False): output = self.GetNextConnector() @@ -939,7 +940,7 @@ output_block.RefreshModel(False) if not isinstance(output_block, SFC_Divergence) or move: output_block.RefreshOutputModel(move) - + # Refreshes the transition model def RefreshModel(self, move=True): self.Parent.RefreshTransitionModel(self) @@ -950,20 +951,20 @@ self.RefreshOutputModel() else: self.Output.RefreshWires() - + # Adds an highlight to the block - def AddHighlight(self, infos, start, end ,highlight_type): + def AddHighlight(self, infos, start, end, highlight_type): if infos[0] in ["reference", "inline", "priority"] and start[0] == 0 and end[0] == 0: highlights = self.Highlights.setdefault(infos[0], []) AddHighlight(highlights, (start, end, highlight_type)) - + # Removes an highlight from the block def RemoveHighlight(self, infos, start, end, highlight_type): if infos[0] in ["reference", "inline", "priority"]: highlights = self.Highlights.get(infos[0], []) if RemoveHighlight(highlights, (start, end, highlight_type)) and len(highlights) == 0: self.Highlights.pop(infos[0]) - + # Removes all the highlights of one particular type from the block def ClearHighlight(self, highlight_type=None): if highlight_type is None: @@ -974,7 +975,7 @@ highlights = ClearHighlights(highlight, highlight_type) if len(highlights) == 0: self.Highlights.pop(name) - + # Draws transition def Draw(self, dc): Graphic_Element.Draw(self, dc) @@ -991,7 +992,7 @@ else: dc.SetPen(MiterPen(wx.BLACK)) dc.SetBrush(wx.BLACK_BRUSH) - + if getattr(dc, "printing", False): if self.Type != "connection": condition_size = dc.GetTextExtent(self.Condition) @@ -1002,14 +1003,14 @@ condition_size = self.ConditionSize if self.Priority != 0: priority_size = self.PrioritySize - + # Draw plain rectangle for representing the transition - dc.DrawRectangle(self.Pos.x, - self.Pos.y + (self.Size[1] - SFC_TRANSITION_SIZE[1])/2, + dc.DrawRectangle(self.Pos.x, + self.Pos.y + (self.Size[1] - SFC_TRANSITION_SIZE[1])/2, self.Size[0] + 1, SFC_TRANSITION_SIZE[1] + 1) vertical_line_x = self.Input.GetPosition()[0] - dc.DrawLine(vertical_line_x, self.Pos.y, vertical_line_x, self.Pos.y + self.Size[1] + 1) + dc.DrawLine(vertical_line_x, self.Pos.y, vertical_line_x, self.Pos.y + self.Size[1] + 1) # Draw transition condition if self.Type != "connection": if self.Condition != "": @@ -1028,7 +1029,7 @@ self.Output.Draw(dc) if self.Type == "connection": self.Condition.Draw(dc) - + if not getattr(dc, "printing", False): for name, highlights in self.Highlights.iteritems(): if name == "priority": @@ -1036,19 +1037,20 @@ else: DrawHighlightedText(dc, condition, highlights, condition_pos[0], condition_pos[1]) -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Sequencial Function Chart Divergence and Convergence -#------------------------------------------------------------------------------- - -""" -Class that implements the graphic representation of a divergence or convergence, -selection or simultaneous -""" +# ------------------------------------------------------------------------------- + class SFC_Divergence(Graphic_Element): - + """ + Class that implements the graphic representation of a divergence or convergence, + selection or simultaneous + """ + # Create a new divergence - def __init__(self, parent, type, number = 2, id = None): + def __init__(self, parent, type, number=2, id=None): Graphic_Element.__init__(self, parent) self.Type = type self.Id = id @@ -1057,18 +1059,18 @@ self.Size = wx.Size((number - 1) * SFC_DEFAULT_SEQUENCE_INTERVAL, self.GetMinSize()[1]) # Create an input and output connector if self.Type in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]: - self.Inputs = [Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH, onlyone = True)] + self.Inputs = [Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH, onlyone=True)] self.Outputs = [] for i in xrange(number): - self.Outputs.append(Connector(self, "", None, wx.Point(i * SFC_DEFAULT_SEQUENCE_INTERVAL, self.Size[1]), SOUTH, onlyone = True)) + self.Outputs.append(Connector(self, "", None, wx.Point(i * SFC_DEFAULT_SEQUENCE_INTERVAL, self.Size[1]), SOUTH, onlyone=True)) elif self.Type in [SELECTION_CONVERGENCE, SIMULTANEOUS_CONVERGENCE]: self.Inputs = [] for i in xrange(number): - self.Inputs.append(Connector(self, "", None, wx.Point(i * SFC_DEFAULT_SEQUENCE_INTERVAL, 0), NORTH, onlyone = True)) - self.Outputs = [Connector(self, "", None, wx.Point(self.Size[0] / 2, self.Size[1]), SOUTH, onlyone = True)] + self.Inputs.append(Connector(self, "", None, wx.Point(i * SFC_DEFAULT_SEQUENCE_INTERVAL, 0), NORTH, onlyone=True)) + self.Outputs = [Connector(self, "", None, wx.Point(self.Size[0] / 2, self.Size[1]), SOUTH, onlyone=True)] self.Value = None self.PreviousValue = None - + def Flush(self): for input in self.Inputs: input.Flush() @@ -1076,7 +1078,7 @@ for output in self.Outputs: output.Flush() self.Outputs = [] - + def SpreadCurrent(self): if self.Parent.Debug: self.PreviousValue = self.Value @@ -1102,9 +1104,9 @@ self.Parent.ElementNeedRefresh(self) for output in self.Outputs: output.SpreadCurrent(False) - + # Make a clone of this SFC_Divergence - def Clone(self, parent, id = None, pos = None): + def Clone(self, parent, id=None, pos=None): divergence = SFC_Divergence(parent, self.Type, max(len(self.Inputs), len(self.Outputs)), id) divergence.SetSize(self.Size[0], self.Size[1]) if pos is not None: @@ -1114,12 +1116,12 @@ divergence.Inputs = [input.Clone(divergence) for input in self.Inputs] divergence.Outputs = [output.Clone(divergence) for output in self.Outputs] return divergence - + def GetConnectorTranslation(self, element): return dict(zip(self.Inputs + self.Outputs, element.Inputs + element.Outputs)) - + # Returns the RedrawRect - def GetRedrawRect(self, movex = 0, movey = 0): + def GetRedrawRect(self, movex=0, movey=0): rect = Graphic_Element.GetRedrawRect(self, movex, movey) if movex != 0 or movey != 0: for input in self.Inputs: @@ -1129,27 +1131,27 @@ if output.IsConnected(): rect = rect.Union(output.GetConnectedRedrawRect(movex, movey)) return rect - + # Forbids to resize the divergence def Resize(self, x, y, width, height): if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: Graphic_Element.Resize(self, x, 0, width, self.GetMinSize()[1]) - + # Delete this divergence by calling the appropriate method def Delete(self): self.Parent.DeleteDivergence(self) - + # Returns the divergence type def GetType(self): return self.Type - + # Unconnect input and output def Clean(self): for input in self.Inputs: - input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + input.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) for output in self.Outputs: - output.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) - + output.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + # Add a branch to the divergence def AddBranch(self): if self.Type in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]: @@ -1157,7 +1159,7 @@ for output in self.Outputs: pos = output.GetRelPosition() maxx = max(maxx, pos.x) - connector = Connector(self, "", None, wx.Point(maxx + SFC_DEFAULT_SEQUENCE_INTERVAL, self.Size[1]), SOUTH, onlyone = True) + connector = Connector(self, "", None, wx.Point(maxx + SFC_DEFAULT_SEQUENCE_INTERVAL, self.Size[1]), SOUTH, onlyone=True) self.Outputs.append(connector) self.MoveConnector(connector, 0) elif self.Type in [SELECTION_CONVERGENCE, SIMULTANEOUS_CONVERGENCE]: @@ -1165,10 +1167,10 @@ for input in self.Inputs: pos = input.GetRelPosition() maxx = max(maxx, pos.x) - connector = Connector(self, "", None, wx.Point(maxx + SFC_DEFAULT_SEQUENCE_INTERVAL, 0), NORTH, onlyone = True) + connector = Connector(self, "", None, wx.Point(maxx + SFC_DEFAULT_SEQUENCE_INTERVAL, 0), NORTH, onlyone=True) self.Inputs.append(connector) self.MoveConnector(connector, SFC_DEFAULT_SEQUENCE_INTERVAL) - + # Remove a branch from the divergence def RemoveBranch(self, connector): if self.Type in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]: @@ -1179,41 +1181,42 @@ if connector in self.Inputs and len(self.Inputs) > 2: self.Inputs.remove(connector) self.MoveConnector(self.Inputs[0], 0) - + # Remove the handled branch from the divergence def RemoveHandledBranch(self): handle_type, handle = self.Handle if handle_type == HANDLE_CONNECTOR: handle.UnConnect(delete=True) self.RemoveBranch(handle) - + # Return the number of branches for the divergence def GetBranchNumber(self): if self.Type in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]: return len(self.Outputs) elif self.Type in [SELECTION_CONVERGENCE, SIMULTANEOUS_CONVERGENCE]: return len(self.Inputs) - + # Returns if the point given is in the bounding box def HitTest(self, pt, connectors=True): - return self.BoundingBox.InsideXY(pt.x, pt.y) or self.TestConnector(pt, exclude=False) != None - + return self.BoundingBox.InsideXY(pt.x, pt.y) or self.TestConnector(pt, exclude=False) is not None + # Refresh the divergence bounding box def RefreshBoundingBox(self): if self.Type in [SELECTION_DIVERGENCE, SELECTION_CONVERGENCE]: - self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, - self.Size[0] + 1, self.Size[1] + 1) + self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, + self.Size[0] + 1, self.Size[1] + 1) elif self.Type in [SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE]: - self.BoundingBox = wx.Rect(self.Pos.x - SFC_SIMULTANEOUS_SEQUENCE_EXTRA, self.Pos.y, + self.BoundingBox = wx.Rect( + self.Pos.x - SFC_SIMULTANEOUS_SEQUENCE_EXTRA, self.Pos.y, self.Size[0] + 2 * SFC_SIMULTANEOUS_SEQUENCE_EXTRA + 1, self.Size[1] + 1) - + # Refresh the position of wires connected to divergence - def RefreshConnected(self, exclude = []): + def RefreshConnected(self, exclude=[]): for input in self.Inputs: input.MoveConnected(exclude) for output in self.Outputs: output.MoveConnected(exclude) - + # Moves the divergence connector given def MoveConnector(self, connector, movex): position = connector.GetRelPosition() @@ -1241,26 +1244,26 @@ self.Size[0] = maxx - minx connector.MoveConnected() self.RefreshBoundingBox() - - # Returns the divergence connector that starts with the point given if it exists - def GetConnector(self, position, name = None): + + # Returns the divergence connector that starts with the point given if it exists + def GetConnector(self, position, name=None): # if a name is given if name is not None: # Test each input and output connector - #for input in self.Inputs: + # for input in self.Inputs: # if name == input.GetName(): # return input for output in self.Outputs: if name == output.GetName(): return output return self.FindNearestConnector(position, self.Inputs + self.Outputs) - - # Returns input and output divergence connectors + + # Returns input and output divergence connectors def GetConnectors(self): return {"inputs": self.Inputs, "outputs": self.Outputs} - + # Test if point given is on divergence input or output connector - def TestConnector(self, pt, direction = None, exclude=True): + def TestConnector(self, pt, direction=None, exclude=True): # Test input connector for input in self.Inputs: if input.TestPoint(pt, direction, exclude): @@ -1270,7 +1273,7 @@ if output.TestPoint(pt, direction, exclude): return output return None - + # Changes the divergence size def SetSize(self, width, height): height = self.GetMinSize()[1] @@ -1290,7 +1293,7 @@ output.MoveConnected() self.Size = wx.Size(width, height) self.RefreshBoundingBox() - + # Returns the divergence minimum size def GetMinSize(self, default=False): width = 0 @@ -1304,7 +1307,7 @@ elif self.Type in [SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE]: return width, 3 return 0, 0 - + # Refresh the position of the block connected to connector def RefreshConnectedPosition(self, connector): wires = connector.GetWires() @@ -1340,9 +1343,9 @@ self.RefreshOutputPosition((0, diffy)) for input in self.Inputs: input.MoveConnected() - + # Align output element with this divergence - def RefreshOutputPosition(self, move = None): + def RefreshOutputPosition(self, move=None): if move: for output_connector in self.Outputs: wires = output_connector.GetWires() @@ -1359,10 +1362,10 @@ if not isinstance(output_block, SFC_Divergence) or output_block.GetConnectors()["inputs"].index(output) == 0: output_block.Move(move[0], move[1], self.Parent.Wires) output_block.RefreshOutputPosition(move) - + # Method called when a LeftDown event have been generated def OnLeftDown(self, event, dc, scaling): - self.RealConnectors = {"Inputs":[],"Outputs":[]} + self.RealConnectors = {"Inputs": [], "Outputs": []} for input in self.Inputs: position = input.GetRelPosition() self.RealConnectors["Inputs"].append(float(position.x)/float(self.Size[0])) @@ -1370,12 +1373,12 @@ position = output.GetRelPosition() self.RealConnectors["Outputs"].append(float(position.x)/float(self.Size[0])) Graphic_Element.OnLeftDown(self, event, dc, scaling) - + # Method called when a LeftUp event have been generated def OnLeftUp(self, event, dc, scaling): Graphic_Element.OnLeftUp(self, event, dc, scaling) self.RealConnectors = None - + # Method called when a RightDown event have been generated def OnRightDown(self, event, dc, scaling): pos = GetScaledEventPosition(event, dc, scaling) @@ -1389,7 +1392,7 @@ self.oldPos = GetScaledEventPosition(event, dc, scaling) else: Graphic_Element.OnRightDown(self, event, dc, scaling) - + # Method called when a RightUp event have been generated def OnRightUp(self, event, dc, scaling): pos = GetScaledEventPosition(event, dc, scaling) @@ -1414,7 +1417,7 @@ else: # Popup the divergence menu without delete branch self.Parent.PopupDivergenceMenu(False) - + # Refreshes the divergence state according to move defined and handle selected def ProcessDragging(self, movex, movey, event, scaling): handle_type, handle = self.Handle @@ -1430,7 +1433,7 @@ elif self.Parent.GetDrawingMode() == FREEDRAWING_MODE: return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling) return 0, 0 - + # Refresh output element model def RefreshOutputModel(self, move=False): if move and self.Parent.GetDrawingMode() != FREEDRAWING_MODE: @@ -1442,7 +1445,7 @@ output_block.RefreshModel(False) if not isinstance(output_block, SFC_Divergence) or move: output_block.RefreshOutputModel(move) - + # Refreshes the divergence model def RefreshModel(self, move=True): self.Parent.RefreshDivergenceModel(self) @@ -1453,7 +1456,7 @@ else: for output in self.Outputs: output.RefreshWires() - + # Draws the highlightment of this element if it is highlighted def DrawHighlightment(self, dc): scalex, scaley = dc.GetUserScale() @@ -1467,13 +1470,13 @@ if self.Type in [SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE]: posx -= SFC_SIMULTANEOUS_SEQUENCE_EXTRA width += SFC_SIMULTANEOUS_SEQUENCE_EXTRA * 2 - dc.DrawRectangle(int(round((posx - 1) * scalex)) - 2, - int(round((self.Pos.y - 1) * scaley)) - 2, - int(round((width + 3) * scalex)) + 5, + dc.DrawRectangle(int(round((posx - 1) * scalex)) - 2, + int(round((self.Pos.y - 1) * scaley)) - 2, + int(round((width + 3) * scalex)) + 5, int(round((self.Size.height + 3) * scaley)) + 5) dc.SetLogicalFunction(wx.COPY) dc.SetUserScale(scalex, scaley) - + # Draws divergence def Draw(self, dc): Graphic_Element.Draw(self, dc) @@ -1487,53 +1490,53 @@ if self.Type in [SELECTION_DIVERGENCE, SELECTION_CONVERGENCE]: dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1) elif self.Type in [SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE]: - dc.DrawLine(self.Pos.x - SFC_SIMULTANEOUS_SEQUENCE_EXTRA, self.Pos.y, + dc.DrawLine(self.Pos.x - SFC_SIMULTANEOUS_SEQUENCE_EXTRA, self.Pos.y, self.Pos.x + self.Size[0] + SFC_SIMULTANEOUS_SEQUENCE_EXTRA + 1, self.Pos.y) - dc.DrawLine(self.Pos.x - SFC_SIMULTANEOUS_SEQUENCE_EXTRA, self.Pos.y + self.Size[1], + dc.DrawLine(self.Pos.x - SFC_SIMULTANEOUS_SEQUENCE_EXTRA, self.Pos.y + self.Size[1], self.Pos.x + self.Size[0] + SFC_SIMULTANEOUS_SEQUENCE_EXTRA + 1, self.Pos.y + self.Size[1]) # Draw inputs and outputs connectors for input in self.Inputs: input.Draw(dc) for output in self.Outputs: output.Draw(dc) - - -#------------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------------- # Sequencial Function Chart Jump to Step -#------------------------------------------------------------------------------- - -""" -Class that implements the graphic representation of a jump to step -""" +# ------------------------------------------------------------------------------- + class SFC_Jump(Graphic_Element): - + """ + Class that implements the graphic representation of a jump to step + """ + # Create a new jump - def __init__(self, parent, target, id = None): + def __init__(self, parent, target, id=None): Graphic_Element.__init__(self, parent) self.SetTarget(target) self.Id = id self.Size = wx.Size(SFC_JUMP_SIZE[0], SFC_JUMP_SIZE[1]) self.Highlights = [] # Create an input and output connector - self.Input = Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH, onlyone = True) + self.Input = Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH, onlyone=True) self.Value = None self.PreviousValue = None - + def Flush(self): if self.Input is not None: self.Input.Flush() self.Input = None - + def SpreadCurrent(self): if self.Parent.Debug: self.PreviousValue = self.Value self.Value = self.Input.ReceivingCurrent() if self.Value != self.PreviousValue and self.Visible: self.Parent.ElementNeedRefresh(self) - + # Make a clone of this SFC_Jump - def Clone(self, parent, id = None, pos = None): + def Clone(self, parent, id=None, pos=None): jump = SFC_Jump(parent, self.Target, id) jump.SetSize(self.Size[0], self.Size[1]) if pos is not None: @@ -1542,12 +1545,12 @@ jump.SetPosition(self.Pos.x, self.Pos.y) jump.Input = self.Input.Clone(jump) return jump - + def GetConnectorTranslation(self, element): - return {self.Input : element.Input} - + return {self.Input: element.Input} + # Returns the RedrawRect - def GetRedrawRect(self, movex = 0, movey = 0): + def GetRedrawRect(self, movex=0, movey=0): rect = Graphic_Element.GetRedrawRect(self, movex, movey) if self.Input: rect = rect.Union(self.Input.GetRedrawRect(movex, movey)) @@ -1555,29 +1558,29 @@ if self.Input.IsConnected(): rect = rect.Union(self.Input.GetConnectedRedrawRect(movex, movey)) return rect - + # Forbids to change the jump size def SetSize(self, width, height): if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: Graphic_Element.SetSize(self, width, height) - + # Forbids to resize jump def Resize(self, x, y, width, height): if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: Graphic_Element.Resize(self, x, y, width, height) - + # Delete this jump by calling the appropriate method def Delete(self): self.Parent.DeleteJump(self) - + # Unconnect input def Clean(self): - self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) - + self.Input.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + # Refresh the size of text for target def RefreshTargetSize(self): self.TargetSize = self.Parent.GetTextExtent(self.Target) - + # Returns if the point given is in the bounding box def HitTest(self, pt, connectors=True): # Calculate the bounding box of the condition outside the transition @@ -1587,22 +1590,22 @@ text_width, text_height) return text_bbx.InsideXY(pt.x, pt.y) or Graphic_Element.HitTest(self, pt, connectors) - + # Refresh the jump bounding box def RefreshBoundingBox(self): text_width, text_height = self.Parent.GetTextExtent(self.Target) # Calculate the bounding box size bbx_width = self.Size[0] + 2 + text_width - self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y - CONNECTOR_SIZE, - bbx_width + 1, self.Size[1] + CONNECTOR_SIZE + 1) - + self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y - CONNECTOR_SIZE, + bbx_width + 1, self.Size[1] + CONNECTOR_SIZE + 1) + # Returns the connector connected to input def GetPreviousConnector(self): wires = self.Input.GetWires() if len(wires) == 1: return wires[0][0].GetOtherConnected(self.Input) return None - + # Refresh the element connectors position def RefreshConnectors(self): scaling = self.Parent.GetScaling() @@ -1611,41 +1614,41 @@ horizontal_pos = round(float(self.Pos.x + horizontal_pos) / float(scaling[0])) * scaling[0] - self.Pos.x self.Input.SetPosition(wx.Point(horizontal_pos, 0)) self.RefreshConnected() - + # Refresh the position of wires connected to jump - def RefreshConnected(self, exclude = []): + def RefreshConnected(self, exclude=[]): if self.Input: self.Input.MoveConnected(exclude) - - # Returns input jump connector - def GetConnector(self, position = None, name = None): + + # Returns input jump connector + def GetConnector(self, position=None, name=None): return self.Input - - # Returns all the jump connectors + + # Returns all the jump connectors def GetConnectors(self): return {"inputs": [self.Input], "outputs": []} - + # Test if point given is on jump input connector - def TestConnector(self, pt, direction = None, exclude = True): + def TestConnector(self, pt, direction=None, exclude=True): # Test input connector if self.Input and self.Input.TestPoint(pt, direction, exclude): return self.Input return None - + # Changes the jump target def SetTarget(self, target): self.Target = target self.RefreshTargetSize() self.RefreshBoundingBox() - + # Returns the jump target def GetTarget(self): return self.Target - + # Returns the jump minimum size def GetMinSize(self): return SFC_JUMP_SIZE - + # Align input element with this jump def RefreshInputPosition(self): if self.Input: @@ -1662,21 +1665,21 @@ input_block.MoveActionBlock((diffx, 0)) input_block.Move(diffx, 0) input_block.RefreshInputPosition() - + # Can't align output element, because there is no output - def RefreshOutputPosition(self, move = None): + def RefreshOutputPosition(self, move=None): pass - + # Method called when a LeftDClick event have been generated def OnLeftDClick(self, event, dc, scaling): # Edit the jump properties self.Parent.EditJumpContent(self) - + # Method called when a RightUp event have been generated def OnRightUp(self, event, dc, scaling): # Popup the default menu self.Parent.PopupDefaultMenu() - + # Refreshes the jump state according to move defined and handle selected def ProcessDragging(self, movex, movey, event, scaling): if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: @@ -1687,8 +1690,8 @@ self.RefreshInputPosition() return movex, 0 else: - return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling, width_fac = 2) - + return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling, width_fac=2) + # Refresh input element model def RefreshInputModel(self): if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: @@ -1698,32 +1701,32 @@ input_block.RefreshModel(False) if not isinstance(input_block, SFC_Divergence): input_block.RefreshInputModel() - + # Refresh output element model def RefreshOutputModel(self, move=False): pass - + # Refreshes the jump model def RefreshModel(self, move=True): self.Parent.RefreshJumpModel(self) if move: if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: self.RefreshInputModel() - + # Adds an highlight to the variable def AddHighlight(self, infos, start, end, highlight_type): if infos[0] == "target" and start[0] == 0 and end[0] == 0: AddHighlight(self.Highlights, (start, end, highlight_type)) - + # Removes an highlight from the variable def RemoveHighlight(self, infos, start, end, highlight_type): if infos[0] == "target": RemoveHighlight(self.Highlights, (start, end, highlight_type)) - + # Removes all the highlights of one particular type from the variable def ClearHighlight(self, highlight_type=None): ClearHighlights(self.Highlights, highlight_type) - + # Draws the highlightment of this element if it is highlighted def DrawHighlightment(self, dc): scalex, scaley = dc.GetUserScale() @@ -1731,16 +1734,16 @@ dc.SetPen(MiterPen(HIGHLIGHTCOLOR)) dc.SetBrush(wx.Brush(HIGHLIGHTCOLOR)) dc.SetLogicalFunction(wx.AND) - points = [wx.Point(int(round((self.Pos.x - 2) * scalex)) - 3, + points = [wx.Point(int(round((self.Pos.x - 2) * scalex)) - 3, int(round((self.Pos.y - 2) * scaley)) - 2), - wx.Point(int(round((self.Pos.x + self.Size[0] + 2) * scalex)) + 4, + wx.Point(int(round((self.Pos.x + self.Size[0] + 2) * scalex)) + 4, int(round((self.Pos.y - 2) * scaley)) - 2), - wx.Point(int(round((self.Pos.x + self.Size[0] / 2) * scalex)), + wx.Point(int(round((self.Pos.x + self.Size[0] / 2) * scalex)), int(round((self.Pos.y + self.Size[1] + 3) * scaley)) + 4)] dc.DrawPolygon(points) dc.SetLogicalFunction(wx.COPY) dc.SetUserScale(scalex, scaley) - + # Draws divergence def Draw(self, dc): Graphic_Element.Draw(self, dc) @@ -1750,12 +1753,12 @@ else: dc.SetPen(MiterPen(wx.BLACK)) dc.SetBrush(wx.BLACK_BRUSH) - + if getattr(dc, "printing", False): target_size = dc.GetTextExtent(self.Target) else: target_size = self.TargetSize - + # Draw plain rectangle for representing the divergence dc.DrawLine(self.Pos.x + self.Size[0] / 2, self.Pos.y, self.Pos.x + self.Size[0] / 2, self.Pos.y + self.Size[1]) points = [wx.Point(self.Pos.x, self.Pos.y), @@ -1769,48 +1772,48 @@ # Draw input connector if self.Input: self.Input.Draw(dc) - + if not getattr(dc, "printing", False): DrawHighlightedText(dc, self.Target, self.Highlights, target_pos[0], target_pos[1]) - - -#------------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------------- # Sequencial Function Chart Action Block -#------------------------------------------------------------------------------- - -""" -Class that implements the graphic representation of an action block -""" +# ------------------------------------------------------------------------------- + class SFC_ActionBlock(Graphic_Element): - + """ + Class that implements the graphic representation of an action block + """ + # Create a new action block - def __init__(self, parent, actions = [], id = None): + def __init__(self, parent, actions=[], id=None): Graphic_Element.__init__(self, parent) self.Id = id self.Size = wx.Size(SFC_ACTION_MIN_SIZE[0], SFC_ACTION_MIN_SIZE[1]) self.MinSize = wx.Size(SFC_ACTION_MIN_SIZE[0], SFC_ACTION_MIN_SIZE[1]) self.Highlights = {} # Create an input and output connector - self.Input = Connector(self, "", None, wx.Point(0, SFC_ACTION_MIN_SIZE[1] / 2), WEST, onlyone = True) + self.Input = Connector(self, "", None, wx.Point(0, SFC_ACTION_MIN_SIZE[1] / 2), WEST, onlyone=True) self.SetActions(actions) self.Value = None self.PreviousValue = None - + def Flush(self): if self.Input is not None: self.Input.Flush() self.Input = None - + def SpreadCurrent(self): if self.Parent.Debug: self.PreviousValue = self.Value self.Value = self.Input.ReceivingCurrent() if self.Value != self.PreviousValue and self.Visible: self.Parent.ElementNeedRefresh(self) - + # Make a clone of this SFC_ActionBlock - def Clone(self, parent, id = None, pos = None): + def Clone(self, parent, id=None, pos=None): actions = [action.copy() for action in self.Actions] action_block = SFC_ActionBlock(parent, actions, id) action_block.SetSize(self.Size[0], self.Size[1]) @@ -1820,12 +1823,12 @@ action_block.SetPosition(self.Pos.x, self.Pos.y) action_block.Input = self.Input.Clone(action_block) return action_block - + def GetConnectorTranslation(self, element): - return {self.Input : element.Input} - + return {self.Input: element.Input} + # Returns the RedrawRect - def GetRedrawRect(self, movex = 0, movey = 0): + def GetRedrawRect(self, movex=0, movey=0): rect = Graphic_Element.GetRedrawRect(self, movex, movey) if self.Input: rect = rect.Union(self.Input.GetRedrawRect(movex, movey)) @@ -1833,17 +1836,17 @@ if self.Input.IsConnected(): rect = rect.Union(self.Input.GetConnectedRedrawRect(movex, movey)) return rect - + # Returns the number of action lines def GetLineNumber(self): return len(self.Actions) - + def GetLineSize(self): if len(self.Actions) > 0: return self.Size[1] / len(self.Actions) else: return SFC_ACTION_MIN_SIZE[1] - + # Forbids to resize the action block def Resize(self, x, y, width, height): if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: @@ -1851,38 +1854,38 @@ self.SetSize(width, self.Size[1]) else: Graphic_Element.Resize(self, x, y, width, height) - + # Delete this action block by calling the appropriate method def Delete(self): self.Parent.DeleteActionBlock(self) - + # Unconnect input and output def Clean(self): - self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) - + self.Input.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + # Refresh the action block bounding box def RefreshBoundingBox(self): self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1) - + # Refresh the position of wires connected to action block - def RefreshConnected(self, exclude = []): + def RefreshConnected(self, exclude=[]): self.Input.MoveConnected(exclude) - - # Returns input action block connector - def GetConnector(self, position = None, name = None): + + # Returns input action block connector + def GetConnector(self, position=None, name=None): return self.Input - - # Returns all the action block connectors + + # Returns all the action block connectors def GetConnectors(self): return {"inputs": [self.Input], "outputs": []} - + # Test if point given is on action block input connector - def TestConnector(self, pt, direction = None, exclude = True): + def TestConnector(self, pt, direction=None, exclude=True): # Test input connector if self.Input.TestPoint(pt, direction, exclude): return self.Input return None - + # Refresh the element connectors position def RefreshConnectors(self): scaling = self.Parent.GetScaling() @@ -1891,7 +1894,7 @@ vertical_pos = round(float(self.Pos.y + vertical_pos) / float(scaling[1])) * scaling[1] - self.Pos.y self.Input.SetPosition(wx.Point(0, vertical_pos)) self.RefreshConnected() - + # Changes the action block actions def SetActions(self, actions): self.Actions = actions @@ -1917,13 +1920,14 @@ if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: self.Size = wx.Size(self.ColSize[0] + self.ColSize[1] + self.ColSize[2], max(min_height, SFC_ACTION_MIN_SIZE[1], self.Size[1])) self.MinSize = max(self.ColSize[0] + self.ColSize[1] + self.ColSize[2], - SFC_ACTION_MIN_SIZE[0]), max(SFC_ACTION_MIN_SIZE[1], min_height) + SFC_ACTION_MIN_SIZE[0]), max(SFC_ACTION_MIN_SIZE[1], min_height) self.RefreshBoundingBox() else: self.Size = wx.Size(max(self.ColSize[0] + self.ColSize[1] + self.ColSize[2], - SFC_ACTION_MIN_SIZE[0]), len(self.Actions) * SFC_ACTION_MIN_SIZE[1]) + SFC_ACTION_MIN_SIZE[0]), + len(self.Actions) * SFC_ACTION_MIN_SIZE[1]) self.MinSize = max(self.ColSize[0] + self.ColSize[1] + self.ColSize[2], - SFC_ACTION_MIN_SIZE[0]), len(self.Actions) * SFC_ACTION_MIN_SIZE[1] + SFC_ACTION_MIN_SIZE[0]), len(self.Actions) * SFC_ACTION_MIN_SIZE[1] self.RefreshBoundingBox() if self.Input is not None: wires = self.Input.GetWires() @@ -1931,25 +1935,25 @@ input_block = wires[0][0].GetOtherConnected(self.Input).GetParentBlock() input_block.RefreshOutputPosition() input_block.RefreshOutputModel(True) - + # Returns the action block actions def GetActions(self): return self.Actions - + # Returns the action block minimum size def GetMinSize(self): return self.MinSize - + # Method called when a LeftDClick event have been generated def OnLeftDClick(self, event, dc, scaling): # Edit the action block properties self.Parent.EditActionBlockContent(self) - + # Method called when a RightUp event have been generated def OnRightUp(self, event, dc, scaling): # Popup the default menu self.Parent.PopupDefaultMenu() - + # Refreshes the action block state according to move defined and handle selected def ProcessDragging(self, movex, movey, event, scaling): if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: @@ -1970,18 +1974,17 @@ else: return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling) - # Refreshes the action block model def RefreshModel(self, move=True): self.Parent.RefreshActionBlockModel(self) - + # Adds an highlight to the variable def AddHighlight(self, infos, start, end, highlight_type): if infos[0] == "action" and infos[1] < len(self.Actions): action_highlights = self.Highlights.setdefault(infos[1], {}) attribute_highlights = action_highlights.setdefault(infos[2], []) AddHighlight(attribute_highlights, (start, end, highlight_type)) - + # Removes an highlight from the block def RemoveHighlight(self, infos, start, end, highlight_type): if infos[0] == "action" and infos[1] < len(self.Actions): @@ -1991,7 +1994,7 @@ action_highlights.pop(infos[2]) if len(action_highlights) == 0: self.Highlights.pop(infos[1]) - + # Removes all the highlights of one particular type from the block def ClearHighlight(self, highlight_type=None): if highlight_type is None: @@ -2006,7 +2009,7 @@ action_highlights.pop(name) if len(action_highlights) == 0: self.Highlights.pop(number) - + # Draws divergence def Draw(self, dc): Graphic_Element.Draw(self, dc) @@ -2018,15 +2021,15 @@ colsize = [self.ColSize[0], self.Size[0] - self.ColSize[0] - self.ColSize[2], self.ColSize[2]] # Draw plain rectangle for representing the action block dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1) - dc.DrawLine(self.Pos.x + colsize[0], self.Pos.y, - self.Pos.x + colsize[0], self.Pos.y + self.Size[1]) - dc.DrawLine(self.Pos.x + colsize[0] + colsize[1], self.Pos.y, - self.Pos.x + colsize[0] + colsize[1], self.Pos.y + self.Size[1]) + dc.DrawLine(self.Pos.x + colsize[0], self.Pos.y, + self.Pos.x + colsize[0], self.Pos.y + self.Size[1]) + dc.DrawLine(self.Pos.x + colsize[0] + colsize[1], self.Pos.y, + self.Pos.x + colsize[0] + colsize[1], self.Pos.y + self.Size[1]) line_size = self.GetLineSize() for i, action in enumerate(self.Actions): if i != 0: - dc.DrawLine(self.Pos.x, self.Pos.y + i * line_size, - self.Pos.x + self.Size[0], self.Pos.y + i * line_size) + dc.DrawLine(self.Pos.x, self.Pos.y + i * line_size, + self.Pos.x + self.Size[0], self.Pos.y + i * line_size) qualifier_size = dc.GetTextExtent(action.qualifier) if action.duration != "": qualifier_pos = (self.Pos.x + (colsize[0] - qualifier_size[0]) / 2, @@ -2048,7 +2051,7 @@ indicator_pos = (self.Pos.x + colsize[0] + colsize[1] + (colsize[2] - indicator_size[0]) / 2, self.Pos.y + i * line_size + (line_size - indicator_size[1]) / 2) dc.DrawText(action.indicator, indicator_pos[0], indicator_pos[1]) - + if not getattr(dc, "printing", False): action_highlights = self.Highlights.get(i, {}) for name, attribute_highlights in action_highlights.iteritems(): @@ -2060,7 +2063,6 @@ DrawHighlightedText(dc, action.value, attribute_highlights, content_pos[0], content_pos[1]) elif name == "indicator": DrawHighlightedText(dc, action.indicator, attribute_highlights, indicator_pos[0], indicator_pos[1]) - + # Draw input connector self.Input.Draw(dc) - diff -r 31e63e25b4cc -r 64beb9e9c749 graphics/ToolTipProducer.py --- a/graphics/ToolTipProducer.py Mon Aug 21 20:17:19 2017 +0000 +++ b/graphics/ToolTipProducer.py Mon Aug 21 23:22:58 2017 +0300 @@ -25,39 +25,40 @@ import wx from controls.CustomToolTip import CustomToolTip, TOOLTIP_WAIT_PERIOD - -#------------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------------- # Tool Tip Producer class -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -""" -Class that implements an element that generate Tool Tip -""" class ToolTipProducer: - + """ + Class that implements an element that generate Tool Tip + """ + def __init__(self, parent): """ Constructor @param parent: Parent Viewer """ self.Parent = parent - + self.ToolTip = None self.ToolTipPos = None - + # Timer for firing Tool tip display self.ToolTipTimer = wx.Timer(self.Parent, -1) - self.Parent.Bind(wx.EVT_TIMER, - self.OnToolTipTimer, - self.ToolTipTimer) - + self.Parent.Bind(wx.EVT_TIMER, + self.OnToolTipTimer, + self.ToolTipTimer) + def __del__(self): """ Destructor """ self.DestroyToolTip() - + def OnToolTipTimer(self, event): """ Callback for Tool Tip firing timer Event @@ -65,21 +66,21 @@ """ # Get Tool Tip text value = self.GetToolTipValue() - + if value is not None and self.ToolTipPos is not None: # Create Tool Tip self.ToolTip = CustomToolTip(self.Parent, value) self.ToolTip.SetToolTipPosition(self.ToolTipPos) self.ToolTip.Show() - + def GetToolTipValue(self): """ Return tool tip text - Have to be overridden by inherited classes - @return: Tool tip text (None if not overridden) + Have to be overridden by inherited classes + @return: Tool tip text (None if not overridden) """ return None - + def DisplayToolTip(self, pos): """ Display Tool tip @@ -87,14 +88,14 @@ """ # Destroy current displayed Tool tip self.DestroyToolTip() - + # Save Tool Tip position self.ToolTipPos = pos # Start Tool tip firing timer self.ToolTipTimer.Start( - int(TOOLTIP_WAIT_PERIOD * 1000), + int(TOOLTIP_WAIT_PERIOD * 1000), oneShot=True) - + def SetToolTipText(self, text): """ Set current Tool tip text @@ -102,7 +103,7 @@ """ if self.ToolTip is not None: self.ToolTip.SetTip(text) - + def DestroyToolTip(self): """ Destroy current displayed Tool Tip @@ -110,7 +111,7 @@ # Stop Tool tip firing timer self.ToolTipTimer.Stop() self.ToolTipPos = None - + # Destroy Tool Tip if self.ToolTip is not None: self.ToolTip.Destroy() diff -r 31e63e25b4cc -r 64beb9e9c749 i18n/mki18n.py --- a/i18n/mki18n.py Mon Aug 21 20:17:19 2017 +0000 +++ b/i18n/mki18n.py Mon Aug 21 23:22:58 2017 +0300 @@ -1,503 +1,497 @@ -#! /usr/bin/env python -# -*- coding: iso-8859-1 -*- -# -# PYTHON MODULE: MKI18N.PY -# ========= -# -# Abstract: Make Internationalization (i18n) files for an application. -# -# Copyright Pierre Rouleau. 2003. Released to public domain. -# -# Last update: Saturday, November 8, 2003. @ 15:55:18. -# -# File: ROUP2003N01::C:/dev/python/mki18n.py -# -# RCS $Header: //software/official/MKS/MKS_SI/TV_NT/dev/Python/rcs/mki18n.py 1.5 2003/11/05 19:40:04 PRouleau Exp $ -# -# Update history: -# -# - File created: Saturday, June 7, 2003. by Pierre Rouleau -# - 10/06/03 rcs : RCS Revision 1.1 2003/06/10 10:06:12 PRouleau -# - 10/06/03 rcs : RCS Initial revision -# - 23/08/03 rcs : RCS Revision 1.2 2003/06/10 10:54:27 PRouleau -# - 23/08/03 P.R.: [code:fix] : The strings encoded in this file are encode in iso-8859-1 format. Added the encoding -# notification to Python to comply with Python's 2.3 PEP 263. -# - 23/08/03 P.R.: [feature:new] : Added the '-e' switch which is used to force the creation of the empty English .mo file. -# - 22/10/03 P.R.: [code] : incorporated utility functions in here to make script self sufficient. -# - 05/11/03 rcs : RCS Revision 1.4 2003/10/22 06:39:31 PRouleau -# - 05/11/03 P.R.: [code:fix] : included the unixpath() in this file. -# - 08/11/03 rcs : RCS Revision 1.5 2003/11/05 19:40:04 PRouleau -# -# RCS $Log: $ -# -# -# ----------------------------------------------------------------------------- -""" -mki18n allows you to internationalize your software. You can use it to -create the GNU .po files (Portable Object) and the compiled .mo files -(Machine Object). - -mki18n module can be used from the command line or from within a script (see -the Usage at the end of this page). - - Table of Contents - ----------------- - - makePO() -- Build the Portable Object file for the application -- - catPO() -- Concatenate one or several PO files with the application domain files. -- - makeMO() -- Compile the Portable Object files into the Machine Object stored in the right location. -- - printUsage -- Displays how to use this script from the command line -- - - Scriptexecution -- Runs when invoked from the command line -- - - -NOTE: this module uses GNU gettext utilities. - -You can get the gettext tools from the following sites: - - - `GNU FTP site for gettetx`_ where several versions (0.10.40, 0.11.2, 0.11.5 and 0.12.1) are available. - Note that you need to use `GNU libiconv`_ to use this. Get it from the `GNU - libiconv ftp site`_ and get version 1.9.1 or later. Get the Windows .ZIP - files and install the packages inside c:/gnu. All binaries will be stored - inside c:/gnu/bin. Just put c:/gnu/bin inside your PATH. You will need - the following files: - - - `gettext-runtime-0.12.1.bin.woe32.zip`_ - - `gettext-tools-0.12.1.bin.woe32.zip`_ - - `libiconv-1.9.1.bin.woe32.zip`_ - - -.. _GNU libiconv: http://www.gnu.org/software/libiconv/ -.. _GNU libiconv ftp site: http://www.ibiblio.org/pub/gnu/libiconv/ -.. _gettext-runtime-0.12.1.bin.woe32.zip: ftp://ftp.gnu.org/gnu/gettext/gettext-runtime-0.12.1.bin.woe32.zip -.. _gettext-tools-0.12.1.bin.woe32.zip: ftp://ftp.gnu.org/gnu/gettext/gettext-tools-0.12.1.bin.woe32.zip -.. _libiconv-1.9.1.bin.woe32.zip: http://www.ibiblio.org/pub/gnu/libiconv/libiconv-1.9.1.bin.woe32.zip - -""" -# ----------------------------------------------------------------------------- -# Module Import -# ------------- -# -import os -import sys -import wx -import re - -# ----------------------------------------------------------------------------- -# Global variables -# ---------------- -# - -__author__ = "Pierre Rouleau" -__version__= "$Revision: 1.5 $" - -# ----------------------------------------------------------------------------- - -def getlanguageDict(): - languageDict = {} - - if wx.VERSION >= (3, 0, 0): - app = wx.App() - else: - app = wx.PySimpleApp() - - for lang in [x for x in dir(wx) if x.startswith("LANGUAGE")]: - i = wx.Locale(wx.LANGUAGE_DEFAULT).GetLanguageInfo(getattr(wx, lang)) - if i: - languageDict[i.CanonicalName] = i.Description - - return languageDict - - - -def processCustomFiles(filein, fileout, regexp, prefix = ''): - appfil_file = open(filein, 'r') - messages_file = open(fileout, 'r') - messages = messages_file.read() - messages_file.close() - messages_file = open(fileout, 'a') - messages_file.write('\n') - messages_file.write('#: %s\n' % prefix) - messages_file.write('\n') - - words_found = {} - for filepath in appfil_file.xreadlines(): - code_file = open(filepath.strip(), 'r') - for match in regexp.finditer(code_file.read()): - word = match.group(1) - if not words_found.get(word, False) and messages.find("msgid \"%s\"\nmsgstr \"\"" % word) == -1: - words_found[word] = True - messages_file.write('\n') - messages_file.write("msgid \"%s\"\n"%word) - messages_file.write("msgstr \"\"\n") - code_file.close() - - messages_file.close() - appfil_file.close() - - -# ----------------------------------------------------------------------------- -# m a k e P O ( ) -- Build the Portable Object file for the application -- -# ^^^^^^^^^^^^^^^ -# -def makePO(applicationDirectoryPath, applicationDomain=None, verbose=0) : - """Build the Portable Object Template file for the application. - - makePO builds the .pot file for the application stored inside - a specified directory by running xgettext for all application source - files. It finds the name of all files by looking for a file called 'app.fil'. - If this file does not exists, makePo raises an IOError exception. - By default the application domain (the application - name) is the same as the directory name but it can be overridden by the - 'applicationDomain' argument. - - makePO always creates a new file called messages.pot. If it finds files - of the form app_xx.po where 'app' is the application name and 'xx' is one - of the ISO 639 two-letter language codes, makePO resynchronizes those - files with the latest extracted strings (now contained in messages.pot). - This process updates all line location number in the language-specific - .po files and may also create new entries for translation (or comment out - some). The .po file is not changed, instead a new file is created with - the .new extension appended to the name of the .po file. - - By default the function does not display what it is doing. Set the - verbose argument to 1 to force it to print its commands. - """ - - if applicationDomain is None: - applicationName = fileBaseOf(applicationDirectoryPath,withPath=0) - else: - applicationName = applicationDomain - currentDir = os.getcwd() - os.chdir(applicationDirectoryPath) - filelist = 'app.fil' - if not os.path.exists(filelist): - raise IOError(2,'No module file: ' % filelist) - - fileout = 'messages.pot' - # Steps: - # Use xgettext to parse all application modules - # The following switches are used: - # - # -s : sort output by string content (easier to use when we need to merge several .po files) - # --files-from=app.fil : The list of files is taken from the file: app.fil - # --output= : specifies the name of the output file (using a .pot extension) - cmd = 'xgettext -s --no-wrap --language=Python --files-from=' + filelist + ' --output=' + fileout + ' --package-name ' + applicationName - if verbose: print cmd - os.system(cmd) - - XSD_STRING_MODEL = re.compile("]*\>") - processCustomFiles(filelist, fileout, XSD_STRING_MODEL, 'Extra XSD strings') - - XML_TC6_STRING_MODEL = re.compile("\s*\s*", re.MULTILINE | re.DOTALL) - processCustomFiles(filelist, fileout, XML_TC6_STRING_MODEL, 'Extra TC6 documentation strings') - - # generate messages.po - cmd = 'msginit --no-wrap --no-translator -i %s -l en_US.UTF-8 -o messages.po' % (fileout) - if verbose: print cmd - os.system(cmd) - - languageDict = getlanguageDict() - - for langCode in languageDict.keys(): - if langCode == 'en': - pass - else: - langPOfileName = "%s_%s.po" % (applicationName , langCode) - if os.path.exists(langPOfileName): - cmd = 'msgmerge -s --no-wrap "%s" %s > "%s.new"' % (langPOfileName, fileout, langPOfileName) - if verbose: print cmd - os.system(cmd) - os.chdir(currentDir) - -# ----------------------------------------------------------------------------- -# c a t P O ( ) -- Concatenate one or several PO files with the application domain files. -- -# ^^^^^^^^^^^^^ -# -def catPO(applicationDirectoryPath, listOf_extraPo, applicationDomain=None, targetDir=None, verbose=0) : - """Concatenate one or several PO files with the application domain files. - """ - - if applicationDomain is None: - applicationName = fileBaseOf(applicationDirectoryPath,withPath=0) - else: - applicationName = applicationDomain - currentDir = os.getcwd() - os.chdir(applicationDirectoryPath) - - languageDict = getlanguageDict() - - for langCode in languageDict.keys(): - if langCode == 'en': - pass - else: - langPOfileName = "%s_%s.po" % (applicationName , langCode) - if os.path.exists(langPOfileName): - fileList = '' - for fileName in listOf_extraPo: - fileList += ("%s_%s.po " % (fileName,langCode)) - cmd = "msgcat -s --no-wrap %s %s > %s.cat" % (langPOfileName, fileList, langPOfileName) - if verbose: print cmd - os.system(cmd) - if targetDir is None: - pass - else: - mo_targetDir = "%s/%s/LC_MESSAGES" % (targetDir,langCode) - cmd = "msgfmt --output-file=%s/%s.mo %s_%s.po.cat" % (mo_targetDir,applicationName,applicationName,langCode) - if verbose: print cmd - os.system(cmd) - os.chdir(currentDir) - -# ----------------------------------------------------------------------------- -# m a k e M O ( ) -- Compile the Portable Object files into the Machine Object stored in the right location. -- -# ^^^^^^^^^^^^^^^ -# -def makeMO(applicationDirectoryPath,targetDir='./locale',applicationDomain=None, verbose=0, forceEnglish=0) : - """Compile the Portable Object files into the Machine Object stored in the right location. - - makeMO converts all translated language-specific PO files located inside - the application directory into the binary .MO files stored inside the - LC_MESSAGES sub-directory for the found locale files. - - makeMO searches for all files that have a name of the form 'app_xx.po' - inside the application directory specified by the first argument. The - 'app' is the application domain name (that can be specified by the - applicationDomain argument or is taken from the directory name). The 'xx' - corresponds to one of the ISO 639 two-letter language codes. - - makeMo stores the resulting files inside a sub-directory of `targetDir` - called xx/LC_MESSAGES where 'xx' corresponds to the 2-letter language - code. - """ - if targetDir is None: - targetDir = './locale' - if verbose: - print "Target directory for .mo files is: %s" % targetDir - - if applicationDomain is None: - applicationName = fileBaseOf(applicationDirectoryPath,withPath=0) - else: - applicationName = applicationDomain - currentDir = os.getcwd() - os.chdir(applicationDirectoryPath) - - languageDict = getlanguageDict() - - for langCode in languageDict.keys(): - if (langCode == 'en') and (forceEnglish==0): - pass - else: - langPOfileName = "%s_%s.po" % (applicationName , langCode) - if os.path.exists(langPOfileName): - mo_targetDir = "%s/%s/LC_MESSAGES" % (targetDir,langCode) - if not os.path.exists(mo_targetDir): - mkdir(mo_targetDir) - cmd = 'msgfmt --output-file="%s/%s.mo" "%s_%s.po"' % (mo_targetDir,applicationName,applicationName,langCode) - if verbose: print cmd - os.system(cmd) - os.chdir(currentDir) - -# ----------------------------------------------------------------------------- -# p r i n t U s a g e -- Displays how to use this script from the command line -- -# ^^^^^^^^^^^^^^^^^^^ -# -def printUsage(errorMsg=None) : - """Displays how to use this script from the command line.""" - print """ - ################################################################################## - # mki18n : Make internationalization files. # - # Uses the GNU gettext system to create PO (Portable Object) files # - # from source code, coimpile PO into MO (Machine Object) files. # - # Supports C,C++,Python source files. # - # # - # Usage: mki18n {OPTION} [appDirPath] # - # # - # Options: # - # -e : When -m is used, forces English .mo file creation # - # -h : prints this help # - # -m : make MO from existing PO files # - # -p : make PO, update PO files: Creates a new messages.pot # - # file. Creates a dom_xx.po.new for every existing # - # language specific .po file. ('xx' stands for the ISO639 # - # two-letter language code and 'dom' stands for the # - # application domain name). mki18n requires that you # - # write a 'app.fil' file which contains the list of all # - # source code to parse. # - # -v : verbose (prints comments while running) # - # --domain=appName : specifies the application domain name. By default # - # the directory name is used. # - # --moTarget=dir : specifies the directory where .mo files are stored. # - # If not specified, the target is './locale' # - # # - # You must specify one of the -p or -m option to perform the work. You can # - # specify the path of the target application. If you leave it out mki18n # - # will use the current directory as the application main directory. # - # # - ##################################################################################""" - if errorMsg: - print "\n ERROR: %s" % errorMsg - -# ----------------------------------------------------------------------------- -# f i l e B a s e O f ( ) -- Return base name of filename -- -# ^^^^^^^^^^^^^^^^^^^^^^^ -# -def fileBaseOf(filename,withPath=0) : - """fileBaseOf(filename,withPath) ---> string - - Return base name of filename. The returned string never includes the extension. - Use os.path.basename() to return the basename with the extension. The - second argument is optional. If specified and if set to 'true' (non zero) - the string returned contains the full path of the file name. Otherwise the - path is excluded. - - [Example] - >>> fn = 'd:/dev/telepath/tvapp/code/test.html' - >>> fileBaseOf(fn) - 'test' - >>> fileBaseOf(fn) - 'test' - >>> fileBaseOf(fn,1) - 'd:/dev/telepath/tvapp/code/test' - >>> fileBaseOf(fn,0) - 'test' - >>> fn = 'abcdef' - >>> fileBaseOf(fn) - 'abcdef' - >>> fileBaseOf(fn,1) - 'abcdef' - >>> fn = "abcdef." - >>> fileBaseOf(fn) - 'abcdef' - >>> fileBaseOf(fn,1) - 'abcdef' - """ - pos = filename.rfind('.') - if pos > 0: - filename = filename[:pos] - if withPath: - return filename - else: - return os.path.basename(filename) -# ----------------------------------------------------------------------------- -# m k d i r ( ) -- Create a directory (and possibly the entire tree) -- -# ^^^^^^^^^^^^^ -# -def mkdir(directory) : - """Create a directory (and possibly the entire tree). - - The os.mkdir() will fail to create a directory if one of the - directory in the specified path does not exist. mkdir() - solves this problem. It creates every intermediate directory - required to create the final path. Under Unix, the function - only supports forward slash separator, but under Windows and MacOS - the function supports the forward slash and the OS separator (backslash - under windows). - """ - - # translate the path separators - directory = unixpath(directory) - # build a list of all directory elements - aList = filter(lambda x: len(x)>0, directory.split('/')) - theLen = len(aList) - # if the first element is a Windows-style disk drive - # concatenate it with the first directory - if aList[0].endswith(':'): - if theLen > 1: - aList[1] = aList[0] + '/' + aList[1] - del aList[0] - theLen -= 1 - # if the original directory starts at root, - # make sure the first element of the list - # starts at root too - if directory[0] == '/': - aList[0] = '/' + aList[0] - # Now iterate through the list, check if the - # directory exists and if not create it - theDir = '' - for i in range(theLen): - theDir += aList[i] - if not os.path.exists(theDir): - os.mkdir(theDir) - theDir += '/' - -# ----------------------------------------------------------------------------- -# u n i x p a t h ( ) -- Return a path name that contains Unix separator. -- -# ^^^^^^^^^^^^^^^^^^^ -# -def unixpath(thePath) : - r"""Return a path name that contains Unix separator. - - [Example] - >>> unixpath(r"d:\test") - 'd:/test' - >>> unixpath("d:/test/file.txt") - 'd:/test/file.txt' - >>> - """ - thePath = os.path.normpath(thePath) - if os.sep == '/': - return thePath - else: - return thePath.replace(os.sep,'/') - -# ----------------------------------------------------------------------------- - -# S c r i p t e x e c u t i o n -- Runs when invoked from the command line -- -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -# -if __name__ == "__main__": - import getopt # command line parsing - argc = len(sys.argv) - if argc == 1: - printUsage('Missing argument: specify at least one of -m or -p (or both).') - sys.exit(1) - # If there is some arguments, parse the command line - validOptions = "ehmpv" - validLongOptions = ['domain=', 'moTarget='] - option = {} - option['forceEnglish'] = 0 - option['mo'] = 0 - option['po'] = 0 - option['verbose'] = 0 - option['domain'] = None - option['moTarget'] = None - try: - optionList,pargs = getopt.getopt(sys.argv[1:],validOptions,validLongOptions) - except getopt.GetoptError, e: - printUsage(e[0]) - sys.exit(1) - for (opt,val) in optionList: - if (opt == '-h'): - printUsage() - sys.exit(0) - elif (opt == '-e'): option['forceEnglish'] = 1 - elif (opt == '-m'): option['mo'] = 1 - elif (opt == '-p'): option['po'] = 1 - elif (opt == '-v'): option['verbose'] = 1 - elif (opt == '--domain'): option['domain'] = val - elif (opt == '--moTarget'): option['moTarget'] = val - if len(pargs) == 0: - appDirPath = os.getcwd() - if option['verbose']: - print "No project directory given. Using current one: %s" % appDirPath - elif len(pargs) == 1: - appDirPath = pargs[0] - else: - printUsage('Too many arguments (%u). Use double quotes if you have space in directory name' % len(pargs)) - sys.exit(1) - if option['domain'] is None: - # If no domain specified, use the name of the target directory - option['domain'] = fileBaseOf(appDirPath) - if option['verbose']: - print "Application domain used is: '%s'" % option['domain'] - if option['po']: - try: - makePO(appDirPath,option['domain'],option['verbose']) - except IOError, e: - printUsage(e[1] + '\n You must write a file app.fil that contains the list of all files to parse.') - if option['mo']: - makeMO(appDirPath,option['moTarget'],option['domain'],option['verbose'],option['forceEnglish']) - sys.exit(1) - - -# ----------------------------------------------------------------------------- +#! /usr/bin/env python +# -*- coding: iso-8859-1 -*- +# +# PYTHON MODULE: MKI18N.PY +# ========= +# +# Abstract: Make Internationalization (i18n) files for an application. +# +# Copyright Pierre Rouleau. 2003. Released to public domain. +# +# Last update: Saturday, November 8, 2003. @ 15:55:18. +# +# File: ROUP2003N01::C:/dev/python/mki18n.py +# +# RCS $Header: //software/official/MKS/MKS_SI/TV_NT/dev/Python/rcs/mki18n.py 1.5 2003/11/05 19:40:04 PRouleau Exp $ +# +# Update history: +# +# - File created: Saturday, June 7, 2003. by Pierre Rouleau +# - 10/06/03 rcs : RCS Revision 1.1 2003/06/10 10:06:12 PRouleau +# - 10/06/03 rcs : RCS Initial revision +# - 23/08/03 rcs : RCS Revision 1.2 2003/06/10 10:54:27 PRouleau +# - 23/08/03 P.R.: [code:fix] : The strings encoded in this file are encode in iso-8859-1 format. Added the encoding +# notification to Python to comply with Python's 2.3 PEP 263. +# - 23/08/03 P.R.: [feature:new] : Added the '-e' switch which is used to force the creation of the empty English .mo file. +# - 22/10/03 P.R.: [code] : incorporated utility functions in here to make script self sufficient. +# - 05/11/03 rcs : RCS Revision 1.4 2003/10/22 06:39:31 PRouleau +# - 05/11/03 P.R.: [code:fix] : included the unixpath() in this file. +# - 08/11/03 rcs : RCS Revision 1.5 2003/11/05 19:40:04 PRouleau +# +# RCS $Log: $ +# +# +# ----------------------------------------------------------------------------- +""" +mki18n allows you to internationalize your software. You can use it to +create the GNU .po files (Portable Object) and the compiled .mo files +(Machine Object). + +mki18n module can be used from the command line or from within a script (see +the Usage at the end of this page). + + Table of Contents + ----------------- + + makePO() -- Build the Portable Object file for the application -- + catPO() -- Concatenate one or several PO files with the application domain files. -- + makeMO() -- Compile the Portable Object files into the Machine Object stored in the right location. -- + printUsage -- Displays how to use this script from the command line -- + + Scriptexecution -- Runs when invoked from the command line -- + + +NOTE: this module uses GNU gettext utilities. + +You can get the gettext tools from the following sites: + + - `GNU FTP site for gettetx`_ where several versions (0.10.40, 0.11.2, 0.11.5 and 0.12.1) are available. + Note that you need to use `GNU libiconv`_ to use this. Get it from the `GNU + libiconv ftp site`_ and get version 1.9.1 or later. Get the Windows .ZIP + files and install the packages inside c:/gnu. All binaries will be stored + inside c:/gnu/bin. Just put c:/gnu/bin inside your PATH. You will need + the following files: + + - `gettext-runtime-0.12.1.bin.woe32.zip`_ + - `gettext-tools-0.12.1.bin.woe32.zip`_ + - `libiconv-1.9.1.bin.woe32.zip`_ + + +.. _GNU libiconv: http://www.gnu.org/software/libiconv/ +.. _GNU libiconv ftp site: http://www.ibiblio.org/pub/gnu/libiconv/ +.. _gettext-runtime-0.12.1.bin.woe32.zip: ftp://ftp.gnu.org/gnu/gettext/gettext-runtime-0.12.1.bin.woe32.zip +.. _gettext-tools-0.12.1.bin.woe32.zip: ftp://ftp.gnu.org/gnu/gettext/gettext-tools-0.12.1.bin.woe32.zip +.. _libiconv-1.9.1.bin.woe32.zip: http://www.ibiblio.org/pub/gnu/libiconv/libiconv-1.9.1.bin.woe32.zip + +""" +# ----------------------------------------------------------------------------- +# Module Import +# ------------- +# +import os +import sys +import wx +import re + +# ----------------------------------------------------------------------------- +# Global variables +# ---------------- +# + +__author__ = "Pierre Rouleau" +__version__ = "$Revision: 1.5 $" + +# ----------------------------------------------------------------------------- + + +def getlanguageDict(): + languageDict = {} + + if wx.VERSION >= (3, 0, 0): + app = wx.App() + else: + app = wx.PySimpleApp() + + for lang in [x for x in dir(wx) if x.startswith("LANGUAGE")]: + i = wx.Locale(wx.LANGUAGE_DEFAULT).GetLanguageInfo(getattr(wx, lang)) + if i: + languageDict[i.CanonicalName] = i.Description + + return languageDict + + +def verbosePrint(verbose, str): + if verbose: + print str + + +def processCustomFiles(filein, fileout, regexp, prefix=''): + appfil_file = open(filein, 'r') + messages_file = open(fileout, 'r') + messages = messages_file.read() + messages_file.close() + messages_file = open(fileout, 'a') + messages_file.write('\n') + messages_file.write('#: %s\n' % prefix) + messages_file.write('\n') + + words_found = {} + for filepath in appfil_file.xreadlines(): + code_file = open(filepath.strip(), 'r') + for match in regexp.finditer(code_file.read()): + word = match.group(1) + if not words_found.get(word, False) and messages.find("msgid \"%s\"\nmsgstr \"\"" % word) == -1: + words_found[word] = True + messages_file.write('\n') + messages_file.write("msgid \"%s\"\n" % word) + messages_file.write("msgstr \"\"\n") + code_file.close() + + messages_file.close() + appfil_file.close() + + +# ----------------------------------------------------------------------------- +# m a k e P O ( ) -- Build the Portable Object file for the application -- +# ^^^^^^^^^^^^^^^ +# +def makePO(applicationDirectoryPath, applicationDomain=None, verbose=0): + """Build the Portable Object Template file for the application. + + makePO builds the .pot file for the application stored inside + a specified directory by running xgettext for all application source + files. It finds the name of all files by looking for a file called 'app.fil'. + If this file does not exists, makePo raises an IOError exception. + By default the application domain (the application + name) is the same as the directory name but it can be overridden by the + 'applicationDomain' argument. + + makePO always creates a new file called messages.pot. If it finds files + of the form app_xx.po where 'app' is the application name and 'xx' is one + of the ISO 639 two-letter language codes, makePO resynchronizes those + files with the latest extracted strings (now contained in messages.pot). + This process updates all line location number in the language-specific + .po files and may also create new entries for translation (or comment out + some). The .po file is not changed, instead a new file is created with + the .new extension appended to the name of the .po file. + + By default the function does not display what it is doing. Set the + verbose argument to 1 to force it to print its commands. + """ + + if applicationDomain is None: + applicationName = fileBaseOf(applicationDirectoryPath, withPath=0) + else: + applicationName = applicationDomain + currentDir = os.getcwd() + os.chdir(applicationDirectoryPath) + filelist = 'app.fil' + if not os.path.exists(filelist): + raise IOError(2, 'No module file: ' % filelist) + + fileout = 'messages.pot' + # Steps: + # Use xgettext to parse all application modules + # The following switches are used: + # + # -s : sort output by string content (easier to use when we need to merge several .po files) + # --files-from=app.fil : The list of files is taken from the file: app.fil + # --output= : specifies the name of the output file (using a .pot extension) + cmd = 'xgettext -s --no-wrap --language=Python --files-from=' + filelist + ' --output=' + fileout + ' --package-name ' + applicationName + verbosePrint(verbose, cmd) + os.system(cmd) + + XSD_STRING_MODEL = re.compile("]*\>") + processCustomFiles(filelist, fileout, XSD_STRING_MODEL, 'Extra XSD strings') + + XML_TC6_STRING_MODEL = re.compile("\s*\s*", re.MULTILINE | re.DOTALL) + processCustomFiles(filelist, fileout, XML_TC6_STRING_MODEL, 'Extra TC6 documentation strings') + + # generate messages.po + cmd = 'msginit --no-wrap --no-translator -i %s -l en_US.UTF-8 -o messages.po' % (fileout) + verbosePrint(verbose, cmd) + os.system(cmd) + + languageDict = getlanguageDict() + + for langCode in languageDict.keys(): + if langCode == 'en': + pass + else: + langPOfileName = "%s_%s.po" % (applicationName, langCode) + if os.path.exists(langPOfileName): + cmd = 'msgmerge -s --no-wrap "%s" %s > "%s.new"' % (langPOfileName, fileout, langPOfileName) + verbosePrint(verbose, cmd) + os.system(cmd) + os.chdir(currentDir) + + +def catPO(applicationDirectoryPath, listOf_extraPo, applicationDomain=None, targetDir=None, verbose=0): + """Concatenate one or several PO files with the application domain files. + """ + + if applicationDomain is None: + applicationName = fileBaseOf(applicationDirectoryPath, withPath=0) + else: + applicationName = applicationDomain + currentDir = os.getcwd() + os.chdir(applicationDirectoryPath) + + languageDict = getlanguageDict() + + for langCode in languageDict.keys(): + if langCode == 'en': + pass + else: + langPOfileName = "%s_%s.po" % (applicationName, langCode) + if os.path.exists(langPOfileName): + fileList = '' + for fileName in listOf_extraPo: + fileList += ("%s_%s.po " % (fileName, langCode)) + cmd = "msgcat -s --no-wrap %s %s > %s.cat" % (langPOfileName, fileList, langPOfileName) + verbosePrint(verbose, cmd) + os.system(cmd) + if targetDir is None: + pass + else: + mo_targetDir = "%s/%s/LC_MESSAGES" % (targetDir, langCode) + cmd = "msgfmt --output-file=%s/%s.mo %s_%s.po.cat" % (mo_targetDir, applicationName, applicationName, langCode) + verbosePrint(verbose, cmd) + os.system(cmd) + os.chdir(currentDir) + + +def makeMO(applicationDirectoryPath, targetDir='./locale', applicationDomain=None, verbose=0, forceEnglish=0): + """Compile the Portable Object files into the Machine Object stored in the right location. + + makeMO converts all translated language-specific PO files located inside + the application directory into the binary .MO files stored inside the + LC_MESSAGES sub-directory for the found locale files. + + makeMO searches for all files that have a name of the form 'app_xx.po' + inside the application directory specified by the first argument. The + 'app' is the application domain name (that can be specified by the + applicationDomain argument or is taken from the directory name). The 'xx' + corresponds to one of the ISO 639 two-letter language codes. + + makeMo stores the resulting files inside a sub-directory of `targetDir` + called xx/LC_MESSAGES where 'xx' corresponds to the 2-letter language + code. + """ + + if targetDir is None: + targetDir = './locale' + + verbosePrint(verbose, "Target directory for .mo files is: %s" % targetDir) + + if applicationDomain is None: + applicationName = fileBaseOf(applicationDirectoryPath, withPath=0) + else: + applicationName = applicationDomain + currentDir = os.getcwd() + os.chdir(applicationDirectoryPath) + + languageDict = getlanguageDict() + + for langCode in languageDict.keys(): + if (langCode == 'en') and (forceEnglish == 0): + pass + else: + langPOfileName = "%s_%s.po" % (applicationName, langCode) + if os.path.exists(langPOfileName): + mo_targetDir = "%s/%s/LC_MESSAGES" % (targetDir, langCode) + if not os.path.exists(mo_targetDir): + mkdir(mo_targetDir) + cmd = 'msgfmt --output-file="%s/%s.mo" "%s_%s.po"' % (mo_targetDir, applicationName, applicationName, langCode) + verbosePrint(verbose, cmd) + os.system(cmd) + os.chdir(currentDir) + + +def printUsage(errorMsg=None): + """Displays how to use this script from the command line.""" + print """ + ################################################################################## + # mki18n : Make internationalization files. # + # Uses the GNU gettext system to create PO (Portable Object) files # + # from source code, coimpile PO into MO (Machine Object) files. # + # Supports C,C++,Python source files. # + # # + # Usage: mki18n {OPTION} [appDirPath] # + # # + # Options: # + # -e : When -m is used, forces English .mo file creation # + # -h : prints this help # + # -m : make MO from existing PO files # + # -p : make PO, update PO files: Creates a new messages.pot # + # file. Creates a dom_xx.po.new for every existing # + # language specific .po file. ('xx' stands for the ISO639 # + # two-letter language code and 'dom' stands for the # + # application domain name). mki18n requires that you # + # write a 'app.fil' file which contains the list of all # + # source code to parse. # + # -v : verbose (prints comments while running) # + # --domain=appName : specifies the application domain name. By default # + # the directory name is used. # + # --moTarget=dir : specifies the directory where .mo files are stored. # + # If not specified, the target is './locale' # + # # + # You must specify one of the -p or -m option to perform the work. You can # + # specify the path of the target application. If you leave it out mki18n # + # will use the current directory as the application main directory. # + # # + ##################################################################################""" + if errorMsg: + print "\n ERROR: %s" % errorMsg + + +def fileBaseOf(filename, withPath=0): + """fileBaseOf(filename,withPath) ---> string + + Return base name of filename. The returned string never includes the extension. + Use os.path.basename() to return the basename with the extension. The + second argument is optional. If specified and if set to 'true' (non zero) + the string returned contains the full path of the file name. Otherwise the + path is excluded. + + [Example] + >>> fn = 'd:/dev/telepath/tvapp/code/test.html' + >>> fileBaseOf(fn) + 'test' + >>> fileBaseOf(fn) + 'test' + >>> fileBaseOf(fn,1) + 'd:/dev/telepath/tvapp/code/test' + >>> fileBaseOf(fn,0) + 'test' + >>> fn = 'abcdef' + >>> fileBaseOf(fn) + 'abcdef' + >>> fileBaseOf(fn,1) + 'abcdef' + >>> fn = "abcdef." + >>> fileBaseOf(fn) + 'abcdef' + >>> fileBaseOf(fn,1) + 'abcdef' + """ + pos = filename.rfind('.') + if pos > 0: + filename = filename[:pos] + if withPath: + return filename + else: + return os.path.basename(filename) + + +def mkdir(directory): + """Create a directory (and possibly the entire tree). + + The os.mkdir() will fail to create a directory if one of the + directory in the specified path does not exist. mkdir() + solves this problem. It creates every intermediate directory + required to create the final path. Under Unix, the function + only supports forward slash separator, but under Windows and MacOS + the function supports the forward slash and the OS separator (backslash + under windows). + """ + + # translate the path separators + directory = unixpath(directory) + # build a list of all directory elements + aList = filter(lambda x: len(x) > 0, directory.split('/')) + theLen = len(aList) + # if the first element is a Windows-style disk drive + # concatenate it with the first directory + if aList[0].endswith(':'): + if theLen > 1: + aList[1] = aList[0] + '/' + aList[1] + del aList[0] + theLen -= 1 + # if the original directory starts at root, + # make sure the first element of the list + # starts at root too + if directory[0] == '/': + aList[0] = '/' + aList[0] + # Now iterate through the list, check if the + # directory exists and if not create it + theDir = '' + for i in range(theLen): + theDir += aList[i] + if not os.path.exists(theDir): + os.mkdir(theDir) + theDir += '/' + + +def unixpath(thePath): + r"""Return a path name that contains Unix separator. + + [Example] + >>> unixpath(r"d:\test") + 'd:/test' + >>> unixpath("d:/test/file.txt") + 'd:/test/file.txt' + >>> + """ + thePath = os.path.normpath(thePath) + if os.sep == '/': + return thePath + else: + return thePath.replace(os.sep, '/') + + +# ----------------------------------------------------------------------------- + +# S c r i p t e x e c u t i o n -- Runs when invoked from the command line -- +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# +if __name__ == "__main__": + import getopt # command line parsing + argc = len(sys.argv) + if argc == 1: + printUsage('Missing argument: specify at least one of -m or -p (or both).') + sys.exit(1) + # If there is some arguments, parse the command line + validOptions = "ehmpv" + validLongOptions = ['domain=', 'moTarget='] + option = {} + option['forceEnglish'] = 0 + option['mo'] = 0 + option['po'] = 0 + option['verbose'] = 0 + option['domain'] = None + option['moTarget'] = None + optionKey = { + '-e': 'forceEnglish', + '-m': 'mo', + '-p': 'po', + '-v': 'verbose', + '--domain': 'domain', + '--moTarget': 'moTarget', + } + + try: + optionList, pargs = getopt.getopt(sys.argv[1:], validOptions, validLongOptions) + except getopt.GetoptError, e: + printUsage(e[0]) + sys.exit(1) + for (opt, val) in optionList: + if (opt == '-h'): + printUsage() + sys.exit(0) + option[optionKey[opt]] = 1 if val == '' else val + if len(pargs) == 0: + appDirPath = os.getcwd() + if option['verbose']: + print "No project directory given. Using current one: %s" % appDirPath + elif len(pargs) == 1: + appDirPath = pargs[0] + else: + printUsage('Too many arguments (%u). Use double quotes if you have space in directory name' % len(pargs)) + sys.exit(1) + if option['domain'] is None: + # If no domain specified, use the name of the target directory + option['domain'] = fileBaseOf(appDirPath) + if option['verbose']: + print "Application domain used is: '%s'" % option['domain'] + if option['po']: + try: + makePO(appDirPath, option['domain'], option['verbose']) + except IOError, e: + printUsage(e[1] + '\n You must write a file app.fil that contains the list of all files to parse.') + if option['mo']: + makeMO(appDirPath, option['moTarget'], option['domain'], option['verbose'], option['forceEnglish']) + sys.exit(1) + + +# ----------------------------------------------------------------------------- diff -r 31e63e25b4cc -r 64beb9e9c749 plcopen/__init__.py --- a/plcopen/__init__.py Mon Aug 21 20:17:19 2017 +0000 +++ b/plcopen/__init__.py Mon Aug 21 23:22:58 2017 +0300 @@ -28,5 +28,3 @@ from plcopen import PLCOpenParser, LoadProject, SaveProject, LoadPou, \ LoadPouInstances, VarOrder, QualifierList, rect - - diff -r 31e63e25b4cc -r 64beb9e9c749 plcopen/definitions.py --- a/plcopen/definitions.py Mon Aug 21 20:17:19 2017 +0000 +++ b/plcopen/definitions.py Mon Aug 21 23:22:58 2017 +0300 @@ -26,29 +26,31 @@ from os.path import join, split, realpath import util.paths as paths +from util.TranslationCatalogs import NoTranslate sd = paths.AbsDir(__file__) # Override gettext _ in this module # since we just want string to be added to dictionnary # but translation should happen here -_ = lambda x:x +_ = NoTranslate -LANGUAGES = ["IL","ST","FBD","LD","SFC"] +LANGUAGES = ["IL", "ST", "FBD", "LD", "SFC"] -LOCATIONDATATYPES = {"X" : ["BOOL"], - "B" : ["SINT", "USINT", "BYTE", "STRING"], - "W" : ["INT", "UINT", "WORD", "WSTRING"], - "D" : ["DINT", "UDINT", "REAL", "DWORD"], - "L" : ["LINT", "ULINT", "LREAL", "LWORD"]} +LOCATIONDATATYPES = {"X": ["BOOL"], + "B": ["SINT", "USINT", "BYTE", "STRING"], + "W": ["INT", "UINT", "WORD", "WSTRING"], + "D": ["DINT", "UDINT", "REAL", "DWORD"], + "L": ["LINT", "ULINT", "LREAL", "LWORD"]} -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Function Block Types definitions -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -StdTC6Libs = [(_("Standard function blocks"), join(sd, "Standard_Function_Blocks.xml")), - (_("Additional function blocks"),join(sd, "Additional_Function_Blocks.xml"))] +StdTC6Libs = [(_("Standard function blocks"), join(sd, "Standard_Function_Blocks.xml")), + (_("Additional function blocks"), join(sd, "Additional_Function_Blocks.xml"))] -StdFuncsCSV = join(sd,"iec_std.csv") +StdFuncsCSV = join(sd, "iec_std.csv") + def GetBlockInfos(pou): infos = pou.getblockInfos() @@ -59,9 +61,10 @@ for var_name, var_type, var_modifier in infos["inputs"]] return infos -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Data Types definitions -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + """ Ordored list of common data types defined in the IEC 61131-3 @@ -102,45 +105,45 @@ ("WORD", "ANY_NBIT"), ("DWORD", "ANY_NBIT"), ("LWORD", "ANY_NBIT") - #("WSTRING", "ANY_STRING") # TODO + # ("WSTRING", "ANY_STRING") # TODO ] DefaultType = "DINT" DataTypeRange_list = [ - ("SINT", (-2**7, 2**7 - 1)), - ("INT", (-2**15, 2**15 - 1)), - ("DINT", (-2**31, 2**31 - 1)), - ("LINT", (-2**31, 2**31 - 1)), - ("USINT", (0, 2**8 - 1)), - ("UINT", (0, 2**16 - 1)), - ("UDINT", (0, 2**31 - 1)), - ("ULINT", (0, 2**31 - 1)) + ("SINT", (-2**7, 2**7 - 1)), + ("INT", (-2**15, 2**15 - 1)), + ("DINT", (-2**31, 2**31 - 1)), + ("LINT", (-2**31, 2**31 - 1)), + ("USINT", (0, 2**8 - 1)), + ("UINT", (0, 2**16 - 1)), + ("UDINT", (0, 2**31 - 1)), + ("ULINT", (0, 2**31 - 1)) ] ANY_TO_ANY_FILTERS = { - "ANY_TO_ANY":[ + "ANY_TO_ANY": [ # simple type conv are let as C cast - (("ANY_INT","ANY_BIT"),("ANY_NUM","ANY_BIT")), - (("ANY_REAL",),("ANY_REAL",)), + (("ANY_INT", "ANY_BIT"), ("ANY_NUM", "ANY_BIT")), + (("ANY_REAL",), ("ANY_REAL",)), # REAL_TO_INT - (("ANY_REAL",),("ANY_SINT",)), - (("ANY_REAL",),("ANY_UINT",)), - (("ANY_REAL",),("ANY_BIT",)), + (("ANY_REAL",), ("ANY_SINT",)), + (("ANY_REAL",), ("ANY_UINT",)), + (("ANY_REAL",), ("ANY_BIT",)), # TO_TIME - (("ANY_INT","ANY_BIT"),("ANY_DATE","TIME")), - (("ANY_REAL",),("ANY_DATE","TIME")), - (("ANY_STRING",), ("ANY_DATE","TIME")), + (("ANY_INT", "ANY_BIT"), ("ANY_DATE", "TIME")), + (("ANY_REAL",), ("ANY_DATE", "TIME")), + (("ANY_STRING",), ("ANY_DATE", "TIME")), # FROM_TIME - (("ANY_DATE","TIME"), ("ANY_REAL",)), - (("ANY_DATE","TIME"), ("ANY_INT","ANY_NBIT")), + (("ANY_DATE", "TIME"), ("ANY_REAL",)), + (("ANY_DATE", "TIME"), ("ANY_INT", "ANY_NBIT")), (("TIME",), ("ANY_STRING",)), (("DATE",), ("ANY_STRING",)), - (("TOD",), ("ANY_STRING",)), - (("DT",), ("ANY_STRING",)), + (("TOD",), ("ANY_STRING",)), + (("DT",), ("ANY_STRING",)), # TO_STRING - (("BOOL",), ("ANY_STRING",)), - (("ANY_BIT",), ("ANY_STRING",)), + (("BOOL",), ("ANY_STRING",)), + (("ANY_BIT",), ("ANY_STRING",)), (("ANY_REAL",), ("ANY_STRING",)), (("ANY_SINT",), ("ANY_STRING",)), (("ANY_UINT",), ("ANY_STRING",)), @@ -149,17 +152,20 @@ (("ANY_STRING",), ("ANY_BIT",)), (("ANY_STRING",), ("ANY_SINT",)), (("ANY_STRING",), ("ANY_UINT",)), - (("ANY_STRING",), ("ANY_REAL",))], - "BCD_TO_ANY":[ - (("BYTE",),("USINT",)), - (("WORD",),("UINT",)), - (("DWORD",),("UDINT",)), - (("LWORD",),("ULINT",))], - "ANY_TO_BCD":[ - (("USINT",),("BYTE",)), - (("UINT",),("WORD",)), - (("UDINT",),("DWORD",)), - (("ULINT",),("LWORD",))] + (("ANY_STRING",), ("ANY_REAL",)) + ], + "BCD_TO_ANY": [ + (("BYTE",), ("USINT",)), + (("WORD",), ("UINT",)), + (("DWORD",), ("UDINT",)), + (("LWORD",), ("ULINT",)) + ], + "ANY_TO_BCD": [ + (("USINT",), ("BYTE",)), + (("UINT",), ("WORD",)), + (("UDINT",), ("DWORD",)), + (("ULINT",), ("LWORD",)) + ] } # remove gettext override diff -r 31e63e25b4cc -r 64beb9e9c749 plcopen/plcopen.py --- a/plcopen/plcopen.py Mon Aug 21 20:17:19 2017 +0000 +++ b/plcopen/plcopen.py Mon Aug 21 23:22:58 2017 +0300 @@ -25,7 +25,8 @@ from xmlclass import * from types import * -import os, re +import os +import re from lxml import etree from collections import OrderedDict import util.paths as paths @@ -33,30 +34,39 @@ """ Dictionary that makes the relation between var names in plcopen and displayed values """ -VarTypes = {"Local" : "localVars", "Temp" : "tempVars", "Input" : "inputVars", - "Output" : "outputVars", "InOut" : "inOutVars", "External" : "externalVars", - "Global" : "globalVars", "Access" : "accessVars"} +VarTypes = { + "Local": "localVars", + "Temp": "tempVars", + "Input": "inputVars", + "Output": "outputVars", + "InOut": "inOutVars", + "External": "externalVars", + "Global": "globalVars", + "Access": "accessVars" +} searchResultVarTypes = { - "inputVars": "var_input", + "inputVars": "var_input", "outputVars": "var_output", - "inOutVars": "var_inout" + "inOutVars": "var_inout" } """ Define in which order var types must be displayed """ -VarOrder = ["Local","Temp","Input","Output","InOut","External","Global","Access"] +VarOrder = ["Local", "Temp", "Input", "Output", "InOut", "External", "Global", "Access"] """ -Define which action qualifier must be associated with a duration +Define which action qualifier must be associated with a duration """ -QualifierList = OrderedDict([("N", False), ("R", False), ("S", False), - ("L", True), ("D", True), ("P", False), ("P0", False), +QualifierList = OrderedDict([ + ("N", False), ("R", False), ("S", False), + ("L", True), ("D", True), ("P", False), ("P0", False), ("P1", False), ("SD", True), ("DS", True), ("SL", True)]) -FILTER_ADDRESS_MODEL = "(%%[IQM](?:[XBWDL])?)(%s)((?:\.[0-9]+)*)" +FILTER_ADDRESS_MODEL = "(%%[IQM](?:[XBWDL])?)(%s)((?:\.[0-9]+)*)" + def update_address(address, address_model, new_leading): result = address_model.match(address) @@ -65,6 +75,7 @@ groups = result.groups() return groups[0] + new_leading + groups[2] + def _init_and_compare(function, v1, v2): if v1 is None: return v2 @@ -72,11 +83,12 @@ return function(v1, v2) return v1 -""" -Helper class for bounding_box calculation -""" + class rect: - + """ + Helper class for bounding_box calculation + """ + def __init__(self, x=None, y=None, width=None, height=None): self.x_min = x self.x_max = None @@ -86,19 +98,19 @@ self.x_max = x + width if height is not None and y is not None: self.y_max = y + height - + def update(self, x, y): self.x_min = _init_and_compare(min, self.x_min, x) self.x_max = _init_and_compare(max, self.x_max, x) self.y_min = _init_and_compare(min, self.y_min, y) self.y_max = _init_and_compare(max, self.y_max, y) - + def union(self, rect): self.x_min = _init_and_compare(min, self.x_min, rect.x_min) self.x_max = _init_and_compare(max, self.x_max, rect.x_max) self.y_min = _init_and_compare(min, self.y_min, rect.y_min) self.y_max = _init_and_compare(max, self.y_max, rect.y_max) - + def bounding_box(self): width = height = None if self.x_min is not None and self.x_max is not None: @@ -107,12 +119,14 @@ height = self.y_max - self.y_min return self.x_min, self.y_min, width, height + def TextLenInRowColumn(text): if text == "": return (0, 0) lines = text.split("\n") return len(lines) - 1, len(lines[-1]) + def CompilePattern(criteria): flag = 0 if criteria["case_sensitive"] else re.IGNORECASE find_pattern = criteria["find_pattern"] @@ -120,32 +134,39 @@ find_pattern = re.escape(find_pattern) criteria["pattern"] = re.compile(find_pattern, flag) + def TestTextElement(text, criteria): lines = text.splitlines() test_result = [] result = criteria["pattern"].search(text) while result is not None: - prev_pos=result.endpos + prev_pos = result.endpos start = TextLenInRowColumn(text[:result.start()]) end = TextLenInRowColumn(text[:result.end() - 1]) test_result.append((start, end, "\n".join(lines[start[0]:end[0] + 1]))) result = criteria["pattern"].search(text, result.end()) - if result is not None and prev_pos==result.endpos: + if result is not None and prev_pos == result.endpos: break return test_result + def TextMatched(str1, str2): return str1 and str2 and (str1.upper() == str2.upper()) + PLCOpenParser = GenerateParserFromXSD(paths.AbsNeighbourFile(__file__, "tc6_xml_v201.xsd")) -PLCOpen_XPath = lambda xpath: etree.XPath(xpath, namespaces=PLCOpenParser.NSMAP) + + +def PLCOpen_XPath(xpath): + return etree.XPath(xpath, namespaces=PLCOpenParser.NSMAP) + LOAD_POU_PROJECT_TEMPLATE = """ - - @@ -164,6 +185,7 @@ """ + def LOAD_POU_INSTANCES_PROJECT_TEMPLATE(body_type): return LOAD_POU_PROJECT_TEMPLATE % """ @@ -172,12 +194,13 @@ """ % locals() + PLCOpen_v1_file = open(paths.AbsNeighbourFile(__file__, "TC6_XML_V10_B.xsd")) PLCOpen_v1_xml = PLCOpen_v1_file.read() PLCOpen_v1_file.close() PLCOpen_v1_xml = PLCOpen_v1_xml.replace( - "http://www.plcopen.org/xml/tc6.xsd", - "http://www.plcopen.org/xml/tc6_0201") + "http://www.plcopen.org/xml/tc6.xsd", + "http://www.plcopen.org/xml/tc6_0201") PLCOpen_v1_xsd = etree.XMLSchema(etree.fromstring(PLCOpen_v1_xml)) # XPath for file compatibility process @@ -188,23 +211,24 @@ ActionBlocksXPath = PLCOpen_XPath("ppx:types/ppx:pous/ppx:pou/ppx:body/*/ppx:actionBlock") ActionBlocksConnectionPointOutXPath = PLCOpen_XPath("ppx:connectionPointOut") + def LoadProjectXML(project_xml): project_xml = project_xml.replace( - "http://www.plcopen.org/xml/tc6.xsd", + "http://www.plcopen.org/xml/tc6.xsd", "http://www.plcopen.org/xml/tc6_0201") for cre, repl in [ - (re.compile("(?)(?:)(?!)"), "]]>")]: + (re.compile("(?)(?:)(?!)"), "]]>")]: project_xml = cre.sub(repl, project_xml) - + try: tree, error = PLCOpenParser.LoadXMLString(project_xml) if error is None: return tree, None - + if PLCOpen_v1_xsd.validate(tree): # Make file compatible with PLCOpen v2 - + # Update resource interval value for resource in ProjectResourcesXPath(tree): for task in resource.gettask(): @@ -218,24 +242,24 @@ time_values.extend([int(seconds), int((seconds % 1) * 1000000)]) text = "T#" if time_values[0] != 0: - text += "%dh"%time_values[0] + text += "%dh" % time_values[0] if time_values[1] != 0: - text += "%dm"%time_values[1] + text += "%dm" % time_values[1] if time_values[2] != 0: - text += "%ds"%time_values[2] + text += "%ds" % time_values[2] if time_values[3] != 0: if time_values[3] % 1000 != 0: - text += "%.3fms"%(float(time_values[3]) / 1000) + text += "%.3fms" % (float(time_values[3]) / 1000) else: - text += "%dms"%(time_values[3] / 1000) + text += "%dms" % (time_values[3] / 1000) task.set("interval", text) - + # Update resources pou instance attributes for pouInstance in ResourceInstancesXpath(resource): type_name = pouInstance.attrib.pop("type") if type_name is not None: pouInstance.set("typeName", type_name) - + # Update transitions condition for transition_condition in TransitionsConditionXPath(tree): connections = ConditionConnectionsXPath(transition_condition) @@ -245,55 +269,64 @@ connectionPointIn.setrelPositionXY(0, 0) for connection in connections: connectionPointIn.append(connection) - + # Update actionBlocks for actionBlock in ActionBlocksXPath(tree): for connectionPointOut in ActionBlocksConnectionPointOutXPath(actionBlock): actionBlock.remove(connectionPointOut) - + for action in actionBlock.getaction(): action.set("localId", "0") relPosition = PLCOpenParser.CreateElement("relPosition", "action") relPosition.set("x", "0") relPosition.set("y", "0") action.setrelPosition(relPosition) - + return tree, None - + return tree, error - + except Exception, e: return None, e.message + def LoadProject(filepath): project_file = open(filepath) project_xml = project_file.read() project_file.close() return LoadProjectXML(project_xml) + project_pou_xpath = PLCOpen_XPath("/ppx:project/ppx:types/ppx:pous/ppx:pou") + + def LoadPou(xml_string): root, error = LoadProjectXML(LOAD_POU_PROJECT_TEMPLATE % xml_string) return project_pou_xpath(root)[0], error + project_pou_instances_xpath = { body_type: PLCOpen_XPath( "/ppx:project/ppx:types/ppx:pous/ppx:pou[@name='paste_pou']/ppx:body/ppx:%s/*" % body_type) for body_type in ["FBD", "LD", "SFC"]} + + def LoadPouInstances(xml_string, body_type): root, error = LoadProjectXML( LOAD_POU_INSTANCES_PROJECT_TEMPLATE(body_type) % xml_string) return project_pou_instances_xpath[body_type](root), error + def SaveProject(project, filepath): project_file = open(filepath, 'w') project_file.write(etree.tostring( - project, - pretty_print=True, - xml_declaration=True, + project, + pretty_print=True, + xml_declaration=True, encoding='utf-8')) project_file.close() + cls = PLCOpenParser.GetElementClass("formattedText") if cls: def updateElementName(self, old_name, new_name): @@ -302,7 +335,7 @@ text = pattern.sub(new_name, text) self.setanyText(text) setattr(cls, "updateElementName", updateElementName) - + def updateElementAddress(self, address_model, new_leading): text = self.getanyText() startpos = 0 @@ -315,28 +348,28 @@ result = address_model.search(text, startpos) self.setanyText(text) setattr(cls, "updateElementAddress", updateElementAddress) - + def hasblock(self, block_type): - text = self.getanyText() + text = self.getanyText() pattern = re.compile('\\b' + block_type + '\\b', re.IGNORECASE) return pattern.search(text) is not None setattr(cls, "hasblock", hasblock) - + def Search(self, criteria, parent_infos): return [(tuple(parent_infos),) + result for result in TestTextElement(self.getanyText(), criteria)] setattr(cls, "Search", Search) - + cls = PLCOpenParser.GetElementClass("project") if cls: - + def setname(self, name): self.contentHeader.setname(name) setattr(cls, "setname", setname) - + def getname(self): return self.contentHeader.getname() setattr(cls, "getname", getname) - + def getfileHeader(self): fileheader_obj = self.fileHeader return { @@ -351,7 +384,7 @@ ("contentDescription", fileheader_obj.getcontentDescription())] } setattr(cls, "getfileHeader", getfileHeader) - + def setfileHeader(self, fileheader): fileheader_obj = self.fileHeader for attr in ["companyName", "companyURL", "productName", @@ -361,7 +394,7 @@ if value is not None: setattr(fileheader_obj, attr, value) setattr(cls, "setfileHeader", setfileHeader) - + def getcontentHeader(self): contentheader_obj = self.contentHeader contentheader = { @@ -378,7 +411,7 @@ contentheader["scaling"] = self.contentHeader.getscaling() return contentheader setattr(cls, "getcontentHeader", getcontentHeader) - + def setcontentHeader(self, contentheader): contentheader_obj = self.contentHeader for attr, value in contentheader.iteritems(): @@ -392,74 +425,78 @@ elif attr in ["modificationDateTime", "organization", "language"]: setattr(contentheader_obj, attr, value) setattr(cls, "setcontentHeader", setcontentHeader) - + def gettypeElementFunc(element_type): elements_xpath = PLCOpen_XPath( "ppx:types/ppx:%(element_type)ss/ppx:%(element_type)s[@name=$name]" % locals()) + def gettypeElement(self, name): elements = elements_xpath(self, name=name) if len(elements) == 1: return elements[0] return None return gettypeElement - + datatypes_xpath = PLCOpen_XPath("ppx:types/ppx:dataTypes/ppx:dataType") filtered_datatypes_xpath = PLCOpen_XPath( "ppx:types/ppx:dataTypes/ppx:dataType[@name!=$exclude]") + def getdataTypes(self, exclude=None): if exclude is not None: return filtered_datatypes_xpath(self, exclude=exclude) return datatypes_xpath(self) setattr(cls, "getdataTypes", getdataTypes) - + setattr(cls, "getdataType", gettypeElementFunc("dataType")) - + def appenddataType(self, name): if self.getdataType(name) is not None: - raise ValueError, "\"%s\" Data Type already exists !!!"%name + raise ValueError("\"%s\" Data Type already exists !!!" % name) self.types.appenddataTypeElement(name) setattr(cls, "appenddataType", appenddataType) - + def insertdataType(self, index, datatype): self.types.insertdataTypeElement(index, datatype) setattr(cls, "insertdataType", insertdataType) - + def removedataType(self, name): self.types.removedataTypeElement(name) setattr(cls, "removedataType", removedataType) - + def getpous(self, exclude=None, filter=[]): return self.xpath( - "ppx:types/ppx:pous/ppx:pou%s%s" % - (("[@name!='%s']" % exclude) if exclude is not None else '', - ("[%s]" % " or ".join( - map(lambda x: "@pouType='%s'" % x, filter))) - if len(filter) > 0 else ""), + "ppx:types/ppx:pous/ppx:pou%s%s" % + (("[@name!='%s']" % exclude) if exclude is not None else '', + ("[%s]" % " or ".join( + map(lambda x: "@pouType='%s'" % x, filter))) + if len(filter) > 0 else ""), namespaces=PLCOpenParser.NSMAP) setattr(cls, "getpous", getpous) - + setattr(cls, "getpou", gettypeElementFunc("pou")) - + def appendpou(self, name, pou_type, body_type): self.types.appendpouElement(name, pou_type, body_type) setattr(cls, "appendpou", appendpou) - + def insertpou(self, index, pou): self.types.insertpouElement(index, pou) setattr(cls, "insertpou", insertpou) - + def removepou(self, name): self.types.removepouElement(name) setattr(cls, "removepou", removepou) configurations_xpath = PLCOpen_XPath( "ppx:instances/ppx:configurations/ppx:configuration") + def getconfigurations(self): return configurations_xpath(self) setattr(cls, "getconfigurations", getconfigurations) configuration_xpath = PLCOpen_XPath( "ppx:instances/ppx:configurations/ppx:configuration[@name=$name]") + def getconfiguration(self, name): configurations = configuration_xpath(self, name=name) if len(configurations) == 1: @@ -469,21 +506,22 @@ def addconfiguration(self, name): if self.getconfiguration(name) is not None: - raise ValueError, _("\"%s\" configuration already exists !!!") % name + raise ValueError(_("\"%s\" configuration already exists !!!") % name) new_configuration = PLCOpenParser.CreateElement("configuration", "configurations") new_configuration.setname(name) self.instances.configurations.appendconfiguration(new_configuration) - setattr(cls, "addconfiguration", addconfiguration) + setattr(cls, "addconfiguration", addconfiguration) def removeconfiguration(self, name): configuration = self.getconfiguration(name) if configuration is None: - raise ValueError, ("\"%s\" configuration doesn't exist !!!") % name + raise ValueError(_("\"%s\" configuration doesn't exist !!!") % name) self.instances.configurations.remove(configuration) setattr(cls, "removeconfiguration", removeconfiguration) - + resources_xpath = PLCOpen_XPath( "ppx:instances/ppx:configurations/ppx:configuration[@name=$configname]/ppx:resource[@name=$name]") + def getconfigurationResource(self, config_name, name): resources = resources_xpath(self, configname=config_name, name=name) if len(resources) == 1: @@ -493,8 +531,10 @@ def addconfigurationResource(self, config_name, name): if self.getconfigurationResource(config_name, name) is not None: - msg = _("\"{a1}\" resource already exists in \"{a2}\" configuration !!!").format(a1 = name, a2 = config_name) - raise ValueError, msg + raise ValueError( + _("\"{a1}\" resource already exists in \"{a2}\" configuration !!!"). + format(a1=name, a2=config_name)) + configuration = self.getconfiguration(config_name) if configuration is not None: new_resource = PLCOpenParser.CreateElement("resource", "configuration") @@ -511,8 +551,10 @@ configuration.remove(resource) found = True if not found: - msg = _("\"{a1}\" resource doesn't exist in \"{a2}\" configuration !!!").format(a1 = name, a2 = config_name) - raise ValueError, msg + raise ValueError( + _("\"{a1}\" resource doesn't exist in \"{a2}\" configuration !!!"). + format(a1=name, a2=config_name)) + setattr(cls, "removeconfigurationResource", removeconfigurationResource) def updateElementName(self, old_name, new_name): @@ -549,6 +591,7 @@ enumerated_values_xpath = PLCOpen_XPath( "ppx:types/ppx:dataTypes/ppx:dataType/ppx:baseType/ppx:enum/ppx:values/ppx:value") + def GetEnumeratedDataTypeValues(self): return [value.getname() for value in enumerated_values_xpath(self)] setattr(cls, "GetEnumeratedDataTypeValues", GetEnumeratedDataTypeValues) @@ -562,11 +605,11 @@ cls = PLCOpenParser.GetElementClass("contentHeader", "project") if cls: - + def setpageSize(self, width, height): self.coordinateInfo.setpageSize(width, height) setattr(cls, "setpageSize", setpageSize) - + def getpageSize(self): return self.coordinateInfo.getpageSize() setattr(cls, "getpageSize", getpageSize) @@ -575,7 +618,7 @@ for language, (x, y) in scaling.items(): self.coordinateInfo.setscaling(language, x, y) setattr(cls, "setscaling", setscaling) - + def getscaling(self): scaling = {} scaling["FBD"] = self.coordinateInfo.getscaling("FBD") @@ -595,7 +638,7 @@ self.pageSize.setx(width) self.pageSize.sety(height) setattr(cls, "setpageSize", setpageSize) - + def getpageSize(self): if self.pageSize is not None: return self.pageSize.getx(), self.pageSize.gety() @@ -613,7 +656,7 @@ self.sfc.scaling.setx(x) self.sfc.scaling.sety(y) setattr(cls, "setscaling", setscaling) - + def getscaling(self, language): if language == "FBD": return self.fbd.scaling.getx(), self.fbd.scaling.gety() @@ -624,6 +667,7 @@ return 0, 0 setattr(cls, "getscaling", getscaling) + def _Search(attributes, criteria, parent_infos): search_result = [] for attr, value in attributes: @@ -631,6 +675,7 @@ search_result.extend([(tuple(parent_infos + [attr]),) + result for result in TestTextElement(value, criteria)]) return search_result + def _updateConfigurationResourceElementName(self, old_name, new_name): for varlist in self.getglobalVars(): for var in varlist.getvariable(): @@ -641,6 +686,7 @@ if TextMatched(var.getname(), old_name): var.setname(new_name) + def _updateConfigurationResourceElementAddress(self, address_model, new_leading): for varlist in self.getglobalVars(): for var in varlist.getvariable(): @@ -648,6 +694,7 @@ if var_address is not None: var.setaddress(update_address(var_address, address_model, new_leading)) + def _removeConfigurationResourceVariableByAddress(self, address): for varlist in self.getglobalVars(): variables = varlist.getvariable() @@ -655,6 +702,7 @@ if variables[i].getaddress() == address: variables.remove(variables[i]) + def _removeConfigurationResourceVariableByFilter(self, address_model): for varlist in self.getglobalVars(): variables = varlist.getvariable() @@ -665,6 +713,7 @@ if result is not None: variables.remove(variables[i]) + def _SearchInConfigurationResource(self, criteria, parent_infos=[]): search_result = _Search([("name", self.getname())], criteria, parent_infos) var_number = 0 @@ -683,9 +732,10 @@ var_number += 1 return search_result + cls = PLCOpenParser.GetElementClass("configuration", "configurations") if cls: - + def addglobalVar(self, var_type, name, location="", description=""): globalvars = self.getglobalVars() if len(globalvars) == 0: @@ -701,7 +751,7 @@ var.setdocumentation(ft) globalvars[-1].appendvariable(var) setattr(cls, "addglobalVar", addglobalVar) - + def updateElementName(self, old_name, new_name): _updateConfigurationResourceElementName(self, old_name, new_name) for resource in self.getresource(): @@ -727,7 +777,7 @@ search_result.extend(resource.Search(criteria, parent_infos)) return search_result setattr(cls, "Search", Search) - + cls = PLCOpenParser.GetElementClass("resource", "configuration") if cls: def updateElementName(self, old_name, new_name): @@ -788,7 +838,7 @@ setattr(cls, "updateElementAddress", updateElementAddress) def Search(self, criteria, parent_infos=[]): - return _Search([("single", self.getsingle()), + return _Search([("single", self.getsingle()), ("interval", self.getinterval()), ("priority", str(self.getpriority()))], criteria, parent_infos) @@ -802,7 +852,7 @@ setattr(cls, "updateElementName", updateElementName) def Search(self, criteria, parent_infos=[]): - return _Search([("name", self.getname()), + return _Search([("name", self.getname()), ("type", self.gettypeName())], criteria, parent_infos) setattr(cls, "Search", Search) @@ -822,22 +872,22 @@ elif vartype_content_name == "array": base_type = vartype_content.baseType.getcontent() base_type_name = base_type.getLocalTag() - # Array derived directly from a user defined type + # Array derived directly from a user defined type if base_type_name == "derived": basetype_name = base_type.getname() - # Array derived directly from a string type + # Array derived directly from a string type elif base_type_name in ["string", "wstring"]: basetype_name = base_type_name.upper() - # Array derived directly from an elementary type + # Array derived directly from an elementary type else: basetype_name = base_type_name - return "ARRAY [%s] OF %s" % (",".join(map(lambda x : "%s..%s" % (x.getlower(), x.getupper()), vartype_content.getdimension())), basetype_name) + return "ARRAY [%s] OF %s" % (",".join(map(lambda x: "%s..%s" % (x.getlower(), x.getupper()), vartype_content.getdimension())), basetype_name) # Variable type is an elementary type return vartype_content_name setattr(cls, "gettypeAsText", gettypeAsText) - + def Search(self, criteria, parent_infos=[]): - search_result = _Search([("name", self.getname()), + search_result = _Search([("name", self.getname()), ("type", self.gettypeAsText()), ("location", self.getaddress())], criteria, parent_infos) @@ -855,7 +905,7 @@ def getdataTypeElements(self): return self.dataTypes.getdataType() setattr(cls, "getdataTypeElements", getdataTypeElements) - + def getdataTypeElement(self, name): elements = self.dataTypes.getdataType() for element in elements: @@ -870,11 +920,11 @@ new_datatype.setname(name) new_datatype.baseType.setcontent(PLCOpenParser.CreateElement("BOOL", "dataType")) setattr(cls, "appenddataTypeElement", appenddataTypeElement) - + def insertdataTypeElement(self, index, dataType): self.dataTypes.insertdataType(index, dataType) setattr(cls, "insertdataTypeElement", insertdataTypeElement) - + def removedataTypeElement(self, name): found = False for element in self.dataTypes.getdataType(): @@ -883,13 +933,13 @@ found = True break if not found: - raise ValueError, _("\"%s\" Data Type doesn't exist !!!")%name + raise ValueError(_("\"%s\" Data Type doesn't exist !!!") % name) setattr(cls, "removedataTypeElement", removedataTypeElement) - + def getpouElements(self): return self.pous.getpou() setattr(cls, "getpouElements", getpouElements) - + def getpouElement(self, name): elements = self.pous.getpou() for element in elements: @@ -901,7 +951,7 @@ def appendpouElement(self, name, pou_type, body_type): for element in self.pous.getpou(): if TextMatched(element.getname(), name): - raise ValueError, _("\"%s\" POU already exists !!!")%name + raise ValueError(_("\"%s\" POU already exists !!!") % name) new_pou = PLCOpenParser.CreateElement("pou", "pous") self.pous.appendpou(new_pou) new_pou.setname(name) @@ -909,11 +959,11 @@ new_pou.appendbody(PLCOpenParser.CreateElement("body", "pou")) new_pou.setbodyType(body_type) setattr(cls, "appendpouElement", appendpouElement) - + def insertpouElement(self, index, pou): self.pous.insertpou(index, pou) setattr(cls, "insertpouElement", insertpouElement) - + def removepouElement(self, name): found = False for element in self.pous.getpou(): @@ -922,7 +972,7 @@ found = True break if not found: - raise ValueError, _("\"%s\" POU doesn't exist !!!")%name + raise ValueError(_("\"%s\" POU doesn't exist !!!") % name) setattr(cls, "removepouElement", removepouElement) def Search(self, criteria, parent_infos=[]): @@ -935,13 +985,15 @@ return search_result setattr(cls, "Search", Search) + def _updateBaseTypeElementName(self, old_name, new_name): self.baseType.updateElementName(old_name, new_name) + cls = PLCOpenParser.GetElementClass("dataType", "dataTypes") if cls: setattr(cls, "updateElementName", _updateBaseTypeElementName) - + def Search(self, criteria, parent_infos=[]): search_result = [] filter = criteria["filter"] @@ -956,7 +1008,7 @@ cls = PLCOpenParser.GetElementClass("dataType") if cls: - + def updateElementName(self, old_name, new_name): content_name = self.content.getLocalTag() if content_name in ["derived", "array", "subrangeSigned", "subrangeUnsigned"]: @@ -987,7 +1039,7 @@ if TextMatched(self.name, old_name): self.name = new_name setattr(cls, "updateElementName", updateElementName) - + def Search(self, criteria, parent_infos=[]): return [(tuple(parent_infos),) + result for result in TestTextElement(self.name, criteria)] setattr(cls, "Search", Search) @@ -995,7 +1047,7 @@ cls = PLCOpenParser.GetElementClass("array", "dataType") if cls: setattr(cls, "updateElementName", _updateBaseTypeElementName) - + def Search(self, criteria, parent_infos=[]): search_result = self.baseType.Search(criteria, parent_infos) for i, dimension in enumerate(self.getdimension()): @@ -1005,6 +1057,7 @@ return search_result setattr(cls, "Search", Search) + def _SearchInSubrange(self, criteria, parent_infos=[]): search_result = self.baseType.Search(criteria, parent_infos) search_result.extend(_Search([("lower", self.range.getlower()), @@ -1012,6 +1065,7 @@ criteria, parent_infos)) return search_result + cls = PLCOpenParser.GetElementClass("subrangeSigned", "dataType") if cls: setattr(cls, "updateElementName", _updateBaseTypeElementName) @@ -1024,12 +1078,13 @@ cls = PLCOpenParser.GetElementClass("enum", "dataType") if cls: - + def updateElementName(self, old_name, new_name): pass setattr(cls, "updateElementName", updateElementName) - + enumerated_datatype_values_xpath = PLCOpen_XPath("ppx:values/ppx:value") + def Search(self, criteria, parent_infos=[]): search_result = [] for i, value in enumerate(enumerated_datatype_values_xpath(self)): @@ -1038,28 +1093,31 @@ return search_result setattr(cls, "Search", Search) + def _getvariableTypeinfos(variable_type): type_content = variable_type.getcontent() type_content_type = type_content.getLocalTag() if type_content_type == "derived": return type_content.getname() return type_content_type.upper() - + + cls = PLCOpenParser.GetElementClass("pou", "pous") if cls: - + block_inputs_xpath = PLCOpen_XPath( "ppx:interface/*[self::ppx:inputVars or self::ppx:inOutVars]/ppx:variable") block_outputs_xpath = PLCOpen_XPath( "ppx:interface/*[self::ppx:outputVars or self::ppx:inOutVars]/ppx:variable") - def getblockInfos(self): + + def getblockInfos(self): block_infos = { - "name" : self.getname(), - "type" : self.getpouType(), - "extensible" : False, - "inputs" : [], - "outputs" : [], - "comment" : self.getdescription()} + "name": self.getname(), + "type": self.getpouType(), + "extensible": False, + "inputs": [], + "outputs": [], + "comment": self.getdescription()} if self.interface is not None: return_type = self.interface.getreturnType() if return_type is not None: @@ -1071,15 +1129,15 @@ block_infos["outputs"].extend( [(var.getname(), _getvariableTypeinfos(var.type), "none") for var in block_outputs_xpath(self)]) - - block_infos["usage"] = ("\n (%s) => (%s)" % - (", ".join(["%s:%s" % (input[1], input[0]) - for input in block_infos["inputs"]]), - ", ".join(["%s:%s" % (output[1], output[0]) - for output in block_infos["outputs"]]))) + + block_infos["usage"] = ("\n (%s) => (%s)" % + (", ".join(["%s:%s" % (input[1], input[0]) + for input in block_infos["inputs"]]), + ", ".join(["%s:%s" % (output[1], output[0]) + for output in block_infos["outputs"]]))) return block_infos setattr(cls, "getblockInfos", getblockInfos) - + def setdescription(self, description): doc = self.getdocumentation() if doc is None: @@ -1087,81 +1145,81 @@ self.setdocumentation(doc) doc.setanyText(description) setattr(cls, "setdescription", setdescription) - + def getdescription(self): doc = self.getdocumentation() if doc is not None: return doc.getanyText() return "" setattr(cls, "getdescription", getdescription) - + def setbodyType(self, body_type): if len(self.body) > 0: if body_type in ["IL", "ST", "LD", "FBD", "SFC"]: self.body[0].setcontent(PLCOpenParser.CreateElement(body_type, "body")) else: - raise ValueError, "%s isn't a valid body type!"%type + raise ValueError("%s isn't a valid body type!" % type) setattr(cls, "setbodyType", setbodyType) - + def getbodyType(self): if len(self.body) > 0: return self.body[0].getcontent().getLocalTag() setattr(cls, "getbodyType", getbodyType) - + def resetexecutionOrder(self): if len(self.body) > 0: self.body[0].resetexecutionOrder() setattr(cls, "resetexecutionOrder", resetexecutionOrder) - + def compileexecutionOrder(self): if len(self.body) > 0: self.body[0].compileexecutionOrder() setattr(cls, "compileexecutionOrder", compileexecutionOrder) - + def setelementExecutionOrder(self, instance, new_executionOrder): if len(self.body) > 0: self.body[0].setelementExecutionOrder(instance, new_executionOrder) setattr(cls, "setelementExecutionOrder", setelementExecutionOrder) - + def addinstance(self, instance): if len(self.body) > 0: self.body[0].appendcontentInstance(instance) setattr(cls, "addinstance", addinstance) - + def getinstances(self): if len(self.body) > 0: return self.body[0].getcontentInstances() return [] setattr(cls, "getinstances", getinstances) - + def getinstance(self, id): if len(self.body) > 0: return self.body[0].getcontentInstance(id) return None setattr(cls, "getinstance", getinstance) - + def getinstancesIds(self): if len(self.body) > 0: return self.body[0].getcontentInstancesIds() return [] setattr(cls, "getinstancesIds", getinstancesIds) - + def getinstanceByName(self, name): if len(self.body) > 0: return self.body[0].getcontentInstanceByName(name) return None setattr(cls, "getinstanceByName", getinstanceByName) - + def removeinstance(self, id): if len(self.body) > 0: self.body[0].removecontentInstance(id) setattr(cls, "removeinstance", removeinstance) - + def settext(self, text): if len(self.body) > 0: self.body[0].settext(text) setattr(cls, "settext", settext) - + def gettext(self): if len(self.body) > 0: return self.body[0].gettext() @@ -1178,17 +1236,17 @@ vars.append((reverse_types[varlist.getLocalTag()], varlist)) return vars setattr(cls, "getvars", getvars) - + def setvars(self, vars): if self.interface is None: self.interface = PLCOpenParser.CreateElement("interface", "pou") self.interface.setcontent(vars) setattr(cls, "setvars", setvars) - + def addpouExternalVar(self, var_type, name): self.addpouVar(var_type, name, "externalVars") setattr(cls, "addpouExternalVar", addpouExternalVar) - + def addpouVar(self, var_type, name, var_class="localVars", location="", description="", initval=""): if self.interface is None: self.interface = PLCOpenParser.CreateElement("interface", "pou") @@ -1218,11 +1276,11 @@ el = PLCOpenParser.CreateElement("initialValue", "variable") el.setvalue(initval) var.setinitialValue(el) - + varlist.appendvariable(var) setattr(cls, "addpouVar", addpouVar) setattr(cls, "addpouLocalVar", addpouVar) - + def changepouVar(self, old_type, old_name, new_type, new_name): if self.interface is not None: content = self.interface.getcontent() @@ -1236,7 +1294,7 @@ vartype_content.setname(new_type) return setattr(cls, "changepouVar", changepouVar) - + def removepouVar(self, var_type, name): if self.interface is not None: content = self.interface.getcontent() @@ -1255,15 +1313,16 @@ if self.getbodyType() in ["SFC"]: for instance in self.getinstances(): if isinstance(instance, PLCOpenParser.GetElementClass("step", "sfcObjects")) and TextMatched(instance.getname(), name): - return True + return True return False setattr(cls, "hasstep", hasstep) - + def hasblock(self, name=None, block_type=None): if self.getbodyType() in ["FBD", "LD", "SFC"]: for instance in self.getinstances(): - if (isinstance(instance, PLCOpenParser.GetElementClass("block", "fbdObjects")) and - (TextMatched(instance.getinstanceName(), name) or TextMatched(instance.gettypeName(), block_type))): + if isinstance(instance, PLCOpenParser.GetElementClass("block", "fbdObjects")) \ + and (TextMatched(instance.getinstanceName(), name) or + TextMatched(instance.gettypeName(), block_type)): return True if self.transitions: for transition in self.transitions.gettransition(): @@ -1279,7 +1338,7 @@ return self.body[0].hasblock(block_type) return False setattr(cls, "hasblock", hasblock) - + def addtransition(self, name, body_type): if self.transitions is None: self.addtransitions() @@ -1291,7 +1350,7 @@ if body_type == "ST": transition.settext(":= ;") setattr(cls, "addtransition", addtransition) - + def gettransition(self, name): if self.transitions is not None: for transition in self.transitions.gettransition(): @@ -1299,13 +1358,13 @@ return transition return None setattr(cls, "gettransition", gettransition) - + def gettransitionList(self): if self.transitions is not None: return self.transitions.gettransition() return [] setattr(cls, "gettransitionList", gettransitionList) - + def removetransition(self, name): if self.transitions is not None: removed = False @@ -1314,13 +1373,13 @@ if transition.getbodyType() in ["FBD", "LD", "SFC"]: for instance in transition.getinstances(): if isinstance(instance, PLCOpenParser.GetElementClass("block", "fbdObjects")): - self.removepouVar(instance.gettypeName(), + self.removepouVar(instance.gettypeName(), instance.getinstanceName()) self.transitions.remove(transition) removed = True break if not removed: - raise ValueError, _("Transition with name %s doesn't exist!")%name + raise ValueError(_("Transition with name %s doesn't exist!") % name) setattr(cls, "removetransition", removetransition) def addaction(self, name, body_type): @@ -1332,7 +1391,7 @@ action.setname(name) action.setbodyType(body_type) setattr(cls, "addaction", addaction) - + def getaction(self, name): if self.actions is not None: for action in self.actions.getaction(): @@ -1340,13 +1399,13 @@ return action return None setattr(cls, "getaction", getaction) - + def getactionList(self): if self.actions is not None: return self.actions.getaction() return [] setattr(cls, "getactionList", getactionList) - + def removeaction(self, name): if self.actions is not None: removed = False @@ -1355,13 +1414,13 @@ if action.getbodyType() in ["FBD", "LD", "SFC"]: for instance in action.getinstances(): if isinstance(instance, PLCOpenParser.GetElementClass("block", "fbdObjects")): - self.removepouVar(instance.gettypeName(), + self.removepouVar(instance.gettypeName(), instance.getinstanceName()) self.actions.remove(action) removed = True break if not removed: - raise ValueError, _("Action with name %s doesn't exist!")%name + raise ValueError(_("Action with name %s doesn't exist!") % name) setattr(cls, "removeaction", removeaction) def updateElementName(self, old_name, new_name): @@ -1417,7 +1476,7 @@ if result is not None: content.remove(variable) setattr(cls, "removeVariableByFilter", removeVariableByFilter) - + def Search(self, criteria, parent_infos=[]): search_result = [] filter = criteria["filter"] @@ -1449,64 +1508,80 @@ return search_result setattr(cls, "Search", Search) + def setbodyType(self, body_type): if body_type in ["IL", "ST", "LD", "FBD", "SFC"]: self.body.setcontent(PLCOpenParser.CreateElement(body_type, "body")) else: - raise ValueError, "%s isn't a valid body type!"%type + raise ValueError("%s isn't a valid body type!" % type) + def getbodyType(self): return self.body.getcontent().getLocalTag() + def resetexecutionOrder(self): self.body.resetexecutionOrder() + def compileexecutionOrder(self): self.body.compileexecutionOrder() + def setelementExecutionOrder(self, instance, new_executionOrder): self.body.setelementExecutionOrder(instance, new_executionOrder) + def addinstance(self, instance): self.body.appendcontentInstance(instance) + def getinstances(self): return self.body.getcontentInstances() + def getinstance(self, id): return self.body.getcontentInstance(id) + def getrandomInstance(self, exclude): return self.body.getcontentRandomInstance(exclude) + def getinstanceByName(self, name): return self.body.getcontentInstanceByName(name) + def removeinstance(self, id): self.body.removecontentInstance(id) + def settext(self, text): self.body.settext(text) + def gettext(self): return self.body.gettext() + def hasblock(self, name=None, block_type=None): if self.getbodyType() in ["FBD", "LD", "SFC"]: for instance in self.getinstances(): - if (isinstance(instance, PLCOpenParser.GetElementClass("block", "fbdObjects")) and - (TextMatched(instance.getinstanceName(), name) or TextMatched(instance.gettypeName(), block_type))): + if isinstance(instance, PLCOpenParser.GetElementClass("block", "fbdObjects")) and \ + (TextMatched(instance.getinstanceName(), name) or TextMatched(instance.gettypeName(), block_type)): return True elif block_type is not None: return self.body.hasblock(block_type) return False + def updateElementName(self, old_name, new_name): self.body.updateElementName(old_name, new_name) + def updateElementAddress(self, address_model, new_leading): self.body.updateElementAddress(address_model, new_leading) - + cls = PLCOpenParser.GetElementClass("transition", "transitions") if cls: @@ -1526,7 +1601,7 @@ setattr(cls, "hasblock", hasblock) setattr(cls, "updateElementName", updateElementName) setattr(cls, "updateElementAddress", updateElementAddress) - + def Search(self, criteria, parent_infos): search_result = [] parent_infos = parent_infos[:-1] + ["T::%s::%s" % (parent_infos[-1].split("::")[1], self.getname())] @@ -1554,7 +1629,7 @@ setattr(cls, "hasblock", hasblock) setattr(cls, "updateElementName", updateElementName) setattr(cls, "updateElementAddress", updateElementAddress) - + def Search(self, criteria, parent_infos): search_result = [] parent_infos = parent_infos[:-1] + ["A::%s::%s" % (parent_infos[-1].split("::")[1], self.getname())] @@ -1568,27 +1643,28 @@ if cls: cls.currentExecutionOrderId = 0 cls.checkedBlocksDict = {} + def resetcurrentExecutionOrderId(self): object.__setattr__(self, "currentExecutionOrderId", 0) setattr(cls, "resetcurrentExecutionOrderId", resetcurrentExecutionOrderId) - + def getnewExecutionOrderId(self): object.__setattr__(self, "currentExecutionOrderId", self.currentExecutionOrderId + 1) return self.currentExecutionOrderId setattr(cls, "getnewExecutionOrderId", getnewExecutionOrderId) - + def resetexecutionOrder(self): if self.content.getLocalTag() == "FBD": for element in self.content.getcontent(): - if not isinstance(element, (PLCOpenParser.GetElementClass("comment", "commonObjects"), - PLCOpenParser.GetElementClass("connector", "commonObjects"), + if not isinstance(element, (PLCOpenParser.GetElementClass("comment", "commonObjects"), + PLCOpenParser.GetElementClass("connector", "commonObjects"), PLCOpenParser.GetElementClass("continuation", "commonObjects"))): element.setexecutionOrderId(0) self.checkedBlocksDict.clear() else: - raise TypeError, _("Can only generate execution order on FBD networks!") + raise TypeError(_("Can only generate execution order on FBD networks!")) setattr(cls, "resetexecutionOrder", resetexecutionOrder) - + def compileexecutionOrder(self): if self.content.getLocalTag() == "FBD": self.resetexecutionOrder() @@ -1600,9 +1676,9 @@ self.compileelementExecutionOrder(connections[0]) element.setexecutionOrderId(self.getnewExecutionOrderId()) else: - raise TypeError, _("Can only generate execution order on FBD networks!") + raise TypeError(_("Can only generate execution order on FBD networks!")) setattr(cls, "compileexecutionOrder", compileexecutionOrder) - + def compileelementExecutionOrder(self, link): if self.content.getLocalTag() == "FBD": localid = link.getrefLocalId() @@ -1612,21 +1688,22 @@ for variable in instance.inputVariables.getvariable(): connections = variable.connectionPointIn.getconnections() if connections and len(connections) == 1: - if (self.checkedBlocksDict.has_key(connections[0].getrefLocalId()) == False): + if not connections[0].getrefLocalId() in self.checkedBlocksDict: self.compileelementExecutionOrder(connections[0]) if instance.getexecutionOrderId() == 0: instance.setexecutionOrderId(self.getnewExecutionOrderId()) elif isinstance(instance, PLCOpenParser.GetElementClass("continuation", "commonObjects")) and instance.getexecutionOrderId() == 0: for tmp_instance in self.getcontentInstances(): - if (isinstance(tmp_instance, PLCOpenParser.GetElementClass("connector", "commonObjects")) and - TextMatched(tmp_instance.getname(), instance.getname()) and tmp_instance.getexecutionOrderId() == 0): + if isinstance(tmp_instance, PLCOpenParser.GetElementClass("connector", "commonObjects")) and \ + TextMatched(tmp_instance.getname(), instance.getname()) and \ + tmp_instance.getexecutionOrderId() == 0: connections = tmp_instance.connectionPointIn.getconnections() if connections and len(connections) == 1: self.compileelementExecutionOrder(connections[0]) else: - raise TypeError, _("Can only generate execution order on FBD networks!") + raise TypeError(_("Can only generate execution order on FBD networks!")) setattr(cls, "compileelementExecutionOrder", compileelementExecutionOrder) - + def setelementExecutionOrder(self, instance, new_executionOrder): if self.content.getLocalTag() == "FBD": old_executionOrder = instance.getexecutionOrderId() @@ -1640,85 +1717,86 @@ element.setexecutionOrderId(element_executionOrder + 1) instance.setexecutionOrderId(new_executionOrder) else: - raise TypeError, _("Can only generate execution order on FBD networks!") + raise TypeError(_("Can only generate execution order on FBD networks!")) setattr(cls, "setelementExecutionOrder", setelementExecutionOrder) - + def appendcontentInstance(self, instance): - if self.content.getLocalTag() in ["LD","FBD","SFC"]: + if self.content.getLocalTag() in ["LD", "FBD", "SFC"]: self.content.appendcontent(instance) else: - raise TypeError, _("%s body don't have instances!")%self.content.getLocalTag() + raise TypeError(_("%s body don't have instances!") % self.content.getLocalTag()) setattr(cls, "appendcontentInstance", appendcontentInstance) - + def getcontentInstances(self): - if self.content.getLocalTag() in ["LD","FBD","SFC"]: + if self.content.getLocalTag() in ["LD", "FBD", "SFC"]: return self.content.getcontent() else: - raise TypeError, _("%s body don't have instances!")%self.content.getLocalTag() + raise TypeError(_("%s body don't have instances!") % self.content.getLocalTag()) setattr(cls, "getcontentInstances", getcontentInstances) - + instance_by_id_xpath = PLCOpen_XPath("*[@localId=$localId]") instance_by_name_xpath = PLCOpen_XPath("ppx:block[@instanceName=$name]") + def getcontentInstance(self, local_id): - if self.content.getLocalTag() in ["LD","FBD","SFC"]: + if self.content.getLocalTag() in ["LD", "FBD", "SFC"]: instance = instance_by_id_xpath(self.content, localId=local_id) if len(instance) > 0: return instance[0] return None else: - raise TypeError, _("%s body don't have instances!")%self.content.getLocalTag() + raise TypeError(_("%s body don't have instances!") % self.content.getLocalTag()) setattr(cls, "getcontentInstance", getcontentInstance) - + def getcontentInstancesIds(self): - if self.content.getLocalTag() in ["LD","FBD","SFC"]: + if self.content.getLocalTag() in ["LD", "FBD", "SFC"]: return OrderedDict([(instance.getlocalId(), True) for instance in self.content]) else: - raise TypeError, _("%s body don't have instances!")%self.content.getLocalTag() + raise TypeError(_("%s body don't have instances!") % self.content.getLocalTag()) setattr(cls, "getcontentInstancesIds", getcontentInstancesIds) - + def getcontentInstanceByName(self, name): - if self.content.getLocalTag() in ["LD","FBD","SFC"]: + if self.content.getLocalTag() in ["LD", "FBD", "SFC"]: instance = instance_by_name_xpath(self.content) if len(instance) > 0: return instance[0] return None else: - raise TypeError, _("%s body don't have instances!")%self.content.getLocalTag() + raise TypeError(_("%s body don't have instances!") % self.content.getLocalTag()) setattr(cls, "getcontentInstanceByName", getcontentInstanceByName) - + def removecontentInstance(self, local_id): - if self.content.getLocalTag() in ["LD","FBD","SFC"]: + if self.content.getLocalTag() in ["LD", "FBD", "SFC"]: instance = instance_by_id_xpath(self.content, localId=local_id) if len(instance) > 0: self.content.remove(instance[0]) else: - raise ValueError, _("Instance with id %d doesn't exist!")%id + raise ValueError(_("Instance with id %d doesn't exist!") % id) else: - raise TypeError, "%s body don't have instances!"%self.content.getLocalTag() + raise TypeError(_("%s body don't have instances!") % self.content.getLocalTag()) setattr(cls, "removecontentInstance", removecontentInstance) - + def settext(self, text): - if self.content.getLocalTag() in ["IL","ST"]: + if self.content.getLocalTag() in ["IL", "ST"]: self.content.setanyText(text) else: - raise TypeError, _("%s body don't have text!")%self.content.getLocalTag() + raise TypeError(_("%s body don't have text!") % self.content.getLocalTag()) setattr(cls, "settext", settext) def gettext(self): - if self.content.getLocalTag() in ["IL","ST"]: + if self.content.getLocalTag() in ["IL", "ST"]: return self.content.getanyText() else: - raise TypeError, _("%s body don't have text!")%self.content.getLocalTag() + raise TypeError(_("%s body don't have text!") % self.content.getLocalTag()) setattr(cls, "gettext", gettext) - + def hasblock(self, block_type): - if self.content.getLocalTag() in ["IL","ST"]: + if self.content.getLocalTag() in ["IL", "ST"]: return self.content.hasblock(block_type) else: - raise TypeError, _("%s body don't have text!")%self.content.getLocalTag() + raise TypeError(_("%s body don't have text!") % self.content.getLocalTag()) setattr(cls, "hasblock", hasblock) - + def updateElementName(self, old_name, new_name): if self.content.getLocalTag() in ["IL", "ST"]: self.content.updateElementName(old_name, new_name) @@ -1745,21 +1823,27 @@ return search_result setattr(cls, "Search", Search) + def getx(self): return self.position.getx() + def gety(self): return self.position.gety() + def setx(self, x): self.position.setx(x) - + + def sety(self, y): self.position.sety(y) + def _getBoundingBox(self): return rect(self.getx(), self.gety(), self.getwidth(), self.getheight()) + def _getConnectionsBoundingBox(connectionPointIn): bbox = rect() connections = connectionPointIn.getconnections() @@ -1769,39 +1853,46 @@ bbox.update(x, y) return bbox + def _getBoundingBoxSingle(self): bbox = _getBoundingBox(self) if self.connectionPointIn is not None: bbox.union(_getConnectionsBoundingBox(self.connectionPointIn)) return bbox + def _getBoundingBoxMultiple(self): bbox = _getBoundingBox(self) for connectionPointIn in self.getconnectionPointIn(): bbox.union(_getConnectionsBoundingBox(connectionPointIn)) return bbox + def _filterConnections(connectionPointIn, localId, connections): in_connections = connectionPointIn.getconnections() if in_connections is not None: for connection in in_connections: connected = connection.getrefLocalId() - if not connections.has_key((localId, connected)) and \ - not connections.has_key((connected, localId)): + if not (localId, connected) in connections and \ + not (connected, localId) in connections: connectionPointIn.remove(connection) + def _filterConnectionsSingle(self, connections): if self.connectionPointIn is not None: _filterConnections(self.connectionPointIn, self.localId, connections) + def _filterConnectionsMultiple(self, connections): for connectionPointIn in self.getconnectionPointIn(): _filterConnections(connectionPointIn, self.localId, connections) + def _getconnectionsdefinition(instance, connections_end): local_id = instance.getlocalId() return dict([((local_id, end), True) for end in connections_end]) + def _updateConnectionsId(connectionPointIn, translation): connections_end = [] connections = connectionPointIn.getconnections() @@ -1813,22 +1904,26 @@ connections_end.append(new_reflocalId) return connections_end + def _updateConnectionsIdSingle(self, translation): connections_end = [] if self.connectionPointIn is not None: connections_end = _updateConnectionsId(self.connectionPointIn, translation) return _getconnectionsdefinition(self, connections_end) + def _updateConnectionsIdMultiple(self, translation): connections_end = [] for connectionPointIn in self.getconnectionPointIn(): connections_end.extend(_updateConnectionsId(connectionPointIn, translation)) return _getconnectionsdefinition(self, connections_end) + def _translate(self, dx, dy): self.setx(self.getx() + dx) self.sety(self.gety() + dy) - + + def _translateConnections(connectionPointIn, dx, dy): connections = connectionPointIn.getconnections() if connections is not None: @@ -1837,32 +1932,38 @@ position.setx(position.getx() + dx) position.sety(position.gety() + dy) + def _translateSingle(self, dx, dy): _translate(self, dx, dy) if self.connectionPointIn is not None: _translateConnections(self.connectionPointIn, dx, dy) + def _translateMultiple(self, dx, dy): _translate(self, dx, dy) for connectionPointIn in self.getconnectionPointIn(): _translateConnections(connectionPointIn, dx, dy) + def _updateElementName(self, old_name, new_name): pass + def _updateElementAddress(self, address_model, new_leading): pass + def _SearchInElement(self, criteria, parent_infos=[]): return [] + _connectionsFunctions = { "bbox": {"none": _getBoundingBox, "single": _getBoundingBoxSingle, "multiple": _getBoundingBoxMultiple}, "translate": {"none": _translate, - "single": _translateSingle, - "multiple": _translateMultiple}, + "single": _translateSingle, + "multiple": _translateMultiple}, "filter": {"none": lambda self, connections: None, "single": _filterConnectionsSingle, "multiple": _filterConnectionsMultiple}, @@ -1871,6 +1972,7 @@ "multiple": _updateConnectionsIdMultiple}, } + def _initElementClass(name, parent, connectionPointInType="none"): cls = PLCOpenParser.GetElementClass(name, parent) if cls: @@ -1887,16 +1989,17 @@ setattr(cls, "Search", _SearchInElement) return cls + cls = _initElementClass("comment", "commonObjects") if cls: def setcontentText(self, text): self.content.setanyText(text) setattr(cls, "setcontentText", setcontentText) - + def getcontentText(self): return self.content.getanyText() setattr(cls, "getcontentText", getcontentText) - + def updateElementName(self, old_name, new_name): self.content.updateElementName(old_name, new_name) setattr(cls, "updateElementName", updateElementName) @@ -1958,18 +2061,22 @@ _initElementClass("leftPowerRail", "ldObjects") _initElementClass("rightPowerRail", "ldObjects", "multiple") + def _UpdateLDElementName(self, old_name, new_name): if TextMatched(self.variable, old_name): self.variable = new_name + def _UpdateLDElementAddress(self, address_model, new_leading): self.variable = update_address(self.variable, address_model, new_leading) + def _getSearchInLDElement(ld_element_type): def SearchInLDElement(self, criteria, parent_infos=[]): return _Search([("reference", self.variable)], criteria, parent_infos + [ld_element_type, self.getlocalId()]) return SearchInLDElement + cls = _initElementClass("contact", "ldObjects", "single") if cls: setattr(cls, "updateElementName", _UpdateLDElementName) @@ -2004,11 +2111,11 @@ condition.setcontent(PLCOpenParser.CreateElement("ST", "inline")) condition.settext(value) setattr(cls, "setconditionContent", setconditionContent) - + def getconditionContent(self): if self.condition is not None: content = self.condition.getcontent() - values = {"type" : content.getLocalTag()} + values = {"type": content.getLocalTag()} if values["type"] == "reference": values["value"] = content.getname() elif values["type"] == "inline": @@ -2035,21 +2142,21 @@ bbox.union(_getConnectionsBoundingBox(condition_connection)) return bbox setattr(cls, "getBoundingBox", getBoundingBox) - + def translate(self, dx, dy): _translateSingle(self, dx, dy) condition_connection = self.getconditionConnection() if condition_connection is not None: _translateConnections(condition_connection, dx, dy) setattr(cls, "translate", translate) - + def filterConnections(self, connections): _filterConnectionsSingle(self, connections) condition_connection = self.getconditionConnection() if condition_connection is not None: _filterConnections(condition_connection, self.localId, connections) setattr(cls, "filterConnections", filterConnections) - + def updateConnectionsId(self, translation): connections_end = [] if self.connectionPointIn is not None: @@ -2087,7 +2194,7 @@ return condition_connection.getconnections() return None setattr(cls, "getconnections", getconnections) - + def Search(self, criteria, parent_infos=[]): parent_infos = parent_infos + ["transition", self.getlocalId()] search_result = [] @@ -2104,7 +2211,7 @@ _initElementClass("selectionConvergence", "sfcObjects", "multiple") _initElementClass("simultaneousDivergence", "sfcObjects", "single") _initElementClass("simultaneousConvergence", "sfcObjects", "multiple") - + cls = _initElementClass("jumpStep", "sfcObjects", "single") if cls: def Search(self, criteria, parent_infos): @@ -2117,7 +2224,7 @@ if self.reference is not None: self.reference.setname(name) setattr(cls, "setreferenceName", setreferenceName) - + def getreferenceName(self): if self.reference is not None: return self.reference.getname() @@ -2129,7 +2236,7 @@ self.inline.setcontent(PLCOpenParser.CreateElement("ST", "inline")) self.inline.settext(content) setattr(cls, "setinlineContent", setinlineContent) - + def getinlineContent(self): if self.inline is not None: return self.inline.gettext() @@ -2155,7 +2262,7 @@ if qualifier is None: qualifier = "N" return _Search([("inline", self.getinlineContent()), - ("reference", self.getreferenceName()), + ("reference", self.getreferenceName()), ("qualifier", qualifier), ("duration", self.getduration()), ("indicator", self.getindicator())], @@ -2223,16 +2330,20 @@ return search_result setattr(cls, "Search", Search) + def _SearchInIOVariable(self, criteria, parent_infos=[]): return _Search([("expression", self.expression)], criteria, parent_infos + ["io_variable", self.getlocalId()]) + def _UpdateIOElementName(self, old_name, new_name): if TextMatched(self.expression, old_name): self.expression = new_name + def _UpdateIOElementAddress(self, address_model, new_leading): self.expression = update_address(self.expression, address_model, new_leading) + cls = _initElementClass("inVariable", "fbdObjects") if cls: setattr(cls, "updateElementName", _UpdateIOElementName) @@ -2255,6 +2366,7 @@ def _SearchInConnector(self, criteria, parent_infos=[]): return _Search([("name", self.getname())], criteria, parent_infos + ["connector", self.getlocalId()]) + cls = _initElementClass("continuation", "commonObjects") if cls: setattr(cls, "Search", _SearchInConnector) @@ -2288,7 +2400,7 @@ def getpoints(self): points = [] for position in self.position: - points.append((position.getx(),position.gety())) + points.append((position.getx(), position.gety())) return points setattr(cls, "getpoints", getpoints) @@ -2318,33 +2430,34 @@ def removeconnections(self): self.content = None setattr(cls, "removeconnections", removeconnections) - + connection_xpath = PLCOpen_XPath("ppx:connection") connection_by_position_xpath = PLCOpen_XPath("ppx:connection[position()=$pos]") + def getconnections(self): return connection_xpath(self) setattr(cls, "getconnections", getconnections) - + def getconnection(self, idx): connection = connection_by_position_xpath(self, pos=idx+1) if len(connection) > 0: return connection[0] return None setattr(cls, "getconnection", getconnection) - + def setconnectionId(self, idx, local_id): connection = self.getconnection(idx) if connection is not None: connection.setrefLocalId(local_id) setattr(cls, "setconnectionId", setconnectionId) - + def getconnectionId(self, idx): connection = self.getconnection(idx) if connection is not None: return connection.getrefLocalId() return None setattr(cls, "getconnectionId", getconnectionId) - + def setconnectionPoints(self, idx, points): connection = self.getconnection(idx) if connection is not None: @@ -2363,7 +2476,7 @@ if connection is not None: connection.setformalParameter(parameter) setattr(cls, "setconnectionParameter", setconnectionParameter) - + def getconnectionParameter(self, idx): connection = self.getconnection(idx) if connection is not None: @@ -2398,11 +2511,12 @@ content.setvalue(value) self.setcontent(content) setattr(cls, "setvalue", setvalue) - + def getvalue(self): return self.content.getvalue() setattr(cls, "getvalue", getvalue) + def extractValues(values): items = values.split(",") i = 1 @@ -2414,13 +2528,14 @@ elif opened == closed: i += 1 else: - raise ValueError, _("\"%s\" is an invalid value!")%value + raise ValueError(_("\"%s\" is an invalid value!") % value) return items + cls = PLCOpenParser.GetElementClass("arrayValue", "value") if cls: arrayValue_model = re.compile("([0-9]+)\((.*)\)$") - + def setvalue(self, value): elements = [] for item in extractValues(value[1:-1]): @@ -2436,28 +2551,28 @@ elements.append(element) self.value = elements setattr(cls, "setvalue", setvalue) - + def getvalue(self): values = [] for element in self.value: try: repetition = int(element.getrepetitionValue()) - except: + except Exception: repetition = 1 if repetition > 1: value = element.getvalue() if value is None: value = "" - values.append("%s(%s)"%(repetition, value)) + values.append("%s(%s)" % (repetition, value)) else: values.append(element.getvalue()) - return "[%s]"%", ".join(values) + return "[%s]" % ", ".join(values) setattr(cls, "getvalue", getvalue) cls = PLCOpenParser.GetElementClass("structValue", "value") if cls: structValue_model = re.compile("(.*):=(.*)") - + def setvalue(self, value): elements = [] for item in extractValues(value[1:-1]): @@ -2470,11 +2585,10 @@ elements.append(element) self.value = elements setattr(cls, "setvalue", setvalue) - + def getvalue(self): values = [] for element in self.value: - values.append("%s := %s"%(element.getmember(), element.getvalue())) - return "(%s)"%", ".join(values) + values.append("%s := %s" % (element.getmember(), element.getvalue())) + return "(%s)" % ", ".join(values) setattr(cls, "getvalue", getvalue) - diff -r 31e63e25b4cc -r 64beb9e9c749 plcopen/structures.py --- a/plcopen/structures.py Mon Aug 21 20:17:19 2017 +0000 +++ b/plcopen/structures.py Mon Aug 21 23:22:58 2017 +0300 @@ -22,17 +22,19 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import string, re +import string +import re from plcopen import LoadProject from collections import OrderedDict from definitions import * TypeHierarchy = dict(TypeHierarchy_list) -""" -returns true if the given data type is the same that "reference" meta-type or one of its types. -""" + def IsOfType(type, reference): + """ + Returns true if the given data type is the same that "reference" meta-type or one of its types. + """ if reference is None: return True elif type == reference: @@ -43,12 +45,14 @@ return IsOfType(parent_type, reference) return False -""" -returns list of all types that correspont to the ANY* meta type -""" + def GetSubTypes(type): + """ + Returns list of all types that correspont to the ANY* meta type + """ return [typename for typename, parenttype in TypeHierarchy.items() if not typename.startswith("ANY") and IsOfType(typename, type)] + DataTypeRange = dict(DataTypeRange_list) """ @@ -67,76 +71,82 @@ - The default modifier which can be "none", "negated", "rising" or "falling" """ -StdBlckLibs = {libname : LoadProject(tc6fname)[0] - for libname, tc6fname in StdTC6Libs} -StdBlckLst = [{"name" : libname, "list": +StdBlckLibs = {libname: LoadProject(tc6fname)[0] + for libname, tc6fname in StdTC6Libs} +StdBlckLst = [{"name": libname, "list": [GetBlockInfos(pous) for pous in lib.getpous()]} - for libname, lib in StdBlckLibs.iteritems()] - -#------------------------------------------------------------------------------- + for libname, lib in StdBlckLibs.iteritems()] + +# ------------------------------------------------------------------------------- # Test identifier -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- IDENTIFIER_MODEL = re.compile( "(?:%(letter)s|_(?:%(letter)s|%(digit)s))(?:_?(?:%(letter)s|%(digit)s))*$" % {"letter": "[a-zA-Z]", "digit": "[0-9]"}) -# Test if identifier is valid + def TestIdentifier(identifier): - return IDENTIFIER_MODEL.match(identifier) is not None - -#------------------------------------------------------------------------------- + """ + Test if identifier is valid + """ + return IDENTIFIER_MODEL.match(identifier) is not None + +# ------------------------------------------------------------------------------- # Standard functions list generation -#------------------------------------------------------------------------------- - - -""" -take a .csv file and translate it it a "csv_table" -""" +# ------------------------------------------------------------------------------- + + def csv_file_to_table(file): - return [ map(string.strip,line.split(';')) for line in file.xreadlines()] - -""" -seek into the csv table to a section ( section_name match 1st field ) -return the matching row without first field -""" + """ + take a .csv file and translate it it a "csv_table" + """ + return [map(string.strip, line.split(';')) for line in file.xreadlines()] + + def find_section(section_name, table): + """ + seek into the csv table to a section ( section_name match 1st field ) + return the matching row without first field + """ fields = [None] while(fields[0] != section_name): fields = table.pop(0) return fields[1:] -""" -extract the standard functions standard parameter names and types... -return a { ParameterName: Type, ...} -""" + def get_standard_funtions_input_variables(table): + """ + extract the standard functions standard parameter names and types... + return a { ParameterName: Type, ...} + """ variables = find_section("Standard_functions_variables_types", table) standard_funtions_input_variables = {} - fields = [True,True] + fields = [True, True] while(fields[1]): fields = table.pop(0) - variable_from_csv = dict([(champ, val) for champ, val in zip(variables, fields[1:]) if champ!='']) + variable_from_csv = dict([(champ, val) for champ, val in zip(variables, fields[1:]) if champ != '']) standard_funtions_input_variables[variable_from_csv['name']] = variable_from_csv['type'] return standard_funtions_input_variables - -""" -translate .csv file input declaration into PLCOpenEditor interessting values -in : "(ANY_NUM, ANY_NUM)" and { ParameterName: Type, ...} -return [("IN1","ANY_NUM","none"),("IN2","ANY_NUM","none")] -""" + + def csv_input_translate(str_decl, variables, base): - decl = str_decl.replace('(','').replace(')','').replace(' ','').split(',') + """ + translate .csv file input declaration into PLCOpenEditor interessting values + in : "(ANY_NUM, ANY_NUM)" and { ParameterName: Type, ...} + return [("IN1","ANY_NUM","none"),("IN2","ANY_NUM","none")] + """ + decl = str_decl.replace('(', '').replace(')', '').replace(' ', '').split(',') params = [] - + len_of_not_predifined_variable = len([True for param_type in decl if param_type not in variables]) - + for param_type in decl: if param_type in variables.keys(): param_name = param_type param_type = variables[param_type] elif len_of_not_predifined_variable > 1: - param_name = "IN%d"%base + param_name = "IN%d" % base base += 1 else: param_name = "IN" @@ -144,10 +154,11 @@ return params -""" -Returns this kind of declaration for all standard functions - - [{"name" : "Numerical", 'list': [ { +def get_standard_funtions(table): + """ + Returns this kind of declaration for all standard functions + + [{"name" : "Numerical", 'list': [ { 'baseinputnumber': 1, 'comment': 'Addition', 'extensible': True, @@ -156,21 +167,20 @@ 'name': 'ADD', 'outputs': [('OUT', 'ANY_NUM', 'none')], 'type': 'function'}, ...... ] },.....] -""" -def get_standard_funtions(table): - + """ + variables = get_standard_funtions_input_variables(table) - - fonctions = find_section("Standard_functions_type",table) + + fonctions = find_section("Standard_functions_type", table) Standard_Functions_Decl = [] Current_section = None - + translate = { - "extensible" : lambda x: {"yes":True, "no":False}[x], - "inputs" : lambda x:csv_input_translate(x,variables,baseinputnumber), - "outputs":lambda x:[("OUT",x,"none")]} - + "extensible": lambda x: {"yes": True, "no": False}[x], + "inputs": lambda x: csv_input_translate(x, variables, baseinputnumber), + "outputs": lambda x: [("OUT", x, "none")]} + for fields in table: if fields[1]: # If function section name given @@ -180,82 +190,83 @@ section_name = words[1] else: section_name = fields[0] - Current_section = {"name" : section_name, "list" : []} + Current_section = {"name": section_name, "list": []} Standard_Functions_Decl.append(Current_section) Function_decl_list = [] if Current_section: Function_decl = dict([(champ, val) for champ, val in zip(fonctions, fields[1:]) if champ]) - baseinputnumber = int(Function_decl.get("baseinputnumber",1)) + baseinputnumber = int(Function_decl.get("baseinputnumber", 1)) Function_decl["baseinputnumber"] = baseinputnumber for param, value in Function_decl.iteritems(): if param in translate: Function_decl[param] = translate[param](value) Function_decl["type"] = "function" - - if Function_decl["name"].startswith('*') or Function_decl["name"].endswith('*') : + + if Function_decl["name"].startswith('*') or Function_decl["name"].endswith('*'): input_ovrloading_types = GetSubTypes(Function_decl["inputs"][0][1]) output_types = GetSubTypes(Function_decl["outputs"][0][1]) else: input_ovrloading_types = [None] output_types = [None] - + funcdeclname_orig = Function_decl["name"] funcdeclname = Function_decl["name"].strip('*_') fdc = Function_decl["inputs"][:] for intype in input_ovrloading_types: - if intype != None: + if intype is not None: Function_decl["inputs"] = [] for decl_tpl in fdc: if IsOfType(intype, decl_tpl[1]): Function_decl["inputs"] += [(decl_tpl[0], intype, decl_tpl[2])] else: Function_decl["inputs"] += [(decl_tpl)] - + if funcdeclname_orig.startswith('*'): - funcdeclin = intype + '_' + funcdeclname + funcdeclin = intype + '_' + funcdeclname else: funcdeclin = funcdeclname else: funcdeclin = funcdeclname - + for outype in output_types: - if outype != None: + if outype is not None: decl_tpl = Function_decl["outputs"][0] - Function_decl["outputs"] = [ (decl_tpl[0] , outype, decl_tpl[2])] + Function_decl["outputs"] = [(decl_tpl[0], outype, decl_tpl[2])] if funcdeclname_orig.endswith('*'): - funcdeclout = funcdeclin + '_' + outype + funcdeclout = funcdeclin + '_' + outype else: - funcdeclout = funcdeclin + funcdeclout = funcdeclin else: - funcdeclout = funcdeclin + funcdeclout = funcdeclin Function_decl["name"] = funcdeclout # apply filter given in "filter" column filter_name = Function_decl["filter"] store = True - for (InTypes, OutTypes) in ANY_TO_ANY_FILTERS.get(filter_name,[]): - outs = reduce(lambda a,b: a or b, - map(lambda testtype : IsOfType( - Function_decl["outputs"][0][1], - testtype), OutTypes)) - inps = reduce(lambda a,b: a or b, - map(lambda testtype : IsOfType( - Function_decl["inputs"][0][1], - testtype), InTypes)) + for (InTypes, OutTypes) in ANY_TO_ANY_FILTERS.get(filter_name, []): + outs = reduce(lambda a, b: a or b, + map(lambda testtype: IsOfType( + Function_decl["outputs"][0][1], + testtype), OutTypes)) + inps = reduce(lambda a, b: a or b, + map(lambda testtype: IsOfType( + Function_decl["inputs"][0][1], + testtype), InTypes)) if inps and outs and Function_decl["outputs"][0][1] != Function_decl["inputs"][0][1]: store = True break else: store = False - if store : + if store: # create the copy of decl dict to be appended to section Function_decl_copy = Function_decl.copy() Current_section["list"].append(Function_decl_copy) else: raise "First function must be in a category" - + return Standard_Functions_Decl + StdBlckLst.extend(get_standard_funtions(csv_file_to_table(open(StdFuncsCSV)))) # Dictionary to speedup block type fetching by name @@ -266,17 +277,17 @@ words = desc["comment"].split('"') if len(words) > 1: desc["comment"] = words[1] - desc["usage"] = ("\n (%s) => (%s)" % - (", ".join(["%s:%s" % (input[1], input[0]) - for input in desc["inputs"]]), - ", ".join(["%s:%s" % (output[1], output[0]) - for output in desc["outputs"]]))) - BlkLst = StdBlckDct.setdefault(desc["name"],[]) + desc["usage"] = ("\n (%s) => (%s)" % + (", ".join(["%s:%s" % (input[1], input[0]) + for input in desc["inputs"]]), + ", ".join(["%s:%s" % (output[1], output[0]) + for output in desc["outputs"]]))) + BlkLst = StdBlckDct.setdefault(desc["name"], []) BlkLst.append((section["name"], desc)) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Languages Keywords -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Keywords for Pou Declaration POU_BLOCK_START_KEYWORDS = ["FUNCTION", "FUNCTION_BLOCK", "PROGRAM"] @@ -313,16 +324,20 @@ # Keywords for Instruction List -IL_KEYWORDS = ["TRUE", "FALSE", "LD", "LDN", "ST", "STN", "S", "R", "AND", "ANDN", "OR", "ORN", - "XOR", "XORN", "NOT", "ADD", "SUB", "MUL", "DIV", "MOD", "GT", "GE", "EQ", "NE", - "LE", "LT", "JMP", "JMPC", "JMPCN", "CAL", "CALC", "CALCN", "RET", "RETC", "RETCN"] +IL_KEYWORDS = [ + "TRUE", "FALSE", "LD", "LDN", "ST", "STN", "S", "R", "AND", "ANDN", "OR", "ORN", + "XOR", "XORN", "NOT", "ADD", "SUB", "MUL", "DIV", "MOD", "GT", "GE", "EQ", "NE", + "LE", "LT", "JMP", "JMPC", "JMPCN", "CAL", "CALC", "CALCN", "RET", "RETC", "RETCN" +] # Keywords for Structured Text ST_BLOCK_START_KEYWORDS = ["IF", "ELSIF", "ELSE", "CASE", "FOR", "WHILE", "REPEAT"] ST_BLOCK_END_KEYWORDS = ["END_IF", "END_CASE", "END_FOR", "END_WHILE", "END_REPEAT"] -ST_KEYWORDS = ["TRUE", "FALSE", "THEN", "OF", "TO", "BY", "DO", "DO", "UNTIL", "EXIT", - "RETURN", "NOT", "MOD", "AND", "XOR", "OR"] + ST_BLOCK_START_KEYWORDS + ST_BLOCK_END_KEYWORDS +ST_KEYWORDS = [ + "TRUE", "FALSE", "THEN", "OF", "TO", "BY", "DO", "DO", "UNTIL", "EXIT", + "RETURN", "NOT", "MOD", "AND", "XOR", "OR" +] + ST_BLOCK_START_KEYWORDS + ST_BLOCK_END_KEYWORDS # All the keywords of IEC IEC_BLOCK_START_KEYWORDS = [] @@ -338,5 +353,3 @@ SFC_KEYWORDS, IL_KEYWORDS, ST_KEYWORDS])]: for keywords in keywords_list: all_keywords.extend([keyword for keyword in keywords if keyword not in all_keywords]) - - diff -r 31e63e25b4cc -r 64beb9e9c749 py_ext/PythonEditor.py --- a/py_ext/PythonEditor.py Mon Aug 21 20:17:19 2017 +0000 +++ b/py_ext/PythonEditor.py Mon Aug 21 23:22:58 2017 +0300 @@ -28,20 +28,21 @@ from controls.CustomStyledTextCtrl import faces from editors.CodeFileEditor import CodeFileEditor, CodeEditor + class PythonCodeEditor(CodeEditor): KEYWORDS = keyword.kwlist COMMENT_HEADER = "#" - + def SetCodeLexer(self): self.SetLexer(stc.STC_LEX_PYTHON) - + # Line numbers in margin - self.StyleSetSpec(stc.STC_STYLE_LINENUMBER,'fore:#000000,back:#99A9C2,size:%(size)d' % faces) + self.StyleSetSpec(stc.STC_STYLE_LINENUMBER, 'fore:#000000,back:#99A9C2,size:%(size)d' % faces) # Highlighted brace - self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,'fore:#00009D,back:#FFFF00,size:%(size)d' % faces) + self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, 'fore:#00009D,back:#FFFF00,size:%(size)d' % faces) # Unmatched brace - self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,'fore:#00009D,back:#FF0000,size:%(size)d' % faces) + self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, 'fore:#00009D,back:#FF0000,size:%(size)d' % faces) # Indentation guide self.StyleSetSpec(stc.STC_STYLE_INDENTGUIDE, 'fore:#CDCDCD,size:%(size)d' % faces) @@ -69,15 +70,14 @@ # Identifiers. I leave this as not bold because everything seems # to be an identifier if it doesn't match the above criterae self.StyleSetSpec(stc.STC_P_IDENTIFIER, 'fore:#000000,size:%(size)d' % faces) - -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # CFileEditor Main Frame Class -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- class PythonEditor(CodeFileEditor): - + CONFNODEEDITOR_TABS = [ (_("Python code"), "_create_CodePanel")] CODE_EDITOR = PythonCodeEditor - diff -r 31e63e25b4cc -r 64beb9e9c749 py_ext/PythonFileCTNMixin.py --- a/py_ext/PythonFileCTNMixin.py Mon Aug 21 20:17:19 2017 +0000 +++ b/py_ext/PythonFileCTNMixin.py Mon Aug 21 23:22:58 2017 +0300 @@ -23,7 +23,8 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import os, re +import os +import re from lxml import etree import util.paths as paths @@ -32,6 +33,7 @@ from CodeFileTreeNode import CodeFile from PythonEditor import PythonEditor + class PythonFileCTNMixin(CodeFile): CODEFILE_NAME = "PyFile" @@ -59,8 +61,8 @@ 'xmlns="http://www.w3.org/2001/XMLSchema"', 'xmlns:xhtml="http://www.w3.org/1999/xhtml"') for cre, repl in [ - (re.compile("(?)(?:)(?!)"), "]]>")]: + (re.compile("(?)(?:)(?!)"), "]]>")]: pythonfile_xml = cre.sub(repl, pythonfile_xml) try: @@ -85,34 +87,36 @@ PreSectionsTexts = {} PostSectionsTexts = {} - def GetSection(self,section): - return self.PreSectionsTexts.get(section,"") + "\n" + \ + + def GetSection(self, section): + return self.PreSectionsTexts.get(section, "") + "\n" + \ getattr(self.CodeFile, section).getanyText() + "\n" + \ - self.PostSectionsTexts.get(section,"") + self.PostSectionsTexts.get(section, "") def CTNGenerate_C(self, buildpath, locations): # location string for that CTN - location_str = "_".join(map(lambda x:str(x), + location_str = "_".join(map(lambda x: str(x), self.GetCurrentLocation())) configname = self.GetCTRoot().GetProjectConfigNames()[0] pyextname = self.CTNName() - varinfos = map(lambda variable : { - "name": variable.getname(), - "desc" : repr(variable.getdesc()), - "onchangecode" : '"'+variable.getonchange()+\ - "('"+variable.getname()+"')\"" \ - if variable.getonchange() else '""', - "onchange" : repr(variable.getonchange()) \ - if variable.getonchange() else None, - "opts" : repr(variable.getopts()), - "configname" : configname.upper(), - "uppername" : variable.getname().upper(), - "IECtype" : variable.gettype(), - "pyextname" :pyextname}, - self.CodeFile.variables.variable) + varinfos = map(lambda variable: { + "name": variable.getname(), + "desc": repr(variable.getdesc()), + "onchangecode": '"' + variable.getonchange() + + "('" + variable.getname() + "')\"" + if variable.getonchange() else '""', + "onchange": repr(variable.getonchange()) + if variable.getonchange() else None, + "opts": repr(variable.getopts()), + "configname": configname.upper(), + "uppername": variable.getname().upper(), + "IECtype": variable.gettype(), + "pyextname": pyextname}, + self.CodeFile.variables.variable) # python side PLC global variables access stub - globalstubs = "\n".join(["""\ + globalstubs = "\n".join([ + """\ _%(name)s_ctype, _%(name)s_unpack, _%(name)s_pack = \\ TypeTranslator["%(IECtype)s"] _PySafeGetPLCGlob_%(name)s = PLCBinary.__SafeGetPLCGlob_%(name)s @@ -127,8 +131,7 @@ %(desc)s, %(onchange)s, %(opts)s)) -""" % varinfo - for varinfo in varinfos]) +""" % varinfo for varinfo in varinfos]) # Runtime calls (start, stop, init, and cleanup) rtcalls = "" @@ -170,7 +173,7 @@ # write generated content to python file runtimefile_path = os.path.join(buildpath, - "runtime_%s.py"%location_str) + "runtime_%s.py" % location_str) runtimefile = open(runtimefile_path, 'w') runtimefile.write(PyFileContent.encode('utf-8')) runtimefile.close() @@ -234,14 +237,14 @@ __SET_VAR(__%(name)s_notifier->,CODE,,__STRING_LITERAL(%(onchangelen)d,%(onchangecode)s)); """ vardec = "\n".join([(vardecfmt + vardeconchangefmt - if varinfo["onchange"] else vardecfmt)% varinfo + if varinfo["onchange"] else vardecfmt) % varinfo for varinfo in varinfos]) varret = "\n".join([varretfmt % varinfo for varinfo in varinfos]) varpub = "\n".join([(varpubonchangefmt if varinfo["onchange"] else varpubfmt) % varinfo for varinfo in varinfos]) varinit = "\n".join([varinitonchangefmt % dict( - onchangelen = len(varinfo["onchangecode"]),**varinfo) + onchangelen=len(varinfo["onchangecode"]), **varinfo) for varinfo in varinfos if varinfo["onchange"]]) # TODO : use config name obtained from model instead of default @@ -278,16 +281,15 @@ } """ % locals() - Gen_PyCfile_path = os.path.join(buildpath, "PyCFile_%s.c"%location_str) - pycfile = open(Gen_PyCfile_path,'w') + Gen_PyCfile_path = os.path.join(buildpath, "PyCFile_%s.c" % location_str) + pycfile = open(Gen_PyCfile_path, 'w') pycfile.write(PyCFileContent) pycfile.close() - matiec_CFLAGS = '"-I%s"'%os.path.abspath( + matiec_CFLAGS = '"-I%s"' % os.path.abspath( self.GetCTRoot().GetIECLibPath()) return ([(Gen_PyCfile_path, matiec_CFLAGS)], "", True, - ("runtime_%s.py"%location_str, file(runtimefile_path,"rb"))) - + ("runtime_%s.py" % location_str, file(runtimefile_path, "rb"))) diff -r 31e63e25b4cc -r 64beb9e9c749 py_ext/py_ext.py --- a/py_ext/py_ext.py Mon Aug 21 20:17:19 2017 +0000 +++ b/py_ext/py_ext.py Mon Aug 21 23:22:58 2017 +0300 @@ -28,12 +28,13 @@ from PythonFileCTNMixin import PythonFileCTNMixin import util.paths as paths + class PythonLibrary(POULibrary): def GetLibraryPath(self): - return paths.AbsNeighbourFile(__file__, "pous.xml") + return paths.AbsNeighbourFile(__file__, "pous.xml") def Generate_C(self, buildpath, varlist, IECCFLAGS): - + plc_python_filepath = paths.AbsNeighbourFile(__file__, "plc_python.c") plc_python_file = open(plc_python_filepath, 'r') plc_python_code = plc_python_file.read() @@ -44,21 +45,20 @@ "PYTHON_POLL"]: python_eval_fb_list.append(v) python_eval_fb_count = max(1, len(python_eval_fb_list)) - + # prepare python code plc_python_code = plc_python_code % { - "python_eval_fb_count": python_eval_fb_count } - + "python_eval_fb_count": python_eval_fb_count} + Gen_Pythonfile_path = os.path.join(buildpath, "py_ext.c") - pythonfile = open(Gen_Pythonfile_path,'w') + pythonfile = open(Gen_Pythonfile_path, 'w') pythonfile.write(plc_python_code) pythonfile.close() - + return (["py_ext"], [(Gen_Pythonfile_path, IECCFLAGS)], True), "" + class PythonFile(PythonFileCTNMixin): - + def GetIconName(self): return "Pyfile" - - diff -r 31e63e25b4cc -r 64beb9e9c749 runtime/NevowServer.py --- a/runtime/NevowServer.py Mon Aug 21 20:17:19 2017 +0000 +++ b/runtime/NevowServer.py Mon Aug 21 23:22:58 2017 +0300 @@ -36,6 +36,7 @@ WorkingDir = None + class PLCHMI(athena.LiveElement): initialised = False @@ -46,21 +47,24 @@ def HMIinitialisation(self): self.HMIinitialised(None) + class DefaultPLCStartedHMI(PLCHMI): docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[ tags.h1["PLC IS NOW STARTED"], ]) + class PLCStoppedHMI(PLCHMI): docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[ tags.h1["PLC IS STOPPED"], ]) + class MainPage(athena.LiveElement): jsClass = u"WebInterface.PLC" docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[ tags.div(id='content')[ - tags.div(render = tags.directive('PLCElement')), + tags.div(render=tags.directive('PLCElement')), ]]) def __init__(self, *a, **kw): @@ -85,7 +89,7 @@ def HMIexec(self, function, *args, **kwargs): if self.HMI is not None: - getattr(self.HMI, function, lambda:None)(*args, **kwargs) + getattr(self.HMI, function, lambda: None)(*args, **kwargs) athena.expose(HMIexec) def resetHMI(self): @@ -110,15 +114,16 @@ for child in self.liveFragmentChildren[:]: child.detach() + class WebInterface(athena.LivePage): docFactory = loaders.stan([tags.raw(xhtml_header), - tags.html(xmlns="http://www.w3.org/1999/xhtml")[ - tags.head(render=tags.directive('liveglue')), - tags.body[ - tags.div[ - tags.div( render = tags.directive( "MainPage" )) - ]]]]) + tags.html(xmlns="http://www.w3.org/1999/xhtml")[ + tags.head(render=tags.directive('liveglue')), + tags.body[ + tags.div[ + tags.div(render=tags.directive("MainPage")) + ]]]]) MainPage = MainPage() PLCHMI = PLCHMI @@ -170,8 +175,9 @@ def disconnected(self, reason): self.MainPage.resetHMI() - #print reason - #print "We will be called back when the client disconnects" + # print reason + # print "We will be called back when the client disconnects" + def RegisterWebsite(port): website = WebInterface() @@ -182,6 +188,7 @@ print _("HTTP interface port :"), port return website + class statuslistener: def __init__(self, site): self.oldstate = None @@ -191,8 +198,10 @@ if state != self.oldstate: action = {'Started': self.site.PLCStarted, 'Stopped': self.site.PLCStopped}.get(state, None) - if action is not None: action () + if action is not None: + action() self.oldstate = state + def website_statuslistener_factory(site): return statuslistener(site).listen diff -r 31e63e25b4cc -r 64beb9e9c749 runtime/PLCObject.py --- a/runtime/PLCObject.py Mon Aug 21 20:17:19 2017 +0000 +++ b/runtime/PLCObject.py Mon Aug 21 23:22:58 2017 +0300 @@ -23,7 +23,12 @@ import Pyro.core as pyro from threading import Timer, Thread, Lock, Semaphore, Event -import ctypes, os, commands, types, sys +import ctypes +import os +import commands +import types +import sys +import traceback from targets.typemapping import LogLevelsDefault, LogLevelsCount, TypeTranslator, UnpackDebugBuffer from time import time @@ -34,26 +39,29 @@ elif os.name == "posix": from _ctypes import dlopen, dlclose -import traceback + def get_last_traceback(tb): while tb.tb_next: tb = tb.tb_next return tb -lib_ext ={ - "linux2":".so", - "win32":".dll", + +lib_ext = { + "linux2": ".so", + "win32": ".dll", }.get(sys.platform, "") + def PLCprint(message): sys.stdout.write("PLCobject : "+message+"\n") sys.stdout.flush() + class PLCObject(pyro.ObjBase): def __init__(self, workingdir, daemon, argv, statuschange, evaluator, pyruntimevars): pyro.ObjBase.__init__(self) self.evaluator = evaluator - self.argv = [workingdir] + argv # force argv[0] to be "path" to exec... + self.argv = [workingdir] + argv # force argv[0] to be "path" to exec... self.workingdir = workingdir self.PLCStatus = "Empty" self.PLClibraryHandle = None @@ -75,14 +83,14 @@ def AutoLoad(self): # Get the last transfered PLC if connector must be restart try: - self.CurrentPLCFilename=open( + self.CurrentPLCFilename = open( self._GetMD5FileName(), "r").read().strip() + lib_ext if self.LoadPLC(): self.PLCStatus = "Stopped" except Exception, e: self.PLCStatus = "Empty" - self.CurrentPLCFilename=None + self.CurrentPLCFilename = None def StatusChange(self): if self.statuschange is not None: @@ -102,9 +110,9 @@ self._ResetLogCount() def GetLogCount(self, level): - if self._GetLogCount is not None : + if self._GetLogCount is not None: return int(self._GetLogCount(level)) - elif self._loading_error is not None and level==0: + elif self._loading_error is not None and level == 0: return 1 def GetLogMessage(self, level, msgid): @@ -114,23 +122,22 @@ if self._GetLogMessage is not None: maxsz = len(self._log_read_buffer)-1 sz = self._GetLogMessage(level, msgid, - self._log_read_buffer, maxsz, - ctypes.byref(tick), - ctypes.byref(tv_sec), - ctypes.byref(tv_nsec)) + self._log_read_buffer, maxsz, + ctypes.byref(tick), + ctypes.byref(tv_sec), + ctypes.byref(tv_nsec)) if sz and sz <= maxsz: self._log_read_buffer[sz] = '\x00' - return self._log_read_buffer.value,tick.value,tv_sec.value,tv_nsec.value - elif self._loading_error is not None and level==0: - return self._loading_error,0,0,0 + return self._log_read_buffer.value, tick.value, tv_sec.value, tv_nsec.value + elif self._loading_error is not None and level == 0: + return self._loading_error, 0, 0, 0 return None def _GetMD5FileName(self): return os.path.join(self.workingdir, "lasttransferedPLC.md5") def _GetLibFileName(self): - return os.path.join(self.workingdir,self.CurrentPLCFilename) - + return os.path.join(self.workingdir, self.CurrentPLCFilename) def LoadPLC(self): """ @@ -143,8 +150,8 @@ self.PLClibraryHandle = ctypes.CDLL(self.CurrentPLCFilename, handle=self._PLClibraryHandle) self.PLC_ID = ctypes.c_char_p.in_dll(self.PLClibraryHandle, "PLC_ID") - if len(md5) == 32 : - self.PLC_ID.value = md5 + if len(md5) == 32: + self.PLC_ID.value = md5 self._startPLC = self.PLClibraryHandle.startPLC self._startPLC.restype = ctypes.c_int @@ -163,6 +170,7 @@ # If python confnode is not enabled, we reuse _PythonIterator # as a call that block pythonthread until StopPLC self.PlcStopping = Event() + def PythonIterator(res, blkid): self.PlcStopping.clear() self.PlcStopping.wait() @@ -174,7 +182,6 @@ self.PlcStopping.set() self._stopPLC = __StopPLC - self._ResetDebugVariables = self.PLClibraryHandle.ResetDebugVariables self._ResetDebugVariables.restype = None @@ -207,7 +214,7 @@ self._LogMessage.restype = ctypes.c_int self._LogMessage.argtypes = [ctypes.c_uint8, ctypes.c_char_p, ctypes.c_uint32] - self._log_read_buffer = ctypes.create_string_buffer(1<<14) #16K + self._log_read_buffer = ctypes.create_string_buffer(1 << 14) # 16K self._GetLogMessage = self.PLClibraryHandle.GetLogMessage self._GetLogMessage.restype = ctypes.c_uint32 self._GetLogMessage.argtypes = [ctypes.c_uint8, ctypes.c_uint32, ctypes.c_char_p, ctypes.c_uint32, ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_uint32)] @@ -217,7 +224,7 @@ self.PythonRuntimeInit() return True - except: + except Exception: self._loading_error = traceback.format_exc() PLCprint(self._loading_error) return False @@ -233,22 +240,22 @@ """ self.PLClibraryLock.acquire() # Forget all refs to library - self._startPLC = lambda x,y:None - self._stopPLC = lambda:None - self._ResetDebugVariables = lambda:None - self._RegisterDebugVariable = lambda x, y:None - self._IterDebugData = lambda x,y:None - self._FreeDebugData = lambda:None - self._GetDebugData = lambda:-1 - self._suspendDebug = lambda x:-1 - self._resumeDebug = lambda:None - self._PythonIterator = lambda:"" + self._startPLC = lambda x, y: None + self._stopPLC = lambda: None + self._ResetDebugVariables = lambda: None + self._RegisterDebugVariable = lambda x, y: None + self._IterDebugData = lambda x, y: None + self._FreeDebugData = lambda: None + self._GetDebugData = lambda: -1 + self._suspendDebug = lambda x: -1 + self._resumeDebug = lambda: None + self._PythonIterator = lambda: "" self._GetLogCount = None - self._LogMessage = lambda l,m,s:PLCprint("OFF LOG :"+m) + self._LogMessage = lambda l, m, s: PLCprint("OFF LOG :"+m) self._GetLogMessage = None self.PLClibraryHandle = None # Unload library explicitely - if getattr(self,"_PLClibraryHandle",None) is not None: + if getattr(self, "_PLClibraryHandle", None) is not None: dlclose(self._PLClibraryHandle) self._PLClibraryHandle = None @@ -260,10 +267,10 @@ Calls init, start, stop or cleanup method provided by runtime python files, loaded when new PLC uploaded """ - for method in self.python_runtime_vars.get("_runtime_%s"%methodname, []): - res,exp = self.evaluator(method) + for method in self.python_runtime_vars.get("_runtime_%s" % methodname, []): + res, exp = self.evaluator(method) if exp is not None: - self.LogMessage(0,'\n'.join(traceback.format_exception(*exp))) + self.LogMessage(0, '\n'.join(traceback.format_exception(*exp))) def PythonRuntimeInit(self): MethodNames = ["init", "start", "stop", "cleanup"] @@ -272,30 +279,31 @@ class PLCSafeGlobals: def __getattr__(_self, name): - try : + try: t = self.python_runtime_vars["_"+name+"_ctype"] except KeyError: - raise KeyError("Try to get unknown shared global variable : %s"%name) + raise KeyError("Try to get unknown shared global variable : %s" % name) v = t() r = self.python_runtime_vars["_PySafeGetPLCGlob_"+name](ctypes.byref(v)) return self.python_runtime_vars["_"+name+"_unpack"](v) + def __setattr__(_self, name, value): - try : + try: t = self.python_runtime_vars["_"+name+"_ctype"] except KeyError: - raise KeyError("Try to set unknown shared global variable : %s"%name) - v = self.python_runtime_vars["_"+name+"_pack"](t,value) + raise KeyError("Try to set unknown shared global variable : %s" % name) + v = self.python_runtime_vars["_"+name+"_pack"](t, value) self.python_runtime_vars["_PySafeSetPLCGlob_"+name](ctypes.byref(v)) self.python_runtime_vars.update({ - "PLCGlobals" : PLCSafeGlobals(), - "WorkingDir" : self.workingdir, - "PLCObject" : self, - "PLCBinary" : self.PLClibraryHandle, - "PLCGlobalsDesc" : []}) - - for methodname in MethodNames : - self.python_runtime_vars["_runtime_%s"%methodname] = [] + "PLCGlobals": PLCSafeGlobals(), + "WorkingDir": self.workingdir, + "PLCObject": self, + "PLCBinary": self.PLClibraryHandle, + "PLCGlobalsDesc": []}) + + for methodname in MethodNames: + self.python_runtime_vars["_runtime_%s" % methodname] = [] try: filenames = os.listdir(self.workingdir) @@ -307,15 +315,13 @@ for methodname in MethodNames: method = self.python_runtime_vars.get("_%s_%s" % (name, methodname), None) if method is not None: - self.python_runtime_vars["_runtime_%s"%methodname].append(method) - except: - self.LogMessage(0,traceback.format_exc()) + self.python_runtime_vars["_runtime_%s" % methodname].append(method) + except Exception: + self.LogMessage(0, traceback.format_exc()) raise self.PythonRuntimeCall("init") - - def PythonRuntimeCleanup(self): if self.python_runtime_vars is not None: self.PythonRuntimeCall("cleanup") @@ -324,49 +330,49 @@ def PythonThreadProc(self): self.StartSem.release() - res,cmd,blkid = "None","None",ctypes.c_void_p() - compile_cache={} + res, cmd, blkid = "None", "None", ctypes.c_void_p() + compile_cache = {} while True: # print "_PythonIterator(", res, ")", - cmd = self._PythonIterator(res,blkid) + cmd = self._PythonIterator(res, blkid) FBID = blkid.value # print " -> ", cmd, blkid if cmd is None: break - try : - self.python_runtime_vars["FBID"]=FBID - ccmd,AST =compile_cache.get(FBID, (None,None)) - if ccmd is None or ccmd!=cmd: + try: + self.python_runtime_vars["FBID"] = FBID + ccmd, AST = compile_cache.get(FBID, (None, None)) + if ccmd is None or ccmd != cmd: AST = compile(cmd, '', 'eval') - compile_cache[FBID]=(cmd,AST) - result,exp = self.evaluator(eval,AST,self.python_runtime_vars) + compile_cache[FBID] = (cmd, AST) + result, exp = self.evaluator(eval, AST, self.python_runtime_vars) if exp is not None: res = "#EXCEPTION : "+str(exp[1]) - self.LogMessage(1,('PyEval@0x%x(Code="%s") Exception "%s"')%(FBID,cmd, - '\n'.join(traceback.format_exception(*exp)))) + self.LogMessage(1, ('PyEval@0x%x(Code="%s") Exception "%s"') % ( + FBID, cmd, '\n'.join(traceback.format_exception(*exp)))) else: - res=str(result) - self.python_runtime_vars["FBID"]=None - except Exception,e: + res = str(result) + self.python_runtime_vars["FBID"] = None + except Exception, e: res = "#EXCEPTION : "+str(e) - self.LogMessage(1,('PyEval@0x%x(Code="%s") Exception "%s"')%(FBID,cmd,str(e))) + self.LogMessage(1, ('PyEval@0x%x(Code="%s") Exception "%s"') % (FBID, cmd, str(e))) def StartPLC(self): if self.CurrentPLCFilename is not None and self.PLCStatus == "Stopped": c_argv = ctypes.c_char_p * len(self.argv) error = None - res = self._startPLC(len(self.argv),c_argv(*self.argv)) + res = self._startPLC(len(self.argv), c_argv(*self.argv)) if res == 0: self.PLCStatus = "Started" self.StatusChange() self.PythonRuntimeCall("start") - self.StartSem=Semaphore(0) + self.StartSem = Semaphore(0) self.PythonThread = Thread(target=self.PythonThreadProc) self.PythonThread.start() self.StartSem.acquire() self.LogMessage("PLC started") else: - self.LogMessage(0,_("Problem starting PLC : error %d" % res)) + self.LogMessage(0, _("Problem starting PLC : error %d" % res)) self.PLCStatus = "Broken" self.StatusChange() @@ -378,7 +384,7 @@ self.PLCStatus = "Stopped" self.StatusChange() self.PythonRuntimeCall("stop") - if self.TraceThread is not None : + if self.TraceThread is not None: self.TraceWakeup.set() self.TraceThread.join() self.TraceThread = None @@ -388,26 +394,26 @@ def _Reload(self): self.daemon.shutdown(True) self.daemon.sock.close() - os.execv(sys.executable,[sys.executable]+sys.argv[:]) + os.execv(sys.executable, [sys.executable]+sys.argv[:]) # never reached return 0 def ForceReload(self): # respawn python interpreter - Timer(0.1,self._Reload).start() + Timer(0.1, self._Reload).start() return True def GetPLCstatus(self): - return self.PLCStatus, map(self.GetLogCount,xrange(LogLevelsCount)) + return self.PLCStatus, map(self.GetLogCount, xrange(LogLevelsCount)) def NewPLC(self, md5sum, data, extrafiles): if self.PLCStatus in ["Stopped", "Empty", "Broken"]: NewFileName = md5sum + lib_ext - extra_files_log = os.path.join(self.workingdir,"extra_files.txt") + extra_files_log = os.path.join(self.workingdir, "extra_files.txt") self.UnLoadPLC() - self.LogMessage("NewPLC (%s)"%md5sum) + self.LogMessage("NewPLC (%s)" % md5sum) self.PLCStatus = "Empty" try: @@ -416,14 +422,14 @@ for filename in file(extra_files_log, "r").readlines() + [extra_files_log]: try: os.remove(os.path.join(self.workingdir, filename.strip())) - except: + except Exception: pass - except: + except Exception: pass try: # Create new PLC file - open(os.path.join(self.workingdir,NewFileName), + open(os.path.join(self.workingdir, NewFileName), 'wb').write(data) # Store new PLC filename based on md5 key @@ -431,14 +437,14 @@ # Then write the files log = file(extra_files_log, "w") - for fname,fdata in extrafiles: - fpath = os.path.join(self.workingdir,fname) + for fname, fdata in extrafiles: + fpath = os.path.join(self.workingdir, fname) open(fpath, "wb").write(fdata) log.write(fname+'\n') # Store new PLC filename self.CurrentPLCFilename = NewFileName - except: + except Exception: self.PLCStatus = "Broken" self.StatusChange() PLCprint(traceback.format_exc()) @@ -458,7 +464,7 @@ try: last_md5 = open(self._GetMD5FileName(), "r").read() return last_md5 == MD5 - except: + except Exception: pass return False @@ -472,12 +478,12 @@ if self._suspendDebug(False) == 0: # keep a copy of requested idx self._ResetDebugVariables() - for idx,iectype,force in idxs: - if force !=None: - c_type,unpack_func, pack_func = \ + for idx, iectype, force in idxs: + if force is not None: + c_type, unpack_func, pack_func = \ TypeTranslator.get(iectype, - (None,None,None)) - force = ctypes.byref(pack_func(c_type,force)) + (None, None, None)) + force = ctypes.byref(pack_func(c_type, force)) self._RegisterDebugVariable(idx, force) self._TracesSwap() self._resumeDebug() @@ -487,7 +493,7 @@ def _TracesPush(self, trace): self.TraceLock.acquire() lT = len(self.Traces) - if lT != 0 and lT * len(self.Traces[0]) > 1024 * 1024 : + if lT != 0 and lT * len(self.Traces[0]) > 1024 * 1024: self.Traces.pop(0) self.Traces.append(trace) self.TraceLock.release() @@ -511,10 +517,10 @@ self.TraceLock.acquire() self.Traces = [] self.TraceLock.release() - self._suspendDebug(True) # Disable debugger + self._suspendDebug(True) # Disable debugger self.TraceWakeup.clear() self.TraceWakeup.wait() - self._resumeDebug() # Re-enable debugger + self._resumeDebug() # Re-enable debugger def _TracesFlush(self): self.TraceLock.acquire() @@ -528,7 +534,7 @@ """ Return a list of traces, corresponding to the list of required idx """ - while self.PLCStatus == "Started" : + while self.PLCStatus == "Started": tick = ctypes.c_uint32() size = ctypes.c_uint32() buff = ctypes.c_void_p() @@ -546,15 +552,12 @@ self._TracesAutoSuspend() self._TracesFlush() - def RemoteExec(self, script, *kwargs): try: exec script in kwargs - except: + except Exception: e_type, e_value, e_traceback = sys.exc_info() line_no = traceback.tb_lineno(get_last_traceback(e_traceback)) return (-1, "RemoteExec script failed!\n\nLine %d: %s\n\t%s" % (line_no, e_value, script.splitlines()[line_no - 1])) return (0, kwargs.get("returnVal", None)) - - diff -r 31e63e25b4cc -r 64beb9e9c749 runtime/ServicePublisher.py --- a/runtime/ServicePublisher.py Mon Aug 21 20:17:19 2017 +0000 +++ b/runtime/ServicePublisher.py Mon Aug 21 23:22:58 2017 +0300 @@ -21,33 +21,35 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -import socket, threading +import socket +import threading from util import Zeroconf service_type = '_PYRO._tcp.local.' + class ServicePublisher(): def __init__(self): # type: fully qualified service type name - self.serviceproperties = {'description':'Beremiz remote PLC'} - + self.serviceproperties = {'description': 'Beremiz remote PLC'} + self.name = None self.ip_32b = None self.port = None self.server = None self.service_name = None self.retrytimer = None - + def RegisterService(self, name, ip, port): try: self._RegisterService(name, ip, port) - except Exception,e: - self.retrytimer = threading.Timer(2,self.RegisterService,[name, ip, port]) - self.retrytimer.start() + except Exception, e: + self.retrytimer = threading.Timer(2, self.RegisterService, [name, ip, port]) + self.retrytimer.start() def _RegisterService(self, name, ip, port): # name: fully qualified service name - self.service_name = 'Beremiz_%s.%s'%(name,service_type) + self.service_name = 'Beremiz_%s.%s' % (name, service_type) self.name = name self.port = port @@ -64,23 +66,23 @@ self.service_name, self.ip_32b, self.port, - properties = self.serviceproperties)) - self.retrytimer=None - + properties=self.serviceproperties)) + self.retrytimer = None + def UnRegisterService(self): if self.retrytimer is not None: self.retrytimer.cancel() self.server.unregisterService( - Zeroconf.ServiceInfo(service_type, - self.service_name, - self.ip_32b, - self.port, - properties = self.serviceproperties)) + Zeroconf.ServiceInfo(service_type, + self.service_name, + self.ip_32b, + self.port, + properties=self.serviceproperties)) self.server.close() self.server = None - - def gethostaddr(self, dst = '224.0.1.41'): + + def gethostaddr(self, dst='224.0.1.41'): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: s.connect((dst, 7)) @@ -88,6 +90,6 @@ s.close() if host != '0.0.0.0': return host - except Exception,e: + except Exception, e: pass return socket.gethostbyname(socket.gethostname()) diff -r 31e63e25b4cc -r 64beb9e9c749 runtime/WampClient.py --- a/runtime/WampClient.py Mon Aug 21 20:17:19 2017 +0000 +++ b/runtime/WampClient.py Mon Aug 21 23:22:58 2017 +0300 @@ -22,7 +22,6 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import sys -#from twisted.python import log from autobahn.twisted import wamp from autobahn.twisted.websocket import WampWebSocketClientFactory, connectWS from twisted.internet.defer import inlineCallbacks @@ -51,14 +50,17 @@ DoOnJoin = [] + def GetCallee(name): """ Get Callee or Subscriber corresponding to '.' spearated object path """ global _PySrv names = name.split('.') obj = _PySrv.plcobj - while names: obj = getattr(obj, names.pop(0)) + while names: + obj = getattr(obj, names.pop(0)) return obj + class WampSession(wamp.ApplicationSession): @inlineCallbacks @@ -68,7 +70,7 @@ ID = self.config.extra["ID"] print 'WAMP session joined by :', ID for name in ExposedCalls: - reg = yield self.register(GetCallee(name), '.'.join((ID,name))) + reg = yield self.register(GetCallee(name), '.'.join((ID, name))) for name in SubscribedEvents: reg = yield self.subscribe(GetCallee(name), name) @@ -81,52 +83,57 @@ _WampSession = None print 'WAMP session left' + class ReconnectingWampWebSocketClientFactory(WampWebSocketClientFactory, ReconnectingClientFactory): def clientConnectionFailed(self, connector, reason): print("WAMP Client connection failed .. retrying ..") self.retry(connector) + def clientConnectionLost(self, connector, reason): print("WAMP Client connection lost .. retrying ..") self.retry(connector) + def LoadWampClientConf(wampconf): WSClientConf = json.load(open(wampconf)) return WSClientConf + def RegisterWampClient(wampconf): WSClientConf = LoadWampClientConf(wampconf) - ## start logging to console + # start logging to console # log.startLogging(sys.stdout) # create a WAMP application session factory component_config = types.ComponentConfig( - realm = WSClientConf["realm"], - extra = {"ID":WSClientConf["ID"]}) + realm=WSClientConf["realm"], + extra={"ID": WSClientConf["ID"]}) session_factory = wamp.ApplicationSessionFactory( - config = component_config) + config=component_config) session_factory.session = WampSession # create a WAMP-over-WebSocket transport client factory transport_factory = ReconnectingWampWebSocketClientFactory( session_factory, - url = WSClientConf["url"], - serializers = [MsgPackSerializer()], - debug = False, - debug_wamp = False) + url=WSClientConf["url"], + serializers=[MsgPackSerializer()], + debug=False, + debug_wamp=False) # start the client from a Twisted endpoint conn = connectWS(transport_factory) - print "WAMP client connecting to :",WSClientConf["url"] + print "WAMP client connecting to :", WSClientConf["url"] return conn + def GetSession(): global _WampSession return _WampSession + def SetServer(pysrv): global _PySrv _PySrv = pysrv - diff -r 31e63e25b4cc -r 64beb9e9c749 svgui/pyjs/__init__.py --- a/svgui/pyjs/__init__.py Mon Aug 21 20:17:19 2017 +0000 +++ b/svgui/pyjs/__init__.py Mon Aug 21 23:22:58 2017 +0300 @@ -1,2 +1,1 @@ from pyjs import * - diff -r 31e63e25b4cc -r 64beb9e9c749 svgui/pyjs/build.py --- a/svgui/pyjs/build.py Mon Aug 21 20:17:19 2017 +0000 +++ b/svgui/pyjs/build.py Mon Aug 21 23:22:58 2017 +0300 @@ -7,11 +7,12 @@ from os.path import join, dirname, basename, abspath, split, isfile, isdir from optparse import OptionParser import pyjs +import time from cStringIO import StringIO try: # Python 2.5 and above from hashlib import md5 -except: +except Exception: import md5 import re @@ -45,7 +46,7 @@ # .cache.html files produces look like this -CACHE_HTML_PAT=re.compile('^[a-z]*.[0-9a-f]{32}\.cache\.html$') +CACHE_HTML_PAT = re.compile('^[a-z]*.[0-9a-f]{32}\.cache\.html$') # ok these are the three "default" library directories, containing # the builtins (str, List, Dict, ord, round, len, range etc.) @@ -63,6 +64,7 @@ def read_boilerplate(data_dir, filename): return open(join(data_dir, "builder/boilerplate", filename)).read() + def copy_boilerplate(data_dir, filename, output_dir): filename = join(data_dir, "builder/boilerplate", filename) shutil.copy(filename, output_dir) @@ -76,7 +78,7 @@ names = os.listdir(src) try: os.mkdir(dst) - except: + except Exception: pass errors = [] @@ -103,6 +105,7 @@ if errors: print errors + def check_html_file(source_file, dest_path): """ Checks if a base HTML-file is available in the PyJamas output directory. @@ -136,19 +139,17 @@ """ - filename = os.path.split ( source_file )[1] - mod_name = os.path.splitext ( filename )[0] - file_name = os.path.join ( dest_path, mod_name + '.html' ) + filename = os.path.split(source_file)[1] + mod_name = os.path.splitext(filename)[0] + file_name = os.path.join(dest_path, mod_name + '.html') # if html file in output directory exists, leave it alone. - if os.path.exists ( file_name ): + if os.path.exists(file_name): return 0 - if os.path.exists ( - os.path.join ( dest_path, mod_name + '.css' ) ) : + if os.path.exists(os.path.join(dest_path, mod_name + '.css')): css = "" - elif os.path.exists ( - os.path.join ( dest_path, 'pyjamas_default.css' ) ) : + elif os.path.exists(os.path.join(dest_path, 'pyjamas_default.css')): css = "" else: @@ -158,9 +159,9 @@ base_html = base_html % {'modulename': mod_name, 'title': title, 'css': css} - fh = open (file_name, 'w') - fh.write (base_html) - fh.close () + fh = open(file_name, 'w') + fh.write(base_html) + fh.close() return 1 @@ -187,14 +188,14 @@ except StandardError, e: print >>sys.stderr, "Exception creating output directory %s: %s" % (output, e) - ## public dir + # public dir for p in pyjs.path: pub_dir = join(p, 'public') if isdir(pub_dir): print "Copying: public directory of library %r" % p copytree_exists(pub_dir, output) - ## AppName.html - can be in current or public directory + # AppName.html - can be in current or public directory html_input_filename = app_name + ".html" html_output_filename = join(output, basename(html_input_filename)) if os.path.isfile(html_input_filename): @@ -203,7 +204,7 @@ os.path.getmtime(html_output_filename): try: shutil.copy(html_input_filename, html_output_filename) - except: + except Exception: print >>sys.stderr, "Warning: Missing module HTML file %s" % html_input_filename print "Copying: %(html_input_filename)s" % locals() @@ -211,7 +212,7 @@ if check_html_file(html_input_filename, output): print >>sys.stderr, "Warning: Module HTML file %s has been auto-generated" % html_input_filename - ## pygwt.js + # pygwt.js print "Copying: pygwt.js" @@ -222,7 +223,7 @@ pygwt_js_output.close() - ## Images + # Images print "Copying: Images and History" copy_boilerplate(data_dir, "corner_dialog_topleft_black.png", output) @@ -240,12 +241,11 @@ copy_boilerplate(data_dir, "tree_white.gif", output) copy_boilerplate(data_dir, "history.html", output) - - ## all.cache.html + # all.cache.html app_files = generateAppFiles(data_dir, js_includes, app_name, debug, output, dynamic, cache_buster, optimize) - ## AppName.nocache.html + # AppName.nocache.html print "Creating: %(app_name)s.nocache.html" % locals() @@ -261,8 +261,8 @@ print >> script_selectors, select_tmpl % (platform, file_prefix) print >>home_nocache_html_output, home_nocache_html_template % dict( - app_name = app_name, - script_selectors = script_selectors.getvalue(), + app_name=app_name, + script_selectors=script_selectors.getvalue(), ) home_nocache_html_output.close() @@ -287,8 +287,8 @@ tmpl = read_boilerplate(data_dir, "all.cache.html") parser = pyjs.PlatformParser("platform") app_headers = '' - scripts = [''%script \ - for script in js_includes] + scripts = ['' % + script for script in js_includes] app_body = '\n'.join(scripts) mod_code = {} @@ -306,7 +306,7 @@ # Second, (dynamic only), post-analyse the places where modules # haven't changed # Third, write everything out. - + for platform in app_platforms: mod_code[platform] = {} @@ -324,24 +324,24 @@ app_translator = pyjs.AppTranslator( parser=parser, dynamic=dynamic, optimize=optimize) early_app_libs[platform], appcode = \ - app_translator.translate(None, is_app=False, - debug=debug, - library_modules=['dynamicajax.js', - '_pyjs.js', 'sys', - 'pyjslib']) + app_translator.translate(None, is_app=False, + debug=debug, + library_modules=['dynamicajax.js', + '_pyjs.js', 'sys', + 'pyjslib']) pover[platform].update(app_translator.overrides.items()) for mname, name in app_translator.overrides.items(): pd = overrides.setdefault(mname, {}) pd[platform] = name print appcode - #mod_code[platform][app_name] = appcode - - # platform.Module.cache.js + # mod_code[platform][app_name] = appcode + + # platform.Module.cache.js modules_done = ['pyjslib', 'sys', '_pyjs.js'] - #modules_to_do = [app_name] + app_translator.library_modules - modules_to_do = [app_name] + app_translator.library_modules + # modules_to_do = [app_name] + app_translator.library_modules + modules_to_do = [app_name] + app_translator.library_modules dependencies = {} @@ -350,13 +350,13 @@ sublist = add_subdeps(dependencies, d) modules_to_do += sublist deps = uniquify(deps) - #dependencies[app_name] = deps + # dependencies[app_name] = deps modules[platform] = modules_done + modules_to_do while modules_to_do: - #print "modules to do", modules_to_do + # print "modules to do", modules_to_do mn = modules_to_do.pop() mod_name = pyjs.strip_py(mn) @@ -371,9 +371,9 @@ parser.setPlatform(platform) mod_translator = pyjs.AppTranslator(parser=parser, optimize=optimize) mod_libs[platform][mod_name], mod_code[platform][mod_name] = \ - mod_translator.translate(mod_name, - is_app=False, - debug=debug) + mod_translator.translate(mod_name, + is_app=False, + debug=debug) pover[platform].update(mod_translator.overrides.items()) for mname, name in mod_translator.overrides.items(): pd = overrides.setdefault(mname, {}) @@ -390,22 +390,22 @@ while mod_name in deps: deps.remove(mod_name) - #print - #print - #print "modname preadd:", mod_name, deps - #print - #print + # print + # print + # print "modname preadd:", mod_name, deps + # print + # print for d in deps: sublist = add_subdeps(dependencies, d) modules_to_do += sublist modules_to_do += add_subdeps(dependencies, mod_name) - #print "modname:", mod_name, deps + # print "modname:", mod_name, deps deps = uniquify(deps) - #print "modname:", mod_name, deps + # print "modname:", mod_name, deps dependencies[mod_name] = deps - + # work out the dependency ordering of the modules - + mod_levels[platform] = make_deps(None, dependencies, modules_done) # now write everything out @@ -415,7 +415,7 @@ early_app_libs_ = early_app_libs[platform] app_libs_ = app_libs[platform] app_code_ = app_code[platform] - #modules_ = filter_mods(app_name, modules[platform]) + # modules_ = filter_mods(app_name, modules[platform]) mods = flattenlist(mod_levels[platform]) mods.reverse() modules_ = filter_mods(None, mods) @@ -427,7 +427,7 @@ mod_name = pyjs.strip_py(mod_name) override_name = "%s.%s" % (platform.lower(), mod_name) - if pover[platform].has_key(override_name): + if override_name in pover[platform]: mod_cache_name = "%s.cache.js" % (override_name) else: mod_cache_name = "%s.cache.js" % (mod_name) @@ -457,13 +457,13 @@ mod_cache_html_output = StringIO() print >>mod_cache_html_output, mod_cache_html_template % dict( - mod_name = mod_name, - app_name = app_name, - modnames = modnames, - overrides = overnames, - mod_libs = mod_libs[platform][mod_name], - dynamic = dynamic, - mod_code = mod_code_, + mod_name=mod_name, + app_name=app_name, + modnames=modnames, + overrides=overnames, + mod_libs=mod_libs[platform][mod_name], + dynamic=dynamic, + mod_code=mod_code_, ) if dynamic: @@ -473,7 +473,7 @@ app_libs_ += mod_cache_html_output.read() # write out the dependency ordering of the modules - + app_modnames = [] for md in mod_levels[platform]: @@ -489,30 +489,30 @@ overnames = map(lambda x: "'%s': '%s'" % x, pover[platform].items()) overnames = "new pyjslib.Dict({\n\t\t%s\n\t})" % ',\n\t\t'.join(overnames) - #print "platform names", platform, overnames - #print pover + # print "platform names", platform, overnames + # print pover # now write app.allcache including dependency-ordered list of # library modules file_contents = all_cache_html_template % dict( - app_name = app_name, - early_app_libs = early_app_libs_, - app_libs = app_libs_, - app_code = app_code_, - app_body = app_body, - overrides = overnames, - platform = platform.lower(), - dynamic = dynamic, - app_modnames = app_modnames, - app_headers = app_headers + app_name=app_name, + early_app_libs=early_app_libs_, + app_libs=app_libs_, + app_code=app_code_, + app_body=app_body, + overrides=overnames, + platform=platform.lower(), + dynamic=dynamic, + app_modnames=app_modnames, + app_headers=app_headers ) if cache_buster: digest = md5.new(file_contents).hexdigest() file_name = "%s.%s.%s" % (platform.lower(), app_name, digest) else: file_name = "%s.%s" % (platform.lower(), app_name) - file_name += ".cache.html" + file_name += ".cache.html" out_path = join(output, file_name) out_file = open(out_path, 'w') out_file.write(file_contents) @@ -523,49 +523,56 @@ return app_files + def flattenlist(ll): res = [] for l in ll: res += l return res -# creates sub-dependencies e.g. pyjamas.ui.Widget -# creates pyjamas.ui.Widget, pyjamas.ui and pyjamas. + def subdeps(m): + """ + creates sub-dependencies e.g. pyjamas.ui.Widget + creates pyjamas.ui.Widget, pyjamas.ui and pyjamas. + """ d = [] m = m.split(".") for i in range(0, len(m)): d.append('.'.join(m[:i+1])) return d -import time def add_subdeps(deps, mod_name): sd = subdeps(mod_name) if len(sd) == 1: return [] - #print "subdeps", mod_name, sd - #print "deps", deps + # print "subdeps", mod_name, sd + # print "deps", deps res = [] for i in range(0, len(sd)-1): parent = sd[i] child = sd[i+1] - l = deps.get(child, []) - l.append(parent) - deps[child] = l + k = deps.get(child, []) + k.append(parent) + deps[child] = k if parent not in res: res.append(parent) - #print deps + # print deps return res -# makes unique and preserves list order + def uniquify(md): + """ + makes unique and preserves list order + """ res = [] for m in md: if m not in res: res.append(m) return res + def filter_mods(app_name, md): while 'sys' in md: md.remove('sys') @@ -578,6 +585,7 @@ return uniquify(md) + def filter_deps(app_name, deps): res = {} @@ -588,18 +596,20 @@ res[k] = mods return res + def has_nodeps(mod, deps): - if not deps.has_key(mod) or not deps[mod]: + if mod not in deps or not deps[mod]: return True return False + def nodeps_list(mod_list, deps): res = [] for mod in mod_list: if has_nodeps(mod, deps): res.append(mod) return res - + # this function takes a dictionary of dependent modules and # creates a list of lists. the first list will be modules # that have no dependencies; the second list will be those @@ -616,26 +626,26 @@ if not mod_list: return [] - #print mod_list - #print deps + # print mod_list + # print deps ordered_deps = [] last_len = -1 while deps: l_deps = len(deps) - #print l_deps - if l_deps==last_len: + # print l_deps + if l_deps == last_len: for m, dl in deps.items(): for d in dl: if m in deps.get(d, []): raise Exception('Circular Imports found: \n%s %s -> %s %s' % (m, dl, d, deps[d])) - #raise Exception('Could not calculate dependencies: \n%s' % deps) + # raise Exception('Could not calculate dependencies: \n%s' % deps) break last_len = l_deps - #print "modlist", mod_list + # print "modlist", mod_list nodeps = nodeps_list(mod_list, deps) - #print "nodeps", nodeps + # print "nodeps", nodeps mod_list = filter(lambda x: x not in nodeps, mod_list) newdeps = {} for k in deps.keys(): @@ -643,45 +653,86 @@ depslist = filter(lambda x: x not in nodeps, depslist) if depslist: newdeps[k] = depslist - #print "newdeps", newdeps + # print "newdeps", newdeps deps = newdeps ordered_deps.append(nodeps) - #time.sleep(0) + # time.sleep(0) if mod_list: - ordered_deps.append(mod_list) # last dependencies - usually the app(s) + ordered_deps.append(mod_list) # last dependencies - usually the app(s) ordered_deps.reverse() return ordered_deps + def main(): global app_platforms - parser = OptionParser(usage = usage, version = version) - parser.add_option("-o", "--output", dest="output", - help="directory to which the webapp should be written") - parser.add_option("-j", "--include-js", dest="js_includes", action="append", - help="javascripts to load into the same frame as the rest of the script") - parser.add_option("-I", "--library_dir", dest="library_dirs", - action="append", help="additional paths appended to PYJSPATH") - parser.add_option("-D", "--data_dir", dest="data_dir", - help="path for data directory") - parser.add_option("-m", "--dynamic-modules", action="store_true", - dest="dynamic", default=False, - help="Split output into separate dynamically-loaded modules (experimental)") - parser.add_option("-P", "--platforms", dest="platforms", - help="platforms to build for, comma-separated") - parser.add_option("-d", "--debug", action="store_true", dest="debug") - parser.add_option("-O", "--optimize", action="store_true", - dest="optimize", default=False, - help="Optimize generated code (removes all print statements)", - ) - parser.add_option("-c", "--cache_buster", action="store_true", - dest="cache_buster", - help="Enable browser cache-busting (MD5 hash added to output filenames)") - - parser.set_defaults(output = "output", js_includes=[], library_dirs=[], + parser = OptionParser(usage=usage, version=version) + parser.add_option( + "-o", + "--output", + dest="output", + help="directory to which the webapp should be written" + ) + parser.add_option( + "-j", + "--include-js", + dest="js_includes", + action="append", + help="javascripts to load into the same frame as the rest of the script" + ) + parser.add_option( + "-I", + "--library_dir", + dest="library_dirs", + action="append", + help="additional paths appended to PYJSPATH" + ) + parser.add_option( + "-D", + "--data_dir", + dest="data_dir", + help="path for data directory" + ) + parser.add_option( + "-m", + "--dynamic-modules", + action="store_true", + dest="dynamic", + default=False, + help="Split output into separate dynamically-loaded modules (experimental)" + ) + parser.add_option( + "-P", + "--platforms", + dest="platforms", + help="platforms to build for, comma-separated" + ) + parser.add_option( + "-d", + "--debug", + action="store_true", + dest="debug" + ) + parser.add_option( + "-O", + "--optimize", + action="store_true", + dest="optimize", + default=False, + help="Optimize generated code (removes all print statements)", + ) + parser.add_option( + "-c", + "--cache_buster", + action="store_true", + dest="cache_buster", + help="Enable browser cache-busting (MD5 hash added to output filenames)" + ) + + parser.set_defaults(output="output", js_includes=[], library_dirs=[], platforms=(','.join(app_platforms)), data_dir=os.path.join(sys.prefix, "share/pyjamas"), dynamic=False, @@ -710,7 +761,7 @@ pyjs.path.append(abspath(d)) if options.platforms: - app_platforms = options.platforms.split(',') + app_platforms = options.platforms.split(',') # this is mostly for getting boilerplate stuff data_dir = os.path.abspath(options.data_dir) @@ -719,6 +770,6 @@ options.debug, options.dynamic and 1 or 0, data_dir, options.cache_buster, options.optimize) + if __name__ == "__main__": main() - diff -r 31e63e25b4cc -r 64beb9e9c749 svgui/pyjs/jsonrpc/django/jsonrpc.py --- a/svgui/pyjs/jsonrpc/django/jsonrpc.py Mon Aug 21 20:17:19 2017 +0000 +++ b/svgui/pyjs/jsonrpc/django/jsonrpc.py Mon Aug 21 23:22:58 2017 +0300 @@ -1,9 +1,15 @@ # jsonrpc.py # original code: http://trac.pyworks.org/pyjamas/wiki/DjangoWithPyJamas # also from: http://www.pimentech.fr/technologies/outils +from datetime import date +import datetime +import sys + from django.utils import simplejson from django.http import HttpResponse -import sys +from django import forms +from django.core.serializers import serialize + from pyjs.jsonrpc import JSONRPCServiceBase # JSONRPCService and jsonremote are used in combination to drastically @@ -18,14 +24,16 @@ # dump jsonservice into urlpatterns: # (r'^service1/$', 'djangoapp.views.jsonservice'), + class JSONRPCService(JSONRPCServiceBase): - + def __call__(self, request, extra=None): return self.process(request.raw_post_data) + def jsonremote(service): """Make JSONRPCService a decorator so that you can write : - + from jsonrpc import JSONRPCService chatservice = JSONRPCService() @@ -38,7 +46,7 @@ service.add_method(func.__name__, func) else: emsg = 'Service "%s" not found' % str(service.__name__) - raise NotImplementedError, emsg + raise NotImplementedError(emsg) return func return remotify @@ -62,7 +70,6 @@ # part of the app: # (r'^formsservice/$', 'djangoapp.views.processor'), -from django import forms def builderrors(form): d = {} @@ -83,7 +90,7 @@ 'DateField': ['input_formats'], 'DateTimeField': ['input_formats'], 'TimeField': ['input_formats'], - 'RegexField': ['max_length', 'min_length'], # sadly we can't get the expr + 'RegexField': ['max_length', 'min_length'], # sadly we can't get the expr 'EmailField': ['max_length', 'min_length'], 'URLField': ['max_length', 'min_length', 'verify_exists', 'user_agent'], 'ChoiceField': ['choices'], @@ -91,6 +98,7 @@ 'IPAddressField': ['max_length', 'min_length'], } + def describe_field_errors(field): res = {} field_type = field.__class__.__name__ @@ -102,6 +110,7 @@ res['fields'] = map(describe_field, field.fields) return res + def describe_fields_errors(fields, field_names): res = {} if not field_names: @@ -111,16 +120,18 @@ res[name] = describe_field_errors(field) return res + def describe_field(field): res = {} field_type = field.__class__.__name__ - for fname in field_names.get(field_type, []) + \ - ['help_text', 'label', 'initial', 'required']: + for fname in (field_names.get(field_type, []) + + ['help_text', 'label', 'initial', 'required']): res[fname] = getattr(field, fname) if field_type in ['ComboField', 'MultiValueField', 'SplitDateTimeField']: res['fields'] = map(describe_field, field.fields) return res + def describe_fields(fields, field_names): res = {} if not field_names: @@ -130,13 +141,14 @@ res[name] = describe_field(field) return res + class FormProcessor(JSONRPCService): def __init__(self, forms, _formcls=None): if _formcls is None: JSONRPCService.__init__(self) for k in forms.keys(): - s = FormProcessor({}, forms[k]) + s = FormProcessor({}, forms[k]) self.add_method(k, s.__process) else: JSONRPCService.__init__(self, forms) @@ -146,33 +158,30 @@ f = self.formcls(params) - if command is None: # just validate + if command is None: # just validate if not f.is_valid(): - return {'success':False, 'errors': builderrors(f)} - return {'success':True} - - elif command.has_key('describe_errors'): + return {'success': False, 'errors': builderrors(f)} + return {'success': True} + + elif 'describe_errors' in command: field_names = command['describe_errors'] return describe_fields_errors(f.fields, field_names) - elif command.has_key('describe'): + elif 'describe' in command: field_names = command['describe'] return describe_fields(f.fields, field_names) - elif command.has_key('save'): + elif 'save' in command: if not f.is_valid(): - return {'success':False, 'errors': builderrors(f)} - instance = f.save() # XXX: if you want more, over-ride save. - return {'success': True, 'instance': json_convert(instance) } - - elif command.has_key('html'): + return {'success': False, 'errors': builderrors(f)} + instance = f.save() # XXX: if you want more, over-ride save. + return {'success': True, 'instance': json_convert(instance)} + + elif 'html' in command: return {'success': True, 'html': f.as_table()} return "unrecognised command" - - - # The following is incredibly convenient for saving vast amounts of # coding, avoiding doing silly things like this: # jsonresult = {'field1': djangoobject.field1, @@ -202,9 +211,6 @@ # list_some_model and list_another_model part of the django app: # (r'^service1/$', 'djangoapp.views.jsonservice'), -from django.core.serializers import serialize -import datetime -from datetime import date def dict_datetimeflatten(item): d = {} @@ -218,9 +224,9 @@ d[k] = v return d + def json_convert(l, fields=None): res = [] for item in serialize('python', l, fields=fields): res.append(dict_datetimeflatten(item)) return res - diff -r 31e63e25b4cc -r 64beb9e9c749 svgui/pyjs/jsonrpc/jsonrpc.py --- a/svgui/pyjs/jsonrpc/jsonrpc.py Mon Aug 21 20:17:19 2017 +0000 +++ b/svgui/pyjs/jsonrpc/jsonrpc.py Mon Aug 21 23:22:58 2017 +0300 @@ -2,22 +2,24 @@ import types import sys + class JSONRPCServiceBase: def __init__(self): - self.methods={} + self.methods = {} def response(self, id, result): - return simplejson.dumps({'version': '1.1', 'id':id, - 'result':result, 'error':None}) + return simplejson.dumps({'version': '1.1', 'id': id, + 'result': result, 'error': None}) + def error(self, id, code, message): - return simplejson.dumps({'id': id, - 'version': '1.1', - 'error': {'name': 'JSONRPCError', - 'code': code, - 'message': message - } - }) + return simplejson.dumps({ + 'id': id, + 'version': '1.1', + 'error': {'name': 'JSONRPCError', + 'code': code, + 'message': message} + }) def add_method(self, name, method): self.methods[name] = method @@ -27,17 +29,16 @@ id, method, params = data["id"], data["method"], data["params"] if method in self.methods: try: - result =self.methods[method](*params) + result = self.methods[method](*params) return self.response(id, result) except BaseException: etype, eval, etb = sys.exc_info() - return self.error(id, 100, '%s: %s' %(etype.__name__, eval)) - except: + return self.error(id, 100, '%s: %s' % (etype.__name__, eval)) + except Exception: etype, eval, etb = sys.exc_info() - return self.error(id, 100, 'Exception %s: %s' %(etype, eval)) + return self.error(id, 100, 'Exception %s: %s' % (etype, eval)) else: return self.error(id, 100, 'method "%s" does not exist' % method) def listmethods(self): - return self.methods.keys() - + return self.methods.keys() diff -r 31e63e25b4cc -r 64beb9e9c749 svgui/pyjs/jsonrpc/web2py/jsonrpc.py --- a/svgui/pyjs/jsonrpc/web2py/jsonrpc.py Mon Aug 21 20:17:19 2017 +0000 +++ b/svgui/pyjs/jsonrpc/web2py/jsonrpc.py Mon Aug 21 23:22:58 2017 +0300 @@ -1,11 +1,11 @@ from pyjs.jsonrpc import JSONRPCServiceBase + class JSONRPCService(JSONRPCServiceBase): def serve(self): return self.process(request.body.read()) - def __call__(self,func): - self.methods[func.__name__]=func + def __call__(self, func): + self.methods[func.__name__] = func return func - diff -r 31e63e25b4cc -r 64beb9e9c749 svgui/pyjs/lib/pyjslib.py --- a/svgui/pyjs/lib/pyjslib.py Mon Aug 21 20:17:19 2017 +0000 +++ b/svgui/pyjs/lib/pyjslib.py Mon Aug 21 23:22:58 2017 +0300 @@ -19,8 +19,9 @@ # must declare import _before_ importing sys + def import_module(path, parent_module, module_name, dynamic=1, async=False): - """ + """ """ JS(""" @@ -38,7 +39,7 @@ } var override_name = sys.platform + "." + module_name; - if (((sys.overrides != null) && + if (((sys.overrides != null) && (sys.overrides.has_key(override_name)))) { cache_file = sys.overrides.__getitem__(override_name) ; @@ -67,7 +68,7 @@ module_load_request[module_name] = 1; } - /* following a load, this first executes the script + /* following a load, this first executes the script * "preparation" function MODULENAME_loaded_fn() * and then sets up the loaded module in the namespace * of the parent. @@ -105,6 +106,7 @@ """) + JS(""" function import_wait(proceed_fn, parent_mod, dynamic) { @@ -184,11 +186,14 @@ } """) + class Object: pass + object = Object + class Modload: def __init__(self, path, app_modlist, app_imported_fn, dynamic, @@ -196,15 +201,15 @@ self.app_modlist = app_modlist self.app_imported_fn = app_imported_fn self.path = path - self.idx = 0; + self.idx = 0 self.dynamic = dynamic self.parent_mod = parent_mod def next(self): - + for i in range(len(self.app_modlist[self.idx])): app = self.app_modlist[self.idx][i] - import_module(self.path, self.parent_mod, app, self.dynamic, True); + import_module(self.path, self.parent_mod, app, self.dynamic, True) self.idx += 1 if self.idx >= len(self.app_modlist): @@ -212,18 +217,24 @@ else: import_wait(getattr(self, "next"), self.parent_mod, self.dynamic) + def get_module(module_name): ev = "__mod = %s;" % module_name JS("pyjs_eval(ev);") return __mod + def preload_app_modules(path, app_modnames, app_imported_fn, dynamic, parent_mod=None): loader = Modload(path, app_modnames, app_imported_fn, dynamic, parent_mod) loader.next() -import sys + +# as comment on line 20 says +# import sys should be below +import sys # noqa + class BaseException: @@ -242,32 +253,37 @@ def toString(self): return str(self) + class Exception(BaseException): - name = "Exception" + class TypeError(BaseException): name = "TypeError" + class StandardError(Exception): name = "StandardError" + class LookupError(StandardError): name = "LookupError" def toString(self): return self.name + ": " + self.args[0] + class KeyError(LookupError): name = "KeyError" + class AttributeError(StandardError): - name = "AttributeError" def toString(self): return "AttributeError: %s of %s" % (self.args[1], self.args[0]) + JS(""" pyjslib.StopIteration = function () { }; pyjslib.StopIteration.prototype = new Error(); @@ -407,6 +423,7 @@ """) + class Class: def __init__(self, name): self.name = name @@ -414,7 +431,8 @@ def __str___(self): return self.name -def eq(a,b): + +def eq(a, b): JS(""" if (pyjslib.hasattr(a, "__cmp__")) { return a.__cmp__(b) == 0; @@ -424,7 +442,8 @@ return a == b; """) -def cmp(a,b): + +def cmp(a, b): if hasattr(a, "__cmp__"): return a.__cmp__(b) elif hasattr(b, "__cmp__"): @@ -436,6 +455,7 @@ else: return 0 + def bool(v): # this needs to stay in native code without any dependencies here, # because this is used by if and while, we need to prevent @@ -456,6 +476,7 @@ return Boolean(v); """) + class List: def __init__(self, data=None): JS(""" @@ -511,7 +532,7 @@ def insert(self, index, value): JS(""" var a = this.l; this.l=a.slice(0, index).concat(value, a.slice(index));""") - def pop(self, index = -1): + def pop(self, index=-1): JS(""" if (index<0) index = this.l.length + index; var a = this.l[index]; @@ -580,15 +601,15 @@ global cmp compareFunc = cmp if keyFunc and reverse: - def thisSort1(a,b): + def thisSort1(a, b): return -compareFunc(keyFunc(a), keyFunc(b)) self.l.sort(thisSort1) elif keyFunc: - def thisSort2(a,b): + def thisSort2(a, b): return compareFunc(keyFunc(a), keyFunc(b)) self.l.sort(thisSort2) elif reverse: - def thisSort3(a,b): + def thisSort3(a, b): return -compareFunc(a, b) self.l.sort(thisSort3) else: @@ -603,8 +624,10 @@ def __str__(self): return repr(self) + list = List + class Tuple: def __init__(self, data=None): JS(""" @@ -660,7 +683,7 @@ def insert(self, index, value): JS(""" var a = this.l; this.l=a.slice(0, index).concat(value, a.slice(index));""") - def pop(self, index = -1): + def pop(self, index=-1): JS(""" if (index<0) index = this.l.length + index; var a = this.l[index]; @@ -729,15 +752,15 @@ global cmp compareFunc = cmp if keyFunc and reverse: - def thisSort1(a,b): + def thisSort1(a, b): return -compareFunc(keyFunc(a), keyFunc(b)) self.l.sort(thisSort1) elif keyFunc: - def thisSort2(a,b): + def thisSort2(a, b): return compareFunc(keyFunc(a), keyFunc(b)) self.l.sort(thisSort2) elif reverse: - def thisSort3(a,b): + def thisSort3(a, b): return -compareFunc(a, b) self.l.sort(thisSort3) else: @@ -752,6 +775,7 @@ def __str__(self): return repr(self) + tuple = Tuple @@ -866,22 +890,22 @@ return self.__iter__() def itervalues(self): - return self.values().__iter__(); + return self.values().__iter__() def iteritems(self): - return self.items().__iter__(); + return self.items().__iter__() def setdefault(self, key, default_value): - if not self.has_key(key): + if key not in self: self[key] = default_value def get(self, key, default_=None): - if not self.has_key(key): + if key not in self: return default_ return self[key] def update(self, d): - for k,v in d.iteritems(): + for k, v in d.iteritems(): self[k] = v def getObject(self): @@ -897,9 +921,12 @@ def __str__(self): return repr(self) + dict = Dict # taken from mochikit: range( [start,] stop[, step] ) + + def range(): JS(""" var start = 0; @@ -930,6 +957,7 @@ } """) + def slice(object, lower, upper): JS(""" if (pyjslib.isString(object)) { @@ -948,6 +976,7 @@ return null; """) + def str(text): JS(""" if (pyjslib.hasattr(text,"__str__")) { @@ -956,6 +985,7 @@ return String(text); """) + def ord(x): if(isString(x) and len(x) is 1): JS(""" @@ -967,11 +997,13 @@ """) return None + def chr(x): JS(""" return String.fromCharCode(x) """) + def is_basetype(x): JS(""" var t = typeof(x); @@ -983,6 +1015,7 @@ ; """) + def get_pyjs_classtype(x): JS(""" if (pyjslib.hasattr(x, "__class__")) @@ -992,6 +1025,7 @@ return null; """) + def repr(x): """ Return the string representation of 'x'. """ @@ -1088,16 +1122,19 @@ return "<" + constructor + " object>"; """) + def float(text): JS(""" return parseFloat(text); """) + def int(text, radix=0): JS(""" return parseInt(text, radix); """) + def len(object): JS(""" if (object==null) return 0; @@ -1105,11 +1142,12 @@ return object.length; """) + def isinstance(object_, classinfo): if pyjslib.isUndefined(object_): return False if not pyjslib.isObject(object_): - + return False if _isinstance(classinfo, Tuple): for ci in classinfo: @@ -1119,6 +1157,7 @@ else: return _isinstance(object_, classinfo) + def _isinstance(object_, classinfo): if not pyjslib.isObject(object_): return False @@ -1130,6 +1169,7 @@ return false; """) + def getattr(obj, name, default_): JS(""" if ((!pyjslib.isObject(obj))||(pyjslib.isUndefined(obj[name]))){ @@ -1151,6 +1191,7 @@ return fnwrap; """) + def setattr(obj, name, value): JS(""" if (!pyjslib.isObject(obj)) return null; @@ -1159,6 +1200,7 @@ """) + def hasattr(obj, name): JS(""" if (!pyjslib.isObject(obj)) return false; @@ -1167,6 +1209,7 @@ return true; """) + def dir(obj): JS(""" var properties=new pyjslib.List(); @@ -1174,6 +1217,7 @@ return properties; """) + def filter(obj, method, sequence=None): # object context is LOST when a method is passed, hence object must be passed separately # to emulate python behaviour, should generate this code inline rather than as a function call @@ -1240,6 +1284,7 @@ next_hash_id = 0 + def hash(obj): JS(""" if (obj == null) return null; @@ -1259,41 +1304,49 @@ return (a != null && (typeof a == 'object')) || pyjslib.isFunction(a); """) + def isFunction(a): JS(""" return typeof a == 'function'; """) + def isString(a): JS(""" return typeof a == 'string'; """) + def isNull(a): JS(""" return typeof a == 'object' && !a; """) + def isArray(a): JS(""" return pyjslib.isObject(a) && a.constructor == Array; """) + def isUndefined(a): JS(""" return typeof a == 'undefined'; """) + def isIteratable(a): JS(""" return pyjslib.isString(a) || (pyjslib.isObject(a) && a.__iter__); """) + def isNumber(a): JS(""" return typeof a == 'number' && isFinite(a); """) + def toJSObjects(x): """ Convert the pyjs pythonic List and Dict objects into javascript Object and Array @@ -1337,6 +1390,7 @@ """) return x + def printFunc(objs): JS(""" if ($wnd.console==undefined) return; @@ -1348,6 +1402,7 @@ console.debug(s) """) + def type(clsname, bases=None, methods=None): """ creates a class, derived from bases, with methods and variables """ @@ -1362,4 +1417,3 @@ if bases: JS("bss = bases.l;") JS(" return pyjs_type(clsname, bss, mths); ") - diff -r 31e63e25b4cc -r 64beb9e9c749 svgui/pyjs/lib/sys.py --- a/svgui/pyjs/lib/sys.py Mon Aug 21 20:17:19 2017 +0000 +++ b/svgui/pyjs/lib/sys.py Mon Aug 21 23:22:58 2017 +0300 @@ -1,32 +1,37 @@ # the platform name (PyV8, smjs, Mozilla, IE6, Opera, Safari etc.) -platform = '' # to be updated by app, on compile +platform = '' # to be updated by app, on compile # a dictionary of module override names (platform-specific) -overrides = None # to be updated by app, on compile +overrides = None # to be updated by app, on compile # the remote path for loading modules -loadpath = None +loadpath = None -stacktrace = None +stacktrace = None -appname = None +appname = None + def setloadpath(lp): global loadpath loadpath = lp + def setappname(an): global appname appname = an + def getloadpath(): global loadpath return loadpath + def addoverride(module_name, path): global overrides overrides[module_name] = path + def addstack(linedebug): JS(""" if (pyjslib.bool((sys.stacktrace === null))) { @@ -34,11 +39,14 @@ } sys.stacktrace.append(linedebug); """) + + def popstack(): JS(""" sys.stacktrace.pop() """) + def printstack(): JS(""" var res = ''; diff -r 31e63e25b4cc -r 64beb9e9c749 svgui/pyjs/pyjs.py --- a/svgui/pyjs/pyjs.py Mon Aug 21 20:17:19 2017 +0000 +++ b/svgui/pyjs/pyjs.py Mon Aug 21 23:22:58 2017 +0300 @@ -20,6 +20,7 @@ from compiler import ast import os import copy +import cStringIO # the standard location for builtins (e.g. pyjslib) can be # over-ridden by changing this. it defaults to sys.prefix @@ -31,7 +32,7 @@ prefix = sys.prefix -if os.environ.has_key('PYJSPREFIX'): +if 'PYJSPREFIX' in os.environ: prefix = os.environ['PYJSPREFIX'] # pyjs.path is the list of paths, just like sys.path, from which @@ -41,7 +42,7 @@ path = [os.path.abspath('')] -if os.environ.has_key('PYJSPATH'): +if 'PYJSPATH' in os.environ: for p in os.environ['PYJSPATH'].split(os.pathsep): p = os.path.abspath(p) if os.path.isdir(p): @@ -52,43 +53,43 @@ UU = "" -PYJSLIB_BUILTIN_FUNCTIONS=("cmp", - "map", - "filter", - "dir", - "getattr", - "setattr", - "hasattr", - "int", - "float", - "str", - "repr", - "range", - "len", - "hash", - "abs", - "ord", - "chr", - "enumerate", - "min", - "max", - "bool", - "type", - "isinstance") - -PYJSLIB_BUILTIN_CLASSES=("BaseException", - "Exception", - "StandardError", - "StopIteration", - "AttributeError", - "TypeError", - "KeyError", - "LookupError", - "list", - "dict", - "object", - "tuple", - ) +PYJSLIB_BUILTIN_FUNCTIONS = ("cmp", + "map", + "filter", + "dir", + "getattr", + "setattr", + "hasattr", + "int", + "float", + "str", + "repr", + "range", + "len", + "hash", + "abs", + "ord", + "chr", + "enumerate", + "min", + "max", + "bool", + "type", + "isinstance") + +PYJSLIB_BUILTIN_CLASSES = ("BaseException", + "Exception", + "StandardError", + "StopIteration", + "AttributeError", + "TypeError", + "KeyError", + "LookupError", + "list", + "dict", + "object", + "tuple") + def pyjs_builtin_remap(name): # XXX HACK! @@ -102,10 +103,11 @@ name = 'Tuple' return name + # XXX: this is a hack: these should be dealt with another way # however, console is currently the only global name which is causing # problems. -PYJS_GLOBAL_VARS=("console") +PYJS_GLOBAL_VARS = ("console") # This is taken from the django project. # Escape every ASCII character with a value less than 32. @@ -119,17 +121,20 @@ (';', r'\x3B') ) + tuple([('%c' % z, '\\x%02X' % z) for z in range(32)]) + def escapejs(value): """Hex encodes characters for use in JavaScript strings.""" for bad, good in JS_ESCAPES: value = value.replace(bad, good) return value + def uuprefix(name, leave_alone=0): name = name.split(".") name = name[:leave_alone] + map(lambda x: "__%s" % x, name[leave_alone:]) return '.'.join(name) + class Klass: klasses = {} @@ -154,9 +159,11 @@ def __str__(self): return self.message + def strip_py(name): return name + def mod_var_name_decl(raw_module_name): """ function to get the last component of the module e.g. pyjamas.ui.DOM into the "namespace". i.e. doing @@ -174,12 +181,14 @@ child_name = name[-1] return "var %s = %s;\n" % (child_name, raw_module_name) + def gen_mod_import(parentName, importName, dynamic=1): - #pyjs_ajax_eval("%(n)s.cache.js", null, true); + # pyjs_ajax_eval("%(n)s.cache.js", null, true); return """ pyjslib.import_module(sys.loadpath, '%(p)s', '%(n)s', %(d)d, false); """ % ({'p': parentName, 'd': dynamic, 'n': importName}) + \ - mod_var_name_decl(importName) + mod_var_name_decl(importName) + class Translator: @@ -228,7 +237,6 @@ if decl: print >>self.output, decl - if self.debug: haltException = self.module_prefix + "HaltException" print >>self.output, haltException + ' = function () {' @@ -273,14 +281,14 @@ self._class(child) elif isinstance(child, ast.Import): importName = child.names[0][0] - if importName == '__pyjamas__': # special module to help make pyjamas modules loadable in the python interpreter + if importName == '__pyjamas__': # special module to help make pyjamas modules loadable in the python interpreter pass elif importName.endswith('.js'): - self.imported_js.add(importName) + self.imported_js.add(importName) else: - self.add_imported_module(strip_py(importName)) + self.add_imported_module(strip_py(importName)) elif isinstance(child, ast.From): - if child.modname == '__pyjamas__': # special module to help make pyjamas modules loadable in the python interpreter + if child.modname == '__pyjamas__': # special module to help make pyjamas modules loadable in the python interpreter pass else: self.add_imported_module(child.modname) @@ -302,9 +310,9 @@ elif isinstance(child, ast.Global): self._global(child, None) elif isinstance(child, ast.Printnl): - self._print(child, None) + self._print(child, None) elif isinstance(child, ast.Print): - self._print(child, None) + self._print(child, None) elif isinstance(child, ast.TryExcept): self._tryExcept(child, None) elif isinstance(child, ast.Raise): @@ -315,14 +323,14 @@ raise TranslationError("unsupported type (in __init__)", child) # Initialize all classes for this module - #print >> self.output, "__"+self.modpfx()+\ + # print >> self.output, "__"+self.modpfx()+\ # "classes_initialize = function() {\n" - #for className in self.top_level_classes: + # for className in self.top_level_classes: # print >> self.output, "\t"+UU+self.modpfx()+"__"+className+"_initialize();" - #print >> self.output, "};\n" + # print >> self.output, "};\n" print >> self.output, "return this;\n" - print >> self.output, "}; /* end %s */ \n" % module_name + print >> self.output, "}; /* end %s */ \n" % module_name def module_imports(self): return self.imported_modules + self.imported_modules_as @@ -345,7 +353,7 @@ # added to the dependencies, and it's half way up the # module import directory structure! child_name = name[-1] - self.imported_modules_as.append(child_name) + self.imported_modules_as.append(child_name) print >> self.output, gen_mod_import(self.raw_module_name, strip_py(importName), self.dynamic) @@ -396,12 +404,13 @@ # raise TranslationError("unsupported type (in _method)", default_node) default_name = arg_names[default_pos] - print >>self.output, " if (typeof %s == 'undefined')"%(default_name) - print >>self.output, " %s=__kwargs.%s;"% (default_name, default_name) + print >>self.output, " if (typeof %s == 'undefined')" % (default_name) + print >>self.output, " %s=__kwargs.%s;" % (default_name, default_name) default_pos += 1 - #self._default_args_handler(node, arg_names, current_klass) - if node.kwargs: arg_names += ["pyjslib.Dict(__kwargs)"] + # self._default_args_handler(node, arg_names, current_klass) + if node.kwargs: + arg_names += ["pyjslib.Dict(__kwargs)"] print >>self.output, " var __r = "+"".join(["[", ", ".join(arg_names), "]"])+";" if node.varargs: self._varargs_handler(node, "__args", arg_names, current_klass) @@ -418,16 +427,19 @@ arg_names = list(node.argnames) normal_arg_names = list(arg_names) - if node.kwargs: kwargname = normal_arg_names.pop() - if node.varargs: varargname = normal_arg_names.pop() + if node.kwargs: + kwargname = normal_arg_names.pop() + if node.varargs: + varargname = normal_arg_names.pop() declared_arg_names = list(normal_arg_names) - if node.kwargs: declared_arg_names.append(kwargname) + if node.kwargs: + declared_arg_names.append(kwargname) function_args = "(" + ", ".join(declared_arg_names) + ")" print >>self.output, "%s = function%s {" % (function_name, function_args) self._default_args_handler(node, normal_arg_names, None) - local_arg_names = normal_arg_names + declared_arg_names + local_arg_names = normal_arg_names + declared_arg_names if node.varargs: self._varargs_handler(node, varargname, declared_arg_names, None) @@ -451,25 +463,20 @@ print >>self.output, "};" print >>self.output, "%s.__name__ = '%s';\n" % (function_name, node.name) - self._kwargs_parser(node, function_name, normal_arg_names, None) - def _return(self, node, current_klass): expr = self.expr(node.value, current_klass) # in python a function call always returns None, so we do it # here too print >>self.output, " return " + expr + ";" - def _break(self, node, current_klass): print >>self.output, " break;" - def _continue(self, node, current_klass): print >>self.output, " continue;" - def _callfunc(self, v, current_klass): if isinstance(v.node, ast.Name): @@ -477,7 +484,7 @@ call_name = self.modpfx() + v.node.name elif v.node.name in self.top_level_classes: call_name = self.modpfx() + v.node.name - elif self.imported_classes.has_key(v.node.name): + elif v.node.name in self.imported_classes: call_name = self.imported_classes[v.node.name] + '.' + v.node.name elif v.node.name in PYJSLIB_BUILTIN_FUNCTIONS: call_name = 'pyjslib.' + v.node.name @@ -535,18 +542,15 @@ if kwargs or star_arg_name: if not star_arg_name: star_arg_name = 'null' - try: call_this, method_name = call_name.rsplit(".", 1) + try: + call_this, method_name = call_name.rsplit(".", 1) except ValueError: # Must be a function call ... return ("pyjs_kwargs_function_call("+call_name+", " - + star_arg_name - + ", ["+fn_args+"]" - + ")" ) + + star_arg_name + ", ["+fn_args+"]" + ")") else: return ("pyjs_kwargs_method_call("+call_this+", '"+method_name+"', " - + star_arg_name - + ", ["+fn_args+"]" - + ")") + + star_arg_name + ", ["+fn_args+"]" + ")") else: return call_name + "(" + ", ".join(call_args) + ")" @@ -581,19 +585,19 @@ self._stmt(stmt, current_klass) print >> self.output, " } catch(%s) {" % errName if expr: - l = [] + k = [] if isinstance(expr, ast.Tuple): for x in expr.nodes: - l.append("(%(err)s.__name__ == %(expr)s.__name__)" % dict (err=errName, expr=self.expr(x, current_klass))) - else: - l = [ " (%(err)s.__name__ == %(expr)s.__name__) " % dict (err=errName, expr=self.expr(expr, current_klass)) ] - print >> self.output, " if(%s) {" % '||\n\t\t'.join(l) + k.append("(%(err)s.__name__ == %(expr)s.__name__)" % dict(err=errName, expr=self.expr(x, current_klass))) + else: + k = [" (%(err)s.__name__ == %(expr)s.__name__) " % dict(err=errName, expr=self.expr(expr, current_klass))] + print >> self.output, " if(%s) {" % '||\n\t\t'.join(k) for stmt in node.handlers[0][2]: self._stmt(stmt, current_klass) if expr: - #print >> self.output, "} else { throw(%s); } " % errName + # print >> self.output, "} else { throw(%s); } " % errName print >> self.output, "}" - if node.else_ != None: + if node.else_ is not None: print >>self.output, " } finally {" for stmt in node.else_: self._stmt(stmt, current_klass) @@ -605,13 +609,13 @@ attr_name = v.attrname if isinstance(v.expr, ast.Name): obj = self._name(v.expr, current_klass, return_none_for_module=True) - if obj == None and v.expr.name in self.module_imports(): + if obj is None and v.expr.name in self.module_imports(): # XXX TODO: distinguish between module import classes # and variables. right now, this is a hack to get # the sys module working. - #if v.expr.name == 'sys': + # if v.expr.name == 'sys': return v.expr.name+'.'+attr_name - #return v.expr.name+'.__'+attr_name+'.prototype.__class__' + # return v.expr.name+'.__'+attr_name+'.prototype.__class__' if not use_getattr or attr_name == '__class__' or \ attr_name == '__name__': return obj + "." + attr_name @@ -625,12 +629,11 @@ else: raise TranslationError("unsupported type (in _getattr)", v.expr) - def modpfx(self): return strip_py(self.module_prefix) - + def _name(self, v, current_klass, top_level=False, - return_none_for_module=False): + return_none_for_module=False): if v.name == 'ilikesillynamesfornicedebugcode': print top_level, current_klass, repr(v) @@ -662,14 +665,14 @@ return UU+self.modpfx() + v.name elif v.name in local_var_names: return v.name - elif self.imported_classes.has_key(v.name): + elif v.name in self.imported_classes: return UU+self.imported_classes[v.name] + '.__' + v.name + ".prototype.__class__" elif v.name in self.top_level_classes: return UU+self.modpfx() + "__" + v.name + ".prototype.__class__" elif v.name in self.module_imports() and return_none_for_module: return None elif v.name in PYJSLIB_BUILTIN_CLASSES: - return "pyjslib." + pyjs_builtin_remap( v.name ) + return "pyjslib." + pyjs_builtin_remap(v.name) elif current_klass: if v.name not in local_var_names and \ v.name not in self.top_level_vars and \ @@ -681,9 +684,8 @@ cls_name_ = cls_name.name_ cls_name = cls_name.name else: - cls_name_ = current_klass + "_" # XXX ??? - name = UU+cls_name_ + ".prototype.__class__." \ - + v.name + cls_name_ = current_klass + "_" # XXX ??? + name = UU+cls_name_ + ".prototype.__class__." + v.name if v.name == 'listener': name = 'listener+' + name return name @@ -695,33 +697,31 @@ if obj in self.method_imported_globals: call_name = UU+self.modpfx() + obj + "." + attr_name - elif self.imported_classes.has_key(obj): - #attr_str = "" - #if attr_name != "__init__": + elif obj in self.imported_classes: + # attr_str = "" + # if attr_name != "__init__": attr_str = ".prototype.__class__." + attr_name call_name = UU+self.imported_classes[obj] + '.__' + obj + attr_str elif obj in self.module_imports(): call_name = obj + "." + attr_name - elif obj[0] == obj[0].upper(): # XXX HACK ALERT + elif obj[0] == obj[0].upper(): # XXX HACK ALERT call_name = UU + self.modpfx() + "__" + obj + ".prototype.__class__." + attr_name else: call_name = UU+self._name(v, current_klass) + "." + attr_name return call_name - def _getattr2(self, v, current_klass, attr_name): if isinstance(v.expr, ast.Getattr): call_name = self._getattr2(v.expr, current_klass, v.attrname + "." + attr_name) elif isinstance(v.expr, ast.Name) and v.expr.name in self.module_imports(): - call_name = UU+v.expr.name + '.__' +v.attrname+".prototype.__class__."+attr_name + call_name = UU+v.expr.name + '.__' + v.attrname+".prototype.__class__."+attr_name else: obj = self.expr(v.expr, current_klass) call_name = obj + "." + v.attrname + "." + attr_name return call_name - def _class(self, node): """ Handle a class definition. @@ -761,12 +761,11 @@ if child.name == "__init__": init_method = child - if len(node.bases) == 0: base_class = "pyjslib.__Object" elif len(node.bases) == 1: if isinstance(node.bases[0], ast.Name): - if self.imported_classes.has_key(node.bases[0].name): + if node.bases[0].name in self.imported_classes: base_class_ = self.imported_classes[node.bases[0].name] + '.__' + node.bases[0].name base_class = self.imported_classes[node.bases[0].name] + '.' + node.bases[0].name else: @@ -777,8 +776,9 @@ # pass our class to self._name base_class_ = self._name(node.bases[0].expr, None) + \ ".__" + node.bases[0].attrname - base_class = self._name(node.bases[0].expr, None) + \ - "." + node.bases[0].attrname + base_class = \ + self._name(node.bases[0].expr, None) + \ + "." + node.bases[0].attrname else: raise TranslationError("unsupported type (in _class)", node.bases[0]) @@ -788,28 +788,28 @@ print >>self.output, UU+class_name_ + " = function () {" # call superconstructor - #if base_class: + # if base_class: # print >>self.output, " __" + base_class + ".call(this);" print >>self.output, "}" if not init_method: init_method = ast.Function([], "__init__", ["self"], [], 0, None, []) - #self._method(init_method, current_klass, class_name) + # self._method(init_method, current_klass, class_name) # Generate a function which constructs the object - clsfunc = ast.Function([], - node.name, - init_method.argnames[1:], - init_method.defaults, - init_method.flags, - None, - [ast.Discard(ast.CallFunc(ast.Name("JS"), [ast.Const( -# I attempted lazy initialization, but then you can't access static class members -# " if(!__"+base_class+".__was_initialized__)"+ -# " __" + class_name + "_initialize();\n" + - " var instance = new " + UU + class_name_ + "();\n" + - " if(instance.__init__) instance.__init__.apply(instance, arguments);\n" + - " return instance;" + clsfunc = ast.Function( + [], node.name, + init_method.argnames[1:], + init_method.defaults, + init_method.flags, + None, + [ast.Discard(ast.CallFunc(ast.Name("JS"), [ast.Const( + # I attempted lazy initialization, but then you can't access static class members + # " if(!__"+base_class+".__was_initialized__)"+ + # " __" + class_name + "_initialize();\n" + + " var instance = new " + UU + class_name_ + "();\n" + + " if(instance.__init__) instance.__init__.apply(instance, arguments);\n" + + " return instance;" )]))]) self._function(clsfunc, False) @@ -847,7 +847,6 @@ print >> self.output, class_name_+".__initialize__();" - def classattr(self, node, current_klass): self._assign(node, current_klass, True) @@ -876,22 +875,25 @@ if staticmethod: staticfunc = ast.Function([], class_name_+"."+node.name, node.argnames, node.defaults, node.flags, node.doc, node.code, node.lineno) self._function(staticfunc, True) - print >>self.output, " " + UU+class_name_ + ".prototype.__class__." + node.name + " = " + class_name_+"."+node.name+";"; - print >>self.output, " " + UU+class_name_ + ".prototype.__class__." + node.name + ".static_method = true;"; + print >>self.output, " " + UU+class_name_ + ".prototype.__class__." + node.name + " = " + class_name_+"."+node.name+";" + print >>self.output, " " + UU+class_name_ + ".prototype.__class__." + node.name + ".static_method = true;" return else: if len(arg_names) == 0: raise TranslationError("methods must take an argument 'self' (in _method)", node) self.method_self = arg_names[0] - #if not classmethod and arg_names[0] != "self": + # if not classmethod and arg_names[0] != "self": # raise TranslationError("first arg not 'self' (in _method)", node) normal_arg_names = arg_names[1:] - if node.kwargs: kwargname = normal_arg_names.pop() - if node.varargs: varargname = normal_arg_names.pop() + if node.kwargs: + kwargname = normal_arg_names.pop() + if node.varargs: + varargname = normal_arg_names.pop() declared_arg_names = list(normal_arg_names) - if node.kwargs: declared_arg_names.append(kwargname) + if node.kwargs: + declared_arg_names.append(kwargname) function_args = "(" + ", ".join(declared_arg_names) + ")" @@ -904,13 +906,12 @@ # default arguments self._default_args_handler(node, normal_arg_names, current_klass) - local_arg_names = normal_arg_names + declared_arg_names + local_arg_names = normal_arg_names + declared_arg_names if node.varargs: self._varargs_handler(node, varargname, declared_arg_names, current_klass) local_arg_names.append(varargname) - # stack of local variable names for this function call self.local_arg_stack.append(local_arg_names) @@ -944,7 +945,7 @@ print >>self.output, " "+altexpr+".__name__ = '%s';" % node.name print >>self.output, UU + class_name_ + ".prototype.%s.__name__ = '%s';" % \ - (node.name, node.name) + (node.name, node.name) if node.kwargs or len(node.defaults): print >>self.output, " "+altexpr + ".parse_kwargs = " + fexpr + ".parse_kwargs;" @@ -992,9 +993,9 @@ elif isinstance(node, ast.Function): self._function(node, True) elif isinstance(node, ast.Printnl): - self._print(node, current_klass) + self._print(node, current_klass) elif isinstance(node, ast.Print): - self._print(node, current_klass) + self._print(node, current_klass) elif isinstance(node, ast.TryExcept): self._tryExcept(node, current_klass) elif isinstance(node, ast.Raise): @@ -1009,30 +1010,28 @@ haltException = self.module_prefix + "HaltException" isHaltFunction = self.module_prefix + "IsHaltException" - print >>self.output, ' } catch (__err) {' - print >>self.output, ' if (' + isHaltFunction + '(__err.name)) {' - print >>self.output, ' throw __err;' - print >>self.output, ' } else {' - print >>self.output, " st = sys.printstack() + "\ - + '"%s"' % lt + "+ '\\n' ;" - print >>self.output, ' alert("' + "Error in " \ - + lt + '"' \ - + '+"\\n"+__err.name+": "+__err.message'\ - + '+"\\n\\nStack trace:\\n"' \ - + '+st' \ - + ');' - print >>self.output, ' debugger;' - - print >>self.output, ' throw new ' + self.module_prefix + "HaltException();" - print >>self.output, ' }' - print >>self.output, ' }' - + out = ( + ' } catch (__err) {', + ' if (' + isHaltFunction + '(__err.name)) {', + ' throw __err;', + ' } else {', + ' st = sys.printstack() + ' + '"%s"' % lt + "+ '\\n' ;" + ' alert("' + 'Error in ' + lt + '"' + + '+"\\n"+__err.name+": "+__err.message' + + '+"\\n\\nStack trace:\\n"' + '+st' + ');', + ' debugger;', + ' throw new ' + self.module_prefix + 'HaltException();', + ' }', + ' }' + ) + for s in out: + print >>self.output, s def get_line_trace(self, node): lineNum = "Unknown" srcLine = "" if hasattr(node, "lineno"): - if node.lineno != None: + if node.lineno is not None: lineNum = node.lineno srcLine = self.src[min(lineNum, len(self.src))-1] srcLine = srcLine.replace('\\', '\\\\') @@ -1040,9 +1039,9 @@ srcLine = srcLine.replace("'", "\\'") return self.raw_module_name + ".py, line " \ - + str(lineNum) + ":"\ - + "\\n" \ - + " " + srcLine + + str(lineNum) + ":"\ + + "\\n" \ + + " " + srcLine def _augassign(self, node, current_klass): v = node.node @@ -1056,15 +1055,14 @@ rhs = self.expr(node.expr, current_klass) print >>self.output, " " + lhs + " " + op + " " + rhs + ";" - - def _assign(self, node, current_klass, top_level = False): + def _assign(self, node, current_klass, top_level=False): if len(node.nodes) != 1: tempvar = '__temp'+str(node.lineno) tnode = ast.Assign([ast.AssName(tempvar, "OP_ASSIGN", node.lineno)], node.expr, node.lineno) self._assign(tnode, current_klass, top_level) for v in node.nodes: - tnode2 = ast.Assign([v], ast.Name(tempvar, node.lineno), node.lineno) - self._assign(tnode2, current_klass, top_level) + tnode2 = ast.Assign([v], ast.Name(tempvar, node.lineno), node.lineno) + self._assign(tnode2, current_klass, top_level) return local_var_names = None @@ -1093,7 +1091,7 @@ self.top_level_vars.add(v.name) vname = self.modpfx() + v.name if not self.modpfx() and v.name not in\ - self.method_imported_globals: + self.method_imported_globals: lhs = "var " + vname else: lhs = UU + vname @@ -1140,7 +1138,7 @@ tempName = "__tupleassign" + str(uniqueID) + "__" print >>self.output, " var " + tempName + " = " + \ self.expr(node.expr, current_klass) + ";" - for index,child in enumerate(v.getChildNodes()): + for index, child in enumerate(v.getChildNodes()): rhs = tempName + ".__getitem__(" + str(index) + ")" if isinstance(child, ast.AssAttr): @@ -1156,7 +1154,7 @@ idx = self.expr(child.subs[0], current_klass) value = self.expr(node.expr, current_klass) print >>self.output, " " + obj + ".__setitem__(" \ - + idx + ", " + rhs + ");" + + idx + ", " + rhs + ");" continue print >>self.output, " " + lhs + " = " + rhs + ";" return @@ -1168,14 +1166,13 @@ print "b", repr(node.expr), rhs print >>self.output, " " + lhs + " " + op + " " + rhs + ";" - def _discard(self, node, current_klass): - + if isinstance(node.expr, ast.CallFunc): debugStmt = self.debug and not self._isNativeFunc(node) if debugStmt and isinstance(node.expr.node, ast.Name) and \ node.expr.node.name == 'import_wait': - debugStmt = False + debugStmt = False if debugStmt: st = self.get_line_trace(node) print >>self.output, "sys.addstack('%s');\n" % st @@ -1194,12 +1191,11 @@ print >>self.output, "sys.popstack();\n" elif isinstance(node.expr, ast.Const): - if node.expr.value is not None: # Empty statements generate ignore None + if node.expr.value is not None: # Empty statements generate ignore None print >>self.output, self._const(node.expr) else: raise TranslationError("unsupported type (in _discard)", node.expr) - def _if(self, node, current_klass): for i in range(len(node.tests)): test, consequence = node.tests[i] @@ -1217,7 +1213,6 @@ self._if_test(keyword, test, consequence, current_klass) - def _if_test(self, keyword, test, consequence, current_klass): if test: expr = self.expr(test, current_klass) @@ -1234,7 +1229,6 @@ print >>self.output, " }" - def _from(self, node): for name in node.names: # look up "hack" in AppTranslator as to how findFile gets here @@ -1248,7 +1242,6 @@ else: self.imported_classes[name[0]] = node.modname - def _compare(self, node, current_klass): lhs = self.expr(node.expr, current_klass) @@ -1272,7 +1265,6 @@ return "(" + lhs + " " + op + " " + rhs + ")" - def _not(self, node, current_klass): expr = self.expr(node.expr, current_klass) @@ -1341,7 +1333,6 @@ } """ % locals() - def _while(self, node, current_klass): test = self.expr(node.test, current_klass) print >>self.output, " while (pyjslib.bool(" + test + ")) {" @@ -1352,7 +1343,6 @@ raise TranslationError("unsupported type (in _while)", node.body) print >>self.output, " }" - def _const(self, node): if isinstance(node.value, int): return str(node.value) @@ -1362,7 +1352,7 @@ v = node.value if isinstance(node.value, unicode): v = v.encode('utf-8') - return "String('%s')" % escapejs(v) + return "String('%s')" % escapejs(v) elif node.value is None: return "null" else: @@ -1388,8 +1378,8 @@ def _mod(self, node, current_klass): if isinstance(node.left, ast.Const) and isinstance(node.left.value, StringType): - self.imported_js.add("sprintf.js") # Include the sprintf functionality if it is used - return "sprintf("+self.expr(node.left, current_klass) + ", " + self.expr(node.right, current_klass)+")" + self.imported_js.add("sprintf.js") # Include the sprintf functionality if it is used + return "sprintf("+self.expr(node.left, current_klass) + ", " + self.expr(node.right, current_klass)+")" return self.expr(node.left, current_klass) + " % " + self.expr(node.right, current_klass) def _invert(self, node, current_klass): @@ -1404,7 +1394,7 @@ def _bitshiftright(self, node, current_klass): return self.expr(node.left, current_klass) + " >>> " + self.expr(node.right, current_klass) - def _bitxor(self,node, current_klass): + def _bitxor(self, node, current_klass): return " ^ ".join([self.expr(child, current_klass) for child in node.nodes]) def _bitor(self, node, current_klass): @@ -1459,11 +1449,11 @@ if node.flags == "OP_APPLY": lower = "null" upper = "null" - if node.lower != None: + if node.lower is not None: lower = self.expr(node.lower, current_klass) - if node.upper != None: + if node.upper is not None: upper = self.expr(node.upper, current_klass) - return "pyjslib.slice(" + self.expr(node.expr, current_klass) + ", " + lower + ", " + upper + ")" + return "pyjslib.slice(" + self.expr(node.expr, current_klass) + ", " + lower + ", " + upper + ")" else: raise TranslationError("unsupported flag (in _slice)", node) @@ -1499,7 +1489,7 @@ return self._invert(node, current_klass) elif isinstance(node, ast.Bitand): return "("+self._bitand(node, current_klass)+")" - elif isinstance(node,ast.LeftShift): + elif isinstance(node, ast.LeftShift): return self._bitshiftleft(node, current_klass) elif isinstance(node, ast.RightShift): return self._bitshiftright(node, current_klass) @@ -1531,9 +1521,6 @@ raise TranslationError("unsupported type (in expr)", node) - -import cStringIO - def translate(file_name, module_name, debug=False): f = file(file_name, "r") src = f.read() @@ -1545,7 +1532,7 @@ class PlatformParser: - def __init__(self, platform_dir = "", verbose=True): + def __init__(self, platform_dir="", verbose=True): self.platform_dir = platform_dir self.parse_cache = {} self.platform = "" @@ -1557,7 +1544,7 @@ def parseModule(self, module_name, file_name): importing = False - if not self.parse_cache.has_key(file_name): + if file_name not in self.parse_cache: importing = True mod = compiler.parseFile(file_name) self.parse_cache[file_name] = mod @@ -1631,12 +1618,14 @@ target.code = source.code target.argnames = source.argnames target.defaults = source.defaults - target.doc = source.doc # @@@ not sure we need to do this any more + target.doc = source.doc # @@@ not sure we need to do this any more + def dotreplace(fname): path, ext = os.path.splitext(fname) return path.replace(".", "/") + ext + class AppTranslator: def __init__(self, library_dirs=[], parser=None, dynamic=False, @@ -1693,7 +1682,7 @@ mod, override = self.parser.parseModule(module_name, file_name) if override: override_name = "%s.%s" % (self.parser.platform.lower(), - module_name) + module_name) self.overrides[override_name] = override_name if is_app: mn = '__main__' @@ -1709,13 +1698,12 @@ for module in t.imported_modules: if module not in self.library_modules: self.library_modules.append(module) - #imported_js.update(set(t.imported_js)) - #imported_modules_str += self._translate( + # imported_js.update(set(t.imported_js)) + # imported_modules_str += self._translate( # module, False, debug=debug, imported_js=imported_js) return imported_modules_str + module_str - def translate(self, module_name, is_app=True, debug=False, library_modules=[]): app_code = cStringIO.StringIO() @@ -1742,24 +1730,26 @@ print >> app_code, self._translate( module_name, is_app, debug=debug, imported_js=imported_js) for js in imported_js: - path = self.findFile(js) - if os.path.isfile(path): - if self.verbose: - print 'Including JS', js - print >> lib_code, '\n//\n// BEGIN JS '+js+'\n//\n' - print >> lib_code, file(path).read() - print >> lib_code, '\n//\n// END JS '+js+'\n//\n' - else: - print >>sys.stderr, 'Warning: Unable to find imported javascript:', js + path = self.findFile(js) + if os.path.isfile(path): + if self.verbose: + print 'Including JS', js + print >> lib_code, '\n//\n// BEGIN JS '+js+'\n//\n' + print >> lib_code, file(path).read() + print >> lib_code, '\n//\n// END JS '+js+'\n//\n' + else: + print >>sys.stderr, 'Warning: Unable to find imported javascript:', js return lib_code.getvalue(), app_code.getvalue() + usage = """ usage: %s file_name [module_name] """ + def main(): import sys - if len(sys.argv)<2: + if len(sys.argv) < 2: print >> sys.stderr, usage % sys.argv[0] sys.exit(1) file_name = os.path.abspath(sys.argv[1]) @@ -1772,6 +1762,6 @@ module_name = None print translate(file_name, module_name), + if __name__ == "__main__": main() - diff -r 31e63e25b4cc -r 64beb9e9c749 svgui/svgui.py --- a/svgui/svgui.py Mon Aug 21 20:17:19 2017 +0000 +++ b/svgui/svgui.py Mon Aug 21 23:22:58 2017 +0300 @@ -24,7 +24,9 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. import wx -import os, sys, shutil +import os +import sys +import shutil from pyjs import translate @@ -33,21 +35,27 @@ from docutil import open_svg from py_ext import PythonFileCTNMixin + class SVGUILibrary(POULibrary): def GetLibraryPath(self): - return paths.AbsNeighbourFile(__file__, "pous.xml") + return paths.AbsNeighbourFile(__file__, "pous.xml") + class SVGUI(PythonFileCTNMixin): ConfNodeMethods = [ - {"bitmap" : "ImportSVG", - "name" : _("Import SVG"), - "tooltip" : _("Import SVG"), - "method" : "_ImportSVG"}, - {"bitmap" : "ImportSVG", # should be something different - "name" : _("Inkscape"), - "tooltip" : _("Create HMI"), - "method" : "_StartInkscape"}, + { + "bitmap": "ImportSVG", + "name": _("Import SVG"), + "tooltip": _("Import SVG"), + "method": "_ImportSVG" + }, + { + "bitmap": "ImportSVG", # should be something different + "name": _("Inkscape"), + "tooltip": _("Create HMI"), + "method": "_StartInkscape" + }, ] def ConfNodePath(self): @@ -70,21 +78,21 @@ 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 """ - + current_location = self.GetCurrentLocation() # define a unique name for the generated C file - location_str = "_".join(map(lambda x:str(x), current_location)) - + location_str = "_".join(map(lambda x: str(x), current_location)) + res = ([], "", False) - - svgfile=self._getSVGpath() + + svgfile = self._getSVGpath() if os.path.exists(svgfile): - res += (("gui.svg", file(svgfile,"rb")),) + res += (("gui.svg", file(svgfile, "rb")),) svguiserverfile = open(self._getSVGUIserverpath(), 'r') svguiservercode = svguiserverfile.read() @@ -92,7 +100,7 @@ svguilibpath = os.path.join(self._getBuildPath(), "svguilib.js") svguilibfile = open(svguilibpath, 'w') - fpath=paths.AbsDir(__file__) + fpath = paths.AbsDir(__file__) svguilibfile.write(translate(os.path.join(fpath, "pyjs", "lib", "sys.py"), "sys")) svguilibfile.write(open(os.path.join(fpath, "pyjs", "lib", "_pyjs.js"), 'r').read()) svguilibfile.write(translate(os.path.join(fpath, "pyjs", "lib", "pyjslib.py"), "pyjslib")) @@ -102,26 +110,25 @@ svguilibfile.write(open(os.path.join(fpath, "livesvg.js"), 'r').read()) svguilibfile.close() jsmodules = {"LiveSVGPage": "svguilib.js"} - res += (("svguilib.js", file(svguilibpath,"rb")),) - - runtimefile_path = os.path.join(buildpath, "runtime_%s.py"%location_str) + res += (("svguilib.js", file(svguilibpath, "rb")),) + + runtimefile_path = os.path.join(buildpath, "runtime_%s.py" % location_str) runtimefile = open(runtimefile_path, 'w') - runtimefile.write(svguiservercode % {"svgfile" : "gui.svg"}) + runtimefile.write(svguiservercode % {"svgfile": "gui.svg"}) runtimefile.write(""" def _runtime_%(location)s_start(): website.LoadHMI(%(svgui_class)s, %(jsmodules)s) - + def _runtime_%(location)s_stop(): website.UnLoadHMI() - -""" % {"location": location_str, - "svgui_class": "SVGUI_HMI", - "jsmodules" : str(jsmodules), - }) + + """ % {"location": location_str, + "svgui_class": "SVGUI_HMI", + "jsmodules": str(jsmodules)}) runtimefile.close() - - res += (("runtime_%s.py"%location_str, file(runtimefile_path,"rb")),) - + + res += (("runtime_%s.py" % location_str, file(runtimefile_path, "rb")),) + return res def _ImportSVG(self): @@ -131,8 +138,8 @@ if os.path.isfile(svgpath): shutil.copy(svgpath, self._getSVGpath()) else: - self.GetCTRoot().logger.write_error(_("No such SVG file: %s\n")%svgpath) - dialog.Destroy() + self.GetCTRoot().logger.write_error(_("No such SVG file: %s\n") % svgpath) + dialog.Destroy() def _StartInkscape(self): svgfile = self._getSVGpath() @@ -141,7 +148,7 @@ dialog = wx.MessageDialog(self.GetCTRoot().AppFrame, _("You don't have write permissions.\nOpen Inkscape anyway ?"), _("Open Inkscape"), - wx.YES_NO|wx.ICON_QUESTION) + wx.YES_NO | wx.ICON_QUESTION) open_inkscape = dialog.ShowModal() == wx.ID_YES dialog.Destroy() if open_inkscape: diff -r 31e63e25b4cc -r 64beb9e9c749 svgui/svgui_server.py --- a/svgui/svgui_server.py Mon Aug 21 20:17:19 2017 +0000 +++ b/svgui/svgui_server.py Mon Aug 21 23:22:58 2017 +0300 @@ -32,13 +32,16 @@ svguiWidgets = {} currentId = 0 + + def getNewId(): global currentId currentId += 1 return currentId + class SvguiWidget: - + def __init__(self, classname, id, **kwargs): self.classname = classname self.id = id @@ -50,9 +53,9 @@ def setinput(self, attrname, value): self.inputs[attrname] = value - + def getinput(self, attrname, default=None): - if not self.inputs.has_key(attrname): + if attrname not in self.inputs: self.inputs[attrname] = default return self.inputs[attrname] @@ -61,14 +64,14 @@ self.outputs[attrname] = value self.changed = True self.RefreshInterface() - + def updateoutputs(self, **kwargs): for attrname, value in kwargs.iteritems(): if self.outputs.get(attrname) != value: self.outputs[attrname] = value self.changed = True self.RefreshInterface() - + def RefreshInterface(self): interface = website.getHMI() if isinstance(interface, SVGUI_HMI) and self.changed and not self.inhibit: @@ -77,52 +80,58 @@ if d is not None: self.inhibit = True d.addCallback(self.InterfaceRefreshed) - + def InterfaceRefreshed(self, result): self.inhibit = False if self.changed: self.RefreshInterface() + def get_object_init_state(obj): # Convert objects to a dictionary of their representation attrs = obj.attrs.copy() attrs.update(obj.inputs) - d = { '__class__': obj.classname, - 'id': obj.id, - 'kwargs': json.dumps(attrs), - } + d = { + '__class__': obj.classname, + 'id': obj.id, + 'kwargs': json.dumps(attrs), + } return d + def get_object_current_state(obj): # Convert objects to a dictionary of their representation - d = { '__class__': obj.classname, - 'id': obj.id, - 'kwargs': json.dumps(obj.outputs), - } + d = { + '__class__': obj.classname, + 'id': obj.id, + 'kwargs': json.dumps(obj.outputs), + } return d + class SVGUI_HMI(website.PLCHMI): jsClass = u"LiveSVGPage.LiveSVGWidget" - - docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[ + + docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[ tags.xml(loaders.xmlfile(os.path.join(WorkingDir, svgfile))), ]) - + def HMIinitialisation(self): gadgets = [] for gadget in svguiWidgets.values(): gadgets.append(unicode(json.dumps(gadget, default=get_object_init_state, indent=2), 'ascii')) d = self.callRemote('init', gadgets) d.addCallback(self.HMIinitialised) - - def sendData(self,data): + + def sendData(self, data): if self.initialised: - return self.callRemote('receiveData',unicode(json.dumps(data, default=get_object_current_state, indent=2), 'ascii')) + return self.callRemote('receiveData', unicode(json.dumps(data, default=get_object_current_state, indent=2), 'ascii')) return None - + def setattr(self, id, attrname, value): svguiWidgets[id].setinput(attrname, value) + def createSVGUIControl(*args, **kwargs): id = getNewId() gad = SvguiWidget(args[0], id, **kwargs) @@ -133,19 +142,21 @@ interface.callRemote('init', gadget) return id + def setAttr(id, attrname, value): gad = svguiWidgets.get(id, None) if gad is not None: gad.setoutput(attrname, value) + def updateAttr(id, **kwargs): gad = svguiWidgets.get(id, None) if gad is not None: gad.updateoutput(**kwargs) + def getAttr(id, attrname, default=None): gad = svguiWidgets.get(id, None) if gad is not None: return gad.getinput(attrname, default) return default - diff -r 31e63e25b4cc -r 64beb9e9c749 svgui/svguilib.py --- a/svgui/svguilib.py Mon Aug 21 20:17:19 2017 +0000 +++ b/svgui/svguilib.py Mon Aug 21 23:22:58 2017 +0300 @@ -22,8 +22,9 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + class button: - + def __init__(self, parent, id, args): self.parent = parent self.id = id @@ -40,19 +41,19 @@ self.up = not self.state else: self.up = True - + # Add event on each element of the button if self.active: self.back_elt.addEventListener("mouseup", self, False) self.back_elt.addEventListener("mousedown", self, False) self.back_elt.addEventListener("mouseover", self, False) self.back_elt.addEventListener("mouseout", self, False) - + self.sele_elt.addEventListener("mouseup", self, False) self.sele_elt.addEventListener("mousedown", self, False) self.sele_elt.addEventListener("mouseover", self, False) self.sele_elt.addEventListener("mouseout", self, False) - + blockSVGElementDrag(self.back_elt) blockSVGElementDrag(self.sele_elt) @@ -66,7 +67,7 @@ else: self.sele_elt.removeAttribute("display") self.back_elt.setAttribute("display", "none") - + def updateValues(self, values): if values.state != self.state: self.state = values.state @@ -80,9 +81,9 @@ if evt.type == "mousedown": evt.stopPropagation() setCurrentObject(self) - + self.dragging = True - + if self.toggle: self.up = self.state else: @@ -90,18 +91,18 @@ self.state = True updateAttr(self.id, 'state', self.state) self.updateElements() - + if isCurrentObject(self) and self.dragging: # Quand le bouton est survole if evt.type == "mouseover" and self.toggle: self.up = self.state self.updateElements() - + # Quand le curseur quitte la zone du bouton - elif evt.type == "mouseout" and self.toggle: + elif evt.type == "mouseout" and self.toggle: self.up = not self.state self.updateElements() - + # Quand le bouton de la souris est relache elif evt.type == "mouseup": evt.stopPropagation() @@ -115,8 +116,9 @@ self.updateElements() self.dragging = False + class textControl: - + def __init__(self, parent, id, args): self.parent = parent self.id = id @@ -126,17 +128,15 @@ else: self.text = "" self.updateElements() - + def updateValues(self, values): if values.text != self.value: self.text = values.text updateAttr(self.id, 'text', self.text) self.updateElements() - + def updateElements(self): self.back_elt.firstChild.firstChild.textContent = self.text - + def handleEvent(self, evt): pass - - diff -r 31e63e25b4cc -r 64beb9e9c749 targets/Generic/__init__.py --- a/targets/Generic/__init__.py Mon Aug 21 20:17:19 2017 +0000 +++ b/targets/Generic/__init__.py Mon Aug 21 23:22:58 2017 +0300 @@ -24,5 +24,6 @@ from ..toolchain_makefile import toolchain_makefile + class Generic_target(toolchain_makefile): pass diff -r 31e63e25b4cc -r 64beb9e9c749 targets/Linux/__init__.py --- a/targets/Linux/__init__.py Mon Aug 21 20:17:19 2017 +0000 +++ b/targets/Linux/__init__.py Mon Aug 21 23:22:58 2017 +0300 @@ -24,10 +24,13 @@ from ..toolchain_gcc import toolchain_gcc + class Linux_target(toolchain_gcc): dlopen_prefix = "./" extension = ".so" + def getBuilderCFLAGS(self): return toolchain_gcc.getBuilderCFLAGS(self) + ["-fPIC"] + def getBuilderLDFLAGS(self): return toolchain_gcc.getBuilderLDFLAGS(self) + ["-shared", "-lrt"] diff -r 31e63e25b4cc -r 64beb9e9c749 targets/Win32/__init__.py --- a/targets/Win32/__init__.py Mon Aug 21 20:17:19 2017 +0000 +++ b/targets/Win32/__init__.py Mon Aug 21 23:22:58 2017 +0300 @@ -24,8 +24,10 @@ from ..toolchain_gcc import toolchain_gcc + class Win32_target(toolchain_gcc): dlopen_prefix = "" extension = ".dll" + def getBuilderLDFLAGS(self): return toolchain_gcc.getBuilderLDFLAGS(self) + ["-shared", "-lwinmm"] diff -r 31e63e25b4cc -r 64beb9e9c749 targets/Xenomai/__init__.py --- a/targets/Xenomai/__init__.py Mon Aug 21 20:17:19 2017 +0000 +++ b/targets/Xenomai/__init__.py Mon Aug 21 23:22:58 2017 +0300 @@ -24,22 +24,24 @@ from ..toolchain_gcc import toolchain_gcc + class Xenomai_target(toolchain_gcc): dlopen_prefix = "./" extension = ".so" + def getXenoConfig(self, flagsname): """ Get xeno-config from target parameters """ - xeno_config=self.CTRInstance.GetTarget().getcontent().getXenoConfig() + xeno_config = self.CTRInstance.GetTarget().getcontent().getXenoConfig() if xeno_config: from util.ProcessLogger import ProcessLogger status, result, err_result = ProcessLogger(self.CTRInstance.logger, xeno_config + " --skin=native --"+flagsname, no_stdout=True).spin() if status: - self.CTRInstance.logger.write_error(_("Unable to get Xenomai's %s \n")%flagsname) + self.CTRInstance.logger.write_error(_("Unable to get Xenomai's %s \n") % flagsname) return [result.strip()] return [] - + def getBuilderLDFLAGS(self): xeno_ldflags = self.getXenoConfig("ldflags") return toolchain_gcc.getBuilderLDFLAGS(self) + xeno_ldflags + ["-shared"] @@ -47,4 +49,3 @@ def getBuilderCFLAGS(self): xeno_cflags = self.getXenoConfig("cflags") return toolchain_gcc.getBuilderCFLAGS(self) + xeno_cflags + ["-fPIC"] - diff -r 31e63e25b4cc -r 64beb9e9c749 targets/__init__.py --- a/targets/__init__.py Mon Aug 21 20:17:19 2017 +0000 +++ b/targets/__init__.py Mon Aug 21 23:22:58 2017 +0300 @@ -1,89 +1,95 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# This file is part of Beremiz, a Integrated Development Environment for -# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. -# -# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD -# Copyright (C) 2017: Andrey Skvortsov -# -# See COPYING file for copyrights details. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -# Package initialisation -#import targets - -""" -Beremiz Targets - -- Target are python packages, containing at least one "XSD" file -- Target class may inherit from a toolchain_(toolchainname) -- The target folder's name must match to name define in the XSD for TargetType -""" - -from os import listdir, path -import util.paths as paths - -_base_path = paths.AbsDir(__file__) -def _GetLocalTargetClassFactory(name): - return lambda:getattr(__import__(name,globals(),locals()), name+"_target") - -targets = dict([(name, {"xsd":path.join(_base_path, name, "XSD"), - "class":_GetLocalTargetClassFactory(name), - "code": { fname: path.join(_base_path, name, fname) - for fname in listdir(path.join(_base_path, name)) - if fname.startswith("plc_%s_main"%name) and - fname.endswith(".c")}}) - for name in listdir(_base_path) - if path.isdir(path.join(_base_path, name)) - and not name.startswith("__")]) - -toolchains = {"gcc": path.join(_base_path, "XSD_toolchain_gcc"), - "makefile": path.join(_base_path, "XSD_toolchain_makefile")} - -def GetBuilder(targetname): - return targets[targetname]["class"]() - -def GetTargetChoices(): - DictXSD_toolchain = {} - targetchoices = "" - - # Get all xsd toolchains - for toolchainname,xsdfilename in toolchains.iteritems() : - if path.isfile(xsdfilename): - DictXSD_toolchain["toolchain_"+toolchainname] = \ - open(xsdfilename).read() - - # Get all xsd targets - for targetname,nfo in targets.iteritems(): - xsd_string = open(nfo["xsd"]).read() - targetchoices += xsd_string%DictXSD_toolchain - - return targetchoices - -def GetTargetCode(targetname): - codedesc = targets[targetname]["code"] - code = "\n".join([open(fpath).read() for fname, fpath in sorted(codedesc.items())]) - return code - -def GetHeader(): - filename = paths.AbsNeighbourFile(__file__,"beremiz.h") - return open(filename).read() - -def GetCode(name): - filename = paths.AbsNeighbourFile(__file__,name) - return open(filename).read() - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# This file is part of Beremiz, a Integrated Development Environment for +# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. +# +# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov +# +# See COPYING file for copyrights details. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# Package initialisation + + +""" +Beremiz Targets + +- Target are python packages, containing at least one "XSD" file +- Target class may inherit from a toolchain_(toolchainname) +- The target folder's name must match to name define in the XSD for TargetType +""" + +from os import listdir, path +import util.paths as paths + +_base_path = paths.AbsDir(__file__) + + +def _GetLocalTargetClassFactory(name): + return lambda: getattr(__import__(name, globals(), locals()), name+"_target") + + +targets = dict([(name, {"xsd": path.join(_base_path, name, "XSD"), + "class": _GetLocalTargetClassFactory(name), + "code": {fname: path.join(_base_path, name, fname) + for fname in listdir(path.join(_base_path, name)) + if (fname.startswith("plc_%s_main" % name) and + fname.endswith(".c"))}}) + for name in listdir(_base_path) + if (path.isdir(path.join(_base_path, name)) + and not name.startswith("__"))]) + +toolchains = {"gcc": path.join(_base_path, "XSD_toolchain_gcc"), + "makefile": path.join(_base_path, "XSD_toolchain_makefile")} + + +def GetBuilder(targetname): + return targets[targetname]["class"]() + + +def GetTargetChoices(): + DictXSD_toolchain = {} + targetchoices = "" + + # Get all xsd toolchains + for toolchainname, xsdfilename in toolchains.iteritems(): + if path.isfile(xsdfilename): + DictXSD_toolchain["toolchain_"+toolchainname] = open(xsdfilename).read() + + # Get all xsd targets + for targetname, nfo in targets.iteritems(): + xsd_string = open(nfo["xsd"]).read() + targetchoices += xsd_string % DictXSD_toolchain + + return targetchoices + + +def GetTargetCode(targetname): + codedesc = targets[targetname]["code"] + code = "\n".join([open(fpath).read() for fname, fpath in sorted(codedesc.items())]) + return code + + +def GetHeader(): + filename = paths.AbsNeighbourFile(__file__, "beremiz.h") + return open(filename).read() + + +def GetCode(name): + filename = paths.AbsNeighbourFile(__file__, name) + return open(filename).read() diff -r 31e63e25b4cc -r 64beb9e9c749 targets/toolchain_gcc.py --- a/targets/toolchain_gcc.py Mon Aug 21 20:17:19 2017 +0000 +++ b/targets/toolchain_gcc.py Mon Aug 21 23:22:58 2017 +0300 @@ -1,237 +1,239 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# This file is part of Beremiz, a Integrated Development Environment for -# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. -# -# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD -# Copyright (C) 2017: Paul Beltyukov -# -# See COPYING file for copyrights details. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -import os, re, operator -from util.ProcessLogger import ProcessLogger -import hashlib - -includes_re = re.compile('\s*#include\s*["<]([^">]*)[">].*') - -class toolchain_gcc(): - """ - This abstract class contains GCC specific code. - It cannot be used as this and should be inherited in a target specific - class such as target_linux or target_win32 - """ - def __init__(self, CTRInstance): - self.CTRInstance = CTRInstance - self.buildpath = None - self.SetBuildPath(self.CTRInstance._getBuildPath()) - - def getBuilderCFLAGS(self): - """ - Returns list of builder specific CFLAGS - """ - return [self.CTRInstance.GetTarget().getcontent().getCFLAGS()] - - def getBuilderLDFLAGS(self): - """ - Returns list of builder specific LDFLAGS - """ - return self.CTRInstance.LDFLAGS + \ - [self.CTRInstance.GetTarget().getcontent().getLDFLAGS()] - - def getCompiler(self): - """ - Returns compiler - """ - return self.CTRInstance.GetTarget().getcontent().getCompiler() - - def getLinker(self): - """ - Returns linker - """ - return self.CTRInstance.GetTarget().getcontent().getLinker() - - def GetBinaryCode(self): - try: - return open(self.exe_path, "rb").read() - except Exception, e: - return None - - def _GetMD5FileName(self): - return os.path.join(self.buildpath, "lastbuildPLC.md5") - - def ResetBinaryCodeMD5(self): - self.md5key = None - try: - os.remove(self._GetMD5FileName()) - except Exception, e: - pass - - def GetBinaryCodeMD5(self): - if self.md5key is not None: - return self.md5key - else: - try: - return open(self._GetMD5FileName(), "r").read() - except Exception, e: - return None - - def SetBuildPath(self, buildpath): - if self.buildpath != buildpath: - self.buildpath = buildpath - self.exe = self.CTRInstance.GetProjectName() + self.extension - self.exe_path = os.path.join(self.buildpath, self.exe) - self.md5key = None - self.srcmd5 = {} - - def append_cfile_deps(self, src, deps): - for l in src.splitlines(): - res = includes_re.match(l) - if res is not None: - depfn = res.groups()[0] - if os.path.exists(os.path.join(self.buildpath, depfn)): - deps.append(depfn) - - def concat_deps(self, bn): - # read source - src = open(os.path.join(self.buildpath, bn),"r").read() - # update direct dependencies - deps = [] - self.append_cfile_deps(src, deps) - # recurse through deps - # TODO detect cicular deps. - return reduce(operator.concat, map(self.concat_deps, deps), src) - - def check_and_update_hash_and_deps(self, bn): - # Get latest computed hash and deps - oldhash, deps = self.srcmd5.get(bn,(None,[])) - # read source - src = open(os.path.join(self.buildpath, bn)).read() - # compute new hash - newhash = hashlib.md5(src).hexdigest() - # compare - match = (oldhash == newhash) - if not match: - # file have changed - # update direct dependencies - deps = [] - self.append_cfile_deps(src, deps) - # store that hashand deps - self.srcmd5[bn] = (newhash, deps) - # recurse through deps - # TODO detect cicular deps. - return reduce(operator.and_, map(self.check_and_update_hash_and_deps, deps), match) - - def calc_source_md5(self): - wholesrcdata = "" - for Location, CFilesAndCFLAGS, DoCalls in self.CTRInstance.LocationCFilesAndCFLAGS: - # Get CFiles list to give it to makefile - for CFile, CFLAGS in CFilesAndCFLAGS: - CFileName = os.path.basename(CFile) - wholesrcdata += self.concat_deps(CFileName) - return hashlib.md5(wholesrcdata).hexdigest() - - def calc_md5(self): - return hashlib.md5(self.GetBinaryCode()).hexdigest() - - def build(self): - # Retrieve compiler and linker - self.compiler = self.getCompiler() - self.linker = self.getLinker() - - Builder_CFLAGS = ' '.join(self.getBuilderCFLAGS()) - - ######### GENERATE OBJECT FILES ######################################## - obns = [] - objs = [] - relink = self.GetBinaryCode() is None - for Location, CFilesAndCFLAGS, DoCalls in self.CTRInstance.LocationCFilesAndCFLAGS: - if CFilesAndCFLAGS: - if Location : - self.CTRInstance.logger.write(".".join(map(str,Location))+" :\n") - else: - self.CTRInstance.logger.write(_("PLC :\n")) - - for CFile, CFLAGS in CFilesAndCFLAGS: - if CFile.endswith(".c"): - bn = os.path.basename(CFile) - obn = os.path.splitext(bn)[0]+".o" - objectfilename = os.path.splitext(CFile)[0]+".o" - - match = self.check_and_update_hash_and_deps(bn) - - if match: - self.CTRInstance.logger.write(" [pass] "+bn+" -> "+obn+"\n") - else: - relink = True - - self.CTRInstance.logger.write(" [CC] "+bn+" -> "+obn+"\n") - - status, result, err_result = ProcessLogger( - self.CTRInstance.logger, - "\"%s\" -c \"%s\" -o \"%s\" %s %s"% - (self.compiler, CFile, objectfilename, Builder_CFLAGS, CFLAGS) - ).spin() - - if status : - self.srcmd5.pop(bn) - self.CTRInstance.logger.write_error(_("C compilation of %s failed.\n")%bn) - return False - obns.append(obn) - objs.append(objectfilename) - elif CFile.endswith(".o"): - obns.append(os.path.basename(CFile)) - objs.append(CFile) - - ######### GENERATE OUTPUT FILE ######################################## - # Link all the object files into one binary file - self.CTRInstance.logger.write(_("Linking :\n")) - if relink: - objstring = [] - - # Generate list .o files - listobjstring = '"' + '" "'.join(objs) + '"' - - ALLldflags = ' '.join(self.getBuilderLDFLAGS()) - - self.CTRInstance.logger.write(" [CC] " + ' '.join(obns)+" -> " + self.exe + "\n") - - status, result, err_result = ProcessLogger( - self.CTRInstance.logger, - "\"%s\" %s -o \"%s\" %s"% - (self.linker, - listobjstring, - self.exe_path, - ALLldflags) - ).spin() - - if status : - return False - - else: - self.CTRInstance.logger.write(" [pass] " + ' '.join(obns)+" -> " + self.exe + "\n") - - # Calculate md5 key and get data for the new created PLC - self.md5key = self.calc_md5() - - # Store new PLC filename based on md5 key - f = open(self._GetMD5FileName(), "w") - f.write(self.md5key) - f.close() - - return True - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# This file is part of Beremiz, a Integrated Development Environment for +# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. +# +# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Paul Beltyukov +# +# See COPYING file for copyrights details. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +import os +import re +import operator +from util.ProcessLogger import ProcessLogger +import hashlib + +includes_re = re.compile('\s*#include\s*["<]([^">]*)[">].*') + + +class toolchain_gcc(): + """ + This abstract class contains GCC specific code. + It cannot be used as this and should be inherited in a target specific + class such as target_linux or target_win32 + """ + def __init__(self, CTRInstance): + self.CTRInstance = CTRInstance + self.buildpath = None + self.SetBuildPath(self.CTRInstance._getBuildPath()) + + def getBuilderCFLAGS(self): + """ + Returns list of builder specific CFLAGS + """ + return [self.CTRInstance.GetTarget().getcontent().getCFLAGS()] + + def getBuilderLDFLAGS(self): + """ + Returns list of builder specific LDFLAGS + """ + return self.CTRInstance.LDFLAGS + \ + [self.CTRInstance.GetTarget().getcontent().getLDFLAGS()] + + def getCompiler(self): + """ + Returns compiler + """ + return self.CTRInstance.GetTarget().getcontent().getCompiler() + + def getLinker(self): + """ + Returns linker + """ + return self.CTRInstance.GetTarget().getcontent().getLinker() + + def GetBinaryCode(self): + try: + return open(self.exe_path, "rb").read() + except Exception, e: + return None + + def _GetMD5FileName(self): + return os.path.join(self.buildpath, "lastbuildPLC.md5") + + def ResetBinaryCodeMD5(self): + self.md5key = None + try: + os.remove(self._GetMD5FileName()) + except Exception, e: + pass + + def GetBinaryCodeMD5(self): + if self.md5key is not None: + return self.md5key + else: + try: + return open(self._GetMD5FileName(), "r").read() + except Exception, e: + return None + + def SetBuildPath(self, buildpath): + if self.buildpath != buildpath: + self.buildpath = buildpath + self.exe = self.CTRInstance.GetProjectName() + self.extension + self.exe_path = os.path.join(self.buildpath, self.exe) + self.md5key = None + self.srcmd5 = {} + + def append_cfile_deps(self, src, deps): + for l in src.splitlines(): + res = includes_re.match(l) + if res is not None: + depfn = res.groups()[0] + if os.path.exists(os.path.join(self.buildpath, depfn)): + deps.append(depfn) + + def concat_deps(self, bn): + # read source + src = open(os.path.join(self.buildpath, bn), "r").read() + # update direct dependencies + deps = [] + self.append_cfile_deps(src, deps) + # recurse through deps + # TODO detect cicular deps. + return reduce(operator.concat, map(self.concat_deps, deps), src) + + def check_and_update_hash_and_deps(self, bn): + # Get latest computed hash and deps + oldhash, deps = self.srcmd5.get(bn, (None, [])) + # read source + src = open(os.path.join(self.buildpath, bn)).read() + # compute new hash + newhash = hashlib.md5(src).hexdigest() + # compare + match = (oldhash == newhash) + if not match: + # file have changed + # update direct dependencies + deps = [] + self.append_cfile_deps(src, deps) + # store that hashand deps + self.srcmd5[bn] = (newhash, deps) + # recurse through deps + # TODO detect cicular deps. + return reduce(operator.and_, map(self.check_and_update_hash_and_deps, deps), match) + + def calc_source_md5(self): + wholesrcdata = "" + for Location, CFilesAndCFLAGS, DoCalls in self.CTRInstance.LocationCFilesAndCFLAGS: + # Get CFiles list to give it to makefile + for CFile, CFLAGS in CFilesAndCFLAGS: + CFileName = os.path.basename(CFile) + wholesrcdata += self.concat_deps(CFileName) + return hashlib.md5(wholesrcdata).hexdigest() + + def calc_md5(self): + return hashlib.md5(self.GetBinaryCode()).hexdigest() + + def build(self): + # Retrieve compiler and linker + self.compiler = self.getCompiler() + self.linker = self.getLinker() + + Builder_CFLAGS = ' '.join(self.getBuilderCFLAGS()) + + # ----------------- GENERATE OBJECT FILES ------------------------ + obns = [] + objs = [] + relink = self.GetBinaryCode() is None + for Location, CFilesAndCFLAGS, DoCalls in self.CTRInstance.LocationCFilesAndCFLAGS: + if CFilesAndCFLAGS: + if Location: + self.CTRInstance.logger.write(".".join(map(str, Location))+" :\n") + else: + self.CTRInstance.logger.write(_("PLC :\n")) + + for CFile, CFLAGS in CFilesAndCFLAGS: + if CFile.endswith(".c"): + bn = os.path.basename(CFile) + obn = os.path.splitext(bn)[0]+".o" + objectfilename = os.path.splitext(CFile)[0]+".o" + + match = self.check_and_update_hash_and_deps(bn) + + if match: + self.CTRInstance.logger.write(" [pass] "+bn+" -> "+obn+"\n") + else: + relink = True + + self.CTRInstance.logger.write(" [CC] "+bn+" -> "+obn+"\n") + + status, result, err_result = ProcessLogger( + self.CTRInstance.logger, + "\"%s\" -c \"%s\" -o \"%s\" %s %s" % + (self.compiler, CFile, objectfilename, Builder_CFLAGS, CFLAGS) + ).spin() + + if status: + self.srcmd5.pop(bn) + self.CTRInstance.logger.write_error(_("C compilation of %s failed.\n") % bn) + return False + obns.append(obn) + objs.append(objectfilename) + elif CFile.endswith(".o"): + obns.append(os.path.basename(CFile)) + objs.append(CFile) + + # ---------------- GENERATE OUTPUT FILE -------------------------- + # Link all the object files into one binary file + self.CTRInstance.logger.write(_("Linking :\n")) + if relink: + objstring = [] + + # Generate list .o files + listobjstring = '"' + '" "'.join(objs) + '"' + + ALLldflags = ' '.join(self.getBuilderLDFLAGS()) + + self.CTRInstance.logger.write(" [CC] " + ' '.join(obns)+" -> " + self.exe + "\n") + + status, result, err_result = ProcessLogger( + self.CTRInstance.logger, + "\"%s\" %s -o \"%s\" %s" % + (self.linker, + listobjstring, + self.exe_path, + ALLldflags) + ).spin() + + if status: + return False + + else: + self.CTRInstance.logger.write(" [pass] " + ' '.join(obns)+" -> " + self.exe + "\n") + + # Calculate md5 key and get data for the new created PLC + self.md5key = self.calc_md5() + + # Store new PLC filename based on md5 key + f = open(self._GetMD5FileName(), "w") + f.write(self.md5key) + f.close() + + return True diff -r 31e63e25b4cc -r 64beb9e9c749 targets/toolchain_makefile.py --- a/targets/toolchain_makefile.py Mon Aug 21 20:17:19 2017 +0000 +++ b/targets/toolchain_makefile.py Mon Aug 21 23:22:58 2017 +0300 @@ -22,18 +22,21 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import os, re, operator +import os +import re +import operator from util.ProcessLogger import ProcessLogger import hashlib import time -includes_re = re.compile('\s*#include\s*["<]([^">]*)[">].*') +includes_re = re.compile('\s*#include\s*["<]([^">]*)[">].*') + class toolchain_makefile(): def __init__(self, CTRInstance): self.CTRInstance = CTRInstance - self.md5key = None + self.md5key = None self.buildpath = None self.SetBuildPath(self.CTRInstance._getBuildPath()) @@ -66,7 +69,7 @@ def concat_deps(self, bn): # read source - src = open(os.path.join(self.buildpath, bn),"r").read() + src = open(os.path.join(self.buildpath, bn), "r").read() # update direct dependencies deps = [] for l in src.splitlines(): @@ -74,16 +77,16 @@ if res is not None: depfn = res.groups()[0] if os.path.exists(os.path.join(self.buildpath, depfn)): - #print bn + " depends on "+depfn + # print bn + " depends on "+depfn deps.append(depfn) # recurse through deps # TODO detect cicular deps. return reduce(operator.concat, map(self.concat_deps, deps), src) def build(self): - srcfiles= [] + srcfiles = [] cflags = [] - wholesrcdata = "" + wholesrcdata = "" for Location, CFilesAndCFLAGS, DoCalls in self.CTRInstance.LocationCFilesAndCFLAGS: # Get CFiles list to give it to makefile for CFile, CFLAGS in CFilesAndCFLAGS: @@ -92,7 +95,7 @@ srcfiles.append(CFileName) if CFLAGS not in cflags: cflags.append(CFLAGS) - + oldmd5 = self.md5key self.md5key = hashlib.md5(wholesrcdata).hexdigest() @@ -101,28 +104,26 @@ f.write(self.md5key) f.close() - if oldmd5 != self.md5key : + if oldmd5 != self.md5key: target = self.CTRInstance.GetTarget().getcontent() beremizcommand = {"src": ' '.join(srcfiles), "cflags": ' '.join(cflags), "md5": self.md5key, - "buildpath": self.buildpath - } - - # clean sequence of multiple whitespaces + "buildpath": self.buildpath} + + # clean sequence of multiple whitespaces cmd = re.sub(r"[ ]+", " ", target.getCommand().strip()) - command = [ token % beremizcommand for token in cmd.split(' ')] + command = [token % beremizcommand for token in cmd.split(' ')] # Call Makefile to build PLC code and link it with target specific code status, result, err_result = ProcessLogger(self.CTRInstance.logger, command).spin() - if status : + if status: self.md5key = None self.CTRInstance.logger.write_error(_("C compilation failed.\n")) return False return True - else : + else: self.CTRInstance.logger.write(_("Source didn't change, no build.\n")) return True - diff -r 31e63e25b4cc -r 64beb9e9c749 targets/typemapping.py --- a/targets/typemapping.py Mon Aug 21 20:17:19 2017 +0000 +++ b/targets/typemapping.py Mon Aug 21 23:22:58 2017 +0300 @@ -23,13 +23,13 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import ctypes +from ctypes import * +from datetime import timedelta as td + ctypes.pythonapi.PyString_AsString.argtypes = (ctypes.c_void_p,) ctypes.pythonapi.PyString_AsString.restype = ctypes.POINTER(ctypes.c_char) -from ctypes import * -from datetime import timedelta as td - class IEC_STRING(Structure): """ Must be changed according to changes in iec_types.h @@ -37,68 +37,73 @@ _fields_ = [("len", c_uint8), ("body", c_char * 126)] + class IEC_TIME(Structure): """ Must be changed according to changes in iec_types.h """ - _fields_ = [("s", c_long), #tv_sec - ("ns", c_long)] #tv_nsec + _fields_ = [("s", c_long), # tv_sec + ("ns", c_long)] # tv_nsec -def _t(t, u=lambda x:x.value, p=lambda t,x:t(x)): return (t, u, p) + +def _t(t, u=lambda x: x.value, p=lambda t, x: t(x)): return (t, u, p) + + def _ttime(): return (IEC_TIME, - lambda x:td(0, x.s, x.ns/1000), - lambda t,x:t(x.days * 24 * 3600 + x.seconds, x.microseconds*1000)) + lambda x: td(0, x.s, x.ns/1000), + lambda t, x: t(x.days * 24 * 3600 + x.seconds, x.microseconds*1000)) + SameEndianessTypeTranslator = { - "BOOL" : _t(c_uint8, lambda x:x.value!=0), - "STEP" : _t(c_uint8), - "TRANSITION" : _t(c_uint8), - "ACTION" : _t(c_uint8), - "SINT" : _t(c_int8), - "USINT" : _t(c_uint8), - "BYTE" : _t(c_uint8), - "STRING" : (IEC_STRING, - lambda x:x.body[:x.len], - lambda t,x:t(len(x),x)), - "INT" : _t(c_int16), - "UINT" : _t(c_uint16), - "WORD" : _t(c_uint16), - "DINT" : _t(c_int32), - "UDINT" : _t(c_uint32), - "DWORD" : _t(c_uint32), - "LINT" : _t(c_int64), - "ULINT" : _t(c_uint64), - "LWORD" : _t(c_uint64), - "REAL" : _t(c_float), - "LREAL" : _t(c_double), - "TIME" : _ttime(), - "TOD" : _ttime(), - "DATE" : _ttime(), - "DT" : _ttime(), + "BOOL": _t(c_uint8, lambda x: x.value != 0), + "STEP": _t(c_uint8), + "TRANSITION": _t(c_uint8), + "ACTION": _t(c_uint8), + "SINT": _t(c_int8), + "USINT": _t(c_uint8), + "BYTE": _t(c_uint8), + "STRING": (IEC_STRING, + lambda x: x.body[:x.len], + lambda t, x: t(len(x), x)), + "INT": _t(c_int16), + "UINT": _t(c_uint16), + "WORD": _t(c_uint16), + "DINT": _t(c_int32), + "UDINT": _t(c_uint32), + "DWORD": _t(c_uint32), + "LINT": _t(c_int64), + "ULINT": _t(c_uint64), + "LWORD": _t(c_uint64), + "REAL": _t(c_float), + "LREAL": _t(c_double), + "TIME": _ttime(), + "TOD": _ttime(), + "DATE": _ttime(), + "DT": _ttime(), } SwapedEndianessTypeTranslator = { - #TODO + # TODO } -TypeTranslator=SameEndianessTypeTranslator +TypeTranslator = SameEndianessTypeTranslator # Construct debugger natively supported types -DebugTypesSize = dict([(key,sizeof(t)) for key,(t,p,u) in SameEndianessTypeTranslator.iteritems() if t is not None]) +DebugTypesSize = dict([(key, sizeof(t)) for key, (t, p, u) in SameEndianessTypeTranslator.iteritems() if t is not None]) + def UnpackDebugBuffer(buff, indexes): - res = [] + res = [] buffoffset = 0 buffsize = len(buff) - buffptr = cast(ctypes.pythonapi.PyString_AsString(id(buff)),c_void_p).value + buffptr = cast(ctypes.pythonapi.PyString_AsString(id(buff)), c_void_p).value for iectype in indexes: - c_type,unpack_func, pack_func = \ - TypeTranslator.get(iectype, - (None,None,None)) + c_type, unpack_func, pack_func = \ + TypeTranslator.get(iectype, (None, None, None)) if c_type is not None and buffoffset < buffsize: - cursor = c_void_p( buffptr + buffoffset) - value = unpack_func( cast(cursor, - POINTER(c_type)).contents) + cursor = c_void_p(buffptr + buffoffset) + value = unpack_func(cast(cursor, + POINTER(c_type)).contents) buffoffset += sizeof(c_type) if iectype != "STRING" else len(value)+1 res.append(value) else: @@ -108,9 +113,7 @@ return None - -LogLevels = ["CRITICAL","WARNING","INFO","DEBUG"] +LogLevels = ["CRITICAL", "WARNING", "INFO", "DEBUG"] LogLevelsCount = len(LogLevels) -LogLevelsDict = dict(zip(LogLevels,range(LogLevelsCount))) +LogLevelsDict = dict(zip(LogLevels, range(LogLevelsCount))) LogLevelsDefault = LogLevelsDict["DEBUG"] - diff -r 31e63e25b4cc -r 64beb9e9c749 util/BitmapLibrary.py --- a/util/BitmapLibrary.py Mon Aug 21 20:17:19 2017 +0000 +++ b/util/BitmapLibrary.py Mon Aug 21 23:22:58 2017 +0300 @@ -26,45 +26,48 @@ import wx -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Library Structures -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- BitmapLibrary = {} BitmapFolders = [] -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Library Helpers -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + def AddBitmapFolder(path): if os.path.exists(path) and os.path.isdir(path) and path not in BitmapFolders: BitmapFolders.append(path) + def SearchBitmap(bmp_name): for folder in BitmapFolders: bmp_path = os.path.join(folder, bmp_name + ".png") if os.path.isfile(bmp_path): return wx.Bitmap(bmp_path) return None - + + def GetBitmap(bmp_name1, bmp_name2=None, size=None): bmp = BitmapLibrary.get((bmp_name1, bmp_name2, size)) if bmp is not None: return bmp - + if bmp_name2 is None: bmp = SearchBitmap(bmp_name1) else: # Bitmap with two icon bmp1 = SearchBitmap(bmp_name1) bmp2 = SearchBitmap(bmp_name2) - + if bmp1 is not None and bmp2 is not None: # Calculate bitmap size width = bmp1.GetWidth() + bmp2.GetWidth() - 1 height = max(bmp1.GetHeight(), bmp2.GetHeight()) - + # Create bitmap with both icons bmp = wx.EmptyBitmap(width, height) dc = wx.MemoryDC() @@ -73,13 +76,13 @@ dc.DrawBitmap(bmp1, 0, 0) dc.DrawBitmap(bmp2, bmp1.GetWidth() - 1, 0) dc.Destroy() - + elif bmp1 is not None: bmp = bmp1 elif bmp2 is not None: bmp = bmp2 - + if bmp is not None: BitmapLibrary[(bmp_name1, bmp_name2, size)] = bmp - + return bmp diff -r 31e63e25b4cc -r 64beb9e9c749 util/MiniTextControler.py --- a/util/MiniTextControler.py Mon Aug 21 20:17:19 2017 +0000 +++ b/util/MiniTextControler.py Mon Aug 21 23:22:58 2017 +0300 @@ -28,49 +28,50 @@ import os + class MiniTextControler: - + def __init__(self, filepath, controller): self.FilePath = filepath self.BaseController = controller - + def __del__(self): self.BaseController = None - + def CTNFullName(self): return "" - + def SetEditedElementText(self, tagname, text): file = open(self.FilePath, "w") file.write(text) file.close() - - def GetEditedElementText(self, tagname, debug = False): + + def GetEditedElementText(self, tagname, debug=False): if os.path.isfile(self.FilePath): file = open(self.FilePath, "r") text = file.read() file.close() return text return "" - - def GetEditedElementInterfaceVars(self, tagname, tree=False, debug = False): + + def GetEditedElementInterfaceVars(self, tagname, tree=False, debug=False): return [] - - def GetEditedElementType(self, tagname, debug = False): + + def GetEditedElementType(self, tagname, debug=False): return "program" - - def GetBlockType(self, type, inputs = None, debug = False): + + def GetBlockType(self, type, inputs=None, debug=False): return self.BaseController.GetBlockType(type, inputs, debug) - - def GetBlockTypes(self, tagname = "", debug = False): + + def GetBlockTypes(self, tagname="", debug=False): return self.BaseController.GetBlockTypes(tagname, debug) - - def GetDataTypes(self, tagname = "", basetypes = True, only_locatables = False, debug = False): + + def GetDataTypes(self, tagname="", basetypes=True, only_locatables=False, debug=False): return self.BaseController.GetDataTypes(tagname, basetypes, only_locatables, debug) - - def GetEnumeratedDataValues(self, debug = False): + + def GetEnumeratedDataValues(self, debug=False): return self.BaseController.GetEnumeratedDataValues(debug) - + def StartBuffering(self): pass @@ -79,4 +80,3 @@ def BufferProject(self): pass - diff -r 31e63e25b4cc -r 64beb9e9c749 util/ProcessLogger.py --- a/util/ProcessLogger.py Mon Aug 21 20:17:19 2017 +0000 +++ b/util/ProcessLogger.py Mon Aug 21 23:22:58 2017 +0300 @@ -22,11 +22,13 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +import os +import sys import time import wx -import subprocess, ctypes +import subprocess +import ctypes from threading import Timer, Lock, Thread, Semaphore -import os, sys if os.name == 'posix': from signal import SIGTERM, SIGKILL @@ -48,36 +50,39 @@ def run(self): outchunk = None self.retval = None - while self.retval is None and not self.killed : + while self.retval is None and not self.killed: if self.endcallback: self.retval = self.Proc.poll() else: self.retval = self.Proc.returncode - + outchunk = self.fd.readline() - if self.callback : self.callback(outchunk) - while outchunk != '' and not self.killed : + if self.callback: + self.callback(outchunk) + while outchunk != '' and not self.killed: outchunk = self.fd.readline() - if self.callback : self.callback(outchunk) + if self.callback: + self.callback(outchunk) if self.endcallback: try: err = self.Proc.wait() - except: + except Exception: err = self.retval self.finished = True self.endcallback(self.Proc.pid, err) + class ProcessLogger: - def __init__(self, logger, Command, finish_callback = None, - no_stdout = False, no_stderr = False, no_gui = True, - timeout = None, outlimit = None, errlimit = None, - endlog = None, keyword = None, kill_it = False, cwd = None, - encoding = None): + def __init__(self, logger, Command, finish_callback=None, + no_stdout=False, no_stderr=False, no_gui=True, + timeout=None, outlimit=None, errlimit=None, + endlog=None, keyword=None, kill_it=False, cwd=None, + encoding=None): self.logger = logger if not isinstance(Command, list): self.Command_str = Command self.Command = [] - for i,word in enumerate(Command.replace("'",'"').split('"')): + for i, word in enumerate(Command.replace("'", '"').split('"')): if i % 2 == 0: word = word.strip() if len(word) > 0: @@ -108,17 +113,18 @@ self.errdata = [] self.keyword = keyword self.kill_it = kill_it - self.startsem = Semaphore(0) + self.startsem = Semaphore(0) self.finishsem = Semaphore(0) self.endlock = Lock() - popenargs= { - "cwd":os.getcwd() if cwd is None else cwd, - "stdin":subprocess.PIPE, - "stdout":subprocess.PIPE, - "stderr":subprocess.PIPE} - - if no_gui == True and wx.Platform == '__WXMSW__': + popenargs = { + "cwd": os.getcwd() if cwd is None else cwd, + "stdin": subprocess.PIPE, + "stdout": subprocess.PIPE, + "stderr": subprocess.PIPE + } + + if no_gui and wx.Platform == '__WXMSW__': self.startupinfo = subprocess.STARTUPINFO() self.startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW popenargs["startupinfo"] = self.startupinfo @@ -126,12 +132,12 @@ popenargs["shell"] = False if timeout: - self.timeout = Timer(timeout,self.endlog) + self.timeout = Timer(timeout, self.endlog) self.timeout.start() else: self.timeout = None - - self.Proc = subprocess.Popen( self.Command, **popenargs ) + + self.Proc = subprocess.Popen(self.Command, **popenargs) self.outt = outputThread( self.Proc, @@ -147,16 +153,15 @@ self.errt.start() self.startsem.release() - - def output(self,v): + def output(self, v): self.outdata.append(v) self.outlen += 1 if not self.no_stdout: self.logger.write(v) - if (self.keyword and v.find(self.keyword)!=-1) or (self.outlimit and self.outlen > self.outlimit): + if (self.keyword and v.find(self.keyword) != -1) or (self.outlimit and self.outlen > self.outlimit): self.endlog() - def errors(self,v): + def errors(self, v): self.errdata.append(v) self.errlen += 1 if not self.no_stderr: @@ -164,28 +169,28 @@ if self.errlimit and self.errlen > self.errlimit: self.endlog() - def log_the_end(self,ecode,pid): + def log_the_end(self, ecode, pid): self.logger.write(self.Command_str + "\n") - self.logger.write_warning(_("exited with status {a1} (pid {a2})\n").format(a1 = str(ecode), a2 = str(pid))) - - def finish(self, pid,ecode): - # avoid running function before start is finished + self.logger.write_warning(_("exited with status {a1} (pid {a2})\n").format(a1=str(ecode), a2=str(pid))) + + def finish(self, pid, ecode): + # avoid running function before start is finished self.startsem.acquire() if self.timeout: self.timeout.cancel() self.exitcode = ecode if self.exitcode != 0: - self.log_the_end(ecode,pid) + self.log_the_end(ecode, pid) if self.finish_callback is not None: - self.finish_callback(self,ecode,pid) + self.finish_callback(self, ecode, pid) self.errt.join() self.finishsem.release() - def kill(self,gently=True): + def kill(self, gently=True): # avoid running kill before start is finished self.startsem.acquire() self.startsem.release() - + self.outt.killed = True self.errt.killed = True if wx.Platform == '__WXMSW__': @@ -195,12 +200,12 @@ ctypes.windll.kernel32.CloseHandle(handle) else: if gently: - sig=SIGTERM + sig = SIGTERM else: - sig=SIGKILL + sig = SIGKILL try: os.kill(self.Proc.pid, sig) - except: + except Exception: pass self.outt.join() self.errt.join() @@ -208,11 +213,9 @@ def endlog(self): if self.endlock.acquire(False): if not self.outt.finished and self.kill_it: - self.kill() + self.kill() self.finishsem.release() - def spin(self): self.finishsem.acquire() return [self.exitcode, "".join(self.outdata), "".join(self.errdata)] - diff -r 31e63e25b4cc -r 64beb9e9c749 util/TranslationCatalogs.py --- a/util/TranslationCatalogs.py Mon Aug 21 20:17:19 2017 +0000 +++ b/util/TranslationCatalogs.py Mon Aug 21 23:22:58 2017 +0300 @@ -22,7 +22,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import os +import os import wx @@ -32,6 +32,7 @@ # Define locale for wx locale = wx.Locale(langid) + def GetDomain(path): for name in os.listdir(path): filepath = os.path.join(path, name) @@ -44,9 +45,14 @@ return basename return None + def AddCatalog(locale_dir): if os.path.exists(locale_dir) and os.path.isdir(locale_dir): domain = GetDomain(locale_dir) if domain is not None: locale.AddCatalogLookupPathPrefix(locale_dir) locale.AddCatalog(domain) + + +def NoTranslate(x): + return x diff -r 31e63e25b4cc -r 64beb9e9c749 util/Zeroconf.py --- a/util/Zeroconf.py Mon Aug 21 20:17:19 2017 +0000 +++ b/util/Zeroconf.py Mon Aug 21 23:22:58 2017 +0300 @@ -1,82 +1,78 @@ -""" Multicast DNS Service Discovery for Python, v0.12 - Copyright (C) 2003, Paul Scott-Murphy - - This module provides a framework for the use of DNS Service Discovery - using IP multicast. It has been tested against the JRendezvous - implementation from StrangeBerry, - and against the mDNSResponder from Mac OS X 10.3.8. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -""" - -"""0.12 update - allow selection of binding interface - typo fix - Thanks A. M. Kuchlingi - removed all use of word 'Rendezvous' - this is an API change""" - -"""0.11 update - correction to comments for addListener method - support for new record types seen from OS X - - IPv6 address - - hostinfo - ignore unknown DNS record types - fixes to name decoding - works alongside other processes using port 5353 (e.g. on Mac OS X) - tested against Mac OS X 10.3.2's mDNSResponder - corrections to removal of list entries for service browser""" - -"""0.10 update - Jonathon Paisley contributed these corrections: - always multicast replies, even when query is unicast - correct a pointer encoding problem - can now write records in any order - traceback shown on failure - better TXT record parsing - server is now separate from name - can cancel a service browser - - modified some unit tests to accommodate these changes""" - -"""0.09 update - remove all records on service unregistration - fix DOS security problem with readName""" - -"""0.08 update - changed licensing to LGPL""" - -"""0.07 update - faster shutdown on engine - pointer encoding of outgoing names - ServiceBrowser now works - new unit tests""" - -"""0.06 update - small improvements with unit tests - added defined exception types - new style objects - fixed hostname/interface problem - fixed socket timeout problem - fixed addServiceListener() typo bug - using select() for socket reads - tested on Debian unstable with Python 2.2.2""" - -"""0.05 update - ensure case insensitivty on domain names - support for unicast DNS queries""" - -"""0.04 update - added some unit tests - added __ne__ adjuncts where required - ensure names end in '.local.' - timeout on receiving socket for clean shutdown""" - -__author__ = "Paul Scott-Murphy" -__email__ = "paul at scott dash murphy dot com" -__version__ = "0.12" +# Multicast DNS Service Discovery for Python, v0.12 +# Copyright (C) 2003, Paul Scott-Murphy +# +# This module provides a framework for the use of DNS Service Discovery +# using IP multicast. It has been tested against the JRendezvous +# implementation from StrangeBerry, +# and against the mDNSResponder from Mac OS X 10.3.8. + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# +# +# 0.12 update - allow selection of binding interface +# typo fix - Thanks A. M. Kuchlingi +# removed all use of word 'Rendezvous' - this is an API change +# +# 0.11 update - correction to comments for addListener method +# support for new record types seen from OS X +# - IPv6 address +# - hostinfo +# ignore unknown DNS record types +# fixes to name decoding +# works alongside other processes using port 5353 (e.g. on Mac OS X) +# tested against Mac OS X 10.3.2's mDNSResponder +# corrections to removal of list entries for service browser +# +# 0.10 update - Jonathon Paisley contributed these corrections: +# always multicast replies, even when query is unicast +# correct a pointer encoding problem +# can now write records in any order +# traceback shown on failure +# better TXT record parsing +# server is now separate from name +# can cancel a service browser +# +# modified some unit tests to accommodate these changes +# +# 0.09 update - remove all records on service unregistration +# fix DOS security problem with readName +# +# 0.08 update - changed licensing to LGPL +# +# 0.07 update - faster shutdown on engine +# pointer encoding of outgoing names +# ServiceBrowser now works +# new unit tests +# +# 0.06 update - small improvements with unit tests +# added defined exception types +# new style objects +# fixed hostname/interface problem +# fixed socket timeout problem +# fixed addServiceListener() typo bug +# using select() for socket reads +# tested on Debian unstable with Python 2.2.2 +# +# 0.05 update - ensure case insensitivty on domain names +# support for unicast DNS queries +# +# 0.04 update - added some unit tests +# added __ne__ adjuncts where required +# ensure names end in '.local.' +# timeout on receiving socket for clean shutdown import string import time @@ -86,6 +82,12 @@ import select import traceback + +__author__ = "Paul Scott-Murphy" +__email__ = "paul at scott dash murphy dot com" +__version__ = "0.12" + + __all__ = ["Zeroconf", "ServiceInfo", "ServiceBrowser"] # hook for threads @@ -101,27 +103,27 @@ _BROWSER_TIME = 500 # Some DNS constants - + _MDNS_ADDR = '224.0.0.251' -_MDNS_PORT = 5353; -_DNS_PORT = 53; -_DNS_TTL = 60 * 60; # one hour default TTL - -_MAX_MSG_TYPICAL = 1460 # unused +_MDNS_PORT = 5353 +_DNS_PORT = 53 +_DNS_TTL = 60 * 60 # one hour default TTL + +_MAX_MSG_TYPICAL = 1460 # unused _MAX_MSG_ABSOLUTE = 8972 -_FLAGS_QR_MASK = 0x8000 # query response mask -_FLAGS_QR_QUERY = 0x0000 # query -_FLAGS_QR_RESPONSE = 0x8000 # response - -_FLAGS_AA = 0x0400 # Authorative answer -_FLAGS_TC = 0x0200 # Truncated -_FLAGS_RD = 0x0100 # Recursion desired -_FLAGS_RA = 0x8000 # Recursion available - -_FLAGS_Z = 0x0040 # Zero -_FLAGS_AD = 0x0020 # Authentic data -_FLAGS_CD = 0x0010 # Checking disabled +_FLAGS_QR_MASK = 0x8000 # query response mask +_FLAGS_QR_QUERY = 0x0000 # query +_FLAGS_QR_RESPONSE = 0x8000 # response + +_FLAGS_AA = 0x0400 # Authorative answer +_FLAGS_TC = 0x0200 # Truncated +_FLAGS_RD = 0x0100 # Recursion desired +_FLAGS_RA = 0x8000 # Recursion available + +_FLAGS_Z = 0x0040 # Zero +_FLAGS_AD = 0x0020 # Authentic data +_FLAGS_CD = 0x0010 # Checking disabled _CLASS_IN = 1 _CLASS_CS = 2 @@ -150,65 +152,76 @@ _TYPE_TXT = 16 _TYPE_AAAA = 28 _TYPE_SRV = 33 -_TYPE_ANY = 255 +_TYPE_ANY = 255 # Mapping constants to names -_CLASSES = { _CLASS_IN : "in", - _CLASS_CS : "cs", - _CLASS_CH : "ch", - _CLASS_HS : "hs", - _CLASS_NONE : "none", - _CLASS_ANY : "any" } - -_TYPES = { _TYPE_A : "a", - _TYPE_NS : "ns", - _TYPE_MD : "md", - _TYPE_MF : "mf", - _TYPE_CNAME : "cname", - _TYPE_SOA : "soa", - _TYPE_MB : "mb", - _TYPE_MG : "mg", - _TYPE_MR : "mr", - _TYPE_NULL : "null", - _TYPE_WKS : "wks", - _TYPE_PTR : "ptr", - _TYPE_HINFO : "hinfo", - _TYPE_MINFO : "minfo", - _TYPE_MX : "mx", - _TYPE_TXT : "txt", - _TYPE_AAAA : "quada", - _TYPE_SRV : "srv", - _TYPE_ANY : "any" } +_CLASSES = { + _CLASS_IN: "in", + _CLASS_CS: "cs", + _CLASS_CH: "ch", + _CLASS_HS: "hs", + _CLASS_NONE: "none", + _CLASS_ANY: "any" +} + +_TYPES = { + _TYPE_A: "a", + _TYPE_NS: "ns", + _TYPE_MD: "md", + _TYPE_MF: "mf", + _TYPE_CNAME: "cname", + _TYPE_SOA: "soa", + _TYPE_MB: "mb", + _TYPE_MG: "mg", + _TYPE_MR: "mr", + _TYPE_NULL: "null", + _TYPE_WKS: "wks", + _TYPE_PTR: "ptr", + _TYPE_HINFO: "hinfo", + _TYPE_MINFO: "minfo", + _TYPE_MX: "mx", + _TYPE_TXT: "txt", + _TYPE_AAAA: "quada", + _TYPE_SRV: "srv", + _TYPE_ANY: "any" +} # utility functions + def currentTimeMillis(): """Current system time in milliseconds""" return time.time() * 1000 # Exceptions + class NonLocalNameException(Exception): pass + class NonUniqueNameException(Exception): pass + class NamePartTooLongException(Exception): pass + class AbstractMethodException(Exception): pass + class BadTypeInNameException(Exception): pass # implementation classes + class DNSEntry(object): """A DNS entry""" - + def __init__(self, name, type, clazz): self.key = string.lower(name) self.name = name @@ -230,14 +243,14 @@ """Class accessor""" try: return _CLASSES[clazz] - except: + except Exception: return "?(%s)" % (clazz) def getType(self, type): """Type accessor""" try: return _TYPES[type] - except: + except Exception: return "?(%s)" % (type) def toString(self, hdr, other): @@ -254,9 +267,10 @@ result += "]" return result + class DNSQuestion(DNSEntry): """A DNS question entry""" - + def __init__(self, name, type, clazz): if not name.endswith(".local."): raise NonLocalNameException @@ -273,7 +287,7 @@ class DNSRecord(DNSEntry): """A DNS record - like a DNS entry, but has a TTL""" - + def __init__(self, name, type, clazz, ttl): DNSEntry.__init__(self, name, type, clazz) self.ttl = ttl @@ -332,9 +346,10 @@ arg = "%s/%s,%s" % (self.ttl, self.getRemainingTTL(currentTimeMillis()), other) return DNSEntry.toString(self, "record", arg) + class DNSAddress(DNSRecord): """A DNS address record""" - + def __init__(self, name, type, clazz, ttl, address): DNSRecord.__init__(self, name, type, clazz, ttl) self.address = address @@ -353,9 +368,10 @@ """String representation""" try: return socket.inet_ntoa(self.address) - except: + except Exception: return self.address + class DNSHinfo(DNSRecord): """A DNS host information record""" @@ -378,10 +394,11 @@ def __repr__(self): """String representation""" return self.cpu + " " + self.os - + + class DNSPointer(DNSRecord): """A DNS pointer record""" - + def __init__(self, name, type, clazz, ttl, alias): DNSRecord.__init__(self, name, type, clazz, ttl) self.alias = alias @@ -400,9 +417,10 @@ """String representation""" return self.toString(self.alias) + class DNSText(DNSRecord): """A DNS text record""" - + def __init__(self, name, type, clazz, ttl, text): DNSRecord.__init__(self, name, type, clazz, ttl) self.text = text @@ -424,9 +442,10 @@ else: return self.toString(self.text) + class DNSService(DNSRecord): """A DNS service record""" - + def __init__(self, name, type, clazz, ttl, priority, weight, port, server): DNSRecord.__init__(self, name, type, clazz, ttl) self.priority = priority @@ -451,9 +470,10 @@ """String representation""" return self.toString("%s:%s" % (self.server, self.port)) + class DNSIncoming(object): """Object representation of an incoming DNS packet""" - + def __init__(self, data): """Constructor from string holding bytes of packet""" self.offset = 0 @@ -464,7 +484,7 @@ self.numAnswers = 0 self.numAuthorities = 0 self.numAdditionals = 0 - + self.readHeader() self.readQuestions() self.readOthers() @@ -491,7 +511,7 @@ name = self.readName() info = struct.unpack(format, self.data[self.offset:self.offset+length]) self.offset += length - + question = DNSQuestion(name, info[0], info[1]) self.questions.append(question) @@ -512,7 +532,7 @@ def readString(self, len): """Reads a string of a given length from the packet""" format = '!' + str(len) + 's' - length = struct.calcsize(format) + length = struct.calcsize(format) info = struct.unpack(format, self.data[self.offset:self.offset+length]) self.offset += length return info[0] @@ -555,13 +575,13 @@ # so this is left for debugging. New types # encountered need to be parsed properly. # - #print "UNKNOWN TYPE = " + str(info[0]) - #raise BadTypeInNameException + # print "UNKNOWN TYPE = " + str(info[0]) + # raise BadTypeInNameException pass if rec is not None: self.answers.append(rec) - + def isQuery(self): """Returns true if this is a query""" return (self.flags & _FLAGS_QR_MASK) == _FLAGS_QR_QUERY @@ -574,7 +594,7 @@ """Reads a UTF-8 string of a given length from the packet""" result = self.data[offset:offset+len].decode('utf-8') return result - + def readName(self): """Reads a domain name from the packet""" result = '' @@ -607,12 +627,12 @@ self.offset = off return result - - + + class DNSOutgoing(object): """Object representation of an outgoing packet""" - - def __init__(self, flags, multicast = 1): + + def __init__(self, flags, multicast=1): self.finished = 0 self.id = 0 self.multicast = multicast @@ -620,7 +640,7 @@ self.names = {} self.data = [] self.size = 12 - + self.questions = [] self.answers = [] self.authorities = [] @@ -660,7 +680,7 @@ format = '!H' self.data.insert(index, struct.pack(format, value)) self.size += 2 - + def writeShort(self, value): """Writes an unsigned short to the packet""" format = '!H' @@ -739,9 +759,9 @@ self.size += 2 record.write(self) self.size -= 2 - + length = len(''.join(self.data[index:])) - self.insertShort(index, length) # Here is the short we adjusted for + self.insertShort(index, length) # Here is the short we adjusted for def packet(self): """Returns a string containing the packet's bytes @@ -758,7 +778,7 @@ self.writeRecord(authority, 0) for additional in self.additionals: self.writeRecord(additional, 0) - + self.insertShort(0, len(self.additionals)) self.insertShort(0, len(self.authorities)) self.insertShort(0, len(self.answers)) @@ -773,7 +793,7 @@ class DNSCache(object): """A cache of DNS entries""" - + def __init__(self): self.cache = {} @@ -781,7 +801,7 @@ """Adds an entry""" try: list = self.cache[entry.key] - except: + except Exception: list = self.cache[entry.key] = [] list.append(entry) @@ -790,7 +810,7 @@ try: list = self.cache[entry.key] list.remove(entry) - except: + except Exception: pass def get(self, entry): @@ -799,7 +819,7 @@ try: list = self.cache[entry.key] return list[list.index(entry)] - except: + except Exception: return None def getByDetails(self, name, type, clazz): @@ -812,7 +832,7 @@ """Returns a list of entries whose key matches the name.""" try: return self.cache[name] - except: + except Exception: return [] def entries(self): @@ -820,7 +840,7 @@ def add(x, y): return x+y try: return reduce(add, self.cache.values()) - except: + except Exception: return [] @@ -839,7 +859,7 @@ def __init__(self, zeroconf): threading.Thread.__init__(self) self.zeroconf = zeroconf - self.readers = {} # maps socket to reader + self.readers = {} # maps socket to reader self.timeout = 5 self.condition = threading.Condition() self.start() @@ -860,10 +880,10 @@ for socket in rr: try: self.readers[socket].handle_read() - except: + except Exception: # Ignore errors that occur on shutdown pass - except: + except Exception: pass def getReaders(self): @@ -872,7 +892,7 @@ result = self.readers.keys() self.condition.release() return result - + def addReader(self, reader, socket): self.condition.acquire() self.readers[socket] = reader @@ -890,6 +910,7 @@ self.condition.notify() self.condition.release() + class Listener(object): """A Listener is used by this module to listen on the multicast group to which DNS messages are sent, allowing the implementation @@ -897,7 +918,7 @@ It requires registration with an Engine object in order to have the read() method called when a socket is availble for reading.""" - + def __init__(self, zeroconf): self.zeroconf = zeroconf self.zeroconf.engine.addReader(self, self.zeroconf.socket) @@ -924,7 +945,7 @@ class Reaper(threading.Thread): """A Reaper is used by this module to remove cache entries that have expired.""" - + def __init__(self, zeroconf): threading.Thread.__init__(self) self.zeroconf = zeroconf @@ -948,7 +969,7 @@ The listener object will have its addService() and removeService() methods called when this browser discovers changes in the services availability.""" - + def __init__(self, zeroconf, type, listener): """Creates a browser for a specific type""" threading.Thread.__init__(self) @@ -959,7 +980,7 @@ self.nextTime = currentTimeMillis() self.delay = _BROWSER_TIME self.list = [] - + self.done = 0 self.zeroconf.addListener(self, DNSQuestion(self.type, _TYPE_PTR, _CLASS_IN)) @@ -976,14 +997,16 @@ if not expired: oldrecord.resetTTL(record) else: + def callback(x): + return self.listener.removeService(x, self.type, record.alias) del(self.services[record.alias.lower()]) - callback = lambda x: self.listener.removeService(x, self.type, record.alias) self.list.append(callback) return - except: + except Exception: if not expired: + def callback(x): + return self.listener.addService(x, self.type, record.alias) self.services[record.alias.lower()] = record - callback = lambda x: self.listener.addService(x, self.type, record.alias) self.list.append(callback) expires = record.getExpirationTime(75) @@ -1019,11 +1042,11 @@ if event is not None: event(self.zeroconf) - + class ServiceInfo(object): """Service information""" - + def __init__(self, type, name, address=None, port=None, weight=0, priority=0, properties=None, server=None): """Create a service description. @@ -1089,7 +1112,7 @@ index += 1 strs.append(text[index:index+length]) index += length - + for s in strs: eindex = s.find('=') if eindex == -1: @@ -1105,14 +1128,14 @@ value = 0 # Only update non-existent properties - if key and result.get(key) == None: + if key and result.get(key) is None: result[key] = value self.properties = result - except: + except Exception: traceback.print_exc() self.properties = None - + def getType(self): """Type accessor""" return self.type @@ -1200,7 +1223,7 @@ result = 1 finally: zeroconf.removeListener(self) - + return result def __eq__(self, other): @@ -1225,7 +1248,7 @@ result += self.text[:17] + "..." result += "]" return result - + class Zeroconf(object): """Implementation of Zeroconf Multicast DNS Service Discovery @@ -1242,7 +1265,7 @@ try: self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) - except: + except Exception: # SO_REUSEADDR should be equivalent to SO_REUSEPORT for # multicast UDP sockets (p 731, "TCP/IP Illustrated, # Volume 2"), but some BSD-derived systems require @@ -1257,7 +1280,7 @@ self.socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1) try: self.socket.bind(self.group) - except: + except Exception: # Some versions of linux raise an exception even though # the SO_REUSE* options have been set, so ignore it # @@ -1274,7 +1297,7 @@ self.cache = DNSCache() self.condition = threading.Condition() - + self.engine = Engine(self) self.listener = Listener(self) self.reaper = Reaper(self) @@ -1354,7 +1377,7 @@ """Unregister a service.""" try: del(self.services[info.name.lower()]) - except: + except Exception: pass now = currentTimeMillis() nextTime = now @@ -1439,7 +1462,7 @@ try: self.listeners.remove(listener) self.notifyAll() - except: + except Exception: pass def updateRecord(self, now, rec): @@ -1465,7 +1488,7 @@ record = entry else: self.cache.add(record) - + self.updateRecord(now, record) def handleQuery(self, msg, addr, port): @@ -1479,7 +1502,7 @@ out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA, 0) for question in msg.questions: out.addQuestion(question) - + for question in msg.questions: if question.type == _TYPE_PTR: for service in self.services.values(): @@ -1491,36 +1514,37 @@ try: if out is None: out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) - + # Answer A record queries for any service addresses we know if question.type == _TYPE_A or question.type == _TYPE_ANY: for service in self.services.values(): if service.server == question.name.lower(): out.addAnswer(msg, DNSAddress(question.name, _TYPE_A, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.address)) - + service = self.services.get(question.name.lower(), None) - if not service: continue - + if not service: + continue + if question.type == _TYPE_SRV or question.type == _TYPE_ANY: out.addAnswer(msg, DNSService(question.name, _TYPE_SRV, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.priority, service.weight, service.port, service.server)) if question.type == _TYPE_TXT or question.type == _TYPE_ANY: out.addAnswer(msg, DNSText(question.name, _TYPE_TXT, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.text)) if question.type == _TYPE_SRV: out.addAdditionalAnswer(DNSAddress(service.server, _TYPE_A, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.address)) - except: + except Exception: traceback.print_exc() - + if out is not None and out.answers: out.id = msg.id self.send(out, addr, port) - def send(self, out, addr = _MDNS_ADDR, port = _MDNS_PORT): + def send(self, out, addr=_MDNS_ADDR, port=_MDNS_PORT): """Sends an outgoing packet.""" # This is a quick test to see if we can parse the packets we generate - #temp = DNSIncoming(out.packet()) + # temp = DNSIncoming(out.packet()) try: bytes_sent = self.socket.sendto(out.packet(), 0, (addr, port)) - except: + except Exception: # Ignore this, it may be a temporary loss of network connection pass @@ -1534,15 +1558,16 @@ self.unregisterAllServices() self.socket.setsockopt(socket.SOL_IP, socket.IP_DROP_MEMBERSHIP, socket.inet_aton(_MDNS_ADDR) + socket.inet_aton('0.0.0.0')) self.socket.close() - + # Test a few module features, including service registration, service # query (for Zoe), and service unregistration. -if __name__ == '__main__': + +if __name__ == '__main__': print "Multicast DNS Service Discovery for Python, version", __version__ r = Zeroconf() print "1. Testing registration of a service..." - desc = {'version':'0.10','a':'test value', 'b':'another value'} + desc = {'version': '0.10', 'a': 'test value', 'b': 'another value'} info = ServiceInfo("_http._tcp.local.", "My Service Name._http._tcp.local.", socket.inet_aton("127.0.0.1"), 1234, 0, 0, desc) print " Registering service..." r.registerService(info) diff -r 31e63e25b4cc -r 64beb9e9c749 util/misc.py --- a/util/misc.py Mon Aug 21 20:17:19 2017 +0000 +++ b/util/misc.py Mon Aug 21 23:22:58 2017 +0300 @@ -26,10 +26,12 @@ Misc definitions """ -import os,sys +import os +import sys -# helper func to check path write permission + def CheckPathPerm(path): + """ Helper func to check path write permission """ if path is None or not os.path.isdir(path): return False for root, dirs, files in os.walk(path): @@ -40,15 +42,17 @@ return False return True + def GetClassImporter(classpath): - if type(classpath)==str: + if type(classpath) == str: def fac(): - mod=__import__(classpath.rsplit('.',1)[0]) + mod = __import__(classpath.rsplit('.', 1)[0]) return reduce(getattr, classpath.split('.')[1:], mod) return fac else: return classpath + def InstallLocalRessources(CWD): from BitmapLibrary import AddBitmapFolder from TranslationCatalogs import AddCatalog @@ -61,6 +65,5 @@ AddCatalog(os.path.join(CWD, "locale")) import gettext import __builtin__ - + __builtin__.__dict__['_'] = wx.GetTranslation - diff -r 31e63e25b4cc -r 64beb9e9c749 util/paths.py --- a/util/paths.py Mon Aug 21 20:17:19 2017 +0000 +++ b/util/paths.py Mon Aug 21 23:22:58 2017 +0300 @@ -22,22 +22,26 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import os, sys +import os +import sys + def AbsFile(file): if isinstance(file, str): - file = unicode(file,sys.getfilesystemencoding()) + file = unicode(file, sys.getfilesystemencoding()) return file + def AbsDir(file): file = AbsFile(file) return os.path.dirname(os.path.realpath(file)) + def AbsNeighbourFile(file, *args): return os.path.join(AbsDir(file), *args) -def AbsParentDir(file, level = 1): +def AbsParentDir(file, level=1): path = AbsDir(file) for i in range(0, level): path = os.path.dirname(path) diff -r 31e63e25b4cc -r 64beb9e9c749 version.py --- a/version.py Mon Aug 21 20:17:19 2017 +0000 +++ b/version.py Mon Aug 21 23:22:58 2017 +0300 @@ -23,105 +23,114 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import subprocess, os +import subprocess +import os + import util.paths as paths + def GetCommunityHelpMsg(): - return _("The best place to ask questions about Beremiz/PLCOpenEditor\n" - "is project's mailing list: beremiz-devel@lists.sourceforge.net\n" - "\n" - "This is the main community support channel.\n" - "For posting it is required to be subscribed to the mailing list.\n" - "\n" - "You can subscribe to the list here:\n" - "https://lists.sourceforge.net/lists/listinfo/beremiz-devel") + return _( + "The best place to ask questions about Beremiz/PLCOpenEditor\n" + "is project's mailing list: beremiz-devel@lists.sourceforge.net\n" + "\n" + "This is the main community support channel.\n" + "For posting it is required to be subscribed to the mailing list.\n" + "\n" + "You can subscribe to the list here:\n" + "https://lists.sourceforge.net/lists/listinfo/beremiz-devel" + ) + def GetAppRevision(): rev = None - app_dir=paths.AbsDir(__file__) + app_dir = paths.AbsDir(__file__) try: pipe = subprocess.Popen( ["hg", "id", "-i"], - stdout = subprocess.PIPE, - cwd = app_dir + stdout=subprocess.PIPE, + cwd=app_dir ) rev = pipe.communicate()[0] if pipe.returncode != 0: rev = None - except: + except Exception: pass - + # if this is not mercurial repository # try to read revision from file if rev is None: try: - f = open(os.path.join(app_dir,"revision")) + f = open(os.path.join(app_dir, "revision")) rev = f.readline() - except: + except Exception: pass return rev + def GetAboutDialogInfo(): import wx info = wx.AboutDialogInfo() info.Name = "Beremiz" info.Version = app_version - - info.Copyright = "(C) 2016 Andrey Skvortsov\n" + + info.Copyright = "" + info.Copyright += "(C) 2016-2017 Andrey Skvortsov\n" info.Copyright += "(C) 2008-2015 Eduard Tisserant\n" info.Copyright += "(C) 2008-2015 Laurent Bessard" info.WebSite = ("http://beremiz.org", "beremiz.org") - + info.Description = _("Open Source framework for automation, " - "implemented IEC 61131 IDE with constantly growing set of extensions " - "and flexible PLC runtime.") - - info.Developers = ("Andrey Skvortsov ", - "Sergey Surkov ", - "Edouard Tisserant ", - "Laurent Bessard ") + "implemented IEC 61131 IDE with constantly growing set of extensions " + "and flexible PLC runtime.") + info.Developers = ( + "Andrey Skvortsov ", + "Sergey Surkov ", + "Edouard Tisserant ", + "Laurent Bessard ") - info.License = ('\n This program is free software; you can redistribute it and/or\n' - ' modify it under the terms of the GNU General Public License\n' - ' as published by the Free Software Foundation; either version 2\n' - ' of the License, or (at your option) any later version.\n' - '\n' - ' This program is distributed in the hope that it will be useful,\n' - ' but WITHOUT ANY WARRANTY; without even the implied warranty of\n' - ' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n' - ' GNU General Public License below for more details.\n' - '\n' - '\n' - '\n' - '') + info.License = ( + '\n This program is free software; you can redistribute it and/or\n' + ' modify it under the terms of the GNU General Public License\n' + ' as published by the Free Software Foundation; either version 2\n' + ' of the License, or (at your option) any later version.\n' + '\n' + ' This program is distributed in the hope that it will be useful,\n' + ' but WITHOUT ANY WARRANTY; without even the implied warranty of\n' + ' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n' + ' GNU General Public License below for more details.\n' + '\n' + '\n' + '\n' + '' + ) # read license file - path=paths.AbsDir(__file__) + path = paths.AbsDir(__file__) license_path = os.path.join(path, "COPYING") - license='' + license = '' if os.path.exists(license_path): with open(license_path) as f: info.License += f.read() info.Icon = wx.Icon(os.path.join(path, "images", "about_brz_logo.png"), wx.BITMAP_TYPE_PNG) - info.Translators = ("Russian\t- Andrey Skvortsov ", - "Korean\t- Reinhard Lee ", - "German\t- Mark Muzenhardt ", - "French\t- Laurent Bessard ", - " \t Fabien M ", - "Slovenian\t- Janez Pregelj", - "Portuguese\t- Thiago Alves " + info.Translators = ( + "Russian\t- Andrey Skvortsov ", + "Korean\t- Reinhard Lee ", + "German\t- Mark Muzenhardt ", + "French\t- Laurent Bessard ", + " \t Fabien M ", + "Slovenian\t- Janez Pregelj", + "Portuguese\t- Thiago Alves " ) return info -app_version = "1.2" + +app_version = "1.2" rev = GetAppRevision() if rev is not None: app_version = app_version + "-" + rev.rstrip() - - - diff -r 31e63e25b4cc -r 64beb9e9c749 wxglade_hmi/wxglade_hmi.py --- a/wxglade_hmi/wxglade_hmi.py Mon Aug 21 20:17:19 2017 +0000 +++ b/wxglade_hmi/wxglade_hmi.py Mon Aug 21 23:22:58 2017 +0300 @@ -24,19 +24,24 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. import wx -import os, sys, shutil +import os +import sys +import shutil from xml.dom import minidom import util.paths as paths from py_ext import PythonFileCTNMixin + class WxGladeHMI(PythonFileCTNMixin): ConfNodeMethods = [ - {"bitmap" : "editWXGLADE", - "name" : _("WXGLADE GUI"), - "tooltip" : _("Edit a WxWidgets GUI with WXGlade"), - "method" : "_editWXGLADE"}, + { + "bitmap": "editWXGLADE", + "name": _("WXGLADE GUI"), + "tooltip": _("Edit a WxWidgets GUI with WXGlade"), + "method": "_editWXGLADE" + }, ] def GetIconName(self): @@ -51,29 +56,28 @@ # define name for wxGlade gui file return os.path.join(project_path, "hmi.wxg") - def GetWxGladePath(self): path = None try: from wxglade import __file__ as fileName - path = os.path.dirname(fileName) + path = os.path.dirname(fileName) return path except ImportError: pass - defLibDir="/usr/share/wxglade" + defLibDir = "/usr/share/wxglade" if os.path.isdir(defLibDir): path = defLibDir return path - + def launch_wxglade(self, options, wait=False): path = self.GetWxGladePath() glade = os.path.join(path, 'wxglade.py') if wx.Platform == '__WXMSW__': - glade = "\"%s\""%glade - mode = {False:os.P_NOWAIT, True:os.P_WAIT}[wait] - os.spawnv(mode, sys.executable, ["\"%s\""%sys.executable] + [glade] + options) + glade = "\"%s\"" % glade + mode = {False: os.P_NOWAIT, True: os.P_WAIT}[wait] + os.spawnv(mode, sys.executable, ["\"%s\"" % sys.executable] + [glade] + options) def OnCTNSave(self, from_project_path=None): if from_project_path is not None: @@ -82,45 +86,45 @@ return PythonFileCTNMixin.OnCTNSave(self, from_project_path) def CTNGenerate_C(self, buildpath, locations): - + hmi_frames = [] - - wxgfile_path=self._getWXGLADEpath() + + wxgfile_path = self._getWXGLADEpath() if os.path.exists(wxgfile_path): wxgfile = open(wxgfile_path, 'r') wxgtree = minidom.parse(wxgfile) wxgfile.close() - + for node in wxgtree.childNodes[1].childNodes: if node.nodeType == wxgtree.ELEMENT_NODE: hmi_frames.append({ - "name" : node.getAttribute("name"), - "class" : node.getAttribute("class"), - "handlers" : [ - hnode.firstChild.data for hnode in + "name": node.getAttribute("name"), + "class": node.getAttribute("class"), + "handlers": [ + hnode.firstChild.data for hnode in node.getElementsByTagName("handler")]}) - - hmipyfile_path=os.path.join(self._getBuildPath(), "hmi.py") + + hmipyfile_path = os.path.join(self._getBuildPath(), "hmi.py") if wx.Platform == '__WXMSW__': - wxgfile_path = "\"%s\""%wxgfile_path - wxghmipyfile_path = "\"%s\""%hmipyfile_path + wxgfile_path = "\"%s\"" % wxgfile_path + wxghmipyfile_path = "\"%s\"" % hmipyfile_path else: wxghmipyfile_path = hmipyfile_path self.launch_wxglade(['-o', wxghmipyfile_path, '-g', 'python', wxgfile_path], wait=True) - + hmipyfile = open(hmipyfile_path, 'r') define_hmi = hmipyfile.read().decode('utf-8') hmipyfile.close() - + else: define_hmi = "" - - declare_hmi = "\n".join(["%(name)s = None\n" % x + - "\n".join(["%(class)s.%(h)s = %(h)s"% - dict(x,h=h) for h in x['handlers']]) + + declare_hmi = "\n".join(["%(name)s = None\n" % x + + "\n".join(["%(class)s.%(h)s = %(h)s" % + dict(x, h=h) for h in x['handlers']]) for x in hmi_frames]) - global_hmi = ("global %s\n"%",".join( - [x["name"] for x in hmi_frames]) + global_hmi = ("global %s\n" % ",".join( + [x["name"] for x in hmi_frames]) if len(hmi_frames) > 0 else "") init_hmi = "\n".join(["""\ def OnCloseFrame(evt): @@ -131,17 +135,17 @@ %(name)s.Show() """ % x for x in hmi_frames]) cleanup_hmi = "\n".join( - ["if %(name)s is not None: %(name)s.Destroy()" % x + ["if %(name)s is not None: %(name)s.Destroy()" % x for x in hmi_frames]) - + self.PreSectionsTexts = { - "globals":define_hmi, - "start":global_hmi, - "stop":global_hmi + cleanup_hmi + "globals": define_hmi, + "start": global_hmi, + "stop": global_hmi + cleanup_hmi } self.PostSectionsTexts = { - "globals":declare_hmi, - "start":init_hmi, + "globals": declare_hmi, + "start": init_hmi, } return PythonFileCTNMixin.CTNGenerate_C(self, buildpath, locations) @@ -153,13 +157,13 @@ dialog = wx.MessageDialog(self.GetCTRoot().AppFrame, _("You don't have write permissions.\nOpen wxGlade anyway ?"), _("Open wxGlade"), - wx.YES_NO|wx.ICON_QUESTION) + wx.YES_NO | wx.ICON_QUESTION) open_wxglade = dialog.ShowModal() == wx.ID_YES dialog.Destroy() if open_wxglade: if not os.path.exists(wxg_filename): hmi_name = self.BaseParams.getName() - open(wxg_filename,"w").write(""" + open(wxg_filename, "w").write(""" @@ -172,6 +176,5 @@ """ % {"name": hmi_name, "class": "Class_%s" % hmi_name}) if wx.Platform == '__WXMSW__': - wxg_filename = "\"%s\""%wxg_filename + wxg_filename = "\"%s\"" % wxg_filename self.launch_wxglade([wxg_filename]) - diff -r 31e63e25b4cc -r 64beb9e9c749 xmlclass/xmlclass.py --- a/xmlclass/xmlclass.py Mon Aug 21 20:17:19 2017 +0000 +++ b/xmlclass/xmlclass.py Mon Aug 21 23:22:58 2017 +0300 @@ -22,7 +22,8 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import os, sys +import os +import sys import re import datetime from types import * @@ -32,6 +33,7 @@ from new import classobj from collections import OrderedDict + def CreateNode(name): node = minidom.Node() node.nodeName = name @@ -39,9 +41,11 @@ node.childNodes = [] return node + def NodeRenameAttr(node, old_name, new_name): node._attrs[new_name] = node._attrs.pop(old_name) + def NodeSetAttr(node, name, value): attr = minidom.Attr(name) text = minidom.Text() @@ -49,6 +53,7 @@ attr.childNodes[0] = text node._attrs[name] = attr + """ Regular expression models for checking all kind of string values defined in XML standard @@ -72,14 +77,15 @@ date_model = re.compile('([0-9]{4})-([0-9]{2})-([0-9]{2})((?:[\-\+][0-9]{2}:[0-9]{2})|Z)?$') datetime_model = re.compile('([0-9]{4})-([0-9]{2})-([0-9]{2})[ T]([0-9]{2}):([0-9]{2}):([0-9]{2}(?:\.[0-9]*)?)((?:[\-\+][0-9]{2}:[0-9]{2})|Z)?$') + class xml_timezone(datetime.tzinfo): def SetOffset(self, offset): if offset == "Z": - self.__offset = timedelta(minutes = 0) + self.__offset = timedelta(minutes=0) self.__name = "UTC" else: - sign = {"-" : -1, "+" : 1}[offset[0]] + sign = {"-": -1, "+": 1}[offset[0]] hours, minutes = [int(val) for val in offset[1:].split(":")] self.__offset = timedelta(minutes=sign * (hours * 60 + minutes)) self.__name = "" @@ -93,10 +99,13 @@ def dst(self, dt): return ZERO -[SYNTAXELEMENT, SYNTAXATTRIBUTE, SIMPLETYPE, COMPLEXTYPE, COMPILEDCOMPLEXTYPE, - ATTRIBUTESGROUP, ELEMENTSGROUP, ATTRIBUTE, ELEMENT, CHOICE, ANY, TAG, CONSTRAINT, + +[ + SYNTAXELEMENT, SYNTAXATTRIBUTE, SIMPLETYPE, COMPLEXTYPE, COMPILEDCOMPLEXTYPE, + ATTRIBUTESGROUP, ELEMENTSGROUP, ATTRIBUTE, ELEMENT, CHOICE, ANY, TAG, CONSTRAINT, ] = range(13) + def NotSupportedYet(type): """ Function that generates a function that point out to user that datatype @@ -105,14 +114,14 @@ @return: function generated """ def GetUnknownValue(attr): - raise ValueError("\"%s\" type isn't supported by \"xmlclass\" yet!" % \ - type) + raise ValueError("\"%s\" type isn't supported by \"xmlclass\" yet!" % type) return GetUnknownValue -""" -This function calculates the number of whitespace for indentation -""" + def getIndent(indent, balise): + """ + This function calculates the number of whitespace for indentation + """ first = indent * 2 second = first + len(balise) + 1 return u'\t'.expandtabs(first), u'\t'.expandtabs(second) @@ -140,7 +149,7 @@ def GetNormalizedString(attr, extract=True): """ - Function that normalizes a string according to XML 1.0. Replace + Function that normalizes a string according to XML 1.0. Replace tabulations, line feed and carriage return by white space @param attr: tree node containing data to extract or data to normalize @param extract: attr is a tree node or not @@ -155,14 +164,14 @@ def GetToken(attr, extract=True): """ - Function that tokenizes a string according to XML 1.0. Remove any leading - and trailing white space and replace internal sequence of two or more + Function that tokenizes a string according to XML 1.0. Remove any leading + and trailing white space and replace internal sequence of two or more spaces by only one white space @param attr: tree node containing data to extract or data to tokenize @param extract: attr is a tree node or not @return: data tokenized as string """ - return " ".join([part for part in + return " ".join([part for part in GetNormalizedString(attr, extract).split(" ") if part]) @@ -182,11 +191,11 @@ raise ValueError("\"%s\" isn't a valid hexadecimal integer!" % value) try: return int(value, 16) - except: + except Exception: raise ValueError("\"%s\" isn't a valid hexadecimal integer!" % value) -def GenerateIntegerExtraction(minInclusive=None, maxInclusive=None, +def GenerateIntegerExtraction(minInclusive=None, maxInclusive=None, minExclusive=None, maxExclusive=None): """ Function that generates an extraction function for integer defining min and @@ -212,19 +221,19 @@ try: # TODO: permit to write value like 1E2 value = int(value) - except: + except Exception: raise ValueError("\"%s\" isn't a valid integer!" % value) if minInclusive is not None and value < minInclusive: - raise ValueError("\"%d\" isn't greater or equal to %d!" % \ + raise ValueError("\"%d\" isn't greater or equal to %d!" % (value, minInclusive)) if maxInclusive is not None and value > maxInclusive: - raise ValueError("\"%d\" isn't lesser or equal to %d!" % \ + raise ValueError("\"%d\" isn't lesser or equal to %d!" % (value, maxInclusive)) if minExclusive is not None and value <= minExclusive: - raise ValueError("\"%d\" isn't greater than %d!" % \ + raise ValueError("\"%d\" isn't greater than %d!" % (value, minExclusive)) if maxExclusive is not None and value >= maxExclusive: - raise ValueError("\"%d\" isn't lesser than %d!" % \ + raise ValueError("\"%d\" isn't lesser than %d!" % (value, maxExclusive)) return value return GetInteger @@ -236,7 +245,7 @@ @param type: name of the type of float @return: function generated """ - def GetFloat(attr, extract = True): + def GetFloat(attr, extract=True): """ Function that extracts a float from a tree node or a string @param attr: tree node containing data to extract or data as a string @@ -251,7 +260,7 @@ return value try: return float(value) - except: + except Exception: raise ValueError("\"%s\" isn't a valid %s!" % (value, type)) return GetFloat @@ -401,7 +410,7 @@ raise ValueError("Member limit can't be defined to \"unbounded\"!") try: limit = int(value) - except: + except Exception: raise ValueError("\"%s\" isn't a valid value for this member limit!" % value) if limit < 0: raise ValueError("Member limit can't be negative!") @@ -435,8 +444,8 @@ if value in list: return value else: - raise ValueError("\"%s\" isn't a valid value for %s!" % \ - (value, type)) + raise ValueError( + "\"%s\" isn't a valid value for %s!" % (value, type)) return GetEnumerated @@ -497,8 +506,8 @@ if item in list: values.append(item) else: - raise ValueError("\"%s\" isn't a valid value for %s!" % \ - (value, type)) + raise ValueError( + "\"%s\" isn't a valid value for %s!" % (value, type)) return values return GetLists @@ -517,7 +526,7 @@ check that all extracted items match the model @param attr: tree node containing data to extract or data as a string @param extract: attr is a tree node or not - @return: data as a list of string if matching + @return: data as a list of string if matching """ if extract: value = GetAttributeValue(attr) @@ -529,24 +538,24 @@ if result is not None: values.append(item) else: - raise ValueError("\"%s\" isn't a valid value for %s!" % \ - (value, type)) + raise ValueError("\"%s\" isn't a valid value for %s!" % (value, type)) return values return GetModelNameList + def GenerateAnyInfos(infos): - + def GetTextElement(tree): if infos["namespace"][0] == "##any": return tree.xpath("p")[0] return tree.xpath("ns:p", namespaces={"ns": infos["namespace"][0]})[0] - + def ExtractAny(tree): return GetTextElement(tree).text - + def GenerateAny(tree, value): GetTextElement(tree).text = etree.CDATA(value) - + def InitialAny(): if infos["namespace"][0] == "##any": element_name = "p" @@ -555,15 +564,16 @@ p = etree.Element(element_name) p.text = etree.CDATA("") return p - + return { - "type": COMPLEXTYPE, + "type": COMPLEXTYPE, "extract": ExtractAny, "generate": GenerateAny, "initial": InitialAny, "check": lambda x: isinstance(x, (StringType, UnicodeType, etree.ElementBase)) } + def GenerateTagInfos(infos): def ExtractTag(tree): if len(tree._attrs) > 0: @@ -574,28 +584,30 @@ return True else: return None - + def GenerateTag(value, name=None, indent=0): if name is not None and not (infos["minOccurs"] == 0 and value is None): ind1, ind2 = getIndent(indent, name) return ind1 + "<%s/>\n" % name else: return "" - + return { - "type": TAG, + "type": TAG, "extract": ExtractTag, "generate": GenerateTag, "initial": lambda: None, - "check": lambda x: x == None or infos["minOccurs"] == 0 and value == True + "check": lambda x: x is None or infos["minOccurs"] == 0 and value } + def FindTypeInfos(factory, infos): if isinstance(infos, (UnicodeType, StringType)): namespace, name = DecomposeQualifiedName(infos) return factory.GetQualifiedNameInfos(name, namespace) return infos - + + def GetElementInitialValue(factory, infos): infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"]) if infos["minOccurs"] == 1: @@ -616,6 +628,7 @@ else: return [] + def GetContentInfos(name, choices): for choice_infos in choices: if choices_infos["type"] == "sequence": @@ -629,6 +642,7 @@ return choices_infos return None + def ComputeContentChoices(factory, name, infos): choices = [] for choice in infos["choices"]: @@ -649,6 +663,7 @@ choices.append((choice["name"], choice)) return choices + def GenerateContentInfos(factory, name, choices): choices_dict = {} for choice_name, infos in choices: @@ -656,18 +671,18 @@ for element in infos["elements"]: if element["type"] == CHOICE: element["elmt_type"] = GenerateContentInfos(factory, name, ComputeContentChoices(factory, name, element)) - elif choices_dict.has_key(element["name"]): + elif element["name"] in choices_dict: raise ValueError("'%s' element defined two times in choice" % choice_name) else: choices_dict[element["name"]] = infos else: - if choices_dict.has_key(choice_name): + if choice_name in choices_dict: raise ValueError("'%s' element defined two times in choice" % choice_name) choices_dict[choice_name] = infos prefix = ("%s:" % factory.TargetNamespace if factory.TargetNamespace is not None else "") choices_xpath = "|".join(map(lambda x: prefix + x, choices_dict.keys())) - + def GetContentInitial(): content_name, infos = choices[0] if content_name == "sequence": @@ -678,28 +693,29 @@ else: content_value = GetElementInitialValue(factory, infos) return content_value - + return { "type": COMPLEXTYPE, "choices_xpath": etree.XPath(choices_xpath, namespaces=factory.NSMAP), "initial": GetContentInitial, } -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Structure extraction functions -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- def DecomposeQualifiedName(name): result = QName_model.match(name) if not result: - raise ValueError("\"%s\" isn't a valid QName value!" % name) + raise ValueError("\"%s\" isn't a valid QName value!" % name) parts = result.groups()[0].split(':') if len(parts) == 1: return None, parts[0] return parts - -def GenerateElement(element_name, attributes, elements_model, + + +def GenerateElement(element_name, attributes, elements_model, accept_text=False): def ExtractElement(factory, node): attrs = factory.ExtractNodeAttrs(element_name, node, attributes) @@ -709,7 +725,7 @@ for child in node.childNodes: if child.nodeName not in ["#comment", "#text"]: namespace, childname = DecomposeQualifiedName(child.nodeName) - children_structure += "%s "%childname + children_structure += "%s " % childname result = elements_model.match(children_structure) if not result: raise ValueError("Invalid structure for \"%s\" children!. First element invalid." % node.nodeName) @@ -726,7 +742,7 @@ infos = factory.GetQualifiedNameInfos(childname, namespace) if infos["type"] != SYNTAXELEMENT: raise ValueError("\"%s\" can't be a member child!" % name) - if infos["extract"].has_key(element_name): + if element_name in infos["extract"]: children.append(infos["extract"][element_name](factory, child)) else: children.append(infos["extract"]["default"](factory, child)) @@ -734,10 +750,10 @@ return ExtractElement -""" -Class that generate class from an XML Tree -""" class ClassFactory: + """ + Class that generate class from an XML Tree + """ def __init__(self, document, filepath=None, debug=False): self.Document = document @@ -746,20 +762,20 @@ else: self.BaseFolder = self.FileName = None self.Debug = debug - + # Dictionary for stocking Classes and Types definitions created from # the XML tree self.XMLClassDefinitions = {} - + self.DefinedNamespaces = {} self.NSMAP = {} self.Namespaces = {} self.SchemaNamespace = None self.TargetNamespace = None self.etreeNamespaceFormat = "%s" - + self.CurrentCompilations = [] - + # Dictionaries for stocking Classes and Types generated self.ComputeAfter = [] if self.FileName is not None: @@ -773,36 +789,36 @@ def GetQualifiedNameInfos(self, name, namespace=None, canbenone=False): if namespace is None: - if self.Namespaces[self.SchemaNamespace].has_key(name): + if name in self.Namespaces[self.SchemaNamespace]: return self.Namespaces[self.SchemaNamespace][name] for space, elements in self.Namespaces.iteritems(): - if space != self.SchemaNamespace and elements.has_key(name): + if space != self.SchemaNamespace and name in elements: return elements[name] parts = name.split("_", 1) if len(parts) > 1: group = self.GetQualifiedNameInfos(parts[0], namespace) if group is not None and group["type"] == ELEMENTSGROUP: elements = [] - if group.has_key("elements"): + if "elements" in group: elements = group["elements"] - elif group.has_key("choices"): + elif "choices" in group: elements = group["choices"] for element in elements: if element["name"] == parts[1]: return element if not canbenone: raise ValueError("Unknown element \"%s\" for any defined namespaces!" % name) - elif self.Namespaces.has_key(namespace): - if self.Namespaces[namespace].has_key(name): + elif namespace in self.Namespaces: + if name in self.Namespaces[namespace]: return self.Namespaces[namespace][name] parts = name.split("_", 1) if len(parts) > 1: group = self.GetQualifiedNameInfos(parts[0], namespace) if group is not None and group["type"] == ELEMENTSGROUP: elements = [] - if group.has_key("elements"): + if "elements" in group: elements = group["elements"] - elif group.has_key("choices"): + elif "choices" in group: elements = group["choices"] for element in elements: if element["name"] == parts[1]: @@ -815,36 +831,36 @@ def SplitQualifiedName(self, name, namespace=None, canbenone=False): if namespace is None: - if self.Namespaces[self.SchemaNamespace].has_key(name): + if name in self.Namespaces[self.SchemaNamespace]: return name, None for space, elements in self.Namespaces.items(): - if space != self.SchemaNamespace and elements.has_key(name): + if space != self.SchemaNamespace and name in elements: return name, None parts = name.split("_", 1) if len(parts) > 1: group = self.GetQualifiedNameInfos(parts[0], namespace) if group is not None and group["type"] == ELEMENTSGROUP: elements = [] - if group.has_key("elements"): + if "elements" in group: elements = group["elements"] - elif group.has_key("choices"): + elif "choices" in group: elements = group["choices"] for element in elements: if element["name"] == parts[1]: return part[1], part[0] if not canbenone: raise ValueError("Unknown element \"%s\" for any defined namespaces!" % name) - elif self.Namespaces.has_key(namespace): - if self.Namespaces[namespace].has_key(name): + elif namespace in self.Namespaces: + if name in self.Namespaces[namespace]: return name, None parts = name.split("_", 1) if len(parts) > 1: group = self.GetQualifiedNameInfos(parts[0], namespace) if group is not None and group["type"] == ELEMENTSGROUP: elements = [] - if group.has_key("elements"): + if "elements" in group: elements = group["elements"] - elif group.has_key("choices"): + elif "choices" in group: elements = group["choices"] for element in elements: if element["name"] == parts[1]: @@ -858,7 +874,7 @@ def ExtractNodeAttrs(self, element_name, node, valid_attrs): attrs = {} for qualified_name, attr in node._attrs.items(): - namespace, name = DecomposeQualifiedName(qualified_name) + namespace, name = DecomposeQualifiedName(qualified_name) if name in valid_attrs: infos = self.GetQualifiedNameInfos(name, namespace) if infos["type"] != SYNTAXATTRIBUTE: @@ -878,9 +894,9 @@ raise ValueError("Invalid attribute \"%s\" for member \"%s\"!" % (qualified_name, node.nodeName)) for attr in valid_attrs: if attr not in attrs and \ - self.Namespaces[self.SchemaNamespace].has_key(attr) and \ - self.Namespaces[self.SchemaNamespace][attr].has_key("default"): - if self.Namespaces[self.SchemaNamespace][attr]["default"].has_key(element_name): + attr in self.Namespaces[self.SchemaNamespace] and \ + "default" in self.Namespaces[self.SchemaNamespace][attr]: + if element_name in self.Namespaces[self.SchemaNamespace][attr]["default"]: default = self.Namespaces[self.SchemaNamespace][attr]["default"][element_name] else: default = self.Namespaces[self.SchemaNamespace][attr]["default"]["default"] @@ -892,7 +908,7 @@ result = [] for child_infos in elements: if child_infos is not None: - if child_infos[1].has_key("name") and schema: + if "name" in child_infos[1] and schema: self.CurrentCompilations.append(child_infos[1]["name"]) namespace, name = DecomposeQualifiedName(child_infos[0]) infos = self.GetQualifiedNameInfos(name, namespace) @@ -901,7 +917,7 @@ element = infos["reduce"](self, child_infos[1], child_infos[2]) if element is not None: result.append(element) - if child_infos[1].has_key("name") and schema: + if "name" in child_infos[1] and schema: self.CurrentCompilations.pop(-1) annotations = [] children = [] @@ -913,22 +929,22 @@ return annotations, children def AddComplexType(self, typename, infos): - if not self.XMLClassDefinitions.has_key(typename): + if typename not in self.XMLClassDefinitions: self.XMLClassDefinitions[typename] = infos else: raise ValueError("\"%s\" class already defined. Choose another name!" % typename) def ParseSchema(self): pass - + def AddEquivalentClass(self, name, base): if name != base: equivalences = self.EquivalentClassesParent.setdefault(self.etreeNamespaceFormat % base, {}) equivalences[self.etreeNamespaceFormat % name] = True - + def AddDistinctionBetweenParentsInLookupClass( self, lookup_classes, parent, typeinfos): - parent = (self.etreeNamespaceFormat % parent + parent = (self.etreeNamespaceFormat % parent if parent is not None else None) parent_class = lookup_classes.get(parent) if parent_class is not None: @@ -939,7 +955,7 @@ lookup_classes[parent] = [typeinfos, parent_class] else: lookup_classes[parent] = typeinfos - + def AddToLookupClass(self, name, parent, typeinfos): lookup_name = self.etreeNamespaceFormat % name if isinstance(typeinfos, (StringType, UnicodeType)): @@ -958,7 +974,7 @@ self.AddDistinctionBetweenParentsInLookupClass( lookup_classes, parent, typeinfos) self.ComputedClassesLookUp[lookup_name] = lookup_classes - + def ExtractTypeInfos(self, name, parent, typeinfos): if isinstance(typeinfos, (StringType, UnicodeType)): namespace, type_name = DecomposeQualifiedName(typeinfos) @@ -986,12 +1002,12 @@ return self.CreateClass(name, parent, typeinfos) elif typeinfos["type"] == SIMPLETYPE: return typeinfos - + def GetEquivalentParents(self, parent): return reduce(lambda x, y: x + y, - [[p] + self.GetEquivalentParents(p) - for p in self.EquivalentClassesParent.get(parent, {}).keys()], []) - + [[p] + self.GetEquivalentParents(p) + for p in self.EquivalentClassesParent.get(parent, {}).keys()], []) + """ Methods that generates the classes """ @@ -1015,9 +1031,9 @@ self.Namespaces[self.TargetNamespace][result["name"]] = result elif infos["type"] == ELEMENTSGROUP: elements = [] - if infos.has_key("elements"): + if "elements" in infos: elements = infos["elements"] - elif infos.has_key("choices"): + elif "choices" in infos: elements = infos["choices"] for element in elements: if not isinstance(element["elmt_type"], (UnicodeType, StringType)) and \ @@ -1028,7 +1044,7 @@ if result is not None and \ not isinstance(result, (UnicodeType, StringType)): self.Namespaces[self.TargetNamespace][result["name"]] = result - + for name, parents in self.ComputedClassesLookUp.iteritems(): if isinstance(parents, DictType): computed_classes = parents.items() @@ -1042,19 +1058,19 @@ parents = dict(computed_classes) self.ComputedClassesLookUp[name] = parents parents[equivalent_parent] = computed_class - + return self.ComputedClasses - def CreateClass(self, name, parent, classinfos, baseclass = False): + def CreateClass(self, name, parent, classinfos, baseclass=False): if parent is not None: classname = "%s_%s" % (parent, name) else: classname = name - + # Checks that classe haven't been generated yet if self.AlreadyComputed.get(classname, False): return self.ComputedClassesInfos.get(classname, None) - + # If base classes haven't been generated bases = [] base_infos = classinfos.get("base", None) @@ -1088,29 +1104,29 @@ bases.append(DefaultElementClass) bases = tuple(bases) classmembers = {"__doc__": classinfos.get("doc", ""), "IsBaseClass": baseclass} - + self.AlreadyComputed[classname] = True - + for attribute in classinfos["attributes"]: infos = self.ExtractTypeInfos(attribute["name"], name, attribute["attr_type"]) - if infos is not None: + if infos is not None: if infos["type"] != SIMPLETYPE: raise ValueError("\"%s\" type is not a simple type!" % attribute["attr_type"]) attrname = attribute["name"] if attribute["use"] == "optional": - classmembers["add%s"%attrname] = generateAddMethod(attrname, self, attribute) - classmembers["delete%s"%attrname] = generateDeleteMethod(attrname) - classmembers["set%s"%attrname] = generateSetMethod(attrname) - classmembers["get%s"%attrname] = generateGetMethod(attrname) + classmembers["add%s" % attrname] = generateAddMethod(attrname, self, attribute) + classmembers["delete%s" % attrname] = generateDeleteMethod(attrname) + classmembers["set%s" % attrname] = generateSetMethod(attrname) + classmembers["get%s" % attrname] = generateGetMethod(attrname) else: raise ValueError("\"%s\" type unrecognized!" % attribute["attr_type"]) attribute["attr_type"] = infos - + for element in classinfos["elements"]: if element["type"] == CHOICE: elmtname = element["name"] choices = ComputeContentChoices(self, name, element) - classmembers["get%schoices"%elmtname] = generateGetChoicesMethod(element["choices"]) + classmembers["get%schoices" % elmtname] = generateGetChoicesMethod(element["choices"]) if element["maxOccurs"] == "unbounded" or element["maxOccurs"] > 1: classmembers["append%sbytype" % elmtname] = generateAppendChoiceByTypeMethod(element["maxOccurs"], self, element["choices"]) classmembers["insert%sbytype" % elmtname] = generateInsertChoiceByTypeMethod(element["maxOccurs"], self, element["choices"]) @@ -1141,30 +1157,31 @@ classmembers["delete%s" % elmtname] = generateDeleteMethod(elmtname) classmembers["set%s" % elmtname] = generateSetMethod(elmtname) classmembers["get%s" % elmtname] = generateGetMethod(elmtname) - + classmembers["_init_"] = generateInitMethod(self, classinfos) classmembers["StructurePattern"] = GetStructurePattern(classinfos) classmembers["getElementAttributes"] = generateGetElementAttributes(self, classinfos) classmembers["getElementInfos"] = generateGetElementInfos(self, classinfos) classmembers["setElementValue"] = generateSetElementValue(self, classinfos) - + class_definition = classobj(str(name), bases, classmembers) setattr(class_definition, "__getattr__", generateGetattrMethod(self, class_definition, classinfos)) setattr(class_definition, "__setattr__", generateSetattrMethod(self, class_definition, classinfos)) - class_infos = {"type": COMPILEDCOMPLEXTYPE, - "name": classname, - "initial": generateClassCreateFunction(class_definition), + class_infos = { + "type": COMPILEDCOMPLEXTYPE, + "name": classname, + "initial": generateClassCreateFunction(class_definition), } - + if self.FileName is not None: self.ComputedClasses[self.FileName][classname] = class_definition else: self.ComputedClasses[classname] = class_definition self.ComputedClassesInfos[classname] = class_infos - + self.AddToLookupClass(name, parent, class_definition) self.AddToLookupClass(classname, None, class_definition) - + return class_infos """ @@ -1183,18 +1200,19 @@ else: for classname, xmlclass in items: print "%s: %s" % (classname, str(xmlclass)) - + def PrintClassNames(self): classnames = self.XMLClassDefinitions.keys() classnames.sort() for classname in classnames: print classname -""" -Method that generate the method for generating the xml tree structure model by -following the attributes list defined -""" + def ComputeMultiplicity(name, infos): + """ + Method that generate the method for generating the xml tree structure model by + following the attributes list defined + """ if infos["minOccurs"] == 0: if infos["maxOccurs"] == "unbounded": return "(?:%s)*" % name @@ -1213,13 +1231,15 @@ if infos["maxOccurs"] == "unbounded": return "(?:%s){%d,}" % (name, infos["minOccurs"], name) else: - return "(?:%s){%d,%d}" % (name, infos["minOccurs"], - infos["maxOccurs"]) + return "(?:%s){%d,%d}" % (name, + infos["minOccurs"], + infos["maxOccurs"]) + def GetStructurePattern(classinfos): base_structure_pattern = ( classinfos["base"].StructurePattern.pattern[:-1] - if classinfos.has_key("base") else "") + if "base" in classinfos else "") elements = [] for element in classinfos["elements"]: if element["type"] == ANY: @@ -1244,33 +1264,35 @@ else: raise ValueError("XSD structure not yet supported!") -""" -Method that generate the method for creating a class instance -""" + def generateClassCreateFunction(class_definition): + """ + Method that generate the method for creating a class instance + """ def classCreatefunction(): return class_definition() return classCreatefunction + def generateGetattrMethod(factory, class_definition, classinfos): attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"]) optional_attributes = dict([(attr["name"], True) for attr in classinfos["attributes"] if attr["use"] == "optional"]) elements = dict([(element["name"], element) for element in classinfos["elements"]]) - + def getattrMethod(self, name): - if attributes.has_key(name): + if name in attributes: attribute_infos = attributes[name] attribute_infos["attr_type"] = FindTypeInfos(factory, attribute_infos["attr_type"]) value = self.get(name) if value is not None: return attribute_infos["attr_type"]["extract"](value, extract=False) - elif attribute_infos.has_key("fixed"): + elif "fixed" in attribute_infos: return attribute_infos["attr_type"]["extract"](attribute_infos["fixed"], extract=False) - elif attribute_infos.has_key("default"): + elif "default" in attribute_infos: return attribute_infos["attr_type"]["extract"](attribute_infos["default"], extract=False) return None - - elif elements.has_key(name): + + elif name in elements: element_infos = elements[name] element_infos["elmt_type"] = FindTypeInfos(factory, element_infos["elmt_type"]) if element_infos["type"] == CHOICE: @@ -1279,7 +1301,7 @@ return content elif len(content) > 0: return content[0] - return None + return None elif element_infos["type"] == ANY: return element_infos["elmt_type"]["extract"](self) elif name == "content" and element_infos["elmt_type"]["type"] == SIMPLETYPE: @@ -1290,29 +1312,30 @@ values = self.findall(element_name) if element_infos["elmt_type"]["type"] == SIMPLETYPE: return map(lambda value: - element_infos["elmt_type"]["extract"](value.text, extract=False), - values) + element_infos["elmt_type"]["extract"](value.text, extract=False), + values) return values else: value = self.find(element_name) if element_infos["elmt_type"]["type"] == SIMPLETYPE: return element_infos["elmt_type"]["extract"](value.text, extract=False) return value - - elif classinfos.has_key("base"): + + elif "base" in classinfos: return classinfos["base"].__getattr__(self, name) - + return DefaultElementClass.__getattribute__(self, name) - + return getattrMethod + def generateSetattrMethod(factory, class_definition, classinfos): attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"]) optional_attributes = dict([(attr["name"], True) for attr in classinfos["attributes"] if attr["use"] == "optional"]) elements = OrderedDict([(element["name"], element) for element in classinfos["elements"]]) - + def setattrMethod(self, name, value): - if attributes.has_key(name): + if name in attributes: attribute_infos = attributes[name] attribute_infos["attr_type"] = FindTypeInfos(factory, attribute_infos["attr_type"]) if optional_attributes.get(name, False): @@ -1320,65 +1343,66 @@ if value is None or value == default: self.attrib.pop(name, None) return - elif attribute_infos.has_key("fixed"): + elif "fixed" in attribute_infos: return return self.set(name, attribute_infos["attr_type"]["generate"](value)) - - elif elements.has_key(name): + + elif name in elements: element_infos = elements[name] element_infos["elmt_type"] = FindTypeInfos(factory, element_infos["elmt_type"]) if element_infos["type"] == ANY: element_infos["elmt_type"]["generate"](self, value) - + elif name == "content" and element_infos["elmt_type"]["type"] == SIMPLETYPE: self.text = element_infos["elmt_type"]["generate"](value) - + else: prefix = ("%s:" % factory.TargetNamespace if factory.TargetNamespace is not None else "") element_xpath = (prefix + name if name != "content" else elements["content"]["elmt_type"]["choices_xpath"].path) - + for element in self.xpath(element_xpath, namespaces=factory.NSMAP): self.remove(element) - + if value is not None: element_idx = elements.keys().index(name) if element_idx > 0: previous_elements_xpath = "|".join(map( lambda x: prefix + x - if x != "content" - else elements["content"]["elmt_type"]["choices_xpath"].path, + if x != "content" + else elements["content"]["elmt_type"]["choices_xpath"].path, elements.keys()[:element_idx])) - + insertion_point = len(self.xpath(previous_elements_xpath, namespaces=factory.NSMAP)) else: insertion_point = 0 - + if not isinstance(value, ListType): value = [value] - + for element in reversed(value): if element_infos["elmt_type"]["type"] == SIMPLETYPE: tmp_element = etree.Element(factory.etreeNamespaceFormat % name) tmp_element.text = element_infos["elmt_type"]["generate"](element) element = tmp_element self.insert(insertion_point, element) - - elif classinfos.has_key("base"): + + elif "base" in classinfos: return classinfos["base"].__setattr__(self, name, value) - + else: raise AttributeError("'%s' can't have an attribute '%s'." % (self.__class__.__name__, name)) - + return setattrMethod + def gettypeinfos(name, facets): - if facets.has_key("enumeration") and facets["enumeration"][0] is not None: + if "enumeration" in facets and facets["enumeration"][0] is not None: return facets["enumeration"][0] - elif facets.has_key("maxInclusive"): - limits = {"max" : None, "min" : None} + elif "maxInclusive" in facets: + limits = {"max": None, "min": None} if facets["maxInclusive"][0] is not None: limits["max"] = facets["maxInclusive"][0] elif facets["maxExclusive"][0] is not None: @@ -1391,24 +1415,28 @@ return limits return name + def generateGetElementAttributes(factory, classinfos): def getElementAttributes(self): attr_list = [] - if classinfos.has_key("base"): + if "base" in classinfos: attr_list.extend(classinfos["base"].getElementAttributes(self)) for attr in classinfos["attributes"]: if attr["use"] != "prohibited": - attr_params = {"name" : attr["name"], "use" : attr["use"], - "type" : gettypeinfos(attr["attr_type"]["basename"], attr["attr_type"]["facets"]), - "value" : getattr(self, attr["name"], "")} + attr_params = { + "name": attr["name"], + "use": attr["use"], + "type": gettypeinfos(attr["attr_type"]["basename"], attr["attr_type"]["facets"]), + "value": getattr(self, attr["name"], "")} attr_list.append(attr_params) return attr_list return getElementAttributes + def generateGetElementInfos(factory, classinfos): attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"]) elements = dict([(element["name"], element) for element in classinfos["elements"]]) - + def getElementInfos(self, name, path=None, derived=False): attr_type = "element" value = None @@ -1416,17 +1444,17 @@ children = [] if path is not None: parts = path.split(".", 1) - if attributes.has_key(parts[0]): + if parts[0] in attributes: if len(parts) != 1: raise ValueError("Wrong path!") - attr_type = gettypeinfos(attributes[parts[0]]["attr_type"]["basename"], + attr_type = gettypeinfos(attributes[parts[0]]["attr_type"]["basename"], attributes[parts[0]]["attr_type"]["facets"]) value = getattr(self, parts[0], "") - elif elements.has_key(parts[0]): + elif parts[0] in elements: if elements[parts[0]]["elmt_type"]["type"] == SIMPLETYPE: if len(parts) != 1: raise ValueError("Wrong path!") - attr_type = gettypeinfos(elements[parts[0]]["elmt_type"]["basename"], + attr_type = gettypeinfos(elements[parts[0]]["elmt_type"]["basename"], elements[parts[0]]["elmt_type"]["facets"]) value = getattr(self, parts[0], "") elif parts[0] == "content": @@ -1439,17 +1467,17 @@ return attr.getElementInfos(parts[0]) else: return attr.getElementInfos(parts[0], parts[1]) - elif elements.has_key("content"): + elif "content" in elements: if len(parts) > 0: return self.content.getElementInfos(name, path) - elif classinfos.has_key("base"): + elif "base" in classinfos: classinfos["base"].getElementInfos(name, path) else: raise ValueError("Wrong path!") else: if not derived: children.extend(self.getElementAttributes()) - if classinfos.has_key("base"): + if "base" in classinfos: children.extend(classinfos["base"].getElementInfos(self, name, derived=True)["children"]) for element_name, element in elements.items(): if element["minOccurs"] == 0: @@ -1463,8 +1491,10 @@ if self.content is not None: children.extend(self.content.getElementInfos(value)["children"]) elif element["elmt_type"]["type"] == SIMPLETYPE: - children.append({"name": element_name, "require": element["minOccurs"] != 0, - "type": gettypeinfos(element["elmt_type"]["basename"], + children.append({ + "name": element_name, + "require": element["minOccurs"] != 0, + "type": gettypeinfos(element["elmt_type"]["basename"], element["elmt_type"]["facets"]), "value": getattr(self, element_name, None)}) else: @@ -1475,28 +1505,29 @@ return {"name": name, "type": attr_type, "value": value, "use": use, "children": children} return getElementInfos + def generateSetElementValue(factory, classinfos): attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"]) elements = dict([(element["name"], element) for element in classinfos["elements"]]) - + def setElementValue(self, path, value): if path is not None: parts = path.split(".", 1) - if attributes.has_key(parts[0]): + if parts[0] in attributes: if len(parts) != 1: raise ValueError("Wrong path!") if attributes[parts[0]]["attr_type"]["basename"] == "boolean": setattr(self, parts[0], value) elif attributes[parts[0]]["use"] == "optional" and value == "": - if attributes[parts[0]].has_key("default"): - setattr(self, parts[0], - attributes[parts[0]]["attr_type"]["extract"]( - attributes[parts[0]]["default"], False)) + if "default" in attributes[parts[0]]: + setattr(self, parts[0], + attributes[parts[0]]["attr_type"]["extract"]( + attributes[parts[0]]["default"], False)) else: setattr(self, parts[0], None) else: setattr(self, parts[0], attributes[parts[0]]["attr_type"]["extract"](value, False)) - elif elements.has_key(parts[0]): + elif parts[0] in elements: if elements[parts[0]]["elmt_type"]["type"] == SIMPLETYPE: if len(parts) != 1: raise ValueError("Wrong path!") @@ -1511,17 +1542,17 @@ if instance is None and elements[parts[0]]["minOccurs"] == 0: instance = elements[parts[0]]["elmt_type"]["initial"]() setattr(self, parts[0], instance) - if instance != None: + if instance is not None: if len(parts) > 1: instance.setElementValue(parts[1], value) else: instance.setElementValue(None, value) - elif elements.has_key("content"): + elif "content" in elements: if len(parts) > 0: self.content.setElementValue(path, value) - elif classinfos.has_key("base"): + elif "base" in classinfos: classinfos["base"].setElementValue(self, path, value) - elif elements.has_key("content"): + elif "content" in elements: if value == "": if elements["content"]["minOccurs"] == 0: self.setcontent([]) @@ -1531,12 +1562,14 @@ self.setcontentbytype(value) return setElementValue -""" -Methods that generates the different methods for setting and getting the attributes -""" + def generateInitMethod(factory, classinfos): + """ + Methods that generates the different methods for setting and getting the attributes + """ + def initMethod(self): - if classinfos.has_key("base"): + if "base" in classinfos: classinfos["base"]._init_(self) for attribute in classinfos["attributes"]: attribute["attr_type"] = FindTypeInfos(factory, attribute["attr_type"]) @@ -1553,21 +1586,24 @@ map(self.append, initial) return initMethod + def generateSetMethod(attr): def setMethod(self, value): setattr(self, attr, value) return setMethod + def generateGetMethod(attr): def getMethod(self): return getattr(self, attr, None) return getMethod + def generateAddMethod(attr, factory, infos): def addMethod(self): if infos["type"] == ATTRIBUTE: infos["attr_type"] = FindTypeInfos(factory, infos["attr_type"]) - if not infos.has_key("default"): + if "default" not in infos: setattr(self, attr, infos["attr_type"]["initial"]()) elif infos["type"] == ELEMENT: infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"]) @@ -1579,11 +1615,13 @@ raise ValueError("Invalid class attribute!") return addMethod + def generateDeleteMethod(attr): def deleteMethod(self): setattr(self, attr, None) return deleteMethod + def generateAppendMethod(attr, maxOccurs, factory, infos): def appendMethod(self, value): infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"]) @@ -1597,6 +1635,7 @@ raise ValueError("There can't be more than %d values in \"%s\"!" % (maxOccurs, attr)) return appendMethod + def generateInsertMethod(attr, maxOccurs, factory, infos): def insertMethod(self, index, value): infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"]) @@ -1612,15 +1651,18 @@ raise ValueError("There can't be more than %d values in \"%s\"!" % (maxOccurs, attr)) return insertMethod + def generateGetChoicesMethod(choice_types): def getChoicesMethod(self): return [choice["name"] for choice in choice_types] return getChoicesMethod + def generateSetChoiceByTypeMethod(factory, choice_types): choices = dict([(choice["name"], choice) for choice in choice_types]) + def setChoiceMethod(self, content_type): - if not choices.has_key(content_type): + if content_type not in choices: raise ValueError("Unknown \"%s\" choice type for \"content\"!" % content_type) choices[content_type]["elmt_type"] = FindTypeInfos(factory, choices[content_type]["elmt_type"]) new_content = choices[content_type]["elmt_type"]["initial"]() @@ -1629,10 +1671,12 @@ return new_content return setChoiceMethod + def generateAppendChoiceByTypeMethod(maxOccurs, factory, choice_types): choices = dict([(choice["name"], choice) for choice in choice_types]) + def appendChoiceMethod(self, content_type): - if not choices.has_key(content_type): + if content_type not in choices: raise ValueError("Unknown \"%s\" choice type for \"content\"!" % content_type) choices[content_type]["elmt_type"] = FindTypeInfos(factory, choices[content_type]["elmt_type"]) if maxOccurs == "unbounded" or len(self.content) < maxOccurs: @@ -1644,10 +1688,12 @@ raise ValueError("There can't be more than %d values in \"content\"!" % maxOccurs) return appendChoiceMethod + def generateInsertChoiceByTypeMethod(maxOccurs, factory, choice_types): choices = dict([(choice["name"], choice) for choice in choice_types]) + def insertChoiceMethod(self, index, content_type): - if not choices.has_key(content_type): + if content_type not in choices: raise ValueError("Unknown \"%s\" choice type for \"content\"!" % content_type) choices[type]["elmt_type"] = FindTypeInfos(factory, choices[content_type]["elmt_type"]) if maxOccurs == "unbounded" or len(self.content) < maxOccurs: @@ -1659,6 +1705,7 @@ raise ValueError("There can't be more than %d values in \"content\"!" % maxOccurs) return insertChoiceMethod + def generateRemoveMethod(attr, minOccurs): def removeMethod(self, index): attr_list = getattr(self, attr) @@ -1668,52 +1715,56 @@ raise ValueError("There can't be less than %d values in \"%s\"!" % (minOccurs, attr)) return removeMethod + def generateCountMethod(attr): def countMethod(self): return len(getattr(self, attr)) return countMethod + """ This function generate a xml parser from a class factory """ NAMESPACE_PATTERN = re.compile("xmlns(?:\:[^\=]*)?=\"[^\"]*\" ") + class DefaultElementClass(etree.ElementBase): - + StructurePattern = re.compile("$") - + def _init_(self): pass - + def getLocalTag(self): return etree.QName(self.tag).localname - + def tostring(self): return NAMESPACE_PATTERN.sub("", etree.tostring(self, pretty_print=True, encoding='utf-8')).decode('utf-8') + class XMLElementClassLookUp(etree.PythonElementClassLookup): - + def __init__(self, classes, *args, **kwargs): etree.PythonElementClassLookup.__init__(self, *args, **kwargs) self.LookUpClasses = classes - + def GetElementClass(self, element_tag, parent_tag=None, default=DefaultElementClass): element_class = self.LookUpClasses.get(element_tag, (default, None)) if not isinstance(element_class, DictType): if isinstance(element_class[0], (StringType, UnicodeType)): return self.GetElementClass(element_class[0], default=default) return element_class[0] - + element_with_parent_class = element_class.get(parent_tag, default) if isinstance(element_with_parent_class, (StringType, UnicodeType)): return self.GetElementClass(element_with_parent_class, default=default) return element_with_parent_class - + def lookup(self, document, element): parent = element.getparent() - element_class = self.GetElementClass(element.tag, - parent.tag if parent is not None else None) + element_class = self.GetElementClass( + element.tag, parent.tag if parent is not None else None) if isinstance(element_class, ListType): children = "".join([ "%s " % etree.QName(child.tag).localname @@ -1726,6 +1777,7 @@ return element_class[0] return element_class + class XMLClassParser(etree.XMLParser): def __init__(self, namespaces, default_namespace_format, base_class, xsd_schema, *args, **kwargs): @@ -1741,24 +1793,24 @@ self.RootNSMAP = namespaces self.BaseClass = base_class self.XSDSchema = xsd_schema - + def set_element_class_lookup(self, class_lookup): etree.XMLParser.set_element_class_lookup(self, class_lookup) self.ClassLookup = class_lookup - + def LoadXMLString(self, xml_string): tree = etree.fromstring(xml_string, self) if not self.XSDSchema.validate(tree): error = self.XSDSchema.error_log.last_error return tree, (error.line, error.message) - return tree, None - + return tree, None + def Dumps(self, xml_obj): return etree.tostring(xml_obj, encoding='utf-8') - + def Loads(self, xml_string): return etree.fromstring(xml_string, self) - + def CreateRoot(self): if self.BaseClass is not None: root = self.makeelement( @@ -1767,42 +1819,42 @@ root._init_() return root return None - + def GetElementClass(self, element_tag, parent_tag=None): return self.ClassLookup.GetElementClass( - self.DefaultNamespaceFormat % element_tag, - self.DefaultNamespaceFormat % parent_tag - if parent_tag is not None else parent_tag, + self.DefaultNamespaceFormat % element_tag, + self.DefaultNamespaceFormat % parent_tag + if parent_tag is not None else parent_tag, None) - + def CreateElement(self, element_tag, parent_tag=None, class_idx=None): element_class = self.GetElementClass(element_tag, parent_tag) if isinstance(element_class, ListType): if class_idx is not None and class_idx < len(element_class): new_element = element_class[class_idx]() else: - raise ValueError, "No corresponding class found!" + raise ValueError("No corresponding class found!") else: new_element = element_class() DefaultElementClass.__setattr__(new_element, "tag", self.DefaultNamespaceFormat % element_tag) new_element._init_() return new_element - + + def GenerateParser(factory, xsdstring): ComputedClasses = factory.CreateClasses() - + if factory.FileName is not None: ComputedClasses = ComputedClasses[factory.FileName] BaseClass = [(name, XSDclass) for name, XSDclass in ComputedClasses.items() if XSDclass.IsBaseClass] - + parser = XMLClassParser( factory.NSMAP, factory.etreeNamespaceFormat, BaseClass[0] if len(BaseClass) == 1 else None, etree.XMLSchema(etree.fromstring(xsdstring)), - strip_cdata = False, remove_blank_text=True) + strip_cdata=False, remove_blank_text=True) class_lookup = XMLElementClassLookUp(factory.ComputedClassesLookUp) parser.set_element_class_lookup(class_lookup) - + return parser - diff -r 31e63e25b4cc -r 64beb9e9c749 xmlclass/xsdschema.py --- a/xmlclass/xsdschema.py Mon Aug 21 20:17:19 2017 +0000 +++ b/xmlclass/xsdschema.py Mon Aug 21 23:22:58 2017 +0300 @@ -22,16 +22,19 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import os, re +import os +import re import datetime from xml.dom import minidom from types import * from xmlclass import * + def GenerateDictFacets(facets): return dict([(name, (None, False)) for name in facets]) + def GenerateSimpleTypeXMLText(function): def generateXMLTextMethod(value, name=None, indent=0): text = "" @@ -44,9 +47,11 @@ return text return generateXMLTextMethod + def GenerateFloatXMLText(extra_values=[], decimal=None): float_format = (lambda x: "{:.{width}f}".format(x, width=decimal).rstrip('0') if decimal is not None else str) + def generateXMLTextMethod(value, name=None, indent=0): text = "" if name is not None: @@ -62,31 +67,32 @@ text += "\n" % name return text return generateXMLTextMethod - + + DEFAULT_FACETS = GenerateDictFacets(["pattern", "whiteSpace", "enumeration"]) NUMBER_FACETS = GenerateDictFacets(DEFAULT_FACETS.keys() + ["maxInclusive", "maxExclusive", "minInclusive", "minExclusive"]) DECIMAL_FACETS = GenerateDictFacets(NUMBER_FACETS.keys() + ["totalDigits", "fractionDigits"]) STRING_FACETS = GenerateDictFacets(DEFAULT_FACETS.keys() + ["length", "minLength", "maxLength"]) -ALL_FACETS = ["pattern", "whiteSpace", "enumeration", "maxInclusive", - "maxExclusive", "minInclusive", "minExclusive", "totalDigits", - "fractionDigits", "length", "minLength", "maxLength"] - - -#------------------------------------------------------------------------------- +ALL_FACETS = ["pattern", "whiteSpace", "enumeration", "maxInclusive", + "maxExclusive", "minInclusive", "minExclusive", "totalDigits", + "fractionDigits", "length", "minLength", "maxLength"] + + +# ------------------------------------------------------------------------------- # Structure reducing functions -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Documentation elements def ReduceAppInfo(factory, attributes, elements): - return {"type": "appinfo", "source": attributes.get("source", None), + return {"type": "appinfo", "source": attributes.get("source", None), "content": "\n".join(elements)} def ReduceDocumentation(factory, attributes, elements): - return {"type": "documentation", "source": attributes.get("source", None), + return {"type": "documentation", "source": attributes.get("source", None), "language": attributes.get("lang", "any"), "content": "\n".join(elements)} @@ -98,10 +104,10 @@ annotation["appinfo"].append((child["source"], child["content"])) elif child["type"] == "documentation": if child["source"] is not None: - text = "(source: %(source)s):\n%(content)s\n\n"%child + text = "(source: %(source)s):\n%(content)s\n\n" % child else: text = child["content"] + "\n\n" - if not annotation["documentation"].has_key(child["language"]): + if not child["language"] in annotation["documentation"]: annotation["documentation"] = text else: annotation["documentation"] += text @@ -109,10 +115,11 @@ # Simple type elements + def GenerateFacetReducing(facetname, canbefixed): def ReduceFacet(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) - if attributes.has_key("value"): + if "value" in attributes: facet = {"type": facetname, "value": attributes["value"], "doc": annotations} if canbefixed: facet["fixed"] = attributes.get("fixed", False) @@ -124,7 +131,7 @@ def ReduceList(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) list = {"type": "list", "itemType": attributes.get("itemType", None), "doc": annotations} - + if len(children) > 0 and children[0]["type"] == SIMPLETYPE: if list["itemType"] is None: list["itemType"] = children[0] @@ -138,7 +145,7 @@ def ReduceUnion(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) union = {"type": "union", "memberTypes": attributes.get("memberTypes", []), "doc": annotations} - + for child in children: if child["type"] == SIMPLETYPE: union["memberTypes"].appendchild @@ -151,35 +158,35 @@ # Initialize type informations facets = {} simpleType = {"type": SIMPLETYPE, "final": attributes.get("final", [])} - if attributes.has_key("name"): + if "name" in attributes: simpleType["name"] = attributes["name"] - + if typeinfos["type"] in ["restriction", "extension"]: # Search for base type definition if isinstance(typeinfos["base"], (StringType, UnicodeType)): basetypeinfos = factory.FindSchemaElement(typeinfos["base"], SIMPLETYPE) if basetypeinfos is None: - raise "\"%s\" isn't defined!" % typeinfos["base"] + raise "\"%s\" isn't defined!" % typeinfos["base"] else: basetypeinfos = typeinfos["base"] - + # Check that base type is a simple type if basetypeinfos["type"] != SIMPLETYPE: raise ValueError("Base type given isn't a simpleType!") - + simpleType["basename"] = basetypeinfos["basename"] - + # Check that derivation is allowed - if basetypeinfos.has_key("final"): + if "final" in basetypeinfos: if "#all" in basetypeinfos["final"]: raise ValueError("Base type can't be derivated!") if "restriction" in basetypeinfos["final"] and typeinfos["type"] == "restriction": raise ValueError("Base type can't be derivated by restriction!") - + # Extract simple type facets for facet in typeinfos.get("facets", []): facettype = facet["type"] - if not basetypeinfos["facets"].has_key(facettype): + if facettype not in basetypeinfos["facets"]: raise ValueError("\"%s\" facet can't be defined for \"%s\" type!" % (facettype, type)) elif basetypeinfos["facets"][facettype][1]: raise ValueError("\"%s\" facet is fixed on base type!" % facettype) @@ -195,103 +202,103 @@ continue else: raise ValueError("\"%s\" facet can't be defined with another facet type!" % facettype) - elif facets.has_key("enumeration"): + elif "enumeration" in facets: raise ValueError("\"enumeration\" facet can't be defined with another facet type!") - elif facets.has_key("pattern"): + elif "pattern" in facets: raise ValueError("\"pattern\" facet can't be defined with another facet type!") - elif facets.has_key(facettype): + elif facettype in facets: raise ValueError("\"%s\" facet can't be defined two times!" % facettype) elif facettype == "length": - if facets.has_key("minLength"): + if "minLength" in facets: raise ValueError("\"length\" and \"minLength\" facets can't be defined at the same time!") - if facets.has_key("maxLength"): + if "maxLength" in facets: raise ValueError("\"length\" and \"maxLength\" facets can't be defined at the same time!") try: value = int(value) - except: + except Exception: raise ValueError("\"length\" must be an integer!") if value < 0: raise ValueError("\"length\" can't be negative!") elif basevalue is not None and basevalue != value: raise ValueError("\"length\" can't be different from \"length\" defined in base type!") elif facettype == "minLength": - if facets.has_key("length"): + if "length" in facets: raise ValueError("\"length\" and \"minLength\" facets can't be defined at the same time!") try: value = int(value) - except: + except Exception: raise ValueError("\"minLength\" must be an integer!") if value < 0: raise ValueError("\"minLength\" can't be negative!") - elif facets.has_key("maxLength") and value > facets["maxLength"]: + elif "maxLength" in facets and value > facets["maxLength"]: raise ValueError("\"minLength\" must be lesser than or equal to \"maxLength\"!") elif basevalue is not None and basevalue < value: raise ValueError("\"minLength\" can't be lesser than \"minLength\" defined in base type!") elif facettype == "maxLength": - if facets.has_key("length"): + if "length" in facets: raise ValueError("\"length\" and \"maxLength\" facets can't be defined at the same time!") try: value = int(value) - except: + except Exception: raise ValueError("\"maxLength\" must be an integer!") if value < 0: raise ValueError("\"maxLength\" can't be negative!") - elif facets.has_key("minLength") and value < facets["minLength"]: + elif "minLength" in facets and value < facets["minLength"]: raise ValueError("\"minLength\" must be lesser than or equal to \"maxLength\"!") elif basevalue is not None and basevalue > value: raise ValueError("\"maxLength\" can't be greater than \"maxLength\" defined in base type!") elif facettype == "minInclusive": - if facets.has_key("minExclusive"): + if "minExclusive" in facets: raise ValueError("\"minExclusive\" and \"minInclusive\" facets can't be defined at the same time!") value = basetypeinfos["extract"](facet["value"], False) - if facets.has_key("maxInclusive") and value > facets["maxInclusive"][0]: + if "maxInclusive" in facets and value > facets["maxInclusive"][0]: raise ValueError("\"minInclusive\" must be lesser than or equal to \"maxInclusive\"!") - elif facets.has_key("maxExclusive") and value >= facets["maxExclusive"][0]: + elif "maxExclusive" in facets and value >= facets["maxExclusive"][0]: raise ValueError("\"minInclusive\" must be lesser than \"maxExclusive\"!") elif facettype == "minExclusive": - if facets.has_key("minInclusive"): + if "minInclusive" in facets: raise ValueError("\"minExclusive\" and \"minInclusive\" facets can't be defined at the same time!") value = basetypeinfos["extract"](facet["value"], False) - if facets.has_key("maxInclusive") and value >= facets["maxInclusive"][0]: + if "maxInclusive" in facets and value >= facets["maxInclusive"][0]: raise ValueError("\"minExclusive\" must be lesser than \"maxInclusive\"!") - elif facets.has_key("maxExclusive") and value >= facets["maxExclusive"][0]: + elif "maxExclusive" in facets and value >= facets["maxExclusive"][0]: raise ValueError("\"minExclusive\" must be lesser than \"maxExclusive\"!") elif facettype == "maxInclusive": - if facets.has_key("maxExclusive"): + if "maxExclusive" in facets: raise ValueError("\"maxExclusive\" and \"maxInclusive\" facets can't be defined at the same time!") value = basetypeinfos["extract"](facet["value"], False) - if facets.has_key("minInclusive") and value < facets["minInclusive"][0]: + if "minInclusive" in facets and value < facets["minInclusive"][0]: raise ValueError("\"minInclusive\" must be lesser than or equal to \"maxInclusive\"!") - elif facets.has_key("minExclusive") and value <= facets["minExclusive"][0]: + elif "minExclusive" in facets and value <= facets["minExclusive"][0]: raise ValueError("\"minExclusive\" must be lesser than \"maxInclusive\"!") elif facettype == "maxExclusive": - if facets.has_key("maxInclusive"): + if "maxInclusive" in facets: raise ValueError("\"maxExclusive\" and \"maxInclusive\" facets can't be defined at the same time!") value = basetypeinfos["extract"](facet["value"], False) - if facets.has_key("minInclusive") and value <= facets["minInclusive"][0]: + if "minInclusive" in facets and value <= facets["minInclusive"][0]: raise ValueError("\"minInclusive\" must be lesser than \"maxExclusive\"!") - elif facets.has_key("minExclusive") and value <= facets["minExclusive"][0]: + elif "minExclusive" in facets and value <= facets["minExclusive"][0]: raise ValueError("\"minExclusive\" must be lesser than \"maxExclusive\"!") elif facettype == "whiteSpace": if basevalue == "collapse" and value in ["preserve", "replace"] or basevalue == "replace" and value == "preserve": - raise ValueError("\"whiteSpace\" is incompatible with \"whiteSpace\" defined in base type!") + raise ValueError("\"whiteSpace\" is incompatible with \"whiteSpace\" defined in base type!") elif facettype == "totalDigits": - if facets.has_key("fractionDigits") and value <= facets["fractionDigits"][0]: + if "fractionDigits" in facets and value <= facets["fractionDigits"][0]: raise ValueError("\"fractionDigits\" must be lesser than or equal to \"totalDigits\"!") elif basevalue is not None and value > basevalue: raise ValueError("\"totalDigits\" can't be greater than \"totalDigits\" defined in base type!") elif facettype == "fractionDigits": - if facets.has_key("totalDigits") and value <= facets["totalDigits"][0]: + if "totalDigits" in facets and value <= facets["totalDigits"][0]: raise ValueError("\"fractionDigits\" must be lesser than or equal to \"totalDigits\"!") elif basevalue is not None and value > basevalue: raise ValueError("\"totalDigits\" can't be greater than \"totalDigits\" defined in base type!") facets[facettype] = (value, facet.get("fixed", False)) - - # Report not redefined facet from base type to new created type + + # Report not redefined facet from base type to new created type for facettype, facetvalue in basetypeinfos["facets"].items(): - if not facets.has_key(facettype): + if facettype not in facets: facets[facettype] = facetvalue - + # Generate extract value for new created type def ExtractSimpleTypeValue(attr, extract=True): value = basetypeinfos["extract"](attr, extract) @@ -311,13 +318,13 @@ raise ValueError("value must be greater than %s" % str(facetvalue)) elif facetname == "maxInclusive" and value > facetvalue: raise ValueError("value must be lesser than or equal to %s" % str(facetvalue)) - elif facetname == "maxExclusive" and value >= facetvalue: + elif facetname == "maxExclusive" and value >= facetvalue: raise ValueError("value must be lesser than %s" % str(facetvalue)) elif facetname == "pattern": model = re.compile("(?:%s)?$" % "|".join(map(lambda x: "(?:%s)" % x, facetvalue))) result = model.match(value) if result is None: - if len(facetvalue) > 1: + if len(facetvalue) > 1: raise ValueError("value doesn't follow any of the patterns %s" % ",".join(facetvalue)) else: raise ValueError("value doesn't follow the pattern %s" % facetvalue[0]) @@ -327,7 +334,7 @@ elif facetvalue == "collapse": value = GetToken(value, False) return value - + def CheckSimpleTypeValue(value): for facetname, (facetvalue, facetfixed) in facets.items(): if facetvalue is not None: @@ -345,18 +352,18 @@ return False elif facetname == "maxInclusive" and value > facetvalue: return False - elif facetname == "maxExclusive" and value >= facetvalue: + elif facetname == "maxExclusive" and value >= facetvalue: return False elif facetname == "pattern": model = re.compile("(?:%s)?$" % "|".join(map(lambda x: "(?:%s)" % x, facetvalue))) result = model.match(value) if result is None: - if len(facetvalue) > 1: + if len(facetvalue) > 1: raise ValueError("value doesn't follow any of the patterns %s" % ",".join(facetvalue)) else: raise ValueError("value doesn't follow the pattern %s" % facetvalue[0]) return True - + def SimpleTypeInitialValue(): for facetname, (facetvalue, facetfixed) in facets.items(): if facetvalue is not None: @@ -372,12 +379,12 @@ return facetvalue + 1 elif facetname == "maxInclusive" and facetvalue < 0: return facetvalue - elif facetname == "maxExclusive" and facetvalue <= 0: + elif facetname == "maxExclusive" and facetvalue <= 0: return facetvalue - 1 return basetypeinfos["initial"]() - + GenerateSimpleType = basetypeinfos["generate"] - + elif typeinfos["type"] == "list": # Search for item type definition if isinstance(typeinfos["itemType"], (StringType, UnicodeType)): @@ -386,41 +393,42 @@ raise "\"%s\" isn't defined!" % typeinfos["itemType"] else: itemtypeinfos = typeinfos["itemType"] - + # Check that item type is a simple type if itemtypeinfos["type"] != SIMPLETYPE: - raise ValueError, "Item type given isn't a simpleType!" - + raise ValueError("Item type given isn't a simpleType!") + simpleType["basename"] = "list" - + # Check that derivation is allowed - if itemtypeinfos.has_key("final"): - if itemtypeinfos["final"].has_key("#all"): + if "final" in itemtypeinfos: + if "#all" in itemtypeinfos["final"]: raise ValueError("Item type can't be derivated!") - if itemtypeinfos["final"].has_key("list"): + if "list" in itemtypeinfos["final"]: raise ValueError("Item type can't be derivated by list!") - + # Generate extract value for new created type - def ExtractSimpleTypeValue(attr, extract = True): + def ExtractSimpleTypeValue(attr, extract=True): values = [] for value in GetToken(attr, extract).split(" "): values.append(itemtypeinfos["extract"](value, False)) return values - + def CheckSimpleTypeValue(value): for item in value: result = itemtypeinfos["check"](item) if not result: return result return True - - SimpleTypeInitialValue = lambda: [] - + + def SimpleTypeInitialValue(): + return [] + GenerateSimpleType = GenerateSimpleTypeXMLText(lambda x: " ".join(map(itemtypeinfos["generate"], x))) - + facets = GenerateDictFacets(["length", "maxLength", "minLength", "enumeration", "pattern"]) facets["whiteSpace"] = ("collapse", False) - + elif typeinfos["type"] == "union": # Search for member types definition membertypesinfos = [] @@ -431,24 +439,24 @@ raise ValueError("\"%s\" isn't defined!" % membertype) else: infos = membertype - + # Check that member type is a simple type if infos["type"] != SIMPLETYPE: raise ValueError("Member type given isn't a simpleType!") - + # Check that derivation is allowed - if infos.has_key("final"): - if infos["final"].has_key("#all"): + if "final" in infos: + if "#all" in infos["final"]: raise ValueError("Item type can't be derivated!") - if infos["final"].has_key("union"): + if "union" in infos["final"]: raise ValueError("Member type can't be derivated by union!") - + membertypesinfos.append(infos) - + simpleType["basename"] = "union" - + # Generate extract value for new created type - def ExtractSimpleTypeValue(attr, extract = True): + def ExtractSimpleTypeValue(attr, extract=True): if extract: value = GetAttributeValue(attr) else: @@ -456,28 +464,28 @@ for infos in membertypesinfos: try: return infos["extract"](attr, False) - except: + except Exception: pass raise ValueError("\"%s\" isn't valid for type defined for union!") - + def CheckSimpleTypeValue(value): for infos in membertypesinfos: result = infos["check"](value) if result: return result return False - + SimpleTypeInitialValue = membertypesinfos[0]["initial"] - + def GenerateSimpleTypeFunction(value): if isinstance(value, BooleanType): return {True: "true", False: "false"}[value] else: return str(value) GenerateSimpleType = GenerateSimpleTypeXMLText(GenerateSimpleTypeFunction) - + facets = GenerateDictFacets(["pattern", "enumeration"]) - + simpleType["facets"] = facets simpleType["extract"] = ExtractSimpleTypeValue simpleType["initial"] = SimpleTypeInitialValue @@ -485,25 +493,27 @@ simpleType["generate"] = GenerateSimpleType return simpleType + def ReduceSimpleType(factory, attributes, elements): # Reduce all the simple type children annotations, children = factory.ReduceElements(elements) - + simpleType = CreateSimpleType(factory, attributes, children[0]) simpleType["doc"] = annotations - + return simpleType # Complex type + def ExtractAttributes(factory, elements, base=None): attrs = [] attrnames = {} if base is not None: basetypeinfos = factory.FindSchemaElement(base) if not isinstance(basetypeinfos, (UnicodeType, StringType)) and basetypeinfos["type"] == COMPLEXTYPE: - attrnames = dict(map(lambda x:(x["name"], True), basetypeinfos["attributes"])) - + attrnames = dict(map(lambda x: (x["name"], True), basetypeinfos["attributes"])) + for element in elements: if element["type"] == ATTRIBUTE: if attrnames.get(element["name"], False): @@ -534,7 +544,7 @@ raise ValueError("Only one base type can be defined for restriction!") if restriction["base"] is None: raise ValueError("No base type has been defined for restriction!") - + while len(children) > 0 and children[0]["type"] in ALL_FACETS: restriction["facets"].append(children.pop(0)) restriction["attributes"] = ExtractAttributes(factory, children, restriction["base"]) @@ -543,7 +553,7 @@ def ReduceExtension(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) - if not attributes.has_key("base"): + if "base" not in attributes: raise ValueError("No base type has been defined for extension!") extension = {"type": "extension", "attributes": [], "elements": [], "base": attributes["base"], "doc": annotations} if len(children) > 0: @@ -558,7 +568,7 @@ extension["elements"].append(content) elif group["type"] == "group": elmtgroup = factory.FindSchemaElement(child["ref"], ELEMENTSGROUP) - if elmtgroup.has_key("elements"): + if "elements" in elmtgroup: extension["elements"] = elmtgroup["elements"] extension["order"] = elmtgroup["order"] else: @@ -571,24 +581,24 @@ def ReduceSimpleContent(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) - + simpleContent = children[0].copy() - + basetypeinfos = factory.FindSchemaElement(simpleContent["base"]) if basetypeinfos["type"] == SIMPLETYPE: contenttypeinfos = simpleContent.copy() simpleContent.pop("base") - elif basetypeinfos["type"] == COMPLEXTYPE and \ - len(basetypeinfos["elements"]) == 1 and \ - basetypeinfos["elements"][0]["name"] == "content" and \ - basetypeinfos["elements"][0].has_key("elmt_type") and \ - basetypeinfos["elements"][0]["elmt_type"]["type"] == SIMPLETYPE: + elif (basetypeinfos["type"] == COMPLEXTYPE and + len(basetypeinfos["elements"]) == 1 and + basetypeinfos["elements"][0]["name"] == "content" and + "elmt_type" in basetypeinfos["elements"][0] and + basetypeinfos["elements"][0]["elmt_type"]["type"] == SIMPLETYPE): contenttypeinfos = simpleContent.copy() contenttypeinfos["base"] = basetypeinfos["elements"][0]["elmt_type"] else: raise ValueError("No compatible base type defined for simpleContent!") contenttypeinfos = CreateSimpleType(factory, attributes, contenttypeinfos) - + simpleContent["elements"] = [{"name": "content", "type": ELEMENT, "elmt_type": contenttypeinfos, "doc": annotations, "minOccurs": 1, "maxOccurs": 1}] @@ -605,7 +615,7 @@ def ReduceComplexType(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) - + if len(children) > 0: if children[0]["type"] in ["simpleContent", "complexContent"]: complexType = children[0].copy() @@ -641,7 +651,7 @@ complexType["elements"].append(content) elif group["type"] == "group": elmtgroup = factory.FindSchemaElement(child["ref"], ELEMENTSGROUP) - if elmtgroup.has_key("elements"): + if "elements" in elmtgroup: complexType["elements"] = elmtgroup["elements"] complexType["order"] = elmtgroup["order"] else: @@ -661,36 +671,36 @@ # Attribute elements def ReduceAnyAttribute(factory, attributes, elements): - return {"type" : "anyAttribute"} + return {"type": "anyAttribute"} def ReduceAttribute(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) - - if attributes.has_key("default"): - if attributes.has_key("fixed"): + + if "default" in attributes: + if "fixed" in attributes: raise ValueError("\"default\" and \"fixed\" can't be defined at the same time!") elif attributes.get("use", "optional") != "optional": raise ValueError("if \"default\" present, \"use\" can only have the value \"optional\"!") - + attribute = {"type": ATTRIBUTE, "attr_type": attributes.get("type", None), "doc": annotations} if len(children) > 0: if attribute["attr_type"] is None: attribute["attr_type"] = children[0] else: raise ValueError("Only one type can be defined for attribute!") - - if attributes.has_key("ref"): - if attributes.has_key("name"): + + if "ref" in attributes: + if "name" in attributes: raise ValueError("\"ref\" and \"name\" can't be defined at the same time!") - elif attributes.has_key("form"): + elif "form" in attributes: raise ValueError("\"ref\" and \"form\" can't be defined at the same time!") elif attribute["attr_type"] is not None: raise ValueError("if \"ref\" is present, no type can be defined!") elif attribute["attr_type"] is None: raise ValueError("No type has been defined for attribute \"%s\"!" % attributes["name"]) - - if attributes.has_key("type"): + + if "type" in attributes: tmp_attrs = attributes.copy() tmp_attrs.pop("type") attribute.update(tmp_attrs) @@ -701,7 +711,7 @@ def ReduceAttributeGroup(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) - if attributes.has_key("ref"): + if "ref" in attributes: return {"type": "attributeGroup", "ref": attributes["ref"], "doc": annotations} else: return {"type": ATTRIBUTESGROUP, "attributes": ExtractAttributes(factory, children), "doc": annotations} @@ -711,14 +721,15 @@ def ReduceAny(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) - + any = {"type": ANY, "doc": annotations} any.update(attributes) return any + def ReduceElement(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) - + types = [] constraints = [] for child in children: @@ -726,19 +737,19 @@ constraints.append(child) else: types.append(child) - - if attributes.has_key("default") and attributes.has_key("fixed"): + + if "default" in attributes and "fixed" in attributes: raise ValueError("\"default\" and \"fixed\" can't be defined at the same time!") - - if attributes.has_key("ref"): + + if "ref" in attributes: for attr in ["name", "default", "fixed", "form", "block", "type"]: - if attributes.has_key(attr): + if attr in attributes: raise ValueError("\"ref\" and \"%s\" can't be defined at the same time!" % attr) - if attributes.has_key("nillable"): + if "nillable" in attributes: raise ValueError("\"ref\" and \"nillable\" can't be defined at the same time!") if len(types) > 0: raise ValueError("No type and no constraints can be defined where \"ref\" is defined!") - + infos = factory.FindSchemaElement(attributes["ref"], ELEMENT) if infos is not None: element = infos.copy() @@ -748,8 +759,8 @@ return element else: raise ValueError("\"%s\" base type isn't defined or circular referenced!" % name) - - elif attributes.has_key("name"): + + elif "name" in attributes: element = {"type": ELEMENT, "elmt_type": attributes.get("type", None), "constraints": constraints, "doc": annotations} if len(types) > 0: if element["elmt_type"] is None: @@ -759,8 +770,8 @@ elif element["elmt_type"] is None: element["elmt_type"] = "tag" element["type"] = TAG - - if attributes.has_key("type"): + + if "type" in attributes: tmp_attrs = attributes.copy() tmp_attrs.pop("type") element.update(tmp_attrs) @@ -770,20 +781,21 @@ else: raise ValueError("\"Element\" must have at least a \"ref\" or a \"name\" defined!") + def ReduceAll(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) - + for child in children: if children["maxOccurs"] == "unbounded" or children["maxOccurs"] > 1: raise ValueError("\"all\" item can't have \"maxOccurs\" attribute greater than 1!") - + return {"type": "all", "elements": children, "minOccurs": attributes["minOccurs"], "maxOccurs": attributes["maxOccurs"], "order": False, "doc": annotations} def ReduceChoice(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) - + choices = [] for child in children: if child["type"] in [ELEMENT, ANY, TAG]: @@ -791,12 +803,12 @@ elif child["type"] == "sequence": child["minOccurs"] = child["maxOccurs"] = 1 choices.append(child) - #raise ValueError("\"sequence\" in \"choice\" is not supported. Create instead a new complex type!") + # raise ValueError("\"sequence\" in \"choice\" is not supported. Create instead a new complex type!") elif child["type"] == CHOICE: choices.extend(child["choices"]) elif child["type"] == "group": - elmtgroup = factory.FindSchemaElement(child["ref"], ELEMENTSGROUP) - if not elmtgroup.has_key("choices"): + elmtgroup = factory.FindSchemaElement(child["ref"], ELEMENTSGROUP) + if "choices" not in elmtgroup: raise ValueError("Only group composed of \"choice\" can be referenced in \"choice\" element!") choices_tmp = [] for choice in elmtgroup["choices"]: @@ -810,18 +822,18 @@ else: choices_tmp.append(choice) choices.extend(choices_tmp) - + for choice in choices: attributes["minOccurs"] = min(attributes["minOccurs"], choice["minOccurs"]) choice["minOccurs"] = 1 - + return {"type": CHOICE, "choices": choices, "minOccurs": attributes["minOccurs"], "maxOccurs": attributes["maxOccurs"], "doc": annotations} def ReduceSequence(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) - + sequence = [] for child in children: if child["type"] in [ELEMENT, ANY, TAG, CHOICE]: @@ -830,12 +842,12 @@ sequence.extend(child["elements"]) elif child["type"] == "group": elmtgroup = factory.FindSchemaElement(child["ref"], ELEMENTSGROUP) - if not elmtgroup.has_key("elements") or not elmtgroup["order"]: + if "elements" not in elmtgroup or not elmtgroup["order"]: raise ValueError("Only group composed of \"sequence\" can be referenced in \"sequence\" element!") elements_tmp = [] for element in elmtgroup["elements"]: if not isinstance(element["elmt_type"], (UnicodeType, StringType)) and element["elmt_type"]["type"] == COMPLEXTYPE: - elmt_type = "%s_%s"%(elmtgroup["name"], element["name"]) + elmt_type = "%s_%s" % (elmtgroup["name"], element["name"]) if factory.TargetNamespace is not None: elmt_type = "%s:%s" % (factory.TargetNamespace, elmt_type) new_element = element.copy() @@ -844,15 +856,15 @@ else: elements_tmp.append(element) sequence.extend(elements_tmp) - + return {"type": "sequence", "elements": sequence, "minOccurs": attributes["minOccurs"], "maxOccurs": attributes["maxOccurs"], "order": True, "doc": annotations} - - + + def ReduceGroup(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) - - if attributes.has_key("ref"): + + if "ref" in attributes: return {"type": "group", "ref": attributes["ref"], "doc": annotations} else: element = children[0] @@ -866,41 +878,46 @@ # Constraint elements + def ReduceUnique(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) - + unique = {"type": CONSTRAINT, "const_type": "unique", "selector": children[0], "fields": children[1:]} unique.update(attributes) return unique - + + def ReduceKey(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) - + key = {"type": CONSTRAINT, "const_type": "key", "selector": children[0], "fields": children[1:]} key.update(attributes) return key + def ReduceKeyRef(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) - + keyref = {"type": CONSTRAINT, "const_type": "keyref", "selector": children[0], "fields": children[1:]} keyref.update(attributes) return keyref - + + def ReduceSelector(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) - + selector = {"type": CONSTRAINT, "const_type": "selector"} selector.update(attributes) return selector + def ReduceField(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) - + field = {"type": CONSTRAINT, "const_type": "field"} field.update(attributes) return field - + # Inclusion elements @@ -908,9 +925,10 @@ annotations, children = factory.ReduceElements(elements) raise ValueError("\"import\" element isn't supported yet!") + def ReduceInclude(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) - + if factory.FileName is None: raise ValueError("Include in XSD string not yet supported") filepath = attributes["schemaLocation"] @@ -922,7 +940,7 @@ include_factory = XSDClassFactory(minidom.parse(xsdfile), filepath) xsdfile.close() include_factory.CreateClasses() - + if factory.TargetNamespace == include_factory.TargetNamespace: factory.Namespaces[factory.TargetNamespace].update(include_factory.Namespaces[include_factory.TargetNamespace]) else: @@ -931,7 +949,8 @@ factory.ComputedClassesLookUp.update(include_factory.ComputedClassesLookUp) factory.EquivalentClassesParent.update(include_factory.EquivalentClassesParent) return None - + + def ReduceRedefine(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) raise ValueError("\"redefine\" element isn't supported yet!") @@ -944,23 +963,24 @@ factory.ElementFormDefault = attributes["elementFormDefault"] factory.BlockDefault = attributes["blockDefault"] factory.FinalDefault = attributes["finalDefault"] - + targetNamespace = attributes.get("targetNamespace", None) factory.TargetNamespace = factory.DefinedNamespaces.get(targetNamespace, None) if factory.TargetNamespace is not None: factory.etreeNamespaceFormat = "{%s}%%s" % targetNamespace factory.Namespaces[factory.TargetNamespace] = {} - + annotations, children = factory.ReduceElements(elements, True) - + for child in children: - if child.has_key("name"): + if "name" in child: infos = factory.GetQualifiedNameInfos(child["name"], factory.TargetNamespace, True) if infos is None: factory.Namespaces[factory.TargetNamespace][child["name"]] = child elif not CompareSchema(infos, child): raise ValueError("\"%s\" is defined twice in targetNamespace!" % child["name"]) + def CompareSchema(schema, reference): if isinstance(schema, ListType): if not isinstance(reference, ListType) or len(schema) != len(reference): @@ -975,7 +995,7 @@ return False for name, value in schema.items(): ref_value = reference.get(name, None) - if ref_value is None and value != None: + if ref_value is None and value is not None: return False result = CompareSchema(value, ref_value) if not result: @@ -987,10 +1007,11 @@ else: return True return schema == reference - -#------------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------------- # Base class for XSD schema extraction -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- class XSDClassFactory(ClassFactory): @@ -999,7 +1020,7 @@ ClassFactory.__init__(self, document, filepath, debug) self.Namespaces["xml"] = { "lang": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateModelNameExtraction("lang", LANGUAGE_model) } @@ -1007,31 +1028,31 @@ } self.Namespaces["xsi"] = { "noNamespaceSchemaLocation": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": NotSupportedYet("noNamespaceSchemaLocation") } }, "nil": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": NotSupportedYet("nil") } }, "schemaLocation": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": NotSupportedYet("schemaLocation") } }, "type": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": NotSupportedYet("type") } } } - + def ParseSchema(self): for child in self.Document.childNodes: if child.nodeType == self.Document.ELEMENT_NODE: @@ -1067,7 +1088,7 @@ if element_type is not None and element["type"] != element_type: raise ValueError("\"%s\" isn't of the expected type!" % element_name) return element - + def CreateSchemaElement(self, element_name, element_type): for type, attributes, elements in self.Schema[2]: namespace, name = DecomposeQualifiedName(type) @@ -1090,11 +1111,12 @@ return element_infos return None -""" -This function opens the xsd file and generate a xml parser with class lookup from -the xml tree -""" + def GenerateParserFromXSD(filepath): + """ + This function opens the xsd file and generate a xml parser with class lookup from + the xml tree + """ xsdfile = open(filepath, 'r') xsdstring = xsdfile.read() xsdfile.close() @@ -1104,24 +1126,27 @@ os.chdir(cwd) return parser -""" -This function generate a xml from the xsd given as a string -""" + def GenerateParserFromXSDstring(xsdstring): + """ + This function generate a xml from the xsd given as a string + """ return GenerateParser(XSDClassFactory(minidom.parseString(xsdstring)), xsdstring) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # XSD schema syntax elements -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + XSD_NAMESPACE = { -#------------------------------------------------------------------------------- -# Syntax elements definition -#------------------------------------------------------------------------------- - - "all": {"struct": """ + # ------------------------------------------------------------------------------- + # Syntax elements definition + # ------------------------------------------------------------------------------- + + "all": { + "struct": """ Content: (annotation?, element*) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("all", ["id", "maxOccurs", "minOccurs"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "all", ["id", "maxOccurs", "minOccurs"], re.compile("((?:annotation )?(?:element )*)")) }, "reduce": ReduceAll }, - "annotation": {"struct": """ + "annotation": { + "struct": """ Content: (appinfo | documentation)* """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("annotation", ["id"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "annotation", ["id"], re.compile("((?:app_info |documentation )*)")) }, "reduce": ReduceAnnotation }, - "any": {"struct": """ + "any": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("any", - ["id", "maxOccurs", "minOccurs", "namespace", "processContents"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "any", + ["id", "maxOccurs", "minOccurs", "namespace", "processContents"], re.compile("((?:annotation )?(?:simpleType )*)")) }, "reduce": ReduceAny }, - "anyAttribute": {"struct": """ + "anyAttribute": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("anyAttribute", - ["id", "namespace", "processContents"], ONLY_ANNOTATION) + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "anyAttribute", ["id", "namespace", "processContents"], ONLY_ANNOTATION) }, "reduce": ReduceAnyAttribute }, - "appinfo": {"struct": """ + "appinfo": { + "struct": """ Content: ({any})* """, - "type": SYNTAXELEMENT, + "type": SYNTAXELEMENT, "extract": { "default": GenerateElement("appinfo", ["source"], re.compile("(.*)"), True) }, "reduce": ReduceAppInfo }, - "attribute": {"struct": """ + "attribute": { + "struct": """ Content: (annotation?, simpleType?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("attribute", - ["default", "fixed", "form", "id", "name", "ref", "type", "use"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "attribute", + ["default", "fixed", "form", "id", "name", "ref", "type", "use"], re.compile("((?:annotation )?(?:simpleType )?)")), - "schema": GenerateElement("attribute", - ["default", "fixed", "form", "id", "name", "type"], + "schema": GenerateElement( + "attribute", + ["default", "fixed", "form", "id", "name", "type"], re.compile("((?:annotation )?(?:simpleType )?)")) }, "reduce": ReduceAttribute }, - "attributeGroup": {"struct": """ + "attributeGroup": { + "struct": """ Content: (annotation?, ((attribute | attributeGroup)*, anyAttribute?)) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("attributeGroup", + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "attributeGroup", ["id", "ref"], ONLY_ANNOTATION), - "schema": GenerateElement("attributeGroup", - ["id", "name"], + "schema": GenerateElement( + "attributeGroup", + ["id", "name"], re.compile("((?:annotation )?(?:(?:attribute |attributeGroup )*(?:anyAttribute )?))")) }, "reduce": ReduceAttributeGroup }, - "choice": {"struct": """ + "choice": { + "struct": """ Content: (annotation?, (element | group | choice | sequence | any)*) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("choice", ["id", "maxOccurs", "minOccurs"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "choice", + ["id", "maxOccurs", "minOccurs"], re.compile("((?:annotation )?(?:element |group |choice |sequence |any )*)")) }, "reduce": ReduceChoice }, - "complexContent": {"struct": """ + "complexContent": { + "struct": """ Content: (annotation?, (restriction | extension)) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("complexContent", ["id", "mixed"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "complexContent", + ["id", "mixed"], re.compile("((?:annotation )?(?:restriction |extension ))")) }, "reduce": ReduceComplexContent }, - "complexType": {"struct": """ + "complexType": { + "struct": """ Content: (annotation?, (simpleContent | complexContent | ((group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?)))) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("complexType", - ["abstract", "block", "final", "id", "mixed", "name"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "complexType", + ["abstract", "block", "final", "id", "mixed", "name"], re.compile("((?:annotation )?(?:simpleContent |complexContent |(?:(?:group |all |choice |sequence )?(?:(?:attribute |attributeGroup )*(?:anyAttribute )?))))")) }, "reduce": ReduceComplexType }, - "documentation": {"struct" : """ + "documentation": { + "struct": """ Content: ({any})* """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("documentation", - ["source", "lang"], re.compile("(.*)"), True) + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "documentation", + ["source", "lang"], + re.compile("(.*)"), True) }, "reduce": ReduceDocumentation }, - "element": {"struct": """ + "element": { + "struct": """ Content: (annotation?, ((simpleType | complexType)?, (unique | key | keyref)*)) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("element", - ["abstract", "block", "default", "final", "fixed", "form", "id", "maxOccurs", "minOccurs", "name", "nillable", "ref", "substitutionGroup", "type"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "element", + ["abstract", "block", "default", "final", "fixed", "form", "id", "maxOccurs", "minOccurs", "name", "nillable", "ref", "substitutionGroup", "type"], re.compile("((?:annotation )?(?:simpleType |complexType )?(?:unique |key |keyref )*)")), - "schema": GenerateElement("element", - ["abstract", "block", "default", "final", "fixed", "form", "id", "name", "nillable", "substitutionGroup", "type"], + "schema": GenerateElement( + "element", + ["abstract", "block", "default", "final", "fixed", "form", "id", "name", "nillable", "substitutionGroup", "type"], re.compile("((?:annotation )?(?:simpleType |complexType )?(?:unique |key |keyref )*)")) }, "reduce": ReduceElement }, - "enumeration": {"struct": """ + "enumeration": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, + "type": SYNTAXELEMENT, "extract": { "default": GenerateElement("enumeration", ["id", "value"], ONLY_ANNOTATION) }, "reduce": GenerateFacetReducing("enumeration", False) }, - "extension": {"struct": """ + "extension": { + "struct": """ Content: (annotation?, ((group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?))) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("extension", ["base", "id"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "extension", + ["base", "id"], re.compile("((?:annotation )?(?:(?:attribute |attributeGroup )*(?:anyAttribute )?))")), - "complexContent": GenerateElement("extension", ["base", "id"], + "complexContent": GenerateElement( + "extension", + ["base", "id"], re.compile("((?:annotation )?(?:group |all |choice |sequence )?(?:(?:attribute |attributeGroup )*(?:anyAttribute )?))")) }, "reduce": ReduceExtension }, - "field": {"struct": """ + "field": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, + "type": SYNTAXELEMENT, "extract": { "default": GenerateElement("field", ["id", "xpath"], ONLY_ANNOTATION) }, "reduce": ReduceField }, - "fractionDigits": {"struct": """ + "fractionDigits": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("fractionDigits", - ["fixed", "id", "value"], ONLY_ANNOTATION) + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "fractionDigits", + ["fixed", "id", "value"], + ONLY_ANNOTATION) }, "reduce": GenerateFacetReducing("fractionDigits", True) }, - "group": {"struct": """ + "group": { + "struct": """ Content: (annotation?, (all | choice | sequence)?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("group", - ["id", "maxOccurs", "minOccurs", "ref"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "group", + ["id", "maxOccurs", "minOccurs", "ref"], re.compile("((?:annotation )?(?:all |choice |sequence )?)")), - "schema": GenerateElement("group", - ["id", "name"], + "schema": GenerateElement( + "group", + ["id", "name"], re.compile("((?:annotation )?(?:all |choice |sequence )?)")) }, "reduce": ReduceGroup }, - "import": {"struct": """ + "import": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("import", - ["id", "namespace", "schemaLocation"], ONLY_ANNOTATION) + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "import", + ["id", "namespace", "schemaLocation"], + ONLY_ANNOTATION) }, "reduce": ReduceImport }, - "include": {"struct": """ + "include": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("include", - ["id", "schemaLocation"], ONLY_ANNOTATION) + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "include", + ["id", "schemaLocation"], + ONLY_ANNOTATION) }, "reduce": ReduceInclude }, - "key": {"struct": """ + "key": { + "struct": """ Content: (annotation?, (selector, field+)) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("key", ["id", "name"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "key", ["id", "name"], re.compile("((?:annotation )?(?:selector (?:field )+))")) }, "reduce": ReduceKey }, - "keyref": {"struct": """ + "keyref": { + "struct": """ Content: (annotation?, (selector, field+)) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("keyref", ["id", "name", "refer"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "keyref", ["id", "name", "refer"], re.compile("((?:annotation )?(?:selector (?:field )+))")) }, "reduce": ReduceKeyRef }, - "length": {"struct" : """ + "length": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("length", - ["fixed", "id", "value"], ONLY_ANNOTATION) + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "length", ["fixed", "id", "value"], ONLY_ANNOTATION) }, "reduce": GenerateFacetReducing("length", True) }, - "list": {"struct": """ + "list": { + "struct": """ Content: (annotation?, simpleType?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("list", ["id", "itemType"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "list", ["id", "itemType"], re.compile("((?:annotation )?(?:simpleType )?)$")) }, "reduce": ReduceList }, - "maxExclusive": {"struct": """ + "maxExclusive": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("maxExclusive", - ["fixed", "id", "value"], ONLY_ANNOTATION) + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "maxExclusive", ["fixed", "id", "value"], ONLY_ANNOTATION) }, "reduce": GenerateFacetReducing("maxExclusive", True) }, - "maxInclusive": {"struct": """ + "maxInclusive": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("maxInclusive", - ["fixed", "id", "value"], ONLY_ANNOTATION) + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "maxInclusive", ["fixed", "id", "value"], ONLY_ANNOTATION) }, "reduce": GenerateFacetReducing("maxInclusive", True) }, - "maxLength": {"struct": """ + "maxLength": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("maxLength", - ["fixed", "id", "value"], ONLY_ANNOTATION) + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "maxLength", ["fixed", "id", "value"], ONLY_ANNOTATION) }, "reduce": GenerateFacetReducing("maxLength", True) }, - "minExclusive": {"struct": """ + "minExclusive": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("minExclusive", - ["fixed", "id", "value"], ONLY_ANNOTATION) + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "minExclusive", ["fixed", "id", "value"], ONLY_ANNOTATION) }, "reduce": GenerateFacetReducing("minExclusive", True) }, - "minInclusive": {"struct": """ + "minInclusive": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("minInclusive", - ["fixed", "id", "value"], ONLY_ANNOTATION) + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "minInclusive", ["fixed", "id", "value"], ONLY_ANNOTATION) }, "reduce": GenerateFacetReducing("minInclusive", True) }, - "minLength": {"struct": """ + "minLength": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("minLength", - ["fixed", "id", "value"], ONLY_ANNOTATION) + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "minLength", ["fixed", "id", "value"], ONLY_ANNOTATION) }, "reduce": GenerateFacetReducing("minLength", True) }, - "pattern": {"struct": """ + "pattern": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, + "type": SYNTAXELEMENT, "extract": { "default": GenerateElement("pattern", ["id", "value"], ONLY_ANNOTATION) }, "reduce": GenerateFacetReducing("pattern", False) }, - "redefine": {"struct": """ + "redefine": { + "struct": """ Content: (annotation | (simpleType | complexType | group | attributeGroup))* """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("refine", ["id", "schemaLocation"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "refine", ["id", "schemaLocation"], re.compile("((?:annotation |(?:simpleType |complexType |group |attributeGroup ))*)")) }, "reduce": ReduceRedefine }, - "restriction": {"struct": """ + "restriction": { + "struct": """ Content: (annotation?, (group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?)) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("restriction", ["base", "id"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "restriction", + ["base", "id"], re.compile("((?:annotation )?(?:(?:simpleType )?(?:(?:minExclusive |minInclusive |maxExclusive |maxInclusive |totalDigits |fractionDigits |length |minLength |maxLength |enumeration |whiteSpace |pattern )*)))")), - "simpleContent": GenerateElement("restriction", ["base", "id"], + "simpleContent": GenerateElement( + "restriction", + ["base", "id"], re.compile("((?:annotation )?(?:(?:simpleType )?(?:(?:minExclusive |minInclusive |maxExclusive |maxInclusive |totalDigits |fractionDigits |length |minLength |maxLength |enumeration |whiteSpace |pattern )*)?(?:(?:attribute |attributeGroup )*(?:anyAttribute )?)))")), - "complexContent": GenerateElement("restriction", ["base", "id"], + "complexContent": GenerateElement( + "restriction", + ["base", "id"], re.compile("((?:annotation )?(?:(?:simpleType )?(?:group |all |choice |sequence )?(?:(?:attribute |attributeGroup )*(?:anyAttribute )?)))")), }, "reduce": ReduceRestriction }, - "schema": {"struct": """ + "schema": { + "struct": """ Content: ((include | import | redefine | annotation)*, (((simpleType | complexType | group | attributeGroup) | element | attribute | notation), annotation*)*) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("schema", - ["attributeFormDefault", "blockDefault", "elementFormDefault", "finalDefault", "id", "targetNamespace", "version", "lang"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "schema", + ["attributeFormDefault", + "blockDefault", + "elementFormDefault", + "finalDefault", + "id", + "targetNamespace", + "version", + "lang"], re.compile("((?:include |import |redefine |annotation )*(?:(?:(?:simpleType |complexType |group |attributeGroup )|element |attribute |annotation )(?:annotation )*)*)")) } }, - "selector": {"struct": """ + "selector": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, + "type": SYNTAXELEMENT, "extract": { "default": GenerateElement("selector", ["id", "xpath"], ONLY_ANNOTATION) }, "reduce": ReduceSelector }, - "sequence": {"struct": """ + "sequence": { + "struct": """ Content: (annotation?, (element | group | choice | sequence | any)*) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("sequence", ["id", "maxOccurs", "minOccurs"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "sequence", ["id", "maxOccurs", "minOccurs"], re.compile("((?:annotation )?(?:element |group |choice |sequence |any )*)")) }, "reduce": ReduceSequence }, - "simpleContent": {"struct" : """ + "simpleContent": { + "struct": """ Content: (annotation?, (restriction | extension)) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("simpleContent", ["id"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "simpleContent", ["id"], re.compile("((?:annotation )?(?:restriction |extension ))")) }, "reduce": ReduceSimpleContent }, - "simpleType": {"struct" : """ + "simpleType": { + "struct": """ Content: (annotation?, (restriction | list | union)) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("simpleType", ["final", "id", "name"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "simpleType", ["final", "id", "name"], re.compile("((?:annotation )?(?:restriction |list |union ))")) }, "reduce": ReduceSimpleType }, - "totalDigits": {"struct" : """ + "totalDigits": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("totalDigits", - ["fixed", "id", "value"], ONLY_ANNOTATION), + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "totalDigits", ["fixed", "id", "value"], ONLY_ANNOTATION), }, "reduce": GenerateFacetReducing("totalDigits", True) }, - "union": {"struct": """ + "union": { + "struct": """ Content: (annotation?, simpleType*) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("union", ["id", "memberTypes"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "union", ["id", "memberTypes"], re.compile("((?:annotation )?(?:simpleType )*)")) }, "reduce": ReduceUnion }, - "unique": {"struct": """ + "unique": { + "struct": """ Content: (annotation?, (selector, field+)) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("unique", ["id", "name"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "unique", ["id", "name"], re.compile("((?:annotation )?(?:selector |(?:field )+))")) }, "reduce": ReduceUnique }, - - "whiteSpace": {"struct" : """ + + "whiteSpace": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("whiteSpace", - ["fixed", "id", "value"], ONLY_ANNOTATION) + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "whiteSpace", ["fixed", "id", "value"], ONLY_ANNOTATION) }, "reduce": GenerateFacetReducing("whiteSpace", True) }, -#------------------------------------------------------------------------------- -# Syntax attributes definition -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Syntax attributes definition + # ------------------------------------------------------------------------------- "abstract": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GetBoolean }, @@ -1818,31 +1934,32 @@ }, "attributeFormDefault": { - "type": SYNTAXATTRIBUTE, - "extract": { - "default": GenerateEnumeratedExtraction("member attributeFormDefault", ["qualified", "unqualified"]) + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GenerateEnumeratedExtraction( + "member attributeFormDefault", ["qualified", "unqualified"]) }, "default": { "default": "unqualified" } }, - + "base": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateModelNameExtraction("member base", QName_model) } }, - + "block": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateGetList("block", ["restriction", "extension", "substitution"]) } }, - + "blockDefault": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateGetList("block", ["restriction", "extension", "substitution"]) }, @@ -1850,16 +1967,16 @@ "default": "" } }, - + "default": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GetAttributeValue } }, "elementFormDefault": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateEnumeratedExtraction("member elementFormDefault", ["qualified", "unqualified"]) }, @@ -1867,9 +1984,9 @@ "default": "unqualified" } }, - + "final": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateGetList("final", ["restriction", "extension", "substitution"]), "simpleType": GenerateGetList("final", ["list", "union", "restriction"]) @@ -1877,7 +1994,7 @@ }, "finalDefault": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateGetList("finalDefault", ["restriction", "extension", "list", "union"]) }, @@ -1885,9 +2002,9 @@ "default": "" } }, - + "fixed": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GetBoolean, "attribute": GetAttributeValue, @@ -1901,35 +2018,35 @@ }, "form": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateEnumeratedExtraction("member form", ["qualified", "unqualified"]) } }, "id": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateModelNameExtraction("member id", NCName_model) } }, - + "itemType": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateModelNameExtraction("member itemType", QName_model) } }, "memberTypes": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateModelNameListExtraction("member memberTypes", QNames_model) }, }, - + "maxOccurs": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateLimitExtraction(), "all": GenerateLimitExtraction(1, 1, False) @@ -1940,9 +2057,9 @@ }, "minOccurs": { - "type": SYNTAXATTRIBUTE, - "extract": { - "default": GenerateLimitExtraction(unbounded = False), + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GenerateLimitExtraction(unbounded=False), "all": GenerateLimitExtraction(0, 1, False) }, "default": { @@ -1951,7 +2068,7 @@ }, "mixed": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GetBoolean }, @@ -1960,16 +2077,16 @@ "complexType": False } }, - + "name": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateModelNameExtraction("member name", NCName_model) } }, - + "namespace": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateModelNameExtraction("member namespace", URI_model), "any": GetNamespaces @@ -1981,14 +2098,14 @@ }, "nillable": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GetBoolean }, }, - + "processContents": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateEnumeratedExtraction("member processContents", ["lax", "skip", "strict"]) }, @@ -1996,9 +2113,9 @@ "default": "strict" } }, - + "ref": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateModelNameExtraction("member ref", QName_model) } @@ -2010,44 +2127,44 @@ "default": GenerateModelNameExtraction("member refer", QName_model) } }, - + "schemaLocation": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateModelNameExtraction("member schemaLocation", URI_model) } }, - + "source": { "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateModelNameExtraction("member source", URI_model) } }, - + "substitutionGroup": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateModelNameExtraction("member substitutionGroup", QName_model) } }, "targetNamespace": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateModelNameExtraction("member targetNamespace", URI_model) } }, - + "type": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateModelNameExtraction("member type", QName_model) } }, "use": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateEnumeratedExtraction("member usage", ["required", "optional", "prohibited"]) }, @@ -2057,7 +2174,7 @@ }, "value": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GetAttributeValue, "fractionDigits": GenerateIntegerExtraction(minInclusive=0), @@ -2077,23 +2194,23 @@ }, "xpath": { - "type": SYNTAXATTRIBUTE, - "extract": { -# "default": NotSupportedYet("xpath") + "type": SYNTAXATTRIBUTE, + "extract": { + # "default": NotSupportedYet("xpath") "default": GetAttributeValue } }, - -#------------------------------------------------------------------------------- -# Simple types definition -#------------------------------------------------------------------------------- + + # ------------------------------------------------------------------------------- + # Simple types definition + # ------------------------------------------------------------------------------- "string": { "type": SIMPLETYPE, "basename": "string", "extract": GetAttributeValue, "facets": STRING_FACETS, - "generate": GenerateSimpleTypeXMLText(lambda x : x), + "generate": GenerateSimpleTypeXMLText(lambda x: x), "initial": lambda: "", "check": lambda x: isinstance(x, (StringType, UnicodeType)) }, @@ -2103,61 +2220,61 @@ "basename": "normalizedString", "extract": GetNormalizedString, "facets": STRING_FACETS, - "generate": GenerateSimpleTypeXMLText(lambda x : x), + "generate": GenerateSimpleTypeXMLText(lambda x: x), "initial": lambda: "", "check": lambda x: isinstance(x, (StringType, UnicodeType)) }, "token": { "type": SIMPLETYPE, - "basename": "token", + "basename": "token", "extract": GetToken, "facets": STRING_FACETS, - "generate": GenerateSimpleTypeXMLText(lambda x : x), + "generate": GenerateSimpleTypeXMLText(lambda x: x), "initial": lambda: "", "check": lambda x: isinstance(x, (StringType, UnicodeType)) }, - + "base64Binary": { - "type": SIMPLETYPE, - "basename": "base64Binary", + "type": SIMPLETYPE, + "basename": "base64Binary", "extract": NotSupportedYet("base64Binary"), "facets": STRING_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: 0, "check": lambda x: isinstance(x, (IntType, LongType)) }, - + "hexBinary": { "type": SIMPLETYPE, - "basename": "hexBinary", + "basename": "hexBinary", "extract": GetHexInteger, "facets": STRING_FACETS, - "generate": GenerateSimpleTypeXMLText(lambda x: ("%."+str(int(round(len("%X"%x)/2.)*2))+"X")%x), + "generate": GenerateSimpleTypeXMLText(lambda x: ("%."+str(int(round(len("%X" % x)/2.)*2))+"X") % x), "initial": lambda: 0, "check": lambda x: isinstance(x, (IntType, LongType)) }, "integer": { "type": SIMPLETYPE, - "basename": "integer", + "basename": "integer", "extract": GenerateIntegerExtraction(), "facets": DECIMAL_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: 0, "check": lambda x: isinstance(x, IntType) }, - + "positiveInteger": { "type": SIMPLETYPE, - "basename": "positiveInteger", + "basename": "positiveInteger", "extract": GenerateIntegerExtraction(minExclusive=0), "facets": DECIMAL_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: 1, "check": lambda x: isinstance(x, IntType) }, - + "negativeInteger": { "type": SIMPLETYPE, "basename": "negativeInteger", @@ -2167,41 +2284,41 @@ "initial": lambda: -1, "check": lambda x: isinstance(x, IntType) }, - + "nonNegativeInteger": { - "type": SIMPLETYPE, - "basename": "nonNegativeInteger", + "type": SIMPLETYPE, + "basename": "nonNegativeInteger", "extract": GenerateIntegerExtraction(minInclusive=0), "facets": DECIMAL_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: 0, "check": lambda x: isinstance(x, IntType) }, - + "nonPositiveInteger": { "type": SIMPLETYPE, - "basename": "nonPositiveInteger", + "basename": "nonPositiveInteger", "extract": GenerateIntegerExtraction(maxInclusive=0), "facets": DECIMAL_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: 0, "check": lambda x: isinstance(x, IntType) }, - + "long": { "type": SIMPLETYPE, "basename": "long", - "extract": GenerateIntegerExtraction(minInclusive=-2**63,maxExclusive=2**63), + "extract": GenerateIntegerExtraction(minInclusive=-2**63, maxExclusive=2**63), "facets": DECIMAL_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: 0, "check": lambda x: isinstance(x, IntType) }, - + "unsignedLong": { "type": SIMPLETYPE, "basename": "unsignedLong", - "extract": GenerateIntegerExtraction(minInclusive=0,maxExclusive=2**64), + "extract": GenerateIntegerExtraction(minInclusive=0, maxExclusive=2**64), "facets": DECIMAL_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: 0, @@ -2211,7 +2328,7 @@ "int": { "type": SIMPLETYPE, "basename": "int", - "extract": GenerateIntegerExtraction(minInclusive=-2**31,maxExclusive=2**31), + "extract": GenerateIntegerExtraction(minInclusive=-2**31, maxExclusive=2**31), "facets": DECIMAL_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: 0, @@ -2221,7 +2338,7 @@ "unsignedInt": { "type": SIMPLETYPE, "basename": "unsignedInt", - "extract": GenerateIntegerExtraction(minInclusive=0,maxExclusive=2**32), + "extract": GenerateIntegerExtraction(minInclusive=0, maxExclusive=2**32), "facets": DECIMAL_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: 0, @@ -2231,7 +2348,7 @@ "short": { "type": SIMPLETYPE, "basename": "short", - "extract": GenerateIntegerExtraction(minInclusive=-2**15,maxExclusive=2**15), + "extract": GenerateIntegerExtraction(minInclusive=-2**15, maxExclusive=2**15), "facets": DECIMAL_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: 0, @@ -2240,8 +2357,8 @@ "unsignedShort": { "type": SIMPLETYPE, - "basename": "unsignedShort", - "extract": GenerateIntegerExtraction(minInclusive=0,maxExclusive=2**16), + "basename": "unsignedShort", + "extract": GenerateIntegerExtraction(minInclusive=0, maxExclusive=2**16), "facets": DECIMAL_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: 0, @@ -2251,7 +2368,7 @@ "byte": { "type": SIMPLETYPE, "basename": "byte", - "extract": GenerateIntegerExtraction(minInclusive=-2**7,maxExclusive=2**7), + "extract": GenerateIntegerExtraction(minInclusive=-2**7, maxExclusive=2**7), "facets": DECIMAL_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: 0, @@ -2261,7 +2378,7 @@ "unsignedByte": { "type": SIMPLETYPE, "basename": "unsignedByte", - "extract": GenerateIntegerExtraction(minInclusive=0,maxExclusive=2**8), + "extract": GenerateIntegerExtraction(minInclusive=0, maxExclusive=2**8), "facets": DECIMAL_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: 0, @@ -2285,7 +2402,7 @@ "facets": NUMBER_FACETS, "generate": GenerateFloatXMLText(["INF", "-INF", "NaN"]), "initial": lambda: 0., - "check": lambda x: {"INF" : True, "-INF" : True, "NaN" : True}.get(x, isinstance(x, (IntType, FloatType))) + "check": lambda x: {"INF": True, "-INF": True, "NaN": True}.get(x, isinstance(x, (IntType, FloatType))) }, "double": { @@ -2295,7 +2412,7 @@ "facets": NUMBER_FACETS, "generate": GenerateFloatXMLText(["INF", "-INF", "NaN"]), "initial": lambda: 0., - "check": lambda x: {"INF" : True, "-INF" : True, "NaN" : True}.get(x, isinstance(x, (IntType, FloatType))) + "check": lambda x: {"INF": True, "-INF": True, "NaN": True}.get(x, isinstance(x, (IntType, FloatType))) }, "boolean": { @@ -2303,10 +2420,10 @@ "basename": "boolean", "extract": GetBoolean, "facets": GenerateDictFacets(["pattern", "whiteSpace"]), - "generate": GenerateSimpleTypeXMLText(lambda x:{True : "true", False : "false"}[x]), + "generate": GenerateSimpleTypeXMLText(lambda x: {True: "true", False: "false"}[x]), "initial": lambda: False, "check": lambda x: isinstance(x, BooleanType) - }, + }, "duration": { "type": SIMPLETYPE, @@ -2324,7 +2441,7 @@ "extract": GetDateTime, "facets": NUMBER_FACETS, "generate": GenerateSimpleTypeXMLText(datetime.datetime.isoformat), - "initial": lambda: datetime.datetime(1,1,1,0,0,0,0), + "initial": lambda: datetime.datetime(1, 1, 1, 0, 0, 0, 0), "check": lambda x: isinstance(x, datetime.datetime) }, @@ -2334,17 +2451,17 @@ "extract": GetDate, "facets": NUMBER_FACETS, "generate": GenerateSimpleTypeXMLText(datetime.date.isoformat), - "initial": lambda: datetime.date(1,1,1), + "initial": lambda: datetime.date(1, 1, 1), "check": lambda x: isinstance(x, datetime.date) }, - + "time": { "type": SIMPLETYPE, "basename": "time", "extract": GetTime, "facets": NUMBER_FACETS, "generate": GenerateSimpleTypeXMLText(datetime.time.isoformat), - "initial": lambda: datetime.time(0,0,0,0), + "initial": lambda: datetime.time(0, 0, 0, 0), "check": lambda x: isinstance(x, datetime.time) }, @@ -2407,7 +2524,7 @@ "initial": lambda: "", "check": lambda x: isinstance(x, (StringType, UnicodeType)) }, - + "QName": { "type": SIMPLETYPE, "basename": "QName", @@ -2529,6 +2646,5 @@ }, # Complex Types - "anyType": {"type": COMPLEXTYPE, "extract": lambda x:None}, + "anyType": {"type": COMPLEXTYPE, "extract": lambda x: None}, } -