PLCOpenEditor.py
changeset 814 5743cbdff669
child 815 e4f24593a758
equal deleted inserted replaced
813:1460273f40ed 814:5743cbdff669
       
     1 #!/usr/bin/env python
       
     2 # -*- coding: utf-8 -*-
       
     3 
       
     4 #This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
       
     5 #based on the plcopen standard. 
       
     6 #
       
     7 #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
       
     8 #
       
     9 #See COPYING file for copyrights details.
       
    10 #
       
    11 #This library is free software; you can redistribute it and/or
       
    12 #modify it under the terms of the GNU General Public
       
    13 #License as published by the Free Software Foundation; either
       
    14 #version 2.1 of the License, or (at your option) any later version.
       
    15 #
       
    16 #This library is distributed in the hope that it will be useful,
       
    17 #but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    18 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    19 #General Public License for more details.
       
    20 #
       
    21 #You should have received a copy of the GNU General Public
       
    22 #License along with this library; if not, write to the Free Software
       
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
       
    24 
       
    25 import wx
       
    26 import os, sys, platform, time, traceback, getopt
       
    27 
       
    28 CWD = os.path.split(os.path.realpath(__file__))[0]
       
    29 
       
    30 from util.BitmapLibrary import AddBitmapFolder, GetBitmap
       
    31 AddBitmapFolder(os.path.join(CWD, "images"))
       
    32 
       
    33 from docutil import *
       
    34 
       
    35 __version__ = "$Revision: 1.130 $"
       
    36 
       
    37 if __name__ == '__main__':
       
    38     # Usage message displayed when help request or when error detected in 
       
    39     # command line
       
    40     def usage():
       
    41         print "\nUsage of PLCOpenEditor.py :"
       
    42         print "\n   %s [Filepath]\n"%sys.argv[0]
       
    43 
       
    44     # Parse options given to PLCOpenEditor in command line
       
    45     try:
       
    46         opts, args = getopt.getopt(sys.argv[1:], "h", ["help"])
       
    47     except getopt.GetoptError:
       
    48         # print help information and exit:
       
    49         usage()
       
    50         sys.exit(2)
       
    51     
       
    52     # Extract if help has been requested
       
    53     for o, a in opts:
       
    54         if o in ("-h", "--help"):
       
    55             usage()
       
    56             sys.exit()
       
    57     
       
    58     # Extract the optional filename to open
       
    59     fileOpen = None
       
    60     if len(args) > 1:
       
    61         usage()
       
    62         sys.exit()
       
    63     elif len(args) == 1:
       
    64         fileOpen = args[0]
       
    65     
       
    66     # Create wxApp (Need to create App before internationalization because of
       
    67     # Windows) 
       
    68     app = wx.PySimpleApp()
       
    69 
       
    70 # Import module for internationalization
       
    71 import gettext
       
    72 import __builtin__
       
    73 
       
    74 # Get folder containing translation files
       
    75 localedir = os.path.join(CWD,"locale")
       
    76 # Get the default language
       
    77 langid = wx.LANGUAGE_DEFAULT
       
    78 # Define translation domain (name of translation files)
       
    79 domain = "Beremiz"
       
    80 
       
    81 # Define locale for wx
       
    82 loc = __builtin__.__dict__.get('loc', None)
       
    83 if loc is None:
       
    84     test_loc = wx.Locale(langid)
       
    85     test_loc.AddCatalogLookupPathPrefix(localedir)
       
    86     if test_loc.AddCatalog(domain):
       
    87         loc = wx.Locale(langid)
       
    88     else:
       
    89         loc = wx.Locale(wx.LANGUAGE_ENGLISH)
       
    90     __builtin__.__dict__['loc'] = loc
       
    91 # Define location for searching translation files
       
    92 loc.AddCatalogLookupPathPrefix(localedir)
       
    93 # Define locale domain
       
    94 loc.AddCatalog(domain)
       
    95 
       
    96 if __name__ == '__main__':
       
    97     __builtin__.__dict__['_'] = wx.GetTranslation
       
    98 
       
    99 from IDEFrame import IDEFrame, AppendMenu
       
   100 from IDEFrame import TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE, PAGETITLES
       
   101 from IDEFrame import EncodeFileSystemPath, DecodeFileSystemPath
       
   102 from editors.Viewer import Viewer
       
   103 from PLCControler import PLCControler
       
   104 
       
   105 #-------------------------------------------------------------------------------
       
   106 #                            PLCOpenEditor Main Class
       
   107 #-------------------------------------------------------------------------------
       
   108 
       
   109 # Define PLCOpenEditor FileMenu extra items id
       
   110 [ID_PLCOPENEDITORFILEMENUGENERATE, 
       
   111 ] = [wx.NewId() for _init_coll_FileMenu_Items in range(1)]
       
   112 
       
   113 class PLCOpenEditor(IDEFrame):
       
   114 
       
   115     # Compatibility function for wx versions < 2.6
       
   116     if wx.VERSION < (2, 6, 0):
       
   117         def Bind(self, event, function, id = None):
       
   118             if id is not None:
       
   119                 event(self, id, function)
       
   120             else:
       
   121                 event(self, function)
       
   122 
       
   123     def _init_coll_FileMenu_Items(self, parent):
       
   124         AppendMenu(parent, help='', id=wx.ID_NEW,
       
   125               kind=wx.ITEM_NORMAL, text=_(u'New') +'\tCTRL+N')
       
   126         AppendMenu(parent, help='', id=wx.ID_OPEN,
       
   127               kind=wx.ITEM_NORMAL, text=_(u'Open') + '\tCTRL+O')
       
   128         AppendMenu(parent, help='', id=wx.ID_CLOSE,
       
   129               kind=wx.ITEM_NORMAL, text=_(u'Close Tab') + '\tCTRL+W')
       
   130         AppendMenu(parent, help='', id=wx.ID_CLOSE_ALL,
       
   131               kind=wx.ITEM_NORMAL, text=_(u'Close Project') + '\tCTRL+SHIFT+W')
       
   132         parent.AppendSeparator()
       
   133         AppendMenu(parent, help='', id=wx.ID_SAVE,
       
   134               kind=wx.ITEM_NORMAL, text=_(u'Save') + '\tCTRL+S')
       
   135         AppendMenu(parent, help='', id=wx.ID_SAVEAS,
       
   136               kind=wx.ITEM_NORMAL, text=_(u'Save As...') + '\tCTRL+SHIFT+S')
       
   137         AppendMenu(parent, help='', id=ID_PLCOPENEDITORFILEMENUGENERATE,
       
   138               kind=wx.ITEM_NORMAL, text=_(u'Generate Program') + '\tCTRL+G')
       
   139         parent.AppendSeparator()
       
   140         AppendMenu(parent, help='', id=wx.ID_PAGE_SETUP,
       
   141               kind=wx.ITEM_NORMAL, text=_(u'Page Setup') + '\tCTRL+ALT+P')
       
   142         AppendMenu(parent, help='', id=wx.ID_PREVIEW,
       
   143               kind=wx.ITEM_NORMAL, text=_(u'Preview') + '\tCTRL+SHIFT+P')
       
   144         AppendMenu(parent, help='', id=wx.ID_PRINT,
       
   145               kind=wx.ITEM_NORMAL, text=_(u'Print') + '\tCTRL+P')
       
   146         parent.AppendSeparator()
       
   147         AppendMenu(parent, help='', id=wx.ID_PROPERTIES,
       
   148               kind=wx.ITEM_NORMAL, text=_(u'&Properties'))
       
   149         parent.AppendSeparator()
       
   150         AppendMenu(parent, help='', id=wx.ID_EXIT,
       
   151               kind=wx.ITEM_NORMAL, text=_(u'Quit') + '\tCTRL+Q')
       
   152         
       
   153         self.Bind(wx.EVT_MENU, self.OnNewProjectMenu, id=wx.ID_NEW)
       
   154         self.Bind(wx.EVT_MENU, self.OnOpenProjectMenu, id=wx.ID_OPEN)
       
   155         self.Bind(wx.EVT_MENU, self.OnCloseTabMenu, id=wx.ID_CLOSE)
       
   156         self.Bind(wx.EVT_MENU, self.OnCloseProjectMenu, id=wx.ID_CLOSE_ALL)
       
   157         self.Bind(wx.EVT_MENU, self.OnSaveProjectMenu, id=wx.ID_SAVE)
       
   158         self.Bind(wx.EVT_MENU, self.OnSaveProjectAsMenu, id=wx.ID_SAVEAS)
       
   159         self.Bind(wx.EVT_MENU, self.OnGenerateProgramMenu,
       
   160               id=ID_PLCOPENEDITORFILEMENUGENERATE)
       
   161         self.Bind(wx.EVT_MENU, self.OnPageSetupMenu, id=wx.ID_PAGE_SETUP)
       
   162         self.Bind(wx.EVT_MENU, self.OnPreviewMenu, id=wx.ID_PREVIEW)
       
   163         self.Bind(wx.EVT_MENU, self.OnPrintMenu, id=wx.ID_PRINT)
       
   164         self.Bind(wx.EVT_MENU, self.OnPropertiesMenu, id=wx.ID_PROPERTIES)
       
   165         self.Bind(wx.EVT_MENU, self.OnQuitMenu, id=wx.ID_EXIT)
       
   166         
       
   167         self.AddToMenuToolBar([(wx.ID_NEW, "new", _(u'New'), None),
       
   168                                (wx.ID_OPEN, "open", _(u'Open'), None),
       
   169                                (wx.ID_SAVE, "save", _(u'Save'), None),
       
   170                                (wx.ID_SAVEAS, "saveas", _(u'Save As...'), None),
       
   171                                (wx.ID_PRINT, "print", _(u'Print'), None)])
       
   172             
       
   173     def _init_coll_HelpMenu_Items(self, parent):
       
   174         AppendMenu(parent, help='', id=wx.ID_HELP, 
       
   175             kind=wx.ITEM_NORMAL, text=_(u'PLCOpenEditor') + '\tF1')
       
   176         #AppendMenu(parent, help='', id=wx.ID_HELP_CONTENTS,
       
   177         #      kind=wx.ITEM_NORMAL, text=u'PLCOpen\tF2')
       
   178         #AppendMenu(parent, help='', id=wx.ID_HELP_CONTEXT,
       
   179         #      kind=wx.ITEM_NORMAL, text=u'IEC 61131-3\tF3')
       
   180         AppendMenu(parent, help='', id=wx.ID_ABOUT,
       
   181             kind=wx.ITEM_NORMAL, text=_(u'About'))
       
   182         self.Bind(wx.EVT_MENU, self.OnPLCOpenEditorMenu, id=wx.ID_HELP)
       
   183         #self.Bind(wx.EVT_MENU, self.OnPLCOpenMenu, id=wx.ID_HELP_CONTENTS)
       
   184         self.Bind(wx.EVT_MENU, self.OnAboutMenu, id=wx.ID_ABOUT)
       
   185 
       
   186     ## Constructor of the PLCOpenEditor class.
       
   187     #  @param parent The parent window.
       
   188     #  @param controler The controler been used by PLCOpenEditor (default: None).
       
   189     #  @param fileOpen The filepath to open if no controler defined (default: None).
       
   190     #  @param debug The filepath to open if no controler defined (default: False).
       
   191     def __init__(self, parent, fileOpen = None):
       
   192         IDEFrame.__init__(self, parent)
       
   193         
       
   194         result = None
       
   195         
       
   196         # Open the filepath if defined
       
   197         if fileOpen is not None:
       
   198             fileOpen = DecodeFileSystemPath(fileOpen, False)
       
   199             if os.path.isfile(fileOpen):
       
   200                 # Create a new controller
       
   201                 controler = PLCControler()
       
   202                 result = controler.OpenXMLFile(fileOpen)
       
   203                 if result is None:
       
   204                     self.Controler = controler
       
   205                     self.LibraryPanel.SetController(controler)
       
   206                     self.ProjectTree.Enable(True)
       
   207                     self.PouInstanceVariablesPanel.SetController(controler)
       
   208                     self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE)
       
   209         
       
   210         # Define PLCOpenEditor icon
       
   211         self.SetIcon(wx.Icon(os.path.join(CWD, "images", "poe.ico"),wx.BITMAP_TYPE_ICO))
       
   212 
       
   213         self.Bind(wx.EVT_CLOSE, self.OnCloseFrame)
       
   214         
       
   215         self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU)
       
   216         
       
   217         if result is not None:
       
   218             self.ShowErrorMessage(result)
       
   219 
       
   220     def OnCloseFrame(self, event):
       
   221         if self.Controler is None or self.CheckSaveBeforeClosing(_("Close Application")):
       
   222             self.AUIManager.UnInit()
       
   223             
       
   224             self.SaveLastState()
       
   225             
       
   226             event.Skip()
       
   227         else:
       
   228             event.Veto()
       
   229 
       
   230     def RefreshTitle(self):
       
   231         name = _("PLCOpenEditor")
       
   232         if self.Controler is not None:
       
   233             self.SetTitle("%s - %s"%(name, self.Controler.GetFilename()))
       
   234         else:
       
   235             self.SetTitle(name)
       
   236 
       
   237 #-------------------------------------------------------------------------------
       
   238 #                            File Menu Functions
       
   239 #-------------------------------------------------------------------------------
       
   240 
       
   241     def RefreshFileMenu(self):
       
   242         MenuToolBar = self.Panes["MenuToolBar"]
       
   243         if self.Controler is not None:
       
   244             selected = self.TabsOpened.GetSelection()
       
   245             if selected >= 0:
       
   246                 graphic_viewer = isinstance(self.TabsOpened.GetPage(selected), Viewer)
       
   247             else:
       
   248                 graphic_viewer = False
       
   249             if self.TabsOpened.GetPageCount() > 0:
       
   250                 self.FileMenu.Enable(wx.ID_CLOSE, True)
       
   251                 if graphic_viewer:
       
   252                     self.FileMenu.Enable(wx.ID_PREVIEW, True)
       
   253                     self.FileMenu.Enable(wx.ID_PRINT, True)
       
   254                     MenuToolBar.EnableTool(wx.ID_PRINT, True)
       
   255                 else:
       
   256                     self.FileMenu.Enable(wx.ID_PREVIEW, False)
       
   257                     self.FileMenu.Enable(wx.ID_PRINT, False)
       
   258                     MenuToolBar.EnableTool(wx.ID_PRINT, False)
       
   259             else:
       
   260                 self.FileMenu.Enable(wx.ID_CLOSE, False)
       
   261                 self.FileMenu.Enable(wx.ID_PREVIEW, False)
       
   262                 self.FileMenu.Enable(wx.ID_PRINT, False)
       
   263                 MenuToolBar.EnableTool(wx.ID_PRINT, False)
       
   264             self.FileMenu.Enable(wx.ID_PAGE_SETUP, True)
       
   265             project_modified = not self.Controler.ProjectIsSaved()
       
   266             self.FileMenu.Enable(wx.ID_SAVE, project_modified)
       
   267             MenuToolBar.EnableTool(wx.ID_SAVE, project_modified)
       
   268             self.FileMenu.Enable(wx.ID_PROPERTIES, True)
       
   269             self.FileMenu.Enable(wx.ID_CLOSE_ALL, True)
       
   270             self.FileMenu.Enable(wx.ID_SAVEAS, True)
       
   271             MenuToolBar.EnableTool(wx.ID_SAVEAS, True)
       
   272             self.FileMenu.Enable(ID_PLCOPENEDITORFILEMENUGENERATE, True)
       
   273         else:
       
   274             self.FileMenu.Enable(wx.ID_CLOSE, False)
       
   275             self.FileMenu.Enable(wx.ID_PAGE_SETUP, False)
       
   276             self.FileMenu.Enable(wx.ID_PREVIEW, False)
       
   277             self.FileMenu.Enable(wx.ID_PRINT, False)
       
   278             MenuToolBar.EnableTool(wx.ID_PRINT, False)
       
   279             self.FileMenu.Enable(wx.ID_SAVE, False)
       
   280             MenuToolBar.EnableTool(wx.ID_SAVE, False)
       
   281             self.FileMenu.Enable(wx.ID_PROPERTIES, False)
       
   282             self.FileMenu.Enable(wx.ID_CLOSE_ALL, False)
       
   283             self.FileMenu.Enable(wx.ID_SAVEAS, False)
       
   284             MenuToolBar.EnableTool(wx.ID_SAVEAS, False)
       
   285             self.FileMenu.Enable(ID_PLCOPENEDITORFILEMENUGENERATE, False)
       
   286 
       
   287     def OnNewProjectMenu(self, event):
       
   288         if self.Controler is not None and not self.CheckSaveBeforeClosing():
       
   289             return
       
   290         dialog = ProjectDialog(self)
       
   291         if dialog.ShowModal() == wx.ID_OK:
       
   292             properties = dialog.GetValues()
       
   293             self.ResetView()
       
   294             self.Controler = PLCControler()
       
   295             self.Controler.CreateNewProject(properties)
       
   296             self.LibraryPanel.SetController(self.Controler)
       
   297             self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL, 
       
   298                           LIBRARYTREE)
       
   299 
       
   300     def OnOpenProjectMenu(self, event):
       
   301         if self.Controler is not None and not self.CheckSaveBeforeClosing():
       
   302             return
       
   303         filepath = ""
       
   304         if self.Controler is not None:
       
   305             filepath = self.Controler.GetFilePath()
       
   306         if filepath != "":
       
   307             directory = os.path.dirname(filepath)
       
   308         else:
       
   309             directory = os.getcwd()
       
   310         
       
   311         result = None
       
   312         
       
   313         dialog = wx.FileDialog(self, _("Choose a file"), directory, "",  _("PLCOpen files (*.xml)|*.xml|All files|*.*"), wx.OPEN)
       
   314         if dialog.ShowModal() == wx.ID_OK:
       
   315             filepath = dialog.GetPath()
       
   316             if os.path.isfile(filepath):
       
   317                 self.ResetView()
       
   318                 controler = PLCControler()
       
   319                 result = controler.OpenXMLFile(filepath)
       
   320                 if result is None:
       
   321                     self.Controler = controler
       
   322                     self.LibraryPanel.SetController(controler)
       
   323                     self.ProjectTree.Enable(True)
       
   324                     self.PouInstanceVariablesPanel.SetController(controler)
       
   325                     self.LoadProjectLayout()
       
   326                     self._Refresh(PROJECTTREE, LIBRARYTREE)
       
   327             self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU)
       
   328         dialog.Destroy()
       
   329         
       
   330         if result is not None:
       
   331             self.ShowErrorMessage(result)
       
   332     
       
   333     def OnCloseProjectMenu(self, event):
       
   334         if not self.CheckSaveBeforeClosing():
       
   335             return
       
   336         self.SaveProjectLayout()
       
   337         self.ResetView()
       
   338         self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU)
       
   339 
       
   340     def OnSaveProjectMenu(self, event):
       
   341         self.SaveProject()
       
   342 
       
   343     def OnSaveProjectAsMenu(self, event):
       
   344         self.SaveProjectAs()
       
   345 
       
   346     def OnGenerateProgramMenu(self, event):
       
   347         dialog = wx.FileDialog(self, _("Choose a file"), os.getcwd(), self.Controler.GetProgramFilePath(),  _("ST files (*.st)|*.st|All files|*.*"), wx.SAVE|wx.CHANGE_DIR)
       
   348         if dialog.ShowModal() == wx.ID_OK:
       
   349             filepath = dialog.GetPath()
       
   350             message_text = ""
       
   351             header, icon = _("Done"), wx.ICON_INFORMATION
       
   352             if os.path.isdir(os.path.dirname(filepath)):
       
   353                 program, errors, warnings = self.Controler.GenerateProgram(filepath)
       
   354                 message_text += "".join([_("warning: %s\n") for warning in warnings])
       
   355                 if len(errors) > 0:
       
   356                     message_text += "".join([_("error: %s\n") for error in errors])
       
   357                     message_text += _("Can't generate program to file %s!")%filepath
       
   358                     header, icon = _("Error"), wx.ICON_ERROR
       
   359                 else:
       
   360                     message_text += _("Program was successfully generated!")
       
   361             else:
       
   362                 message_text += _("\"%s\" is not a valid folder!")%os.path.dirname(filepath)
       
   363                 header, icon = _("Error"), wx.ICON_ERROR
       
   364             message = wx.MessageDialog(self, message_text, header, wx.OK|icon)
       
   365             message.ShowModal()
       
   366             message.Destroy()
       
   367         dialog.Destroy()
       
   368 
       
   369     def OnPLCOpenEditorMenu(self, event):
       
   370         wx.MessageBox(_("No documentation available.\nComing soon."))
       
   371         
       
   372     def OnPLCOpenMenu(self, event):
       
   373         open_pdf(os.path.join(CWD, "plcopen", "TC6_XML_V101.pdf"))
       
   374     
       
   375     def OnAboutMenu(self, event):
       
   376         OpenHtmlFrame(self,_("About PLCOpenEditor"), os.path.join(CWD, "doc", "plcopen_about.html"), wx.Size(350, 350))
       
   377 
       
   378     def SaveProject(self):
       
   379         result = self.Controler.SaveXMLFile()
       
   380         if not result:
       
   381             self.SaveProjectAs()
       
   382         else:
       
   383             self._Refresh(TITLE, FILEMENU, PAGETITLES)
       
   384         
       
   385     def SaveProjectAs(self):
       
   386         filepath = self.Controler.GetFilePath()
       
   387         if filepath != "":
       
   388             directory, filename = os.path.split(filepath)
       
   389         else:
       
   390             directory, filename = os.getcwd(), "%(projectName)s.xml"%self.Controler.GetProjectProperties()
       
   391         dialog = wx.FileDialog(self, _("Choose a file"), directory, filename,  _("PLCOpen files (*.xml)|*.xml|All files|*.*"), wx.SAVE|wx.OVERWRITE_PROMPT)
       
   392         if dialog.ShowModal() == wx.ID_OK:
       
   393             filepath = dialog.GetPath()
       
   394             if os.path.isdir(os.path.dirname(filepath)):
       
   395                 result = self.Controler.SaveXMLFile(filepath)
       
   396                 if not result:
       
   397                     self.ShowErrorMessage(_("Can't save project to file %s!")%filepath)
       
   398             else:
       
   399                 self.ShowErrorMessage(_("\"%s\" is not a valid folder!")%os.path.dirname(filepath))
       
   400             self._Refresh(TITLE, FILEMENU, PAGETITLES)
       
   401         dialog.Destroy()
       
   402 
       
   403 #-------------------------------------------------------------------------------
       
   404 #                            Debug Variables Panel
       
   405 #-------------------------------------------------------------------------------
       
   406 
       
   407 #-------------------------------------------------------------------------------
       
   408 #                               Viewer Printout
       
   409 #-------------------------------------------------------------------------------
       
   410 
       
   411 UPPER_DIV = lambda x, y: (x / y) + {True : 0, False : 1}[(x % y) == 0]
       
   412 
       
   413 class GraphicPrintout(wx.Printout):
       
   414     def __init__(self, viewer, page_size, margins, preview = False):
       
   415         wx.Printout.__init__(self)
       
   416         self.Viewer = viewer
       
   417         self.PageSize = page_size
       
   418         if self.PageSize[0] == 0 or self.PageSize[1] == 0:
       
   419             self.PageSize = (1050, 1485)
       
   420         self.Preview = preview
       
   421         self.Margins = margins
       
   422         self.FontSize = 5
       
   423         self.TextMargin = 3
       
   424         
       
   425         maxx, maxy = viewer.GetMaxSize()
       
   426         self.PageGrid = (UPPER_DIV(maxx, self.PageSize[0]), 
       
   427                          UPPER_DIV(maxy, self.PageSize[1]))
       
   428         
       
   429     def GetPageNumber(self):
       
   430         return self.PageGrid[0] * self.PageGrid[1]
       
   431     
       
   432     def HasPage(self, page):
       
   433         return page <= self.GetPageNumber()
       
   434         
       
   435     def GetPageInfo(self):
       
   436         page_number = self.GetPageNumber()
       
   437         return (1, page_number, 1, page_number)
       
   438 
       
   439     def OnBeginDocument(self, startPage, endPage):
       
   440         dc = self.GetDC()
       
   441         if not self.Preview and isinstance(dc, wx.PostScriptDC):
       
   442             dc.SetResolution(720)
       
   443         super(GraphicPrintout, self).OnBeginDocument(startPage, endPage)
       
   444 
       
   445     def OnPrintPage(self, page):
       
   446         dc = self.GetDC()
       
   447         dc.SetUserScale(1.0, 1.0)
       
   448         dc.SetDeviceOrigin(0, 0)
       
   449         dc.printing = not self.Preview
       
   450         
       
   451         # Get the size of the DC in pixels
       
   452         ppiPrinterX, ppiPrinterY = self.GetPPIPrinter()
       
   453         ppiScreenX, ppiScreenY = self.GetPPIScreen()
       
   454         pw, ph = self.GetPageSizePixels()
       
   455         dw, dh = dc.GetSizeTuple()
       
   456         Xscale = (float(dw) * float(ppiPrinterX)) / (float(pw) * 25.4)
       
   457         Yscale = (float(dh) * float(ppiPrinterY)) / (float(ph) * 25.4)
       
   458         
       
   459         fontsize = self.FontSize * Yscale
       
   460         text_margin = self.TextMargin * Yscale
       
   461         
       
   462         margin_left = self.Margins[0].x * Xscale
       
   463         margin_top = self.Margins[0].y * Yscale
       
   464         area_width = dw - self.Margins[1].x * Xscale - margin_left
       
   465         area_height = dh - self.Margins[1].y * Yscale - margin_top
       
   466         
       
   467         dc.SetPen(MiterPen(wx.BLACK))
       
   468         dc.SetBrush(wx.TRANSPARENT_BRUSH)    
       
   469         dc.DrawRectangle(margin_left, margin_top, area_width, area_height)
       
   470         
       
   471         dc.SetFont(wx.Font(fontsize, wx.DEFAULT, wx.NORMAL, wx.NORMAL))
       
   472         dc.SetTextForeground(wx.BLACK)
       
   473         block_name = " - ".join(self.Viewer.GetTagName().split("::")[1:])
       
   474         text_width, text_height = dc.GetTextExtent(block_name)
       
   475         dc.DrawText(block_name, margin_left, margin_top - text_height - self.TextMargin)
       
   476         dc.DrawText(_("Page: %d") % page, margin_left, margin_top + area_height + self.TextMargin)
       
   477         
       
   478         # Calculate the position on the DC for centering the graphic
       
   479         posX = area_width * ((page - 1) % self.PageGrid[0])
       
   480         posY = area_height * ((page - 1) / self.PageGrid[0])
       
   481 
       
   482         scaleX = float(area_width) / float(self.PageSize[0])
       
   483         scaleY = float(area_height) / float(self.PageSize[1])
       
   484         scale = min(scaleX, scaleY)
       
   485 
       
   486         # Set the scale and origin
       
   487         dc.SetDeviceOrigin(-posX + margin_left, -posY + margin_top)
       
   488         dc.SetClippingRegion(posX, posY, self.PageSize[0] * scale, self.PageSize[1] * scale)
       
   489         dc.SetUserScale(scale, scale)
       
   490         
       
   491         #-------------------------------------------
       
   492         
       
   493         self.Viewer.DoDrawing(dc, True)
       
   494         
       
   495         return True
       
   496 
       
   497 #-------------------------------------------------------------------------------
       
   498 #                               Exception Handler
       
   499 #-------------------------------------------------------------------------------
       
   500 
       
   501 Max_Traceback_List_Size = 20
       
   502 
       
   503 def Display_Exception_Dialog(e_type,e_value,e_tb):
       
   504     trcbck_lst = []
       
   505     for i,line in enumerate(traceback.extract_tb(e_tb)):
       
   506         trcbck = " " + str(i+1) + _(". ")
       
   507         if line[0].find(os.getcwd()) == -1:
       
   508             trcbck += _("file : ") + str(line[0]) + _(",   ")
       
   509         else:
       
   510             trcbck += _("file : ") + str(line[0][len(os.getcwd()):]) + _(",   ")
       
   511         trcbck += _("line : ") + str(line[1]) + _(",   ") + _("function : ") + str(line[2])
       
   512         trcbck_lst.append(trcbck)
       
   513         
       
   514     # Allow clicking....
       
   515     cap = wx.Window_GetCapture()
       
   516     if cap:
       
   517         cap.ReleaseMouse()
       
   518 
       
   519     dlg = wx.SingleChoiceDialog(None, 
       
   520         _("""
       
   521 An error has occurred.
       
   522 
       
   523 Click OK to save an error report.
       
   524 
       
   525 Please be kind enough to send this file to:
       
   526 edouard.tisserant@gmail.com
       
   527 
       
   528 Error:
       
   529 """) +
       
   530         str(e_type) + _(" : ") + str(e_value), 
       
   531         _("Error"),
       
   532         trcbck_lst)
       
   533     try:
       
   534         res = (dlg.ShowModal() == wx.ID_OK)
       
   535     finally:
       
   536         dlg.Destroy()
       
   537 
       
   538     return res
       
   539 
       
   540 def Display_Error_Dialog(e_value):
       
   541     message = wx.MessageDialog(None, str(e_value), _("Error"), wx.OK|wx.ICON_ERROR)
       
   542     message.ShowModal()
       
   543     message.Destroy()
       
   544 
       
   545 def get_last_traceback(tb):
       
   546     while tb.tb_next:
       
   547         tb = tb.tb_next
       
   548     return tb
       
   549 
       
   550 
       
   551 def format_namespace(d, indent='    '):
       
   552     return '\n'.join(['%s%s: %s' % (indent, k, repr(v)[:10000]) for k, v in d.iteritems()])
       
   553 
       
   554 
       
   555 ignored_exceptions = [] # a problem with a line in a module is only reported once per session
       
   556 
       
   557 def AddExceptHook(path, app_version='[No version]'):#, ignored_exceptions=[]):
       
   558     
       
   559     def handle_exception(e_type, e_value, e_traceback):
       
   560         traceback.print_exception(e_type, e_value, e_traceback) # this is very helpful when there's an exception in the rest of this func
       
   561         last_tb = get_last_traceback(e_traceback)
       
   562         ex = (last_tb.tb_frame.f_code.co_filename, last_tb.tb_frame.f_lineno)
       
   563         if str(e_value).startswith("!!!"):
       
   564             Display_Error_Dialog(e_value)
       
   565         elif ex not in ignored_exceptions:
       
   566             result = Display_Exception_Dialog(e_type,e_value,e_traceback)
       
   567             if result:
       
   568                 ignored_exceptions.append(ex)
       
   569                 info = {
       
   570                     'app-title' : wx.GetApp().GetAppName(), # app_title
       
   571                     'app-version' : app_version,
       
   572                     'wx-version' : wx.VERSION_STRING,
       
   573                     'wx-platform' : wx.Platform,
       
   574                     'python-version' : platform.python_version(), #sys.version.split()[0],
       
   575                     'platform' : platform.platform(),
       
   576                     'e-type' : e_type,
       
   577                     'e-value' : e_value,
       
   578                     'date' : time.ctime(),
       
   579                     'cwd' : os.getcwd(),
       
   580                     }
       
   581                 if e_traceback:
       
   582                     info['traceback'] = ''.join(traceback.format_tb(e_traceback)) + '%s: %s' % (e_type, e_value)
       
   583                     last_tb = get_last_traceback(e_traceback)
       
   584                     exception_locals = last_tb.tb_frame.f_locals # the locals at the level of the stack trace where the exception actually occurred
       
   585                     info['locals'] = format_namespace(exception_locals)
       
   586                     if 'self' in exception_locals:
       
   587                         info['self'] = format_namespace(exception_locals['self'].__dict__)
       
   588                 
       
   589                 output = open(path+os.sep+"bug_report_"+info['date'].replace(':','-').replace(' ','_')+".txt",'w')
       
   590                 lst = info.keys()
       
   591                 lst.sort()
       
   592                 for a in lst:
       
   593                     output.write(a+":\n"+str(info[a])+"\n\n")
       
   594 
       
   595     #sys.excepthook = lambda *args: wx.CallAfter(handle_exception, *args)
       
   596     sys.excepthook = handle_exception
       
   597 
       
   598 if __name__ == '__main__':
       
   599     wx.InitAllImageHandlers()
       
   600     
       
   601     # Install a exception handle for bug reports
       
   602     AddExceptHook(os.getcwd(),__version__)
       
   603     
       
   604     frame = PLCOpenEditor(None, fileOpen=fileOpen)
       
   605 
       
   606     frame.Show()
       
   607     app.MainLoop()
       
   608