SVGHMI: DnD UI: SVG for DnD now generated again based on paths and args entries filled by user. svghmi
authorEdouard Tisserant <edouard.tisserant@gmail.com>
Fri, 18 Jun 2021 10:49:10 +0200
branchsvghmi
changeset 3261 06ea7a1152af
parent 3260 6bd918732047
child 3262 689ac4ac0dbe
SVGHMI: DnD UI: SVG for DnD now generated again based on paths and args entries filled by user.
svghmi/gen_dnd_widget_svg.ysl2
svghmi/ui.py
--- 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"