# HG changeset patch # User Edouard Tisserant # Date 1701985292 -3600 # Node ID 0b3ac94f494c6ab2426f79628a3a86f6710ca9b8 # Parent 89549813a6c19c8e72706fbd303f980620faa9fa# Parent 7e17f7e02a2bdf21efd0de93f868e52accc2c146 Merge diff -r 7e17f7e02a2b -r 0b3ac94f494c .github/workflows/run_tests_in_docker.yml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.github/workflows/run_tests_in_docker.yml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,76 @@ +name: CI Automated testing + +on: + push: + branches: [ python3 ] + +jobs: + + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + path: beremiz + + - uses: actions/checkout@v3 + with: + repository: beremiz/matiec + ref: e5be6a1f036d21cd7b5ee75ef352783a7cfcc1a7 + path: matiec + + - uses: actions/checkout@v3 + with: + repository: open62541/open62541 + ref: v1.3.6 + path: open62541 + submodules: recursive + + - name: Restore cached docker image + id: cache-docker-restore + uses: actions/cache/restore@v3 + env: + cache-name: cache-docker + with: + path: /tmp/latest.tar + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('beremiz/tests/tools/Docker', 'beremiz/requirements.txt') }} + + - if: ${{ steps.cache-docker-restore.outputs.cache-hit == false }} + name: Create docker image + run: | + cd beremiz/tests/tools/Docker + ./build_docker_image.sh + docker image save --output="/tmp/latest.tar" beremiz_sikuli + + - if: ${{ steps.cache-docker-restore.outputs.cache-hit == false }} + name: Save docker image in cache + id: cache-docker-save + uses: actions/cache/save@v3 + with: + path: /tmp/latest.tar + key: ${{ steps.cache-docker-restore.outputs.cache-primary-key }} + + - if: ${{ steps.cache-docker-restore.outputs.cache-hit != false }} + name: Re-use docker image + run: | + docker image load --input="/tmp/latest.tar" + + - name: Create docker container + run: | + cd beremiz/tests/tools/Docker + ./create_docker_container.sh ${{ github.workspace }}/test + + - name: Run tests in docker + run: | + cd beremiz/tests/tools/Docker + ./do_test_in_docker.sh + + - name: Upload test resuts artifact + uses: actions/upload-artifact@v3 + if: failure() + with: + name: test_results + path: ${{ github.workspace }}/test + retention-days: 5 diff -r 7e17f7e02a2b -r 0b3ac94f494c .gitignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.gitignore Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,9 @@ +*.pyc +**/__pycache__ +.vscode +**/build/** +*.class +**/.svghmithumbs/** +**/my_*.der +**/my_*.pem +tests/tools/Docker/requirements.txt diff -r 7e17f7e02a2b -r 0b3ac94f494c .hgignore --- a/.hgignore Wed Nov 29 11:54:56 2023 +0100 +++ b/.hgignore Thu Dec 07 22:41:32 2023 +0100 @@ -24,3 +24,5 @@ doc/_build doc/locale + +^.*\$py.class$ diff -r 7e17f7e02a2b -r 0b3ac94f494c Beremiz.py --- a/Beremiz.py Wed Nov 29 11:54:56 2023 +0100 +++ b/Beremiz.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,12 +23,9 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import print_function import os import sys import getopt -from past.builtins import execfile import wx from wx.lib.agw.advancedsplash import AdvancedSplash, AS_NOTIMEOUT, AS_CENTER_ON_SCREEN @@ -50,6 +47,7 @@ self.modules = ["BeremizIDE"] self.debug = os.path.exists("BEREMIZ_DEBUG") self.handle_exception = None + self.logf = None def Bpath(self, *args): return os.path.join(self.app_dir, *args) @@ -62,12 +60,13 @@ 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("-l --log path write content of console tab to given file") print("") print("") def SetCmdOptions(self): - self.shortCmdOpts = "hu:e:" - self.longCmdOpts = ["help", "updatecheck=", "extend="] + self.shortCmdOpts = "hu:e:l:" + self.longCmdOpts = ["help", "updatecheck=", "extend=", "log="] def ProcessOption(self, o, a): if o in ("-h", "--help"): @@ -77,6 +76,8 @@ self.updateinfo_url = a if o in ("-e", "--extend"): self.extensions.append(a) + if o in ("-l", "--log"): + self.logf = open(a, 'a') def ProcessCommandLineArgs(self): self.SetCmdOptions() @@ -103,7 +104,7 @@ def CreateApplication(self): - BeremizAppType = wx.App if wx.VERSION >= (3, 0, 0) else wx.PySimpleApp + BeremizAppType = wx.App class BeremizApp(BeremizAppType): def OnInit(_self): # pylint: disable=no-self-argument @@ -112,8 +113,6 @@ self.app = BeremizApp(redirect=self.debug) self.app.SetAppName('beremiz') - if wx.VERSION < (3, 0, 0): - wx.InitAllImageHandlers() def ShowSplashScreen(self): class Splash(AdvancedSplash): @@ -154,7 +153,7 @@ sys.path.append(extension_folder) AddCatalog(os.path.join(extension_folder, "locale")) AddBitmapFolder(os.path.join(extension_folder, "images")) - execfile(extfilename, self.globals()) + exec(compile(open(extfilename, "rb").read(), extfilename, 'exec'), self.globals()) def CheckUpdates(self): if self.updateinfo_url is not None: @@ -162,8 +161,8 @@ def updateinfoproc(): try: - import urllib2 - self.updateinfo = urllib2.urlopen(self.updateinfo_url, None).read() + import urllib.request, urllib.error, urllib.parse + self.updateinfo = urllib.request.urlopen(self.updateinfo_url, None).read() except Exception: self.updateinfo = _("update info unavailable.") @@ -182,10 +181,10 @@ def InstallExceptionHandler(self): import version import util.ExceptionHandler - self.handle_exception = util.ExceptionHandler.AddExceptHook(version.app_version) + self.handle_exception = util.ExceptionHandler.AddExceptHook(version.app_version, logf=self.logf) def CreateUI(self): - self.frame = self.BeremizIDE.Beremiz(None, self.projectOpen, self.buildpath) + self.frame = self.BeremizIDE.Beremiz(None, self.projectOpen, self.buildpath, logf=self.logf) def CloseSplash(self): if self.splash: diff -r 7e17f7e02a2b -r 0b3ac94f494c BeremizIDE.py --- a/BeremizIDE.py Wed Nov 29 11:54:56 2023 +0100 +++ b/BeremizIDE.py Thu Dec 07 22:41:32 2023 +0100 @@ -24,21 +24,19 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import print_function import os +import pickle import sys -import tempfile import shutil -import random import time +import signal from time import time as gettime -from threading import Lock, Timer, currentThread - -from six.moves import cPickle, xrange +from threading import Lock, Timer, current_thread + import wx.lib.buttons import wx.lib.statbmp import wx.stc +import wx.adv import version @@ -47,9 +45,8 @@ from editors.TextViewer import TextViewer from editors.ResourceEditor import ConfigurationEditor, ResourceEditor from editors.DataTypeEditor import DataTypeEditor -from util import paths as paths +from util.paths import Bpath from util.MiniTextControler import MiniTextControler -from util.ProcessLogger import ProcessLogger from util.BitmapLibrary import GetBitmap from controls.LogViewer import LogViewer from controls.CustomStyledTextCtrl import CustomStyledTextCtrl @@ -84,15 +81,11 @@ EncodeFileSystemPath, \ DecodeFileSystemPath - -beremiz_dir = paths.AbsDir(__file__) - - -def Bpath(*args): - return os.path.join(beremiz_dir, *args) +from LocalRuntimeMixin import LocalRuntimeMixin + def AppendMenu(parent, help, id, kind, text): - return parent.Append(help=help, id=id, kind=kind, text=text) + return parent.Append(wx.MenuItem(helpString=help, id=id, kind=kind, text=text)) MAX_RECENT_PROJECTS = 9 @@ -109,13 +102,13 @@ } -MainThread = currentThread().ident +MainThread = current_thread().ident REFRESH_PERIOD = 0.1 class LogPseudoFile(object): """ Base class for file like objects to facilitate StdOut for the Shell.""" - def __init__(self, output, risecall): + def __init__(self, output, risecall, logf): self.red_white = 1 self.red_yellow = 2 self.black_white = wx.stc.STC_STYLE_DEFAULT @@ -131,8 +124,12 @@ self.LastRefreshTime = gettime() self.LastRefreshTimer = None self.refreshPending = False + self.logf = logf def write(self, s, style=None): + if self.logf is not None: + self.logf.write(s) + self.logf.flush() self.StackLock.acquire() self.stack.append((s, style)) self.StackLock.release() @@ -156,7 +153,7 @@ self.LastRefreshTimer = None def _should_write(self): - if MainThread == currentThread().ident: + if MainThread == current_thread().ident: app = wx.GetApp() if app is not None: self._write() @@ -179,7 +176,7 @@ if style is None: style = self.black_white if style != self.black_white: - self.output.StartStyling(self.output.GetLength(), 0xff) + self.output.StartStyling(self.output.GetLength()) # Temporary deactivate read only mode on StyledTextCtrl for # adding text. It seems that text modifications, even @@ -233,10 +230,10 @@ self.YieldLock.release() -ID_FILEMENURECENTPROJECTS = wx.NewId() - - -class Beremiz(IDEFrame): +ID_FILEMENURECENTPROJECTS = wx.NewIdRef() + + +class Beremiz(IDEFrame, LocalRuntimeMixin): def _init_utils(self): self.ConfNodeMenu = wx.Menu(title='') @@ -247,18 +244,18 @@ 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=_('New') + '\tCTRL+N') AppendMenu(parent, help='', id=wx.ID_OPEN, - kind=wx.ITEM_NORMAL, text=_(u'Open') + '\tCTRL+O') - parent.AppendMenu(ID_FILEMENURECENTPROJECTS, _("&Recent Projects"), self.RecentProjectsMenu) + kind=wx.ITEM_NORMAL, text=_('Open') + '\tCTRL+O') + self.RecentProjectsMenuItem = parent.AppendSubMenu(self.RecentProjectsMenu, _("&Recent Projects")) parent.AppendSeparator() - parent.AppendMenu(wx.ID_ANY, _("&Tutorials and Examples"), self.TutorialsProjectsMenu) + parent.AppendSubMenu(self.TutorialsProjectsMenu, _("&Tutorials and Examples")) exemples_dir = Bpath("exemples") project_list = sorted(os.listdir(exemples_dir)) for idx, dirname in enumerate(project_list): - text = u'&%d: %s' % (idx + 1, dirname) + text = '&%d: %s' % (idx + 1, dirname) item = self.TutorialsProjectsMenu.Append(wx.ID_ANY, text, '') @@ -275,23 +272,23 @@ self.Bind(wx.EVT_MENU, OpenExemple, item) parent.AppendSeparator() AppendMenu(parent, help='', id=wx.ID_SAVE, - kind=wx.ITEM_NORMAL, text=_(u'Save') + '\tCTRL+S') + kind=wx.ITEM_NORMAL, text=_('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=_('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=_('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=_('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=_('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=_('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=_('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=_('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) @@ -304,20 +301,20 @@ self.Bind(wx.EVT_MENU, self.OnPrintMenu, id=wx.ID_PRINT) self.Bind(wx.EVT_MENU, self.OnQuitMenu, id=wx.ID_EXIT) - self.AddToMenuToolBar([(wx.ID_NEW, "new", _(u'New'), None), - (wx.ID_OPEN, "open", _(u'Open'), None), - (wx.ID_SAVE, "save", _(u'Save'), None), - (wx.ID_SAVEAS, "saveas", _(u'Save As...'), None), - (wx.ID_PRINT, "print", _(u'Print'), None)]) + self.AddToMenuToolBar([(wx.ID_NEW, "new", _('New'), None), + (wx.ID_OPEN, "open", _('Open'), None), + (wx.ID_SAVE, "save", _('Save'), None), + (wx.ID_SAVEAS, "saveas", _('Save As...'), None), + (wx.ID_PRINT, "print", _('Print'), None)]) def _RecursiveAddMenuItems(self, menu, items): for name, text, helpstr, children in items: if len(children) > 0: new_menu = wx.Menu(title='') - menu.AppendMenu(wx.ID_ANY, text, new_menu) + menu.AppendSubMenu(new_menu, text) self._RecursiveAddMenuItems(new_menu, children) else: - item = menu.Append(wx.ID_ANY, text, helpstr) + item = menu.Append(wx.MenuItem(text=text, helpString=helpstr, kind=wx.ITEM_NORMAL, id=wx.ID_ANY)) self.Bind(wx.EVT_MENU, self.GetAddConfNodeFunction(name), item) def _init_coll_AddMenu_Items(self, parent): @@ -328,22 +325,22 @@ def handler(event): return wx.MessageBox( version.GetCommunityHelpMsg(), - _(u'Community support'), + _('Community support'), wx.OK | wx.ICON_INFORMATION) - item = parent.Append(wx.ID_ANY, _(u'Community support'), '') + item = parent.Append(wx.ID_ANY, _('Community support'), '') self.Bind(wx.EVT_MENU, handler, item) - parent.Append(help='', id=wx.ID_ABOUT, - kind=wx.ITEM_NORMAL, text=_(u'About')) + parent.Append(wx.MenuItem(helpString='', id=wx.ID_ABOUT, + kind=wx.ITEM_NORMAL, text=_('About'))) self.Bind(wx.EVT_MENU, self.OnAboutMenu, id=wx.ID_ABOUT) def _init_coll_ConnectionStatusBar_Fields(self, parent): parent.SetFieldsCount(3) - parent.SetStatusText(number=0, text='') - parent.SetStatusText(number=1, text='') - parent.SetStatusText(number=2, text='') + parent.SetStatusText(i=0, text='') + parent.SetStatusText(i=1, text='') + parent.SetStatusText(i=2, text='') parent.SetStatusWidths([-1, 300, 200]) @@ -352,10 +349,12 @@ self.EditMenuSize = self.EditMenu.GetMenuItemCount() - inspectorID = wx.NewId() + inspectorID = wx.NewIdRef() self.Bind(wx.EVT_MENU, self.OnOpenWidgetInspector, id=inspectorID) accels = [wx.AcceleratorEntry(wx.ACCEL_CTRL | wx.ACCEL_ALT, ord('I'), inspectorID)] + self.methodLock = Lock() + for method, shortcut in [("Stop", wx.WXK_F4), ("Run", wx.WXK_F5), ("Transfer", wx.WXK_F6), @@ -365,10 +364,17 @@ def OnMethodGen(obj, meth): def OnMethod(evt): if obj.CTR is not None: - obj.CTR.CallMethod('_'+meth) - wx.CallAfter(self.RefreshStatusToolBar) + if obj.methodLock.acquire(False): + obj.CTR.CallMethod('_'+meth) + obj.methodLock.release() + wx.CallAfter(obj.RefreshStatusToolBar) + else: + # Postpone call if one of method already running + # can happen because of long method using log, + # itself calling wx.Yield + wx.CallLater(50, OnMethod, evt) return OnMethod - newid = wx.NewId() + newid = wx.NewIdRef() self.Bind(wx.EVT_MENU, OnMethodGen(self, method), id=newid) accels += [wx.AcceleratorEntry(wx.ACCEL_NORMAL, shortcut, newid)] @@ -395,6 +401,7 @@ self.LogConsole.MarkerDefine(0, wx.stc.STC_MARK_CIRCLE, "BLACK", "RED") self.LogConsole.SetModEventMask(wx.stc.STC_MOD_INSERTTEXT) + self.LogConsole.SetCaretPeriod(0) self.LogConsole.Bind(wx.stc.EVT_STC_MARGINCLICK, self.OnLogConsoleMarginClick) self.LogConsole.Bind(wx.stc.EVT_STC_MODIFIED, self.OnLogConsoleModified) @@ -409,7 +416,7 @@ # 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_HORIZONTAL | wx.NO_BORDER) StatusToolBar.SetToolBitmapSize(wx.Size(25, 25)) StatusToolBar.Realize() self.Panes["StatusToolBar"] = StatusToolBar @@ -420,7 +427,7 @@ self.AUIManager.Update() - self.ConnectionStatusBar = esb.EnhancedStatusBar(self, style=wx.ST_SIZEGRIP) + self.ConnectionStatusBar = esb.EnhancedStatusBar(self, style=wx.STB_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) @@ -436,17 +443,16 @@ # found here. os.environ["PATH"] = os.getcwd()+';'+os.environ["PATH"] - def __init__(self, parent, projectOpen=None, buildpath=None, ctr=None, debug=True): + def __init__(self, parent, projectOpen=None, buildpath=None, ctr=None, debug=True, logf=None): + # 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.local_runtime = None - self.runtime_port = None - self.local_runtime_tmpdir = None + self.Log = LogPseudoFile(self.LogConsole, self.SelectTab, logf) + + LocalRuntimeMixin.__init__(self, self.Log) self.LastPanelSelected = None @@ -501,6 +507,8 @@ self.RefreshAll() self.LogConsole.SetFocus() + signal.signal(signal.SIGTERM,self.signalTERM_handler) + def RefreshTitle(self): name = _("Beremiz") if self.CTR is not None: @@ -511,37 +519,6 @@ else: self.SetTitle(name) - 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 - self.Log.write(_("Starting local runtime...\n")) - # launch local runtime - 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], - self.local_runtime_tmpdir), - no_gui=False, - timeout=500, keyword=self.local_runtime_tmpdir, - cwd=self.local_runtime_tmpdir) - self.local_runtime.spin() - return self.runtime_port - - def KillLocalRuntime(self): - if self.local_runtime is not None: - # shutdown local runtime - self.local_runtime.kill(gently=False) - # clear temp dir - shutil.rmtree(self.local_runtime_tmpdir) - - self.local_runtime = None - def OnOpenWidgetInspector(self, evt): # Activate the widget inspection tool from wx.lib.inspection import InspectionTool @@ -560,7 +537,8 @@ event.Skip() def OnLogConsoleUpdateUI(self, event): - self.SetCopyBuffer(self.LogConsole.GetSelectedText(), True) + if event.GetUpdated()==wx.stc.STC_UPDATE_SELECTION: + self.SetCopyBuffer(self.LogConsole.GetSelectedText(), True) event.Skip() def OnLogConsoleMarginClick(self, event): @@ -603,7 +581,7 @@ elif answer == wx.ID_CANCEL: return False - for idx in xrange(self.TabsOpened.GetPageCount()): + for idx in range(self.TabsOpened.GetPageCount()): window = self.TabsOpened.GetPage(idx) if not window.CheckSaveBeforeClosing(): return False @@ -667,6 +645,11 @@ # prevent event to continue, i.e. cancel closing event.Veto() + def signalTERM_handler(self, sig, frame): + print ("Signal TERM caught: kill local runtime and quit, no save") + self.KillLocalRuntime() + sys.exit() + def RefreshFileMenu(self): self.RefreshRecentProjectsMenu() @@ -715,18 +698,18 @@ def RefreshRecentProjectsMenu(self): try: - recent_projects = map(DecodeFileSystemPath, - self.GetConfigEntry("RecentProjects", [])) + recent_projects = list(map(DecodeFileSystemPath, + self.GetConfigEntry("RecentProjects", []))) except Exception: recent_projects = [] while self.RecentProjectsMenu.GetMenuItemCount() > 0: item = self.RecentProjectsMenu.FindItemByPosition(0) - self.RecentProjectsMenu.RemoveItem(item) - - self.FileMenu.Enable(ID_FILEMENURECENTPROJECTS, len(recent_projects) > 0) + self.RecentProjectsMenu.Remove(item) + + self.RecentProjectsMenuItem.Enable(len(recent_projects) > 0) for idx, projectpath in enumerate(recent_projects): - text = u'&%d: %s' % (idx + 1, projectpath) + text = '&%d: %s' % (idx + 1, projectpath) item = self.RecentProjectsMenu.Append(wx.ID_ANY, text, '') self.Bind(wx.EVT_MENU, self.GenerateOpenRecentProjectFunction(projectpath), item) @@ -769,8 +752,9 @@ for confnode_method in self.CTR.StatusMethods: if "method" in confnode_method and confnode_method.get("shown", True): - tool = StatusToolBar.AddSimpleTool( - wx.ID_ANY, GetBitmap(confnode_method.get("bitmap", "Unknown")), + tool = StatusToolBar.AddTool( + wx.ID_ANY, confnode_method["name"], + GetBitmap(confnode_method.get("bitmap", "Unknown")), confnode_method["tooltip"]) self.Bind(wx.EVT_MENU, self.GetMenuCallBackFunction(confnode_method["method"]), tool) @@ -795,7 +779,7 @@ else: panel = None if panel != self.LastPanelSelected: - for i in xrange(self.EditMenuSize, self.EditMenu.GetMenuItemCount()): + for i in range(self.EditMenuSize, self.EditMenu.GetMenuItemCount()): item = self.EditMenu.FindItemByPosition(self.EditMenuSize) if item is not None: if item.IsSeparator(): @@ -813,7 +797,7 @@ if panel is not None: panel.RefreshConfNodeMenu(self.EditMenu) else: - for i in xrange(self.EditMenuSize, self.EditMenu.GetMenuItemCount()): + for i in range(self.EditMenuSize, self.EditMenu.GetMenuItemCount()): item = self.EditMenu.FindItemByPosition(i) if item is not None: if item.IsSeparator(): @@ -821,7 +805,7 @@ else: self.EditMenu.Delete(item.GetId()) self.LastPanelSelected = None - self.MenuBar.UpdateMenus() + self.MenuBar.Refresh() def RefreshAll(self): self.RefreshStatusToolBar() @@ -838,10 +822,12 @@ return OnMenu def GetConfigEntry(self, entry_name, default): - return cPickle.loads(str(self.Config.Read(entry_name, cPickle.dumps(default)))) + return pickle.loads(self.Config.Read(entry_name, + pickle.dumps(default, + 0).decode()).encode()) def ResetConnectionStatusBar(self): - for field in xrange(self.ConnectionStatusBar.GetFieldsCount()): + for field in range(self.ConnectionStatusBar.GetFieldsCount()): self.ConnectionStatusBar.SetStatusText('', field) def ResetView(self): @@ -856,16 +842,19 @@ def RefreshConfigRecentProjects(self, projectpath, err=False): try: - recent_projects = map(DecodeFileSystemPath, - self.GetConfigEntry("RecentProjects", [])) + recent_projects = list(map(DecodeFileSystemPath, + self.GetConfigEntry("RecentProjects", []))) except Exception: recent_projects = [] if projectpath in recent_projects: recent_projects.remove(projectpath) if not err: recent_projects.insert(0, projectpath) - self.Config.Write("RecentProjects", cPickle.dumps( - map(EncodeFileSystemPath, recent_projects[:MAX_RECENT_PROJECTS]))) + self.Config.Write("RecentProjects", + pickle.dumps( + list(map(EncodeFileSystemPath, + recent_projects[:MAX_RECENT_PROJECTS])), + 0)) self.Config.Flush() def ResetPerspective(self): @@ -877,7 +866,7 @@ return try: - defaultpath = DecodeFileSystemPath(self.Config.Read("lastopenedfolder")) + defaultpath = DecodeFileSystemPath(self.Config.Read("lastopenedfolder").encode()) except Exception: defaultpath = os.path.expanduser("~") @@ -988,19 +977,27 @@ self.Close() def OnAboutMenu(self, event): - info = version.GetAboutDialogInfo() + info = wx.adv.AboutDialogInfo() + info = version.GetAboutDialogInfo(info) + info.Name = "Beremiz" + info.Description = _("Open Source framework for automation, " + "implementing IEC 61131 IDE with constantly growing set of extensions " + "and flexible PLC runtime.") + + info.Icon = wx.Icon(Bpath("images", "about_brz_logo.png"), wx.BITMAP_TYPE_PNG) + ShowAboutDialog(self, info) def OnProjectTreeItemBeginEdit(self, event): selected = event.GetItem() - if self.ProjectTree.GetPyData(selected)["type"] == ITEM_CONFNODE: + if self.ProjectTree.GetItemData(selected)["type"] == ITEM_CONFNODE: event.Veto() else: IDEFrame.OnProjectTreeItemBeginEdit(self, event) def OnProjectTreeRightUp(self, event): item = event.GetItem() - item_infos = self.ProjectTree.GetPyData(item) + item_infos = self.ProjectTree.GetItemData(item) if item_infos["type"] == ITEM_CONFNODE: confnode_menu = wx.Menu(title='') @@ -1036,7 +1033,7 @@ def OnProjectTreeItemActivated(self, event): selected = event.GetItem() - item_infos = self.ProjectTree.GetPyData(selected) + item_infos = self.ProjectTree.GetItemData(selected) if item_infos["type"] == ITEM_CONFNODE: item_infos["confnode"]._OpenView() event.Skip() @@ -1047,7 +1044,7 @@ def ProjectTreeItemSelect(self, select_item): if select_item is not None and select_item.IsOk(): - item_infos = self.ProjectTree.GetPyData(select_item) + item_infos = self.ProjectTree.GetItemData(select_item) if item_infos["type"] == ITEM_CONFNODE: item_infos["confnode"]._OpenView(onlyopened=True) elif item_infos["type"] == ITEM_PROJECT: diff -r 7e17f7e02a2b -r 0b3ac94f494c Beremiz_cli.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Beremiz_cli.py Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,131 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import posixpath +import sys +import time + +from functools import wraps +from importlib import import_module + +import click + +class CLISession(object): + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + self.controller = None + +pass_session = click.make_pass_decorator(CLISession) + + +@click.group(chain=True) +@click.option( + "--project-home", + envvar="PROJECT_HOME", + default=".", + metavar="PATH", + help="Changes the project folder location.", +) +@click.option( + "--config", + nargs=2, + multiple=True, + metavar="KEY VALUE", + help="Overrides a config key/value pair.", +) +@click.option( + "--keep", "-k", is_flag=True, + help="Keep local runtime, do not kill it after executing commands.", +) +@click.option("--verbose", "-v", is_flag=True, help="Enables verbose mode.") +@click.option( + "--buildpath", "-b", help="Where to store files created during build." +) +@click.option( + "--uri", "-u", help="URI to reach remote PLC." +) +@click.version_option("0.1") +@click.pass_context +def cli(ctx, **kwargs): + """Beremiz CLI manipulates beremiz projects and runtimes. """ + + ctx.obj = CLISession(**kwargs) + +def ensure_controller(func): + @wraps(func) + def func_wrapper(session, *args, **kwargs): + if session.controller is None: + session.controller = import_module("CLIController").CLIController(session) + ret = func(session, *args, **kwargs) + return ret + + return func_wrapper + +@cli.command() +@click.option( + "--target", "-t", help="Target system triplet." +) +@pass_session +@ensure_controller +def build(session, target): + """Builds project. """ + def processor(): + return session.controller.build_project(target) + return processor + +@cli.command() +@pass_session +@ensure_controller +def transfer(session): + """Transfer program to PLC runtim.""" + def processor(): + return session.controller.transfer_project() + return processor + +@cli.command() +@pass_session +@ensure_controller +def run(session): + """Run program already present in PLC. """ + def processor(): + return session.controller.run_project() + return processor + +@cli.command() +@pass_session +@ensure_controller +def stop(session): + """Stop program running in PLC. """ + def processor(): + return session.controller.stop_project() + return processor + + +@cli.result_callback() +@pass_session +def process_pipeline(session, processors, **kwargs): + ret = 0 + for processor in processors: + ret = processor() + if ret != 0: + if len(processors) > 1 : + click.echo("Command sequence aborted") + break + + if session.keep: + click.echo("Press Ctrl+C to quit") + try: + while True: + session.controller.UpdateMethodsFromPLCStatus() + time.sleep(0.5) + except KeyboardInterrupt: + pass + + session.controller.finish() + + return ret + +if __name__ == '__main__': + cli() + diff -r 7e17f7e02a2b -r 0b3ac94f494c Beremiz_service.py --- a/Beremiz_service.py Wed Nov 29 11:54:56 2023 +0100 +++ b/Beremiz_service.py Thu Dec 07 22:41:32 2023 +0100 @@ -24,8 +24,6 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import print_function import os import sys import getopt @@ -33,10 +31,9 @@ import shlex import traceback import threading -from threading import Thread, Semaphore, Lock, currentThread -from builtins import str as text -from past.builtins import execfile -from six.moves import builtins +from threading import Thread, Semaphore, Lock, current_thread +import builtins +from functools import partial import runtime from runtime.PyroServer import PyroServer @@ -211,19 +208,7 @@ # Define locale domain loc.AddCatalog(domain) - 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'). - # And unicode string returned by wx.GetTranslation() are - # automatically converted to 'ascii' string. - def unicode_translation(message): - return wx.GetTranslation(message).encode(default_locale) - - builtins.__dict__['_'] = unicode_translation - # builtins.__dict__['_'] = wx.GetTranslation + builtins.__dict__['_'] = wx.GetTranslation # Life is hard... have a candy. @@ -238,11 +223,9 @@ if havewx: import re - - if wx.VERSION >= (3, 0, 0): - app = wx.App(redirect=False) - else: - app = wx.PySimpleApp(redirect=False) + import wx.adv + + app = wx.App(redirect=False) app.SetTopWindow(wx.Frame(None, -1)) default_locale = None @@ -279,19 +262,19 @@ def SetTests(self, tests): self.Tests = tests - class BeremizTaskBarIcon(wx.TaskBarIcon): - TBMENU_START = wx.NewId() - TBMENU_STOP = wx.NewId() - TBMENU_CHANGE_NAME = wx.NewId() - TBMENU_CHANGE_PORT = wx.NewId() - TBMENU_CHANGE_INTERFACE = wx.NewId() - TBMENU_LIVE_SHELL = wx.NewId() - TBMENU_WXINSPECTOR = wx.NewId() - TBMENU_CHANGE_WD = wx.NewId() - TBMENU_QUIT = wx.NewId() + class BeremizTaskBarIcon(wx.adv.TaskBarIcon): + TBMENU_START = wx.NewIdRef() + TBMENU_STOP = wx.NewIdRef() + TBMENU_CHANGE_NAME = wx.NewIdRef() + TBMENU_CHANGE_PORT = wx.NewIdRef() + TBMENU_CHANGE_INTERFACE = wx.NewIdRef() + TBMENU_LIVE_SHELL = wx.NewIdRef() + TBMENU_WXINSPECTOR = wx.NewIdRef() + TBMENU_CHANGE_WD = wx.NewIdRef() + TBMENU_QUIT = wx.NewIdRef() def __init__(self, pyroserver): - wx.TaskBarIcon.__init__(self) + wx.adv.TaskBarIcon.__init__(self) self.pyroserver = pyroserver # Set the image self.UpdateIcon(None) @@ -339,7 +322,7 @@ 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.Icon(img.ConvertToBitmap()) return icon def OnTaskBarStartPLC(self, evt): @@ -361,7 +344,7 @@ def OnTaskBarChangePort(self, evt): dlg = ParamsEntryDialog(None, _("Enter a port number "), defaultValue=str(self.pyroserver.port)) - dlg.SetTests([(text.isdigit, _("Port number must be an integer!")), (lambda port: 0 <= int(port) <= 65535, _("Port number must be 0 <= port <= 65535!"))]) + dlg.SetTests([(str.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.Restart() @@ -376,7 +359,7 @@ _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) != 0, _("Name must not be null!"))]) if dlg.ShowModal() == wx.ID_OK: self.pyroserver.servicename = dlg.GetValue() self.pyroserver.Restart() @@ -426,7 +409,12 @@ if havewx: from twisted.internet import wxreactor wxreactor.install() - from twisted.internet import reactor + from twisted.internet import reactor + reactor.registerWxApp(app) + else: + # from twisted.internet import pollreactor + # pollreactor.install() + from twisted.internet import reactor havetwisted = True except ImportError: @@ -435,13 +423,6 @@ pyruntimevars = {} -if havetwisted: - if havewx: - reactor.registerWxApp(app) - -twisted_reactor_thread_id = None -ui_thread = None - if havewx: wx_eval_lock = Semaphore(0) @@ -455,24 +436,18 @@ obj.res = default_evaluator(tocall, *args, **kwargs) wx_eval_lock.release() + main_thread_id = current_thread().ident def evaluator(tocall, *args, **kwargs): - # To prevent deadlocks, check if current thread is not one of the UI - # UI threads can be either the one from WX main loop or - # worker thread from twisted "threadselect" reactor - current_id = currentThread().ident - - if ui_thread is not None \ - and ui_thread.ident != current_id \ - and (not havetwisted or ( - twisted_reactor_thread_id is not None - and twisted_reactor_thread_id != current_id)): - + # To prevent deadlocks, check if current thread is not one already main + current_id = current_thread().ident + + if main_thread_id != current_id: o = type('', (object,), dict(call=(tocall, args, kwargs), res=None)) wx.CallAfter(wx_evaluator, o) wx_eval_lock.acquire() return o.res else: - # avoid dead lock if called from the wx mainloop + # avoid dead lock if called from main : do job immediately return default_evaluator(tocall, *args, **kwargs) else: evaluator = default_evaluator @@ -527,7 +502,7 @@ # Load extensions for extention_file, extension_folder in extensions: sys.path.append(extension_folder) - execfile(os.path.join(extension_folder, extention_file), locals()) + exec(compile(open(os.path.join(extension_folder, extention_file), "rb").read(), os.path.join(extension_folder, extention_file), 'exec'), locals()) # Service name is used as an ID for stunnel's PSK # Some extension may set 'servicename' to a computed ID or Serial Number @@ -548,7 +523,6 @@ try: website = NS.RegisterWebsite(interface, webport) pyruntimevars["website"] = website - statuschange.append(NS.website_statuslistener_factory(website)) except Exception: LogMessageAndException(_("Nevow Web service failed. ")) @@ -559,13 +533,15 @@ except Exception: LogMessageAndException(_("WAMP client startup failed. ")) +pyro_thread = None + def FirstWorkerJob(): """ RPC through pyro/wamp/UI may lead to delegation to Worker, then this function ensures that Worker is already created when pyro starts """ - global pyro_thread, pyroserver, ui_thread, reactor, twisted_reactor_thread_id + global pyro_thread, pyroserver pyro_thread_started = Lock() pyro_thread_started.acquire() @@ -582,46 +558,63 @@ # Beremiz IDE detects LOCAL:// runtime is ready by looking # for self.workdir in the daemon's stdout. - sys.stdout.write(_("Current working directory :") + WorkingDir + "\n") - sys.stdout.flush() - - if not (havetwisted or havewx): - return + if sys.stdout: + sys.stdout.write(_("Current working directory :") + WorkingDir + "\n") + sys.stdout.flush() + + runtime.GetPLCObjectSingleton().AutoLoad(autostart) + +if havetwisted and havewx: + + waker_func = wx.CallAfter + + # This orders ui loop to signal when ready on Stdout + waker_func(print,"UI thread started successfully.") + + # interleaved worker copes with wxreactor by delegating all asynchronous + # calls to wx's mainloop + runtime.MainWorker.interleave(waker_func, reactor.stop, FirstWorkerJob) + + try: + reactor.run(installSignalHandlers=False) + except KeyboardInterrupt: + pass + + runtime.MainWorker.stop() + +elif havewx: + + try: + app.MainLoop + except KeyboardInterrupt: + pass + +elif havetwisted: ui_thread_started = Lock() ui_thread_started.acquire() - if havetwisted: - # reactor._installSignalHandlersAgain() - def ui_thread_target(): - # FIXME: had to disable SignaHandlers install because - # signal not working in non-main thread - reactor.run(installSignalHandlers=False) - else: - ui_thread_target = app.MainLoop - - ui_thread = Thread(target=ui_thread_target, name="UIThread") + + reactor.callLater(0, ui_thread_started.release) + + ui_thread = Thread( + target=partial(reactor.run, installSignalHandlers=False), + name="UIThread") ui_thread.start() - # This order ui loop to unblock main thread when ready. - if havetwisted: - def signal_uithread_started(): - global twisted_reactor_thread_id - twisted_reactor_thread_id = currentThread().ident - ui_thread_started.release() - reactor.callLater(0, signal_uithread_started) - else: - wx.CallAfter(ui_thread_started.release) - - # Wait for ui thread to be effective ui_thread_started.acquire() print("UI thread started successfully.") - - runtime.GetPLCObjectSingleton().AutoLoad(autostart) - -try: - runtime.MainWorker.runloop(FirstWorkerJob) -except KeyboardInterrupt: - pass + try: + # blocking worker loop + runtime.MainWorker.runloop(FirstWorkerJob) + except KeyboardInterrupt: + pass +else: + try: + # blocking worker loop + runtime.MainWorker.runloop(FirstWorkerJob) + except KeyboardInterrupt: + pass + pyroserver.Quit() pyro_thread.join() @@ -635,9 +628,9 @@ if havetwisted: reactor.stop() - ui_thread.join() + if not havewx: + ui_thread.join() elif havewx: app.ExitMainLoop() - ui_thread.join() sys.exit(0) diff -r 7e17f7e02a2b -r 0b3ac94f494c CLIController.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CLIController.py Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,165 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import sys +from functools import wraps +from threading import Timer +from datetime import datetime + +import click + +import fake_wx + +from ProjectController import ProjectController +from LocalRuntimeMixin import LocalRuntimeMixin +from runtime.loglevels import LogLevelsCount, LogLevels + + +class Log: + + def __init__(self): + self.crlfpending = False + + def write(self, s): + if s: + if self.crlfpending: + sys.stdout.write("\n") + sys.stdout.write(s) + sys.stdout.flush() + self.crlfpending = 0 + + def write_error(self, s): + if s: + self.write("Error: "+s) + + def write_warning(self, s): + if s: + self.write("Warning: "+s) + + def flush(self): + sys.stdout.flush() + + def isatty(self): + return False + + def progress(self, s): + if s: + sys.stdout.write(s+"\r") + self.crlfpending = True + + +def with_project_loaded(func): + @wraps(func) + def func_wrapper(self, *args, **kwargs): + if not self.HasOpenedProject(): + if self.check_and_load_project(): + return 1 + self.apply_config() + return func(self, *args, **kwargs) + + return func_wrapper + +def connected(func): + @wraps(func) + def func_wrapper(self, *args, **kwargs): + if self._connector is None: + if self.session.uri: + self.BeremizRoot.setURI_location(self.session.uri) + if not self._Connect(): + return 1 + return func(self, *args, **kwargs) + + return func_wrapper + +class CLIController(LocalRuntimeMixin, ProjectController): + def __init__(self, session): + self.session = session + log = Log() + LocalRuntimeMixin.__init__(self, log, use_gui=False) + ProjectController.__init__(self, None, log) + + def _SetConnector(self, connector, update_status=True): + self._connector = connector + self.previous_log_count = [None]*LogLevelsCount + if connector is None and update_status: + self.UpdateMethodsFromPLCStatus() + + def UpdatePLCLog(self, log_count): + connector = self._connector + new_messages = [] + if connector: + for level, count, prev in zip( + range(LogLevelsCount), log_count, self.previous_log_count): + if count is not None and prev != count: + if prev is None: + dump_end = max(-1, count - 10) + else: + dump_end = prev - 1 + for msgidx in range(count-1, dump_end, -1): + message = connector.GetLogMessage(level, msgidx) + if message is not None: + msg, _tick, tv_sec, tv_nsec = message + date = datetime.utcfromtimestamp(tv_sec + tv_nsec * 1e-9) + txt = "%s at %s: %s\n" % (LogLevels[level], date.isoformat(' '), msg) + new_messages.append((date,txt)) + else: + break + self.previous_log_count[level] = count + new_messages.sort() + for date, txt in new_messages: + self.logger.write(txt) + + def check_and_load_project(self): + if not os.path.isdir(self.session.project_home): + self.logger.write_error( + _("\"%s\" is not a valid Beremiz project\n") % self.session.project_home) + return True + + if not os.path.isabs(self.session.project_home): + self.session.project_home = os.path.join(os.getcwd(), self.session.project_home) + + errmsg, error = self.LoadProject(self.session.project_home, self.session.buildpath) + if error: + self.logger.write_error(errmsg) + return True + + def apply_config(self): + for k,v in self.session.config: + self.SetParamsAttribute("BeremizRoot."+k, v) + + @with_project_loaded + def build_project(self, target): + + if target: + self.SetParamsAttribute("BeremizRoot.TargetType", target) + + return 0 if self._Build() else 1 + + @with_project_loaded + @connected + def transfer_project(self): + + return 0 if self._Transfer() else 1 + + @with_project_loaded + @connected + def run_project(self): + + return 0 if self._Run() else 1 + + @with_project_loaded + @connected + def stop_project(self): + + return 0 if self._Stop() else 1 + + + def finish(self): + + self._Disconnect() + + if not self.session.keep: + self.KillLocalRuntime() + + diff -r 7e17f7e02a2b -r 0b3ac94f494c CodeFileTreeNode.py --- a/CodeFileTreeNode.py Wed Nov 29 11:54:56 2023 +0100 +++ b/CodeFileTreeNode.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,11 +23,9 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import import os import re import traceback -from builtins import str as text from copy import deepcopy from lxml import etree @@ -128,7 +126,7 @@ self.GetCTRoot().logger.write_warning(XSDSchemaErrorMessage.format(a1=fname, a2=lnum, a3=src)) self.CreateCodeFileBuffer(True) except Exception as exc: - msg = _("Couldn't load confnode parameters {a1} :\n {a2}").format(a1=self.CTNName(), a2=text(exc)) + msg = _("Couldn't load confnode parameters {a1} :\n {a2}").format(a1=self.CTNName(), a2=str(exc)) self.GetCTRoot().logger.write_error(msg) self.GetCTRoot().logger.write_error(traceback.format_exc()) raise Exception @@ -195,7 +193,7 @@ self.CodeFile, pretty_print=True, xml_declaration=True, - encoding='utf-8')) + encoding='utf-8').decode()) xmlfile.close() self.MarkCodeFileAsSaved() diff -r 7e17f7e02a2b -r 0b3ac94f494c ConfigTreeNode.py --- a/ConfigTreeNode.py Wed Nov 29 11:54:56 2023 +0100 +++ b/ConfigTreeNode.py Thu Dec 07 22:41:32 2023 +0100 @@ -31,15 +31,13 @@ - ... TODO : document """ -from __future__ import absolute_import + import os import traceback import types import shutil from operator import add from functools import reduce -from builtins import str as text -from past.builtins import execfile from lxml import etree @@ -133,6 +131,15 @@ def CTNTestModified(self): return self.ChangesToSave + def CTNMarkModified(self): + oldChangesToSave = self.ChangesToSave + self.ChangesToSave = True + if not oldChangesToSave: + appframe = self.GetCTRoot().AppFrame + if appframe is not None: + appframe.RefreshTitle() + appframe.RefreshPageTitles() + def ProjectTestModified(self): """ recursively check modified status @@ -203,22 +210,22 @@ # generate XML for base XML parameters controller of the confnode if self.MandatoryParams: - BaseXMLFile = open(self.ConfNodeBaseXmlFilePath(), 'w') + BaseXMLFile = open(self.ConfNodeBaseXmlFilePath(), 'w', encoding='utf-8') BaseXMLFile.write(etree.tostring( self.MandatoryParams[1], pretty_print=True, xml_declaration=True, - encoding='utf-8')) + encoding='utf-8').decode()) BaseXMLFile.close() # generate XML for XML parameters controller of the confnode if self.CTNParams: - XMLFile = open(self.ConfNodeXmlFilePath(), 'w') + XMLFile = open(self.ConfNodeXmlFilePath(), 'w', encoding='utf-8') XMLFile.write(etree.tostring( self.CTNParams[1], pretty_print=True, xml_declaration=True, - encoding='utf-8')) + encoding='utf-8').decode()) XMLFile.close() # Call the confnode specific OnCTNSave method @@ -285,7 +292,7 @@ LDFLAGS = [] if CTNLDFLAGS is not None: # LDFLAGS can be either string - if isinstance(CTNLDFLAGS, (str, text)): + if isinstance(CTNLDFLAGS, str): LDFLAGS += [CTNLDFLAGS] # or list of strings elif isinstance(CTNLDFLAGS, list): @@ -310,7 +317,7 @@ return LocationCFilesAndCFLAGS, LDFLAGS, extra_files def IterChildren(self): - for _CTNType, Children in self.Children.items(): + for _CTNType, Children in list(self.Children.items()): for CTNInstance in Children: yield CTNInstance @@ -319,7 +326,7 @@ ordered = [(chld.BaseParams.getIEC_Channel(), chld) for chld in self.IterChildren()] if ordered: ordered.sort() - return zip(*ordered)[1] + return list(zip(*ordered))[1] else: return [] @@ -471,7 +478,7 @@ return None def GetView(self, onlyopened=False): - if self._View is None and not onlyopened and self.EditorType is not None: + if not self._View and not onlyopened and self.EditorType is not None: app_frame = self.GetCTRoot().AppFrame self._View = self.EditorType(app_frame.TabsOpened, self, app_frame) @@ -534,8 +541,8 @@ """ # 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]))) + transpose = list(zip(*self.CTNChildrenTypes)) + CTNChildrenTypes = dict(list(zip(transpose[0], list(zip(transpose[1], transpose[2]))))) # Check that adding this confnode is allowed try: CTNClass, CTNHelp = CTNChildrenTypes[CTNType] @@ -551,9 +558,12 @@ ChildrenWithSameClass = self.Children.setdefault(CTNType, list()) # Check count if getattr(CTNClass, "CTNMaxCount", None) and len(ChildrenWithSameClass) >= CTNClass.CTNMaxCount: - raise Exception( - _("Max count ({a1}) reached for this confnode of type {a2} "). - format(a1=CTNClass.CTNMaxCount, a2=CTNType)) + + msg = _("Max count ({a1}) reached for this confnode of type {a2} ").format( + a1=CTNClass.CTNMaxCount, a2=CTNType) + self.GetCTRoot().logger.write_warning(msg) + + return None # create the final class, derived of provided confnode and template class FinalCTNClass(CTNClass, ConfigTreeNode): @@ -623,7 +633,7 @@ def LoadXMLParams(self, CTNName=None): methode_name = os.path.join(self.CTNPath(CTNName), "methods.py") if os.path.isfile(methode_name): - execfile(methode_name) + exec(compile(open(methode_name, "rb").read(), methode_name, 'exec')) ConfNodeName = CTNName if CTNName is not None else self.CTNName() @@ -638,7 +648,7 @@ self.MandatoryParams = ("BaseParams", self.BaseParams) basexmlfile.close() except Exception as exc: - msg = _("Couldn't load confnode base parameters {a1} :\n {a2}").format(a1=ConfNodeName, a2=text(exc)) + msg = _("Couldn't load confnode base parameters {a1} :\n {a2}").format(a1=ConfNodeName, a2=str(exc)) self.GetCTRoot().logger.write_error(msg) self.GetCTRoot().logger.write_error(traceback.format_exc()) @@ -655,7 +665,7 @@ self.CTNParams = (name, obj) xmlfile.close() except Exception as exc: - msg = _("Couldn't load confnode parameters {a1} :\n {a2}").format(a1=ConfNodeName, a2=text(exc)) + msg = _("Couldn't load confnode parameters {a1} :\n {a2}").format(a1=ConfNodeName, a2=str(exc)) self.GetCTRoot().logger.write_error(msg) self.GetCTRoot().logger.write_error(traceback.format_exc()) @@ -668,7 +678,7 @@ try: self.CTNAddChild(pname, ptype) except Exception as exc: - msg = _("Could not add child \"{a1}\", type {a2} :\n{a3}\n").format(a1=pname, a2=ptype, a3=text(exc)) + msg = _("Could not add child \"{a1}\", type {a2} :\n{a3}\n").format(a1=pname, a2=ptype, a3=str(exc)) self.GetCTRoot().logger.write_error(msg) self.GetCTRoot().logger.write_error(traceback.format_exc()) diff -r 7e17f7e02a2b -r 0b3ac94f494c IDEFrame.py --- a/IDEFrame.py Wed Nov 29 11:54:56 2023 +0100 +++ b/IDEFrame.py Thu Dec 07 22:41:32 2023 +0100 @@ -22,18 +22,15 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import division -import sys + +from functools import cmp_to_key +import pickle +from operator import eq import base64 -from future.builtins import \ - round, \ - str as text import wx import wx.grid import wx.aui -from six.moves import cPickle, xrange from editors.EditorPanel import EditorPanel from editors.SFCViewer import SFC_Viewer @@ -60,7 +57,7 @@ ID_PLCOPENEDITORTABSOPENED, ID_PLCOPENEDITORTABSOPENED, ID_PLCOPENEDITOREDITORMENUTOOLBAR, ID_PLCOPENEDITOREDITORTOOLBAR, ID_PLCOPENEDITORPROJECTPANEL, -] = [wx.NewId() for _init_ctrls in range(17)] +] = [wx.NewIdRef() for _init_ctrls in range(17)] # Define PLCOpenEditor EditMenu extra items id [ @@ -69,14 +66,14 @@ ID_PLCOPENEDITOREDITMENUADDPROGRAM, ID_PLCOPENEDITOREDITMENUADDCONFIGURATION, ID_PLCOPENEDITOREDITMENUFINDNEXT, ID_PLCOPENEDITOREDITMENUFINDPREVIOUS, ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, ID_PLCOPENEDITOREDITMENUADDRESOURCE -] = [wx.NewId() for _init_coll_EditMenu_Items in range(10)] +] = [wx.NewIdRef() for _init_coll_EditMenu_Items in range(10)] # Define PLCOpenEditor DisplayMenu extra items id [ ID_PLCOPENEDITORDISPLAYMENURESETPERSPECTIVE, ID_PLCOPENEDITORDISPLAYMENUSWITCHPERSPECTIVE, ID_PLCOPENEDITORDISPLAYMENUFULLSCREEN, -] = [wx.NewId() for _init_coll_DisplayMenu_Items in range(3)] +] = [wx.NewIdRef() for _init_coll_DisplayMenu_Items in range(3)] # ------------------------------------------------------------------------------- # EditorToolBar definitions @@ -93,7 +90,7 @@ ID_PLCOPENEDITOREDITORTOOLBARSTEP, ID_PLCOPENEDITOREDITORTOOLBARTRANSITION, ID_PLCOPENEDITOREDITORTOOLBARACTIONBLOCK, ID_PLCOPENEDITOREDITORTOOLBARDIVERGENCE, ID_PLCOPENEDITOREDITORTOOLBARJUMP, ID_PLCOPENEDITOREDITORTOOLBARMOTION, -] = [wx.NewId() for _init_coll_DefaultEditorToolBar_Items in range(18)] +] = [wx.NewIdRef() for _init_coll_DefaultEditorToolBar_Items in range(18)] # ------------------------------------------------------------------------------- @@ -102,26 +99,25 @@ def EncodeFileSystemPath(path, use_base64=True): - path = path.encode(sys.getfilesystemencoding()) if use_base64: - return base64.encodestring(path) + path = base64.b64encode(path.encode()).decode() return path def DecodeFileSystemPath(path, is_base64=True): if is_base64: - path = base64.decodestring(path) - return text(path, sys.getfilesystemencoding()) + path = base64.b64decode(path.encode()).decode() + return path def AppendMenu(parent, help, kind, text, id=wx.ID_ANY): - return parent.Append(help=help, kind=kind, text=text, id=id) + return parent.Append(wx.MenuItem(helpString=help, kind=kind, text=text, id=id)) [ TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE, SCALING, PAGETITLES -] = range(10) +] = list(range(10)) def GetShortcutKeyCallbackFunction(viewer_function): @@ -141,7 +137,7 @@ name = self.ProjectTree.GetItemText(selected) if check_function is None or check_function(name): if parent_type is not None: - item_infos = self.ProjectTree.GetPyData(selected) + item_infos = self.ProjectTree.GetItemData(selected) parent_name = item_infos["tagname"].split("::")[1] remove_function(self.Controler, parent_name, name) else: @@ -161,7 +157,8 @@ 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(key=cmp_to_key(lambda x, y: eq(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 \ @@ -176,7 +173,8 @@ 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(key=cmp_to_key(lambda x, y: eq(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 \ @@ -343,59 +341,59 @@ } def _init_coll_MenuBar_Menus(self, parent): - parent.Append(menu=self.FileMenu, title=_(u'&File')) - parent.Append(menu=self.EditMenu, title=_(u'&Edit')) - parent.Append(menu=self.DisplayMenu, title=_(u'&Display')) - parent.Append(menu=self.HelpMenu, title=_(u'&Help')) + parent.Append(menu=self.FileMenu, title=_('&File')) + parent.Append(menu=self.EditMenu, title=_('&Edit')) + parent.Append(menu=self.DisplayMenu, title=_('&Display')) + parent.Append(menu=self.HelpMenu, title=_('&Help')) def _init_coll_FileMenu_Items(self, parent): pass 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=_('&Data Type')) AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDFUNCTION, - kind=wx.ITEM_NORMAL, text=_(u'&Function')) + kind=wx.ITEM_NORMAL, text=_('&Function')) AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDFUNCTIONBLOCK, - kind=wx.ITEM_NORMAL, text=_(u'Function &Block')) + kind=wx.ITEM_NORMAL, text=_('Function &Block')) AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDPROGRAM, - kind=wx.ITEM_NORMAL, text=_(u'&Program')) + kind=wx.ITEM_NORMAL, text=_('&Program')) AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDRESOURCE, - kind=wx.ITEM_NORMAL, text=_(u'&Resource')) + kind=wx.ITEM_NORMAL, text=_('&Resource')) if add_config: AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDCONFIGURATION, - kind=wx.ITEM_NORMAL, text=_(u'&Configuration')) + kind=wx.ITEM_NORMAL, text=_('&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=_('Undo') + '\tCTRL+Z') AppendMenu(parent, help='', id=wx.ID_REDO, - kind=wx.ITEM_NORMAL, text=_(u'Redo') + '\tCTRL+Y') + kind=wx.ITEM_NORMAL, text=_('Redo') + '\tCTRL+Y') parent.AppendSeparator() AppendMenu(parent, help='', id=wx.ID_CUT, - kind=wx.ITEM_NORMAL, text=_(u'Cut') + '\tCTRL+X') + kind=wx.ITEM_NORMAL, text=_('Cut') + '\tCTRL+X') AppendMenu(parent, help='', id=wx.ID_COPY, - kind=wx.ITEM_NORMAL, text=_(u'Copy') + '\tCTRL+C') + kind=wx.ITEM_NORMAL, text=_('Copy') + '\tCTRL+C') AppendMenu(parent, help='', id=wx.ID_PASTE, - kind=wx.ITEM_NORMAL, text=_(u'Paste') + '\tCTRL+V') + kind=wx.ITEM_NORMAL, text=_('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=_('Find') + '\tCTRL+F') AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUFINDNEXT, - kind=wx.ITEM_NORMAL, text=_(u'Find Next') + '\tCTRL+K') + kind=wx.ITEM_NORMAL, text=_('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=_('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=_('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) + self.AddMenuItem = parent.AppendSubMenu(add_menu, _("&Add Element")) AppendMenu(parent, help='', id=wx.ID_SELECTALL, - kind=wx.ITEM_NORMAL, text=_(u'Select All') + '\tCTRL+A') + kind=wx.ITEM_NORMAL, text=_('Select All') + '\tCTRL+A') AppendMenu(parent, help='', id=wx.ID_DELETE, - kind=wx.ITEM_NORMAL, text=_(u'&Delete')) + kind=wx.ITEM_NORMAL, text=_('&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) @@ -424,41 +422,42 @@ self.Bind(wx.EVT_MENU, self.OnSelectAllMenu, id=wx.ID_SELECTALL) self.Bind(wx.EVT_MENU, self.OnDeleteMenu, id=wx.ID_DELETE) - self.AddToMenuToolBar([(wx.ID_UNDO, "undo", _(u'Undo'), None), - (wx.ID_REDO, "redo", _(u'Redo'), None), + self.AddToMenuToolBar([(wx.ID_UNDO, "undo", _('Undo'), None), + (wx.ID_REDO, "redo", _('Redo'), None), None, - (wx.ID_CUT, "cut", _(u'Cut'), None), - (wx.ID_COPY, "copy", _(u'Copy'), None), - (wx.ID_PASTE, "paste", _(u'Paste'), None), + (wx.ID_CUT, "cut", _('Cut'), None), + (wx.ID_COPY, "copy", _('Copy'), None), + (wx.ID_PASTE, "paste", _('Paste'), None), None, - (ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, "find", _(u'Search in Project'), None), - (ID_PLCOPENEDITORDISPLAYMENUFULLSCREEN, "fullscreen", _(u'Toggle fullscreen mode'), None)]) + (ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, "find", _('Search in Project'), None), + (ID_PLCOPENEDITORDISPLAYMENUFULLSCREEN, "fullscreen", _('Toggle fullscreen mode'), None)]) 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=_('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=_('Clear Errors') + '\tCTRL+K') parent.AppendSeparator() zoommenu = wx.Menu(title='') - parent.AppendMenu(wx.ID_ZOOM_FIT, _("Zoom"), zoommenu) + self.ZoomMenuItem = parent.AppendSubMenu(zoommenu, _("Zoom")) for idx, value in enumerate(ZOOM_FACTORS): new_item = AppendMenu(zoommenu, help='', kind=wx.ITEM_RADIO, text=str(int(round(value * 100))) + "%") self.Bind(wx.EVT_MENU, self.GenerateZoomFunction(idx), new_item) parent.AppendSeparator() - AppendMenu(parent, help='', id=ID_PLCOPENEDITORDISPLAYMENUSWITCHPERSPECTIVE, - kind=wx.ITEM_NORMAL, text=_(u'Switch perspective') + '\tF12') - self.Bind(wx.EVT_MENU, self.SwitchPerspective, id=ID_PLCOPENEDITORDISPLAYMENUSWITCHPERSPECTIVE) + if wx.VERSION >= (4, 1, 0): + AppendMenu(parent, help='', id=ID_PLCOPENEDITORDISPLAYMENUSWITCHPERSPECTIVE, + kind=wx.ITEM_NORMAL, text=_('Switch perspective') + '\tF12') + self.Bind(wx.EVT_MENU, self.SwitchPerspective, id=ID_PLCOPENEDITORDISPLAYMENUSWITCHPERSPECTIVE) AppendMenu(parent, help='', id=ID_PLCOPENEDITORDISPLAYMENUFULLSCREEN, - kind=wx.ITEM_NORMAL, text=_(u'Full screen') + '\tShift-F12') + kind=wx.ITEM_NORMAL, text=_('Full screen') + '\tShift-F12') self.Bind(wx.EVT_MENU, self.SwitchFullScrMode, id=ID_PLCOPENEDITORDISPLAYMENUFULLSCREEN) AppendMenu(parent, help='', id=ID_PLCOPENEDITORDISPLAYMENURESETPERSPECTIVE, - kind=wx.ITEM_NORMAL, text=_(u'Reset Perspective')) + kind=wx.ITEM_NORMAL, text=_('Reset Perspective')) self.Bind(wx.EVT_MENU, self.OnResetPerspective, id=ID_PLCOPENEDITORDISPLAYMENURESETPERSPECTIVE) self.Bind(wx.EVT_MENU, self.OnRefreshMenu, id=wx.ID_REFRESH) @@ -569,8 +568,8 @@ 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) + name='ProjectPanel', parent=self.LeftNoteBook, + size=wx.Size(0, 0)) self.ProjectTree = CustomTree(id=ID_PLCOPENEDITORPROJECTTREE, name='ProjectTree', @@ -617,7 +616,7 @@ MenuToolBar = wx.ToolBar(self, ID_PLCOPENEDITOREDITORMENUTOOLBAR, wx.DefaultPosition, wx.DefaultSize, - wx.TB_FLAT | wx.TB_NODIVIDER | wx.NO_BORDER) + wx.TB_FLAT | wx.TB_HORIZONTAL | wx.NO_BORDER) MenuToolBar.SetToolBitmapSize(wx.Size(25, 25)) MenuToolBar.Realize() self.Panes["MenuToolBar"] = MenuToolBar @@ -628,9 +627,10 @@ EditorToolBar = wx.ToolBar(self, ID_PLCOPENEDITOREDITORTOOLBAR, wx.DefaultPosition, wx.DefaultSize, - wx.TB_FLAT | wx.TB_NODIVIDER | wx.NO_BORDER) + wx.TB_FLAT | wx.TB_HORIZONTAL | wx.NO_BORDER) EditorToolBar.SetToolBitmapSize(wx.Size(25, 25)) EditorToolBar.AddRadioTool(ID_PLCOPENEDITOREDITORTOOLBARSELECTION, + _("Select"), GetBitmap("select"), wx.NullBitmap, _("Select an object")) @@ -763,9 +763,6 @@ wx.CallAfter(self.InitFindDialog) - def __del__(self): - self.FindDialog.Destroy() - def InitFindDialog(self): self.FindDialog = FindInPouDialog(self) self.FindDialog.Hide() @@ -791,7 +788,7 @@ # ------------------------------------------------------------------------------- def GetTabInfos(self, tab): - for page_name, (page_ref, _page_title) in self.MainTabs.iteritems(): + for page_name, (page_ref, _page_title) in self.MainTabs.items(): if page_ref == tab: return ("main", page_name) return None @@ -804,7 +801,7 @@ pos = child.GetPosition() tab = {"pos": (pos.x, pos.y), "pages": []} tab_size = child.GetSize() - for page_idx in xrange(child.GetPageCount()): + for page_idx in range(child.GetPageCount()): page = child.GetWindowFromIdx(page_idx) if "size" not in tab: tab["size"] = (tab_size[0], tab_size[1] + page.GetSize()[1]) @@ -812,7 +809,7 @@ if tab_infos is not None: tab["pages"].append((tab_infos, page_idx == child.GetActivePage())) tabs.append(tab) - tabs.sort(lambda x, y: cmp(x["pos"], y["pos"])) + tabs.sort(key=cmp_to_key(lambda x, y: eq(x["pos"], y["pos"]))) size = notebook.GetSize() return ComputeTabsLayout(tabs, wx.Rect(1, 1, size[0] - NOTEBOOK_BORDER, size[1] - NOTEBOOK_BORDER)) @@ -870,7 +867,7 @@ self.AUIManager.LoadPerspective(self.DefaultPerspective["perspective"]) for notebook in [self.LeftNoteBook, self.BottomNoteBook, self.RightNoteBook]: - for dummy in xrange(notebook.GetPageCount()): + for dummy in range(notebook.GetPageCount()): notebook.RemovePage(0) notebooks = self.DefaultPerspective["notebooks"] @@ -884,7 +881,7 @@ def RestoreLastState(self): frame_size = None if self.Config.HasEntry("framesize"): - frame_size = cPickle.loads(str(self.Config.Read("framesize"))) + frame_size = pickle.loads(self.Config.Read("framesize").encode()) if frame_size is None: self.Maximize() @@ -893,7 +890,7 @@ def SaveLastState(self): if not self.IsMaximized(): - self.Config.Write("framesize", cPickle.dumps(self.GetClientSize())) + self.Config.Write("framesize", pickle.dumps(self.GetClientSize(), 0)) elif self.Config.HasEntry("framesize"): self.Config.DeleteEntry("framesize") @@ -921,12 +918,8 @@ :param elements: List of elements to refresh. """ - try: - for element in elements: - self.RefreshFunctions[element]() - except wx.PyDeadObjectError: - # ignore exceptions caused by refresh while quitting - pass + for element in elements: + self.RefreshFunctions[element]() def OnPageClose(self, event): """Callback function when AUINotebook Page closed with CloseButton @@ -985,7 +978,7 @@ return self.DrawingMode def RefreshScaling(self): - for i in xrange(self.TabsOpened.GetPageCount()): + for i in range(self.TabsOpened.GetPageCount()): editor = self.TabsOpened.GetPage(i) editor.RefreshScaling() @@ -1017,7 +1010,7 @@ self.RefreshTabCtrlEvent() def DeletePage(self, window): - for idx in xrange(self.TabsOpened.GetPageCount()): + for idx in range(self.TabsOpened.GetPageCount()): if self.TabsOpened.GetPage(idx) == window: self.TabsOpened.DeletePage(idx) self.RefreshTabCtrlEvent() @@ -1027,7 +1020,7 @@ """Function that fix difference in deleting all tabs between wx.Notebook and wx.aui.AUINotebook. """ - for dummy in xrange(self.TabsOpened.GetPageCount()): + for dummy in range(self.TabsOpened.GetPageCount()): self.TabsOpened.DeletePage(0) self.RefreshTabCtrlEvent() @@ -1068,7 +1061,7 @@ elif answer == wx.ID_CANCEL: return False - for idx in xrange(self.TabsOpened.GetPageCount()): + for idx in range(self.TabsOpened.GetPageCount()): window = self.TabsOpened.GetPage(idx) if not window.CheckSaveBeforeClosing(): return False @@ -1117,7 +1110,7 @@ window = self.TabsOpened.GetPage(selected) data = wx.PrintDialogData(self.PrintData) properties = self.Controler.GetProjectProperties(window.IsDebugging()) - page_size = map(int, properties["pageSize"]) + page_size = list(map(int, properties["pageSize"])) margins = (self.PageSetupData.GetMarginTopLeft(), self.PageSetupData.GetMarginBottomRight()) printout = GraphicPrintout(window, page_size, margins, True) printout2 = GraphicPrintout(window, page_size, margins, True) @@ -1141,7 +1134,7 @@ dialog_data = wx.PrintDialogData(self.PrintData) dialog_data.SetToPage(1) properties = self.Controler.GetProjectProperties(window.IsDebugging()) - page_size = map(int, properties["pageSize"]) + page_size = list(map(int, properties["pageSize"])) margins = (self.PageSetupData.GetMarginTopLeft(), self.PageSetupData.GetMarginBottomRight()) printer = wx.Printer(dialog_data) printout = GraphicPrintout(window, page_size, margins) @@ -1186,7 +1179,7 @@ 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) + self.AddMenuItem.Enable(True) self.EditMenu.Enable(wx.ID_DELETE, True) if self.TabsOpened.GetPageCount() > 0: self.EditMenu.Enable(wx.ID_CUT, True) @@ -1226,11 +1219,11 @@ self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUFINDPREVIOUS, False) self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, False) MenuToolBar.EnableTool(ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, False) - self.EditMenu.Enable(wx.ID_ADD, False) + self.AddMenuItem.Enable( False) self.EditMenu.Enable(wx.ID_DELETE, False) def CloseTabsWithoutModel(self, refresh=True): - idxs = range(self.TabsOpened.GetPageCount()) + idxs = list(range(self.TabsOpened.GetPageCount())) idxs.reverse() for idx in idxs: window = self.TabsOpened.GetPage(idx) @@ -1301,7 +1294,7 @@ if window == self.ProjectTree or window is None: selected = self.ProjectTree.GetSelection() if selected is not None and selected.IsOk(): - function = self.DeleteFunctions.get(self.ProjectTree.GetPyData(selected)["type"], None) + function = self.DeleteFunctions.get(self.ProjectTree.GetItemData(selected)["type"], None) if function is not None: function(self, selected) self.CloseTabsWithoutModel() @@ -1358,24 +1351,24 @@ if selected != -1: window = self.TabsOpened.GetPage(selected) if isinstance(window, Viewer): - self.DisplayMenu.Enable(wx.ID_ZOOM_FIT, True) - zoommenu = self.DisplayMenu.FindItemById(wx.ID_ZOOM_FIT).GetSubMenu() + self.ZoomMenuItem.Enable(True) + zoommenu = self.ZoomMenuItem.GetSubMenu() zoomitem = zoommenu.FindItemByPosition(window.GetScale()) zoomitem.Check(True) else: - self.DisplayMenu.Enable(wx.ID_ZOOM_FIT, False) + self.ZoomMenuItem.Enable(False) else: - self.DisplayMenu.Enable(wx.ID_ZOOM_FIT, False) + self.ZoomMenuItem.Enable(False) else: self.DisplayMenu.Enable(wx.ID_REFRESH, False) - self.DisplayMenu.Enable(wx.ID_ZOOM_FIT, False) + self.ZoomMenuItem.Enable(False) if self.EnableDebug: self.DisplayMenu.Enable(wx.ID_CLEAR, True) else: self.DisplayMenu.Enable(wx.ID_REFRESH, False) if self.EnableDebug: self.DisplayMenu.Enable(wx.ID_CLEAR, False) - self.DisplayMenu.Enable(wx.ID_ZOOM_FIT, False) + self.ZoomMenuItem.Enable(False) def OnRefreshMenu(self, event): self.RefreshEditor() @@ -1413,12 +1406,14 @@ for child in self.TabsOpened.GetChildren(): if isinstance(child, wx.aui.AuiTabCtrl): auitabctrl.append(child) - if child not in self.AuiTabCtrl: + if wx.VERSION >= (4, 1, 0) and child not in self.AuiTabCtrl: child.Bind(wx.EVT_LEFT_DCLICK, self.GetTabsOpenedDClickFunction(child)) self.AuiTabCtrl = auitabctrl - if self.TabsOpened.GetPageCount() == 0: + # on wxPython 4.0.7, AuiManager has no "RestorePane" method... + if wx.VERSION >= (4, 1, 0) and self.TabsOpened.GetPageCount() == 0: pane = self.AUIManager.GetPane(self.TabsOpened) - if pane.IsMaximized(): + # on wxPython 4.1.0, AuiPaneInfo has no "IsMaximized" attribute... + if (not hasattr(pane, "IsMaximized")) or pane.IsMaximized(): self.AUIManager.RestorePane(pane) self.AUIManager.Update() @@ -1477,19 +1472,19 @@ self._Refresh(FILEMENU, EDITMENU, DISPLAYMENU, EDITORTOOLBAR) def RefreshEditorNames(self, old_tagname, new_tagname): - for i in xrange(self.TabsOpened.GetPageCount()): + for i in range(self.TabsOpened.GetPageCount()): editor = self.TabsOpened.GetPage(i) if editor.GetTagName() == old_tagname: editor.SetTagName(new_tagname) def IsOpened(self, tagname): - for idx in xrange(self.TabsOpened.GetPageCount()): + for idx in range(self.TabsOpened.GetPageCount()): if self.TabsOpened.GetPage(idx).IsViewing(tagname): return idx return None def RefreshPageTitles(self): - for idx in xrange(self.TabsOpened.GetPageCount()): + for idx in range(self.TabsOpened.GetPageCount()): window = self.TabsOpened.GetPage(idx) icon = window.GetIcon() if icon is not None: @@ -1499,17 +1494,25 @@ def GetTabsOpenedDClickFunction(self, tabctrl): def OnTabsOpenedDClick(event): pos = event.GetPosition() - if tabctrl.TabHitTest(pos.x, pos.y, None): + if tabctrl.TabHitTest(pos.x, pos.y): self.SwitchPerspective(event) event.Skip() return OnTabsOpenedDClick def SwitchPerspective(self, evt): + if not hasattr(self.AUIManager, "MaximizePane"): + return pane = self.AUIManager.GetPane(self.TabsOpened) - if pane.IsMaximized(): + # on wxPython 4.1.0, AuiPaneInfo has no "IsMaximized" attribute... + IsMaximized = pane.IsMaximized() if hasattr(pane, "IsMaximized") \ + else (self.TabBookIsMaximized if hasattr(self, "TabBookIsMaximized") \ + else False) + if IsMaximized: self.AUIManager.RestorePane(pane) + self.TabBookIsMaximized = False else: self.AUIManager.MaximizePane(pane) + self.TabBookIsMaximized = True self.AUIManager.Update() def SwitchFullScrMode(self, evt): @@ -1524,7 +1527,7 @@ # Extract current selected item tagname selected = self.ProjectTree.GetSelection() if selected is not None and selected.IsOk(): - item_infos = self.ProjectTree.GetPyData(selected) + item_infos = self.ProjectTree.GetItemData(selected) tagname = item_infos.get("tagname", None) else: tagname = None @@ -1597,14 +1600,14 @@ if root is not None and root.IsOk(): words = tagname.split("::") result = self.RecursiveProjectTreeItemSelection( - root, zip(words[1:], self.TagNamePartsItemTypes.get(words[0], []))) + root, list(zip(words[1:], self.TagNamePartsItemTypes.get(words[0], [])))) return result def RecursiveProjectTreeItemSelection(self, root, items): found = False item, root_cookie = self.ProjectTree.GetFirstChild(root) while item is not None and item.IsOk() and not found: - item_infos = self.ProjectTree.GetPyData(item) + item_infos = self.ProjectTree.GetItemData(item) if (item_infos["name"].split(":")[-1].strip(), item_infos["type"]) == items[0]: if len(items) == 1: self.SelectedItem = item @@ -1625,7 +1628,7 @@ selected_item = (self.SelectedItem if self.SelectedItem is not None else event.GetItem()) - if selected_item.IsOk() and self.ProjectTree.GetPyData(selected_item)["type"] == ITEM_POU: + if selected_item.IsOk() and self.ProjectTree.GetItemData(selected_item)["type"] == ITEM_POU: block_name = self.ProjectTree.GetItemText(selected_item) block_type = self.Controler.GetPouType(block_name) if block_type != "program": @@ -1637,7 +1640,7 @@ def OnProjectTreeItemBeginEdit(self, event): selected = event.GetItem() - if self.ProjectTree.GetPyData(selected)["type"] in ITEMS_UNEDITABLE: + if self.ProjectTree.GetItemData(selected)["type"] in ITEMS_UNEDITABLE: event.Veto() else: event.Skip() @@ -1654,7 +1657,7 @@ else: item = event.GetItem() old_name = self.ProjectTree.GetItemText(item) - item_infos = self.ProjectTree.GetPyData(item) + item_infos = self.ProjectTree.GetItemData(item) if item_infos["type"] == ITEM_PROJECT: self.Controler.SetProjectProperties(name=new_name) elif item_infos["type"] == ITEM_DATATYPE: @@ -1758,7 +1761,7 @@ def OnProjectTreeItemActivated(self, event): selected = event.GetItem() - item_infos = self.ProjectTree.GetPyData(selected) + item_infos = self.ProjectTree.GetItemData(selected) if item_infos["type"] == ITEM_PROJECT: self.EditProjectSettings() else: @@ -1770,7 +1773,7 @@ def ProjectTreeItemSelect(self, select_item): if select_item is not None and select_item.IsOk(): - item_infos = self.ProjectTree.GetPyData(select_item) + item_infos = self.ProjectTree.GetItemData(select_item) if item_infos["type"] in [ITEM_DATATYPE, ITEM_POU, ITEM_CONFIGURATION, ITEM_RESOURCE, ITEM_TRANSITION, ITEM_ACTION]: @@ -1789,7 +1792,7 @@ pt = wx.Point(event.GetX(), event.GetY()) item, flags = self.ProjectTree.HitTest(pt) if item is not None and item.IsOk() and flags & wx.TREE_HITTEST_ONITEMLABEL: - item_infos = self.ProjectTree.GetPyData(item) + item_infos = self.ProjectTree.GetItemData(item) if item != self.LastToolTipItem and self.LastToolTipItem is not None: self.ProjectTree.SetToolTip(None) self.LastToolTipItem = None @@ -1808,7 +1811,7 @@ else: block_type = "Action" self.LastToolTipItem = item - wx.CallAfter(self.ProjectTree.SetToolTipString, + wx.CallAfter(self.ProjectTree.SetToolTip, "%s : %s : %s" % ( block_type, bodytype, item_infos["name"])) elif self.LastToolTipItem is not None: @@ -1817,7 +1820,7 @@ event.Skip() def OnProjectTreeItemChanging(self, event): - if self.ProjectTree.GetPyData(event.GetItem())["type"] not in ITEMS_UNEDITABLE and self.SelectedItem is None: + if self.ProjectTree.GetItemData(event.GetItem())["type"] not in ITEMS_UNEDITABLE and self.SelectedItem is None: self.SelectedItem = event.GetItem() event.Veto() else: @@ -1886,7 +1889,7 @@ if old_selected != openedidx: if old_selected >= 0: self.TabsOpened.GetPage(old_selected).ResetBuffer() - for i in xrange(self.TabsOpened.GetPageCount()): + for i in range(self.TabsOpened.GetPageCount()): window = self.TabsOpened.GetPage(i) if window == new_window: self.TabsOpened.SetSelection(i) @@ -1899,7 +1902,7 @@ self.ProjectTree.SelectItem(item) self.ProjectTreeItemSelect(item) name = self.ProjectTree.GetItemText(item) - item_infos = self.ProjectTree.GetPyData(item) + item_infos = self.ProjectTree.GetItemData(item) menu = None if item_infos["type"] in ITEMS_UNEDITABLE + [ITEM_PROJECT]: @@ -1923,7 +1926,7 @@ new_item = AppendMenu(menu, help='', kind=wx.ITEM_NORMAL, text=_("Paste POU")) self.Bind(wx.EVT_MENU, self.OnPastePou, new_item) if self.GetCopyBuffer() is None: - menu.Enable(new_item, False) + new_item.Enable(False) elif name == "Configurations": menu = wx.Menu(title='') @@ -1934,30 +1937,30 @@ menu = wx.Menu(title='') new_item = AppendMenu(menu, help='', kind=wx.ITEM_NORMAL, text=_("Add Transition")) parent = self.ProjectTree.GetItemParent(item) - parent_type = self.ProjectTree.GetPyData(parent)["type"] + parent_type = self.ProjectTree.GetItemData(parent)["type"] while parent_type != ITEM_POU: parent = self.ProjectTree.GetItemParent(parent) - parent_type = self.ProjectTree.GetPyData(parent)["type"] + parent_type = self.ProjectTree.GetItemData(parent)["type"] self.Bind(wx.EVT_MENU, self.GenerateAddTransitionFunction(self.ProjectTree.GetItemText(parent)), new_item) elif name == "Actions": menu = wx.Menu(title='') new_item = AppendMenu(menu, help='', kind=wx.ITEM_NORMAL, text=_("Add Action")) parent = self.ProjectTree.GetItemParent(item) - parent_type = self.ProjectTree.GetPyData(parent)["type"] + parent_type = self.ProjectTree.GetItemData(parent)["type"] while parent_type != ITEM_POU: parent = self.ProjectTree.GetItemParent(parent) - parent_type = self.ProjectTree.GetPyData(parent)["type"] + parent_type = self.ProjectTree.GetItemData(parent)["type"] self.Bind(wx.EVT_MENU, self.GenerateAddActionFunction(self.ProjectTree.GetItemText(parent)), new_item) elif name == "Resources": menu = wx.Menu(title='') new_item = AppendMenu(menu, help='', kind=wx.ITEM_NORMAL, text=_("Add Resource")) parent = self.ProjectTree.GetItemParent(item) - parent_type = self.ProjectTree.GetPyData(parent)["type"] + parent_type = self.ProjectTree.GetItemData(parent)["type"] while parent_type not in [ITEM_CONFIGURATION, ITEM_PROJECT]: parent = self.ProjectTree.GetItemParent(parent) - parent_type = self.ProjectTree.GetPyData(parent)["type"] + parent_type = self.ProjectTree.GetItemData(parent)["type"] parent_name = None if parent_type == ITEM_PROJECT: config_names = self.Controler.GetProjectConfigNames() @@ -2079,7 +2082,7 @@ def CloseObsoleteDebugTabs(self): if self.EnableDebug: - idxs = range(self.TabsOpened.GetPageCount()) + idxs = list(range(self.TabsOpened.GetPageCount())) idxs.reverse() for idx in idxs: editor = self.TabsOpened.GetPage(idx) @@ -2118,7 +2121,7 @@ MenuToolBar.AddSeparator() else: id, bitmap, help, callback = toolbar_item - MenuToolBar.AddSimpleTool(id=id, shortHelpString=help, bitmap=GetBitmap(bitmap)) + MenuToolBar.AddTool(id, help, GetBitmap(bitmap), help) if callback is not None: self.Bind(wx.EVT_TOOL, callback, id=id) MenuToolBar.Realize() @@ -2155,13 +2158,13 @@ self.CurrentEditorToolBar = [] EditorToolBar = self.Panes["EditorToolBar"] if EditorToolBar: - for radio, modes, id, method, picture, help in self.EditorToolBarItems[menu]: + for radio, modes, id, method_name, picture, help in self.EditorToolBarItems[menu]: if modes & self.DrawingMode: if radio or self.DrawingMode == FREEDRAWING_MODE: - EditorToolBar.AddRadioTool(id, GetBitmap(picture), wx.NullBitmap, help) + EditorToolBar.AddRadioTool(id, method_name, GetBitmap(picture), wx.NullBitmap, help) else: - EditorToolBar.AddSimpleTool(id, GetBitmap(picture), help) - self.Bind(wx.EVT_MENU, getattr(self, method), id=id) + EditorToolBar.AddTool(id, method_name, GetBitmap(picture), help) + self.Bind(wx.EVT_MENU, getattr(self, method_name), id=id) self.CurrentEditorToolBar.append(id) EditorToolBar.Realize() self.AUIManager.GetPane("EditorToolBar").Show() @@ -2188,8 +2191,8 @@ EditorToolBar.ToggleTool(ID_PLCOPENEDITOREDITORTOOLBARSELECTION, True) def ResetToolToggle(self, id): - tool = self.Panes["EditorToolBar"].FindById(id) - tool.SetToggle(False) + tool = self.Panes["EditorToolBar"] + tool.ToggleTool(toolId=id, toggle=False) def OnSelectionTool(self, event): selected = self.TabsOpened.GetSelection() @@ -2402,7 +2405,7 @@ def GenerateChangePouTypeFunction(self, name, new_type): def OnChangePouTypeMenu(event): selected = self.ProjectTree.GetSelection() - if self.ProjectTree.GetPyData(selected)["type"] == ITEM_POU: + if self.ProjectTree.GetItemData(selected)["type"] == ITEM_POU: self.Controler.ProjectChangePouType(name, new_type) self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, PROJECTTREE, LIBRARYTREE) return OnChangePouTypeMenu @@ -2419,7 +2422,7 @@ def OnPastePou(self, event): selected = self.ProjectTree.GetSelection() - if self.ProjectTree.GetPyData(selected)["type"] != ITEM_PROJECT: + if self.ProjectTree.GetItemData(selected)["type"] != ITEM_PROJECT: pou_type = self.ProjectTree.GetItemText(selected) pou_type = self.UNEDITABLE_NAMES_DICT[pou_type] # one of 'Functions', 'Function Blocks' or 'Programs' pou_type = {'Functions': 'function', 'Function Blocks': 'functionBlock', 'Programs': 'program'}[pou_type] @@ -2464,7 +2467,7 @@ def OnRemoveDataTypeMenu(self, event): selected = self.ProjectTree.GetSelection() - if self.ProjectTree.GetPyData(selected)["type"] == ITEM_DATATYPE: + if self.ProjectTree.GetItemData(selected)["type"] == ITEM_DATATYPE: name = self.ProjectTree.GetItemText(selected) if self.CheckDataTypeIsUsedBeforeDeletion(name): self.Controler.ProjectRemoveDataType(name) @@ -2476,12 +2479,12 @@ def OnRenamePouMenu(self, event): selected = self.ProjectTree.GetSelection() - if self.ProjectTree.GetPyData(selected)["type"] == ITEM_POU: + if self.ProjectTree.GetItemData(selected)["type"] == ITEM_POU: wx.CallAfter(self.ProjectTree.EditLabel, selected) def OnRemovePouMenu(self, event): selected = self.ProjectTree.GetSelection() - if self.ProjectTree.GetPyData(selected)["type"] == ITEM_POU: + if self.ProjectTree.GetItemData(selected)["type"] == ITEM_POU: name = self.ProjectTree.GetItemText(selected) if self.CheckPouIsUsedBeforeDeletion(name): self.Controler.ProjectRemovePou(name) @@ -2493,7 +2496,7 @@ def OnRemoveTransitionMenu(self, event): selected = self.ProjectTree.GetSelection() - item_infos = self.ProjectTree.GetPyData(selected) + item_infos = self.ProjectTree.GetItemData(selected) if item_infos["type"] == ITEM_TRANSITION: transition = self.ProjectTree.GetItemText(selected) pou_name = item_infos["tagname"].split("::")[1] @@ -2506,7 +2509,7 @@ def OnRemoveActionMenu(self, event): selected = self.ProjectTree.GetSelection() - item_infos = self.ProjectTree.GetPyData(selected) + item_infos = self.ProjectTree.GetItemData(selected) if item_infos["type"] == ITEM_ACTION: action = self.ProjectTree.GetItemText(selected) pou_name = item_infos["tagname"].split("::")[1] @@ -2519,7 +2522,7 @@ def OnRemoveConfigurationMenu(self, event): selected = self.ProjectTree.GetSelection() - if self.ProjectTree.GetPyData(selected)["type"] == ITEM_CONFIGURATION: + if self.ProjectTree.GetItemData(selected)["type"] == ITEM_CONFIGURATION: name = self.ProjectTree.GetItemText(selected) self.Controler.ProjectRemoveConfiguration(name) tagname = ComputeConfigurationName(name) @@ -2530,7 +2533,7 @@ def OnRemoveResourceMenu(self, event): selected = self.ProjectTree.GetSelection() - item_infos = self.ProjectTree.GetPyData(selected) + item_infos = self.ProjectTree.GetItemData(selected) if item_infos["type"] == ITEM_RESOURCE: resource = self.ProjectTree.GetItemText(selected) config_name = item_infos["tagname"].split("::")[1] @@ -2568,9 +2571,9 @@ if highlight_type is None: self.Highlights = {} else: - self.Highlights = dict([(name, highlight) for name, highlight in self.Highlights.iteritems() if highlight != highlight_type]) + self.Highlights = dict([(name, highlight) for name, highlight in self.Highlights.items() if highlight != highlight_type]) self.RefreshProjectTree() - for i in xrange(self.TabsOpened.GetPageCount()): + for i in range(self.TabsOpened.GetPageCount()): viewer = self.TabsOpened.GetPage(i) viewer.ClearHighlights(highlight_type) diff -r 7e17f7e02a2b -r 0b3ac94f494c LocalRuntimeMixin.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LocalRuntimeMixin.py Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import sys +import tempfile +import random +import shutil + +from util.ProcessLogger import ProcessLogger +from util.paths import Bpath + +_exec = sys.executable if "darwin" not in sys.platform else sys.executable + 'w' +LocalRuntimeInterpreterPath = os.environ.get("BEREMIZPYTHONPATH", _exec) + +LocalHost = os.environ.get("BEREMIZ_LOCAL_HOST", "127.0.0.1") + +class LocalRuntimeMixin(): + + def __init__(self, log, use_gui=True): + self.local_runtime_log = log + self.local_runtime = None + self.runtime_port = None + self.local_runtime_tmpdir = None + self.use_gui = use_gui + + def StartLocalRuntime(self): + 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 + self.local_runtime_log.write(_("Starting local runtime...\n")) + # launch local runtime + self.local_runtime = ProcessLogger( + self.local_runtime_log, + ("\"%s\" \"%s\" -p %s -i "+LocalHost+" %s %s") % ( + LocalRuntimeInterpreterPath, + Bpath("Beremiz_service.py"), + self.runtime_port, + {False: "-x 0", True: "-x 1"}[self.use_gui], + self.local_runtime_tmpdir), + no_gui=False, + timeout=500, keyword=self.local_runtime_tmpdir, + cwd=self.local_runtime_tmpdir) + self.local_runtime.spin() + return self.runtime_port + + def KillLocalRuntime(self): + if self.local_runtime is not None: + # shutdown local runtime + self.local_runtime.kill(gently=False) + # clear temp dir + shutil.rmtree(self.local_runtime_tmpdir) + + self.local_runtime = None + diff -r 7e17f7e02a2b -r 0b3ac94f494c NativeLib.py --- a/NativeLib.py Wed Nov 29 11:54:56 2023 +0100 +++ b/NativeLib.py Thu Dec 07 22:41:32 2023 +0100 @@ -24,7 +24,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import util.paths as paths from POULibrary import SimplePOULibraryFactory diff -r 7e17f7e02a2b -r 0b3ac94f494c PLCControler.py --- a/PLCControler.py Wed Nov 29 11:54:56 2023 +0100 +++ b/PLCControler.py Thu Dec 07 22:41:32 2023 +0100 @@ -24,15 +24,12 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import division from copy import deepcopy import os import re import datetime from time import localtime from functools import reduce -from future.builtins import round import util.paths as paths from plcopen import * @@ -74,7 +71,7 @@ self.MinIndex = 0 self.MaxIndex = 0 # Initialising buffer with currentstate at the first place - for i in xrange(UNDO_BUFFER_LENGTH): + for i in range(UNDO_BUFFER_LENGTH): if i == 0: self.Buffer.append(currentstate) else: @@ -457,8 +454,8 @@ self.NextCompiledProject = self.Copy(self.Project) program_text = "".join([item[0] for item in self.ProgramChunks]) if filepath is not None: - programfile = open(filepath, "w") - programfile.write(program_text.encode("utf-8")) + programfile = open(filepath, "w", encoding='utf-8') + programfile.write(program_text) programfile.close() self.ProgramFilePath = filepath return program_text, errors, warnings @@ -1184,7 +1181,7 @@ 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): + if reduce(lambda x, y: x and y, [x[0] == "ANY" or self.IsOfType(*x) for x in zip(inputs, block_inputs)], True): return blocktype else: if result_blocktype: @@ -1247,7 +1244,7 @@ if project is not None and words[0] in ["P", "T", "A"]: name = words[1] blocktypes = [] - for blocks in self.TotalTypesDict.itervalues(): + for blocks in self.TotalTypesDict.values(): for _sectioname, block in blocks: if block["type"] == "functionBlock": blocktypes.append(block["name"]) @@ -1302,7 +1299,7 @@ result = project.getpou(typename) if result is not None: return result - for standardlibrary in StdBlckLibs.values(): + for standardlibrary in list(StdBlckLibs.values()): result = standardlibrary.getpou(typename) if result is not None: return result @@ -1455,7 +1452,7 @@ # Return Subrange types def GetSubrangeBaseTypes(self, exclude, debug=False): - subrange_basetypes = DataTypeRange.keys() + subrange_basetypes = list(DataTypeRange.keys()) project = self.GetProject(debug) if project is not None: subrange_basetypes.extend( @@ -1970,9 +1967,9 @@ new_pos[0] -= width // 2 new_pos[1] -= height // 2 else: - new_pos = map(lambda x: x + 30, new_pos) + new_pos = [x + 30 for x in new_pos] if scaling[0] != 0 and scaling[1] != 0: - min_pos = map(lambda x: 30 / x, scaling) + min_pos = [30 / x for x in scaling] minx = round(min_pos[0]) if int(min_pos[0]) == round(min_pos[0]): minx += 1 @@ -2118,7 +2115,7 @@ self.ChangeEditedElementPouVar(tagname, old_type, old_name, new_type, new_name) elif new_name != old_name: self.ChangeEditedElementPouVar(tagname, old_type, old_name, new_type, new_name) - for param, value in infos.items(): + for param, value in list(infos.items()): if param == "name": if value != "": block.setinstanceName(value) @@ -2179,7 +2176,7 @@ variable = element.getinstance(id) if variable is None: return - for param, value in infos.items(): + for param, value in list(infos.items()): if param == "name": variable.setexpression(value) elif param == "executionOrder" and variable.getexecutionOrderId() != value: @@ -2232,7 +2229,7 @@ connection = element.getinstance(id) if connection is None: return - for param, value in infos.items(): + for param, value in list(infos.items()): if param == "name": connection.setname(value) elif param == "height": @@ -2264,7 +2261,7 @@ element = self.GetEditedElement(tagname) if element is not None: comment = element.getinstance(id) - for param, value in infos.items(): + for param, value in list(infos.items()): if param == "content": comment.setcontentText(value) elif param == "height": @@ -2291,7 +2288,7 @@ powerrail = element.getinstance(id) if powerrail is None: return - for param, value in infos.items(): + for param, value in list(infos.items()): if param == "height": powerrail.setheight(value) elif param == "width": @@ -2330,7 +2327,7 @@ contact = element.getinstance(id) if contact is None: return - for param, value in infos.items(): + for param, value in list(infos.items()): if param == "name": contact.setvariable(value) elif param == "type": @@ -2373,7 +2370,7 @@ coil = element.getinstance(id) if coil is None: return - for param, value in infos.items(): + for param, value in list(infos.items()): if param == "name": coil.setvariable(value) elif param == "type": @@ -2419,7 +2416,7 @@ step = element.getinstance(id) if step is None: return - for param, value in infos.items(): + for param, value in list(infos.items()): if param == "name": step.setname(value) elif param == "initial": @@ -2469,7 +2466,7 @@ transition = element.getinstance(id) if transition is None: return - for param, value in infos.items(): + for param, value in list(infos.items()): if param == "type" and value != "connection": transition.setconditionContent(value, infos["condition"]) elif param == "height": @@ -2529,7 +2526,7 @@ divergence = element.getinstance(id) if divergence is None: return - for param, value in infos.items(): + for param, value in list(infos.items()): if param == "height": divergence.setheight(value) elif param == "width": @@ -2580,7 +2577,7 @@ jump = element.getinstance(id) if jump is None: return - for param, value in infos.items(): + for param, value in list(infos.items()): if param == "target": jump.settargetName(value) elif param == "height": @@ -2610,7 +2607,7 @@ actionBlock = element.getinstance(id) if actionBlock is None: return - for param, value in infos.items(): + for param, value in list(infos.items()): if param == "actions": actionBlock.setactions(value) elif param == "height": diff -r 7e17f7e02a2b -r 0b3ac94f494c PLCGenerator.py --- a/PLCGenerator.py Wed Nov 29 11:54:56 2023 +0100 +++ b/PLCGenerator.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,10 +23,10 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import +from functools import cmp_to_key +from operator import eq import re from functools import reduce -from six.moves import xrange from plcopen import PLCOpenParser from plcopen.structures import * @@ -65,7 +65,7 @@ while lines[line_num][spaces] == " ": spaces += 1 indent = "" - for dummy in xrange(spaces, nb_spaces): + for dummy in range(spaces, nb_spaces): indent += " " for line in lines: if line != "": @@ -79,9 +79,9 @@ ax, ay = int(a.getx()), int(a.gety()) bx, by = int(b.getx()), int(b.gety()) if abs(ay - by) < 10: - return cmp(ax, bx) + return eq(ax, bx) else: - return cmp(ay, by) + return eq(ay, by) def JoinList(separator, mylist): @@ -264,7 +264,7 @@ # Generate a POU defined and used in text def GeneratePouProgramInText(self, text): - for pou_name in self.PouComputed.keys(): + for pou_name in list(self.PouComputed.keys()): 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) @@ -472,12 +472,12 @@ if len(self.DatatypeComputed) > 0: self.Program += [("TYPE\n", ())] # Generate every data types defined - for datatype_name in self.DatatypeComputed.keys(): + for datatype_name in list(self.DatatypeComputed.keys()): log("Generate Data Type %s"%datatype_name) self.GenerateDataType(datatype_name) self.Program += [("END_TYPE\n\n", ())] # Generate every POUs defined - for pou_name in self.PouComputed.keys(): + for pou_name in list(self.PouComputed.keys()): log("Generate POU %s"%pou_name) self.GeneratePouProgram(pou_name) if noconfig: @@ -895,7 +895,7 @@ 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(): + for var_type, connections in list(undefined.items()): related = [] for connection in connections: connection_type = self.ConnectionTypes.get(connection) @@ -984,7 +984,7 @@ orderedInstances = [] for instance in body.getcontentInstances(): if isinstance(instance, (OutVariableClass, InOutVariableClass, BlockClass)): - executionOrderId = instance.getexecutionOrderId() + executionOrderId = instance.getexecutionOrderId() or 0 # 0 if None if executionOrderId > 0: orderedInstances.append((executionOrderId, instance)) elif isinstance(instance, (OutVariableClass, InOutVariableClass)): @@ -995,9 +995,9 @@ otherInstances["connectors"].append(instance) elif isinstance(instance, CoilClass): otherInstances["outVariables&coils"].append(instance) - orderedInstances.sort() - otherInstances["outVariables&coils"].sort(SortInstances) - otherInstances["blocks"].sort(SortInstances) + orderedInstances.sort(key=lambda n: n[0]) + otherInstances["outVariables&coils"].sort(key=cmp_to_key(SortInstances)) + otherInstances["blocks"].sort(key=cmp_to_key(SortInstances)) instances = [instance for (executionOrderId, instance) in orderedInstances] instances.extend(otherInstances["outVariables&coils"] + otherInstances["blocks"] + otherInstances["connectors"]) for instance in instances: @@ -1055,7 +1055,7 @@ def FactorizePaths(self, paths): same_paths = {} - uncomputed_index = range(len(paths)) + uncomputed_index = list(range(len(paths))) factorized_paths = [] for num, path in enumerate(paths): if isinstance(path, list): @@ -1066,7 +1066,7 @@ else: factorized_paths.append(path) uncomputed_index.remove(num) - for same_path, elements in same_paths.items(): + for same_path, elements in list(same_paths.items()): if len(elements) > 1: elements_paths = self.FactorizePaths([path for path, num in elements]) if len(elements_paths) > 1: @@ -1100,7 +1100,7 @@ name = block.getinstanceName() type = block.gettypeName() - executionOrderId = block.getexecutionOrderId() + executionOrderId = block.getexecutionOrderId() or 0 # 0 if None input_variables = block.inputVariables.getvariable() output_variables = block.outputVariables.getvariable() inout_variables = {} @@ -1452,7 +1452,7 @@ def GenerateSFCStep(self, step, pou): step_name = step.getname() - if step_name not in self.SFCNetworks["Steps"].keys(): + if step_name not in list(self.SFCNetworks["Steps"].keys()): if step.getinitialStep(): self.InitialSteps.append(step_name) step_infos = {"id": step.getlocalId(), @@ -1482,7 +1482,7 @@ instances.extend(self.ExtractConvergenceInputs(transition, pou)) for instance in instances: self.GenerateSFCTransition(instance, pou) - if instance in self.SFCNetworks["Transitions"].keys(): + if instance in list(self.SFCNetworks["Transitions"].keys()): target_info = (self.TagName, "transition", instance.getlocalId(), "to", step_infos["id"]) self.SFCNetworks["Transitions"][instance]["to"].append([(step_name, target_info)]) @@ -1516,7 +1516,7 @@ instances.extend(self.ExtractConvergenceInputs(transition, pou)) for instance in instances: self.GenerateSFCTransition(instance, pou) - if instance in self.SFCNetworks["Transitions"].keys(): + if instance in list(self.SFCNetworks["Transitions"].keys()): target_info = (self.TagName, "jump", jump.getlocalId(), "target") self.SFCNetworks["Transitions"][instance]["to"].append([(jump_target, target_info)]) @@ -1530,7 +1530,7 @@ step = body.getcontentInstance(stepLocalId) self.GenerateSFCStep(step, pou) step_name = step.getname() - if step_name in self.SFCNetworks["Steps"].keys(): + if step_name in list(self.SFCNetworks["Steps"].keys()): actions = actionBlock.getactions() for i, action in enumerate(actions): action_infos = {"id": actionBlock.getlocalId(), @@ -1555,7 +1555,7 @@ self.SFCNetworks["Steps"][step_name]["actions"].append(action_infos) def GenerateSFCAction(self, action_name, pou): - if action_name not in self.SFCNetworks["Actions"].keys(): + if action_name not in list(self.SFCNetworks["Actions"].keys()): actionContent = pou.getaction(action_name) if actionContent is not None: previous_tagname = self.TagName @@ -1566,7 +1566,7 @@ self.TagName = previous_tagname def GenerateSFCTransition(self, transition, pou): - if transition not in self.SFCNetworks["Transitions"].keys(): + if transition not in list(self.SFCNetworks["Transitions"].keys()): steps = [] connections = transition.connectionPointIn.getconnections() if connections is not None and len(connections) == 1: @@ -1639,12 +1639,12 @@ for step in steps: self.GenerateSFCStep(step, pou) step_name = step.getname() - if step_name in self.SFCNetworks["Steps"].keys(): + if step_name in list(self.SFCNetworks["Steps"].keys()): transition_infos["from"].append([(step_name, (self.TagName, "transition", transition.getlocalId(), "from", step.getlocalId()))]) self.SFCNetworks["Steps"][step_name]["transitions"].append(transition) def ComputeSFCStep(self, step_name): - if step_name in self.SFCNetworks["Steps"].keys(): + if step_name in list(self.SFCNetworks["Steps"].keys()): step_infos = self.SFCNetworks["Steps"].pop(step_name) self.Program += [(self.CurrentIndent, ())] if step_infos["initial"]: @@ -1679,7 +1679,7 @@ self.ComputeSFCTransition(transition) def ComputeSFCAction(self, action_name): - if action_name in self.SFCNetworks["Actions"].keys(): + if action_name in list(self.SFCNetworks["Actions"].keys()): action_content, action_info = self.SFCNetworks["Actions"].pop(action_name) self.Program += [("%sACTION " % self.CurrentIndent, ()), (action_name, action_info), @@ -1688,7 +1688,7 @@ self.Program += [("%sEND_ACTION\n\n" % self.CurrentIndent, ())] def ComputeSFCTransition(self, transition): - if transition in self.SFCNetworks["Transitions"].keys(): + if transition in list(self.SFCNetworks["Transitions"].keys()): transition_infos = self.SFCNetworks["Transitions"].pop(transition) self.Program += [("%sTRANSITION" % self.CurrentIndent, ())] if transition_infos["priority"] is not None: diff -r 7e17f7e02a2b -r 0b3ac94f494c PLCOpenEditor.py --- a/PLCOpenEditor.py Wed Nov 29 11:54:56 2023 +0100 +++ b/PLCOpenEditor.py Thu Dec 07 22:41:32 2023 +0100 @@ -24,13 +24,14 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import print_function + + import os import sys import getopt import wx +import wx.adv import version import util.paths as paths @@ -63,7 +64,7 @@ [ ID_PLCOPENEDITORFILEMENUGENERATE, ID_PLCOPENEDITORFILEMENUGENERATEAS, -] = [wx.NewId() for _init_coll_FileMenu_Items in range(2)] +] = [wx.NewIdRef() for _init_coll_FileMenu_Items in range(2)] beremiz_dir = paths.AbsDir(__file__) @@ -73,35 +74,35 @@ 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=_('New') + '\tCTRL+N') AppendMenu(parent, help='', id=wx.ID_OPEN, - kind=wx.ITEM_NORMAL, text=_(u'Open') + '\tCTRL+O') + kind=wx.ITEM_NORMAL, text=_('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=_('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=_('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=_('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=_('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=_('Generate Program') + '\tCTRL+G') AppendMenu(parent, help='', id=ID_PLCOPENEDITORFILEMENUGENERATEAS, - kind=wx.ITEM_NORMAL, text=_(u'Generate Program As...') + '\tCTRL+SHIFT+G') + kind=wx.ITEM_NORMAL, text=_('Generate Program As...') + '\tCTRL+SHIFT+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=_('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=_('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=_('Print') + '\tCTRL+P') parent.AppendSeparator() AppendMenu(parent, help='', id=wx.ID_PROPERTIES, - kind=wx.ITEM_NORMAL, text=_(u'&Properties')) + kind=wx.ITEM_NORMAL, text=_('&Properties')) parent.AppendSeparator() AppendMenu(parent, help='', id=wx.ID_EXIT, - kind=wx.ITEM_NORMAL, text=_(u'Quit') + '\tCTRL+Q') + kind=wx.ITEM_NORMAL, text=_('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) @@ -119,16 +120,16 @@ self.Bind(wx.EVT_MENU, self.OnPropertiesMenu, id=wx.ID_PROPERTIES) self.Bind(wx.EVT_MENU, self.OnQuitMenu, id=wx.ID_EXIT) - self.AddToMenuToolBar([(wx.ID_NEW, "new", _(u'New'), None), - (wx.ID_OPEN, "open", _(u'Open'), None), - (wx.ID_SAVE, "save", _(u'Save'), None), - (wx.ID_SAVEAS, "saveas", _(u'Save As...'), None), - (wx.ID_PRINT, "print", _(u'Print'), None), - (ID_PLCOPENEDITORFILEMENUGENERATE, "Build", _(u'Generate Program'), None)]) + self.AddToMenuToolBar([(wx.ID_NEW, "new", _('New'), None), + (wx.ID_OPEN, "open", _('Open'), None), + (wx.ID_SAVE, "save", _('Save'), None), + (wx.ID_SAVEAS, "saveas", _('Save As...'), None), + (wx.ID_PRINT, "print", _('Print'), None), + (ID_PLCOPENEDITORFILEMENUGENERATE, "Build", _('Generate Program'), None)]) def _init_coll_HelpMenu_Items(self, parent): AppendMenu(parent, help='', id=wx.ID_HELP, - kind=wx.ITEM_NORMAL, text=_(u'PLCOpenEditor') + '\tF1') + kind=wx.ITEM_NORMAL, text=_('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, @@ -137,14 +138,14 @@ def handler(event): return wx.MessageBox( version.GetCommunityHelpMsg(), - _(u'Community support'), + _('Community support'), wx.OK | wx.ICON_INFORMATION) - menu_entry = parent.Append(help='', id=wx.ID_ANY, kind=wx.ITEM_NORMAL, text=_(u'Community support')) + menu_entry = parent.Append(help='', id=wx.ID_ANY, kind=wx.ITEM_NORMAL, text=_('Community support')) self.Bind(wx.EVT_MENU, handler, menu_entry) AppendMenu(parent, help='', id=wx.ID_ABOUT, - kind=wx.ITEM_NORMAL, text=_(u'About')) + kind=wx.ITEM_NORMAL, text=_('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.OnAboutMenu, id=wx.ID_ABOUT) @@ -355,6 +356,7 @@ open_pdf(os.path.join(beremiz_dir, "plcopen", "TC6_XML_V101.pdf")) def OnAboutMenu(self, event): + info = wx.adv.AboutDialogInfo() info = version.GetAboutDialogInfo() info.Name = "PLCOpenEditor" info.Description = _("PLCOpenEditor is part of Beremiz project.\n\n" @@ -422,8 +424,6 @@ self.SetAppName('plcopeneditor') self.ParseCommandLine() InstallLocalRessources(beremiz_dir) - if wx.VERSION < (3, 0, 0): - wx.InitAllImageHandlers() util.ExceptionHandler.AddExceptHook(version.app_version) self.frame = PLCOpenEditor(None, fileOpen=self.fileOpen) return True diff -r 7e17f7e02a2b -r 0b3ac94f494c POULibrary.py --- a/POULibrary.py Wed Nov 29 11:54:56 2023 +0100 +++ b/POULibrary.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,7 +23,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + from weakref import ref # Exception type for problems that user has to take action in order to fix diff -r 7e17f7e02a2b -r 0b3ac94f494c PSKManagement.py --- a/PSKManagement.py Wed Nov 29 11:54:56 2023 +0100 +++ b/PSKManagement.py Thu Dec 07 22:41:32 2023 +0100 @@ -3,7 +3,7 @@ # See COPYING file for copyrights details. -from __future__ import absolute_import + import os import time import json @@ -11,8 +11,8 @@ # PSK Management Data model : # [[ID,Desc, LastKnownURI, LastConnect]] -COL_ID, COL_URI, COL_DESC, COL_LAST = range(4) -REPLACE, REPLACE_ALL, KEEP, KEEP_ALL, CANCEL = range(5) +COL_ID, COL_URI, COL_DESC, COL_LAST = list(range(4)) +REPLACE, REPLACE_ALL, KEEP, KEEP_ALL, CANCEL = list(range(5)) def _pskpath(project_path): diff -r 7e17f7e02a2b -r 0b3ac94f494c ProjectController.py --- a/ProjectController.py Wed Nov 29 11:54:56 2023 +0100 +++ b/ProjectController.py Thu Dec 07 22:41:32 2023 +0100 @@ -27,8 +27,7 @@ Beremiz Project Controller """ - -from __future__ import absolute_import +import sys import os import traceback import time @@ -37,12 +36,11 @@ import re import tempfile import hashlib +import shutil from datetime import datetime from weakref import WeakKeyDictionary from functools import reduce -from itertools import izip -from distutils.dir_util import copy_tree -from six.moves import xrange +from collections import OrderedDict import wx @@ -75,22 +73,22 @@ def ExtractChildrenTypesFromCatalog(catalog): children_types = [] - for n, d, _h, c in catalog: - if isinstance(c, list): - children_types.extend(ExtractChildrenTypesFromCatalog(c)) + for name, displayname, _helpstr, moduleclassname in catalog: + if isinstance(moduleclassname, list): + children_types.extend(ExtractChildrenTypesFromCatalog(moduleclassname)) else: - children_types.append((n, GetClassImporter(c), d)) + children_types.append((name, GetClassImporter(moduleclassname), displayname)) return children_types def ExtractMenuItemsFromCatalog(catalog): menu_items = [] - for n, d, h, c in catalog: - if isinstance(c, list): - children = ExtractMenuItemsFromCatalog(c) + for name, displayname, helpstr, moduleclassname in catalog: + if isinstance(moduleclassname, list): + children = ExtractMenuItemsFromCatalog(moduleclassname) else: children = [] - menu_items.append((n, d, h, children)) + menu_items.append((name, displayname, helpstr, children)) return menu_items @@ -116,7 +114,7 @@ return path def findCmd(self): - cmd = "iec2c" + (".exe" if wx.Platform == '__WXMSW__' else "") + cmd = "iec2c" + (".exe" if os.name == 'nt' else "") paths = [ os.path.join(base_folder, "matiec") ] @@ -206,7 +204,7 @@ """ + "\n".join(['' + ('false' if type(default)==str or default==False else 'true') + '"/>' for libname, _lib, default in features.libraries]) + """ """) if len(features.libraries) > 0 else '') + """ @@ -282,13 +280,11 @@ self.IECcodeDigest = None self.LastBuiltIECcodeDigest = None - def __del__(self): - self.KillDebugThread() - def LoadLibraries(self): - self.Libraries = [] + self.Libraries = OrderedDict() TypeStack = [] - for libname, clsname, lib_enabled in features.libraries: + for libname, clsname, default in features.libraries: + lib_enabled = False if type(default)==str else default if self.BeremizRoot.Libraries is not None: enable_attr = getattr(self.BeremizRoot.Libraries, "Enable_" + libname + "_Library") @@ -298,7 +294,34 @@ if lib_enabled: Lib = GetClassImporter(clsname)()(self, libname, TypeStack) TypeStack.append(Lib.GetTypes()) - self.Libraries.append(Lib) + self.Libraries[libname] = Lib + + def CTNAddChild(self, CTNName, CTNType, IEC_Channel=0): + """ + Project controller applies libraries requirements when adding new CTN + """ + res = ConfigTreeNode.CTNAddChild(self, CTNName, CTNType, IEC_Channel) + + # find library associated with new CTN, if any + associated_lib = {default:libname + for libname, _clsname, default + in features.libraries}.get(CTNType, None) + + # if any, then enable it if it wasn't and inform user + if associated_lib is not None: + # FIXME: This should be done with GetParamsAttribute + # but it fails with missing optional attributes + attrname = "Enable_" + associated_lib + "_Library" + libobj = self.BeremizRoot.Libraries + lib_enabled = False if libobj is None else getattr(libobj, attrname) + if not lib_enabled: + # use SetParamsAttribute to trigger reload of libs + self.SetParamsAttribute("BeremizRoot.Libraries.Enable_" + associated_lib + "_Library", True) + msg = _("Enabled {a1} library, required by {a2} extension\n").format( + a1=associated_lib, a2=CTNType) + self.GetCTRoot().logger.write(msg) + + return res def SetAppFrame(self, frame, logger): self.AppFrame = frame @@ -373,10 +396,16 @@ return "PROJECT" def GetDefaultTargetName(self): - if wx.Platform == '__WXMSW__': + if sys.platform.startswith('linux'): + return "Linux" + elif sys.platform.startswith('darwin'): + return "OSX" + elif sys.platform.startswith('win32'): return "Win32" - else: - return "Linux" + + # Fall back to Linux as default target + return "Linux" + def GetTarget(self): target = self.BeremizRoot.getTargetType() @@ -587,7 +616,7 @@ old_projectfiles_path = self._getProjectFilesPath( from_project_path) if os.path.isdir(old_projectfiles_path): - copy_tree(old_projectfiles_path, + shutil.copytree(old_projectfiles_path, self._getProjectFilesPath(self.ProjectPath)) self.SaveXMLFile(os.path.join(self.ProjectPath, 'plc.xml')) result = self.CTNRequestSave(from_project_path) @@ -616,10 +645,10 @@ def GetLibrariesTypes(self): self.LoadLibraries() - return [lib.GetTypes() for lib in self.Libraries] + return [lib.GetTypes() for lib in self.Libraries.values()] def GetLibrariesSTCode(self): - return "\n".join([lib.GetSTCode() for lib in self.Libraries]) + return "\n".join([lib.GetSTCode() for lib in self.Libraries.values()]) def GetLibrariesCCode(self, buildpath): if len(self.Libraries) == 0: @@ -629,12 +658,12 @@ self.GetIECLibPath()) LocatedCCodeAndFlags = [] Extras = [] - for lib in self.Libraries: + for lib in self.Libraries.values(): res = lib.Generate_C(buildpath, self._VariablesList, LibIECCflags) LocatedCCodeAndFlags.append(res[:2]) if len(res) > 2: Extras.extend(res[2:]) - return map(list, zip(*LocatedCCodeAndFlags)) + [tuple(Extras)] + return list(map(list, list(zip(*LocatedCCodeAndFlags)))) + [tuple(Extras)] # Update PLCOpenEditor ConfNode Block types from loaded confnodes def RefreshConfNodesBlockLists(self): @@ -750,7 +779,7 @@ def GetConfNodeGlobalInstances(self): LibGlobals = [] - for lib in self.Libraries: + for lib in self.Libraries.values(): LibGlobals += lib.GlobalInstances() CTNGlobals = self._GlobalInstances() return LibGlobals + CTNGlobals @@ -808,8 +837,8 @@ plc_file.write(POUsIECCodeContent) hasher = hashlib.md5() - hasher.update(IECCodeContent) - hasher.update(POUsIECCodeContent) + hasher.update(IECCodeContent.encode()) + hasher.update(POUsIECCodeContent.encode()) self.IECcodeDigest = hasher.hexdigest() return True @@ -888,15 +917,13 @@ _("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 = [os.path.join(buildpath, filename) for filename in 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.remove("LOCATED_VARIABLES.h") - H_files = map( - lambda filename: os.path.join(buildpath, filename), H_files) + H_files = [os.path.join(buildpath, filename) for filename in H_files] for H_file in H_files: with open(H_file, 'r') as original: data = original.read() @@ -995,7 +1022,7 @@ for line in ListGroup[0]: # Split and Maps each field to dictionnary entries attrs = dict( - zip(ProgramsListAttributeName, line.strip().split(';'))) + list(zip(ProgramsListAttributeName, line.strip().split(';')))) # Truncate "C_path" to remove conf an resources names attrs["C_path"] = '__'.join( attrs["C_path"].split(".", 2)[1:]) @@ -1008,7 +1035,7 @@ for line in ListGroup[1]: # Split and Maps each field to dictionnary entries attrs = dict( - zip(VariablesListAttributeName, line.strip().split(';'))) + list(zip(VariablesListAttributeName, line.strip().split(';')))) # Truncate "C_path" to remove conf an resources names parts = attrs["C_path"].split(".", 2) if len(parts) > 2: @@ -1098,9 +1125,8 @@ """ # 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 = ["_".join(map(str, x)) for x in [loc for loc, _Cfiles, DoCalls in + self.LocationCFilesAndCFLAGS if loc and DoCalls]] # Generate main, based on template if not self.BeremizRoot.getDisable_Extensions(): @@ -1113,7 +1139,7 @@ "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)]), + "__publish_%s();" % locstrs[i - 1] for i in range(len(locstrs), 0, -1)]), "init_calls": "\n ".join([ "init_level=%d; " % (i + 1) + "if((res = __init_%s(argc,argv))){" % locstr + @@ -1121,7 +1147,7 @@ "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)]) + "__cleanup_%s();" % locstrs[i - 1] for i in range(len(locstrs), 0, -1)]) } else: plc_main_code = targets.GetCode("plc_main_head.c") % { @@ -1204,7 +1230,7 @@ LibCFilesAndCFLAGS, LibLDFLAGS, LibExtraFiles = self.GetLibrariesCCode( buildpath) except UserAddressedException as e: - self.logger.write_error(e.message) + self.logger.write_error(str(e)) return False except Exception as e: self.logger.write_error( @@ -1218,7 +1244,7 @@ buildpath, self.PLCGeneratedLocatedVars) except UserAddressedException as e: - self.logger.write_error(e.message) + self.logger.write_error(str(e)) return False except Exception: self.logger.write_error( @@ -1226,6 +1252,13 @@ self.logger.write_error(traceback.format_exc()) return False + # Extensions also need plcCFLAGS in case they include beremiz.h + CTNLocationCFilesAndCFLAGS = [ + (loc, [ + (code, self.plcCFLAGS+" "+cflags) + for code,cflags in code_and_cflags], do_calls) + for loc, code_and_cflags, do_calls in CTNLocationCFilesAndCFLAGS] + self.LocationCFilesAndCFLAGS = LibCFilesAndCFLAGS + \ CTNLocationCFilesAndCFLAGS self.LDFLAGS = CTNLDFLAGS + LibLDFLAGS @@ -1322,7 +1355,7 @@ def _OpenView(self, name=None, onlyopened=False): if name == "IEC code": - if self._IECCodeView is None: + if not self._IECCodeView: plc_file = self._getIECcodepath() self._IECCodeView = IECCodeViewer( @@ -1344,7 +1377,7 @@ return self._IECCodeView elif name == "IEC raw code": - if self._IECRawCodeView is None: + if not self._IECRawCodeView: controler = MiniTextControler(self._getIECrawcodepath(), self) self._IECRawCodeView = IECCodeViewer( @@ -1361,7 +1394,7 @@ return self._IECRawCodeView elif name == "Project Files": - if self._ProjectFilesView is None: + if not self._ProjectFilesView: self._ProjectFilesView = FileManagementPanel( self.AppFrame.TabsOpened, self, name, self._getProjectFilesPath(), True) @@ -1388,9 +1421,9 @@ if editor_name == "": if len(editors) == 1: - editor_name = editors.keys()[0] + editor_name = list(editors.keys())[0] elif len(editors) > 0: - names = editors.keys() + names = list(editors.keys()) dialog = wx.SingleChoiceDialog( self.AppFrame, _("Select an editor:"), @@ -1427,7 +1460,7 @@ self._IECRawCodeView = None if self._ProjectFilesView == view: self._ProjectFilesView = None - if view in self._FileEditors.values(): + if view in list(self._FileEditors.values()): self._FileEditors.pop(view.GetFilePath()) def _Clean(self): @@ -1499,21 +1532,17 @@ allmethods = self.DefaultMethods.copy() allmethods.update( self.MethodsFromStatus.get(status, {})) - for method, active in allmethods.items(): + for method, active in list(allmethods.items()): self.ShowMethod(method, active) self.previous_plcstate = status if self.AppFrame is not None: updated = True self.AppFrame.RefreshStatusToolBar() - if status == PlcStatus.Disconnected: - self.AppFrame.ConnectionStatusBar.SetStatusText( - _(status), 1) - self.AppFrame.ConnectionStatusBar.SetStatusText('', 2) - else: - self.AppFrame.ConnectionStatusBar.SetStatusText( - _("Connected to URI: %s") % self.BeremizRoot.getURI_location().strip(), 1) - self.AppFrame.ConnectionStatusBar.SetStatusText( - _(status), 2) + texts = [_(PlcStatus.Disconnected), ''] \ + if status == PlcStatus.Disconnected or self._connector is None else \ + [_("Connected to URI: %s") % self.BeremizRoot.getURI_location().strip(), _(status)] + for i,txt in enumerate(texts): + self.AppFrame.ConnectionStatusBar.SetStatusText(txt, i+1) return updated def ShowPLCProgress(self, status="", progress=0): @@ -1526,7 +1555,8 @@ # clear previous_plcstate to restore status # in UpdateMethodsFromPLCStatus() self.previous_plcstate = "" - self.AppFrame.ProgressStatusBar.Hide() + if self.AppFrame is not None: + self.AppFrame.ProgressStatusBar.Hide() self.UpdateMethodsFromPLCStatus() def PullPLCStatusProc(self, event): @@ -1544,7 +1574,7 @@ debug_vars = UnpackDebugBuffer( debug_buff, self.TracedIECTypes) if debug_vars is not None: - for IECPath, values_buffer, value in izip( + for IECPath, values_buffer, value in zip( self.TracedIECPath, self.DebugValuesBuffers, debug_vars): @@ -1570,7 +1600,7 @@ buffers, self.DebugValuesBuffers = (self.DebugValuesBuffers, - [list() for dummy in xrange(len(self.TracedIECPath))]) + [list() for dummy in range(len(self.TracedIECPath))]) ticks, self.DebugTicks = self.DebugTicks, [] @@ -1595,7 +1625,7 @@ self.TracedIECTypes = [] if self._connector is not None and self.debug_status != PlcStatus.Broken: IECPathsToPop = [] - for IECPath, data_tuple in self.IECdebug_datas.iteritems(): + for IECPath, data_tuple in self.IECdebug_datas.items(): WeakCallableDict, _data_log, _status, fvalue, _buffer_list = data_tuple if len(WeakCallableDict) == 0: # Callable Dict is empty. @@ -1619,10 +1649,10 @@ if Idxs: Idxs.sort() - IdxsT = zip(*Idxs) + IdxsT = list(zip(*Idxs)) self.TracedIECPath = IdxsT[3] self.TracedIECTypes = IdxsT[1] - res = self._connector.SetTraceVariablesList(zip(*IdxsT[0:3])) + res = self._connector.SetTraceVariablesList(list(zip(*IdxsT[0:3]))) if res is not None and res > 0: self.DebugToken = res else: @@ -1687,7 +1717,7 @@ else: IECdebug_data[4] = reduce( lambda x, y: x | y, - IECdebug_data[0].itervalues(), + iter(IECdebug_data[0].values()), False) self.AppendDebugUpdate() @@ -1724,7 +1754,7 @@ 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(): + for weakcallable, buffer_list in WeakCallableDict.items(): function = getattr(weakcallable, function_name, None) if function is not None: # FIXME: apparently, despite of weak ref objects, @@ -1790,13 +1820,16 @@ """ Start PLC """ + success = False if self.GetIECProgramsAndVariables(): self._connector.StartPLC() self.logger.write(_("Starting PLC\n")) self._connect_debug() + success = True else: self.logger.write_error(_("Couldn't start PLC !\n")) wx.CallAfter(self.UpdateMethodsFromPLCStatus) + return success def _Stop(self): """ @@ -1810,6 +1843,10 @@ wx.CallAfter(self.UpdateMethodsFromPLCStatus) + def StartLocalRuntime(self): + if self.AppFrame: + return self.AppFrame.StartLocalRuntime() + def _SetConnector(self, connector, update_status=True): self._connector = connector if self.AppFrame is not None: @@ -1826,6 +1863,7 @@ wx.CallAfter(self.UpdateMethodsFromPLCStatus) def _Connect(self): + success = False # don't accept re-connetion if already connected if self._connector is not None: self.logger.write_error( @@ -1887,6 +1925,8 @@ else: self.logger.write_warning( _("Debug does not match PLC - stop/transfert/start to re-enable\n")) + success = True + return success def CompareLocalAndRemotePLC(self): if self._connector is None: @@ -1910,6 +1950,7 @@ self._SetConnector(None) def _Transfer(self): + success = False if self.IsPLCStarted(): dialog = wx.MessageDialog( self.AppFrame, @@ -1972,16 +2013,19 @@ if self.GetIECProgramsAndVariables(): self.UnsubscribeAllDebugIECVariable() self.ProgramTransferred() - self.AppFrame.CloseObsoleteDebugTabs() - self.AppFrame.RefreshPouInstanceVariablesPanel() - self.AppFrame.LogViewer.ResetLogCounters() + if self.AppFrame is not None: + self.AppFrame.CloseObsoleteDebugTabs() + self.AppFrame.RefreshPouInstanceVariablesPanel() + self.AppFrame.LogViewer.ResetLogCounters() self.logger.write(_("PLC installed successfully.\n")) + success = True else: self.logger.write_error(_("Missing debug data\n")) else: self.logger.write_error(_("PLC couldn't be installed\n")) wx.CallAfter(self.UpdateMethodsFromPLCStatus) + return success def _Repair(self): dialog = wx.MessageDialog( diff -r 7e17f7e02a2b -r 0b3ac94f494c README.md --- a/README.md Wed Nov 29 11:54:56 2023 +0100 +++ b/README.md Thu Dec 07 22:41:32 2023 +0100 @@ -1,4 +1,7 @@ + +[![CI Automated testing](https://github.com/beremiz/beremiz/actions/workflows/run_tests_in_docker.yml/badge.svg?branch=python3)](https://github.com/beremiz/beremiz/actions/workflows/run_tests_in_docker.yml) # Beremiz # @@ -10,109 +13,200 @@ Beremiz consists of two components: -* Integrated Development Environment (IDE), [Beremiz.py](https://bitbucket.org/automforge/beremiz/src/tip/Beremiz.py?at=default). It's running on user's computer and is used to write/compile/debug PLC programs and control PLC runtime. -* Reference runtime implementation in python, [Beremiz_service.py](https://bitbucket.org/automforge/beremiz/src/tip/Beremiz_service.py?at=default). It's running on target platform, communicates with I/O and executes PLC program. +* Integrated Development Environment (IDE), Beremiz.py. It is running on user's computer and is used to write/compile/debug PLC programs and control PLC runtime. +* Reference runtime implementation in python, Beremiz_service.py. It's running on target platform, communicates with I/O and executes PLC program. See official [Beremiz website](http://www.beremiz.org/) for more information. -## Build on Linux ## - -* Prerequisites - - # Ubuntu/Debian : - sudo apt-get install build-essential bison flex autoconf - sudo apt-get install python-wxgtk3.0 pyro mercurial - sudo apt-get install python-nevow python-matplotlib python-lxml python-zeroconf python-cycler - sudo apt-get install python-autobahn python-u-msgpack - - sudo apt-get install libpython2.7-dev - pip2 install --user sslpsk posix_spawn - -* Prepare - - mkdir ~/Beremiz - cd ~/Beremiz - -* Get Source Code - - cd ~/Beremiz - hg clone https://bitbucket.org/automforge/beremiz - hg clone https://bitbucket.org/automforge/matiec - -* Build MatIEC compiler - - cd ~/Beremiz/matiec - autoreconf -i - ./configure - make - -* Build CanFestival (optional) - Only needed for CANopen support. Please read CanFestival manual to choose CAN interface other than 'virtual'. - - cd ~/Beremiz - hg clone http://dev.automforge.net/CanFestival-3 - cd ~/Beremiz/CanFestival-3 - ./configure --can=virtual - make - -* Build Modbus library (optional) - Only needed for Modbus support. - - cd ~/Beremiz - hg clone https://bitbucket.org/mjsousa/modbus Modbus - cd ~/Beremiz/Modbus - make - -* Build BACnet (optional) - Only needed for BACnet support. - - cd ~/Beremiz - svn checkout https://svn.code.sf.net/p/bacnet/code/trunk/bacnet-stack/ BACnet - cd BACnet - make MAKE_DEFINE='-fPIC' MY_BACNET_DEFINES='-DPRINT_ENABLED=1 -DBACAPP_ALL -DBACFILE -DINTRINSIC_REPORTING -DBACNET_TIME_MASTER -DBACNET_PROPERTY_LISTS=1 -DBACNET_PROTOCOL_REVISION=16' library - - -* Launch Beremiz IDE - - cd ~/Beremiz/beremiz - python Beremiz.py +## Install latest release ## + +Windows installer and Snap package for Linux are available in [Github releases](https://github.com/beremiz/beremiz/releases) and [Snapcraft's store](https://snapcraft.io/beremiz) + +## Tutorials and examples ## + +In IDE, find menu "File>Tutorials and examples" to quickly open examples that should run as-is. + +There are more examples in `tests/projects` and `exemples` directories. + +Some example and test are shown on [Beremiz youtube channel](https://www.youtube.com/channel/UCcE4KYI0p1f6CmSwtzyg-ZA). + +## Development with Beremiz ## + +Developers are invited to subscribe to [mailing list](https://sourceforge.net/p/beremiz/mailman/beremiz-devel/) (beremiz-devel@lists.sourceforge.net). + +The list is moderated and requires subscription before posting. + +To subscribe to the mailing list go [here](https://sourceforge.net/p/beremiz/mailman/beremiz-devel/). + +Searchable archive using search engine of your choice is available [here](http://beremiz-devel.2374573.n4.nabble.com/). + +## Build on Linux (developer setup) ## + +### System prerequisites (Ubuntu 22.04) : +``` +# install required system packages as root +sudo apt-get install \ + build-essential automake flex bison mercurial \ + libgtk-3-dev libgl1-mesa-dev libglu1-mesa-dev \ + libpython3.10-dev libssl-dev \ + python3.10 virtualenv cmake git mercurial +``` + + +### Prepare build directory + +All commands hereafter assume that selected directory to contain all downloaded source code and build results is `~/Beremiz` + +``` +mkdir ~/Beremiz +cd ~/Beremiz +``` + +### Get Source Code (Mercurial) + +``` +cd ~/Beremiz +hg clone https://hg.beremiz.org/beremiz +hg clone https://hg.beremiz.org/matiec +``` + +### Get Source Code (Git) + +``` +cd ~/Beremiz +git clone https://github.com/beremiz/beremiz +git clone https://github.com/beremiz/matiec +``` + +### Python prerequisites (virtualenv) : +``` +# setup isolated python environment +virtualenv ~/Beremiz/venv + +# install required python packages +~/Beremiz/venv/bin/pip install -r ~/Beremiz/beremiz/requirements.txt + +``` + +### Build MatIEC compiler + +``` +cd ~/Beremiz/matiec +autoreconf -i +./configure +make +``` + +### Build CanFestival (optional) + +Only needed for CANopen support. Please read CanFestival manual to choose CAN interface other than `virtual`. + +``` +cd ~/Beremiz + +hg clone http://hg.beremiz.org/canfestival +# -- or -- +git clone https://github.com/beremiz/canfestival + +cd ~/Beremiz/canfestival +./configure --can=virtual +make +``` + +### Build Modbus library (optional) + +Only needed for Modbus support. + +``` +cd ~/Beremiz + +hg clone http://hg.beremiz.org/Modbus +# -- or -- +git clone https://github.com/beremiz/Modbus + +cd ~/Beremiz/Modbus +make +``` + +### Build BACnet (optional) + +Only needed for BACnet support. + +``` +cd ~/Beremiz +svn checkout https://svn.code.sf.net/p/bacnet/code/trunk/bacnet-stack/ BACnet +cd BACnet +make MAKE_DEFINE='-fPIC' MY_BACNET_DEFINES='-DPRINT_ENABLED=1 -DBACAPP_ALL -DBACFILE -DINTRINSIC_REPORTING -DBACNET_TIME_MASTER -DBACNET_PROPERTY_LISTS=1 -DBACNET_PROTOCOL_REVISION=16' library +``` + +### Launch Beremiz IDE + +``` +~/Beremiz/venv/python ~/Beremiz/beremiz/Beremiz.py +``` ## Run standalone Beremiz runtime ## -Runtime implementation can be different on different platforms. -For example, PLC used Cortex-M most likely would have C-based runtime. Beremiz project contains reference implementation in python, that can be easily run on GNU/Linux, Windows and Mac OS X. -This section will describe how to run it. - -If project's URL is 'LOCAL://', then IDE launches temprorary instance of Beremiz python runtime (Beremiz_service.py) localy as user tries to connect to PLC. This allows to debug programs localy without PLC. - -If you want to run Beremiz_service.py as standalone service, then follow these instructions: - * Start standalone Beremiz service - cd ~/Beremiz - mkdir beremiz_workdir - cd ~/beremiz - python Beremiz_service.py -p 61194 -i localhost -x 0 -a 1 ~/Beremiz/beremiz_workdir - -* Launch Beremiz IDE - - cd ~/Beremiz/beremiz - python Beremiz.py - -* Open/Create PLC project in Beremiz IDE. - Enter target location URI in project's settings (project->Config->BeremizRoot/URI_location) pointed to your running Beremiz service (For example, PYRO://127.0.0.1:61194). - Save project and connect to running Beremiz service. - -## Examples ## - -Almost for all functionality exists example in ['tests'](https://bitbucket.org/automforge/beremiz/src/tip/tests/?at=default) directory. -Most of examples are shown on [Beremiz youtube channel](https://www.youtube.com/channel/UCcE4KYI0p1f6CmSwtzyg-ZA). +``` +mkdir ~/beremiz_runtime_workdir +~/Beremiz/venv/python ~/Beremiz/beremiz/Beremiz_service.py -p 61194 -i localhost -x 0 -a 1 ~/beremiz_runtime_workdir +``` + +To connect IDE with runtime, enter target location URI in project's settings (project->Config->BeremizRoot/URI_location) pointed to your running Beremiz service in this case : + +``` +PYRO://127.0.0.1:61194 +``` + +If project's URL is 'LOCAL://', then IDE launches on demand a local instance of Beremiz python runtime working on a temporary directory. + +## Build documentation + +Source code for documentation is stored in `doc` directory in project's source tree. +It's written in reStructuredText (ReST) and uses Sphinx to generate documentation in different formats. + +To build documentation you need following packages on Ubuntu/Debian: + +``` +sudo apt-get install build-essential python-sphynx +``` + +### Documentation in HTML + +Build documentation + +``` +cd ~/Beremiz/doc +make all +``` + +Result documentation is stored in directories `doc/_build/dirhtml*`. + +### Documentation in PDF + +To build pdf documentation you have to install additional packages on Ubuntu/Debian: + +``` +sudo apt-get install textlive-latex-base texlive-latex-recommended \ + texlive-fonts-recommended texlive-latex-extra +``` + +Build documentation + +``` +cd ~/Beremiz/doc +make latexpdf +``` + +Result documentation is stored in `doc/_build/latex/Beremiz.pdf`. ## Documentation ## * See [Beremiz youtube channel](https://www.youtube.com/channel/UCcE4KYI0p1f6CmSwtzyg-ZA) to get quick information how to use Beremiz IDE. - - * [Official user manual](http://beremiz.readthedocs.io/) is built from sources in doc directory. + + * [Official documentation](http://beremiz.readthedocs.io/) is built from sources in doc directory. Documentation does not cover all aspects of Beremiz use yet. Contribution are very welcome! @@ -126,12 +220,3 @@ * See official [Beremiz website](http://www.beremiz.org/) for more information. -## Support and development ## - -Main community support channel is [mailing list](https://sourceforge.net/p/beremiz/mailman/beremiz-devel/) (beremiz-devel@lists.sourceforge.net). - -The list is moderated and requires subscription for posting to it. - -To subscribe to the mailing list go [here](https://sourceforge.net/p/beremiz/mailman/beremiz-devel/). - -Searchable archive using search engine of your choice is available [here](http://beremiz-devel.2374573.n4.nabble.com/). \ No newline at end of file diff -r 7e17f7e02a2b -r 0b3ac94f494c XSLTransform.py --- a/XSLTransform.py Wed Nov 29 11:54:56 2023 +0100 +++ b/XSLTransform.py Thu Dec 07 22:41:32 2023 +0100 @@ -3,7 +3,7 @@ # This file is part of Beremiz. # See COPYING file for copyrights details. -from __future__ import absolute_import + from lxml import etree class XSLTransform(object): @@ -18,7 +18,7 @@ extensions={("beremiz", name): call for name, call in xsltext}) def transform(self, root, profile_run=False, **kwargs): - res = self.xslt(root, profile_run=profile_run, **{k: etree.XSLT.strparam(v) for k, v in kwargs.iteritems()}) + res = self.xslt(root, profile_run=profile_run, **{k: etree.XSLT.strparam(v) for k, v in kwargs.items()}) # print(self.xslt.error_log) return res diff -r 7e17f7e02a2b -r 0b3ac94f494c bacnet/BacnetSlaveEditor.py --- a/bacnet/BacnetSlaveEditor.py Wed Nov 29 11:54:56 2023 +0100 +++ b/bacnet/BacnetSlaveEditor.py Thu Dec 07 22:41:32 2023 +0100 @@ -367,8 +367,7 @@ "Engineering Units": {"GridCellEditor": wx.grid.GridCellChoiceEditor, # use string renderer with choice editor! "GridCellRenderer": wx.grid.GridCellStringRenderer, - # syntax for GridCellChoiceEditor -> comma separated values - "GridCellEditorParam": ','.join([x[0] for x in BACnetEngineeringUnits])} + "GridCellEditorConstructorArgs": [x[0] for x in BACnetEngineeringUnits]} } # obj_properties should be a dictionary, with keys "Object Identifier", @@ -576,7 +575,10 @@ PropertyName = self.BACnetObjectType.PropertyNames[col] PropertyConfig = self.BACnetObjectType.PropertyConfig[PropertyName] grid.SetReadOnly(row, col, False) - grid.SetCellEditor(row, col, PropertyConfig["GridCellEditor"]()) + GridCellEditorConstructorArgs = \ + PropertyConfig["GridCellEditorConstructorArgs"] \ + if "GridCellEditorConstructorArgs" in PropertyConfig else [] + grid.SetCellEditor(row, col, PropertyConfig["GridCellEditor"](*GridCellEditorConstructorArgs)) grid.SetCellRenderer(row, col, PropertyConfig["GridCellRenderer"]()) grid.SetCellBackgroundColour(row, col, wx.WHITE) grid.SetCellTextColour(row, col, wx.BLACK) @@ -816,7 +818,7 @@ self, bitmap=GetBitmap(bitmap), size=wx.Size(28, 28), style=wx.NO_BORDER) - button.SetToolTipString(help) + button.SetToolTip(help) setattr(self, name, button) controls_sizer.Add(button) @@ -826,7 +828,7 @@ # use only to enable drag'n'drop # self.VariablesGrid.SetDropTarget(VariableDropTarget(self)) self.VariablesGrid.Bind( - wx.grid.EVT_GRID_CELL_CHANGE, self.OnVariablesGridCellChange) + wx.grid.EVT_GRID_CELL_CHANGING, 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.Add(self.VariablesGrid, flag=wx.GROW) diff -r 7e17f7e02a2b -r 0b3ac94f494c bacnet/__init__.py --- a/bacnet/__init__.py Wed Nov 29 11:54:56 2023 +0100 +++ b/bacnet/__init__.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,6 +23,6 @@ # used in safety-critical situations without a full and competent review. -from __future__ import absolute_import + from bacnet.bacnet import * diff -r 7e17f7e02a2b -r 0b3ac94f494c bacnet/bacnet.py --- a/bacnet/bacnet.py Wed Nov 29 11:54:56 2023 +0100 +++ b/bacnet/bacnet.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,7 +23,7 @@ # This code is made available on the understanding that it will not be # used in safety-critical situations without a full and competent review. -from __future__ import absolute_import + import os from collections import Counter diff -r 7e17f7e02a2b -r 0b3ac94f494c bitbucket-pipelines.yml --- a/bitbucket-pipelines.yml Wed Nov 29 11:54:56 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ -image: skvorl/beremiz-requirements - -pipelines: - default: - - parallel: - - step: - name: Codestyle checks - script: - - ln -s /CanFestival-3 $BITBUCKET_CLONE_DIR/../CanFestival-3 - - /usr/bin/python --version - - ./tests/tools/check_source.sh - - step: - name: Application tests - max-time: 10 - script: - - ln -s /CanFestival-3 $BITBUCKET_CLONE_DIR/../CanFestival-3 - - ./tests/tools/run_python_tests.sh - \ No newline at end of file diff -r 7e17f7e02a2b -r 0b3ac94f494c c_ext/CFileEditor.py --- a/c_ext/CFileEditor.py Wed Nov 29 11:54:56 2023 +0100 +++ b/c_ext/CFileEditor.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,7 +23,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx.stc as stc from controls.CustomStyledTextCtrl import faces diff -r 7e17f7e02a2b -r 0b3ac94f494c c_ext/__init__.py --- a/c_ext/__init__.py Wed Nov 29 11:54:56 2023 +0100 +++ b/c_ext/__init__.py Thu Dec 07 22:41:32 2023 +0100 @@ -22,5 +22,5 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + from c_ext.c_ext import * diff -r 7e17f7e02a2b -r 0b3ac94f494c c_ext/c_ext.py --- a/c_ext/c_ext.py Wed Nov 29 11:54:56 2023 +0100 +++ b/c_ext/c_ext.py Thu Dec 07 22:41:32 2023 +0100 @@ -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. -from __future__ import absolute_import + import os from c_ext.CFileEditor import CFileEditor diff -r 7e17f7e02a2b -r 0b3ac94f494c canfestival/NetworkEditor.py --- a/canfestival/NetworkEditor.py Wed Nov 29 11:54:56 2023 +0100 +++ b/canfestival/NetworkEditor.py Thu Dec 07 22:41:32 2023 +0100 @@ -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. -from __future__ import absolute_import + import wx from networkeditortemplate import NetworkEditorTemplate @@ -68,7 +68,7 @@ main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(0) - main_sizer.AddWindow(self.NetworkNodes, 0, border=5, flag=wx.GROW | wx.ALL) + main_sizer.Add(self.NetworkNodes, 0, border=5, flag=wx.GROW | wx.ALL) self.NetworkEditor.SetSizer(main_sizer) diff -r 7e17f7e02a2b -r 0b3ac94f494c canfestival/SlaveEditor.py --- a/canfestival/SlaveEditor.py Wed Nov 29 11:54:56 2023 +0100 +++ b/canfestival/SlaveEditor.py Thu Dec 07 22:41:32 2023 +0100 @@ -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. -from __future__ import absolute_import + import wx from subindextable import EditingPanel diff -r 7e17f7e02a2b -r 0b3ac94f494c canfestival/__init__.py --- a/canfestival/__init__.py Wed Nov 29 11:54:56 2023 +0100 +++ b/canfestival/__init__.py Thu Dec 07 22:41:32 2023 +0100 @@ -22,5 +22,5 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + from canfestival.canfestival import * diff -r 7e17f7e02a2b -r 0b3ac94f494c canfestival/canfestival.py --- a/canfestival/canfestival.py Wed Nov 29 11:54:56 2023 +0100 +++ b/canfestival/canfestival.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,8 +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. -from __future__ import absolute_import -from __future__ import division + + import os import sys import shutil @@ -352,7 +352,7 @@ def GetVariableLocationTree(self): current_location = self.GetCurrentLocation() - nodeindexes = self.SlaveNodes.keys() + nodeindexes = list(self.SlaveNodes.keys()) nodeindexes.sort() children = [] children += [GetSlaveLocationTree(self.Manager.GetCurrentNodeCopy(), diff -r 7e17f7e02a2b -r 0b3ac94f494c canfestival/config_utils.py --- a/canfestival/config_utils.py Wed Nov 29 11:54:56 2023 +0100 +++ b/canfestival/config_utils.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,12 +23,10 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import print_function import os import sys import getopt -from past.builtins import long +from functools import reduce # Translation between IEC types and Can Open types IECToCOType = { @@ -63,7 +61,7 @@ VariableIncrement = 0x100 VariableStartIndex = {TPDO: 0x2000, RPDO: 0x4000} VariableDirText = {TPDO: "__I", RPDO: "__Q"} -VariableTypeOffset = dict(zip(["", "X", "B", "W", "D", "L"], range(6))) +VariableTypeOffset = dict(list(zip(["", "X", "B", "W", "D", "L"], list(range(6))))) TrashVariables = [(1, 0x01), (8, 0x05), (16, 0x06), (32, 0x07), (64, 0x1B)] @@ -85,7 +83,7 @@ """ 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 = [data[i:i+2] for i in range(0, len(data), 2)] list_car.reverse() return "".join([chr(int(car, 16)) for car in list_car]) @@ -170,7 +168,7 @@ # Dictionary of location informations classed by name self.MasterMapping = {} # List of COB IDs available - self.ListCobIDAvailable = range(0x180, 0x580) + self.ListCobIDAvailable = list(range(0x180, 0x580)) # Dictionary of mapping value where unexpected variables are stored self.TrashVariables = {} # Dictionary of pointed variables @@ -303,7 +301,7 @@ values = self.NodeList.GetSlaveNodeEntry(nodeid, index + 0x200) 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): + if reduce(lambda x, y: x and y, [x == 0 for x in values[1:]], True): cobid = self.NodeList.GetSlaveNodeEntry(nodeid, index, 1) # If no COB ID defined in PDO, generate a new one (not used) if cobid == 0: @@ -374,7 +372,7 @@ nodeid, index, subindex = loc[:3] # Check Id is in slave node list - if nodeid not in self.NodeList.SlaveNodes.keys(): + if nodeid not in list(self.NodeList.SlaveNodes.keys()): raise PDOmappingException( _("Non existing node ID : {a1} (variable {a2})"). format(a1=nodeid, a2=name)) @@ -430,7 +428,7 @@ # Search for locations already mapped # ------------------------------------------------------------------------------- - for name, locationinfos in self.IECLocations.items(): + for name, locationinfos in list(self.IECLocations.items()): node = self.NodeList.SlaveNodes[locationinfos["nodeid"]]["Node"] # Search if slave has a PDO mapping this locations @@ -441,7 +439,7 @@ cobid = self.NodeList.GetSlaveNodeEntry(locationinfos["nodeid"], index - 0x200, 1) # Add PDO to MasterMapping - if cobid not in self.MasterMapping.keys(): + if cobid not in list(self.MasterMapping.keys()): # Verify that PDO transmit type is conform to sync_TPDOs transmittype = self.NodeList.GetSlaveNodeEntry(locationinfos["nodeid"], index - 0x200, 2) if sync_TPDOs and transmittype != 0x01 or transmittype != 0xFF: @@ -474,7 +472,7 @@ else: # Add location to those that haven't been mapped yet - if locationinfos["nodeid"] not in self.LocationsNotMapped.keys(): + if locationinfos["nodeid"] not in list(self.LocationsNotMapped.keys()): self.LocationsNotMapped[locationinfos["nodeid"]] = {TPDO: [], RPDO: []} self.LocationsNotMapped[locationinfos["nodeid"]][locationinfos["pdotype"]].append((name, locationinfos)) @@ -482,7 +480,7 @@ # Build concise DCF for the others locations # ------------------------------------------------------------------------------- - for nodeid, locations in self.LocationsNotMapped.items(): + for nodeid, locations in list(self.LocationsNotMapped.items()): node = self.NodeList.SlaveNodes[nodeid]["Node"] # Initialize number of params and data to add to node DCF @@ -531,7 +529,7 @@ # ------------------------------------------------------------------------------- # Generate Master's Configuration from informations stored in MasterMapping - for cobid, pdo_infos in self.MasterMapping.items(): + for cobid, pdo_infos in list(self.MasterMapping.items()): # Get next PDO index in MasterNode for this PDO type current_idx = self.CurrentPDOParamsIdx[pdo_infos["type"]] @@ -572,7 +570,7 @@ continue new_index = False - if isinstance(variable, (int, long)): + if isinstance(variable, int): # If variable is an integer then variable is unexpected self.MasterNode.SetEntry(current_idx + 0x200, subindex, self.TrashVariables[variable]) else: @@ -735,7 +733,7 @@ # Extract workspace base folder base_folder = sys.path[0] - for i in xrange(3): + for i in range(3): 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")) diff -r 7e17f7e02a2b -r 0b3ac94f494c connectors/ConnectorBase.py --- a/connectors/ConnectorBase.py Wed Nov 29 11:54:56 2023 +0100 +++ b/connectors/ConnectorBase.py Thu Dec 07 22:41:32 2023 +0100 @@ -3,7 +3,7 @@ # See COPYING file for copyrights details. -from __future__ import absolute_import + import hashlib from runtime import PlcStatus @@ -22,8 +22,8 @@ def BlobFromFile(self, filepath, seed): s = hashlib.new('md5') - s.update(seed) - blobID = self.SeedBlob(seed) + s.update(seed.encode()) + blobID = self.SeedBlob(seed.encode()) with open(filepath, "rb") as f: while blobID == s.digest(): chunk = f.read(self.chuncksize) diff -r 7e17f7e02a2b -r 0b3ac94f494c connectors/PYRO/PSK_Adapter.py --- a/connectors/PYRO/PSK_Adapter.py Wed Nov 29 11:54:56 2023 +0100 +++ b/connectors/PYRO/PSK_Adapter.py Thu Dec 07 22:41:32 2023 +0100 @@ -28,8 +28,8 @@ but using Pre Shared Keys instead of Certificates """ -from __future__ import absolute_import -from __future__ import print_function + + import socket import re diff -r 7e17f7e02a2b -r 0b3ac94f494c connectors/PYRO/__init__.py --- a/connectors/PYRO/__init__.py Wed Nov 29 11:54:56 2023 +0100 +++ b/connectors/PYRO/__init__.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,32 +23,21 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import print_function -import traceback from time import sleep import copy import socket import os.path -import Pyro -import Pyro.core -import Pyro.util -from Pyro.errors import PyroError +import Pyro5 +import Pyro5.client +import Pyro5.errors -import PSKManagement as PSK -from connectors.PYRO.PSK_Adapter import setupPSKAdapter +# TODO: PSK + +import importlib -def switch_pyro_adapter(use_ssl): - """ - Reloads Pyro module with new settings. - This is workaround for Pyro, because it doesn't work with SSL wrapper. - """ - # Pyro.config.PYRO_BROKEN_MSGWAITALL = use_ssl - reload(Pyro.protocol) - if use_ssl: - setupPSKAdapter() +Pyro5.config.SERIALIZER = "msgpack" def PYRO_connector_factory(uri, confnodesroot): @@ -58,34 +47,21 @@ confnodesroot.logger.write(_("PYRO connecting to URI : %s\n") % uri) scheme, location = uri.split("://") - use_ssl = scheme == "PYROS" - switch_pyro_adapter(use_ssl) - if use_ssl: - schemename = "PYROLOCPSK" - url, ID = location.split('#') # TODO fix exception when # not found - # load PSK from project - secpath = os.path.join(str(confnodesroot.ProjectPath), 'psk', ID+'.secret') - if not os.path.exists(secpath): - confnodesroot.logger.write_error( - 'Error: Pre-Shared-Key Secret in %s is missing!\n' % secpath) - return None - secret = open(secpath).read().partition(':')[2].rstrip('\n\r') - Pyro.config.PYROPSK = (secret, ID) - # strip ID from URL, so that pyro can understand it. - location = url - else: - schemename = "PYROLOC" + + # TODO: use ssl + + schemename = "PYRO" # Try to get the proxy object try: - RemotePLCObjectProxy = Pyro.core.getAttrProxyForURI(schemename + "://" + location + "/PLCObject") + RemotePLCObjectProxy = Pyro5.client.Proxy(f"{schemename}:PLCObject@{location}") except Exception as e: confnodesroot.logger.write_error( _("Connection to {loc} failed with exception {ex}\n").format( loc=location, ex=str(e))) return None - RemotePLCObjectProxy.adapter.setTimeout(60) + RemotePLCObjectProxy._pyroTimeout = 60 class MissingCallException(Exception): pass @@ -98,16 +74,15 @@ def catcher_func(*args, **kwargs): try: return func(*args, **kwargs) - except Pyro.errors.ConnectionClosedError as e: + except Pyro5.errors.ConnectionClosedError as e: confnodesroot._SetConnector(None) confnodesroot.logger.write_error(_("Connection lost!\n")) - except Pyro.errors.ProtocolError as e: + except Pyro5.errors.ProtocolError as e: confnodesroot.logger.write_error(_("Pyro exception: %s\n") % e) except MissingCallException as e: confnodesroot.logger.write_warning(_("Remote call not supported: %s\n") % e.message) except Exception as e: - # confnodesroot.logger.write_error(traceback.format_exc()) - errmess = ''.join(Pyro.util.getPyroTraceback(e)) + errmess = ''.join(Pyro5.errors.get_pyro_traceback()) confnodesroot.logger.write_error(errmess + "\n") print(errmess) confnodesroot._SetConnector(None) diff -r 7e17f7e02a2b -r 0b3ac94f494c connectors/PYRO_dialog.py --- a/connectors/PYRO_dialog.py Wed Nov 29 11:54:56 2023 +0100 +++ b/connectors/PYRO_dialog.py Thu Dec 07 22:41:32 2023 +0100 @@ -3,7 +3,7 @@ # See COPYING file for copyrights details. -from __future__ import absolute_import + from itertools import repeat, islice, chain @@ -14,9 +14,9 @@ ('port', _("Port:"))] # (scheme, model, secure) -models = [("LOCAL", [], False), ("PYRO", model, False), ("PYROS", model, True)] +models = [("LOCAL", [], False), ("PYRO", model, False)] -Schemes = list(zip(*models)[0]) +Schemes = list(zip(*models))[0] _PerSchemeConf = {sch: (mod, sec) for sch, mod, sec in models} diff -r 7e17f7e02a2b -r 0b3ac94f494c connectors/SchemeEditor.py --- a/connectors/SchemeEditor.py Wed Nov 29 11:54:56 2023 +0100 +++ b/connectors/SchemeEditor.py Thu Dec 07 22:41:32 2023 +0100 @@ -3,7 +3,7 @@ # See COPYING file for copyrights details. -from __future__ import absolute_import + from functools import partial import wx @@ -28,19 +28,19 @@ (wx.StaticText(self, label=label), wx.ALIGN_CENTER_VERTICAL), (txtctrl, wx.GROW)]: - self.fieldsizer.AddWindow(win, flag=flag) + self.fieldsizer.Add(win, flag=flag) self.fieldsizer.AddSpacer(20) if self.EnableIDSelector: self.mainsizer = wx.FlexGridSizer(cols=2, hgap=10, vgap=10) - self.mainsizer.AddSizer(self.fieldsizer) + self.mainsizer.Add(self.fieldsizer) self.idselector = IDBrowser( self, parent.ctr, # use a callafter, as editor can be deleted by calling SetURI partial(wx.CallAfter, parent.SetURI), self.txtctrls["ID"].SetValue) - self.mainsizer.AddWindow(self.idselector) + self.mainsizer.Add(self.idselector) self.SetSizer(self.mainsizer) else: self.SetSizer(self.fieldsizer) diff -r 7e17f7e02a2b -r 0b3ac94f494c connectors/WAMP/__init__.py --- a/connectors/WAMP/__init__.py Wed Nov 29 11:54:56 2023 +0100 +++ b/connectors/WAMP/__init__.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,13 +23,10 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import print_function import sys import traceback from functools import partial from threading import Thread, Event -from six import text_type as text from twisted.internet import reactor, threads from autobahn.twisted import wamp @@ -76,7 +73,7 @@ # create a WAMP application session factory component_config = types.ComponentConfig( - realm=text(realm), + realm=str(realm), extra={"ID": ID}) session_factory = wamp.ApplicationSessionFactory( config=component_config) @@ -119,7 +116,7 @@ # reactor.stop() def WampSessionProcMapper(self, funcname): - wampfuncname = text('.'.join((ID, funcname))) + wampfuncname = str('.'.join((ID, funcname))) def catcher_func(*args, **kwargs): if _WampSession is not None: diff -r 7e17f7e02a2b -r 0b3ac94f494c connectors/WAMP_dialog.py --- a/connectors/WAMP_dialog.py Wed Nov 29 11:54:56 2023 +0100 +++ b/connectors/WAMP_dialog.py Thu Dec 07 22:41:32 2023 +0100 @@ -3,7 +3,7 @@ # See COPYING file for copyrights details. -from __future__ import absolute_import + from itertools import repeat, islice, chain diff -r 7e17f7e02a2b -r 0b3ac94f494c connectors/__init__.py --- a/connectors/__init__.py Wed Nov 29 11:54:56 2023 +0100 +++ b/connectors/__init__.py Thu Dec 07 22:41:32 2023 +0100 @@ -26,15 +26,17 @@ # Package initialisation -from __future__ import absolute_import +import os +import importlib from os import listdir, path from connectors.ConnectorBase import ConnectorBase -connectors_packages = ["PYRO", "WAMP"] +connectors_packages = ["PYRO"] def _GetLocalConnectorClassFactory(name): - return lambda: getattr(__import__(name, globals(), locals()), name + "_connector_factory") + return lambda: getattr(importlib.import_module(f"connectors.{name}"), + f"{name}_connector_factory") connectors = {name: _GetLocalConnectorClassFactory(name) @@ -53,13 +55,15 @@ per_URI_connectors = {} schemes = [] for con_name in connectors_packages: - module = __import__(con_name + '_dialog', globals(), locals()) + module = importlib.import_module(f"connectors.{con_name}_dialog") for scheme in module.Schemes: per_URI_connectors[scheme] = getattr(module, con_name + '_dialog') schemes += [scheme] +LocalHost = os.environ.get("BEREMIZ_LOCAL_HOST", "127.0.0.1") + def ConnectorFactory(uri, confnodesroot): """ Return a connector corresponding to the URI @@ -76,9 +80,8 @@ # pyro connection to local runtime # started on demand, listening on random port scheme = "PYRO" - runtime_port = confnodesroot.AppFrame.StartLocalRuntime( - taskbaricon=True) - uri = "PYROLOC://127.0.0.1:" + str(runtime_port) + runtime_port = confnodesroot.StartLocalRuntime() + uri = f"PYRO://{LocalHost}:{runtime_port}" # commented code to enable for MDNS:// support # elif _scheme == "MDNS": diff -r 7e17f7e02a2b -r 0b3ac94f494c controls/CustomEditableListBox.py --- a/controls/CustomEditableListBox.py Wed Nov 29 11:54:56 2023 +0100 +++ b/controls/CustomEditableListBox.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,15 +23,15 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx -import wx.gizmos +import wx.adv -class CustomEditableListBox(wx.gizmos.EditableListBox): +class CustomEditableListBox(wx.adv.EditableListBox): def __init__(self, *args, **kwargs): - wx.gizmos.EditableListBox.__init__(self, *args, **kwargs) + wx.adv.EditableListBox.__init__(self, *args, **kwargs) listbox = self.GetListCtrl() listbox.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) @@ -44,7 +44,7 @@ (self.GetDelButton(), _("Delete item"), "_OnDelButton"), (self.GetUpButton(), _("Move up"), "_OnUpButton"), (self.GetDownButton(), _("Move down"), "_OnDownButton")]: - button.SetToolTipString(tooltip) + button.SetToolTip(tooltip) button.Bind(wx.EVT_BUTTON, self.GetButtonPressedFunction(call_function)) self.Editing = False diff -r 7e17f7e02a2b -r 0b3ac94f494c controls/CustomGrid.py --- a/controls/CustomGrid.py Wed Nov 29 11:54:56 2023 +0100 +++ b/controls/CustomGrid.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,7 +23,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx import wx.grid diff -r 7e17f7e02a2b -r 0b3ac94f494c controls/CustomIntCtrl.py --- a/controls/CustomIntCtrl.py Wed Nov 29 11:54:56 2023 +0100 +++ b/controls/CustomIntCtrl.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,7 +23,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx import wx.lib.intctrl diff -r 7e17f7e02a2b -r 0b3ac94f494c controls/CustomStyledTextCtrl.py --- a/controls/CustomStyledTextCtrl.py Wed Nov 29 11:54:56 2023 +0100 +++ b/controls/CustomStyledTextCtrl.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,11 +23,9 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import from functools import reduce import wx import wx.stc -from six.moves import xrange if wx.Platform == '__WXMSW__': faces = { @@ -72,7 +70,7 @@ new_length = len(new) common_length = min(old_length, new_length) i = 0 - for i in xrange(common_length): + for i in range(common_length): if old[i] != new[i]: break if old_length < new_length: @@ -102,12 +100,12 @@ x, _y = event.GetPosition() margin_width = reduce( lambda x, y: x + y, - [self.GetMarginWidth(i) for i in xrange(3)], + [self.GetMarginWidth(i) for i in range(3)], 0) if x <= margin_width: - self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) + self.SetCursor(wx.Cursor(wx.CURSOR_ARROW)) else: - self.SetCursor(wx.StockCursor(wx.CURSOR_IBEAM)) + self.SetCursor(wx.Cursor(wx.CURSOR_IBEAM)) else: event.Skip() else: @@ -116,3 +114,10 @@ def AppendText(self, text): self.GotoPos(self.GetLength()) self.AddText(text) + + if wx.VERSION < (4, 1, 0): + def StartStyling(self, pos, mask=0xff): + wx.stc.StyledTextCtrl.StartStyling(self, pos, mask) + else: + def StartStyling(self, pos, *ignored): + wx.stc.StyledTextCtrl.StartStyling(self, pos) diff -r 7e17f7e02a2b -r 0b3ac94f494c controls/CustomTable.py --- a/controls/CustomTable.py Wed Nov 29 11:54:56 2023 +0100 +++ b/controls/CustomTable.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,7 +23,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx import wx.grid @@ -40,7 +40,7 @@ """ def __init__(self, parent, data, colnames): # The base class must be initialized *first* - wx.grid.PyGridTableBase.__init__(self) + wx.grid.GridTableBase.__init__(self) self.data = data self.colnames = colnames self.Highlights = {} @@ -64,7 +64,7 @@ return self.colnames[col] def GetRowLabelValue(self, row, translate=True): - return row + return str(row) def GetValue(self, row, col): if row < self.GetNumberRows(): @@ -204,8 +204,8 @@ if highlight_type is None: self.Highlights = {} else: - for _row, row_highlights in self.Highlights.iteritems(): - row_items = row_highlights.items() + for _row, row_highlights in self.Highlights.items(): + row_items = list(row_highlights.items()) for col, col_highlights in row_items: if highlight_type in col_highlights: col_highlights.remove(highlight_type) diff -r 7e17f7e02a2b -r 0b3ac94f494c controls/CustomToolTip.py --- a/controls/CustomToolTip.py Wed Nov 29 11:54:56 2023 +0100 +++ b/controls/CustomToolTip.py Thu Dec 07 22:41:32 2023 +0100 @@ -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. -from __future__ import absolute_import + import wx from controls.CustomStyledTextCtrl import faces @@ -137,7 +137,7 @@ max_width = max_height = 0 # Create a memory DC for calculating text extent - dc = wx.MemoryDC(wx.EmptyBitmap(1, 1)) + dc = wx.MemoryDC(wx.Bitmap(1, 1)) dc.SetFont(self.Font) # Compute max tip text size @@ -175,7 +175,6 @@ dc.SetFont(self.Font) # Draw Tool tip - dc.BeginDrawing() tip_width, tip_height = self.GetToolTipSize() # Draw background rectangle @@ -188,6 +187,5 @@ _line_width, line_height = dc.GetTextExtent(line) line_offset += line_height - dc.EndDrawing() event.Skip() diff -r 7e17f7e02a2b -r 0b3ac94f494c controls/CustomTree.py --- a/controls/CustomTree.py Wed Nov 29 11:54:56 2023 +0100 +++ b/controls/CustomTree.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,8 +23,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import division + + import wx import wx.lib.agw.customtreectrl as CT @@ -120,9 +120,9 @@ _item, flags = self.HitTest(pos) bitmap_rect = self.GetBitmapRect() - if ((bitmap_rect.InsideXY(pos.x, pos.y) or + if ((bitmap_rect.Contains(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) + wx.CallAfter(self.PopupMenu, self.AddMenu, pos.x, pos.y) event.Skip() def OnEraseBackground(self, event): diff -r 7e17f7e02a2b -r 0b3ac94f494c controls/DebugVariablePanel/DebugVariableGraphicViewer.py --- a/controls/DebugVariablePanel/DebugVariableGraphicViewer.py Wed Nov 29 11:54:56 2023 +0100 +++ b/controls/DebugVariablePanel/DebugVariableGraphicViewer.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,8 +23,6 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import division from time import time as gettime from cycler import cycler @@ -33,10 +31,8 @@ import matplotlib import matplotlib.pyplot from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas -from matplotlib.backends.backend_wxagg import _convert_agg_to_wx_bitmap from matplotlib.backends.backend_agg import FigureCanvasAgg from mpl_toolkits.mplot3d import Axes3D -from six.moves import xrange from editors.DebugViewer import REFRESH_PERIOD from controls.DebugVariablePanel.DebugVariableViewer import * @@ -44,7 +40,7 @@ # Graph variable display type -GRAPH_PARALLEL, GRAPH_ORTHOGONAL = range(2) +GRAPH_PARALLEL, GRAPH_ORTHOGONAL = list(range(2)) # Canvas height [SIZE_MINI, SIZE_MIDDLE, SIZE_MAXI] = [0, 100, 200] @@ -121,15 +117,6 @@ self.ParentControl = parent self.ParentWindow = window - def __del__(self): - """ - Destructor - """ - # Remove reference to Debug Variable Graphic Viewer and Debug Variable - # Panel - self.ParentControl = None - self.ParentWindow = None - def OnDragOver(self, x, y, d): """ Function called when mouse is dragged over Drop Target @@ -166,6 +153,7 @@ # Display message if data is invalid if message is not None: wx.CallAfter(self.ShowMessage, message) + return False # Data contain a reference to a variable to debug elif values[1] == "debug": @@ -174,15 +162,15 @@ # 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): + if not self.ParentControl.Is3DCanvas() and rect.Contains(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, - rect.width / 2., rect.height) - if merge_rect.InsideXY(x, y): + rect.width // 2, rect.height) + if merge_rect.Contains(x, y): merge_type = GRAPH_ORTHOGONAL # Merge graphs @@ -209,6 +197,8 @@ self.ParentWindow.InsertValue(values[0], target_idx, force=True) + return True + return False def OnLeave(self): """ @@ -278,7 +268,6 @@ 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) @@ -490,11 +479,12 @@ @param item: Item from which data to export, all items if None (default None) """ - self.ParentWindow.CopyDataToClipboard( - [(item, [entry for entry in item.GetData()]) - for item in (self.Items - if item is None - else [item])]) + if item is not None and item.GetData(): + self.ParentWindow.CopyDataToClipboard( + [(item, [entry for entry in item.GetData()]) + for item in (self.Items + if item is None + else [item])]) def OnZoomFitButton(self): """ @@ -565,7 +555,7 @@ """ start_tick, end_tick = self.ParentWindow.GetRange() cursor_tick = None - items = self.ItemsDict.values() + items = list(self.ItemsDict.values()) # Graph is orthogonal if self.GraphType == GRAPH_ORTHOGONAL: @@ -591,7 +581,7 @@ # 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: + if data is not None and len(data) > 0: cursor_tick = data[numpy.argmin( numpy.abs(data[:, 0] - event.xdata)), 0] @@ -623,9 +613,10 @@ [pair for pair in enumerate(self.Labels)]): # Get label bounding box (x0, y0), (x1, y1) = t.get_window_extent().get_points() + x0, y0, x1, y1 = map(int,(x0, y0, x1, y1)) rect = wx.Rect(x0, height - y1, x1 - x0, y1 - y0) # Check if mouse was over label - if rect.InsideXY(x, y): + if rect.Contains(x, y): item_idx = i break @@ -639,7 +630,7 @@ # parent xw, yw = self.GetPosition() self.ParentWindow.StartDragNDrop( - self, self.ItemsDict.values()[item_idx], + self, list(self.ItemsDict.values())[item_idx], x + xw, y + yw, # Current mouse position x + xw, y + yw) # Mouse position when button was clicked @@ -677,7 +668,7 @@ if self.ParentWindow.IsDragging(): _width, height = self.GetSize() xw, yw = self.GetPosition() - item = self.ParentWindow.DraggingAxesPanel.ItemsDict.values()[0] + item = list(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) @@ -734,9 +725,10 @@ directions): # Check every label paired with corresponding item (x0, y0), (x1, y1) = t.get_window_extent().get_points() + x0, y0, x1, y1 = map(int,(x0, y0, x1, y1)) rect = wx.Rect(x0, height - y1, x1 - x0, y1 - y0) # Check if mouse was over label - if rect.InsideXY(event.x, height - event.y): + if rect.Contains(event.x, height - event.y): item_idx = i menu_direction = dir break @@ -744,7 +736,7 @@ # If mouse is over an item label, if item_idx is not None: self.PopupContextualButtons( - self.ItemsDict.values()[item_idx], + list(self.ItemsDict.values())[item_idx], rect, menu_direction) return @@ -756,7 +748,7 @@ # Update resize highlight if event.y <= 5: if self.SetHighlight(HIGHLIGHT_RESIZE): - self.SetCursor(wx.StockCursor(wx.CURSOR_SIZENS)) + self.SetCursor(wx.Cursor(wx.CURSOR_SIZENS)) self.ParentWindow.ForceRefresh() else: if self.SetHighlight(HIGHLIGHT_NONE): @@ -782,7 +774,7 @@ xw, yw = self.GetPosition() self.ParentWindow.SetCursorTick(self.StartCursorTick) self.ParentWindow.StartDragNDrop( - self, self.ItemsDict.values()[0], + self, list(self.ItemsDict.values())[0], # Current mouse position event.x + xw, height - event.y + yw, # Mouse position when button was clicked @@ -816,7 +808,7 @@ # and mouse position if self.GraphType == GRAPH_ORTHOGONAL: start_tick, end_tick = self.ParentWindow.GetRange() - tick = (start_tick + end_tick) / 2. + tick = (start_tick + end_tick) // 2. else: tick = event.xdata self.ParentWindow.ChangeRange(int(-event.step) // 3, tick) @@ -832,7 +824,7 @@ # Check that double click was done inside figure pos = event.GetPosition() rect = self.GetAxesBoundingBox() - if rect.InsideXY(pos.x, pos.y): + if rect.Contains(pos.x, pos.y): # Reset Cursor tick to value before starting clicking self.ParentWindow.SetCursorTick(self.StartCursorTick) # Toggle to text Viewer(s) @@ -882,8 +874,8 @@ # 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)) + int(CANVAS_BORDER[0] + CANVAS_BORDER[1] + + 2 * CANVAS_PADDING + VALUE_LABEL_HEIGHT * len(self.Items)) ) def SetCanvasHeight(self, height): """ @@ -904,7 +896,7 @@ # Calculate figure bounding box. Y coordinate is inverted in matplotlib # figure comparing to wx panel width, height = self.GetSize() - ax, ay, aw, ah = self.figure.gca().get_position().bounds + ax, ay, aw, ah = map(int, self.figure.gca().get_position().bounds) bbox = wx.Rect(ax * width, height - (ay + ah) * height - 1, aw * width + 2, ah * height + 1) @@ -926,10 +918,10 @@ # Mouse is over Viewer figure and graph is not 3D bbox = self.GetAxesBoundingBox() - if bbox.InsideXY(x, y) and not self.Is3DCanvas(): + if bbox.Contains(x, y) and not self.Is3DCanvas(): rect = wx.Rect(bbox.x, bbox.y, bbox.width // 2, bbox.height) # Mouse is over Viewer left part of figure - if rect.InsideXY(x, y): + if rect.Contains(x, y): self.SetHighlight(HIGHLIGHT_LEFT) # Mouse is over Viewer right part of figure @@ -1027,7 +1019,7 @@ # 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): + for idx in range(num_item): # Get color from color cycle (black if only one item) color = ('k' if num_item == 1 else @@ -1091,7 +1083,7 @@ # Update position of items labels if self.GraphType == GRAPH_PARALLEL or self.Is3DCanvas(): num_item = len(self.Items) - for idx in xrange(num_item): + for idx in range(num_item): # In 3D graph items variable label are not displayed if not self.Is3DCanvas(): @@ -1187,7 +1179,7 @@ # each variable start_tick = max(start_tick, self.GetItemsMinCommonTick()) end_tick = max(end_tick, start_tick) - items = self.ItemsDict.values() + items = list(self.ItemsDict.values()) # Get data and range for first variable (X coordinate) x_data, x_min, x_max = items[0].GetDataAndValueRange( @@ -1329,7 +1321,7 @@ item.GetValue(self.CursorTick) if self.CursorTick is not None else (item.GetValue(), item.IsForced())) for item in self.Items] - values, forced = zip(*args) + values, forced = list(zip(*args)) # Get path of each variable displayed simplified using panel variable # name mask @@ -1337,7 +1329,7 @@ for item in self.Items] # Get style for each variable according to - styles = map(lambda x: {True: 'italic', False: 'normal'}[x], forced) + styles = [{True: 'italic', False: 'normal'}[x] for x in forced] # Graph is orthogonal 3D, set variables path as 3D axis label if self.Is3DCanvas(): @@ -1369,9 +1361,9 @@ 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): - self.bitmap.UseAlpha() + agg_bitmap = self.get_renderer() + self.bitmap = wx.Bitmap.FromBufferRGBA(int(agg_bitmap.width), int(agg_bitmap.height), + agg_bitmap.buffer_rgba()) # Create DC for rendering graphics in bitmap destDC = wx.MemoryDC() @@ -1381,8 +1373,6 @@ # rendering destGC = wx.GCDC(destDC) - destGC.BeginDrawing() - # Get canvas size and figure bounding box in canvas width, height = self.GetSize() bbox = self.GetAxesBoundingBox() @@ -1409,7 +1399,5 @@ # Draw other Viewer common elements self.DrawCommonElements(destGC, self.GetButtons()) - destGC.EndDrawing() - self._isDrawn = True self.gui_repaint(drawDC=drawDC) diff -r 7e17f7e02a2b -r 0b3ac94f494c controls/DebugVariablePanel/DebugVariableItem.py --- a/controls/DebugVariablePanel/DebugVariableItem.py Wed Nov 29 11:54:56 2023 +0100 +++ b/controls/DebugVariablePanel/DebugVariableItem.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,7 +23,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + from datetime import timedelta import binascii import numpy as np @@ -63,13 +63,6 @@ # 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 @@ -262,7 +255,7 @@ if self.VariableType in ["STRING", "WSTRING"]: # String data value is CRC - num_value = (binascii.crc32(value) & STRING_CRC_MASK) + num_value = (binascii.crc32(value.encode()) & STRING_CRC_MASK) elif self.VariableType in ["TIME", "TOD", "DT", "DATE"]: # Numeric value of time type variables # is represented in seconds diff -r 7e17f7e02a2b -r 0b3ac94f494c controls/DebugVariablePanel/DebugVariablePanel.py --- a/controls/DebugVariablePanel/DebugVariablePanel.py Wed Nov 29 11:54:56 2023 +0100 +++ b/controls/DebugVariablePanel/DebugVariablePanel.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,8 +23,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import division + + from functools import reduce import numpy as np @@ -92,13 +92,6 @@ 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 @@ -134,6 +127,7 @@ # Display message if data is invalid if message is not None: wx.CallAfter(self.ShowMessage, message) + return False # Data contain a reference to a variable to debug elif values[1] == "debug": @@ -147,6 +141,9 @@ else: self.ParentWindow.InsertValue(values[0], force=True) + return True + return False + def OnLeave(self): """ Function called when mouse is leave Drop Target @@ -202,7 +199,6 @@ # data is available self.Force = False - self.SetBackgroundColour(wx.WHITE) main_sizer = wx.BoxSizer(wx.VERTICAL) @@ -221,14 +217,14 @@ self.GraphicPanels = [] graphics_button_sizer = wx.BoxSizer(wx.HORIZONTAL) - main_sizer.AddSizer(graphics_button_sizer, border=5, flag=wx.GROW | wx.ALL) + main_sizer.Add(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) + graphics_button_sizer.Add(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, + graphics_button_sizer.Add(self.CanvasRange, 1, border=5, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) @@ -246,10 +242,10 @@ button = wx.lib.buttons.GenBitmapButton( self, bitmap=GetBitmap(bitmap), size=wx.Size(28, 28), style=wx.NO_BORDER) - button.SetToolTipString(help) + button.SetToolTip(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) + graphics_button_sizer.Add(button, border=5, flag=wx.LEFT) self.CanvasPosition = wx.ScrollBar( self, size=wx.Size(0, 16), style=wx.SB_HORIZONTAL) @@ -263,19 +259,19 @@ 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) + main_sizer.Add(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.Add(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.TickSizer.Add(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.TickSizer.Add(self.MaskLabel, 1, border=5, flag=wx.RIGHT | wx.GROW) self.TickTimeLabel = wx.StaticText(self) - self.TickSizer.AddWindow(self.TickTimeLabel) + self.TickSizer.Add(self.TickTimeLabel) self.GraphicsWindow = wx.ScrolledWindow(self, style=wx.HSCROLL | wx.VSCROLL) self.GraphicsWindow.SetBackgroundColour(wx.WHITE) @@ -284,7 +280,7 @@ 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) + main_sizer.Add(self.GraphicsWindow, 1, flag=wx.GROW) self.GraphicsSizer = wx.BoxSizer(wx.VERTICAL) self.GraphicsWindow.SetSizer(self.GraphicsSizer) @@ -441,7 +437,7 @@ x, y = panel.GetPosition() width, height = panel.GetSize() rect = wx.Rect(x, y, width, height) - if rect.InsideXY(x_mouse, y_mouse) or \ + if rect.Contains(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) @@ -488,7 +484,7 @@ xw, yw = panel.GetPosition() width, height = panel.GetSize() bbox = wx.Rect(xw, yw, width, height) - if bbox.InsideXY(x_mouse, y_mouse): + if bbox.Contains(x_mouse, y_mouse): panel.ShowButtons(True) merge_type = GRAPH_PARALLEL if isinstance(panel, DebugVariableTextViewer) or panel.Is3DCanvas(): @@ -497,9 +493,9 @@ wx.CallAfter(self.MoveValue, variable, idx, True) else: rect = panel.GetAxesBoundingBox(True) - if rect.InsideXY(x_mouse, y_mouse): + if rect.Contains(x_mouse, y_mouse): merge_rect = wx.Rect(rect.x, rect.y, rect.width // 2, rect.height) - if merge_rect.InsideXY(x_mouse, y_mouse): + if merge_rect.Contains(x_mouse, y_mouse): merge_type = GRAPH_ORTHOGONAL wx.CallAfter(self.MergeGraphs, variable, idx, merge_type, force=True) else: @@ -510,7 +506,7 @@ return width, height = self.GraphicsWindow.GetVirtualSize() rect = wx.Rect(0, 0, width, height) - if rect.InsideXY(x_mouse, y_mouse): + if rect.Contains(x_mouse, y_mouse): wx.CallAfter(self.MoveValue, variable, len(self.GraphicPanels), True) self.ForceRefresh() @@ -518,7 +514,7 @@ self.GraphicsSizer.Clear() for panel in self.GraphicPanels: - self.GraphicsSizer.AddWindow(panel, flag=wx.GROW) + self.GraphicsSizer.Add(panel, flag=wx.GROW) self.GraphicsSizer.Layout() self.RefreshGraphicsWindowScrollbars() diff -r 7e17f7e02a2b -r 0b3ac94f494c controls/DebugVariablePanel/DebugVariableTextViewer.py --- a/controls/DebugVariablePanel/DebugVariableTextViewer.py Wed Nov 29 11:54:56 2023 +0100 +++ b/controls/DebugVariablePanel/DebugVariableTextViewer.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,8 +23,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import division + + import wx @@ -51,15 +51,6 @@ self.ParentControl = parent self.ParentWindow = window - def __del__(self): - """ - Destructor - """ - # Remove reference to Debug Variable Text Viewer and Debug Variable - # Panel - self.ParentControl = None - self.ParentWindow = None - def OnDragOver(self, x, y, d): """ Function called when mouse is dragged over Drop Target @@ -96,6 +87,7 @@ # Display message if data is invalid if message is not None: wx.CallAfter(self.ShowMessage, message) + return False # Data contain a reference to a variable to debug elif values[1] == "debug": @@ -118,6 +110,10 @@ self.ParentWindow.InsertValue(values[0], target_idx, force=True) + return True + return False + + return True def OnLeave(self): """ @@ -195,7 +191,7 @@ # Create buffered DC for drawing in panel width, height = self.GetSize() - bitmap = wx.EmptyBitmap(width, height) + bitmap = wx.Bitmap(width, height) dc = wx.BufferedDC(wx.PaintDC(self), bitmap) dc.Clear() @@ -203,10 +199,8 @@ # rendering gc = wx.GCDC(dc) - gc.BeginDrawing() - # Get first item - item = self.ItemsDict.values()[0] + item = list(self.ItemsDict.values())[0] # Get item variable path masked according Debug Variable Panel mask item_path = item.GetVariable( @@ -232,15 +226,13 @@ # Draw other Viewer common elements self.DrawCommonElements(gc) - gc.EndDrawing() - def OnLeftDown(self, event): """ Function called when mouse left button is pressed @param event: wx.MouseEvent """ # Get first item - item = self.ItemsDict.values()[0] + item = list(self.ItemsDict.values())[0] # Calculate item path bounding box _width, height = self.GetSize() @@ -251,8 +243,8 @@ # Test if mouse has been pressed in this bounding box. In that case # 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): + item_path_bbox = wx.Rect(20, (height - h) // 2, w, h) + if item_path_bbox.Contains(x, y): self.ShowButtons(False) data = wx.TextDataObject(str((item.GetVariable(), "debug", "move"))) dragSource = wx.DropSource(self) @@ -279,7 +271,7 @@ @param event: wx.MouseEvent """ # Only numeric variables can be toggled to graph canvas - if self.ItemsDict.values()[0].IsNumVariable(): + if list(self.ItemsDict.values())[0].IsNumVariable(): self.ParentWindow.ToggleViewerType(self) def OnPaint(self, event): diff -r 7e17f7e02a2b -r 0b3ac94f494c controls/DebugVariablePanel/DebugVariableViewer.py --- a/controls/DebugVariablePanel/DebugVariableViewer.py Wed Nov 29 11:54:56 2023 +0100 +++ b/controls/DebugVariablePanel/DebugVariableViewer.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,13 +23,12 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import division + + from collections import OrderedDict from functools import reduce import wx -from matplotlib.backends.backend_wxagg import _convert_agg_to_wx_bitmap from dialogs.ForceVariableDialog import ForceVariableDialog @@ -39,7 +38,7 @@ HIGHLIGHT_AFTER, HIGHLIGHT_LEFT, HIGHLIGHT_RIGHT, - HIGHLIGHT_RESIZE] = range(6) + HIGHLIGHT_RESIZE] = list(range(6)) # Viewer highlight styles HIGHLIGHT = { @@ -66,7 +65,7 @@ items = [] if items is None else items self.ItemsDict = OrderedDict([(item.GetVariable(), item) for item in items]) - self.Items = self.ItemsDict.viewvalues() + self.Items = self.ItemsDict.values() # Variable storing current highlight displayed in Viewer self.Highlight = HIGHLIGHT_NONE @@ -74,13 +73,6 @@ self.Buttons = [] self.InitHighlightPensBrushes() - def __del__(self): - """ - Destructor - """ - # Remove reference to Debug Variable Panel - self.ParentWindow = None - def InitHighlightPensBrushes(self): """ Init global pens and brushes @@ -111,7 +103,7 @@ Return items displayed by Viewer @return: List of items displayed in Viewer """ - return self.ItemsDict.values() + return list(self.ItemsDict.values()) def AddItem(self, item): """ @@ -150,7 +142,7 @@ Function that unsubscribe and remove every item that store values of a variable that doesn't exist in PLC anymore """ - for item in self.ItemsDict.values()[:]: + for item in self.ItemsDict.values(): iec_path = item.GetVariable() # Check that variablepath exist in PLC @@ -310,8 +302,10 @@ 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) + agg_bitmap = srcPanel.get_renderer() + srcBmp = wx.Bitmap.FromBufferRGBA(int(agg_bitmap.width), int(agg_bitmap.height), + agg_bitmap.buffer_rgba()) + srcDC = wx.MemoryDC() srcDC.SelectObject(srcBmp) @@ -347,13 +341,13 @@ """ Function called when Force button is pressed """ - self.ForceValue(self.ItemsDict.values()[0]) + self.ForceValue(list(self.ItemsDict.values())[0]) def OnReleaseButton(self): """ Function called when Release button is pressed """ - self.ReleaseValue(self.ItemsDict.values()[0]) + self.ReleaseValue(list(self.ItemsDict.values())[0]) def OnMouseDragging(self, x, y): """ diff -r 7e17f7e02a2b -r 0b3ac94f494c controls/DebugVariablePanel/GraphButton.py --- a/controls/DebugVariablePanel/GraphButton.py Wed Nov 29 11:54:56 2023 +0100 +++ b/controls/DebugVariablePanel/GraphButton.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,7 +23,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx from util.BitmapLibrary import GetBitmap @@ -58,13 +58,6 @@ # 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 @@ -146,7 +139,7 @@ # 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) + return rect.Contains(x, y) def ProcessCallback(self): """ diff -r 7e17f7e02a2b -r 0b3ac94f494c controls/DebugVariablePanel/RingBuffer.py --- a/controls/DebugVariablePanel/RingBuffer.py Wed Nov 29 11:54:56 2023 +0100 +++ b/controls/DebugVariablePanel/RingBuffer.py Thu Dec 07 22:41:32 2023 +0100 @@ -44,7 +44,7 @@ note: only when this function is called, is an O(size) performance hit incurred, and this cost is amortized over the whole padding space """ - print 'compacting' + print('compacting') self.buffer[:self.count] = self.view self.cursor -= self.size diff -r 7e17f7e02a2b -r 0b3ac94f494c controls/DebugVariablePanel/__init__.py --- a/controls/DebugVariablePanel/__init__.py Wed Nov 29 11:54:56 2023 +0100 +++ b/controls/DebugVariablePanel/__init__.py Thu Dec 07 22:41:32 2023 +0100 @@ -22,5 +22,5 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + from controls.DebugVariablePanel.DebugVariablePanel import DebugVariablePanel diff -r 7e17f7e02a2b -r 0b3ac94f494c controls/DiscoveryPanel.py --- a/controls/DiscoveryPanel.py Wed Nov 29 11:54:56 2023 +0100 +++ b/controls/DiscoveryPanel.py Thu Dec 07 22:41:32 2023 +0100 @@ -24,9 +24,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import import socket -from six.moves import xrange import wx import wx.lib.mixins.listctrl as listmix from zeroconf import ServiceBrowser, Zeroconf, get_all_addresses @@ -44,17 +42,17 @@ class DiscoveryPanel(wx.Panel, 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.Add(self.staticText1, 0, border=20, flag=wx.TOP | wx.LEFT | wx.RIGHT | wx.GROW) + parent.Add(self.ServicesList, 0, border=20, flag=wx.LEFT | wx.RIGHT | wx.GROW) + parent.Add(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.ByIPCheck, 0, border=0, flag=0) + parent.Add(self.RefreshButton, 0, border=0, flag=0) + # parent.Add(self.ByIPCheck, 0, border=0, flag=0) def _init_coll_ButtonGridSizer_Growables(self, parent): parent.AddGrowableCol(0) @@ -129,13 +127,26 @@ self.IfacesMonitorTimer.Start(2000) self.Bind(wx.EVT_TIMER, self.IfacesMonitor, self.IfacesMonitorTimer) + def _cleanup(self): + if self.IfacesMonitorTimer is not None: + self.IfacesMonitorTimer.Stop() + self.IfacesMonitorTimer = None + if self.Browser is not None: + self.Browser.cancel() + self.Browser = None + if self.ZeroConfInstance is not None: + self.ZeroConfInstance.close() + self.ZeroConfInstance = None + def __del__(self): - self.IfacesMonitorTimer.Stop() - self.Browser.cancel() - self.ZeroConfInstance.close() + self._cleanup() + + def Destroy(self): + self._cleanup() + wx.Panel.Destroy(self) def IfacesMonitor(self, event): - NewState = get_all_addresses(socket.AF_INET) + NewState = get_all_addresses() if self.IfacesMonitorState != NewState: if self.IfacesMonitorState is not None: @@ -190,8 +201,7 @@ if self.LatestSelection is not None: # if self.ByIPCheck.IsChecked(): svcname, scheme, host, port = \ - map(lambda col: self.getColumnText(self.LatestSelection, col), - range(4)) + [self.getColumnText(self.LatestSelection, col) for col in range(4)] return ("%s://%s:%s#%s" % (scheme, host, port, svcname)) \ if scheme[-1] == "S" \ else ("%s://%s:%s" % (scheme, host, port)) @@ -210,7 +220,7 @@ ''' # loop through the list items looking for the service that went offline - for idx in xrange(self.ServicesList.GetItemCount()): + for idx in range(self.ServicesList.GetItemCount()): # this is the unique identifier assigned to the item item_id = self.ServicesList.GetItemData(idx) diff -r 7e17f7e02a2b -r 0b3ac94f494c controls/DurationCellEditor.py --- a/controls/DurationCellEditor.py Wed Nov 29 11:54:56 2023 +0100 +++ b/controls/DurationCellEditor.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,7 +23,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx from dialogs.DurationEditorDialog import DurationEditorDialog @@ -46,12 +46,12 @@ 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) + main_sizer.Add(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) + main_sizer.Add(self.EditButton, flag=wx.GROW) self.Bind(wx.EVT_SIZE, self.OnSize) @@ -98,19 +98,16 @@ self.Duration.SetFocus() -class DurationCellEditor(wx.grid.PyGridCellEditor): +class DurationCellEditor(wx.grid.GridCellEditor): ''' Grid cell editor that uses DurationCellControl to display an edit button. ''' def __init__(self, table, colname): - wx.grid.PyGridCellEditor.__init__(self) + wx.grid.GridCellEditor.__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) diff -r 7e17f7e02a2b -r 0b3ac94f494c controls/EnhancedStatusBar.py --- a/controls/EnhancedStatusBar.py Wed Nov 29 11:54:56 2023 +0100 +++ b/controls/EnhancedStatusBar.py Thu Dec 07 22:41:32 2023 +0100 @@ -55,8 +55,8 @@ """ -from __future__ import absolute_import -from __future__ import division + + import wx # Horizontal Alignment Constants @@ -85,12 +85,12 @@ class EnhancedStatusBar(wx.StatusBar): - def __init__(self, parent, id=wx.ID_ANY, style=wx.ST_SIZEGRIP, + def __init__(self, parent, id=wx.ID_ANY, style=wx.STB_SIZEGRIP, name="EnhancedStatusBar"): """Default Class Constructor. EnhancedStatusBar.__init__(self, parent, id=wx.ID_ANY, - style=wx.ST_SIZEGRIP, + style=wx.STB_SIZEGRIP, name="EnhancedStatusBar") """ @@ -100,7 +100,7 @@ self._curPos = 0 self._parent = parent - wx.EVT_SIZE(self, self.OnSize) + self.Bind(wx.EVT_SIZE, self.OnSize) wx.CallAfter(self.OnSize, None) def OnSize(self, event): @@ -109,7 +109,7 @@ Actually, All The Calculations Linked To HorizontalAlignment And VerticalAlignment Are Done In This Function.""" - for pos, item in self._items.items(): + for pos, item in list(self._items.items()): widget, horizontalalignment, verticalalignment = item.widget, item.horizontalalignment, item.verticalalignment rect = self.GetFieldRect(pos) diff -r 7e17f7e02a2b -r 0b3ac94f494c controls/FolderTree.py --- a/controls/FolderTree.py Wed Nov 29 11:54:56 2023 +0100 +++ b/controls/FolderTree.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,20 +23,20 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import +from functools import cmp_to_key +from operator import eq import os import wx -from six.moves import xrange from util.BitmapLibrary import GetBitmap -DRIVE, FOLDER, FILE = range(3) +DRIVE, FOLDER, FILE = list(range(3)) def sort_folder(x, y): if x[1] == y[1]: - return cmp(x[0], y[0]) + return eq(x[0], y[0]) elif x[1] != FILE: return -1 else: @@ -74,12 +74,12 @@ self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnTreeItemCollapsed, self.Tree) 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) + main_sizer.Add(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) + main_sizer.Add(self.Filter, flag=wx.GROW) else: self.Filter = None @@ -99,7 +99,7 @@ self.Filters = {} if self.Filter is not None: filter_parts = filter.split("|") - for idx in xrange(0, len(filter_parts), 2): + for idx in range(0, len(filter_parts), 2): if filter_parts[idx + 1] == "*.*": self.Filters[filter_parts[idx]] = "" else: @@ -115,7 +115,7 @@ def _GetFolderChildren(self, folderpath, recursive=True): items = [] if wx.Platform == '__WXMSW__' and folderpath == "/": - for c in xrange(ord('a'), ord('z')): + for c in range(ord('a'), ord('z')): drive = os.path.join("%s:\\" % chr(c)) if os.path.exists(drive): items.append((drive, DRIVE, self._GetFolderChildren(drive, False))) @@ -137,7 +137,7 @@ os.path.splitext(filename)[1] == self.CurrentFilter): items.append((filename, FILE, None)) if recursive: - items.sort(sort_folder) + items.sort(key=cmp_to_key(sort_folder)) return items def SetFilter(self, filter): @@ -160,7 +160,7 @@ if wx.Platform != '__WXMSW__': item, item_cookie = self.Tree.GetNextChild(root, item_cookie) elif self.Tree.GetItemText(item) != filename: - item = self.Tree.InsertItemBefore(root, idx, filename, self.TreeImageDict[item_type]) + item = self.Tree.InsertItem(root, idx, filename, self.TreeImageDict[item_type]) filepath = os.path.join(folderpath, filename) if item_type != FILE: if self.Tree.IsExpanded(item): diff -r 7e17f7e02a2b -r 0b3ac94f494c controls/IDBrowser.py --- a/controls/IDBrowser.py Wed Nov 29 11:54:56 2023 +0100 +++ b/controls/IDBrowser.py Thu Dec 07 22:41:32 2023 +0100 @@ -3,7 +3,8 @@ # See COPYING file for copyrights details. -from __future__ import absolute_import + +from operator import eq import wx import wx.dataview as dv import PSKManagement as PSK @@ -43,9 +44,9 @@ row1 = self.GetRow(item1) row2 = self.GetRow(item2) if col == 0: - return cmp(int(self.data[row1][col]), int(self.data[row2][col])) + return eq(int(self.data[row1][col]), int(self.data[row2][col])) else: - return cmp(self.data[row1][col], self.data[row2][col]) + return eq(self.data[row1][col], self.data[row2][col]) def DeleteRows(self, rows): rows = list(rows) diff -r 7e17f7e02a2b -r 0b3ac94f494c controls/LibraryPanel.py --- a/controls/LibraryPanel.py Wed Nov 29 11:54:56 2023 +0100 +++ b/controls/LibraryPanel.py Thu Dec 07 22:41:32 2023 +0100 @@ -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. -from __future__ import absolute_import + from functools import reduce import wx @@ -31,7 +31,7 @@ # ------------------------------------------------------------------------------- -[CATEGORY, BLOCK] = range(2) +[CATEGORY, BLOCK] = list(range(2)) # ------------------------------------------------------------------------------- @@ -77,12 +77,12 @@ search_textctrl = self.SearchCtrl.GetChildren()[0] search_textctrl.Bind(wx.EVT_CHAR, self.OnKeyDown) - main_sizer.AddWindow(self.SearchCtrl, flag=wx.GROW) + main_sizer.Add(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) + main_sizer.Add(splitter_window, flag=wx.GROW) # Add TreeCtrl for functions and function blocks library in splitter # window @@ -114,13 +114,6 @@ # 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 @@ -173,7 +166,7 @@ # Get current selected item for selected it when values refreshed selected_item = self.Tree.GetSelection() - selected_pydata = (self.Tree.GetPyData(selected_item) + selected_pydata = (self.Tree.GetItemData(selected_item) if (selected_item.IsOk() and selected_item != self.Tree.GetRootItem()) else None) @@ -216,7 +209,7 @@ # Set data associated to tree item (only save that item is a # category) - self.Tree.SetPyData(category_item, {"type": CATEGORY}) + self.Tree.SetItemData(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 @@ -253,7 +246,7 @@ if blocktype["extensible"] else None), "comment": _(comment) + blocktype.get("usage", "") } - self.Tree.SetPyData(blocktype_item, block_data) + self.Tree.SetItemData(blocktype_item, block_data) # Select block tree item in tree if it corresponds to # previously selected one @@ -298,7 +291,7 @@ """ # Get selected item associated data in tree selected_item = self.Tree.GetSelection() - selected_pydata = (self.Tree.GetPyData(selected_item) + selected_pydata = (self.Tree.GetItemData(selected_item) if (selected_item.IsOk() and selected_item != self.Tree.GetRootItem()) else None) @@ -336,7 +329,7 @@ return None # Get data associated to item to test - item_pydata = self.Tree.GetPyData(item) + item_pydata = self.Tree.GetItemData(item) if item_pydata is not None and item_pydata["type"] == BLOCK: # Only test item corresponding to block @@ -346,12 +339,10 @@ if inputs is not None and type_inputs is not None: same_inputs = reduce( lambda x, y: x and y, - map( - lambda x: x[0] == x[1] or x[0] == 'ANY' or x[1] == 'ANY', - zip(type_inputs, + [x[0] == x[1] or x[0] == 'ANY' or x[1] == 'ANY' for x in zip(type_inputs, (inputs[:type_extension] if type_extension is not None - else inputs))), + else inputs))], True) else: same_inputs = True @@ -402,7 +393,7 @@ while item.IsOk(): # Get item data to get item type - item_pydata = self.Tree.GetPyData(item) + item_pydata = self.Tree.GetItemData(item) # Item is a block category if (item == root) or item_pydata["type"] == CATEGORY: @@ -467,7 +458,7 @@ @param event: wx.TreeEvent """ # Update TextCtrl value with block selected usage - item_pydata = self.Tree.GetPyData(event.GetItem()) + item_pydata = self.Tree.GetItemData(event.GetItem()) self.Comment.SetValue( item_pydata["comment"] if item_pydata is not None and item_pydata["type"] == BLOCK @@ -485,7 +476,7 @@ @param event: wx.TreeEvent """ selected_item = event.GetItem() - item_pydata = self.Tree.GetPyData(selected_item) + item_pydata = self.Tree.GetItemData(selected_item) # Item dragged is a block if item_pydata is not None and item_pydata["type"] == BLOCK: diff -r 7e17f7e02a2b -r 0b3ac94f494c controls/LocationCellEditor.py --- a/controls/LocationCellEditor.py Wed Nov 29 11:54:56 2023 +0100 +++ b/controls/LocationCellEditor.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,20 +23,20 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx from dialogs.BrowseLocationsDialog import BrowseLocationsDialog -class LocationCellControl(wx.PyControl): +class LocationCellControl(wx.Control): ''' Custom cell editor control with a text box and a button that launches the BrowseLocationsDialog. ''' def __init__(self, parent): - wx.PyControl.__init__(self, parent) + wx.Control.__init__(self, parent) main_sizer = wx.FlexGridSizer(cols=2, hgap=0, rows=1, vgap=0) main_sizer.AddGrowableCol(0) @@ -46,12 +46,12 @@ 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) + main_sizer.Add(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) + main_sizer.Add(self.BrowseButton, flag=wx.GROW) self.Bind(wx.EVT_SIZE, self.OnSize) @@ -62,9 +62,6 @@ self.Default = False self.VariableName = None - def __del__(self): - self.Controller = None - def SetController(self, controller): self.Controller = controller @@ -150,20 +147,16 @@ self.Location.SetFocus() -class LocationCellEditor(wx.grid.PyGridCellEditor): +class LocationCellEditor(wx.grid.GridCellEditor): ''' Grid cell editor that uses LocationCellControl to display a browse button. ''' def __init__(self, table, controller): - wx.grid.PyGridCellEditor.__init__(self) + wx.grid.GridCellEditor.__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) @@ -178,7 +171,7 @@ self.CellControl.SetVarType(self.Table.GetValueByName(row, 'Type')) self.CellControl.SetFocus() - def EndEditInternal(self, row, col, grid, old_loc): + def EndEdit(self, row, col, grid, old_loc): loc = self.CellControl.GetValue() changed = loc != old_loc if changed: @@ -201,18 +194,13 @@ 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') - return self.EndEditInternal(row, col, grid, old_loc) + def ApplyEdit(self, row, col, grid): + pass def SetSize(self, rect): - self.CellControl.SetDimensions(rect.x + 1, rect.y, - rect.width, rect.height, - wx.SIZE_ALLOW_MINUS_ONE) + self.CellControl.SetSize(rect.x + 1, rect.y, + rect.width, rect.height, + wx.SIZE_ALLOW_MINUS_ONE) def Clone(self): return LocationCellEditor(self.Table, self.Controller) diff -r 7e17f7e02a2b -r 0b3ac94f494c controls/LogViewer.py --- a/controls/LogViewer.py Wed Nov 29 11:54:56 2023 +0100 +++ b/controls/LogViewer.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,15 +23,12 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import division from datetime import datetime from time import time as gettime from weakref import proxy import numpy import wx -from six.moves import xrange from controls.CustomToolTip import CustomToolTip, TOOLTIP_WAIT_PERIOD from editors.DebugViewer import DebugViewer, REFRESH_PERIOD @@ -42,7 +39,8 @@ THUMB_SIZE_RATIO = 1. / 8. -def ArrowPoints(direction, width, height, xoffset, yoffset): +def ArrowPoints(*args): + direction, width, height, xoffset, yoffset = map(lambda x:x if type(x)==int else int(x), args) if direction == wx.TOP: return [wx.Point(xoffset + 1, yoffset + height - 2), wx.Point(xoffset + width // 2, yoffset + 1), @@ -99,8 +97,8 @@ width, height = self.GetClientSize() range_rect = self.GetRangeRect() thumb_rect = self.GetThumbRect() - if range_rect.InsideXY(posx, posy): - if thumb_rect.InsideXY(posx, posy): + if range_rect.Contains(posx, posy): + if thumb_rect.Contains(posx, posy): self.ThumbScrollingStartPos = wx.Point(posx, posy) elif posy < thumb_rect.y: self.Parent.ScrollToLast() @@ -139,13 +137,12 @@ def OnPaint(self, event): dc = wx.BufferedPaintDC(self) dc.Clear() - dc.BeginDrawing() gc = wx.GCDC(dc) width, height = self.GetClientSize() - gc.SetPen(wx.Pen(wx.NamedColour("GREY"), 3)) + gc.SetPen(wx.Pen(wx.Colour("GREY"), 3)) gc.SetBrush(wx.GREY_BRUSH) gc.DrawLines(ArrowPoints(wx.TOP, width * 0.75, width * 0.5, 2, (width + height) // 4 - 3)) @@ -162,7 +159,7 @@ if self.Parent.IsMessagePanelBottom(): exclusion_rect.height = height - width - exclusion_rect.y if exclusion_rect != thumb_rect: - colour = wx.NamedColour("LIGHT GREY") + colour = wx.Colour("LIGHT GREY") gc.SetPen(wx.Pen(colour)) gc.SetBrush(wx.Brush(colour)) @@ -179,7 +176,6 @@ gc.DrawRectangle(thumb_rect.x, thumb_rect.y, thumb_rect.width, thumb_rect.height) - dc.EndDrawing() event.Skip() @@ -195,9 +191,6 @@ self.Shown = True self.Callback = callback - def __del__(self): - self.callback = None - def GetSize(self): return self.Size @@ -207,7 +200,7 @@ def HitTest(self, x, y): rect = wx.Rect(self.Position.x, self.Position.y, self.Size.width, self.Size.height) - if rect.InsideXY(x, y): + if rect.Contains(x, y): return True return False @@ -217,7 +210,7 @@ def Draw(self, dc): dc.SetPen(wx.TRANSPARENT_PEN) - dc.SetBrush(wx.Brush(wx.NamedColour("LIGHT GREY"))) + dc.SetBrush(wx.Brush(wx.Colour("LIGHT GREY"))) dc.DrawRectangle(self.Position.x, self.Position.y, self.Size.width, self.Size.height) @@ -244,10 +237,10 @@ self.Message = msg self.DrawDate = True - def __cmp__(self, other): + def __lt__(self, other): if self.Date == other.Date: - return cmp(self.Seconds, other.Seconds) - return cmp(self.Date, other.Date) + return self.Seconds < other.Seconds + return self.Date < other.Date def GetFullText(self): date = self.Date.replace(second=int(self.Seconds)) @@ -303,7 +296,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.Add(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")) @@ -312,7 +305,7 @@ 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.Add(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) @@ -322,18 +315,18 @@ 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) + filter_sizer.Add(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) - self.CleanButton.SetToolTipString(_("Clean log messages")) + self.CleanButton.SetToolTip(_("Clean log messages")) self.Bind(wx.EVT_BUTTON, self.OnCleanButton, self.CleanButton) - filter_sizer.AddWindow(self.CleanButton) + filter_sizer.Add(self.CleanButton) 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.Add(message_panel_sizer, border=5, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.GROW) self.MessagePanel = wx.Panel(self) if wx.Platform == '__WXMSW__': @@ -349,10 +342,10 @@ self.MessagePanel.Bind(wx.EVT_ERASE_BACKGROUND, self.OnMessagePanelEraseBackground) self.MessagePanel.Bind(wx.EVT_PAINT, self.OnMessagePanelPaint) self.MessagePanel.Bind(wx.EVT_SIZE, self.OnMessagePanelResize) - message_panel_sizer.AddWindow(self.MessagePanel, flag=wx.GROW) + message_panel_sizer.Add(self.MessagePanel, flag=wx.GROW) self.MessageScrollBar = LogScrollBar(self, wx.Size(16, -1)) - message_panel_sizer.AddWindow(self.MessageScrollBar, flag=wx.GROW) + message_panel_sizer.Add(self.MessageScrollBar, flag=wx.GROW) self.SetSizer(main_sizer) @@ -372,7 +365,7 @@ self.ParentWindow = window self.LevelIcons = [GetBitmap("LOG_" + level) for level in LogLevels] - self.LevelFilters = [range(i) for i in xrange(4, 0, -1)] + self.LevelFilters = [list(range(i)) for i in range(4, 0, -1)] self.CurrentFilter = self.LevelFilters[0] self.CurrentSearchValue = "" @@ -386,9 +379,6 @@ self.MessageToolTipTimer = wx.Timer(self, -1) self.Bind(wx.EVT_TIMER, self.OnMessageToolTipTimer, self.MessageToolTipTimer) - def __del__(self): - self.ScrollTimer.Stop() - def ResetLogMessages(self): self.ResetLogCounters() self.OldestMessages = [] @@ -417,14 +407,14 @@ def SetLogCounters(self, log_count): new_messages = [] - for level, count, prev in zip(xrange(LogLevelsCount), log_count, self.previous_log_count): + for level, count, prev in zip(range(LogLevelsCount), log_count, self.previous_log_count): if count is not None and prev != count: if prev is None: dump_end = max(-1, count - 10) oldest_message = (-1, None) else: dump_end = prev - 1 - for msgidx in xrange(count-1, dump_end, -1): + for msgidx in range(count-1, dump_end, -1): new_message = self.GetLogMessageFromSource(msgidx, level) if new_message is None: if prev is None: @@ -534,10 +524,9 @@ def RefreshView(self): width, height = self.MessagePanel.GetClientSize() - bitmap = wx.EmptyBitmap(width, height) + bitmap = wx.Bitmap(width, height) dc = wx.BufferedDC(wx.ClientDC(self.MessagePanel), bitmap) dc.Clear() - dc.BeginDrawing() if self.CurrentMessage is not None: @@ -559,13 +548,11 @@ draw_date = message.Date != previous_message.Date message = previous_message - dc.EndDrawing() - self.MessageScrollBar.RefreshThumbPosition() def IsPLCLogEmpty(self): empty = True - for _level, prev in zip(xrange(LogLevelsCount), self.previous_log_count): + for _level, prev in zip(range(LogLevelsCount), self.previous_log_count): if prev is not None: empty = False break @@ -711,7 +698,7 @@ if message is not None: menu = wx.Menu(title='') - menu_entry = menu.Append(help='', id=wx.ID_ANY, kind=wx.ITEM_NORMAL, text=_("Copy")) + menu_entry = menu.Append(wx.ID_ANY, _("Copy")) self.Bind(wx.EVT_MENU, self.GetCopyMessageToClipboardFunction(message), menu_entry) self.MessagePanel.PopupMenu(menu) diff -r 7e17f7e02a2b -r 0b3ac94f494c controls/PouInstanceVariablesPanel.py --- a/controls/PouInstanceVariablesPanel.py Wed Nov 29 11:54:56 2023 +0100 +++ b/controls/PouInstanceVariablesPanel.py Thu Dec 07 22:41:32 2023 +0100 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#.!/usr/bin/env python # -*- coding: utf-8 -*- # This file is part of Beremiz, a Integrated Development Environment for @@ -23,8 +23,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import division + + from collections import namedtuple import wx @@ -93,7 +93,7 @@ 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): + if rect.Contains(point): return r_image rect.x += r_image_w + 4 @@ -132,7 +132,7 @@ 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.ParentButton.SetToolTip(_("Parent instance")) self.Bind(wx.EVT_BUTTON, self.OnParentButtonClick, self.ParentButton) @@ -142,7 +142,7 @@ 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.DebugButton.SetToolTip(_("Debug instance")) self.Bind(wx.EVT_BUTTON, self.OnDebugButtonClick, self.DebugButton) @@ -188,16 +188,16 @@ 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.Add(self.ParentButton) + buttons_sizer.Add(self.InstanceChoice, flag=wx.GROW) + buttons_sizer.Add(self.DebugButton) buttons_sizer.AddGrowableCol(1) buttons_sizer.AddGrowableRow(0) main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=0) - main_sizer.AddSizer(buttons_sizer, flag=wx.GROW) - main_sizer.AddWindow(self.VariablesList, flag=wx.GROW) - main_sizer.AddWindow(self.FilterCtrl, flag=wx.GROW) + main_sizer.Add(buttons_sizer, flag=wx.GROW) + main_sizer.Add(self.VariablesList, flag=wx.GROW) + main_sizer.Add(self.FilterCtrl, flag=wx.GROW) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(1) @@ -217,10 +217,6 @@ self.FilterCaseSensitive = False self.FilterWholeWord = False - - def __del__(self): - self.Controller = None - def SetTreeImageList(self, tree_image_list): self.VariablesList.SetImageList(tree_image_list) @@ -444,7 +440,7 @@ 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) + item_infos = self.VariablesList.GetItemData(selected_item) if item_infos is not None: item_button = self.VariablesList.IsOverItemRightImage( @@ -464,7 +460,7 @@ else: tagname = None else: - parent_infos = self.VariablesList.GetPyData(selected_item.GetParent()) + parent_infos = self.VariablesList.GetItemData(selected_item.GetParent()) if item_infos.var_class == ITEM_ACTION: tagname = ComputePouActionName(parent_infos.type, item_infos.name) elif item_infos.var_class == ITEM_TRANSITION: @@ -487,7 +483,7 @@ instance_path = self.InstanceChoice.GetStringSelection() item, flags = self.VariablesList.HitTest(event.GetPosition()) if item is not None: - item_infos = self.VariablesList.GetPyData(item) + item_infos = self.VariablesList.GetItemData(item) if item_infos is not None: item_button = self.VariablesList.IsOverItemRightImage( diff -r 7e17f7e02a2b -r 0b3ac94f494c controls/ProjectPropertiesPanel.py --- a/controls/ProjectPropertiesPanel.py Wed Nov 29 11:54:56 2023 +0100 +++ b/controls/ProjectPropertiesPanel.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,7 +23,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx from wx.lib.scrolledpanel import ScrolledPanel @@ -38,7 +38,7 @@ [ TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE, SCALING, PAGETITLES -] = range(10) +] = list(range(10)) # ------------------------------------------------------------------------------- @@ -57,7 +57,7 @@ border |= wx.BOTTOM st = wx.StaticText(parent, label=label) - sizer.AddWindow(st, border=10, + sizer.Add(st, border=10, flag=wx.ALIGN_CENTER_VERTICAL | border | wx.LEFT) tc = wx.TextCtrl(parent, style=wx.TE_PROCESS_ENTER) @@ -65,7 +65,7 @@ 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, + sizer.Add(tc, border=10, flag=wx.GROW | border | wx.RIGHT) def __init__(self, parent, controller=None, window=None, enable_required=True, scrolling=True): @@ -125,19 +125,19 @@ pageSize_st = wx.StaticText(self.GraphicsPanel, label=_('Page Size (optional):')) - graphicpanel_sizer.AddWindow( + graphicpanel_sizer.Add( 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, + graphicpanel_sizer.Add(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, + pageSize_sizer.Add(st, border=12, flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT) sp = wx.SpinCtrl(self.GraphicsPanel, @@ -146,15 +146,15 @@ 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) + pageSize_sizer.Add(sp, flag=wx.GROW) scaling_st = wx.StaticText(self.GraphicsPanel, label=_('Grid Resolution:')) - graphicpanel_sizer.AddWindow(scaling_st, border=10, + graphicpanel_sizer.Add(scaling_st, border=10, flag=wx.GROW | wx.LEFT | wx.RIGHT) scaling_nb = wx.Notebook(self.GraphicsPanel) - graphicpanel_sizer.AddWindow(scaling_nb, border=10, + graphicpanel_sizer.Add(scaling_nb, border=10, flag=wx.GROW | wx.BOTTOM | wx.LEFT | wx.RIGHT) self.Scalings = {} @@ -173,7 +173,7 @@ border = wx.BOTTOM st = wx.StaticText(scaling_panel, label=label) - scalingpanel_sizer.AddWindow( + scalingpanel_sizer.Add( st, border=10, flag=wx.ALIGN_CENTER_VERTICAL | border | wx.LEFT) @@ -183,7 +183,7 @@ 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, + scalingpanel_sizer.Add(sp, border=10, flag=wx.GROW | border | wx.RIGHT) self.Scalings[language] = scaling_controls @@ -206,18 +206,18 @@ language_label = wx.StaticText(self.MiscellaneousPanel, label=_('Language (optional):')) - miscellaneouspanel_sizer.AddWindow(language_label, border=10, + miscellaneouspanel_sizer.Add(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, + miscellaneouspanel_sizer.Add(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, + miscellaneouspanel_sizer.Add(description_label, border=10, flag=wx.BOTTOM | wx.LEFT) self.ContentDescription = wx.TextCtrl( @@ -227,7 +227,7 @@ self.ContentDescription) self.ContentDescription.Bind(wx.EVT_KILL_FOCUS, self.OnContentDescriptionChanged) - miscellaneouspanel_sizer.AddWindow(self.ContentDescription, border=10, + miscellaneouspanel_sizer.Add(self.ContentDescription, border=10, flag=wx.GROW | wx.BOTTOM | wx.RIGHT) self.AddPage(self.MiscellaneousPanel, _("Miscellaneous")) @@ -246,19 +246,19 @@ def SetValues(self, values): self.Values = values - for item, value in values.items(): + for item, value in list(values.items()): if item == "language": self.Language.SetStringSelection(value) elif item == "contentDescription": self.ContentDescription.SetValue(value) elif item == "pageSize": - self.PageWidth.SetValue(value[0]) - self.PageHeight.SetValue(value[1]) + self.PageWidth.SetValue(int(value[0])) + self.PageHeight.SetValue(int(value[1])) elif item == "scaling": - for language, (x, y) in value.items(): + for language, (x, y) in list(value.items()): if language in self.Scalings: - self.Scalings[language][0].SetValue(x) - self.Scalings[language][1].SetValue(y) + self.Scalings[language][0].SetValue(int(x)) + self.Scalings[language][1].SetValue(int(y)) else: tc = getattr(self, item, None) if tc is not None: diff -r 7e17f7e02a2b -r 0b3ac94f494c controls/SearchResultPanel.py --- a/controls/SearchResultPanel.py Wed Nov 29 11:54:56 2023 +0100 +++ b/controls/SearchResultPanel.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,7 +23,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + from functools import reduce import wx @@ -52,16 +52,16 @@ class SearchResultPanel(wx.Panel): 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) + parent.Add(self.HeaderSizer, 0, border=0, flag=wx.GROW) + parent.Add(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.ResetButton, 0, border=0, flag=0) + parent.Add(self.HeaderLabel, 1, border=5, flag=wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL) + parent.Add(self.ResetButton, 0, border=0, flag=0) def _init_coll_HeaderSizer_Growables(self, parent): parent.AddGrowableCol(0) @@ -90,7 +90,7 @@ 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.ResetButton.SetToolTip(_("Reset search result")) self.Bind(wx.EVT_BUTTON, self.OnResetButton, self.ResetButton) self._init_sizers() @@ -289,7 +289,7 @@ start, end = infos["data"][1:3] text_lines = infos["text"].splitlines() start_idx = start[1] - end_idx = reduce(lambda x, y: x + y, map(lambda x: len(x) + 1, text_lines[:end[0] - start[0]]), end[1] + 1) + end_idx = reduce(lambda x, y: x + y, [len(x) + 1 for x in text_lines[:end[0] - start[0]]], end[1] + 1) style = wx.TextAttr(wx.BLACK, wx.Colour(206, 204, 247)) elif infos["type"] is not None and infos["matches"] > 1: text = _("(%d matches)") % infos["matches"] @@ -320,7 +320,7 @@ item, root_cookie = self.SearchResultsTree.GetNextChild(root, root_cookie) def ShowSearchResults(self, item): - data = self.SearchResultsTree.GetPyData(item) + data = self.SearchResultsTree.GetItemData(item) if isinstance(data, tuple): search_results = [data] else: diff -r 7e17f7e02a2b -r 0b3ac94f494c controls/TextCtrlAutoComplete.py --- a/controls/TextCtrlAutoComplete.py Wed Nov 29 11:54:56 2023 +0100 +++ b/controls/TextCtrlAutoComplete.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,8 +23,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from six.moves import cPickle +import pickle import wx MAX_ITEM_COUNT = 10 @@ -97,7 +96,7 @@ parent_rect = wx.Rect(0, -parent_size[1], parent_size[0], parent_size[1]) if selected != wx.NOT_FOUND: wx.CallAfter(self.Parent.SetValueFromSelected, self.ListBox.GetString(selected)) - elif parent_rect.InsideXY(event.GetX(), event.GetY()): + elif parent_rect.Contains(event.GetX(), event.GetY()): result, x, y = self.Parent.HitTest(wx.Point(event.GetX(), event.GetY() + parent_size[1])) if result != wx.TE_HT_UNKNOWN: self.Parent.SetInsertionPoint(self.Parent.XYToPosition(x, y)) @@ -198,10 +197,12 @@ def OnControlChanged(self, event): res = self.GetValue() config = wx.ConfigBase.Get() - listentries = cPickle.loads(str(config.Read(self.element_path, cPickle.dumps([])))) + listentries = pickle.loads(config.Read(self.element_path, + pickle.dumps([], 0).decode() + ).encode()) if res and res not in listentries: listentries = (listentries + [res])[-MAX_ITEM_COUNT:] - config.Write(self.element_path, cPickle.dumps(listentries)) + config.Write(self.element_path, pickle.dumps(listentries, 0)) config.Flush() self.SetChoices(listentries) self.DismissListBox() diff -r 7e17f7e02a2b -r 0b3ac94f494c controls/VariablePanel.py --- a/controls/VariablePanel.py Wed Nov 29 11:54:56 2023 +0100 +++ b/controls/VariablePanel.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,17 +23,11 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import division import re -from builtins import str as text import wx import wx.grid import wx.lib.buttons -from six import string_types -from six.moves import xrange - from plcopen.structures import LOCATIONDATATYPES, TestIdentifier, IEC_KEYWORDS, DefaultType from plcopen.VariableInfoCollector import _VariableInfos @@ -54,7 +48,7 @@ [ TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE, SCALING, PAGETITLES -] = range(10) +] = list(range(10)) def GetVariableTableColnames(location): @@ -123,7 +117,7 @@ self.OPTIONS_DICT = dict([(_(option), option) for option in GetOptions()]) self.VARIABLE_CLASSES_DICT = dict([(_(_class), _class) - for _class in GetFilterChoiceTransfer().itervalues()]) + for _class in GetFilterChoiceTransfer().values()]) def GetValueByName(self, row, colname): if row < self.GetNumberRows(): @@ -144,7 +138,7 @@ if colname == "Type" and isinstance(value, tuple): if value[0] == "array": return "ARRAY [%s] OF %s" % (",".join(map("..".join, value[2])), value[1]) - if not isinstance(value, string_types): + if not isinstance(value, str): value = str(value) if colname in ["Class", "Option"]: return _(value) @@ -200,8 +194,7 @@ 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))) + editor = wx.grid.GridCellChoiceEditor(list(map(_, options))) else: grid.SetReadOnly(row, col, True) elif col != 0 and self._GetRowEdit(row): @@ -212,8 +205,7 @@ 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))) + editor = wx.grid.GridCellChoiceEditor([""] + self.Parent.Controler.GetEnumeratedDataValues(var_type)) else: editor = wx.grid.GridCellTextEditor() renderer = wx.grid.GridCellStringRenderer() @@ -229,11 +221,10 @@ 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])) + editor = wx.grid.GridCellChoiceEditor([_(choice) for choice in self.Parent.ClassList if choice not in excluded]) elif colname != "Documentation": grid.SetReadOnly(row, col, True) @@ -325,7 +316,7 @@ selected = None dialog.Destroy() if selected is None: - return + return False if selected == 0: location = "%I" + location elif selected == 1: @@ -360,7 +351,7 @@ var_name = dlg.GetValue() if dlg.ShowModal() == wx.ID_OK else None dlg.Destroy() if var_name is None: - return + return False elif var_name.upper() in [ name.upper() for name in self.ParentWindow.Controler.GetProjectPouNames(self.ParentWindow.Debug)]: @@ -388,7 +379,7 @@ selected = None dialog.Destroy() if selected is None: - return + return False if selected == 0: location = "%I" + location elif selected == 1: @@ -399,7 +390,7 @@ configs = self.ParentWindow.Controler.GetProjectConfigNames( self.ParentWindow.Debug) if len(configs) == 0: - return + return False if not var_name.upper() in [ name.upper() for name in self.ParentWindow.Controler.GetConfigurationVariableNames(configs[0])]: @@ -417,7 +408,7 @@ var_infos.Class = "Local" var_infos.InitialValue = values[0] else: - return + return False else: var_infos.Class = "External" var_infos.Number = len(self.ParentWindow.Values) @@ -429,6 +420,11 @@ if message is not None: wx.CallAfter(self.ShowMessage, message) + return False + + return True + + return True def ShowMessage(self, message): message = wx.MessageDialog(self.ParentWindow, message, _("Error"), wx.OK | wx.ICON_ERROR) @@ -447,7 +443,7 @@ wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL) self.VARIABLE_CHOICES_DICT = dict([(_(_class), _class) for - _class in GetFilterChoiceTransfer().iterkeys()]) + _class in GetFilterChoiceTransfer().keys()]) self.MainSizer = wx.FlexGridSizer(cols=1, hgap=10, rows=2, vgap=0) self.MainSizer.AddGrowableCol(0) @@ -456,32 +452,32 @@ 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.MainSizer.Add(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) + controls_sizer.Add(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) + controls_sizer.Add(self.ReturnType) self.DescriptionLabel = wx.StaticText(self, label=_('Description:')) - controls_sizer.AddWindow(self.DescriptionLabel, flag=wx.ALIGN_CENTER_VERTICAL) + controls_sizer.Add(self.DescriptionLabel, flag=wx.ALIGN_CENTER_VERTICAL) self.Description = wx.TextCtrl(self, size=wx.Size(250, -1), style=wx.TE_PROCESS_ENTER) self.Bind(wx.EVT_TEXT_ENTER, self.OnDescriptionChanged, self.Description) self.Description.Bind(wx.EVT_KILL_FOCUS, self.OnDescriptionChanged) - controls_sizer.AddWindow(self.Description) + controls_sizer.Add(self.Description) class_filter_label = wx.StaticText(self, label=_('Class Filter:')) - controls_sizer.AddWindow(class_filter_label, flag=wx.ALIGN_CENTER_VERTICAL) + controls_sizer.Add(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) + controls_sizer.Add(self.ClassFilter) for name, bitmap, help in [ ("AddButton", "add_element", _("Add variable")), @@ -490,19 +486,19 @@ ("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) + button.SetToolTip(help) setattr(self, name, button) - controls_sizer.AddWindow(button) + controls_sizer.Add(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.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGED, 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.MainSizer.Add(self.VariablesGrid, flag=wx.GROW) self.SetSizer(self.MainSizer) @@ -575,14 +571,13 @@ 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(): + for filter, choice in list(self.FilterChoiceTransfer.items()): reverse_transfer[choice] = filter self.ClassFilter.SetStringSelection(_(reverse_transfer[self.Filter])) self.RefreshTypeList() @@ -610,7 +605,7 @@ model = re.compile(r"%[IQM][XBWLD]?(.*\.|)") prefix = model.match(old_location).group(0) addr = int(re.split(model, old_location)[-1]) + 1 - row_content.Location = prefix + text(addr) + row_content.Location = prefix + str(addr) if not row_content.Class: row_content.Class = self.DefaultTypes.get(self.Filter, self.Filter) @@ -690,9 +685,6 @@ 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) @@ -848,20 +840,20 @@ # build a submenu containing standard IEC types base_menu = wx.Menu(title='') for base_type in self.Controler.GetBaseTypes(): - item = base_menu.Append(wx.ID_ANY, help='', kind=wx.ITEM_NORMAL, text=base_type) + item = base_menu.Append(wx.ID_ANY, helpString='', kind=wx.ITEM_NORMAL, item=base_type) self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(base_type), item) - type_menu.AppendMenu(wx.ID_ANY, _("Base Types"), base_menu) + type_menu.Append(wx.ID_ANY, _("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: - item = datatype_menu.Append(wx.ID_ANY, help='', kind=wx.ITEM_NORMAL, text=datatype) + item = datatype_menu.Append(wx.ID_ANY, helpString='', kind=wx.ITEM_NORMAL, item=datatype) self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(datatype), item) - type_menu.AppendMenu(wx.ID_ANY, _("User Data Types"), datatype_menu) + type_menu.Append(wx.ID_ANY, _("User Data Types"), datatype_menu) def BuildLibsTypesMenu(self, type_menu): for category in self.Controler.GetConfNodeDataTypes(): @@ -869,10 +861,10 @@ # build a submenu containing confnode types confnode_datatype_menu = wx.Menu(title='') for datatype in category["list"]: - item = confnode_datatype_menu.Append(wx.ID_ANY, help='', kind=wx.ITEM_NORMAL, text=datatype) + item = confnode_datatype_menu.Append(wx.ID_ANY, helpString='', kind=wx.ITEM_NORMAL, item=datatype) self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(datatype), item) - type_menu.AppendMenu(wx.ID_ANY, category["name"], confnode_datatype_menu) + type_menu.Append(wx.ID_ANY, category["name"], confnode_datatype_menu) def BuildProjectTypesMenu(self, type_menu, classtype): # build a submenu containing function block types @@ -883,13 +875,13 @@ functionblock_menu = wx.Menu(title='') fbtypes = self.Controler.GetFunctionBlockTypes(self.TagName) for functionblock_type in fbtypes: - item = functionblock_menu.Append(wx.ID_ANY, help='', kind=wx.ITEM_NORMAL, text=functionblock_type) + item = functionblock_menu.Append(wx.ID_ANY, helpString='', kind=wx.ITEM_NORMAL, item=functionblock_type) self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(functionblock_type), item) - type_menu.AppendMenu(wx.ID_ANY, _("Function Block Types"), functionblock_menu) + type_menu.Append(wx.ID_ANY, _("Function Block Types"), functionblock_menu) def BuildArrayTypesMenu(self, type_menu): - item = type_menu.Append(wx.ID_ANY, help='', kind=wx.ITEM_NORMAL, text=_("Array")) + item = type_menu.Append(wx.ID_ANY, helpString='', kind=wx.ITEM_NORMAL, item=_("Array")) self.Bind(wx.EVT_MENU, self.VariableArrayTypeFunction, item) def OnVariablesGridEditorShown(self, event): @@ -916,7 +908,7 @@ corner_y = rect.y + self.VariablesGrid.GetColLabelSize() # pop up this new menu - self.VariablesGrid.PopupMenuXY(type_menu, corner_x, corner_y) + self.VariablesGrid.PopupMenu(type_menu, corner_x, corner_y) type_menu.Destroy() event.Veto() value = self.Values[row].Type @@ -996,7 +988,7 @@ def AddVariableHighlight(self, infos, highlight_type): if isinstance(infos[0], tuple): - for i in xrange(*infos[0]): + for i in range(*infos[0]): self.Table.AddHighlight((i,) + infos[1:], highlight_type) cell_visible = infos[0][0] else: @@ -1008,7 +1000,7 @@ def RemoveVariableHighlight(self, infos, highlight_type): if isinstance(infos[0], tuple): - for i in xrange(*infos[0]): + for i in range(*infos[0]): self.Table.RemoveHighlight((i,) + infos[1:], highlight_type) else: self.Table.RemoveHighlight(infos, highlight_type) diff -r 7e17f7e02a2b -r 0b3ac94f494c controls/__init__.py --- a/controls/__init__.py Wed Nov 29 11:54:56 2023 +0100 +++ b/controls/__init__.py Thu Dec 07 22:41:32 2023 +0100 @@ -24,7 +24,7 @@ # Package initialization -from __future__ import absolute_import + from controls.CustomEditableListBox import CustomEditableListBox from controls.CustomGrid import CustomGrid diff -r 7e17f7e02a2b -r 0b3ac94f494c dialogs/AboutDialog.py --- a/dialogs/AboutDialog.py Wed Nov 29 11:54:56 2023 +0100 +++ b/dialogs/AboutDialog.py Thu Dec 07 22:41:32 2023 +0100 @@ -31,9 +31,10 @@ """ -from __future__ import absolute_import + import os import wx +import wx.adv from wx.lib.agw.hyperlink import HyperLinkCtrl @@ -52,7 +53,8 @@ image = None if self.info.Icon: - bitmap = wx.BitmapFromIcon(self.info.Icon) + bitmap = wx.Bitmap() + bitmap.CopyFromIcon(self.info.Icon) image = wx.StaticBitmap(self, bitmap=bitmap) name = wx.StaticText(self, label="%s %s" % (info.Name, info.Version)) @@ -120,8 +122,8 @@ developer = wx.TextCtrl(notebook, style=wx.TE_READONLY | wx.TE_MULTILINE) translators = wx.TextCtrl(notebook, style=wx.TE_READONLY | wx.TE_MULTILINE) - developer.SetValue(u'\n'.join(info.Developers)) - translators.SetValue(u'\n'.join(info.Translators)) + developer.SetValue('\n'.join(info.Developers)) + translators.SetValue('\n'.join(info.Translators)) notebook.AddPage(developer, text=_("Written by")) notebook.AddPage(translators, text=_("Translated by")) @@ -172,4 +174,4 @@ if os.name == "nt": AboutDialog(parent, info) else: - wx.AboutBox(info) + wx.adv.AboutBox(info) diff -r 7e17f7e02a2b -r 0b3ac94f494c dialogs/ActionBlockDialog.py --- a/dialogs/ActionBlockDialog.py Wed Nov 29 11:54:56 2023 +0100 +++ b/dialogs/ActionBlockDialog.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,7 +23,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx import wx.grid import wx.lib.buttons @@ -85,35 +85,29 @@ readonly = False colname = self.GetColLabelValue(col, False) if colname == "Qualifier": - editor = wx.grid.GridCellChoiceEditor() - editor.SetParameters(self.Parent.QualifierList) + editor = wx.grid.GridCellChoiceEditor(self.Parent.QualifierList) if colname == "Duration": editor = wx.grid.GridCellTextEditor() renderer = wx.grid.GridCellStringRenderer() readonly = not self.Parent.DurationList[self.data[row].qualifier] elif colname == "Type": - editor = wx.grid.GridCellChoiceEditor() - editor.SetParameters(self.Parent.TypeList) + editor = wx.grid.GridCellChoiceEditor(self.Parent.TypeList) elif colname == "Value": value_type = self.data[row].type if value_type == "Action": - editor = wx.grid.GridCellChoiceEditor() - editor.SetParameters(self.Parent.ActionList) + editor = wx.grid.GridCellChoiceEditor(self.Parent.ActionList) elif value_type == "Variable": - editor = wx.grid.GridCellChoiceEditor() - editor.SetParameters(self.Parent.VariableList) + editor = wx.grid.GridCellChoiceEditor(self.Parent.VariableList) elif value_type == "Inline": editor = wx.grid.GridCellTextEditor() renderer = wx.grid.GridCellStringRenderer() elif colname == "Indicator": - editor = wx.grid.GridCellChoiceEditor() - editor.SetParameters(self.Parent.VariableList) + editor = wx.grid.GridCellChoiceEditor(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) # ------------------------------------------------------------------------------- @@ -133,11 +127,11 @@ 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, + main_sizer.Add(top_sizer, border=20, 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) + top_sizer.Add(actions_label, flag=wx.ALIGN_BOTTOM) for name, bitmap, help in [ ("AddButton", "add_element", _("Add action")), @@ -147,28 +141,28 @@ button = wx.lib.buttons.GenBitmapButton( self, bitmap=GetBitmap(bitmap), size=wx.Size(28, 28), style=wx.NO_BORDER) - button.SetToolTipString(help) + button.SetToolTip(help) setattr(self, name, button) - top_sizer.AddWindow(button) + top_sizer.Add(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_CHANGING, self.OnActionsGridCellChange) - main_sizer.AddSizer(self.ActionsGrid, border=20, + main_sizer.Add(self.ActionsGrid, 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, + self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetAffirmativeId()) + main_sizer.Add(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)) + self.TypeList = list(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] @@ -201,15 +195,15 @@ 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) + def SetQualifierList(self, odict): + self.QualifierList = [qname for qname in odict] + self.DurationList = odict + + def SetVariableList(self, lst): + self.VariableList = [variable.Name for variable in lst] + + def SetActionList(self, lst): + self.ActionList = lst def SetValues(self, actions): for action in actions: diff -r 7e17f7e02a2b -r 0b3ac94f494c dialogs/ArrayTypeDialog.py --- a/dialogs/ArrayTypeDialog.py Wed Nov 29 11:54:56 2023 +0100 +++ b/dialogs/ArrayTypeDialog.py Thu Dec 07 22:41:32 2023 +0100 @@ -22,7 +22,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import re import wx @@ -50,31 +50,31 @@ main_sizer.AddGrowableRow(1) top_sizer = wx.BoxSizer(wx.HORIZONTAL) - main_sizer.AddSizer(top_sizer, border=20, + main_sizer.Add(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) + top_sizer.Add(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) + top_sizer.Add(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)) + style=(wx.adv.EL_ALLOW_NEW | + wx.adv.EL_ALLOW_EDIT | + wx.adv.EL_ALLOW_DELETE)) for func in ["_OnLabelEndEdit", "_OnAddButton", "_OnDelButton", "_OnUpButton", "_OnDownButton"]: setattr(self.Dimensions, func, self.OnDimensionsChanged) - main_sizer.AddSizer(self.Dimensions, border=20, + main_sizer.Add(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, + self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetAffirmativeId()) + main_sizer.Add(button_sizer, border=20, flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) self.SetSizer(main_sizer) @@ -84,7 +84,7 @@ if isinstance(infos, tuple) and infos[0] == "array": self.BaseType.SetStringSelection(infos[1]) - self.Dimensions.SetStrings(map("..".join, infos[2])) + self.Dimensions.SetStrings(list(map("..".join, infos[2]))) elif infos in datatypes: self.BaseType.SetStringSelection(infos) diff -r 7e17f7e02a2b -r 0b3ac94f494c dialogs/BlockPreviewDialog.py --- a/dialogs/BlockPreviewDialog.py Wed Nov 29 11:54:56 2023 +0100 +++ b/dialogs/BlockPreviewDialog.py Thu Dec 07 22:41:32 2023 +0100 @@ -24,8 +24,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import division + + import wx from plcopen.structures import TestIdentifier, IEC_KEYWORDS @@ -75,8 +75,7 @@ # Add default dialog buttons sizer self.ButtonSizer = self.CreateButtonSizer(wx.OK | wx.CANCEL | wx.CENTRE) - self.Bind(wx.EVT_BUTTON, self.OnOK, - self.ButtonSizer.GetAffirmativeButton()) + self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetAffirmativeId()) self.Element = None # Graphic element to display in preview self.MinElementSize = None # Graphic element minimal size @@ -88,13 +87,6 @@ # 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, @@ -120,7 +112,7 @@ # Create a sizer for dividing parameters in two columns self.ColumnSizer = wx.BoxSizer(wx.HORIZONTAL) - self.MainSizer.AddSizer(self.ColumnSizer, border=20, + self.MainSizer.Add(self.ColumnSizer, border=20, flag=wx.GROW | wx.TOP | wx.LEFT | wx.RIGHT) # Create a sizer for left column @@ -129,7 +121,7 @@ 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, + self.ColumnSizer.Add(self.LeftGridSizer, 1, border=5, flag=wx.GROW | wx.RIGHT | wx.EXPAND) # Create a sizer for right column @@ -138,7 +130,7 @@ 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, + self.ColumnSizer.Add(self.RightGridSizer, 1, border=5, flag=wx.GROW | wx.LEFT) self.SetSizer(self.MainSizer) diff -r 7e17f7e02a2b -r 0b3ac94f494c dialogs/BrowseLocationsDialog.py --- a/dialogs/BrowseLocationsDialog.py Wed Nov 29 11:54:56 2023 +0100 +++ b/dialogs/BrowseLocationsDialog.py Thu Dec 07 22:41:32 2023 +0100 @@ -24,7 +24,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.# -from __future__ import absolute_import + import wx from plcopen.structures import LOCATIONDATATYPES @@ -54,7 +54,7 @@ # turn LOCATIONDATATYPES inside-out LOCATION_SIZES = {} -for size, types in LOCATIONDATATYPES.iteritems(): +for size, types in LOCATIONDATATYPES.items(): for type in types: LOCATION_SIZES[type] = size @@ -76,7 +76,7 @@ main_sizer.AddGrowableRow(1) locations_label = wx.StaticText(self, label=_('Locations available:')) - main_sizer.AddWindow(locations_label, border=20, + main_sizer.Add(locations_label, border=20, flag=wx.TOP | wx.LEFT | wx.RIGHT | wx.GROW) self.LocationsTree = wx.TreeCtrl(self, @@ -88,7 +88,7 @@ self.LocationsTree.SetInitialSize(wx.Size(-1, 300)) self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnLocationsTreeItemActivated, self.LocationsTree) - main_sizer.AddWindow(self.LocationsTree, border=20, + main_sizer.Add(self.LocationsTree, border=20, flag=wx.LEFT | wx.RIGHT | wx.GROW) self.RenameCheckBox = wx.CheckBox(self, label=_("Rename variable to signal name")) @@ -97,37 +97,37 @@ self.RenameCheckBox.SetValue(default_checked) self.do_rename = default_checked - main_sizer.AddWindow(self.RenameCheckBox, border=20, + main_sizer.Add(self.RenameCheckBox, 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, + main_sizer.Add(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, + button_gridsizer.Add(direction_label, 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, + button_gridsizer.Add(self.DirFilterChoice, flag=wx.GROW | wx.ALIGN_CENTER_VERTICAL) filter_label = wx.StaticText(self, label=_('Type:')) - button_gridsizer.AddWindow(filter_label, + button_gridsizer.Add(filter_label, 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, + button_gridsizer.Add(self.TypeFilterChoice, 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.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetAffirmativeId()) + button_gridsizer.Add(button_sizer, flag=wx.ALIGN_RIGHT) self.SetSizer(main_sizer) @@ -204,7 +204,7 @@ item, root_cookie = self.LocationsTree.GetNextChild(root, root_cookie) else: self.LocationsTree.SetItemText(item, infos["name"]) - self.LocationsTree.SetPyData(item, infos) + self.LocationsTree.SetItemData(item, infos) self.LocationsTree.SetItemImage(item, self.TreeImageDict[infos["type"]]) self.GenerateLocationsTreeBranch(item, children) item, root_cookie = self.LocationsTree.GetNextChild(root, root_cookie) @@ -215,7 +215,7 @@ self.LocationsTree.Delete(item) def OnLocationsTreeItemActivated(self, event): - infos = self.LocationsTree.GetPyData(event.GetItem()) + infos = self.LocationsTree.GetItemData(event.GetItem()) if infos["type"] not in [LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP]: wx.CallAfter(self.EndModal, wx.ID_OK) event.Skip() @@ -226,7 +226,7 @@ def GetValues(self): selected = self.LocationsTree.GetSelection() - infos = self.LocationsTree.GetPyData(selected) + infos = self.LocationsTree.GetItemData(selected) if not self.do_rename: infos["var_name"] = None return infos @@ -237,7 +237,7 @@ selected = self.LocationsTree.GetSelection() var_infos = None if selected.IsOk(): - var_infos = self.LocationsTree.GetPyData(selected) + var_infos = self.LocationsTree.GetItemData(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.ShowModal() diff -r 7e17f7e02a2b -r 0b3ac94f494c dialogs/BrowseValuesLibraryDialog.py --- a/dialogs/BrowseValuesLibraryDialog.py Wed Nov 29 11:54:56 2023 +0100 +++ b/dialogs/BrowseValuesLibraryDialog.py Thu Dec 07 22:41:32 2023 +0100 @@ -24,7 +24,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx @@ -51,13 +51,13 @@ self.ButtonSizer = self.CreateButtonSizer(wx.OK | wx.CANCEL | wx.CENTRE) - self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.ButtonSizer.GetAffirmativeButton().GetId()) + self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetAffirmativeId()) 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.Add(self.staticText1, 0, border=20, flag=wx.GROW | wx.TOP | wx.LEFT | wx.RIGHT) + self.flexGridSizer1.Add(self.ValuesLibrary, 0, border=20, flag=wx.GROW | wx.LEFT | wx.RIGHT) + self.flexGridSizer1.Add(self.ButtonSizer, 0, border=20, flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) self.flexGridSizer1.AddGrowableCol(0) self.flexGridSizer1.AddGrowableRow(1) @@ -79,11 +79,11 @@ def GetValueInfos(self): selected = self.ValuesLibrary.GetSelection() - return self.ValuesLibrary.GetPyData(selected) + return self.ValuesLibrary.GetItemData(selected) def OnOK(self, event): selected = self.ValuesLibrary.GetSelection() - if not selected.IsOk() or self.ValuesLibrary.GetPyData(selected) is None: + if not selected.IsOk() or self.ValuesLibrary.GetItemData(selected) is None: message = wx.MessageDialog(self, _("No valid value selected!"), _("Error"), wx.OK | wx.ICON_ERROR) message.ShowModal() message.Destroy() diff -r 7e17f7e02a2b -r 0b3ac94f494c dialogs/CommentEditDialog.py --- a/dialogs/CommentEditDialog.py Wed Nov 29 11:54:56 2023 +0100 +++ b/dialogs/CommentEditDialog.py Thu Dec 07 22:41:32 2023 +0100 @@ -20,7 +20,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx diff -r 7e17f7e02a2b -r 0b3ac94f494c dialogs/ConnectionDialog.py --- a/dialogs/ConnectionDialog.py Wed Nov 29 11:54:56 2023 +0100 +++ b/dialogs/ConnectionDialog.py Thu Dec 07 22:41:32 2023 +0100 @@ -24,7 +24,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx from graphics.GraphicCommons import CONNECTOR, CONTINUATION @@ -59,7 +59,7 @@ # Create label for connection type type_label = wx.StaticText(self, label=_('Type:')) - self.LeftGridSizer.AddWindow(type_label, flag=wx.GROW) + self.LeftGridSizer.Add(type_label, flag=wx.GROW) # Create radio buttons for selecting connection type self.TypeRadioButtons = {} @@ -70,39 +70,39 @@ 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.LeftGridSizer.Add(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) + self.LeftGridSizer.Add(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.Bind(wx.EVT_TEXT, self.OnNameChanged, self.ConnectionName) - self.LeftGridSizer.AddWindow(self.ConnectionName, flag=wx.GROW) + self.LeftGridSizer.Add(self.ConnectionName, flag=wx.GROW) # Add preview panel and associated label to sizers self.Preview.SetMinSize(wx.Size(-1, 100)) - self.LeftGridSizer.AddWindow(self.PreviewLabel, flag=wx.GROW) - self.LeftGridSizer.AddWindow(self.Preview, flag=wx.GROW) + self.LeftGridSizer.Add(self.PreviewLabel, flag=wx.GROW) + self.LeftGridSizer.Add(self.Preview, flag=wx.GROW) # Add buttons sizer to sizers - self.MainSizer.AddSizer( + self.MainSizer.Add( self.ButtonSizer, border=20, flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) - self.ColumnSizer.RemoveSizer(self.RightGridSizer) + self.ColumnSizer.Remove(self.RightGridSizer) # Add button for applying connection name modification to all connection # of POU if apply_button: self.ApplyToAllButton = wx.Button(self, label=_("Propagate Name")) - self.ApplyToAllButton.SetToolTipString( + self.ApplyToAllButton.SetToolTip( _("Apply name modification to all continuations with the same name")) self.Bind(wx.EVT_BUTTON, self.OnApplyToAll, self.ApplyToAllButton) - self.ButtonSizer.AddWindow(self.ApplyToAllButton, flag=wx.LEFT) + self.ButtonSizer.Add(self.ApplyToAllButton, flag=wx.LEFT) else: self.ConnectionName.ChangeValue( controller.GenerateNewName( @@ -127,7 +127,7 @@ @param values: Connection parameters values """ # For each parameters defined, set corresponding control value - for name, value in values.items(): + for name, value in list(values.items()): # Parameter is connection type if name == "type": diff -r 7e17f7e02a2b -r 0b3ac94f494c dialogs/DurationEditorDialog.py --- a/dialogs/DurationEditorDialog.py Wed Nov 29 11:54:56 2023 +0100 +++ b/dialogs/DurationEditorDialog.py Thu Dec 07 22:41:32 2023 +0100 @@ -24,8 +24,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import division + + import re import wx @@ -68,7 +68,7 @@ main_sizer.AddGrowableRow(0) controls_sizer = wx.FlexGridSizer(cols=len(CONTROLS), hgap=10, rows=2, vgap=10) - main_sizer.AddSizer(controls_sizer, border=20, + main_sizer.Add(controls_sizer, border=20, flag=wx.TOP | wx.LEFT | wx.RIGHT | wx.GROW) controls = [] @@ -85,14 +85,14 @@ controls.append((st, txtctrl)) for st, txtctrl in controls: - controls_sizer.AddWindow(st, flag=wx.GROW) + controls_sizer.Add(st, flag=wx.GROW) for st, txtctrl in controls: - controls_sizer.AddWindow(txtctrl, flag=wx.GROW) + controls_sizer.Add(txtctrl, 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, + self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetAffirmativeId()) + main_sizer.Add(button_sizer, border=20, flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) self.SetSizer(main_sizer) @@ -153,6 +153,7 @@ return duration def OnOK(self, event): + event.Skip() self.OnCloseDialog() def OnCloseDialog(self): diff -r 7e17f7e02a2b -r 0b3ac94f494c dialogs/FBDBlockDialog.py --- a/dialogs/FBDBlockDialog.py Wed Nov 29 11:54:56 2023 +0100 +++ b/dialogs/FBDBlockDialog.py Thu Dec 07 22:41:32 2023 +0100 @@ -24,7 +24,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import re import wx @@ -68,7 +68,7 @@ # 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) + self.LeftGridSizer.Add(left_staticboxsizer, border=5, flag=wx.GROW) # Create Library panel and add it to static box self.LibraryPanel = LibraryPanel(self) @@ -77,65 +77,65 @@ # Set function to call when selection in Library panel changed setattr(self.LibraryPanel, "_OnTreeItemSelected", self.OnLibraryTreeItemSelected) - left_staticboxsizer.AddWindow(self.LibraryPanel, 1, border=5, + left_staticboxsizer.Add(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) + self.RightGridSizer.Add(top_right_gridsizer, flag=wx.GROW) # Create label for block name name_label = wx.StaticText(self, label=_('Name:')) - top_right_gridsizer.AddWindow(name_label, + top_right_gridsizer.Add(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) + top_right_gridsizer.Add(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, + top_right_gridsizer.Add(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) self.Bind(wx.EVT_SPINCTRL, self.OnInputsChanged, self.Inputs) - top_right_gridsizer.AddWindow(self.Inputs, flag=wx.GROW) + top_right_gridsizer.Add(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, + top_right_gridsizer.Add(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.ExecutionOrder) - top_right_gridsizer.AddWindow(self.ExecutionOrder, flag=wx.GROW) + top_right_gridsizer.Add(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, + top_right_gridsizer.Add(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.ExecutionControl) - top_right_gridsizer.AddWindow(self.ExecutionControl, flag=wx.GROW) + top_right_gridsizer.Add(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) + self.RightGridSizer.Add(self.PreviewLabel, flag=wx.GROW) + self.RightGridSizer.Add(self.Preview, flag=wx.GROW) # Add buttons sizer to sizers - self.MainSizer.AddSizer(self.ButtonSizer, border=20, + self.MainSizer.Add(self.ButtonSizer, border=20, flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) # Dictionary containing correspondence between parameter exchanged and @@ -177,7 +177,7 @@ default_name_model = GetBlockTypeDefaultNameModel(blocktype) # For each parameters defined, set corresponding control value - for name, value in values.items(): + for name, value in list(values.items()): # Parameter is block name if name == "name": @@ -212,7 +212,7 @@ values["width"], values["height"] = self.Element.GetSize() values.update({ name: control.GetValue() - for name, control in self.ParamsControl.iteritems()}) + for name, control in self.ParamsControl.items()}) return values def OnOK(self, event): diff -r 7e17f7e02a2b -r 0b3ac94f494c dialogs/FBDVariableDialog.py --- a/dialogs/FBDVariableDialog.py Wed Nov 29 11:54:56 2023 +0100 +++ b/dialogs/FBDVariableDialog.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,7 +23,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx from graphics.GraphicCommons import INPUT, INOUT, OUTPUT @@ -66,61 +66,61 @@ } self.VARIABLE_CLASSES_DICT_REVERSE = dict( - [(value, key) for key, value in self.VARIABLE_CLASSES_DICT.iteritems()]) + [(value, key) for key, value in self.VARIABLE_CLASSES_DICT.items()]) # 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) + self.LeftGridSizer.Add(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) + self.LeftGridSizer.Add(self.Class, flag=wx.GROW) # Create label for variable execution order execution_order_label = wx.StaticText(self, label=_('Execution Order:')) - self.LeftGridSizer.AddWindow(execution_order_label, flag=wx.GROW) + self.LeftGridSizer.Add(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.ExecutionOrder) - self.LeftGridSizer.AddWindow(self.ExecutionOrder, flag=wx.GROW) + self.LeftGridSizer.Add(self.ExecutionOrder, flag=wx.GROW) # Create label for variable expression name_label = wx.StaticText(self, label=_('Expression:')) - self.RightGridSizer.AddWindow(name_label, border=5, + self.RightGridSizer.Add(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) + self.RightGridSizer.Add(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.Bind(wx.EVT_LISTBOX, self.OnNameChanged, self.VariableName) - self.RightGridSizer.AddWindow(self.VariableName, border=4, flag=wx.GROW | wx.TOP) + self.RightGridSizer.Add(self.VariableName, border=4, flag=wx.GROW | wx.TOP) # Add preview panel and associated label to sizers - self.MainSizer.AddWindow(self.PreviewLabel, border=20, + self.MainSizer.Add(self.PreviewLabel, border=20, flag=wx.GROW | wx.LEFT | wx.RIGHT) - self.MainSizer.AddWindow(self.Preview, border=20, + self.MainSizer.Add(self.Preview, border=20, flag=wx.GROW | wx.LEFT | wx.RIGHT) # Add buttons sizer to sizers - self.MainSizer.AddSizer( + self.MainSizer.Add( 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 self.VARIABLE_CLASSES_DICT.iteritems(): + for var_class, choice in self.VARIABLE_CLASSES_DICT.items(): if not exclude_input or var_class != INPUT: self.Class.Append(choice) self.Class.SetSelection(0) @@ -148,7 +148,7 @@ # 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(): + for name, (var_type, _value_type) in self.VariableList.items(): if var_type != "Input" or var_class == INPUT: self.VariableName.Append(name) @@ -178,7 +178,7 @@ self.RefreshNameList() # For each parameters defined, set corresponding control value - for name, value in values.items(): + for name, value in list(values.items()): # Parameter is variable expression if name == "expression": diff -r 7e17f7e02a2b -r 0b3ac94f494c dialogs/FindInPouDialog.py --- a/dialogs/FindInPouDialog.py Wed Nov 29 11:54:56 2023 +0100 +++ b/dialogs/FindInPouDialog.py Thu Dec 07 22:41:32 2023 +0100 @@ -24,7 +24,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx from plcopen.plcopen import * @@ -48,76 +48,76 @@ main_sizer.AddGrowableRow(0) controls_sizer = wx.BoxSizer(wx.VERTICAL) - main_sizer.AddSizer(controls_sizer, border=20, + main_sizer.Add(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.Add(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) + patterns_sizer.Add(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) + patterns_sizer.Add(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.Add(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, + params_sizer.Add(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, + direction_staticboxsizer.Add(self.Forward, border=5, flag=wx.ALL | wx.GROW) self.Backward = wx.RadioButton(panel, label=_("Backward")) - direction_staticboxsizer.AddWindow(self.Backward, border=5, + direction_staticboxsizer.Add(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) + params_sizer.Add(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, + options_staticboxsizer.Add(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, + options_staticboxsizer.Add(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, + options_staticboxsizer.Add(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, + main_sizer.Add(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) + buttons_sizer.Add(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) + buttons_sizer.Add(self.CloseButton) # 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) - controls_sizer.AddWindow(self.StatusLabel, flag=wx.ALIGN_CENTER_VERTICAL) + controls_sizer.Add(self.StatusLabel) panel.SetSizer(main_sizer) main_sizer.Fit(self) diff -r 7e17f7e02a2b -r 0b3ac94f494c dialogs/ForceVariableDialog.py --- a/dialogs/ForceVariableDialog.py Wed Nov 29 11:54:56 2023 +0100 +++ b/dialogs/ForceVariableDialog.py Thu Dec 07 22:41:32 2023 +0100 @@ -22,10 +22,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import import re import datetime -from builtins import str as text import wx @@ -189,7 +187,7 @@ info_sizer = wx.BoxSizer(wx.VERTICAL) message_label = wx.StaticText(self, label=_("Forcing Variable Value")) - info_sizer.AddWindow(message_label, border=10, + info_sizer.Add(message_label, border=10, flag=wx.ALIGN_LEFT | wx.GROW | wx.TOP | wx.LEFT | wx.RIGHT) if GetTypeValue[self.IEC_Type] in [getinteger, getfloat]: @@ -201,8 +199,8 @@ self.GetEnteredValue = self.GetValueDefault button_sizer = self.CreateButtonSizer(wx.OK | wx.CANCEL | wx.CENTRE) - self.Bind(wx.EVT_BUTTON, self.OnOK, button_sizer.GetAffirmativeButton()) - info_sizer.AddSizer(button_sizer, border=10, flag=wx.ALIGN_RIGHT | wx.ALL) + self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetAffirmativeId()) + info_sizer.Add(button_sizer, border=10, flag=wx.ALIGN_RIGHT | wx.ALL) self.SetSizer(info_sizer) self.Fit() @@ -216,7 +214,7 @@ """Add simple text control to change variable of any type""" self.ValueCtrl = wx.TextCtrl(self) self.ValueCtrl.SetValue(defaultValue) - info_sizer.AddWindow(self.ValueCtrl, border=10, proportion=1, + info_sizer.Add(self.ValueCtrl, border=10, proportion=1, flag=wx.ALIGN_LEFT | wx.GROW | wx.TOP | wx.LEFT | wx.RIGHT) def GetValueDefault(self): @@ -224,7 +222,7 @@ Returns text representation for a variable value @return: variable value as a string """ - return text(self.ValueCtrl.GetValue()) + return str(self.ValueCtrl.GetValue()) # ----------------------------------------------- # integer and floating point number type methods @@ -235,11 +233,11 @@ sizer = wx.BoxSizer(wx.HORIZONTAL) self.InitCtrlDefault(sizer, defaultValue) self.SpinButtonCtrl = wx.SpinButton(self, style=wx.HORIZONTAL | wx.SP_WRAP) - sizer.AddWindow(self.SpinButtonCtrl, border=10, + sizer.Add(self.SpinButtonCtrl, border=10, flag=wx.ALIGN_LEFT | wx.GROW | wx.TOP | wx.LEFT | wx.RIGHT | wx.EXPAND) self.Bind(wx.EVT_SPIN_UP, self.SpinButtonChanged) self.Bind(wx.EVT_SPIN_DOWN, self.SpinButtonChanged) - info_sizer.AddWindow(sizer, proportion=1, flag=wx.EXPAND) + info_sizer.Add(sizer, proportion=1, flag=wx.EXPAND) def SpinButtonChanged(self, evt): """Increment/decrement variable value""" @@ -247,7 +245,7 @@ if value is not None: up = evt.GetEventType() == wx.EVT_SPIN_UP._getEvtType() value = value + 1 if up else value - 1 - self.ValueCtrl.SetValue(text(value)) + self.ValueCtrl.SetValue(str(value)) evt.Skip() # ----------------------------------------------- @@ -261,7 +259,7 @@ if value is not None: self.ValueCtrl.SetValue(value) - info_sizer.AddWindow(self.ValueCtrl, border=10, + info_sizer.Add(self.ValueCtrl, border=10, flag=wx.ALIGN_LEFT | wx.GROW | wx.TOP | wx.LEFT | wx.RIGHT | wx.GROW) def OnOK(self, event): diff -r 7e17f7e02a2b -r 0b3ac94f494c dialogs/IDManager.py --- a/dialogs/IDManager.py Wed Nov 29 11:54:56 2023 +0100 +++ b/dialogs/IDManager.py Thu Dec 07 22:41:32 2023 +0100 @@ -1,4 +1,4 @@ -from __future__ import absolute_import + import wx from controls.IDBrowser import IDBrowser diff -r 7e17f7e02a2b -r 0b3ac94f494c dialogs/IDMergeDialog.py --- a/dialogs/IDMergeDialog.py Wed Nov 29 11:54:56 2023 +0100 +++ b/dialogs/IDMergeDialog.py Thu Dec 07 22:41:32 2023 +0100 @@ -3,7 +3,7 @@ # See COPYING file for copyrights details. -from __future__ import absolute_import + import wx @@ -15,11 +15,11 @@ main_sizer = wx.BoxSizer(wx.VERTICAL) message = wx.StaticText(self, label=question) - main_sizer.AddWindow(message, border=20, + main_sizer.Add(message, border=20, flag=wx.ALIGN_CENTER_HORIZONTAL | wx.TOP | wx.LEFT | wx.RIGHT) self.check = wx.CheckBox(self, label=optiontext) - main_sizer.AddWindow(self.check, border=20, + main_sizer.Add(self.check, border=20, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.ALIGN_CENTER_HORIZONTAL) buttons_sizer = wx.BoxSizer(wx.HORIZONTAL) @@ -30,9 +30,9 @@ return lambda event: self.EndModal(_wxID) self.Bind(wx.EVT_BUTTON, OnButtonFactory(wxID), Button) - buttons_sizer.AddWindow(Button) + buttons_sizer.Add(Button) - main_sizer.AddSizer(buttons_sizer, border=20, + main_sizer.Add(buttons_sizer, border=20, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.ALIGN_RIGHT) self.SetSizer(main_sizer) diff -r 7e17f7e02a2b -r 0b3ac94f494c dialogs/LDElementDialog.py --- a/dialogs/LDElementDialog.py Wed Nov 29 11:54:56 2023 +0100 +++ b/dialogs/LDElementDialog.py Thu Dec 07 22:41:32 2023 +0100 @@ -24,7 +24,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx from graphics.GraphicCommons import CONTACT_NORMAL, CONTACT_REVERSE, \ @@ -63,7 +63,7 @@ # Create label for LD element modifier modifier_label = wx.StaticText(self, label=_('Modifier:')) - self.LeftGridSizer.AddWindow(modifier_label, border=5, + self.LeftGridSizer.Add(modifier_label, border=5, flag=wx.GROW | wx.BOTTOM) # Create radio buttons for selecting LD element modifier @@ -84,13 +84,13 @@ 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.LeftGridSizer.Add(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, + self.LeftGridSizer.Add(element_variable_label, border=5, flag=wx.GROW | wx.TOP) # Create a combo box for defining LD element variable @@ -99,15 +99,15 @@ self.ElementVariable) self.Bind(wx.EVT_TEXT, self.OnVariableChanged, self.ElementVariable) - self.LeftGridSizer.AddWindow(self.ElementVariable, border=5, + self.LeftGridSizer.Add(self.ElementVariable, border=5, 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) + self.RightGridSizer.Add(self.PreviewLabel, flag=wx.GROW) + self.RightGridSizer.Add(self.Preview, flag=wx.GROW) # Add buttons sizer to sizers - self.MainSizer.AddSizer(self.ButtonSizer, border=20, + self.MainSizer.Add(self.ButtonSizer, border=20, flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) # Save LD element class @@ -117,7 +117,7 @@ self.RefreshVariableList() # Set values in ElementVariable - for name, (var_type, value_type) in self.VariableList.iteritems(): + for name, (var_type, value_type) in self.VariableList.items(): # Only select BOOL variable and avoid input for coil if (type == "contact" or var_type != "Input") and \ value_type == "BOOL": @@ -134,7 +134,7 @@ """ # Go through radio buttons and return modifier associated to the one # that is selected - for modifier, control in self.ModifierRadioButtons.iteritems(): + for modifier, control in self.ModifierRadioButtons.items(): if control.GetValue(): return modifier return None @@ -145,7 +145,7 @@ @param values: LD element parameters values """ # For each parameters defined, set corresponding control value - for name, value in values.items(): + for name, value in list(values.items()): # Parameter is LD element variable if name == "variable": @@ -198,8 +198,9 @@ self.GetElementModifier(), value) - button = self.ButtonSizer.GetAffirmativeButton() - button.Enable(value != "") + # FIXME : how to disable OK button when content is not valid + # button = self.ButtonSizer.GetAffirmativeButton() + # button.Enable(value != "") # Call BlockPreviewDialog function BlockPreviewDialog.DrawPreview(self) diff -r 7e17f7e02a2b -r 0b3ac94f494c dialogs/LDPowerRailDialog.py --- a/dialogs/LDPowerRailDialog.py Wed Nov 29 11:54:56 2023 +0100 +++ b/dialogs/LDPowerRailDialog.py Thu Dec 07 22:41:32 2023 +0100 @@ -24,7 +24,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx from graphics.GraphicCommons import LEFTRAIL, RIGHTRAIL @@ -56,7 +56,7 @@ # Create label for connection type type_label = wx.StaticText(self, label=_('Type:')) - self.LeftGridSizer.AddWindow(type_label, flag=wx.GROW) + self.LeftGridSizer.Add(type_label, flag=wx.GROW) # Create radio buttons for selecting power rail type self.TypeRadioButtons = {} @@ -67,27 +67,27 @@ 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.LeftGridSizer.Add(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) + self.LeftGridSizer.Add(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) self.PinNumber.SetValue(1) self.Bind(wx.EVT_SPINCTRL, self.OnPinNumberChanged, self.PinNumber) - self.LeftGridSizer.AddWindow(self.PinNumber, flag=wx.GROW) + self.LeftGridSizer.Add(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) + self.RightGridSizer.Add(self.PreviewLabel, flag=wx.GROW) + self.RightGridSizer.Add(self.Preview, flag=wx.GROW) # Add buttons sizer to sizers - self.MainSizer.AddSizer( + self.MainSizer.Add( self.ButtonSizer, border=20, flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) self.Fit() @@ -118,7 +118,7 @@ @param values: Power rail parameters values """ # For each parameters defined, set corresponding control value - for name, value in values.items(): + for name, value in list(values.items()): # Parameter is power rail type if name == "type": diff -r 7e17f7e02a2b -r 0b3ac94f494c dialogs/MessageBoxOnce.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dialogs/MessageBoxOnce.py Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# This file is part of Beremiz +# Copyright (C) 2022: Edouard TISSERANT +# +# See COPYING file for copyrights details. + + + +import wx + + +# class RichMessageDialog is still not available in wxPython 3.0.2 +class MessageBoxOnce(wx.Dialog): + """ + wx.MessageBox that user can ask not to show again + """ + def __init__(self, title, message, config_key): + self.Config = wx.ConfigBase.Get() + self.config_key = config_key + dont_show = self.Config.Read(self.config_key) == "True" + + if dont_show: + return + + wx.Dialog.__init__(self, None, title=title) + + main_sizer = wx.BoxSizer(wx.VERTICAL) + + message = wx.StaticText(self, label=message) + main_sizer.Add(message, border=20, + flag=wx.ALIGN_CENTER_HORIZONTAL | wx.TOP | wx.LEFT | wx.RIGHT) + + self.check = wx.CheckBox(self, label=_("don't show this message again")) + main_sizer.Add(self.check, border=20, + flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.ALIGN_CENTER_HORIZONTAL) + + buttons_sizer = wx.BoxSizer(wx.HORIZONTAL) + + Button = wx.Button(self, label="OK") + + self.Bind(wx.EVT_BUTTON, self.OnOKButton, Button) + buttons_sizer.Add(Button) + + main_sizer.Add(buttons_sizer, border=20, + flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.ALIGN_RIGHT) + + self.SetSizer(main_sizer) + self.Fit() + + self.ShowModal() + + def OnOKButton(self, event): + if self.check.GetValue(): + self.Config.Write(self.config_key, "True") + self.EndModal(wx.ID_OK) diff -r 7e17f7e02a2b -r 0b3ac94f494c dialogs/PouActionDialog.py --- a/dialogs/PouActionDialog.py Wed Nov 29 11:54:56 2023 +0100 +++ b/dialogs/PouActionDialog.py Thu Dec 07 22:41:32 2023 +0100 @@ -24,7 +24,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx from plcopen.structures import TestIdentifier, IEC_KEYWORDS @@ -50,27 +50,26 @@ infos_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=3, vgap=15) infos_sizer.AddGrowableCol(1) - main_sizer.AddSizer(infos_sizer, border=20, + main_sizer.Add(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, + infos_sizer.Add(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) + infos_sizer.Add(self.ActionName, flag=wx.GROW) language_label = wx.StaticText(self, label=_('Language:')) - infos_sizer.AddWindow(language_label, border=4, + infos_sizer.Add(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) + infos_sizer.Add(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, + self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetAffirmativeId()) + main_sizer.Add(button_sizer, border=20, flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) self.SetSizer(main_sizer) @@ -122,7 +121,7 @@ self.PouElementNames = [element_name.upper() for element_name in element_names] def SetValues(self, values): - for item, value in values.items(): + for item, value in list(values.items()): if item == "actionName": self.ActionName.SetValue(value) elif item == "language": diff -r 7e17f7e02a2b -r 0b3ac94f494c dialogs/PouDialog.py --- a/dialogs/PouDialog.py Wed Nov 29 11:54:56 2023 +0100 +++ b/dialogs/PouDialog.py Thu Dec 07 22:41:32 2023 +0100 @@ -24,7 +24,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx from plcopen.structures import TestIdentifier, IEC_KEYWORDS @@ -58,34 +58,34 @@ infos_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=3, vgap=15) infos_sizer.AddGrowableCol(1) - main_sizer.AddSizer(infos_sizer, border=20, + main_sizer.Add(infos_sizer, border=20, flag=wx.GROW | wx.TOP | wx.LEFT | wx.RIGHT) pouname_label = wx.StaticText(self, label=_('POU Name:')) - infos_sizer.AddWindow(pouname_label, border=4, + infos_sizer.Add(pouname_label, border=4, flag=wx.ALIGN_CENTER_VERTICAL | wx.TOP) self.PouName = wx.TextCtrl(self) - infos_sizer.AddWindow(self.PouName, flag=wx.GROW) + infos_sizer.Add(self.PouName, flag=wx.GROW) poutype_label = wx.StaticText(self, label=_('POU Type:')) - infos_sizer.AddWindow(poutype_label, border=4, + infos_sizer.Add(poutype_label, border=4, 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) - infos_sizer.AddWindow(self.PouType, flag=wx.GROW) + infos_sizer.Add(self.PouType, flag=wx.GROW) language_label = wx.StaticText(self, label=_('Language:')) - infos_sizer.AddWindow(language_label, border=4, + infos_sizer.Add(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) + infos_sizer.Add(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, + self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetAffirmativeId()) + main_sizer.Add(button_sizer, border=20, flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) self.SetSizer(main_sizer) @@ -201,7 +201,7 @@ self.PouElementNames = [element_name.upper() for element_name in element_names] def SetValues(self, values): - for item, value in values.items(): + for item, value in list(values.items()): if item == "pouName": self.PouName.SetValue(value) elif item == "pouType": diff -r 7e17f7e02a2b -r 0b3ac94f494c dialogs/PouNameDialog.py --- a/dialogs/PouNameDialog.py Wed Nov 29 11:54:56 2023 +0100 +++ b/dialogs/PouNameDialog.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,7 +23,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx from plcopen.structures import TestIdentifier, IEC_KEYWORDS @@ -40,8 +40,7 @@ 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, id=self.GetAffirmativeId()) def OnOK(self, event): message = None diff -r 7e17f7e02a2b -r 0b3ac94f494c dialogs/PouTransitionDialog.py --- a/dialogs/PouTransitionDialog.py Wed Nov 29 11:54:56 2023 +0100 +++ b/dialogs/PouTransitionDialog.py Thu Dec 07 22:41:32 2023 +0100 @@ -24,7 +24,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx from plcopen.structures import TestIdentifier, IEC_KEYWORDS @@ -53,26 +53,26 @@ infos_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=3, vgap=10) infos_sizer.AddGrowableCol(1) - main_sizer.AddSizer(infos_sizer, border=20, + main_sizer.Add(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, + infos_sizer.Add(transitionname_label, border=4, flag=wx.ALIGN_CENTER_VERTICAL | wx.TOP) self.TransitionName = wx.TextCtrl(self, size=wx.Size(180, -1)) - infos_sizer.AddWindow(self.TransitionName, flag=wx.GROW) + infos_sizer.Add(self.TransitionName, flag=wx.GROW) language_label = wx.StaticText(self, label=_('Language:')) - infos_sizer.AddWindow(language_label, border=4, + infos_sizer.Add(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) + infos_sizer.Add(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) + self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetAffirmativeId()) + main_sizer.Add(button_sizer, border=20, flag=wx.ALIGN_RIGHT | wx.BOTTOM) self.SetSizer(main_sizer) @@ -123,7 +123,7 @@ self.PouElementNames = [pou_name.upper() for pou_name in pou_names] def SetValues(self, values): - for item, value in values.items(): + for item, value in list(values.items()): if item == "transitionName": self.TransitionName.SetValue(value) elif item == "language": diff -r 7e17f7e02a2b -r 0b3ac94f494c dialogs/ProjectDialog.py --- a/dialogs/ProjectDialog.py Wed Nov 29 11:54:56 2023 +0100 +++ b/dialogs/ProjectDialog.py Thu Dec 07 22:41:32 2023 +0100 @@ -24,7 +24,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx from controls.ProjectPropertiesPanel import ProjectPropertiesPanel @@ -42,12 +42,11 @@ self.ProjectProperties = ProjectPropertiesPanel( self, enable_required=enable_required, scrolling=False) - main_sizer.AddWindow(self.ProjectProperties, flag=wx.GROW) + main_sizer.Add(self.ProjectProperties, flag=wx.GROW) 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, + self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.GetAffirmativeId()) + main_sizer.Add(self.ButtonSizer, border=20, flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) self.SetSizer(main_sizer) diff -r 7e17f7e02a2b -r 0b3ac94f494c dialogs/SFCDivergenceDialog.py --- a/dialogs/SFCDivergenceDialog.py Wed Nov 29 11:54:56 2023 +0100 +++ b/dialogs/SFCDivergenceDialog.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,7 +23,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx from graphics.GraphicCommons import SELECTION_DIVERGENCE, \ @@ -58,7 +58,7 @@ # Create label for divergence type type_label = wx.StaticText(self, label=_('Type:')) - self.LeftGridSizer.AddWindow(type_label, flag=wx.GROW) + self.LeftGridSizer.Add(type_label, flag=wx.GROW) # Create radio buttons for selecting divergence type divergence_buttons = [ @@ -80,7 +80,7 @@ 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.LeftGridSizer.Add(radio_button, flag=wx.GROW) self.TypeRadioButtons[type] = radio_button if first: focusbtn = type @@ -89,19 +89,19 @@ # Create label for number of divergence sequences sequences_label = wx.StaticText(self, label=_('Number of sequences:')) - self.LeftGridSizer.AddWindow(sequences_label, flag=wx.GROW) + self.LeftGridSizer.Add(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) + self.LeftGridSizer.Add(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) + self.RightGridSizer.Add(self.PreviewLabel, flag=wx.GROW) + self.RightGridSizer.Add(self.Preview, flag=wx.GROW) # Add buttons sizer to sizers - self.MainSizer.AddSizer( + self.MainSizer.Add( self.ButtonSizer, border=20, flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) @@ -126,7 +126,7 @@ """ # Go through radio buttons and return type associated to the one that # is selected - for type, control in self.TypeRadioButtons.iteritems(): + for type, control in self.TypeRadioButtons.items(): if control.GetValue(): return type return None diff -r 7e17f7e02a2b -r 0b3ac94f494c dialogs/SFCStepDialog.py --- a/dialogs/SFCStepDialog.py Wed Nov 29 11:54:56 2023 +0100 +++ b/dialogs/SFCStepDialog.py Thu Dec 07 22:41:32 2023 +0100 @@ -24,7 +24,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx from graphics.SFC_Objects import SFC_Step @@ -57,16 +57,16 @@ # Create label for SFC step name name_label = wx.StaticText(self, label=_('Name:')) - self.LeftGridSizer.AddWindow(name_label, flag=wx.GROW) + self.LeftGridSizer.Add(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) + self.LeftGridSizer.Add(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) + self.LeftGridSizer.Add(connectors_label, flag=wx.GROW) # Create check boxes for defining connectors available on SFC step self.ConnectorsCheckBox = {} @@ -77,15 +77,15 @@ if name == "output" or (name == "input" and not initial): check_box.SetValue(True) self.Bind(wx.EVT_CHECKBOX, self.OnConnectorsChanged, check_box) - self.LeftGridSizer.AddWindow(check_box, flag=wx.GROW) + self.LeftGridSizer.Add(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) + self.RightGridSizer.Add(self.PreviewLabel, flag=wx.GROW) + self.RightGridSizer.Add(self.Preview, flag=wx.GROW) # Add buttons sizer to sizers - self.MainSizer.AddSizer( + self.MainSizer.Add( self.ButtonSizer, border=20, flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) @@ -107,7 +107,7 @@ @param values: Block parameters values """ # For each parameters defined, set corresponding control value - for name, value in values.items(): + for name, value in list(values.items()): # Parameter is step name if name == "name": @@ -130,7 +130,7 @@ values = {"name": self.StepName.GetValue()} values.update({ name: control.IsChecked() - for name, control in self.ConnectorsCheckBox.iteritems()}) + for name, control in self.ConnectorsCheckBox.items()}) values["width"], values["height"] = self.Element.GetSize() return values @@ -185,7 +185,7 @@ self.Initial) # Update connectors of SFC step element according to check boxes value - for name, control in self.ConnectorsCheckBox.iteritems(): + for name, control in self.ConnectorsCheckBox.items(): if control.IsChecked(): getattr(self.Element, "Add" + name.capitalize())() else: diff -r 7e17f7e02a2b -r 0b3ac94f494c dialogs/SFCStepNameDialog.py --- a/dialogs/SFCStepNameDialog.py Wed Nov 29 11:54:56 2023 +0100 +++ b/dialogs/SFCStepNameDialog.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,7 +23,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx from plcopen.structures import TestIdentifier, IEC_KEYWORDS @@ -42,8 +42,7 @@ 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, id=self.GetAffirmativeId()) def OnOK(self, event): message = None diff -r 7e17f7e02a2b -r 0b3ac94f494c dialogs/SFCTransitionDialog.py --- a/dialogs/SFCTransitionDialog.py Wed Nov 29 11:54:56 2023 +0100 +++ b/dialogs/SFCTransitionDialog.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,7 +23,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx from graphics.SFC_Objects import SFC_Transition @@ -57,7 +57,7 @@ # Create label for transition type type_label = wx.StaticText(self, label=_('Type:')) - self.LeftGridSizer.AddWindow(type_label, flag=wx.GROW) + self.LeftGridSizer.Add(type_label, flag=wx.GROW) # Create combo box for selecting reference value reference = wx.ComboBox(self, style=wx.CB_READONLY) @@ -80,28 +80,28 @@ 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.LeftGridSizer.Add(radio_button, flag=wx.GROW) if control is not None: control.Enable(first) - self.LeftGridSizer.AddWindow(control, flag=wx.GROW) + self.LeftGridSizer.Add(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) + self.LeftGridSizer.Add(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) + self.LeftGridSizer.Add(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) + self.RightGridSizer.Add(self.PreviewLabel, flag=wx.GROW) + self.RightGridSizer.Add(self.Preview, flag=wx.GROW) # Add buttons sizer to sizers - self.MainSizer.AddSizer( + self.MainSizer.Add( self.ButtonSizer, border=20, flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) @@ -117,7 +117,7 @@ """ # Go through radio buttons and return type and value associated to the # one that is selected - for type, (radio, control) in self.TypeRadioButtons.iteritems(): + for type, (radio, control) in self.TypeRadioButtons.items(): if radio.GetValue(): if isinstance(control, wx.ComboBox): return type, control.GetStringSelection() @@ -136,7 +136,7 @@ type_value = values.get("value", None) # For each parameters defined, set corresponding control value - for name, value in values.items(): + for name, value in list(values.items()): # Parameter is SFC transition priority if name == "priority": @@ -144,7 +144,7 @@ # Parameter is SFC transition type elif name == "type": - for type, (radio, control) in self.TypeRadioButtons.iteritems(): + for type, (radio, control) in self.TypeRadioButtons.items(): radio.SetValue(type == value) if control is not None: # Enable associated control to type and set value @@ -197,7 +197,7 @@ @param event: wx.RadioButtonEvent """ # Refresh sensibility of control associated to transition types - for _type, (radio, control) in self.TypeRadioButtons.iteritems(): + for _type, (radio, control) in self.TypeRadioButtons.items(): if control is not None: control.Enable(radio.GetValue()) diff -r 7e17f7e02a2b -r 0b3ac94f494c dialogs/SearchInProjectDialog.py --- a/dialogs/SearchInProjectDialog.py Wed Nov 29 11:54:56 2023 +0100 +++ b/dialogs/SearchInProjectDialog.py Thu Dec 07 22:41:32 2023 +0100 @@ -24,7 +24,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx from plcopen.plcopen import * from util.TranslationCatalogs import NoTranslate @@ -54,59 +54,59 @@ pattern_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=5) pattern_sizer.AddGrowableCol(0) - main_sizer.AddSizer(pattern_sizer, border=20, + main_sizer.Add(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) + pattern_sizer.Add(pattern_label, flag=wx.ALIGN_BOTTOM) self.CaseSensitive = wx.CheckBox(self, label=_('Case sensitive')) - pattern_sizer.AddWindow(self.CaseSensitive, flag=wx.GROW) + pattern_sizer.Add(self.CaseSensitive, flag=wx.GROW) 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) + pattern_sizer.Add(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) + pattern_sizer.Add(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, + main_sizer.Add(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, + scope_sizer.Add(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, + scope_selection_sizer.Add(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) + scope_selection_sizer.Add(self.OnlyElements, flag=wx.GROW) self.ElementsList = wx.CheckListBox(self) self.ElementsList.Enable(False) - scope_sizer.AddWindow(self.ElementsList, 1, border=5, + scope_sizer.Add(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, + main_sizer.Add(buttons_sizer, border=20, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.ALIGN_RIGHT) self.FindButton = wx.Button(self, label=_("Find")) self.FindButton.SetDefault() self.Bind(wx.EVT_BUTTON, self.OnFindButton, self.FindButton) - buttons_sizer.AddWindow(self.FindButton, border=5, flag=wx.RIGHT) + buttons_sizer.Add(self.FindButton, border=5, flag=wx.RIGHT) self.CloseButton = wx.Button(self, label=_("Close")) self.Bind(wx.EVT_BUTTON, self.OnCloseButton, self.CloseButton) - buttons_sizer.AddWindow(self.CloseButton) + buttons_sizer.Add(self.CloseButton) self.SetSizer(main_sizer) diff -r 7e17f7e02a2b -r 0b3ac94f494c dialogs/UriEditor.py --- a/dialogs/UriEditor.py Wed Nov 29 11:54:56 2023 +0100 +++ b/dialogs/UriEditor.py Thu Dec 07 22:41:32 2023 +0100 @@ -1,4 +1,4 @@ -from __future__ import absolute_import + import wx from connectors import ConnectorSchemes, EditorClassFromScheme diff -r 7e17f7e02a2b -r 0b3ac94f494c dialogs/__init__.py --- a/dialogs/__init__.py Wed Nov 29 11:54:56 2023 +0100 +++ b/dialogs/__init__.py Thu Dec 07 22:41:32 2023 +0100 @@ -25,7 +25,7 @@ # Package initialization -from __future__ import absolute_import + from dialogs.CommentEditDialog import CommentEditDialog from dialogs.ConnectionDialog import ConnectionDialog @@ -51,3 +51,4 @@ from dialogs.BrowseValuesLibraryDialog import BrowseValuesLibraryDialog from dialogs.UriEditor import UriEditor from dialogs.IDManager import IDManager +from dialogs.MessageBoxOnce import MessageBoxOnce diff -r 7e17f7e02a2b -r 0b3ac94f494c docutil/__init__.py --- a/docutil/__init__.py Wed Nov 29 11:54:56 2023 +0100 +++ b/docutil/__init__.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,7 +23,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + from docutil.dochtml import * from docutil.docpdf import * from docutil.docsvg import * diff -r 7e17f7e02a2b -r 0b3ac94f494c docutil/dochtml.py --- a/docutil/dochtml.py Wed Nov 29 11:54:56 2023 +0100 +++ b/docutil/dochtml.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,7 +23,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import subprocess import wx import wx.html @@ -41,7 +41,7 @@ window.Show() -EVT_HTML_URL_CLICK = wx.NewId() +EVT_HTML_URL_CLICK = wx.NewIdRef() class HtmlWindowUrlClick(wx.PyEvent): diff -r 7e17f7e02a2b -r 0b3ac94f494c docutil/docpdf.py --- a/docutil/docpdf.py Wed Nov 29 11:54:56 2023 +0100 +++ b/docutil/docpdf.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,8 +23,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import print_function + + import os import wx diff -r 7e17f7e02a2b -r 0b3ac94f494c docutil/docsvg.py --- a/docutil/docsvg.py Wed Nov 29 11:54:56 2023 +0100 +++ b/docutil/docsvg.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,16 +23,19 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + +import os +import sys import wx import subprocess +from dialogs import MessageBoxOnce def _get_inkscape_path(): """ Return the Inkscape binary path """ - if wx.Platform == '__WXMSW__': - from six.moves import winreg + if sys.platform.startswith('win32'): + import winreg inkcmd = None tries = [(winreg.HKEY_LOCAL_MACHINE, 'Software\\Classes\\svgfile\\shell\\Inkscape\\command'), (winreg.HKEY_LOCAL_MACHINE, 'Software\\Classes\\inkscape.svg\\shell\\open\\command'), @@ -73,8 +76,8 @@ inkpath = get_inkscape_path() if inkpath is None: return None - return map(int, - subprocess.check_output([inkpath,"--version"]).split()[1].split('.')) + return list(map(int, + subprocess.check_output([inkpath,"--version"]).split()[1].split(b'.'))) _inkscape_version = None def get_inkscape_version(): @@ -86,11 +89,23 @@ _inkscape_version = _get_inkscape_version() return _inkscape_version -def open_svg(svgfile): - """ Generic function to open SVG file """ - - inkpath = get_inkscape_path() - if inkpath is None: - wx.MessageBox("Inkscape is not found or installed !") - else: - subprocess.Popen([inkpath,svgfile]) +if "SNAP" in os.environ: + def open_svg(svgfile): + MessageBoxOnce("Launching Inkscape with xdg-open", + "Confined app can't launch Inkscape directly.\n"+ + "Instead, SVG file is passed to xdg-open.\n"+ + "Please select Inskape when proposed.\n\n"+ + "Notes: \n"+ + " - Inkscape must be installed on you system.\n"+ + " - If no choice is proposed, use file manager to change SVG file properties.\n", + "DocSVGSnapWarning") + + subprocess.Popen(["xdg-open",svgfile]) +else: + def open_svg(svgfile): + """ Generic function to open SVG file """ + inkpath = get_inkscape_path() + if inkpath is None: + wx.MessageBox("Inkscape is not found or installed !") + else: + subprocess.Popen([inkpath,svgfile]) diff -r 7e17f7e02a2b -r 0b3ac94f494c editors/CodeFileEditor.py --- a/editors/CodeFileEditor.py Wed Nov 29 11:54:56 2023 +0100 +++ b/editors/CodeFileEditor.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,16 +23,12 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import division import re -from builtins import str as text import wx import wx.grid import wx.stc as stc import wx.lib.buttons -from six.moves import xrange from plcopen.plcopen import TestTextElement @@ -46,7 +42,7 @@ [STC_CODE_ERROR, STC_CODE_SEARCH_RESULT, - STC_CODE_SECTION] = range(15, 18) + STC_CODE_SECTION] = list(range(15, 18)) HIGHLIGHT_TYPES = { ERROR_HIGHLIGHT: STC_CODE_ERROR, @@ -285,23 +281,28 @@ def RefreshSectionStyling(self): self.Colourise(0, -1) - for line in xrange(self.GetLineCount()): + for line in range(self.GetLineCount()): self.SetLineState(line, 0) doc_end_pos = self.GetLength() for section in self.Controler.SECTIONS_NAMES: section_comments = self.SectionsComments[section] - start_pos = self.FindText(0, doc_end_pos, section_comments["comment"]) - end_pos = start_pos + len(section_comments["comment"]) - self.StartStyling(start_pos, 0xff) + txttofind = section_comments["comment"] + results = self.FindText(0, doc_end_pos, txttofind) + if wx.VERSION < (4, 1, 0): + start_pos = results + end_pos = start_pos+len(txttofind) + else: + start_pos, end_pos = results + self.StartStyling(start_pos) self.SetStyling(end_pos - start_pos, STC_CODE_SECTION) self.SetLineState(self.LineFromPosition(start_pos), 1) - self.StartStyling(end_pos, 0x00) + self.StartStyling(end_pos) self.SetStyling(doc_end_pos - end_pos, stc.STC_STYLE_DEFAULT) def DoGetBestSize(self): - return self.ParentWindow.GetPanelBestSize() + return self.ParentWindow.GetBestSize() def RefreshModel(self): text = self.GetText() @@ -597,7 +598,7 @@ highlight_end_pos = end[1] + 1 else: highlight_end_pos = self.GetLineEndPosition(end[0] - 1) + end[1] + 2 - self.StartStyling(highlight_start_pos, 0xff) + self.StartStyling(highlight_start_pos) self.SetStyling(highlight_end_pos - highlight_start_pos, highlight_type) self.StartStyling(highlight_end_pos, 0x00) self.SetStyling(len(self.GetText()) - highlight_end_pos, stc.STC_STYLE_DEFAULT) @@ -614,8 +615,7 @@ class ClassGridCellEditor(wx.grid.GridCellChoiceEditor): def __init__(self, table, row, col): - wx.grid.GridCellChoiceEditor.__init__(self) - self.SetParameters("input,memory,output") + wx.grid.GridCellChoiceEditor.__init__(self,["input","memory","output"]) class VariablesTable(CustomTable): @@ -629,8 +629,8 @@ super(VariablesTable, self).__init__(*args, **kwargs) self.columnTypes = dict(self.__defaultColumnType) if my_columns is not None: - for key in my_columns.keys(): - if key in self.columnTypes.keys(): + for key in list(my_columns.keys()): + if key in list(self.columnTypes.keys()): self.columnTypes[key] = my_columns[key] def GetValue(self, row, col): @@ -638,7 +638,7 @@ if col == 0: return row + 1 else: - return text(self.data[row].get(self.GetColLabelValue(col, False), "")) + return str(self.data[row].get(self.GetColLabelValue(col, False), "")) def _updateColAttrs(self, grid): """ @@ -678,7 +678,7 @@ main_sizer.AddGrowableRow(0) controls_sizer = wx.BoxSizer(wx.VERTICAL) - main_sizer.AddSizer(controls_sizer, border=5, flag=wx.ALL) + main_sizer.Add(controls_sizer, border=5, flag=wx.ALL) for name, bitmap, help in [ ("AddVariableButton", "add_element", _("Add variable")), @@ -687,15 +687,15 @@ ("DownVariableButton", "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) + button.SetToolTip(help) setattr(self, name, button) - controls_sizer.AddWindow(button, border=5, flag=wx.BOTTOM) + controls_sizer.Add(button, border=5, flag=wx.BOTTOM) self.VariablesGrid = CustomGrid(self, style=wx.VSCROLL) - self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.OnVariablesGridCellChange) + self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGING, self.OnVariablesGridCellChange) self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.OnVariablesGridCellLeftClick) self.VariablesGrid.Bind(wx.grid.EVT_GRID_EDITOR_SHOWN, self.OnVariablesGridEditorShown) - main_sizer.AddWindow(self.VariablesGrid, flag=wx.GROW) + main_sizer.Add(self.VariablesGrid, flag=wx.GROW) self.SetSizer(main_sizer) @@ -785,7 +785,7 @@ self.VariablesGrid.RefreshButtons() def DoGetBestSize(self): - return self.ParentWindow.GetPanelBestSize() + return self.ParentWindow.GetBestSize() def ShowErrorMessage(self, message): dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) @@ -826,17 +826,17 @@ type_menu = wx.Menu(title='') base_menu = wx.Menu(title='') for base_type in self.Controler.GetBaseTypes(): - new_entry = base_menu.Append(help='', id=wx.ID_ANY, kind=wx.ITEM_NORMAL, text=base_type) + new_entry = base_menu.Append(wx.ID_ANY, base_type) self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(base_type), new_entry) type_menu.AppendMenu(wx.ID_ANY, "Base Types", base_menu) datatype_menu = wx.Menu(title='') for datatype in self.Controler.GetDataTypes(): - new_entry = datatype_menu.Append(help='', id=wx.ID_ANY, kind=wx.ITEM_NORMAL, text=datatype) + new_entry = datatype_menu.Append(wx.ID_ANY, item=datatype) self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(datatype), new_entry) type_menu.AppendMenu(wx.ID_ANY, "User Data Types", datatype_menu) rect = self.VariablesGrid.BlockToDeviceRect((row, col), (row, col)) - self.VariablesGrid.PopupMenuXY(type_menu, rect.x + rect.width, rect.y + self.VariablesGrid.GetColLabelSize()) + self.VariablesGrid.PopupMenu(type_menu, rect.x + rect.width, rect.y + self.VariablesGrid.GetColLabelSize()) type_menu.Destroy() event.Veto() else: diff -r 7e17f7e02a2b -r 0b3ac94f494c editors/ConfTreeNodeEditor.py --- a/editors/ConfTreeNodeEditor.py Wed Nov 29 11:54:56 2023 +0100 +++ b/editors/ConfTreeNodeEditor.py Thu Dec 07 22:41:32 2023 +0100 @@ -24,8 +24,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import division + + import wx @@ -120,7 +120,7 @@ bitmap = GetBitmap(bitmapname) if bitmap is None: - bitmap = wx.EmptyBitmap(0, 0) + bitmap = wx.Bitmap() wx.StaticBitmap.__init__(self, parent, ID, bitmap, @@ -148,18 +148,18 @@ if self.SHOW_BASE_PARAMS: baseparamseditor_sizer = wx.BoxSizer(wx.HORIZONTAL) - self.MainSizer.AddSizer(baseparamseditor_sizer, border=5, + self.MainSizer.Add(baseparamseditor_sizer, border=5, 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"])) - baseparamseditor_sizer.AddWindow(self.FullIECChannel, + baseparamseditor_sizer.Add(self.FullIECChannel, flag=wx.ALIGN_CENTER_VERTICAL) updownsizer = wx.BoxSizer(wx.VERTICAL) - baseparamseditor_sizer.AddSizer(updownsizer, border=5, + baseparamseditor_sizer.Add(updownsizer, border=5, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) self.IECCUpButton = wx.lib.buttons.GenBitmapTextButton( @@ -169,35 +169,35 @@ style=wx.NO_BORDER) self.IECCUpButton.Bind(wx.EVT_BUTTON, self.GetItemChannelChangedFunction(1), self.IECCUpButton) - updownsizer.AddWindow(self.IECCUpButton, flag=wx.ALIGN_LEFT) + updownsizer.Add(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.Bind(wx.EVT_BUTTON, self.GetItemChannelChangedFunction(-1), self.IECCDownButton) - updownsizer.AddWindow(self.IECCDownButton, flag=wx.ALIGN_LEFT) + updownsizer.Add(self.IECCDownButton, flag=wx.ALIGN_LEFT) self.ConfNodeName = wx.TextCtrl(self.Editor, size=wx.Size(150, 25)) self.ConfNodeName.SetFont( - wx.Font(faces["size"] * 0.75, wx.DEFAULT, wx.NORMAL, + wx.Font(round(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( + baseparamseditor_sizer.Add( 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) + baseparamseditor_sizer.Add(buttons_sizer, flag=wx.ALIGN_CENTER) if tabs_num > 1: self.ConfNodeNoteBook = wx.Notebook(self.Editor) parent = self.ConfNodeNoteBook - self.MainSizer.AddWindow(self.ConfNodeNoteBook, 1, flag=wx.GROW) + self.MainSizer.Add(self.ConfNodeNoteBook, 1, flag=wx.GROW) else: parent = self.Editor self.ConfNodeNoteBook = None @@ -212,7 +212,7 @@ if self.ConfNodeNoteBook is not None: self.ConfNodeNoteBook.AddPage(editor, title) elif self.SHOW_BASE_PARAMS: - self.MainSizer.AddWindow(editor, 1, flag=wx.GROW) + self.MainSizer.Add(editor, 1, flag=wx.GROW) else: self.Editor = editor @@ -232,7 +232,7 @@ self.ParamsEditor.SetSizer(self.ParamsEditorSizer) self.ConfNodeParamsSizer = wx.BoxSizer(wx.VERTICAL) - self.ParamsEditorSizer.AddSizer(self.ConfNodeParamsSizer, border=5, + self.ParamsEditorSizer.Add(self.ConfNodeParamsSizer, border=5, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM) self.RefreshConfNodeParamsSizer() @@ -240,7 +240,7 @@ if self.ConfNodeNoteBook is not None: self.ConfNodeNoteBook.AddPage(self.ParamsEditor, _("Config")) elif self.SHOW_BASE_PARAMS: - self.MainSizer.AddWindow(self.ParamsEditor, 1, flag=wx.GROW) + self.MainSizer.Add(self.ParamsEditor, 1, flag=wx.GROW) else: self.Editor = self.ParamsEditor else: @@ -255,9 +255,6 @@ else: self.SetIcon(GetBitmap("Extension")) - def __del__(self): - self.Controler.OnCloseEditor(self) - def GetTagName(self): return self.Controler.CTNFullName() @@ -317,7 +314,7 @@ label=confnode_method["name"], style=wx.NO_BORDER) button.SetFont(normal_bt_font) - button.SetToolTipString(confnode_method["tooltip"]) + button.SetToolTip(confnode_method["tooltip"]) if confnode_method.get("push", False): button.Bind(wx.EVT_LEFT_DOWN, self.GetButtonCallBackFunction(confnode_method["method"], True)) else: @@ -335,7 +332,7 @@ # hack to force size to mini if not confnode_method.get("enabled", True): button.Disable() - msizer.AddWindow(button, flag=wx.ALIGN_CENTER) + msizer.Add(button, flag=wx.ALIGN_CENTER) return msizer def UriOptions(self, event): @@ -380,32 +377,33 @@ flags = (wx.GROW | wx.BOTTOM | wx.LEFT | wx.RIGHT) if first: flags |= wx.TOP - sizer.AddSizer(staticboxsizer, border=5, flag=flags) + sizer.Add(staticboxsizer, border=5, flag=flags) self.GenerateSizerElements(staticboxsizer, element_infos["children"], element_path) else: - boxsizer = wx.FlexGridSizer(cols=4, rows=1) + boxsizer = wx.FlexGridSizer(cols=4, rows=1, gap=wx.Size(0,0)) boxsizer.AddGrowableCol(1) flags = (wx.GROW | wx.BOTTOM | wx.LEFT | wx.RIGHT) if first: flags |= wx.TOP - sizer.AddSizer(boxsizer, border=5, flag=flags) + sizer.Add(boxsizer, border=5, flag=flags) 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) - + boxsizer.Add(staticbitmap, border=5, flag=wx.RIGHT) + + label = element_infos["name"].replace('_', ' ') statictext = wx.StaticText(self.ParamsEditor, - label="%s:" % _(element_infos["name"])) - boxsizer.AddWindow(statictext, border=5, + label="%s:" % _(label)) + boxsizer.Add(statictext, border=5, flag=wx.ALIGN_CENTER_VERTICAL | wx.RIGHT) if isinstance(element_infos["type"], list): if isinstance(element_infos["value"], tuple): browse_boxsizer = wx.BoxSizer(wx.HORIZONTAL) - boxsizer.AddSizer(browse_boxsizer) + boxsizer.Add(browse_boxsizer) textctrl = wx.TextCtrl(self.ParamsEditor, size=wx.Size(275, -1), style=wx.TE_READONLY) @@ -414,10 +412,10 @@ value_infos = element_infos["value"][1] else: value_infos = None - browse_boxsizer.AddWindow(textctrl) + browse_boxsizer.Add(textctrl) button = wx.Button(self.ParamsEditor, label="...") - browse_boxsizer.AddWindow(button) + browse_boxsizer.Add(button) button.Bind(wx.EVT_BUTTON, self.GetBrowseCallBackFunction(element_infos["name"], textctrl, element_infos["type"], value_infos, element_path), @@ -425,23 +423,26 @@ else: combobox = wx.ComboBox(self.ParamsEditor, size=wx.Size(300, -1), style=wx.CB_READONLY) - boxsizer.AddWindow(combobox) + boxsizer.Add(combobox) if element_infos["use"] == "optional": combobox.Append("") if len(element_infos["type"]) > 0 and isinstance(element_infos["type"][0], tuple): for choice, _xsdclass in element_infos["type"]: - combobox.Append(choice) + combobox.Append(choice.replace('_',' ')) name = element_infos["name"] value = element_infos["value"] - staticbox = wx.StaticBox(self.ParamsEditor, - label="%s - %s" % (_(name), _(value)), - size=wx.Size(10, 0)) - staticboxsizer = wx.StaticBoxSizer(staticbox, wx.VERTICAL) - sizer.AddSizer(staticboxsizer, border=5, flag=wx.GROW | wx.BOTTOM | wx.LEFT | wx.RIGHT) - self.GenerateSizerElements(staticboxsizer, element_infos["children"], element_path) - callback = self.GetChoiceContentCallBackFunction(combobox, staticboxsizer, element_path) + staticboxsizer = None + if element_infos["children"]: + staticbox = wx.StaticBox(self.ParamsEditor, + label="%s - %s" % (_(name), _(value)), + size=wx.Size(10, 0)) + staticboxsizer = wx.StaticBoxSizer(staticbox, wx.VERTICAL) + sizer.Add(staticboxsizer, border=5, flag=wx.GROW | wx.BOTTOM | wx.LEFT | wx.RIGHT) + self.GenerateSizerElements(staticboxsizer, element_infos["children"], element_path) + + callback = self.GetChoiceContentCallBackFunction(combobox, element_path) else: for choice in element_infos["type"]: combobox.Append(choice) @@ -449,7 +450,8 @@ if element_infos["value"] is None: combobox.SetStringSelection("") else: - combobox.SetStringSelection(element_infos["value"]) + combobox.SetStringSelection( + element_infos["value"].replace('_',' ')) combobox.Bind(wx.EVT_COMBOBOX, callback, combobox) elif isinstance(element_infos["type"], dict): @@ -463,7 +465,7 @@ size=wx.Size(300, -1), style=wx.SP_ARROW_KEYS | wx.ALIGN_RIGHT) spinctrl.SetRange(scmin, scmax) - boxsizer.AddWindow(spinctrl) + boxsizer.Add(spinctrl) if element_infos["value"] is not None: spinctrl.SetValue(element_infos["value"]) spinctrl.Bind(wx.EVT_SPINCTRL, @@ -473,7 +475,7 @@ else: if element_infos["type"] == "boolean": checkbox = wx.CheckBox(self.ParamsEditor) - boxsizer.AddWindow(checkbox, flag=wx.ALIGN_CENTER_VERTICAL | wx.RIGHT) + boxsizer.Add(checkbox, flag=wx.ALIGN_CENTER_VERTICAL | wx.RIGHT) if element_infos["value"] is not None: checkbox.SetValue(element_infos["value"]) checkbox.Bind(wx.EVT_CHECKBOX, @@ -490,7 +492,7 @@ size=wx.Size(300, -1), style=wx.SP_ARROW_KEYS | wx.ALIGN_RIGHT) spinctrl.SetRange(scmin, scmax) - boxsizer.AddWindow(spinctrl) + boxsizer.Add(spinctrl) if element_infos["value"] is not None: spinctrl.SetValue(element_infos["value"]) spinctrl.Bind(wx.EVT_SPINCTRL, @@ -513,12 +515,12 @@ self.EditButton = wx.Button(self.ParamsEditor, label='...', size=wx.Size(30, -1)) self.Bind(wx.EVT_BUTTON, self.UriOptions, self.EditButton) - uriSizer.AddWindow(textctrl, flag=wx.GROW) - uriSizer.AddWindow(self.EditButton, flag=wx.GROW) - - boxsizer.AddWindow(uriSizer) + uriSizer.Add(textctrl, flag=wx.GROW) + uriSizer.Add(self.EditButton, flag=wx.GROW) + + boxsizer.Add(uriSizer) else: - boxsizer.AddWindow(textctrl) + boxsizer.Add(textctrl) if element_infos["value"] is not None: textctrl.ChangeValue(str(element_infos["value"])) @@ -527,7 +529,7 @@ textctrl.Bind(wx.EVT_TEXT, callback) textctrl.Bind(wx.EVT_KILL_FOCUS, callback) - if not isinstance(element_infos["type"], list) and element_infos["use"] == "optional": + if not isinstance(element_infos["type"], list) and element_infos.get("use", None) == "optional": bt = wx.BitmapButton(self.ParamsEditor, bitmap=wx.ArtProvider.GetBitmap(wx.ART_UNDO, wx.ART_TOOLBAR, (16,16)), style=wx.BORDER_NONE) @@ -535,7 +537,7 @@ self.GetResetFunction(element_path), bt) - boxsizer.AddWindow(bt, border=5, flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT) + boxsizer.Add(bt, border=5, flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT) first = False sizer.Layout() self.RefreshScrollbars() @@ -579,9 +581,10 @@ event.Skip() return OnChoiceChanged - def GetChoiceContentCallBackFunction(self, choicectrl, staticboxsizer, path): + def GetChoiceContentCallBackFunction(self, choicectrl, path): def OnChoiceContentChanged(event): - self.SetConfNodeParamsAttribute(path, choicectrl.GetStringSelection()) + self.SetConfNodeParamsAttribute( + path, choicectrl.GetStringSelection().replace(' ','_')) wx.CallAfter(self.RefreshConfNodeParamsSizer) event.Skip() return OnChoiceContentChanged diff -r 7e17f7e02a2b -r 0b3ac94f494c editors/DataTypeEditor.py --- a/editors/DataTypeEditor.py Wed Nov 29 11:54:56 2023 +0100 +++ b/editors/DataTypeEditor.py Thu Dec 07 22:41:32 2023 +0100 @@ -24,9 +24,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import import re -from six.moves import xrange import wx import wx.grid @@ -47,7 +45,7 @@ def AppendMenu(parent, help, kind, text): - return parent.Append(help=help, id=wx.ID_ANY, kind=kind, text=text) + return parent.Append(wx.MenuItem(helpString=help, id=wx.ID_ANY, kind=kind, text=text)) def GetElementsTableColnames(): @@ -155,49 +153,49 @@ self.MainSizer.AddGrowableRow(1) top_sizer = wx.BoxSizer(wx.HORIZONTAL) - self.MainSizer.AddSizer(top_sizer, border=5, + self.MainSizer.Add(top_sizer, border=5, flag=wx.GROW | wx.TOP | wx.LEFT | wx.RIGHT) derivation_type_label = wx.StaticText(self.Editor, label=_('Derivation Type:')) - top_sizer.AddWindow(derivation_type_label, border=5, + top_sizer.Add(derivation_type_label, border=5, flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT) self.DerivationType = wx.ComboBox(self.Editor, size=wx.Size(200, -1), style=wx.CB_READONLY) self.Bind(wx.EVT_COMBOBOX, self.OnDerivationTypeChanged, self.DerivationType) - top_sizer.AddWindow(self.DerivationType, border=5, flag=wx.GROW | wx.RIGHT) + top_sizer.Add(self.DerivationType, border=5, flag=wx.GROW | wx.RIGHT) typeinfos_staticbox = wx.StaticBox(self.Editor, label=_('Type infos:')) typeinfos_sizer = wx.StaticBoxSizer(typeinfos_staticbox, wx.HORIZONTAL) - self.MainSizer.AddSizer(typeinfos_sizer, border=5, + self.MainSizer.Add(typeinfos_sizer, border=5, flag=wx.GROW | wx.BOTTOM | wx.LEFT | wx.RIGHT) # Panel for Directly derived data types self.DirectlyPanel = wx.Panel(self.Editor, style=wx.TAB_TRAVERSAL) - typeinfos_sizer.AddWindow(self.DirectlyPanel, 1) + typeinfos_sizer.Add(self.DirectlyPanel, 1) directly_panel_sizer = wx.BoxSizer(wx.HORIZONTAL) directly_basetype_label = wx.StaticText(self.DirectlyPanel, label=_('Base Type:')) - directly_panel_sizer.AddWindow(directly_basetype_label, 1, border=5, + directly_panel_sizer.Add(directly_basetype_label, 1, border=5, flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL) self.DirectlyBaseType = wx.ComboBox(self.DirectlyPanel, style=wx.CB_READONLY) self.Bind(wx.EVT_COMBOBOX, self.OnInfosChanged, self.DirectlyBaseType) - directly_panel_sizer.AddWindow(self.DirectlyBaseType, 1, border=5, + directly_panel_sizer.Add(self.DirectlyBaseType, 1, border=5, flag=wx.GROW | wx.ALL) directly_initialvalue_label = wx.StaticText(self.DirectlyPanel, label=_('Initial Value:')) - directly_panel_sizer.AddWindow(directly_initialvalue_label, 1, border=5, + directly_panel_sizer.Add(directly_initialvalue_label, 1, border=5, flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL) self.DirectlyInitialValue = wx.TextCtrl(self.DirectlyPanel, style=wx.TE_PROCESS_ENTER | wx.TE_RICH) self.Bind(wx.EVT_TEXT_ENTER, self.OnReturnKeyPressed, self.DirectlyInitialValue) - directly_panel_sizer.AddWindow(self.DirectlyInitialValue, 1, border=5, + directly_panel_sizer.Add(self.DirectlyInitialValue, 1, border=5, flag=wx.ALL) self.DirectlyPanel.SetSizer(directly_panel_sizer) @@ -205,52 +203,52 @@ # Panel for Subrange data types self.SubrangePanel = wx.Panel(self.Editor, style=wx.TAB_TRAVERSAL) - typeinfos_sizer.AddWindow(self.SubrangePanel, 1) + typeinfos_sizer.Add(self.SubrangePanel, 1) subrange_panel_sizer = wx.GridSizer(cols=4, hgap=5, rows=3, vgap=0) subrange_basetype_label = wx.StaticText(self.SubrangePanel, label=_('Base Type:')) - subrange_panel_sizer.AddWindow(subrange_basetype_label, 1, border=5, + subrange_panel_sizer.Add(subrange_basetype_label, 1, border=5, flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL) self.SubrangeBaseType = wx.ComboBox(self.SubrangePanel, style=wx.CB_READONLY) self.Bind(wx.EVT_COMBOBOX, self.OnSubrangeBaseTypeChanged, self.SubrangeBaseType) - subrange_panel_sizer.AddWindow(self.SubrangeBaseType, 1, border=5, + subrange_panel_sizer.Add(self.SubrangeBaseType, 1, border=5, flag=wx.GROW | wx.ALL) subrange_initialvalue_label = wx.StaticText(self.SubrangePanel, label=_('Initial Value:')) - subrange_panel_sizer.AddWindow(subrange_initialvalue_label, 1, border=5, + subrange_panel_sizer.Add(subrange_initialvalue_label, 1, border=5, flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL) self.SubrangeInitialValue = CustomIntCtrl(self.SubrangePanel, style=wx.TAB_TRAVERSAL) self.SubrangeInitialValue.Bind(CustomIntCtrl.EVT_CUSTOM_INT, self.OnInfosChanged) - subrange_panel_sizer.AddWindow(self.SubrangeInitialValue, 1, border=5, + subrange_panel_sizer.Add(self.SubrangeInitialValue, 1, border=5, flag=wx.GROW | wx.ALL) subrange_minimum_label = wx.StaticText(self.SubrangePanel, label=_('Minimum:')) - subrange_panel_sizer.AddWindow(subrange_minimum_label, 1, border=5, + subrange_panel_sizer.Add(subrange_minimum_label, 1, border=5, flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL) self.SubrangeMinimum = CustomIntCtrl(self.SubrangePanel, style=wx.TAB_TRAVERSAL) self.SubrangeMinimum.Bind(CustomIntCtrl.EVT_CUSTOM_INT, self.OnSubrangeMinimumChanged) - subrange_panel_sizer.AddWindow(self.SubrangeMinimum, 1, border=5, + subrange_panel_sizer.Add(self.SubrangeMinimum, 1, border=5, flag=wx.GROW | wx.ALL) - for dummy in xrange(2): - subrange_panel_sizer.AddWindow(wx.Size(0, 0), 1) + for dummy in range(2): + subrange_panel_sizer.Add(wx.Size(0, 0), 1) subrange_maximum_label = wx.StaticText(self.SubrangePanel, label=_('Maximum:')) - subrange_panel_sizer.AddWindow(subrange_maximum_label, 1, border=5, + subrange_panel_sizer.Add(subrange_maximum_label, 1, border=5, flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL) self.SubrangeMaximum = CustomIntCtrl(self.SubrangePanel, style=wx.TAB_TRAVERSAL) self.SubrangeMaximum.Bind(CustomIntCtrl.EVT_CUSTOM_INT, self.OnSubrangeMaximumChanged) - subrange_panel_sizer.AddWindow(self.SubrangeMaximum, 1, border=5, + subrange_panel_sizer.Add(self.SubrangeMaximum, 1, border=5, flag=wx.GROW | wx.ALL) self.SubrangePanel.SetSizer(subrange_panel_sizer) @@ -258,35 +256,35 @@ # Panel for Enumerated data types self.EnumeratedPanel = wx.Panel(self.Editor, style=wx.TAB_TRAVERSAL) - typeinfos_sizer.AddWindow(self.EnumeratedPanel, 1) + typeinfos_sizer.Add(self.EnumeratedPanel, 1) enumerated_panel_sizer = wx.BoxSizer(wx.HORIZONTAL) self.EnumeratedValues = CustomEditableListBox( self.EnumeratedPanel, label=_("Values:"), - style=(wx.gizmos.EL_ALLOW_NEW | - wx.gizmos.EL_ALLOW_EDIT | - wx.gizmos.EL_ALLOW_DELETE)) + style=(wx.adv.EL_ALLOW_NEW | + wx.adv.EL_ALLOW_EDIT | + wx.adv.EL_ALLOW_DELETE)) setattr(self.EnumeratedValues, "_OnLabelEndEdit", self.OnEnumeratedValueEndEdit) for func in ["_OnAddButton", "_OnDelButton", "_OnUpButton", "_OnDownButton"]: setattr(self.EnumeratedValues, func, self.OnEnumeratedValuesChanged) - enumerated_panel_sizer.AddWindow(self.EnumeratedValues, 1, border=5, + enumerated_panel_sizer.Add(self.EnumeratedValues, 1, border=5, flag=wx.GROW | wx.ALL) enumerated_panel_rightsizer = wx.BoxSizer(wx.HORIZONTAL) - enumerated_panel_sizer.AddSizer(enumerated_panel_rightsizer, 1) + enumerated_panel_sizer.Add(enumerated_panel_rightsizer, 1) enumerated_initialvalue_label = wx.StaticText(self.EnumeratedPanel, label=_('Initial Value:')) - enumerated_panel_rightsizer.AddWindow(enumerated_initialvalue_label, 1, + enumerated_panel_rightsizer.Add(enumerated_initialvalue_label, 1, border=5, flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL) self.EnumeratedInitialValue = wx.ComboBox(self.EnumeratedPanel, style=wx.CB_READONLY) self.Bind(wx.EVT_COMBOBOX, self.OnInfosChanged, self.EnumeratedInitialValue) - enumerated_panel_rightsizer.AddWindow(self.EnumeratedInitialValue, 1, + enumerated_panel_rightsizer.Add(self.EnumeratedInitialValue, 1, border=5, flag=wx.ALL) self.EnumeratedPanel.SetSizer(enumerated_panel_sizer) @@ -294,7 +292,7 @@ # Panel for Array data types self.ArrayPanel = wx.Panel(self.Editor, style=wx.TAB_TRAVERSAL) - typeinfos_sizer.AddWindow(self.ArrayPanel, 1) + typeinfos_sizer.Add(self.ArrayPanel, 1) array_panel_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=0) array_panel_sizer.AddGrowableCol(0) @@ -302,41 +300,41 @@ array_panel_sizer.AddGrowableRow(1) array_panel_leftSizer = wx.BoxSizer(wx.HORIZONTAL) - array_panel_sizer.AddSizer(array_panel_leftSizer, flag=wx.GROW) + array_panel_sizer.Add(array_panel_leftSizer, flag=wx.GROW) array_basetype_label = wx.StaticText(self.ArrayPanel, label=_('Base Type:')) - array_panel_leftSizer.AddWindow(array_basetype_label, 1, border=5, + array_panel_leftSizer.Add(array_basetype_label, 1, border=5, flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL) self.ArrayBaseType = wx.ComboBox(self.ArrayPanel, style=wx.CB_READONLY) self.Bind(wx.EVT_COMBOBOX, self.OnInfosChanged, self.ArrayBaseType) - array_panel_leftSizer.AddWindow(self.ArrayBaseType, 1, border=5, + array_panel_leftSizer.Add(self.ArrayBaseType, 1, border=5, flag=wx.GROW | wx.ALL) array_panel_rightsizer = wx.BoxSizer(wx.HORIZONTAL) - array_panel_sizer.AddSizer(array_panel_rightsizer, flag=wx.GROW) + array_panel_sizer.Add(array_panel_rightsizer, flag=wx.GROW) array_initialvalue_label = wx.StaticText(self.ArrayPanel, label=_('Initial Value:')) - array_panel_rightsizer.AddWindow(array_initialvalue_label, 1, border=5, + array_panel_rightsizer.Add(array_initialvalue_label, 1, border=5, flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL) self.ArrayInitialValue = wx.TextCtrl(self.ArrayPanel, style=wx.TE_PROCESS_ENTER | wx.TE_RICH) self.Bind(wx.EVT_TEXT_ENTER, self.OnReturnKeyPressed, self.ArrayInitialValue) - array_panel_rightsizer.AddWindow(self.ArrayInitialValue, 1, border=5, + array_panel_rightsizer.Add(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)) + style=(wx.adv.EL_ALLOW_NEW | + wx.adv.EL_ALLOW_EDIT | + wx.adv.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, + array_panel_sizer.Add(self.ArrayDimensions, 0, border=5, flag=wx.GROW | wx.ALL) self.ArrayPanel.SetSizer(array_panel_sizer) @@ -344,7 +342,7 @@ # Panel for Structure data types self.StructurePanel = wx.Panel(self.Editor, style=wx.TAB_TRAVERSAL) - typeinfos_sizer.AddWindow(self.StructurePanel, 1) + typeinfos_sizer.Add(self.StructurePanel, 1) structure_panel_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0) structure_panel_sizer.AddGrowableCol(0) @@ -353,12 +351,12 @@ structure_button_sizer = wx.FlexGridSizer(cols=5, hgap=5, rows=1, vgap=0) structure_button_sizer.AddGrowableCol(0) structure_button_sizer.AddGrowableRow(0) - structure_panel_sizer.AddSizer(structure_button_sizer, 0, border=5, + structure_panel_sizer.Add(structure_button_sizer, 0, border=5, flag=wx.ALL | wx.GROW) structure_elements_label = wx.StaticText(self.StructurePanel, label=_('Elements :')) - structure_button_sizer.AddWindow(structure_elements_label, flag=wx.ALIGN_BOTTOM) + structure_button_sizer.Add(structure_elements_label, flag=wx.ALIGN_BOTTOM) for name, bitmap, help in [ ("StructureAddButton", "add_element", _("Add element")), @@ -369,17 +367,17 @@ bitmap=GetBitmap(bitmap), size=wx.Size(28, 28), style=wx.NO_BORDER) - button.SetToolTipString(help) + button.SetToolTip(help) setattr(self, name, button) - structure_button_sizer.AddWindow(button) + structure_button_sizer.Add(button) self.StructureElementsGrid = CustomGrid(self.StructurePanel, size=wx.Size(0, 150), style=wx.VSCROLL) - self.StructureElementsGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, + self.StructureElementsGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGED, self.OnStructureElementsGridCellChange) self.StructureElementsGrid.Bind(wx.grid.EVT_GRID_EDITOR_SHOWN, self.OnStructureElementsGridEditorShown) - structure_panel_sizer.AddWindow(self.StructureElementsGrid, flag=wx.GROW) + structure_panel_sizer.Add(self.StructureElementsGrid, flag=wx.GROW) self.StructurePanel.SetSizer(structure_panel_sizer) @@ -458,9 +456,6 @@ self.RefreshHighlightsTimer = wx.Timer(self, -1) self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer) - def __del__(self): - self.RefreshHighlightsTimer.Stop() - def GetBufferState(self): return self.Controler.GetBufferState() @@ -512,7 +507,7 @@ self.EnumeratedInitialValue.SetStringSelection(type_infos["initial"]) elif type_infos["type"] == "Array": self.ArrayBaseType.SetStringSelection(type_infos["base_type"]) - self.ArrayDimensions.SetStrings(map("..".join, type_infos["dimensions"])) + self.ArrayDimensions.SetStrings(list(map("..".join, type_infos["dimensions"]))) self.ArrayInitialValue.SetValue(type_infos["initial"]) elif type_infos["type"] == "Structure": self.StructureElementsTable.SetData(type_infos["elements"]) @@ -647,7 +642,7 @@ self.Bind(wx.EVT_MENU, self.ElementArrayTypeFunction, new_entry) rect = self.StructureElementsGrid.BlockToDeviceRect((row, col), (row, col)) - self.StructureElementsGrid.PopupMenuXY(type_menu, rect.x + rect.width, rect.y + self.StructureElementsGrid.GetColLabelSize()) + self.StructureElementsGrid.PopupMenu(type_menu, rect.x + rect.width, rect.y + self.StructureElementsGrid.GetColLabelSize()) type_menu.Destroy() event.Veto() else: @@ -778,7 +773,7 @@ self.Highlights = [] else: self.Highlights = [(infos, start, end, highlight) for (infos, start, end, highlight) in self.Highlights if highlight != highlight_type] - for control in self.HighlightControls.itervalues(): + for control in self.HighlightControls.values(): if isinstance(control, (wx.ComboBox, wx.SpinCtrl)): control.SetBackgroundColour(wx.NullColour) control.SetForegroundColour(wx.NullColour) @@ -786,9 +781,9 @@ value = control.GetValueStr() if isinstance(control, CustomIntCtrl) else \ control.GetValue() control.SetStyle(0, len(value), wx.TextAttr(wx.NullColour)) - elif isinstance(control, wx.gizmos.EditableListBox): + elif isinstance(control, wx.adv.EditableListBox): listctrl = control.GetListCtrl() - for i in xrange(listctrl.GetItemCount()): + for i in range(listctrl.GetItemCount()): listctrl.SetItemBackgroundColour(i, wx.NullColour) listctrl.SetItemTextColour(i, wx.NullColour) self.StructureElementsTable.ClearHighlights(highlight_type) @@ -811,7 +806,7 @@ control.SetForegroundColour(highlight_type[1]) elif isinstance(control, wx.TextCtrl): control.SetStyle(start[1], end[1] + 1, wx.TextAttr(highlight_type[1], highlight_type[0])) - elif isinstance(control, wx.gizmos.EditableListBox): + elif isinstance(control, wx.adv.EditableListBox): listctrl = control.GetListCtrl() listctrl.SetItemBackgroundColour(infos[1], highlight_type[0]) listctrl.SetItemTextColour(infos[1], highlight_type[1]) diff -r 7e17f7e02a2b -r 0b3ac94f494c editors/DebugViewer.py --- a/editors/DebugViewer.py Wed Nov 29 11:54:56 2023 +0100 +++ b/editors/DebugViewer.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,7 +23,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + from threading import Lock, Timer from time import time as gettime @@ -79,24 +79,6 @@ # 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 @@ -130,7 +112,7 @@ @param inhibit: Inhibit flag """ # Inhibit every data consumers in list - for consumer, _iec_path in self.DataConsumers.iteritems(): + for consumer, _iec_path in self.DataConsumers.items(): consumer.Inhibit(inhibit) # Save inhibit flag @@ -192,7 +174,7 @@ self.DataProducer.UnsubscribeDebugIECVariable("__tick__", self) # Unsubscribe all data consumers in list - for consumer, iec_path in self.DataConsumers.iteritems(): + for consumer, iec_path in self.DataConsumers.items(): self.DataProducer.UnsubscribeDebugIECVariable(iec_path, consumer) self.DataConsumers = {} diff -r 7e17f7e02a2b -r 0b3ac94f494c editors/EditorPanel.py --- a/editors/EditorPanel.py Wed Nov 29 11:54:56 2023 +0100 +++ b/editors/EditorPanel.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,7 +23,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx from controls import VariablePanel diff -r 7e17f7e02a2b -r 0b3ac94f494c editors/FileManagementPanel.py --- a/editors/FileManagementPanel.py Wed Nov 29 11:54:56 2023 +0100 +++ b/editors/FileManagementPanel.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,7 +23,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import os import shutil @@ -43,14 +43,14 @@ 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.Add(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.Add(managed_dir_label, border=5, flag=wx.GROW | wx.BOTTOM) FILTER = _("All files (*.*)|*.*|CSV files (*.csv)|*.csv") self.ManagedDir = FolderTree(self.Editor, self.Folder, FILTER) - left_sizer.AddWindow(self.ManagedDir, 1, flag=wx.GROW) + left_sizer.Add(self.ManagedDir, 1, flag=wx.GROW) managed_treectrl = self.ManagedDir.GetTreeCtrl() self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnTreeItemChanged, managed_treectrl) @@ -58,7 +58,7 @@ self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnTreeBeginDrag, managed_treectrl) button_sizer = wx.BoxSizer(wx.VERTICAL) - main_sizer.AddSizer(button_sizer, border=5, + main_sizer.Add(button_sizer, border=5, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL) for idx, (name, bitmap, help) in enumerate([ @@ -70,26 +70,26 @@ self.Editor, bitmap=GetBitmap(bitmap), size=wx.Size(28, 28), style=wx.NO_BORDER) - button.SetToolTipString(help) + button.SetToolTip(help) setattr(self, name, button) if idx > 0: flag = wx.TOP else: flag = 0 self.Bind(wx.EVT_BUTTON, getattr(self, "On" + name), button) - button_sizer.AddWindow(button, border=20, flag=flag) + button_sizer.Add(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.Add(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.Add(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) + right_sizer.Add(self.SystemDir, 1, flag=wx.GROW) system_treectrl = self.SystemDir.GetTreeCtrl() self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnTreeItemChanged, system_treectrl) @@ -114,9 +114,6 @@ self.SetIcon(GetBitmap("FOLDER")) - def __del__(self): - self.Controler.OnCloseEditor(self) - def GetTitle(self): return _(self.TagName) diff -r 7e17f7e02a2b -r 0b3ac94f494c editors/IECCodeViewer.py --- a/editors/IECCodeViewer.py Wed Nov 29 11:54:56 2023 +0100 +++ b/editors/IECCodeViewer.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,18 +23,13 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + 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) diff -r 7e17f7e02a2b -r 0b3ac94f494c editors/LDViewer.py --- a/editors/LDViewer.py Wed Nov 29 11:54:56 2023 +0100 +++ b/editors/LDViewer.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,12 +23,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import division -from future.builtins import round - import wx -from six.moves import xrange from editors.Viewer import * @@ -69,7 +64,7 @@ elif element_tree[element]: element_tree[element]["parents"].append("start") remove_stops = {"start": [], "stop": []} - for element, values in element_tree.items(): + for element, values in list(element_tree.items()): if "stop" in values["children"]: removed = [] for child in values["children"]: @@ -88,7 +83,7 @@ element_tree[element]["parents"].remove("start") for element in remove_stops["stop"]: element_tree[element]["children"].remove("stop") - for element, values in element_tree.items(): + for element, values in list(element_tree.items()): if values and "stop" in values["children"]: CalcWeight(element, element_tree) if values["weight"]: @@ -301,7 +296,7 @@ return Viewer.SearchElements(self, bbox) elements = [] - for element in self.Blocks.values() + self.Comments.values(): + for element in list(self.Blocks.values()) + list(self.Comments.values()): if element.IsInSelection(bbox): elements.append(element) return elements @@ -1190,7 +1185,7 @@ def RefreshRungs(self, movey, fromidx): if movey != 0: - for i in xrange(fromidx, len(self.Rungs)): + for i in range(fromidx, len(self.Rungs)): self.RungComments[i].Move(0, movey) self.RungComments[i].RefreshModel() self.Rungs[i].Move(0, movey) diff -r 7e17f7e02a2b -r 0b3ac94f494c editors/ProjectNodeEditor.py --- a/editors/ProjectNodeEditor.py Wed Nov 29 11:54:56 2023 +0100 +++ b/editors/ProjectNodeEditor.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,7 +23,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx from controls import ProjectPropertiesPanel, VariablePanel @@ -60,7 +60,7 @@ 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.Insert(0, buttons_sizer, 0, border=5, flag=wx.ALL) self.MainSizer.Layout() self.VariableEditor = self.VariableEditorPanel diff -r 7e17f7e02a2b -r 0b3ac94f494c editors/ResourceEditor.py --- a/editors/ResourceEditor.py Wed Nov 29 11:54:56 2023 +0100 +++ b/editors/ResourceEditor.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,11 +23,9 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import import wx import wx.lib.buttons import wx.grid -from six.moves import xrange from graphics.GraphicCommons import REFRESH_HIGHLIGHT_PERIOD, ERROR_HIGHLIGHT from controls import CustomGrid, CustomTable, DurationCellEditor @@ -76,10 +74,6 @@ return [_("Interrupt"), _("Cyclic")] -def SingleCellEditor(*x): - return wx.grid.GridCellChoiceEditor() - - def CheckSingle(single, varlist): return single in varlist @@ -162,25 +156,21 @@ if interval != "" and IEC_TIME_MODEL.match(interval.upper()) is None: error = True elif colname == "Single": - editor = SingleCellEditor(self, colname) - editor.SetParameters(self.Parent.VariableList) + editor = wx.grid.GridCellChoiceEditor(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): error = True elif colname == "Triggering": - editor = wx.grid.GridCellChoiceEditor() - editor.SetParameters(",".join(map(_, GetTaskTriggeringOptions()))) + editor = wx.grid.GridCellChoiceEditor(list(map(_, GetTaskTriggeringOptions()))) elif colname == "Type": - editor = wx.grid.GridCellChoiceEditor() - editor.SetParameters(self.Parent.TypeList) + editor = wx.grid.GridCellChoiceEditor(self.Parent.TypeList) elif colname == "Priority": editor = wx.grid.GridCellNumberEditor() editor.SetParameters("0,65535") elif colname == "Task": - editor = wx.grid.GridCellChoiceEditor() - editor.SetParameters(self.Parent.TaskList) + editor = wx.grid.GridCellChoiceEditor(self.Parent.TaskList) grid.SetCellEditor(row, col, editor) grid.SetCellRenderer(row, col, renderer) @@ -206,8 +196,8 @@ if highlight_type is None: self.Highlights = {} else: - for _row, row_highlights in self.Highlights.iteritems(): - row_items = row_highlights.items() + for _row, row_highlights in self.Highlights.items(): + row_items = list(row_highlights.items()) for col, col_highlights in row_items: if highlight_type in col_highlights: col_highlights.remove(highlight_type) @@ -230,16 +220,16 @@ tasks_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=5) tasks_sizer.AddGrowableCol(0) tasks_sizer.AddGrowableRow(1) - main_sizer.AddSizer(tasks_sizer, border=5, + main_sizer.Add(tasks_sizer, border=5, 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) tasks_buttons_sizer.AddGrowableRow(0) - tasks_sizer.AddSizer(tasks_buttons_sizer, flag=wx.GROW) - - tasks_label = wx.StaticText(self.Editor, label=_(u'Tasks:')) - tasks_buttons_sizer.AddWindow(tasks_label, flag=wx.ALIGN_BOTTOM) + tasks_sizer.Add(tasks_buttons_sizer, flag=wx.GROW) + + tasks_label = wx.StaticText(self.Editor, label=_('Tasks:')) + tasks_buttons_sizer.Add(tasks_label, flag=wx.ALIGN_BOTTOM) for name, bitmap, help in [ ("AddTaskButton", "add_element", _("Add task")), @@ -250,27 +240,27 @@ bitmap=GetBitmap(bitmap), size=wx.Size(28, 28), style=wx.NO_BORDER) - button.SetToolTipString(help) + button.SetToolTip(help) setattr(self, name, button) - tasks_buttons_sizer.AddWindow(button) + tasks_buttons_sizer.Add(button) self.TasksGrid = CustomGrid(self.Editor, style=wx.VSCROLL) - self.TasksGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.OnTasksGridCellChange) - tasks_sizer.AddWindow(self.TasksGrid, flag=wx.GROW) + self.TasksGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGED, self.OnTasksGridCellChange) + tasks_sizer.Add(self.TasksGrid, flag=wx.GROW) instances_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=5) instances_sizer.AddGrowableCol(0) instances_sizer.AddGrowableRow(1) - main_sizer.AddSizer(instances_sizer, border=5, + main_sizer.Add(instances_sizer, border=5, 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) instances_buttons_sizer.AddGrowableRow(0) - instances_sizer.AddSizer(instances_buttons_sizer, flag=wx.GROW) - - instances_label = wx.StaticText(self.Editor, label=_(u'Instances:')) - instances_buttons_sizer.AddWindow(instances_label, flag=wx.ALIGN_BOTTOM) + instances_sizer.Add(instances_buttons_sizer, flag=wx.GROW) + + instances_label = wx.StaticText(self.Editor, label=_('Instances:')) + instances_buttons_sizer.Add(instances_label, flag=wx.ALIGN_BOTTOM) for name, bitmap, help in [ ("AddInstanceButton", "add_element", _("Add instance")), @@ -280,13 +270,13 @@ button = wx.lib.buttons.GenBitmapButton( self.Editor, bitmap=GetBitmap(bitmap), size=wx.Size(28, 28), style=wx.NO_BORDER) - button.SetToolTipString(help) + button.SetToolTip(help) setattr(self, name, button) - instances_buttons_sizer.AddWindow(button) + instances_buttons_sizer.Add(button) self.InstancesGrid = CustomGrid(self.Editor, style=wx.VSCROLL) - self.InstancesGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.OnInstancesGridCellChange) - instances_sizer.AddWindow(self.InstancesGrid, flag=wx.GROW) + self.InstancesGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGED, self.OnInstancesGridCellChange) + instances_sizer.Add(self.InstancesGrid, flag=wx.GROW) self.Editor.SetSizer(main_sizer) @@ -401,24 +391,21 @@ self.TasksGrid.SetFocus() - def __del__(self): - self.RefreshHighlightsTimer.Stop() - def RefreshTypeList(self): - self.TypeList = "" + self.TypeList = [] blocktypes = self.Controler.GetBlockResource() for blocktype in blocktypes: - self.TypeList += ",%s" % blocktype + self.TypeList.append(blocktype) def RefreshTaskList(self): - self.TaskList = "" - for row in xrange(self.TasksTable.GetNumberRows()): - self.TaskList += ",%s" % self.TasksTable.GetValueByName(row, "Name") + self.TaskList = [] + for row in range(self.TasksTable.GetNumberRows()): + self.TaskList.append(self.TasksTable.GetValueByName(row, "Name")) def RefreshVariableList(self): - self.VariableList = "" + self.VariableList = [] for variable in self.Controler.GetEditedResourceVariables(self.TagName): - self.VariableList += ",%s" % variable + self.VariableList.append(variable) def RefreshModel(self): self.Controler.SetEditedResourceInfos(self.TagName, self.TasksTable.GetData(), self.InstancesTable.GetData()) @@ -481,15 +468,15 @@ wx.CallAfter(self.ShowErrorMessage, message) return - tasklist = [name for name in self.TaskList.split(",") if name != ""] - for i in xrange(self.TasksTable.GetNumberRows()): + tasklist = [name for name in self.TaskList if name != ""] + for i in range(self.TasksTable.GetNumberRows()): task = self.TasksTable.GetValueByName(i, "Name") if task in tasklist: tasklist.remove(task) if len(tasklist) > 0: old_name = tasklist[0].upper() new_name = self.TasksTable.GetValue(row, col) - for i in xrange(self.InstancesTable.GetNumberRows()): + for i in range(self.InstancesTable.GetNumberRows()): name = self.InstancesTable.GetValueByName(i, "Task").upper() if old_name == name: self.InstancesTable.SetValueByName(i, "Task", new_name) diff -r 7e17f7e02a2b -r 0b3ac94f494c editors/SFCViewer.py --- a/editors/SFCViewer.py Wed Nov 29 11:54:56 2023 +0100 +++ b/editors/SFCViewer.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,8 +23,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import division + + import wx diff -r 7e17f7e02a2b -r 0b3ac94f494c editors/TextViewer.py --- a/editors/TextViewer.py Wed Nov 29 11:54:56 2023 +0100 +++ b/editors/TextViewer.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,14 +23,11 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import division import re from functools import reduce import wx import wx.stc -from six.moves import xrange from graphics.GraphicCommons import ERROR_HIGHLIGHT, SEARCH_RESULT_HIGHLIGHT, REFRESH_HIGHLIGHT_PERIOD from plcopen.structures import ST_BLOCK_START_KEYWORDS, IEC_BLOCK_START_KEYWORDS, LOCATIONDATATYPES @@ -43,17 +40,17 @@ NEWLINE = "\n" -NUMBERS = [str(i) for i in xrange(10)] +NUMBERS = [str(i) for i in range(10)] LETTERS = ['_'] -for i in xrange(26): +for i in range(26): LETTERS.append(chr(ord('a') + i)) LETTERS.append(chr(ord('A') + i)) [STC_PLC_WORD, STC_PLC_COMMENT, STC_PLC_NUMBER, STC_PLC_STRING, STC_PLC_VARIABLE, STC_PLC_PARAMETER, STC_PLC_FUNCTION, STC_PLC_JUMP, STC_PLC_ERROR, STC_PLC_SEARCH_RESULT, - STC_PLC_EMPTY] = range(11) -[SPACE, WORD, NUMBER, STRING, WSTRING, COMMENT, PRAGMA, DPRAGMA] = range(8) + STC_PLC_EMPTY] = list(range(11)) +[SPACE, WORD, NUMBER, STRING, WSTRING, COMMENT, PRAGMA, DPRAGMA] = list(range(8)) re_texts = {} re_texts["letter"] = "[A-Za-z]" @@ -70,7 +67,7 @@ def LineStartswith(line, symbols): - return reduce(lambda x, y: x or y, map(line.startswith, symbols), False) + return reduce(lambda x, y: x or y, list(map(line.startswith, symbols)), False) class TextViewer(EditorPanel): @@ -166,9 +163,6 @@ self.RefreshHighlightsTimer = wx.Timer(self, -1) self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer) - def __del__(self): - self.RefreshHighlightsTimer.Stop() - def GetTitle(self): if self.Debug or self.TagName == "": if len(self.InstancePath) > 15: @@ -197,12 +191,20 @@ def Colourise(self, start, end): self.Editor.Colourise(start, end) - def StartStyling(self, pos, mask): - self.Editor.StartStyling(pos, mask) + def StartStyling(self, *a, **k): + self.Editor.StartStyling(*a, **k) + + INDIC0 = 0 + INDIC1 = 1 + INDIC2 = 2 def SetStyling(self, length, style): self.Editor.SetStyling(length, style) + def SetIndicatorCurrentFillRange(self, start, length, indic): + self.Editor.SetIndicatorCurrent(indic) + self.Editor.IndicatorFillRange(start, length) + def GetCurrentPos(self): return self.Editor.GetCurrentPos() @@ -488,7 +490,7 @@ for category in self.Controler.GetBlockTypes(self.TagName, self.Debug): for blocktype in category["list"]: blockname = blocktype["name"].upper() - if blocktype["type"] == "function" and blockname not in self.Keywords and blockname not in self.Variables.keys(): + if blocktype["type"] == "function" and blockname not in self.Keywords and blockname not in list(self.Variables.keys()): interface = dict([(name, {}) for name, _type, _modifier in blocktype["inputs"] + blocktype["outputs"] if name != '']) for param in ["EN", "ENO"]: if param not in interface: @@ -562,7 +564,7 @@ start_pos = last_styled_pos = self.Editor.GetLineEndPosition(line_number - 1) + 1 self.RefreshLineFolding(line_number) end_pos = event.GetPosition() - self.StartStyling(start_pos, 0xff) + self.StartStyling(start_pos) current_context = self.Variables current_call = None @@ -597,9 +599,8 @@ else: self.SetStyling(current_pos - last_styled_pos, STC_PLC_EMPTY) if word not in ["]", ")"] and (self.GetCurrentPos() < last_styled_pos or self.GetCurrentPos() > current_pos): - self.StartStyling(last_styled_pos, wx.stc.STC_INDICS_MASK) - self.SetStyling(current_pos - last_styled_pos, wx.stc.STC_INDIC0_MASK) - self.StartStyling(current_pos, 0xff) + self.SetIndicatorCurrentFillRange(last_styled_pos, current_pos - last_styled_pos, self.INDIC0) + self.StartStyling(current_pos) else: self.SetStyling(current_pos - last_styled_pos, STC_PLC_EMPTY) last_styled_pos = current_pos @@ -700,9 +701,8 @@ else: self.SetStyling(current_pos - last_styled_pos, STC_PLC_EMPTY) if word not in ["]", ")"] and (self.GetCurrentPos() < last_styled_pos or self.GetCurrentPos() > current_pos): - self.StartStyling(last_styled_pos, wx.stc.STC_INDICS_MASK) - self.SetStyling(current_pos - last_styled_pos, wx.stc.STC_INDIC0_MASK) - self.StartStyling(current_pos, 0xff) + self.SetIndicatorCurrentFillRange(last_styled_pos, current_pos - last_styled_pos, self.INDIC0) + self.StartStyling(current_pos) if char == '.': if word != "]": if current_context is not None: @@ -893,9 +893,9 @@ elif words[0].upper() in ["JMP", "JMPC", "JMPNC"]: kw = self.Jumps else: - kw = self.Variables.keys() - else: - kw = self.Keywords + self.Variables.keys() + self.Functions.keys() + kw = list(self.Variables.keys()) + else: + kw = self.Keywords + list(self.Variables.keys()) + list(self.Functions.keys()) if len(kw) > 0: if len(words[-1]) > 0: kw = [keyword for keyword in kw if keyword.startswith(words[-1])] @@ -975,8 +975,8 @@ else: highlight_end_pos = self.Editor.GetLineEndPosition(end[0] - 1) + end[1] - indent + 2 if highlight_start_pos < end_pos and highlight_end_pos > start_pos: - self.StartStyling(highlight_start_pos, 0xff) + self.StartStyling(highlight_start_pos) self.SetStyling(highlight_end_pos - highlight_start_pos, highlight_type) - self.StartStyling(highlight_start_pos, 0x00) + self.StartStyling(highlight_end_pos, 0x00) until_end = max(0, len(self.Editor.GetText()) - highlight_end_pos) self.SetStyling(until_end, wx.stc.STC_STYLE_DEFAULT) diff -r 7e17f7e02a2b -r 0b3ac94f494c editors/Viewer.py --- a/editors/Viewer.py Wed Nov 29 11:54:56 2023 +0100 +++ b/editors/Viewer.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,15 +23,13 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import division +from functools import cmp_to_key +from operator import eq import math from time import time as gettime from threading import Lock -from future.builtins import round import wx -from six.moves import xrange from plcopen.structures import * from plcopen.types_enums import ComputePouName @@ -60,11 +58,11 @@ global CURSORS if CURSORS is None: CURSORS = [wx.NullCursor, - wx.StockCursor(wx.CURSOR_HAND), - wx.StockCursor(wx.CURSOR_SIZENWSE), - wx.StockCursor(wx.CURSOR_SIZENESW), - wx.StockCursor(wx.CURSOR_SIZEWE), - wx.StockCursor(wx.CURSOR_SIZENS)] + wx.Cursor(wx.CURSOR_HAND), + wx.Cursor(wx.CURSOR_SIZENWSE), + wx.Cursor(wx.CURSOR_SIZENESW), + wx.Cursor(wx.CURSOR_SIZEWE), + wx.Cursor(wx.CURSOR_SIZENS)] if wx.Platform == '__WXMSW__': @@ -88,8 +86,10 @@ MAX_ZOOMIN = 4 else: MAX_ZOOMIN = 7 -ZOOM_FACTORS = [math.sqrt(2) ** x for x in xrange(-6, MAX_ZOOMIN)] - +ZOOM_FACTORS = [math.sqrt(2) ** x for x in range(-6, MAX_ZOOMIN)] + + +WX_NO_LOGICAL = "gtk3" in wx.PlatformInfo def GetVariableCreationFunction(variable_type): def variableCreationFunction(viewer, id, specific_values): @@ -225,9 +225,9 @@ x1, y1 = block_infos1[0].GetPosition() x2, y2 = block_infos2[0].GetPosition() if y1 == y2: - return cmp(x1, x2) + return eq(x1, x2) else: - return cmp(y1, y2) + return eq(y1, y2) # ------------------------------------------------------------------------------- # Graphic elements Viewer base class @@ -317,7 +317,7 @@ selected = None dialog.Destroy() if selected is None: - return + return False if selected == 0: location = "%I" + location elif selected == 1: @@ -333,7 +333,7 @@ var_name = dlg.GetValue() if dlg.ShowModal() == wx.ID_OK else None dlg.Destroy() if var_name is None: - return + return False 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)]: @@ -363,7 +363,7 @@ var_name = dlg.GetValue() if dlg.ShowModal() == wx.ID_OK else None dlg.Destroy() if var_name is None: - return + return False 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)]: @@ -385,7 +385,7 @@ var_name = dlg.GetValue() if dlg.ShowModal() == wx.ID_OK else None dlg.Destroy() if var_name is None: - return + return False 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)]: @@ -410,7 +410,7 @@ if len(tree[0]) > 0: menu = wx.Menu(title='') self.GenerateTreeMenu(x, y, scaling, menu, "", var_class, [(values[0], values[2], tree)]) - self.ParentWindow.PopupMenuXY(menu) + self.ParentWindow.PopupMenu(menu) else: self.ParentWindow.AddVariableBlock(x, y, scaling, var_class, values[0], values[2]) else: @@ -419,6 +419,8 @@ message = _("Variable don't belong to this POU!") if message is not None: wx.CallAfter(self.ShowMessage, message) + return False + return True def GenerateTreeMenu(self, x, y, scaling, menu, base_path, var_class, tree): for child_name, child_type, (child_tree, child_dimensions) in tree: @@ -429,8 +431,10 @@ if len(child_dimensions) > 0: child_path += "[%s]" % ",".join([str(dimension[0]) for dimension in child_dimensions]) child_name += "[]" - item = menu.Append(wx.ID_ANY, help='', kind=wx.ITEM_NORMAL, text=child_name) - self.ParentWindow.Bind(wx.EVT_MENU, self.GetAddVariableBlockFunction(x, y, scaling, var_class, child_path, child_type), item) + + item = self.AppendItem(menu, + child_name, + self.GetAddVariableBlockFunction(x, y, scaling, var_class, child_path, child_type)) if len(child_tree) > 0: child_menu = wx.Menu(title='') self.GenerateTreeMenu(x, y, scaling, child_menu, child_path, var_class, child_tree) @@ -475,7 +479,7 @@ dc = self.Parent.GetLogicalDC() ipw, _iph = dc.GetTextExtent(self.GetInstanceName()) vw, vh = 0, 0 - for value in self.VALUE_TRANSLATION.itervalues(): + for value in self.VALUE_TRANSLATION.values(): w, h = dc.GetTextExtent(" (%s)" % value) vw = max(vw, w) vh = max(vh, h) @@ -520,10 +524,10 @@ # Add Block Pin Menu items to the given menu def AddBlockPinMenuItems(self, menu, connector): - no_modifier = self.AppendItem(menu, _(u'No modifier'), self.OnNoModifierMenu, kind=wx.ITEM_RADIO) - negated = self.AppendItem(menu, _(u'Negated'), self.OnNegatedMenu, kind=wx.ITEM_RADIO) - rising_edge = self.AppendItem(menu, _(u'Rising Edge'), self.OnRisingEdgeMenu, kind=wx.ITEM_RADIO) - falling_edge = self.AppendItem(menu, _(u'Falling Edge'), self.OnFallingEdgeMenu, kind=wx.ITEM_RADIO) + no_modifier = self.AppendItem(menu, _('No modifier'), self.OnNoModifierMenu, kind=wx.ITEM_RADIO) + negated = self.AppendItem(menu, _('Negated'), self.OnNegatedMenu, kind=wx.ITEM_RADIO) + rising_edge = self.AppendItem(menu, _('Rising Edge'), self.OnRisingEdgeMenu, kind=wx.ITEM_RADIO) + falling_edge = self.AppendItem(menu, _('Falling Edge'), self.OnFallingEdgeMenu, kind=wx.ITEM_RADIO) not_a_function = self.Controler.GetEditedElementType( self.TagName, self.Debug) != "function" @@ -541,20 +545,20 @@ # Add Alignment Menu items to the given menu def AddAlignmentMenuItems(self, menu): - self.AppendItem(menu, _(u'Left'), self.OnAlignLeftMenu) - self.AppendItem(menu, _(u'Center'), self.OnAlignCenterMenu) - self.AppendItem(menu, _(u'Right'), self.OnAlignRightMenu) + self.AppendItem(menu, _('Left'), self.OnAlignLeftMenu) + self.AppendItem(menu, _('Center'), self.OnAlignCenterMenu) + self.AppendItem(menu, _('Right'), self.OnAlignRightMenu) menu.AppendSeparator() - self.AppendItem(menu, _(u'Top'), self.OnAlignTopMenu) - self.AppendItem(menu, _(u'Middle'), self.OnAlignMiddleMenu) - self.AppendItem(menu, _(u'Bottom'), self.OnAlignBottomMenu) + self.AppendItem(menu, _('Top'), self.OnAlignTopMenu) + self.AppendItem(menu, _('Middle'), self.OnAlignMiddleMenu) + self.AppendItem(menu, _('Bottom'), self.OnAlignBottomMenu) # Add Wire Menu items to the given menu def AddWireMenuItems(self, menu, delete=False, replace=False): - self.AppendItem(menu, _(u'Add Wire Segment'), self.OnAddSegmentMenu) - delete_segment = self.AppendItem(menu, _(u'Delete Wire Segment'), + self.AppendItem(menu, _('Add Wire Segment'), self.OnAddSegmentMenu) + delete_segment = self.AppendItem(menu, _('Delete Wire Segment'), self.OnDeleteSegmentMenu) - replace_wire = self.AppendItem(menu, _(u'Replace Wire by connections'), + replace_wire = self.AppendItem(menu, _('Replace Wire by connections'), self.OnReplaceWireMenu) delete_segment.Enable(delete) @@ -562,81 +566,81 @@ # Add Divergence Menu items to the given menu def AddDivergenceMenuItems(self, menu, delete=False): - self.AppendItem(menu, _(u'Add Divergence Branch'), + self.AppendItem(menu, _('Add Divergence Branch'), self.OnAddBranchMenu) - delete_branch = self.AppendItem(menu, _(u'Delete Divergence Branch'), + delete_branch = self.AppendItem(menu, _('Delete Divergence Branch'), self.OnDeleteBranchMenu) delete_branch.Enable(delete) # Add Add Menu items to the given menu def AddAddMenuItems(self, menu): - self.AppendItem(menu, _(u'Block'), + self.AppendItem(menu, _('Block'), self.GetAddMenuCallBack(self.AddNewBlock)) - self.AppendItem(menu, _(u'Variable'), + self.AppendItem(menu, _('Variable'), self.GetAddMenuCallBack(self.AddNewVariable)) - self.AppendItem(menu, _(u'Connection'), + self.AppendItem(menu, _('Connection'), self.GetAddMenuCallBack(self.AddNewConnection)) menu.AppendSeparator() if self.CurrentLanguage != "FBD": - self.AppendItem(menu, _(u'Power Rail'), + self.AppendItem(menu, _('Power Rail'), self.GetAddMenuCallBack(self.AddNewPowerRail)) - self.AppendItem(menu, _(u'Contact'), + self.AppendItem(menu, _('Contact'), self.GetAddMenuCallBack(self.AddNewContact)) if self.CurrentLanguage != "SFC": - self.AppendItem(menu, _(u'Coil'), + self.AppendItem(menu, _('Coil'), self.GetAddMenuCallBack(self.AddNewCoil)) menu.AppendSeparator() if self.CurrentLanguage == "SFC": - self.AppendItem(menu, _(u'Initial Step'), + self.AppendItem(menu, _('Initial Step'), self.GetAddMenuCallBack(self.AddNewStep, True)) - self.AppendItem(menu, (u'Step'), + self.AppendItem(menu, ('Step'), self.GetAddMenuCallBack(self.AddNewStep)) - self.AppendItem(menu, (u'Transition'), + self.AppendItem(menu, ('Transition'), self.GetAddMenuCallBack(self.AddNewTransition)) - self.AppendItem(menu, (u'Action Block'), + self.AppendItem(menu, ('Action Block'), self.GetAddMenuCallBack(self.AddNewActionBlock)) - self.AppendItem(menu, (u'Divergence'), + self.AppendItem(menu, ('Divergence'), self.GetAddMenuCallBack(self.AddNewDivergence)) - self.AppendItem(menu, (u'Jump'), + self.AppendItem(menu, ('Jump'), self.GetAddMenuCallBack(self.AddNewJump)) menu.AppendSeparator() - self.AppendItem(menu, _(u'Comment'), + self.AppendItem(menu, _('Comment'), self.GetAddMenuCallBack(self.AddNewComment)) # Add Default Menu items to the given menu def AddDefaultMenuItems(self, menu, edit=False, block=False): if block: - edit_block = self.AppendItem(menu, _(u'Edit Block'), + edit_block = self.AppendItem(menu, _('Edit Block'), self.OnEditBlockMenu) - self.AppendItem(menu, _(u'Adjust Block Size'), + self.AppendItem(menu, _('Adjust Block Size'), self.OnAdjustBlockSizeMenu) - self.AppendItem(menu, _(u'Delete'), self.OnDeleteMenu) + self.AppendItem(menu, _('Delete'), self.OnDeleteMenu) edit_block.Enable(edit) else: if self.CurrentLanguage == 'FBD': - self.AppendItem(menu, _(u'Clear Execution Order'), + self.AppendItem(menu, _('Clear Execution Order'), self.OnClearExecutionOrderMenu) - self.AppendItem(menu, _(u'Reset Execution Order'), + self.AppendItem(menu, _('Reset Execution Order'), self.OnResetExecutionOrderMenu) menu.AppendSeparator() add_menu = wx.Menu(title='') self.AddAddMenuItems(add_menu) - menu.AppendMenu(-1, _(u'Add'), add_menu) + menu.AppendMenu(-1, _('Add'), add_menu) menu.AppendSeparator() - cut = self.AppendItem(menu, _(u'Cut'), self.GetClipboardCallBack(self.Cut)) - copy = self.AppendItem(menu, _(u'Copy'), self.GetClipboardCallBack(self.Copy)) - paste = self.AppendItem(menu, _(u'Paste'), self.GetAddMenuCallBack(self.Paste)) + cut = self.AppendItem(menu, _('Cut'), self.GetClipboardCallBack(self.Cut)) + copy = self.AppendItem(menu, _('Copy'), self.GetClipboardCallBack(self.Copy)) + paste = self.AppendItem(menu, _('Paste'), self.GetAddMenuCallBack(self.Paste)) cut.Enable(block) copy.Enable(block) @@ -712,8 +716,8 @@ break faces["size"] -= 1 self.Editor.SetFont(font) - self.MiniTextDC = wx.MemoryDC(wx.EmptyBitmap(1, 1)) - self.MiniTextDC.SetFont(wx.Font(faces["size"] * 0.75, wx.SWISS, wx.NORMAL, wx.NORMAL, faceName=faces["helv"])) + self.MiniTextDC = wx.MemoryDC(wx.Bitmap(1, 1)) + self.MiniTextDC.SetFont(wx.Font(int(faces["size"] * 0.75), wx.SWISS, wx.NORMAL, wx.NORMAL, faceName=faces["helv"])) self.CurrentScale = None self.SetScale(ZOOM_FACTORS.index(1.0), False) @@ -741,13 +745,6 @@ self.Editor.Bind(wx.EVT_SIZE, self.OnMoveWindow) self.Editor.Bind(wx.EVT_MOUSE_EVENTS, self.OnViewerMouseEvent) - # Destructor - def __del__(self): - DebugViewer.__del__(self) - self.Flush() - self.ResetView() - self.RefreshHighlightsTimer.Stop() - def SetCurrentCursor(self, cursor): if self.Mode != MODE_MOTION: if self.CurrentCursor != cursor: @@ -808,8 +805,8 @@ pos = mouse_event.GetLogicalPosition(dc) xmax = self.GetScrollRange(wx.HORIZONTAL) - self.GetScrollThumb(wx.HORIZONTAL) ymax = self.GetScrollRange(wx.VERTICAL) - self.GetScrollThumb(wx.VERTICAL) - scrollx = max(0, round(pos.x * self.ViewScale[0] - mouse_pos.x) / SCROLLBAR_UNIT) - scrolly = max(0, round(pos.y * self.ViewScale[1] - mouse_pos.y) / SCROLLBAR_UNIT) + scrollx = max(0, round(pos.x * self.ViewScale[0] - mouse_pos.x) // SCROLLBAR_UNIT) + scrolly = max(0, round(pos.y * self.ViewScale[1] - mouse_pos.y) // SCROLLBAR_UNIT) if scrollx > xmax or scrolly > ymax: self.RefreshScrollBars(max(0, scrollx - xmax), max(0, scrolly - ymax)) self.Scroll(scrollx, scrolly) @@ -825,15 +822,16 @@ def GetViewScale(self): return self.ViewScale - def GetLogicalDC(self, buffered=False): - if buffered: - bitmap = wx.EmptyBitmap(*self.Editor.GetClientSize()) - dc = wx.MemoryDC(bitmap) - else: - dc = wx.ClientDC(self.Editor) + def PrepareDC(self, dc): dc.SetFont(self.GetFont()) self.Editor.DoPrepareDC(dc) dc.SetUserScale(self.ViewScale[0], self.ViewScale[1]) + if WX_NO_LOGICAL: + dc.SetLogicalFunction = lambda *a,**k: None + + def GetLogicalDC(self): + dc = wx.ClientDC(self.Editor) + self.PrepareDC(dc) return dc def RefreshRect(self, rect, eraseBackground=True): @@ -913,20 +911,22 @@ self.Comments.pop(comment.GetId()) def GetElements(self, sort_blocks=False, sort_wires=False, sort_comments=False): - blocks = self.Blocks.values() - wires = self.Wires.keys() - comments = self.Comments.values() + blocks = list(self.Blocks.values()) + wires = list(self.Wires.keys()) + comments = list(self.Comments.values()) if sort_blocks: - blocks.sort(lambda x, y: cmp(x.GetId(), y.GetId())) + blocks.sort(key=cmp_to_key(lambda x, y: eq(x.GetId(), y.GetId()))) if sort_wires: - wires.sort(lambda x, y: cmp(self.Wires[x], self.Wires[y])) + wires.sort(key=cmp_to_key(lambda x, y: eq(self.Wires[x], + self.Wires[y]))) if sort_comments: - comments.sort(lambda x, y: cmp(x.GetId(), y.GetId())) + comments.sort(key=cmp_to_key(lambda x, y: eq(x.GetId(), + y.GetId()))) return blocks + wires + comments def GetContinuationByName(self, name): blocks = [] - for block in self.Blocks.itervalues(): + for block in self.Blocks.values(): if isinstance(block, FBD_Connector) and\ block.GetType() == CONTINUATION and\ block.GetName() == name: @@ -934,7 +934,7 @@ return blocks def GetConnectorByName(self, name): - for block in self.Blocks.itervalues(): + for block in self.Blocks.values(): if isinstance(block, FBD_Connector) and\ block.GetType() == CONNECTOR and\ block.GetName() == name: @@ -950,11 +950,11 @@ width, height = self.Editor.GetClientSize() screen = wx.Rect(int(x / self.ViewScale[0]), int(y / self.ViewScale[1]), int(width / self.ViewScale[0]), int(height / self.ViewScale[1])) - for comment in self.Comments.itervalues(): + for comment in self.Comments.values(): comment.TestVisible(screen) - for wire in self.Wires.iterkeys(): + for wire in self.Wires.keys(): wire.TestVisible(screen) - for block in self.Blocks.itervalues(): + for block in self.Blocks.values(): block.TestVisible(screen) def GetElementIECPath(self, element): @@ -1035,12 +1035,12 @@ def Flush(self): self.UnsubscribeAllDataConsumers(tick=False) - for block in self.Blocks.itervalues(): + for block in self.Blocks.values(): block.Flush() # Remove all elements def CleanView(self): - for block in self.Blocks.itervalues(): + for block in self.Blocks.values(): block.Clean() self.ResetView() @@ -1058,7 +1058,7 @@ self.SelectedElement.SetSelected(False) self.SelectedElement = None if self.Mode == MODE_MOTION: - wx.CallAfter(self.Editor.SetCursor, wx.StockCursor(wx.CURSOR_HAND)) + wx.CallAfter(self.Editor.SetCursor, wx.Cursor(wx.CURSOR_HAND)) self.SavedMode = True # Return current drawing mode @@ -1116,13 +1116,13 @@ if self.DrawGrid: width = max(2, int(scaling[0] * self.ViewScale[0])) height = max(2, int(scaling[1] * self.ViewScale[1])) - bitmap = wx.EmptyBitmap(width, height) + bitmap = wx.Bitmap(width, height) dc = wx.MemoryDC(bitmap) dc.SetBackground(wx.Brush(self.Editor.GetBackgroundColour())) dc.Clear() dc.SetPen(MiterPen(wx.Colour(180, 180, 180))) dc.DrawPoint(0, 0) - self.GridBrush = wx.BrushFromBitmap(bitmap) + self.GridBrush = wx.Brush(bitmap) else: self.GridBrush = wx.TRANSPARENT_BRUSH else: @@ -1130,7 +1130,7 @@ self.GridBrush = wx.TRANSPARENT_BRUSH page_size = properties["pageSize"] if page_size != (0, 0): - self.PageSize = map(int, page_size) + self.PageSize = list(map(int, page_size)) self.PagePen = MiterPen(wx.Colour(180, 180, 180)) else: self.PageSize = None @@ -1212,7 +1212,7 @@ wire.SetModifier(self.GetWireModifier(wire)) if self.Debug: - for block in self.Blocks.itervalues(): + for block in self.Blocks.values(): block.SpreadCurrent() if isinstance(block, FBD_Block): for output_connector in block.GetConnectors()["outputs"]: @@ -1373,7 +1373,7 @@ element.SetPosition(instance.x, instance.y) element.SetSize(instance.width, instance.height) for i, output_connector in enumerate(instance.outputs): - connector_pos = wx.Point(*output_connector.position) + connector_pos = wx.Point(*map(int, output_connector.position)) if isinstance(element, FBD_Block): connector = element.GetConnector(connector_pos, output_name=output_connector.name) @@ -1389,7 +1389,7 @@ if connectors["outputs"].index(connector) == i: connector.SetPosition(connector_pos) for i, input_connector in enumerate(instance.inputs): - connector_pos = wx.Point(*input_connector.position) + connector_pos = wx.Point(*map(int,input_connector.position)) if isinstance(element, FBD_Block): connector = element.GetConnector(connector_pos, input_name=input_connector.name) @@ -1430,7 +1430,7 @@ points = link.points end_connector = connected.GetConnector( - wx.Point(points[-1].x, points[-1].y) + wx.Point(int(points[-1].x), int(points[-1].y)) if len(points) > 0 else wx.Point(0, 0), link.formalParameter) if end_connector is not None: @@ -1472,7 +1472,7 @@ def FindBlock(self, event): dc = self.GetLogicalDC() pos = event.GetLogicalPosition(dc) - for block in self.Blocks.itervalues(): + for block in self.Blocks.values(): if block.HitTest(pos) or block.TestHandle(event) != (0, 0): return block return None @@ -1503,7 +1503,7 @@ def FindBlockConnectorWithError(self, pos, direction=None, exclude=None): error = False startblock = None - for block in self.Blocks.itervalues(): + for block in self.Blocks.values(): connector = block.TestConnector(pos, direction, exclude) if connector: if self.IsWire(self.SelectedElement): @@ -1572,10 +1572,15 @@ iec_path = self.GetElementIECPath(self.SelectedElement) if iec_path is not None: menu = wx.Menu(title='') - item = menu.Append(wx.ID_ANY, help='', kind=wx.ITEM_NORMAL, text=_("Force value")) - self.Bind(wx.EVT_MENU, self.GetForceVariableMenuFunction(iec_path.upper(), self.SelectedElement), item) - ritem = menu.Append(wx.ID_ANY, help='', kind=wx.ITEM_NORMAL, text=_("Release value")) - self.Bind(wx.EVT_MENU, self.GetReleaseVariableMenuFunction(iec_path.upper()), ritem) + item = self.AppendItem(menu, + _("Force value"), + self.GetForceVariableMenuFunction( + iec_path.upper(), + self.SelectedElement)) + + ritem = self.AppendItem(menu, + _("Release value"), + self.GetReleaseVariableMenuFunction(iec_path.upper())) if self.SelectedElement.IsForced(): ritem.Enable(True) else: @@ -1658,7 +1663,7 @@ menu = wx.Menu(title='') align_menu = wx.Menu(title='') self.AddAlignmentMenuItems(align_menu) - menu.AppendMenu(-1, _(u'Alignment'), align_menu) + menu.AppendMenu(-1, _('Alignment'), align_menu) menu.AppendSeparator() self.AddDefaultMenuItems(menu, block=True) self.Editor.PopupMenu(menu) @@ -1903,9 +1908,9 @@ def OnViewerMouseEvent(self, event): self.ResetBuffer() - if event.Leaving() and self.ToolTipElement is not None: + if (event.Leaving() or event.RightUp()) and self.ToolTipElement is not None: self.ToolTipElement.DestroyToolTip() - elif (not event.Entering() and + elif (not event.Entering() and not event.RightUp() and gettime() - self.LastToolTipCheckTime > REFRESH_PERIOD): self.LastToolTipCheckTime = gettime() element = None @@ -1987,10 +1992,10 @@ NORTH: [NORTH, SOUTH], SOUTH: [SOUTH, NORTH]}[connector.GetDirection()] wire = Wire(self, - *map(list, zip( + *list(map(list, list(zip( [wx.Point(pos.x, pos.y), wx.Point(scaled_pos.x, scaled_pos.y)], - directions))) + directions))))) wire.oldPos = scaled_pos wire.Handle = (HANDLE_POINT, 0) wire.ProcessDragging(0, 0, event, None) @@ -2267,13 +2272,12 @@ 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: - self.HighlightedElement.SetHighlighted(False) - self.HighlightedElement = None - if highlighted is not None: - if not self.Debug and isinstance(highlighted, (Wire, Graphic_Group)): - highlighted.HighlightPoint(pos) - if self.HighlightedElement != highlighted: + if self.HighlightedElement != highlighted: + if self.HighlightedElement is not None: + self.HighlightedElement.SetHighlighted(False) + if highlighted is not None: + if not self.Debug and isinstance(highlighted, (Wire, Graphic_Group)): + highlighted.HighlightPoint(pos) highlighted.SetHighlighted(True) self.HighlightedElement = highlighted if self.rubberBand.IsShown(): @@ -2361,10 +2365,10 @@ poss_div_types = [] SFC_WireMenu_Buttons = { - 'SFC_Step': (_(u'Step'), self.GetAddToWireMenuCallBack(self.AddNewStep, False)), - 'SFC_Jump': (_(u'Jump'), self.GetAddToWireMenuCallBack(self.AddNewJump)), - 'SFC_Transition': (_(u'Transition'), self.GetAddToWireMenuCallBack(self.AddNewTransition, False)), - 'SFC_ActionBlock': (_(u'Action Block'), self.GetAddToWireMenuCallBack(self.AddNewActionBlock))} + 'SFC_Step': (_('Step'), self.GetAddToWireMenuCallBack(self.AddNewStep, False)), + 'SFC_Jump': (_('Jump'), self.GetAddToWireMenuCallBack(self.AddNewJump)), + 'SFC_Transition': (_('Transition'), self.GetAddToWireMenuCallBack(self.AddNewTransition, False)), + 'SFC_ActionBlock': (_('Action Block'), self.GetAddToWireMenuCallBack(self.AddNewActionBlock))} for endblock in self.SFC_StandardRules.get(startblockname): if start_direction in endblock: @@ -2373,27 +2377,27 @@ else: items.append(SFC_WireMenu_Buttons[endblock[0]]) if len(poss_div_types) > 0: - items.append((_(u'Divergence'), self.GetAddToWireMenuCallBack(self.AddNewDivergence, + items.append((_('Divergence'), self.GetAddToWireMenuCallBack(self.AddNewDivergence, poss_div_types))) elif start_direction == EAST: items.extend([ - (_(u'Block'), self.GetAddToWireMenuCallBack(self.AddNewBlock)), - (_(u'Connection'), self.GetAddToWireMenuCallBack(self.AddNewConnection))]) + (_('Block'), self.GetAddToWireMenuCallBack(self.AddNewBlock)), + (_('Connection'), self.GetAddToWireMenuCallBack(self.AddNewConnection))]) if self.CurrentLanguage != "FBD": - items.append((_(u'Contact'), self.GetAddToWireMenuCallBack(self.AddNewContact))) + items.append((_('Contact'), self.GetAddToWireMenuCallBack(self.AddNewContact))) if self.CurrentLanguage == "LD": items.extend([ - (_(u'Coil'), self.GetAddToWireMenuCallBack(self.AddNewCoil)), - (_(u'Power Rail'), self.GetAddToWireMenuCallBack(self.AddNewPowerRail))]) + (_('Coil'), self.GetAddToWireMenuCallBack(self.AddNewCoil)), + (_('Power Rail'), self.GetAddToWireMenuCallBack(self.AddNewPowerRail))]) if self.CurrentLanguage == "SFC": items.append( - (_(u'Transition'), self.GetAddToWireMenuCallBack(self.AddNewTransition, True))) + (_('Transition'), self.GetAddToWireMenuCallBack(self.AddNewTransition, True))) else: items.append( - (_(u'Variable'), self.GetAddToWireMenuCallBack(self.AddNewVariable, True))) + (_('Variable'), self.GetAddToWireMenuCallBack(self.AddNewVariable, True))) return items # ------------------------------------------------------------------------------- @@ -2721,7 +2725,7 @@ def AddNewJump(self, bbox, wire=None): choices = [] - for block in self.Blocks.itervalues(): + for block in self.Blocks.values(): if isinstance(block, SFC_Step): choices.append(block.GetName()) dialog = wx.SingleChoiceDialog(self.ParentWindow, @@ -2936,7 +2940,7 @@ if self.GetDrawingMode() == DRIVENDRAWING_MODE: old_name = step.GetName().upper() if new_name.upper() != old_name: - for block in self.Blocks.itervalues(): + for block in self.Blocks.values(): if isinstance(block, SFC_Jump): if old_name == block.GetTarget().upper(): block.SetTarget(new_name) @@ -2989,7 +2993,7 @@ def EditJumpContent(self, jump): choices = [] - for block in self.Blocks.itervalues(): + for block in self.Blocks.values(): if isinstance(block, SFC_Step): choices.append(block.GetName()) dialog = wx.SingleChoiceDialog(self.ParentWindow, @@ -3303,7 +3307,7 @@ if self.GetDrawingMode() == DRIVENDRAWING_MODE: name = step.GetName().upper() remove_jumps = [] - for block in self.Blocks.itervalues(): + for block in self.Blocks.values(): if isinstance(block, SFC_Jump): if name == block.GetTarget().upper(): remove_jumps.append(block) @@ -3379,7 +3383,7 @@ element = self.ParentWindow.GetCopyBuffer() if bbx is None: mouse_pos = self.Editor.ScreenToClient(wx.GetMousePosition()) - middle = wx.Rect(0, 0, *self.Editor.GetClientSize()).InsideXY(mouse_pos.x, mouse_pos.y) + middle = wx.Rect(0, 0, *self.Editor.GetClientSize()).Contains(mouse_pos.x, mouse_pos.y) if middle: x, y = self.CalcUnscrolledPosition(mouse_pos.x, mouse_pos.y) else: @@ -3389,7 +3393,7 @@ middle = True new_pos = [bbx.x, bbx.y] result = self.Controler.PasteEditedElementInstances(self.TagName, element, new_pos, middle, self.Debug) - if not isinstance(result, string_types): + if not isinstance(result, str): self.RefreshBuffer() self.RefreshView(selection=result) self.RefreshVariablePanel() @@ -3502,7 +3506,7 @@ block = self.Blocks.get(infos[2]) if block is not None: blocks.append((block, (infos[1:], start, end, SEARCH_RESULT_HIGHLIGHT))) - blocks.sort(sort_blocks) + blocks.sort(key=cmp_to_key(sort_blocks)) self.SearchResults.extend([infos for block, infos in blocks]) self.CurrentFindHighlight = None @@ -3633,7 +3637,6 @@ else: dc.SetBackground(wx.Brush(self.Editor.GetBackgroundColour())) dc.Clear() - dc.BeginDrawing() if self.Scaling is not None and self.DrawGrid and not printing: dc.SetPen(wx.TRANSPARENT_PEN) dc.SetBrush(self.GridBrush) @@ -3648,27 +3651,27 @@ xstart, ystart = self.GetViewStart() window_size = self.Editor.GetClientSize() if self.PageSize[0] != 0: - for x in xrange(self.PageSize[0] - (xstart * SCROLLBAR_UNIT) % self.PageSize[0], int(window_size[0] / self.ViewScale[0]), self.PageSize[0]): + for x in range(self.PageSize[0] - (xstart * SCROLLBAR_UNIT) % self.PageSize[0], int(window_size[0] / self.ViewScale[0]), self.PageSize[0]): dc.DrawLine(xstart * SCROLLBAR_UNIT + x + 1, int(ystart * SCROLLBAR_UNIT / self.ViewScale[0]), xstart * SCROLLBAR_UNIT + x + 1, int((ystart * SCROLLBAR_UNIT + window_size[1]) / self.ViewScale[0])) if self.PageSize[1] != 0: - for y in xrange(self.PageSize[1] - (ystart * SCROLLBAR_UNIT) % self.PageSize[1], int(window_size[1] / self.ViewScale[1]), self.PageSize[1]): + for y in range(self.PageSize[1] - (ystart * SCROLLBAR_UNIT) % self.PageSize[1], int(window_size[1] / self.ViewScale[1]), self.PageSize[1]): dc.DrawLine(int(xstart * SCROLLBAR_UNIT / self.ViewScale[0]), ystart * SCROLLBAR_UNIT + y + 1, int((xstart * SCROLLBAR_UNIT + window_size[0]) / self.ViewScale[1]), ystart * SCROLLBAR_UNIT + y + 1) # Draw all elements - for comment in self.Comments.itervalues(): + for comment in self.Comments.values(): if comment != self.SelectedElement and (comment.IsVisible() or printing): comment.Draw(dc) - for wire in self.Wires.iterkeys(): + for wire in self.Wires.keys(): if wire != self.SelectedElement and (wire.IsVisible() or printing): if not self.Debug or not wire.GetValue(): wire.Draw(dc) if self.Debug: - for wire in self.Wires.iterkeys(): + for wire in self.Wires.keys(): if wire != self.SelectedElement and (wire.IsVisible() or printing) and wire.GetValue(): wire.Draw(dc) - for block in self.Blocks.itervalues(): + for block in self.Blocks.values(): if block != self.SelectedElement and (block.IsVisible() or printing): block.Draw(dc) @@ -3680,12 +3683,15 @@ self.InstanceName.Draw(dc) if self.rubberBand.IsShown(): self.rubberBand.Draw(dc) - dc.EndDrawing() def OnPaint(self, event): - dc = self.GetLogicalDC(True) + event.Skip() + sx,sy = self.Editor.GetClientSize() + if sx <= 0 or sy <= 0 : + return + dc = wx.MemoryDC(wx.Bitmap(sx,sy)) + self.PrepareDC(dc) self.DoDrawing(dc) wx.BufferedPaintDC(self.Editor, dc.GetAsBitmap()) if self.Debug: DebugViewer.RefreshNewData(self) - event.Skip() diff -r 7e17f7e02a2b -r 0b3ac94f494c etherlab/ConfigEditor.py --- a/etherlab/ConfigEditor.py Wed Nov 29 11:54:56 2023 +0100 +++ b/etherlab/ConfigEditor.py Thu Dec 07 22:41:32 2023 +0100 @@ -9,14 +9,14 @@ # # See COPYING file for copyrights details. -from __future__ import absolute_import -from __future__ import division + + import os import re import wx import wx.grid -import wx.gizmos +import wx.adv import wx.lib.buttons from plcopen.structures import IEC_KEYWORDS, TestIdentifier @@ -30,7 +30,7 @@ from etherlab.EtherCATManagementEditor import EtherCATManagementTreebook, MasterStatePanelClass # ----------------------------------------------------------------------- -[ETHERCAT_VENDOR, ETHERCAT_GROUP, ETHERCAT_DEVICE] = range(3) +[ETHERCAT_VENDOR, ETHERCAT_GROUP, ETHERCAT_DEVICE] = list(range(3)) def GetVariablesTableColnames(position=False): @@ -81,9 +81,9 @@ self.VariablesFilter.Bind(wx.EVT_COMBOBOX, self.OnVariablesFilterChanged) self.VariablesFilter.Bind(wx.EVT_TEXT_ENTER, self.OnVariablesFilterChanged) self.VariablesFilter.Bind(wx.EVT_CHAR, self.OnVariablesFilterKeyDown) - self.AddWindow(self.VariablesFilter, flag=wx.GROW) - - self.VariablesGrid = wx.gizmos.TreeListCtrl(parent, + self.Add(self.VariablesFilter, flag=wx.GROW) + + self.VariablesGrid = wx.adv.TreeListCtrl(parent, style=wx.TR_DEFAULT_STYLE | wx.TR_ROW_LINES | wx.TR_COLUMN_LINES | @@ -91,7 +91,7 @@ wx.TR_FULL_ROW_HIGHLIGHT) self.VariablesGrid.GetMainWindow().Bind(wx.EVT_LEFT_DOWN, self.OnVariablesGridLeftClick) - self.AddWindow(self.VariablesGrid, flag=wx.GROW) + self.Add(self.VariablesGrid, flag=wx.GROW) self.Filters = [] for desc, value in VARIABLES_FILTERS: @@ -274,10 +274,10 @@ variables_label = wx.StaticText(self.EthercatNodeEditor, label=_('Variable entries:')) - main_sizer.AddWindow(variables_label, border=10, flag=wx.TOP | wx.LEFT | wx.RIGHT) + main_sizer.Add(variables_label, border=10, flag=wx.TOP | wx.LEFT | wx.RIGHT) self.NodeVariables = NodeVariablesSizer(self.EthercatNodeEditor, self.Controler) - main_sizer.AddSizer(self.NodeVariables, border=10, + main_sizer.Add(self.NodeVariables, border=10, flag=wx.GROW | wx.BOTTOM | wx.LEFT | wx.RIGHT) self.EthercatNodeEditor.SetSizer(main_sizer) @@ -310,7 +310,7 @@ self.EtherCATManagementTreebook = EtherCATManagementTreebook(self.EtherCATManagementEditor, self.Controler, self) - self.EtherCATManagermentEditor_Main_Sizer.AddSizer(self.EtherCATManagementTreebook, border=10, flag=wx.GROW) + self.EtherCATManagermentEditor_Main_Sizer.Add(self.EtherCATManagementTreebook, border=10, flag=wx.GROW) self.EtherCATManagementEditor.SetSizer(self.EtherCATManagermentEditor_Main_Sizer) return self.EtherCATManagementEditor @@ -421,7 +421,7 @@ if values[1] == "location": result = LOCATION_MODEL.match(values[0]) if result is not None: - location = map(int, result.group(1).split('.')) + location = list(map(int, result.group(1).split('.'))) master_location = self.ParentWindow.GetMasterLocation() if master_location == tuple(location[:len(master_location)]) and \ len(location) - len(master_location) == 3: @@ -449,6 +449,8 @@ if message is not None: wx.CallAfter(self.ShowMessage, message) + return False + return True def ShowMessage(self, message): message = wx.MessageDialog(self.ParentWindow, message, _("Error"), wx.OK | wx.ICON_ERROR) @@ -483,7 +485,7 @@ if values[1] == "location": result = LOCATION_MODEL.match(values[0]) if result is not None and len(values) > 5: - location = map(int, result.group(1).split('.')) + location = list(map(int, result.group(1).split('.'))) access = values[5] elif values[1] == "variable": location = values[0] @@ -501,6 +503,8 @@ if message is not None: wx.CallAfter(self.ShowMessage, message) + return False + return True def ShowMessage(self, message): message = wx.MessageDialog(self.ParentWindow, message, _("Error"), wx.OK | wx.ICON_ERROR) @@ -617,7 +621,7 @@ self.MasterStateEditor_Panel = MasterStatePanelClass(self.MasterStateEditor, self.Controler) - self.MasterStateEditor_Panel_Main_Sizer.AddSizer(self.MasterStateEditor_Panel, border=10, flag=wx.GROW) + self.MasterStateEditor_Panel_Main_Sizer.Add(self.MasterStateEditor_Panel, border=10, flag=wx.GROW) self.MasterStateEditor.SetSizer(self.MasterStateEditor_Panel_Main_Sizer) return self.MasterStateEditor @@ -651,7 +655,7 @@ process_variables_label = wx.StaticText(self.EthercatMasterEditor, label=_("Process variables mapped between nodes:")) - process_variables_header.AddWindow(process_variables_label, 1, + process_variables_header.Add(process_variables_label, 1, flag=wx.ALIGN_CENTER_VERTICAL) for name, bitmap, help in [ @@ -661,14 +665,14 @@ ("DownVariableButton", "down", _("Move process variable down"))]: button = wx.lib.buttons.GenBitmapButton(self.EthercatMasterEditor, bitmap=GetBitmap(bitmap), size=wx.Size(28, 28), style=wx.NO_BORDER) - button.SetToolTipString(help) + button.SetToolTip(help) setattr(self, name, button) - process_variables_header.AddWindow(button, border=5, flag=wx.LEFT) + process_variables_header.Add(button, border=5, flag=wx.LEFT) self.ProcessVariablesGrid = CustomGrid(self.EthercatMasterEditor, style=wx.VSCROLL) self.ProcessVariablesGrid.SetMinSize(wx.Size(0, 150)) self.ProcessVariablesGrid.SetDropTarget(ProcessVariableDropTarget(self)) - self.ProcessVariablesGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, + self.ProcessVariablesGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGING, self.OnProcessVariablesGridCellChange) self.ProcessVariablesGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.OnProcessVariablesGridCellLeftClick) @@ -678,7 +682,7 @@ startup_commands_label = wx.StaticText(self.EthercatMasterEditor, label=_("Startup service variables assignments:")) - startup_commands_header.AddWindow(startup_commands_label, 1, + startup_commands_header.Add(startup_commands_label, 1, flag=wx.ALIGN_CENTER_VERTICAL) for name, bitmap, help in [ @@ -686,14 +690,14 @@ ("DeleteCommandButton", "remove_element", _("Remove startup service variable"))]: button = wx.lib.buttons.GenBitmapButton(self.EthercatMasterEditor, bitmap=GetBitmap(bitmap), size=wx.Size(28, 28), style=wx.NO_BORDER) - button.SetToolTipString(help) + button.SetToolTip(help) setattr(self, name, button) - startup_commands_header.AddWindow(button, border=5, flag=wx.LEFT) + startup_commands_header.Add(button, border=5, flag=wx.LEFT) self.StartupCommandsGrid = CustomGrid(self.EthercatMasterEditor, style=wx.VSCROLL) self.StartupCommandsGrid.SetDropTarget(StartupCommandDropTarget(self)) self.StartupCommandsGrid.SetMinSize(wx.Size(0, 150)) - self.StartupCommandsGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, + self.StartupCommandsGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGING, self.OnStartupCommandsGridCellChange) self.StartupCommandsGrid.Bind(wx.grid.EVT_GRID_EDITOR_SHOWN, self.OnStartupCommandsGridEditorShow) @@ -702,29 +706,29 @@ main_staticbox = wx.StaticBox(self.EthercatMasterEditor, label=_("Node filter:")) staticbox_sizer = wx.StaticBoxSizer(main_staticbox, wx.VERTICAL) - self.EthercatMasterEditorSizer.AddSizer(staticbox_sizer, 0, border=10, flag=wx.GROW | wx.ALL) + self.EthercatMasterEditorSizer.Add(staticbox_sizer, 0, border=10, flag=wx.GROW | wx.ALL) main_staticbox_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=6, vgap=0) main_staticbox_sizer.AddGrowableCol(0) main_staticbox_sizer.AddGrowableRow(2) main_staticbox_sizer.AddGrowableRow(4) main_staticbox_sizer.AddGrowableRow(5) - staticbox_sizer.AddSizer(main_staticbox_sizer, 1, flag=wx.GROW) - main_staticbox_sizer.AddWindow(self.NodesFilter, border=5, flag=wx.GROW | wx.ALL) - main_staticbox_sizer.AddSizer(process_variables_header, border=5, + staticbox_sizer.Add(main_staticbox_sizer, 1, flag=wx.GROW) + main_staticbox_sizer.Add(self.NodesFilter, border=5, flag=wx.GROW | wx.ALL) + main_staticbox_sizer.Add(process_variables_header, border=5, flag=wx.GROW | wx.LEFT | wx.RIGHT | wx.BOTTOM) - main_staticbox_sizer.AddWindow(self.ProcessVariablesGrid, 1, + main_staticbox_sizer.Add(self.ProcessVariablesGrid, 1, border=5, flag=wx.GROW | wx.LEFT | wx.RIGHT | wx.BOTTOM) - main_staticbox_sizer.AddSizer(startup_commands_header, + main_staticbox_sizer.Add(startup_commands_header, border=5, flag=wx.GROW | wx.LEFT | wx.RIGHT | wx.BOTTOM) - main_staticbox_sizer.AddWindow(self.StartupCommandsGrid, 1, + main_staticbox_sizer.Add(self.StartupCommandsGrid, 1, border=5, flag=wx.GROW | wx.LEFT | wx.RIGHT | wx.BOTTOM) second_staticbox = wx.StaticBox(self.EthercatMasterEditor, label=_("Nodes variables filter:")) second_staticbox_sizer = wx.StaticBoxSizer(second_staticbox, wx.VERTICAL) - second_staticbox_sizer.AddSizer(self.NodesVariables, 1, border=5, flag=wx.GROW | wx.ALL) - - main_staticbox_sizer.AddSizer(second_staticbox_sizer, 1, + second_staticbox_sizer.Add(self.NodesVariables, 1, border=5, flag=wx.GROW | wx.ALL) + + main_staticbox_sizer.Add(second_staticbox_sizer, 1, border=5, flag=wx.GROW | wx.LEFT | wx.RIGHT | wx.BOTTOM) self.EthercatMasterEditor.SetSizer(self.EthercatMasterEditorSizer) @@ -1113,21 +1117,21 @@ ESI_files_label = wx.StaticText(parent, label=_("ESI Files:")) - self.AddWindow(ESI_files_label, border=10, + self.Add(ESI_files_label, border=10, flag=wx.TOP | wx.LEFT | wx.RIGHT) folder_tree_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=1, vgap=0) folder_tree_sizer.AddGrowableCol(0) folder_tree_sizer.AddGrowableRow(0) - self.AddSizer(folder_tree_sizer, border=10, + self.Add(folder_tree_sizer, border=10, flag=wx.GROW | wx.LEFT | wx.RIGHT) self.ESIFiles = FolderTree(parent, self.GetPath(), editable=False) self.ESIFiles.SetFilter(".xml") - folder_tree_sizer.AddWindow(self.ESIFiles, flag=wx.GROW) + folder_tree_sizer.Add(self.ESIFiles, flag=wx.GROW) buttons_sizer = wx.BoxSizer(wx.VERTICAL) - folder_tree_sizer.AddSizer(buttons_sizer, + folder_tree_sizer.Add(buttons_sizer, flag=wx.ALIGN_CENTER_VERTICAL) for idx, (name, bitmap, help, callback) in enumerate(buttons): @@ -1135,7 +1139,7 @@ bitmap=GetBitmap(bitmap), size=wx.Size(28, 28), style=wx.NO_BORDER) - button.SetToolTipString(help) + button.SetToolTip(help) setattr(self, name, button) if idx > 0: flag = wx.TOP @@ -1145,14 +1149,14 @@ callback = getattr(self, "On" + name, None) if callback is not None: parent.Bind(wx.EVT_BUTTON, callback, button) - buttons_sizer.AddWindow(button, border=10, flag=flag) + buttons_sizer.Add(button, border=10, flag=flag) modules_label = wx.StaticText(parent, label=_("Modules library:")) - self.AddSizer(modules_label, border=10, + self.Add(modules_label, border=10, flag=wx.LEFT | wx.RIGHT) - self.ModulesGrid = wx.gizmos.TreeListCtrl(parent, + self.ModulesGrid = wx.adv.TreeListCtrl(parent, style=wx.TR_DEFAULT_STYLE | wx.TR_ROW_LINES | wx.TR_COLUMN_LINES | @@ -1166,7 +1170,7 @@ self.OnModulesGridEndLabelEdit) self.ModulesGrid.GetHeaderWindow().Bind(wx.EVT_MOTION, self.OnModulesGridHeaderMotion) - self.AddWindow(self.ModulesGrid, border=10, + self.Add(self.ModulesGrid, border=10, flag=wx.GROW | wx.BOTTOM | wx.LEFT | wx.RIGHT) for colname, colsize, colalign in zip( @@ -1240,7 +1244,7 @@ _("Choose an XML file"), os.getcwd(), "", _("XML files (*.xml)|*.xml|All files|*.*"), - wx.OPEN) + wx.FD_OPEN) if dialog.ShowModal() == wx.ID_OK: filepath = dialog.GetPath() @@ -1335,7 +1339,7 @@ if col > 0 and self.LastToolTipCol != col: self.LastToolTipCol = col _param, param_infos = self.ModuleLibrary.MODULES_EXTRA_PARAMS[col - 1] - wx.CallAfter(self.ModulesGrid.GetHeaderWindow().SetToolTipString, + wx.CallAfter(self.ModulesGrid.GetHeaderWindow().SetToolTip, param_infos["description"]) event.Skip() @@ -1359,13 +1363,14 @@ ("DeleteButton", "remove_element", _("Remove file from database"), None) ]) self.DatabaseSizer.SetControlMinSize(wx.Size(0, 0)) - main_sizer.AddSizer(self.DatabaseSizer, border=10, + main_sizer.Add(self.DatabaseSizer, border=10, flag=wx.GROW | wx.TOP | wx.LEFT | wx.RIGHT) button_sizer = self.CreateButtonSizer(wx.OK | wx.CANCEL | wx.CENTRE) - button_sizer.GetAffirmativeButton().SetLabel(_("Add file to project")) - button_sizer.GetCancelButton().SetLabel(_("Close")) - main_sizer.AddSizer(button_sizer, border=10, + # FIXME: find a way to change buttons label compatible with wxPython 4.x + # button_sizer.GetAffirmativeButton().SetLabel(_("Add file to project")) + # button_sizer.GetCancelButton().SetLabel(_("Close")) + main_sizer.Add(button_sizer, border=10, flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) self.SetSizer(main_sizer) diff -r 7e17f7e02a2b -r 0b3ac94f494c etherlab/EtherCATManagementEditor.py --- a/etherlab/EtherCATManagementEditor.py Wed Nov 29 11:54:56 2023 +0100 +++ b/etherlab/EtherCATManagementEditor.py Thu Dec 07 22:41:32 2023 +0100 @@ -7,15 +7,15 @@ # # See COPYING file for copyrights details. -from __future__ import absolute_import -from __future__ import division + + import os import string from xml.dom import minidom import wx import wx.grid -import wx.gizmos +import wx.adv import wx.lib.buttons # -------------------------------------------------------------------- @@ -135,7 +135,7 @@ self.SizerDic["SlaveInfosDetailsInnerSizer"].AddMany([self.StaticTextDic[statictext_name], self.TextCtrlDic[textctrl_name]]) - self.SizerDic["SlaveInfosDetailsBox"].AddSizer(self.SizerDic["SlaveInfosDetailsInnerSizer"]) + self.SizerDic["SlaveInfosDetailsBox"].Add(self.SizerDic["SlaveInfosDetailsInnerSizer"]) self.SyncManagersGrid = CustomGrid(self, size=wx.Size(605, 155), style=wx.VSCROLL) @@ -153,7 +153,7 @@ for button_name, button_id, button_label, button_tooltipstring, event_method, sub_item in buttons: self.ButtonDic[button_name] = wx.Button(self, id=button_id, label=_(button_label)) self.ButtonDic[button_name].Bind(wx.EVT_BUTTON, event_method) - self.ButtonDic[button_name].SetToolTipString(button_tooltipstring) + self.ButtonDic[button_name].SetToolTip(button_tooltipstring) self.SizerDic["SlaveState_up_sizer"].Add(self.ButtonDic[button_name]) for statictext_name, statictext_label, textctrl_name in sub_item: self.StaticTextDic[statictext_name] = wx.StaticText(self, label=_(statictext_label)) @@ -166,7 +166,7 @@ ("StopTimerButton", "Stop State Monitoring", "Slave State Update Stop", self.CurrentStateThreadStop)]: self.ButtonDic[button_name] = wx.Button(self, label=_(button_label)) self.ButtonDic[button_name].Bind(wx.EVT_BUTTON, event_method) - self.ButtonDic[button_name].SetToolTipString(button_tooltipstring) + self.ButtonDic[button_name].SetToolTip(button_tooltipstring) self.SizerDic["SlaveState_down_sizer"].Add(self.ButtonDic[button_name]) self.SizerDic["SlaveState_sizer"].AddMany([self.SizerDic["SlaveState_up_sizer"], @@ -331,7 +331,7 @@ wx.Panel.__init__(self, parent, -1) self.DatatypeDescription, self.CommunicationObject, self.ManufacturerSpecific, \ - self.ProfileSpecific, self.Reserved, self.AllSDOData = range(6) + self.ProfileSpecific, self.Reserved, self.AllSDOData = list(range(6)) self.Controler = controler @@ -418,7 +418,7 @@ If the off is selected, the monitor thread gets off. @param event: wx.EVT_RADIOBOX object """ - on, off = range(2) + on, off = list(range(2)) if event.GetInt() == on: CheckThreadFlag = self.SDOMonitoringThreadOn() @@ -455,7 +455,7 @@ def SDOMonitorThreadProc(self): while self.SDOMonitoringFlag and self.Controler.GetCTRoot()._connector.PLCStatus != "Started": self.SDOValuesList = self.Controler.GetCTRoot()._connector.GetSDOData() - LocalData = self.SDOValuesList[0].items() + LocalData = list(self.SDOValuesList[0].items()) LocalData.sort() if self.SDOValuesList[1] != self.Controler.GetSlavePos(): continue @@ -508,7 +508,7 @@ Parse SDO data set that obtain "ESI file" @param entries: SDO entry list """ - entries_list = entries.items() + entries_list = list(entries.items()) entries_list.sort() self.ForDefaultValueFlag = False self.CompareValue = "" @@ -518,7 +518,7 @@ # exclude entry that isn't in the objects check_mapping = entry["PDOMapping"] if check_mapping is "T" or check_mapping is "R": - if "PDO index" not in entry.keys(): + if "PDO index" not in list(entry.keys()): continue idx = "0" + entry["Index"].strip("#") @@ -793,7 +793,7 @@ SDOPanel.SDOMonitorGrid.AppendRows(len(SDOPanel.SDOMonitorEntries)) SDOPanel.SetSDOTraceValues(SDOPanel.SDOMonitorEntries) - SME_list = SDOPanel.SDOMonitorEntries.items() + SME_list = list(SDOPanel.SDOMonitorEntries.items()) SME_list.sort() gridRow = 0 @@ -1497,7 +1497,7 @@ if check_connect_flag: status, _log_count = self.Controler.GetCTRoot()._connector.GetPLCstatus() if status is not PlcStatus.Started: - dialog = wx.FileDialog(self, _("Choose a binary file"), os.getcwd(), "", _("bin files (*.bin)|*.bin"), wx.OPEN) + dialog = wx.FileDialog(self, _("Choose a binary file"), os.getcwd(), "", _("bin files (*.bin)|*.bin"), wx.FD_OPEN) if dialog.ShowModal() == wx.ID_OK: filepath = dialog.GetPath() @@ -1547,7 +1547,7 @@ # Config Data: EEPROM Size, PDI Type, Device Emulation # Find PDI Type in pdiType dictionary cnt_pdi_type = self.Controler.CommonMethod.SmartViewInfosFromXML["pdi_type"] - for i in self.PDIType.keys(): + for i in list(self.PDIType.keys()): if cnt_pdi_type == i: cnt_pdi_type = self.PDIType[i][0] break @@ -1627,7 +1627,7 @@ eeprom_size = str((int(self.GetWordAddressData(sii_dict.get('Size'), 10))+1)//8*1024) # Find PDI Type in pdiType dictionary cnt_pdi_type = int(self.GetWordAddressData(sii_dict.get('PDIControl'), 16).split('x')[1][2:4], 16) - for i in self.PDIType.keys(): + for i in list(self.PDIType.keys()): if cnt_pdi_type == i: cnt_pdi_type = self.PDIType[i][0] break @@ -1729,7 +1729,7 @@ wx.Panel.__init__(self, parent, -1, size=(350, 500)) - self.Tree = wx.gizmos.TreeListCtrl(self, -1, size=(350, 500), + self.Tree = wx.adv.TreeListCtrl(self, -1, size=(350, 500), style=(wx.TR_DEFAULT_STYLE | wx.TR_FULL_ROW_HIGHLIGHT | wx.TR_HIDE_ROOT | @@ -1896,7 +1896,7 @@ @param event : wx.EVT_BUTTON object """ dialog = wx.FileDialog(self, _("Choose a binary file"), os.getcwd(), "", - _("bin files (*.bin)|*.bin"), wx.OPEN) + _("bin files (*.bin)|*.bin"), wx.FD_OPEN) if dialog.ShowModal() == wx.ID_OK: filepath = dialog.GetPath() @@ -2106,7 +2106,7 @@ ("pdi", "type", self.PDIType), ("fmmu", "number", self.FMMUNumber), ("sm", "number", self.SMNumber)]: - if property in register.attributes.keys(): + if property in list(register.attributes.keys()): if type == "type": if register.attributes[property].value == value: self.GetRegisterInfo(reg_info_tree, register) @@ -2153,7 +2153,7 @@ ("pdi", "type", self.PDIType), ("fmmu", "number", self.FMMUNumber), ("sm", "number", self.SMNumber)]: - if property in detail.attributes.keys(): + if property in list(detail.attributes.keys()): if type == "type": if detail.attributes[property].value == value: self.GetRegisterDetailInfo(reg_info_tree, reg_index, detail) @@ -2522,7 +2522,7 @@ reg_sub_grid_data = [] - BIT_RANGE, NAME, DESCRIPTIONS = range(3) + BIT_RANGE, NAME, DESCRIPTIONS = list(range(3)) # Check if this register's detail description is exist or not, # and create data structure for the detail description table ; sub grid @@ -2692,7 +2692,7 @@ self.TextCtrl[key] = wx.TextCtrl(self, size=wx.Size(130, 24), style=wx.TE_READONLY) self.MasterStateSizer['innerMasterState'].AddMany([self.StaticText[key], self.TextCtrl[key]]) - self.MasterStateSizer['masterState'].AddSizer(self.MasterStateSizer['innerMasterState']) + self.MasterStateSizer['masterState'].Add(self.MasterStateSizer['innerMasterState']) # ----------------------- Ethernet Network Card Information --------------------------------------- for key, label in [ @@ -2705,7 +2705,7 @@ self.TextCtrl[key] = wx.TextCtrl(self, size=wx.Size(130, 24), style=wx.TE_READONLY) self.MasterStateSizer['innerDeviceInfo'].AddMany([self.StaticText[key], self.TextCtrl[key]]) - self.MasterStateSizer['deviceInfo'].AddSizer(self.MasterStateSizer['innerDeviceInfo']) + self.MasterStateSizer['deviceInfo'].Add(self.MasterStateSizer['innerDeviceInfo']) # ----------------------- Network Frame Information ----------------------------------------------- for key, label in [ @@ -2722,13 +2722,13 @@ self.TextCtrl[key][index] = wx.TextCtrl(self, size=wx.Size(130, 24), style=wx.TE_READONLY) self.MasterStateSizer['innerFrameInfo'].Add(self.TextCtrl[key][index]) - self.MasterStateSizer['frameInfo'].AddSizer(self.MasterStateSizer['innerFrameInfo']) + self.MasterStateSizer['frameInfo'].Add(self.MasterStateSizer['innerFrameInfo']) # ------------------------------- Slave Information ----------------------------------------------- self.SITreeListCtrl = SITreeListCtrl(self, self.Controler) self.MasterStateSizer["innerSlaveInfo"].AddMany([self.SIUpdateButton, self.SITreeListCtrl]) - self.MasterStateSizer["slaveInfo"].AddSizer( + self.MasterStateSizer["slaveInfo"].Add( self.MasterStateSizer["innerSlaveInfo"]) # --------------------------------- Main Sizer ---------------------------------------------------- @@ -2743,7 +2743,7 @@ ("main", [ "innerTop", "innerMiddle", "innerBottom"])]: for key2 in sub: - self.MasterStateSizer[key].AddSizer(self.MasterStateSizer[key2]) + self.MasterStateSizer[key].Add(self.MasterStateSizer[key2]) self.SetSizer(self.MasterStateSizer["main"]) @@ -2798,7 +2798,7 @@ self.Controler=controler - self.Tree = wx.gizmos.TreeListCtrl(self, -1, size=wx.Size(750,350), + self.Tree = wx.adv.TreeListCtrl(self, -1, size=wx.Size(750,350), style=wx.TR_HAS_BUTTONS |wx.TR_HIDE_ROOT |wx.TR_ROW_LINES @@ -2817,7 +2817,7 @@ """ Update the data of the slave information. """ - position, not_used, state, not_used, name = range(5) + position, not_used, state, not_used, name = list(range(5)) slave_node = [] slave_info_list = [] @@ -2933,7 +2933,7 @@ ec_idx += 1 # set texts in "error" column. - ec_info_list = error_counter.items() + ec_info_list = list(error_counter.items()) ec_info_list.sort() err_checker = "none" diff -r 7e17f7e02a2b -r 0b3ac94f494c etherlab/EthercatCIA402Slave.py --- a/etherlab/EthercatCIA402Slave.py Wed Nov 29 11:54:56 2023 +0100 +++ b/etherlab/EthercatCIA402Slave.py Thu Dec 07 22:41:32 2023 +0100 @@ -9,7 +9,7 @@ # # See COPYING file for copyrights details. -from __future__ import absolute_import + import os import wx @@ -424,7 +424,7 @@ if variable in check_variable: continue - var_infos = dict(zip(["name", "index", "subindex", "var_type", "dir"], variable)) + var_infos = dict(list(zip(["name", "index", "subindex", "var_type", "dir"], variable))) var_infos["location"] = location_str var_infos["var_size"] = self.GetSizeOfType(var_infos["var_type"]) var_infos["var_name"] = "__%(dir)s%(var_size)s%(location)s_%(index)d_%(subindex)d" % var_infos diff -r 7e17f7e02a2b -r 0b3ac94f494c etherlab/EthercatMaster.py --- a/etherlab/EthercatMaster.py Wed Nov 29 11:54:56 2023 +0100 +++ b/etherlab/EthercatMaster.py Thu Dec 07 22:41:32 2023 +0100 @@ -9,7 +9,7 @@ # # See COPYING file for copyrights details. -from __future__ import absolute_import + import os from copy import deepcopy from functools import reduce @@ -298,7 +298,7 @@ EtherCATConfigParser.LoadXMLString(config_xmlfile.read()) if error is None: config_is_saved = True - except Exception, e: + except Exception as e: error = e.message config_xmlfile.close() @@ -651,7 +651,7 @@ # Test OD entries = device.GetEntriesList(limits) #entries = self.CTNParent.GetEntriesList() - entries_list = entries.items() + entries_list = list(entries.items()) entries_list.sort() entries = [] current_index = None @@ -817,7 +817,7 @@ else: sync_managers.append(LOCATION_VAR_INPUT) - entries = device.GetEntriesList().items() + entries = list(device.GetEntriesList().items()) entries.sort() for (index, subindex), entry in entries: var_size = self.GetSizeOfType(entry["Type"]) diff -r 7e17f7e02a2b -r 0b3ac94f494c etherlab/EthercatSlave.py --- a/etherlab/EthercatSlave.py Wed Nov 29 11:54:56 2023 +0100 +++ b/etherlab/EthercatSlave.py Thu Dec 07 22:41:32 2023 +0100 @@ -9,7 +9,7 @@ # # See COPYING file for copyrights details. -from __future__ import absolute_import + from PLCControler import LOCATION_CONFNODE, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY from ConfigTreeNode import ConfigTreeNode diff -r 7e17f7e02a2b -r 0b3ac94f494c etherlab/__init__.py --- a/etherlab/__init__.py Wed Nov 29 11:54:56 2023 +0100 +++ b/etherlab/__init__.py Thu Dec 07 22:41:32 2023 +0100 @@ -1,4 +1,4 @@ -from __future__ import absolute_import + from etherlab.etherlab import * from util.BitmapLibrary import AddBitmapFolder diff -r 7e17f7e02a2b -r 0b3ac94f494c etherlab/etherlab.py --- a/etherlab/etherlab.py Wed Nov 29 11:54:56 2023 +0100 +++ b/etherlab/etherlab.py Thu Dec 07 22:41:32 2023 +0100 @@ -13,7 +13,6 @@ import os import shutil import csv -from builtins import str as text from lxml import etree import wx @@ -425,7 +424,7 @@ # self.GetCTRoot().logger.write_warning( # XSDSchemaErrorMessage % (filepath + error)) except Exception as exc: - self.modules_infos, error = None, text(exc) + self.modules_infos, error = None, str(exc) xmlfile.close() if self.modules_infos is not None: diff -r 7e17f7e02a2b -r 0b3ac94f494c etherlab/runtime_etherlab.py --- a/etherlab/runtime_etherlab.py Wed Nov 29 11:54:56 2023 +0100 +++ b/etherlab/runtime_etherlab.py Thu Dec 07 22:41:32 2023 +0100 @@ -9,7 +9,7 @@ # # see copying file for copyrights details. -from __future__ import absolute_import + import os import signal import subprocess diff -r 7e17f7e02a2b -r 0b3ac94f494c exemples/modbus/beremiz.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/exemples/modbus/beremiz.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,6 @@ + + + + + + diff -r 7e17f7e02a2b -r 0b3ac94f494c exemples/modbus/modbus_0@modbus/ModbusTCPclient_0@ModbusTCPclient/ModbusRequest_0@ModbusRequest/baseconfnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/exemples/modbus/modbus_0@modbus/ModbusTCPclient_0@ModbusTCPclient/ModbusRequest_0@ModbusRequest/baseconfnode.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,2 @@ + + diff -r 7e17f7e02a2b -r 0b3ac94f494c exemples/modbus/modbus_0@modbus/ModbusTCPclient_0@ModbusTCPclient/ModbusRequest_0@ModbusRequest/confnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/exemples/modbus/modbus_0@modbus/ModbusTCPclient_0@ModbusTCPclient/ModbusRequest_0@ModbusRequest/confnode.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,2 @@ + + diff -r 7e17f7e02a2b -r 0b3ac94f494c exemples/modbus/modbus_0@modbus/ModbusTCPclient_0@ModbusTCPclient/ModbusRequest_1@ModbusRequest/baseconfnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/exemples/modbus/modbus_0@modbus/ModbusTCPclient_0@ModbusTCPclient/ModbusRequest_1@ModbusRequest/baseconfnode.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,2 @@ + + diff -r 7e17f7e02a2b -r 0b3ac94f494c exemples/modbus/modbus_0@modbus/ModbusTCPclient_0@ModbusTCPclient/ModbusRequest_1@ModbusRequest/confnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/exemples/modbus/modbus_0@modbus/ModbusTCPclient_0@ModbusTCPclient/ModbusRequest_1@ModbusRequest/confnode.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,2 @@ + + diff -r 7e17f7e02a2b -r 0b3ac94f494c exemples/modbus/modbus_0@modbus/ModbusTCPclient_0@ModbusTCPclient/baseconfnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/exemples/modbus/modbus_0@modbus/ModbusTCPclient_0@ModbusTCPclient/baseconfnode.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,2 @@ + + diff -r 7e17f7e02a2b -r 0b3ac94f494c exemples/modbus/modbus_0@modbus/ModbusTCPclient_0@ModbusTCPclient/confnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/exemples/modbus/modbus_0@modbus/ModbusTCPclient_0@ModbusTCPclient/confnode.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,2 @@ + + diff -r 7e17f7e02a2b -r 0b3ac94f494c exemples/modbus/modbus_0@modbus/ModbusTCPserver_0@ModbusTCPserver/HoldingRegs@MemoryArea/baseconfnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/exemples/modbus/modbus_0@modbus/ModbusTCPserver_0@ModbusTCPserver/HoldingRegs@MemoryArea/baseconfnode.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,2 @@ + + diff -r 7e17f7e02a2b -r 0b3ac94f494c exemples/modbus/modbus_0@modbus/ModbusTCPserver_0@ModbusTCPserver/HoldingRegs@MemoryArea/confnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/exemples/modbus/modbus_0@modbus/ModbusTCPserver_0@ModbusTCPserver/HoldingRegs@MemoryArea/confnode.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,2 @@ + + diff -r 7e17f7e02a2b -r 0b3ac94f494c exemples/modbus/modbus_0@modbus/ModbusTCPserver_0@ModbusTCPserver/InputRegs@MemoryArea/baseconfnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/exemples/modbus/modbus_0@modbus/ModbusTCPserver_0@ModbusTCPserver/InputRegs@MemoryArea/baseconfnode.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,2 @@ + + diff -r 7e17f7e02a2b -r 0b3ac94f494c exemples/modbus/modbus_0@modbus/ModbusTCPserver_0@ModbusTCPserver/InputRegs@MemoryArea/confnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/exemples/modbus/modbus_0@modbus/ModbusTCPserver_0@ModbusTCPserver/InputRegs@MemoryArea/confnode.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,2 @@ + + diff -r 7e17f7e02a2b -r 0b3ac94f494c exemples/modbus/modbus_0@modbus/ModbusTCPserver_0@ModbusTCPserver/baseconfnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/exemples/modbus/modbus_0@modbus/ModbusTCPserver_0@ModbusTCPserver/baseconfnode.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,2 @@ + + diff -r 7e17f7e02a2b -r 0b3ac94f494c exemples/modbus/modbus_0@modbus/ModbusTCPserver_0@ModbusTCPserver/confnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/exemples/modbus/modbus_0@modbus/ModbusTCPserver_0@ModbusTCPserver/confnode.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,2 @@ + + diff -r 7e17f7e02a2b -r 0b3ac94f494c exemples/modbus/modbus_0@modbus/baseconfnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/exemples/modbus/modbus_0@modbus/baseconfnode.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,2 @@ + + diff -r 7e17f7e02a2b -r 0b3ac94f494c exemples/modbus/modbus_0@modbus/confnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/exemples/modbus/modbus_0@modbus/confnode.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,2 @@ + + diff -r 7e17f7e02a2b -r 0b3ac94f494c exemples/modbus/plc.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/exemples/modbus/plc.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,314 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "beremiz" + "Modbus" + +If Modbus library is installed elsewhere, then place corresponding paths +in CFLAGS/LDFLAGS in project settings. + +For GNU/Linux to install Modbus library in parent directory run following commands: +$ hg clone https://bitbucket.org/mjsousa/modbus Modbus +$ cd Modbus +$ make + +After that Modbus extension is ready to be used in Beremiz projects.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + T#1s + + + + + + + 32767 + + + + + + + + + + + + + + Counter + + + + + + + + + + + MasterWriteToReg0 + + + + + + + MasterReadFromReg1 + + + + + + + + + + + CounterReadBack + + + + + + + + + + + + + SlaveHoldReg0 + + + + + + + + + + + SlaveInputReg0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 7e17f7e02a2b -r 0b3ac94f494c exemples/python/plc.xml --- a/exemples/python/plc.xml Wed Nov 29 11:54:56 2023 +0100 +++ b/exemples/python/plc.xml Thu Dec 07 22:41:32 2023 +0100 @@ -1,1644 +1,1678 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - '666' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - pytest_var2 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 'sys.stdout.write("FBID :"+str(FBID)+"\n")' - - - - - - - 'PLCBinary.Simple_C_Call(5678)' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 'MyPythonFunc(42)' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - pytest_var1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - mux1_sel - - - - - - - - - - - pytest_var3 - - - - - - - - - - - FromC - - - - - - - 23 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - TestInput - - - - - - - - - - - TestOutput - - - - - - - - - - - FromInput - - - - - - - 10 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Test_BCD_RESULT - - - - - - - Test_BCD - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Test_DT - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Test_Date - - - - - - - - - - - Test_String - - - - - - - - - - - Test_Bool - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 'True' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Global_RS.Q1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - BOOL#TRUE - - - - - - - - - - - Global_RS.S - - - - - - - - - - - Global_RS.R1 - - - - - - - Global_RS.Q1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - BOOL#FALSE - - - - - - - - - - - Test_TOD_STRING - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Test_TOD - - - - - - - 42 - - - - - - - - - - - TOTO - - - - - - - - - - - - - TUTU - - - - - - - Second_Python_Var - - - - - - - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - fefvsd - - - - - - - - - - - fefvsd - - - - - - - - - - - - - mux2_sel - - - - - - - - - - - - - - - - - - - - - - - - - Test_BCD_WRONG - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Test_BCD_WRONG_RESULT - - - - - - - - - - - - - - - - - Test_BCD_CONVERTED - - - - - - - - - - - - - - - - - - - Grumpf - - - - - - - BOOL#TRUE - - - - - - - Test_DT - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Test_Python_Var - - - - - - - 23 - - - - - - - - - - - - - - SomeVarName - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 'MyPrintFunction("Hello world\n")' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pytest_var2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 'MyPrintFunction("FBID :"+str(FBID)+"\n")' + + + + + + + 'PLCBinary.Simple_C_Call(5678)' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 'MyPythonFunc(42)' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pytest_var1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + mux1_sel + + + + + + + + + + + pytest_var3 + + + + + + + + + + + FromC + + + + + + + 23 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TestInput + + + + + + + + + + + TestOutput + + + + + + + + + + + FromInput + + + + + + + 10 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Test_BCD_RESULT + + + + + + + Test_BCD + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Test_DT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Test_Date + + + + + + + + + + + Test_String + + + + + + + + + + + Test_Bool + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 'True' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Global_RS.Q1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BOOL#TRUE + + + + + + + + + + + Global_RS.S + + + + + + + + + + + Global_RS.R1 + + + + + + + Global_RS.Q1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BOOL#FALSE + + + + + + + + + + + Test_TOD_STRING + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Test_TOD + + + + + + + 42 + + + + + + + + + + + TOTO + + + + + + + + + + + + + TUTU + + + + + + + Second_Python_Var + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fefvsd + + + + + + + + + + + fefvsd + + + + + + + + + + + + + mux2_sel + + + + + + + + + + + + + + + + + + + + + + + + + Test_BCD_WRONG + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Test_BCD_WRONG_RESULT + + + + + + + + + + + + + + + + + Test_BCD_CONVERTED + + + + + + + + + + + + + + + + + + + Grumpf + + + + + + + BOOL#TRUE + + + + + + + Test_DT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Test_Python_Var + + + + + + + 23 + + + + + + + + + + + + + + SomeVarName + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 7e17f7e02a2b -r 0b3ac94f494c exemples/python/py_ext_0@py_ext/pyfile.xml --- a/exemples/python/py_ext_0@py_ext/pyfile.xml Wed Nov 29 11:54:56 2023 +0100 +++ b/exemples/python/py_ext_0@py_ext/pyfile.xml Thu Dec 07 22:41:32 2023 +0100 @@ -6,19 +6,19 @@ diff -r 7e17f7e02a2b -r 0b3ac94f494c exemples/python/python@py_ext/pyfile.xml --- a/exemples/python/python@py_ext/pyfile.xml Wed Nov 29 11:54:56 2023 +0100 +++ b/exemples/python/python@py_ext/pyfile.xml Thu Dec 07 22:41:32 2023 +0100 @@ -1,69 +1,79 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff -r 7e17f7e02a2b -r 0b3ac94f494c exemples/svghmi_traffic_light/svghmi_0@svghmi/svghmi.svg --- a/exemples/svghmi_traffic_light/svghmi_0@svghmi/svghmi.svg Wed Nov 29 11:54:56 2023 +0100 +++ b/exemples/svghmi_traffic_light/svghmi_0@svghmi/svghmi.svg Thu Dec 07 22:41:32 2023 +0100 @@ -1251,14 +1251,14 @@ inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="1.979899" - inkscape:cx="205.65994" - inkscape:cy="103.00174" + inkscape:cx="52.116754" + inkscape:cy="96.940825" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="false" units="px" - inkscape:window-width="1600" - inkscape:window-height="836" + inkscape:window-width="3840" + inkscape:window-height="2096" inkscape:window-x="0" inkscape:window-y="27" inkscape:window-maximized="1" @@ -1274,7 +1274,7 @@ image/svg+xml - + @@ -1283,6 +1283,14 @@ inkscape:groupmode="layer" id="layer1" transform="translate(37.474617,-760.93329)"> + ON - diff -r 7e17f7e02a2b -r 0b3ac94f494c fake_wx.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fake_wx.py Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,122 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import sys +from types import ModuleType + +# TODO use gettext instead +def get_translation(txt): + return txt + + +class FakeObject: + def __init__(self, *args, **kwargs): + if "__classname__" in kwargs: + self.__classname__ = kwargs["__classname__"] + + def __getattr__(self,name): + if name.startswith('__'): + raise AttributeError(name) + return FakeObject(__classname__=self.__classname__+"."+name) + + def __call__(self, *args, **kwargs): + return FakeObject(__classname__=self.__classname__+"()") + + def __getitem__(self, key): + return FakeObject(__classname__=self.__classname__+"["+repr(key)+"]") + + def __str__(self): + return self.__classname__ + + def __or__(self, other): + return FakeObject(__classname__=self.__classname__+"|"+other.__classname__) + + def __hash__(self) -> int: + return id(self) + + def __cmp__(self,other): + return True + __lt__=__cmp__ + __le__=__cmp__ + __eq__=__cmp__ + __ne__=__cmp__ + __gt__=__cmp__ + __ge__=__cmp__ + +class FakeClass: + def __init__(self, *args, **kwargs): + print(("DUMMY Class __init__ !",self.__name__,args,kwargs)) + + +class FakeModule(ModuleType): + def __init__(self, name, classes): + self.__modname__ = name + self.__objects__ = dict([(desc, type(desc, (FakeClass,), {})) + if type(desc)==str else desc for desc in classes]) + ModuleType(name) + + def __getattr__(self,name): + if name.startswith('__'): + raise AttributeError(name) + + if name in self.__objects__: + return self.__objects__[name] + + obj = FakeObject(__classname__=self.__modname__+"."+name) + self.__objects__[name] = obj + return obj + + +# Keep track of already faked modules to catch those +# that are already present in sys.modules from start +# (i.e. mpl_toolkits for exemple) +already_patched = {} + +for name, classes in [ + # list given for each module name contains name string for a FakeClass, + # otherwise a tuple (name, object) for arbitrary object/function/class + ('wx',[ + 'Panel', 'PyCommandEvent', 'Dialog', 'PopupWindow', 'TextEntryDialog', + 'Notebook', 'ListCtrl', 'TextDropTarget', 'PyControl', 'TextCtrl', + 'SplitterWindow', 'Frame', 'Printout', 'StaticBitmap', 'DropTarget', + ('GetTranslation', get_translation)]), + ('wx.lib.agw.advancedsplash',[]), + ('wx.dataview',['DataViewIndexListModel', 'PyDataViewIndexListModel']), + ('wx.lib.buttons',['GenBitmapTextButton']), + ('wx.adv',['EditableListBox']), + ('wx.grid',[ + 'Grid', 'PyGridTableBase', 'GridCellEditor', 'GridCellTextEditor', + 'GridCellChoiceEditor']), + ('wx.lib.agw.customtreectrl',['CustomTreeCtrl']), + ('wx.lib.gizmos',[]), + ('wx.lib.intctrl',['IntCtrl']), + ('matplotlib.pyplot',[]), + ('matplotlib.backends.backend_wxagg',['FigureCanvasWxAgg']), + ('wx.stc',['StyledTextCtrl']), + ('wx.lib.scrolledpanel',[]), + ('wx.lib.mixins.listctrl',['ColumnSorterMixin', 'ListCtrlAutoWidthMixin']), + ('wx.dataview',['PyDataViewIndexListModel']), + ('matplotlib.backends.backend_agg',[]), + ('wx.aui',[]), + ('wx.html',['HtmlWindow']), + ('mpl_toolkits.mplot3d',[])]: + modpath = None + parentmod = None + for identifier in name.split("."): + modpath = (modpath + "." + identifier) if modpath else identifier + mod = sys.modules.get(modpath, None) + + if mod is None or modpath not in already_patched: + mod = FakeModule(modpath, classes) + sys.modules[modpath] = mod + already_patched[modpath] = True + + if parentmod is not None: + parentmod.__objects__[identifier] = mod + + parentmod = mod + +import builtins + +builtins.__dict__['_'] = get_translation + diff -r 7e17f7e02a2b -r 0b3ac94f494c features.py --- a/features.py Wed Nov 29 11:54:56 2023 +0100 +++ b/features.py Thu Dec 07 22:41:32 2023 +0100 @@ -11,14 +11,14 @@ libraries = [ ('Native', 'NativeLib.NativeLibrary', True), ('Python', 'py_ext.PythonLibrary', True), - ('Etherlab', 'etherlab.EthercatMaster.EtherlabLibrary', False), - ('SVGHMI', 'svghmi.SVGHMILibrary', False)] + # FIXME ('Etherlab', 'etherlab.EthercatMaster.EtherlabLibrary', False), + ('SVGHMI', 'svghmi.SVGHMILibrary', 'svghmi')] catalog = [ ('opcua', _('OPC-UA client'), _('Map OPC-UA server as located variables'), 'opc_ua.OPCUAClient'), - ('canfestival', _('CANopen support'), _('Map located variables over CANopen'), 'canfestival.canfestival.RootClass'), + # FIXME ('canfestival', _('CANopen support'), _('Map located variables over CANopen'), 'canfestival.canfestival.RootClass'), ('bacnet', _('Bacnet support'), _('Map located variables over Bacnet'), 'bacnet.bacnet.RootClass'), - ('etherlab', _('EtherCAT master'), _('Map located variables over EtherCAT'), 'etherlab.etherlab.RootClass'), + # FIXME ('etherlab', _('EtherCAT master'), _('Map located variables over EtherCAT'), 'etherlab.etherlab.RootClass'), ('modbus', _('Modbus support'), _('Map located variables over Modbus'), 'modbus.modbus.RootClass'), ('c_ext', _('C extension'), _('Add C code accessing located variables synchronously'), 'c_ext.CFile'), ('py_ext', _('Python file'), _('Add Python code executed asynchronously'), 'py_ext.PythonFile'), diff -r 7e17f7e02a2b -r 0b3ac94f494c graphics/DebugDataConsumer.py --- a/graphics/DebugDataConsumer.py Wed Nov 29 11:54:56 2023 +0100 +++ b/graphics/DebugDataConsumer.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,8 +23,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import division + + import datetime diff -r 7e17f7e02a2b -r 0b3ac94f494c graphics/FBD_Objects.py --- a/graphics/FBD_Objects.py Wed Nov 29 11:54:56 2023 +0100 +++ b/graphics/FBD_Objects.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,10 +23,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import division import wx -from six.moves import xrange from graphics.GraphicCommons import * from plcopen.structures import * @@ -78,7 +75,7 @@ return block def GetConnectorTranslation(self, element): - return dict(zip(self.Inputs + self.Outputs, element.Inputs + element.Outputs)) + return dict(list(zip(self.Inputs + self.Outputs, element.Inputs + element.Outputs))) def Flush(self): for input in self.Inputs: @@ -122,10 +119,10 @@ # Returns if the point given is in the bounding box def HitTest(self, pt, connectors=True): if self.Name != "": - test_text = self.GetTextBoundingBox().InsideXY(pt.x, pt.y) + test_text = self.GetTextBoundingBox().Contains(pt.x, pt.y) else: test_text = False - test_block = self.GetBlockBoundingBox(connectors).InsideXY(pt.x, pt.y) + test_block = self.GetBlockBoundingBox(connectors).Contains(pt.x, pt.y) return test_text or test_block # Returns the bounding box of the name outside the block @@ -165,7 +162,7 @@ linesize = max((self.Size[1] - BLOCK_LINE_SIZE) // lines, BLOCK_LINE_SIZE) # Update inputs and outputs positions position = BLOCK_LINE_SIZE + linesize // 2 - for i in xrange(lines): + for i in range(lines): if scaling is not None: ypos = round_scaling(self.Pos.y + position, scaling[1]) - self.Pos.y else: @@ -256,7 +253,7 @@ outputs = [output for output in blocktype["outputs"]] if blocktype["extensible"]: start = int(inputs[-1][0].replace("IN", "")) - for dummy in xrange(self.Extension - len(blocktype["inputs"])): + for dummy in range(self.Extension - len(blocktype["inputs"])): start += 1 inputs.append(("IN%d" % start, inputs[-1][1], inputs[-1][2])) comment = blocktype["comment"] @@ -392,7 +389,7 @@ # pos = event.GetLogicalPosition(dc) # for input in self.Inputs: # rect = input.GetRedrawRect() -# if rect.InsideXY(pos.x, pos.y): +# if rect.Contains(pos.x, pos.y): # print "Find input" # tip = wx.TipWindow(self.Parent, "Test") # tip.SetBoundingRect(rect) @@ -451,7 +448,7 @@ if highlight_type is None: self.Highlights = {} else: - highlight_items = self.Highlights.items() + highlight_items = list(self.Highlights.items()) for name, highlights in highlight_items: highlights = ClearHighlights(highlights, highlight_type) if len(highlights) == 0: @@ -771,6 +768,7 @@ Graphic_Element.Draw(self, dc) dc.SetPen(MiterPen(wx.BLACK)) dc.SetBrush(wx.WHITE_BRUSH) + dc.SetTextForeground(wx.BLACK) if getattr(dc, "printing", False): name_size = dc.GetTextExtent(self.Name) @@ -1011,6 +1009,7 @@ Graphic_Element.Draw(self, dc) dc.SetPen(MiterPen(wx.BLACK)) dc.SetBrush(wx.WHITE_BRUSH) + dc.SetTextForeground(wx.BLACK) if getattr(dc, "printing", False): name_size = dc.GetTextExtent(self.Name) diff -r 7e17f7e02a2b -r 0b3ac94f494c graphics/GraphicCommons.py --- a/graphics/GraphicCommons.py Wed Nov 29 11:54:56 2023 +0100 +++ b/graphics/GraphicCommons.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,12 +23,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import division from math import * -from future.builtins import round -from six import string_types -from six.moves import xrange import wx from graphics.ToolTipProducer import ToolTipProducer @@ -68,15 +63,15 @@ SFC_ACTION_MIN_SIZE = (100, 30) # Minimum size of an action block line # Type definition constants for graphic elements -[INPUT, OUTPUT, INOUT] = range(3) -[CONNECTOR, CONTINUATION] = range(2) -[LEFTRAIL, RIGHTRAIL] = range(2) -[CONTACT_NORMAL, CONTACT_REVERSE, CONTACT_RISING, CONTACT_FALLING] = range(4) -[COIL_NORMAL, COIL_REVERSE, COIL_SET, COIL_RESET, COIL_RISING, COIL_FALLING] = range(6) -[SELECTION_DIVERGENCE, SELECTION_CONVERGENCE, SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE] = range(4) +[INPUT, OUTPUT, INOUT] = list(range(3)) +[CONNECTOR, CONTINUATION] = list(range(2)) +[LEFTRAIL, RIGHTRAIL] = list(range(2)) +[CONTACT_NORMAL, CONTACT_REVERSE, CONTACT_RISING, CONTACT_FALLING] = list(range(4)) +[COIL_NORMAL, COIL_REVERSE, COIL_SET, COIL_RESET, COIL_RISING, COIL_FALLING] = list(range(6)) +[SELECTION_DIVERGENCE, SELECTION_CONVERGENCE, SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE] = list(range(4)) # Constants for defining the type of dragging that has been selected -[HANDLE_MOVE, HANDLE_RESIZE, HANDLE_POINT, HANDLE_SEGMENT, HANDLE_CONNECTOR] = range(5) +[HANDLE_MOVE, HANDLE_RESIZE, HANDLE_POINT, HANDLE_SEGMENT, HANDLE_CONNECTOR] = list(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)] @@ -87,10 +82,10 @@ # 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) + MODE_TRANSITION, MODE_DIVERGENCE, MODE_JUMP, MODE_ACTION, MODE_MOTION] = list(range(15)) # Contants for defining alignment types for graphic group -[ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT, ALIGN_TOP, ALIGN_MIDDLE, ALIGN_BOTTOM] = range(6) +[ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT, ALIGN_TOP, ALIGN_MIDDLE, ALIGN_BOTTOM] = list(range(6)) # Contants for defining which drawing mode is selected for app [FREEDRAWING_MODE, DRIVENDRAWING_MODE] = [1, 2] @@ -99,8 +94,8 @@ HIGHLIGHTCOLOR = wx.CYAN # Define highlight types -ERROR_HIGHLIGHT = (wx.Colour(255, 255, 0), wx.RED) -SEARCH_RESULT_HIGHLIGHT = (wx.Colour(255, 165, 0), wx.WHITE) +ERROR_HIGHLIGHT = (wx.Colour(255, 255, 0).GetIM(), wx.RED.GetIM()) +SEARCH_RESULT_HIGHLIGHT = (wx.Colour(255, 165, 0).GetIM(), wx.WHITE.GetIM()) # Define highlight refresh inhibition period in second REFRESH_HIGHLIGHT_PERIOD = 0.1 @@ -142,6 +137,10 @@ return vector +def ivector(*a,**k): + return tuple(map(round, vector(*a,**k))) + + def norm(v): """ Calculate the norm of a given vector @@ -188,8 +187,9 @@ """ pos = event.GetLogicalPosition(dc) if scaling: - pos.x = round(pos.x / scaling[0]) * scaling[0] - pos.y = round(pos.y / scaling[1]) * scaling[1] + sx,sy=tuple(map(round,scaling)) + pos.x = round(pos.x / sx) * sx + pos.y = round(pos.y / sy) * sy return pos @@ -206,7 +206,7 @@ def MiterPen(colour, width=1, style=wx.SOLID): - pen = wx.Pen(colour, width, style) + pen = wx.Pen(colour, round(width), style) pen.SetJoin(wx.JOIN_MITER) pen.SetCap(wx.CAP_PROJECTING) return pen @@ -299,7 +299,7 @@ distances.append((sqrt((self.Pos.x + connector_pos.x - position.x) ** 2 + (self.Pos.y + connector_pos.y - position.y) ** 2), connector)) - distances.sort() + distances.sort(key=lambda n: n[0]) if len(distances) > 0: return distances[0][1] return None @@ -319,8 +319,8 @@ # Changes the block position def SetPosition(self, x, y): - self.Pos.x = x - self.Pos.y = y + self.Pos.x = int(x) + self.Pos.y = int(y) self.RefreshConnected() self.RefreshBoundingBox() @@ -330,8 +330,8 @@ # Changes the element size def SetSize(self, width, height): - self.Size.SetWidth(width) - self.Size.SetHeight(height) + self.Size.SetWidth(int(width)) + self.Size.SetHeight(int(height)) self.RefreshConnectors() self.RefreshBoundingBox() @@ -388,11 +388,11 @@ rect = self.BoundingBox else: rect = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0], self.Size[1]) - return rect.InsideXY(pt.x, pt.y) + return rect.Contains(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) + return rect.Contains(self.BoundingBox.x, self.BoundingBox.y) and rect.Contains(self.BoundingBox.x + self.BoundingBox.width, self.BoundingBox.y + self.BoundingBox.height) # Override this method for refreshing the bounding box def RefreshBoundingBox(self): @@ -436,19 +436,19 @@ 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 + left = round((self.BoundingBox.x - 2) * scalex - HANDLE_SIZE) + center = round((self.BoundingBox.x + self.BoundingBox.width // 2) * scalex - HANDLE_SIZE // 2) + right = round((self.BoundingBox.x + self.BoundingBox.width + 2) * scalex) + + top = round((self.BoundingBox.y - 2) * scaley - HANDLE_SIZE) + middle = round((self.BoundingBox.y + self.BoundingBox.height / 2) * scaley - HANDLE_SIZE // 2) + bottom = round((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): + if self.Selected and extern_rect.Contains(pt.x, pt.y) and not intern_rect.Contains(pt.x, pt.y): # Find if point is on a handle horizontally if left <= pt.x < left + HANDLE_SIZE: handle_x = 1 @@ -684,13 +684,13 @@ 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 + left = round((self.BoundingBox.x - 2) * scalex - HANDLE_SIZE) + center = round((self.BoundingBox.x + self.BoundingBox.width // 2) * scalex - HANDLE_SIZE // 2) + right = round((self.BoundingBox.x + self.BoundingBox.width + 2) * scalex) + + top = round((self.BoundingBox.y - 2) * scaley - HANDLE_SIZE) + middle = round((self.BoundingBox.y + self.BoundingBox.height // 2) * scaley - HANDLE_SIZE // 2) + bottom = round((self.BoundingBox.y + self.BoundingBox.height + 2) * scaley) for x, y in [(left, top), (center, top), (right, top), (left, middle), (right, middle), @@ -717,10 +717,6 @@ self.RefreshWireExclusion() self.RefreshBoundingBox() - # Destructor - def __del__(self): - self.Elements = [] - def GetDefinition(self): blocks = [] wires = [] @@ -1084,7 +1080,7 @@ y -= 5 height += 5 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, string_types): + if self.ValueSize is None and isinstance(self.ComputedValue, str): self.ValueSize = self.ParentBlock.Parent.GetMiniTextExtent(self.ComputedValue) if self.ValueSize is not None: width, height = self.ValueSize @@ -1401,7 +1397,7 @@ 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.Contains(pt.x, pt.y) return inside @@ -1481,7 +1477,7 @@ else: dc.SetPen(MiterPen(wx.GREEN)) elif self.Value == "undefined": - dc.SetPen(MiterPen(wx.NamedColour("orange"))) + dc.SetPen(MiterPen(wx.Colour("orange"))) elif self.Forced: dc.SetPen(MiterPen(wx.BLUE)) else: @@ -1541,8 +1537,8 @@ if self.Value is not None and not isinstance(self.Value, bool) and self.Value != "undefined": dc.SetFont(self.ParentBlock.Parent.GetMiniFont()) - dc.SetTextForeground(wx.NamedColour("purple")) - if self.ValueSize is None and isinstance(self.ComputedValue, string_types): + dc.SetTextForeground(wx.Colour("purple")) + if self.ValueSize is None and isinstance(self.ComputedValue, str): self.ValueSize = self.ParentBlock.Parent.GetMiniTextExtent(self.ComputedValue) if self.ValueSize is not None: width, height = self.ValueSize @@ -1610,7 +1606,7 @@ rect = rect.Union(self.StartConnected.GetRedrawRect(movex, movey)) if self.EndConnected: rect = rect.Union(self.EndConnected.GetRedrawRect(movex, movey)) - if self.ValueSize is None and isinstance(self.ComputedValue, string_types): + if self.ValueSize is None and isinstance(self.ComputedValue, str): self.ValueSize = self.Parent.GetMiniTextExtent(self.ComputedValue) if self.ValueSize is not None: width, height = self.ValueSize @@ -1918,7 +1914,7 @@ # Returns if the point given is on one of the wire segments def HitTest(self, pt, connectors=True): test = False - for i in xrange(len(self.Points) - 1): + for i in range(len(self.Points) - 1): rect = wx.Rect(0, 0, 0, 0) if i == 0 and self.StartConnected is not None: x1 = self.Points[i].x - self.Segments[0][0] * CONNECTOR_SIZE @@ -1933,7 +1929,7 @@ # 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) + test |= rect.Contains(pt.x, pt.y) return test # Returns the wire start or end point if the point given is on one of them @@ -1941,19 +1937,19 @@ # 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) - if rect.InsideXY(pt.x, pt.y): + if rect.Contains(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) - if rect.InsideXY(pt.x, pt.y): + if rect.Contains(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)): + for i in range(len(self.Segments)): # If wire is not in a Ladder Diagram, first and last segments are excluded if all or 0 < i < len(self.Segments) - 1: x1, y1 = self.Points[i].x, self.Points[i].y @@ -1961,7 +1957,7 @@ # 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) - if rect.InsideXY(pt.x, pt.y): + if rect.Contains(pt.x, pt.y): return i, self.Segments[i] return None @@ -1984,13 +1980,13 @@ lx, ly = x, y # Calculate the start and end directions - self.StartPoint = [None, vector(self.Points[0], self.Points[1])] - self.EndPoint = [None, vector(self.Points[-1], self.Points[-2])] + self.StartPoint = [None, ivector(self.Points[0], self.Points[1])] + self.EndPoint = [None, ivector(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 + round(CONNECTOR_SIZE * self.StartPoint[1][0]), + self.Points[0].y + round(CONNECTOR_SIZE * self.StartPoint[1][1])) + self.EndPoint[0] = wx.Point(self.Points[-1].x + round(CONNECTOR_SIZE * self.EndPoint[1][0]), + self.Points[-1].y + round(CONNECTOR_SIZE * self.EndPoint[1][1])) self.Points[0] = self.StartPoint[0] self.Points[-1] = self.EndPoint[0] # Calculate the segments directions @@ -2001,7 +1997,7 @@ if i > lp - 2: break - segment = vector(self.Points[i], self.Points[i + 1]) + segment = ivector(self.Points[i], self.Points[i + 1]) # merge segment if requested if merge_segments and 0 < i and \ @@ -2014,7 +2010,7 @@ # remove corner when two segments are in opposite direction if i < lp - 2: - next = vector(self.Points[i + 1], self.Points[i + 2]) + next = ivector(self.Points[i + 1], self.Points[i + 2]) if is_null_vector(add_vectors(segment, next)): self.Points.pop(i+1) continue @@ -2035,10 +2031,10 @@ # Returns a list of the position of all wire points 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 - round(CONNECTOR_SIZE * self.StartPoint[1][0]), + points[0].y - round(CONNECTOR_SIZE * self.StartPoint[1][1])) + points[-1] = wx.Point(points[-1].x - round(CONNECTOR_SIZE * self.EndPoint[1][0]), + points[-1].y - round(CONNECTOR_SIZE * self.EndPoint[1][1])) # An inversion of the list is asked if invert: points.reverse() @@ -2077,10 +2073,10 @@ 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) + end = wx.Point(self.EndPoint[0].x + round(self.EndPoint[1][0] * MIN_SEGMENT_SIZE), + self.EndPoint[0].y + round(self.EndPoint[1][1] * MIN_SEGMENT_SIZE)) + start = wx.Point(self.StartPoint[0].x + round(self.StartPoint[1][0] * MIN_SEGMENT_SIZE), + self.StartPoint[0].y + round(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 @@ -2233,7 +2229,7 @@ i = 1 while i < len(points) - 1: if points[i] == points[i + 1] and segments[i - 1] == segments[i + 1]: - for dummy in xrange(2): + for dummy in range(2): points.pop(i) segments.pop(i) else: @@ -2295,8 +2291,8 @@ # during a resize dragging for i, point in enumerate(self.RealPoints): if not (i == 0 and self.StartConnected) and not (i == len(self.Points) - 1 and self.EndConnected): - point[0] = point[0] * width / max(lastwidth, 1) - point[1] = point[1] * height / max(lastheight, 1) + point[0] = int(point[0] * width / max(lastwidth, 1)) + point[1] = int(point[1] * height / max(lastheight, 1)) # Calculate the correct position of the points from real points for i, point in enumerate(self.RealPoints): if not (i == 0 and self.StartConnected) and not (i == len(self.Points) - 1 and self.EndConnected): @@ -2459,7 +2455,7 @@ handle_type, handle = self.Handle if handle_type == HANDLE_SEGMENT: segment, _dir = handle - for dummy in xrange(2): + for dummy in range(2): self.Points.pop(segment) self.Segments.pop(segment) self.GeneratePoints() @@ -2680,9 +2676,9 @@ 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), - (POINT_RADIUS + 1) * scalex + 2) + round((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) + dc.DrawCircle(round(self.Points[-1].x * scalex), round(self.Points[-1].y * scaley), round((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), @@ -2717,8 +2713,8 @@ dc.SetPen(MiterPen(wx.GREEN)) dc.SetBrush(wx.GREEN_BRUSH) elif self.Value == "undefined": - dc.SetPen(MiterPen(wx.NamedColour("orange"))) - dc.SetBrush(wx.Brush(wx.NamedColour("orange"))) + dc.SetPen(MiterPen(wx.Colour("orange"))) + dc.SetBrush(wx.Brush(wx.Colour("orange"))) elif self.Forced: dc.SetPen(MiterPen(wx.BLUE)) dc.SetBrush(wx.BLUE_BRUSH) @@ -2749,8 +2745,8 @@ self.Points[self.SelectedSegment + 1].x + end, self.Points[self.SelectedSegment + 1].y) if self.Value is not None and not isinstance(self.Value, bool) and self.Value != "undefined": dc.SetFont(self.Parent.GetMiniFont()) - dc.SetTextForeground(wx.NamedColour("purple")) - if self.ValueSize is None and isinstance(self.ComputedValue, string_types): + dc.SetTextForeground(wx.Colour("purple")) + if self.ValueSize is None and isinstance(self.ComputedValue, str): self.ValueSize = self.Parent.GetMiniTextExtent(self.ComputedValue) if self.ValueSize is not None: width, height = self.ValueSize @@ -2931,12 +2927,12 @@ 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 + left = round((self.Pos.x - 1) * scalex - 2) + right = round((self.Pos.x + self.Size[0] + 1) * scalex + 2) + top = round((self.Pos.y - 1) * scaley - 2) + bottom = round((self.Pos.y + self.Size[1] + 1) * scaley + 2) + angle_top = round((self.Pos.x + self.Size[0] - 9) * scalex + 2) + angle_right = round((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), diff -r 7e17f7e02a2b -r 0b3ac94f494c graphics/LD_Objects.py --- a/graphics/LD_Objects.py Wed Nov 29 11:54:56 2023 +0100 +++ b/graphics/LD_Objects.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,11 +23,9 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import division +from functools import cmp_to_key +from operator import eq import wx -from future.builtins import round -from six.moves import xrange from graphics.GraphicCommons import * from graphics.DebugDataConsumer import DebugDataConsumer @@ -73,8 +71,8 @@ return powerrail def GetConnectorTranslation(self, element): - return dict(zip([connector for connector in self.Connectors], - [connector for connector in element.Connectors])) + return dict(list(zip([connector for connector in self.Connectors], + [connector for connector in element.Connectors]))) # Returns the RedrawRect def GetRedrawRect(self, movex=0, movey=0): @@ -161,7 +159,7 @@ for connect in self.Connectors: connect_pos = connect.GetRelPosition() connect.SetPosition(wx.Point(connect_pos.x, connect_pos.y - miny)) - self.Connectors.sort(lambda x, y: cmp(x.Pos.y, y.Pos.y)) + self.Connectors.sort(key=cmp_to_key(lambda x, y: eq(x.Pos.y, y.Pos.y))) maxy = 0 for connect in self.Connectors: connect_pos = connect.GetRelPosition() @@ -238,7 +236,7 @@ self.Type = type self.Clean() self.Connectors = [] - for dummy in xrange(connectors): + for dummy in range(connectors): self.AddConnector() self.RefreshSize() @@ -255,7 +253,7 @@ position = connector.GetRelPosition() self.RealConnectors.append(max(0., min((position.y - self.Extensions[0]) / height, 1.))) elif len(self.Connectors) > 1: - self.RealConnectors = map(lambda x: x * 1 / (len(self.Connectors) - 1), xrange(len(self.Connectors))) + self.RealConnectors = [x * 1 / (len(self.Connectors) - 1) for x in range(len(self.Connectors))] else: self.RealConnectors = [0.5] Graphic_Element.OnLeftDown(self, event, dc, scaling) @@ -626,7 +624,7 @@ if highlight_type is None: self.Highlights = {} else: - highlight_items = self.Highlights.items() + highlight_items = list(self.Highlights.items()) for name, highlights in highlight_items: highlights = ClearHighlights(highlights, highlight_type) if len(highlights) == 0: @@ -687,7 +685,7 @@ self.Output.Draw(dc) if not getattr(dc, "printing", False): - for name, highlights in self.Highlights.iteritems(): + for name, highlights in self.Highlights.items(): if name == "reference": DrawHighlightedText(dc, self.Name, highlights, name_pos[0], name_pos[1]) elif typetext != "": @@ -948,7 +946,7 @@ if highlight_type is None: self.Highlights = {} else: - highlight_items = self.Highlights.items() + highlight_items = list(self.Highlights.items()) for name, highlights in highlight_items: highlights = ClearHighlights(highlights, highlight_type) if len(highlights) == 0: @@ -1016,7 +1014,7 @@ self.Output.Draw(dc) if not getattr(dc, "printing", False): - for name, highlights in self.Highlights.iteritems(): + for name, highlights in self.Highlights.items(): if name == "reference": DrawHighlightedText(dc, self.Name, highlights, name_pos[0], name_pos[1]) elif typetext != "": diff -r 7e17f7e02a2b -r 0b3ac94f494c graphics/RubberBand.py --- a/graphics/RubberBand.py Wed Nov 29 11:54:56 2023 +0100 +++ b/graphics/RubberBand.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,7 +23,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx from graphics.GraphicCommons import GetScaledEventPosition @@ -94,7 +94,7 @@ # Change viewer mouse cursor to reflect a rubberband bounding box is # edited - self.DrawingSurface.SetCursor(wx.StockCursor(wx.CURSOR_CROSS)) + self.DrawingSurface.SetCursor(wx.Cursor(wx.CURSOR_CROSS)) self.Redraw() @@ -139,6 +139,11 @@ self.Redraw() + def SetRubberBandPen(self, dc): + # Set DC drawing style + dc.SetPen(wx.Pen(wx.WHITE, style=wx.DOT)) + dc.SetLogicalFunction(wx.XOR) + def DrawBoundingBoxes(self, bboxes, dc=None): """ Draw a list of bounding box on Viewer in the order given using XOR @@ -155,17 +160,16 @@ scalex, scaley = dc.GetUserScale() dc.SetUserScale(1, 1) - # Set DC drawing style - dc.SetPen(wx.Pen(wx.WHITE, style=wx.DOT)) + self.SetRubberBandPen(dc) + 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.width * scalex, bbox.height * scaley) + round(bbox.x * scalex), round(bbox.y * scaley), + round(bbox.width * scalex), round(bbox.height * scaley)) dc.SetLogicalFunction(wx.COPY) @@ -195,3 +199,29 @@ """ # Erase last bbox and draw current bbox self.DrawBoundingBoxes([self.CurrentBBox], dc) + + +def PatchRubberBandForGTK3(): + """ + GTK3 implementation of DC doesn't support SetLogicalFuntion(XOR) + Then Rubberband can't be erased by just redrawing it on the same place + So this is a complete refresh instead, eating a lot of CPU. + """ + def Redraw(self, dc=None): + self.Viewer.Refresh() + self.Draw() + + RubberBand.Redraw = Redraw + + def Erase(self, dc=None): + self.Viewer.Refresh() + + RubberBand.Erase = Erase + + def SetRubberBandPen(self, dc): + dc.SetPen(wx.Pen(wx.BLACK, style=wx.DOT)) + + RubberBand.SetRubberBandPen = SetRubberBandPen + +if "gtk3" in wx.PlatformInfo: + PatchRubberBandForGTK3() diff -r 7e17f7e02a2b -r 0b3ac94f494c graphics/SFC_Objects.py --- a/graphics/SFC_Objects.py Wed Nov 29 11:54:56 2023 +0100 +++ b/graphics/SFC_Objects.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,12 +23,9 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import division -from future.builtins import round - +from functools import cmp_to_key +from operator import eq import wx -from six.moves import xrange from graphics.GraphicCommons import * from graphics.DebugDataConsumer import DebugDataConsumer @@ -719,7 +716,7 @@ self.Pos.y + (self.Size[1] - text_height) // 2, text_width, text_height) - test_text = text_bbx.InsideXY(pt.x, pt.y) + test_text = text_bbx.Contains(pt.x, pt.y) else: test_text = False return test_text or Graphic_Element.HitTest(self, pt, connectors) @@ -976,7 +973,7 @@ if highlight_type is None: self.Highlights = {} else: - highlight_items = self.Highlights.items() + highlight_items = list(self.Highlights.items()) for name, highlights in highlight_items: highlights = ClearHighlights(highlights, highlight_type) if len(highlights) == 0: @@ -1037,7 +1034,7 @@ self.Condition.Draw(dc) if not getattr(dc, "printing", False): - for name, highlights in self.Highlights.iteritems(): + for name, highlights in self.Highlights.items(): if name == "priority": DrawHighlightedText(dc, str(self.Priority), highlights, priority_pos[0], priority_pos[1]) else: @@ -1067,11 +1064,11 @@ if self.Type in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]: self.Inputs = [Connector(self, "", None, wx.Point(self.Size[0] // 2, 0), NORTH, onlyone=True)] self.Outputs = [] - for i in xrange(number): + for i in range(number): 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): + for i in range(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.Value = None @@ -1124,7 +1121,7 @@ return divergence def GetConnectorTranslation(self, element): - return dict(zip(self.Inputs + self.Outputs, element.Inputs + element.Outputs)) + return dict(list(zip(self.Inputs + self.Outputs, element.Inputs + element.Outputs))) # Returns the RedrawRect def GetRedrawRect(self, movex=0, movey=0): @@ -1204,7 +1201,7 @@ # 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) is not None + return self.BoundingBox.Contains(pt.x, pt.y) or self.TestConnector(pt, exclude=False) is not None # Refresh the divergence bounding box def RefreshBoundingBox(self): @@ -1244,8 +1241,8 @@ for output in self.Outputs: output_pos = output.GetRelPosition() output.SetPosition(wx.Point(output_pos.x - minx, output_pos.y)) - self.Inputs.sort(lambda x, y: cmp(x.Pos.x, y.Pos.x)) - self.Outputs.sort(lambda x, y: cmp(x.Pos.x, y.Pos.x)) + self.Inputs.sort(key=cmp_to_key(lambda x, y: eq(x.Pos.y, y.Pos.y))) + self.Outputs.sort(key=cmp_to_key(lambda x, y: eq(x.Pos.y, y.Pos.y))) self.Pos.x += minx self.Size[0] = maxx - minx connector.MoveConnected() @@ -1592,7 +1589,7 @@ self.Pos.y + (self.Size[1] - text_height) // 2, text_width, text_height) - return text_bbx.InsideXY(pt.x, pt.y) or Graphic_Element.HitTest(self, pt, connectors) + return text_bbx.Contains(pt.x, pt.y) or Graphic_Element.HitTest(self, pt, connectors) # Refresh the jump bounding box def RefreshBoundingBox(self): @@ -2004,9 +2001,9 @@ if highlight_type is None: self.Highlights = {} else: - highlight_items = self.Highlights.items() + highlight_items = list(self.Highlights.items()) for number, action_highlights in highlight_items: - action_highlight_items = action_highlights.items() + action_highlight_items = list(action_highlights.items()) for name, attribute_highlights in action_highlight_items: attribute_highlights = ClearHighlights(attribute_highlights, highlight_type) if len(attribute_highlights) == 0: @@ -2058,7 +2055,7 @@ if not getattr(dc, "printing", False): action_highlights = self.Highlights.get(i, {}) - for name, attribute_highlights in action_highlights.iteritems(): + for name, attribute_highlights in action_highlights.items(): if name == "qualifier": DrawHighlightedText(dc, action.qualifier, attribute_highlights, qualifier_pos[0], qualifier_pos[1]) elif name == "duration": diff -r 7e17f7e02a2b -r 0b3ac94f494c graphics/ToolTipProducer.py --- a/graphics/ToolTipProducer.py Wed Nov 29 11:54:56 2023 +0100 +++ b/graphics/ToolTipProducer.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,7 +23,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import wx from controls.CustomToolTip import CustomToolTip, TOOLTIP_WAIT_PERIOD @@ -55,12 +55,6 @@ self.OnToolTipTimer, self.ToolTipTimer) - def __del__(self): - """ - Destructor - """ - self.DestroyToolTip() - def OnToolTipTimer(self, event): """ Callback for Tool Tip firing timer Event diff -r 7e17f7e02a2b -r 0b3ac94f494c i18n/mki18n.py --- a/i18n/mki18n.py Wed Nov 29 11:54:56 2023 +0100 +++ b/i18n/mki18n.py Thu Dec 07 22:41:32 2023 +0100 @@ -79,12 +79,10 @@ # ------------- # -from __future__ import absolute_import -from __future__ import print_function + import os import sys import re -from builtins import str as text import wx @@ -122,10 +120,7 @@ def getlanguageDict(): languageDict = {} getSupportedLanguageDict('Beremiz') - if wx.VERSION >= (3, 0, 0): - _app = wx.App() - else: - _app = wx.PySimpleApp() + _app = wx.App() for lang in [x for x in dir(wx) if x.startswith("LANGUAGE")]: i = wx.Locale(wx.LANGUAGE_DEFAULT).GetLanguageInfo(getattr(wx, lang)) @@ -229,7 +224,7 @@ languageDict = getSupportedLanguageDict(applicationName) - for langCode in languageDict.keys(): + for langCode in list(languageDict.keys()): if langCode == 'en': pass else: @@ -254,7 +249,7 @@ languageDict = getSupportedLanguageDict(applicationName) - for langCode in languageDict.keys(): + for langCode in list(languageDict.keys()): if langCode == 'en': pass else: @@ -308,7 +303,7 @@ languageDict = getSupportedLanguageDict(applicationName) - for langCode in languageDict.keys(): + for langCode in list(languageDict.keys()): if (langCode == 'en') and (forceEnglish == 0): pass else: @@ -414,7 +409,7 @@ # translate the path separators directory = unixpath(directory) # build a list of all directory elements - aList = filter(lambda x: len(x) > 0, directory.split('/')) + aList = [x for x in directory.split('/') if len(x) > 0] theLen = len(aList) # if the first element is a Windows-style disk drive # concatenate it with the first directory @@ -515,7 +510,7 @@ makePO(appDirPath, option['domain'], option['verbose']) exit_code = 0 except IOError as e: - printUsage(text(e) + '\n You must write a file app.fil that contains the list of all files to parse.') + printUsage(str(e) + '\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']) exit_code = 0 diff -r 7e17f7e02a2b -r 0b3ac94f494c images/Build.png Binary file images/Build.png has changed diff -r 7e17f7e02a2b -r 0b3ac94f494c images/Clean.png Binary file images/Clean.png has changed diff -r 7e17f7e02a2b -r 0b3ac94f494c images/Connect.png Binary file images/Connect.png has changed diff -r 7e17f7e02a2b -r 0b3ac94f494c images/Disconnect.png Binary file images/Disconnect.png has changed diff -r 7e17f7e02a2b -r 0b3ac94f494c images/Transfer.png Binary file images/Transfer.png has changed diff -r 7e17f7e02a2b -r 0b3ac94f494c images/about_brz_logo.png Binary file images/about_brz_logo.png has changed diff -r 7e17f7e02a2b -r 0b3ac94f494c images/brz.ico Binary file images/brz.ico has changed diff -r 7e17f7e02a2b -r 0b3ac94f494c images/brz.png Binary file images/brz.png has changed diff -r 7e17f7e02a2b -r 0b3ac94f494c images/genicons.sh --- a/images/genicons.sh Wed Nov 29 11:54:56 2023 +0100 +++ b/images/genicons.sh Thu Dec 07 22:41:32 2023 +0100 @@ -16,8 +16,8 @@ done cp ico024.png brz.png -convert -compress none ico*.png brz.ico -rm -f ico*.png +convert -compress none ico???.png brz.ico +rm -f ico???.png convert -compress none poeico*.png poe.ico diff -r 7e17f7e02a2b -r 0b3ac94f494c images/icons.svg --- a/images/icons.svg Wed Nov 29 11:54:56 2023 +0100 +++ b/images/icons.svg Thu Dec 07 22:41:32 2023 +0100 @@ -32,7 +32,7 @@ + inkscape:measure-end="930.144,704.058" + inkscape:object-paths="true" + inkscape:snap-bbox="true" + inkscape:bbox-paths="true"> + + + @@ -89183,18 +89196,13 @@ inkscape:connector-curvature="0" id="path18378" d="m 70,192.36218 0,22 17,0 0,-17 -5,-5 z" - style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="color:#000000;fill:#ededed;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> - @@ -90141,83 +90149,91 @@ id="g17803" transform="matrix(0.2224431,0,0,0.2224431,-580.61956,554.56584)"> - - - - - - - - - - - - - - - - Free Software for Automation - + transform="translate(0,107.89276)" /> + + + + + + + + + + + + + + + + + id="ico064" + style="display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:0;marker:none;enable-background:accumulate" + inkscape:label="#ico064" /> - - - - - - - - - - - - - - - + + + + + + + + + + + Icons - - - - @@ -91280,8 +91253,8 @@ id="rect17210" width="10.821424" height="10.821424" - x="666.58472" - y="634.71997" /> + x="661.57739" + y="629.05286" /> + style="display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;enable-background:accumulate" /> @@ -92932,7 +92905,7 @@ inkscape:connector-curvature="0" /> + style="fill:#84c225;fill-rule:evenodd;stroke:#5d9d35;stroke-width:2.82220006" /> - - - - - - - - - + sodipodi:nodetypes="cccccccccccccccc" /> + + + + @@ -93778,7 +93719,7 @@ inkscape:radius="1" inkscape:original="M 108 192.36133 L 108 214.36133 L 110 214.36133 L 110 203.36133 L 111 203.36133 L 111 213.36133 L 113 213.36133 L 113 201.36133 L 114 201.36133 L 114 214.36133 L 117 214.36133 L 117 202.36133 L 118 202.36133 L 118 215.36133 L 120 215.36133 L 120 201.36133 L 121 201.36133 L 121 212.36133 L 123 212.36133 L 123 202.36133 L 124 202.36133 L 124 214.36133 L 125 214.36133 L 125 197.36133 L 120 192.36133 L 108 192.36133 z " xlink:href="#path18406" - style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;marker:none;enable-background:accumulate" + style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#aaaaaa;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;marker:none;enable-background:accumulate" id="path18446" inkscape:href="#path18406" d="m 108,191.36133 a 1.0001,1.0001 0 0 0 -1,1 v 22 a 1.0001,1.0001 0 0 0 1,1 h 2 a 1.0001,1.0001 0 0 0 1,-1 h 2 a 1.0001,1.0001 0 0 0 1,1 h 3 a 1.0001,1.0001 0 0 0 1,1 h 2 a 1.0001,1.0001 0 0 0 1,-1 v -2 h 2 v 1 a 1.0001,1.0001 0 0 0 1,1 h 1 a 1.0001,1.0001 0 0 0 1,-1 v -17 a 1.0001,1.0001 0 0 0 -0.29297,-0.70703 l -5,-5 A 1.0001,1.0001 0 0 0 120,191.36133 Z" /> @@ -93787,7 +93728,7 @@ inkscape:connector-curvature="0" id="path18406" d="m 108,192.36218 v 22 h 2 v -11 h 1 v 10 h 2 v -12 h 1 v 13 h 3 v -12 h 1 v 13 h 2 v -14 h 1 v 11 h 2 v -10 h 1 v 12 h 1 v -17 l -5,-5 z" - style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;marker:none;enable-background:accumulate" /> + style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ededed;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;marker:none;enable-background:accumulate" /> @@ -93803,12 +93744,6 @@ id="path18412" /> - %% about_brz_logo %%
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + RUN + + + + + + + RUN + + + + + + + TIME#10s + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + START_TIME + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + START_TIME + + + + + + + + + + + + + + + + + VALID + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TIME#1s + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 'OK' + + + + + + + 'BAD' + + + + + + + + + + + RESULT + + + + + + + + + + + + + + + + + + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/modbus/beremiz.xml --- a/tests/projects/modbus/beremiz.xml Wed Nov 29 11:54:56 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ - - - - - - diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/modbus/modbus_0@modbus/ModbusTCPclient_0@ModbusTCPclient/ModbusRequest_0@ModbusRequest/baseconfnode.xml --- a/tests/projects/modbus/modbus_0@modbus/ModbusTCPclient_0@ModbusTCPclient/ModbusRequest_0@ModbusRequest/baseconfnode.xml Wed Nov 29 11:54:56 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ - - diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/modbus/modbus_0@modbus/ModbusTCPclient_0@ModbusTCPclient/ModbusRequest_0@ModbusRequest/confnode.xml --- a/tests/projects/modbus/modbus_0@modbus/ModbusTCPclient_0@ModbusTCPclient/ModbusRequest_0@ModbusRequest/confnode.xml Wed Nov 29 11:54:56 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ - - diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/modbus/modbus_0@modbus/ModbusTCPclient_0@ModbusTCPclient/ModbusRequest_1@ModbusRequest/baseconfnode.xml --- a/tests/projects/modbus/modbus_0@modbus/ModbusTCPclient_0@ModbusTCPclient/ModbusRequest_1@ModbusRequest/baseconfnode.xml Wed Nov 29 11:54:56 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ - - diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/modbus/modbus_0@modbus/ModbusTCPclient_0@ModbusTCPclient/ModbusRequest_1@ModbusRequest/confnode.xml --- a/tests/projects/modbus/modbus_0@modbus/ModbusTCPclient_0@ModbusTCPclient/ModbusRequest_1@ModbusRequest/confnode.xml Wed Nov 29 11:54:56 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ - - diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/modbus/modbus_0@modbus/ModbusTCPclient_0@ModbusTCPclient/baseconfnode.xml --- a/tests/projects/modbus/modbus_0@modbus/ModbusTCPclient_0@ModbusTCPclient/baseconfnode.xml Wed Nov 29 11:54:56 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ - - diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/modbus/modbus_0@modbus/ModbusTCPclient_0@ModbusTCPclient/confnode.xml --- a/tests/projects/modbus/modbus_0@modbus/ModbusTCPclient_0@ModbusTCPclient/confnode.xml Wed Nov 29 11:54:56 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ - - diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/modbus/modbus_0@modbus/ModbusTCPserver_0@ModbusTCPserver/HoldingRegs@MemoryArea/baseconfnode.xml --- a/tests/projects/modbus/modbus_0@modbus/ModbusTCPserver_0@ModbusTCPserver/HoldingRegs@MemoryArea/baseconfnode.xml Wed Nov 29 11:54:56 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ - - diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/modbus/modbus_0@modbus/ModbusTCPserver_0@ModbusTCPserver/HoldingRegs@MemoryArea/confnode.xml --- a/tests/projects/modbus/modbus_0@modbus/ModbusTCPserver_0@ModbusTCPserver/HoldingRegs@MemoryArea/confnode.xml Wed Nov 29 11:54:56 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ - - diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/modbus/modbus_0@modbus/ModbusTCPserver_0@ModbusTCPserver/InputRegs@MemoryArea/baseconfnode.xml --- a/tests/projects/modbus/modbus_0@modbus/ModbusTCPserver_0@ModbusTCPserver/InputRegs@MemoryArea/baseconfnode.xml Wed Nov 29 11:54:56 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ - - diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/modbus/modbus_0@modbus/ModbusTCPserver_0@ModbusTCPserver/InputRegs@MemoryArea/confnode.xml --- a/tests/projects/modbus/modbus_0@modbus/ModbusTCPserver_0@ModbusTCPserver/InputRegs@MemoryArea/confnode.xml Wed Nov 29 11:54:56 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ - - diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/modbus/modbus_0@modbus/ModbusTCPserver_0@ModbusTCPserver/baseconfnode.xml --- a/tests/projects/modbus/modbus_0@modbus/ModbusTCPserver_0@ModbusTCPserver/baseconfnode.xml Wed Nov 29 11:54:56 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ - - diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/modbus/modbus_0@modbus/ModbusTCPserver_0@ModbusTCPserver/confnode.xml --- a/tests/projects/modbus/modbus_0@modbus/ModbusTCPserver_0@ModbusTCPserver/confnode.xml Wed Nov 29 11:54:56 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ - - diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/modbus/modbus_0@modbus/baseconfnode.xml --- a/tests/projects/modbus/modbus_0@modbus/baseconfnode.xml Wed Nov 29 11:54:56 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ - - diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/modbus/modbus_0@modbus/confnode.xml --- a/tests/projects/modbus/modbus_0@modbus/confnode.xml Wed Nov 29 11:54:56 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ - - diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/modbus/plc.xml --- a/tests/projects/modbus/plc.xml Wed Nov 29 11:54:56 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,314 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "beremiz" - "Modbus" - -If Modbus library is installed elsewhere, then place corresponding paths -in CFLAGS/LDFLAGS in project settings. - -For GNU/Linux to install Modbus library in parent directory run following commands: -$ hg clone https://bitbucket.org/mjsousa/modbus Modbus -$ cd Modbus -$ make - -After that Modbus extension is ready to be used in Beremiz projects.]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - T#1s - - - - - - - 32767 - - - - - - - - - - - - - - Counter - - - - - - - - - - - MasterWriteToReg0 - - - - - - - MasterReadFromReg1 - - - - - - - - - - - CounterReadBack - - - - - - - - - - - - - SlaveHoldReg0 - - - - - - - - - - - SlaveInputReg0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/opcua_browse/beremiz.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/opcua_browse/beremiz.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,4 @@ + + + + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/opcua_browse/opcua_0@opcua/baseconfnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/opcua_browse/opcua_0@opcua/baseconfnode.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,2 @@ + + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/opcua_browse/opcua_0@opcua/confnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/opcua_browse/opcua_0@opcua/confnode.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,4 @@ + + + + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/opcua_browse/plc.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/opcua_browse/plc.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,208 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + LocalVar0 + + + + + + + + + + + LocalVar1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TRUE + + + + + + + 'pfunc("' + + + + + + + '\n")' + + + + + + + 3.4 + + + + + + + + + + + + + + + + + + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/opcua_browse/py_ext_0@py_ext/baseconfnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/opcua_browse/py_ext_0@py_ext/baseconfnode.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,2 @@ + + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/opcua_browse/py_ext_0@py_ext/pyfile.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/opcua_browse/py_ext_0@py_ext/pyfile.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/opcua_browse_encrypted/beremiz.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/opcua_browse_encrypted/beremiz.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,4 @@ + + + + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/opcua_browse_encrypted/opcua_0@opcua/baseconfnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/opcua_browse_encrypted/opcua_0@opcua/baseconfnode.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,2 @@ + + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/opcua_browse_encrypted/opcua_0@opcua/confnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/opcua_browse_encrypted/opcua_0@opcua/confnode.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,9 @@ + + + + + + + + + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/opcua_browse_encrypted/plc.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/opcua_browse_encrypted/plc.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,208 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + LocalVar0 + + + + + + + + + + + LocalVar1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TRUE + + + + + + + 'pfunc("' + + + + + + + '\n")' + + + + + + + 3.4 + + + + + + + + + + + + + + + + + + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/opcua_browse_encrypted/py_ext_0@py_ext/baseconfnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/opcua_browse_encrypted/py_ext_0@py_ext/baseconfnode.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,2 @@ + + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/opcua_browse_encrypted/py_ext_0@py_ext/pyfile.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/opcua_browse_encrypted/py_ext_0@py_ext/pyfile.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/opcua_client/beremiz.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/opcua_client/beremiz.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,4 @@ + + + + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/opcua_client/opcua_0@opcua/baseconfnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/opcua_client/opcua_0@opcua/baseconfnode.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,2 @@ + + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/opcua_client/opcua_0@opcua/confnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/opcua_client/opcua_0@opcua/confnode.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,2 @@ + + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/opcua_client/opcua_0@opcua/selected.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/opcua_client/opcua_0@opcua/selected.csv Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,2 @@ +input,TestOut,2,int,2,Double,0 +output,TestIn,2,int,3,Double,0 diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/opcua_client/plc.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/opcua_client/plc.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,208 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + LocalVar0 + + + + + + + + + + + LocalVar1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TRUE + + + + + + + 'pfunc("' + + + + + + + '\n")' + + + + + + + 3.4 + + + + + + + + + + + + + + + + + + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/opcua_client/py_ext_0@py_ext/baseconfnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/opcua_client/py_ext_0@py_ext/baseconfnode.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,2 @@ + + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/opcua_client/py_ext_0@py_ext/pyfile.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/opcua_client/py_ext_0@py_ext/pyfile.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/opcua_client_encrypted/beremiz.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/opcua_client_encrypted/beremiz.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,4 @@ + + + + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/opcua_client_encrypted/opcua_0@opcua/baseconfnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/opcua_client_encrypted/opcua_0@opcua/baseconfnode.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,2 @@ + + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/opcua_client_encrypted/opcua_0@opcua/confnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/opcua_client_encrypted/opcua_0@opcua/confnode.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,9 @@ + + + + + + + + + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/opcua_client_encrypted/opcua_0@opcua/selected.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/opcua_client_encrypted/opcua_0@opcua/selected.csv Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,2 @@ +input,TestOut,2,int,2,Double,0 +output,TestIn,2,int,3,Double,0 diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/opcua_client_encrypted/plc.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/opcua_client_encrypted/plc.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,208 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + LocalVar0 + + + + + + + + + + + LocalVar1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TRUE + + + + + + + 'pfunc("' + + + + + + + '\n")' + + + + + + + 3.4 + + + + + + + + + + + + + + + + + + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/opcua_client_encrypted/py_ext_0@py_ext/baseconfnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/opcua_client_encrypted/py_ext_0@py_ext/baseconfnode.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,2 @@ + + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/opcua_client_encrypted/py_ext_0@py_ext/pyfile.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/opcua_client_encrypted/py_ext_0@py_ext/pyfile.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/svghmi/py_ext_0@py_ext/pyfile.xml --- a/tests/projects/svghmi/py_ext_0@py_ext/pyfile.xml Wed Nov 29 11:54:56 2023 +0100 +++ b/tests/projects/svghmi/py_ext_0@py_ext/pyfile.xml Thu Dec 07 22:41:32 2023 +0100 @@ -87,7 +87,7 @@ diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/svghmi_basic/beremiz.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/svghmi_basic/beremiz.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,5 @@ + + + + + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/svghmi_basic/plc.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/svghmi_basic/plc.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,1490 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ORANGE_LIGHT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + T#500ms + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ORANGE_LIGHT + + + + + + + + + + + + + + + + + + + + + + + + ORANGE_LIGHT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ORANGE_LIGHT + + + + + + + T#500ms + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SWITCH_BUTTON + + + + + + + + + + + STOP + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PEDESTRIAN_BUTTON + + + + + + + T#2s + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WARN_CARS + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SWITCH_BUTTON + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SwitchButton + + + + + + + PedestrianButton + + + + + + + + + + + + + RedLight + + + + + + + + + + + + + OrangeLight + + + + + + + + + + + GreenLight + + + + + + + + + + + + + PedestrianRedLight + + + + + + + + + + + + + PedestrianGreenLight + + + + + + + 'sys.stdout.write("ALL GREEN LIGHTS\n"), sys.stdout.flush()' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/svghmi_basic/svghmi_0@svghmi/baseconfnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/svghmi_basic/svghmi_0@svghmi/baseconfnode.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,2 @@ + + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/svghmi_basic/svghmi_0@svghmi/confnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/svghmi_basic/svghmi_0@svghmi/confnode.xml Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,2 @@ + + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/projects/svghmi_basic/svghmi_0@svghmi/svghmi.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/projects/svghmi_basic/svghmi_0@svghmi/svghmi.svg Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,1546 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ON + OFF + + + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/tools/Docker/Dockerfile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/tools/Docker/Dockerfile Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,111 @@ +# +# Dockerfile for Beremiz +# This container is used to run tests for Beremiz +# +FROM ubuntu:jammy + +ENV TERM xterm-256color + +ENV LANG en_US.UTF-8 +ENV LANGUAGE en_US:en +ENV LC_ALL en_US.UTF-8 + +ARG UNAME=testing +ENV UNAME ${UNAME} +ARG UID=1000 +ARG GID=1000 +RUN groupadd -g $GID $UNAME +RUN useradd -m -u $UID -g $GID -s /bin/bash $UNAME + +RUN set -xe \ + && apt-get update \ + && apt-get install locales \ + && locale-gen en_US.UTF-8 \ + && update-locale LANG=en_US.UTF-8 + +RUN set -xe \ + && TZ="America/Paris" \ + DEBIAN_FRONTEND="noninteractive" \ + apt-get install -y --no-install-recommends \ + `# to run sikuli` \ + wget \ + openjdk-11-jre \ + libtesseract4 \ + \ + `# to run X based tests` \ + fluxbox \ + wmctrl xdotool xvfb \ + x11vnc xterm xnest \ + materia-gtk-theme \ + \ + `# to build tested apps` \ + build-essential automake flex bison mercurial \ + libgtk-3-dev libgl1-mesa-dev libglu1-mesa-dev \ + libpython3.10-dev libssl-dev \ + python3.10 virtualenv cmake git + + +# force bigger font and flat theme for GTK in order to make OCR more reliable +RUN mkdir -p /etc/gtk-3.0 +RUN env echo -e '[Settings]\ngtk-font-name=FreeSans,12\ngtk-theme-name=Materia\n' > /etc/gtk-3.0/settings.ini + +# link obtained from https://raiman.github.io/SikuliX1/downloads.html +RUN set -xe && \ + wget -qP /usr/local/bin \ + https://launchpad.net/sikuli/sikulix/2.0.5/+download/sikulixide-2.0.5.jar && \ + echo 0795f1e0866ee5a7a84e4c89793ea78c /usr/local/bin/sikulixide-2.0.5.jar | md5sum -c && \ + ( echo '#!/bin/sh' && \ + echo "exec java -jar /usr/local/bin/sikulixide-*.jar \"\$@\"" \ + ) | install /dev/stdin /usr/local/bin/sikulix + + +### SVGHMI dependencies : Chromium browser + Inkscape ### +# +# On ubuntu chromium is distrinuted as a snap. +# Running snapd on docker is a mess. +# As a workaround, there is a PPA where chromium .deb packges build is still beeing maintained : +# +# https://launchpad.net/~savoury1/+archive/ubuntu/chromium +# ppa:savoury1, maintained by Rob Savoury at the time of writing +# +# As a side effect of docker limitations, chromium need --no-sandbox command line argument. +# +RUN apt-get install -qqy --no-install-recommends gnupg software-properties-common \ + && add-apt-repository -y ppa:savoury1/chromium \ + && add-apt-repository -y ppa:savoury1/ffmpeg4 \ + && apt-get install -qqy --no-install-recommends chromium-browser inkscape + +# easy to remember 'do_tests' alias to invoke main makefile +RUN env echo -e '#!/bin/bash\nmake -f /home/testing/src/beremiz/tests/Makefile $*' > /usr/local/bin/do_tests +RUN chmod +x /usr/local/bin/do_tests + +USER $UNAME + +RUN mkdir /home/$UNAME/build /home/$UNAME/src /home/$UNAME/test + +RUN virtualenv ~/beremizenv + +COPY requirements.txt /home/$UNAME + +# beremiz python requirements +RUN ~/beremizenv/bin/pip install -r /home/$UNAME/requirements.txt + +# tests python requirements +RUN ~/beremizenv/bin/pip install \ + pytest pytest-timeout ddt + +#TODO sslpsk posix_spawn + +RUN set -xe && \ + cd /home/$UNAME && mkdir tessdata && \ + wget -q https://github.com/tesseract-ocr/tessdata/archive/refs/tags/4.1.0.tar.gz \ + -O tessdata.tar.gz && \ + echo 89e25c7c40a59be7195422a01f57fcb2 tessdata.tar.gz | md5sum -c && \ + tar --strip-components=1 -C tessdata -x -v -z -f tessdata.tar.gz && \ + rm tessdata.tar.gz + +ENV TESSDATAPATH /home/$UNAME/tessdata + +# Points to python binary that test will use +ENV BEREMIZPYTHONPATH /home/$UNAME/beremizenv/bin/python + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/tools/Docker/beremiz-requirements/Dockerfile --- a/tests/tools/Docker/beremiz-requirements/Dockerfile Wed Nov 29 11:54:56 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +0,0 @@ -# -# Dockerfile for Beremiz -# This container is used to run tests for Beremiz -# -# To run test localy use following command executed from beremiz directory: -# $ docker run --volume=$PWD:/beremiz --workdir="/beremiz" --volume=$PWD/../CanFestival-3:/CanFestival-3 --memory=1g --entrypoint=/beremiz/tests/tools/check_source.sh skvorl/beremiz-requirements -# - -FROM skvorl/python2.7-wxpython -MAINTAINER Andrey Skvortsov - -RUN set -xe \ - && apt-get update \ - && apt-get install -y --no-install-recommends \ - python-nevow \ - python-lxml \ - python-zeroconf \ - python-m2crypto \ - python-autobahn \ - python-future \ - python-simplejson \ - && apt-get install -y --no-install-recommends ca-certificates \ - && apt-get install -y --no-install-recommends wxglade python-cwiid \ - && apt-get install -y --no-install-recommends build-essential automake flex bison mercurial python-pip \ - && apt-get install -y --no-install-recommends \ - pep8 \ - pylint \ - python-pytest \ - python-pytest-timeout \ - gettext \ - python-ddt \ - libpython2.7-dev \ - \ - && apt-get install -y python3-pip \ - && pip3 install crossbar \ - \ - && /usr/bin/pip install gnosis \ - pyro \ - sslpsk \ - posix_spawn \ - && cd / \ - && hg clone http://dev.automforge.net/CanFestival-3 \ - && cd CanFestival-3 \ - && ./configure \ - \ - && cd / \ - && hg clone -r 24ef30a9bcee1e65b027be2c7f7a8d52c41a7479 https://bitbucket.org/automforge/matiec \ - && cd matiec \ - && autoreconf -i \ - && ./configure \ - && make \ - && make install \ - && mkdir /usr/lib/matiec \ - && cp -vR lib/* /usr/lib/matiec \ - && rm -rf /matiec \ - \ - && cd / \ - && hg clone https://bitbucket.org/mjsousa/modbus Modbus \ - && cd Modbus \ - && make \ - \ - && cd / \ - && svn checkout https://svn.code.sf.net/p/bacnet/code/trunk/bacnet-stack/ BACnet \ - && cd BACnet \ - && make MAKE_DEFINE='-fPIC' all \ - \ - && apt-get remove -y bison flex automake python-pip python3-pip libpython2.7-dev \ - && apt-get autoremove -y \ - && apt-get clean -y \ diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/tools/Docker/build_docker_image.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/tools/Docker/build_docker_image.sh Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,11 @@ +#!/bin/bash + +set -e + +echo "Building docker image" +cp -f ../../../requirements.txt requirements.txt +docker build \ + --build-arg UID=$(id -u) \ + --build-arg GID=$(id -g) \ + -t beremiz_sikuli . + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/tools/Docker/clean_docker_container.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/tools/Docker/clean_docker_container.sh Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,5 @@ +#!/bin/bash + +# delete container +docker rm beremiz_sikuli_current + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/tools/Docker/clean_docker_image.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/tools/Docker/clean_docker_image.sh Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,5 @@ +#!/bin/bash + +# delete image +docker rmi beremiz_sikuli + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/tools/Docker/create_docker_container.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/tools/Docker/create_docker_container.sh Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,30 @@ +#!/bin/bash + +set -e + +# source directory containing beremiz, matiec, etc.. +SRCDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd ../../../.. && pwd )" +echo "SOURCE direcory : $SRCDIR" + +# absolute path to test directory. ~/test if not given as only argument +TESTDIR=${1:-~/test} +mkdir -p $TESTDIR +echo "TEST direcory : $TESTDIR" + +UNAME=testing +UHOME=/home/$UNAME + +# define TESTDEBUG in env to enable dev-mode. This enables : +# - debug pasthrough for Xnest +# - VNC port passthrough +DEBUGARGS="-v /tmp/.X11-unix/X0:/tmp/.X11-unix/X0 -e DISPLAY=$DISPLAY -p 5900:5900" + +echo "Creating docker container" +docker create \ + --name beremiz_sikuli_current \ + -v $SRCDIR:$UHOME/src \ + -v $TESTDIR:$UHOME/test \ + `if [ "$TESTDEBUG" == "YES" ]; then echo $DEBUGARGS; fi` \ + -w $UHOME/test \ + -i -t beremiz_sikuli /bin/bash + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/tools/Docker/do_test_in_docker.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/tools/Docker/do_test_in_docker.sh Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,11 @@ +#!/bin/bash + +set -e + +CONTAINER=beremiz_sikuli_current + +docker stop $CONTAINER +docker start $CONTAINER +docker exec $CONTAINER bash -c "do_tests $1" +docker stop $CONTAINER + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/tools/Docker/enter_docker.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/tools/Docker/enter_docker.sh Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,5 @@ +#!/bin/bash + +CONTAINER=beremiz_sikuli_current + +docker start -i $CONTAINER diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/tools/Docker/enter_docker_as_root.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/tools/Docker/enter_docker_as_root.sh Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,7 @@ +#!/bin/bash + +CONTAINER=beremiz_sikuli_current + +docker start $CONTAINER +docker exec -i -t -u root $CONTAINER bash +docker stop $CONTAINER diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/tools/Docker/python2.7-wxpython/Dockerfile --- a/tests/tools/Docker/python2.7-wxpython/Dockerfile Wed Nov 29 11:54:56 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -# -# Dockerfile for wxPython3.0 running on python2.7 -# - -FROM python:2.7-stretch - -RUN set -xe \ - && apt-get update \ - && apt-get install -y --no-install-recommends python-wxgtk3.0 python-matplotlib \ - && apt-get install -y --no-install-recommends python-xvfbwrapper xvfb \ - && apt-get clean diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/tools/Docker/rebuild_docker.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/tools/Docker/rebuild_docker.sh Thu Dec 07 22:41:32 2023 +0100 @@ -0,0 +1,9 @@ +#!/bin/bash + +set -e + +./clean_docker_container.sh || true +./clean_docker_image.sh || true +./build_docker_image.sh +./create_docker_container.sh $1 + diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/tools/conftest.py --- a/tests/tools/conftest.py Wed Nov 29 11:54:56 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,78 +0,0 @@ -#!/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) 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. - - -from __future__ import absolute_import -import os -import sys - -# import pytest -# import xvfbwrapper - - -def init_environment(): - """Append module root directory to sys.path""" - try: - import Beremiz as _Beremiz - except ImportError: - sys.path.append( - os.path.abspath( - os.path.join( - os.path.dirname(__file__), '..', '..') - ) - ) - - -init_environment() - -# -# Something seems to be broken in Beremiz application, -# because after tests in test_application.py during Xvfb shutdown -# pytest returns error message: -# pytest: Fatal IO error 11 (Die Ressource ist zur Zeit nicht verfügbar) on X server :2821. -# -# As a result of that pytest returns code 1 as some tests were failed, -# but they aren't. -# -# To avoid this Xvfb is launched and killed not by pytest. -# $ Xvfb :42 -screen 0 1280x1024x24 & -# $ export DISPLAY=:42 -# $ pytest --timeout=10 ./tests/tools -# $ pkill -9 Xvfb -# -# TODO: find root of this problem. - - -# vdisplay = None -# -# @pytest.fixture(scope="session", autouse=True) -# def start_xvfb_server(request): -# global vdisplay -# vdisplay = xvfbwrapper.Xvfb(width=1280, height=720) -# vdisplay.start() -# request.addfinalizer(stop_xvfb_server) -# -# def stop_xvfb_server(): -# if vdisplay is not None: -# vdisplay.stop() diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/tools/run_python_tests.sh --- a/tests/tools/run_python_tests.sh Wed Nov 29 11:54:56 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ -#!/bin/sh - - - -cleanup() -{ - find $PYTEST_DIR -name '*.pyc' -delete -} - - - -print_help() -{ - echo "Usage: run_python_tests.sh [--on-local-xserver]" - echo "" - echo "--on-local-xserver" - echo " all tests are run on local X-server. " - echo " User can see test in action." - echo " Any interaction (mouse, keyboard) should be avoided" - echo " By default without arguments script runs pytest on virtual X serverf." - echo "" - - exit 1 -} - -main() -{ - LC_ALL=ru_RU.utf-8 - PYTEST_DIR=./tests/tools - - if [ ! -d $PYTEST_DIR ]; then - echo "Script should be run from top directory in repository" - exit 1; - fi - - use_xvfb=0 - if [ "$1" != "--on-local-xserver" ]; then - export DISPLAY=:42 - use_xvfb=1 - Xvfb $DISPLAY -screen 0 1280x1024x24 & - sleep 1 - fi - - - cleanup - - ret=0 - DELAY=400 - KILL_DELAY=$(($DELAY + 30)) - timeout -k $KILL_DELAY $DELAY pytest --timeout=10 ./tests/tools - ret=$? - - cleanup - - [ $use_xvfb = 1 ] && pkill -9 Xvfb - exit $ret -} - - -[ "$1" = "--help" -o "$1" = "-h" ] && print_help -main $@ diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/tools/test_CustomIntCtrl.py --- a/tests/tools/test_CustomIntCtrl.py Wed Nov 29 11:54:56 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,140 +0,0 @@ -#!/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) 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. - - -from __future__ import absolute_import -from __future__ import division -import unittest -import time - -import wx -import conftest -import controls.CustomIntCtrl - - -class TestCustomIntCtrl(unittest.TestCase): - def setUp(self): - self.app = wx.App() - self.frame = wx.Frame(None) - - def tearDown(self): - self.frame.Destroy() - wx.CallAfter(self.app.Exit) - self.app.MainLoop() - - def testMaxLimit(self): - """Test working upper bound""" - self.AddControls() - self.int_ctrl.SetValue(self.max_val + 100) - self.ProcessEvents() - - self.txt_ctrl.SetFocus() - self.ProcessEvents() - self.assertEqual(self.int_ctrl.GetValue(), self.max_val) - - def testMinLimit(self): - """Test working lower bound""" - self.AddControls() - self.int_ctrl.SetValue(self.min_val - 100) - self.ProcessEvents() - - self.txt_ctrl.SetFocus() - self.ProcessEvents() - - self.assertEqual(self.int_ctrl.GetValue(), self.min_val) - - def testCorrectValue(self): - """Test case if no limiting is necessary""" - self.AddControls() - val = (self.max_val + self.min_val) // 2 - self.int_ctrl.SetValue(val) - self.ProcessEvents() - - self.txt_ctrl.SetFocus() - self.ProcessEvents() - - self.assertEqual(self.int_ctrl.GetValue(), val) - - def testEventBinding(self): - """Test event sending after edit and bound checks are done""" - self.AddControls() - self.event_happend = False - - def EventHandler(event): - self.event_happend = True - event.Skip() - - self.int_ctrl.Bind(controls.CustomIntCtrl.EVT_CUSTOM_INT, EventHandler) - - val = (self.max_val + self.min_val) // 2 - - self.int_ctrl.SetValue(val) - self.ProcessEvents() - self.txt_ctrl.SetFocus() - - self.ProcessEvents() - self.txt_ctrl.SetFocus() - self.ProcessEvents() - - self.assertEqual(self.int_ctrl.GetValue(), val) - self.assertTrue(self.event_happend) - - def testLongNumbers(self): - """Test support of long integer""" - self.AddControls() - val = 40000000000 - self.int_ctrl.SetMax(val) - self.int_ctrl.SetValue(val) - self.ProcessEvents() - - self.txt_ctrl.SetFocus() - self.ProcessEvents() - - self.assertEqual(val, val) - - def ProcessEvents(self): - for dummy in range(0, 10): - wx.Yield() - time.sleep(0.01) - - def AddControls(self): - vs = wx.BoxSizer(wx.VERTICAL) - self.int_ctrl = controls.CustomIntCtrl(self.frame) - self.txt_ctrl = wx.TextCtrl(self.frame) - vs.Add(self.int_ctrl, 0, wx.ALIGN_CENTRE | wx.ALL, 5) - vs.Add(self.txt_ctrl, 0, wx.ALIGN_CENTRE | wx.ALL, 5) - self.frame.SetSizer(vs) - vs.Fit(self.frame) - self.frame.Show() - self.frame.Raise() - - self.min_val = 50 - self.max_val = 100 - self.int_ctrl.SetBounds(self.min_val, self.max_val) - self.ProcessEvents() - - -if __name__ == '__main__': - conftest.init_environment() - unittest.main() diff -r 7e17f7e02a2b -r 0b3ac94f494c tests/tools/test_application.py --- a/tests/tools/test_application.py Wed Nov 29 11:54:56 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,231 +0,0 @@ -#!/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) 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. - - -from __future__ import absolute_import -from __future__ import print_function -import os -import sys -import unittest -import time - -import six -import pytest -import wx -import ddt - -import conftest -import Beremiz -import PLCOpenEditor - - -class UserApplicationTest(unittest.TestCase): - def InstallExceptionHandler(self): - def handle_exception(e_type, e_value, e_traceback, exit=False): - # traceback.print_exception(e_type, e_value, e_traceback) - self.exc_info = [e_type, e_value, e_traceback] - self.exc_info = None - self.old_excepthook = sys.excepthook - sys.excepthook = handle_exception - - def StartApp(self): - self.app = None - - def FinishApp(self): - wx.CallAfter(self.app.frame.Close) - self.app.MainLoop() - self.app = None - - def setUp(self): - self.app = None - - def tearDown(self): - if self.app is not None and self.app.frame is not None: - self.FinishApp() - - def RunUIActions(self, actions): - for act in actions: - wx.CallAfter(*act) - self.ProcessEvents() - - def CheckForErrors(self): - if self.exc_info is not None: - # reraise catched previously exception - exc_type = self.exc_info[0] - exc_value = self.exc_info[1] - exc_traceback = self.exc_info[2] - six.reraise(exc_type, exc_value, exc_traceback) - - def ProcessEvents(self): - for dummy in range(0, 30): - self.CheckForErrors() - wx.Yield() - time.sleep(0.01) - - -@ddt.ddt -class BeremizApplicationTest(UserApplicationTest): - """Test Beremiz as whole application""" - - def StartApp(self): - self.app = Beremiz.BeremizIDELauncher() - # disable default exception handler in Beremiz - self.app.InstallExceptionHandler = lambda: None - self.InstallExceptionHandler() - self.app.handle_exception = sys.excepthook - self.app.PreStart() - self.ProcessEvents() - self.app.frame.Show() - self.ProcessEvents() - self.app.frame.ShowFullScreen(True) - self.ProcessEvents() - - def FinishApp(self): - wx.CallAfter(self.app.frame.Close) - self.app.MainLoop() - time.sleep(1) - self.app = None - - def GetSkippedProjectTreeItems(self): - """ - Returns the list of skipped items in the project tree. - - Beremiz test don't need to skip any elemnts in the project tree. - """ - return [] - - def OpenAllProjectElements(self): - """Open editor for every object in the project tree""" - self.app.frame.ProjectTree.ExpandAll() - self.ProcessEvents() - item = self.app.frame.ProjectTree.GetRootItem() - skip = self.GetSkippedProjectTreeItems() - tree_id = self.app.frame.ProjectTree.GetId() - while item is not None: - self.app.frame.ProjectTree.SelectItem(item, True) - self.ProcessEvents() - if item not in skip: - event = wx.lib.agw.customtreectrl.TreeEvent( - wx.lib.agw.customtreectrl.wxEVT_TREE_ITEM_ACTIVATED, - tree_id, item) - self.app.frame.OnProjectTreeItemActivated(event) - self.ProcessEvents() - item = self.app.frame.ProjectTree.GetNextVisible(item) - - def CheckTestProject(self, project): - sys.argv = ["", project] - self.StartApp() - self.OpenAllProjectElements() - user_actions = self.GetUserActions() - self.RunUIActions(user_actions) - self.FinishApp() - - def GetProjectPath(self, project): - return os.path.abspath(os.path.join(os.path.dirname(__file__), "..", project)) - - def GetUserActions(self): - """ - Returns list of user actions that will be executed - on every test project by testCheckProject test. - """ - user_actions = [ - [self.app.frame.SwitchFullScrMode, None], - [self.app.frame.SwitchFullScrMode, None], - [self.app.frame.CTR._Clean], - [self.app.frame.CTR._Build], - [self.app.frame.CTR._Connect], - [self.app.frame.CTR._Transfer], - [self.app.frame.CTR._Run], - [self.app.frame.CTR._Stop], - [self.app.frame.CTR._Disconnect], - [self.app.frame.CTR._Clean], - ] - return user_actions - - def testStartUp(self): - """Checks whether the app starts and finishes correctly""" - sys.argv = [""] - self.StartApp() - self.FinishApp() - - @ddt.data( - "first_steps", - "logging", - "traffic_lights", - "wxGlade", - "python", - "wiimote", - "wxHMI", - ) - @pytest.mark.timeout(30) - def testCheckProject(self, name): - """ - Checks that test PLC project can be open, - compiled and run on SoftPLC. - """ - project = self.GetProjectPath(name) - print("Testing example " + name) - self.CheckTestProject(project) - - -class PLCOpenEditorApplicationTest(BeremizApplicationTest): - """Test PLCOpenEditor as whole application""" - - def StartApp(self): - self.app = PLCOpenEditor.PLCOpenEditorApp() - # disable default exception handler in application - self.app.InstallExceptionHandler = lambda: None - self.InstallExceptionHandler() - self.app.Show() - self.ProcessEvents() - self.app.frame.ShowFullScreen(True) - self.ProcessEvents() - - def FinishApp(self): - wx.CallAfter(self.app.frame.Close) - self.app.MainLoop() - time.sleep(1) - self.app = None - - def GetSkippedProjectTreeItems(self): - """ - Returns the list of skipped items in the project tree. - - Root item opens dialog window for project settings. - To avoid code that handles closing dialog windows just skip this item. - """ - return [self.app.frame.ProjectTree.GetRootItem()] - - def GetUserActions(self): - return [] - - def GetProjectPath(self, project): - """Open PLC program in every Beremiz test project""" - project_dir = BeremizApplicationTest.GetProjectPath(self, project) - return os.path.join(project_dir, "plc.xml") - - -if __name__ == '__main__': - conftest.init_environment() - unittest.main() diff -r 7e17f7e02a2b -r 0b3ac94f494c util/BitmapLibrary.py --- a/util/BitmapLibrary.py Wed Nov 29 11:54:56 2023 +0100 +++ b/util/BitmapLibrary.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,7 +23,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import os import wx @@ -71,7 +71,7 @@ height = max(bmp1.GetHeight(), bmp2.GetHeight()) # Create bitmap with both icons - bmp = wx.EmptyBitmap(width, height) + bmp = wx.Bitmap(width, height) dc = wx.MemoryDC() dc.SelectObject(bmp) dc.Clear() diff -r 7e17f7e02a2b -r 0b3ac94f494c util/ExceptionHandler.py --- a/util/ExceptionHandler.py Wed Nov 29 11:54:56 2023 +0100 +++ b/util/ExceptionHandler.py Thu Dec 07 22:41:32 2023 +0100 @@ -24,7 +24,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import os import sys import time @@ -49,7 +49,7 @@ trcbck_lst.append(trcbck) # Allow clicking.... - cap = wx.Window_GetCapture() + cap = wx.Window.GetCapture() if cap: cap.ReleaseMouse() @@ -81,19 +81,25 @@ def get_last_traceback(tb): - while tb.tb_next: - tb = tb.tb_next + while True: + if not hasattr(tb, "tb_next"): + break + if tb.tb_next: + tb = tb.tb_next + else: + break + return tb def format_namespace(d, indent=' '): - return '\n'.join(['%s%s: %s' % (indent, k, repr(v)[:10000]) for k, v in d.iteritems()]) + return '\n'.join(['%s%s: %s' % (indent, k, repr(v)[:10000]) for k, v in d.items()]) ignored_exceptions = [] # a problem with a line in a module is only reported once per session -def AddExceptHook(app_version='[No version]'): +def AddExceptHook(app_version='[No version]', logf = None): def save_bug_report(e_type, e_value, e_traceback, bug_report_path, date): info = { @@ -111,7 +117,8 @@ 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 + # save locals at the level of the stack trace where the exception actually occurred + exception_locals = last_tb.tb_frame.f_locals if last_tb else {}; info['locals'] = format_namespace(exception_locals) if 'self' in exception_locals: try: @@ -122,16 +129,20 @@ if not os.path.exists(path): os.mkdir(path) output = open(bug_report_path, 'w') - lst = info.keys() + lst = list(info.keys()) lst.sort() for a in lst: - output.write(a + ":\n" + str(info[a]) + "\n\n") + line = a + ":\n" + str(info[a]) + "\n\n" + output.write(line) + if logf is not None: + logf.write(line) output.close() def handle_exception(e_type, e_value, e_traceback, exit=False): 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) + ex = (last_tb.tb_frame.f_code.co_filename if last_tb else "unknown", + last_tb.tb_frame.f_lineno if last_tb else None) if ex not in ignored_exceptions: ignored_exceptions.append(ex) date = time.ctime() diff -r 7e17f7e02a2b -r 0b3ac94f494c util/MiniTextControler.py --- a/util/MiniTextControler.py Wed Nov 29 11:54:56 2023 +0100 +++ b/util/MiniTextControler.py Thu Dec 07 22:41:32 2023 +0100 @@ -27,7 +27,7 @@ """ -from __future__ import absolute_import + import os diff -r 7e17f7e02a2b -r 0b3ac94f494c util/ProcessLogger.py --- a/util/ProcessLogger.py Wed Nov 29 11:54:56 2023 +0100 +++ b/util/ProcessLogger.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,7 +23,6 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import import os import sys import subprocess @@ -47,6 +46,7 @@ self.callback = callback self.endcallback = endcallback self.fd = fd + self.daemon = True def run(self): outchunk = None @@ -78,7 +78,7 @@ 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, output_encoding=None): + encoding=None, output_encoding=None, env=None): self.logger = logger if not isinstance(Command, list): self.Command_str = Command @@ -91,7 +91,7 @@ else: self.Command.append(word) else: - self.Command = Command + self.Command = [x if type(x)==str else x.decode() for x in Command] self.Command_str = subprocess.list2cmdline(self.Command) fsencoding = sys.getfilesystemencoding() @@ -99,8 +99,7 @@ if encoding is None: encoding = fsencoding - self.Command = [self.Command[0].encode(fsencoding)]+map( - lambda x: x.encode(encoding), self.Command[1:]) + self.Command = [self.Command[0].encode(fsencoding)]+[x.encode(encoding) for x in self.Command[1:]] self.finish_callback = finish_callback self.no_stdout = no_stdout @@ -123,7 +122,8 @@ "cwd": os.getcwd() if cwd is None else cwd, "stdin": subprocess.PIPE, "stdout": subprocess.PIPE, - "stderr": subprocess.PIPE + "stderr": subprocess.PIPE, + "env": env } if no_gui and os.name in ("nt", "ce"): @@ -142,7 +142,7 @@ if _debug and self.logger: self.logger.write("(DEBUG) launching:\n" + self.Command_str + "\n") - self.Proc = subprocess.Popen(self.Command, **popenargs) + self.Proc = subprocess.Popen(self.Command, encoding="utf-8", **popenargs) self.outt = outputThread( self.Proc, diff -r 7e17f7e02a2b -r 0b3ac94f494c util/TranslationCatalogs.py --- a/util/TranslationCatalogs.py Wed Nov 29 11:54:56 2023 +0100 +++ b/util/TranslationCatalogs.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,9 +23,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import import os -from six.moves import builtins +import builtins import wx diff -r 7e17f7e02a2b -r 0b3ac94f494c util/misc.py --- a/util/misc.py Wed Nov 29 11:54:56 2023 +0100 +++ b/util/misc.py Thu Dec 07 22:41:32 2023 +0100 @@ -27,7 +27,7 @@ """ -from __future__ import absolute_import + import os,sys import random from functools import reduce diff -r 7e17f7e02a2b -r 0b3ac94f494c util/paths.py --- a/util/paths.py Wed Nov 29 11:54:56 2023 +0100 +++ b/util/paths.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,19 +23,16 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import import os import sys -from builtins import str as text def AbsFile(file): if isinstance(file, str): - file = text(file, sys.getfilesystemencoding()) + file = str(file, sys.getfilesystemencoding()) return file def AbsDir(file): - file = AbsFile(file) return os.path.dirname(os.path.realpath(file)) @@ -55,3 +52,8 @@ """ return os.path.join(AbsParentDir(__file__, 2), name) +def Bpath(*names): + """ + Return path of files in Beremiz project + """ + return os.path.join(AbsParentDir(__file__, 1), *names) diff -r 7e17f7e02a2b -r 0b3ac94f494c version.py --- a/version.py Wed Nov 29 11:54:56 2023 +0100 +++ b/version.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,15 +23,12 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import unicode_literals -import subprocess + + + import os -import util.paths as paths - - def GetCommunityHelpMsg(): return _( "The best place to ask questions about Beremiz/PLCOpenEditor\n" @@ -45,54 +42,25 @@ ) -def GetAppRevision(): - rev = None - app_dir = paths.AbsDir(__file__) - try: - pipe = subprocess.Popen( - ["hg", "id", "-i"], - stdout=subprocess.PIPE, - cwd=app_dir - ) - rev = pipe.communicate()[0] - if pipe.returncode != 0: - rev = None - except Exception: - pass +def GetAboutDialogInfo(info): - # 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")) - rev = f.readline() - except Exception: - pass - return rev - - -def GetAboutDialogInfo(): - import wx - info = wx.AboutDialogInfo() - - info.Name = "Beremiz" info.Version = app_version info.Copyright = "" + info.Copyright += "(C) 2006-2023 Edouard Tisserant\n" + info.Copyright += "(C) 2003-2023 Mario de Sousa\n" + info.Copyright += "(C) 2022-2023 GP Orcullo\n" info.Copyright += "(C) 2016-2018 Andrey Skvortsov\n" - info.Copyright += "(C) 2008-2018 Eduard Tisserant\n" - info.Copyright += "(C) 2008-2015 Laurent Bessard" + info.Copyright += "(C) 2006-2013 Laurent Bessard\n" 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 = ( + "Edouard Tisserant ", + "Mario de Sousa ", + "GP Orcullo ", "Andrey Skvortsov ", "Sergey Surkov ", - "Edouard Tisserant ", "Laurent Bessard ") info.License = ( @@ -112,14 +80,12 @@ ) # read license file - path = paths.AbsDir(__file__) - license_path = os.path.join(path, "COPYING") + license_path = os.path.join( + os.path.dirname(os.path.realpath(__file__)), "COPYING") 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 = ( "Basque", "José Miguel Andonegi , 2019", @@ -135,6 +101,7 @@ " Yiwei Yan <523136664@qq.com>, 2018", " Ji Wang <2485567515@qq.com>, 2019", " 珂 曾 <15627997@qq.com>, 2019", + " Gastonfeng, 2019", "", "Dutch (Netherlands)", @@ -218,7 +185,7 @@ return info -app_version = "1.2" -rev = GetAppRevision() -if rev is not None: - app_version = app_version + "-" + rev.rstrip() +app_version = "1.4-beta2" + +if __name__ == "__main__": + print(app_version) diff -r 7e17f7e02a2b -r 0b3ac94f494c wxglade_hmi/__init__.py --- a/wxglade_hmi/__init__.py Wed Nov 29 11:54:56 2023 +0100 +++ b/wxglade_hmi/__init__.py Thu Dec 07 22:41:32 2023 +0100 @@ -22,5 +22,5 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + from wxglade_hmi.wxglade_hmi import * diff -r 7e17f7e02a2b -r 0b3ac94f494c wxglade_hmi/wxglade_hmi.py --- a/wxglade_hmi/wxglade_hmi.py Wed Nov 29 11:54:56 2023 +0100 +++ b/wxglade_hmi/wxglade_hmi.py Thu Dec 07 22:41:32 2023 +0100 @@ -24,7 +24,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import + import os import sys import shutil diff -r 7e17f7e02a2b -r 0b3ac94f494c xmlclass/__init__.py --- a/xmlclass/__init__.py Wed Nov 29 11:54:56 2023 +0100 +++ b/xmlclass/__init__.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,7 +23,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Package initialisation -from __future__ import absolute_import + from .xmlclass import (ClassFactory, GenerateParser, DefaultElementClass, diff -r 7e17f7e02a2b -r 0b3ac94f494c xmlclass/xmlclass.py --- a/xmlclass/xmlclass.py Wed Nov 29 11:54:56 2023 +0100 +++ b/xmlclass/xmlclass.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,8 +23,6 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import print_function import os import re import datetime @@ -32,10 +30,7 @@ from xml.dom import minidom from xml.sax.saxutils import unescape from collections import OrderedDict -from builtins import str as text - -from six import string_types -from six.moves import xrange + from lxml import etree @@ -107,7 +102,7 @@ [ SYNTAXELEMENT, SYNTAXATTRIBUTE, SIMPLETYPE, COMPLEXTYPE, COMPILEDCOMPLEXTYPE, ATTRIBUTESGROUP, ELEMENTSGROUP, ATTRIBUTE, ELEMENT, CHOICE, ANY, TAG, CONSTRAINT, -] = range(13) +] = list(range(13)) def NotSupportedYet(type): @@ -128,7 +123,7 @@ """ first = indent * 2 second = first + len(balise) + 1 - return u'\t'.expandtabs(first), u'\t'.expandtabs(second) + return '\t'.expandtabs(first), '\t'.expandtabs(second) def GetAttributeValue(attr, extract=True): @@ -141,14 +136,14 @@ if not extract: return attr if len(attr.childNodes) == 1: - return text(unescape(attr.childNodes[0].data)) + return str(unescape(attr.childNodes[0].data)) else: # content is a CDATA - txt = u'' + txt = '' for node in attr.childNodes: - if not (node.nodeName == "#text" and node.data.strip() == u''): - txt += text(unescape(node.data)) - return text + if not (node.nodeName == "#text" and node.data.strip() == ''): + txt += str(unescape(node.data)) + return txt def GetNormalizedString(attr, extract=True): @@ -576,7 +571,7 @@ "extract": ExtractAny, "generate": GenerateAny, "initial": InitialAny, - "check": lambda x: isinstance(x, (string_types, etree.ElementBase)) + "check": lambda x: isinstance(x, (str, etree.ElementBase)) } @@ -598,17 +593,21 @@ else: return "" + def Initial(): + p = etree.Element(infos["name"]) + return p + return { "type": TAG, "extract": ExtractTag, "generate": GenerateTag, - "initial": lambda: None, + "initial": Initial, "check": lambda x: x is None or infos["minOccurs"] == 0 and x } def FindTypeInfos(factory, infos): - if isinstance(infos, string_types): + if isinstance(infos, str): namespace, name = DecomposeQualifiedName(infos) return factory.GetQualifiedNameInfos(name, namespace) return infos @@ -630,7 +629,7 @@ if infos["type"] != ANY: DefaultElementClass.__setattr__(value, "tag", element_name) return value - return [initial_value() for dummy in xrange(infos["minOccurs"])] + return [initial_value() for dummy in range(infos["minOccurs"])] else: return [] @@ -687,13 +686,13 @@ 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())) + choices_xpath = "|".join([prefix + x for x in list(choices_dict.keys())]) def GetContentInitial(): content_name, infos = choices[0] if content_name == "sequence": content_value = [] - for dummy in xrange(infos["minOccurs"]): + for dummy in range(infos["minOccurs"]): for element_infos in infos["elements"]: content_value.extend(GetElementInitialValue(factory, element_infos)) else: @@ -797,7 +796,7 @@ if namespace is None: if name in self.Namespaces[self.SchemaNamespace]: return self.Namespaces[self.SchemaNamespace][name] - for space, elements in self.Namespaces.iteritems(): + for space, elements in self.Namespaces.items(): if space != self.SchemaNamespace and name in elements: return elements[name] parts = name.split("_", 1) @@ -839,7 +838,7 @@ if namespace is None: if name in self.Namespaces[self.SchemaNamespace]: return name, None - for space, elements in self.Namespaces.items(): + for space, elements in list(self.Namespaces.items()): if space != self.SchemaNamespace and name in elements: return name, None parts = name.split("_", 1) @@ -879,25 +878,27 @@ def ExtractNodeAttrs(self, element_name, node, valid_attrs): attrs = {} - for qualified_name, attr in node._attrs.items(): - namespace, name = DecomposeQualifiedName(qualified_name) - if name in valid_attrs: - infos = self.GetQualifiedNameInfos(name, namespace) - if infos["type"] != SYNTAXATTRIBUTE: - raise ValueError("\"%s\" can't be a member attribute!" % name) - elif name in attrs: - raise ValueError("\"%s\" attribute has been twice!" % name) - elif element_name in infos["extract"]: - attrs[name] = infos["extract"][element_name](attr) + if node._attrs: + for qualified_name, attr in list(node._attrs.items()): + namespace, name = DecomposeQualifiedName(qualified_name) + if name in valid_attrs: + infos = self.GetQualifiedNameInfos(name, namespace) + if infos["type"] != SYNTAXATTRIBUTE: + raise ValueError("\"%s\" can't be a member attribute!" % name) + elif name in attrs: + raise ValueError("\"%s\" attribute has been twice!" % name) + elif element_name in infos["extract"]: + attrs[name] = infos["extract"][element_name](attr) + else: + attrs[name] = infos["extract"]["default"](attr) + elif namespace == "xmlns": + infos = self.GetQualifiedNameInfos("anyURI", self.SchemaNamespace) + value = infos["extract"](attr) + self.DefinedNamespaces[value] = name + self.NSMAP[name] = value else: - attrs[name] = infos["extract"]["default"](attr) - elif namespace == "xmlns": - infos = self.GetQualifiedNameInfos("anyURI", self.SchemaNamespace) - value = infos["extract"](attr) - self.DefinedNamespaces[value] = name - self.NSMAP[name] = value - else: - raise ValueError("Invalid attribute \"%s\" for member \"%s\"!" % (qualified_name, node.nodeName)) + raise ValueError("Invalid attribute \"%s\" for member \"%s\"!" % + (qualified_name, node.nodeName)) for attr in valid_attrs: if attr not in attrs and \ attr in self.Namespaces[self.SchemaNamespace] and \ @@ -964,7 +965,7 @@ def AddToLookupClass(self, name, parent, typeinfos): lookup_name = self.etreeNamespaceFormat % name - if isinstance(typeinfos, string_types): + if isinstance(typeinfos, str): self.AddEquivalentClass(name, typeinfos) typeinfos = self.etreeNamespaceFormat % typeinfos lookup_classes = self.ComputedClassesLookUp.get(lookup_name) @@ -982,7 +983,7 @@ self.ComputedClassesLookUp[lookup_name] = lookup_classes def ExtractTypeInfos(self, name, parent, typeinfos): - if isinstance(typeinfos, string_types): + if isinstance(typeinfos, str): namespace, type_name = DecomposeQualifiedName(typeinfos) infos = self.GetQualifiedNameInfos(type_name, namespace) if name != "base": @@ -993,13 +994,13 @@ if infos["type"] == COMPLEXTYPE: type_name, parent = self.SplitQualifiedName(type_name, namespace) result = self.CreateClass(type_name, parent, infos) - if result is not None and not isinstance(result, string_types): + if result is not None and not isinstance(result, str): self.Namespaces[self.TargetNamespace][result["name"]] = result return result elif infos["type"] == ELEMENT and infos["elmt_type"]["type"] == COMPLEXTYPE: type_name, parent = self.SplitQualifiedName(type_name, namespace) result = self.CreateClass(type_name, parent, infos["elmt_type"]) - if result is not None and not isinstance(result, string_types): + if result is not None and not isinstance(result, str): self.Namespaces[self.TargetNamespace][result["name"]] = result return result else: @@ -1012,28 +1013,28 @@ def GetEquivalentParents(self, parent): return reduce(lambda x, y: x + y, [[p] + self.GetEquivalentParents(p) - for p in self.EquivalentClassesParent.get(parent, {}).keys()], []) + for p in list(self.EquivalentClassesParent.get(parent, {}).keys())], []) def CreateClasses(self): """ Method that generates the classes """ self.ParseSchema() - for name, infos in self.Namespaces[self.TargetNamespace].items(): + for name, infos in list(self.Namespaces[self.TargetNamespace].items()): if infos["type"] == ELEMENT: - if not isinstance(infos["elmt_type"], string_types) and \ + if not isinstance(infos["elmt_type"], str) and \ infos["elmt_type"]["type"] == COMPLEXTYPE: self.ComputeAfter.append((name, None, infos["elmt_type"], True)) while len(self.ComputeAfter) > 0: result = self.CreateClass(*self.ComputeAfter.pop(0)) - if result is not None and not isinstance(result, string_types): + if result is not None and not isinstance(result, str): self.Namespaces[self.TargetNamespace][result["name"]] = result elif infos["type"] == COMPLEXTYPE: self.ComputeAfter.append((name, None, infos)) while len(self.ComputeAfter) > 0: result = self.CreateClass(*self.ComputeAfter.pop(0)) if result is not None and \ - not isinstance(result, string_types): + not isinstance(result, str): self.Namespaces[self.TargetNamespace][result["name"]] = result elif infos["type"] == ELEMENTSGROUP: elements = [] @@ -1042,18 +1043,18 @@ elif "choices" in infos: elements = infos["choices"] for element in elements: - if not isinstance(element["elmt_type"], string_types) and \ + if not isinstance(element["elmt_type"], str) and \ element["elmt_type"]["type"] == COMPLEXTYPE: self.ComputeAfter.append((element["name"], infos["name"], element["elmt_type"])) while len(self.ComputeAfter) > 0: result = self.CreateClass(*self.ComputeAfter.pop(0)) if result is not None and \ - not isinstance(result, string_types): + not isinstance(result, str): self.Namespaces[self.TargetNamespace][result["name"]] = result - for name, parents in self.ComputedClassesLookUp.iteritems(): + for name, parents in self.ComputedClassesLookUp.items(): if isinstance(parents, dict): - computed_classes = parents.items() + computed_classes = list(parents.items()) elif parents[1] is not None: computed_classes = [(self.etreeNamespaceFormat % parents[1], parents[0])] else: @@ -1097,7 +1098,7 @@ if self.FileName is not None: classinfos["base"] = self.ComputedClasses[self.FileName].get(result["name"], None) if classinfos["base"] is None: - for filename, classes in self.ComputedClasses.iteritems(): + for filename, classes in self.ComputedClasses.items(): if filename != self.FileName: classinfos["base"] = classes.get(result["name"], None) if classinfos["base"] is not None: @@ -1193,12 +1194,12 @@ """ Method that print the classes generated """ - items = self.ComputedClasses.items() + items = list(self.ComputedClasses.items()) items.sort() if self.FileName is not None: for filename, classes in items: print("File '%s':" % filename) - class_items = classes.items() + class_items = list(classes.items()) class_items.sort() for classname, xmlclass in class_items: print("%s: %s" % (classname, str(xmlclass))) @@ -1207,7 +1208,7 @@ print("%s: %s" % (classname, str(xmlclass))) def PrintClassNames(self): - classnames = self.XMLClassDefinitions.keys() + classnames = list(self.XMLClassDefinitions.keys()) classnames.sort() for classname in classnames: print(classname) @@ -1315,9 +1316,7 @@ if element_infos["maxOccurs"] == "unbounded" or element_infos["maxOccurs"] > 1: 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) + return [element_infos["elmt_type"]["extract"](value.text, extract=False) for value in values] return values else: value = self.find(element_name) @@ -1371,13 +1370,11 @@ self.remove(element) if value is not None: - element_idx = elements.keys().index(name) + element_idx = list(elements.keys()).index(name) if element_idx > 0: - previous_elements_xpath = "|".join(map( - lambda x: prefix + x + previous_elements_xpath = "|".join([prefix + x if x != "content" - else elements["content"]["elmt_type"]["choices_xpath"].path, - elements.keys()[:element_idx])) + else elements["content"]["elmt_type"]["choices_xpath"].path for x in list(elements.keys())[:element_idx]]) insertion_point = len(self.xpath(previous_elements_xpath, namespaces=factory.NSMAP)) else: @@ -1450,14 +1447,14 @@ parts = path.split(".", 1) if parts[0] in attributes: if len(parts) != 1: - raise ValueError("Wrong path!") + raise ValueError("Wrong path: "+path) attr_type = gettypeinfos(attributes[parts[0]]["attr_type"]["basename"], attributes[parts[0]]["attr_type"]["facets"]) value = getattr(self, parts[0], "") elif parts[0] in elements: if elements[parts[0]]["elmt_type"]["type"] == SIMPLETYPE: if len(parts) != 1: - raise ValueError("Wrong path!") + raise ValueError("Wrong path: "+path) attr_type = gettypeinfos(elements[parts[0]]["elmt_type"]["basename"], elements[parts[0]]["elmt_type"]["facets"]) value = getattr(self, parts[0], "") @@ -1466,7 +1463,7 @@ else: attr = getattr(self, parts[0], None) if attr is None: - raise ValueError("Wrong path!") + raise ValueError("Wrong path: "+path) if len(parts) == 1: return attr.getElementInfos(parts[0]) else: @@ -1477,13 +1474,13 @@ elif "base" in classinfos: classinfos["base"].getElementInfos(name, path) else: - raise ValueError("Wrong path!") + raise ValueError("Wrong path: "+path) else: if not derived: children.extend(self.getElementAttributes()) if "base" in classinfos: children.extend(classinfos["base"].getElementInfos(self, name, derived=True)["children"]) - for element_name, element in elements.items(): + for element_name, element in list(elements.items()): if element["minOccurs"] == 0: use = "optional" if element_name == "content" and element["type"] == CHOICE: @@ -1519,7 +1516,7 @@ parts = path.split(".", 1) if parts[0] in attributes: if len(parts) != 1: - raise ValueError("Wrong path!") + raise ValueError("Wrong path: "+path) if attributes[parts[0]]["attr_type"]["basename"] == "boolean": setattr(self, parts[0], value) elif attributes[parts[0]]["use"] == "optional" and value == None: @@ -1534,7 +1531,7 @@ elif parts[0] in elements: if elements[parts[0]]["elmt_type"]["type"] == SIMPLETYPE: if len(parts) != 1: - raise ValueError("Wrong path!") + raise ValueError("Wrong path: "+path) if elements[parts[0]]["elmt_type"]["basename"] == "boolean": setattr(self, parts[0], value) elif attributes[parts[0]]["minOccurs"] == 0 and value == "": @@ -1583,7 +1580,7 @@ if element["type"] != CHOICE: initial = GetElementInitialValue(factory, element) if initial is not None: - map(self.append, initial) + list(map(self.append, initial)) return initMethod @@ -1736,8 +1733,10 @@ 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') - + return NAMESPACE_PATTERN.sub("", etree.tostring(self, encoding='unicode')) + + def getElementInfos(self, name, path=None, derived=False): + return {"name": name, "type": TAG, "value": None, "use": None, "children": []} class XMLElementClassLookUp(etree.PythonElementClassLookup): @@ -1750,12 +1749,12 @@ def GetElementClass(self, element_tag, parent_tag=None, default=DefaultElementClass): element_class = self.LookUpClasses.get(element_tag, (default, None)) if not isinstance(element_class, dict): - if isinstance(element_class[0], string_types): + if isinstance(element_class[0], str): 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, string_types): + if isinstance(element_with_parent_class, str): return self.GetElementClass(element_with_parent_class, default=default) return element_with_parent_class @@ -1817,7 +1816,7 @@ "%s " % etree.QName(child.tag).localname for child in element]) for possible_class in element_class: - if isinstance(possible_class, string_types): + if isinstance(possible_class, str): possible_class = self.GetElementClass(possible_class) if possible_class.StructurePattern.match(children) is not None: return possible_class @@ -1836,7 +1835,7 @@ if targetNamespace is not None: self.RootNSMAP = { name if targetNamespace != uri else None: uri - for name, uri in namespaces.iteritems()} + for name, uri in namespaces.items()} else: self.RootNSMAP = namespaces self.BaseClass = base_class @@ -1847,7 +1846,7 @@ self.ClassLookup = class_lookup def LoadXMLString(self, xml_string): - tree = etree.fromstring(xml_string, self) + tree = etree.fromstring(xml_string.encode(), self) if not self.XSDSchema.validate(tree): error = self.XSDSchema.error_log.last_error return tree, (error.line, error.message) @@ -1939,13 +1938,13 @@ 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] + BaseClass = [(name, XSDclass) for name, XSDclass in list(ComputedClasses.items()) if XSDclass.IsBaseClass] parser.initMembers( factory.NSMAP, factory.etreeNamespaceFormat, BaseClass[0] if len(BaseClass) == 1 else None, - etree.XMLSchema(etree.fromstring(xsdstring))) + etree.XMLSchema(etree.fromstring(xsdstring.encode()))) class_lookup = XMLElementClassLookUp(factory.ComputedClassesLookUp) parser.set_element_class_lookup(class_lookup) diff -r 7e17f7e02a2b -r 0b3ac94f494c xmlclass/xsdschema.py --- a/xmlclass/xsdschema.py Wed Nov 29 11:54:56 2023 +0100 +++ b/xmlclass/xsdschema.py Thu Dec 07 22:41:32 2023 +0100 @@ -23,16 +23,11 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from __future__ import absolute_import -from __future__ import print_function import os import re import datetime from types import FunctionType from xml.dom import minidom -from future.builtins import round -from six import string_types -from past.builtins import long from xmlclass.xmlclass import * @@ -77,9 +72,9 @@ 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"]) +NUMBER_FACETS = GenerateDictFacets(list(DEFAULT_FACETS.keys()) + ["maxInclusive", "maxExclusive", "minInclusive", "minExclusive"]) +DECIMAL_FACETS = GenerateDictFacets(list(NUMBER_FACETS.keys()) + ["totalDigits", "fractionDigits"]) +STRING_FACETS = GenerateDictFacets(list(DEFAULT_FACETS.keys()) + ["length", "minLength", "maxLength"]) ALL_FACETS = ["pattern", "whiteSpace", "enumeration", "maxInclusive", "maxExclusive", "minInclusive", "minExclusive", "totalDigits", @@ -170,7 +165,7 @@ if typeinfos["type"] in ["restriction", "extension"]: # Search for base type definition - if isinstance(typeinfos["base"], string_types): + if isinstance(typeinfos["base"], str): basetypeinfos = factory.FindSchemaElement(typeinfos["base"], SIMPLETYPE) if basetypeinfos is None: raise "\"%s\" isn't defined!" % typeinfos["base"] @@ -204,7 +199,7 @@ if len(facets) == 0: facets[facettype] = ([value], False) continue - elif facets.keys() == [facettype]: + elif list(facets.keys()) == [facettype]: facets[facettype][0].append(value) continue else: @@ -302,14 +297,14 @@ facets[facettype] = (value, facet.get("fixed", False)) # Report not redefined facet from base type to new created type - for facettype, facetvalue in basetypeinfos["facets"].items(): + for facettype, facetvalue in list(basetypeinfos["facets"].items()): 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) - for facetname, (facetvalue, _facetfixed) in facets.items(): + for facetname, (facetvalue, _facetfixed) in list(facets.items()): if facetvalue is not None: if facetname == "enumeration" and value not in facetvalue: raise ValueError("\"%s\" not in enumerated values" % value) @@ -328,7 +323,7 @@ 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))) + model = re.compile("(?:%s)?$" % "|".join(["(?:%s)" % x for x in facetvalue])) result = model.match(value) if result is None: if len(facetvalue) > 1: @@ -343,7 +338,7 @@ return value def CheckSimpleTypeValue(value): - for facetname, (facetvalue, _facetfixed) in facets.items(): + for facetname, (facetvalue, _facetfixed) in list(facets.items()): if facetvalue is not None: if facetname == "enumeration" and value not in facetvalue: return False @@ -362,7 +357,7 @@ elif facetname == "maxExclusive" and value >= facetvalue: return False elif facetname == "pattern": - model = re.compile("(?:%s)?$" % "|".join(map(lambda x: "(?:%s)" % x, facetvalue))) + model = re.compile("(?:%s)?$" % "|".join(["(?:%s)" % x for x in facetvalue])) result = model.match(value) if result is None: if len(facetvalue) > 1: @@ -372,7 +367,7 @@ return True def SimpleTypeInitialValue(): - for facetname, (facetvalue, _facetfixed) in facets.items(): + for facetname, (facetvalue, _facetfixed) in list(facets.items()): if facetvalue is not None: if facetname == "enumeration": return facetvalue[0] @@ -394,7 +389,7 @@ elif typeinfos["type"] == "list": # Search for item type definition - if isinstance(typeinfos["itemType"], string_types): + if isinstance(typeinfos["itemType"], str): itemtypeinfos = factory.FindSchemaElement(typeinfos["itemType"], SIMPLETYPE) if itemtypeinfos is None: raise "\"%s\" isn't defined!" % typeinfos["itemType"] @@ -440,7 +435,7 @@ # Search for member types definition membertypesinfos = [] for membertype in typeinfos["memberTypes"]: - if isinstance(membertype, string_types): + if isinstance(membertype, str): infos = factory.FindSchemaElement(membertype, SIMPLETYPE) if infos is None: raise ValueError("\"%s\" isn't defined!" % membertype) @@ -514,8 +509,8 @@ attrnames = {} if base is not None: basetypeinfos = factory.FindSchemaElement(base) - if not isinstance(basetypeinfos, string_types) and basetypeinfos["type"] == COMPLEXTYPE: - attrnames = dict(map(lambda x: (x["name"], True), basetypeinfos["attributes"])) + if not isinstance(basetypeinfos, str) and basetypeinfos["type"] == COMPLEXTYPE: + attrnames = dict([(x["name"], True) for x in basetypeinfos["attributes"]]) for element in elements: if element["type"] == ATTRIBUTE: @@ -815,7 +810,7 @@ raise ValueError("Only group composed of \"choice\" can be referenced in \"choice\" element!") choices_tmp = [] for choice in elmtgroup["choices"]: - if not isinstance(choice["elmt_type"], string_types) and choice["elmt_type"]["type"] == COMPLEXTYPE: + if not isinstance(choice["elmt_type"], str) and choice["elmt_type"]["type"] == COMPLEXTYPE: elmt_type = "%s_%s" % (elmtgroup["name"], choice["name"]) if factory.TargetNamespace is not None: elmt_type = "%s:%s" % (factory.TargetNamespace, elmt_type) @@ -849,7 +844,7 @@ 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"], string_types) and element["elmt_type"]["type"] == COMPLEXTYPE: + if not isinstance(element["elmt_type"], str) and element["elmt_type"]["type"] == COMPLEXTYPE: elmt_type = "%s_%s" % (elmtgroup["name"], element["name"]) if factory.TargetNamespace is not None: elmt_type = "%s:%s" % (factory.TargetNamespace, elmt_type) @@ -996,7 +991,7 @@ elif isinstance(schema, dict): if not isinstance(reference, dict) or len(schema) != len(reference): return False - for name, value in schema.items(): + for name, value in list(schema.items()): ref_value = reference.get(name, None) if ref_value is None and value is not None: return False @@ -1061,7 +1056,7 @@ if child.nodeType == self.Document.ELEMENT_NODE: schema = child break - for qualified_name, attr in schema._attrs.items(): + for qualified_name, attr in list(schema._attrs.items()): namespace, name = DecomposeQualifiedName(qualified_name) if namespace == "xmlns": value = GetAttributeValue(attr) @@ -2215,7 +2210,7 @@ "facets": STRING_FACETS, "generate": GenerateSimpleTypeXMLText(lambda x: x), "initial": lambda: "", - "check": lambda x: isinstance(x, string_types)}, + "check": lambda x: isinstance(x, str)}, "normalizedString": { "type": SIMPLETYPE, @@ -2224,7 +2219,7 @@ "facets": STRING_FACETS, "generate": GenerateSimpleTypeXMLText(lambda x: x), "initial": lambda: "", - "check": lambda x: isinstance(x, string_types) + "check": lambda x: isinstance(x, str) }, "token": { @@ -2234,7 +2229,7 @@ "facets": STRING_FACETS, "generate": GenerateSimpleTypeXMLText(lambda x: x), "initial": lambda: "", - "check": lambda x: isinstance(x, string_types) + "check": lambda x: isinstance(x, str) }, "base64Binary": { @@ -2244,7 +2239,7 @@ "facets": STRING_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: 0, - "check": lambda x: isinstance(x, (int, long)) + "check": lambda x: isinstance(x, int) }, "hexBinary": { @@ -2254,7 +2249,7 @@ "facets": STRING_FACETS, "generate": GenerateSimpleTypeXMLText(lambda x: ("%."+str(int(round(len("%X" % x)/2.)*2))+"X") % x), "initial": lambda: 0, - "check": lambda x: isinstance(x, (int, long)) + "check": lambda x: isinstance(x, int) }, "integer": { @@ -2434,7 +2429,7 @@ "facets": NUMBER_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: "", - "check": lambda x: isinstance(x, string_types) + "check": lambda x: isinstance(x, str) }, "dateTime": { @@ -2474,7 +2469,7 @@ "facets": NUMBER_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: "", - "check": lambda x: isinstance(x, string_types) + "check": lambda x: isinstance(x, str) }, "gYearMonth": { @@ -2484,7 +2479,7 @@ "facets": NUMBER_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: "", - "check": lambda x: isinstance(x, string_types) + "check": lambda x: isinstance(x, str) }, "gMonth": { @@ -2494,7 +2489,7 @@ "facets": NUMBER_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: "", - "check": lambda x: isinstance(x, string_types) + "check": lambda x: isinstance(x, str) }, "gMonthDay": { @@ -2504,7 +2499,7 @@ "facets": NUMBER_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: "", - "check": lambda x: isinstance(x, string_types) + "check": lambda x: isinstance(x, str) }, "gDay": { @@ -2514,7 +2509,7 @@ "facets": NUMBER_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: "", - "check": lambda x: isinstance(x, string_types) + "check": lambda x: isinstance(x, str) }, "Name": { @@ -2524,7 +2519,7 @@ "facets": STRING_FACETS, "generate": GenerateSimpleTypeXMLText(lambda x: x), "initial": lambda: "", - "check": lambda x: isinstance(x, string_types) + "check": lambda x: isinstance(x, str) }, "QName": { @@ -2534,7 +2529,7 @@ "facets": STRING_FACETS, "generate": GenerateSimpleTypeXMLText(lambda x: x), "initial": lambda: "", - "check": lambda x: isinstance(x, string_types) + "check": lambda x: isinstance(x, str) }, "NCName": { @@ -2544,7 +2539,7 @@ "facets": STRING_FACETS, "generate": GenerateSimpleTypeXMLText(lambda x: x), "initial": lambda: "", - "check": lambda x: isinstance(x, string_types) + "check": lambda x: isinstance(x, str) }, "anyURI": { @@ -2554,7 +2549,7 @@ "facets": STRING_FACETS, "generate": GenerateSimpleTypeXMLText(lambda x: x), "initial": lambda: "", - "check": lambda x: isinstance(x, string_types) + "check": lambda x: isinstance(x, str) }, "language": { @@ -2564,7 +2559,7 @@ "facets": STRING_FACETS, "generate": GenerateSimpleTypeXMLText(lambda x: x), "initial": lambda: "en", - "check": lambda x: isinstance(x, string_types) + "check": lambda x: isinstance(x, str) }, "ID": { @@ -2574,7 +2569,7 @@ "facets": STRING_FACETS, "generate": GenerateSimpleTypeXMLText(lambda x: x), "initial": lambda: "", - "check": lambda x: isinstance(x, string_types) + "check": lambda x: isinstance(x, str) }, "IDREF": { @@ -2584,7 +2579,7 @@ "facets": STRING_FACETS, "generate": GenerateSimpleTypeXMLText(lambda x: x), "initial": lambda: "", - "check": lambda x: isinstance(x, string_types) + "check": lambda x: isinstance(x, str) }, "IDREFS": { @@ -2594,7 +2589,7 @@ "facets": STRING_FACETS, "generate": GenerateSimpleTypeXMLText(lambda x: x), "initial": lambda: "", - "check": lambda x: isinstance(x, string_types) + "check": lambda x: isinstance(x, str) }, "ENTITY": { @@ -2604,7 +2599,7 @@ "facets": STRING_FACETS, "generate": GenerateSimpleTypeXMLText(lambda x: x), "initial": lambda: "", - "check": lambda x: isinstance(x, string_types) + "check": lambda x: isinstance(x, str) }, "ENTITIES": { @@ -2614,7 +2609,7 @@ "facets": STRING_FACETS, "generate": GenerateSimpleTypeXMLText(lambda x: x), "initial": lambda: "", - "check": lambda x: isinstance(x, string_types) + "check": lambda x: isinstance(x, str) }, "NOTATION": { @@ -2624,7 +2619,7 @@ "facets": STRING_FACETS, "generate": GenerateSimpleTypeXMLText(lambda x: x), "initial": lambda: "", - "check": lambda x: isinstance(x, string_types) + "check": lambda x: isinstance(x, str) }, "NMTOKEN": { @@ -2634,7 +2629,7 @@ "facets": STRING_FACETS, "generate": GenerateSimpleTypeXMLText(lambda x: x), "initial": lambda: "", - "check": lambda x: isinstance(x, string_types) + "check": lambda x: isinstance(x, str) }, "NMTOKENS": { @@ -2644,7 +2639,7 @@ "facets": STRING_FACETS, "generate": GenerateSimpleTypeXMLText(lambda x: x), "initial": lambda: "", - "check": lambda x: isinstance(x, string_types) + "check": lambda x: isinstance(x, str) }, # Complex Types