Laurent@814: #!/usr/bin/env python Laurent@814: # -*- coding: utf-8 -*- Laurent@814: Laurent@814: #This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor Laurent@814: #based on the plcopen standard. Laurent@814: # Laurent@814: #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD Laurent@814: # Laurent@814: #See COPYING file for copyrights details. Laurent@814: # Laurent@814: #This library is free software; you can redistribute it and/or Laurent@814: #modify it under the terms of the GNU General Public Laurent@814: #License as published by the Free Software Foundation; either Laurent@814: #version 2.1 of the License, or (at your option) any later version. Laurent@814: # Laurent@814: #This library is distributed in the hope that it will be useful, Laurent@814: #but WITHOUT ANY WARRANTY; without even the implied warranty of Laurent@814: #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Laurent@814: #General Public License for more details. Laurent@814: # Laurent@814: #You should have received a copy of the GNU General Public Laurent@814: #License along with this library; if not, write to the Free Software Laurent@814: #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Laurent@814: Laurent@814: import wx Laurent@814: import os, sys, platform, time, traceback, getopt Laurent@814: Laurent@814: CWD = os.path.split(os.path.realpath(__file__))[0] Laurent@814: Laurent@814: from util.BitmapLibrary import AddBitmapFolder, GetBitmap Laurent@814: AddBitmapFolder(os.path.join(CWD, "images")) Laurent@814: Laurent@814: from docutil import * Laurent@814: Laurent@814: __version__ = "$Revision: 1.130 $" Laurent@814: Laurent@814: if __name__ == '__main__': Laurent@814: # Usage message displayed when help request or when error detected in Laurent@814: # command line Laurent@814: def usage(): Laurent@814: print "\nUsage of PLCOpenEditor.py :" Laurent@814: print "\n %s [Filepath]\n"%sys.argv[0] Laurent@814: Laurent@814: # Parse options given to PLCOpenEditor in command line Laurent@814: try: Laurent@814: opts, args = getopt.getopt(sys.argv[1:], "h", ["help"]) Laurent@814: except getopt.GetoptError: Laurent@814: # print help information and exit: Laurent@814: usage() Laurent@814: sys.exit(2) Laurent@814: Laurent@814: # Extract if help has been requested Laurent@814: for o, a in opts: Laurent@814: if o in ("-h", "--help"): Laurent@814: usage() Laurent@814: sys.exit() Laurent@814: Laurent@814: # Extract the optional filename to open Laurent@814: fileOpen = None Laurent@814: if len(args) > 1: Laurent@814: usage() Laurent@814: sys.exit() Laurent@814: elif len(args) == 1: Laurent@814: fileOpen = args[0] Laurent@814: Laurent@814: # Create wxApp (Need to create App before internationalization because of Laurent@814: # Windows) Laurent@814: app = wx.PySimpleApp() Laurent@814: Laurent@814: # Import module for internationalization Laurent@814: import gettext Laurent@814: import __builtin__ Laurent@814: Laurent@814: # Get folder containing translation files Laurent@814: localedir = os.path.join(CWD,"locale") Laurent@814: # Get the default language Laurent@814: langid = wx.LANGUAGE_DEFAULT Laurent@814: # Define translation domain (name of translation files) Laurent@814: domain = "Beremiz" Laurent@814: Laurent@814: # Define locale for wx Laurent@814: loc = __builtin__.__dict__.get('loc', None) Laurent@814: if loc is None: Laurent@814: test_loc = wx.Locale(langid) Laurent@814: test_loc.AddCatalogLookupPathPrefix(localedir) Laurent@814: if test_loc.AddCatalog(domain): Laurent@814: loc = wx.Locale(langid) Laurent@814: else: Laurent@814: loc = wx.Locale(wx.LANGUAGE_ENGLISH) Laurent@814: __builtin__.__dict__['loc'] = loc Laurent@814: # Define location for searching translation files Laurent@814: loc.AddCatalogLookupPathPrefix(localedir) Laurent@814: # Define locale domain Laurent@814: loc.AddCatalog(domain) Laurent@814: Laurent@814: if __name__ == '__main__': Laurent@814: __builtin__.__dict__['_'] = wx.GetTranslation Laurent@814: Laurent@814: from IDEFrame import IDEFrame, AppendMenu Laurent@814: from IDEFrame import TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE, PAGETITLES Laurent@814: from IDEFrame import EncodeFileSystemPath, DecodeFileSystemPath Laurent@814: from editors.Viewer import Viewer Laurent@814: from PLCControler import PLCControler Laurent@814: Laurent@814: #------------------------------------------------------------------------------- Laurent@814: # PLCOpenEditor Main Class Laurent@814: #------------------------------------------------------------------------------- Laurent@814: Laurent@814: # Define PLCOpenEditor FileMenu extra items id Laurent@814: [ID_PLCOPENEDITORFILEMENUGENERATE, Laurent@814: ] = [wx.NewId() for _init_coll_FileMenu_Items in range(1)] Laurent@814: Laurent@814: class PLCOpenEditor(IDEFrame): Laurent@814: Laurent@814: # Compatibility function for wx versions < 2.6 Laurent@814: if wx.VERSION < (2, 6, 0): Laurent@814: def Bind(self, event, function, id = None): Laurent@814: if id is not None: Laurent@814: event(self, id, function) Laurent@814: else: Laurent@814: event(self, function) Laurent@814: Laurent@814: def _init_coll_FileMenu_Items(self, parent): Laurent@814: AppendMenu(parent, help='', id=wx.ID_NEW, Laurent@814: kind=wx.ITEM_NORMAL, text=_(u'New') +'\tCTRL+N') Laurent@814: AppendMenu(parent, help='', id=wx.ID_OPEN, Laurent@814: kind=wx.ITEM_NORMAL, text=_(u'Open') + '\tCTRL+O') Laurent@814: AppendMenu(parent, help='', id=wx.ID_CLOSE, Laurent@814: kind=wx.ITEM_NORMAL, text=_(u'Close Tab') + '\tCTRL+W') Laurent@814: AppendMenu(parent, help='', id=wx.ID_CLOSE_ALL, Laurent@814: kind=wx.ITEM_NORMAL, text=_(u'Close Project') + '\tCTRL+SHIFT+W') Laurent@814: parent.AppendSeparator() Laurent@814: AppendMenu(parent, help='', id=wx.ID_SAVE, Laurent@814: kind=wx.ITEM_NORMAL, text=_(u'Save') + '\tCTRL+S') Laurent@814: AppendMenu(parent, help='', id=wx.ID_SAVEAS, Laurent@814: kind=wx.ITEM_NORMAL, text=_(u'Save As...') + '\tCTRL+SHIFT+S') Laurent@814: AppendMenu(parent, help='', id=ID_PLCOPENEDITORFILEMENUGENERATE, Laurent@814: kind=wx.ITEM_NORMAL, text=_(u'Generate Program') + '\tCTRL+G') Laurent@814: parent.AppendSeparator() Laurent@814: AppendMenu(parent, help='', id=wx.ID_PAGE_SETUP, Laurent@814: kind=wx.ITEM_NORMAL, text=_(u'Page Setup') + '\tCTRL+ALT+P') Laurent@814: AppendMenu(parent, help='', id=wx.ID_PREVIEW, Laurent@814: kind=wx.ITEM_NORMAL, text=_(u'Preview') + '\tCTRL+SHIFT+P') Laurent@814: AppendMenu(parent, help='', id=wx.ID_PRINT, Laurent@814: kind=wx.ITEM_NORMAL, text=_(u'Print') + '\tCTRL+P') Laurent@814: parent.AppendSeparator() Laurent@814: AppendMenu(parent, help='', id=wx.ID_PROPERTIES, Laurent@814: kind=wx.ITEM_NORMAL, text=_(u'&Properties')) Laurent@814: parent.AppendSeparator() Laurent@814: AppendMenu(parent, help='', id=wx.ID_EXIT, Laurent@814: kind=wx.ITEM_NORMAL, text=_(u'Quit') + '\tCTRL+Q') Laurent@814: 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, Laurent@814: 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) Laurent@814: 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)]) Laurent@814: Laurent@814: def _init_coll_HelpMenu_Items(self, parent): Laurent@814: AppendMenu(parent, help='', id=wx.ID_HELP, Laurent@814: kind=wx.ITEM_NORMAL, text=_(u'PLCOpenEditor') + '\tF1') Laurent@814: #AppendMenu(parent, help='', id=wx.ID_HELP_CONTENTS, Laurent@814: # kind=wx.ITEM_NORMAL, text=u'PLCOpen\tF2') Laurent@814: #AppendMenu(parent, help='', id=wx.ID_HELP_CONTEXT, Laurent@814: # kind=wx.ITEM_NORMAL, text=u'IEC 61131-3\tF3') Laurent@814: AppendMenu(parent, help='', id=wx.ID_ABOUT, Laurent@814: kind=wx.ITEM_NORMAL, text=_(u'About')) Laurent@814: self.Bind(wx.EVT_MENU, self.OnPLCOpenEditorMenu, id=wx.ID_HELP) Laurent@814: #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: Laurent@814: ## Constructor of the PLCOpenEditor class. Laurent@814: # @param parent The parent window. Laurent@814: # @param controler The controler been used by PLCOpenEditor (default: None). Laurent@814: # @param fileOpen The filepath to open if no controler defined (default: None). Laurent@814: # @param debug The filepath to open if no controler defined (default: False). Laurent@814: def __init__(self, parent, fileOpen = None): Laurent@814: IDEFrame.__init__(self, parent) Laurent@814: Laurent@814: result = None Laurent@814: 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@814: if result is None: Laurent@814: self.Controler = controler Laurent@814: self.LibraryPanel.SetController(controler) Laurent@814: self.ProjectTree.Enable(True) Laurent@814: self.PouInstanceVariablesPanel.SetController(controler) Laurent@814: self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) Laurent@814: Laurent@814: # Define PLCOpenEditor icon Laurent@814: self.SetIcon(wx.Icon(os.path.join(CWD, "images", "poe.ico"),wx.BITMAP_TYPE_ICO)) Laurent@814: Laurent@814: self.Bind(wx.EVT_CLOSE, self.OnCloseFrame) Laurent@814: Laurent@814: self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU) Laurent@814: Laurent@814: if result is not None: Laurent@814: self.ShowErrorMessage(result) 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() Laurent@814: Laurent@814: self.SaveLastState() Laurent@814: 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: Laurent@814: self.SetTitle("%s - %s"%(name, self.Controler.GetFilename())) Laurent@814: else: Laurent@814: self.SetTitle(name) Laurent@814: Laurent@814: #------------------------------------------------------------------------------- Laurent@814: # File Menu Functions Laurent@814: #------------------------------------------------------------------------------- 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@814: 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() Laurent@814: Laurent@814: result = None Laurent@814: 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@814: if result is None: Laurent@814: self.Controler = controler Laurent@814: self.LibraryPanel.SetController(controler) Laurent@814: self.ProjectTree.Enable(True) Laurent@814: self.PouInstanceVariablesPanel.SetController(controler) Laurent@814: self.LoadProjectLayout() Laurent@814: self._Refresh(PROJECTTREE, LIBRARYTREE) Laurent@814: self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU) Laurent@814: dialog.Destroy() Laurent@814: Laurent@814: if result is not None: Laurent@814: self.ShowErrorMessage(result) Laurent@814: Laurent@814: def OnCloseProjectMenu(self, event): Laurent@814: if not self.CheckSaveBeforeClosing(): Laurent@814: return Laurent@814: self.SaveProjectLayout() 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): Laurent@814: dialog = wx.FileDialog(self, _("Choose a file"), os.getcwd(), 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)): Laurent@814: program, errors, warnings = self.Controler.GenerateProgram(filepath) Laurent@814: message_text += "".join([_("warning: %s\n") for warning in warnings]) Laurent@814: if len(errors) > 0: Laurent@814: message_text += "".join([_("error: %s\n") for error in errors]) Laurent@814: 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: Laurent@814: message_text += _("\"%s\" is not a valid folder!")%os.path.dirname(filepath) Laurent@814: header, icon = _("Error"), wx.ICON_ERROR Laurent@814: 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.")) Laurent@814: Laurent@814: def OnPLCOpenMenu(self, event): Laurent@814: open_pdf(os.path.join(CWD, "plcopen", "TC6_XML_V101.pdf")) Laurent@814: Laurent@814: def OnAboutMenu(self, event): Laurent@814: OpenHtmlFrame(self,_("About PLCOpenEditor"), os.path.join(CWD, "doc", "plcopen_about.html"), wx.Size(350, 350)) 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) Laurent@814: 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: Laurent@814: directory, filename = os.getcwd(), "%(projectName)s.xml"%self.Controler.GetProjectProperties() Laurent@814: 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: Laurent@814: self.ShowErrorMessage(_("Can't save project to file %s!")%filepath) Laurent@814: else: Laurent@814: 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: Laurent@814: #------------------------------------------------------------------------------- Laurent@814: # Debug Variables Panel Laurent@814: #------------------------------------------------------------------------------- Laurent@814: Laurent@814: #------------------------------------------------------------------------------- Laurent@814: # Viewer Printout Laurent@814: #------------------------------------------------------------------------------- Laurent@814: Laurent@814: UPPER_DIV = lambda x, y: (x / y) + {True : 0, False : 1}[(x % y) == 0] Laurent@814: Laurent@814: class GraphicPrintout(wx.Printout): Laurent@814: def __init__(self, viewer, page_size, margins, preview = False): Laurent@814: wx.Printout.__init__(self) Laurent@814: self.Viewer = viewer Laurent@814: self.PageSize = page_size Laurent@814: if self.PageSize[0] == 0 or self.PageSize[1] == 0: Laurent@814: self.PageSize = (1050, 1485) Laurent@814: self.Preview = preview Laurent@814: self.Margins = margins Laurent@814: self.FontSize = 5 Laurent@814: self.TextMargin = 3 Laurent@814: Laurent@814: maxx, maxy = viewer.GetMaxSize() Laurent@814: self.PageGrid = (UPPER_DIV(maxx, self.PageSize[0]), Laurent@814: UPPER_DIV(maxy, self.PageSize[1])) Laurent@814: Laurent@814: def GetPageNumber(self): Laurent@814: return self.PageGrid[0] * self.PageGrid[1] Laurent@814: Laurent@814: def HasPage(self, page): Laurent@814: return page <= self.GetPageNumber() Laurent@814: Laurent@814: def GetPageInfo(self): Laurent@814: page_number = self.GetPageNumber() Laurent@814: return (1, page_number, 1, page_number) Laurent@814: Laurent@814: def OnBeginDocument(self, startPage, endPage): Laurent@814: dc = self.GetDC() Laurent@814: if not self.Preview and isinstance(dc, wx.PostScriptDC): Laurent@814: dc.SetResolution(720) Laurent@814: super(GraphicPrintout, self).OnBeginDocument(startPage, endPage) Laurent@814: Laurent@814: def OnPrintPage(self, page): Laurent@814: dc = self.GetDC() Laurent@814: dc.SetUserScale(1.0, 1.0) Laurent@814: dc.SetDeviceOrigin(0, 0) Laurent@814: dc.printing = not self.Preview Laurent@814: Laurent@814: # Get the size of the DC in pixels Laurent@814: ppiPrinterX, ppiPrinterY = self.GetPPIPrinter() Laurent@814: ppiScreenX, ppiScreenY = self.GetPPIScreen() Laurent@814: pw, ph = self.GetPageSizePixels() Laurent@814: dw, dh = dc.GetSizeTuple() Laurent@814: Xscale = (float(dw) * float(ppiPrinterX)) / (float(pw) * 25.4) Laurent@814: Yscale = (float(dh) * float(ppiPrinterY)) / (float(ph) * 25.4) Laurent@814: Laurent@814: fontsize = self.FontSize * Yscale Laurent@814: text_margin = self.TextMargin * Yscale Laurent@814: Laurent@814: margin_left = self.Margins[0].x * Xscale Laurent@814: margin_top = self.Margins[0].y * Yscale Laurent@814: area_width = dw - self.Margins[1].x * Xscale - margin_left Laurent@814: area_height = dh - self.Margins[1].y * Yscale - margin_top Laurent@814: Laurent@814: dc.SetPen(MiterPen(wx.BLACK)) Laurent@814: dc.SetBrush(wx.TRANSPARENT_BRUSH) Laurent@814: dc.DrawRectangle(margin_left, margin_top, area_width, area_height) Laurent@814: Laurent@814: dc.SetFont(wx.Font(fontsize, wx.DEFAULT, wx.NORMAL, wx.NORMAL)) Laurent@814: dc.SetTextForeground(wx.BLACK) Laurent@814: block_name = " - ".join(self.Viewer.GetTagName().split("::")[1:]) Laurent@814: text_width, text_height = dc.GetTextExtent(block_name) Laurent@814: dc.DrawText(block_name, margin_left, margin_top - text_height - self.TextMargin) Laurent@814: dc.DrawText(_("Page: %d") % page, margin_left, margin_top + area_height + self.TextMargin) Laurent@814: Laurent@814: # Calculate the position on the DC for centering the graphic Laurent@814: posX = area_width * ((page - 1) % self.PageGrid[0]) Laurent@814: posY = area_height * ((page - 1) / self.PageGrid[0]) Laurent@814: Laurent@814: scaleX = float(area_width) / float(self.PageSize[0]) Laurent@814: scaleY = float(area_height) / float(self.PageSize[1]) Laurent@814: scale = min(scaleX, scaleY) Laurent@814: Laurent@814: # Set the scale and origin Laurent@814: dc.SetDeviceOrigin(-posX + margin_left, -posY + margin_top) Laurent@814: dc.SetClippingRegion(posX, posY, self.PageSize[0] * scale, self.PageSize[1] * scale) Laurent@814: dc.SetUserScale(scale, scale) Laurent@814: Laurent@814: #------------------------------------------- Laurent@814: Laurent@814: self.Viewer.DoDrawing(dc, True) Laurent@814: Laurent@814: return True Laurent@814: Laurent@814: #------------------------------------------------------------------------------- Laurent@814: # Exception Handler Laurent@814: #------------------------------------------------------------------------------- Laurent@814: Laurent@814: Max_Traceback_List_Size = 20 Laurent@814: Laurent@814: def Display_Exception_Dialog(e_type,e_value,e_tb): Laurent@814: trcbck_lst = [] Laurent@814: for i,line in enumerate(traceback.extract_tb(e_tb)): Laurent@814: trcbck = " " + str(i+1) + _(". ") Laurent@814: if line[0].find(os.getcwd()) == -1: Laurent@814: trcbck += _("file : ") + str(line[0]) + _(", ") Laurent@814: else: Laurent@814: trcbck += _("file : ") + str(line[0][len(os.getcwd()):]) + _(", ") Laurent@814: trcbck += _("line : ") + str(line[1]) + _(", ") + _("function : ") + str(line[2]) Laurent@814: trcbck_lst.append(trcbck) Laurent@814: Laurent@814: # Allow clicking.... Laurent@814: cap = wx.Window_GetCapture() Laurent@814: if cap: Laurent@814: cap.ReleaseMouse() Laurent@814: Laurent@814: dlg = wx.SingleChoiceDialog(None, Laurent@814: _(""" Laurent@814: An error has occurred. Laurent@814: Laurent@814: Click OK to save an error report. Laurent@814: Laurent@814: Please be kind enough to send this file to: Laurent@814: edouard.tisserant@gmail.com Laurent@814: Laurent@814: Error: Laurent@814: """) + Laurent@814: str(e_type) + _(" : ") + str(e_value), Laurent@814: _("Error"), Laurent@814: trcbck_lst) Laurent@814: try: Laurent@814: res = (dlg.ShowModal() == wx.ID_OK) Laurent@814: finally: Laurent@814: dlg.Destroy() Laurent@814: Laurent@814: return res Laurent@814: Laurent@814: def Display_Error_Dialog(e_value): Laurent@814: message = wx.MessageDialog(None, str(e_value), _("Error"), wx.OK|wx.ICON_ERROR) Laurent@814: message.ShowModal() Laurent@814: message.Destroy() Laurent@814: Laurent@814: def get_last_traceback(tb): Laurent@814: while tb.tb_next: Laurent@814: tb = tb.tb_next Laurent@814: return tb Laurent@814: Laurent@814: Laurent@814: def format_namespace(d, indent=' '): Laurent@814: return '\n'.join(['%s%s: %s' % (indent, k, repr(v)[:10000]) for k, v in d.iteritems()]) Laurent@814: Laurent@814: Laurent@814: ignored_exceptions = [] # a problem with a line in a module is only reported once per session Laurent@814: Laurent@814: def AddExceptHook(path, app_version='[No version]'):#, ignored_exceptions=[]): Laurent@814: Laurent@814: def handle_exception(e_type, e_value, e_traceback): Laurent@814: traceback.print_exception(e_type, e_value, e_traceback) # this is very helpful when there's an exception in the rest of this func Laurent@814: last_tb = get_last_traceback(e_traceback) Laurent@814: ex = (last_tb.tb_frame.f_code.co_filename, last_tb.tb_frame.f_lineno) Laurent@814: if str(e_value).startswith("!!!"): Laurent@814: Display_Error_Dialog(e_value) Laurent@814: elif ex not in ignored_exceptions: Laurent@814: result = Display_Exception_Dialog(e_type,e_value,e_traceback) Laurent@814: if result: Laurent@814: ignored_exceptions.append(ex) Laurent@814: info = { Laurent@814: 'app-title' : wx.GetApp().GetAppName(), # app_title Laurent@814: 'app-version' : app_version, Laurent@814: 'wx-version' : wx.VERSION_STRING, Laurent@814: 'wx-platform' : wx.Platform, Laurent@814: 'python-version' : platform.python_version(), #sys.version.split()[0], Laurent@814: 'platform' : platform.platform(), Laurent@814: 'e-type' : e_type, Laurent@814: 'e-value' : e_value, Laurent@814: 'date' : time.ctime(), Laurent@814: 'cwd' : os.getcwd(), Laurent@814: } Laurent@814: if e_traceback: Laurent@814: info['traceback'] = ''.join(traceback.format_tb(e_traceback)) + '%s: %s' % (e_type, e_value) Laurent@814: last_tb = get_last_traceback(e_traceback) Laurent@814: exception_locals = last_tb.tb_frame.f_locals # the locals at the level of the stack trace where the exception actually occurred Laurent@814: info['locals'] = format_namespace(exception_locals) Laurent@814: if 'self' in exception_locals: Laurent@814: info['self'] = format_namespace(exception_locals['self'].__dict__) Laurent@814: Laurent@814: output = open(path+os.sep+"bug_report_"+info['date'].replace(':','-').replace(' ','_')+".txt",'w') Laurent@814: lst = info.keys() Laurent@814: lst.sort() Laurent@814: for a in lst: Laurent@814: output.write(a+":\n"+str(info[a])+"\n\n") Laurent@814: Laurent@814: #sys.excepthook = lambda *args: wx.CallAfter(handle_exception, *args) Laurent@814: sys.excepthook = handle_exception Laurent@814: Laurent@814: if __name__ == '__main__': Laurent@814: wx.InitAllImageHandlers() Laurent@814: Laurent@814: # Install a exception handle for bug reports Laurent@814: AddExceptHook(os.getcwd(),__version__) Laurent@814: Laurent@814: frame = PLCOpenEditor(None, fileOpen=fileOpen) Laurent@814: Laurent@814: frame.Show() Laurent@814: app.MainLoop() Laurent@814: