# HG changeset patch # User Edouard Tisserant # Date 1624006150 -7200 # Node ID 06ea7a1152af1b39dea9fe1983fea64f498e7805 # Parent 6bd91873204743d9015d1e9997764f542504c888 SVGHMI: DnD UI: SVG for DnD now generated again based on paths and args entries filled by user. diff -r 6bd918732047 -r 06ea7a1152af svghmi/gen_dnd_widget_svg.ysl2 --- 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"; } diff -r 6bd918732047 -r 06ea7a1152af svghmi/ui.py --- 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"