SVGHMI: Add analyse_widget stylesheet and python code to execute it, in order to obtain widget signature independently of DnD SVG file generation. svghmi
authorEdouard Tisserant <edouard.tisserant@gmail.com>
Mon, 03 May 2021 00:04:08 +0200
branchsvghmi
changeset 3235 b2b6bf45aa2d
parent 3234 f2bfb047d0e6
child 3236 e4e27c4efb96
SVGHMI: Add analyse_widget stylesheet and python code to execute it, in order to obtain widget signature independently of DnD SVG file generation.
svghmi/Makefile
svghmi/analyse_widget.xslt
svghmi/analyse_widget.ysl2
svghmi/ui.py
svghmi/widgetlib/modern_knob_1.svg
--- a/svghmi/Makefile	Sun May 02 23:43:57 2021 +0200
+++ b/svghmi/Makefile	Mon May 03 00:04:08 2021 +0200
@@ -11,7 +11,7 @@
 
 yml2path ?= $(abspath ../../yml2)
 
-ysl2files := gen_index_xhtml.ysl2 gen_dnd_widget_svg.ysl2
+ysl2files := gen_index_xhtml.ysl2 gen_dnd_widget_svg.ysl2 analyse_widget.ysl2
 ysl2includes := $(filter-out $(ysl2files), $(wildcard *.ysl2))
 xsltfiles := $(patsubst %.ysl2, %.xslt, $(ysl2files))
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/svghmi/analyse_widget.xslt	Mon May 03 00:04:08 2021 +0200
@@ -0,0 +1,153 @@
+<?xml version="1.0"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" xmlns:regexp="http://exslt.org/regular-expressions" xmlns:str="http://exslt.org/strings" xmlns:func="http://exslt.org/functions" xmlns:svg="http://www.w3.org/2000/svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.0" extension-element-prefixes="ns func exsl regexp str dyn" exclude-result-prefixes="ns func exsl regexp str dyn svg inkscape">
+  <xsl:output method="xml"/>
+  <xsl:variable name="indexed_hmitree" select="/.."/>
+  <xsl:variable name="pathregex" select="'^([^\[,]+)(\[[^\]]+\])?([\d,]*)$'"/>
+  <xsl:template mode="parselabel" match="*">
+    <xsl:variable name="label" select="@inkscape:label"/>
+    <xsl:variable name="id" select="@id"/>
+    <xsl:variable name="description" select="substring-after($label,'HMI:')"/>
+    <xsl:variable name="_args" select="substring-before($description,'@')"/>
+    <xsl:variable name="args">
+      <xsl:choose>
+        <xsl:when test="$_args">
+          <xsl:value-of select="$_args"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$description"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+    <xsl:variable name="_type" select="substring-before($args,':')"/>
+    <xsl:variable name="type">
+      <xsl:choose>
+        <xsl:when test="$_type">
+          <xsl:value-of select="$_type"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$args"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+    <xsl:if test="$type">
+      <widget>
+        <xsl:attribute name="id">
+          <xsl:value-of select="$id"/>
+        </xsl:attribute>
+        <xsl:attribute name="type">
+          <xsl:value-of select="$type"/>
+        </xsl:attribute>
+        <xsl:for-each select="str:split(substring-after($args, ':'), ':')">
+          <arg>
+            <xsl:attribute name="value">
+              <xsl:value-of select="."/>
+            </xsl:attribute>
+          </arg>
+        </xsl:for-each>
+        <xsl:variable name="paths" select="substring-after($description,'@')"/>
+        <xsl:for-each select="str:split($paths, '@')">
+          <xsl:if test="string-length(.) &gt; 0">
+            <path>
+              <xsl:variable name="path_match" select="regexp:match(.,$pathregex)"/>
+              <xsl:variable name="pathminmax" select="str:split($path_match[4],',')"/>
+              <xsl:variable name="path" select="$path_match[2]"/>
+              <xsl:variable name="path_accepts" select="$path_match[3]"/>
+              <xsl:variable name="pathminmaxcount" select="count($pathminmax)"/>
+              <xsl:attribute name="value">
+                <xsl:value-of select="$path"/>
+              </xsl:attribute>
+              <xsl:if test="string-length($path_accepts)">
+                <xsl:attribute name="accepts">
+                  <xsl:value-of select="$path_accepts"/>
+                </xsl:attribute>
+              </xsl:if>
+              <xsl:choose>
+                <xsl:when test="$pathminmaxcount = 2">
+                  <xsl:attribute name="min">
+                    <xsl:value-of select="$pathminmax[1]"/>
+                  </xsl:attribute>
+                  <xsl:attribute name="max">
+                    <xsl:value-of select="$pathminmax[2]"/>
+                  </xsl:attribute>
+                </xsl:when>
+                <xsl:when test="$pathminmaxcount = 1 or $pathminmaxcount &gt; 2">
+                  <xsl:message terminate="yes">
+                    <xsl:text>Widget id:</xsl:text>
+                    <xsl:value-of select="$id"/>
+                    <xsl:text> label:</xsl:text>
+                    <xsl:value-of select="$label"/>
+                    <xsl:text> has wrong syntax of path section </xsl:text>
+                    <xsl:value-of select="$pathminmax"/>
+                  </xsl:message>
+                </xsl:when>
+              </xsl:choose>
+              <xsl:if test="$indexed_hmitree">
+                <xsl:choose>
+                  <xsl:when test="regexp:test($path,'^\.[a-zA-Z0-9_]+$')">
+                    <xsl:attribute name="type">
+                      <xsl:text>PAGE_LOCAL</xsl:text>
+                    </xsl:attribute>
+                  </xsl:when>
+                  <xsl:when test="regexp:test($path,'^[a-zA-Z0-9_]+$')">
+                    <xsl:attribute name="type">
+                      <xsl:text>HMI_LOCAL</xsl:text>
+                    </xsl:attribute>
+                  </xsl:when>
+                  <xsl:otherwise>
+                    <xsl:variable name="item" select="$indexed_hmitree/*[@hmipath = $path]"/>
+                    <xsl:variable name="pathtype" select="local-name($item)"/>
+                    <xsl:if test="$pathminmaxcount = 3 and not($pathtype = 'HMI_INT' or $pathtype = 'HMI_REAL')">
+                      <xsl:message terminate="yes">
+                        <xsl:text>Widget id:</xsl:text>
+                        <xsl:value-of select="$id"/>
+                        <xsl:text> label:</xsl:text>
+                        <xsl:value-of select="$label"/>
+                        <xsl:text> path section </xsl:text>
+                        <xsl:value-of select="$pathminmax"/>
+                        <xsl:text> use min and max on non mumeric value</xsl:text>
+                      </xsl:message>
+                    </xsl:if>
+                    <xsl:if test="count($item) = 1">
+                      <xsl:attribute name="index">
+                        <xsl:value-of select="$item/@index"/>
+                      </xsl:attribute>
+                      <xsl:attribute name="type">
+                        <xsl:value-of select="$pathtype"/>
+                      </xsl:attribute>
+                    </xsl:if>
+                  </xsl:otherwise>
+                </xsl:choose>
+              </xsl:if>
+            </path>
+          </xsl:if>
+        </xsl:for-each>
+      </widget>
+    </xsl:if>
+  </xsl:template>
+  <xsl:template mode="genlabel" match="arg">
+    <xsl:text>:</xsl:text>
+    <xsl:value-of select="@value"/>
+  </xsl:template>
+  <xsl:template mode="genlabel" match="path">
+    <xsl:text>@</xsl:text>
+    <xsl:value-of select="@value"/>
+    <xsl:if test="string-length(@min)&gt;0 or string-length(@max)&gt;0">
+      <xsl:text>,</xsl:text>
+      <xsl:value-of select="@min"/>
+      <xsl:text>,</xsl:text>
+      <xsl:value-of select="@max"/>
+    </xsl:if>
+  </xsl:template>
+  <xsl:template mode="genlabel" match="widget">
+    <xsl:text>HMI:</xsl:text>
+    <xsl:value-of select="@type"/>
+    <xsl:apply-templates mode="genlabel" select="arg"/>
+    <xsl:apply-templates mode="genlabel" select="path"/>
+  </xsl:template>
+  <xsl:variable name="hmi_elements" select="//svg:*[starts-with(@inkscape:label, 'HMI:')]"/>
+  <xsl:template match="/">
+    <widgets>
+      <xsl:apply-templates mode="parselabel" select="$hmi_elements"/>
+    </widgets>
+  </xsl:template>
+</xsl:stylesheet>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/svghmi/analyse_widget.ysl2	Mon May 03 00:04:08 2021 +0200
@@ -0,0 +1,20 @@
+include yslt_noindent.yml2
+
+istylesheet
+            /* From Inkscape */
+            xmlns:svg="http://www.w3.org/2000/svg"
+            xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+
+            extension-element-prefixes="ns func exsl regexp str dyn"
+            exclude-result-prefixes="ns func exsl regexp str dyn svg inkscape" {
+
+    const "indexed_hmitree", "/.."; // compatibility with parse_labels.ysl2
+    include parse_labels.ysl2
+
+    const "hmi_elements", "//svg:*[starts-with(@inkscape:label, 'HMI:')]";
+
+    template "/"  
+        widgets
+            apply "$hmi_elements", mode="parselabel";
+
+}
--- a/svghmi/ui.py	Sun May 02 23:43:57 2021 +0200
+++ b/svghmi/ui.py	Mon May 03 00:04:08 2021 +0200
@@ -299,10 +299,54 @@
 
     def GetSubHMITree(self, _context):
         return [self.hmitree_node.etree()]
+    def AnalyseWidget(self):
+        self.msg = ""
+
+        try:
+            if self.selected_SVG is None:
+                raise Exception(_("No widget selected"))
+
+            transform = XSLTransform(
+                os.path.join(ScriptDirectory, "analyse_widget.xslt"),[])
+
+            svgdom = etree.parse(self.selected_SVG)
+
+            result = transform.transform(svgdom)
+
+            for entry in transform.get_error_log():
+                self.msg += "XSLT: " + entry.message + "\n" 
+
+        except Exception as e:
+            self.msg += str(e)
+        except XSLTApplyError as e:
+            self.msg += "Widget analysis error: " + e.message
+        else:
+            return result
+
+    def UpdateUI(self, signature):
+        if signature is not None:
+            print(etree.tostring(signature, pretty_print=True))
+            widgets = signature.getroot()
+            for widget in widgets:
+                widget_type = widget.get("type")
+                print(widget_type)
+                for path in widget:
+                    path_value = path.get("value")
+                    path_accepts = map(
+                        str.strip, path.get("accepts", '')[1:-1].split(','))
+                    print(path_value, path_accepts)
+
+
 
     def ValidateWidget(self):
         self.msg = ""
 
+        signature = self.AnalyseWidget()
+        
+        self.UpdateUI(signature)
+
+        return
+
         if self.tempf is not None:
             os.unlink(self.tempf.name)
             self.tempf = None
--- a/svghmi/widgetlib/modern_knob_1.svg	Sun May 02 23:43:57 2021 +0200
+++ b/svghmi/widgetlib/modern_knob_1.svg	Mon May 03 00:04:08 2021 +0200
@@ -159,16 +159,16 @@
      borderopacity="1.0"
      inkscape:pageopacity="0.0"
      inkscape:pageshadow="2"
-     inkscape:zoom="0.35"
-     inkscape:cx="41.428571"
-     inkscape:cy="555.71429"
+     inkscape:zoom="1.979899"
+     inkscape:cx="208.80035"
+     inkscape:cy="841.9769"
      inkscape:document-units="mm"
-     inkscape:current-layer="svg2637"
+     inkscape:current-layer="g3058"
      showgrid="false"
-     inkscape:window-width="1414"
-     inkscape:window-height="840"
-     inkscape:window-x="1690"
-     inkscape:window-y="117"
+     inkscape:window-width="1623"
+     inkscape:window-height="1446"
+     inkscape:window-x="3346"
+     inkscape:window-y="244"
      inkscape:window-maximized="0" />
   <metadata
      id="metadata2634">
@@ -178,7 +178,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>
@@ -206,7 +206,7 @@
     <g
        style="fill:none;stroke-width:1.47405899"
        transform="matrix(0.53304115,0,0,0.53229017,1417.5153,1776.3135)"
-       inkscape:label="HMI:CircularBar@/CIRCULARBAR"
+       inkscape:label="HMI:CircularBar@value[HMI_REAL,HMI_INT]0,111@label[HMI_STRING]"
        id="g30664">
       <text
          inkscape:label="value"