# HG changeset patch
# User Edouard Tisserant
# Date 1617954426 -7200
# Node ID d5b2369a103f65599b1764ff08157924dc2e5ee6
# Parent 60a8531efa458504000369edbafb974945d63c70# Parent 2b00f90c688891e4a4827658a7bb4c592a9a70eb
Merged default in SVGHMI
diff -r 2b00f90c6888 -r d5b2369a103f .hgignore
--- a/.hgignore Fri Apr 09 09:45:28 2021 +0200
+++ b/.hgignore Fri Apr 09 09:47:06 2021 +0200
@@ -1,5 +1,7 @@
.project
+.svghmithumbs
+
.directory
.pytest_cache
.cache
diff -r 2b00f90c6888 -r d5b2369a103f BeremizIDE.py
--- a/BeremizIDE.py Fri Apr 09 09:45:28 2021 +0200
+++ b/BeremizIDE.py Fri Apr 09 09:47:06 2021 +0200
@@ -122,86 +122,87 @@
self.risecall = risecall
# to prevent rapid fire on rising log panel
self.rising_timer = 0
- self.lock = Lock()
+ self.StackLock = Lock()
self.YieldLock = Lock()
self.RefreshLock = Lock()
self.TimerAccessLock = Lock()
self.stack = []
self.LastRefreshTime = gettime()
self.LastRefreshTimer = None
+ self.refreshPending = False
def write(self, s, style=None):
- if self.lock.acquire():
- self.stack.append((s, style))
- self.lock.release()
- current_time = gettime()
- self.TimerAccessLock.acquire()
- if self.LastRefreshTimer:
+ self.StackLock.acquire()
+ self.stack.append((s, style))
+ self.StackLock.release()
+ current_time = gettime()
+ with self.TimerAccessLock:
+ if self.LastRefreshTimer is not None:
self.LastRefreshTimer.cancel()
self.LastRefreshTimer = None
- self.TimerAccessLock.release()
- if current_time - self.LastRefreshTime > REFRESH_PERIOD and self.RefreshLock.acquire(False):
- self._should_write()
- else:
- self.TimerAccessLock.acquire()
- self.LastRefreshTimer = Timer(REFRESH_PERIOD, self._timer_expired)
- self.LastRefreshTimer.start()
- self.TimerAccessLock.release()
+ elapsed = current_time - self.LastRefreshTime
+ if elapsed > REFRESH_PERIOD:
+ self._should_write()
+ else:
+ with self.TimerAccessLock:
+ if self.LastRefreshTimer is None:
+ self.LastRefreshTimer = Timer(REFRESH_PERIOD - elapsed, self._timer_expired)
+ self.LastRefreshTimer.start()
def _timer_expired(self):
- if self.RefreshLock.acquire(False):
- self._should_write()
- else:
- self.TimerAccessLock.acquire()
- self.LastRefreshTimer = Timer(REFRESH_PERIOD, self._timer_expired)
- self.LastRefreshTimer.start()
- self.TimerAccessLock.release()
+ self._should_write()
+ with self.TimerAccessLock:
+ self.LastRefreshTimer = None
def _should_write(self):
- app = wx.GetApp()
- if app is not None:
- wx.CallAfter(self._write)
-
if MainThread == currentThread().ident:
+ app = wx.GetApp()
if app is not None:
+ self._write()
if self.YieldLock.acquire(0):
app.Yield()
self.YieldLock.release()
+ else:
+ with self.RefreshLock:
+ if not self.refreshPending:
+ self.refreshPending = True
+ wx.CallAfter(self._write)
def _write(self):
if self.output:
- self.output.Freeze()
- self.lock.acquire()
- for s, style in self.stack:
- if style is None:
- style = self.black_white
- if style != self.black_white:
- self.output.StartStyling(self.output.GetLength(), 0xff)
-
- # Temporary deactivate read only mode on StyledTextCtrl for
- # adding text. It seems that text modifications, even
- # programmatically, are disabled in StyledTextCtrl when read
- # only is active
- start_pos = self.output.GetLength()
- self.output.SetReadOnly(False)
- self.output.AppendText(s)
- self.output.SetReadOnly(True)
- text_len = self.output.GetLength() - start_pos
-
- if style != self.black_white:
- self.output.SetStyling(text_len, style)
- self.stack = []
- self.lock.release()
- self.output.Thaw()
- self.LastRefreshTime = gettime()
- try:
- self.RefreshLock.release()
- except Exception:
- pass
- newtime = time.time()
- if newtime - self.rising_timer > 1:
- self.risecall(self.output)
- self.rising_timer = newtime
+ with self.RefreshLock:
+ self.output.Freeze()
+ self.output.AnnotationClearAll()
+ self.StackLock.acquire()
+ for s, style in self.stack:
+ if style is None:
+ style = self.black_white
+ if style != self.black_white:
+ self.output.StartStyling(self.output.GetLength(), 0xff)
+
+ # Temporary deactivate read only mode on StyledTextCtrl for
+ # adding text. It seems that text modifications, even
+ # programmatically, are disabled in StyledTextCtrl when read
+ # only is active
+ start_pos = self.output.GetLength()
+ self.output.SetReadOnly(False)
+ self.output.AppendText(s)
+ self.output.SetReadOnly(True)
+ text_len = self.output.GetLength() - start_pos
+
+ if style != self.black_white:
+ self.output.SetStyling(text_len, style)
+ self.stack = []
+ self.StackLock.release()
+ self.output.ScrollToEnd()
+ self.output.Thaw()
+ self.LastRefreshTime = gettime()
+ newtime = time.time()
+ if newtime - self.rising_timer > 1:
+ self.risecall(self.output)
+ self.rising_timer = newtime
+ self.refreshPending = False
+
def write_warning(self, s):
self.write(s, self.red_white)
@@ -220,6 +221,16 @@
def isatty(self):
return False
+ def progress(self, text):
+ l = self.output.GetLineCount()-2
+ self.output.AnnotationSetText(l, text)
+ self.output.AnnotationSetVisible(wx.stc.STC_ANNOTATION_BOXED)
+ self.output.AnnotationSetStyle(l, self.black_white)
+ if self.YieldLock.acquire(0):
+ app = wx.GetApp()
+ app.Yield()
+ self.YieldLock.release()
+
ID_FILEMENURECENTPROJECTS = wx.NewId()
@@ -325,6 +336,7 @@
("Run", wx.WXK_F5),
("Transfer", wx.WXK_F6),
("Connect", wx.WXK_F7),
+ ("Clean", wx.WXK_F9),
("Build", wx.WXK_F11)]:
def OnMethodGen(obj, meth):
def OnMethod(evt):
diff -r 2b00f90c6888 -r d5b2369a103f PLCGenerator.py
--- a/PLCGenerator.py Fri Apr 09 09:45:28 2021 +0200
+++ b/PLCGenerator.py Fri Apr 09 09:47:06 2021 +0200
@@ -458,10 +458,12 @@
return resrce
# Generate the entire program for current project
- def GenerateProgram(self):
+ def GenerateProgram(self, log):
+ log("Collecting data types")
# Find all data types defined
for datatype in self.Project.getdataTypes():
self.DatatypeComputed[datatype.getname()] = False
+ log("Collecting POUs")
# Find all data types defined
for pou in self.Project.getpous():
self.PouComputed[pou.getname()] = False
@@ -471,12 +473,15 @@
self.Program += [("TYPE\n", ())]
# Generate every data types defined
for datatype_name in self.DatatypeComputed.keys():
+ log("Generate Data Type %s"%datatype_name)
self.GenerateDataType(datatype_name)
self.Program += [("END_TYPE\n\n", ())]
# Generate every POUs defined
for pou_name in self.PouComputed.keys():
+ log("Generate POU %s"%pou_name)
self.GeneratePouProgram(pou_name)
# Generate every configurations defined
+ log("Generate Config(s)")
for config in self.Project.getconfigurations():
self.Program += self.GenerateConfiguration(config)
@@ -1762,5 +1767,12 @@
def GenerateCurrentProgram(controler, project, errors, warnings):
generator = ProgramGenerator(controler, project, errors, warnings)
- generator.GenerateProgram()
+ if hasattr(controler, "logger"):
+ def log(txt):
+ controler.logger.write(" "+txt+"\n")
+ else:
+ def log(txt):
+ pass
+
+ generator.GenerateProgram(log)
return generator.GetGeneratedProgram()
diff -r 2b00f90c6888 -r d5b2369a103f ProjectController.py
--- a/ProjectController.py Fri Apr 09 09:45:28 2021 +0200
+++ b/ProjectController.py Fri Apr 09 09:47:06 2021 +0200
@@ -99,11 +99,12 @@
class Iec2CSettings(object):
- def __init__(self):
+ def __init__(self, controler):
self.iec2c = None
self.iec2c_buildopts = None
self.ieclib_path = self.findLibPath()
self.ieclib_c_path = self.findLibCPath()
+ self.controler = controler
def findObject(self, paths, test):
path = None
@@ -155,11 +156,11 @@
try:
# Invoke compiler.
# Output files are listed to stdout, errors to stderr
- _status, result, _err_result = ProcessLogger(None, buildcmd,
+ _status, result, _err_result = ProcessLogger(self.controler.logger, buildcmd,
no_stdout=True,
no_stderr=True).spin()
except Exception:
- self.logger.write_error(_("Couldn't launch IEC compiler to determine compatible options.\n"))
+ self.controler.logger.write_error(_("Couldn't launch IEC compiler to determine compatible options.\n"))
return buildopt
for opt in options:
@@ -242,7 +243,7 @@
ConfigTreeNode.__init__(self)
if ProjectController.iec2c_cfg is None:
- ProjectController.iec2c_cfg = Iec2CSettings()
+ ProjectController.iec2c_cfg = Iec2CSettings(self)
self.MandatoryParams = None
self._builder = None
diff -r 2b00f90c6888 -r d5b2369a103f XSLTransform.py
--- a/XSLTransform.py Fri Apr 09 09:45:28 2021 +0200
+++ b/XSLTransform.py Fri Apr 09 09:47:06 2021 +0200
@@ -17,8 +17,8 @@
etree.XMLParser()),
extensions={("beremiz", name): call for name, call in xsltext})
- def transform(self, root, **kwargs):
- res = self.xslt(root, **{k: etree.XSLT.strparam(v) for k, v in kwargs.iteritems()})
+ def transform(self, root, profile_run=False, **kwargs):
+ res = self.xslt(root, profile_run=profile_run, **{k: etree.XSLT.strparam(v) for k, v in kwargs.iteritems()})
# print(self.xslt.error_log)
return res
diff -r 2b00f90c6888 -r d5b2369a103f controls/DurationCellEditor.py
--- a/controls/DurationCellEditor.py Fri Apr 09 09:45:28 2021 +0200
+++ b/controls/DurationCellEditor.py Fri Apr 09 09:47:06 2021 +0200
@@ -122,21 +122,18 @@
self.CellControl.SetValue(self.Table.GetValueByName(row, self.Colname))
self.CellControl.SetFocus()
- def EndEditInternal(self, row, col, grid, old_duration):
- duration = self.CellControl.GetValue()
- changed = duration != old_duration
+ def EndEdit(self, row, col, grid, oldval):
+ value = self.CellControl.GetValue()
+ changed = value != oldval
if changed:
- self.Table.SetValueByName(row, self.Colname, duration)
+ return value
+ else:
+ return None
+
+ def ApplyEdit(self, row, col, grid):
+ value = self.CellControl.GetValue()
+ self.Table.SetValueByName(row, self.Colname, value)
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):
- oldval = self.Table.GetValueByName(row, self.Colname)
- return self.EndEditInternal(row, col, grid, oldval)
def SetSize(self, rect):
self.CellControl.SetDimensions(rect.x + 1, rect.y,
diff -r 2b00f90c6888 -r d5b2369a103f docutil/docsvg.py
--- a/docutil/docsvg.py Fri Apr 09 09:45:28 2021 +0200
+++ b/docutil/docsvg.py Fri Apr 09 09:47:06 2021 +0200
@@ -24,61 +24,38 @@
from __future__ import absolute_import
-import os
+import wx
import subprocess
-import wx
-
def get_inkscape_path():
- """ Return the Inkscape path """
+ """ Return the Inkscape binary path """
+
if wx.Platform == '__WXMSW__':
from six.moves import winreg
+ inkcmd = None
try:
- svgexepath = winreg.QueryValue(winreg.HKEY_LOCAL_MACHINE,
- 'Software\\Classes\\svgfile\\shell\\Inkscape\\command')
+ inkcmd = winreg.QueryValue(winreg.HKEY_LOCAL_MACHINE,
+ 'Software\\Classes\\svgfile\\shell\\Inkscape\\command')
except OSError:
try:
- svgexepath = winreg.QueryValue(winreg.HKEY_LOCAL_MACHINE,
+ inkcmd = winreg.QueryValue(winreg.HKEY_LOCAL_MACHINE,
'Software\\Classes\\inkscape.svg\\shell\\open\\command')
- except Exception:
+ except OSError:
return None
- svgexepath = svgexepath.replace('"%1"', '').strip()
- return svgexepath.replace('"', '')
+ return inkcmd.replace('"%1"', '').strip().replace('"', '')
+
else:
- # TODO: search for inkscape in $PATH
- svgexepath = os.path.join("/usr/bin", "inkscape")
- if os.path.exists(svgexepath):
- return svgexepath
- return None
-
-
-def open_win_svg(svgexepath, svgfile):
- """ Open Inkscape on Windows platform """
- popenargs = [svgexepath]
- if svgfile is not None:
- popenargs.append(svgfile)
- subprocess.Popen(popenargs)
-
-
-def open_lin_svg(svgexepath, svgfile):
- """ Open Inkscape on Linux platform """
- if os.path.isfile("/usr/bin/inkscape"):
- os.system("%s %s &" % (svgexepath, svgfile))
-
+ try:
+ return subprocess.check_output("command -v inkscape", shell=True).strip()
+ except subprocess.CalledProcessError:
+ return None
def open_svg(svgfile):
""" Generic function to open SVG file """
- if wx.Platform == '__WXMSW__':
- try:
- open_win_svg(get_inkscape_path(), svgfile)
- except Exception:
- wx.MessageBox("Inkscape is not found or installed !")
- return None
+
+ inkpath = get_inkscape_path()
+ if inkpath is None:
+ wx.MessageBox("Inkscape is not found or installed !")
else:
- svgexepath=get_inkscape_path()
- if os.path.isfile(svgexepath):
- open_lin_svg(svgexepath, svgfile)
- else:
- wx.MessageBox("Inkscape is not found or installed !")
- return None
+ subprocess.Popen([inkpath,svgfile])
diff -r 2b00f90c6888 -r d5b2369a103f features.py
--- a/features.py Fri Apr 09 09:45:28 2021 +0200
+++ b/features.py Fri Apr 09 09:47:06 2021 +0200
@@ -12,7 +12,8 @@
('Native', 'NativeLib.NativeLibrary', True),
('Python', 'py_ext.PythonLibrary', True),
('Etherlab', 'etherlab.EthercatMaster.EtherlabLibrary', False),
- ('SVGUI', 'svgui.SVGUILibrary', False)]
+ ('SVGUI', 'svgui.SVGUILibrary', False),
+ ('SVGHMI', 'svghmi.SVGHMILibrary', False)]
catalog = [
('canfestival', _('CANopen support'), _('Map located variables over CANopen'), 'canfestival.canfestival.RootClass'),
@@ -22,6 +23,7 @@
('c_ext', _('C extension'), _('Add C code accessing located variables synchronously'), 'c_ext.CFile'),
('py_ext', _('Python file'), _('Add Python code executed asynchronously'), 'py_ext.PythonFile'),
('wxglade_hmi', _('WxGlade GUI'), _('Add a simple WxGlade based GUI.'), 'wxglade_hmi.WxGladeHMI'),
- ('svgui', _('SVGUI'), _('Experimental web based HMI'), 'svgui.SVGUI')]
+ ('svgui', _('SVGUI'), _('Experimental web based HMI'), 'svgui.SVGUI'),
+ ('svghmi', _('SVGHMI'), _('SVG based HMI'), 'svghmi.SVGHMI')]
file_editors = []
diff -r 2b00f90c6888 -r d5b2369a103f images/AddFont.png
Binary file images/AddFont.png has changed
diff -r 2b00f90c6888 -r d5b2369a103f images/DelFont.png
Binary file images/DelFont.png has changed
diff -r 2b00f90c6888 -r d5b2369a103f images/EditPO.png
Binary file images/EditPO.png has changed
diff -r 2b00f90c6888 -r d5b2369a103f images/EditSVG.png
Binary file images/EditSVG.png has changed
diff -r 2b00f90c6888 -r d5b2369a103f images/ImportSVG.png
Binary file images/ImportSVG.png has changed
diff -r 2b00f90c6888 -r d5b2369a103f images/OpenPOT.png
Binary file images/OpenPOT.png has changed
diff -r 2b00f90c6888 -r d5b2369a103f images/SVGHMI.png
Binary file images/SVGHMI.png has changed
diff -r 2b00f90c6888 -r d5b2369a103f images/icons.svg
--- a/images/icons.svg Fri Apr 09 09:45:28 2021 +0200
+++ b/images/icons.svg Fri Apr 09 09:47:06 2021 +0200
@@ -15,7 +15,7 @@
height="1052.3622"
id="svg2"
sodipodi:version="0.32"
- inkscape:version="0.91 r13725"
+ inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
sodipodi:docname="icons.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape">
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+ sodipodi:cx="38.638336" />
-
-
+
+ sodipodi:cx="38.638336" />
%% Build Clean editPLC HMIEditor ImportFile ManageFolder ImportDEF ImportSVG NetworkEdit ShowMaster ExportSlave Run ShowIECcode Stop Unknown %%
+ style="font-size:12.76095104px;line-height:1.25">%% Build Clean editPLC HMIEditor ImportFile ManageFolder ImportSVG NetworkEdit ShowMaster ExportSlave Run ShowIECcode Stop EditSVG OpenPOT EditPO AddFont DelFont %%
@@ -87820,7 +88668,7 @@
id="g5077"
transform="matrix(0.09090112,0,0,0.09090112,-708.74866,934.66705)">
@@ -87889,7 +88737,7 @@
y="892.03345"
x="633.36249">Master
@@ -87897,7 +88745,7 @@
id="g8206"
transform="matrix(-0.05230834,0,0,0.05230834,-370.7166,804.48617)">
@@ -87966,7 +88814,7 @@
y="462.98654"
x="-1088.8175">Slave
@@ -87974,7 +88822,7 @@
id="g8301"
transform="matrix(-0.05230834,0,0,0.05230834,-450.7166,836.48617)">
@@ -88047,31 +88895,31 @@
id="g9184"
transform="matrix(0.7769546,0,0,0.7769546,-2279.9093,796.92596)">
+ transform="translate(1848.9892,-430.1329)">
+ 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" />
ST
@@ -88163,10 +89011,10 @@
style="opacity:0.47906979;fill:url(#linearGradient19984);stroke:none"
inkscape:connector-curvature="0" />
-
-
- D
- E
- F
-
-
-
-
-
-
-
-
-
+ 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" />
@@ -88362,23 +89134,19 @@
transform="matrix(1.824846,0,0,1.824846,58.301023,-6.9917586)"
id="g2837">
-
-
@@ -89273,60 +90031,60 @@
id="g19199"
transform="matrix(5.3097304e-2,0,0,5.3097304e-2,247.38564,260.36282)">
+ cx="0"
+ sodipodi:ry="226"
+ sodipodi:rx="226"
+ sodipodi:cy="0"
+ sodipodi:cx="0" />
+ cx="0"
+ sodipodi:ry="169.5"
+ sodipodi:rx="169.5"
+ sodipodi:cy="0"
+ sodipodi:cx="0" />
-
+
-
+
+ cx="0"
+ sodipodi:ry="106.5"
+ sodipodi:rx="106.5"
+ sodipodi:cy="0"
+ sodipodi:cx="0" />
-
+ sodipodi:cx="91.923882" />
+ transform="translate(1653.0897,-400.03854)">
+ 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" />
+ style="display:inline;overflow:visible;visibility:visible;fill:#3d993d;fill-opacity:1;fill-rule:nonzero;stroke:#3d993d;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10.43299961;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none" />
+ style="display:inline;overflow:visible;visibility:visible;fill:url(#linearGradient16607);fill-opacity:1;fill-rule:nonzero;stroke:#3d993d;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10.43299961;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none" />
+ style="display:inline;overflow:visible;visibility:visible;fill:url(#linearGradient16609);fill-opacity:1;fill-rule:nonzero;stroke:#3d993d;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10.43299961;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none" />
@@ -91056,15 +91813,15 @@
@@ -91089,8 +91846,8 @@
style="fill:url(#linearGradient16647)"
inkscape:connector-curvature="0" />
@@ -91262,7 +92019,7 @@
x="166.52481"
id="tspan16195-0"
sodipodi:role="line"
- style="font-size:12.76000023px;line-height:1.25">%% Extension Cfile Pyfile wxGlade SVGUI FOLDER FILE %%
+ style="font-size:12.76000023px;line-height:1.25">%% Extension Cfile Pyfile wxGlade SVGHMI FOLDER FILE %%