--- a/svghmi/svghmi.py Tue Mar 23 05:08:51 2021 +0100
+++ b/svghmi/svghmi.py Tue Mar 23 05:11:23 2021 +0100
@@ -21,6 +21,7 @@
from lxml.etree import XSLTApplyError
+from IDEFrame import EncodeFileSystemPath, DecodeFileSystemPath import util.paths as paths
from POULibrary import POULibrary
from docutil import open_svg, get_inkscape_path
@@ -373,6 +374,13 @@
# to ensure placement before other CTN generated code in execution order
+def SVGHMIEditorUpdater(ref): + def SVGHMIEditorUpdate(): + wx.CallAfter(o.MakeTree) + return SVGHMIEditorUpdate class HMITreeSelector(wx.TreeCtrl):
def __init__(self, parent):
@@ -382,7 +390,7 @@
- on_hmitree_update = self.SVGHMIEditorUpdater()
+ on_hmitree_update = SVGHMIEditorUpdater(weakref.ref(self)) def _recurseTree(self, current_hmitree_root, current_tc_root):
@@ -391,13 +399,13 @@
display_name = ('{} (class={})'.format(c.name, c.hmiclass)) \
if c.hmiclass is not None else c.name
tc_child = self.AppendItem(current_tc_root, display_name)
- self.SetPyData(tc_child, None)
+ self.SetPyData(tc_child, None) # TODO self._recurseTree(c,tc_child)
display_name = '{} {}'.format(c.nodetype[4:], c.name)
tc_child = self.AppendItem(current_tc_root, display_name)
- self.SetPyData(tc_child, None)
+ self.SetPyData(tc_child, None) # TODO @@ -407,7 +415,8 @@
- root_display_name = _("Please build to see HMI Tree") if hmi_tree_root is None else "HMI"
+ root_display_name = _("Please build to see HMI Tree") \ + if hmi_tree_root is None else "HMI" self.root = self.AddRoot(root_display_name)
self.SetPyData(self.root, None)
@@ -417,13 +426,226 @@
- def SVGHMIEditorUpdater(self):
- selfref = weakref.ref(self)
- def SVGHMIEditorUpdate():
- wx.CallAfter(o.MakeTree)
- return SVGHMIEditorUpdate
+class WidgetPicker(wx.TreeCtrl): + def __init__(self, parent, initialdir=None): + wx.TreeCtrl.__init__(self, parent, style=( + self.MakeTree(initialdir) + def _recurseTree(self, current_dir, current_tc_root, dirlist): + recurse through subdirectories, but creates tree nodes + only when (sub)directory conbtains .svg file + for f in sorted(os.listdir(current_dir)): + p = os.path.join(current_dir,f) + r = self._recurseTree(p, current_tc_root, dirlist + [f]) + current_tc_root = res.pop() + elif os.path.splitext(f)[1].upper() == ".SVG": + current_tc_root = self.AppendItem(current_tc_root, d) + res.append(current_tc_root) + self.SetPyData(current_tc_root, None) + tc_child = self.AppendItem(current_tc_root, f) + self.SetPyData(tc_child, p) + def MakeTree(self, lib_dir = None): + root_display_name = _("Please select widget library directory") \ + if lib_dir is None else os.path.basename(lib_dir) + self.root = self.AddRoot(root_display_name) + self.SetPyData(self.root, None) + if lib_dir is not None: + self._recurseTree(lib_dir, self.root, []) +_conf_key = "SVGHMIWidgetLib" +class WidgetLibBrowser(wx.Panel): + def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, + wx.Panel.__init__(self, parent, id, pos, size) + self.hmitree_node = None + self.selected_SVG = None + self.Config = wx.ConfigBase.Get() + self.libdir = self.RecallLibDir() + sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=0) + sizer.AddGrowableCol(0) + sizer.AddGrowableRow(1) + self.libbutton = wx.Button(self, -1, _("Select SVG widget library")) + self.widgetpicker = WidgetPicker(self, self.libdir) + self.preview = wx.Panel(self, size=(-1, _preview_height + 10)) #, style=wx.SIMPLE_BORDER) + #self.preview.SetBackgroundColour(wx.WHITE) + sizer.AddWindow(self.libbutton, flag=wx.GROW) + sizer.AddWindow(self.widgetpicker, flag=wx.GROW) + sizer.AddWindow(self.preview, flag=wx.GROW) + self.SetAutoLayout(True) + self.Bind(wx.EVT_BUTTON, self.OnSelectLibDir, self.libbutton) + self.preview.Bind(wx.EVT_PAINT, self.OnPaint) + self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnWidgetSelection, self.widgetpicker) + self.msg = _("Drag selected Widget from here to Inkscape") + def RecallLibDir(self): + conf = self.Config.Read(_conf_key) + return DecodeFileSystemPath(conf) + def RememberLibDir(self, path): + self.Config.Write(_conf_key, + EncodeFileSystemPath(path)) + # Init preview panel paint device context + dc = wx.PaintDC(self.preview) + # Get Preview panel size + sz = self.preview.GetClientSize() + w = self.bmp.GetWidth() + dc.DrawBitmap(self.bmp, (sz.width - w)/2, 5) + dc.SetFont(self.GetFont()) + dc.DrawText(self.msg, 25,25) + def OnSelectLibDir(self, event): + defaultpath = self.RecallLibDir() + if defaultpath == None: + defaultpath = os.path.expanduser("~") + dialog = wx.DirDialog(self, _("Choose a widget library"), defaultpath, + style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) + if dialog.ShowModal() == wx.ID_OK: + self.libdir = dialog.GetPath() + self.RememberLibDir(self.libdir) + self.widgetpicker.MakeTree(self.libdir) + def OnPaint(self, event): + Called when Preview panel needs to be redrawn + @param event: wx.PaintEvent + def GenThumbnail(self, svgpath, thumbpath): + inkpath = get_inkscape_path() + self.msg = _("Inkscape is not installed.") + # TODO: spawn a thread, to decouple thumbnail gen + status, result, _err_result = ProcessLogger( + '"' + inkpath + '" "' + svgpath + '" -e "' + thumbpath + + '" -D -h ' + str(_preview_height)).spin() + self.msg = _("Inkscape couldn't generate thumbnail.") + def OnWidgetSelection(self, event): + Called when tree item is selected + @param event: wx.TreeEvent + item_pydata = self.widgetpicker.GetPyData(event.GetItem()) + if item_pydata is not None: + dname = os.path.dirname(svgpath) + fname = os.path.basename(svgpath) + hasher = hashlib.new('md5') + with open(svgpath, 'rb') as afile: + buf = afile.read(65536) + digest = hasher.hexdigest() + thumbfname = os.path.splitext(fname)[0]+"_"+digest+".png" + thumbdir = os.path.join(dname, ".svghmithumbs") + thumbpath = os.path.join(thumbdir, thumbfname) + have_thumb = os.path.exists(thumbpath) + if not os.path.exists(thumbdir): + self.msg = _("Widget library must be writable") + have_thumb = self.GenThumbnail(svgpath, thumbpath) + self.bmp = wx.Bitmap(thumbpath) if have_thumb else None + self.selected_SVG = svgpath if have_thumb else None + def OnHMITreeNodeSelection(self, hmitree_node): + self.hmitree_node = hmitree_node + def ValidateWidget(self): + if self.selected_SVG is not None: + if self.hmitree_node is not None: + # - check SVG is valid for selected HMI tree item class HMITreeView(wx.SplitterWindow):
@@ -432,9 +654,8 @@
style=wx.SUNKEN_BORDER | wx.SP_3D)
self.SelectionTree = HMITreeSelector(self)
- #self.Staging = wx.Panel(self)
- #self.SplitHorizontally(self.SelectionTree, self.Staging, 200)
- self.Initialize(self.SelectionTree)
+ self.Staging = WidgetLibBrowser(self) + self.SplitVertically(self.SelectionTree, self.Staging, 300) class SVGHMIEditor(ConfTreeNodeEditor):
@@ -442,7 +663,6 @@
(_("HMI Tree"), "CreateHMITreeView")]
def CreateHMITreeView(self, parent):
- #self.HMITreeView = HMITreeView(self)
if hmi_tree_root is None:
@@ -452,7 +672,10 @@
hmitree_backup_file = open(hmitree_backup_path, 'rb')
hmi_tree_root = HMITreeNode.from_etree(etree.parse(hmitree_backup_file).getroot())
- return HMITreeSelector(parent)
+ #self.HMITreeView = HMITreeView(self) + #return HMITreeSelector(parent) + return HMITreeView(parent) XSD = """<?xml version="1.0" encoding="utf-8" ?>