--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/PLCOpenEditor.py Sun Sep 09 23:05:01 2012 +0200
@@ -0,0 +1,590 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+#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 wx
+import os, sys, platform, time, traceback, getopt
+
+CWD = os.path.split(os.path.realpath(__file__))[0]
+
+__version__ = "$Revision: 1.130 $"
+
+if __name__ == '__main__':
+ # Usage message displayed when help request or when error detected in
+ # command line
+ def usage():
+ print "\nUsage of PLCOpenEditor.py :"
+ print "\n %s [Filepath]\n"%sys.argv[0]
+
+ # Parse options given to PLCOpenEditor in command line
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "h", ["help"])
+ except getopt.GetoptError:
+ # print help information and exit:
+ usage()
+ sys.exit(2)
+
+ # Extract if help has been requested
+ for o, a in opts:
+ if o in ("-h", "--help"):
+ usage()
+ sys.exit()
+
+ # Extract the optional filename to open
+ fileOpen = None
+ if len(args) > 1:
+ usage()
+ sys.exit()
+ elif len(args) == 1:
+ fileOpen = args[0]
+
+ # Create wxApp (Need to create App before internationalization because of
+ # Windows)
+ app = wx.PySimpleApp()
+
+from docutil import *
+
+from util.TranslationCatalogs import AddCatalog, locale
+from util.BitmapLibrary import AddBitmapFolder, GetBitmap
+
+AddCatalog(os.path.join(CWD, "locale"))
+AddBitmapFolder(os.path.join(CWD, "images"))
+
+if __name__ == '__main__':
+ # Import module for internationalization
+ import gettext
+ import __builtin__
+
+ __builtin__.__dict__['loc'] = locale
+ __builtin__.__dict__['_'] = wx.GetTranslation
+
+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
+
+#-------------------------------------------------------------------------------
+# 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):
+ if id is not None:
+ event(self, id, function)
+ else:
+ event(self, 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')
+ parent.AppendSeparator()
+ 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')
+ parent.AppendSeparator()
+ 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')
+ parent.AppendSeparator()
+ AppendMenu(parent, help='', id=wx.ID_PROPERTIES,
+ kind=wx.ITEM_NORMAL, text=_(u'&Properties'))
+ parent.AppendSeparator()
+ 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):
+ IDEFrame.__init__(self, parent)
+
+ result = None
+
+ # Open the filepath if defined
+ if fileOpen is not None:
+ fileOpen = DecodeFileSystemPath(fileOpen, False)
+ if os.path.isfile(fileOpen):
+ # Create a new controller
+ controler = PLCControler()
+ result = controler.OpenXMLFile(fileOpen)
+ if result is None:
+ self.Controler = controler
+ self.LibraryPanel.SetController(controler)
+ self.ProjectTree.Enable(True)
+ self.PouInstanceVariablesPanel.SetController(controler)
+ self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE)
+
+ # Define PLCOpenEditor icon
+ self.SetIcon(wx.Icon(os.path.join(CWD, "images", "poe.ico"),wx.BITMAP_TYPE_ICO))
+
+ self.Bind(wx.EVT_CLOSE, self.OnCloseFrame)
+
+ self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU)
+
+ if result is not None:
+ self.ShowErrorMessage(result)
+
+ def OnCloseFrame(self, event):
+ if self.Controler is None or self.CheckSaveBeforeClosing(_("Close Application")):
+ self.AUIManager.UnInit()
+
+ self.SaveLastState()
+
+ event.Skip()
+ else:
+ event.Veto()
+
+ def RefreshTitle(self):
+ name = _("PLCOpenEditor")
+ if self.Controler is not None:
+ self.SetTitle("%s - %s"%(name, self.Controler.GetFilename()))
+ else:
+ self.SetTitle(name)
+
+#-------------------------------------------------------------------------------
+# File Menu Functions
+#-------------------------------------------------------------------------------
+
+ def RefreshFileMenu(self):
+ MenuToolBar = self.Panes["MenuToolBar"]
+ if self.Controler is not None:
+ selected = self.TabsOpened.GetSelection()
+ if selected >= 0:
+ graphic_viewer = isinstance(self.TabsOpened.GetPage(selected), Viewer)
+ else:
+ graphic_viewer = False
+ if self.TabsOpened.GetPageCount() > 0:
+ self.FileMenu.Enable(wx.ID_CLOSE, True)
+ if graphic_viewer:
+ self.FileMenu.Enable(wx.ID_PREVIEW, True)
+ self.FileMenu.Enable(wx.ID_PRINT, True)
+ MenuToolBar.EnableTool(wx.ID_PRINT, True)
+ else:
+ self.FileMenu.Enable(wx.ID_PREVIEW, False)
+ self.FileMenu.Enable(wx.ID_PRINT, False)
+ MenuToolBar.EnableTool(wx.ID_PRINT, False)
+ else:
+ 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)
+ else:
+ 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():
+ return
+ dialog = ProjectDialog(self)
+ if dialog.ShowModal() == wx.ID_OK:
+ properties = dialog.GetValues()
+ self.ResetView()
+ self.Controler = PLCControler()
+ self.Controler.CreateNewProject(properties)
+ self.LibraryPanel.SetController(self.Controler)
+ self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL,
+ LIBRARYTREE)
+
+ def OnOpenProjectMenu(self, event):
+ if self.Controler is not None and not self.CheckSaveBeforeClosing():
+ return
+ filepath = ""
+ if self.Controler is not None:
+ filepath = self.Controler.GetFilePath()
+ if filepath != "":
+ directory = os.path.dirname(filepath)
+ else:
+ directory = os.getcwd()
+
+ result = None
+
+ 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):
+ self.ResetView()
+ controler = PLCControler()
+ result = controler.OpenXMLFile(filepath)
+ if result is None:
+ self.Controler = controler
+ self.LibraryPanel.SetController(controler)
+ self.ProjectTree.Enable(True)
+ self.PouInstanceVariablesPanel.SetController(controler)
+ self.LoadProjectLayout()
+ self._Refresh(PROJECTTREE, LIBRARYTREE)
+ self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU)
+ dialog.Destroy()
+
+ if result is not None:
+ self.ShowErrorMessage(result)
+
+ def OnCloseProjectMenu(self, event):
+ if not self.CheckSaveBeforeClosing():
+ return
+ self.SaveProjectLayout()
+ self.ResetView()
+ self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU)
+
+ def OnSaveProjectMenu(self, event):
+ self.SaveProject()
+
+ def OnSaveProjectAsMenu(self, event):
+ self.SaveProjectAs()
+
+ 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()
+ message_text = ""
+ 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") for warning in warnings])
+ if len(errors) > 0:
+ message_text += "".join([_("error: %s\n") for error in errors])
+ message_text += _("Can't generate program to file %s!")%filepath
+ header, icon = _("Error"), wx.ICON_ERROR
+ else:
+ message_text += _("Program was successfully generated!")
+ else:
+ 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)
+ message.ShowModal()
+ message.Destroy()
+ dialog.Destroy()
+
+ def OnPLCOpenEditorMenu(self, event):
+ wx.MessageBox(_("No documentation available.\nComing soon."))
+
+ def OnPLCOpenMenu(self, event):
+ open_pdf(os.path.join(CWD, "plcopen", "TC6_XML_V101.pdf"))
+
+ def OnAboutMenu(self, event):
+ OpenHtmlFrame(self,_("About PLCOpenEditor"), os.path.join(CWD, "doc", "plcopen_about.html"), wx.Size(350, 350))
+
+ def SaveProject(self):
+ result = self.Controler.SaveXMLFile()
+ if not result:
+ self.SaveProjectAs()
+ else:
+ self._Refresh(TITLE, FILEMENU, PAGETITLES)
+
+ def SaveProjectAs(self):
+ filepath = self.Controler.GetFilePath()
+ if filepath != "":
+ directory, filename = os.path.split(filepath)
+ else:
+ 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)
+ if not result:
+ self.ShowErrorMessage(_("Can't save project to file %s!")%filepath)
+ else:
+ self.ShowErrorMessage(_("\"%s\" is not a valid folder!")%os.path.dirname(filepath))
+ self._Refresh(TITLE, FILEMENU, PAGETITLES)
+ dialog.Destroy()
+
+#-------------------------------------------------------------------------------
+# Debug Variables Panel
+#-------------------------------------------------------------------------------
+
+#-------------------------------------------------------------------------------
+# Viewer Printout
+#-------------------------------------------------------------------------------
+
+UPPER_DIV = lambda x, y: (x / y) + {True : 0, False : 1}[(x % y) == 0]
+
+class GraphicPrintout(wx.Printout):
+ def __init__(self, viewer, page_size, margins, preview = False):
+ wx.Printout.__init__(self)
+ self.Viewer = viewer
+ self.PageSize = page_size
+ if self.PageSize[0] == 0 or self.PageSize[1] == 0:
+ self.PageSize = (1050, 1485)
+ self.Preview = preview
+ self.Margins = margins
+ self.FontSize = 5
+ self.TextMargin = 3
+
+ maxx, maxy = viewer.GetMaxSize()
+ self.PageGrid = (UPPER_DIV(maxx, self.PageSize[0]),
+ UPPER_DIV(maxy, self.PageSize[1]))
+
+ def GetPageNumber(self):
+ return self.PageGrid[0] * self.PageGrid[1]
+
+ def HasPage(self, page):
+ return page <= self.GetPageNumber()
+
+ def GetPageInfo(self):
+ page_number = self.GetPageNumber()
+ return (1, page_number, 1, page_number)
+
+ def OnBeginDocument(self, startPage, endPage):
+ dc = self.GetDC()
+ if not self.Preview and isinstance(dc, wx.PostScriptDC):
+ dc.SetResolution(720)
+ super(GraphicPrintout, self).OnBeginDocument(startPage, endPage)
+
+ def OnPrintPage(self, page):
+ dc = self.GetDC()
+ dc.SetUserScale(1.0, 1.0)
+ dc.SetDeviceOrigin(0, 0)
+ dc.printing = not self.Preview
+
+ # Get the size of the DC in pixels
+ ppiPrinterX, ppiPrinterY = self.GetPPIPrinter()
+ ppiScreenX, ppiScreenY = self.GetPPIScreen()
+ pw, ph = self.GetPageSizePixels()
+ dw, dh = dc.GetSizeTuple()
+ Xscale = (float(dw) * float(ppiPrinterX)) / (float(pw) * 25.4)
+ Yscale = (float(dh) * float(ppiPrinterY)) / (float(ph) * 25.4)
+
+ fontsize = self.FontSize * Yscale
+ text_margin = self.TextMargin * Yscale
+
+ margin_left = self.Margins[0].x * Xscale
+ margin_top = self.Margins[0].y * Yscale
+ area_width = dw - self.Margins[1].x * Xscale - margin_left
+ area_height = dh - self.Margins[1].y * Yscale - margin_top
+
+ dc.SetPen(MiterPen(wx.BLACK))
+ dc.SetBrush(wx.TRANSPARENT_BRUSH)
+ dc.DrawRectangle(margin_left, margin_top, area_width, area_height)
+
+ dc.SetFont(wx.Font(fontsize, wx.DEFAULT, wx.NORMAL, wx.NORMAL))
+ dc.SetTextForeground(wx.BLACK)
+ block_name = " - ".join(self.Viewer.GetTagName().split("::")[1:])
+ text_width, text_height = dc.GetTextExtent(block_name)
+ dc.DrawText(block_name, margin_left, margin_top - text_height - self.TextMargin)
+ dc.DrawText(_("Page: %d") % page, margin_left, margin_top + area_height + self.TextMargin)
+
+ # Calculate the position on the DC for centering the graphic
+ posX = area_width * ((page - 1) % self.PageGrid[0])
+ posY = area_height * ((page - 1) / self.PageGrid[0])
+
+ scaleX = float(area_width) / float(self.PageSize[0])
+ scaleY = float(area_height) / float(self.PageSize[1])
+ scale = min(scaleX, scaleY)
+
+ # Set the scale and origin
+ dc.SetDeviceOrigin(-posX + margin_left, -posY + margin_top)
+ dc.SetClippingRegion(posX, posY, self.PageSize[0] * scale, self.PageSize[1] * scale)
+ dc.SetUserScale(scale, scale)
+
+ #-------------------------------------------
+
+ self.Viewer.DoDrawing(dc, True)
+
+ return True
+
+#-------------------------------------------------------------------------------
+# Exception Handler
+#-------------------------------------------------------------------------------
+
+Max_Traceback_List_Size = 20
+
+def Display_Exception_Dialog(e_type,e_value,e_tb):
+ trcbck_lst = []
+ 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]) + _(", ")
+ else:
+ trcbck += _("file : ") + str(line[0][len(os.getcwd()):]) + _(", ")
+ trcbck += _("line : ") + str(line[1]) + _(", ") + _("function : ") + str(line[2])
+ trcbck_lst.append(trcbck)
+
+ # Allow clicking....
+ cap = wx.Window_GetCapture()
+ if cap:
+ cap.ReleaseMouse()
+
+ dlg = wx.SingleChoiceDialog(None,
+ _("""
+An error has occurred.
+
+Click OK to save an error report.
+
+Please be kind enough to send this file to:
+edouard.tisserant@gmail.com
+
+Error:
+""") +
+ str(e_type) + _(" : ") + str(e_value),
+ _("Error"),
+ trcbck_lst)
+ try:
+ res = (dlg.ShowModal() == wx.ID_OK)
+ finally:
+ dlg.Destroy()
+
+ return res
+
+def Display_Error_Dialog(e_value):
+ message = wx.MessageDialog(None, str(e_value), _("Error"), wx.OK|wx.ICON_ERROR)
+ message.ShowModal()
+ message.Destroy()
+
+def get_last_traceback(tb):
+ while tb.tb_next:
+ tb = tb.tb_next
+ return 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)
+ if result:
+ ignored_exceptions.append(ex)
+ info = {
+ '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(),
+ 'e-type' : e_type,
+ 'e-value' : e_value,
+ 'date' : time.ctime(),
+ 'cwd' : os.getcwd(),
+ }
+ if e_traceback:
+ 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')
+ lst = info.keys()
+ lst.sort()
+ for a in lst:
+ 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__':
+ wx.InitAllImageHandlers()
+
+ # Install a exception handle for bug reports
+ AddExceptHook(os.getcwd(),__version__)
+
+ frame = PLCOpenEditor(None, fileOpen=fileOpen)
+
+ frame.Show()
+ app.MainLoop()
+