--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.github/workflows/run_tests_in_docker.yml Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,68 @@
+name: Docker Image CI
+
+on:
+ push:
+ branches: [ wxPython4 ]
+
+jobs:
+
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ path: beremiz
+
+ - uses: actions/checkout@v3
+ with:
+ repository: beremiz/matiec
+ ref: 2a25f4dbf4e2b1e017a3a583db7dede4771fe523
+ path: matiec
+
+ - uses: actions/checkout@v3
+ with:
+ repository: open62541/open62541
+ ref: v1.3.3
+ path: open62541
+ submodules: recursive
+
+ - name: Cache docker image
+ id: cache-docker
+ uses: actions/cache@v3
+ env:
+ cache-name: cache-docker
+ with:
+ path: /tmp/latest.tar
+ key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('beremiz/tests/tools/Docker/beremiz-sikuli') }}
+
+ - if: ${{ steps.cache-docker.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.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
--- a/.hgignore Wed Mar 01 10:54:54 2023 +0100
+++ b/.hgignore Fri Mar 03 19:20:49 2023 +0100
@@ -24,3 +24,5 @@
doc/_build
doc/locale
+
+^.*\$py.class$
--- a/Beremiz.py Wed Mar 01 10:54:54 2023 +0100
+++ b/Beremiz.py Fri Mar 03 19:20:49 2023 +0100
@@ -50,6 +50,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 +63,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 +79,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()
@@ -182,10 +186,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:
--- a/BeremizIDE.py Wed Mar 01 10:54:54 2023 +0100
+++ b/BeremizIDE.py Fri Mar 03 19:20:49 2023 +0100
@@ -28,10 +28,9 @@
from __future__ import print_function
import os
import sys
-import tempfile
import shutil
-import random
import time
+import signal
from time import time as gettime
from threading import Lock, Timer, currentThread
@@ -39,6 +38,7 @@
import wx.lib.buttons
import wx.lib.statbmp
import wx.stc
+import wx.adv
import version
@@ -47,9 +47,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 +83,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
@@ -115,7 +110,7 @@
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 +126,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()
@@ -179,7 +178,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
@@ -236,7 +235,7 @@
ID_FILEMENURECENTPROJECTS = wx.NewId()
-class Beremiz(IDEFrame):
+class Beremiz(IDEFrame, LocalRuntimeMixin):
def _init_utils(self):
self.ConfNodeMenu = wx.Menu(title='')
@@ -250,9 +249,9 @@
kind=wx.ITEM_NORMAL, text=_(u'New') + '\tCTRL+N')
AppendMenu(parent, help='', id=wx.ID_OPEN,
kind=wx.ITEM_NORMAL, text=_(u'Open') + '\tCTRL+O')
- parent.AppendMenu(ID_FILEMENURECENTPROJECTS, _("&Recent Projects"), self.RecentProjectsMenu)
+ parent.Append(ID_FILEMENURECENTPROJECTS, _("&Recent Projects"), self.RecentProjectsMenu)
parent.AppendSeparator()
- parent.AppendMenu(wx.ID_ANY, _("&Tutorials and Examples"), self.TutorialsProjectsMenu)
+ parent.Append(wx.ID_ANY, _("&Tutorials and Examples"), self.TutorialsProjectsMenu)
exemples_dir = Bpath("exemples")
project_list = sorted(os.listdir(exemples_dir))
@@ -314,10 +313,10 @@
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):
@@ -334,16 +333,16 @@
item = parent.Append(wx.ID_ANY, _(u'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=_(u'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])
@@ -356,6 +355,8 @@
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,8 +366,15 @@
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()
self.Bind(wx.EVT_MENU, OnMethodGen(self, method), id=newid)
@@ -395,6 +403,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 +418,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 +429,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 +445,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 +509,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 +521,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 +539,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):
@@ -667,6 +647,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()
@@ -722,7 +707,7 @@
while self.RecentProjectsMenu.GetMenuItemCount() > 0:
item = self.RecentProjectsMenu.FindItemByPosition(0)
- self.RecentProjectsMenu.RemoveItem(item)
+ self.RecentProjectsMenu.Remove(item)
self.FileMenu.Enable(ID_FILEMENURECENTPROJECTS, len(recent_projects) > 0)
for idx, projectpath in enumerate(recent_projects):
@@ -769,8 +754,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)
@@ -821,7 +807,7 @@
else:
self.EditMenu.Delete(item.GetId())
self.LastPanelSelected = None
- self.MenuBar.UpdateMenus()
+ self.MenuBar.Refresh()
def RefreshAll(self):
self.RefreshStatusToolBar()
@@ -988,7 +974,15 @@
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):
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Beremiz_cli.py Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,121 @@
+#!/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.resultcallback()
+@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:
+ time.sleep(1)
+ except KeyboardInterrupt:
+ pass
+
+ session.controller.finish()
+
+ return ret
+
+if __name__ == '__main__':
+ cli()
+
--- a/Beremiz_service.py Wed Mar 01 10:54:54 2023 +0100
+++ b/Beremiz_service.py Fri Mar 03 19:20:49 2023 +0100
@@ -37,6 +37,7 @@
from builtins import str as text
from past.builtins import execfile
from six.moves import builtins
+from functools import partial
import runtime
from runtime.PyroServer import PyroServer
@@ -238,6 +239,7 @@
if havewx:
import re
+ import wx.adv
if wx.VERSION >= (3, 0, 0):
app = wx.App(redirect=False)
@@ -279,7 +281,7 @@
def SetTests(self, tests):
self.Tests = tests
- class BeremizTaskBarIcon(wx.TaskBarIcon):
+ class BeremizTaskBarIcon(wx.adv.TaskBarIcon):
TBMENU_START = wx.NewId()
TBMENU_STOP = wx.NewId()
TBMENU_CHANGE_NAME = wx.NewId()
@@ -291,7 +293,7 @@
TBMENU_QUIT = wx.NewId()
def __init__(self, pyroserver):
- wx.TaskBarIcon.__init__(self)
+ wx.adv.TaskBarIcon.__init__(self)
self.pyroserver = pyroserver
# Set the image
self.UpdateIcon(None)
@@ -339,7 +341,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):
@@ -426,7 +428,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 +442,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 +455,18 @@
obj.res = default_evaluator(tocall, *args, **kwargs)
wx_eval_lock.release()
+ main_thread_id = currentThread().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
+ # To prevent deadlocks, check if current thread is not one already main
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)):
-
+ 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
@@ -559,13 +553,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()
@@ -585,43 +581,59 @@
sys.stdout.write(_("Current working directory :") + WorkingDir + "\n")
sys.stdout.flush()
- if not (havetwisted or havewx):
- return
+ 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 +647,9 @@
if havetwisted:
reactor.stop()
- ui_thread.join()
+ if not havewx:
+ ui_thread.join()
elif havewx:
app.ExitMainLoop()
- ui_thread.join()
sys.exit(0)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/CLIController.py Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,183 @@
+#!/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)
+ self.CLIStatusTimer = None
+ self.KillCLIStatusTimer = False
+
+
+ def StartCLIStatusTimer(self):
+ if self.CLIStatusTimer is not None:
+ return
+ self.CLIStatusTimer = Timer(0.5, self.CLIStatusTimerProc)
+ self.KillCLIStatusTimer = False
+ self.CLIStatusTimer.start()
+
+ def StopCLIStatusTimer(self):
+ if self.CLIStatusTimer is None:
+ return
+ self.KillCLIStatusTimer = True
+ self.CLIStatusTimer.cancel()
+ self.CLIStatusTimer = None
+
+ def CLIStatusTimerProc(self):
+ self.CLIStatusTimer = None
+ if not self.KillCLIStatusTimer:
+ self.PullPLCStatusProc(None)
+ self.StartCLIStatusTimer()
+
+ def _SetConnector(self, connector, update_status=True):
+ self._connector = connector
+ self.previous_log_count = [None]*LogLevelsCount
+ if connector is not None:
+ self.StartCLIStatusTimer()
+ else:
+ self.StopCLIStatusTimer()
+ if update_status:
+ self.UpdateMethodsFromPLCStatus()
+
+ def UpdatePLCLog(self, log_count):
+ connector = self._connector
+ new_messages = []
+ if connector:
+ for level, count, prev in zip(
+ xrange(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
+
+ 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
+
+
+ def finish(self):
+
+ self._Disconnect()
+
+ if not self.session.keep:
+ self.KillLocalRuntime()
+
+
--- a/ConfigTreeNode.py Wed Mar 01 10:54:54 2023 +0100
+++ b/ConfigTreeNode.py Fri Mar 03 19:20:49 2023 +0100
@@ -133,6 +133,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
@@ -471,7 +480,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)
--- a/IDEFrame.py Wed Mar 01 10:54:54 2023 +0100
+++ b/IDEFrame.py Fri Mar 03 19:20:49 2023 +0100
@@ -115,7 +115,7 @@
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))
[
@@ -391,7 +391,7 @@
parent.AppendSeparator()
add_menu = wx.Menu(title='')
self._init_coll_AddMenu_Items(add_menu)
- parent.AppendMenu(wx.ID_ADD, _(u"&Add Element"), add_menu)
+ parent.Append(wx.ID_ADD, _(u"&Add Element"), add_menu)
AppendMenu(parent, help='', id=wx.ID_SELECTALL,
kind=wx.ITEM_NORMAL, text=_(u'Select All') + '\tCTRL+A')
AppendMenu(parent, help='', id=wx.ID_DELETE,
@@ -442,7 +442,7 @@
kind=wx.ITEM_NORMAL, text=_(u'Clear Errors') + '\tCTRL+K')
parent.AppendSeparator()
zoommenu = wx.Menu(title='')
- parent.AppendMenu(wx.ID_ZOOM_FIT, _("Zoom"), zoommenu)
+ parent.Append(wx.ID_ZOOM_FIT, _("Zoom"), zoommenu)
for idx, value in enumerate(ZOOM_FACTORS):
new_item = AppendMenu(zoommenu, help='',
kind=wx.ITEM_RADIO, text=str(int(round(value * 100))) + "%")
@@ -569,8 +569,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 +617,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 +628,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"))
@@ -921,12 +922,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
@@ -1418,7 +1415,8 @@
self.AuiTabCtrl = auitabctrl
if 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()
@@ -1499,17 +1497,23 @@
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):
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):
@@ -1808,7 +1812,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:
@@ -1923,7 +1927,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='')
@@ -2118,7 +2122,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 +2159,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()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/LocalRuntimeMixin.py Fri Mar 03 19:20:49 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
+
+LocalRuntimeInterpreterPath = \
+ os.environ.get("BEREMIZPYTHONPATH", sys.executable)
+
+LocalHost = os.environ.get("BEREMIZ_LOCAL_HOST", "localhost")
+
+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
+
--- a/PLCOpenEditor.py Wed Mar 01 10:54:54 2023 +0100
+++ b/PLCOpenEditor.py Fri Mar 03 19:20:49 2023 +0100
@@ -31,6 +31,7 @@
import getopt
import wx
+import wx.adv
import version
import util.paths as paths
@@ -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"
--- a/ProjectController.py Wed Mar 01 10:54:54 2023 +0100
+++ b/ProjectController.py Fri Mar 03 19:20:49 2023 +0100
@@ -1226,6 +1226,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
@@ -1526,7 +1533,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):
@@ -1790,13 +1798,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 +1821,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 +1841,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 +1903,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 +1928,7 @@
self._SetConnector(None)
def _Transfer(self):
+ success = False
if self.IsPLCStarted():
dialog = wx.MessageDialog(
self.AppFrame,
@@ -1972,16 +1991,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(
--- a/README.md Wed Mar 01 10:54:54 2023 +0100
+++ b/README.md Fri Mar 03 19:20:49 2023 +0100
@@ -1,4 +1,6 @@
+<!---
[![docs](https://readthedocs.org/projects/beremiz/badge/?version=latest)](https://beremiz.readthedocs.io)
+-->
# Beremiz #
@@ -10,109 +12,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) ##
+
+### Prerequisites (Ubuntu/Debian) :
+```
+sudo apt-get install build-essential bison flex autoconf
+sudo apt-get install python2-dev libpython2.7-dev libgtk-3-dev libssl-dev libgl1-mesa-dev libglu1-mesa-dev python-setuptools
+
+python2 -m pip install \
+ future \
+ matplotlib \
+ msgpack_python \
+ u-msgpack-python \
+ zeroconf2 \
+ enum34 \
+ pyro \
+ sslpsk \
+ posix_spawn \
+ twisted \
+ nevow \
+ autobahn \
+ click \
+ opcua \
+ pycountry \
+ fonttools \
+ Brotli \
+ lxml==4.5.0 \
+ wxPython==4.1.1
+
+```
+
+### 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
+```
+
+### 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-3
+cd ~/Beremiz/CanFestival-3
+./configure --can=virtual
+make
+```
+
+### Build Modbus library (optional)
+
+Only needed for Modbus support.
+
+```
+cd ~/Beremiz
+hg clone https://hg.beremiz.org/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
+```
## 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
+python 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 +219,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
--- a/bacnet/BacnetSlaveEditor.py Wed Mar 01 10:54:54 2023 +0100
+++ b/bacnet/BacnetSlaveEditor.py Fri Mar 03 19:20:49 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)
--- a/canfestival/NetworkEditor.py Wed Mar 01 10:54:54 2023 +0100
+++ b/canfestival/NetworkEditor.py Fri Mar 03 19:20:49 2023 +0100
@@ -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)
--- a/connectors/SchemeEditor.py Wed Mar 01 10:54:54 2023 +0100
+++ b/connectors/SchemeEditor.py Fri Mar 03 19:20:49 2023 +0100
@@ -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)
--- a/connectors/__init__.py Wed Mar 01 10:54:54 2023 +0100
+++ b/connectors/__init__.py Fri Mar 03 19:20:49 2023 +0100
@@ -27,6 +27,7 @@
from __future__ import absolute_import
+import os
from os import listdir, path
from connectors.ConnectorBase import ConnectorBase
@@ -60,6 +61,8 @@
schemes += [scheme]
+LocalHost = os.environ.get("BEREMIZ_LOCAL_HOST", "localhost")
+
def ConnectorFactory(uri, confnodesroot):
"""
Return a connector corresponding to the URI
@@ -76,9 +79,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 = "PYROLOC://"+LocalHost+":" + str(runtime_port)
# commented code to enable for MDNS:// support
# elif _scheme == "MDNS":
--- a/controls/CustomEditableListBox.py Wed Mar 01 10:54:54 2023 +0100
+++ b/controls/CustomEditableListBox.py Fri Mar 03 19:20:49 2023 +0100
@@ -25,13 +25,13 @@
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
--- a/controls/CustomStyledTextCtrl.py Wed Mar 01 10:54:54 2023 +0100
+++ b/controls/CustomStyledTextCtrl.py Fri Mar 03 19:20:49 2023 +0100
@@ -105,9 +105,9 @@
[self.GetMarginWidth(i) for i in xrange(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:
--- a/controls/CustomTable.py Wed Mar 01 10:54:54 2023 +0100
+++ b/controls/CustomTable.py Fri Mar 03 19:20:49 2023 +0100
@@ -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():
--- a/controls/CustomToolTip.py Wed Mar 01 10:54:54 2023 +0100
+++ b/controls/CustomToolTip.py Fri Mar 03 19:20:49 2023 +0100
@@ -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()
--- a/controls/CustomTree.py Wed Mar 01 10:54:54 2023 +0100
+++ b/controls/CustomTree.py Fri Mar 03 19:20:49 2023 +0100
@@ -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):
--- a/controls/DebugVariablePanel/DebugVariableGraphicViewer.py Wed Mar 01 10:54:54 2023 +0100
+++ b/controls/DebugVariablePanel/DebugVariableGraphicViewer.py Fri Mar 03 19:20:49 2023 +0100
@@ -166,6 +166,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,7 +175,7 @@
# 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
@@ -182,7 +183,7 @@
# wall be merged orthogonally
merge_rect = wx.Rect(rect.x, rect.y,
rect.width / 2., rect.height)
- if merge_rect.InsideXY(x, y):
+ if merge_rect.Contains(x, y):
merge_type = GRAPH_ORTHOGONAL
# Merge graphs
@@ -209,6 +210,8 @@
self.ParentWindow.InsertValue(values[0],
target_idx,
force=True)
+ return True
+ return False
def OnLeave(self):
"""
@@ -278,7 +281,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)
@@ -625,7 +627,7 @@
(x0, y0), (x1, y1) = t.get_window_extent().get_points()
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
@@ -736,7 +738,7 @@
(x0, y0), (x1, y1) = t.get_window_extent().get_points()
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
@@ -756,7 +758,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):
@@ -832,7 +834,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)
@@ -926,10 +928,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
@@ -1381,8 +1383,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 +1409,5 @@
# Draw other Viewer common elements
self.DrawCommonElements(destGC, self.GetButtons())
- destGC.EndDrawing()
-
self._isDrawn = True
self.gui_repaint(drawDC=drawDC)
--- a/controls/DebugVariablePanel/DebugVariablePanel.py Wed Mar 01 10:54:54 2023 +0100
+++ b/controls/DebugVariablePanel/DebugVariablePanel.py Fri Mar 03 19:20:49 2023 +0100
@@ -134,6 +134,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 +148,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 +206,6 @@
# data is available
self.Force = False
- self.SetBackgroundColour(wx.WHITE)
main_sizer = wx.BoxSizer(wx.VERTICAL)
@@ -221,14 +224,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 +249,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 +266,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 +287,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 +444,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 +491,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 +500,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 +513,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 +521,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()
--- a/controls/DebugVariablePanel/DebugVariableTextViewer.py Wed Mar 01 10:54:54 2023 +0100
+++ b/controls/DebugVariablePanel/DebugVariableTextViewer.py Fri Mar 03 19:20:49 2023 +0100
@@ -96,6 +96,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 +119,8 @@
self.ParentWindow.InsertValue(values[0],
target_idx,
force=True)
+ return True
+ return False
def OnLeave(self):
"""
@@ -195,7 +198,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,8 +206,6 @@
# rendering
gc = wx.GCDC(dc)
- gc.BeginDrawing()
-
# Get first item
item = self.ItemsDict.values()[0]
@@ -232,8 +233,6 @@
# Draw other Viewer common elements
self.DrawCommonElements(gc)
- gc.EndDrawing()
-
def OnLeftDown(self, event):
"""
Function called when mouse left button is pressed
@@ -252,7 +251,7 @@
# 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):
+ if item_path_bbox.Contains(x, y):
self.ShowButtons(False)
data = wx.TextDataObject(str((item.GetVariable(), "debug", "move")))
dragSource = wx.DropSource(self)
--- a/controls/DebugVariablePanel/GraphButton.py Wed Mar 01 10:54:54 2023 +0100
+++ b/controls/DebugVariablePanel/GraphButton.py Fri Mar 03 19:20:49 2023 +0100
@@ -146,7 +146,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):
"""
--- a/controls/DiscoveryPanel.py Wed Mar 01 10:54:54 2023 +0100
+++ b/controls/DiscoveryPanel.py Fri Mar 03 19:20:49 2023 +0100
@@ -44,17 +44,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,10 +129,23 @@
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)
--- a/controls/DurationCellEditor.py Wed Mar 01 10:54:54 2023 +0100
+++ b/controls/DurationCellEditor.py Fri Mar 03 19:20:49 2023 +0100
@@ -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,12 +98,12 @@
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
--- a/controls/EnhancedStatusBar.py Wed Mar 01 10:54:54 2023 +0100
+++ b/controls/EnhancedStatusBar.py Fri Mar 03 19:20:49 2023 +0100
@@ -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):
--- a/controls/FolderTree.py Wed Mar 01 10:54:54 2023 +0100
+++ b/controls/FolderTree.py Fri Mar 03 19:20:49 2023 +0100
@@ -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
@@ -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):
--- a/controls/LibraryPanel.py Wed Mar 01 10:54:54 2023 +0100
+++ b/controls/LibraryPanel.py Fri Mar 03 19:20:49 2023 +0100
@@ -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
@@ -216,7 +216,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 +253,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
--- a/controls/LocationCellEditor.py Wed Mar 01 10:54:54 2023 +0100
+++ b/controls/LocationCellEditor.py Fri Mar 03 19:20:49 2023 +0100
@@ -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)
@@ -150,12 +150,12 @@
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
@@ -178,7 +178,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,13 +201,8 @@
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,
--- a/controls/LogViewer.py Wed Mar 01 10:54:54 2023 +0100
+++ b/controls/LogViewer.py Fri Mar 03 19:20:49 2023 +0100
@@ -99,8 +99,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 +139,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 +161,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 +178,6 @@
gc.DrawRectangle(thumb_rect.x, thumb_rect.y,
thumb_rect.width, thumb_rect.height)
- dc.EndDrawing()
event.Skip()
@@ -207,7 +205,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 +215,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)
@@ -303,7 +301,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 +310,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 +320,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 +347,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)
@@ -534,10 +532,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,8 +556,6 @@
draw_date = message.Date != previous_message.Date
message = previous_message
- dc.EndDrawing()
-
self.MessageScrollBar.RefreshThumbPosition()
def IsPLCLogEmpty(self):
@@ -711,7 +706,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)
--- a/controls/PouInstanceVariablesPanel.py Wed Mar 01 10:54:54 2023 +0100
+++ b/controls/PouInstanceVariablesPanel.py Fri Mar 03 19:20:49 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
@@ -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)
--- a/controls/ProjectPropertiesPanel.py Wed Mar 01 10:54:54 2023 +0100
+++ b/controls/ProjectPropertiesPanel.py Fri Mar 03 19:20:49 2023 +0100
@@ -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"))
--- a/controls/SearchResultPanel.py Wed Mar 01 10:54:54 2023 +0100
+++ b/controls/SearchResultPanel.py Fri Mar 03 19:20:49 2023 +0100
@@ -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()
--- a/controls/TextCtrlAutoComplete.py Wed Mar 01 10:54:54 2023 +0100
+++ b/controls/TextCtrlAutoComplete.py Fri Mar 03 19:20:49 2023 +0100
@@ -97,7 +97,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))
--- a/controls/VariablePanel.py Wed Mar 01 10:54:54 2023 +0100
+++ b/controls/VariablePanel.py Fri Mar 03 19:20:49 2023 +0100
@@ -200,8 +200,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(map(_, options))
else:
grid.SetReadOnly(row, col, True)
elif col != 0 and self._GetRowEdit(row):
@@ -212,8 +211,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 +227,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 +322,7 @@
selected = None
dialog.Destroy()
if selected is None:
- return
+ return False
if selected == 0:
location = "%I" + location
elif selected == 1:
@@ -360,7 +357,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 +385,7 @@
selected = None
dialog.Destroy()
if selected is None:
- return
+ return False
if selected == 0:
location = "%I" + location
elif selected == 1:
@@ -399,7 +396,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 +414,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 +426,9 @@
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)
@@ -456,32 +456,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 +490,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,7 +575,6 @@
self.PanelWidthMin = sum(self.ColSettings["size"])
- self.ElementType = element_type
self.BodyType = None
for choice in self.FilterChoices:
@@ -848,7 +847,7 @@
# 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)
@@ -858,7 +857,7 @@
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)
@@ -869,7 +868,7 @@
# 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)
@@ -883,13 +882,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)
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 +915,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
--- a/dialogs/AboutDialog.py Wed Mar 01 10:54:54 2023 +0100
+++ b/dialogs/AboutDialog.py Fri Mar 03 19:20:49 2023 +0100
@@ -34,6 +34,7 @@
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))
@@ -172,4 +174,4 @@
if os.name == "nt":
AboutDialog(parent, info)
else:
- wx.AboutBox(info)
+ wx.adv.AboutBox(info)
--- a/dialogs/ActionBlockDialog.py Wed Mar 01 10:54:54 2023 +0100
+++ b/dialogs/ActionBlockDialog.py Fri Mar 03 19:20:49 2023 +0100
@@ -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 = 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:
--- a/dialogs/ArrayTypeDialog.py Wed Mar 01 10:54:54 2023 +0100
+++ b/dialogs/ArrayTypeDialog.py Fri Mar 03 19:20:49 2023 +0100
@@ -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)
--- a/dialogs/BlockPreviewDialog.py Wed Mar 01 10:54:54 2023 +0100
+++ b/dialogs/BlockPreviewDialog.py Fri Mar 03 19:20:49 2023 +0100
@@ -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
@@ -120,7 +119,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 +128,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 +137,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)
--- a/dialogs/BrowseLocationsDialog.py Wed Mar 01 10:54:54 2023 +0100
+++ b/dialogs/BrowseLocationsDialog.py Fri Mar 03 19:20:49 2023 +0100
@@ -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)
--- a/dialogs/BrowseValuesLibraryDialog.py Wed Mar 01 10:54:54 2023 +0100
+++ b/dialogs/BrowseValuesLibraryDialog.py Fri Mar 03 19:20:49 2023 +0100
@@ -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)
--- a/dialogs/ConnectionDialog.py Wed Mar 01 10:54:54 2023 +0100
+++ b/dialogs/ConnectionDialog.py Fri Mar 03 19:20:49 2023 +0100
@@ -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(
--- a/dialogs/DurationEditorDialog.py Wed Mar 01 10:54:54 2023 +0100
+++ b/dialogs/DurationEditorDialog.py Fri Mar 03 19:20:49 2023 +0100
@@ -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):
--- a/dialogs/FBDBlockDialog.py Wed Mar 01 10:54:54 2023 +0100
+++ b/dialogs/FBDBlockDialog.py Fri Mar 03 19:20:49 2023 +0100
@@ -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
--- a/dialogs/FBDVariableDialog.py Wed Mar 01 10:54:54 2023 +0100
+++ b/dialogs/FBDVariableDialog.py Fri Mar 03 19:20:49 2023 +0100
@@ -73,49 +73,49 @@
# 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)
--- a/dialogs/FindInPouDialog.py Wed Mar 01 10:54:54 2023 +0100
+++ b/dialogs/FindInPouDialog.py Fri Mar 03 19:20:49 2023 +0100
@@ -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)
--- a/dialogs/ForceVariableDialog.py Wed Mar 01 10:54:54 2023 +0100
+++ b/dialogs/ForceVariableDialog.py Fri Mar 03 19:20:49 2023 +0100
@@ -189,7 +189,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 +201,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 +216,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):
@@ -235,11 +235,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"""
@@ -261,7 +261,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):
--- a/dialogs/IDMergeDialog.py Wed Mar 01 10:54:54 2023 +0100
+++ b/dialogs/IDMergeDialog.py Fri Mar 03 19:20:49 2023 +0100
@@ -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)
--- a/dialogs/LDElementDialog.py Wed Mar 01 10:54:54 2023 +0100
+++ b/dialogs/LDElementDialog.py Fri Mar 03 19:20:49 2023 +0100
@@ -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
@@ -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)
--- a/dialogs/LDPowerRailDialog.py Wed Mar 01 10:54:54 2023 +0100
+++ b/dialogs/LDPowerRailDialog.py Fri Mar 03 19:20:49 2023 +0100
@@ -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()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dialogs/MessageBoxOnce.py Fri Mar 03 19:20:49 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.
+
+
+from __future__ import absolute_import
+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)
--- a/dialogs/PouActionDialog.py Wed Mar 01 10:54:54 2023 +0100
+++ b/dialogs/PouActionDialog.py Fri Mar 03 19:20:49 2023 +0100
@@ -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)
--- a/dialogs/PouDialog.py Wed Mar 01 10:54:54 2023 +0100
+++ b/dialogs/PouDialog.py Fri Mar 03 19:20:49 2023 +0100
@@ -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)
--- a/dialogs/PouNameDialog.py Wed Mar 01 10:54:54 2023 +0100
+++ b/dialogs/PouNameDialog.py Fri Mar 03 19:20:49 2023 +0100
@@ -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
--- a/dialogs/PouTransitionDialog.py Wed Mar 01 10:54:54 2023 +0100
+++ b/dialogs/PouTransitionDialog.py Fri Mar 03 19:20:49 2023 +0100
@@ -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)
--- a/dialogs/ProjectDialog.py Wed Mar 01 10:54:54 2023 +0100
+++ b/dialogs/ProjectDialog.py Fri Mar 03 19:20:49 2023 +0100
@@ -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)
--- a/dialogs/SFCDivergenceDialog.py Wed Mar 01 10:54:54 2023 +0100
+++ b/dialogs/SFCDivergenceDialog.py Fri Mar 03 19:20:49 2023 +0100
@@ -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)
--- a/dialogs/SFCStepDialog.py Wed Mar 01 10:54:54 2023 +0100
+++ b/dialogs/SFCStepDialog.py Fri Mar 03 19:20:49 2023 +0100
@@ -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)
--- a/dialogs/SFCStepNameDialog.py Wed Mar 01 10:54:54 2023 +0100
+++ b/dialogs/SFCStepNameDialog.py Fri Mar 03 19:20:49 2023 +0100
@@ -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
--- a/dialogs/SFCTransitionDialog.py Wed Mar 01 10:54:54 2023 +0100
+++ b/dialogs/SFCTransitionDialog.py Fri Mar 03 19:20:49 2023 +0100
@@ -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)
--- a/dialogs/SearchInProjectDialog.py Wed Mar 01 10:54:54 2023 +0100
+++ b/dialogs/SearchInProjectDialog.py Fri Mar 03 19:20:49 2023 +0100
@@ -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)
--- a/dialogs/__init__.py Wed Mar 01 10:54:54 2023 +0100
+++ b/dialogs/__init__.py Fri Mar 03 19:20:49 2023 +0100
@@ -51,3 +51,4 @@
from dialogs.BrowseValuesLibraryDialog import BrowseValuesLibraryDialog
from dialogs.UriEditor import UriEditor
from dialogs.IDManager import IDManager
+from dialogs.MessageBoxOnce import MessageBoxOnce
--- a/docutil/docsvg.py Wed Mar 01 10:54:54 2023 +0100
+++ b/docutil/docsvg.py Fri Mar 03 19:20:49 2023 +0100
@@ -24,8 +24,10 @@
from __future__ import absolute_import
+import os
import wx
import subprocess
+from dialogs import MessageBoxOnce
def _get_inkscape_path():
@@ -86,11 +88,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 os.environ.has_key("SNAP"):
+ 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])
--- a/editors/CodeFileEditor.py Wed Mar 01 10:54:54 2023 +0100
+++ b/editors/CodeFileEditor.py Fri Mar 03 19:20:49 2023 +0100
@@ -291,17 +291,16 @@
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)
+ start_pos, end_pos = self.FindText(0, doc_end_pos, section_comments["comment"])
+ 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,9 +596,9 @@
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.StartStyling(highlight_end_pos)
self.SetStyling(len(self.GetText()) - highlight_end_pos, stc.STC_STYLE_DEFAULT)
@@ -614,8 +613,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):
@@ -678,7 +676,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 +685,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 +783,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 +824,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:
--- a/editors/ConfTreeNodeEditor.py Wed Mar 01 10:54:54 2023 +0100
+++ b/editors/ConfTreeNodeEditor.py Fri Mar 03 19:20:49 2023 +0100
@@ -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,14 +169,14 @@
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))
@@ -187,17 +187,17 @@
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:
@@ -317,7 +317,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 +335,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 +380,32 @@
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)
statictext = wx.StaticText(self.ParamsEditor,
label="%s:" % _(element_infos["name"]))
- boxsizer.AddWindow(statictext, border=5,
+ 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 +414,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,7 +425,7 @@
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("")
@@ -435,13 +435,16 @@
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)
@@ -463,7 +466,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 +476,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 +493,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 +516,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 +530,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 +538,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,7 +582,7 @@
event.Skip()
return OnChoiceChanged
- def GetChoiceContentCallBackFunction(self, choicectrl, staticboxsizer, path):
+ def GetChoiceContentCallBackFunction(self, choicectrl, path):
def OnChoiceContentChanged(event):
self.SetConfNodeParamsAttribute(path, choicectrl.GetStringSelection())
wx.CallAfter(self.RefreshConfNodeParamsSizer)
--- a/editors/DataTypeEditor.py Wed Mar 01 10:54:54 2023 +0100
+++ b/editors/DataTypeEditor.py Fri Mar 03 19:20:49 2023 +0100
@@ -47,7 +47,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 +155,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 +205,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)
+ 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 +258,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 +294,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 +302,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 +344,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 +353,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 +369,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)
@@ -647,7 +647,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:
@@ -786,7 +786,7 @@
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()):
listctrl.SetItemBackgroundColour(i, wx.NullColour)
@@ -811,7 +811,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])
--- a/editors/FileManagementPanel.py Wed Mar 01 10:54:54 2023 +0100
+++ b/editors/FileManagementPanel.py Fri Mar 03 19:20:49 2023 +0100
@@ -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)
--- a/editors/ProjectNodeEditor.py Wed Mar 01 10:54:54 2023 +0100
+++ b/editors/ProjectNodeEditor.py Fri Mar 03 19:20:49 2023 +0100
@@ -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
--- a/editors/ResourceEditor.py Wed Mar 01 10:54:54 2023 +0100
+++ b/editors/ResourceEditor.py Fri Mar 03 19:20:49 2023 +0100
@@ -76,10 +76,6 @@
return [_("Interrupt"), _("Cyclic")]
-def SingleCellEditor(*x):
- return wx.grid.GridCellChoiceEditor()
-
-
def CheckSingle(single, varlist):
return single in varlist
@@ -162,25 +158,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(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)
@@ -230,16 +222,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_sizer.Add(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_buttons_sizer.Add(tasks_label, flag=wx.ALIGN_BOTTOM)
for name, bitmap, help in [
("AddTaskButton", "add_element", _("Add task")),
@@ -250,27 +242,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_sizer.Add(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_buttons_sizer.Add(instances_label, flag=wx.ALIGN_BOTTOM)
for name, bitmap, help in [
("AddInstanceButton", "add_element", _("Add instance")),
@@ -280,13 +272,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)
@@ -405,20 +397,20 @@
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 = ""
+ self.TaskList = []
for row in xrange(self.TasksTable.GetNumberRows()):
- self.TaskList += ",%s" % self.TasksTable.GetValueByName(row, "Name")
+ 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,7 +473,7 @@
wx.CallAfter(self.ShowErrorMessage, message)
return
- tasklist = [name for name in self.TaskList.split(",") if name != ""]
+ tasklist = [name for name in self.TaskList if name != ""]
for i in xrange(self.TasksTable.GetNumberRows()):
task = self.TasksTable.GetValueByName(i, "Name")
if task in tasklist:
--- a/editors/TextViewer.py Wed Mar 01 10:54:54 2023 +0100
+++ b/editors/TextViewer.py Fri Mar 03 19:20:49 2023 +0100
@@ -197,12 +197,20 @@
def Colourise(self, start, end):
self.Editor.Colourise(start, end)
- def StartStyling(self, pos, mask):
- self.Editor.StartStyling(pos, mask)
+ def StartStyling(self, pos):
+ self.Editor.StartStyling(pos)
+
+ 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()
@@ -562,7 +570,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 +605,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 +707,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:
@@ -975,8 +981,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_start_pos)
until_end = max(0, len(self.Editor.GetText()) - highlight_end_pos)
self.SetStyling(until_end, wx.stc.STC_STYLE_DEFAULT)
--- a/editors/Viewer.py Wed Mar 01 10:54:54 2023 +0100
+++ b/editors/Viewer.py Fri Mar 03 19:20:49 2023 +0100
@@ -60,11 +60,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__':
@@ -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)
@@ -712,7 +716,7 @@
break
faces["size"] -= 1
self.Editor.SetFont(font)
- self.MiniTextDC = wx.MemoryDC(wx.EmptyBitmap(1, 1))
+ self.MiniTextDC = wx.MemoryDC(wx.Bitmap(1, 1))
self.MiniTextDC.SetFont(wx.Font(faces["size"] * 0.75, wx.SWISS, wx.NORMAL, wx.NORMAL, faceName=faces["helv"]))
self.CurrentScale = None
@@ -825,15 +829,14 @@
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])
+
+ def GetLogicalDC(self):
+ dc = wx.ClientDC(self.Editor)
+ self.PrepareDC(dc)
return dc
def RefreshRect(self, rect, eraseBackground=True):
@@ -1058,7 +1061,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 +1119,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:
@@ -1572,10 +1575,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:
@@ -1903,9 +1911,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
@@ -3379,7 +3387,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:
@@ -3633,7 +3641,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)
@@ -3680,12 +3687,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()
--- a/etherlab/ConfigEditor.py Wed Mar 01 10:54:54 2023 +0100
+++ b/etherlab/ConfigEditor.py Fri Mar 03 19:20:49 2023 +0100
@@ -16,7 +16,7 @@
import wx
import wx.grid
-import wx.gizmos
+import wx.adv
import wx.lib.buttons
from plcopen.structures import IEC_KEYWORDS, TestIdentifier
@@ -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
@@ -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)
@@ -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)
--- a/etherlab/EtherCATManagementEditor.py Wed Mar 01 10:54:54 2023 +0100
+++ b/etherlab/EtherCATManagementEditor.py Fri Mar 03 19:20:49 2023 +0100
@@ -15,7 +15,7 @@
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"],
@@ -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()
@@ -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()
@@ -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
--- a/exemples/python/plc.xml Wed Mar 01 10:54:54 2023 +0100
+++ b/exemples/python/plc.xml Fri Mar 03 19:20:49 2023 +0100
@@ -1,7 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<project xmlns="http://www.plcopen.org/xml/tc6_0201" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xhtml="http://www.w3.org/1999/xhtml" xsi:schemaLocation="http://www.plcopen.org/xml/tc6_0201">
<fileHeader companyName="" productName="Beremiz" productVersion="0.0" creationDateTime="2008-12-14T16:21:19" contentDescription="This example shows many features in Beremiz: 1. How to implement python extensions. 2. How to implement basic C extension. 3. How to use C code in IEC POUs. 4. How to call C functions from python code. 5. How to avoid race conditions between IEC, C and python code. 6. How to convert betweet different IEC types. "/>
- <contentHeader name="Beremiz Python Support Tests" modificationDateTime="2020-10-19T23:53:08">
+ <contentHeader name="Beremiz Python Support Tests" modificationDateTime="2022-07-03T16:04:31">
<coordinateInfo>
<pageSize x="1024" y="1024"/>
<fbd>
@@ -269,12 +269,12 @@
</interface>
<body>
<FBD>
- <inVariable localId="4" height="30" width="160" executionOrderId="0" negated="false">
- <position x="295" y="450"/>
- <connectionPointOut>
- <relPosition x="160" y="15"/>
- </connectionPointOut>
- <expression>'666'</expression>
+ <inVariable localId="4" height="30" width="315" executionOrderId="0" negated="false">
+ <position x="200" y="390"/>
+ <connectionPointOut>
+ <relPosition x="315" y="15"/>
+ </connectionPointOut>
+ <expression>'sys.stdout.write("Hello world\n")'</expression>
</inVariable>
<block localId="5" width="125" height="80" typeName="python_eval" instanceName="py1" executionOrderId="0">
<position x="686" y="400"/>
@@ -293,9 +293,11 @@
<variable formalParameter="CODE">
<connectionPointIn>
<relPosition x="0" y="65"/>
- <connection refLocalId="4">
+ <connection refLocalId="80" formalParameter="OUT">
<position x="686" y="465"/>
- <position x="455" y="465"/>
+ <position x="653" y="465"/>
+ <position x="653" y="485"/>
+ <position x="630" y="485"/>
</connection>
</connectionPointIn>
</variable>
@@ -739,12 +741,6 @@
Happy hacking! ]]></xhtml:p>
</content>
</comment>
- <comment localId="31" height="90" width="345">
- <position x="295" y="485"/>
- <content>
- <xhtml:p><![CDATA[Sleep here is bad. It blocks other py_eval instances. Whith a wxGlade GUI, GUI freeze for a second.]]></xhtml:p>
- </content>
- </comment>
<comment localId="6" height="80" width="345">
<position x="295" y="630"/>
<content>
@@ -1443,10 +1439,10 @@
</connectionPointIn>
<expression>Test_Python_Var</expression>
</outVariable>
- <inVariable localId="79" executionOrderId="0" height="25" width="30" negated="false">
+ <inVariable localId="79" executionOrderId="0" height="27" width="30" negated="false">
<position x="560" y="1340"/>
<connectionPointOut>
- <relPosition x="30" y="10"/>
+ <relPosition x="30" y="15"/>
</connectionPointOut>
<expression>23</expression>
</inVariable>
@@ -1464,6 +1460,44 @@
</connectionPointOut>
<expression>SomeVarName</expression>
</inOutVariable>
+ <block localId="80" typeName="MOVE" executionOrderId="0" height="40" width="60">
+ <position x="570" y="455"/>
+ <inputVariables>
+ <variable formalParameter="IN">
+ <connectionPointIn>
+ <relPosition x="0" y="30"/>
+ <connection refLocalId="82">
+ <position x="578" y="485"/>
+ <position x="532" y="485"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ </inputVariables>
+ <inOutVariables/>
+ <outputVariables>
+ <variable formalParameter="OUT">
+ <connectionPointOut>
+ <relPosition x="60" y="30"/>
+ </connectionPointOut>
+ </variable>
+ </outputVariables>
+ </block>
+ <connector name="Connection0" localId="81" height="30" width="130">
+ <position x="545" y="390"/>
+ <connectionPointIn>
+ <relPosition x="0" y="15"/>
+ <connection refLocalId="4">
+ <position x="545" y="405"/>
+ <position x="515" y="405"/>
+ </connection>
+ </connectionPointIn>
+ </connector>
+ <continuation name="Connection0" localId="82" height="30" width="130">
+ <position x="410" y="470"/>
+ <connectionPointOut>
+ <relPosition x="130" y="15"/>
+ </connectionPointOut>
+ </continuation>
</FBD>
</body>
</pou>
--- a/exemples/svghmi_traffic_light/svghmi_0@svghmi/svghmi.svg Wed Mar 01 10:54:54 2023 +0100
+++ b/exemples/svghmi_traffic_light/svghmi_0@svghmi/svghmi.svg Fri Mar 03 19:20:49 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 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
+ <dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
@@ -1283,6 +1283,14 @@
inkscape:groupmode="layer"
id="layer1"
transform="translate(37.474617,-760.93329)">
+ <rect
+ style="opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none"
+ id="rect250"
+ width="320"
+ height="240"
+ x="-37.474617"
+ y="760.93329"
+ inkscape:label="HMI:Page:Home" />
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#282828;fill-opacity:1;stroke:none;stroke-width:2.04116011;marker:none;enable-background:accumulate"
d="m 114.28125,14.28125 v 130 h 18.9375 v 93.5625 h 5.71875 V 176.4375 h 8.90625 v 15.71875 h 36.4375 v -32.5 h -36.4375 v 12.125 h -8.90625 v -27.5 h 21.78125 v -130 z"
@@ -1529,13 +1537,5 @@
x="62.818459"
y="812.17749"
style="font-size:6.17188501px;line-height:1.25;font-family:sans-serif">ON</tspan></text>
- <rect
- style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none"
- id="rect250"
- width="320"
- height="240"
- x="-37.474617"
- y="760.93329"
- inkscape:label="HMI:Page:Home" />
</g>
</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/fake_wx.py Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,111 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import sys
+import new
+from types import ModuleType
+
+# TODO use gettext instead
+def get_translation(txt):
+ return txt
+
+
+class FakeObject:
+ def __init__(self, *args, **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):
+ raise IndexError, key
+
+ def __str__(self):
+ return self.__classname__
+
+ def __or__(self, other):
+ return FakeObject(__classname__=self.__classname__+"|"+other.__classname__)
+
+
+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(map(lambda desc:
+ (desc, new.classobj(desc, (FakeClass,), {}))
+ if type(desc)==str else desc, classes))
+ ModuleType(name)
+
+ def __getattr__(self,name):
+ if name.startswith('__'):
+ raise AttributeError, name
+
+ if self.__objects__.has_key(name):
+ 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',[]),
+ ('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
+
+from six.moves import builtins
+
+builtins.__dict__['_'] = get_translation
+
--- a/graphics/FBD_Objects.py Wed Mar 01 10:54:54 2023 +0100
+++ b/graphics/FBD_Objects.py Fri Mar 03 19:20:49 2023 +0100
@@ -122,10 +122,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
@@ -392,7 +392,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)
@@ -771,6 +771,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 +1012,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)
--- a/graphics/GraphicCommons.py Wed Mar 01 10:54:54 2023 +0100
+++ b/graphics/GraphicCommons.py Fri Mar 03 19:20:49 2023 +0100
@@ -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):
@@ -448,7 +448,7 @@
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
@@ -1401,7 +1401,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
@@ -1933,7 +1933,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,13 +1941,13 @@
# 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
@@ -1961,7 +1961,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
--- a/graphics/RubberBand.py Wed Mar 01 10:54:54 2023 +0100
+++ b/graphics/RubberBand.py Fri Mar 03 19:20:49 2023 +0100
@@ -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()
@@ -195,3 +195,25 @@
"""
# 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
+
+
+if "gtk3" in wx.PlatformInfo:
+ PatchRubberBandForGTK3()
--- a/graphics/SFC_Objects.py Wed Mar 01 10:54:54 2023 +0100
+++ b/graphics/SFC_Objects.py Fri Mar 03 19:20:49 2023 +0100
@@ -719,7 +719,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)
@@ -1204,7 +1204,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):
@@ -1592,7 +1592,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):
Binary file images/Build.png has changed
Binary file images/Clean.png has changed
Binary file images/Connect.png has changed
Binary file images/Disconnect.png has changed
Binary file images/Transfer.png has changed
Binary file images/about_brz_logo.png has changed
Binary file images/brz.ico has changed
Binary file images/brz.png has changed
--- a/images/genicons.sh Wed Mar 01 10:54:54 2023 +0100
+++ b/images/genicons.sh Fri Mar 03 19:20:49 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
--- a/images/icons.svg Wed Mar 01 10:54:54 2023 +0100
+++ b/images/icons.svg Fri Mar 03 19:20:49 2023 +0100
@@ -32,7 +32,7 @@
</metadata>
<sodipodi:namedview
inkscape:window-height="2096"
- inkscape:window-width="3840"
+ inkscape:window-width="2880"
inkscape:pageshadow="2"
inkscape:pageopacity="0"
guidetolerance="10.0"
@@ -43,17 +43,20 @@
pagecolor="#ffffff"
id="base"
showgrid="true"
- inkscape:zoom="32"
- inkscape:cx="1126.6889"
- inkscape:cy="857.99192"
- inkscape:window-x="1600"
+ inkscape:zoom="4"
+ inkscape:cx="384.40645"
+ inkscape:cy="510.71306"
+ inkscape:window-x="0"
inkscape:window-y="27"
- inkscape:current-layer="svg2"
+ inkscape:current-layer="g19354"
showguides="true"
inkscape:guide-bbox="true"
- inkscape:window-maximized="1"
+ inkscape:window-maximized="0"
inkscape:measure-start="904.956,703.964"
- inkscape:measure-end="930.144,704.058">
+ inkscape:measure-end="930.144,704.058"
+ inkscape:object-paths="true"
+ inkscape:snap-bbox="true"
+ inkscape:bbox-paths="true">
<inkscape:grid
type="xygrid"
id="grid16717"
@@ -88311,6 +88314,16 @@
cx="-159.0049"
cy="-108.7241"
r="6.9022002" />
+ <clipPath
+ clipPathUnits="userSpaceOnUse"
+ id="clipPath16483">
+ <path
+ sodipodi:nodetypes="ccccccccccccccccccc"
+ inkscape:connector-curvature="0"
+ id="use16485"
+ d="m 566.50059,73.36489 c -3.21171,0 -6.18403,1.71679 -7.78988,4.49822 -0.54331,0.94101 -0.88738,1.959 -1.06307,2.99881 h -6.14112 c -2.02768,-0.0286 -2.02768,3.0275 0,2.99882 h 6.14112 c 0.17569,1.03979 0.51976,2.05778 1.06307,2.99881 1.60585,2.78143 4.57817,4.49822 7.78988,4.49822 0.82807,-9e-5 1.49932,-0.67134 1.49941,-1.4994 v -1.49941 h 4.49822 c 2.02743,0.02842 2.02743,-3.027239 0,-2.99882 H 568 v -5.99762 h 4.49822 c 2.02743,0.02842 2.02743,-3.027239 0,-2.99882 H 568 v -1.49941 c -9e-5,-0.82806 -0.67134,-1.49931 -1.49941,-1.4994 z"
+ style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill-opacity:1;fill-rule:nonzero;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+ </clipPath>
</defs>
<g
id="g19063"
@@ -89174,7 +89187,7 @@
inkscape:radius="1"
inkscape:original="M 70 192.36133 L 70 214.36133 L 87 214.36133 L 87 197.36133 L 82 192.36133 L 70 192.36133 z "
xlink:href="#path18378"
- style="color:#000000;fill:#000000;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:#aaaaaa;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="path18380"
inkscape:href="#path18378"
d="m 68,192.375 0,22 19,0 0,-17 -5,-5 -14,0 z" />
@@ -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" />
<path
d="m 80.431156,207.75778 0,1 1,0 0,-1 -1,0 z m 1,1 0,4 1,0 0,-4 -1,0 z m 0,4 -1,0 0,1 1,0 0,-1 z m -1,0 0,-4 -1,0 0,4 1,0 z m 3,-4 1,0 0,-1 1,0 0,5 1,0 0,1 -3,0 0,-1 1,0 0,-3 -1,0 0,-1 m -11,-1 0,1 1,0 0,-1 -1,0 z m 1,1 0,4 1,0 0,-4 -1,0 z m 0,4 -1,0 0,1 1,0 0,-1 z m -1,0 0,-4 -1,0 0,4 1,0 z m 3,-4 1,0 0,-1 1,0 0,5 1,0 0,1 -3,0 0,-1 1,0 0,-3 -1,0 0,-1 m 9,-9 0,1 1,0 0,-1 -1,0 z m 1,1 0,4 1,0 0,-4 -1,0 z m 0,4 -1,0 0,1 1,0 0,-1 z m -1,0 0,-4 -1,0 0,4 1,0 z m -5,-4 1,0 0,-1 1,0 0,5 1,0 0,1 -3,0 0,-1 1,0 0,-3 -1,0 0,-1 m -7,-1 0,1 1,0 0,-1 -1,0 z m 1,1 0,4 1,0 0,-4 -1,0 z m 0,4 -1,0 0,1 1,0 0,-1 z m -1,0 0,-4 -1,0 0,4 1,0 z m 3,-4 1,0 0,-1 1,0 0,5 1,0 0,1 -3,0 0,-1 1,0 0,-3 -1,0 0,-1"
style="color:#000000;fill:#cccccc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="path18400"
inkscape:connector-curvature="0" />
<path
- id="path18402"
- d="m 79.568848,193.4415 0,2 -3,0 0,4 3,0 0,2 4,-4 -4,-4 z m -14,2 0,4 1,0 0,-4 -1,0 z m 2,0 0,4 1,0 0,-4 -1,0 z m 2,0 0,4 2,0 0,-4 -2,0 z m 3,0 0,4 3,0 0,-4 -3,0 z"
- style="fill:#808080;stroke:none"
- inkscape:connector-curvature="0" />
- <path
id="path19304"
style="color:#000000;fill:#000080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 80,207.36218 0,1 1,0 0,-1 -1,0 z m 1,1 0,4 1,0 0,-4 -1,0 z m 0,4 -1,0 0,1 1,0 0,-1 z m -1,0 0,-4 -1,0 0,4 1,0 z m 3,-4 1,0 0,-1 1,0 0,5 1,0 0,1 -3,0 0,-1 1,0 0,-3 -1,0 0,-1 m -11,-1 0,1 1,0 0,-1 -1,0 z m 1,1 0,4 1,0 0,-4 -1,0 z m 0,4 -1,0 0,1 1,0 0,-1 z m -1,0 0,-4 -1,0 0,4 1,0 z m 3,-4 1,0 0,-1 1,0 0,5 1,0 0,1 -3,0 0,-1 1,0 0,-3 -1,0 0,-1 m 9,-9 0,1 1,0 0,-1 -1,0 z m 1,1 0,4 1,0 0,-4 -1,0 z m 0,4 -1,0 0,1 1,0 0,-1 z m -1,0 0,-4 -1,0 0,4 1,0 z m -5,-4 1,0 0,-1 1,0 0,5 1,0 0,1 -3,0 0,-1 1,0 0,-3 -1,0 0,-1 m -7,-1 0,1 1,0 0,-1 -1,0 z m 1,1 0,4 1,0 0,-4 -1,0 z m 0,4 -1,0 0,1 1,0 0,-1 z m -1,0 0,-4 -1,0 0,4 1,0 z m 3,-4 1,0 0,-1 1,0 0,5 1,0 0,1 -3,0 0,-1 1,0 0,-3 -1,0 0,-1"
@@ -90133,7 +90141,7 @@
id="g18993"
transform="translate(1165.0472,106.05367)">
<path
- style="display:inline;overflow:visible;visibility:visible;fill:url(#linearGradient8148);fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:10.43299961;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ style="display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:10.43299961;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="splash"
d="m -1031,857.23823 h 476 v 299.99997 h -476 z"
inkscape:connector-curvature="0" />
@@ -90141,83 +90149,91 @@
id="g17803"
transform="matrix(0.2224431,0,0,0.2224431,-580.61956,554.56584)">
<g
- id="use16743"
- mask="url(#mask6467)"
- transform="translate(-1840,2339.9999)" />
- <g
id="g7269"
- transform="translate(0,89.910633)">
- <path
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:314.89779663px;line-height:125%;font-family:'Arial Black';text-align:start;writing-mode:lr-tb;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;filter:url(#filter17760);enable-background:accumulate"
- id="path17845"
- d="m -1470.2813,1725.0291 c -56.0138,0.054 -112.0828,20.5177 -156.0937,61.5937 -85.0794,79.4058 -95.9453,209.1111 -29.5938,301.1563 l 9.7813,69.3437 0.9062,6.4375 5.125,-4 30.5938,-23.9687 c 87.5525,67.3697 213.0935,63.1007 295.9375,-14.2188 18.4642,-17.2329 33.4323,-36.8343 44.875,-57.9062 6.4003,0.736 13.3613,1.0937 20.875,1.0937 24.9087,0 44.0178,-3.5634 57.3437,-10.6875 13.3257,-7.1242 24.6943,-18.8804 34.125,-35.2812 l -61.6562,-5.6875 c -3.8953,4.9202 -7.5237,8.3649 -10.9063,10.3125 -5.5355,3.0752 -11.381,4.5937 -17.5312,4.5937 -2.2646,0 -4.435,-0.18 -6.5,-0.5625 3.5746,-10.6475 6.37,-21.5105 8.3437,-32.5 h 91.8125 v -7.0625 c -10e-5,-21.5262 -3.5522,-39.0091 -10.625,-52.4375 -7.0731,-13.4281 -17.3756,-23.6769 -30.9062,-30.75 -13.3838,-6.9958 -31.5824,-10.5176 -54.5938,-10.5937 -7.0146,-25.9757 -18.6908,-50.9762 -35.0625,-73.6875 l -9.7812,-69.3438 -0.9063,-6.4375 -5.125,4 -30.5937,23.9688 c -41.0402,-31.5796 -90.4197,-47.4228 -139.8438,-47.375 z m 228.125,206.2187 c 6.3617,0.8346 11.6486,3.3459 15.875,7.5313 5.279,5.2278 8.5511,13.9044 9.7813,26 h -24.7813 c 0.5248,-11.1718 0.225,-22.3843 -0.875,-33.5313 z m 118.9731,-33.6623 h 58.582 v 26.754 c 5.6377,-11.583 11.4549,-19.5528 17.4516,-23.9095 5.9965,-4.3563 13.4025,-6.5346 22.2182,-6.5347 9.2253,1e-4 19.3222,2.8703 30.2904,8.6104 l -19.3736,44.5901 c -7.3805,-3.0751 -13.2233,-4.6127 -17.5285,-4.6128 -8.2005,10e-5 -14.5559,3.3828 -19.066,10.1481 -6.458,9.5331 -9.6869,27.3691 -9.6868,53.508 v 54.7381 h -62.8873 z m 320.2793,97.1755 h -125.46709 c 1.12748,10.0456 3.84388,17.5285 8.14921,22.4487 6.04775,7.073 13.94069,10.6094 23.67883,10.6094 6.15024,0 11.99306,-1.5376 17.5285,-4.6128 3.38256,-1.9476 7.02151,-5.3815 10.91686,-10.3018 l 61.65724,5.6891 c -9.43072,16.4009 -20.80885,28.1634 -34.13443,35.2876 -13.3259,7.1241 -32.44322,10.6862 -57.35199,10.6862 -21.62881,0 -38.64475,-3.0495 -51.04789,-9.1486 -12.40324,-6.0991 -22.67944,-15.7859 -30.82862,-29.0604 -8.14922,-13.2745 -12.22382,-28.881 -12.22382,-46.8195 0,-25.5239 8.17483,-46.1788 24.52452,-61.9648 16.34962,-15.7857 38.9265,-23.6787 67.7307,-23.6788 23.3712,1e-4 41.82222,3.5366 55.35313,10.6093 13.53059,7.0731 23.83241,17.3236 30.9055,30.7517 7.0727,13.4284 10.60915,30.9056 10.60935,52.4318 z m -63.6561,-29.983 c -1.23021,-12.0956 -4.48476,-20.7573 -9.76368,-25.9852 -5.27917,-5.2277 -12.22393,-7.8416 -20.8343,-7.8417 -9.94316,10e-5 -17.88735,3.9466 -23.8326,11.8394 -3.79279,4.9204 -6.20167,12.2496 -7.22666,21.9875 z m 93.17774,-67.1925 h 58.4283 v 23.8326 c 8.40539,-9.9429 16.88773,-17.0158 25.44706,-21.2187 8.55912,-4.2026 18.88657,-6.304 30.98238,-6.3041 13.01809,1e-4 23.31991,2.3065 30.90549,6.9191 7.58526,4.6129 13.78685,11.4808 18.6048,20.6037 9.84037,-10.6605 18.80962,-17.9128 26.90778,-21.7569 8.09773,-3.8438 18.09204,-5.7658 29.98294,-5.7659 17.52823,1e-4 31.21274,5.2023 41.05357,15.6065 9.84026,10.4044 14.76054,26.6772 14.76083,48.8184 v 102.557 h -62.73354 v -93.024 c -2.3e-4,-7.3803 -1.43531,-12.8644 -4.30524,-16.4522 -4.20297,-5.6377 -9.43076,-8.4566 -15.68339,-8.4567 -7.38062,10e-5 -13.32595,2.6652 -17.83601,7.9954 -4.51044,5.3304 -6.76557,13.8897 -6.76538,25.6777 v 84.2598 h -62.73355 v -89.9488 c -1.2e-4,-7.1753 -0.41015,-12.0444 -1.23007,-14.6071 -1.3327,-4.1001 -3.63907,-7.4059 -6.91914,-9.9174 -3.2803,-2.5113 -7.12426,-3.767 -11.5319,-3.7671 -7.1755,10e-5 -13.06958,2.7165 -17.68225,8.1492 -4.61284,5.4329 -6.91922,14.3509 -6.91914,26.754 v 83.3372 h -62.73354 z m 316.74293,-62.1185 h 62.57978 v 42.5911 h -62.57978 z m 0,62.1185 h 62.57978 v 163.2917 h -62.57978 z m 95.79168,0 h 151.45231 v 36.5945 l -82.41466,83.9523 h 87.48869 v 42.7449 H -366.998 v -40.5923 l 78.10941,-79.9545 h -71.95906 z"
- inkscape:connector-curvature="0" />
- <path
- style="display:inline;overflow:visible;visibility:visible;fill:#d19f34;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:40;marker:none;enable-background:accumulate"
- id="use16735"
- d="m -1336.1632,1788.2263 c 0,0 56.3913,141.5671 -147.8368,147.7737 -204.2612,6.2076 -147.8368,147.7737 -147.8368,147.7737 -37.8479,-37.8317 -61.2676,-90.0855 -61.2676,-147.7737 0,-57.6882 23.4197,-109.942 61.2676,-147.7737 37.8479,-37.8318 90.124,-61.2415 147.8368,-61.2415 57.7128,0 109.9889,23.4097 147.8368,61.2415 z"
- inkscape:connector-curvature="0" />
- <path
- style="display:inline;overflow:visible;visibility:visible;fill:#469837;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:40;marker:none;enable-background:accumulate"
- id="use16737"
- d="m -1631.8368,2083.7737 c 0,0 -56.3913,-141.5671 147.8368,-147.7737 204.2612,-6.2076 147.8368,-147.7737 147.8368,-147.7737 37.8479,37.8317 61.2676,90.0855 61.2676,147.7737 0,57.6882 -23.4197,109.942 -61.2676,147.7737 -37.8479,37.8318 -90.124,61.2415 -147.8368,61.2415 -57.7128,0 -109.9889,-23.4097 -147.8368,-61.2415 z"
- inkscape:connector-curvature="0" />
- <path
- style="display:inline;overflow:visible;visibility:visible;fill:none;stroke:#4c4c4c;stroke-width:49.86504364;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10.43299961;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
- id="path16739"
- transform="matrix(0.8023362,0,0,0.8019941,-2094.2929,1769.2301)"
- d="m 1021.2642,207.94408 c 0,143.9361 -116.68326,260.61936 -260.61936,260.61936 -143.9361,0 -260.61936,-116.68326 -260.61936,-260.61936 0,-143.936098 116.68326,-260.61936 260.61936,-260.61936 143.9361,0 260.61936,116.683262 260.61936,260.61936 z"
- inkscape:connector-curvature="0" />
- <path
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:306.80871582px;line-height:125%;font-family:'Arial Black';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:40;marker:none;enable-background:accumulate"
- id="path16741"
- d="m -1577.2331,1825.1726 h 127.038 c 21.1728,2e-4 37.4271,5.2435 48.7628,15.7299 11.3353,10.4868 17.0031,23.4703 17.0033,38.9503 -2e-4,12.9836 -4.045,24.1194 -12.1345,33.4074 -5.3933,6.1923 -13.2833,11.086 -23.6698,14.6813 15.7797,3.7953 27.3898,10.312 34.8306,19.5501 7.4402,9.2383 11.1605,20.8485 11.1607,34.8306 -2e-4,11.3855 -2.6468,21.6224 -7.9399,30.7108 -5.2934,9.0884 -12.5342,16.2792 -21.7223,21.5725 -5.6929,3.2958 -14.2819,5.6927 -25.7671,7.1908 -15.2807,1.9975 -25.4177,2.9962 -30.4112,2.9962 h -117.1506 z m 68.4627,86.1401 h 29.5124 c 10.5863,10e-5 17.9519,-1.8225 22.0968,-5.468 4.1445,-3.6452 6.2169,-8.9135 6.217,-15.8049 -1e-4,-6.3916 -2.0725,-11.3853 -6.217,-14.9809 -4.1449,-3.5952 -11.3607,-5.3929 -21.6474,-5.3931 h -29.9618 z m 0,86.29 h 34.6059 c 11.6849,0 19.9244,-2.0723 24.7184,-6.2171 4.7938,-4.1447 7.1907,-9.7126 7.1909,-16.7037 -2e-4,-6.4916 -2.3722,-11.71 -7.116,-15.655 -4.7441,-3.9449 -13.0584,-5.9174 -24.9432,-5.9175 h -34.456 z"
- inkscape:connector-curvature="0" />
- <g
- id="use16745"
- mask="url(#mask6467)"
- transform="rotate(180,-563.99995,766)">
- <path
- style="display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;filter:url(#filter6447);enable-background:accumulate"
- id="use6863"
- d="m 507.8125,-566.84375 c -7.54052,0.31127 -14.32442,4.87714 -17.4375,11.84375 -3.32061,7.43106 -1.79456,16.12851 3.84375,22 71.38742,76.4228 67.29917,195.79932 -9.15625,267.15625 C 418.92868,-204.1201 321.00173,-198.52349 249.15625,-248 l 26.65625,-20.875 5.125,-4 -6.03125,-2.40625 -105.9375,-42.59375 -6,-2.4375 0.90625,6.4375 15.9375,113 0.90625,6.4375 5.125,-4 30.59375,-23.96875 c 87.55252,67.36975 213.09347,63.10079 295.9375,-14.21875 92.27769,-86.12407 97.25455,-231.41793 11.09375,-323.65625 -3.63563,-4.00109 -8.72059,-6.38195 -14.125,-6.5625 -0.50858,-0.0174 -1.02855,-0.0208 -1.53125,0 z"
- inkscape:connector-curvature="0" />
- <path
- style="display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:49.86504364;marker:none;enable-background:accumulate"
- id="use6865"
- d="m 507.8125,-566.84375 c -7.54052,0.31127 -14.32442,4.87714 -17.4375,11.84375 -3.32061,7.43106 -1.79456,16.12851 3.84375,22 71.38742,76.4228 67.29917,195.79932 -9.15625,267.15625 C 418.92868,-204.1201 321.00173,-198.52349 249.15625,-248 l 26.65625,-20.875 5.125,-4 -6.03125,-2.40625 -105.9375,-42.59375 -6,-2.4375 0.90625,6.4375 15.9375,113 0.90625,6.4375 5.125,-4 30.59375,-23.96875 c 87.55252,67.36975 213.09347,63.10079 295.9375,-14.21875 92.27769,-86.12407 97.25455,-231.41793 11.09375,-323.65625 -3.63563,-4.00109 -8.72059,-6.38195 -14.125,-6.5625 -0.50858,-0.0174 -1.02855,-0.0208 -1.53125,0 z"
- inkscape:connector-curvature="0" />
- </g>
- <g
- id="g7285"
- mask="url(#mask6467)"
- transform="translate(-1839.8676,2340.0508)">
- <path
- style="display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;filter:url(#filter6447);enable-background:accumulate"
- id="path7287"
- d="m 507.8125,-566.84375 c -7.54052,0.31127 -14.32442,4.87714 -17.4375,11.84375 -3.32061,7.43106 -1.79456,16.12851 3.84375,22 71.38742,76.4228 67.29917,195.79932 -9.15625,267.15625 C 418.92868,-204.1201 321.00173,-198.52349 249.15625,-248 l 26.65625,-20.875 5.125,-4 -6.03125,-2.40625 -105.9375,-42.59375 -6,-2.4375 0.90625,6.4375 15.9375,113 0.90625,6.4375 5.125,-4 30.59375,-23.96875 c 87.55252,67.36975 213.09347,63.10079 295.9375,-14.21875 92.27769,-86.12407 97.25455,-231.41793 11.09375,-323.65625 -3.63563,-4.00109 -8.72059,-6.38195 -14.125,-6.5625 -0.50858,-0.0174 -1.02855,-0.0208 -1.53125,0 z"
- inkscape:connector-curvature="0" />
- <path
- style="display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:49.86504364;marker:none;enable-background:accumulate"
- id="path7289"
- d="m 507.8125,-566.84375 c -7.54052,0.31127 -14.32442,4.87714 -17.4375,11.84375 -3.32061,7.43106 -1.79456,16.12851 3.84375,22 71.38742,76.4228 67.29917,195.79932 -9.15625,267.15625 C 418.92868,-204.1201 321.00173,-198.52349 249.15625,-248 l 26.65625,-20.875 5.125,-4 -6.03125,-2.40625 -105.9375,-42.59375 -6,-2.4375 0.90625,6.4375 15.9375,113 0.90625,6.4375 5.125,-4 30.59375,-23.96875 c 87.55252,67.36975 213.09347,63.10079 295.9375,-14.21875 92.27769,-86.12407 97.25455,-231.41793 11.09375,-323.65625 -3.63563,-4.00109 -8.72059,-6.38195 -14.125,-6.5625 -0.50858,-0.0174 -1.02855,-0.0208 -1.53125,0 z"
- inkscape:connector-curvature="0" />
- </g>
- <path
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:314.89779663px;line-height:125%;font-family:'Arial Black';text-align:start;writing-mode:lr-tb;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#dadada;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10.43299961;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
- id="text16729"
- d="m -1166.8587,1976.761 h -125.4671 c 1.1275,10.0456 3.8439,17.5285 8.1492,22.4487 6.0478,7.073 13.9407,10.6094 23.6789,10.6094 6.1502,0 11.993,-1.5376 17.5285,-4.6128 3.3825,-1.9476 7.0215,-5.3815 10.9168,-10.3018 l 61.6573,5.6891 c -9.4307,16.4009 -20.8089,28.1634 -34.1345,35.2876 -13.3259,7.1241 -32.4432,10.6862 -57.3519,10.6862 -21.6289,0 -38.6448,-3.0495 -51.0479,-9.1486 -12.4033,-6.0991 -22.6795,-15.7859 -30.8286,-29.0604 -8.1493,-13.2745 -12.2239,-28.881 -12.2239,-46.8195 0,-25.5239 8.1749,-46.1788 24.5245,-61.9648 16.3497,-15.7857 38.9265,-23.6787 67.7307,-23.6788 23.3712,1e-4 41.8223,3.5366 55.3532,10.6093 13.5306,7.0731 23.8324,17.3236 30.9055,30.7517 7.0727,13.4284 10.6091,30.9056 10.6093,52.4318 z m -63.6561,-29.983 c -1.2302,-12.0956 -4.4847,-20.7573 -9.7637,-25.9852 -5.2791,-5.2277 -12.2239,-7.8416 -20.8343,-7.8417 -9.9431,10e-5 -17.8873,3.9466 -23.8325,11.8394 -3.7928,4.9204 -6.2017,12.2496 -7.2267,21.9875 z m 93.3316,-67.1925 h 58.582 v 26.754 c 5.6377,-11.583 11.4549,-19.5528 17.4516,-23.9095 5.9965,-4.3563 13.4025,-6.5346 22.2182,-6.5347 9.2253,1e-4 19.3222,2.8703 30.2904,8.6104 l -19.3736,44.5901 c -7.3805,-3.0751 -13.2233,-4.6127 -17.5285,-4.6128 -8.2005,10e-5 -14.5559,3.3828 -19.066,10.1481 -6.458,9.5331 -9.6869,27.3691 -9.6868,53.508 v 54.7381 h -62.8873 z m 320.2793,97.1755 h -125.46709 c 1.12748,10.0456 3.84388,17.5285 8.14921,22.4487 6.04775,7.073 13.94069,10.6094 23.67883,10.6094 6.15024,0 11.99306,-1.5376 17.5285,-4.6128 3.38256,-1.9476 7.02151,-5.3815 10.91686,-10.3018 l 61.65724,5.6891 c -9.43072,16.4009 -20.80885,28.1634 -34.13443,35.2876 -13.3259,7.1241 -32.44322,10.6862 -57.35199,10.6862 -21.62881,0 -38.64475,-3.0495 -51.04789,-9.1486 -12.40324,-6.0991 -22.67944,-15.7859 -30.82862,-29.0604 -8.14922,-13.2745 -12.22382,-28.881 -12.22382,-46.8195 0,-25.5239 8.17483,-46.1788 24.52452,-61.9648 16.34962,-15.7857 38.9265,-23.6787 67.7307,-23.6788 23.3712,1e-4 41.82222,3.5366 55.35313,10.6093 13.53059,7.0731 23.83241,17.3236 30.9055,30.7517 7.0727,13.4284 10.60915,30.9056 10.60935,52.4318 z m -63.6561,-29.983 c -1.23021,-12.0956 -4.48476,-20.7573 -9.76368,-25.9852 -5.27917,-5.2277 -12.22393,-7.8416 -20.8343,-7.8417 -9.94316,10e-5 -17.88735,3.9466 -23.8326,11.8394 -3.79279,4.9204 -6.20167,12.2496 -7.22666,21.9875 z m 93.17774,-67.1925 h 58.4283 v 23.8326 c 8.40539,-9.9429 16.88773,-17.0158 25.44706,-21.2187 8.55912,-4.2026 18.88657,-6.304 30.98238,-6.3041 13.01809,1e-4 23.31991,2.3065 30.90549,6.9191 7.58526,4.6129 13.78685,11.4808 18.6048,20.6037 9.84037,-10.6605 18.80962,-17.9128 26.90778,-21.7569 8.09773,-3.8438 18.09204,-5.7658 29.98294,-5.7659 17.52823,1e-4 31.21274,5.2023 41.05357,15.6065 9.84026,10.4044 14.76054,26.6772 14.76083,48.8184 v 102.557 h -62.73354 v -93.024 c -2.3e-4,-7.3803 -1.43531,-12.8644 -4.30524,-16.4522 -4.20297,-5.6377 -9.43076,-8.4566 -15.68339,-8.4567 -7.38062,10e-5 -13.32595,2.6652 -17.83601,7.9954 -4.51044,5.3304 -6.76557,13.8897 -6.76538,25.6777 v 84.2598 h -62.73355 v -89.9488 c -1.2e-4,-7.1753 -0.41015,-12.0444 -1.23007,-14.6071 -1.3327,-4.1001 -3.63907,-7.4059 -6.91914,-9.9174 -3.2803,-2.5113 -7.12426,-3.767 -11.5319,-3.7671 -7.1755,10e-5 -13.06958,2.7165 -17.68225,8.1492 -4.61284,5.4329 -6.91922,14.3509 -6.91914,26.754 v 83.3372 h -62.73354 z m 316.74293,-62.1185 h 62.57978 v 42.5911 h -62.57978 z m 0,62.1185 h 62.57978 v 163.2917 h -62.57978 z m 95.79168,0 h 151.45231 v 36.5945 l -82.41466,83.9523 h 87.48869 v 42.7449 H -380.998 v -40.5923 l 78.10941,-79.9545 h -71.95906 z"
- inkscape:connector-curvature="0" />
- <text
- style="font-style:normal;font-weight:normal;font-size:71.35877228px;line-height:0%;font-family:'Bitstream Vera Sans';display:inline;fill:#000000;fill-opacity:1;stroke:none"
- xml:space="preserve"
- id="text3172"
- y="2147.7705"
- x="-1259.6362"><tspan
- id="tspan3174"
- y="2147.7705"
- x="-1259.6362">Free Software for Automation </tspan></text>
- </g>
+ transform="translate(0,107.89276)" />
+ </g>
+ <g
+ transform="matrix(8.4066435,0,0,8.4066435,-1377.662,86.510482)"
+ style="stroke-width:0.26217723"
+ id="g52678">
+ <path
+ style="opacity:1;vector-effect:none;fill:#9acd32;fill-opacity:1;stroke:none;stroke-width:0.06936772;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none"
+ d="m 43.7625,109.87305 h 50.998012 l -1.961981,2.54 H 41.80053 Z"
+ id="rect482-3"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ style="opacity:1;vector-effect:none;fill:#ff8c00;fill-opacity:1;stroke:none;stroke-width:0.06936772;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none"
+ d="m 46.296709,106.59222 h 50.998 l -1.847508,2.39183 H 44.44919 Z"
+ id="rect480-6"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ d="m 46.983389,108.98405 h 2.508251 c 1.05833,0 1.5875,-0.45508 1.5875,-1.25941 0,-0.21872 -0.0388,-0.40569 -0.11642,-0.56092 -0.0705,-0.15522 -0.15522,-0.26811 -0.254,-0.33866 -0.0917,-0.0776 -0.22225,-0.13759 -0.39158,-0.17992 -0.16228,-0.0423 -0.26018,-0.0529 -0.39158,-0.0529 h -0.39159 -2.550581 v -0.88899 h 2.730501 c 0.49389,0 0.88194,0.0388 1.16417,0.11641 0.28222,0.0705 0.53622,0.21873 0.762,0.4445 0.35983,0.34573 0.53975,0.80081 0.53975,1.36525 0,0.23989 -0.0353,0.45862 -0.10583,0.65617 -0.0706,0.1905 -0.14817,0.3422 -0.23284,0.45508 -0.0847,0.11289 -0.20108,0.21873 -0.34925,0.3175 -0.14111,0.0917 -0.24694,0.15523 -0.3175,0.1905 -0.0706,0.0282 -0.16581,0.0635 -0.28575,0.10584 0.13406,0.0353 0.24695,0.0706 0.33867,0.10583 0.0917,0.0353 0.21872,0.10231 0.381,0.20108 0.16933,0.0988 0.30339,0.21167 0.40217,0.33867 0.10583,0.11995 0.19755,0.29281 0.27516,0.51858 0.0847,0.21873 0.127,0.46567 0.127,0.74084 0,0.74789 -0.30691,1.32644 -0.92075,1.73566 -0.16227,0.10584 -0.35277,0.18698 -0.5715,0.24342 -0.21167,0.0564 -0.39158,0.0882 -0.53975,0.0952 -0.14111,0.007 -0.34572,0.0106 -0.61383,0.0106 l -2.783421,1e-5 v -0.93135 h 2.836331 c 0.43745,0 0.79728,-0.0388 1.0795,-0.26457 0.28223,-0.23284 0.42334,-0.56092 0.42334,-0.98425 0,-0.38806 -0.13053,-0.70556 -0.39159,-0.9525 -0.254,-0.24695 -0.6103,-0.33868 -1.06891,-0.33868 h -2.878671 z"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:1.25;font-family:'Univers LT Std';-inkscape-font-specification:'Univers LT Std, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.06936772"
+ id="path415-7"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="csscccscccsccscccccccccsccccccscscscc" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path417-5"
+ transform="matrix(0.06936772,0,0,0.06936772,41.661795,105.56437)"
+ d="M 175.58008,2.0019531 V 14.970703 h 69.11328 V 2.0019531 Z m 0,47.2949219 v 0.002 12.814453 36.617188 13.425784 0.002 h 15.25781 v -0.002 h 55.07617 V 98.730469 H 190.83789 V 62.113281 h 51.26172 V 49.298828 h -51.26172 v -0.002 z"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:1.25;font-family:'Univers LT Std';-inkscape-font-specification:'Univers LT Std, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1" />
+ <path
+ d="m 60.41363,108.99464 h 2.69874 c 0.47978,0 0.81492,-0.0564 1.00542,-0.16933 0.34572,-0.19756 0.51858,-0.52917 0.51858,-0.99484 0,-0.23283 -0.0423,-0.43391 -0.127,-0.60325 -0.0847,-0.16933 -0.17992,-0.2928 -0.28575,-0.37041 -0.0988,-0.0776 -0.23283,-0.13759 -0.40216,-0.17992 -0.16228,-0.0423 -0.28928,-0.067 -0.381,-0.0741 -0.0847,-0.007 -0.19403,-0.0106 -0.32809,-0.0106 l -2.69874,-1e-5 v -0.889 h 2.73049 c 0.79728,0 1.38289,0.11995 1.75684,0.35984 0.56444,0.35278 0.84666,0.87136 0.84666,1.55575 0,0.54328 -0.17992,0.99483 -0.53975,1.35466 -0.23283,0.23284 -0.53269,0.39864 -0.89958,0.49742 0.37395,0.0988 0.64911,0.254 0.8255,0.46567 0.17639,0.20461 0.28928,0.53975 0.33867,1.00541 0.11994,1.04423 0.27869,1.84503 0.47625,2.40242 h -1.13242 c -0.0988,-0.29633 -0.2152,-0.97014 -0.34925,-2.02142 -0.0635,-0.5715 -0.20105,-0.9525 -0.41275,-1.143 -0.21167,-0.1905 -0.63147,-0.28575 -1.25942,-0.28575 h -2.38124 z"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:1.25;font-family:'Univers LT Std';-inkscape-font-specification:'Univers LT Std, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.06936772"
+ id="path419"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cscssccccccscsscccccccscc" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path421"
+ transform="matrix(0.06936772,0,0,0.06936772,41.661795,105.56437)"
+ d="M 370.5625,2.0019531 V 14.970703 h 69.11328 V 2.0019531 Z m 0,47.2949219 v 0.002 12.814453 36.617188 13.425784 0.002 h 15.25586 v -0.002 h 55.07812 V 98.730469 H 385.81836 V 62.113281 h 51.26367 V 49.298828 h -51.26367 v -0.002 z"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:1.25;font-family:'Univers LT Std';-inkscape-font-specification:'Univers LT Std, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1" />
+ <path
+ sodipodi:nodetypes="cccccccccc"
+ inkscape:connector-curvature="0"
+ id="path423"
+ d="m 83.95087,105.70311 v 0.88911 h 1.05833 v -0.88911 z m 0,3.28094 v 4.36046 h 1.05833 v -4.36046 z"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:1.25;font-family:'Univers LT Std';-inkscape-font-specification:'Univers LT Std, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.06936772" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path425"
+ d="m 73.98146,106.60281 v -0.89959 h 1.873271 l 1.429369,4.16984 h -1.0883 l -1.18753,-3.27025 z"
+ style="fill:#000000;stroke:none;stroke-width:0.06936772px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path427"
+ d="m 79.07313,108.98405 -1.299141,3.42925 h -0.68162 l 0.328661,0.93121 h 1.005631 l 0.33951,-0.93121 1.250059,-3.42925 z"
+ style="fill:#000000;stroke:none;stroke-width:0.06936772px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path429"
+ d="m 80.22551,105.70311 -0.315741,0.89969 h 1.024753 v 2.38125 0.88883 h 1.037137 v -0.88883 -2.38125 -0.89969 z"
+ style="fill:#000000;stroke:none;stroke-width:0.06936772px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path431"
+ d="m 80.93453,112.41306 -8e-6,0.93133 h 1.037148 v -0.93133 z"
+ style="fill:#000000;stroke:none;stroke-width:0.06936772px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:#000000;stroke:none;stroke-width:0.06936772px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 73.981409,108.98405 v 0.78341 0.10542 3.47163 h 1.026811 v -3.47163 -0.10542 -0.78341 z"
+ id="path433"
+ inkscape:connector-curvature="0" />
+ <path
+ sodipodi:nodetypes="ccccccccccccc"
+ inkscape:connector-curvature="0"
+ id="path435"
+ d="m 86.448391,105.70311 v 0.93121 h 5.55625 l 0.727599,-0.93121 z m 2.462379,3.28094 -2.57865,3.30212 v 1.05834 h 5.746929 V 112.4133 H 87.5491 l 2.698541,-3.42925 z"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:1.25;font-family:'Univers LT Std';-inkscape-font-specification:'Univers LT Std, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.06936772" />
+ <path
+ style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.06936772;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none"
+ d="m 60.413582,112.41305 h 1.04775 v 0.93134 h -1.04775 z"
+ id="rect474"
+ inkscape:connector-curvature="0" />
</g>
</g>
<text
@@ -90261,8 +90277,9 @@
height="178.66196"
x="-1492.3632"
y="2013.0917"
- id="ico64"
- style="display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:0;marker:none;enable-background:accumulate" />
+ id="ico064"
+ style="display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:0;marker:none;enable-background:accumulate"
+ inkscape:label="#ico064" />
<use
transform="translate(223.29672,0.68429344)"
id="use15274"
@@ -90276,69 +90293,9 @@
id="g19356"
mask="url(#mask6467)"
transform="translate(-1840,2339.9999)" />
- <g
- id="g19358"
- transform="matrix(0.3669311,0,0,0.3669311,-1086.1197,1386.8468)">
- <path
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:314.89779663px;line-height:125%;font-family:'Arial Black';text-align:start;writing-mode:lr-tb;text-anchor:start;display:inline;overflow:visible;visibility:visible;opacity:0.5;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;filter:url(#filter19931);enable-background:accumulate"
- id="path19360"
- d="m -1253,2027.2478 c -3.5746,10.6475 -4.3073,15.1469 -15.75,36.2188 -11.4427,21.0719 -26.4108,40.6733 -44.875,57.9062 -82.844,77.3195 -208.385,81.5885 -295.9375,14.2188 l -30.5938,23.9687 -5.125,4 -0.9062,-6.4375 -9.7813,-69.3437 c -66.3515,-92.0452 -55.4856,-221.7505 29.5938,-301.1563 44.0109,-41.076 100.0799,-61.5397 156.0937,-61.5937 49.4241,-0.048 98.8036,15.7954 139.8438,47.375 l 30.5937,-23.9688 5.125,-4 0.9063,6.4375 9.7812,69.3438 c 16.3717,22.7113 28.0479,47.7118 35.0625,73.6875 7.0146,25.9757 5.7125,26.1967 6.8125,37.3437 1.1,11.147 1.3998,22.3595 0.875,33.5313 -0.5248,11.1718 -1.4013,18.9792 -3.375,29.9687 -1.9737,10.9895 -4.7691,21.8525 -8.3437,32.5 z"
- inkscape:connector-curvature="0" />
- <path
- style="display:inline;overflow:visible;visibility:visible;fill:#d19f34;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:40;marker:none;enable-background:accumulate"
- id="path19362"
- d="m -1336.1632,1788.2263 c 0,0 56.3913,141.5671 -147.8368,147.7737 -204.2612,6.2076 -147.8368,147.7737 -147.8368,147.7737 -37.8479,-37.8317 -61.2676,-90.0855 -61.2676,-147.7737 0,-57.6882 23.4197,-109.942 61.2676,-147.7737 37.8479,-37.8318 90.124,-61.2415 147.8368,-61.2415 57.7128,0 109.9889,23.4097 147.8368,61.2415 z"
- inkscape:connector-curvature="0" />
- <path
- style="display:inline;overflow:visible;visibility:visible;fill:#469837;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:40;marker:none;enable-background:accumulate"
- id="path19364"
- d="m -1631.8368,2083.7737 c 0,0 -56.3913,-141.5671 147.8368,-147.7737 204.2612,-6.2076 147.8368,-147.7737 147.8368,-147.7737 37.8479,37.8317 61.2676,90.0855 61.2676,147.7737 0,57.6882 -23.4197,109.942 -61.2676,147.7737 -37.8479,37.8318 -90.124,61.2415 -147.8368,61.2415 -57.7128,0 -109.9889,-23.4097 -147.8368,-61.2415 z"
- inkscape:connector-curvature="0" />
- <path
- style="display:inline;overflow:visible;visibility:visible;fill:none;stroke:#4c4c4c;stroke-width:49.86504364;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10.43299961;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
- id="path19366"
- transform="matrix(0.8023362,0,0,0.8019941,-2094.2929,1769.2301)"
- d="m 1021.2642,207.94408 c 0,143.9361 -116.68326,260.61936 -260.61936,260.61936 -143.9361,0 -260.61936,-116.68326 -260.61936,-260.61936 0,-143.936098 116.68326,-260.61936 260.61936,-260.61936 143.9361,0 260.61936,116.683262 260.61936,260.61936 z"
- inkscape:connector-curvature="0" />
- <path
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:306.80871582px;line-height:125%;font-family:'Arial Black';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:40;marker:none;enable-background:accumulate"
- id="path19368"
- d="m -1577.2331,1825.1726 h 127.038 c 21.1728,2e-4 37.4271,5.2435 48.7628,15.7299 11.3353,10.4868 17.0031,23.4703 17.0033,38.9503 -2e-4,12.9836 -4.045,24.1194 -12.1345,33.4074 -5.3933,6.1923 -13.2833,11.086 -23.6698,14.6813 15.7797,3.7953 27.3898,10.312 34.8306,19.5501 7.4402,9.2383 11.1605,20.8485 11.1607,34.8306 -2e-4,11.3855 -2.6468,21.6224 -7.9399,30.7108 -5.2934,9.0884 -12.5342,16.2792 -21.7223,21.5725 -5.6929,3.2958 -14.2819,5.6927 -25.7671,7.1908 -15.2807,1.9975 -25.4177,2.9962 -30.4112,2.9962 h -117.1506 z m 68.4627,86.1401 h 29.5124 c 10.5863,10e-5 17.9519,-1.8225 22.0968,-5.468 4.1445,-3.6452 6.2169,-8.9135 6.217,-15.8049 -1e-4,-6.3916 -2.0725,-11.3853 -6.217,-14.9809 -4.1449,-3.5952 -11.3607,-5.3929 -21.6474,-5.3931 h -29.9618 z m 0,86.29 h 34.6059 c 11.6849,0 19.9244,-2.0723 24.7184,-6.2171 4.7938,-4.1447 7.1907,-9.7126 7.1909,-16.7037 -2e-4,-6.4916 -2.3722,-11.71 -7.116,-15.655 -4.7441,-3.9449 -13.0584,-5.9174 -24.9432,-5.9175 h -34.456 z"
- inkscape:connector-curvature="0" />
- <g
- id="g19370"
- mask="url(#mask6467)"
- transform="rotate(180,-563.99995,766)">
- <path
- style="display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;filter:url(#filter6447);enable-background:accumulate"
- id="path19372"
- d="m 507.8125,-566.84375 c -7.54052,0.31127 -14.32442,4.87714 -17.4375,11.84375 -3.32061,7.43106 -1.79456,16.12851 3.84375,22 71.38742,76.4228 67.29917,195.79932 -9.15625,267.15625 C 418.92868,-204.1201 321.00173,-198.52349 249.15625,-248 l 26.65625,-20.875 5.125,-4 -6.03125,-2.40625 -105.9375,-42.59375 -6,-2.4375 0.90625,6.4375 15.9375,113 0.90625,6.4375 5.125,-4 30.59375,-23.96875 c 87.55252,67.36975 213.09347,63.10079 295.9375,-14.21875 92.27769,-86.12407 97.25455,-231.41793 11.09375,-323.65625 -3.63563,-4.00109 -8.72059,-6.38195 -14.125,-6.5625 -0.50858,-0.0174 -1.02855,-0.0208 -1.53125,0 z"
- inkscape:connector-curvature="0" />
- <path
- style="display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:49.86504364;marker:none;enable-background:accumulate"
- id="path19374"
- d="m 507.8125,-566.84375 c -7.54052,0.31127 -14.32442,4.87714 -17.4375,11.84375 -3.32061,7.43106 -1.79456,16.12851 3.84375,22 71.38742,76.4228 67.29917,195.79932 -9.15625,267.15625 C 418.92868,-204.1201 321.00173,-198.52349 249.15625,-248 l 26.65625,-20.875 5.125,-4 -6.03125,-2.40625 -105.9375,-42.59375 -6,-2.4375 0.90625,6.4375 15.9375,113 0.90625,6.4375 5.125,-4 30.59375,-23.96875 c 87.55252,67.36975 213.09347,63.10079 295.9375,-14.21875 92.27769,-86.12407 97.25455,-231.41793 11.09375,-323.65625 -3.63563,-4.00109 -8.72059,-6.38195 -14.125,-6.5625 -0.50858,-0.0174 -1.02855,-0.0208 -1.53125,0 z"
- inkscape:connector-curvature="0" />
- </g>
- <g
- id="g19376"
- mask="url(#mask6467)"
- transform="translate(-1839.8676,2340.0508)">
- <path
- style="display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;filter:url(#filter6447);enable-background:accumulate"
- id="path19378"
- d="m 507.8125,-566.84375 c -7.54052,0.31127 -14.32442,4.87714 -17.4375,11.84375 -3.32061,7.43106 -1.79456,16.12851 3.84375,22 71.38742,76.4228 67.29917,195.79932 -9.15625,267.15625 C 418.92868,-204.1201 321.00173,-198.52349 249.15625,-248 l 26.65625,-20.875 5.125,-4 -6.03125,-2.40625 -105.9375,-42.59375 -6,-2.4375 0.90625,6.4375 15.9375,113 0.90625,6.4375 5.125,-4 30.59375,-23.96875 c 87.55252,67.36975 213.09347,63.10079 295.9375,-14.21875 92.27769,-86.12407 97.25455,-231.41793 11.09375,-323.65625 -3.63563,-4.00109 -8.72059,-6.38195 -14.125,-6.5625 -0.50858,-0.0174 -1.02855,-0.0208 -1.53125,0 z"
- inkscape:connector-curvature="0" />
- <path
- style="display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:49.86504364;marker:none;enable-background:accumulate"
- id="path19380"
- d="m 507.8125,-566.84375 c -7.54052,0.31127 -14.32442,4.87714 -17.4375,11.84375 -3.32061,7.43106 -1.79456,16.12851 3.84375,22 71.38742,76.4228 67.29917,195.79932 -9.15625,267.15625 C 418.92868,-204.1201 321.00173,-198.52349 249.15625,-248 l 26.65625,-20.875 5.125,-4 -6.03125,-2.40625 -105.9375,-42.59375 -6,-2.4375 0.90625,6.4375 15.9375,113 0.90625,6.4375 5.125,-4 30.59375,-23.96875 c 87.55252,67.36975 213.09347,63.10079 295.9375,-14.21875 92.27769,-86.12407 97.25455,-231.41793 11.09375,-323.65625 -3.63563,-4.00109 -8.72059,-6.38195 -14.125,-6.5625 -0.50858,-0.0174 -1.02855,-0.0208 -1.53125,0 z"
- inkscape:connector-curvature="0" />
- </g>
- </g>
<rect
- style="display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:0;marker:none;enable-background:accumulate"
- id="ico48"
+ style="display:inline;overflow:visible;visibility:visible;fill:none;fill-opacity:1;stroke:none;stroke-width:0;marker:none;enable-background:accumulate"
+ id="ico048"
y="2012.4073"
x="-1715.66"
height="178.66196"
@@ -90348,7 +90305,7 @@
transform="matrix(0.5,0,0,0.5,-593.54387,1095.1925)">
<rect
style="display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:0;marker:none;enable-background:accumulate"
- id="ico24"
+ id="ico024"
y="2013.0917"
x="-1492.3632"
height="178.66196"
@@ -90367,7 +90324,7 @@
transform="matrix(0.6666666,0,0,0.6666666,-493.70173,729.90034)">
<rect
style="display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:0;marker:none;enable-background:accumulate"
- id="ico32"
+ id="ico032"
y="2013.0917"
x="-1492.3632"
height="178.66196"
@@ -90389,7 +90346,7 @@
height="178.66196"
x="-1492.3632"
y="2013.0917"
- id="ico16"
+ id="ico016"
style="display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:0;marker:none;enable-background:accumulate" />
<use
transform="translate(223.29672,0.68429344)"
@@ -90400,6 +90357,49 @@
height="1052.3622"
xlink:href="#g19358" />
</g>
+ <g
+ id="g19358"
+ transform="matrix(0.3669311,0,0,0.3669311,-1086.1197,1386.8468)">
+ <g
+ id="g837"
+ transform="matrix(0.48690873,0,0,0.48690873,-1715.6909,1704.8447)"
+ inkscape:label="g837">
+ <rect
+ id="rect2"
+ ry="200"
+ rx="200"
+ height="1000"
+ width="1000"
+ x="0"
+ y="0"
+ style="fill:#eeeeec" />
+ <path
+ id="rect482"
+ d="M 0,526.82227 V 721.9043 H 1000 V 526.82227 Z"
+ style="opacity:1;vector-effect:none;fill:#9acd32;fill-opacity:1;stroke:none;stroke-width:5.32772112;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none"
+ inkscape:connector-curvature="0" />
+ <path
+ id="rect480"
+ d="M 0,274.8418 V 458.54297 H 1000 V 274.8418 Z"
+ style="opacity:1;vector-effect:none;fill:#ff8c00;fill-opacity:1;stroke:none;stroke-width:5.32772112;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none"
+ inkscape:connector-curvature="0" />
+ <g
+ transform="translate(9.7650495)"
+ id="g145">
+ <path
+ sodipodi:nodetypes="csscccscccsccscccccccccsccccccscscscc"
+ inkscape:connector-curvature="0"
+ id="path415"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:1.25;font-family:'Univers LT Std';-inkscape-font-specification:'Univers LT Std, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:5.32772112"
+ d="M 39.514141,458.54323 H 232.15795 c 81.28402,0 121.92641,-34.95199 121.92641,-96.72778 0,-16.79858 -2.98,-31.15863 -8.94153,-43.08092 -5.41468,-11.92152 -11.92152,-20.59193 -19.50822,-26.01046 -7.04293,-5.95999 -17.0697,-10.56746 -30.07493,-13.81858 -12.46376,-3.24881 -19.98287,-4.06293 -30.07492,-4.06293 H 235.40906 39.514141 V 206.56454 H 249.22765 c 37.93274,0 67.73655,2.97999 89.41295,8.94075 21.67564,5.41469 41.18387,16.79935 58.52468,34.1394 27.6364,26.55346 41.45498,61.50544 41.45498,104.85671 0,18.42452 -2.71118,35.22387 -8.12817,50.39651 -5.42236,14.63117 -11.38005,26.28234 -17.88305,34.95198 -6.5053,8.67041 -15.44376,16.79935 -26.82381,24.38528 -10.83782,7.04293 -18.96599,11.92229 -24.38528,14.63117 -5.42237,2.16588 -12.73488,4.87706 -21.94676,8.12894 10.29635,2.71119 18.96676,5.42237 26.01123,8.12817 7.04293,2.71119 16.79857,7.85783 29.26233,15.44376 13.00523,7.58824 23.30158,16.25711 30.88828,26.01122 8.12818,9.21265 15.17264,22.48899 21.1334,39.82904 6.50531,16.79935 9.75412,35.76534 9.75412,56.89951 0,57.44097 -23.57193,101.87595 -70.71732,133.3057 -12.46299,8.12893 -27.09416,14.36081 -43.89351,18.69563 -16.25711,4.33175 -30.07493,6.77412 -41.45498,7.31175 -10.83782,0.53763 -26.55269,0.81412 -47.14462,0.81412 l -213.777979,7.7e-4 V 721.90351 H 257.35582 c 33.59792,0 61.23432,-2.98 82.90996,-20.32005 21.6764,-17.88305 32.51422,-43.08092 32.51422,-75.59437 0,-29.80458 -10.02523,-54.18986 -30.07569,-73.15585 C 323.19608,533.86648 295.8308,526.82125 260.6077,526.82125 H 39.514141 Z" />
+ <path
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:1.25;font-family:'Univers LT Std';-inkscape-font-specification:'Univers LT Std, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:5.32772112"
+ d="m 566.23593,206.56384 v 69.09388 h 368.21628 v -69.09388 z m 0,251.97415 v 0.0107 68.27183 195.08616 71.52884 0.0107 h 81.28935 v -0.0107 H 940.95576 V 721.90639 H 647.52528 V 526.82023 H 920.63343 V 458.5484 H 647.52528 v -0.0107 z"
+ id="path417"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+ </g>
</g>
<text
style="font-style:normal;font-weight:normal;font-size:13.88476658px;line-height:0%;font-family:'Bitstream Vera Sans';fill:#000000;fill-opacity:1;stroke:none"
@@ -91172,13 +91172,6 @@
x="37.5"
y="473.61218"
id="tspan16384">Icons</tspan></text>
- <path
- sodipodi:type="inkscape:offset"
- inkscape:radius="1"
- inkscape:original="M 505 135.34375 L 505 147.34375 L 502 147.34375 L 506 151.34375 L 510 147.34375 L 507 147.34375 L 507 135.34375 L 505 135.34375 z M 498 135.375 L 494 139.375 L 497 139.375 L 497 151.375 L 499 151.375 L 499 139.375 L 502 139.375 L 498 135.375 z "
- style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
- id="path18475"
- d="m 504.90625,134.34375 a 1.0001,1.0001 0 0 0 -0.90625,1 l 0,11 -2,0 a 1.0001,1.0001 0 0 0 -0.71875,1.71875 l 4,4 a 1.0001,1.0001 0 0 0 1.4375,0 l 4,-4 A 1.0001,1.0001 0 0 0 510,146.34375 l -2,0 0,-11 a 1.0001,1.0001 0 0 0 -1,-1 l -2,0 a 1.0001,1.0001 0 0 0 -0.0937,0 z m -7.03125,0.0312 a 1.0001,1.0001 0 0 0 -0.59375,0.28125 l -4,4 A 1.0001,1.0001 0 0 0 494,140.375 l 2,0 0,11 a 1.0001,1.0001 0 0 0 1,1 l 2,0 a 1.0001,1.0001 0 0 0 1,-1 l 0,-11 2,0 a 1.0001,1.0001 0 0 0 0.71875,-1.71875 l -4,-4 A 1.0001,1.0001 0 0 0 497.875,134.375 z" />
<rect
width="24"
height="24"
@@ -91187,13 +91180,6 @@
id="Transfer"
style="fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
inkscape:label="#rect16270" />
- <path
- sodipodi:type="inkscape:offset"
- inkscape:radius="1"
- inkscape:original="M 558 141.375 L 555.4375 144.375 L 550 144.375 L 550 145.375 L 555.4375 145.375 L 558 148.375 L 562 148.34375 L 562 144.875 L 562 141.40625 L 558 141.375 z M 567 141.375 L 563 141.40625 L 563 144.875 L 563 148.34375 L 567 148.375 L 569.5625 145.375 L 574 145.375 L 574 144.375 L 569.5625 144.375 L 567 141.375 z "
- style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.10000000000000001;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
- id="path18481"
- d="m 558,141.375 -2.5625,3 -5.4375,0 0,1 5.4375,0 2.5625,3 4,-0.0312 0,-3.46875 0,-3.46875 -4,-0.0312 z m 9,0 -4,0.0312 0,3.46875 0,3.46875 4,0.0312 2.5625,-3 4.4375,0 0,-1 -4.4375,0 -2.5625,-3 z" />
<rect
inkscape:label="#rect16270"
style="fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
@@ -91202,13 +91188,6 @@
x="550"
height="24"
width="24" />
- <path
- sodipodi:type="inkscape:offset"
- inkscape:radius="1"
- inkscape:original="M 616 141.375 L 613.4375 144.375 L 608 144.375 L 608 145.375 L 613.4375 145.375 L 616 148.375 L 620 148.34375 L 620 144.875 L 620 141.40625 L 616 141.375 z M 629 141.375 L 625 141.40625 L 625 144.875 L 625 148.34375 L 629 148.375 L 631.5625 145.375 L 636 145.375 L 636 144.375 L 631.5625 144.375 L 629 141.375 z "
- style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.10000000000000001;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
- id="path18491"
- d="m 616,141.375 -2.5625,3 -5.4375,0 0,1 5.4375,0 2.5625,3 4,-0.0312 0,-3.46875 0,-3.46875 -4,-0.0312 z m 13,0 -4,0.0312 0,3.46875 0,3.46875 4,0.0312 2.5625,-3 4.4375,0 0,-1 -4.4375,0 -2.5625,-3 z" />
<rect
width="24"
height="24"
@@ -91217,12 +91196,6 @@
id="Disconnect"
style="fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
inkscape:label="#rect16270" />
- <path
- style="fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.10000000000000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:8.59499931000000039;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
- d="m 562,141.40625 -4,-0.0441 -2.5625,3.01282 -5.4375,0 0,1 5.4375,0 2.5625,2.98718 4,-0.0184 0,-3.46875 z m 1,0 0,3.46875 0,3.46875 4,0.0184 2.5625,-2.98718 4.4375,0 0,-1 -4.4375,0 L 567,141.36218 z"
- id="path16742"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="cccccccccccccccccccc" />
<rect
inkscape:label="#rect16270"
style="fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
@@ -91254,7 +91227,7 @@
transform="matrix(0.1343319,0,0,0.1343319,885.45598,321.13309)" />
<path
style="fill:#0fff09;fill-opacity:1;fill-rule:evenodd;stroke:#1d0000;stroke-width:0.30481818;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="m 666.0806,603.80447 10.83519,5.4176 -10.83519,5.41759 z"
+ d="m 661.47632,598.046 10.83519,5.4176 -10.83519,5.41759 z"
id="rect16426"
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0" />
@@ -91280,8 +91253,8 @@
id="rect17210"
width="10.821424"
height="10.821424"
- x="666.58472"
- y="634.71997" />
+ x="661.57739"
+ y="629.05286" />
<rect
inkscape:label="#rect16270"
style="fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
@@ -92909,7 +92882,7 @@
x="-1060.788"
y="501.19318"
id="ImportFile"
- style="fill:#000000;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ 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" />
<g
id="g17936"
transform="translate(-1334.2792,308.62148)">
@@ -92932,7 +92905,7 @@
inkscape:connector-curvature="0" />
<path
d="m 277.24218,192.81529 h 11.5 c 0.683,0.2373 4.541,3.1281 5.5,5 0,5.7292 4e-5,11.271 4e-5,17 h -17 v -22 z"
- style="fill:url(#linearGradient17977);stroke:url(#linearGradient17979);stroke-width:0.99992001;stroke-linejoin:round"
+ style="display:inline;fill:url(#linearGradient17977);stroke:url(#linearGradient17979);stroke-width:0.99992001;stroke-linejoin:round"
id="path4160"
inkscape:connector-curvature="0" />
<path
@@ -92968,15 +92941,11 @@
r="34.144001"
transform="matrix(0.3316761,0,0,0.3316761,48.927852,9.2318583)"
id="circle29-8"
- style="fill:#84c225;fill-rule:evenodd;stroke:#5d9d35;stroke-width:2.82220006"
- sodipodi:ry="34.144001"
- sodipodi:rx="34.144001"
- sodipodi:cy="110.081"
- sodipodi:cx="100.287" />
+ style="fill:#84c225;fill-rule:evenodd;stroke:#5d9d35;stroke-width:2.82220006" />
<path
- d="m 84.515333,38.943636 0,3.981494 3.981494,0 0,4.799639 -3.981494,0 0,3.981494 -4.782372,0 0,-3.981494 -3.981494,0 0,-4.799639 3.981494,0 0,-3.981494 4.782372,0"
+ d="m 84.515333,38.943636 v 3.981494 h 3.981494 v 4.799639 h -3.981494 v 3.981494 H 79.732961 V 47.724769 H 75.751467 V 42.92513 h 3.981494 v -3.981494 h 4.782372"
id="text1332-4"
- style="font-size:12px;font-style:normal;font-weight:normal;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
+ style="font-style:normal;font-weight:normal;font-size:12px;font-family:'Bitstream Vera Sans';display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none"
inkscape:connector-curvature="0" />
<path
d="m 82.190677,35.644078 c 5.025924,0 9.194867,3.673581 9.969229,8.481465 -6.921917,-5.82623 -17.958314,0.09291 -16.467662,9.346307 -2.201037,-1.852678 -3.600446,-4.62745 -3.600446,-7.728889 0,-5.576508 4.522377,-10.098883 10.098879,-10.098883 z"
@@ -93722,54 +93691,26 @@
</g>
<path
id="path18470"
- style="color:#000000;fill:#ffcc00;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
- d="m 506,151.34936 3.99998,-4 -2.99999,-1e-5 -3e-5,-11.99998 -1.99998,-2e-5 -10e-6,12 -2.99998,3e-5 4.00001,3.99998 z m -8,-15.98718 -4,4 3,0 0,12 2,0 0,-12 3,0 -4,-4 z" />
- <path
- sodipodi:nodetypes="cccccccccccccccccccc"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffcc00;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;marker:none;enable-background:accumulate"
+ d="m 507,153.36218 4.99998,-6.01282 -2.99999,-1e-5 -3e-5,-13.99998 -3.99998,-2e-5 -10e-6,14 -2.99998,3e-5 z m -10,-20 -5,6 h 3 v 14 h 4 v -14 h 3 z"
inkscape:connector-curvature="0"
- id="path18485"
- d="m 620,141.40625 -4,-0.0441 -2.5625,3.01282 -5.4375,0 0,1 5.4375,0 2.5625,2.98718 4,-0.0184 0,-3.46875 z m 5,0 0,3.46875 0,3.46875 4,0.0184 2.5625,-2.98718 4.4375,0 0,-1 -4.4375,0 L 629,141.36218 z"
- style="fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
- <path
- style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1"
- d="m 620,139.36218 -1,-1"
- id="path18493"
- inkscape:connector-curvature="0" />
- <path
- inkscape:connector-curvature="0"
- id="path19263"
- d="m 625,139.36218 1,-1"
- style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
- <path
- style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1"
- d="m 622.52384,138.1738 0,-1.4142"
- id="path19265"
- inkscape:connector-curvature="0" />
- <path
- inkscape:connector-curvature="0"
- id="path19267"
- d="m 620,150.36218 -1,1"
- style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
- <path
- style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1"
- d="m 625,150.36218 1,1"
- id="path19269"
- inkscape:connector-curvature="0" />
- <path
- inkscape:connector-curvature="0"
- id="path19271"
- d="m 622.52384,151.55056 0,1.4142"
- style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
- <path
- style="fill:#00ff00;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- d="M 79 192.375 L 79 194.375 L 76 194.375 L 76 198.375 L 79 198.375 L 79 200.375 L 83 196.375 L 79 192.375 z M 65 194.375 L 65 198.375 L 66 198.375 L 66 194.375 L 65 194.375 z M 67 194.375 L 67 198.375 L 68 198.375 L 68 194.375 L 67 194.375 z M 69 194.375 L 69 198.375 L 71 198.375 L 71 194.375 L 69 194.375 z M 72 194.375 L 72 198.375 L 75 198.375 L 75 194.375 L 72 194.375 z "
- id="path18382" />
- <path
- sodipodi:nodetypes="ccccccccccccccccccccccccccccc"
- inkscape:connector-curvature="0"
- id="path18467"
- d="m 105,193.88061 0,5.01282 2,0 0,-5.01282 z m 0,6.01282 0,4 2,0 0,-4 z m 0,5 0,3 2,0 0,-3 z m 0,4 0,2 2,0 0,-2 z m -2,3 1,1 4,0 1,-1 z m 2,2 1,1 1,-1 z"
- style="fill:#808080;stroke:none" />
+ sodipodi:nodetypes="cccccccccccccccc" />
+ <g
+ id="g52799"
+ transform="rotate(90,68.506409,196.86859)">
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#808080;stroke:none"
+ d="m 79.943848,189.4415 v 2.9335 h -15.375 v 6.0665 h 15.375 v 3 l 6,-6 z"
+ id="path18402"
+ sodipodi:nodetypes="cccccccc" />
+ <path
+ id="path18382"
+ d="m 79.375,188.375 v 2.9335 H 64 v 6.0665 h 15.375 v 3 l 6,-6 z"
+ style="fill:#00ff00;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccc" />
+ </g>
<g
id="g18416"
clip-path="none">
@@ -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" />
<g
id="g18450"
clip-path="url(#clipPath18454)">
@@ -93803,12 +93744,6 @@
id="path18412" />
</g>
</g>
- <path
- style="fill:#ff0000;stroke:none"
- d="m 104,193.34936 0,5.01282 2,0 0,-5.01282 z m 0,6.01282 0,4 2,0 0,-4 z m 0,5 0,3 2,0 0,-3 z m 0,4 0,2 2,0 0,-2 z m -2,3 1,1 4,0 1,-1 z m 2,2 1,1 1,-1 z"
- id="path18458"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="ccccccccccccccccccccccccccccc" />
<text
style="font-style:normal;font-weight:normal;font-size:20px;line-height:0%;font-family:'Bitstream Vera Sans';fill:#000000;fill-opacity:1;stroke:none"
xml:space="preserve"
@@ -93949,86 +93884,15 @@
style="font-size:51.04000092px">%% about_brz_logo %%</tspan></text>
<g
transform="matrix(0.2224431,0,0,0.2224431,608.34301,1025.5992)"
- id="about_brz_logo"
- inkscape:label="#g17803-0">
- <g
- transform="translate(-1840,2339.9999)"
- mask="url(#mask6467-7-2)"
- id="use16743-9" />
- <g
- transform="translate(0,89.910633)"
- id="g7269-3">
- <path
- inkscape:connector-curvature="0"
- d="m -1470.2813,1725.0291 c -56.0138,0.054 -112.0828,20.5177 -156.0937,61.5937 -85.0794,79.4058 -95.9453,209.1111 -29.5938,301.1563 l 9.7813,69.3437 0.9062,6.4375 5.125,-4 30.5938,-23.9687 c 87.5525,67.3697 213.0935,63.1007 295.9375,-14.2188 18.4642,-17.2329 33.4323,-36.8343 44.875,-57.9062 6.4003,0.736 13.3613,1.0937 20.875,1.0937 24.9087,0 44.0178,-3.5634 57.3437,-10.6875 13.3257,-7.1242 24.6943,-18.8804 34.125,-35.2812 l -61.6562,-5.6875 c -3.8953,4.9202 -7.5237,8.3649 -10.9063,10.3125 -5.5355,3.0752 -11.381,4.5937 -17.5312,4.5937 -2.2646,0 -4.435,-0.18 -6.5,-0.5625 3.5746,-10.6475 6.37,-21.5105 8.3437,-32.5 h 91.8125 v -7.0625 c -10e-5,-21.5262 -3.5522,-39.0091 -10.625,-52.4375 -7.0731,-13.4281 -17.3756,-23.6769 -30.9062,-30.75 -13.3838,-6.9958 -31.5824,-10.5176 -54.5938,-10.5937 -7.0146,-25.9757 -18.6908,-50.9762 -35.0625,-73.6875 l -9.7812,-69.3438 -0.9063,-6.4375 -5.125,4 -30.5937,23.9688 c -41.0402,-31.5796 -90.4197,-47.4228 -139.8438,-47.375 z m 228.125,206.2187 c 6.3617,0.8346 11.6486,3.3459 15.875,7.5313 5.279,5.2278 8.5511,13.9044 9.7813,26 h -24.7813 c 0.5248,-11.1718 0.225,-22.3843 -0.875,-33.5313 z m 118.9731,-33.6623 h 58.582 v 26.754 c 5.6377,-11.583 11.4549,-19.5528 17.4516,-23.9095 5.9965,-4.3563 13.4025,-6.5346 22.2182,-6.5347 9.2253,1e-4 19.3222,2.8703 30.2904,8.6104 l -19.3736,44.5901 c -7.3805,-3.0751 -13.2233,-4.6127 -17.5285,-4.6128 -8.2005,10e-5 -14.5559,3.3828 -19.066,10.1481 -6.458,9.5331 -9.6869,27.3691 -9.6868,53.508 v 54.7381 h -62.8873 z m 320.2793,97.1755 h -125.46709 c 1.12748,10.0456 3.84388,17.5285 8.14921,22.4487 6.04775,7.073 13.94069,10.6094 23.67883,10.6094 6.15024,0 11.99306,-1.5376 17.5285,-4.6128 3.38256,-1.9476 7.02151,-5.3815 10.91686,-10.3018 l 61.65724,5.6891 c -9.43072,16.4009 -20.80885,28.1634 -34.13443,35.2876 -13.3259,7.1241 -32.44322,10.6862 -57.35199,10.6862 -21.62881,0 -38.64475,-3.0495 -51.04789,-9.1486 -12.40324,-6.0991 -22.67944,-15.7859 -30.82862,-29.0604 -8.14922,-13.2745 -12.22382,-28.881 -12.22382,-46.8195 0,-25.5239 8.17483,-46.1788 24.52452,-61.9648 16.34962,-15.7857 38.9265,-23.6787 67.7307,-23.6788 23.3712,1e-4 41.82222,3.5366 55.35313,10.6093 13.53059,7.0731 23.83241,17.3236 30.9055,30.7517 7.0727,13.4284 10.60915,30.9056 10.60935,52.4318 z m -63.6561,-29.983 c -1.23021,-12.0956 -4.48476,-20.7573 -9.76368,-25.9852 -5.27917,-5.2277 -12.22393,-7.8416 -20.8343,-7.8417 -9.94316,10e-5 -17.88735,3.9466 -23.8326,11.8394 -3.79279,4.9204 -6.20167,12.2496 -7.22666,21.9875 z m 93.17774,-67.1925 h 58.4283 v 23.8326 c 8.40539,-9.9429 16.88773,-17.0158 25.44706,-21.2187 8.55912,-4.2026 18.88657,-6.304 30.98238,-6.3041 13.01809,1e-4 23.31991,2.3065 30.90549,6.9191 7.58526,4.6129 13.78685,11.4808 18.6048,20.6037 9.84037,-10.6605 18.80962,-17.9128 26.90778,-21.7569 8.09773,-3.8438 18.09204,-5.7658 29.98294,-5.7659 17.52823,1e-4 31.21274,5.2023 41.05357,15.6065 9.84026,10.4044 14.76054,26.6772 14.76083,48.8184 v 102.557 h -62.73354 v -93.024 c -2.3e-4,-7.3803 -1.43531,-12.8644 -4.30524,-16.4522 -4.20297,-5.6377 -9.43076,-8.4566 -15.68339,-8.4567 -7.38062,10e-5 -13.32595,2.6652 -17.83601,7.9954 -4.51044,5.3304 -6.76557,13.8897 -6.76538,25.6777 v 84.2598 h -62.73355 v -89.9488 c -1.2e-4,-7.1753 -0.41015,-12.0444 -1.23007,-14.6071 -1.3327,-4.1001 -3.63907,-7.4059 -6.91914,-9.9174 -3.2803,-2.5113 -7.12426,-3.767 -11.5319,-3.7671 -7.1755,10e-5 -13.06958,2.7165 -17.68225,8.1492 -4.61284,5.4329 -6.91922,14.3509 -6.91914,26.754 v 83.3372 h -62.73354 z m 316.74293,-62.1185 h 62.57978 v 42.5911 h -62.57978 z m 0,62.1185 h 62.57978 v 163.2917 h -62.57978 z m 95.79168,0 h 151.45231 v 36.5945 l -82.41466,83.9523 h 87.48869 v 42.7449 H -366.998 v -40.5923 l 78.10941,-79.9545 h -71.95906 z"
- id="path17845-6"
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:314.89779663px;line-height:125%;font-family:'Arial Black';text-align:start;writing-mode:lr-tb;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;filter:url(#filter17760-6-7);enable-background:accumulate" />
- <path
- inkscape:connector-curvature="0"
- d="m -1336.1632,1788.2263 c 0,0 56.3913,141.5671 -147.8368,147.7737 -204.2612,6.2076 -147.8368,147.7737 -147.8368,147.7737 -37.8479,-37.8317 -61.2676,-90.0855 -61.2676,-147.7737 0,-57.6882 23.4197,-109.942 61.2676,-147.7737 37.8479,-37.8318 90.124,-61.2415 147.8368,-61.2415 57.7128,0 109.9889,23.4097 147.8368,61.2415 z"
- id="use16735-0"
- style="display:inline;overflow:visible;visibility:visible;fill:#d19f34;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:40;marker:none;enable-background:accumulate" />
- <path
- inkscape:connector-curvature="0"
- d="m -1631.8368,2083.7737 c 0,0 -56.3913,-141.5671 147.8368,-147.7737 204.2612,-6.2076 147.8368,-147.7737 147.8368,-147.7737 37.8479,37.8317 61.2676,90.0855 61.2676,147.7737 0,57.6882 -23.4197,109.942 -61.2676,147.7737 -37.8479,37.8318 -90.124,61.2415 -147.8368,61.2415 -57.7128,0 -109.9889,-23.4097 -147.8368,-61.2415 z"
- id="use16737-6"
- style="display:inline;overflow:visible;visibility:visible;fill:#469837;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:40;marker:none;enable-background:accumulate" />
- <path
- inkscape:connector-curvature="0"
- d="m 1021.2642,207.94408 c 0,143.9361 -116.68326,260.61936 -260.61936,260.61936 -143.9361,0 -260.61936,-116.68326 -260.61936,-260.61936 0,-143.936098 116.68326,-260.61936 260.61936,-260.61936 143.9361,0 260.61936,116.683262 260.61936,260.61936 z"
- transform="matrix(0.8023362,0,0,0.8019941,-2094.2929,1769.2301)"
- id="path16739-2"
- style="display:inline;overflow:visible;visibility:visible;fill:none;stroke:#4c4c4c;stroke-width:49.86504364;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10.43299961;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
- <path
- inkscape:connector-curvature="0"
- d="m -1577.2331,1825.1726 h 127.038 c 21.1728,2e-4 37.4271,5.2435 48.7628,15.7299 11.3353,10.4868 17.0031,23.4703 17.0033,38.9503 -2e-4,12.9836 -4.045,24.1194 -12.1345,33.4074 -5.3933,6.1923 -13.2833,11.086 -23.6698,14.6813 15.7797,3.7953 27.3898,10.312 34.8306,19.5501 7.4402,9.2383 11.1605,20.8485 11.1607,34.8306 -2e-4,11.3855 -2.6468,21.6224 -7.9399,30.7108 -5.2934,9.0884 -12.5342,16.2792 -21.7223,21.5725 -5.6929,3.2958 -14.2819,5.6927 -25.7671,7.1908 -15.2807,1.9975 -25.4177,2.9962 -30.4112,2.9962 h -117.1506 z m 68.4627,86.1401 h 29.5124 c 10.5863,10e-5 17.9519,-1.8225 22.0968,-5.468 4.1445,-3.6452 6.2169,-8.9135 6.217,-15.8049 -1e-4,-6.3916 -2.0725,-11.3853 -6.217,-14.9809 -4.1449,-3.5952 -11.3607,-5.3929 -21.6474,-5.3931 h -29.9618 z m 0,86.29 h 34.6059 c 11.6849,0 19.9244,-2.0723 24.7184,-6.2171 4.7938,-4.1447 7.1907,-9.7126 7.1909,-16.7037 -2e-4,-6.4916 -2.3722,-11.71 -7.116,-15.655 -4.7441,-3.9449 -13.0584,-5.9174 -24.9432,-5.9175 h -34.456 z"
- id="path16741-6"
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:306.80871582px;line-height:125%;font-family:'Arial Black';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:40;marker:none;enable-background:accumulate" />
- <g
- transform="rotate(180,-563.99995,766)"
- mask="url(#mask6467-7-2)"
- id="use16745-1">
- <path
- inkscape:connector-curvature="0"
- d="m 507.8125,-566.84375 c -7.54052,0.31127 -14.32442,4.87714 -17.4375,11.84375 -3.32061,7.43106 -1.79456,16.12851 3.84375,22 71.38742,76.4228 67.29917,195.79932 -9.15625,267.15625 C 418.92868,-204.1201 321.00173,-198.52349 249.15625,-248 l 26.65625,-20.875 5.125,-4 -6.03125,-2.40625 -105.9375,-42.59375 -6,-2.4375 0.90625,6.4375 15.9375,113 0.90625,6.4375 5.125,-4 30.59375,-23.96875 c 87.55252,67.36975 213.09347,63.10079 295.9375,-14.21875 92.27769,-86.12407 97.25455,-231.41793 11.09375,-323.65625 -3.63563,-4.00109 -8.72059,-6.38195 -14.125,-6.5625 -0.50858,-0.0174 -1.02855,-0.0208 -1.53125,0 z"
- id="use6863-8"
- style="display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;filter:url(#filter6447-9-6);enable-background:accumulate" />
- <path
- inkscape:connector-curvature="0"
- d="m 507.8125,-566.84375 c -7.54052,0.31127 -14.32442,4.87714 -17.4375,11.84375 -3.32061,7.43106 -1.79456,16.12851 3.84375,22 71.38742,76.4228 67.29917,195.79932 -9.15625,267.15625 C 418.92868,-204.1201 321.00173,-198.52349 249.15625,-248 l 26.65625,-20.875 5.125,-4 -6.03125,-2.40625 -105.9375,-42.59375 -6,-2.4375 0.90625,6.4375 15.9375,113 0.90625,6.4375 5.125,-4 30.59375,-23.96875 c 87.55252,67.36975 213.09347,63.10079 295.9375,-14.21875 92.27769,-86.12407 97.25455,-231.41793 11.09375,-323.65625 -3.63563,-4.00109 -8.72059,-6.38195 -14.125,-6.5625 -0.50858,-0.0174 -1.02855,-0.0208 -1.53125,0 z"
- id="use6865-7"
- style="display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:49.86504364;marker:none;enable-background:accumulate" />
- </g>
- <g
- transform="translate(-1839.8676,2340.0508)"
- mask="url(#mask6467-7-2)"
- id="g7285-9">
- <path
- inkscape:connector-curvature="0"
- d="m 507.8125,-566.84375 c -7.54052,0.31127 -14.32442,4.87714 -17.4375,11.84375 -3.32061,7.43106 -1.79456,16.12851 3.84375,22 71.38742,76.4228 67.29917,195.79932 -9.15625,267.15625 C 418.92868,-204.1201 321.00173,-198.52349 249.15625,-248 l 26.65625,-20.875 5.125,-4 -6.03125,-2.40625 -105.9375,-42.59375 -6,-2.4375 0.90625,6.4375 15.9375,113 0.90625,6.4375 5.125,-4 30.59375,-23.96875 c 87.55252,67.36975 213.09347,63.10079 295.9375,-14.21875 92.27769,-86.12407 97.25455,-231.41793 11.09375,-323.65625 -3.63563,-4.00109 -8.72059,-6.38195 -14.125,-6.5625 -0.50858,-0.0174 -1.02855,-0.0208 -1.53125,0 z"
- id="path7287-2"
- style="display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;filter:url(#filter6447-9-6);enable-background:accumulate" />
- <path
- inkscape:connector-curvature="0"
- d="m 507.8125,-566.84375 c -7.54052,0.31127 -14.32442,4.87714 -17.4375,11.84375 -3.32061,7.43106 -1.79456,16.12851 3.84375,22 71.38742,76.4228 67.29917,195.79932 -9.15625,267.15625 C 418.92868,-204.1201 321.00173,-198.52349 249.15625,-248 l 26.65625,-20.875 5.125,-4 -6.03125,-2.40625 -105.9375,-42.59375 -6,-2.4375 0.90625,6.4375 15.9375,113 0.90625,6.4375 5.125,-4 30.59375,-23.96875 c 87.55252,67.36975 213.09347,63.10079 295.9375,-14.21875 92.27769,-86.12407 97.25455,-231.41793 11.09375,-323.65625 -3.63563,-4.00109 -8.72059,-6.38195 -14.125,-6.5625 -0.50858,-0.0174 -1.02855,-0.0208 -1.53125,0 z"
- id="path7289-0"
- style="display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:49.86504364;marker:none;enable-background:accumulate" />
- </g>
- <path
- inkscape:connector-curvature="0"
- d="m -1166.8587,1976.761 h -125.4671 c 1.1275,10.0456 3.8439,17.5285 8.1492,22.4487 6.0478,7.073 13.9407,10.6094 23.6789,10.6094 6.1502,0 11.993,-1.5376 17.5285,-4.6128 3.3825,-1.9476 7.0215,-5.3815 10.9168,-10.3018 l 61.6573,5.6891 c -9.4307,16.4009 -20.8089,28.1634 -34.1345,35.2876 -13.3259,7.1241 -32.4432,10.6862 -57.3519,10.6862 -21.6289,0 -38.6448,-3.0495 -51.0479,-9.1486 -12.4033,-6.0991 -22.6795,-15.7859 -30.8286,-29.0604 -8.1493,-13.2745 -12.2239,-28.881 -12.2239,-46.8195 0,-25.5239 8.1749,-46.1788 24.5245,-61.9648 16.3497,-15.7857 38.9265,-23.6787 67.7307,-23.6788 23.3712,1e-4 41.8223,3.5366 55.3532,10.6093 13.5306,7.0731 23.8324,17.3236 30.9055,30.7517 7.0727,13.4284 10.6091,30.9056 10.6093,52.4318 z m -63.6561,-29.983 c -1.2302,-12.0956 -4.4847,-20.7573 -9.7637,-25.9852 -5.2791,-5.2277 -12.2239,-7.8416 -20.8343,-7.8417 -9.9431,10e-5 -17.8873,3.9466 -23.8325,11.8394 -3.7928,4.9204 -6.2017,12.2496 -7.2267,21.9875 z m 93.3316,-67.1925 h 58.582 v 26.754 c 5.6377,-11.583 11.4549,-19.5528 17.4516,-23.9095 5.9965,-4.3563 13.4025,-6.5346 22.2182,-6.5347 9.2253,1e-4 19.3222,2.8703 30.2904,8.6104 l -19.3736,44.5901 c -7.3805,-3.0751 -13.2233,-4.6127 -17.5285,-4.6128 -8.2005,10e-5 -14.5559,3.3828 -19.066,10.1481 -6.458,9.5331 -9.6869,27.3691 -9.6868,53.508 v 54.7381 h -62.8873 z m 320.2793,97.1755 h -125.46709 c 1.12748,10.0456 3.84388,17.5285 8.14921,22.4487 6.04775,7.073 13.94069,10.6094 23.67883,10.6094 6.15024,0 11.99306,-1.5376 17.5285,-4.6128 3.38256,-1.9476 7.02151,-5.3815 10.91686,-10.3018 l 61.65724,5.6891 c -9.43072,16.4009 -20.80885,28.1634 -34.13443,35.2876 -13.3259,7.1241 -32.44322,10.6862 -57.35199,10.6862 -21.62881,0 -38.64475,-3.0495 -51.04789,-9.1486 -12.40324,-6.0991 -22.67944,-15.7859 -30.82862,-29.0604 -8.14922,-13.2745 -12.22382,-28.881 -12.22382,-46.8195 0,-25.5239 8.17483,-46.1788 24.52452,-61.9648 16.34962,-15.7857 38.9265,-23.6787 67.7307,-23.6788 23.3712,1e-4 41.82222,3.5366 55.35313,10.6093 13.53059,7.0731 23.83241,17.3236 30.9055,30.7517 7.0727,13.4284 10.60915,30.9056 10.60935,52.4318 z m -63.6561,-29.983 c -1.23021,-12.0956 -4.48476,-20.7573 -9.76368,-25.9852 -5.27917,-5.2277 -12.22393,-7.8416 -20.8343,-7.8417 -9.94316,10e-5 -17.88735,3.9466 -23.8326,11.8394 -3.79279,4.9204 -6.20167,12.2496 -7.22666,21.9875 z m 93.17774,-67.1925 h 58.4283 v 23.8326 c 8.40539,-9.9429 16.88773,-17.0158 25.44706,-21.2187 8.55912,-4.2026 18.88657,-6.304 30.98238,-6.3041 13.01809,1e-4 23.31991,2.3065 30.90549,6.9191 7.58526,4.6129 13.78685,11.4808 18.6048,20.6037 9.84037,-10.6605 18.80962,-17.9128 26.90778,-21.7569 8.09773,-3.8438 18.09204,-5.7658 29.98294,-5.7659 17.52823,1e-4 31.21274,5.2023 41.05357,15.6065 9.84026,10.4044 14.76054,26.6772 14.76083,48.8184 v 102.557 h -62.73354 v -93.024 c -2.3e-4,-7.3803 -1.43531,-12.8644 -4.30524,-16.4522 -4.20297,-5.6377 -9.43076,-8.4566 -15.68339,-8.4567 -7.38062,10e-5 -13.32595,2.6652 -17.83601,7.9954 -4.51044,5.3304 -6.76557,13.8897 -6.76538,25.6777 v 84.2598 h -62.73355 v -89.9488 c -1.2e-4,-7.1753 -0.41015,-12.0444 -1.23007,-14.6071 -1.3327,-4.1001 -3.63907,-7.4059 -6.91914,-9.9174 -3.2803,-2.5113 -7.12426,-3.767 -11.5319,-3.7671 -7.1755,10e-5 -13.06958,2.7165 -17.68225,8.1492 -4.61284,5.4329 -6.91922,14.3509 -6.91914,26.754 v 83.3372 h -62.73354 z m 316.74293,-62.1185 h 62.57978 v 42.5911 h -62.57978 z m 0,62.1185 h 62.57978 v 163.2917 h -62.57978 z m 95.79168,0 h 151.45231 v 36.5945 l -82.41466,83.9523 h 87.48869 v 42.7449 H -380.998 v -40.5923 l 78.10941,-79.9545 h -71.95906 z"
- id="text16729-2"
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:314.89779663px;line-height:125%;font-family:'Arial Black';text-align:start;writing-mode:lr-tb;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#dadada;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10.43299961;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
- <text
- x="-1259.6362"
- y="2147.7705"
- id="text3172-3"
- xml:space="preserve"
- style="font-style:normal;font-weight:normal;font-size:71.35877228px;line-height:0%;font-family:'Bitstream Vera Sans';display:inline;fill:#000000;fill-opacity:1;stroke:none"><tspan
- x="-1259.6362"
- y="2147.7705"
- id="tspan3174-7">Free Software for Automation </tspan></text>
- </g>
+ id="about_brz_logo">
+ <use
+ transform="matrix(4.4955317,0,0,4.4955317,2592.5922,-2515.4545)"
+ x="0"
+ y="0"
+ xlink:href="#g52678"
+ id="use52780"
+ width="100%"
+ height="100%" />
</g>
<g
transform="translate(960.38982,282.02716)"
@@ -95240,4 +95104,49 @@
d="m 1106,195.36218 v 1 h -6 v -1 z"
sodipodi:nodetypes="ccccc" />
</g>
+ <g
+ id="g52819"
+ transform="rotate(45,111.50001,200.12138)">
+ <path
+ style="fill:#808080;stroke:none"
+ d="m 103.15931,201.66277 h 17.01282 v -2.00001 h -17.01282 z"
+ id="path52811"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ inkscape:connector-curvature="0"
+ id="path52805"
+ d="m 112.66573,209.16918 v -17.01282 h -2.00001 v 17.01282 z"
+ style="fill:#808080;stroke:none" />
+ <path
+ style="fill:#ff0000;stroke:none"
+ d="M 112.33428,208.08643 V 191.0736 h -2.00001 v 17.01283 z"
+ id="path52807"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ inkscape:connector-curvature="0"
+ id="path52813"
+ d="m 102.82786,200.58002 h 17.01283 v -2.00001 h -17.01283 z"
+ style="fill:#ff0000;stroke:none" />
+ </g>
+ <path
+ sodipodi:nodetypes="ccccccccccccccccccc"
+ inkscape:connector-curvature="0"
+ id="use16453"
+ d="m 565.50059,134.36455 c -3.21171,0 -6.18403,1.71679 -7.78988,4.49822 -0.54331,0.94101 -0.88738,1.959 -1.06307,2.99881 h -4.14112 c -2.02768,-0.0286 -2.02768,3.0275 0,2.99882 h 4.14112 c 0.17569,1.03979 0.51976,2.05778 1.06307,2.99881 1.60585,2.78143 4.57817,4.49822 7.78988,4.49822 0.82807,-9e-5 1.49932,-0.67134 1.49941,-1.4994 v -1.49941 h 4.49822 c 2.02743,0.0284 2.02743,-3.02724 0,-2.99882 H 567 v -5.99762 h 4.49822 c 2.02743,0.0284 2.02743,-3.02724 0,-2.99882 H 567 v -1.49941 c -9e-5,-0.82806 -0.67134,-1.49931 -1.49941,-1.4994 z"
+ style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.49940741;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+ <path
+ style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.49940741;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ d="m 625.50059,134.36455 c -3.21171,0 -6.18403,1.71679 -7.78988,4.49822 -0.54331,0.94101 -0.88738,1.959 -1.06307,2.99881 h -4.14112 c -2.02768,-0.0286 -2.02768,3.0275 0,2.99882 h 4.14112 c 0.17569,1.03979 0.51976,2.05778 1.06307,2.99881 1.60585,2.78143 4.57817,4.49822 7.78988,4.49822 0.82807,-9e-5 1.49932,-0.67134 1.49941,-1.4994 v -1.49941 h 4.49822 c 2.02743,0.0284 2.02743,-3.02724 0,-2.99882 H 627 v -5.99762 h 4.49822 c 2.02743,0.0284 2.02743,-3.02724 0,-2.99882 H 627 v -1.49941 c -9e-5,-0.82806 -0.67134,-1.49931 -1.49941,-1.4994 z"
+ id="path16494"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccccccccccccc" />
+ <path
+ style="opacity:1;vector-effect:none;fill:#ff1b00;fill-opacity:1;stroke:none;stroke-width:0.74999982;stroke-opacity:1;marker:none"
+ d="m 615.63605,136.99738 a 8.9999978,8.9999978 0 0 0 0,12.7279 8.9999978,8.9999978 0 0 0 12.7279,0 8.9999978,8.9999978 0 0 0 0,-12.7279 8.9999978,8.9999978 0 0 0 -12.7279,0 z m 1.59098,1.59098 a 6.7499981,6.7499981 0 0 1 8.68207,-0.72921 l -9.40608,9.40612 a 6.7499981,6.7499981 0 0 1 0.72401,-8.67691 z m 0.8618,10.27309 9.40609,-9.40612 a 6.7499981,6.7499981 0 0 1 -0.72195,8.67897 6.7499981,6.7499981 0 0 1 -8.68414,0.72715 z"
+ id="path16496"
+ inkscape:connector-curvature="0" />
</svg>
Binary file images/icoplay24.png has changed
Binary file images/icostop24.png has changed
Binary file images/poe.ico has changed
Binary file images/splash.png has changed
--- a/opc_ua/client.py Wed Mar 01 10:54:54 2023 +0100
+++ b/opc_ua/client.py Fri Mar 03 19:20:49 2023 +0100
@@ -6,7 +6,7 @@
from editors.ConfTreeNodeEditor import ConfTreeNodeEditor
from PLCControler import LOCATION_CONFNODE, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT
-from .opcua_client_maker import OPCUAClientPanel, OPCUAClientModel, UA_IEC_types
+from .opcua_client_maker import OPCUAClientPanel, OPCUAClientModel, UA_IEC_types, authParams
import util.paths as paths
@@ -22,6 +22,9 @@
("include",),
("arch",)]]
+# Tests need to use other default hosts
+OPCUA_DEFAULT_HOST = os.environ.get("OPCUA_DEFAULT_HOST", "127.0.0.1")
+
class OPCUAClientEditor(ConfTreeNodeEditor):
CONFNODEEDITOR_TABS = [
(_("OPC-UA Client"), "CreateOPCUAClient_UI")]
@@ -29,18 +32,57 @@
def Log(self, msg):
self.Controler.GetCTRoot().logger.write(msg)
- def UriGetter(self):
- return self.Controler.GetServerURI()
-
def CreateOPCUAClient_UI(self, parent):
- return OPCUAClientPanel(parent, self.Controler.GetModelData(), self.Log, self.UriGetter)
+ return OPCUAClientPanel(parent, self.Controler.GetModelData(), self.Log, self.Controler.GetConfig)
class OPCUAClient(object):
XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="OPCUAClient">
<xsd:complexType>
- <xsd:attribute name="Server_URI" type="xsd:string" use="optional" default="opc.tcp://localhost:4840"/>
+ <xsd:sequence>
+ <xsd:element name="AuthType" minOccurs="0">
+ <xsd:complexType>
+ <xsd:choice minOccurs="0">
+ <xsd:element name="x509">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="Policy">
+ <xsd:annotation>
+ <xsd:documentation>Default to Basic256Sha256 if not specified</xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexType>
+ <xsd:choice minOccurs="0">
+ <xsd:element name="Basic256Sha256"/>
+ <xsd:element name="Basic128Rsa15"/>
+ <xsd:element name="Basic256"/>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="Mode">
+ <xsd:complexType>
+ <xsd:choice minOccurs="0">
+ <xsd:element name="SignAndEncrypt"/>
+ <xsd:element name="Sign"/>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:sequence>
+ <xsd:attribute name="Certificate" type="xsd:string" use="optional" default="certificate.pem"/>
+ <xsd:attribute name="PrivateKey" type="xsd:string" use="optional" default="private_key.pem"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="UserPassword">
+ <xsd:complexType>
+ <xsd:attribute name="User" type="xsd:string" use="optional"/>
+ <xsd:attribute name="Password" type="xsd:string" use="optional"/>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:sequence>
+ <xsd:attribute name="Server_URI" type="xsd:string" use="optional" default="opc.tcp://"""+OPCUA_DEFAULT_HOST+""":4840"/>
</xsd:complexType>
</xsd:element>
</xsd:schema>
@@ -49,7 +91,7 @@
EditorType = OPCUAClientEditor
def __init__(self):
- self.modeldata = OPCUAClientModel(self.Log)
+ self.modeldata = OPCUAClientModel(self.Log, self.CTNMarkModified)
filepath = self.GetFileName()
if os.path.isfile(filepath):
@@ -60,9 +102,30 @@
def GetModelData(self):
return self.modeldata
-
- def GetServerURI(self):
- return self.GetParamsAttributes("OPCUAClient.Server_URI")["value"]
+
+ def GetConfig(self):
+ def cfg(path):
+ try:
+ attr=self.GetParamsAttributes("OPCUAClient."+path)
+ except ValueError:
+ return None
+ return attr["value"]
+
+ AuthType = cfg("AuthType")
+ res = dict(URI=cfg("Server_URI"), AuthType=AuthType)
+
+ paramList = authParams.get(AuthType, None)
+ if paramList:
+ for name,default in paramList:
+ value = cfg("AuthType."+name)
+ if value == "" or value is None:
+ value = default
+ # cryptomaterial is expected to be in project's user provide file directory
+ if name in ["Certificate","PrivateKey"]:
+ value = os.path.join(self.GetCTRoot()._getProjectFilesPath(), value)
+ res[name] = value
+
+ return res
def GetFileName(self):
return os.path.join(self.CTNPath(), 'selected.csv')
@@ -76,16 +139,18 @@
locstr = "_".join(map(str, current_location))
c_path = os.path.join(buildpath, "opcua_client__%s.c" % locstr)
- c_code = self.modeldata.GenerateC(c_path, locstr,
- self.GetParamsAttributes("OPCUAClient.Server_URI")["value"])
+ c_code = '#include "beremiz.h"\n'
+ c_code += self.modeldata.GenerateC(c_path, locstr, self.GetConfig())
with open(c_path, 'wb') as c_file:
c_file.write(c_code)
- LDFLAGS = [' "' + os.path.join(Open62541LibraryPath, "libopen62541.a") + '"']
+ LDFLAGS = ['"' + os.path.join(Open62541LibraryPath, "libopen62541.a") + '"', '-lcrypto']
CFLAGS = ' '.join(['-I"' + path + '"' for path in Open62541IncludePaths])
+ # Note: all user provided files are systematicaly copied, including cryptomaterial
+
return [(c_path, CFLAGS)], LDFLAGS, True
def GetVariableLocationTree(self):
--- a/opc_ua/opcua_client_maker.py Wed Mar 01 10:54:54 2023 +0100
+++ b/opc_ua/opcua_client_maker.py Fri Mar 03 19:20:49 2023 +0100
@@ -7,7 +7,7 @@
from opcua import ua
import wx
-from wx.lib.agw.hypertreelist import HyperTreeList as TreeListCtrl
+import wx.lib.gizmos as gizmos # Formerly wx.gizmos in Classic
import wx.dataview as dv
@@ -38,9 +38,19 @@
directions = ["input", "output"]
-class OPCUASubListModel(dv.PyDataViewIndexListModel):
+authParams = {
+ "x509":[
+ ("Certificate", "certificate.der"),
+ ("PrivateKey", "private_key.pem"),
+ ("Policy", "Basic256Sha256"),
+ ("Mode", "SignAndEncrypt")],
+ "UserPassword":[
+ ("User", None),
+ ("Password", None)]}
+
+class OPCUASubListModel(dv.DataViewIndexListModel):
def __init__(self, data, log):
- dv.PyDataViewIndexListModel.__init__(self, len(data))
+ dv.DataViewIndexListModel.__init__(self, len(data))
self.data = data
self.log = log
@@ -140,7 +150,7 @@
self.dvc.AppendTextColumn(colname, idx, width=width, mode=dv.DATAVIEW_CELL_EDITABLE)
DropTarget = NodeDropTarget(self)
- self.dvc.SetDropTarget(DropTarget)
+ self.SetDropTarget(DropTarget)
self.Sizer = wx.BoxSizer(wx.VERTICAL)
@@ -230,7 +240,7 @@
class OPCUAClientPanel(wx.SplitterWindow):
- def __init__(self, parent, modeldata, log, uri_getter):
+ def __init__(self, parent, modeldata, log, config_getter):
self.log = log
wx.SplitterWindow.__init__(self, parent, -1)
@@ -242,7 +252,7 @@
self.inout_sizer.AddGrowableRow(1)
self.client = None
- self.uri_getter = uri_getter
+ self.config_getter = config_getter
self.connect_button = wx.ToggleButton(self.inout_panel, -1, "Browse Server")
@@ -278,15 +288,35 @@
def OnConnectButton(self, event):
if self.connect_button.GetValue():
+ config = self.config_getter()
+ self.client = Client(config["URI"])
+ self.log("OPCUA browser: connecting to {}\n".format(config["URI"]))
+
+ AuthType = config["AuthType"]
+ if AuthType=="UserPasword":
+ self.client.set_user(config["User"])
+ self.client.set_password(config["Password"])
+ elif AuthType=="x509":
+ self.client.set_security_string(
+ "{Policy},{Mode},{Certificate},{PrivateKey}".format(**config))
+
+ try :
+ self.client.connect()
+ except Exception as e:
+ self.log("OPCUA browser: "+str(e)+"\n")
+ self.client = None
+ self.connect_button.SetValue(False)
+ return
+
self.tree_panel = wx.Panel(self)
self.tree_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
self.tree_sizer.AddGrowableCol(0)
self.tree_sizer.AddGrowableRow(0)
- self.tree = TreeListCtrl(self.tree_panel, -1, style=0, agwStyle=
- wx.TR_DEFAULT_STYLE
- | wx.TR_MULTIPLE
- | wx.TR_FULL_ROW_HIGHLIGHT
+ self.tree = gizmos.TreeListCtrl(self.tree_panel, -1, style=0, agwStyle=
+ gizmos.TR_DEFAULT_STYLE
+ | gizmos.TR_MULTIPLE
+ | gizmos.TR_FULL_ROW_HIGHLIGHT
)
prepare_image_list()
@@ -298,8 +328,6 @@
self.tree.SetMainColumn(0)
- self.client = Client(self.uri_getter())
- self.client.connect()
self.client.load_type_definitions() # load definition of server specific structures/extension objects
rootnode = self.client.get_root_node()
@@ -419,9 +447,10 @@
class OPCUAClientList(list):
- def __init__(self, log = lambda m:None):
+ def __init__(self, log, change_callback):
super(OPCUAClientList, self).__init__(self)
self.log = log
+ self.change_callback = change_callback
def append(self, value):
v = dict(zip(lstcolnames, value))
@@ -452,13 +481,19 @@
list.append(self, [v[n] for n in lstcolnames])
+ self.change_callback()
+
return True
+ def __delitem__(self, index):
+ list.__delitem__(self, index)
+ self.change_callback()
+
class OPCUAClientModel(dict):
- def __init__(self, log = lambda m:None):
+ def __init__(self, log, change_callback = lambda : None):
super(OPCUAClientModel, self).__init__()
for direction in directions:
- self[direction] = OPCUAClientList(log)
+ self[direction] = OPCUAClientList(log, change_callback)
def LoadCSV(self,path):
with open(path, 'rb') as csvfile:
@@ -468,7 +503,8 @@
self[direction][:] = []
for row in reader:
direction = row[0]
- self[direction].append(row[1:])
+ # avoids calling change callback whe loading CSV
+ list.append(self[direction],row[1:])
def SaveCSV(self,path):
with open(path, 'wb') as csvfile:
@@ -478,85 +514,198 @@
for row in data:
writer.writerow([direction] + row)
- def GenerateC(self, path, locstr, server_uri):
+ def GenerateC(self, path, locstr, config):
template = """/* code generated by beremiz OPC-UA extension */
#include <open62541/client_config_default.h>
#include <open62541/client_highlevel.h>
#include <open62541/plugin/log_stdout.h>
+#include <open62541/plugin/securitypolicy.h>
+#include <open62541/plugin/securitypolicy_default.h>
+
+#include <open62541/types.h>
+#include <open62541/types_generated_handling.h>
+
+#define _Log(level, ...) \\
+ {{ \\
+ char mstr[256]; \\
+ snprintf(mstr, 255, __VA_ARGS__); \\
+ LogMessage(level, mstr, strlen(mstr)); \\
+ }}
+
+#define LogInfo(...) _Log(LOG_INFO, __VA_ARGS__);
+#define LogError(...) _Log(LOG_CRITICAL, __VA_ARGS__);
+#define LogWarning(...) _Log(LOG_WARNING, __VA_ARGS__);
+
+static UA_INLINE UA_ByteString
+loadFile(const char *const path) {{
+ UA_ByteString fileContents = UA_STRING_NULL;
+
+ FILE *fp = fopen(path, "rb");
+ if(!fp) {{
+ errno = 0;
+ LogError("OPC-UA could not open %s", path);
+ return fileContents;
+ }}
+
+ fseek(fp, 0, SEEK_END);
+ fileContents.length = (size_t)ftell(fp);
+ fileContents.data = (UA_Byte *)UA_malloc(fileContents.length * sizeof(UA_Byte));
+ if(fileContents.data) {{
+ fseek(fp, 0, SEEK_SET);
+ size_t read = fread(fileContents.data, sizeof(UA_Byte), fileContents.length, fp);
+ if(read != fileContents.length){{
+ UA_ByteString_clear(&fileContents);
+ LogError("OPC-UA could not read %s", path);
+ }}
+ }} else {{
+ fileContents.length = 0;
+ LogError("OPC-UA Not enough memoty to load %s", path);
+ }}
+ fclose(fp);
+
+ return fileContents;
+}}
static UA_Client *client;
+static UA_ClientConfig *cc;
#define DECL_VAR(ua_type, C_type, c_loc_name) \\
static UA_Variant c_loc_name##_variant; \\
static C_type c_loc_name##_buf = 0; \\
C_type *c_loc_name = &c_loc_name##_buf;
-%(decl)s
-
-void __cleanup_%(locstr)s(void)
-{
+{decl}
+
+void __cleanup_{locstr}(void)
+{{
UA_Client_disconnect(client);
UA_Client_delete(client);
-}
-
+}}
+
+#define INIT_NoAuth() \\
+ LogInfo("OPC-UA Init no auth"); \\
+ UA_ClientConfig_setDefault(cc); \\
+ retval = UA_Client_connect(client, uri);
+
+/* Note : Single policy is enforced here, by default open62541 client supports all policies */
+#define INIT_x509(Policy, UpperCaseMode, PrivateKey, Certificate) \\
+ LogInfo("OPC-UA Init x509 %s,%s,%s,%s", #Policy, #UpperCaseMode, PrivateKey, Certificate); \\
+ \\
+ UA_ByteString certificate = loadFile(Certificate); \\
+ UA_ByteString privateKey = loadFile(PrivateKey); \\
+ \\
+ cc->securityMode = UA_MESSAGESECURITYMODE_##UpperCaseMode; \\
+ \\
+ /* replacement for default behaviour */ \\
+ /* UA_ClientConfig_setDefaultEncryption(cc, certificate, privateKey, NULL, 0, NULL, 0); */ \\
+ do{{ \\
+ retval = UA_ClientConfig_setDefault(cc); \\
+ if(retval != UA_STATUSCODE_GOOD) \\
+ break; \\
+ \\
+ UA_SecurityPolicy *sp = (UA_SecurityPolicy*) \\
+ UA_realloc(cc->securityPolicies, sizeof(UA_SecurityPolicy) * 2); \\
+ if(!sp){{ \\
+ retval = UA_STATUSCODE_BADOUTOFMEMORY; \\
+ break; \\
+ }} \\
+ cc->securityPolicies = sp; \\
+ \\
+ retval = UA_SecurityPolicy_##Policy(&cc->securityPolicies[cc->securityPoliciesSize], \\
+ certificate, privateKey, &cc->logger); \\
+ if(retval != UA_STATUSCODE_GOOD) {{ \\
+ UA_LOG_WARNING(&cc->logger, UA_LOGCATEGORY_USERLAND, \\
+ "Could not add SecurityPolicy Policy with error code %s", \\
+ UA_StatusCode_name(retval)); \\
+ UA_free(cc->securityPolicies); \\
+ cc->securityPolicies = NULL; \\
+ break; \\
+ }} \\
+ \\
+ ++cc->securityPoliciesSize; \\
+ }} while(0); \\
+ \\
+ retval = UA_Client_connect(client, uri); \\
+ \\
+ UA_ByteString_clear(&certificate); \\
+ UA_ByteString_clear(&privateKey);
+
+#define INIT_UserPassword(User, Password) \\
+ LogInfo("OPC-UA Init UserPassword %s,%s", User, Password); \\
+ UA_ClientConfig_setDefault(cc); \\
+ retval = UA_Client_connectUsername(client, uri, User, Password);
#define INIT_READ_VARIANT(ua_type, c_loc_name) \\
UA_Variant_init(&c_loc_name##_variant);
-#define INIT_WRITE_VARIANT(ua_type, ua_type_enum, c_loc_name) \\
+#define INIT_WRITE_VARIANT(ua_type, ua_type_enum, c_loc_name) \\
UA_Variant_setScalar(&c_loc_name##_variant, (ua_type*)c_loc_name, &UA_TYPES[ua_type_enum]);
-int __init_%(locstr)s(int argc,char **argv)
-{
+int __init_{locstr}(int argc,char **argv)
+{{
UA_StatusCode retval;
client = UA_Client_new();
- UA_ClientConfig_setDefault(UA_Client_getConfig(client));
-%(init)s
-
- /* Connect to server */
- retval = UA_Client_connect(client, "%(uri)s");
- if(retval != UA_STATUSCODE_GOOD) {
+ cc = UA_Client_getConfig(client);
+ char *uri = "{uri}";
+{init}
+
+ if(retval != UA_STATUSCODE_GOOD) {{
+ LogError("OPC-UA Init Failed %d", retval);
UA_Client_delete(client);
return EXIT_FAILURE;
- }
-}
+ }}
+ return 0;
+}}
#define READ_VALUE(ua_type, ua_type_enum, c_loc_name, ua_nodeid_type, ua_nsidx, ua_node_id) \\
retval = UA_Client_readValueAttribute( \\
client, ua_nodeid_type(ua_nsidx, ua_node_id), &c_loc_name##_variant); \\
if(retval == UA_STATUSCODE_GOOD && UA_Variant_isScalar(&c_loc_name##_variant) && \\
- c_loc_name##_variant.type == &UA_TYPES[ua_type_enum]) { \\
+ c_loc_name##_variant.type == &UA_TYPES[ua_type_enum]) {{ \\
c_loc_name##_buf = *(ua_type*)c_loc_name##_variant.data; \\
UA_Variant_clear(&c_loc_name##_variant); /* Unalloc requiered on each read ! */ \\
- }
-
-void __retrieve_%(locstr)s(void)
-{
+ }}
+
+void __retrieve_{locstr}(void)
+{{
UA_StatusCode retval;
-%(retrieve)s
-}
-
-#define WRITE_VALUE(ua_type, c_loc_name, ua_nodeid_type, ua_nsidx, ua_node_id) \\
+{retrieve}
+}}
+
+#define WRITE_VALUE(ua_type, c_loc_name, ua_nodeid_type, ua_nsidx, ua_node_id) \\
UA_Client_writeValueAttribute( \\
client, ua_nodeid_type(ua_nsidx, ua_node_id), &c_loc_name##_variant);
-void __publish_%(locstr)s(void)
-{
-%(publish)s
-}
+void __publish_{locstr}(void)
+{{
+{publish}
+}}
"""
formatdict = dict(
locstr = locstr,
- uri = server_uri,
+ uri = config["URI"],
decl = "",
cleanup = "",
init = "",
retrieve = "",
publish = ""
)
+
+ AuthType = config["AuthType"]
+ if AuthType == "x509":
+ config["UpperCaseMode"] = config["Mode"].upper()
+ formatdict["init"] += """
+ INIT_x509({Policy}, {UpperCaseMode}, "{PrivateKey}", "{Certificate}")""".format(**config)
+ elif AuthType == "UserPassword":
+ formatdict["init"] += """
+ INIT_UserPassword("{User}", "{Password}")""".format(**config)
+ else:
+ formatdict["init"] += """
+ INIT_NoAuth()"""
+
for direction, data in self.iteritems():
iec_direction_prefix = {"input": "__I", "output": "__Q"}[direction]
for row in data:
@@ -570,18 +719,18 @@
DECL_VAR({ua_type}, {C_type}, {c_loc_name})""".format(**locals())
if direction == "input":
- formatdict["init"] +="""
+ formatdict["init"] += """
INIT_READ_VARIANT({ua_type}, {c_loc_name})""".format(**locals())
formatdict["retrieve"] += """
READ_VALUE({ua_type}, {ua_type_enum}, {c_loc_name}, {ua_nodeid_type}, {ua_nsidx}, {ua_node_id})""".format(**locals())
if direction == "output":
- formatdict["init"] +="""
+ formatdict["init"] += """
INIT_WRITE_VARIANT({ua_type}, {ua_type_enum}, {c_loc_name})""".format(**locals())
formatdict["publish"] += """
WRITE_VALUE({ua_type}, {c_loc_name}, {ua_nodeid_type}, {ua_nsidx}, {ua_node_id})""".format(**locals())
- Ccode = template%formatdict
+ Ccode = template.format(**formatdict)
return Ccode
@@ -594,7 +743,20 @@
frame = wx.Frame(None, -1, "OPCUA Client Test App", size=(800,600))
- uri = sys.argv[1] if len(sys.argv)>1 else "opc.tcp://localhost:4840"
+ argc = len(sys.argv)
+
+ config={}
+ config["URI"] = sys.argv[1] if argc>1 else "opc.tcp://localhost:4840"
+
+ if argc > 2:
+ AuthType = sys.argv[2]
+ config["AuthType"] = AuthType
+ for (name, default), value in izip_longest(authParams[AuthType], sys.argv[3:]):
+ if value is None:
+ if default is None:
+ raise Exception(name+" param expected")
+ value = default
+ config[name] = value
test_panel = wx.Panel(frame)
test_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
@@ -603,7 +765,7 @@
modeldata = OPCUAClientModel(print)
- opcuatestpanel = OPCUAClientPanel(test_panel, modeldata, print, lambda:uri)
+ opcuatestpanel = OPCUAClientPanel(test_panel, modeldata, print, lambda:config)
def OnGenerate(evt):
dlg = wx.FileDialog(
@@ -624,7 +786,11 @@
-I ../../open62541/arch/ ../../open62541/build/bin/libopen62541.a
*/
-"""%(path, path[:-2]) + modeldata.GenerateC(path, "test", uri) + """
+"""%(path, path[:-2]) + modeldata.GenerateC(path, "test", config) + """
+
+int LogMessage(uint8_t level, char* buf, uint32_t size){
+ printf("log level:%d message:'%.*s'\\n", level, size, buf);
+};
int main(int argc, char *argv[]) {
--- a/runtime/PLCObject.py Wed Mar 01 10:54:54 2023 +0100
+++ b/runtime/PLCObject.py Fri Mar 03 19:20:49 2023 +0100
@@ -623,7 +623,7 @@
def RepairPLC(self):
self.PurgePLC()
- MainWorker.quit()
+ MainWorker.finish()
@RunInMain
def PurgePLC(self):
--- a/runtime/Worker.py Wed Mar 01 10:54:54 2023 +0100
+++ b/runtime/Worker.py Fri Mar 03 19:20:49 2023 +0100
@@ -9,7 +9,8 @@
from __future__ import absolute_import
import sys
-from threading import Lock, Condition
+from threading import Lock, Condition, Thread
+
import six
from six.moves import _thread
@@ -51,6 +52,8 @@
self.free = Condition(self.mutex)
self.job = None
self.enabled = False
+ self.stopper = None
+ self.own_thread = None
def reraise(self, job):
"""
@@ -86,6 +89,61 @@
self.mutex.release()
+ def interleave(self, waker, stopper, *args, **kwargs):
+ """
+ as for twisted reactor's interleave, it passes all jobs to waker func
+ additionaly, it creates a new thread to wait for new job.
+ """
+ self.feed = Condition(self.mutex)
+ self._threadID = _thread.get_ident()
+ self.stopper = stopper
+
+ def wakerfeedingloop():
+ self.mutex.acquire()
+ self.enabled = True
+ if args or kwargs:
+ def first_job_todo():
+ _job = job(*args, **kwargs)
+ _job.do()
+ if not _job.success:
+ self.reraise(_job)
+ self.mutex.acquire()
+ self.feed.notify()
+ self.mutex.release()
+ waker(first_job_todo)
+ self.feed.wait()
+
+ while not self._finish:
+ self.todo.wait()
+ def job_todo():
+ self.mutex.acquire()
+ if self.job is not None:
+ self.job.do()
+ self.feed.notify()
+ self.done.notify()
+ self.mutex.release()
+ if self._finish:
+ break
+ waker(job_todo)
+ self.feed.wait()
+
+ self.mutex.release()
+ self.own_thread = Thread(target = wakerfeedingloop)
+ self.own_thread.start()
+
+ def stop(self):
+ """
+ !interleave
+ """
+ self.mutex.acquire()
+ self._finish = True
+ self.enabled = False
+ self.job = None
+ self.todo.notify()
+ self.done.notify()
+ self.mutex.release()
+ self.own_thread.join()
+
def call(self, *args, **kwargs):
"""
creates a job, execute it in worker thread, and deliver result.
@@ -136,3 +194,9 @@
self.todo.notify()
self.done.notify()
self.mutex.release()
+
+ def finish(self):
+ if self.own_thread is None:
+ self.quit()
+ if self.stopper is not None:
+ self.stopper()
--- a/svghmi/i18n.py Wed Mar 01 10:54:54 2023 +0100
+++ b/svghmi/i18n.py Fri Mar 03 19:20:49 2023 +0100
@@ -20,6 +20,7 @@
# https://pypi.org/project/pycountry/18.12.8/
# python2 -m pip install pycountry==18.12.8 --user
import pycountry
+from dialogs import MessageBoxOnce
cmd_parser = re.compile(r'(?:"([^"]+)"\s*|([^\s]+)\s*)?')
@@ -39,10 +40,21 @@
poedit_path = None
else:
- try:
- poedit_path = subprocess.check_output("command -v poedit", shell=True).strip()
- except subprocess.CalledProcessError:
- poedit_path = None
+ if os.environ.has_key("SNAP"):
+ MessageBoxOnce("Launching POEdit with xdg-open",
+ "Confined app can't launch POEdit directly.\n"+
+ "Instead, PO/POT file is passed to xdg-open.\n"+
+ "Please select POEdit when proposed.\n\n"+
+ "Notes: \n"+
+ " - POEdit must be installed on you system.\n"+
+ " - If no choice is proposed, use file manager to change POT/PO file properties.\n",
+ "SVGHMII18SnapWarning")
+ poedit_path = "xdg-open"
+ else:
+ try:
+ poedit_path = subprocess.check_output("command -v poedit", shell=True).strip()
+ except subprocess.CalledProcessError:
+ poedit_path = None
if poedit_path is None:
wx.MessageBox("POEdit is not found or installed !")
--- a/svghmi/svghmi.py Wed Mar 01 10:54:54 2023 +0100
+++ b/svghmi/svghmi.py Fri Mar 03 19:20:49 2023 +0100
@@ -8,6 +8,7 @@
from __future__ import absolute_import
import os
+import sys
import shutil
import hashlib
import shlex
@@ -302,10 +303,14 @@
return ret
-if wx.Platform == '__WXMSW__':
+if sys.platform.startswith('win'):
default_cmds={
"launch":"cmd.exe /c 'start msedge {url}'",
"watchdog":"cmd.exe /k 'echo watchdog for {url} !'"}
+elif os.environ.has_key("SNAP"):
+ default_cmds={
+ "launch":"xdg-open {url}",
+ "watchdog":"echo Watchdog for {name} !"}
else:
default_cmds={
"launch":"chromium {url}",
@@ -736,7 +741,7 @@
return res
def _ImportSVG(self):
- dialog = wx.FileDialog(self.GetCTRoot().AppFrame, _("Choose a SVG file"), os.getcwd(), "", _("SVG files (*.svg)|*.svg|All files|*.*"), wx.OPEN)
+ dialog = wx.FileDialog(self.GetCTRoot().AppFrame, _("Choose a SVG file"), os.getcwd(), "", _("SVG files (*.svg)|*.svg|All files|*.*"), wx.FD_OPEN)
if dialog.ShowModal() == wx.ID_OK:
svgpath = dialog.GetPath()
if os.path.isfile(svgpath):
@@ -780,7 +785,7 @@
def _EditPO(self):
""" Select a specific translation and edit it with POEdit """
project_path = self.CTNPath()
- dialog = wx.FileDialog(self.GetCTRoot().AppFrame, _("Choose a PO file"), project_path, "", _("PO files (*.po)|*.po"), wx.OPEN)
+ dialog = wx.FileDialog(self.GetCTRoot().AppFrame, _("Choose a PO file"), project_path, "", _("PO files (*.po)|*.po"), wx.FD_OPEN)
if dialog.ShowModal() == wx.ID_OK:
POFile = dialog.GetPath()
if os.path.isfile(POFile):
@@ -806,7 +811,7 @@
_("Choose a font"),
os.path.expanduser("~"),
"",
- _("Font files (*.ttf;*.otf;*.woff;*.woff2)|*.ttf;*.otf;*.woff;*.woff2"), wx.OPEN)
+ _("Font files (*.ttf;*.otf;*.woff;*.woff2)|*.ttf;*.otf;*.woff;*.woff2"), wx.FD_OPEN)
if dialog.ShowModal() == wx.ID_OK:
fontfile = dialog.GetPath()
@@ -843,7 +848,7 @@
_("Choose a font to remove"),
fontdir,
"",
- _("Font files (*.ttf;*.otf;*.woff;*.woff2)|*.ttf;*.otf;*.woff;*.woff2"), wx.OPEN)
+ _("Font files (*.ttf;*.otf;*.woff;*.woff2)|*.ttf;*.otf;*.woff;*.woff2"), wx.FD_OPEN)
if dialog.ShowModal() == wx.ID_OK:
fontfile = dialog.GetPath()
if os.path.isfile(fontfile):
--- a/svghmi/ui.py Wed Mar 01 10:54:54 2023 +0100
+++ b/svghmi/ui.py Fri Mar 03 19:20:49 2023 +0100
@@ -31,6 +31,19 @@
from util.ProcessLogger import ProcessLogger
+# When running as a confined Snap, /tmp isn't accessible from the outside
+# and Widget DnD to Inkscape can't work, since it can't find generated svg
+# This forces tmp directory in $SNAP_USER_DATA, accessible from other apps
+if os.environ.has_key("SNAP"):
+ NamedTemporaryFile_orig = NamedTemporaryFile
+ tmpdir = os.path.join(os.environ["SNAP_USER_DATA"], ".tmp")
+ if not os.path.exists(tmpdir):
+ os.mkdir(tmpdir)
+ def NamedTemporaryFile(*args,**kwargs):
+ kwargs["dir"] = tmpdir
+ return NamedTemporaryFile_orig(*args, **kwargs)
+
+
ScriptDirectory = paths.AbsDir(__file__)
HMITreeDndMagicWord = "text/beremiz-hmitree"
@@ -58,13 +71,13 @@
display_name = ('{} (class={})'.format(c.name, c.hmiclass)) \
if c.hmiclass is not None else c.name
tc_child = self.AppendItem(current_tc_root, display_name)
- self.SetPyData(tc_child, c)
+ self.SetItemData(tc_child, c)
self._recurseTree(c,tc_child)
else:
display_name = '{} {}'.format(c.nodetype[4:], c.name)
tc_child = self.AppendItem(current_tc_root, display_name)
- self.SetPyData(tc_child, c)
+ self.SetItemData(tc_child, c)
def OnTreeNodeSelection(self, event):
items = self.GetSelections()
@@ -106,7 +119,7 @@
root_display_name = _("Please build to see HMI Tree") \
if hmi_tree_root is None else "HMI"
self.root = self.AddRoot(root_display_name)
- self.SetPyData(self.root, hmi_tree_root)
+ self.SetItemData(self.root, hmi_tree_root)
if hmi_tree_root is not None:
self._recurseTree(hmi_tree_root, self.root)
@@ -146,11 +159,11 @@
for d in dirlist:
current_tc_root = self.AppendItem(current_tc_root, d)
res.append(current_tc_root)
- self.SetPyData(current_tc_root, None)
+ self.SetItemData(current_tc_root, None)
dirlist = []
res.pop()
tc_child = self.AppendItem(current_tc_root, f)
- self.SetPyData(tc_child, p)
+ self.SetItemData(tc_child, p)
return res
def MakeTree(self, lib_dir = None):
@@ -163,7 +176,7 @@
root_display_name = _("Please select widget library directory") \
if lib_dir is None else os.path.basename(lib_dir)
self.root = self.AddRoot(root_display_name)
- self.SetPyData(self.root, None)
+ self.SetItemData(self.root, None)
if lib_dir is not None and os.path.exists(lib_dir):
self._recurseTree(lib_dir, self.root, [])
@@ -464,7 +477,8 @@
# TODO: spawn a thread, to decouple thumbnail gen
status, result, _err_result = ProcessLogger(
- self.Controler.GetCTRoot().logger,
+ #self.Controler.GetCTRoot().logger,
+ None,
'"' + inkpath + '" "' + svgpath + '" ' +
export_opt + ' "' + thumbpath +
'" -D -h ' + str(_preview_height)).spin()
--- a/targets/XSD_toolchain_makefile Wed Mar 01 10:54:54 2023 +0100
+++ b/targets/XSD_toolchain_makefile Fri Mar 03 19:20:49 2023 +0100
@@ -1,3 +1,3 @@
- <xsd:attribute name="Command" type="xsd:string" use="optional" default="make -C %(buildpath)s all BEREMIZSRC=%(src)s BEREMIZCFLAGS=%(cflags)s MD5=%(md5)s USE_BEREMIZ=1 FROM_BEREMIZ=1"/>
+ <xsd:attribute name="Command" type="xsd:string" use="optional" default="make -C %(buildpath)s -f ../project_files/Makefile all BEREMIZSRC=%(src)s BEREMIZCFLAGS=%(cflags)s MD5=%(md5)s USE_BEREMIZ=1 FROM_BEREMIZ=1"/>
--- a/targets/toolchain_gcc.py Wed Mar 01 10:54:54 2023 +0100
+++ b/targets/toolchain_gcc.py Fri Mar 03 19:20:49 2023 +0100
@@ -51,14 +51,24 @@
"""
Returns list of builder specific CFLAGS
"""
- return [self.CTRInstance.GetTarget().getcontent().getCFLAGS()]
+ cflags = [self.CTRInstance.GetTarget().getcontent().getCFLAGS()]
+ if os.environ.has_key("CFLAGS"):
+ cflags.append(os.environ["CFLAGS"])
+ if os.environ.has_key("SYSROOT"):
+ cflags.append("--sysroot="+os.environ["SYSROOT"])
+ return cflags
def getBuilderLDFLAGS(self):
"""
Returns list of builder specific LDFLAGS
"""
- return self.CTRInstance.LDFLAGS + \
+ ldflags = self.CTRInstance.LDFLAGS + \
[self.CTRInstance.GetTarget().getcontent().getLDFLAGS()]
+ if os.environ.has_key("LDFLAGS"):
+ ldflags.append(os.environ["LDFLAGS"])
+ if os.environ.has_key("SYSROOT"):
+ ldflags.append("--sysroot="+os.environ["SYSROOT"])
+ return ldflags
def getCompiler(self):
"""
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/Makefile Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,229 @@
+#! gmake
+
+# beremiz/tests/Makefile :
+#
+# Makefile to prepare and run Beremiz tests.
+#
+# For developper to:
+# - quickly run a test (TDD) on current code
+# - write new tests, debug existing tests
+#
+# Use cases :
+#
+# run given tests
+# $ make run_python_exemple.sikuli
+#
+# run tests from particular test classes
+# $ make ide_tests
+#
+# run one particular test in a Xnest window
+# $ make xnest_run_python_exemple.sikuli
+#
+# run Xnest window with just xterm
+# $ make xnest_xterm
+#
+# run Xnest window with sikuli IDE and xterm
+# $ make xnest_sikuli
+#
+# build minimal beremiz and matiec to run tests
+# $ make built_apps
+#
+# For CI/CD scripts to catch and report all failures. Use cases :
+#
+# run all tests
+# $ make
+#
+#
+# Test results, and other test byproducts are in $(test_dir),
+# $(test_dir) defaults to $(HOME)/test and can be overloaded:
+# $ make test_dir=${HOME}/other_test_dir
+#
+# Makefile attemps to use xvfb-run to run each test individually with its own
+# X server instance. This behavior can be overloaded
+# $ DISPLAY=:42 make xserver_command='echo "Using $DISPLAY X Server !";'
+#
+# Matiec and Beremiz code are expected to be clean, ready to build
+# Any change in Matiec directory triggers rebuild of matiec.
+# Any change in Matiec and Beremiz directory triggers copy of source code
+# to $(test_dir)/build.
+#
+# BEREMIZPYTHONPATH is expected to be absolute path to python interpreter
+#
+# Please note:
+# In order to run asside a freshly build Matiec, tested beremiz instance
+# needs to run on code from $(test_dir)/build/beremiz, a fresh copy
+# of the Beremiz directory $(src)/beremiz, where we run tests from.
+#
+
+all: source_check cli_tests ide_tests runtime_tests
+
+# Variable $(src) is directory such that executed
+# $(src)/Makefile is this file.
+src := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
+
+# $(workspace) is directory containing this project
+workspace ?= $(abspath $(src)/../..)
+
+test_dir ?= $(HOME)/test
+build_dir = $(test_dir)/build
+
+#
+# SOURCE and BUILD
+#
+
+BUILT_PROJECTS=beremiz matiec open62541
+
+tar_opts=--absolute-names --exclude=.hg --exclude=.git --exclude=.*.pyc --exclude=.*.swp
+
+# sha1 checksum of source is used to force copy/compile on each change
+
+define make_checksum_assign
+$(1)_checksum = $(shell tar $(tar_opts) -c $(workspace)/$(1) | sha1sum | cut -d ' ' -f 1)
+endef
+$(foreach project,$(BUILT_PROJECTS),$(eval $(call make_checksum_assign,$(project))))
+
+$(build_dir):
+ mkdir -p $(build_dir)
+
+define make_src_rule
+$(build_dir)/$(1)/$($(1)_checksum).sha1: | $(build_dir) $(workspace)/$(1)
+ rm -rf $(build_dir)/$(1)
+ tar -C $(workspace) $(tar_opts) -c $(1) | tar -C $(build_dir) -x
+ touch $$@
+endef
+$(foreach project,$(BUILT_PROJECTS),$(eval $(call make_src_rule,$(project))))
+
+$(build_dir)/matiec/iec2c: $(build_dir)/matiec/$(matiec_checksum).sha1
+ cd $(build_dir)/matiec && \
+ autoreconf -i && \
+ ./configure && \
+ make
+
+$(build_dir)/open62541/build/bin/libopen62541.a: $(build_dir)/open62541/$(open62541_checksum).sha1
+ cd $(build_dir)/open62541 && \
+ rm -rf build && mkdir build && cd build && \
+ cmake -D UA_ENABLE_ENCRYPTION=OPENSSL .. && \
+ make
+
+built_apps: $(build_dir)/matiec/iec2c $(build_dir)/beremiz/$(beremiz_checksum).sha1 $(build_dir)/open62541/build/bin/libopen62541.a
+ touch $@
+
+define log_command
+ $(call $(1),$(2)) | tee test_stdout.txt; exit $$$${PIPESTATUS[0]}
+endef
+
+define prep_test
+ rm -rf $(test_dir)/$(1)_results
+ mkdir $(test_dir)/$(1)_results
+ cd $(test_dir)/$(1)_results
+endef
+
+#
+# IDE TESTS
+#
+
+ide_test_dir = $(src)/ide_tests
+sikuli_ide_tests = $(subst $(ide_test_dir)/,,$(wildcard $(ide_test_dir)/*.sikuli))
+pytest_ide_tests = $(subst $(ide_test_dir)/,,$(wildcard $(ide_test_dir)/*.pytest))
+
+fluxbox_command ?= echo "session.screen0.toolbar.placement: TopCenter" > fluxbox_init; (fluxbox -rc fluxbox_init >/dev/null 2>&1 &)
+
+define sikuli_idetest_command
+ $(fluxbox_command); BEREMIZPATH=$(build_dir)/beremiz sikulix -r $(src)/ide_tests/$(1)
+endef
+
+
+DELAY=400
+KILL_DELAY=430
+PYTEST=$(dir $(BEREMIZPYTHONPATH))/pytest
+define pytest_idetest_command
+ $(fluxbox_command); PYTHONPATH=$(ide_test_dir) timeout -k $(KILL_DELAY) $(DELAY) $(PYTEST) --maxfail=1 --timeout=100 $(src)/ide_tests/$(1)
+endef
+
+# Xnest based interactive sessions for tests edit and debug.
+# Would be nice with something equivalent to xvfb-run, waiting for USR1.
+# Arbitrary "sleep 1" is probably enough for interactive use
+define xnest_run
+ Xnest :42 -geometry 1920x1080+0+0 & export xnestpid=$$!; sleep 1; DISPLAY=:42 $(1); export res=$$?; kill $${xnestpid} 2>/dev/null; exit $${res}
+endef
+
+xserver_command ?= xvfb-run -s '-screen 0 1920x1080x24'
+
+define make_idetest_rule
+$(test_dir)/$(1)_results/.passed: built_apps
+ $(call prep_test,$(1)); $(xserver_command) bash -c '$(call log_command,$(2),$(1))'
+ touch $$@
+
+# Manually invoked rule {testname}.sikuli
+$(1): $(test_dir)/$(1)_results/.passed
+
+# Manually invoked rule xnest_{testname}.sikuli
+# runs test in xnest so that one can see what happens
+xnest_$(1): built_apps
+ $(call prep_test,$(1)); $$(call xnest_run, bash -c '$(call log_command,$(2),$(1))')
+
+ide_tests_targets += $(test_dir)/$(1)_results/.passed
+endef
+$(foreach idetest,$(sikuli_ide_tests),$(eval $(call make_idetest_rule,$(idetest),sikuli_idetest_command)))
+$(foreach idetest,$(pytest_ide_tests),$(eval $(call make_idetest_rule,$(idetest),pytest_idetest_command)))
+
+ide_tests : $(ide_tests_targets)
+ echo "$(ide_tests_targets) : Passed"
+
+xnest_xterm: built_apps
+ $(call xnest_run, bash -c '$(fluxbox_command);xterm')
+
+xnest_sikuli: built_apps
+ $(call xnest_run, bash -c '$(fluxbox_command);(BEREMIZPATH=$(build_dir)/beremiz xterm -e sikulix &);xterm')
+
+xvfb_sikuli: built_apps
+ echo "******************************************"
+ echo "On host, run 'xvncviewer 127.0.0.1:5900' to see sikuli X session"
+ echo "Docker container must be created with TESTDEBUG=YES. For example :"
+ echo "./clean_docker_container.sh && ./build_docker_image.sh && TESTDEBUG=YES ./create_docker_container.sh && ./do_test_in_docker.sh xvfb_sikuli"
+ echo "******************************************"
+ $(xserver_command) bash -c '(fluxbox &);(x11vnc &);(BEREMIZPATH=$(build_dir)/beremiz xterm -e sikulix &);xterm'
+
+#
+# CLI TESTS
+#
+
+cli_test_dir = $(src)/cli_tests
+cli_tests = $(subst $(cli_test_dir)/,,$(wildcard $(cli_test_dir)/*.bash))
+
+define clitest_command
+ BEREMIZPATH=$(build_dir)/beremiz source $(src)/cli_tests/$(1)
+endef
+
+define make_clitest_rule
+$(test_dir)/$(1)_results/.passed: built_apps
+ $(call prep_test,$(1)); bash -c '$(call log_command,$(2),$(1))'
+ touch $$@
+
+# Manually invoked rule
+$(1): $(test_dir)/$(1)_results/.passed
+
+cli_tests_targets += $(test_dir)/$(1)_results/.passed
+endef
+$(foreach clitest,$(cli_tests),$(eval $(call make_clitest_rule,$(clitest),clitest_command)))
+
+cli_tests: $(cli_tests_targets)
+ echo "$(cli_tests_targets) : Passed"
+
+clean_results:
+ rm -rf $(test_dir)/*_results
+
+clean: clean_results
+ rm -rf $(build_dir)
+
+
+# TODOs
+
+source_check:
+ echo TODO $@
+
+runtime_tests:
+ echo TODO $@
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/cli_tests/opcua_test.bash Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,87 @@
+#!/bin/bash
+
+rm -f ./SRVOK ./PLCOK
+
+# Run server
+$BEREMIZPYTHONPATH - > >(
+ echo "Start SRV loop"
+ while read line; do
+ # Wait for server to print modified value
+ echo "SRV>> $line"
+ if [[ "$line" == 3.4 ]]; then
+ echo "PLC could write value"
+ touch ./SRVOK
+ fi
+ done
+ echo "End SRV loop"
+) << EOF &
+
+import sys
+import os
+import time
+
+from opcua import ua, Server
+
+server = Server()
+host = os.environ.get("OPCUA_DEFAULT_HOST", "127.0.0.1")
+endpoint = "opc.tcp://"+host+":4840/freeopcua/server/"
+server.set_endpoint(endpoint)
+
+uri = "http://beremiz.github.io"
+idx = server.register_namespace(uri)
+
+objects = server.get_objects_node()
+
+testobj = objects.add_object(idx, "TestObject")
+testvarout = testobj.add_variable(idx, "TestOut", 1.2)
+testvar = testobj.add_variable(idx, "TestIn", 5.6)
+testvar.set_writable()
+
+server.start()
+
+try:
+ while True:
+ time.sleep(1)
+ print testvar.get_value()
+ sys.stdout.flush()
+finally:
+ server.stop()
+EOF
+SERVER_PID=$!
+
+# Start PLC with opcua test
+setsid $BEREMIZPYTHONPATH $BEREMIZPATH/Beremiz_cli.py -k \
+ --project-home $BEREMIZPATH/tests/projects/opcua_client build transfer run > >(
+echo "Start PLC loop"
+while read line; do
+ # Wait for PLC runtime to output expected value on stdout
+ echo "PLC>> $line"
+ if [[ "$line" == 1.2 ]]; then
+ echo "PLC could read value"
+ touch ./PLCOK
+ fi
+done
+echo "End PLC loop"
+) &
+PLC_PID=$!
+
+echo all subprocess started, start polling results
+res=110 # default to ETIMEDOUT
+c=30
+while ((c--)); do
+ if [[ -a ./SRVOK && -a ./PLCOK ]]; then
+ echo got results.
+ res=0 # OK success
+ break
+ else
+ echo waiting.... $c
+ sleep 1
+ fi
+done
+
+# Kill PLC and subprocess
+echo will kill PLC:$PLC_PID and SERVER:$SERVER_PID
+pkill -s $PLC_PID
+kill $SERVER_PID
+
+exit $res
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/cli_tests/opcua_test_encrypted.bash Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,100 @@
+#!/bin/bash
+
+rm -f ./SRVOK ./PLCOK
+
+
+yes "" | openssl req -x509 -newkey rsa:2048 -keyout my_private_key.pem -out my_cert.pem \
+ -days 355 -nodes -addext "subjectAltName = URI:urn:example.org:FreeOpcUa:python-opcua"
+openssl x509 -outform der -in my_cert.pem -out my_cert.der
+
+# Run server
+$BEREMIZPYTHONPATH - > >(
+ echo "Start SRV loop"
+ while read line; do
+ # Wait for server to print modified value
+ echo "SRV>> $line"
+ if [[ "$line" == 3.4 ]]; then
+ echo "PLC could write value"
+ touch ./SRVOK
+ fi
+ done
+ echo "End SRV loop"
+) << EOF &
+
+import sys
+import os
+import time
+
+from opcua import ua, Server
+
+server = Server()
+host = os.environ.get("OPCUA_DEFAULT_HOST", "127.0.0.1")
+endpoint = "opc.tcp://"+host+":4840/freeopcua/server/"
+server.set_endpoint(endpoint)
+
+server.set_security_policy([ua.SecurityPolicyType.Basic256Sha256_SignAndEncrypt])
+server.load_certificate("my_cert.der")
+server.load_private_key("my_private_key.pem")
+
+uri = "http://beremiz.github.io"
+idx = server.register_namespace(uri)
+
+objects = server.get_objects_node()
+
+testobj = objects.add_object(idx, "TestObject")
+testvarout = testobj.add_variable(idx, "TestOut", 1.2)
+testvar = testobj.add_variable(idx, "TestIn", 5.6)
+testvar.set_writable()
+
+server.start()
+
+try:
+ while True:
+ time.sleep(1)
+ print testvar.get_value()
+ sys.stdout.flush()
+finally:
+ server.stop()
+EOF
+SERVER_PID=$!
+
+PROJECT_FILES_DIR=$BEREMIZPATH/tests/projects/opcua_client_encrypted/project_files
+mkdir $PROJECT_FILES_DIR
+cp my_cert.der my_private_key.pem $PROJECT_FILES_DIR
+
+# Start PLC with opcua test
+setsid $BEREMIZPYTHONPATH $BEREMIZPATH/Beremiz_cli.py -k \
+ --project-home $BEREMIZPATH/tests/projects/opcua_client_encrypted build transfer run > >(
+echo "Start PLC loop"
+while read line; do
+ # Wait for PLC runtime to output expected value on stdout
+ echo "PLC>> $line"
+ if [[ "$line" == 1.2 ]]; then
+ echo "PLC could read value"
+ touch ./PLCOK
+ fi
+done
+echo "End PLC loop"
+) &
+PLC_PID=$!
+
+echo all subprocess started, start polling results
+res=110 # default to ETIMEDOUT
+c=45
+while ((c--)); do
+ if [[ -a ./SRVOK && -a ./PLCOK ]]; then
+ echo got results.
+ res=0 # OK success
+ break
+ else
+ echo waiting.... $c
+ sleep 1
+ fi
+done
+
+# Kill PLC and subprocess
+echo will kill PLC:$PLC_PID and SERVER:$SERVER_PID
+pkill -s $PLC_PID
+kill $SERVER_PID
+
+exit $res
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/cli_tests/run_python_example.bash Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+# Run python example throug command line, and check usual output
+
+coproc setsid $BEREMIZPYTHONPATH $BEREMIZPATH/Beremiz_cli.py -k --project-home $BEREMIZPATH/exemples/python build transfer run;
+
+while read -u ${COPROC[0]} line; do
+ echo "$line"
+ if [[ "$line" == *Grumpf* ]]; then
+ pkill -9 -s $COPROC_PID
+ exit 0
+ fi
+done
+
+exit 42
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/ide_tests/conftest.py Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,78 @@
+#!/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()
Binary file tests/ide_tests/debug_project.sikuli/1646066996789.png has changed
Binary file tests/ide_tests/debug_project.sikuli/1646066996790.png has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/ide_tests/debug_project.sikuli/debug_project.py Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,79 @@
+""" This test opens, modifies, builds and runs exemple project named "python".
+Test succeeds if runtime's stdout behaves as expected
+"""
+
+import os
+import time
+
+# allow module import from current test directory's parent
+addImportPath(os.path.dirname(getBundlePath()))
+
+# common test definitions module
+from sikuliberemiz import run_test
+
+def test(app):
+
+ app.k.Clean()
+
+ app.waitForChangeAndIdleStdout()
+
+ app.k.Build()
+
+ app.waitPatternInStdout("Successfully built.", 10)
+
+ app.k.Connect()
+
+ app.waitForChangeAndIdleStdout()
+
+ app.k.Transfer()
+
+ app.waitForChangeAndIdleStdout()
+
+ app.click("main")
+
+ app.WaitIdleUI()
+
+ app.click("1646066996789.png")
+
+ app.WaitIdleUI()
+
+ app.click("example")
+
+ app.WaitIdleUI()
+
+ app.type(Key.DOWN * 10, Key.CTRL)
+
+ app.WaitIdleUI()
+
+ app.k.Run()
+
+ # wait up to 10 seconds for 10 Grumpfs
+ app.waitPatternInStdout("Grumpf", 10, 10)
+
+ app.rightClick("1646066996790.png")
+
+ # app.click("Force value")
+
+ app.type(Key.DOWN)
+
+ app.type(Key.ENTER)
+
+ # app.click("1646062660790.png")
+
+ # app.type("a", Key.CTRL)
+
+ # app.type(Key.BACKSPACE)
+ app.type(Key.HOME)
+
+ app.type("a", Key.CTRL)
+
+ app.type(Key.DELETE)
+
+ app.type("'sys.stdout.write(\"DEBUG TEST OK\\n\")'")
+
+ app.type(Key.ENTER)
+
+ # wait 10 seconds for 10 patterns
+ return app.waitPatternInStdout("DEBUG TEST OK", 10)
+
+run_test(test, exemple="python")
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/ide_tests/edit_project.sikuli/edit_project.py Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,61 @@
+""" This test opens, modifies, builds and runs exemple project named "python".
+Test succeeds if runtime's stdout behaves as expected
+"""
+
+import os
+import time
+
+# allow module import from current test directory's parent
+addImportPath(os.path.dirname(getBundlePath()))
+
+# common test definitions module
+from sikuliberemiz import run_test
+
+def test(app):
+
+ app.doubleClick("main")
+
+ app.WaitIdleUI()
+
+ app.click("example")
+
+ app.WaitIdleUI()
+
+ app.type(Key.DOWN * 10, Key.CTRL)
+
+ app.WaitIdleUI()
+
+ app.doubleClick("Hello")
+
+ app.WaitIdleUI()
+
+ app.type(Key.TAB*3) # select text content
+
+ app.type("'sys.stdout.write(\"EDIT TEST OK\\n\")'")
+
+ app.type(Key.ENTER)
+
+ app.WaitIdleUI()
+
+ app.k.Clean()
+
+ app.waitForChangeAndIdleStdout()
+
+ app.k.Build()
+
+ app.waitPatternInStdout("Successfully built.", 10)
+
+ app.k.Connect()
+
+ app.waitForChangeAndIdleStdout()
+
+ app.k.Transfer()
+
+ app.waitForChangeAndIdleStdout()
+
+ app.k.Run()
+
+ # wait 10 seconds for 10 patterns
+ return app.waitPatternInStdout("EDIT TEST OK", 10)
+
+run_test(test, exemple="python")
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/ide_tests/load_and_build_tests.pytest/test_application.py Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,232 @@
+#!/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__), "..","..","projects", 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()
+
+ # TODO: also use "exemples/*" projects
+ @ddt.data(
+ #"first_steps",
+ "logging",
+ #"traffic_lights",
+ #"wxGlade",
+ #"python",
+ #"wiimote",
+ # "wxHMI",
+ )
+ @pytest.mark.timeout(300)
+ 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()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/ide_tests/new_project.sikuli/new_project.py Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,136 @@
+""" This test opens, builds and runs a new project.
+Test succeeds if runtime's stdout behaves as expected
+"""
+
+import os
+import time
+
+# allow module import from current test directory's parent
+addImportPath(os.path.dirname(getBundlePath()))
+
+# common test definitions module
+from sikuliberemiz import *
+
+def test(app):
+
+ new_project_path = os.path.join(os.path.abspath(os.path.curdir), "new_test_project")
+
+ # New project path must exist (usually created in directory selection dialog)
+ os.mkdir(new_project_path)
+
+ app.WaitIdleUI()
+
+ # Create new project (opens new project directory selection dialog)
+ app.k.New()
+
+ app.WaitIdleUI()
+
+ # Move to "Home" section of file selecor, otherwise address is
+ # "file ignored" at first run
+ app.type("f", Key.CTRL)
+ app.WaitIdleUI()
+ app.type(Key.ESC)
+ app.WaitIdleUI()
+ app.type(Key.TAB)
+ app.WaitIdleUI()
+
+ # Enter directory by name
+ app.k.Address()
+ app.WaitIdleUI()
+
+ # Fill address bar
+ app.type(new_project_path + Key.ENTER)
+
+ app.WaitIdleUI()
+
+ # When prompted for creating first program select type ST
+ app.type(Key.TAB*4) # go to lang dropdown
+ app.type(Key.DOWN*2) # change selected language
+ app.type(Key.ENTER) # validate
+
+ app.WaitIdleUI()
+
+ # Name created program
+ app.type("Test program")
+
+ app.WaitIdleUI()
+
+ # Focus on Variable grid
+ app.type(Key.TAB*4)
+
+ # Add 2 variables
+ app.type(Key.ADD*2)
+
+ # Focus on ST text
+ app.WaitIdleUI()
+
+ app.type(Key.TAB*8)
+
+ app.type("""\
+ LocalVar0 := LocalVar1;
+ {printf("Test OK\\n");fflush(stdout);}
+ """)
+
+ app.k.Save()
+
+ # Close ST POU
+ app.type("w", Key.CTRL)
+
+ app.WaitIdleUI()
+
+ # Focus project tree and select root item
+ app.type(Key.TAB)
+
+ app.type(Key.LEFT)
+
+ app.type(Key.UP)
+
+ # Edit root item
+ app.type(Key.ENTER)
+
+ app.WaitIdleUI()
+
+ # Switch to config tab
+ app.type(Key.RIGHT*2)
+
+ # Focus on URI
+ app.type(Key.TAB)
+
+ # Set URI
+ app.type("LOCAL://")
+
+ # FIXME: Select other field to ensure URI is validated
+ app.type(Key.TAB)
+
+ app.k.Save()
+
+ # Close project config editor
+ app.type("w", Key.CTRL)
+
+ app.WaitIdleUI()
+
+ # Focus seems undefined at that time (FIXME)
+ # Force focussing on "something" so that next shortcut is taken
+ app.type(Key.TAB)
+
+ app.waitIdleStdout()
+
+ app.k.Build()
+
+ app.waitPatternInStdout("Successfully built.", 10)
+
+ app.k.Connect()
+
+ app.waitIdleStdout()
+
+ app.k.Transfer()
+
+ app.waitIdleStdout()
+
+ app.k.Run()
+
+ # wait 10 seconds
+ return app.waitPatternInStdout("Test OK", 10)
+
+
+run_test(test)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/ide_tests/opcua_browse.sikuli/opcua_browse.py Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,65 @@
+""" This test opens, modifies, builds and runs exemple project named "python".
+Test succeeds if runtime's stdout behaves as expected
+"""
+
+import os
+import time
+
+# allow module import from current test directory's parent
+addImportPath(os.path.dirname(getBundlePath()))
+
+# common test definitions module
+from sikuliberemiz import run_test, AuxiliaryProcess
+
+def test(app):
+
+ server = AuxiliaryProcess(app, ["/bin/bash",os.path.join(getBundlePath(),"opcua_service.bash")])
+
+ app.doubleClick(["opcua_0", "opcua"])
+
+ app.WaitIdleUI()
+
+ app.click("Server")
+
+ app.WaitIdleUI()
+
+ app.doubleClick("Objects")
+
+ app.WaitIdleUI()
+
+ app.doubleClick("TestObject")
+
+ app.dragNdrop(["TestIn", "Testln"], "output variables")
+
+ app.wait(1)
+
+ app.dragNdrop("TestOut", "input variables")
+
+ app.wait(3)
+
+ app.k.Clean()
+
+ app.waitForChangeAndIdleStdout()
+
+ app.k.Build()
+
+ app.waitPatternInStdout("Successfully built.", 10)
+
+ app.k.Connect()
+
+ app.waitForChangeAndIdleStdout()
+
+ app.k.Transfer()
+
+ app.waitForChangeAndIdleStdout()
+
+ app.k.Run()
+
+ # wait 10 seconds for 10 patterns
+ res = app.waitPatternInStdout("6.8", 10)
+
+ server.close()
+
+ return res
+
+run_test(test, testproject="opcua_browse")
Binary file tests/ide_tests/opcua_browse.sikuli/opcua_browse_server.png has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/ide_tests/opcua_browse.sikuli/opcua_service.bash Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+echo "Instant OPC-UA server for test"
+
+# Run server
+exec $BEREMIZPYTHONPATH - << EOF
+
+import sys
+import os
+import time
+
+from opcua import ua, Server
+
+server = Server()
+host = os.environ.get("OPCUA_DEFAULT_HOST", "127.0.0.1")
+endpoint = "opc.tcp://"+host+":4840/freeopcua/server/"
+server.set_endpoint(endpoint)
+
+uri = "http://beremiz.github.io"
+idx = server.register_namespace(uri)
+
+objects = server.get_objects_node()
+
+testobj = objects.add_object(idx, "TestObject")
+testvarout = testobj.add_variable(idx, "TestOut", 1.2)
+testvar = testobj.add_variable(idx, "TestIn", 5.6)
+testvar.set_writable()
+
+server.start()
+
+try:
+ while True:
+ time.sleep(1)
+ inval=testvar.get_value()
+ print inval
+ testvarout.set_value(inval*2)
+ sys.stdout.flush()
+finally:
+ server.stop()
+EOF
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/ide_tests/opcua_browse_encrypted.sikuli/opcua_browse_encrypted.py Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,67 @@
+""" This test opens, modifies, builds and runs exemple project named "python".
+Test succeeds if runtime's stdout behaves as expected
+"""
+
+import os
+import time
+
+# allow module import from current test directory's parent
+addImportPath(os.path.dirname(getBundlePath()))
+
+# common test definitions module
+from sikuliberemiz import run_test, AuxiliaryProcess
+
+def test(app):
+
+ server = AuxiliaryProcess(app, ["/bin/bash",os.path.join(getBundlePath(),"opcua_service.bash")])
+
+ server.waitPatternInStdout("CERTS READY", 5)
+
+ app.doubleClick(["opcua_0", "opcua"])
+
+ app.WaitIdleUI()
+
+ app.click("Server")
+
+ app.WaitIdleUI()
+
+ app.doubleClick("Objects")
+
+ app.WaitIdleUI()
+
+ app.doubleClick("TestObject")
+
+ app.dragNdrop(["TestIn", "Testln"], "output variables")
+
+ app.wait(1)
+
+ app.dragNdrop("TestOut", "input variables")
+
+ app.wait(3)
+
+ app.k.Clean()
+
+ app.waitForChangeAndIdleStdout()
+
+ app.k.Build()
+
+ app.waitPatternInStdout("Successfully built.", 10)
+
+ app.k.Connect()
+
+ app.waitForChangeAndIdleStdout()
+
+ app.k.Transfer()
+
+ app.waitForChangeAndIdleStdout()
+
+ app.k.Run()
+
+ # wait 10 seconds for 10 patterns
+ res = app.waitPatternInStdout("6.8", 10)
+
+ server.close()
+
+ return res
+
+run_test(test, testproject="opcua_browse_encrypted")
Binary file tests/ide_tests/opcua_browse_encrypted.sikuli/opcua_browse_server.png has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/ide_tests/opcua_browse_encrypted.sikuli/opcua_service.bash Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,54 @@
+#!/bin/bash
+
+echo "Instant encrypted OPC-UA server for test"
+
+yes "" | openssl req -x509 -newkey rsa:2048 -keyout my_private_key.pem -out my_cert.pem \
+ -days 355 -nodes -addext "subjectAltName = URI:urn:example.org:FreeOpcUa:python-opcua"
+openssl x509 -outform der -in my_cert.pem -out my_cert.der
+
+PROJECT_FILES_DIR=$BEREMIZPATH/tests/projects/opcua_browse_encrypted/project_files
+mkdir $PROJECT_FILES_DIR
+cp my_cert.der my_private_key.pem $PROJECT_FILES_DIR
+
+echo "CERTS READY"
+
+# Run server
+exec $BEREMIZPYTHONPATH - << EOF
+
+import sys
+import os
+import time
+
+from opcua import ua, Server
+
+server = Server()
+host = os.environ.get("OPCUA_DEFAULT_HOST", "127.0.0.1")
+endpoint = "opc.tcp://"+host+":4840/freeopcua/server/"
+server.set_endpoint(endpoint)
+
+server.set_security_policy([ua.SecurityPolicyType.Basic256Sha256_SignAndEncrypt])
+server.load_certificate("my_cert.der")
+server.load_private_key("my_private_key.pem")
+
+uri = "http://beremiz.github.io"
+idx = server.register_namespace(uri)
+
+objects = server.get_objects_node()
+
+testobj = objects.add_object(idx, "TestObject")
+testvarout = testobj.add_variable(idx, "TestOut", 1.2)
+testvar = testobj.add_variable(idx, "TestIn", 5.6)
+testvar.set_writable()
+
+server.start()
+
+try:
+ while True:
+ time.sleep(1)
+ inval=testvar.get_value()
+ print inval
+ testvarout.set_value(inval*2)
+ sys.stdout.flush()
+finally:
+ server.stop()
+EOF
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/ide_tests/run_python_exemple.sikuli/run_python_exemple.py Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,39 @@
+""" This test opens, builds and runs exemple project named "python".
+Test succeeds if runtime's stdout behaves as expected
+"""
+
+import os
+import time
+
+# allow module import from current test directory's parent
+addImportPath(os.path.dirname(getBundlePath()))
+
+# common test definitions module
+from sikuliberemiz import *
+
+def test(app):
+ # Start the app
+
+ app.k.Clean()
+
+ app.waitForChangeAndIdleStdout()
+
+ app.k.Build()
+
+ app.waitPatternInStdout("Successfully built.", 10)
+
+ app.k.Connect()
+
+ app.waitForChangeAndIdleStdout()
+
+ app.k.Transfer()
+
+ app.waitForChangeAndIdleStdout()
+
+ app.k.Run()
+
+ # wait 10 seconds for 10 Grumpfs
+ return app.waitPatternInStdout("Grumpf", 10, 10)
+
+run_test(test, exemple="python")
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/ide_tests/sikuliberemiz.py Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,441 @@
+"Commons definitions for sikuli based beremiz IDE GUI tests"
+
+import os
+import sys
+import subprocess
+import traceback
+import signal
+import re
+from threading import Thread, Event, Lock
+from time import time as timesec
+from xml.sax.saxutils import escape as escape_xml
+
+import sikuli
+
+beremiz_path = os.environ["BEREMIZPATH"]
+python_bin = os.environ.get("BEREMIZPYTHONPATH", "/usr/bin/python")
+opj = os.path.join
+
+tessdata_path = os.environ["TESSDATAPATH"]
+
+ansi_escape = re.compile(r'(?:\x1B[@-_]|[\x80-\x9F])[0-?]*[ -/]*[@-~]')
+def escape_ansi(line):
+ return ansi_escape.sub('', line)
+
+def escape(txt):
+ return escape_xml(escape_ansi(txt))
+
+class KBDShortcut:
+ """Send shortut to app by calling corresponding methods.
+
+ example:
+ k = KBDShortcut()
+ k.Clean()
+ """
+
+ fkeys = {"Stop": sikuli.Key.F4,
+ "Run": sikuli.Key.F5,
+ "Transfer": sikuli.Key.F6,
+ "Connect": sikuli.Key.F7,
+ "Clean": sikuli.Key.F9,
+ "Build": sikuli.Key.F11,
+ "Save": ("s",sikuli.Key.CTRL),
+ "New": ("n",sikuli.Key.CTRL),
+ "Address": ("l",sikuli.Key.CTRL)} # to reach address bar in GTK's file selector
+
+ def __init__(self, app):
+ self.app = app
+
+ def __getattr__(self, name):
+ fkey = self.fkeys[name]
+ if type(fkey) != tuple:
+ fkey = (fkey,)
+
+ def PressShortCut():
+ self.app.sikuliapp.focus()
+ sikuli.type(*fkey)
+ self.app.ReportText("Sending " + name + " shortcut")
+
+ return PressShortCut
+
+
+class IDEIdleObserver:
+ "Detects when IDE is idle. This is particularly handy when staring an operation and witing for the en of it."
+
+ def __init__(self):
+ """
+ Parameters:
+ app (class BeremizApp)
+ """
+ self.r = sikuli.Region(self.sikuliapp.window())
+ self.targetOffset = self.r.getTopLeft()
+
+ self.idechanged = False
+
+ # 200 was selected because default 50 was still catching cursor blinking in console
+ # FIXME : remove blinking cursor in console
+ self.r.onChange(200,self._OnIDEWindowChange)
+ self.r.observeInBackground()
+
+ def __del__(self):
+ self.r.stopObserver()
+
+ def _OnIDEWindowChange(self, event):
+ self.idechanged = True
+
+ def WaitIdleUI(self, period=1, timeout=15):
+ """
+ Wait for IDE to stop changing
+ Parameters:
+ period (int): how many seconds with no change to consider idle
+ timeout (int): how long to wait for idle, in seconds
+ """
+ c = max(timeout/period,1)
+ while c > 0:
+ self.idechanged = False
+ sikuli.wait(period)
+ if not self.idechanged:
+ break
+ c = c - 1
+
+ self.ReportScreenShot("UI is idle" if c != 0 else "UI is not idle")
+
+ if c == 0:
+ raise Exception("Window did not idle before timeout")
+
+
+class stdoutIdleObserver:
+ "Detects when IDE's stdout is idle. Can be more reliable than pixel based version (false changes ?)"
+
+ def __init__(self):
+ """
+ Parameters:
+ app (class BeremizApp)
+ """
+ self.stdoutchanged = False
+
+ self.event = Event()
+
+ self.pattern = None
+ self.success_event = Event()
+
+ if self.proc is not None:
+ self.thread = Thread(target = self._waitStdoutProc).start()
+
+ def __del__(self):
+ pass # self.thread.join() ?
+
+ def _waitStdoutProc(self):
+ while True:
+ a = self.proc.stdout.readline()
+ if len(a) == 0 or a is None:
+ break
+ self.ReportOutput(a)
+ self.event.set()
+ if self.pattern is not None and a.find(self.pattern) >= 0:
+ sys.stdout.write("found pattern in '" + a +"'")
+ self.success_event.set()
+
+ def waitForChangeAndIdleStdout(self, period=2, timeout=15):
+ """
+ Wait for IDE'stdout to start changing
+ Parameters:
+ timeout (int): how long to wait for change, in seconds
+ """
+ start_time = timesec()
+
+ wait_result = self.event.wait(timeout)
+
+ self.ReportScreenShot("stdout changed" if wait_result else "stdout didn't change")
+
+ if wait_result:
+ self.event.clear()
+ else:
+ raise Exception("Stdout didn't become active before timeout")
+
+ self.waitIdleStdout(period, timeout - (timesec() - start_time))
+
+ def waitIdleStdout(self, period=2, timeout=15):
+ """
+ Wait for IDE'stdout to stop changing
+ Parameters:
+ period (int): how many seconds with no change to consider idle
+ timeout (int): how long to wait for idle, in seconds
+ """
+ end_time = timesec() + timeout
+ self.event.clear()
+ while timesec() < end_time:
+ if self.event.wait(period):
+ # no timeout -> got event -> not idle -> loop again
+ self.event.clear()
+ else:
+ # timeout -> no event -> idle -> exit
+ self.ReportScreenShot("stdout is idle")
+ return True
+
+ self.ReportScreenShot("stdout did not idle")
+
+ raise Exception("Stdout did not idle before timeout")
+
+ def waitPatternInStdout(self, pattern, timeout, count=1):
+ found = 0
+ self.pattern = pattern
+ end_time = timesec() + timeout
+ while True:
+ remain = end_time - timesec()
+ if remain <= 0 :
+ res = False
+ break
+
+ res = self.success_event.wait(remain)
+ if res:
+ self.success_event.clear()
+ found = found + 1
+ if found >= count:
+ break
+ self.pattern = None
+ self.ReportScreenShot("found pattern" if res else "pattern not found")
+ return res
+
+class BeremizApp(IDEIdleObserver, stdoutIdleObserver):
+ def __init__(self, projectpath=None, exemple=None, testproject=None):
+ """
+ Starts Beremiz IDE, waits for main window to appear, maximize it.
+
+ Parameters:
+ projectpath (str): path to project to open
+ exemple (str): path relative to exemples directory
+
+ Returns:
+ Sikuli App class instance
+ """
+ self.ocropts = sikuli.OCR.globalOptions()
+ self.ocropts.dataPath(tessdata_path)
+ self.ocropts.oem(2)
+ self.ocropts.smallFont()
+
+ self.imgnum = 0
+ self.starttime = timesec()
+ self.screen = sikuli.Screen()
+
+ self.report = open("report.xhtml", "w")
+ self.report.write("""\
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta charset="utf-8"/>
+ <meta name="color-scheme" content="light dark"/>
+ <title>Test report</title>
+ </head>
+ <body>
+""")
+
+ command = [python_bin, opj(beremiz_path,"Beremiz.py"), "--log=/dev/stdout"]
+
+ if exemple is not None:
+ command.append(opj(beremiz_path,"exemples",exemple))
+ elif projectpath is not None:
+ command.append(projectpath)
+ elif testproject is not None:
+ command.append(opj(beremiz_path,"tests","projects",testproject))
+
+
+ # App class is broken in Sikuli 2.0.5: can't start process with arguments.
+ #
+ # Workaround : - use subprocess module to spawn IDE process,
+ # - use wmctrl to find IDE window details and maximize it
+ # - pass exact window title to App class constructor
+
+ self.ReportText("Launching " + repr(command))
+
+ self.proc = subprocess.Popen(command, stdout=subprocess.PIPE, bufsize=0)
+
+ # Window are macthed against process' PID
+ ppid = self.proc.pid
+
+ # Timeout 5s
+ c = 50
+ while c > 0:
+ # equiv to "wmctrl -l -p | grep $pid"
+ try:
+ wlist = filter(lambda l:(len(l)>2 and l[2]==str(ppid)), map(lambda s:s.split(None,4), subprocess.check_output(["wmctrl", "-l", "-p"]).splitlines()))
+ except subprocess.CalledProcessError:
+ wlist = []
+
+ # window with no title only has 4 fields do describe it
+ # beremiz splashcreen has no title
+ # wait until main window is visible
+ if len(wlist) == 1 and len(wlist[0]) == 5:
+ windowID,_zero,wpid,_XID,wtitle = wlist[0]
+ break
+
+ sikuli.wait(0.1)
+ c = c - 1
+
+ if c == 0:
+ raise Exception("Couldn't find Beremiz window")
+
+ # Maximize window x and y
+ subprocess.check_call(["wmctrl", "-i", "-r", windowID, "-b", "add,maximized_vert,maximized_horz"])
+
+ # switchApp creates an App object by finding window by title, is not supposed to spawn a process
+ self.sikuliapp = sikuli.switchApp(wtitle)
+ self.k = KBDShortcut(self)
+
+ IDEIdleObserver.__init__(self)
+ stdoutIdleObserver.__init__(self)
+
+ # stubs for common sikuli calls to allow adding hooks later
+ for name, takes_matches in [
+ ("click", True),
+ ("doubleClick", True),
+ ("type", False),
+ ("rightClick", True),
+ ("wait", False)]:
+ def makeMyMeth(n,m):
+ def myMeth(*args, **kwargs):
+ self.ReportScreenShot("Begin: " + n + "(" + repr(args) + "," + repr(kwargs) + ")")
+ if m:
+ args = map(self.handle_PFRML_arg, args)
+ kwargs = dict(map(lambda k,v:(k,self.handle_PFRML_arg(v)), kwargs.items()))
+ try:
+ getattr(sikuli, n)(*args, **kwargs)
+ finally:
+ self.ReportScreenShot("end: " + n + "(" + repr(args) + "," + repr(kwargs) + ")")
+ return myMeth
+ setattr(self, name, makeMyMeth(name,takes_matches))
+
+ def handle_PFRML_arg(self, arg):
+ if type(arg)==list:
+ return self.findBest(*arg)
+ if type(arg)==str and not arg.endswith(".png"):
+ return self.findBest(arg)
+ return arg
+
+ def findBest(self, *args):
+ #match = self.r.findBest(*args)
+ match = None
+ matches = sikuli.OCR.readWords(self.r) + sikuli.OCR.readLines(self.r)
+ for m in matches:
+ mText = m.getText().encode('ascii', 'ignore')
+ for arg in args:
+ if arg in mText:
+ if match is None:
+ match = m
+ if mText == arg:
+ match = m
+ break
+ if match is None:
+ self.ReportText("Not found: " + repr(args) + " OCR content: ")
+ for m in matches:
+ self.ReportText(repr(m) + ": " + m.getText().encode('ascii', 'ignore'))
+ raise Exception("Not Found: " + repr(args))
+
+ # translate match to screen ref
+ #match.setTargetOffset(self.targetOffset)
+ match.setTopLeft(match.getTopLeft().offset(self.targetOffset))
+
+ self.ReportTextImage("Found for " + repr(args) + ": " +
+ " ".join([repr(match), repr(match.getTarget()), repr(match.getTargetOffset())]),
+ self.screen.capture(match))
+ return match.getTarget()
+
+ def dragNdrop(self, src, dst):
+ self.ReportScreenShot("Drag: (" + repr(src) + ")")
+ sikuli.drag(self.handle_PFRML_arg(src))
+ sikuli.mouseMove(5,0)
+ sikuli.dropAt(self.handle_PFRML_arg(dst))
+ self.ReportScreenShot("Drop: (" + repr(dst) + ")")
+
+ def close(self):
+
+ self.ReportScreenShot("Close app")
+ os.kill(self.proc.pid, signal.SIGTERM)
+ #self.sikuliapp.close()
+ #self.sikuliapp = None
+
+ self.report.write("""
+ </body>
+</html>""")
+ self.report.close()
+
+ def __del__(self):
+ if self.sikuliapp is not None:
+ self.sikuliapp.close()
+ IDEIdleObserver.__del__(self)
+ stdoutIdleObserver.__del__(self)
+
+ def ReportScreenShot(self, msg):
+ cap = self.screen.capture(self.r)
+ self.ReportTextImage(msg, cap)
+
+ def ReportTextImage(self, msg, img):
+ elapsed = "%.3fs: "%(timesec() - self.starttime)
+ fname = "capture"+str(self.imgnum)+".png"
+ img.save(".", fname)
+ self.imgnum = self.imgnum + 1
+ self.report.write( "<p>" + escape(elapsed + msg) + "<br/><img src=\""+ fname + "\"/>" + "</p>")
+
+ def ReportText(self, text):
+ elapsed = "%.3fs: "%(timesec() - self.starttime)
+ #res = u"<p><![CDATA[" + elapsed + text + "]]></p>"
+ res = u"<p>" + escape(elapsed + text) + "</p>"
+ self.report.write(res)
+
+ def ReportOutput(self, text):
+ elapsed = "%.3fs: "%(timesec() - self.starttime)
+ sys.stdout.write(elapsed + text)
+ self.report.write("<pre>" + escape(elapsed + text) + "</pre>")
+
+
+class AuxiliaryProcess(stdoutIdleObserver):
+ def __init__(self, beremiz_app, command):
+ self.app = beremiz_app
+ self.app.ReportText("Launching process " + repr(command))
+ self.proc = subprocess.Popen(command, stdout=subprocess.PIPE, bufsize=0)
+ self.app.ReportText("Launched process " + repr(command) + " PID: " + str(self.proc.pid))
+ stdoutIdleObserver.__init__(self)
+
+ def close(self):
+ if self.proc is not None:
+ proc = self.proc
+ self.proc = None
+ # self.proc.stdout.close()
+ self.app.ReportText("Kill process PID: " + str(proc.pid))
+ try:
+ os.kill(proc.pid, signal.SIGTERM)
+ except OSError:
+ pass
+ proc.wait()
+ # self.thread.join()
+
+ def ReportOutput(self, text):
+ self.app.ReportOutput("Aux: "+text)
+
+ def ReportScreenShot(self, msg):
+ self.app.ReportOutput("Aux: "+msg)
+
+ def __del__(self):
+ self.close()
+
+def run_test(func, *args, **kwargs):
+ app = BeremizApp(*args, **kwargs)
+ try:
+ success = func(app)
+ except:
+ # sadly, sys.excepthook is broken in sikuli/jython
+ # purpose of this run_test function is to work around it.
+ # and catch exception cleanly anyhow
+ e_type, e_value, e_traceback = sys.exc_info()
+ err_msg = "\n".join(traceback.format_exception(e_type, e_value, e_traceback))
+ app.ReportOutput(err_msg)
+ success = False
+
+ app.close()
+
+ if success:
+ sikuli.exit(0)
+ else:
+ sikuli.exit(1)
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/ide_tests/wx_widgets.pytest/test_CustomIntCtrl.py Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,140 @@
+#!/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(wx.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()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/projects/opcua_browse/beremiz.xml Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,4 @@
+<?xml version='1.0' encoding='utf-8'?>
+<BeremizRoot xmlns:xsd="http://www.w3.org/2001/XMLSchema" URI_location="LOCAL://">
+ <TargetType/>
+</BeremizRoot>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/projects/opcua_browse/opcua_0@opcua/baseconfnode.xml Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='utf-8'?>
+<BaseParams xmlns:xsd="http://www.w3.org/2001/XMLSchema" IEC_Channel="0" Name="opcua_0"/>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/projects/opcua_browse/opcua_0@opcua/confnode.xml Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,4 @@
+<?xml version='1.0' encoding='utf-8'?>
+<OPCUAClient xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+ <AuthType/>
+</OPCUAClient>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/projects/opcua_browse/plc.xml Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,208 @@
+<?xml version='1.0' encoding='utf-8'?>
+<project xmlns:ns1="http://www.plcopen.org/xml/tc6_0201" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.plcopen.org/xml/tc6_0201">
+ <fileHeader companyName="Unknown" productName="Unnamed" productVersion="1" creationDateTime="2022-07-16T10:46:25"/>
+ <contentHeader name="Unnamed" modificationDateTime="2022-11-10T17:51:34">
+ <coordinateInfo>
+ <fbd>
+ <scaling x="5" y="5"/>
+ </fbd>
+ <ld>
+ <scaling x="0" y="0"/>
+ </ld>
+ <sfc>
+ <scaling x="0" y="0"/>
+ </sfc>
+ </coordinateInfo>
+ </contentHeader>
+ <types>
+ <dataTypes/>
+ <pous>
+ <pou name="program0" pouType="program">
+ <interface>
+ <localVars>
+ <variable name="LocalVar0" address="%IL0.2">
+ <type>
+ <LREAL/>
+ </type>
+ </variable>
+ <variable name="LocalVar1" address="%QL0.3">
+ <type>
+ <LREAL/>
+ </type>
+ </variable>
+ </localVars>
+ <localVars>
+ <variable name="python_poll0">
+ <type>
+ <derived name="python_poll"/>
+ </type>
+ </variable>
+ </localVars>
+ </interface>
+ <body>
+ <FBD>
+ <inVariable localId="1" executionOrderId="0" height="25" width="85" negated="false">
+ <position x="160" y="190"/>
+ <connectionPointOut>
+ <relPosition x="85" y="10"/>
+ </connectionPointOut>
+ <expression>LocalVar0</expression>
+ </inVariable>
+ <outVariable localId="2" executionOrderId="0" height="24" width="82" negated="false">
+ <position x="238" y="49"/>
+ <connectionPointIn>
+ <relPosition x="0" y="11"/>
+ <connection refLocalId="9">
+ <position x="238" y="60"/>
+ <position x="204" y="60"/>
+ </connection>
+ </connectionPointIn>
+ <expression>LocalVar1</expression>
+ </outVariable>
+ <block localId="4" typeName="python_poll" instanceName="python_poll0" executionOrderId="0" height="60" width="98">
+ <position x="658" y="101"/>
+ <inputVariables>
+ <variable formalParameter="TRIG">
+ <connectionPointIn>
+ <relPosition x="0" y="29"/>
+ <connection refLocalId="7">
+ <position x="658" y="130"/>
+ <position x="623" y="130"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ <variable formalParameter="CODE">
+ <connectionPointIn>
+ <relPosition x="0" y="49"/>
+ <connection refLocalId="6" formalParameter="OUT">
+ <position x="658" y="150"/>
+ <position x="560" y="150"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ </inputVariables>
+ <inOutVariables/>
+ <outputVariables>
+ <variable formalParameter="ACK">
+ <connectionPointOut>
+ <relPosition x="98" y="29"/>
+ </connectionPointOut>
+ </variable>
+ <variable formalParameter="RESULT">
+ <connectionPointOut>
+ <relPosition x="98" y="49"/>
+ </connectionPointOut>
+ </variable>
+ </outputVariables>
+ </block>
+ <block localId="5" typeName="LREAL_TO_STRING" executionOrderId="0" height="40" width="130">
+ <position x="280" y="170"/>
+ <inputVariables>
+ <variable formalParameter="IN">
+ <connectionPointIn>
+ <relPosition x="0" y="30"/>
+ <connection refLocalId="1">
+ <position x="280" y="200"/>
+ <position x="255" y="200"/>
+ <position x="255" y="200"/>
+ <position x="300" y="200"/>
+ <position x="300" y="200"/>
+ <position x="245" y="200"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ </inputVariables>
+ <inOutVariables/>
+ <outputVariables>
+ <variable formalParameter="OUT">
+ <connectionPointOut>
+ <relPosition x="130" y="30"/>
+ </connectionPointOut>
+ </variable>
+ </outputVariables>
+ </block>
+ <block localId="6" typeName="CONCAT" executionOrderId="0" height="165" width="63">
+ <position x="497" y="108"/>
+ <inputVariables>
+ <variable formalParameter="IN1">
+ <connectionPointIn>
+ <relPosition x="0" y="42"/>
+ <connection refLocalId="3">
+ <position x="497" y="150"/>
+ <position x="330" y="150"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ <variable formalParameter="IN2">
+ <connectionPointIn>
+ <relPosition x="0" y="92"/>
+ <connection refLocalId="5" formalParameter="OUT">
+ <position x="497" y="200"/>
+ <position x="410" y="200"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ <variable formalParameter="IN3">
+ <connectionPointIn>
+ <relPosition x="0" y="142"/>
+ <connection refLocalId="8">
+ <position x="497" y="250"/>
+ <position x="225" y="250"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ </inputVariables>
+ <inOutVariables/>
+ <outputVariables>
+ <variable formalParameter="OUT">
+ <connectionPointOut>
+ <relPosition x="63" y="42"/>
+ </connectionPointOut>
+ </variable>
+ </outputVariables>
+ </block>
+ <inVariable localId="7" executionOrderId="0" height="24" width="44" negated="false">
+ <position x="579" y="116"/>
+ <connectionPointOut>
+ <relPosition x="44" y="14"/>
+ </connectionPointOut>
+ <expression>TRUE</expression>
+ </inVariable>
+ <inVariable localId="3" executionOrderId="0" height="25" width="180" negated="false">
+ <position x="160" y="140"/>
+ <connectionPointOut>
+ <relPosition x="180" y="10"/>
+ </connectionPointOut>
+ <expression>'pfunc("'</expression>
+ </inVariable>
+ <inVariable localId="8" executionOrderId="0" height="25" width="230" negated="false">
+ <position x="165" y="240"/>
+ <connectionPointOut>
+ <relPosition x="230" y="10"/>
+ </connectionPointOut>
+ <expression>'\n")'</expression>
+ </inVariable>
+ <inVariable localId="9" executionOrderId="0" height="29" width="45" negated="false">
+ <position x="159" y="47"/>
+ <connectionPointOut>
+ <relPosition x="45" y="13"/>
+ </connectionPointOut>
+ <expression>3.4</expression>
+ </inVariable>
+ </FBD>
+ </body>
+ </pou>
+ </pous>
+ </types>
+ <instances>
+ <configurations>
+ <configuration name="config">
+ <resource name="resource1">
+ <task name="task0" priority="0" interval="T#100ms">
+ <pouInstance name="instance0" typeName="program0"/>
+ </task>
+ </resource>
+ </configuration>
+ </configurations>
+ </instances>
+</project>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/projects/opcua_browse/py_ext_0@py_ext/baseconfnode.xml Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='utf-8'?>
+<BaseParams xmlns:xsd="http://www.w3.org/2001/XMLSchema" IEC_Channel="1" Name="py_ext_0"/>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/projects/opcua_browse/py_ext_0@py_ext/pyfile.xml Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,36 @@
+<?xml version='1.0' encoding='utf-8'?>
+<PyFile xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+ <variables/>
+ <globals>
+ <xhtml:p><![CDATA[
+import sys
+def pfunc(arg):
+ sys.stdout.write(arg)
+ sys.stdout.flush()
+
+pfunc("globals section\n")
+
+]]></xhtml:p>
+ </globals>
+ <init>
+ <xhtml:p><![CDATA[
+pfunc("init section\n")
+]]></xhtml:p>
+ </init>
+ <cleanup>
+ <xhtml:p><![CDATA[
+pfunc("cleanup section\n")
+]]></xhtml:p>
+ </cleanup>
+ <start>
+ <xhtml:p><![CDATA[
+pfunc("start section\n")
+]]></xhtml:p>
+ </start>
+ <stop>
+ <xhtml:p><![CDATA[
+pfunc("stop section\n")
+
+]]></xhtml:p>
+ </stop>
+</PyFile>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/projects/opcua_browse_encrypted/beremiz.xml Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,4 @@
+<?xml version='1.0' encoding='utf-8'?>
+<BeremizRoot xmlns:xsd="http://www.w3.org/2001/XMLSchema" URI_location="LOCAL://">
+ <TargetType/>
+</BeremizRoot>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/projects/opcua_browse_encrypted/opcua_0@opcua/baseconfnode.xml Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='utf-8'?>
+<BaseParams xmlns:xsd="http://www.w3.org/2001/XMLSchema" IEC_Channel="0" Name="opcua_0"/>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/projects/opcua_browse_encrypted/opcua_0@opcua/confnode.xml Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,9 @@
+<?xml version='1.0' encoding='utf-8'?>
+<OPCUAClient xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+ <AuthType>
+ <x509 Certificate="my_cert.der" PrivateKey="my_private_key.pem">
+ <Policy/>
+ <Mode/>
+ </x509>
+ </AuthType>
+</OPCUAClient>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/projects/opcua_browse_encrypted/plc.xml Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,208 @@
+<?xml version='1.0' encoding='utf-8'?>
+<project xmlns:ns1="http://www.plcopen.org/xml/tc6_0201" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.plcopen.org/xml/tc6_0201">
+ <fileHeader companyName="Unknown" productName="Unnamed" productVersion="1" creationDateTime="2022-07-16T10:46:25"/>
+ <contentHeader name="Unnamed" modificationDateTime="2022-11-10T17:51:34">
+ <coordinateInfo>
+ <fbd>
+ <scaling x="5" y="5"/>
+ </fbd>
+ <ld>
+ <scaling x="0" y="0"/>
+ </ld>
+ <sfc>
+ <scaling x="0" y="0"/>
+ </sfc>
+ </coordinateInfo>
+ </contentHeader>
+ <types>
+ <dataTypes/>
+ <pous>
+ <pou name="program0" pouType="program">
+ <interface>
+ <localVars>
+ <variable name="LocalVar0" address="%IL0.2">
+ <type>
+ <LREAL/>
+ </type>
+ </variable>
+ <variable name="LocalVar1" address="%QL0.3">
+ <type>
+ <LREAL/>
+ </type>
+ </variable>
+ </localVars>
+ <localVars>
+ <variable name="python_poll0">
+ <type>
+ <derived name="python_poll"/>
+ </type>
+ </variable>
+ </localVars>
+ </interface>
+ <body>
+ <FBD>
+ <inVariable localId="1" executionOrderId="0" height="25" width="85" negated="false">
+ <position x="160" y="190"/>
+ <connectionPointOut>
+ <relPosition x="85" y="10"/>
+ </connectionPointOut>
+ <expression>LocalVar0</expression>
+ </inVariable>
+ <outVariable localId="2" executionOrderId="0" height="24" width="82" negated="false">
+ <position x="238" y="49"/>
+ <connectionPointIn>
+ <relPosition x="0" y="11"/>
+ <connection refLocalId="9">
+ <position x="238" y="60"/>
+ <position x="204" y="60"/>
+ </connection>
+ </connectionPointIn>
+ <expression>LocalVar1</expression>
+ </outVariable>
+ <block localId="4" typeName="python_poll" instanceName="python_poll0" executionOrderId="0" height="60" width="98">
+ <position x="658" y="101"/>
+ <inputVariables>
+ <variable formalParameter="TRIG">
+ <connectionPointIn>
+ <relPosition x="0" y="29"/>
+ <connection refLocalId="7">
+ <position x="658" y="130"/>
+ <position x="623" y="130"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ <variable formalParameter="CODE">
+ <connectionPointIn>
+ <relPosition x="0" y="49"/>
+ <connection refLocalId="6" formalParameter="OUT">
+ <position x="658" y="150"/>
+ <position x="560" y="150"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ </inputVariables>
+ <inOutVariables/>
+ <outputVariables>
+ <variable formalParameter="ACK">
+ <connectionPointOut>
+ <relPosition x="98" y="29"/>
+ </connectionPointOut>
+ </variable>
+ <variable formalParameter="RESULT">
+ <connectionPointOut>
+ <relPosition x="98" y="49"/>
+ </connectionPointOut>
+ </variable>
+ </outputVariables>
+ </block>
+ <block localId="5" typeName="LREAL_TO_STRING" executionOrderId="0" height="40" width="130">
+ <position x="280" y="170"/>
+ <inputVariables>
+ <variable formalParameter="IN">
+ <connectionPointIn>
+ <relPosition x="0" y="30"/>
+ <connection refLocalId="1">
+ <position x="280" y="200"/>
+ <position x="255" y="200"/>
+ <position x="255" y="200"/>
+ <position x="300" y="200"/>
+ <position x="300" y="200"/>
+ <position x="245" y="200"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ </inputVariables>
+ <inOutVariables/>
+ <outputVariables>
+ <variable formalParameter="OUT">
+ <connectionPointOut>
+ <relPosition x="130" y="30"/>
+ </connectionPointOut>
+ </variable>
+ </outputVariables>
+ </block>
+ <block localId="6" typeName="CONCAT" executionOrderId="0" height="165" width="63">
+ <position x="497" y="108"/>
+ <inputVariables>
+ <variable formalParameter="IN1">
+ <connectionPointIn>
+ <relPosition x="0" y="42"/>
+ <connection refLocalId="3">
+ <position x="497" y="150"/>
+ <position x="330" y="150"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ <variable formalParameter="IN2">
+ <connectionPointIn>
+ <relPosition x="0" y="92"/>
+ <connection refLocalId="5" formalParameter="OUT">
+ <position x="497" y="200"/>
+ <position x="410" y="200"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ <variable formalParameter="IN3">
+ <connectionPointIn>
+ <relPosition x="0" y="142"/>
+ <connection refLocalId="8">
+ <position x="497" y="250"/>
+ <position x="225" y="250"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ </inputVariables>
+ <inOutVariables/>
+ <outputVariables>
+ <variable formalParameter="OUT">
+ <connectionPointOut>
+ <relPosition x="63" y="42"/>
+ </connectionPointOut>
+ </variable>
+ </outputVariables>
+ </block>
+ <inVariable localId="7" executionOrderId="0" height="24" width="44" negated="false">
+ <position x="579" y="116"/>
+ <connectionPointOut>
+ <relPosition x="44" y="14"/>
+ </connectionPointOut>
+ <expression>TRUE</expression>
+ </inVariable>
+ <inVariable localId="3" executionOrderId="0" height="25" width="180" negated="false">
+ <position x="160" y="140"/>
+ <connectionPointOut>
+ <relPosition x="180" y="10"/>
+ </connectionPointOut>
+ <expression>'pfunc("'</expression>
+ </inVariable>
+ <inVariable localId="8" executionOrderId="0" height="25" width="230" negated="false">
+ <position x="165" y="240"/>
+ <connectionPointOut>
+ <relPosition x="230" y="10"/>
+ </connectionPointOut>
+ <expression>'\n")'</expression>
+ </inVariable>
+ <inVariable localId="9" executionOrderId="0" height="29" width="45" negated="false">
+ <position x="159" y="47"/>
+ <connectionPointOut>
+ <relPosition x="45" y="13"/>
+ </connectionPointOut>
+ <expression>3.4</expression>
+ </inVariable>
+ </FBD>
+ </body>
+ </pou>
+ </pous>
+ </types>
+ <instances>
+ <configurations>
+ <configuration name="config">
+ <resource name="resource1">
+ <task name="task0" priority="0" interval="T#100ms">
+ <pouInstance name="instance0" typeName="program0"/>
+ </task>
+ </resource>
+ </configuration>
+ </configurations>
+ </instances>
+</project>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/projects/opcua_browse_encrypted/py_ext_0@py_ext/baseconfnode.xml Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='utf-8'?>
+<BaseParams xmlns:xsd="http://www.w3.org/2001/XMLSchema" IEC_Channel="1" Name="py_ext_0"/>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/projects/opcua_browse_encrypted/py_ext_0@py_ext/pyfile.xml Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,36 @@
+<?xml version='1.0' encoding='utf-8'?>
+<PyFile xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+ <variables/>
+ <globals>
+ <xhtml:p><![CDATA[
+import sys
+def pfunc(arg):
+ sys.stdout.write(arg)
+ sys.stdout.flush()
+
+pfunc("globals section\n")
+
+]]></xhtml:p>
+ </globals>
+ <init>
+ <xhtml:p><![CDATA[
+pfunc("init section\n")
+]]></xhtml:p>
+ </init>
+ <cleanup>
+ <xhtml:p><![CDATA[
+pfunc("cleanup section\n")
+]]></xhtml:p>
+ </cleanup>
+ <start>
+ <xhtml:p><![CDATA[
+pfunc("start section\n")
+]]></xhtml:p>
+ </start>
+ <stop>
+ <xhtml:p><![CDATA[
+pfunc("stop section\n")
+
+]]></xhtml:p>
+ </stop>
+</PyFile>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/projects/opcua_client/beremiz.xml Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,4 @@
+<?xml version='1.0' encoding='utf-8'?>
+<BeremizRoot xmlns:xsd="http://www.w3.org/2001/XMLSchema" URI_location="LOCAL://">
+ <TargetType/>
+</BeremizRoot>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/projects/opcua_client/opcua_0@opcua/baseconfnode.xml Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='utf-8'?>
+<BaseParams xmlns:xsd="http://www.w3.org/2001/XMLSchema" IEC_Channel="0" Name="opcua_0"/>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/projects/opcua_client/opcua_0@opcua/confnode.xml Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='utf-8'?>
+<OPCUAClient xmlns:xsd="http://www.w3.org/2001/XMLSchema"/>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/projects/opcua_client/opcua_0@opcua/selected.csv Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,2 @@
+input,TestOut,2,int,2,Double,0
+output,TestIn,2,int,3,Double,0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/projects/opcua_client/plc.xml Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,208 @@
+<?xml version='1.0' encoding='utf-8'?>
+<project xmlns:ns1="http://www.plcopen.org/xml/tc6_0201" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.plcopen.org/xml/tc6_0201">
+ <fileHeader companyName="Unknown" productName="Unnamed" productVersion="1" creationDateTime="2022-07-16T10:46:25"/>
+ <contentHeader name="Unnamed" modificationDateTime="2022-07-16T22:47:46">
+ <coordinateInfo>
+ <fbd>
+ <scaling x="5" y="5"/>
+ </fbd>
+ <ld>
+ <scaling x="0" y="0"/>
+ </ld>
+ <sfc>
+ <scaling x="0" y="0"/>
+ </sfc>
+ </coordinateInfo>
+ </contentHeader>
+ <types>
+ <dataTypes/>
+ <pous>
+ <pou name="program0" pouType="program">
+ <interface>
+ <localVars>
+ <variable name="LocalVar0" address="%IL0.0">
+ <type>
+ <LREAL/>
+ </type>
+ </variable>
+ <variable name="LocalVar1" address="%QL0.0">
+ <type>
+ <LREAL/>
+ </type>
+ </variable>
+ </localVars>
+ <localVars>
+ <variable name="python_poll0">
+ <type>
+ <derived name="python_poll"/>
+ </type>
+ </variable>
+ </localVars>
+ </interface>
+ <body>
+ <FBD>
+ <inVariable localId="1" executionOrderId="0" height="25" width="85" negated="false">
+ <position x="160" y="190"/>
+ <connectionPointOut>
+ <relPosition x="85" y="10"/>
+ </connectionPointOut>
+ <expression>LocalVar0</expression>
+ </inVariable>
+ <outVariable localId="2" executionOrderId="0" height="24" width="82" negated="false">
+ <position x="238" y="49"/>
+ <connectionPointIn>
+ <relPosition x="0" y="11"/>
+ <connection refLocalId="9">
+ <position x="238" y="60"/>
+ <position x="204" y="60"/>
+ </connection>
+ </connectionPointIn>
+ <expression>LocalVar1</expression>
+ </outVariable>
+ <block localId="4" typeName="python_poll" instanceName="python_poll0" executionOrderId="0" height="60" width="98">
+ <position x="658" y="101"/>
+ <inputVariables>
+ <variable formalParameter="TRIG">
+ <connectionPointIn>
+ <relPosition x="0" y="29"/>
+ <connection refLocalId="7">
+ <position x="658" y="130"/>
+ <position x="623" y="130"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ <variable formalParameter="CODE">
+ <connectionPointIn>
+ <relPosition x="0" y="49"/>
+ <connection refLocalId="6" formalParameter="OUT">
+ <position x="658" y="150"/>
+ <position x="560" y="150"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ </inputVariables>
+ <inOutVariables/>
+ <outputVariables>
+ <variable formalParameter="ACK">
+ <connectionPointOut>
+ <relPosition x="98" y="29"/>
+ </connectionPointOut>
+ </variable>
+ <variable formalParameter="RESULT">
+ <connectionPointOut>
+ <relPosition x="98" y="49"/>
+ </connectionPointOut>
+ </variable>
+ </outputVariables>
+ </block>
+ <block localId="5" typeName="LREAL_TO_STRING" executionOrderId="0" height="40" width="130">
+ <position x="280" y="170"/>
+ <inputVariables>
+ <variable formalParameter="IN">
+ <connectionPointIn>
+ <relPosition x="0" y="30"/>
+ <connection refLocalId="1">
+ <position x="280" y="200"/>
+ <position x="255" y="200"/>
+ <position x="255" y="200"/>
+ <position x="300" y="200"/>
+ <position x="300" y="200"/>
+ <position x="245" y="200"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ </inputVariables>
+ <inOutVariables/>
+ <outputVariables>
+ <variable formalParameter="OUT">
+ <connectionPointOut>
+ <relPosition x="130" y="30"/>
+ </connectionPointOut>
+ </variable>
+ </outputVariables>
+ </block>
+ <block localId="6" typeName="CONCAT" executionOrderId="0" height="165" width="63">
+ <position x="497" y="108"/>
+ <inputVariables>
+ <variable formalParameter="IN1">
+ <connectionPointIn>
+ <relPosition x="0" y="42"/>
+ <connection refLocalId="3">
+ <position x="497" y="150"/>
+ <position x="330" y="150"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ <variable formalParameter="IN2">
+ <connectionPointIn>
+ <relPosition x="0" y="92"/>
+ <connection refLocalId="5" formalParameter="OUT">
+ <position x="497" y="200"/>
+ <position x="410" y="200"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ <variable formalParameter="IN3">
+ <connectionPointIn>
+ <relPosition x="0" y="142"/>
+ <connection refLocalId="8">
+ <position x="497" y="250"/>
+ <position x="225" y="250"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ </inputVariables>
+ <inOutVariables/>
+ <outputVariables>
+ <variable formalParameter="OUT">
+ <connectionPointOut>
+ <relPosition x="63" y="42"/>
+ </connectionPointOut>
+ </variable>
+ </outputVariables>
+ </block>
+ <inVariable localId="7" executionOrderId="0" height="24" width="44" negated="false">
+ <position x="579" y="116"/>
+ <connectionPointOut>
+ <relPosition x="44" y="14"/>
+ </connectionPointOut>
+ <expression>TRUE</expression>
+ </inVariable>
+ <inVariable localId="3" executionOrderId="0" height="25" width="180" negated="false">
+ <position x="160" y="140"/>
+ <connectionPointOut>
+ <relPosition x="180" y="10"/>
+ </connectionPointOut>
+ <expression>'pfunc("'</expression>
+ </inVariable>
+ <inVariable localId="8" executionOrderId="0" height="25" width="230" negated="false">
+ <position x="165" y="240"/>
+ <connectionPointOut>
+ <relPosition x="230" y="10"/>
+ </connectionPointOut>
+ <expression>'\n")'</expression>
+ </inVariable>
+ <inVariable localId="9" executionOrderId="0" height="29" width="45" negated="false">
+ <position x="159" y="47"/>
+ <connectionPointOut>
+ <relPosition x="45" y="13"/>
+ </connectionPointOut>
+ <expression>3.4</expression>
+ </inVariable>
+ </FBD>
+ </body>
+ </pou>
+ </pous>
+ </types>
+ <instances>
+ <configurations>
+ <configuration name="config">
+ <resource name="resource1">
+ <task name="task0" priority="0" interval="T#100ms">
+ <pouInstance name="instance0" typeName="program0"/>
+ </task>
+ </resource>
+ </configuration>
+ </configurations>
+ </instances>
+</project>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/projects/opcua_client/py_ext_0@py_ext/baseconfnode.xml Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='utf-8'?>
+<BaseParams xmlns:xsd="http://www.w3.org/2001/XMLSchema" IEC_Channel="1" Name="py_ext_0"/>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/projects/opcua_client/py_ext_0@py_ext/pyfile.xml Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,36 @@
+<?xml version='1.0' encoding='utf-8'?>
+<PyFile xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+ <variables/>
+ <globals>
+ <xhtml:p><![CDATA[
+import sys
+def pfunc(arg):
+ sys.stdout.write(arg)
+ sys.stdout.flush()
+
+pfunc("globals section\n")
+
+]]></xhtml:p>
+ </globals>
+ <init>
+ <xhtml:p><![CDATA[
+pfunc("init section\n")
+]]></xhtml:p>
+ </init>
+ <cleanup>
+ <xhtml:p><![CDATA[
+pfunc("cleanup section\n")
+]]></xhtml:p>
+ </cleanup>
+ <start>
+ <xhtml:p><![CDATA[
+pfunc("start section\n")
+]]></xhtml:p>
+ </start>
+ <stop>
+ <xhtml:p><![CDATA[
+pfunc("stop section\n")
+
+]]></xhtml:p>
+ </stop>
+</PyFile>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/projects/opcua_client_encrypted/beremiz.xml Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,4 @@
+<?xml version='1.0' encoding='utf-8'?>
+<BeremizRoot xmlns:xsd="http://www.w3.org/2001/XMLSchema" URI_location="LOCAL://">
+ <TargetType/>
+</BeremizRoot>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/projects/opcua_client_encrypted/opcua_0@opcua/baseconfnode.xml Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='utf-8'?>
+<BaseParams xmlns:xsd="http://www.w3.org/2001/XMLSchema" IEC_Channel="0" Name="opcua_0"/>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/projects/opcua_client_encrypted/opcua_0@opcua/confnode.xml Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,9 @@
+<?xml version='1.0' encoding='utf-8'?>
+<OPCUAClient xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+ <AuthType>
+ <x509 Certificate="my_cert.der" PrivateKey="my_private_key.pem">
+ <Policy/>
+ <Mode/>
+ </x509>
+ </AuthType>
+</OPCUAClient>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/projects/opcua_client_encrypted/opcua_0@opcua/selected.csv Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,2 @@
+input,TestOut,2,int,2,Double,0
+output,TestIn,2,int,3,Double,0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/projects/opcua_client_encrypted/plc.xml Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,208 @@
+<?xml version='1.0' encoding='utf-8'?>
+<project xmlns:ns1="http://www.plcopen.org/xml/tc6_0201" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.plcopen.org/xml/tc6_0201">
+ <fileHeader companyName="Unknown" productName="Unnamed" productVersion="1" creationDateTime="2022-07-16T10:46:25"/>
+ <contentHeader name="Unnamed" modificationDateTime="2022-09-15T20:56:16">
+ <coordinateInfo>
+ <fbd>
+ <scaling x="5" y="5"/>
+ </fbd>
+ <ld>
+ <scaling x="0" y="0"/>
+ </ld>
+ <sfc>
+ <scaling x="0" y="0"/>
+ </sfc>
+ </coordinateInfo>
+ </contentHeader>
+ <types>
+ <dataTypes/>
+ <pous>
+ <pou name="program0" pouType="program">
+ <interface>
+ <localVars>
+ <variable name="LocalVar0" address="%IL0.0">
+ <type>
+ <LREAL/>
+ </type>
+ </variable>
+ <variable name="LocalVar1" address="%QL0.0">
+ <type>
+ <LREAL/>
+ </type>
+ </variable>
+ </localVars>
+ <localVars>
+ <variable name="python_poll0">
+ <type>
+ <derived name="python_poll"/>
+ </type>
+ </variable>
+ </localVars>
+ </interface>
+ <body>
+ <FBD>
+ <inVariable localId="1" executionOrderId="0" height="25" width="85" negated="false">
+ <position x="160" y="190"/>
+ <connectionPointOut>
+ <relPosition x="85" y="10"/>
+ </connectionPointOut>
+ <expression>LocalVar0</expression>
+ </inVariable>
+ <outVariable localId="2" executionOrderId="0" height="24" width="82" negated="false">
+ <position x="238" y="49"/>
+ <connectionPointIn>
+ <relPosition x="0" y="11"/>
+ <connection refLocalId="9">
+ <position x="238" y="60"/>
+ <position x="204" y="60"/>
+ </connection>
+ </connectionPointIn>
+ <expression>LocalVar1</expression>
+ </outVariable>
+ <block localId="4" typeName="python_poll" instanceName="python_poll0" executionOrderId="0" height="60" width="98">
+ <position x="658" y="101"/>
+ <inputVariables>
+ <variable formalParameter="TRIG">
+ <connectionPointIn>
+ <relPosition x="0" y="29"/>
+ <connection refLocalId="7">
+ <position x="658" y="130"/>
+ <position x="623" y="130"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ <variable formalParameter="CODE">
+ <connectionPointIn>
+ <relPosition x="0" y="49"/>
+ <connection refLocalId="6" formalParameter="OUT">
+ <position x="658" y="150"/>
+ <position x="560" y="150"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ </inputVariables>
+ <inOutVariables/>
+ <outputVariables>
+ <variable formalParameter="ACK">
+ <connectionPointOut>
+ <relPosition x="98" y="29"/>
+ </connectionPointOut>
+ </variable>
+ <variable formalParameter="RESULT">
+ <connectionPointOut>
+ <relPosition x="98" y="49"/>
+ </connectionPointOut>
+ </variable>
+ </outputVariables>
+ </block>
+ <block localId="5" typeName="LREAL_TO_STRING" executionOrderId="0" height="40" width="130">
+ <position x="280" y="170"/>
+ <inputVariables>
+ <variable formalParameter="IN">
+ <connectionPointIn>
+ <relPosition x="0" y="30"/>
+ <connection refLocalId="1">
+ <position x="280" y="200"/>
+ <position x="255" y="200"/>
+ <position x="255" y="200"/>
+ <position x="300" y="200"/>
+ <position x="300" y="200"/>
+ <position x="245" y="200"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ </inputVariables>
+ <inOutVariables/>
+ <outputVariables>
+ <variable formalParameter="OUT">
+ <connectionPointOut>
+ <relPosition x="130" y="30"/>
+ </connectionPointOut>
+ </variable>
+ </outputVariables>
+ </block>
+ <block localId="6" typeName="CONCAT" executionOrderId="0" height="165" width="63">
+ <position x="497" y="108"/>
+ <inputVariables>
+ <variable formalParameter="IN1">
+ <connectionPointIn>
+ <relPosition x="0" y="42"/>
+ <connection refLocalId="3">
+ <position x="497" y="150"/>
+ <position x="330" y="150"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ <variable formalParameter="IN2">
+ <connectionPointIn>
+ <relPosition x="0" y="92"/>
+ <connection refLocalId="5" formalParameter="OUT">
+ <position x="497" y="200"/>
+ <position x="410" y="200"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ <variable formalParameter="IN3">
+ <connectionPointIn>
+ <relPosition x="0" y="142"/>
+ <connection refLocalId="8">
+ <position x="497" y="250"/>
+ <position x="225" y="250"/>
+ </connection>
+ </connectionPointIn>
+ </variable>
+ </inputVariables>
+ <inOutVariables/>
+ <outputVariables>
+ <variable formalParameter="OUT">
+ <connectionPointOut>
+ <relPosition x="63" y="42"/>
+ </connectionPointOut>
+ </variable>
+ </outputVariables>
+ </block>
+ <inVariable localId="7" executionOrderId="0" height="24" width="44" negated="false">
+ <position x="579" y="116"/>
+ <connectionPointOut>
+ <relPosition x="44" y="14"/>
+ </connectionPointOut>
+ <expression>TRUE</expression>
+ </inVariable>
+ <inVariable localId="3" executionOrderId="0" height="25" width="180" negated="false">
+ <position x="160" y="140"/>
+ <connectionPointOut>
+ <relPosition x="180" y="10"/>
+ </connectionPointOut>
+ <expression>'pfunc("'</expression>
+ </inVariable>
+ <inVariable localId="8" executionOrderId="0" height="25" width="230" negated="false">
+ <position x="165" y="240"/>
+ <connectionPointOut>
+ <relPosition x="230" y="10"/>
+ </connectionPointOut>
+ <expression>'\n")'</expression>
+ </inVariable>
+ <inVariable localId="9" executionOrderId="0" height="29" width="45" negated="false">
+ <position x="159" y="47"/>
+ <connectionPointOut>
+ <relPosition x="45" y="13"/>
+ </connectionPointOut>
+ <expression>3.4</expression>
+ </inVariable>
+ </FBD>
+ </body>
+ </pou>
+ </pous>
+ </types>
+ <instances>
+ <configurations>
+ <configuration name="config">
+ <resource name="resource1">
+ <task name="task0" priority="0" interval="T#100ms">
+ <pouInstance name="instance0" typeName="program0"/>
+ </task>
+ </resource>
+ </configuration>
+ </configurations>
+ </instances>
+</project>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/projects/opcua_client_encrypted/py_ext_0@py_ext/baseconfnode.xml Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='utf-8'?>
+<BaseParams xmlns:xsd="http://www.w3.org/2001/XMLSchema" IEC_Channel="1" Name="py_ext_0"/>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/projects/opcua_client_encrypted/py_ext_0@py_ext/pyfile.xml Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,36 @@
+<?xml version='1.0' encoding='utf-8'?>
+<PyFile xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+ <variables/>
+ <globals>
+ <xhtml:p><![CDATA[
+import sys
+def pfunc(arg):
+ sys.stdout.write(arg)
+ sys.stdout.flush()
+
+pfunc("globals section\n")
+
+]]></xhtml:p>
+ </globals>
+ <init>
+ <xhtml:p><![CDATA[
+pfunc("init section\n")
+]]></xhtml:p>
+ </init>
+ <cleanup>
+ <xhtml:p><![CDATA[
+pfunc("cleanup section\n")
+]]></xhtml:p>
+ </cleanup>
+ <start>
+ <xhtml:p><![CDATA[
+pfunc("start section\n")
+]]></xhtml:p>
+ </start>
+ <stop>
+ <xhtml:p><![CDATA[
+pfunc("stop section\n")
+
+]]></xhtml:p>
+ </stop>
+</PyFile>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/tools/Docker/Dockerfile Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,90 @@
+#
+# Dockerfile for Beremiz
+# This container is used to run tests for Beremiz
+#
+FROM ubuntu:focal
+
+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 \
+ libpython2.7-dev libssl-dev \
+ python2 virtualenv cmake
+
+
+# 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
+
+
+# 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 --python=$(which python2) ~/beremizenv
+
+RUN ~/beremizenv/bin/pip install \
+ pytest pytest-timeout ddt \
+ lxml future matplotlib zeroconf2 enum34 pyro sslpsk posix_spawn \
+ twisted nevow autobahn click opcua \
+ wxPython==4.1.1
+
+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
+
--- a/tests/tools/Docker/beremiz-requirements/Dockerfile Wed Mar 01 10:54:54 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 <andrej.skvortzov@gmail.com>
-
-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 \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/tools/Docker/build_docker_image.sh Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+set -e
+
+echo "Building docker image"
+docker build \
+ --build-arg UID=$(id -u) \
+ --build-arg GID=$(id -g) \
+ -t beremiz_sikuli .
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/tools/Docker/clean_docker_container.sh Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+# delete container
+docker rm beremiz_sikuli_current
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/tools/Docker/clean_docker_image.sh Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+# delete image
+docker rmi beremiz_sikuli
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/tools/Docker/create_docker_container.sh Fri Mar 03 19:20:49 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
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/tools/Docker/do_test_in_docker.sh Fri Mar 03 19:20:49 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
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/tools/Docker/enter_docker.sh Fri Mar 03 19:20:49 2023 +0100
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+CONTAINER=beremiz_sikuli_current
+
+docker start -i $CONTAINER
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/tools/Docker/enter_docker_as_root.sh Fri Mar 03 19:20:49 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
--- a/tests/tools/Docker/python2.7-wxpython/Dockerfile Wed Mar 01 10:54:54 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/tools/Docker/rebuild_docker.sh Fri Mar 03 19:20:49 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
+
--- a/tests/tools/conftest.py Wed Mar 01 10:54:54 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()
--- a/tests/tools/run_python_tests.sh Wed Mar 01 10:54:54 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 $@
--- a/tests/tools/test_CustomIntCtrl.py Wed Mar 01 10:54:54 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()
--- a/tests/tools/test_application.py Wed Mar 01 10:54:54 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()
--- a/util/BitmapLibrary.py Wed Mar 01 10:54:54 2023 +0100
+++ b/util/BitmapLibrary.py Fri Mar 03 19:20:49 2023 +0100
@@ -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()
--- a/util/ExceptionHandler.py Wed Mar 01 10:54:54 2023 +0100
+++ b/util/ExceptionHandler.py Fri Mar 03 19:20:49 2023 +0100
@@ -49,7 +49,7 @@
trcbck_lst.append(trcbck)
# Allow clicking....
- cap = wx.Window_GetCapture()
+ cap = wx.Window.GetCapture()
if cap:
cap.ReleaseMouse()
@@ -81,8 +81,14 @@
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
@@ -93,7 +99,7 @@
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:
@@ -125,13 +132,17 @@
lst = 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()
--- a/util/ProcessLogger.py Wed Mar 01 10:54:54 2023 +0100
+++ b/util/ProcessLogger.py Fri Mar 03 19:20:49 2023 +0100
@@ -47,6 +47,7 @@
self.callback = callback
self.endcallback = endcallback
self.fd = fd
+ self.daemon = True
def run(self):
outchunk = None
--- a/util/paths.py Wed Mar 01 10:54:54 2023 +0100
+++ b/util/paths.py Fri Mar 03 19:20:49 2023 +0100
@@ -55,3 +55,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)
--- a/version.py Wed Mar 01 10:54:54 2023 +0100
+++ b/version.py Fri Mar 03 19:20:49 2023 +0100
@@ -25,13 +25,10 @@
from __future__ import absolute_import
from __future__ import unicode_literals
+from __future__ import print_function
-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,23 @@
)
-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-2022 Edouard Tisserant\n"
+ info.Copyright += "(C) 2003-2021 Mario de Sousa\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 <contact@beremiz.fr>",
+ "Mario de Sousa <msousa@fe.up.pt>",
"Andrey Skvortsov <andrej.skvortzov@gmail.com>",
"Sergey Surkov <surkov.sv@summatechnology.ru>",
- "Edouard Tisserant <edouard.tisserant@gmail.com>",
"Laurent Bessard <laurent.bessard@gmail.com>")
info.License = (
@@ -112,14 +78,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 <jm.andonegi@gmail.com>, 2019",
@@ -218,7 +182,7 @@
return info
-app_version = "1.2"
-rev = GetAppRevision()
-if rev is not None:
- app_version = app_version + "-" + rev.rstrip()
+app_version = "1.3-beta2"
+
+if __name__ == "__main__":
+ print(app_version)
--- a/xmlclass/xmlclass.py Wed Mar 01 10:54:54 2023 +0100
+++ b/xmlclass/xmlclass.py Fri Mar 03 19:20:49 2023 +0100
@@ -598,11 +598,15 @@
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
}
@@ -1450,14 +1454,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 +1470,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,7 +1481,7 @@
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())
@@ -1519,7 +1523,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 +1538,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 == "":
@@ -1738,6 +1742,8 @@
def tostring(self):
return NAMESPACE_PATTERN.sub("", etree.tostring(self, pretty_print=True, encoding='utf-8')).decode('utf-8')
+ def getElementInfos(self, name, path=None, derived=False):
+ return {"name": name, "type": TAG, "value": None, "use": None, "children": []}
class XMLElementClassLookUp(etree.PythonElementClassLookup):