#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
#based on the plcopen standard.
#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
#See COPYING file for copyrights details.
#This library is free software; you can redistribute it and/or
#modify it under the terms of the GNU General Public
#License as published by the Free Software Foundation; either
#version 2.1 of the License, or (at your option) any later version.
#This library is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
#General Public License for more details.
#You should have received a copy of the GNU General Public
#License along with this library; if not, write to the Free Software
#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
import os, sys, platform, time, traceback, getopt
beremiz_dir = os.path.dirname(os.path.realpath(__file__))
__version__ = "$Revision: 1.130 $"
if __name__ == '__main__':
# Usage message displayed when help request or when error detected in
print "\nUsage of PLCOpenEditor.py :"
print "\n %s [Filepath]\n"%sys.argv[0]
# Parse options given to PLCOpenEditor in command line
opts, args = getopt.getopt(sys.argv[1:], "h", ["help"])
except getopt.GetoptError:
# print help information and exit:
# Extract if help has been requested
if o in ("-h", "--help"):
# Extract the optional filename to open
# Create wxApp (Need to create App before internationalization because of
if wx.VERSION >= (3, 0, 0):
from util.misc import InstallLocalRessources
InstallLocalRessources(beremiz_dir)
from IDEFrame import IDEFrame, AppendMenu
from IDEFrame import TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE, PAGETITLES
from IDEFrame import EncodeFileSystemPath, DecodeFileSystemPath
from editors.Viewer import Viewer
from PLCControler import PLCControler
from dialogs import ProjectDialog
#-------------------------------------------------------------------------------
# PLCOpenEditor Main Class
#-------------------------------------------------------------------------------
# Define PLCOpenEditor FileMenu extra items id
[ID_PLCOPENEDITORFILEMENUGENERATE,
] = [wx.NewId() for _init_coll_FileMenu_Items in range(1)]
class PLCOpenEditor(IDEFrame):
# Compatibility function for wx versions < 2.6
if wx.VERSION < (2, 6, 0):
def Bind(self, event, function, id = None):
event(self, id, function)
def _init_coll_FileMenu_Items(self, parent):
AppendMenu(parent, help='', id=wx.ID_NEW,
kind=wx.ITEM_NORMAL, text=_(u'New') +'\tCTRL+N')
AppendMenu(parent, help='', id=wx.ID_OPEN,
kind=wx.ITEM_NORMAL, text=_(u'Open') + '\tCTRL+O')
AppendMenu(parent, help='', id=wx.ID_CLOSE,
kind=wx.ITEM_NORMAL, text=_(u'Close Tab') + '\tCTRL+W')
AppendMenu(parent, help='', id=wx.ID_CLOSE_ALL,
kind=wx.ITEM_NORMAL, text=_(u'Close Project') + '\tCTRL+SHIFT+W')
AppendMenu(parent, help='', id=wx.ID_SAVE,
kind=wx.ITEM_NORMAL, text=_(u'Save') + '\tCTRL+S')
AppendMenu(parent, help='', id=wx.ID_SAVEAS,
kind=wx.ITEM_NORMAL, text=_(u'Save As...') + '\tCTRL+SHIFT+S')
AppendMenu(parent, help='', id=ID_PLCOPENEDITORFILEMENUGENERATE,
kind=wx.ITEM_NORMAL, text=_(u'Generate Program') + '\tCTRL+G')
AppendMenu(parent, help='', id=wx.ID_PAGE_SETUP,
kind=wx.ITEM_NORMAL, text=_(u'Page Setup') + '\tCTRL+ALT+P')
AppendMenu(parent, help='', id=wx.ID_PREVIEW,
kind=wx.ITEM_NORMAL, text=_(u'Preview') + '\tCTRL+SHIFT+P')
AppendMenu(parent, help='', id=wx.ID_PRINT,
kind=wx.ITEM_NORMAL, text=_(u'Print') + '\tCTRL+P')
AppendMenu(parent, help='', id=wx.ID_PROPERTIES,
kind=wx.ITEM_NORMAL, text=_(u'&Properties'))
AppendMenu(parent, help='', id=wx.ID_EXIT,
kind=wx.ITEM_NORMAL, text=_(u'Quit') + '\tCTRL+Q')
self.Bind(wx.EVT_MENU, self.OnNewProjectMenu, id=wx.ID_NEW)
self.Bind(wx.EVT_MENU, self.OnOpenProjectMenu, id=wx.ID_OPEN)
self.Bind(wx.EVT_MENU, self.OnCloseTabMenu, id=wx.ID_CLOSE)
self.Bind(wx.EVT_MENU, self.OnCloseProjectMenu, id=wx.ID_CLOSE_ALL)
self.Bind(wx.EVT_MENU, self.OnSaveProjectMenu, id=wx.ID_SAVE)
self.Bind(wx.EVT_MENU, self.OnSaveProjectAsMenu, id=wx.ID_SAVEAS)
self.Bind(wx.EVT_MENU, self.OnGenerateProgramMenu,
id=ID_PLCOPENEDITORFILEMENUGENERATE)
self.Bind(wx.EVT_MENU, self.OnPageSetupMenu, id=wx.ID_PAGE_SETUP)
self.Bind(wx.EVT_MENU, self.OnPreviewMenu, id=wx.ID_PREVIEW)
self.Bind(wx.EVT_MENU, self.OnPrintMenu, id=wx.ID_PRINT)
self.Bind(wx.EVT_MENU, self.OnPropertiesMenu, id=wx.ID_PROPERTIES)
self.Bind(wx.EVT_MENU, self.OnQuitMenu, id=wx.ID_EXIT)
self.AddToMenuToolBar([(wx.ID_NEW, "new", _(u'New'), None),
(wx.ID_OPEN, "open", _(u'Open'), None),
(wx.ID_SAVE, "save", _(u'Save'), None),
(wx.ID_SAVEAS, "saveas", _(u'Save As...'), None),
(wx.ID_PRINT, "print", _(u'Print'), None)])
def _init_coll_HelpMenu_Items(self, parent):
AppendMenu(parent, help='', id=wx.ID_HELP,
kind=wx.ITEM_NORMAL, text=_(u'PLCOpenEditor') + '\tF1')
#AppendMenu(parent, help='', id=wx.ID_HELP_CONTENTS,
# kind=wx.ITEM_NORMAL, text=u'PLCOpen\tF2')
#AppendMenu(parent, help='', id=wx.ID_HELP_CONTEXT,
# kind=wx.ITEM_NORMAL, text=u'IEC 61131-3\tF3')
AppendMenu(parent, help='', id=wx.ID_ABOUT,
kind=wx.ITEM_NORMAL, text=_(u'About'))
self.Bind(wx.EVT_MENU, self.OnPLCOpenEditorMenu, id=wx.ID_HELP)
#self.Bind(wx.EVT_MENU, self.OnPLCOpenMenu, id=wx.ID_HELP_CONTENTS)
self.Bind(wx.EVT_MENU, self.OnAboutMenu, id=wx.ID_ABOUT)
## Constructor of the PLCOpenEditor class.
# @param parent The parent window.
# @param controler The controler been used by PLCOpenEditor (default: None).
# @param fileOpen The filepath to open if no controler defined (default: None).
# @param debug The filepath to open if no controler defined (default: False).
def __init__(self, parent, fileOpen = None):
self.icon = wx.Icon(os.path.join(beremiz_dir, "images", "poe.ico"), wx.BITMAP_TYPE_ICO)
IDEFrame.__init__(self, parent)
# Open the filepath if defined
fileOpen = DecodeFileSystemPath(fileOpen, False)
if os.path.isfile(fileOpen):
# Create a new controller
controler = PLCControler()
result = controler.OpenXMLFile(fileOpen)
self.Controler = controler
self.LibraryPanel.SetController(controler)
self.ProjectTree.Enable(True)
self.PouInstanceVariablesPanel.SetController(controler)
self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE)
# Define PLCOpenEditor icon
self.Bind(wx.EVT_CLOSE, self.OnCloseFrame)
self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU)
_("PLC syntax error at line %d:\n%s") % result)
def OnCloseFrame(self, event):
if self.Controler is None or self.CheckSaveBeforeClosing(_("Close Application")):
name = _("PLCOpenEditor")
if self.Controler is not None:
self.SetTitle("%s - %s"%(name, self.Controler.GetFilename()))
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
def RefreshFileMenu(self):
MenuToolBar = self.Panes["MenuToolBar"]
if self.Controler is not None:
selected = self.TabsOpened.GetSelection()
graphic_viewer = isinstance(self.TabsOpened.GetPage(selected), Viewer)
if self.TabsOpened.GetPageCount() > 0:
self.FileMenu.Enable(wx.ID_CLOSE, True)
self.FileMenu.Enable(wx.ID_PREVIEW, True)
self.FileMenu.Enable(wx.ID_PRINT, True)
MenuToolBar.EnableTool(wx.ID_PRINT, True)
self.FileMenu.Enable(wx.ID_PREVIEW, False)
self.FileMenu.Enable(wx.ID_PRINT, False)
MenuToolBar.EnableTool(wx.ID_PRINT, False)
self.FileMenu.Enable(wx.ID_CLOSE, False)
self.FileMenu.Enable(wx.ID_PREVIEW, False)
self.FileMenu.Enable(wx.ID_PRINT, False)
MenuToolBar.EnableTool(wx.ID_PRINT, False)
self.FileMenu.Enable(wx.ID_PAGE_SETUP, True)
project_modified = not self.Controler.ProjectIsSaved()
self.FileMenu.Enable(wx.ID_SAVE, project_modified)
MenuToolBar.EnableTool(wx.ID_SAVE, project_modified)
self.FileMenu.Enable(wx.ID_PROPERTIES, True)
self.FileMenu.Enable(wx.ID_CLOSE_ALL, True)
self.FileMenu.Enable(wx.ID_SAVEAS, True)
MenuToolBar.EnableTool(wx.ID_SAVEAS, True)
self.FileMenu.Enable(ID_PLCOPENEDITORFILEMENUGENERATE, True)
self.FileMenu.Enable(wx.ID_CLOSE, False)
self.FileMenu.Enable(wx.ID_PAGE_SETUP, False)
self.FileMenu.Enable(wx.ID_PREVIEW, False)
self.FileMenu.Enable(wx.ID_PRINT, False)
MenuToolBar.EnableTool(wx.ID_PRINT, False)
self.FileMenu.Enable(wx.ID_SAVE, False)
MenuToolBar.EnableTool(wx.ID_SAVE, False)
self.FileMenu.Enable(wx.ID_PROPERTIES, False)
self.FileMenu.Enable(wx.ID_CLOSE_ALL, False)
self.FileMenu.Enable(wx.ID_SAVEAS, False)
MenuToolBar.EnableTool(wx.ID_SAVEAS, False)
self.FileMenu.Enable(ID_PLCOPENEDITORFILEMENUGENERATE, False)
def OnNewProjectMenu(self, event):
if self.Controler is not None and not self.CheckSaveBeforeClosing():
dialog = ProjectDialog(self)
if dialog.ShowModal() == wx.ID_OK:
properties = dialog.GetValues()
self.Controler = PLCControler()
self.Controler.CreateNewProject(properties)
self.LibraryPanel.SetController(self.Controler)
self.ProjectTree.Enable(True)
self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL,
def OnOpenProjectMenu(self, event):
if self.Controler is not None and not self.CheckSaveBeforeClosing():
if self.Controler is not None:
filepath = self.Controler.GetFilePath()
directory = os.path.dirname(filepath)
dialog = wx.FileDialog(self, _("Choose a file"), directory, "", _("PLCOpen files (*.xml)|*.xml|All files|*.*"), wx.OPEN)
if dialog.ShowModal() == wx.ID_OK:
filepath = dialog.GetPath()
if os.path.isfile(filepath):
controler = PLCControler()
result = controler.OpenXMLFile(filepath)
self.Controler = controler
self.LibraryPanel.SetController(controler)
self.ProjectTree.Enable(True)
self.PouInstanceVariablesPanel.SetController(controler)
self._Refresh(PROJECTTREE, LIBRARYTREE)
self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU)
_("PLC syntax error at line %d:\n%s") % result)
def OnCloseProjectMenu(self, event):
if not self.CheckSaveBeforeClosing():
self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU)
def OnSaveProjectMenu(self, event):
def OnSaveProjectAsMenu(self, event):
def OnGenerateProgramMenu(self, event):
dialog = wx.FileDialog(self, _("Choose a file"), os.getcwd(), self.Controler.GetProgramFilePath(), _("ST files (*.st)|*.st|All files|*.*"), wx.SAVE|wx.CHANGE_DIR)
if dialog.ShowModal() == wx.ID_OK:
filepath = dialog.GetPath()
header, icon = _("Done"), wx.ICON_INFORMATION
if os.path.isdir(os.path.dirname(filepath)):
program, errors, warnings = self.Controler.GenerateProgram(filepath)
message_text += "".join([_("warning: %s\n") % warning for warning in warnings])
message_text += "".join([_("error: %s\n") % error for error in errors])
message_text += _("Can't generate program to file %s!")%filepath
header, icon = _("Error"), wx.ICON_ERROR
message_text += _("Program was successfully generated!")
message_text += _("\"%s\" is not a valid folder!")%os.path.dirname(filepath)
header, icon = _("Error"), wx.ICON_ERROR
message = wx.MessageDialog(self, message_text, header, wx.OK|icon)
def OnPLCOpenEditorMenu(self, event):
wx.MessageBox(_("No documentation available.\nComing soon."))
def OnPLCOpenMenu(self, event):
open_pdf(os.path.join(beremiz_dir, "plcopen", "TC6_XML_V101.pdf"))
def OnAboutMenu(self, event):
title= _("About PLCOpenEditor") + " " + version.app_version
OpenHtmlFrame(self, title, os.path.join(beremiz_dir, "doc", _("plcopen_about.html")), wx.Size(350, 350))
result = self.Controler.SaveXMLFile()
self._Refresh(TITLE, FILEMENU, PAGETITLES)
filepath = self.Controler.GetFilePath()
directory, filename = os.path.split(filepath)
directory, filename = os.getcwd(), "%(projectName)s.xml"%self.Controler.GetProjectProperties()
dialog = wx.FileDialog(self, _("Choose a file"), directory, filename, _("PLCOpen files (*.xml)|*.xml|All files|*.*"), wx.SAVE|wx.OVERWRITE_PROMPT)
if dialog.ShowModal() == wx.ID_OK:
filepath = dialog.GetPath()
if os.path.isdir(os.path.dirname(filepath)):
result = self.Controler.SaveXMLFile(filepath)
self.ShowErrorMessage(_("Can't save project to file %s!")%filepath)
self.ShowErrorMessage(_("\"%s\" is not a valid folder!")%os.path.dirname(filepath))
self._Refresh(TITLE, FILEMENU, PAGETITLES)
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
Max_Traceback_List_Size = 20
def Display_Exception_Dialog(e_type,e_value,e_tb):
for i,line in enumerate(traceback.extract_tb(e_tb)):
trcbck = " " + str(i+1) + _(". ")
if line[0].find(os.getcwd()) == -1:
trcbck += _("file : ") + str(line[0]) + _(", ")
trcbck += _("file : ") + str(line[0][len(os.getcwd()):]) + _(", ")
trcbck += _("line : ") + str(line[1]) + _(", ") + _("function : ") + str(line[2])
trcbck_lst.append(trcbck)
cap = wx.Window_GetCapture()
dlg = wx.SingleChoiceDialog(None,
Click OK to save an error report.
Please be kind enough to send this file to:
edouard.tisserant@gmail.com
str(e_type) + _(" : ") + str(e_value),
res = (dlg.ShowModal() == wx.ID_OK)
def Display_Error_Dialog(e_value):
message = wx.MessageDialog(None, str(e_value), _("Error"), wx.OK|wx.ICON_ERROR)
def get_last_traceback(tb):
def format_namespace(d, indent=' '):
return '\n'.join(['%s%s: %s' % (indent, k, repr(v)[:10000]) for k, v in d.iteritems()])
ignored_exceptions = [] # a problem with a line in a module is only reported once per session
def AddExceptHook(path, app_version='[No version]'):#, ignored_exceptions=[]):
def handle_exception(e_type, e_value, e_traceback):
traceback.print_exception(e_type, e_value, e_traceback) # this is very helpful when there's an exception in the rest of this func
last_tb = get_last_traceback(e_traceback)
ex = (last_tb.tb_frame.f_code.co_filename, last_tb.tb_frame.f_lineno)
if str(e_value).startswith("!!!"):
Display_Error_Dialog(e_value)
elif ex not in ignored_exceptions:
result = Display_Exception_Dialog(e_type,e_value,e_traceback)
ignored_exceptions.append(ex)
'app-title' : wx.GetApp().GetAppName(), # app_title
'app-version' : app_version,
'wx-version' : wx.VERSION_STRING,
'wx-platform' : wx.Platform,
'python-version' : platform.python_version(), #sys.version.split()[0],
'platform' : platform.platform(),
info['traceback'] = ''.join(traceback.format_tb(e_traceback)) + '%s: %s' % (e_type, e_value)
last_tb = get_last_traceback(e_traceback)
exception_locals = last_tb.tb_frame.f_locals # the locals at the level of the stack trace where the exception actually occurred
info['locals'] = format_namespace(exception_locals)
if 'self' in exception_locals:
info['self'] = format_namespace(exception_locals['self'].__dict__)
output = open(path+os.sep+"bug_report_"+info['date'].replace(':','-').replace(' ','_')+".txt",'w')
output.write(a+":\n"+str(info[a])+"\n\n")
#sys.excepthook = lambda *args: wx.CallAfter(handle_exception, *args)
sys.excepthook = handle_exception
if __name__ == '__main__':
if wx.VERSION < (3, 0, 0):
wx.InitAllImageHandlers()
# Install a exception handle for bug reports
AddExceptHook(os.getcwd(), version.app_version)
frame = PLCOpenEditor(None, fileOpen=fileOpen)