laurent@428: #!/usr/bin/env python laurent@428: # -*- coding: utf-8 -*- laurent@428: andrej@1571: # This file is part of Beremiz, a Integrated Development Environment for andrej@1571: # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. laurent@428: # andrej@1571: # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD laurent@428: # andrej@1571: # See COPYING file for copyrights details. laurent@428: # andrej@1571: # This program is free software; you can redistribute it and/or andrej@1571: # modify it under the terms of the GNU General Public License andrej@1571: # as published by the Free Software Foundation; either version 2 andrej@1571: # of the License, or (at your option) any later version. laurent@428: # andrej@1571: # This program is distributed in the hope that it will be useful, andrej@1571: # but WITHOUT ANY WARRANTY; without even the implied warranty of andrej@1571: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the andrej@1571: # GNU General Public License for more details. laurent@428: # andrej@1571: # You should have received a copy of the GNU General Public License andrej@1571: # along with this program; if not, write to the Free Software andrej@1571: # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Edouard@588: Edouard@588: updateinfo_url = None laurent@428: Edouard@1451: import os, sys, getopt laurent@650: import __builtin__ laurent@428: import tempfile laurent@428: import shutil laurent@428: import random ed@446: import time andrej@1560: import version laurent@675: from types import ListType laurent@428: Edouard@1451: beremiz_dir = os.path.dirname(os.path.realpath(__file__)) Edouard@1451: alexander@1518: if __name__ == '__main__': alexander@1518: import wxversion alexander@1518: wxversion.select(['2.8', '3.0']) alexander@1518: import wx alexander@1518: Edouard@1451: from wx.lib.agw.advancedsplash import AdvancedSplash Edouard@1441: laurent@428: def Bpath(*args): Edouard@1451: return os.path.join(beremiz_dir,*args) laurent@428: andrej@1489: def ShowSplashScreen(): andrej@1489: bmp = wx.Image(Bpath("images", "splash.png")).ConvertToBitmap() andrej@1489: #splash=AdvancedSplash(None, bitmap=bmp, style=wx.SPLASH_CENTRE_ON_SCREEN, timeout=4000) andrej@1489: splash = AdvancedSplash(None, bitmap=bmp) andrej@1489: andrej@1489: # process all events andrej@1489: # even the events generated by splash themself during showing andrej@1489: for i in range(0,30): andrej@1489: wx.Yield() andrej@1489: time.sleep(0.01); andrej@1489: return splash andrej@1489: laurent@428: if __name__ == '__main__': laurent@428: def usage(): laurent@428: print "\nUsage of Beremiz.py :" laurent@428: print "\n %s [Projectpath] [Buildpath]\n"%sys.argv[0] Edouard@1408: laurent@428: try: Edouard@736: opts, args = getopt.getopt(sys.argv[1:], "hu:e:", ["help", "updatecheck=", "extend="]) laurent@428: except getopt.GetoptError: laurent@428: # print help information and exit: laurent@428: usage() laurent@428: sys.exit(2) Edouard@731: Edouard@731: extensions=[] Edouard@1408: laurent@428: for o, a in opts: laurent@428: if o in ("-h", "--help"): laurent@428: usage() laurent@428: sys.exit() Edouard@588: if o in ("-u", "--updatecheck"): Edouard@588: updateinfo_url = a Edouard@731: if o in ("-e", "--extend"): Edouard@731: extensions.append(a) Edouard@1408: laurent@428: if len(args) > 2: laurent@428: usage() laurent@428: sys.exit() laurent@428: elif len(args) == 1: laurent@428: projectOpen = args[0] laurent@428: buildpath = None laurent@428: elif len(args) == 2: laurent@428: projectOpen = args[0] laurent@428: buildpath = args[1] laurent@428: else: laurent@428: projectOpen = None laurent@428: buildpath = None Edouard@1408: edouard@571: if os.path.exists("BEREMIZ_DEBUG"): laurent@650: __builtin__.__dict__["BMZ_DBG"] = True edouard@571: else : laurent@650: __builtin__.__dict__["BMZ_DBG"] = False edouard@571: andrej@1483: if wx.VERSION >= (3, 0, 0): andrej@1483: app = wx.App(redirect=BMZ_DBG) andrej@1483: else: andrej@1483: app = wx.PySimpleApp(redirect=BMZ_DBG) andrej@1483: laurent@428: app.SetAppName('beremiz') andrej@1483: if wx.VERSION < (3, 0, 0): ewald@1572: wx.InitAllImageHandlers() Edouard@1408: Edouard@588: # popup splash andrej@1489: splash = ShowSplashScreen() andrej@1582: andrej@1582: # load internatialization files andrej@1582: from util.misc import InstallLocalRessources andrej@1582: InstallLocalRessources(beremiz_dir) andrej@1489: Edouard@588: if updateinfo_url is not None: andrej@1582: updateinfo = _("Fetching %s") % updateinfo_url Edouard@588: # warn for possible updates Edouard@588: def updateinfoproc(): Edouard@588: global updateinfo Edouard@588: try : Edouard@588: import urllib2 Edouard@588: updateinfo = urllib2.urlopen(updateinfo_url,None).read() Edouard@588: except : andrej@1582: updateinfo = _("update info unavailable.") Edouard@1408: Edouard@588: from threading import Thread Edouard@588: splash.SetText(text=updateinfo) Edouard@588: wx.Yield() Edouard@588: updateinfoThread = Thread(target=updateinfoproc) Edouard@588: updateinfoThread.start() Edouard@588: updateinfoThread.join(2) Edouard@588: splash.SetText(text=updateinfo) Edouard@588: wx.Yield() Edouard@588: Edouard@735: # Load extensions Edouard@735: for extfilename in extensions: Edouard@1393: from util.TranslationCatalogs import AddCatalog Edouard@1393: from util.BitmapLibrary import AddBitmapFolder laurent@781: extension_folder = os.path.split(os.path.realpath(extfilename))[0] laurent@781: sys.path.append(extension_folder) laurent@815: AddCatalog(os.path.join(extension_folder, "locale")) laurent@781: AddBitmapFolder(os.path.join(extension_folder, "images")) Edouard@737: execfile(extfilename, locals()) Edouard@735: Edouard@1388: Laurent@999: import wx.lib.buttons, wx.lib.statbmp, wx.stc Edouard@724: import cPickle laurent@428: import types, time, re, platform, time, traceback, commands laurent@428: Edouard@727: from docutil import OpenHtmlFrame Laurent@814: from editors.EditorPanel import EditorPanel Laurent@814: from editors.Viewer import Viewer Laurent@814: from editors.TextViewer import TextViewer Laurent@814: from editors.ResourceEditor import ConfigurationEditor, ResourceEditor Laurent@814: from editors.DataTypeEditor import DataTypeEditor laurent@807: from util.MiniTextControler import MiniTextControler laurent@807: from util.ProcessLogger import ProcessLogger Laurent@978: from controls.LogViewer import LogViewer Laurent@1091: from controls.CustomStyledTextCtrl import CustomStyledTextCtrl andrej@1574: from controls import EnhancedStatusBar as esb andrej@1565: from dialogs.AboutDialog import ShowAboutDialog Laurent@814: Laurent@814: from PLCControler import LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY, ITEM_PROJECT, ITEM_RESOURCE laurent@1282: from ProjectController import ProjectController, GetAddMenuItems, MATIEC_ERROR_MODEL, ITEM_CONFNODE laurent@807: Laurent@814: laurent@675: MAX_RECENT_PROJECTS = 10 laurent@675: Laurent@999: if wx.Platform == '__WXMSW__': Laurent@999: faces = { Laurent@999: 'mono' : 'Courier New', Laurent@999: 'size' : 8, Laurent@999: } Laurent@999: else: Laurent@999: faces = { Laurent@999: 'mono' : 'Courier', Laurent@999: 'size' : 10, Laurent@999: } Laurent@999: Edouard@705: from threading import Lock,Timer,currentThread Edouard@705: MainThread = currentThread().ident Edouard@686: REFRESH_PERIOD = 0.1 Edouard@686: from time import time as gettime laurent@428: class LogPseudoFile: laurent@428: """ Base class for file like objects to facilitate StdOut for the Shell.""" edouard@451: def __init__(self, output, risecall): Laurent@999: self.red_white = 1 Laurent@999: self.red_yellow = 2 Laurent@999: self.black_white = wx.stc.STC_STYLE_DEFAULT laurent@428: self.output = output edouard@451: self.risecall = risecall ed@446: # to prevent rapid fire on rising log panel ed@446: self.rising_timer = 0 Edouard@686: self.lock = Lock() Edouard@705: self.YieldLock = Lock() Edouard@686: self.RefreshLock = Lock() Laurent@881: self.TimerAccessLock = Lock() Edouard@686: self.stack = [] Edouard@686: self.LastRefreshTime = gettime() Edouard@688: self.LastRefreshTimer = None laurent@428: laurent@428: def write(self, s, style = None): Edouard@686: if self.lock.acquire(): Edouard@686: self.stack.append((s,style)) Edouard@686: self.lock.release() Edouard@686: current_time = gettime() Laurent@881: self.TimerAccessLock.acquire() Edouard@688: if self.LastRefreshTimer: Edouard@688: self.LastRefreshTimer.cancel() Edouard@688: self.LastRefreshTimer=None Laurent@881: self.TimerAccessLock.release() Edouard@686: if current_time - self.LastRefreshTime > REFRESH_PERIOD and self.RefreshLock.acquire(False): Edouard@705: self._should_write() Edouard@688: else: Laurent@881: self.TimerAccessLock.acquire() Laurent@875: self.LastRefreshTimer = Timer(REFRESH_PERIOD, self._timer_expired) Edouard@688: self.LastRefreshTimer.start() Laurent@881: self.TimerAccessLock.release() Edouard@686: Laurent@875: def _timer_expired(self): Laurent@875: if self.RefreshLock.acquire(False): Laurent@875: self._should_write() Laurent@875: else: Laurent@881: self.TimerAccessLock.acquire() Laurent@875: self.LastRefreshTimer = Timer(REFRESH_PERIOD, self._timer_expired) Laurent@875: self.LastRefreshTimer.start() Laurent@881: self.TimerAccessLock.release() Laurent@875: Edouard@705: def _should_write(self): Edouard@705: wx.CallAfter(self._write) Edouard@705: if MainThread == currentThread().ident: Edouard@705: app = wx.GetApp() Edouard@705: if app is not None: Edouard@705: if self.YieldLock.acquire(0): Edouard@705: app.Yield() Edouard@705: self.YieldLock.release() Edouard@705: Edouard@686: def _write(self): Edouard@701: if self.output : Laurent@875: self.output.Freeze() Edouard@701: self.lock.acquire() Edouard@701: for s, style in self.stack: Edouard@701: if style is None : style=self.black_white Laurent@999: if style != self.black_white: Laurent@999: self.output.StartStyling(self.output.GetLength(), 0xff) Edouard@1408: Laurent@1091: # Temporary deactivate read only mode on StyledTextCtrl for Edouard@1408: # adding text. It seems that text modifications, even Laurent@1091: # programmatically, are disabled in StyledTextCtrl when read Edouard@1408: # only is active andrej@1564: start_pos = self.output.GetLength() Laurent@1015: self.output.SetReadOnly(False) Laurent@1091: self.output.AppendText(s) Laurent@1015: self.output.SetReadOnly(True) andrej@1564: text_len = self.output.GetLength() - start_pos Edouard@1408: Laurent@999: if style != self.black_white: andrej@1564: self.output.SetStyling(text_len, style) Edouard@701: self.stack = [] Edouard@701: self.lock.release() Edouard@701: self.output.Thaw() Edouard@701: self.LastRefreshTime = gettime() Edouard@701: try: Edouard@701: self.RefreshLock.release() Edouard@701: except: Edouard@701: pass Edouard@701: newtime = time.time() Edouard@701: if newtime - self.rising_timer > 1: Laurent@999: self.risecall(self.output) Edouard@701: self.rising_timer = newtime Edouard@1408: laurent@428: def write_warning(self, s): laurent@428: self.write(s,self.red_white) laurent@428: laurent@428: def write_error(self, s): laurent@428: self.write(s,self.red_yellow) laurent@428: edouard@569: def writeyield(self, s): edouard@569: self.write(s) edouard@569: wx.GetApp().Yield() edouard@569: laurent@428: def flush(self): Laurent@1091: # Temporary deactivate read only mode on StyledTextCtrl for clearing Laurent@1091: # text. It seems that text modifications, even programmatically, are Edouard@1408: # disabled in StyledTextCtrl when read only is active Laurent@1025: self.output.SetReadOnly(False) Laurent@999: self.output.SetText("") Laurent@1025: self.output.SetReadOnly(True) Edouard@1408: laurent@428: def isatty(self): Laurent@1269: return False laurent@428: Edouard@1281: ID_FILEMENURECENTPROJECTS = wx.NewId() laurent@675: Edouard@1388: from IDEFrame import TITLE,\ Edouard@1388: EDITORTOOLBAR,\ Edouard@1388: FILEMENU,\ Edouard@1388: EDITMENU,\ Edouard@1388: DISPLAYMENU,\ Edouard@1388: PROJECTTREE,\ Edouard@1388: POUINSTANCEVARIABLESPANEL,\ Edouard@1388: LIBRARYTREE,\ Edouard@1388: SCALING,\ Edouard@1388: PAGETITLES,\ Edouard@1388: IDEFrame, AppendMenu,\ Edouard@1388: EncodeFileSystemPath, DecodeFileSystemPath Edouard@1388: from util.BitmapLibrary import GetBitmap Edouard@1388: laurent@428: class Beremiz(IDEFrame): Edouard@1408: laurent@675: def _init_utils(self): Edouard@717: self.ConfNodeMenu = wx.Menu(title='') laurent@675: self.RecentProjectsMenu = wx.Menu(title='') Edouard@1408: laurent@675: IDEFrame._init_utils(self) Edouard@1408: laurent@428: def _init_coll_FileMenu_Items(self, parent): laurent@428: AppendMenu(parent, help='', id=wx.ID_NEW, Laurent@814: kind=wx.ITEM_NORMAL, text=_(u'New') + '\tCTRL+N') laurent@428: AppendMenu(parent, help='', id=wx.ID_OPEN, Laurent@814: kind=wx.ITEM_NORMAL, text=_(u'Open') + '\tCTRL+O') Edouard@702: parent.AppendMenu(ID_FILEMENURECENTPROJECTS, _("&Recent Projects"), self.RecentProjectsMenu) laurent@675: parent.AppendSeparator() laurent@428: AppendMenu(parent, help='', id=wx.ID_SAVE, Laurent@814: kind=wx.ITEM_NORMAL, text=_(u'Save') + '\tCTRL+S') laurent@428: AppendMenu(parent, help='', id=wx.ID_SAVEAS, Laurent@814: kind=wx.ITEM_NORMAL, text=_(u'Save as') + '\tCTRL+SHIFT+S') laurent@428: AppendMenu(parent, help='', id=wx.ID_CLOSE, Laurent@814: kind=wx.ITEM_NORMAL, text=_(u'Close Tab') + '\tCTRL+W') laurent@428: AppendMenu(parent, help='', id=wx.ID_CLOSE_ALL, Laurent@814: kind=wx.ITEM_NORMAL, text=_(u'Close Project') + '\tCTRL+SHIFT+W') laurent@428: parent.AppendSeparator() laurent@428: AppendMenu(parent, help='', id=wx.ID_PAGE_SETUP, Laurent@814: kind=wx.ITEM_NORMAL, text=_(u'Page Setup') + '\tCTRL+ALT+P') laurent@428: AppendMenu(parent, help='', id=wx.ID_PREVIEW, Laurent@814: kind=wx.ITEM_NORMAL, text=_(u'Preview') + '\tCTRL+SHIFT+P') laurent@428: AppendMenu(parent, help='', id=wx.ID_PRINT, Laurent@814: kind=wx.ITEM_NORMAL, text=_(u'Print') + '\tCTRL+P') laurent@428: parent.AppendSeparator() laurent@428: AppendMenu(parent, help='', id=wx.ID_EXIT, Laurent@814: kind=wx.ITEM_NORMAL, text=_(u'Quit') + '\tCTRL+Q') Edouard@1408: laurent@428: self.Bind(wx.EVT_MENU, self.OnNewProjectMenu, id=wx.ID_NEW) laurent@428: self.Bind(wx.EVT_MENU, self.OnOpenProjectMenu, id=wx.ID_OPEN) laurent@428: self.Bind(wx.EVT_MENU, self.OnSaveProjectMenu, id=wx.ID_SAVE) laurent@428: self.Bind(wx.EVT_MENU, self.OnSaveProjectAsMenu, id=wx.ID_SAVEAS) laurent@428: self.Bind(wx.EVT_MENU, self.OnCloseTabMenu, id=wx.ID_CLOSE) laurent@428: self.Bind(wx.EVT_MENU, self.OnCloseProjectMenu, id=wx.ID_CLOSE_ALL) laurent@428: self.Bind(wx.EVT_MENU, self.OnPageSetupMenu, id=wx.ID_PAGE_SETUP) laurent@428: self.Bind(wx.EVT_MENU, self.OnPreviewMenu, id=wx.ID_PREVIEW) laurent@428: self.Bind(wx.EVT_MENU, self.OnPrintMenu, id=wx.ID_PRINT) laurent@428: self.Bind(wx.EVT_MENU, self.OnQuitMenu, id=wx.ID_EXIT) Edouard@1408: laurent@781: self.AddToMenuToolBar([(wx.ID_NEW, "new", _(u'New'), None), laurent@781: (wx.ID_OPEN, "open", _(u'Open'), None), laurent@781: (wx.ID_SAVE, "save", _(u'Save'), None), laurent@781: (wx.ID_SAVEAS, "saveas", _(u'Save As...'), None), laurent@781: (wx.ID_PRINT, "print", _(u'Print'), None)]) Edouard@1408: laurent@1282: def _RecursiveAddMenuItems(self, menu, items): laurent@1282: for name, text, help, children in items: laurent@1282: new_id = wx.NewId() laurent@1282: if len(children) > 0: laurent@1282: new_menu = wx.Menu(title='') laurent@1282: menu.AppendMenu(new_id, text, new_menu) laurent@1282: self._RecursiveAddMenuItems(new_menu, children) laurent@1282: else: Edouard@1408: AppendMenu(menu, help=help, id=new_id, laurent@1282: kind=wx.ITEM_NORMAL, text=text) Edouard@1408: self.Bind(wx.EVT_MENU, self.GetAddConfNodeFunction(name), Edouard@1408: id=new_id) Edouard@1408: laurent@738: def _init_coll_AddMenu_Items(self, parent): laurent@738: IDEFrame._init_coll_AddMenu_Items(self, parent, False) laurent@1282: self._RecursiveAddMenuItems(parent, GetAddMenuItems()) Edouard@1408: laurent@428: def _init_coll_HelpMenu_Items(self, parent): laurent@428: parent.Append(help='', id=wx.ID_ABOUT, laurent@428: kind=wx.ITEM_NORMAL, text=_(u'About')) laurent@428: self.Bind(wx.EVT_MENU, self.OnAboutMenu, id=wx.ID_ABOUT) Edouard@1408: Laurent@1000: def _init_coll_ConnectionStatusBar_Fields(self, parent): Laurent@1000: parent.SetFieldsCount(3) Laurent@1000: Laurent@1000: parent.SetStatusText(number=0, text='') Laurent@1000: parent.SetStatusText(number=1, text='') Laurent@1000: parent.SetStatusText(number=2, text='') Laurent@1000: Laurent@1000: parent.SetStatusWidths([-1, 300, 200]) Edouard@1408: laurent@428: def _init_ctrls(self, prnt): laurent@428: IDEFrame._init_ctrls(self, prnt) Edouard@1408: laurent@771: self.EditMenuSize = self.EditMenu.GetMenuItemCount() Edouard@1408: Edouard@1281: inspectorID = wx.NewId() Edouard@1281: self.Bind(wx.EVT_MENU, self.OnOpenWidgetInspector, id=inspectorID) Edouard@1281: accels = [wx.AcceleratorEntry(wx.ACCEL_CTRL|wx.ACCEL_ALT, ord('I'), inspectorID)] surkovsv93@1530: surkovsv93@1530: keyID = wx.NewId() surkovsv93@1530: self.Bind(wx.EVT_MENU, self.SwitchFullScrMode, id=keyID) surkovsv93@1530: accels += [wx.AcceleratorEntry(wx.ACCEL_NORMAL, wx.WXK_F12, keyID)] surkovsv93@1530: Edouard@623: for method,shortcut in [("Stop", wx.WXK_F4), Edouard@623: ("Run", wx.WXK_F5), Edouard@623: ("Transfer", wx.WXK_F6), Edouard@623: ("Connect", wx.WXK_F7), Edouard@623: ("Build", wx.WXK_F11)]: Edouard@623: def OnMethodGen(obj,meth): Edouard@623: def OnMethod(evt): Edouard@717: if obj.CTR is not None: Edouard@717: obj.CTR.CallMethod('_'+meth) laurent@738: wx.CallAfter(self.RefreshStatusToolBar) Edouard@623: return OnMethod Edouard@623: newid = wx.NewId() Edouard@623: self.Bind(wx.EVT_MENU, OnMethodGen(self,method), id=newid) Edouard@623: accels += [wx.AcceleratorEntry(wx.ACCEL_NORMAL, shortcut,newid)] Edouard@1408: Edouard@623: self.SetAcceleratorTable(wx.AcceleratorTable(accels)) Edouard@1408: Edouard@1281: self.LogConsole = CustomStyledTextCtrl( laurent@428: name='LogConsole', parent=self.BottomNoteBook, pos=wx.Point(0, 0), Laurent@999: size=wx.Size(0, 0)) Laurent@1015: self.LogConsole.Bind(wx.EVT_SET_FOCUS, self.OnLogConsoleFocusChanged) Laurent@1015: self.LogConsole.Bind(wx.EVT_KILL_FOCUS, self.OnLogConsoleFocusChanged) Laurent@1015: self.LogConsole.Bind(wx.stc.EVT_STC_UPDATEUI, self.OnLogConsoleUpdateUI) Laurent@1015: self.LogConsole.SetReadOnly(True) Laurent@1007: self.LogConsole.SetWrapMode(wx.stc.STC_WRAP_CHAR) Edouard@1408: Laurent@999: # Define Log Console styles Laurent@999: self.LogConsole.StyleSetSpec(wx.stc.STC_STYLE_DEFAULT, "face:%(mono)s,size:%(size)d" % faces) Laurent@999: self.LogConsole.StyleClearAll() Laurent@999: self.LogConsole.StyleSetSpec(1, "face:%(mono)s,fore:#FF0000,size:%(size)d" % faces) Laurent@999: self.LogConsole.StyleSetSpec(2, "face:%(mono)s,fore:#FF0000,back:#FFFF00,size:%(size)d" % faces) Edouard@1408: Laurent@999: # Define Log Console markers Laurent@999: self.LogConsole.SetMarginSensitive(1, True) Laurent@999: self.LogConsole.SetMarginType(1, wx.stc.STC_MARGIN_SYMBOL) Laurent@999: self.LogConsole.MarkerDefine(0, wx.stc.STC_MARK_CIRCLE, "BLACK", "RED") Edouard@1408: Laurent@999: self.LogConsole.SetModEventMask(wx.stc.STC_MOD_INSERTTEXT) Edouard@1408: Laurent@999: self.LogConsole.Bind(wx.stc.EVT_STC_MARGINCLICK, self.OnLogConsoleMarginClick) Laurent@999: self.LogConsole.Bind(wx.stc.EVT_STC_MODIFIED, self.OnLogConsoleModified) Edouard@1408: Laurent@986: self.MainTabs["LogConsole"] = (self.LogConsole, _("Console")) laurent@715: self.BottomNoteBook.AddPage(*self.MainTabs["LogConsole"]) Laurent@999: #self.BottomNoteBook.Split(self.BottomNoteBook.GetPageIndex(self.LogConsole), wx.RIGHT) Edouard@1408: Laurent@978: self.LogViewer = LogViewer(self.BottomNoteBook, self) Laurent@986: self.MainTabs["LogViewer"] = (self.LogViewer, _("PLC Log")) Laurent@978: self.BottomNoteBook.AddPage(*self.MainTabs["LogViewer"]) Laurent@994: #self.BottomNoteBook.Split(self.BottomNoteBook.GetPageIndex(self.LogViewer), wx.RIGHT) Edouard@1408: laurent@738: StatusToolBar = wx.ToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize, laurent@738: wx.TB_FLAT | wx.TB_NODIVIDER | wx.NO_BORDER) laurent@738: StatusToolBar.SetToolBitmapSize(wx.Size(25, 25)) laurent@738: StatusToolBar.Realize() laurent@738: self.Panes["StatusToolBar"] = StatusToolBar laurent@738: self.AUIManager.AddPane(StatusToolBar, wx.aui.AuiPaneInfo(). laurent@738: Name("StatusToolBar").Caption(_("Status ToolBar")). laurent@765: ToolbarPane().Top().Position(1). laurent@738: LeftDockable(False).RightDockable(False)) Edouard@1408: laurent@738: self.AUIManager.Update() Edouard@1408: andrej@1574: self.ConnectionStatusBar = esb.EnhancedStatusBar(self, style=wx.ST_SIZEGRIP) Laurent@1000: self._init_coll_ConnectionStatusBar_Fields(self.ConnectionStatusBar) andrej@1574: self.ProgressStatusBar = wx.Gauge(self.ConnectionStatusBar, -1, range = 100) andrej@1574: self.ConnectionStatusBar.AddWidget(self.ProgressStatusBar, esb.ESB_EXACT_FIT, esb.ESB_EXACT_FIT, 2) andrej@1574: self.ProgressStatusBar.Hide() Laurent@1000: self.SetStatusBar(self.ConnectionStatusBar) Edouard@1408: andrej@1549: def __init_execute_path(self): andrej@1549: if os.name == 'nt': andrej@1549: # on windows, desktop shortcut launches Beremiz.py andrej@1549: # with working dir set to mingw/bin. andrej@1549: # then we prefix CWD to PATH in order to ensure that andrej@1549: # commands invoked by build process by default are andrej@1549: # found here. andrej@1549: os.environ["PATH"] = os.getcwd()+';'+os.environ["PATH"] andrej@1549: andrej@1549: Edouard@717: def __init__(self, parent, projectOpen=None, buildpath=None, ctr=None, debug=True): andrej@1490: # Add beremiz's icon in top left corner of the frame andrej@1490: self.icon = wx.Icon(Bpath("images", "brz.ico"), wx.BITMAP_TYPE_ICO) andrej@1549: self.__init_execute_path() andrej@1490: laurent@428: IDEFrame.__init__(self, parent, debug) Laurent@999: self.Log = LogPseudoFile(self.LogConsole,self.SelectTab) Edouard@1408: laurent@428: self.local_runtime = None laurent@428: self.runtime_port = None laurent@428: self.local_runtime_tmpdir = None Edouard@1408: laurent@675: self.LastPanelSelected = None Edouard@1408: laurent@650: # Define Tree item icon list laurent@650: self.LocationImageList = wx.ImageList(16, 16) laurent@650: self.LocationImageDict = {} Edouard@1408: laurent@650: # Icons for location items laurent@650: for imgname, itemtype in [ Edouard@717: ("CONFIGURATION", LOCATION_CONFNODE), laurent@650: ("RESOURCE", LOCATION_MODULE), laurent@650: ("PROGRAM", LOCATION_GROUP), laurent@650: ("VAR_INPUT", LOCATION_VAR_INPUT), laurent@650: ("VAR_OUTPUT", LOCATION_VAR_OUTPUT), laurent@650: ("VAR_LOCAL", LOCATION_VAR_MEMORY)]: laurent@781: self.LocationImageDict[itemtype] = self.LocationImageList.Add(GetBitmap(imgname)) Edouard@1408: laurent@738: # Icons for other items laurent@738: for imgname, itemtype in [ laurent@738: ("Extension", ITEM_CONFNODE)]: laurent@781: self.TreeImageDict[itemtype] = self.TreeImageList.Add(GetBitmap(imgname)) Edouard@1408: laurent@788: if projectOpen is not None: laurent@788: projectOpen = DecodeFileSystemPath(projectOpen, False) Edouard@1408: laurent@428: if projectOpen is not None and os.path.isdir(projectOpen): Edouard@725: self.CTR = ProjectController(self, self.Log) Edouard@717: self.Controler = self.CTR Edouard@717: result = self.CTR.LoadProject(projectOpen, buildpath) laurent@679: if not result: laurent@781: self.LibraryPanel.SetController(self.Controler) laurent@738: self.ProjectTree.Enable(True) laurent@730: self.PouInstanceVariablesPanel.SetController(self.Controler) laurent@679: self.RefreshConfigRecentProjects(os.path.abspath(projectOpen)) laurent@730: self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) laurent@679: else: laurent@679: self.ResetView() laurent@679: self.ShowErrorMessage(result) laurent@428: else: Edouard@717: self.CTR = ctr Edouard@717: self.Controler = ctr Edouard@717: if ctr is not None: laurent@781: self.LibraryPanel.SetController(self.Controler) laurent@738: self.ProjectTree.Enable(True) laurent@730: self.PouInstanceVariablesPanel.SetController(self.Controler) laurent@730: self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) laurent@490: if self.EnableDebug: Edouard@717: self.DebugVariablePanel.SetDataProducer(self.CTR) Edouard@1408: laurent@428: self.Bind(wx.EVT_CLOSE, self.OnCloseFrame) Edouard@1408: laurent@706: self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU) laurent@755: self.RefreshAll() Edouard@590: self.LogConsole.SetFocus() laurent@428: laurent@428: def RefreshTitle(self): laurent@428: name = _("Beremiz") Edouard@717: if self.CTR is not None: Edouard@717: projectname = self.CTR.GetProjectName() Edouard@717: if self.CTR.ProjectTestModified(): laurent@428: projectname = "~%s~" % projectname laurent@428: self.SetTitle("%s - %s" % (name, projectname)) laurent@428: else: laurent@428: self.SetTitle(name) laurent@428: laurent@428: def StartLocalRuntime(self, taskbaricon = True): Edouard@713: if (self.local_runtime is None) or (self.local_runtime.exitcode is not None): laurent@428: # create temporary directory for runtime working directory laurent@428: self.local_runtime_tmpdir = tempfile.mkdtemp() laurent@428: # choose an arbitrary random port for runtime laurent@428: self.runtime_port = int(random.random() * 1000) + 61131 laurent@428: # launch local runtime laurent@428: self.local_runtime = ProcessLogger(self.Log, Edouard@958: "\"%s\" \"%s\" -p %s -i localhost %s %s"%( Edouard@958: sys.executable, Edouard@958: Bpath("Beremiz_service.py"), Edouard@958: self.runtime_port, Edouard@958: {False : "-x 0", True :"-x 1"}[taskbaricon], Edouard@958: self.local_runtime_tmpdir), Edouard@958: no_gui=False, andrej@1595: timeout=500, keyword = self.local_runtime_tmpdir, Edouard@958: cwd = self.local_runtime_tmpdir) Edouard@704: self.local_runtime.spin() laurent@428: return self.runtime_port Edouard@1408: laurent@428: def KillLocalRuntime(self): laurent@428: if self.local_runtime is not None: laurent@428: # shutdown local runtime laurent@428: self.local_runtime.kill(gently=False) laurent@428: # clear temp dir laurent@428: shutil.rmtree(self.local_runtime_tmpdir) Edouard@1408: laurent@539: self.local_runtime = None laurent@428: laurent@428: def OnOpenWidgetInspector(self, evt): laurent@428: # Activate the widget inspection tool laurent@428: from wx.lib.inspection import InspectionTool laurent@428: if not InspectionTool().initialized: laurent@428: InspectionTool().Init() laurent@428: laurent@428: # Find a widget to be selected in the tree. Use either the laurent@428: # one under the cursor, if any, or this frame. laurent@428: wnd = wx.FindWindowAtPointer() laurent@428: if not wnd: laurent@428: wnd = self laurent@428: InspectionTool().Show(wnd, True) laurent@428: Laurent@1015: def OnLogConsoleFocusChanged(self, event): Laurent@1087: self.RefreshEditMenu() Laurent@1015: event.Skip() Laurent@1015: Laurent@1015: def OnLogConsoleUpdateUI(self, event): Laurent@1015: self.SetCopyBuffer(self.LogConsole.GetSelectedText(), True) Laurent@1015: event.Skip() Laurent@1015: Laurent@999: def OnLogConsoleMarginClick(self, event): Laurent@999: line_idx = self.LogConsole.LineFromPosition(event.GetPosition()) Laurent@999: wx.CallAfter(self.SearchLineForError, self.LogConsole.GetLine(line_idx)) laurent@428: event.Skip() Edouard@1408: Laurent@999: def OnLogConsoleModified(self, event): Laurent@999: line_idx = self.LogConsole.LineFromPosition(event.GetPosition()) Laurent@999: line = self.LogConsole.GetLine(line_idx) Laurent@999: if line: Laurent@999: result = MATIEC_ERROR_MODEL.match(line) Laurent@999: if result is not None: Laurent@999: self.LogConsole.MarkerAdd(line_idx, 0) Laurent@999: event.Skip() Laurent@999: Laurent@999: def SearchLineForError(self, line): Edouard@717: if self.CTR is not None: laurent@428: result = MATIEC_ERROR_MODEL.match(line) laurent@428: if result is not None: laurent@428: first_line, first_column, last_line, last_column, error = result.groups() Edouard@717: infos = self.CTR.ShowError(self.Log, Edouard@1408: (int(first_line), int(first_column)), laurent@428: (int(last_line), int(last_column))) Edouard@1408: laurent@429: ## Function displaying an Error dialog in PLCOpenEditor. laurent@429: # @return False if closing cancelled. laurent@429: def CheckSaveBeforeClosing(self, title=_("Close Project")): Edouard@717: if self.CTR.ProjectTestModified(): laurent@429: dialog = wx.MessageDialog(self, laurent@429: _("There are changes, do you want to save?"), laurent@429: title, laurent@429: wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION) laurent@429: answer = dialog.ShowModal() laurent@429: dialog.Destroy() laurent@429: if answer == wx.ID_YES: Edouard@717: self.CTR.SaveProject() laurent@429: elif answer == wx.ID_CANCEL: laurent@429: return False Edouard@1408: laurent@797: for idx in xrange(self.TabsOpened.GetPageCount()): laurent@797: window = self.TabsOpened.GetPage(idx) laurent@797: if not window.CheckSaveBeforeClosing(): laurent@797: return False Edouard@1408: laurent@429: return True Edouard@1408: laurent@715: def GetTabInfos(self, tab): Edouard@1408: if (isinstance(tab, EditorPanel) and Edouard@1408: not isinstance(tab, (Viewer, Edouard@1408: TextViewer, Edouard@1408: ResourceEditor, Edouard@1408: ConfigurationEditor, laurent@715: DataTypeEditor))): laurent@782: return ("confnode", tab.Controler.CTNFullName(), tab.GetTagName()) Edouard@1408: elif (isinstance(tab, TextViewer) and laurent@716: (tab.Controler is None or isinstance(tab.Controler, MiniTextControler))): Edouard@717: return ("confnode", None, tab.GetInstancePath()) laurent@715: else: laurent@715: return IDEFrame.GetTabInfos(self, tab) Edouard@1408: laurent@715: def LoadTab(self, notebook, page_infos): Edouard@717: if page_infos[0] == "confnode": laurent@716: if page_infos[1] is None: Edouard@717: confnode = self.CTR laurent@716: else: Edouard@717: confnode = self.CTR.GetChildByName(page_infos[1]) Edouard@717: return notebook.GetPageIndex(confnode._OpenView(*page_infos[2:])) laurent@715: else: laurent@715: return IDEFrame.LoadTab(self, notebook, page_infos) Edouard@1408: Edouard@1441: # Strange hack required by WAMP connector, using twisted. Edouard@1441: # Twisted reactor needs to be stopped only before quit, Edouard@1441: # since it cannot be restarted Edouard@1441: ToDoBeforeQuit = [] Edouard@1441: def AddToDoBeforeQuit(self, Thing): Edouard@1441: self.ToDoBeforeQuit.append(Thing) Edouard@1441: laurent@428: def OnCloseFrame(self, event): Edouard@1408: for evt_type in [wx.EVT_SET_FOCUS, Edouard@1408: wx.EVT_KILL_FOCUS, Laurent@1090: wx.stc.EVT_STC_UPDATEUI]: Laurent@1090: self.LogConsole.Unbind(evt_type) Edouard@717: if self.CTR is None or self.CheckSaveBeforeClosing(_("Close Application")): Edouard@717: if self.CTR is not None: Edouard@717: self.CTR.KillDebugThread() laurent@429: self.KillLocalRuntime() Edouard@1408: laurent@715: self.SaveLastState() Edouard@1408: Edouard@1441: for Thing in self.ToDoBeforeQuit : Edouard@1441: Thing() Edouard@1441: self.ToDoBeforeQuit = [] Edouard@1441: laurent@429: event.Skip() laurent@429: else: laurent@429: event.Veto() Edouard@1408: laurent@428: def RefreshFileMenu(self): laurent@675: self.RefreshRecentProjectsMenu() Edouard@1408: laurent@706: MenuToolBar = self.Panes["MenuToolBar"] Edouard@717: if self.CTR is not None: laurent@428: selected = self.TabsOpened.GetSelection() laurent@428: if selected >= 0: laurent@784: window = self.TabsOpened.GetPage(selected) laurent@784: viewer_is_modified = window.IsModified() laurent@784: is_viewer = isinstance(window, Viewer) laurent@428: else: laurent@784: viewer_is_modified = is_viewer = False laurent@428: if self.TabsOpened.GetPageCount() > 0: laurent@428: self.FileMenu.Enable(wx.ID_CLOSE, True) laurent@784: if is_viewer: laurent@428: self.FileMenu.Enable(wx.ID_PREVIEW, True) laurent@428: self.FileMenu.Enable(wx.ID_PRINT, True) laurent@706: MenuToolBar.EnableTool(wx.ID_PRINT, True) laurent@428: else: laurent@428: self.FileMenu.Enable(wx.ID_PREVIEW, False) laurent@428: self.FileMenu.Enable(wx.ID_PRINT, False) laurent@706: MenuToolBar.EnableTool(wx.ID_PRINT, False) laurent@428: else: laurent@428: self.FileMenu.Enable(wx.ID_CLOSE, False) laurent@428: self.FileMenu.Enable(wx.ID_PREVIEW, False) laurent@428: self.FileMenu.Enable(wx.ID_PRINT, False) laurent@706: MenuToolBar.EnableTool(wx.ID_PRINT, False) laurent@428: self.FileMenu.Enable(wx.ID_PAGE_SETUP, True) laurent@784: project_modified = self.CTR.ProjectTestModified() or viewer_is_modified laurent@706: self.FileMenu.Enable(wx.ID_SAVE, project_modified) laurent@706: MenuToolBar.EnableTool(wx.ID_SAVE, project_modified) laurent@428: self.FileMenu.Enable(wx.ID_SAVEAS, True) laurent@706: MenuToolBar.EnableTool(wx.ID_SAVEAS, True) laurent@428: self.FileMenu.Enable(wx.ID_CLOSE_ALL, True) laurent@428: else: laurent@428: self.FileMenu.Enable(wx.ID_CLOSE, False) laurent@428: self.FileMenu.Enable(wx.ID_PAGE_SETUP, False) laurent@428: self.FileMenu.Enable(wx.ID_PREVIEW, False) laurent@428: self.FileMenu.Enable(wx.ID_PRINT, False) laurent@706: MenuToolBar.EnableTool(wx.ID_PRINT, False) laurent@428: self.FileMenu.Enable(wx.ID_SAVE, False) laurent@706: MenuToolBar.EnableTool(wx.ID_SAVE, False) laurent@428: self.FileMenu.Enable(wx.ID_SAVEAS, False) laurent@706: MenuToolBar.EnableTool(wx.ID_SAVEAS, False) laurent@428: self.FileMenu.Enable(wx.ID_CLOSE_ALL, False) Edouard@1408: laurent@675: def RefreshRecentProjectsMenu(self): laurent@793: try: Edouard@1408: recent_projects = map(DecodeFileSystemPath, laurent@793: self.GetConfigEntry("RecentProjects", [])) laurent@793: except: laurent@793: recent_projects = [] surkovsv93@1601: surkovsv93@1601: while self.RecentProjectsMenu.GetMenuItemCount() > len(recent_projects): surkovsv93@1601: item = self.RecentProjectsMenu.FindItemByPosition(0) surkovsv93@1601: self.RecentProjectsMenu.RemoveItem(item) surkovsv93@1601: laurent@675: self.FileMenu.Enable(ID_FILEMENURECENTPROJECTS, len(recent_projects) > 0) laurent@675: for idx, projectpath in enumerate(recent_projects): laurent@730: text = u'%d: %s' % (idx + 1, projectpath) Edouard@1408: laurent@730: if idx < self.RecentProjectsMenu.GetMenuItemCount(): laurent@730: item = self.RecentProjectsMenu.FindItemByPosition(idx) laurent@730: id = item.GetId() laurent@730: item.SetItemLabel(text) laurent@730: self.Disconnect(id, id, wx.EVT_BUTTON._getEvtType()) laurent@730: else: laurent@730: id = wx.NewId() Edouard@1408: AppendMenu(self.RecentProjectsMenu, help='', id=id, laurent@730: kind=wx.ITEM_NORMAL, text=text) laurent@675: self.Bind(wx.EVT_MENU, self.GenerateOpenRecentProjectFunction(projectpath), id=id) Edouard@1408: laurent@675: def GenerateOpenRecentProjectFunction(self, projectpath): laurent@675: def OpenRecentProject(event): Edouard@717: if self.CTR is not None and not self.CheckSaveBeforeClosing(): laurent@675: return Edouard@1408: laurent@675: self.OpenProject(projectpath) laurent@675: return OpenRecentProject Edouard@1408: laurent@675: def GenerateMenuRecursive(self, items, menu): laurent@675: for kind, infos in items: laurent@675: if isinstance(kind, ListType): laurent@675: text, id = infos laurent@675: submenu = wx.Menu('') laurent@675: self.GenerateMenuRecursive(kind, submenu) laurent@675: menu.AppendMenu(id, text, submenu) laurent@675: elif kind == wx.ITEM_SEPARATOR: laurent@675: menu.AppendSeparator() laurent@675: else: laurent@675: text, id, help, callback = infos laurent@675: AppendMenu(menu, help='', id=id, kind=kind, text=text) laurent@675: if callback is not None: laurent@675: self.Bind(wx.EVT_MENU, callback, id=id) Edouard@1408: laurent@766: def RefreshEditorToolBar(self): laurent@766: IDEFrame.RefreshEditorToolBar(self) laurent@766: self.AUIManager.GetPane("EditorToolBar").Position(2) laurent@766: self.AUIManager.GetPane("StatusToolBar").Position(1) laurent@766: self.AUIManager.Update() Edouard@1408: laurent@738: def RefreshStatusToolBar(self): laurent@738: StatusToolBar = self.Panes["StatusToolBar"] laurent@738: StatusToolBar.ClearTools() Edouard@1408: laurent@738: if self.CTR is not None: Edouard@1408: laurent@738: for confnode_method in self.CTR.StatusMethods: laurent@738: if "method" in confnode_method and confnode_method.get("shown",True): laurent@738: id = wx.NewId() Edouard@1408: StatusToolBar.AddSimpleTool(id, Edouard@1408: GetBitmap(confnode_method.get("bitmap", "Unknown")), laurent@738: confnode_method["tooltip"]) laurent@738: self.Bind(wx.EVT_MENU, self.GetMenuCallBackFunction(confnode_method["method"]), id=id) Edouard@1408: laurent@738: StatusToolBar.Realize() laurent@738: self.AUIManager.GetPane("StatusToolBar").BestSize(StatusToolBar.GetBestSize()).Show() laurent@738: else: laurent@738: self.AUIManager.GetPane("StatusToolBar").Hide() laurent@766: self.AUIManager.GetPane("EditorToolBar").Position(2) laurent@765: self.AUIManager.GetPane("StatusToolBar").Position(1) laurent@738: self.AUIManager.Update() Edouard@1408: laurent@771: def RefreshEditMenu(self): laurent@771: IDEFrame.RefreshEditMenu(self) Laurent@1015: if self.FindFocus() == self.LogConsole: Laurent@1015: self.EditMenu.Enable(wx.ID_COPY, True) Laurent@1015: self.Panes["MenuToolBar"].EnableTool(wx.ID_COPY, True) Edouard@1408: Edouard@717: if self.CTR is not None: laurent@675: selected = self.TabsOpened.GetSelection() laurent@675: if selected >= 0: laurent@675: panel = self.TabsOpened.GetPage(selected) laurent@675: else: laurent@675: panel = None laurent@675: if panel != self.LastPanelSelected: laurent@771: for i in xrange(self.EditMenuSize, self.EditMenu.GetMenuItemCount()): laurent@771: item = self.EditMenu.FindItemByPosition(self.EditMenuSize) laurent@781: if item is not None: laurent@781: if item.IsSeparator(): laurent@781: self.EditMenu.RemoveItem(item) laurent@781: else: laurent@781: self.EditMenu.Delete(item.GetId()) laurent@675: self.LastPanelSelected = panel laurent@675: if panel is not None: Edouard@717: items = panel.GetConfNodeMenuItems() laurent@675: else: laurent@675: items = [] laurent@771: if len(items) > 0: laurent@771: self.EditMenu.AppendSeparator() laurent@771: self.GenerateMenuRecursive(items, self.EditMenu) laurent@675: if panel is not None: laurent@771: panel.RefreshConfNodeMenu(self.EditMenu) laurent@771: else: laurent@771: for i in xrange(self.EditMenuSize, self.EditMenu.GetMenuItemCount()): laurent@771: item = self.EditMenu.FindItemByPosition(i) laurent@781: if item is not None: laurent@781: if item.IsSeparator(): laurent@781: self.EditMenu.RemoveItem(item) laurent@781: else: laurent@781: self.EditMenu.Delete(item.GetId()) laurent@771: self.LastPanelSelected = None laurent@675: self.MenuBar.UpdateMenus() Edouard@1408: laurent@428: def RefreshAll(self): laurent@755: self.RefreshStatusToolBar() Edouard@1408: laurent@738: def GetMenuCallBackFunction(self, method): laurent@738: """ Generate the callbackfunc for a given CTR method""" laurent@738: def OnMenu(event): Edouard@1408: # Disable button to prevent re-entrant call laurent@738: event.GetEventObject().Disable() laurent@738: # Call laurent@738: getattr(self.CTR, method)() Edouard@1408: # Re-enable button laurent@738: event.GetEventObject().Enable() laurent@738: return OnMenu Edouard@1408: laurent@738: def GetConfigEntry(self, entry_name, default): laurent@738: return cPickle.loads(str(self.Config.Read(entry_name, cPickle.dumps(default)))) Edouard@1408: Laurent@1000: def ResetConnectionStatusBar(self): Laurent@1000: for field in xrange(self.ConnectionStatusBar.GetFieldsCount()): Laurent@1000: self.ConnectionStatusBar.SetStatusText('', field) Edouard@1408: laurent@428: def ResetView(self): laurent@428: IDEFrame.ResetView(self) Edouard@717: self.ConfNodeInfos = {} Edouard@717: if self.CTR is not None: Edouard@717: self.CTR.CloseProject() Edouard@717: self.CTR = None laurent@428: self.Log.flush() laurent@490: if self.EnableDebug: laurent@490: self.DebugVariablePanel.SetDataProducer(None) Laurent@1000: self.ResetConnectionStatusBar() Edouard@1408: surkovsv93@1601: def RefreshConfigRecentProjects(self, projectpath, err=False): laurent@793: try: Edouard@1408: recent_projects = map(DecodeFileSystemPath, laurent@793: self.GetConfigEntry("RecentProjects", [])) laurent@793: except: laurent@793: recent_projects = [] laurent@679: if projectpath in recent_projects: laurent@679: recent_projects.remove(projectpath) surkovsv93@1601: if not err: surkovsv93@1601: recent_projects.insert(0, projectpath) laurent@788: self.Config.Write("RecentProjects", cPickle.dumps( laurent@788: map(EncodeFileSystemPath, recent_projects[:MAX_RECENT_PROJECTS]))) laurent@679: self.Config.Flush() Edouard@1408: laurent@743: def ResetPerspective(self): laurent@743: IDEFrame.ResetPerspective(self) laurent@743: self.RefreshStatusToolBar() Edouard@1408: laurent@428: def OnNewProjectMenu(self, event): Edouard@717: if self.CTR is not None and not self.CheckSaveBeforeClosing(): laurent@429: return Edouard@1408: laurent@790: try: laurent@790: defaultpath = DecodeFileSystemPath(self.Config.Read("lastopenedfolder")) laurent@790: except: laurent@428: defaultpath = os.path.expanduser("~") Edouard@1408: laurent@833: dialog = wx.DirDialog(self , _("Choose a project"), defaultpath) laurent@428: if dialog.ShowModal() == wx.ID_OK: laurent@428: projectpath = dialog.GetPath() Edouard@1408: self.Config.Write("lastopenedfolder", laurent@788: EncodeFileSystemPath(os.path.dirname(projectpath))) laurent@428: self.Config.Flush() laurent@428: self.ResetView() Edouard@725: ctr = ProjectController(self, self.Log) Edouard@717: result = ctr.NewProject(projectpath) laurent@428: if not result: Edouard@717: self.CTR = ctr Edouard@717: self.Controler = self.CTR laurent@781: self.LibraryPanel.SetController(self.Controler) laurent@738: self.ProjectTree.Enable(True) laurent@730: self.PouInstanceVariablesPanel.SetController(self.Controler) laurent@679: self.RefreshConfigRecentProjects(projectpath) laurent@490: if self.EnableDebug: Edouard@717: self.DebugVariablePanel.SetDataProducer(self.CTR) laurent@730: self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) laurent@428: else: laurent@428: self.ResetView() laurent@428: self.ShowErrorMessage(result) laurent@755: self.RefreshAll() laurent@706: self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU) laurent@679: dialog.Destroy() Edouard@1408: laurent@428: def OnOpenProjectMenu(self, event): Edouard@717: if self.CTR is not None and not self.CheckSaveBeforeClosing(): laurent@429: return Edouard@1408: laurent@790: try: laurent@790: defaultpath = DecodeFileSystemPath(self.Config.Read("lastopenedfolder")) laurent@790: except: laurent@428: defaultpath = os.path.expanduser("~") Edouard@1408: laurent@833: dialog = wx.DirDialog(self , _("Choose a project"), defaultpath, style=wx.DEFAULT_DIALOG_STYLE| laurent@833: wx.RESIZE_BORDER) laurent@428: if dialog.ShowModal() == wx.ID_OK: laurent@675: self.OpenProject(dialog.GetPath()) laurent@675: dialog.Destroy() Edouard@1408: laurent@675: def OpenProject(self, projectpath): laurent@675: if os.path.isdir(projectpath): Edouard@1408: self.Config.Write("lastopenedfolder", laurent@788: EncodeFileSystemPath(os.path.dirname(projectpath))) laurent@675: self.Config.Flush() laurent@675: self.ResetView() Edouard@725: self.CTR = ProjectController(self, self.Log) Edouard@717: self.Controler = self.CTR surkovsv93@1602: result, err = self.CTR.LoadProject(projectpath) laurent@675: if not result: laurent@781: self.LibraryPanel.SetController(self.Controler) laurent@738: self.ProjectTree.Enable(True) laurent@730: self.PouInstanceVariablesPanel.SetController(self.Controler) laurent@675: if self.EnableDebug: Edouard@717: self.DebugVariablePanel.SetDataProducer(self.CTR) laurent@730: self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) laurent@675: else: laurent@428: self.ResetView() laurent@675: self.ShowErrorMessage(result) laurent@755: self.RefreshAll() laurent@675: else: laurent@675: self.ShowErrorMessage(_("\"%s\" folder is not a valid Beremiz project\n") % projectpath) surkovsv93@1601: err = True surkovsv93@1601: self.RefreshConfigRecentProjects(projectpath, err) laurent@706: self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU) Edouard@1408: laurent@428: def OnCloseProjectMenu(self, event): Edouard@717: if self.CTR is not None and not self.CheckSaveBeforeClosing(): laurent@429: return Edouard@1408: laurent@429: self.ResetView() laurent@706: self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU) laurent@755: self.RefreshAll() Edouard@1408: laurent@428: def OnSaveProjectMenu(self, event): laurent@784: selected = self.TabsOpened.GetSelection() laurent@784: if selected != -1: laurent@784: window = self.TabsOpened.GetPage(selected) laurent@784: window.Save() Edouard@717: if self.CTR is not None: Edouard@717: self.CTR.SaveProject() laurent@781: self.RefreshAll() laurent@636: self._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES) Edouard@1408: laurent@428: def OnSaveProjectAsMenu(self, event): laurent@784: selected = self.TabsOpened.GetSelection() laurent@784: if selected != -1: laurent@784: window = self.TabsOpened.GetPage(selected) laurent@784: window.SaveAs() Edouard@717: if self.CTR is not None: Edouard@717: self.CTR.SaveProjectAs() laurent@781: self.RefreshAll() surkovsv93@1600: self.RefreshConfigRecentProjects(self.CTR.ProjectPath) laurent@636: self._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES) Edouard@1408: laurent@428: def OnQuitMenu(self, event): laurent@428: self.Close() Edouard@1408: laurent@428: def OnAboutMenu(self, event): andrej@1565: info = version.GetAboutDialogInfo() andrej@1565: ShowAboutDialog(self, info) Edouard@1408: laurent@738: def OnProjectTreeItemBeginEdit(self, event): laurent@738: selected = event.GetItem() laurent@747: if self.ProjectTree.GetPyData(selected)["type"] == ITEM_CONFNODE: laurent@738: event.Veto() laurent@738: else: laurent@738: IDEFrame.OnProjectTreeItemBeginEdit(self, event) Edouard@1408: laurent@738: def OnProjectTreeRightUp(self, event): Laurent@1240: item = event.GetItem() laurent@738: item_infos = self.ProjectTree.GetPyData(item) Edouard@1408: laurent@738: if item_infos["type"] == ITEM_CONFNODE: laurent@738: confnode_menu = wx.Menu(title='') Edouard@1408: laurent@738: confnode = item_infos["confnode"] Laurent@967: if confnode is not None: Laurent@967: menu_items = confnode.GetContextualMenuItems() Laurent@967: if menu_items is not None: Laurent@967: for text, help, callback in menu_items: Laurent@967: new_id = wx.NewId() Laurent@967: confnode_menu.Append(help=help, id=new_id, kind=wx.ITEM_NORMAL, text=text) Laurent@967: self.Bind(wx.EVT_MENU, callback, id=new_id) Laurent@967: else: Laurent@967: for name, XSDClass, help in confnode.CTNChildrenTypes: Laurent@967: new_id = wx.NewId() Laurent@967: confnode_menu.Append(help=help, id=new_id, kind=wx.ITEM_NORMAL, text=_("Add") + " " + name) Laurent@967: self.Bind(wx.EVT_MENU, self.GetAddConfNodeFunction(name, confnode), id=new_id) laurent@738: laurent@738: new_id = wx.NewId() laurent@738: AppendMenu(confnode_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Delete")) laurent@755: self.Bind(wx.EVT_MENU, self.GetDeleteMenuFunction(confnode), id=new_id) Edouard@1408: laurent@738: self.PopupMenu(confnode_menu) laurent@738: confnode_menu.Destroy() Edouard@1408: laurent@738: event.Skip() Edouard@1401: elif item_infos["type"] == ITEM_RESOURCE: Edouard@1401: # prevent last resource to be delted Laurent@1024: parent = self.ProjectTree.GetItemParent(item) Laurent@1024: parent_name = self.ProjectTree.GetItemText(parent) Edouard@1401: if parent_name == _("Resources"): Laurent@1024: IDEFrame.OnProjectTreeRightUp(self, event) Laurent@1038: else: Laurent@1038: IDEFrame.OnProjectTreeRightUp(self, event) Edouard@1408: laurent@738: def OnProjectTreeItemActivated(self, event): laurent@738: selected = event.GetItem() laurent@738: name = self.ProjectTree.GetItemText(selected) laurent@738: item_infos = self.ProjectTree.GetPyData(selected) laurent@738: if item_infos["type"] == ITEM_CONFNODE: laurent@738: item_infos["confnode"]._OpenView() laurent@738: event.Skip() laurent@738: elif item_infos["type"] == ITEM_PROJECT: laurent@738: self.CTR._OpenView() laurent@738: else: laurent@738: IDEFrame.OnProjectTreeItemActivated(self, event) Edouard@1408: laurent@782: def ProjectTreeItemSelect(self, select_item): Laurent@1272: if select_item is not None and select_item.IsOk(): Laurent@1272: name = self.ProjectTree.GetItemText(select_item) Laurent@1272: item_infos = self.ProjectTree.GetPyData(select_item) Laurent@1272: if item_infos["type"] == ITEM_CONFNODE: Laurent@1272: item_infos["confnode"]._OpenView(onlyopened=True) Laurent@1272: elif item_infos["type"] == ITEM_PROJECT: Laurent@1272: self.CTR._OpenView(onlyopened=True) Laurent@1272: else: Laurent@1272: IDEFrame.ProjectTreeItemSelect(self, select_item) Edouard@1408: laurent@738: def SelectProjectTreeItem(self, tagname): laurent@738: if self.ProjectTree is not None: laurent@738: root = self.ProjectTree.GetRootItem() laurent@738: if root.IsOk(): laurent@738: words = tagname.split("::") laurent@738: if len(words) == 1: laurent@738: if tagname == "Project": laurent@738: self.SelectedItem = root laurent@738: self.ProjectTree.SelectItem(root) Laurent@1269: self.ResetSelectedItem() laurent@738: else: Edouard@1408: return self.RecursiveProjectTreeItemSelection(root, laurent@738: [(word, ITEM_CONFNODE) for word in tagname.split(".")]) laurent@738: elif words[0] == "R": laurent@738: return self.RecursiveProjectTreeItemSelection(root, [(words[2], ITEM_RESOURCE)]) Laurent@1274: elif not os.path.exists(words[0]): laurent@738: IDEFrame.SelectProjectTreeItem(self, tagname) Edouard@1408: laurent@760: def GetAddConfNodeFunction(self, name, confnode=None): laurent@760: def AddConfNodeMenuFunction(event): laurent@760: wx.CallAfter(self.AddConfNode, name, confnode) laurent@760: return AddConfNodeMenuFunction Edouard@1408: laurent@755: def GetDeleteMenuFunction(self, confnode): laurent@755: def DeleteMenuFunction(event): Edouard@717: wx.CallAfter(self.DeleteConfNode, confnode) laurent@755: return DeleteMenuFunction Edouard@1408: laurent@738: def AddConfNode(self, ConfNodeType, confnode=None): Edouard@717: if self.CTR.CheckProjectPathPerm(): laurent@833: ConfNodeName = "%s_0" % ConfNodeType laurent@760: if confnode is not None: laurent@760: confnode.CTNAddChild(ConfNodeName, ConfNodeType) laurent@760: else: laurent@760: self.CTR.CTNAddChild(ConfNodeName, ConfNodeType) laurent@760: self._Refresh(TITLE, FILEMENU, PROJECTTREE) Edouard@1408: Edouard@717: def DeleteConfNode(self, confnode): Edouard@717: if self.CTR.CheckProjectPathPerm(): Edouard@1408: dialog = wx.MessageDialog(self, Edouard@1408: _("Really delete node '%s'?") % confnode.CTNName(), Edouard@1408: _("Remove %s node") % confnode.CTNType, laurent@801: wx.YES_NO|wx.NO_DEFAULT) laurent@428: if dialog.ShowModal() == wx.ID_YES: Edouard@718: confnode.CTNRemove() Edouard@717: del confnode laurent@738: self._Refresh(TITLE, FILEMENU, PROJECTTREE) laurent@428: dialog.Destroy() Laurent@900: Laurent@900: #------------------------------------------------------------------------------- Laurent@900: # Highlights showing functions Laurent@900: #------------------------------------------------------------------------------- Laurent@900: Laurent@900: def ShowHighlight(self, infos, start, end, highlight_type): Laurent@900: config_name = self.Controler.GetProjectMainConfigurationName() Laurent@900: if config_name is not None and infos[0] == self.Controler.ComputeConfigurationName(config_name): Laurent@900: self.CTR._OpenView() Laurent@900: selected = self.TabsOpened.GetSelection() Laurent@900: if selected != -1: Laurent@900: viewer = self.TabsOpened.GetPage(selected) Laurent@900: viewer.AddHighlight(infos[1:], start, end, highlight_type) Laurent@900: else: Laurent@900: IDEFrame.ShowHighlight(self, infos, start, end, highlight_type) Laurent@900: laurent@428: #------------------------------------------------------------------------------- laurent@428: # Exception Handler laurent@428: #------------------------------------------------------------------------------- Edouard@1067: import threading, traceback laurent@428: laurent@428: Max_Traceback_List_Size = 20 laurent@428: laurent@428: def Display_Exception_Dialog(e_type, e_value, e_tb, bug_report_path): laurent@428: trcbck_lst = [] laurent@428: for i,line in enumerate(traceback.extract_tb(e_tb)): Edouard@734: trcbck = " " + str(i+1) + ". " laurent@428: if line[0].find(os.getcwd()) == -1: Edouard@734: trcbck += "file : " + str(line[0]) + ", " Edouard@734: else: Edouard@734: trcbck += "file : " + str(line[0][len(os.getcwd()):]) + ", " Edouard@734: trcbck += "line : " + str(line[1]) + ", " + "function : " + str(line[2]) laurent@428: trcbck_lst.append(trcbck) Edouard@1408: laurent@428: # Allow clicking.... laurent@428: cap = wx.Window_GetCapture() laurent@428: if cap: laurent@428: cap.ReleaseMouse() laurent@428: Edouard@1408: dlg = wx.SingleChoiceDialog(None, laurent@428: _(""" laurent@428: An unhandled exception (bug) occured. Bug report saved at : laurent@428: (%s) laurent@428: greg@434: Please be kind enough to send this file to: Edouard@734: beremiz-devel@lists.sourceforge.net laurent@428: andrej@1580: You should now restart program. laurent@428: laurent@428: Traceback: laurent@428: """) % bug_report_path + Edouard@1408: repr(e_type) + " : " + repr(e_value), laurent@428: _("Error"), laurent@428: trcbck_lst) laurent@428: try: laurent@428: res = (dlg.ShowModal() == wx.ID_OK) laurent@428: finally: laurent@428: dlg.Destroy() laurent@428: laurent@428: return res laurent@428: laurent@428: def get_last_traceback(tb): laurent@428: while tb.tb_next: laurent@428: tb = tb.tb_next laurent@428: return tb laurent@428: laurent@428: laurent@428: def format_namespace(d, indent=' '): laurent@428: return '\n'.join(['%s%s: %s' % (indent, k, repr(v)[:10000]) for k, v in d.iteritems()]) laurent@428: laurent@428: laurent@428: ignored_exceptions = [] # a problem with a line in a module is only reported once per session laurent@428: laurent@428: def AddExceptHook(path, app_version='[No version]'):#, ignored_exceptions=[]): Edouard@1408: laurent@428: def handle_exception(e_type, e_value, e_traceback): laurent@428: 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@428: last_tb = get_last_traceback(e_traceback) laurent@428: ex = (last_tb.tb_frame.f_code.co_filename, last_tb.tb_frame.f_lineno) Edouard@701: if ex not in ignored_exceptions: laurent@428: date = time.ctime() laurent@428: bug_report_path = path+os.sep+"bug_report_"+date.replace(':','-').replace(' ','_')+".txt" laurent@428: result = Display_Exception_Dialog(e_type,e_value,e_traceback,bug_report_path) laurent@428: if result: laurent@428: ignored_exceptions.append(ex) laurent@428: info = { laurent@428: 'app-title' : wx.GetApp().GetAppName(), # app_title laurent@428: 'app-version' : app_version, laurent@428: 'wx-version' : wx.VERSION_STRING, laurent@428: 'wx-platform' : wx.Platform, laurent@428: 'python-version' : platform.python_version(), #sys.version.split()[0], laurent@428: 'platform' : platform.platform(), laurent@428: 'e-type' : e_type, laurent@428: 'e-value' : e_value, laurent@428: 'date' : date, laurent@428: 'cwd' : os.getcwd(), laurent@428: } laurent@428: if e_traceback: laurent@428: info['traceback'] = ''.join(traceback.format_tb(e_traceback)) + '%s: %s' % (e_type, e_value) laurent@428: last_tb = get_last_traceback(e_traceback) laurent@428: exception_locals = last_tb.tb_frame.f_locals # the locals at the level of the stack trace where the exception actually occurred laurent@428: info['locals'] = format_namespace(exception_locals) laurent@428: if 'self' in exception_locals: Edouard@1404: try : Edouard@1404: info['self'] = format_namespace(exception_locals['self'].__dict__) Edouard@1404: except : Edouard@1404: pass Edouard@1408: laurent@428: output = open(bug_report_path,'w') laurent@428: lst = info.keys() laurent@428: lst.sort() laurent@428: for a in lst: laurent@428: output.write(a+":\n"+str(info[a])+"\n\n") laurent@428: laurent@428: #sys.excepthook = lambda *args: wx.CallAfter(handle_exception, *args) laurent@428: sys.excepthook = handle_exception laurent@428: Edouard@1067: init_old = threading.Thread.__init__ Edouard@1067: def init(self, *args, **kwargs): Edouard@1067: init_old(self, *args, **kwargs) Edouard@1067: run_old = self.run Edouard@1067: def run_with_except_hook(*args, **kw): Edouard@1067: try: Edouard@1067: run_old(*args, **kw) Edouard@1067: except (KeyboardInterrupt, SystemExit): Edouard@1067: raise Edouard@1067: except: Edouard@1067: sys.excepthook(*sys.exc_info()) Edouard@1067: self.run = run_with_except_hook Edouard@1067: threading.Thread.__init__ = init Edouard@1067: laurent@428: if __name__ == '__main__': laurent@428: # Install a exception handle for bug reports andrej@1560: AddExceptHook(os.getcwd(),version.app_version) Edouard@1408: laurent@428: frame = Beremiz(None, projectOpen, buildpath) Laurent@1211: if splash: Laurent@1211: splash.Close() laurent@428: frame.Show() laurent@428: app.MainLoop()