SVGHMI: added extraction of SVG bounding boxes, obtained from "inkscape -S", and passed to XSLT transform as variable.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This file is part of Beremiz
# Copyright (C) 2019: Edouard TISSERANT
#
# See COPYING file for copyrights details.
from __future__ import absolute_import
import os
import shutil
import wx
import util.paths as paths
from POULibrary import POULibrary
from docutil import open_svg, get_inkscape_path
from lxml import etree
from util.ProcessLogger import ProcessLogger
HMI_TYPES_DESC = {
"HMI_CLASS":{},
"HMI_LABEL":{},
"HMI_STRING":{},
"HMI_INT":{},
"HMI_REAL":{}
}
HMI_TYPES = HMI_TYPES_DESC.keys()
from XSLTransform import XSLTransform
ScriptDirectory = paths.AbsDir(__file__)
class SVGHMILibrary(POULibrary):
def GetLibraryPath(self):
return paths.AbsNeighbourFile(__file__, "pous.xml")
def Generate_C(self, buildpath, varlist, IECCFLAGS):
# Filter known HMI types
hmi_types_instances = [v for v in varlist if v["derived"] in HMI_TYPES]
# TODO deduce HMI tree
# TODO generate C code to observe/access HMI tree variables
svghmi_c_filepath = paths.AbsNeighbourFile(__file__, "svghmi.c")
svghmi_c_file = open(svghmi_c_filepath, 'r')
svghmi_c_code = svghmi_c_file.read()
svghmi_c_file.close()
svghmi_c_code = svghmi_c_code % { "hmi_tree": "TODO !!!"}
gen_svghmi_c_path = os.path.join(buildpath, "svghmi.c")
gen_svghmi_c = open(gen_svghmi_c_path, 'w')
gen_svghmi_c.write(svghmi_c_code)
gen_svghmi_c.close()
return (["svghmi"], [(gen_svghmi_c_path, IECCFLAGS)], True), ""
class SVGHMI(object):
XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="SVGHMI">
<xsd:complexType>
<xsd:attribute name="enableHTTP" type="xsd:boolean" use="optional" default="false"/>
<xsd:attribute name="bindAddress" type="xsd:string" use="optional" default="localhost"/>
<xsd:attribute name="port" type="xsd:string" use="optional" default="8080"/>
</xsd:complexType>
</xsd:element>
</xsd:schema>
"""
ConfNodeMethods = [
{
"bitmap": "ImportSVG",
"name": _("Import SVG"),
"tooltip": _("Import SVG"),
"method": "_ImportSVG"
},
{
"bitmap": "ImportSVG", # should be something different
"name": _("Inkscape"),
"tooltip": _("Edit HMI"),
"method": "_StartInkscape"
},
]
def _getSVGpath(self, project_path=None):
if project_path is None:
project_path = self.CTNPath()
# define name for SVG file containing gui layout
return os.path.join(project_path, "gui.svg")
def OnCTNSave(self, from_project_path=None):
if from_project_path is not None:
shutil.copyfile(self._getSVGpath(from_project_path),
self._getSVGpath())
return True
def GetSVGGeometry(self):
# invoke inskscape -S, csv-parse output, produce elements
InkscapeGeomColumns = ["Id", "x", "y", "w", "h"]
# TODO : move following line to __init__
inkpath = get_inkscape_path()
svgpath = self._getSVGpath()
_status, result, _err_result = ProcessLogger(None,
inkpath + " -S " + svgpath,
no_stdout=True,
no_stderr=True).spin()
res = []
for line in result.split():
strippedline = line.strip()
attrs = dict(
zip(InkscapeGeomColumns, line.strip().split(',')))
res.append(etree.Element("bbox", **attrs))
return res
def CTNGenerate_C(self, buildpath, locations):
"""
Return C code generated by iec2c compiler
when _generate_softPLC have been called
@param locations: ignored
@return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
"""
# TODO fetch HMI tree from library
svgfile = self._getSVGpath()
if os.path.exists(svgfile):
# TODO : move to __init__
transform = XSLTransform(os.path.join(ScriptDirectory, "gen_index_xhtml.xslt"),
[("GetSVGGeometry", lambda *_ignored:self.GetSVGGeometry())])
# load svg as a DOM with Etree
svgdom = etree.parse(svgfile)
# call xslt transform on Inkscape's SVG to generate XHTML
result = transform.transform(svgdom)
print(str(result))
print(transform.xslt.error_log)
# TODO
# - Errors on HMI semantics
# - ... maybe something to have a global view of what is declared in SVG.
else:
# TODO : use default svg that expose the HMI tree as-is
pass
res = ([], "", False)
targetpath = os.path.join(self._getBuildPath(), "target.xhtml")
targetfile = open(targetpath, 'w')
# TODO : DOM to string
targetfile.write("TODO")
targetfile.close()
res += (("target.js", open(targetpath, "rb")),)
# TODO add C code to expose HMI tree variables to shared memory
# TODO generate a description of shared memory (xml or CSV)
# that can be loaded by svghmi QTWeb* app or svghmi server
return res
def _ImportSVG(self):
dialog = wx.FileDialog(self.GetCTRoot().AppFrame, _("Choose a SVG file"), os.getcwd(), "", _("SVG files (*.svg)|*.svg|All files|*.*"), wx.OPEN)
if dialog.ShowModal() == wx.ID_OK:
svgpath = dialog.GetPath()
if os.path.isfile(svgpath):
shutil.copy(svgpath, self._getSVGpath())
else:
self.GetCTRoot().logger.write_error(_("No such SVG file: %s\n") % svgpath)
dialog.Destroy()
def _StartInkscape(self):
svgfile = self._getSVGpath()
open_inkscape = True
if not self.GetCTRoot().CheckProjectPathPerm():
dialog = wx.MessageDialog(self.GetCTRoot().AppFrame,
_("You don't have write permissions.\nOpen Inkscape anyway ?"),
_("Open Inkscape"),
wx.YES_NO | wx.ICON_QUESTION)
open_inkscape = dialog.ShowModal() == wx.ID_YES
dialog.Destroy()
if open_inkscape:
if not os.path.isfile(svgfile):
svgfile = None
open_svg(svgfile)