23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
24 |
24 |
25 import wx |
25 import wx |
26 import os, sys, platform, time, traceback, getopt |
26 import os, sys, platform, time, traceback, getopt |
27 |
27 |
28 CWD = os.path.split(os.path.realpath(__file__))[0] |
28 beremiz_dir = os.path.dirname(os.path.realpath(__file__)) |
29 |
29 |
30 __version__ = "$Revision: 1.130 $" |
30 __version__ = "$Revision: 1.130 $" |
31 |
31 |
32 if __name__ == '__main__': |
32 if __name__ == '__main__': |
33 # Usage message displayed when help request or when error detected in |
33 # Usage message displayed when help request or when error detected in |
34 # command line |
34 # command line |
35 def usage(): |
35 def usage(): |
36 print "\nUsage of PLCOpenEditor.py :" |
36 print "\nUsage of PLCOpenEditor.py :" |
37 print "\n %s [Filepath]\n"%sys.argv[0] |
37 print "\n %s [Filepath]\n"%sys.argv[0] |
38 |
38 |
41 opts, args = getopt.getopt(sys.argv[1:], "h", ["help"]) |
41 opts, args = getopt.getopt(sys.argv[1:], "h", ["help"]) |
42 except getopt.GetoptError: |
42 except getopt.GetoptError: |
43 # print help information and exit: |
43 # print help information and exit: |
44 usage() |
44 usage() |
45 sys.exit(2) |
45 sys.exit(2) |
46 |
46 |
47 # Extract if help has been requested |
47 # Extract if help has been requested |
48 for o, a in opts: |
48 for o, a in opts: |
49 if o in ("-h", "--help"): |
49 if o in ("-h", "--help"): |
50 usage() |
50 usage() |
51 sys.exit() |
51 sys.exit() |
52 |
52 |
53 # Extract the optional filename to open |
53 # Extract the optional filename to open |
54 fileOpen = None |
54 fileOpen = None |
55 if len(args) > 1: |
55 if len(args) > 1: |
56 usage() |
56 usage() |
57 sys.exit() |
57 sys.exit() |
58 elif len(args) == 1: |
58 elif len(args) == 1: |
59 fileOpen = args[0] |
59 fileOpen = args[0] |
60 |
60 |
61 # Create wxApp (Need to create App before internationalization because of |
61 # Create wxApp (Need to create App before internationalization because of |
62 # Windows) |
62 # Windows) |
63 app = wx.PySimpleApp() |
63 app = wx.PySimpleApp() |
64 |
64 |
65 from util.misc import InstallLocalRessources |
65 from util.misc import InstallLocalRessources |
66 InstallLocalRessources(CWD) |
66 InstallLocalRessources(beremiz_dir) |
67 |
67 |
68 from docutil import * |
68 from docutil import * |
69 from IDEFrame import IDEFrame, AppendMenu |
69 from IDEFrame import IDEFrame, AppendMenu |
70 from IDEFrame import TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE, PAGETITLES |
70 from IDEFrame import TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE, PAGETITLES |
71 from IDEFrame import EncodeFileSystemPath, DecodeFileSystemPath |
71 from IDEFrame import EncodeFileSystemPath, DecodeFileSystemPath |
76 #------------------------------------------------------------------------------- |
76 #------------------------------------------------------------------------------- |
77 # PLCOpenEditor Main Class |
77 # PLCOpenEditor Main Class |
78 #------------------------------------------------------------------------------- |
78 #------------------------------------------------------------------------------- |
79 |
79 |
80 # Define PLCOpenEditor FileMenu extra items id |
80 # Define PLCOpenEditor FileMenu extra items id |
81 [ID_PLCOPENEDITORFILEMENUGENERATE, |
81 [ID_PLCOPENEDITORFILEMENUGENERATE, |
82 ] = [wx.NewId() for _init_coll_FileMenu_Items in range(1)] |
82 ] = [wx.NewId() for _init_coll_FileMenu_Items in range(1)] |
83 |
83 |
84 class PLCOpenEditor(IDEFrame): |
84 class PLCOpenEditor(IDEFrame): |
85 |
85 |
86 # Compatibility function for wx versions < 2.6 |
86 # Compatibility function for wx versions < 2.6 |
118 AppendMenu(parent, help='', id=wx.ID_PROPERTIES, |
118 AppendMenu(parent, help='', id=wx.ID_PROPERTIES, |
119 kind=wx.ITEM_NORMAL, text=_(u'&Properties')) |
119 kind=wx.ITEM_NORMAL, text=_(u'&Properties')) |
120 parent.AppendSeparator() |
120 parent.AppendSeparator() |
121 AppendMenu(parent, help='', id=wx.ID_EXIT, |
121 AppendMenu(parent, help='', id=wx.ID_EXIT, |
122 kind=wx.ITEM_NORMAL, text=_(u'Quit') + '\tCTRL+Q') |
122 kind=wx.ITEM_NORMAL, text=_(u'Quit') + '\tCTRL+Q') |
123 |
123 |
124 self.Bind(wx.EVT_MENU, self.OnNewProjectMenu, id=wx.ID_NEW) |
124 self.Bind(wx.EVT_MENU, self.OnNewProjectMenu, id=wx.ID_NEW) |
125 self.Bind(wx.EVT_MENU, self.OnOpenProjectMenu, id=wx.ID_OPEN) |
125 self.Bind(wx.EVT_MENU, self.OnOpenProjectMenu, id=wx.ID_OPEN) |
126 self.Bind(wx.EVT_MENU, self.OnCloseTabMenu, id=wx.ID_CLOSE) |
126 self.Bind(wx.EVT_MENU, self.OnCloseTabMenu, id=wx.ID_CLOSE) |
127 self.Bind(wx.EVT_MENU, self.OnCloseProjectMenu, id=wx.ID_CLOSE_ALL) |
127 self.Bind(wx.EVT_MENU, self.OnCloseProjectMenu, id=wx.ID_CLOSE_ALL) |
128 self.Bind(wx.EVT_MENU, self.OnSaveProjectMenu, id=wx.ID_SAVE) |
128 self.Bind(wx.EVT_MENU, self.OnSaveProjectMenu, id=wx.ID_SAVE) |
132 self.Bind(wx.EVT_MENU, self.OnPageSetupMenu, id=wx.ID_PAGE_SETUP) |
132 self.Bind(wx.EVT_MENU, self.OnPageSetupMenu, id=wx.ID_PAGE_SETUP) |
133 self.Bind(wx.EVT_MENU, self.OnPreviewMenu, id=wx.ID_PREVIEW) |
133 self.Bind(wx.EVT_MENU, self.OnPreviewMenu, id=wx.ID_PREVIEW) |
134 self.Bind(wx.EVT_MENU, self.OnPrintMenu, id=wx.ID_PRINT) |
134 self.Bind(wx.EVT_MENU, self.OnPrintMenu, id=wx.ID_PRINT) |
135 self.Bind(wx.EVT_MENU, self.OnPropertiesMenu, id=wx.ID_PROPERTIES) |
135 self.Bind(wx.EVT_MENU, self.OnPropertiesMenu, id=wx.ID_PROPERTIES) |
136 self.Bind(wx.EVT_MENU, self.OnQuitMenu, id=wx.ID_EXIT) |
136 self.Bind(wx.EVT_MENU, self.OnQuitMenu, id=wx.ID_EXIT) |
137 |
137 |
138 self.AddToMenuToolBar([(wx.ID_NEW, "new", _(u'New'), None), |
138 self.AddToMenuToolBar([(wx.ID_NEW, "new", _(u'New'), None), |
139 (wx.ID_OPEN, "open", _(u'Open'), None), |
139 (wx.ID_OPEN, "open", _(u'Open'), None), |
140 (wx.ID_SAVE, "save", _(u'Save'), None), |
140 (wx.ID_SAVE, "save", _(u'Save'), None), |
141 (wx.ID_SAVEAS, "saveas", _(u'Save As...'), None), |
141 (wx.ID_SAVEAS, "saveas", _(u'Save As...'), None), |
142 (wx.ID_PRINT, "print", _(u'Print'), None)]) |
142 (wx.ID_PRINT, "print", _(u'Print'), None)]) |
143 |
143 |
144 def _init_coll_HelpMenu_Items(self, parent): |
144 def _init_coll_HelpMenu_Items(self, parent): |
145 AppendMenu(parent, help='', id=wx.ID_HELP, |
145 AppendMenu(parent, help='', id=wx.ID_HELP, |
146 kind=wx.ITEM_NORMAL, text=_(u'PLCOpenEditor') + '\tF1') |
146 kind=wx.ITEM_NORMAL, text=_(u'PLCOpenEditor') + '\tF1') |
147 #AppendMenu(parent, help='', id=wx.ID_HELP_CONTENTS, |
147 #AppendMenu(parent, help='', id=wx.ID_HELP_CONTENTS, |
148 # kind=wx.ITEM_NORMAL, text=u'PLCOpen\tF2') |
148 # kind=wx.ITEM_NORMAL, text=u'PLCOpen\tF2') |
149 #AppendMenu(parent, help='', id=wx.ID_HELP_CONTEXT, |
149 #AppendMenu(parent, help='', id=wx.ID_HELP_CONTEXT, |
150 # kind=wx.ITEM_NORMAL, text=u'IEC 61131-3\tF3') |
150 # kind=wx.ITEM_NORMAL, text=u'IEC 61131-3\tF3') |
159 # @param controler The controler been used by PLCOpenEditor (default: None). |
159 # @param controler The controler been used by PLCOpenEditor (default: None). |
160 # @param fileOpen The filepath to open if no controler defined (default: None). |
160 # @param fileOpen The filepath to open if no controler defined (default: None). |
161 # @param debug The filepath to open if no controler defined (default: False). |
161 # @param debug The filepath to open if no controler defined (default: False). |
162 def __init__(self, parent, fileOpen = None): |
162 def __init__(self, parent, fileOpen = None): |
163 IDEFrame.__init__(self, parent) |
163 IDEFrame.__init__(self, parent) |
164 |
164 |
165 result = None |
165 result = None |
166 |
166 |
167 # Open the filepath if defined |
167 # Open the filepath if defined |
168 if fileOpen is not None: |
168 if fileOpen is not None: |
169 fileOpen = DecodeFileSystemPath(fileOpen, False) |
169 fileOpen = DecodeFileSystemPath(fileOpen, False) |
170 if os.path.isfile(fileOpen): |
170 if os.path.isfile(fileOpen): |
171 # Create a new controller |
171 # Create a new controller |
174 self.Controler = controler |
174 self.Controler = controler |
175 self.LibraryPanel.SetController(controler) |
175 self.LibraryPanel.SetController(controler) |
176 self.ProjectTree.Enable(True) |
176 self.ProjectTree.Enable(True) |
177 self.PouInstanceVariablesPanel.SetController(controler) |
177 self.PouInstanceVariablesPanel.SetController(controler) |
178 self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) |
178 self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) |
179 |
179 |
180 # Define PLCOpenEditor icon |
180 # Define PLCOpenEditor icon |
181 self.SetIcon(wx.Icon(os.path.join(CWD, "images", "poe.ico"),wx.BITMAP_TYPE_ICO)) |
181 self.SetIcon(wx.Icon(os.path.join(beremiz_dir, "images", "poe.ico"),wx.BITMAP_TYPE_ICO)) |
182 |
182 |
183 self.Bind(wx.EVT_CLOSE, self.OnCloseFrame) |
183 self.Bind(wx.EVT_CLOSE, self.OnCloseFrame) |
184 |
184 |
185 self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU) |
185 self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU) |
186 |
186 |
187 if result is not None: |
187 if result is not None: |
188 self.ShowErrorMessage( |
188 self.ShowErrorMessage( |
189 _("PLC syntax error at line %d:\n%s") % result) |
189 _("PLC syntax error at line %d:\n%s") % result) |
190 |
190 |
191 def OnCloseFrame(self, event): |
191 def OnCloseFrame(self, event): |
192 if self.Controler is None or self.CheckSaveBeforeClosing(_("Close Application")): |
192 if self.Controler is None or self.CheckSaveBeforeClosing(_("Close Application")): |
193 self.AUIManager.UnInit() |
193 self.AUIManager.UnInit() |
194 |
194 |
195 self.SaveLastState() |
195 self.SaveLastState() |
196 |
196 |
197 event.Skip() |
197 event.Skip() |
198 else: |
198 else: |
199 event.Veto() |
199 event.Veto() |
200 |
200 |
201 def RefreshTitle(self): |
201 def RefreshTitle(self): |
264 self.ResetView() |
264 self.ResetView() |
265 self.Controler = PLCControler() |
265 self.Controler = PLCControler() |
266 self.Controler.CreateNewProject(properties) |
266 self.Controler.CreateNewProject(properties) |
267 self.LibraryPanel.SetController(self.Controler) |
267 self.LibraryPanel.SetController(self.Controler) |
268 self.ProjectTree.Enable(True) |
268 self.ProjectTree.Enable(True) |
269 self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL, |
269 self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL, |
270 LIBRARYTREE) |
270 LIBRARYTREE) |
271 |
271 |
272 def OnOpenProjectMenu(self, event): |
272 def OnOpenProjectMenu(self, event): |
273 if self.Controler is not None and not self.CheckSaveBeforeClosing(): |
273 if self.Controler is not None and not self.CheckSaveBeforeClosing(): |
274 return |
274 return |
277 filepath = self.Controler.GetFilePath() |
277 filepath = self.Controler.GetFilePath() |
278 if filepath != "": |
278 if filepath != "": |
279 directory = os.path.dirname(filepath) |
279 directory = os.path.dirname(filepath) |
280 else: |
280 else: |
281 directory = os.getcwd() |
281 directory = os.getcwd() |
282 |
282 |
283 result = None |
283 result = None |
284 |
284 |
285 dialog = wx.FileDialog(self, _("Choose a file"), directory, "", _("PLCOpen files (*.xml)|*.xml|All files|*.*"), wx.OPEN) |
285 dialog = wx.FileDialog(self, _("Choose a file"), directory, "", _("PLCOpen files (*.xml)|*.xml|All files|*.*"), wx.OPEN) |
286 if dialog.ShowModal() == wx.ID_OK: |
286 if dialog.ShowModal() == wx.ID_OK: |
287 filepath = dialog.GetPath() |
287 filepath = dialog.GetPath() |
288 if os.path.isfile(filepath): |
288 if os.path.isfile(filepath): |
289 self.ResetView() |
289 self.ResetView() |
294 self.ProjectTree.Enable(True) |
294 self.ProjectTree.Enable(True) |
295 self.PouInstanceVariablesPanel.SetController(controler) |
295 self.PouInstanceVariablesPanel.SetController(controler) |
296 self._Refresh(PROJECTTREE, LIBRARYTREE) |
296 self._Refresh(PROJECTTREE, LIBRARYTREE) |
297 self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU) |
297 self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU) |
298 dialog.Destroy() |
298 dialog.Destroy() |
299 |
299 |
300 if result is not None: |
300 if result is not None: |
301 self.ShowErrorMessage( |
301 self.ShowErrorMessage( |
302 _("PLC syntax error at line %d:\n%s") % result) |
302 _("PLC syntax error at line %d:\n%s") % result) |
303 |
303 |
304 def OnCloseProjectMenu(self, event): |
304 def OnCloseProjectMenu(self, event): |
305 if not self.CheckSaveBeforeClosing(): |
305 if not self.CheckSaveBeforeClosing(): |
306 return |
306 return |
307 self.ResetView() |
307 self.ResetView() |
308 self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU) |
308 self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU) |
336 message.Destroy() |
336 message.Destroy() |
337 dialog.Destroy() |
337 dialog.Destroy() |
338 |
338 |
339 def OnPLCOpenEditorMenu(self, event): |
339 def OnPLCOpenEditorMenu(self, event): |
340 wx.MessageBox(_("No documentation available.\nComing soon.")) |
340 wx.MessageBox(_("No documentation available.\nComing soon.")) |
341 |
341 |
342 def OnPLCOpenMenu(self, event): |
342 def OnPLCOpenMenu(self, event): |
343 open_pdf(os.path.join(CWD, "plcopen", "TC6_XML_V101.pdf")) |
343 open_pdf(os.path.join(beremiz_dir, "plcopen", "TC6_XML_V101.pdf")) |
344 |
344 |
345 def OnAboutMenu(self, event): |
345 def OnAboutMenu(self, event): |
346 OpenHtmlFrame(self,_("About PLCOpenEditor"), os.path.join(CWD, "doc", "plcopen_about.html"), wx.Size(350, 350)) |
346 OpenHtmlFrame(self,_("About PLCOpenEditor"), os.path.join(beremiz_dir, "doc", "plcopen_about.html"), wx.Size(350, 350)) |
347 |
347 |
348 def SaveProject(self): |
348 def SaveProject(self): |
349 result = self.Controler.SaveXMLFile() |
349 result = self.Controler.SaveXMLFile() |
350 if not result: |
350 if not result: |
351 self.SaveProjectAs() |
351 self.SaveProjectAs() |
352 else: |
352 else: |
353 self._Refresh(TITLE, FILEMENU, PAGETITLES) |
353 self._Refresh(TITLE, FILEMENU, PAGETITLES) |
354 |
354 |
355 def SaveProjectAs(self): |
355 def SaveProjectAs(self): |
356 filepath = self.Controler.GetFilePath() |
356 filepath = self.Controler.GetFilePath() |
357 if filepath != "": |
357 if filepath != "": |
358 directory, filename = os.path.split(filepath) |
358 directory, filename = os.path.split(filepath) |
359 else: |
359 else: |
384 trcbck += _("file : ") + str(line[0]) + _(", ") |
384 trcbck += _("file : ") + str(line[0]) + _(", ") |
385 else: |
385 else: |
386 trcbck += _("file : ") + str(line[0][len(os.getcwd()):]) + _(", ") |
386 trcbck += _("file : ") + str(line[0][len(os.getcwd()):]) + _(", ") |
387 trcbck += _("line : ") + str(line[1]) + _(", ") + _("function : ") + str(line[2]) |
387 trcbck += _("line : ") + str(line[1]) + _(", ") + _("function : ") + str(line[2]) |
388 trcbck_lst.append(trcbck) |
388 trcbck_lst.append(trcbck) |
389 |
389 |
390 # Allow clicking.... |
390 # Allow clicking.... |
391 cap = wx.Window_GetCapture() |
391 cap = wx.Window_GetCapture() |
392 if cap: |
392 if cap: |
393 cap.ReleaseMouse() |
393 cap.ReleaseMouse() |
394 |
394 |
395 dlg = wx.SingleChoiceDialog(None, |
395 dlg = wx.SingleChoiceDialog(None, |
396 _(""" |
396 _(""" |
397 An error has occurred. |
397 An error has occurred. |
398 |
398 |
399 Click OK to save an error report. |
399 Click OK to save an error report. |
400 |
400 |
401 Please be kind enough to send this file to: |
401 Please be kind enough to send this file to: |
402 edouard.tisserant@gmail.com |
402 edouard.tisserant@gmail.com |
403 |
403 |
404 Error: |
404 Error: |
405 """) + |
405 """) + |
406 str(e_type) + _(" : ") + str(e_value), |
406 str(e_type) + _(" : ") + str(e_value), |
407 _("Error"), |
407 _("Error"), |
408 trcbck_lst) |
408 trcbck_lst) |
409 try: |
409 try: |
410 res = (dlg.ShowModal() == wx.ID_OK) |
410 res = (dlg.ShowModal() == wx.ID_OK) |
411 finally: |
411 finally: |
429 |
429 |
430 |
430 |
431 ignored_exceptions = [] # a problem with a line in a module is only reported once per session |
431 ignored_exceptions = [] # a problem with a line in a module is only reported once per session |
432 |
432 |
433 def AddExceptHook(path, app_version='[No version]'):#, ignored_exceptions=[]): |
433 def AddExceptHook(path, app_version='[No version]'):#, ignored_exceptions=[]): |
434 |
434 |
435 def handle_exception(e_type, e_value, e_traceback): |
435 def handle_exception(e_type, e_value, e_traceback): |
436 traceback.print_exception(e_type, e_value, e_traceback) # this is very helpful when there's an exception in the rest of this func |
436 traceback.print_exception(e_type, e_value, e_traceback) # this is very helpful when there's an exception in the rest of this func |
437 last_tb = get_last_traceback(e_traceback) |
437 last_tb = get_last_traceback(e_traceback) |
438 ex = (last_tb.tb_frame.f_code.co_filename, last_tb.tb_frame.f_lineno) |
438 ex = (last_tb.tb_frame.f_code.co_filename, last_tb.tb_frame.f_lineno) |
439 if str(e_value).startswith("!!!"): |
439 if str(e_value).startswith("!!!"): |
459 last_tb = get_last_traceback(e_traceback) |
459 last_tb = get_last_traceback(e_traceback) |
460 exception_locals = last_tb.tb_frame.f_locals # the locals at the level of the stack trace where the exception actually occurred |
460 exception_locals = last_tb.tb_frame.f_locals # the locals at the level of the stack trace where the exception actually occurred |
461 info['locals'] = format_namespace(exception_locals) |
461 info['locals'] = format_namespace(exception_locals) |
462 if 'self' in exception_locals: |
462 if 'self' in exception_locals: |
463 info['self'] = format_namespace(exception_locals['self'].__dict__) |
463 info['self'] = format_namespace(exception_locals['self'].__dict__) |
464 |
464 |
465 output = open(path+os.sep+"bug_report_"+info['date'].replace(':','-').replace(' ','_')+".txt",'w') |
465 output = open(path+os.sep+"bug_report_"+info['date'].replace(':','-').replace(' ','_')+".txt",'w') |
466 lst = info.keys() |
466 lst = info.keys() |
467 lst.sort() |
467 lst.sort() |
468 for a in lst: |
468 for a in lst: |
469 output.write(a+":\n"+str(info[a])+"\n\n") |
469 output.write(a+":\n"+str(info[a])+"\n\n") |
471 #sys.excepthook = lambda *args: wx.CallAfter(handle_exception, *args) |
471 #sys.excepthook = lambda *args: wx.CallAfter(handle_exception, *args) |
472 sys.excepthook = handle_exception |
472 sys.excepthook = handle_exception |
473 |
473 |
474 if __name__ == '__main__': |
474 if __name__ == '__main__': |
475 wx.InitAllImageHandlers() |
475 wx.InitAllImageHandlers() |
476 |
476 |
477 # Install a exception handle for bug reports |
477 # Install a exception handle for bug reports |
478 AddExceptHook(os.getcwd(),__version__) |
478 AddExceptHook(os.getcwd(),__version__) |
479 |
479 |
480 frame = PLCOpenEditor(None, fileOpen=fileOpen) |
480 frame = PLCOpenEditor(None, fileOpen=fileOpen) |
481 |
481 |
482 frame.Show() |
482 frame.Show() |
483 app.MainLoop() |
483 app.MainLoop() |
484 |
484 |