merge svghmi
authorEdouard Tisserant <edouard.tisserant@gmail.com>
Wed, 16 Jun 2021 18:27:27 +0200
branchsvghmi
changeset 3260 6bd918732047
parent 3259 76da573569a6 (diff)
parent 3258 5ce56021f166 (current diff)
child 3261 06ea7a1152af
merge
--- a/svghmi/parse_labels.ysl2	Wed Jun 16 12:15:02 2021 +0200
+++ b/svghmi/parse_labels.ysl2	Wed Jun 16 18:27:27 2021 +0200
@@ -19,6 +19,7 @@
 {
     const "label","@inkscape:label";
     const "id","@id";
+
     const "description", "substring-after($label,'HMI:')";
 
     const "_args", "substring-before($description,'@')";
@@ -86,6 +87,7 @@
                 }
             }
         }
+        if "svg:desc" desc value "svg:desc/text()";
     }
 }
 
--- a/svghmi/ui.py	Wed Jun 16 12:15:02 2021 +0200
+++ b/svghmi/ui.py	Wed Jun 16 18:27:27 2021 +0200
@@ -10,6 +10,10 @@
 import os
 import hashlib
 import weakref
+import re
+from functools import reduce
+from itertools import izip
+from operator import or_
 from tempfile import NamedTemporaryFile
 
 import wx
@@ -200,19 +204,41 @@
         self.SetSizer(self.main_sizer)
         self.main_sizer.Fit(self)
 
-    def setValidityNOK(self):
-        self.validity_sbmp.SetBitmap(self.invalid_bmp)
-        self.validity_sbmp.Show(True)
-
-    def setValidityOK(self):
-        self.validity_sbmp.SetBitmap(self.valid_bmp)
-        self.validity_sbmp.Show(True)
-
-    def setValidityUnknown(self):
-        self.validity_sbmp.Show(False)
+    def setValidity(self, validity):
+        if validity is not None:
+            bmp = self.valid_bmp if validity else self.invalid_bmp
+            self.validity_sbmp.SetBitmap(bmp)
+            self.validity_sbmp.Show(True)
+        else :
+            self.validity_sbmp.Show(False)
+
+models = { typename: re.compile(regex) for typename, regex in [
+    ("string", r".*"),
+    ("int", r"^-?([1-9][0-9]|0)*$"),
+    ("real", r"^-?([1-9][0-9]|0)*(\.[0-9]+)?$")]}
 
 class ArgEditor(ParamEditor):
-    pass
+    def __init__(self, parent, argdesc, prefillargdesc):
+        ParamEditor.__init__(self, parent, argdesc)
+        self.ParentObj = parent
+        self.argdesc = argdesc
+        self.Bind(wx.EVT_TEXT, self.OnArgChanged, self.edit)
+        prefill = "" if prefillargdesc is None else prefillargdesc.get("value")
+        self.edit.SetValue(prefill)
+        # TODO add a button to add more ArgEditror instance 
+        #      when ordinality is multiple
+
+    def OnArgChanged(self, event):
+        txt = self.edit.GetValue()
+        accepts = self.argdesc.get("accepts").split(',')
+        self.setValidity(
+            reduce(or_,
+                   map(lambda typename: 
+                           models[typename].match(txt) is not None,
+                       accepts), 
+                   False)
+            if accepts and txt else None)
+        event.Skip()
 
 class PathEditor(ParamEditor):
     def __init__(self, parent, pathdesc):
@@ -228,17 +254,19 @@
 
     def SetPath(self, hmitree_node):
         self.edit.ChangeValue(hmitree_node.hmi_path())
-        if hmitree_node.nodetype in self.pathdesc.get("accepts").split(","):
-            self.setValidityOK()
-        else:
-            self.setValidityNOK()
+        self.setValidity(
+            hmitree_node.nodetype in self.pathdesc.get("accepts").split(","))
 
     def OnPathChanged(self, event):
         # TODO : find corresponding hmitre node and type to update validity
         # Lazy way : hide validity
-        self.setValidityUnknown()
+        self.setValidity(None)
         event.Skip()
     
+def KeepDoubleNewLines(txt):
+    return "\n\n".join(map(
+        lambda s:re.sub(r'\s+',' ',s),
+        txt.split("\n\n")))
 
 _conf_key = "SVGHMIWidgetLib"
 _preview_height = 200
@@ -292,11 +320,11 @@
         self.signature_sizer = wx.BoxSizer(wx.VERTICAL)
         self.args_box = wx.StaticBox(self.main_panel, -1,
                                      _("Widget's arguments"),
-                                     style = wx.ALIGN_RIGHT)
+                                     style = wx.ALIGN_CENTRE_HORIZONTAL)
         self.args_sizer = wx.StaticBoxSizer(self.args_box, wx.VERTICAL)
         self.paths_box = wx.StaticBox(self.main_panel, -1,
                                       _("Widget's variables"),
-                                      style = wx.ALIGN_RIGHT)
+                                      style = wx.ALIGN_CENTRE_HORIZONTAL)
         self.paths_sizer = wx.StaticBoxSizer(self.paths_box, wx.VERTICAL)
         self.signature_sizer.Add(self.args_sizer, flag=wx.GROW)
         self.signature_sizer.AddSpacer(5)
@@ -333,8 +361,8 @@
             editor.Destroy()
         self.paths_editors = []
 
-    def AddArgToSignature(self, arg):
-        new_editor = ArgEditor(self, arg)
+    def AddArgToSignature(self, arg, prefillarg):
+        new_editor = ArgEditor(self, arg, prefillarg)
         self.args_editors.append(new_editor)
         self.args_sizer.Add(new_editor, flag=wx.GROW)
 
@@ -512,41 +540,56 @@
 
         except Exception as e:
             self.msg += str(e)
+            return
         except XSLTApplyError as e:
             self.msg += "Widget " + fname + " analysis error: " + e.message
-        else:
+            return
             
-            self.msg += "Widget " + fname + ": OK"
-
-            print(etree.tostring(signature, pretty_print=True))
-            widgets = signature.getroot()
-            for defs in widgets.iter("defs"):
-
-                # Keep double newlines (to mark paragraphs)
-                self.desc.SetValue(defs.find("type").text + ":\n" + "\n\n".join(map(
-                    lambda s:s.replace("\n"," ").replace("  ", " "),
-                    defs.find("longdesc").text.split("\n\n"))))
-                args = [arg for arg in defs.iter("arg")]
-                self.args_box.Show(len(args)!=0)
-                for arg in args:
-                    self.AddArgToSignature(arg)
-                    print(arg.get("name"))
-                    print(arg.get("accepts"))
-                paths = [path for path in defs.iter("path")]
-                self.paths_box.Show(len(paths)!=0)
-                for path in paths:
-                    self.AddPathToSignature(path)
-                    print(path.get("name"))
-                    print(path.get("accepts"))
-
-            for widget in widgets:
-                widget_type = widget.get("type")
-                print(widget_type)
-                for path in widget.iterchildren("path"):
-                    path_value = path.get("value")
-                    path_accepts = map(
-                        str.strip, path.get("accepts", '')[1:-1].split(','))
-                    print(path, path_value, path_accepts)
+        self.msg += "Widget " + fname + ": OK"
+
+        print(etree.tostring(signature, pretty_print=True))
+        widgets = signature.getroot()
+        widget = widgets.find("widget")
+        defs = widget.find("defs")
+        # Keep double newlines (to mark paragraphs)
+        widget_desc = widget.find("desc")
+        self.desc.SetValue(
+            fname + ":\n" + (
+                _("No description given") if widget_desc is None else 
+                KeepDoubleNewLines(widget_desc.text)
+            ) + "\n\n" +
+            defs.find("type").text + ":\n" +
+            KeepDoubleNewLines(defs.find("longdesc").text))
+        prefillargs = widget.findall("arg")
+        args = defs.findall("arg")
+        # extend args description in prefilled args in longer 
+        # (case of variable list of args)
+        if len(prefillargs) < len(args):
+            prefillargs += [None]*(len(args)-len(prefillargs))
+        if args and len(prefillargs) > len(args):
+            # TODO: check ordinality of last arg
+            # TODO: check that only last arg has multiple ordinality
+            args += [args[-1]]*(len(prefillargs)-len(args))
+        self.args_box.Show(len(args)!=0)
+        for arg, prefillarg in izip(args,prefillargs):
+            self.AddArgToSignature(arg, prefillarg)
+            print(arg.get("name"))
+            print(arg.get("accepts"))
+        paths = defs.findall("path")
+        self.paths_box.Show(len(paths)!=0)
+        for path in paths:
+            self.AddPathToSignature(path)
+            print(path.get("name"))
+            print(path.get("accepts"))
+
+        for widget in widgets:
+            widget_type = widget.get("type")
+            print(widget_type)
+            for path in widget.iterchildren("path"):
+                path_value = path.get("value")
+                path_accepts = map(
+                    str.strip, path.get("accepts", '')[1:-1].split(','))
+                print(path, path_value, path_accepts)
 
         self.main_panel.SetupScrolling(scroll_x=False)
 
--- a/svghmi/widgetlib/voltmeter.svg	Wed Jun 16 12:15:02 2021 +0200
+++ b/svghmi/widgetlib/voltmeter.svg	Wed Jun 16 18:27:27 2021 +0200
@@ -93,9 +93,9 @@
      borderopacity="1.0"
      inkscape:pageopacity="0.0"
      inkscape:pageshadow="2"
-     inkscape:zoom="3.312923"
-     inkscape:cx="554.2857"
-     inkscape:cy="554.2857"
+     inkscape:zoom="1.1712952"
+     inkscape:cx="601.92253"
+     inkscape:cy="607.41638"
      inkscape:document-units="mm"
      inkscape:current-layer="svg2354"
      showgrid="false"
@@ -438,9 +438,11 @@
        id="path4304" />
   </g>
   <g
-     inkscape:label="HMI:Meter@/PUMP0/SLOTH,0,666"
+     inkscape:label="HMI:Meter:0:500@/PUMP0/SLOTH,0,666"
      transform="matrix(0.57180538,0,0,0.57180538,88.118425,163.79208)"
      id="g19348">
+    <desc
+       id="desc132">Old Style Russian Voltmeter, from openclipart https://openclipart.org/detail/205486/voltmeter-and-ammeter</desc>
     <path
        inkscape:label="range"
        sodipodi:open="true"