svghmi/ui.py
branchsvghmi
changeset 3261 06ea7a1152af
parent 3259 76da573569a6
child 3263 1205b2d0acf2
equal deleted inserted replaced
3260:6bd918732047 3261:06ea7a1152af
     9 from __future__ import absolute_import
     9 from __future__ import absolute_import
    10 import os
    10 import os
    11 import hashlib
    11 import hashlib
    12 import weakref
    12 import weakref
    13 import re
    13 import re
       
    14 from threading import Thread, Lock
    14 from functools import reduce
    15 from functools import reduce
    15 from itertools import izip
    16 from itertools import izip
    16 from operator import or_
    17 from operator import or_
    17 from tempfile import NamedTemporaryFile
    18 from tempfile import NamedTemporaryFile
    18 
    19 
   202         self.main_sizer.Add(self.desc, flag=wx.GROW)
   203         self.main_sizer.Add(self.desc, flag=wx.GROW)
   203         self.main_sizer.Add(self.edit_sizer, flag=wx.GROW)
   204         self.main_sizer.Add(self.edit_sizer, flag=wx.GROW)
   204         self.SetSizer(self.main_sizer)
   205         self.SetSizer(self.main_sizer)
   205         self.main_sizer.Fit(self)
   206         self.main_sizer.Fit(self)
   206 
   207 
       
   208     def GetValue(self):
       
   209         return self.edit.GetValue()
       
   210 
   207     def setValidity(self, validity):
   211     def setValidity(self, validity):
   208         if validity is not None:
   212         if validity is not None:
   209             bmp = self.valid_bmp if validity else self.invalid_bmp
   213             bmp = self.valid_bmp if validity else self.invalid_bmp
   210             self.validity_sbmp.SetBitmap(bmp)
   214             self.validity_sbmp.SetBitmap(bmp)
   211             self.validity_sbmp.Show(True)
   215             self.validity_sbmp.Show(True)
   236                    map(lambda typename: 
   240                    map(lambda typename: 
   237                            models[typename].match(txt) is not None,
   241                            models[typename].match(txt) is not None,
   238                        accepts), 
   242                        accepts), 
   239                    False)
   243                    False)
   240             if accepts and txt else None)
   244             if accepts and txt else None)
       
   245         self.ParentObj.RegenSVGLater()
   241         event.Skip()
   246         event.Skip()
   242 
   247 
   243 class PathEditor(ParamEditor):
   248 class PathEditor(ParamEditor):
   244     def __init__(self, parent, pathdesc):
   249     def __init__(self, parent, pathdesc):
   245         ParamEditor.__init__(self, parent, pathdesc)
   250         ParamEditor.__init__(self, parent, pathdesc)
   259 
   264 
   260     def OnPathChanged(self, event):
   265     def OnPathChanged(self, event):
   261         # TODO : find corresponding hmitre node and type to update validity
   266         # TODO : find corresponding hmitre node and type to update validity
   262         # Lazy way : hide validity
   267         # Lazy way : hide validity
   263         self.setValidity(None)
   268         self.setValidity(None)
       
   269         self.ParentObj.RegenSVGLater()
   264         event.Skip()
   270         event.Skip()
   265     
   271     
   266 def KeepDoubleNewLines(txt):
   272 def KeepDoubleNewLines(txt):
   267     return "\n\n".join(map(
   273     return "\n\n".join(map(
   268         lambda s:re.sub(r'\s+',' ',s),
   274         lambda s:re.sub(r'\s+',' ',s),
   345         self.picker_desc_splitter.SplitHorizontally(self.picker_panel, self.desc, 400)
   351         self.picker_desc_splitter.SplitHorizontally(self.picker_panel, self.desc, 400)
   346         self.SplitVertically(self.main_panel, self.picker_desc_splitter, 300)
   352         self.SplitVertically(self.main_panel, self.picker_desc_splitter, 300)
   347 
   353 
   348         self.tempf = None 
   354         self.tempf = None 
   349 
   355 
       
   356         self.RegenSVGThread = None
       
   357         self.RegenSVGLock = Lock()
       
   358         self.RegenSVGTimer = wx.Timer(self, -1)
       
   359         self.RegenSVGParams = None
       
   360         self.Bind(wx.EVT_TIMER,
       
   361                   self.RegenSVG,
       
   362                   self.RegenSVGTimer)
       
   363 
   350         self.args_editors = []
   364         self.args_editors = []
   351         self.paths_editors = []
   365         self.paths_editors = []
   352 
   366 
   353     def ResetSignature(self):
   367     def ResetSignature(self):
   354         self.args_sizer.Clear()
   368         self.args_sizer.Clear()
   375         dndindex = self.paths_editors.index(target_editor)
   389         dndindex = self.paths_editors.index(target_editor)
   376 
   390 
   377         for hmitree_node,editor in zip(self.hmitree_nodes,
   391         for hmitree_node,editor in zip(self.hmitree_nodes,
   378                                    self.paths_editors[dndindex:]):
   392                                    self.paths_editors[dndindex:]):
   379             editor.SetPath(hmitree_node)
   393             editor.SetPath(hmitree_node)
       
   394 
       
   395         self.RegenSVGNow()
   380 
   396 
   381     def RecallLibDir(self):
   397     def RecallLibDir(self):
   382         conf = self.Config.Read(_conf_key)
   398         conf = self.Config.Read(_conf_key)
   383         if len(conf) == 0:
   399         if len(conf) == 0:
   384             return None
   400             return None
   479 
   495 
   480                 self.selected_SVG = svgpath if have_thumb else None
   496                 self.selected_SVG = svgpath if have_thumb else None
   481 
   497 
   482                 self.AnalyseWidgetAndUpdateUI(fname)
   498                 self.AnalyseWidgetAndUpdateUI(fname)
   483 
   499 
   484                 if self.msg:
   500                 self.staticmsg.SetLabel(self.msg)
   485                     self.staticmsg.Show()
       
   486                     self.staticmsg.SetLabel(self.msg)
       
   487                 else:
       
   488                     self.staticmsg.Hide()
       
   489 
       
   490 
   501 
   491             except IOError:
   502             except IOError:
   492                 self.msg = _("Widget library must be writable")
   503                 self.msg = _("Widget library must be writable")
   493 
   504 
   494             self.Refresh()
   505             self.Refresh()
   495         event.Skip()
   506         event.Skip()
   496 
   507 
   497     def OnHMITreeNodeSelection(self, hmitree_nodes):
   508     def OnHMITreeNodeSelection(self, hmitree_nodes):
   498         self.hmitree_nodes = hmitree_nodes
   509         self.hmitree_nodes = hmitree_nodes
   499         # [0] if len(hmitree_nodes) else None
       
   500         # self.ValidateWidget()
       
   501         # self.Refresh()
       
   502 
   510 
   503     def OnLeftDown(self, evt):
   511     def OnLeftDown(self, evt):
   504         if self.tempf is not None:
   512         if self.tempf is not None:
   505             filename = self.tempf.name
   513             filename = self.tempf.name
   506             data = wx.FileDataObject()
   514             data = wx.FileDataObject()
   507             data.AddFile(filename)
   515             data.AddFile(filename)
   508             dropSource = wx.DropSource(self)
   516             dropSource = wx.DropSource(self)
   509             dropSource.SetData(data)
   517             dropSource.SetData(data)
   510             dropSource.DoDragDrop(wx.Drag_AllowMove)
   518             dropSource.DoDragDrop(wx.Drag_AllowMove)
   511 
   519 
   512     def GiveDetails(self, _context, msgs):
   520     def RegenSVGLater(self, when=1):
   513         for msg in msgs:
   521         self.RegenSVGTimer.Start(milliseconds=when*1000, oneShot=True)
   514             self.msg += msg.text + "\n"
   522 
       
   523     def RegenSVGNow(self):
       
   524         self.RegenSVGLater(when=0)
       
   525 
       
   526     def RegenSVG(self, event):
       
   527         args = [arged.GetValue() for arged in self.args_editors]
       
   528         paths = [pathed.GetValue() for pathed in self.paths_editors]
       
   529         if self.RegenSVGLock.acquire(True):
       
   530             self.RegenSVGParams = (args, paths)
       
   531             if self.RegenSVGThread is None:
       
   532                 self.RegenSVGThread = \
       
   533                     Thread(target=self.RegenSVGProc,
       
   534                            name="RegenSVGThread").start()
       
   535             self.RegenSVGLock.release()
       
   536         event.Skip()
       
   537 
       
   538     def RegenSVGProc(self):
       
   539         self.RegenSVGLock.acquire(True)
       
   540 
       
   541         newparams = self.RegenSVGParams
       
   542         self.RegenSVGParams = None
       
   543 
       
   544         while newparams is not None:
       
   545             self.RegenSVGLock.release()
       
   546 
       
   547             res = self.GenDnDSVG(newparams)
       
   548 
       
   549             self.RegenSVGLock.acquire(True)
       
   550 
       
   551             newparams = self.RegenSVGParams
       
   552             self.RegenSVGParams = None
       
   553 
       
   554         self.RegenSVGThread = None
       
   555 
       
   556         self.RegenSVGLock.release()
       
   557 
       
   558         wx.CallAfter(self.DoneRegenSVG)
   515         
   559         
   516     def PassMessage(self, _context, msgs):
   560     def DoneRegenSVG(self):
   517         for msg in msgs:
   561         self.staticmsg.SetLabel(self.msg)
   518             self.msg += msg.text + "\n"
   562         
   519 
       
   520     def GetSubHMITree(self, _context):
       
   521         return [self.hmitree_node.etree()]
       
   522 
       
   523     def AnalyseWidgetAndUpdateUI(self, fname):
   563     def AnalyseWidgetAndUpdateUI(self, fname):
   524         self.msg = ""
   564         self.msg = ""
   525         self.ResetSignature()
   565         self.ResetSignature()
   526 
   566 
   527         try:
   567         try:
   592                 print(path, path_value, path_accepts)
   632                 print(path, path_value, path_accepts)
   593 
   633 
   594         self.main_panel.SetupScrolling(scroll_x=False)
   634         self.main_panel.SetupScrolling(scroll_x=False)
   595 
   635 
   596 
   636 
   597 
   637     def PassMessage(self, _context, msgs):
   598     def ValidateWidget(self):
   638         for msg in msgs:
       
   639             self.msg += msg.text + "\n"
       
   640 
       
   641     def GetWidgetParams(self, _context):
       
   642         args,paths = self.GenDnDSVGParams
       
   643         root = etree.Element("params")
       
   644         for arg in args:
       
   645             etree.SubElement(root, "arg", value=arg)
       
   646         for path in paths:
       
   647             etree.SubElement(root, "path", value=path)
       
   648         return root
       
   649 
       
   650 
       
   651     def GenDnDSVG(self, newparams):
   599         self.msg = ""
   652         self.msg = ""
       
   653 
       
   654         self.GenDnDSVGParams = newparams
   600 
   655 
   601         if self.tempf is not None:
   656         if self.tempf is not None:
   602             os.unlink(self.tempf.name)
   657             os.unlink(self.tempf.name)
   603             self.tempf = None
   658             self.tempf = None
   604 
   659 
   605         try:
   660         try:
   606             if self.selected_SVG is None:
   661             if self.selected_SVG is None:
   607                 raise Exception(_("No widget selected"))
   662                 raise Exception(_("No widget selected"))
   608             if self.hmitree_node is None:
       
   609                 raise Exception(_("No HMI tree node selected"))
       
   610 
   663 
   611             transform = XSLTransform(
   664             transform = XSLTransform(
   612                 os.path.join(ScriptDirectory, "gen_dnd_widget_svg.xslt"),
   665                 os.path.join(ScriptDirectory, "gen_dnd_widget_svg.xslt"),
   613                 [("GetSubHMITree", self.GetSubHMITree),
   666                 [("GetWidgetParams", self.GetWidgetParams),
   614                  ("PassMessage", self.GiveDetails)])
   667                  ("PassMessage", self.PassMessage)])
   615 
   668 
   616             svgdom = etree.parse(self.selected_SVG)
   669             svgdom = etree.parse(self.selected_SVG)
   617 
   670 
   618             result = transform.transform(
   671             result = transform.transform(svgdom)
   619                 svgdom, hmi_path = self.hmitree_node.hmi_path())
       
   620 
   672 
   621             for entry in transform.get_error_log():
   673             for entry in transform.get_error_log():
   622                 self.msg += "XSLT: " + entry.message + "\n" 
   674                 self.msg += "XSLT: " + entry.message + "\n" 
   623 
   675 
   624             self.tempf = NamedTemporaryFile(suffix='.svg', delete=False)
   676             self.tempf = NamedTemporaryFile(suffix='.svg', delete=False)