1 #!/usr/bin/env python |
1 #!/usr/bin/env python |
2 # -*- coding: utf-8 -*- |
2 # -*- coding: utf-8 -*- |
3 |
3 |
4 #This file is part of Beremiz, a Integrated Development Environment for |
4 # This file is part of Beremiz, a Integrated Development Environment for |
5 #programming IEC 61131-3 automates supporting plcopen standard and CanFestival. |
5 # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. |
6 # |
6 # |
7 #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD |
7 # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD |
8 # |
8 # |
9 #See COPYING file for copyrights details. |
9 # See COPYING file for copyrights details. |
10 # |
10 # |
11 #This library is free software; you can redistribute it and/or |
11 # This program is free software; you can redistribute it and/or |
12 #modify it under the terms of the GNU General Public |
12 # modify it under the terms of the GNU General Public License |
13 #License as published by the Free Software Foundation; either |
13 # as published by the Free Software Foundation; either version 2 |
14 #version 2.1 of the License, or (at your option) any later version. |
14 # of the License, or (at your option) any later version. |
15 # |
15 # |
16 #This library is distributed in the hope that it will be useful, |
16 # This program is distributed in the hope that it will be useful, |
17 #but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
18 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
19 #General Public License for more details. |
19 # GNU General Public License for more details. |
20 # |
20 # |
21 #You should have received a copy of the GNU General Public |
21 # You should have received a copy of the GNU General Public License |
22 #License along with this library; if not, write to the Free Software |
22 # along with this program; if not, write to the Free Software |
23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
23 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
24 |
|
25 |
24 |
26 updateinfo_url = None |
25 updateinfo_url = None |
27 |
26 |
28 import os, sys, getopt |
27 import os, sys, getopt |
29 import __builtin__ |
28 import __builtin__ |
30 import tempfile |
29 import tempfile |
31 import shutil |
30 import shutil |
32 import random |
31 import random |
33 import time |
32 import time |
|
33 import version |
34 from types import ListType |
34 from types import ListType |
35 |
35 |
36 beremiz_dir = os.path.dirname(os.path.realpath(__file__)) |
36 beremiz_dir = os.path.dirname(os.path.realpath(__file__)) |
37 |
37 |
38 import wxversion |
38 if __name__ == '__main__': |
39 wxversion.select('2.8') |
39 import wxversion |
40 import wx |
40 wxversion.select(['2.8', '3.0']) |
|
41 import wx |
|
42 |
41 from wx.lib.agw.advancedsplash import AdvancedSplash |
43 from wx.lib.agw.advancedsplash import AdvancedSplash |
42 |
44 |
43 def Bpath(*args): |
45 def Bpath(*args): |
44 return os.path.join(beremiz_dir,*args) |
46 return os.path.join(beremiz_dir,*args) |
|
47 |
|
48 def ShowSplashScreen(): |
|
49 bmp = wx.Image(Bpath("images", "splash.png")).ConvertToBitmap() |
|
50 #splash=AdvancedSplash(None, bitmap=bmp, style=wx.SPLASH_CENTRE_ON_SCREEN, timeout=4000) |
|
51 splash = AdvancedSplash(None, bitmap=bmp) |
|
52 |
|
53 # process all events |
|
54 # even the events generated by splash themself during showing |
|
55 for i in range(0,30): |
|
56 wx.Yield() |
|
57 time.sleep(0.01); |
|
58 return splash |
45 |
59 |
46 if __name__ == '__main__': |
60 if __name__ == '__main__': |
47 def usage(): |
61 def usage(): |
48 print "\nUsage of Beremiz.py :" |
62 print "\nUsage of Beremiz.py :" |
49 print "\n %s [Projectpath] [Buildpath]\n"%sys.argv[0] |
63 print "\n %s [Projectpath] [Buildpath]\n"%sys.argv[0] |
82 if os.path.exists("BEREMIZ_DEBUG"): |
96 if os.path.exists("BEREMIZ_DEBUG"): |
83 __builtin__.__dict__["BMZ_DBG"] = True |
97 __builtin__.__dict__["BMZ_DBG"] = True |
84 else : |
98 else : |
85 __builtin__.__dict__["BMZ_DBG"] = False |
99 __builtin__.__dict__["BMZ_DBG"] = False |
86 |
100 |
87 app = wx.PySimpleApp(redirect=BMZ_DBG) |
101 if wx.VERSION >= (3, 0, 0): |
|
102 app = wx.App(redirect=BMZ_DBG) |
|
103 else: |
|
104 app = wx.PySimpleApp(redirect=BMZ_DBG) |
|
105 |
88 app.SetAppName('beremiz') |
106 app.SetAppName('beremiz') |
89 wx.InitAllImageHandlers() |
107 if wx.VERSION < (3, 0, 0): |
|
108 wx.InitAllImageHandlers() |
90 |
109 |
91 # popup splash |
110 # popup splash |
92 bmp = wx.Image(Bpath("images", "splash.png")).ConvertToBitmap() |
111 splash = ShowSplashScreen() |
93 #splash=AdvancedSplash(None, bitmap=bmp, style=wx.SPLASH_CENTRE_ON_SCREEN, timeout=4000) |
112 |
94 splash=AdvancedSplash(None, bitmap=bmp) |
113 # load internatialization files |
95 wx.Yield() |
114 from util.misc import InstallLocalRessources |
96 |
115 InstallLocalRessources(beremiz_dir) |
|
116 |
97 if updateinfo_url is not None: |
117 if updateinfo_url is not None: |
98 updateinfo = "Fetching %s" % updateinfo_url |
118 updateinfo = _("Fetching %s") % updateinfo_url |
99 # warn for possible updates |
119 # warn for possible updates |
100 def updateinfoproc(): |
120 def updateinfoproc(): |
101 global updateinfo |
121 global updateinfo |
102 try : |
122 try : |
103 import urllib2 |
123 import urllib2 |
104 updateinfo = urllib2.urlopen(updateinfo_url,None).read() |
124 updateinfo = urllib2.urlopen(updateinfo_url,None).read() |
105 except : |
125 except : |
106 updateinfo = "update info unavailable." |
126 updateinfo = _("update info unavailable.") |
107 |
127 |
108 from threading import Thread |
128 from threading import Thread |
109 splash.SetText(text=updateinfo) |
129 splash.SetText(text=updateinfo) |
110 wx.Yield() |
130 wx.Yield() |
111 updateinfoThread = Thread(target=updateinfoproc) |
131 updateinfoThread = Thread(target=updateinfoproc) |
112 updateinfoThread.start() |
132 updateinfoThread.start() |
113 updateinfoThread.join(2) |
133 updateinfoThread.join(2) |
114 splash.SetText(text=updateinfo) |
134 splash.SetText(text=updateinfo) |
115 wx.Yield() |
135 wx.Yield() |
116 |
|
117 from util.misc import InstallLocalRessources |
|
118 InstallLocalRessources(beremiz_dir) |
|
119 |
136 |
120 # Load extensions |
137 # Load extensions |
121 for extfilename in extensions: |
138 for extfilename in extensions: |
122 from util.TranslationCatalogs import AddCatalog |
139 from util.TranslationCatalogs import AddCatalog |
123 from util.BitmapLibrary import AddBitmapFolder |
140 from util.BitmapLibrary import AddBitmapFolder |
140 from editors.DataTypeEditor import DataTypeEditor |
157 from editors.DataTypeEditor import DataTypeEditor |
141 from util.MiniTextControler import MiniTextControler |
158 from util.MiniTextControler import MiniTextControler |
142 from util.ProcessLogger import ProcessLogger |
159 from util.ProcessLogger import ProcessLogger |
143 from controls.LogViewer import LogViewer |
160 from controls.LogViewer import LogViewer |
144 from controls.CustomStyledTextCtrl import CustomStyledTextCtrl |
161 from controls.CustomStyledTextCtrl import CustomStyledTextCtrl |
|
162 from controls import EnhancedStatusBar as esb |
|
163 from dialogs.AboutDialog import ShowAboutDialog |
145 |
164 |
146 from PLCControler import LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY, ITEM_PROJECT, ITEM_RESOURCE |
165 from PLCControler import LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY, ITEM_PROJECT, ITEM_RESOURCE |
147 from ProjectController import ProjectController, GetAddMenuItems, MATIEC_ERROR_MODEL, ITEM_CONFNODE |
166 from ProjectController import ProjectController, GetAddMenuItems, MATIEC_ERROR_MODEL, ITEM_CONFNODE |
148 |
167 |
149 |
168 |
229 |
248 |
230 # Temporary deactivate read only mode on StyledTextCtrl for |
249 # Temporary deactivate read only mode on StyledTextCtrl for |
231 # adding text. It seems that text modifications, even |
250 # adding text. It seems that text modifications, even |
232 # programmatically, are disabled in StyledTextCtrl when read |
251 # programmatically, are disabled in StyledTextCtrl when read |
233 # only is active |
252 # only is active |
|
253 start_pos = self.output.GetLength() |
234 self.output.SetReadOnly(False) |
254 self.output.SetReadOnly(False) |
235 self.output.AppendText(s) |
255 self.output.AppendText(s) |
236 self.output.SetReadOnly(True) |
256 self.output.SetReadOnly(True) |
|
257 text_len = self.output.GetLength() - start_pos |
237 |
258 |
238 if style != self.black_white: |
259 if style != self.black_white: |
239 self.output.SetStyling(len(s), style) |
260 self.output.SetStyling(text_len, style) |
240 self.stack = [] |
261 self.stack = [] |
241 self.lock.release() |
262 self.lock.release() |
242 self.output.Thaw() |
263 self.output.Thaw() |
243 self.LastRefreshTime = gettime() |
264 self.LastRefreshTime = gettime() |
244 try: |
265 try: |
375 self.EditMenuSize = self.EditMenu.GetMenuItemCount() |
396 self.EditMenuSize = self.EditMenu.GetMenuItemCount() |
376 |
397 |
377 inspectorID = wx.NewId() |
398 inspectorID = wx.NewId() |
378 self.Bind(wx.EVT_MENU, self.OnOpenWidgetInspector, id=inspectorID) |
399 self.Bind(wx.EVT_MENU, self.OnOpenWidgetInspector, id=inspectorID) |
379 accels = [wx.AcceleratorEntry(wx.ACCEL_CTRL|wx.ACCEL_ALT, ord('I'), inspectorID)] |
400 accels = [wx.AcceleratorEntry(wx.ACCEL_CTRL|wx.ACCEL_ALT, ord('I'), inspectorID)] |
|
401 |
|
402 keyID = wx.NewId() |
|
403 self.Bind(wx.EVT_MENU, self.SwitchFullScrMode, id=keyID) |
|
404 accels += [wx.AcceleratorEntry(wx.ACCEL_NORMAL, wx.WXK_F12, keyID)] |
|
405 |
380 for method,shortcut in [("Stop", wx.WXK_F4), |
406 for method,shortcut in [("Stop", wx.WXK_F4), |
381 ("Run", wx.WXK_F5), |
407 ("Run", wx.WXK_F5), |
382 ("Transfer", wx.WXK_F6), |
408 ("Transfer", wx.WXK_F6), |
383 ("Connect", wx.WXK_F7), |
409 ("Connect", wx.WXK_F7), |
384 ("Build", wx.WXK_F11)]: |
410 ("Build", wx.WXK_F11)]: |
438 ToolbarPane().Top().Position(1). |
464 ToolbarPane().Top().Position(1). |
439 LeftDockable(False).RightDockable(False)) |
465 LeftDockable(False).RightDockable(False)) |
440 |
466 |
441 self.AUIManager.Update() |
467 self.AUIManager.Update() |
442 |
468 |
443 self.ConnectionStatusBar = wx.StatusBar(self, style=wx.ST_SIZEGRIP) |
469 self.ConnectionStatusBar = esb.EnhancedStatusBar(self, style=wx.ST_SIZEGRIP) |
444 self._init_coll_ConnectionStatusBar_Fields(self.ConnectionStatusBar) |
470 self._init_coll_ConnectionStatusBar_Fields(self.ConnectionStatusBar) |
|
471 self.ProgressStatusBar = wx.Gauge(self.ConnectionStatusBar, -1, range = 100) |
|
472 self.ConnectionStatusBar.AddWidget(self.ProgressStatusBar, esb.ESB_EXACT_FIT, esb.ESB_EXACT_FIT, 2) |
|
473 self.ProgressStatusBar.Hide() |
445 self.SetStatusBar(self.ConnectionStatusBar) |
474 self.SetStatusBar(self.ConnectionStatusBar) |
446 |
475 |
|
476 def __init_execute_path(self): |
|
477 if os.name == 'nt': |
|
478 # on windows, desktop shortcut launches Beremiz.py |
|
479 # with working dir set to mingw/bin. |
|
480 # then we prefix CWD to PATH in order to ensure that |
|
481 # commands invoked by build process by default are |
|
482 # found here. |
|
483 os.environ["PATH"] = os.getcwd()+';'+os.environ["PATH"] |
|
484 |
|
485 |
447 def __init__(self, parent, projectOpen=None, buildpath=None, ctr=None, debug=True): |
486 def __init__(self, parent, projectOpen=None, buildpath=None, ctr=None, debug=True): |
|
487 # Add beremiz's icon in top left corner of the frame |
|
488 self.icon = wx.Icon(Bpath("images", "brz.ico"), wx.BITMAP_TYPE_ICO) |
|
489 self.__init_execute_path() |
|
490 |
448 IDEFrame.__init__(self, parent, debug) |
491 IDEFrame.__init__(self, parent, debug) |
449 self.Log = LogPseudoFile(self.LogConsole,self.SelectTab) |
492 self.Log = LogPseudoFile(self.LogConsole,self.SelectTab) |
450 |
493 |
451 self.local_runtime = None |
494 self.local_runtime = None |
452 self.runtime_port = None |
495 self.runtime_port = None |
471 # Icons for other items |
514 # Icons for other items |
472 for imgname, itemtype in [ |
515 for imgname, itemtype in [ |
473 ("Extension", ITEM_CONFNODE)]: |
516 ("Extension", ITEM_CONFNODE)]: |
474 self.TreeImageDict[itemtype] = self.TreeImageList.Add(GetBitmap(imgname)) |
517 self.TreeImageDict[itemtype] = self.TreeImageList.Add(GetBitmap(imgname)) |
475 |
518 |
476 # Add beremiz's icon in top left corner of the frame |
|
477 self.SetIcon(wx.Icon(Bpath("images", "brz.ico"), wx.BITMAP_TYPE_ICO)) |
|
478 |
|
479 if projectOpen is not None: |
519 if projectOpen is not None: |
480 projectOpen = DecodeFileSystemPath(projectOpen, False) |
520 projectOpen = DecodeFileSystemPath(projectOpen, False) |
481 |
521 |
482 if projectOpen is not None and os.path.isdir(projectOpen): |
522 if projectOpen is not None and os.path.isdir(projectOpen): |
483 self.CTR = ProjectController(self, self.Log) |
523 self.CTR = ProjectController(self, self.Log) |
484 self.Controler = self.CTR |
524 self.Controler = self.CTR |
485 result = self.CTR.LoadProject(projectOpen, buildpath) |
525 result, err = self.CTR.LoadProject(projectOpen, buildpath) |
486 if not result: |
526 if not result: |
487 self.LibraryPanel.SetController(self.Controler) |
527 self.LibraryPanel.SetController(self.Controler) |
488 self.ProjectTree.Enable(True) |
528 self.ProjectTree.Enable(True) |
489 self.PouInstanceVariablesPanel.SetController(self.Controler) |
529 self.PouInstanceVariablesPanel.SetController(self.Controler) |
490 self.RefreshConfigRecentProjects(os.path.abspath(projectOpen)) |
530 self.RefreshConfigRecentProjects(os.path.abspath(projectOpen)) |
532 Bpath("Beremiz_service.py"), |
572 Bpath("Beremiz_service.py"), |
533 self.runtime_port, |
573 self.runtime_port, |
534 {False : "-x 0", True :"-x 1"}[taskbaricon], |
574 {False : "-x 0", True :"-x 1"}[taskbaricon], |
535 self.local_runtime_tmpdir), |
575 self.local_runtime_tmpdir), |
536 no_gui=False, |
576 no_gui=False, |
537 timeout=500, keyword = "working", |
577 timeout=500, keyword = self.local_runtime_tmpdir, |
538 cwd = self.local_runtime_tmpdir) |
578 cwd = self.local_runtime_tmpdir) |
539 self.local_runtime.spin() |
579 self.local_runtime.spin() |
540 return self.runtime_port |
580 return self.runtime_port |
541 |
581 |
542 def KillLocalRuntime(self): |
582 def KillLocalRuntime(self): |
715 try: |
755 try: |
716 recent_projects = map(DecodeFileSystemPath, |
756 recent_projects = map(DecodeFileSystemPath, |
717 self.GetConfigEntry("RecentProjects", [])) |
757 self.GetConfigEntry("RecentProjects", [])) |
718 except: |
758 except: |
719 recent_projects = [] |
759 recent_projects = [] |
|
760 |
|
761 while self.RecentProjectsMenu.GetMenuItemCount() > len(recent_projects): |
|
762 item = self.RecentProjectsMenu.FindItemByPosition(0) |
|
763 self.RecentProjectsMenu.RemoveItem(item) |
|
764 |
720 self.FileMenu.Enable(ID_FILEMENURECENTPROJECTS, len(recent_projects) > 0) |
765 self.FileMenu.Enable(ID_FILEMENURECENTPROJECTS, len(recent_projects) > 0) |
721 for idx, projectpath in enumerate(recent_projects): |
766 for idx, projectpath in enumerate(recent_projects): |
722 text = u'%d: %s' % (idx + 1, projectpath) |
767 text = u'%d: %s' % (idx + 1, projectpath) |
723 |
768 |
724 if idx < self.RecentProjectsMenu.GetMenuItemCount(): |
769 if idx < self.RecentProjectsMenu.GetMenuItemCount(): |
854 self.Log.flush() |
899 self.Log.flush() |
855 if self.EnableDebug: |
900 if self.EnableDebug: |
856 self.DebugVariablePanel.SetDataProducer(None) |
901 self.DebugVariablePanel.SetDataProducer(None) |
857 self.ResetConnectionStatusBar() |
902 self.ResetConnectionStatusBar() |
858 |
903 |
859 def RefreshConfigRecentProjects(self, projectpath): |
904 def RefreshConfigRecentProjects(self, projectpath, err=False): |
860 try: |
905 try: |
861 recent_projects = map(DecodeFileSystemPath, |
906 recent_projects = map(DecodeFileSystemPath, |
862 self.GetConfigEntry("RecentProjects", [])) |
907 self.GetConfigEntry("RecentProjects", [])) |
863 except: |
908 except: |
864 recent_projects = [] |
909 recent_projects = [] |
865 if projectpath in recent_projects: |
910 if projectpath in recent_projects: |
866 recent_projects.remove(projectpath) |
911 recent_projects.remove(projectpath) |
867 recent_projects.insert(0, projectpath) |
912 if not err: |
|
913 recent_projects.insert(0, projectpath) |
868 self.Config.Write("RecentProjects", cPickle.dumps( |
914 self.Config.Write("RecentProjects", cPickle.dumps( |
869 map(EncodeFileSystemPath, recent_projects[:MAX_RECENT_PROJECTS]))) |
915 map(EncodeFileSystemPath, recent_projects[:MAX_RECENT_PROJECTS]))) |
870 self.Config.Flush() |
916 self.Config.Flush() |
871 |
917 |
872 def ResetPerspective(self): |
918 def ResetPerspective(self): |
929 EncodeFileSystemPath(os.path.dirname(projectpath))) |
975 EncodeFileSystemPath(os.path.dirname(projectpath))) |
930 self.Config.Flush() |
976 self.Config.Flush() |
931 self.ResetView() |
977 self.ResetView() |
932 self.CTR = ProjectController(self, self.Log) |
978 self.CTR = ProjectController(self, self.Log) |
933 self.Controler = self.CTR |
979 self.Controler = self.CTR |
934 result = self.CTR.LoadProject(projectpath) |
980 result, err = self.CTR.LoadProject(projectpath) |
935 if not result: |
981 if not result: |
936 self.LibraryPanel.SetController(self.Controler) |
982 self.LibraryPanel.SetController(self.Controler) |
937 self.ProjectTree.Enable(True) |
983 self.ProjectTree.Enable(True) |
938 self.PouInstanceVariablesPanel.SetController(self.Controler) |
984 self.PouInstanceVariablesPanel.SetController(self.Controler) |
939 self.RefreshConfigRecentProjects(projectpath) |
|
940 if self.EnableDebug: |
985 if self.EnableDebug: |
941 self.DebugVariablePanel.SetDataProducer(self.CTR) |
986 self.DebugVariablePanel.SetDataProducer(self.CTR) |
942 self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) |
987 self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) |
943 else: |
988 else: |
944 self.ResetView() |
989 self.ResetView() |
945 self.ShowErrorMessage(result) |
990 self.ShowErrorMessage(result) |
946 self.RefreshAll() |
991 self.RefreshAll() |
|
992 self.SearchResultPanel.ResetSearchResults() |
947 else: |
993 else: |
948 self.ShowErrorMessage(_("\"%s\" folder is not a valid Beremiz project\n") % projectpath) |
994 self.ShowErrorMessage(_("\"%s\" folder is not a valid Beremiz project\n") % projectpath) |
|
995 err = True |
|
996 self.RefreshConfigRecentProjects(projectpath, err) |
949 self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU) |
997 self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU) |
950 |
998 |
951 def OnCloseProjectMenu(self, event): |
999 def OnCloseProjectMenu(self, event): |
952 if self.CTR is not None and not self.CheckSaveBeforeClosing(): |
1000 if self.CTR is not None and not self.CheckSaveBeforeClosing(): |
953 return |
1001 return |
972 window = self.TabsOpened.GetPage(selected) |
1020 window = self.TabsOpened.GetPage(selected) |
973 window.SaveAs() |
1021 window.SaveAs() |
974 if self.CTR is not None: |
1022 if self.CTR is not None: |
975 self.CTR.SaveProjectAs() |
1023 self.CTR.SaveProjectAs() |
976 self.RefreshAll() |
1024 self.RefreshAll() |
|
1025 self.RefreshConfigRecentProjects(self.CTR.ProjectPath) |
977 self._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES) |
1026 self._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES) |
978 |
1027 |
979 def OnQuitMenu(self, event): |
1028 def OnQuitMenu(self, event): |
980 self.Close() |
1029 self.Close() |
981 |
1030 |
982 def OnAboutMenu(self, event): |
1031 def OnAboutMenu(self, event): |
983 OpenHtmlFrame(self,_("About Beremiz"), Bpath("doc", "about.html"), wx.Size(550, 500)) |
1032 info = version.GetAboutDialogInfo() |
|
1033 ShowAboutDialog(self, info) |
984 |
1034 |
985 def OnProjectTreeItemBeginEdit(self, event): |
1035 def OnProjectTreeItemBeginEdit(self, event): |
986 selected = event.GetItem() |
1036 selected = event.GetItem() |
987 if self.ProjectTree.GetPyData(selected)["type"] == ITEM_CONFNODE: |
1037 if self.ProjectTree.GetPyData(selected)["type"] == ITEM_CONFNODE: |
988 event.Veto() |
1038 event.Veto() |