andrej@1511: #!/usr/bin/env python andrej@1511: # -*- coding: utf-8 -*- andrej@1511: andrej@1511: # This file is part of Beremiz, a Integrated Development Environment for andrej@1511: # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. andrej@1511: # andrej@1511: # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD andrej@1680: # Copyright (C) 2017: Andrey Skvortsov andrej@1511: # andrej@1511: # See COPYING file for copyrights details. andrej@1511: # andrej@1511: # This program is free software; you can redistribute it and/or andrej@1511: # modify it under the terms of the GNU General Public License andrej@1511: # as published by the Free Software Foundation; either version 2 andrej@1511: # of the License, or (at your option) any later version. andrej@1511: # andrej@1511: # This program is distributed in the hope that it will be useful, andrej@1511: # but WITHOUT ANY WARRANTY; without even the implied warranty of andrej@1511: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the andrej@1511: # GNU General Public License for more details. andrej@1511: # andrej@1511: # You should have received a copy of the GNU General Public License andrej@1511: # along with this program; if not, write to the Free Software andrej@1511: # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. andrej@1511: andrej@1832: andrej@1881: from __future__ import absolute_import andrej@1732: import os andrej@1732: import sys andrej@1732: import shutil laurent@367: from xml.dom import minidom laurent@367: andrej@1832: import wx andrej@1832: andrej@1680: import util.paths as paths Edouard@728: from py_ext import PythonFileCTNMixin laurent@367: andrej@1736: Edouard@728: class WxGladeHMI(PythonFileCTNMixin): laurent@367: Edouard@717: ConfNodeMethods = [ andrej@1739: { andrej@1739: "bitmap": "editWXGLADE", andrej@1739: "name": _("WXGLADE GUI"), andrej@1739: "tooltip": _("Edit a WxWidgets GUI with WXGlade"), andrej@1739: "method": "_editWXGLADE" andrej@1739: }, laurent@367: ] laurent@367: Laurent@1163: def GetIconName(self): Laurent@1163: return "wxGlade" Laurent@1163: Edouard@728: def ConfNodePath(self): andrej@1680: return paths.AbsDir(__file__) Edouard@728: Laurent@1061: def _getWXGLADEpath(self, project_path=None): Laurent@1061: if project_path is None: Laurent@1061: project_path = self.CTNPath() Laurent@1061: # define name for wxGlade gui file Laurent@1061: return os.path.join(project_path, "hmi.wxg") laurent@367: andrej@1688: def GetWxGladePath(self): andrej@1688: path = None andrej@1688: try: andrej@1688: from wxglade import __file__ as fileName andrej@1730: path = os.path.dirname(fileName) andrej@1688: return path andrej@1688: except ImportError: andrej@1688: pass andrej@1688: andrej@1742: defLibDir = "/usr/share/wxglade" andrej@1688: if os.path.isdir(defLibDir): andrej@1688: path = defLibDir andrej@1688: andrej@1688: return path andrej@1730: laurent@367: def launch_wxglade(self, options, wait=False): andrej@1688: path = self.GetWxGladePath() laurent@367: glade = os.path.join(path, 'wxglade.py') laurent@367: if wx.Platform == '__WXMSW__': andrej@1734: glade = "\"%s\"" % glade andrej@1740: mode = {False: os.P_NOWAIT, True: os.P_WAIT}[wait] Edouard@2248: os.spawnv(mode, sys.executable, Edouard@2248: ["\"%s\"" % sys.executable] + [glade] + options) laurent@367: Laurent@1061: def OnCTNSave(self, from_project_path=None): Laurent@1061: if from_project_path is not None: Laurent@1061: shutil.copyfile(self._getWXGLADEpath(from_project_path), Laurent@1061: self._getWXGLADEpath()) Laurent@1061: return PythonFileCTNMixin.OnCTNSave(self, from_project_path) laurent@367: Edouard@718: def CTNGenerate_C(self, buildpath, locations): andrej@1730: Edouard@2188: # list containing description of all objects declared in wxglade Edouard@2188: hmi_objects = [] Edouard@2248: # list containing only description of the main frame object Edouard@2188: main_frames = [] andrej@1730: andrej@1742: wxgfile_path = self._getWXGLADEpath() laurent@367: if os.path.exists(wxgfile_path): laurent@367: wxgfile = open(wxgfile_path, 'r') laurent@367: wxgtree = minidom.parse(wxgfile) laurent@367: wxgfile.close() andrej@1730: laurent@367: for node in wxgtree.childNodes[1].childNodes: laurent@367: if node.nodeType == wxgtree.ELEMENT_NODE: Edouard@2188: name = node.getAttribute("name") Edouard@2188: wxglade_object_desc = { Edouard@2188: "name": name, andrej@1739: "class": node.getAttribute("class"), andrej@1739: "handlers": [ andrej@1730: hnode.firstChild.data for hnode in Edouard@2188: node.getElementsByTagName("handler")]} Edouard@2188: Edouard@2188: hmi_objects.append(wxglade_object_desc) Edouard@2248: if name == self.CTNName(): Edouard@2188: main_frames.append(wxglade_object_desc) andrej@1730: andrej@1742: hmipyfile_path = os.path.join(self._getBuildPath(), "hmi.py") laurent@367: if wx.Platform == '__WXMSW__': andrej@1734: wxgfile_path = "\"%s\"" % wxgfile_path andrej@1734: wxghmipyfile_path = "\"%s\"" % hmipyfile_path laurent@384: else: laurent@384: wxghmipyfile_path = hmipyfile_path Edouard@2248: self.launch_wxglade( Edouard@2248: ['-o', wxghmipyfile_path, '-g', 'python', wxgfile_path], wait=True) andrej@1730: laurent@367: hmipyfile = open(hmipyfile_path, 'r') Edouard@1132: define_hmi = hmipyfile.read().decode('utf-8') laurent@367: hmipyfile.close() andrej@1730: Laurent@1256: else: Laurent@1256: define_hmi = "" andrej@1730: Edouard@2188: declare_hmi = "\n".join(["%(name)s = None\n" % x for x in main_frames]) Edouard@2188: declare_hmi += "\n".join(["\n".join(["%(class)s.%(h)s = %(h)s" % Edouard@2248: dict(x, h=h) for h in x['handlers']]) Edouard@2248: for x in hmi_objects]) andrej@1734: global_hmi = ("global %s\n" % ",".join( Edouard@2188: [x["name"] for x in main_frames]) if len(main_frames) > 0 else "") Edouard@1155: init_hmi = "\n".join(["""\ Edouard@1132: def OnCloseFrame(evt): Edouard@1132: wx.MessageBox(_("Please stop PLC to close")) Edouard@1132: Edouard@1132: %(name)s = %(class)s(None) Edouard@1132: %(name)s.Bind(wx.EVT_CLOSE, OnCloseFrame) Edouard@1132: %(name)s.Show() Edouard@2188: """ % x for x in main_frames]) Edouard@1155: cleanup_hmi = "\n".join( andrej@1730: ["if %(name)s is not None: %(name)s.Destroy()" % x Edouard@2188: for x in main_frames]) andrej@1730: Edouard@1132: self.PreSectionsTexts = { andrej@1740: "globals": define_hmi, andrej@1740: "start": global_hmi, andrej@1740: "stop": global_hmi + cleanup_hmi Edouard@1132: } Edouard@1132: self.PostSectionsTexts = { andrej@1740: "globals": declare_hmi, andrej@1740: "start": init_hmi, Edouard@1132: } Laurent@1124: Edouard@2188: if len(main_frames) == 0 and \ Edouard@2188: len(getattr(self.CodeFile, "start").getanyText().strip()) == 0: Edouard@2248: self.GetCTRoot().logger.write_warning( Edouard@2248: _("Warning: WxGlade HMI has no object with name identical to extension name, and no python code is provided in start section to create object.\n")) Edouard@2248: Edouard@1132: return PythonFileCTNMixin.CTNGenerate_C(self, buildpath, locations) laurent@367: laurent@367: def _editWXGLADE(self): laurent@367: wxg_filename = self._getWXGLADEpath() greg@427: open_wxglade = True Edouard@718: if not self.GetCTRoot().CheckProjectPathPerm(): Edouard@718: dialog = wx.MessageDialog(self.GetCTRoot().AppFrame, greg@427: _("You don't have write permissions.\nOpen wxGlade anyway ?"), greg@427: _("Open wxGlade"), andrej@1745: wx.YES_NO | wx.ICON_QUESTION) greg@427: open_wxglade = dialog.ShowModal() == wx.ID_YES greg@427: dialog.Destroy() greg@427: if open_wxglade: greg@427: if not os.path.exists(wxg_filename): greg@427: hmi_name = self.BaseParams.getName() andrej@1740: open(wxg_filename, "w").write(""" greg@427: greg@427: greg@427: greg@427: frame_1 laurent@834: laurent@834: wxVERTICAL laurent@834: laurent@834: greg@427: greg@427: greg@427: """ % {"name": hmi_name, "class": "Class_%s" % hmi_name}) greg@427: if wx.Platform == '__WXMSW__': andrej@1734: wxg_filename = "\"%s\"" % wxg_filename greg@427: self.launch_wxglade([wxg_filename])