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