SVGHMI: DnD UI: SVG for DnD now generated again based on paths and args entries filled by user.
--- a/svghmi/gen_dnd_widget_svg.ysl2 Wed Jun 16 18:27:27 2021 +0200
+++ b/svghmi/gen_dnd_widget_svg.ysl2 Fri Jun 18 10:49:10 2021 +0200
@@ -18,28 +18,30 @@
extension-element-prefixes="ns func exsl regexp str dyn"
exclude-result-prefixes="ns func exsl regexp str dyn" {
- param "hmi_path";
const "hmi_elements", "//svg:*[starts-with(@inkscape:label, 'HMI:')]";
- const "subhmitree", "ns:GetSubHMITree()";
+ const "widgetparams", "ns:GetWidgetParams()";
const "indexed_hmitree", "/.."; // compatibility with parse_labels.ysl2
include parse_labels.ysl2
const "_parsed_widgets" apply "$hmi_elements", mode="parselabel";
const "parsed_widgets","exsl:node-set($_parsed_widgets)";
- const "selected_node_type","local-name($subhmitree)";
- const "svg_widget", "$parsed_widgets/widget[1]"; // TODO take all widgets
+ const "svg_widget", "$parsed_widgets/widget[1]";
const "svg_widget_type", "$svg_widget/@type";
const "svg_widget_path", "$svg_widget/@path";
const "svg_widget_count", "count($parsed_widgets/widget)";
// Templates to change label paths(s)
- template "@* | node()", mode="replace_path" {
- xsl:copy apply "@* | node()", mode="replace_path";
+ template "@* | node()", mode="replace_params" {
+ xsl:copy apply "@* | node()", mode="replace_params";
}
- template "path/@value", mode="replace_path" {
- attrib "value" > «$hmi_path»
+ template "arg", mode="replace_params";
+ template "path", mode="replace_params";
+ template "widget", mode="replace_params" {
+ xsl:copy {
+ copy "$widgetparams/*"
+ };
}
// all attribs are usually copied
@@ -52,7 +54,7 @@
// in case this node widget's main element inject label
if "@id = $svg_widget/@id" {
- const "substituted_widget" apply "$svg_widget", mode="replace_path";
+ const "substituted_widget" apply "$svg_widget", mode="replace_params";
const "substituted_widget_ns", "exsl:node-set($substituted_widget)";
const "new_label" apply "$substituted_widget_ns", mode="genlabel";
attrib "inkscape:label" > «$new_label»
@@ -61,8 +63,6 @@
apply "@* | node()", mode="inline_svg";
}
- const "NODES_TYPES","str:split('HMI_ROOT HMI_NODE')";
- const "HMI_NODES_COMPAT","str:split('Page Jump Foreach')";
template "/" {
comment > Widget dropped in Inkscape from Beremiz
@@ -71,14 +71,9 @@
error > No widget detected on selected SVG
when "$svg_widget_count > 1"
error > Multiple widget DnD not yet supported
- when """$selected_node_type = $NODES_TYPES and \
- not($svg_widget_type = $HMI_NODES_COMPAT)"""
- error > Widget incompatible with selected HMI tree node
}
const "testmsg" {
- msg value "$hmi_path";
- msg value "$selected_node_type";
msg value "$svg_widget_type";
}
--- a/svghmi/ui.py Wed Jun 16 18:27:27 2021 +0200
+++ b/svghmi/ui.py Fri Jun 18 10:49:10 2021 +0200
@@ -11,6 +11,7 @@
import hashlib
import weakref
import re
+from threading import Thread, Lock
from functools import reduce
from itertools import izip
from operator import or_
@@ -204,6 +205,9 @@
self.SetSizer(self.main_sizer)
self.main_sizer.Fit(self)
+ def GetValue(self):
+ return self.edit.GetValue()
+
def setValidity(self, validity):
if validity is not None:
bmp = self.valid_bmp if validity else self.invalid_bmp
@@ -238,6 +242,7 @@
accepts),
False)
if accepts and txt else None)
+ self.ParentObj.RegenSVGLater()
event.Skip()
class PathEditor(ParamEditor):
@@ -261,6 +266,7 @@
# TODO : find corresponding hmitre node and type to update validity
# Lazy way : hide validity
self.setValidity(None)
+ self.ParentObj.RegenSVGLater()
event.Skip()
def KeepDoubleNewLines(txt):
@@ -347,6 +353,14 @@
self.tempf = None
+ self.RegenSVGThread = None
+ self.RegenSVGLock = Lock()
+ self.RegenSVGTimer = wx.Timer(self, -1)
+ self.RegenSVGParams = None
+ self.Bind(wx.EVT_TIMER,
+ self.RegenSVG,
+ self.RegenSVGTimer)
+
self.args_editors = []
self.paths_editors = []
@@ -378,6 +392,8 @@
self.paths_editors[dndindex:]):
editor.SetPath(hmitree_node)
+ self.RegenSVGNow()
+
def RecallLibDir(self):
conf = self.Config.Read(_conf_key)
if len(conf) == 0:
@@ -481,12 +497,7 @@
self.AnalyseWidgetAndUpdateUI(fname)
- if self.msg:
- self.staticmsg.Show()
- self.staticmsg.SetLabel(self.msg)
- else:
- self.staticmsg.Hide()
-
+ self.staticmsg.SetLabel(self.msg)
except IOError:
self.msg = _("Widget library must be writable")
@@ -496,9 +507,6 @@
def OnHMITreeNodeSelection(self, hmitree_nodes):
self.hmitree_nodes = hmitree_nodes
- # [0] if len(hmitree_nodes) else None
- # self.ValidateWidget()
- # self.Refresh()
def OnLeftDown(self, evt):
if self.tempf is not None:
@@ -509,17 +517,49 @@
dropSource.SetData(data)
dropSource.DoDragDrop(wx.Drag_AllowMove)
- def GiveDetails(self, _context, msgs):
- for msg in msgs:
- self.msg += msg.text + "\n"
+ def RegenSVGLater(self, when=1):
+ self.RegenSVGTimer.Start(milliseconds=when*1000, oneShot=True)
+
+ def RegenSVGNow(self):
+ self.RegenSVGLater(when=0)
+
+ def RegenSVG(self, event):
+ args = [arged.GetValue() for arged in self.args_editors]
+ paths = [pathed.GetValue() for pathed in self.paths_editors]
+ if self.RegenSVGLock.acquire(True):
+ self.RegenSVGParams = (args, paths)
+ if self.RegenSVGThread is None:
+ self.RegenSVGThread = \
+ Thread(target=self.RegenSVGProc,
+ name="RegenSVGThread").start()
+ self.RegenSVGLock.release()
+ event.Skip()
+
+ def RegenSVGProc(self):
+ self.RegenSVGLock.acquire(True)
+
+ newparams = self.RegenSVGParams
+ self.RegenSVGParams = None
+
+ while newparams is not None:
+ self.RegenSVGLock.release()
+
+ res = self.GenDnDSVG(newparams)
+
+ self.RegenSVGLock.acquire(True)
+
+ newparams = self.RegenSVGParams
+ self.RegenSVGParams = None
+
+ self.RegenSVGThread = None
+
+ self.RegenSVGLock.release()
+
+ wx.CallAfter(self.DoneRegenSVG)
- def PassMessage(self, _context, msgs):
- for msg in msgs:
- self.msg += msg.text + "\n"
-
- def GetSubHMITree(self, _context):
- return [self.hmitree_node.etree()]
-
+ def DoneRegenSVG(self):
+ self.staticmsg.SetLabel(self.msg)
+
def AnalyseWidgetAndUpdateUI(self, fname):
self.msg = ""
self.ResetSignature()
@@ -594,10 +634,25 @@
self.main_panel.SetupScrolling(scroll_x=False)
-
- def ValidateWidget(self):
+ def PassMessage(self, _context, msgs):
+ for msg in msgs:
+ self.msg += msg.text + "\n"
+
+ def GetWidgetParams(self, _context):
+ args,paths = self.GenDnDSVGParams
+ root = etree.Element("params")
+ for arg in args:
+ etree.SubElement(root, "arg", value=arg)
+ for path in paths:
+ etree.SubElement(root, "path", value=path)
+ return root
+
+
+ def GenDnDSVG(self, newparams):
self.msg = ""
+ self.GenDnDSVGParams = newparams
+
if self.tempf is not None:
os.unlink(self.tempf.name)
self.tempf = None
@@ -605,18 +660,15 @@
try:
if self.selected_SVG is None:
raise Exception(_("No widget selected"))
- if self.hmitree_node is None:
- raise Exception(_("No HMI tree node selected"))
transform = XSLTransform(
os.path.join(ScriptDirectory, "gen_dnd_widget_svg.xslt"),
- [("GetSubHMITree", self.GetSubHMITree),
- ("PassMessage", self.GiveDetails)])
+ [("GetWidgetParams", self.GetWidgetParams),
+ ("PassMessage", self.PassMessage)])
svgdom = etree.parse(self.selected_SVG)
- result = transform.transform(
- svgdom, hmi_path = self.hmitree_node.hmi_path())
+ result = transform.transform(svgdom)
for entry in transform.get_error_log():
self.msg += "XSLT: " + entry.message + "\n"