Laurent@814: #!/usr/bin/env python Laurent@814: # -*- coding: utf-8 -*- Laurent@814: andrej@1571: # This file is part of Beremiz, a Integrated Development Environment for andrej@1571: # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. Laurent@814: # andrej@1571: # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD andrej@1680: # Copyright (C) 2017: Andrey Skvortsov Laurent@814: # andrej@1571: # See COPYING file for copyrights details. Laurent@814: # andrej@1571: # This program is free software; you can redistribute it and/or andrej@1571: # modify it under the terms of the GNU General Public License andrej@1571: # as published by the Free Software Foundation; either version 2 andrej@1571: # of the License, or (at your option) any later version. Laurent@814: # andrej@1571: # This program is distributed in the hope that it will be useful, andrej@1571: # but WITHOUT ANY WARRANTY; without even the implied warranty of andrej@1571: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the andrej@1571: # GNU General Public License for more details. Laurent@814: # andrej@1571: # You should have received a copy of the GNU General Public License andrej@1571: # along with this program; if not, write to the Free Software andrej@1571: # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Laurent@814: andrej@1826: andrej@1881: from __future__ import absolute_import andrej@1826: from __future__ import print_function andrej@1732: import os andrej@1732: import sys andrej@1732: import getopt andrej@1792: andrej@1832: import wx andrej@1832: andrej@1560: import version andrej@1680: import util.paths as paths andrej@1792: import util.ExceptionHandler andrej@1834: from util.misc import InstallLocalRessources andrej@1872: from docutil.docpdf import open_pdf andrej@1814: from IDEFrame import IDEFrame, AppendMenu andrej@1814: from IDEFrame import \ andrej@1814: TITLE, \ andrej@1814: EDITORTOOLBAR, \ andrej@1814: FILEMENU, \ andrej@1814: EDITMENU, \ andrej@1814: DISPLAYMENU, \ andrej@1814: PROJECTTREE, \ andrej@1814: POUINSTANCEVARIABLESPANEL, \ andrej@1814: LIBRARYTREE, \ andrej@1850: PAGETITLES, \ andrej@1850: DecodeFileSystemPath andrej@1814: from editors.Viewer import Viewer andrej@1814: from PLCControler import PLCControler andrej@1814: from dialogs import ProjectDialog andrej@1814: from dialogs.AboutDialog import ShowAboutDialog andrej@1791: Laurent@814: andrej@1782: # ------------------------------------------------------------------------------- Laurent@814: # PLCOpenEditor Main Class andrej@1782: # ------------------------------------------------------------------------------- Laurent@814: Laurent@814: # Define PLCOpenEditor FileMenu extra items id andrej@1773: [ andrej@1773: ID_PLCOPENEDITORFILEMENUGENERATE, Laurent@814: ] = [wx.NewId() for _init_coll_FileMenu_Items in range(1)] Laurent@814: andrej@1736: andrej@1814: beremiz_dir = paths.AbsDir(__file__) andrej@1814: andrej@1814: Laurent@814: class PLCOpenEditor(IDEFrame): Laurent@814: Laurent@814: def _init_coll_FileMenu_Items(self, parent): Laurent@814: AppendMenu(parent, help='', id=wx.ID_NEW, andrej@1768: kind=wx.ITEM_NORMAL, text=_(u'New') + '\tCTRL+N') Laurent@814: AppendMenu(parent, help='', id=wx.ID_OPEN, andrej@1768: kind=wx.ITEM_NORMAL, text=_(u'Open') + '\tCTRL+O') Laurent@814: AppendMenu(parent, help='', id=wx.ID_CLOSE, andrej@1768: kind=wx.ITEM_NORMAL, text=_(u'Close Tab') + '\tCTRL+W') Laurent@814: AppendMenu(parent, help='', id=wx.ID_CLOSE_ALL, andrej@1768: kind=wx.ITEM_NORMAL, text=_(u'Close Project') + '\tCTRL+SHIFT+W') Laurent@814: parent.AppendSeparator() Laurent@814: AppendMenu(parent, help='', id=wx.ID_SAVE, andrej@1768: kind=wx.ITEM_NORMAL, text=_(u'Save') + '\tCTRL+S') Laurent@814: AppendMenu(parent, help='', id=wx.ID_SAVEAS, andrej@1768: kind=wx.ITEM_NORMAL, text=_(u'Save As...') + '\tCTRL+SHIFT+S') Laurent@814: AppendMenu(parent, help='', id=ID_PLCOPENEDITORFILEMENUGENERATE, andrej@1768: kind=wx.ITEM_NORMAL, text=_(u'Generate Program') + '\tCTRL+G') Laurent@814: parent.AppendSeparator() Laurent@814: AppendMenu(parent, help='', id=wx.ID_PAGE_SETUP, andrej@1768: kind=wx.ITEM_NORMAL, text=_(u'Page Setup') + '\tCTRL+ALT+P') Laurent@814: AppendMenu(parent, help='', id=wx.ID_PREVIEW, andrej@1768: kind=wx.ITEM_NORMAL, text=_(u'Preview') + '\tCTRL+SHIFT+P') Laurent@814: AppendMenu(parent, help='', id=wx.ID_PRINT, andrej@1768: kind=wx.ITEM_NORMAL, text=_(u'Print') + '\tCTRL+P') Laurent@814: parent.AppendSeparator() Laurent@814: AppendMenu(parent, help='', id=wx.ID_PROPERTIES, andrej@1768: kind=wx.ITEM_NORMAL, text=_(u'&Properties')) Laurent@814: parent.AppendSeparator() Laurent@814: AppendMenu(parent, help='', id=wx.ID_EXIT, andrej@1768: kind=wx.ITEM_NORMAL, text=_(u'Quit') + '\tCTRL+Q') Edouard@1451: Laurent@814: self.Bind(wx.EVT_MENU, self.OnNewProjectMenu, id=wx.ID_NEW) Laurent@814: self.Bind(wx.EVT_MENU, self.OnOpenProjectMenu, id=wx.ID_OPEN) Laurent@814: self.Bind(wx.EVT_MENU, self.OnCloseTabMenu, id=wx.ID_CLOSE) Laurent@814: self.Bind(wx.EVT_MENU, self.OnCloseProjectMenu, id=wx.ID_CLOSE_ALL) Laurent@814: self.Bind(wx.EVT_MENU, self.OnSaveProjectMenu, id=wx.ID_SAVE) Laurent@814: self.Bind(wx.EVT_MENU, self.OnSaveProjectAsMenu, id=wx.ID_SAVEAS) Laurent@814: self.Bind(wx.EVT_MENU, self.OnGenerateProgramMenu, andrej@1768: id=ID_PLCOPENEDITORFILEMENUGENERATE) Laurent@814: self.Bind(wx.EVT_MENU, self.OnPageSetupMenu, id=wx.ID_PAGE_SETUP) Laurent@814: self.Bind(wx.EVT_MENU, self.OnPreviewMenu, id=wx.ID_PREVIEW) Laurent@814: self.Bind(wx.EVT_MENU, self.OnPrintMenu, id=wx.ID_PRINT) Laurent@814: self.Bind(wx.EVT_MENU, self.OnPropertiesMenu, id=wx.ID_PROPERTIES) Laurent@814: self.Bind(wx.EVT_MENU, self.OnQuitMenu, id=wx.ID_EXIT) Edouard@1451: Laurent@814: self.AddToMenuToolBar([(wx.ID_NEW, "new", _(u'New'), None), Laurent@814: (wx.ID_OPEN, "open", _(u'Open'), None), Laurent@814: (wx.ID_SAVE, "save", _(u'Save'), None), Laurent@814: (wx.ID_SAVEAS, "saveas", _(u'Save As...'), None), Laurent@814: (wx.ID_PRINT, "print", _(u'Print'), None)]) Edouard@1451: Laurent@814: def _init_coll_HelpMenu_Items(self, parent): Edouard@1451: AppendMenu(parent, help='', id=wx.ID_HELP, andrej@1768: kind=wx.ITEM_NORMAL, text=_(u'PLCOpenEditor') + '\tF1') andrej@1782: # AppendMenu(parent, help='', id=wx.ID_HELP_CONTENTS, Laurent@814: # kind=wx.ITEM_NORMAL, text=u'PLCOpen\tF2') andrej@1782: # AppendMenu(parent, help='', id=wx.ID_HELP_CONTEXT, Laurent@814: # kind=wx.ITEM_NORMAL, text=u'IEC 61131-3\tF3') andrej@1730: andrej@1762: def handler(event): andrej@1762: return wx.MessageBox( andrej@1762: version.GetCommunityHelpMsg(), andrej@1762: _(u'Community support'), andrej@1762: wx.OK | wx.ICON_INFORMATION) andrej@1762: andrej@1692: id = wx.NewId() andrej@1730: parent.Append(help='', id=id, kind=wx.ITEM_NORMAL, text=_(u'Community support')) andrej@1692: self.Bind(wx.EVT_MENU, handler, id=id) andrej@1730: Laurent@814: AppendMenu(parent, help='', id=wx.ID_ABOUT, andrej@1768: kind=wx.ITEM_NORMAL, text=_(u'About')) Laurent@814: self.Bind(wx.EVT_MENU, self.OnPLCOpenEditorMenu, id=wx.ID_HELP) andrej@1782: # self.Bind(wx.EVT_MENU, self.OnPLCOpenMenu, id=wx.ID_HELP_CONTENTS) Laurent@814: self.Bind(wx.EVT_MENU, self.OnAboutMenu, id=wx.ID_ABOUT) Laurent@814: andrej@1744: def __init__(self, parent, fileOpen=None): andrej@1781: """ Constructor of the PLCOpenEditor class. andrej@1781: andrej@1781: :param parent: The parent window. andrej@1781: :param fileOpen: The filepath to open if no controler defined (default: None). andrej@1781: """ andrej@1533: self.icon = wx.Icon(os.path.join(beremiz_dir, "images", "poe.ico"), wx.BITMAP_TYPE_ICO) Laurent@814: IDEFrame.__init__(self, parent) Edouard@1451: Laurent@814: result = None Edouard@1451: Laurent@814: # Open the filepath if defined Laurent@814: if fileOpen is not None: Laurent@814: fileOpen = DecodeFileSystemPath(fileOpen, False) Laurent@814: if os.path.isfile(fileOpen): Laurent@814: # Create a new controller Laurent@814: controler = PLCControler() Laurent@814: result = controler.OpenXMLFile(fileOpen) Laurent@1330: self.Controler = controler Laurent@1330: self.LibraryPanel.SetController(controler) Laurent@1330: self.ProjectTree.Enable(True) Laurent@1330: self.PouInstanceVariablesPanel.SetController(controler) Laurent@1330: self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) Edouard@1451: Laurent@814: # Define PLCOpenEditor icon andrej@1533: self.SetIcon(self.icon) Laurent@814: Laurent@814: self.Bind(wx.EVT_CLOSE, self.OnCloseFrame) Edouard@1451: Laurent@814: self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU) Edouard@1451: Laurent@814: if result is not None: andrej@1581: (num, line) = result andrej@1744: self.ShowErrorMessage(_("PLC syntax error at line {a1}:\n{a2}").format(a1=num, a2=line)) Laurent@814: Laurent@814: def OnCloseFrame(self, event): Laurent@814: if self.Controler is None or self.CheckSaveBeforeClosing(_("Close Application")): Laurent@814: self.AUIManager.UnInit() Edouard@1451: Laurent@814: self.SaveLastState() Edouard@1451: Laurent@814: event.Skip() Laurent@814: else: Laurent@814: event.Veto() Laurent@814: Laurent@814: def RefreshTitle(self): Laurent@814: name = _("PLCOpenEditor") Laurent@814: if self.Controler is not None: andrej@1734: self.SetTitle("%s - %s" % (name, self.Controler.GetFilename())) Laurent@814: else: Laurent@814: self.SetTitle(name) Laurent@814: andrej@1782: # ------------------------------------------------------------------------------- andrej@1782: # File Menu Functions andrej@1782: # ------------------------------------------------------------------------------- Laurent@814: Laurent@814: def RefreshFileMenu(self): Laurent@814: MenuToolBar = self.Panes["MenuToolBar"] Laurent@814: if self.Controler is not None: Laurent@814: selected = self.TabsOpened.GetSelection() Laurent@814: if selected >= 0: Laurent@814: graphic_viewer = isinstance(self.TabsOpened.GetPage(selected), Viewer) Laurent@814: else: Laurent@814: graphic_viewer = False Laurent@814: if self.TabsOpened.GetPageCount() > 0: Laurent@814: self.FileMenu.Enable(wx.ID_CLOSE, True) Laurent@814: if graphic_viewer: Laurent@814: self.FileMenu.Enable(wx.ID_PREVIEW, True) Laurent@814: self.FileMenu.Enable(wx.ID_PRINT, True) Laurent@814: MenuToolBar.EnableTool(wx.ID_PRINT, True) Laurent@814: else: Laurent@814: self.FileMenu.Enable(wx.ID_PREVIEW, False) Laurent@814: self.FileMenu.Enable(wx.ID_PRINT, False) Laurent@814: MenuToolBar.EnableTool(wx.ID_PRINT, False) Laurent@814: else: Laurent@814: self.FileMenu.Enable(wx.ID_CLOSE, False) Laurent@814: self.FileMenu.Enable(wx.ID_PREVIEW, False) Laurent@814: self.FileMenu.Enable(wx.ID_PRINT, False) Laurent@814: MenuToolBar.EnableTool(wx.ID_PRINT, False) Laurent@814: self.FileMenu.Enable(wx.ID_PAGE_SETUP, True) Laurent@814: project_modified = not self.Controler.ProjectIsSaved() Laurent@814: self.FileMenu.Enable(wx.ID_SAVE, project_modified) Laurent@814: MenuToolBar.EnableTool(wx.ID_SAVE, project_modified) Laurent@814: self.FileMenu.Enable(wx.ID_PROPERTIES, True) Laurent@814: self.FileMenu.Enable(wx.ID_CLOSE_ALL, True) Laurent@814: self.FileMenu.Enable(wx.ID_SAVEAS, True) Laurent@814: MenuToolBar.EnableTool(wx.ID_SAVEAS, True) Laurent@814: self.FileMenu.Enable(ID_PLCOPENEDITORFILEMENUGENERATE, True) Laurent@814: else: Laurent@814: self.FileMenu.Enable(wx.ID_CLOSE, False) Laurent@814: self.FileMenu.Enable(wx.ID_PAGE_SETUP, False) Laurent@814: self.FileMenu.Enable(wx.ID_PREVIEW, False) Laurent@814: self.FileMenu.Enable(wx.ID_PRINT, False) Laurent@814: MenuToolBar.EnableTool(wx.ID_PRINT, False) Laurent@814: self.FileMenu.Enable(wx.ID_SAVE, False) Laurent@814: MenuToolBar.EnableTool(wx.ID_SAVE, False) Laurent@814: self.FileMenu.Enable(wx.ID_PROPERTIES, False) Laurent@814: self.FileMenu.Enable(wx.ID_CLOSE_ALL, False) Laurent@814: self.FileMenu.Enable(wx.ID_SAVEAS, False) Laurent@814: MenuToolBar.EnableTool(wx.ID_SAVEAS, False) Laurent@814: self.FileMenu.Enable(ID_PLCOPENEDITORFILEMENUGENERATE, False) Laurent@814: Laurent@814: def OnNewProjectMenu(self, event): Laurent@814: if self.Controler is not None and not self.CheckSaveBeforeClosing(): Laurent@814: return Laurent@814: dialog = ProjectDialog(self) Laurent@814: if dialog.ShowModal() == wx.ID_OK: Laurent@814: properties = dialog.GetValues() Laurent@814: self.ResetView() Laurent@814: self.Controler = PLCControler() Laurent@814: self.Controler.CreateNewProject(properties) Laurent@814: self.LibraryPanel.SetController(self.Controler) Laurent@1295: self.ProjectTree.Enable(True) Edouard@1451: self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL, Laurent@814: LIBRARYTREE) Laurent@814: Laurent@814: def OnOpenProjectMenu(self, event): Laurent@814: if self.Controler is not None and not self.CheckSaveBeforeClosing(): Laurent@814: return Laurent@814: filepath = "" Laurent@814: if self.Controler is not None: Laurent@814: filepath = self.Controler.GetFilePath() Laurent@814: if filepath != "": Laurent@814: directory = os.path.dirname(filepath) Laurent@814: else: Laurent@814: directory = os.getcwd() Edouard@1451: Laurent@814: result = None Edouard@1451: Laurent@814: dialog = wx.FileDialog(self, _("Choose a file"), directory, "", _("PLCOpen files (*.xml)|*.xml|All files|*.*"), wx.OPEN) Laurent@814: if dialog.ShowModal() == wx.ID_OK: Laurent@814: filepath = dialog.GetPath() Laurent@814: if os.path.isfile(filepath): Laurent@814: self.ResetView() Laurent@814: controler = PLCControler() Laurent@814: result = controler.OpenXMLFile(filepath) Laurent@1330: self.Controler = controler Laurent@1330: self.LibraryPanel.SetController(controler) Laurent@1330: self.ProjectTree.Enable(True) Laurent@1330: self.PouInstanceVariablesPanel.SetController(controler) Laurent@1330: self._Refresh(PROJECTTREE, LIBRARYTREE) Laurent@814: self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU) Laurent@814: dialog.Destroy() Edouard@1451: Laurent@814: if result is not None: andrej@1581: (num, line) = result andrej@1744: self.ShowErrorMessage(_("PLC syntax error at line {a1}:\n{a2}").format(a1=num, a2=line)) Edouard@1451: Laurent@814: def OnCloseProjectMenu(self, event): Laurent@814: if not self.CheckSaveBeforeClosing(): Laurent@814: return Laurent@814: self.ResetView() Laurent@814: self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU) Laurent@814: Laurent@814: def OnSaveProjectMenu(self, event): Laurent@814: self.SaveProject() Laurent@814: Laurent@814: def OnSaveProjectAsMenu(self, event): Laurent@814: self.SaveProjectAs() Laurent@814: Laurent@814: def OnGenerateProgramMenu(self, event): schlumpf@2497: dialog = wx.FileDialog(self, _("Choose a file"), os.getcwd(), os.path.basename(self.Controler.GetProgramFilePath()), _("ST files (*.st)|*.st|All files|*.*"), wx.SAVE | wx.CHANGE_DIR) Laurent@814: if dialog.ShowModal() == wx.ID_OK: Laurent@814: filepath = dialog.GetPath() Laurent@814: message_text = "" Laurent@814: header, icon = _("Done"), wx.ICON_INFORMATION Laurent@814: if os.path.isdir(os.path.dirname(filepath)): andrej@1847: _program, errors, warnings = self.Controler.GenerateProgram(filepath) Laurent@1312: message_text += "".join([_("warning: %s\n") % warning for warning in warnings]) Laurent@814: if len(errors) > 0: Laurent@1312: message_text += "".join([_("error: %s\n") % error for error in errors]) andrej@1734: message_text += _("Can't generate program to file %s!") % filepath Laurent@814: header, icon = _("Error"), wx.ICON_ERROR Laurent@814: else: Laurent@814: message_text += _("Program was successfully generated!") Laurent@814: else: andrej@1734: message_text += _("\"%s\" is not a valid folder!") % os.path.dirname(filepath) Laurent@814: header, icon = _("Error"), wx.ICON_ERROR andrej@1745: message = wx.MessageDialog(self, message_text, header, wx.OK | icon) Laurent@814: message.ShowModal() Laurent@814: message.Destroy() Laurent@814: dialog.Destroy() Laurent@814: Laurent@814: def OnPLCOpenEditorMenu(self, event): Laurent@814: wx.MessageBox(_("No documentation available.\nComing soon.")) Edouard@1451: Laurent@814: def OnPLCOpenMenu(self, event): Edouard@1451: open_pdf(os.path.join(beremiz_dir, "plcopen", "TC6_XML_V101.pdf")) Edouard@1451: Laurent@814: def OnAboutMenu(self, event): andrej@1565: info = version.GetAboutDialogInfo() andrej@1565: info.Name = "PLCOpenEditor" andrej@1565: info.Description = _("PLCOpenEditor is part of Beremiz project.\n\n" andrej@1565: "Beremiz is an ") + info.Description andrej@1565: info.Icon = wx.Icon(os.path.join(beremiz_dir, "images", "aboutlogo.png"), wx.BITMAP_TYPE_PNG) andrej@1565: ShowAboutDialog(self, info) Laurent@814: Laurent@814: def SaveProject(self): Laurent@814: result = self.Controler.SaveXMLFile() Laurent@814: if not result: Laurent@814: self.SaveProjectAs() Laurent@814: else: Laurent@814: self._Refresh(TITLE, FILEMENU, PAGETITLES) Edouard@1451: Laurent@814: def SaveProjectAs(self): Laurent@814: filepath = self.Controler.GetFilePath() Laurent@814: if filepath != "": Laurent@814: directory, filename = os.path.split(filepath) Laurent@814: else: andrej@1734: directory, filename = os.getcwd(), "%(projectName)s.xml" % self.Controler.GetProjectProperties() andrej@1745: dialog = wx.FileDialog(self, _("Choose a file"), directory, filename, _("PLCOpen files (*.xml)|*.xml|All files|*.*"), wx.SAVE | wx.OVERWRITE_PROMPT) Laurent@814: if dialog.ShowModal() == wx.ID_OK: Laurent@814: filepath = dialog.GetPath() Laurent@814: if os.path.isdir(os.path.dirname(filepath)): Laurent@814: result = self.Controler.SaveXMLFile(filepath) Laurent@814: if not result: andrej@1734: self.ShowErrorMessage(_("Can't save project to file %s!") % filepath) Laurent@814: else: andrej@1734: self.ShowErrorMessage(_("\"%s\" is not a valid folder!") % os.path.dirname(filepath)) Laurent@814: self._Refresh(TITLE, FILEMENU, PAGETITLES) Laurent@814: dialog.Destroy() Laurent@814: andrej@1749: andrej@1814: class PLCOpenEditorApp(wx.App): andrej@1814: # def SetOpenFile( andrej@1814: andrej@1814: def PrintUsage(self): andrej@1826: print("\nUsage of PLCOpenEditor.py :") andrej@1826: print("\n %s [Filepath]\n" % sys.argv[0]) andrej@1814: andrej@1814: def ParseCommandLine(self): andrej@1814: # Parse options given to PLCOpenEditor in command line andrej@1814: try: andrej@1814: opts, args = getopt.getopt(sys.argv[1:], "h", ["help"]) andrej@1814: except getopt.GetoptError: andrej@1814: # print help information and exit: andrej@1814: self.PrintUsage() andrej@1814: sys.exit(2) andrej@1814: andrej@1814: # Extract if help has been requested andrej@1847: for o, _a in opts: andrej@1814: if o in ("-h", "--help"): andrej@1814: self.PrintUsage() andrej@1814: sys.exit() andrej@1814: andrej@1814: # Extract the optional filename to open andrej@1814: self.fileOpen = None andrej@1814: if len(args) > 1: andrej@1814: self.PrintUsage() andrej@1814: sys.exit() andrej@1814: elif len(args) == 1: andrej@1814: self.fileOpen = args[0] andrej@1814: andrej@1814: def OnInit(self): andrej@1815: self.SetAppName('plcopeneditor') andrej@1814: self.ParseCommandLine() andrej@1814: InstallLocalRessources(beremiz_dir) andrej@1814: if wx.VERSION < (3, 0, 0): andrej@1814: wx.InitAllImageHandlers() andrej@1814: util.ExceptionHandler.AddExceptHook(version.app_version) andrej@1814: self.frame = PLCOpenEditor(None, fileOpen=self.fileOpen) andrej@1814: return True andrej@1814: andrej@1814: def Show(self): andrej@1814: self.frame.Show() andrej@1814: andrej@1814: Laurent@814: if __name__ == '__main__': andrej@1814: app = PLCOpenEditorApp() andrej@1814: app.Show() Laurent@814: app.MainLoop()