eric7/Debugger/DebugUI.py

branch
eric7
changeset 8312
800c432b34c8
parent 8265
0090cfa83159
child 8314
e3642a6a1e71
diff -r 4e8b98454baa -r 800c432b34c8 eric7/Debugger/DebugUI.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric7/Debugger/DebugUI.py	Sat May 15 18:45:04 2021 +0200
@@ -0,0 +1,2619 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2002 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the debugger UI.
+"""
+
+import os
+import copy
+import contextlib
+
+from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, Qt
+from PyQt5.QtGui import QKeySequence
+from PyQt5.QtWidgets import QMenu, QToolBar, QApplication, QDialog
+
+from E5Gui.E5Action import E5Action, createActionGroup
+from E5Gui import E5MessageBox
+
+from UI.Info import Program
+from UI.NotificationWidget import NotificationTypes
+
+from .DebugClientCapabilities import (
+    HasDebugger, HasInterpreter, HasProfiler, HasCoverage
+)
+
+import Preferences
+import Utilities
+import UI.PixmapCache
+import UI.Config
+
+from eric6config import getConfig
+
+
+class DebugUI(QObject):
+    """
+    Class implementing the debugger part of the UI.
+    
+    @signal clientStack(stack, debuggerId) emitted at breaking after a reported
+        exception
+    @signal compileForms() emitted if changed project forms should be compiled
+    @signal compileResources() emitted if changed project resources should be
+        compiled
+    @signal executeMake() emitted if a project specific make run should be
+        performed
+    @signal debuggingStarted(filename) emitted when a debugging session was
+        started
+    @signal resetUI(full) emitted to reset the UI partially or fully
+    @signal exceptionInterrupt() emitted after the execution was interrupted
+        by an exception and acknowledged by the user
+    @signal appendStdout(msg) emitted when the client program has terminated
+        and the display of the termination dialog is suppressed
+    """
+    clientStack = pyqtSignal(list, str)
+    resetUI = pyqtSignal(bool)
+    exceptionInterrupt = pyqtSignal()
+    compileForms = pyqtSignal()
+    compileResources = pyqtSignal()
+    executeMake = pyqtSignal()
+    debuggingStarted = pyqtSignal(str)
+    appendStdout = pyqtSignal(str)
+    
+    def __init__(self, ui, vm, debugServer, debugViewer, project):
+        """
+        Constructor
+        
+        @param ui reference to the main UI
+        @param vm reference to the viewmanager
+        @param debugServer reference to the debug server
+        @param debugViewer reference to the debug viewer widget
+        @param project reference to the project object
+        """
+        super().__init__(ui)
+        
+        self.ui = ui
+        self.viewmanager = vm
+        self.debugServer = debugServer
+        self.debugViewer = debugViewer
+        self.project = project
+        
+        # Clear some variables
+        self.projectOpen = False
+        self.editorOpen = False
+        
+        # read the saved debug info values
+        self.lastUsedVenvName = Preferences.Prefs.settings.value(
+            'DebugInfo/VirtualEnvironment', '')
+        self.argvHistory = Preferences.toList(
+            Preferences.Prefs.settings.value('DebugInfo/ArgumentsHistory'))
+        self.wdHistory = Preferences.toList(
+            Preferences.Prefs.settings.value(
+                'DebugInfo/WorkingDirectoryHistory'))
+        self.envHistory = Preferences.toList(
+            Preferences.Prefs.settings.value('DebugInfo/EnvironmentHistory'))
+        self.excList = Preferences.toList(
+            Preferences.Prefs.settings.value('DebugInfo/Exceptions'))
+        self.excIgnoreList = Preferences.toList(
+            Preferences.Prefs.settings.value('DebugInfo/IgnoredExceptions'))
+        self.exceptions = Preferences.toBool(
+            Preferences.Prefs.settings.value(
+                'DebugInfo/ReportExceptions', True))
+        self.autoClearShell = Preferences.toBool(
+            Preferences.Prefs.settings.value('DebugInfo/AutoClearShell', True))
+        self.tracePython = Preferences.toBool(
+            Preferences.Prefs.settings.value('DebugInfo/TracePython', False))
+        self.autoContinue = Preferences.toBool(
+            Preferences.Prefs.settings.value('DebugInfo/AutoContinue', True))
+        self.enableMultiprocess = Preferences.toBool(
+            Preferences.Prefs.settings.value(
+                'DebugInfo/EnableMultiprocess', False))
+        self.multiprocessNoDebugHistory = Preferences.toList(
+            Preferences.Prefs.settings.value(
+                'DebugInfo/MultiprocessNoDebugHistory'))
+        self.overrideGlobalConfig = {
+            "enable": Preferences.toBool(Preferences.Prefs.settings.value(
+                'DebugInfo/OverrideGlobal', False)),
+            "redirect": Preferences.toBool(Preferences.Prefs.settings.value(
+                'DebugInfo/RedirectStdinStdout', True)),
+        }
+        
+        self.lastDebuggedFile = None
+        self.lastStartAction = 0    # 0=None, 1=Script, 2=Project
+        self.clientType = ""
+        self.lastAction = -1
+        self.debugActions = [
+            self.__continue, self.__step, self.__stepOver, self.__stepOut,
+            self.__stepQuit, self.__runToCursor, self.__runUntil,
+            self.__moveInstructionPointer
+        ]
+        self.__localsVarFilterList, self.__globalsVarFilterList = (
+            Preferences.getVarFilters())
+        self.debugViewer.setVariablesFilter(
+            self.__globalsVarFilterList, self.__localsVarFilterList)
+        
+        self.__clientDebuggerIds = set()
+        
+        # Connect the signals emitted by the debug-server
+        debugServer.clientGone.connect(self.__clientGone)
+        debugServer.clientLine.connect(self.__clientLine)
+        debugServer.clientDisconnected.connect(self.__clientDisconnected)
+        debugServer.clientExit.connect(self.__clientExit)
+        debugServer.lastClientExited.connect(self.__lastClientExited)
+        debugServer.clientSyntaxError.connect(self.__clientSyntaxError)
+        debugServer.clientException.connect(self.__clientException)
+        debugServer.clientSignal.connect(self.__clientSignal)
+        debugServer.clientVariables.connect(self.__clientVariables)
+        debugServer.clientVariable.connect(self.__clientVariable)
+        debugServer.clientBreakConditionError.connect(
+            self.__clientBreakConditionError)
+        debugServer.clientWatchConditionError.connect(
+            self.__clientWatchConditionError)
+        debugServer.passiveDebugStarted.connect(self.__passiveDebugStarted)
+        debugServer.clientThreadSet.connect(self.__clientThreadSet)
+        debugServer.clientDebuggerId.connect(self.__clientDebuggerId)
+        
+        # Connect the signals emitted by the viewmanager
+        vm.editorOpened.connect(self.__editorOpened)
+        vm.lastEditorClosed.connect(self.__lastEditorClosed)
+        vm.checkActions.connect(self.__checkActions)
+        vm.cursorChanged.connect(self.__cursorChanged)
+        vm.breakpointToggled.connect(self.__cursorChanged)
+        
+        # Connect the signals emitted by the project
+        project.projectOpened.connect(self.__projectOpened)
+        project.newProject.connect(self.__projectOpened)
+        project.projectClosed.connect(self.__projectClosed)
+        
+        # Set a flag for the passive debug mode
+        self.passive = Preferences.getDebugger("PassiveDbgEnabled")
+    
+    def showNotification(self, notification,
+                         kind=NotificationTypes.INFORMATION, timeout=None):
+        """
+        Public method to show some notification message.
+        
+        @param notification message to be shown
+        @type str
+        @param kind kind of notification to be shown
+        @type NotificationTypes
+        @param timeout timeout for the notification (None = use configured
+            default, 0 = indefinitely)
+        @type int
+        """
+        self.ui.showNotification(
+            UI.PixmapCache.getPixmap("debug48"),
+            self.tr("Notification"), notification, kind=kind, timeout=timeout)
+    
+    def variablesFilter(self, scope):
+        """
+        Public method to get the variables filter for a scope.
+        
+        @param scope flag indicating global (True) or local (False) scope
+        @return filters list
+        @rtype list of str
+        """
+        if scope:
+            return self.__globalsVarFilterList[:]
+        else:
+            return self.__localsVarFilterList[:]
+        
+    def initActions(self):
+        """
+        Public method defining the user interface actions.
+        """
+        self.actions = []
+        
+        self.runAct = E5Action(
+            self.tr('Run Script'),
+            UI.PixmapCache.getIcon("runScript"),
+            self.tr('&Run Script...'),
+            Qt.Key.Key_F2, 0, self, 'dbg_run_script')
+        self.runAct.setStatusTip(self.tr('Run the current Script'))
+        self.runAct.setWhatsThis(self.tr(
+            """<b>Run Script</b>"""
+            """<p>Set the command line arguments and run the script outside"""
+            """ the debugger. If the file has unsaved changes it may be"""
+            """ saved first.</p>"""
+        ))
+        self.runAct.triggered.connect(self.__runScript)
+        self.actions.append(self.runAct)
+
+        self.runProjectAct = E5Action(
+            self.tr('Run Project'),
+            UI.PixmapCache.getIcon("runProject"),
+            self.tr('Run &Project...'),
+            Qt.Modifier.SHIFT + Qt.Key.Key_F2,
+            0, self, 'dbg_run_project')
+        self.runProjectAct.setStatusTip(self.tr('Run the current Project'))
+        self.runProjectAct.setWhatsThis(self.tr(
+            """<b>Run Project</b>"""
+            """<p>Set the command line arguments and run the current project"""
+            """ outside the debugger."""
+            """ If files of the current project have unsaved changes they"""
+            """ may be saved first.</p>"""
+        ))
+        self.runProjectAct.triggered.connect(self.__runProject)
+        self.actions.append(self.runProjectAct)
+
+        self.coverageAct = E5Action(
+            self.tr('Coverage run of Script'),
+            UI.PixmapCache.getIcon("coverageScript"),
+            self.tr('Coverage run of Script...'), 0, 0, self,
+            'dbg_coverage_script')
+        self.coverageAct.setStatusTip(
+            self.tr('Perform a coverage run of the current Script'))
+        self.coverageAct.setWhatsThis(self.tr(
+            """<b>Coverage run of Script</b>"""
+            """<p>Set the command line arguments and run the script under"""
+            """ the control of a coverage analysis tool. If the file has"""
+            """ unsaved changes it may be saved first.</p>"""
+        ))
+        self.coverageAct.triggered.connect(self.__coverageScript)
+        self.actions.append(self.coverageAct)
+
+        self.coverageProjectAct = E5Action(
+            self.tr('Coverage run of Project'),
+            UI.PixmapCache.getIcon("coverageProject"),
+            self.tr('Coverage run of Project...'), 0, 0, self,
+            'dbg_coverage_project')
+        self.coverageProjectAct.setStatusTip(
+            self.tr('Perform a coverage run of the current Project'))
+        self.coverageProjectAct.setWhatsThis(self.tr(
+            """<b>Coverage run of Project</b>"""
+            """<p>Set the command line arguments and run the current project"""
+            """ under the control of a coverage analysis tool."""
+            """ If files of the current project have unsaved changes"""
+            """ they may be saved first.</p>"""
+        ))
+        self.coverageProjectAct.triggered.connect(self.__coverageProject)
+        self.actions.append(self.coverageProjectAct)
+
+        self.profileAct = E5Action(
+            self.tr('Profile Script'),
+            UI.PixmapCache.getIcon("profileScript"),
+            self.tr('Profile Script...'), 0, 0, self, 'dbg_profile_script')
+        self.profileAct.setStatusTip(self.tr('Profile the current Script'))
+        self.profileAct.setWhatsThis(self.tr(
+            """<b>Profile Script</b>"""
+            """<p>Set the command line arguments and profile the script."""
+            """ If the file has unsaved changes it may be saved first.</p>"""
+        ))
+        self.profileAct.triggered.connect(self.__profileScript)
+        self.actions.append(self.profileAct)
+
+        self.profileProjectAct = E5Action(
+            self.tr('Profile Project'),
+            UI.PixmapCache.getIcon("profileProject"),
+            self.tr('Profile Project...'), 0, 0, self,
+            'dbg_profile_project')
+        self.profileProjectAct.setStatusTip(
+            self.tr('Profile the current Project'))
+        self.profileProjectAct.setWhatsThis(self.tr(
+            """<b>Profile Project</b>"""
+            """<p>Set the command line arguments and profile the current"""
+            """ project. If files of the current project have unsaved"""
+            """ changes they may be saved first.</p>"""
+        ))
+        self.profileProjectAct.triggered.connect(self.__profileProject)
+        self.actions.append(self.profileProjectAct)
+
+        self.debugAct = E5Action(
+            self.tr('Debug Script'),
+            UI.PixmapCache.getIcon("debugScript"),
+            self.tr('&Debug Script...'), Qt.Key.Key_F5, 0, self,
+            'dbg_debug_script')
+        self.debugAct.setStatusTip(self.tr('Debug the current Script'))
+        self.debugAct.setWhatsThis(self.tr(
+            """<b>Debug Script</b>"""
+            """<p>Set the command line arguments and set the current line"""
+            """ to be the first executable Python statement of the current"""
+            """ editor window. If the file has unsaved changes it may be"""
+            """ saved first.</p>"""
+        ))
+        self.debugAct.triggered.connect(self.__debugScript)
+        self.actions.append(self.debugAct)
+
+        self.debugProjectAct = E5Action(
+            self.tr('Debug Project'),
+            UI.PixmapCache.getIcon("debugProject"),
+            self.tr('Debug &Project...'),
+            Qt.Modifier.SHIFT + Qt.Key.Key_F5,
+            0, self, 'dbg_debug_project')
+        self.debugProjectAct.setStatusTip(self.tr(
+            'Debug the current Project'))
+        self.debugProjectAct.setWhatsThis(self.tr(
+            """<b>Debug Project</b>"""
+            """<p>Set the command line arguments and set the current line"""
+            """ to be the first executable Python statement of the main"""
+            """ script of the current project. If files of the current"""
+            """ project have unsaved changes they may be saved first.</p>"""
+        ))
+        self.debugProjectAct.triggered.connect(self.__debugProject)
+        self.actions.append(self.debugProjectAct)
+
+        self.restartAct = E5Action(
+            self.tr('Restart'),
+            UI.PixmapCache.getIcon("debugRestart"),
+            self.tr('Restart'), Qt.Key.Key_F4, 0, self, 'dbg_restart_script')
+        self.restartAct.setStatusTip(self.tr(
+            'Restart the last debugged script'))
+        self.restartAct.setWhatsThis(self.tr(
+            """<b>Restart</b>"""
+            """<p>Set the command line arguments and set the current line"""
+            """ to be the first executable Python statement of the script"""
+            """ that was debugged last. If there are unsaved changes, they"""
+            """ may be saved first.</p>"""
+        ))
+        self.restartAct.triggered.connect(self.__doRestart)
+        self.actions.append(self.restartAct)
+
+        self.stopAct = E5Action(
+            self.tr('Stop'),
+            UI.PixmapCache.getIcon("stopScript"),
+            self.tr('Stop'), Qt.Modifier.SHIFT + Qt.Key.Key_F10, 0,
+            self, 'dbg_stop_script')
+        self.stopAct.setStatusTip(self.tr("""Stop the running script."""))
+        self.stopAct.setWhatsThis(self.tr(
+            """<b>Stop</b>"""
+            """<p>This stops the script running in the debugger backend.</p>"""
+        ))
+        self.stopAct.triggered.connect(self.__stopScript)
+        self.actions.append(self.stopAct)
+
+        self.debugActGrp = createActionGroup(self)
+
+        act = E5Action(
+            self.tr('Continue'),
+            UI.PixmapCache.getIcon("continue"),
+            self.tr('&Continue'), Qt.Key.Key_F6, 0,
+            self.debugActGrp, 'dbg_continue')
+        act.setStatusTip(
+            self.tr('Continue running the program from the current line'))
+        act.setWhatsThis(self.tr(
+            """<b>Continue</b>"""
+            """<p>Continue running the program from the current line. The"""
+            """ program will stop when it terminates or when a breakpoint"""
+            """ is reached.</p>"""
+        ))
+        act.triggered.connect(self.__continue)
+        self.actions.append(act)
+
+        act = E5Action(
+            self.tr('Continue to Cursor'),
+            UI.PixmapCache.getIcon("continueToCursor"),
+            self.tr('Continue &To Cursor'),
+            Qt.Modifier.SHIFT + Qt.Key.Key_F6,
+            0, self.debugActGrp, 'dbg_continue_to_cursor')
+        act.setStatusTip(self.tr(
+            """Continue running the program from the"""
+            """ current line to the current cursor position"""))
+        act.setWhatsThis(self.tr(
+            """<b>Continue To Cursor</b>"""
+            """<p>Continue running the program from the current line to the"""
+            """ current cursor position.</p>"""
+        ))
+        act.triggered.connect(self.__runToCursor)
+        self.actions.append(act)
+        
+        act = E5Action(
+            self.tr('Continue Until'),
+            UI.PixmapCache.getIcon("continueUntil"),
+            self.tr('Continue &Until'), Qt.Modifier.CTRL + Qt.Key.Key_F6, 0,
+            self.debugActGrp, 'dbg_continue_until')
+        act.setStatusTip(self.tr(
+            """Continue running the program from the current line to the"""
+            """ current cursor position or until leaving the current frame"""))
+        act.setWhatsThis(self.tr(
+            """<b>Continue Until</b>"""
+            """<p>Continue running the program from the current line to the"""
+            """ cursor position greater than the current line or until"""
+            """ leaving the current frame.</p>"""
+        ))
+        act.triggered.connect(self.__runUntil)
+        self.actions.append(act)
+        
+        act = E5Action(
+            self.tr('Move Instruction Pointer to Cursor'),
+            UI.PixmapCache.getIcon("moveInstructionPointer"),
+            self.tr('&Jump To Cursor'), Qt.Key.Key_F12, 0,
+            self.debugActGrp, 'dbg_jump_to_cursor')
+        act.setStatusTip(self.tr(
+            """Skip the code from the"""
+            """ current line to the current cursor position"""))
+        act.setWhatsThis(self.tr(
+            """<b>Move Instruction Pointer to Cursor</b>"""
+            """<p>Move the Python internal instruction pointer to the"""
+            """ current cursor position without executing the code in"""
+            """ between.</p>"""
+            """<p>It's not possible to jump out of a function or jump"""
+            """ in a code block, e.g. a loop. In these cases, a error"""
+            """ message is printed to the log window.</p>"""
+        ))
+        act.triggered.connect(self.__moveInstructionPointer)
+        self.actions.append(act)
+
+        act = E5Action(
+            self.tr('Single Step'),
+            UI.PixmapCache.getIcon("step"),
+            self.tr('Sin&gle Step'), Qt.Key.Key_F7, 0,
+            self.debugActGrp, 'dbg_single_step')
+        act.setStatusTip(self.tr('Execute a single Python statement'))
+        act.setWhatsThis(self.tr(
+            """<b>Single Step</b>"""
+            """<p>Execute a single Python statement. If the statement"""
+            """ is an <tt>import</tt> statement, a class constructor, or a"""
+            """ method or function call then control is returned to the"""
+            """ debugger at the next statement.</p>"""
+        ))
+        act.triggered.connect(self.__step)
+        self.actions.append(act)
+
+        act = E5Action(
+            self.tr('Step Over'),
+            UI.PixmapCache.getIcon("stepOver"),
+            self.tr('Step &Over'), Qt.Key.Key_F8, 0,
+            self.debugActGrp, 'dbg_step_over')
+        act.setStatusTip(self.tr(
+            """Execute a single Python statement staying"""
+            """ in the current frame"""))
+        act.setWhatsThis(self.tr(
+            """<b>Step Over</b>"""
+            """<p>Execute a single Python statement staying in the same"""
+            """ frame. If the statement is an <tt>import</tt> statement,"""
+            """ a class constructor, or a method or function call then"""
+            """ control is returned to the debugger after the statement"""
+            """ has completed.</p>"""
+        ))
+        act.triggered.connect(self.__stepOver)
+        self.actions.append(act)
+
+        act = E5Action(
+            self.tr('Step Out'),
+            UI.PixmapCache.getIcon("stepOut"),
+            self.tr('Step Ou&t'), Qt.Key.Key_F9, 0,
+            self.debugActGrp, 'dbg_step_out')
+        act.setStatusTip(self.tr(
+            """Execute Python statements until leaving"""
+            """ the current frame"""))
+        act.setWhatsThis(self.tr(
+            """<b>Step Out</b>"""
+            """<p>Execute Python statements until leaving the current"""
+            """ frame. If the statements are inside an <tt>import</tt>"""
+            """ statement, a class constructor, or a method or function"""
+            """ call then control is returned to the debugger after the"""
+            """ current frame has been left.</p>"""
+        ))
+        act.triggered.connect(self.__stepOut)
+        self.actions.append(act)
+
+        act = E5Action(
+            self.tr('Stop'),
+            UI.PixmapCache.getIcon("stepQuit"),
+            self.tr('&Stop'), Qt.Key.Key_F10, 0,
+            self.debugActGrp, 'dbg_stop')
+        act.setStatusTip(self.tr('Stop debugging'))
+        act.setWhatsThis(self.tr(
+            """<b>Stop</b>"""
+            """<p>Stop the running debugging session.</p>"""
+        ))
+        act.triggered.connect(self.__stepQuit)
+        self.actions.append(act)
+        
+        self.dbgFilterAct = E5Action(
+            self.tr('Variables Type Filter'),
+            self.tr('Varia&bles Type Filter...'), 0, 0, self,
+            'dbg_variables_filter')
+        self.dbgFilterAct.setStatusTip(self.tr(
+            'Configure variables type filter'))
+        self.dbgFilterAct.setWhatsThis(self.tr(
+            """<b>Variables Type Filter</b>"""
+            """<p>Configure the variables type filter. Only variable types"""
+            """ that are not selected are displayed in the global or local"""
+            """ variables window during a debugging session.</p>"""
+        ))
+        self.dbgFilterAct.triggered.connect(
+            self.__configureVariablesFilters)
+        self.actions.append(self.dbgFilterAct)
+
+        self.excFilterAct = E5Action(
+            self.tr('Exceptions Filter'),
+            self.tr('&Exceptions Filter...'), 0, 0, self,
+            'dbg_exceptions_filter')
+        self.excFilterAct.setStatusTip(self.tr(
+            'Configure exceptions filter'))
+        self.excFilterAct.setWhatsThis(self.tr(
+            """<b>Exceptions Filter</b>"""
+            """<p>Configure the exceptions filter. Only exception types"""
+            """ that are listed are highlighted during a debugging"""
+            """ session.</p><p>Please note, that all unhandled exceptions"""
+            """ are highlighted indepent from the filter list.</p>"""
+        ))
+        self.excFilterAct.triggered.connect(
+            self.__configureExceptionsFilter)
+        self.actions.append(self.excFilterAct)
+        
+        self.excIgnoreFilterAct = E5Action(
+            self.tr('Ignored Exceptions'),
+            self.tr('&Ignored Exceptions...'), 0, 0,
+            self, 'dbg_ignored_exceptions')
+        self.excIgnoreFilterAct.setStatusTip(self.tr(
+            'Configure ignored exceptions'))
+        self.excIgnoreFilterAct.setWhatsThis(self.tr(
+            """<b>Ignored Exceptions</b>"""
+            """<p>Configure the ignored exceptions. Only exception types"""
+            """ that are not listed are highlighted during a debugging"""
+            """ session.</p><p>Please note, that unhandled exceptions"""
+            """ cannot be ignored.</p>"""
+        ))
+        self.excIgnoreFilterAct.triggered.connect(
+            self.__configureIgnoredExceptions)
+        self.actions.append(self.excIgnoreFilterAct)
+
+        self.dbgSetBpActGrp = createActionGroup(self)
+
+        self.dbgToggleBpAct = E5Action(
+            self.tr('Toggle Breakpoint'),
+            UI.PixmapCache.getIcon("breakpointToggle"),
+            self.tr('Toggle Breakpoint'),
+            QKeySequence(self.tr("Shift+F11", "Debug|Toggle Breakpoint")),
+            0, self.dbgSetBpActGrp, 'dbg_toggle_breakpoint')
+        self.dbgToggleBpAct.setStatusTip(self.tr('Toggle Breakpoint'))
+        self.dbgToggleBpAct.setWhatsThis(self.tr(
+            """<b>Toggle Breakpoint</b>"""
+            """<p>Toggles a breakpoint at the current line of the"""
+            """ current editor.</p>"""
+        ))
+        self.dbgToggleBpAct.triggered.connect(self.__toggleBreakpoint)
+        self.actions.append(self.dbgToggleBpAct)
+        
+        self.dbgEditBpAct = E5Action(
+            self.tr('Edit Breakpoint'),
+            UI.PixmapCache.getIcon("cBreakpointToggle"),
+            self.tr('Edit Breakpoint...'),
+            QKeySequence(self.tr("Shift+F12", "Debug|Edit Breakpoint")), 0,
+            self.dbgSetBpActGrp, 'dbg_edit_breakpoint')
+        self.dbgEditBpAct.setStatusTip(self.tr('Edit Breakpoint'))
+        self.dbgEditBpAct.setWhatsThis(self.tr(
+            """<b>Edit Breakpoint</b>"""
+            """<p>Opens a dialog to edit the breakpoints properties."""
+            """ It works at the current line of the current editor.</p>"""
+        ))
+        self.dbgEditBpAct.triggered.connect(self.__editBreakpoint)
+        self.actions.append(self.dbgEditBpAct)
+
+        self.dbgNextBpAct = E5Action(
+            self.tr('Next Breakpoint'),
+            UI.PixmapCache.getIcon("breakpointNext"),
+            self.tr('Next Breakpoint'),
+            QKeySequence(
+                self.tr("Ctrl+Shift+PgDown", "Debug|Next Breakpoint")), 0,
+            self.dbgSetBpActGrp, 'dbg_next_breakpoint')
+        self.dbgNextBpAct.setStatusTip(self.tr('Next Breakpoint'))
+        self.dbgNextBpAct.setWhatsThis(self.tr(
+            """<b>Next Breakpoint</b>"""
+            """<p>Go to next breakpoint of the current editor.</p>"""
+        ))
+        self.dbgNextBpAct.triggered.connect(self.__nextBreakpoint)
+        self.actions.append(self.dbgNextBpAct)
+
+        self.dbgPrevBpAct = E5Action(
+            self.tr('Previous Breakpoint'),
+            UI.PixmapCache.getIcon("breakpointPrevious"),
+            self.tr('Previous Breakpoint'),
+            QKeySequence(
+                self.tr("Ctrl+Shift+PgUp", "Debug|Previous Breakpoint")),
+            0, self.dbgSetBpActGrp, 'dbg_previous_breakpoint')
+        self.dbgPrevBpAct.setStatusTip(self.tr('Previous Breakpoint'))
+        self.dbgPrevBpAct.setWhatsThis(self.tr(
+            """<b>Previous Breakpoint</b>"""
+            """<p>Go to previous breakpoint of the current editor.</p>"""
+        ))
+        self.dbgPrevBpAct.triggered.connect(self.__previousBreakpoint)
+        self.actions.append(self.dbgPrevBpAct)
+
+        act = E5Action(
+            self.tr('Clear Breakpoints'),
+            self.tr('Clear Breakpoints'),
+            0, 0,
+            self.dbgSetBpActGrp, 'dbg_clear_breakpoint')
+        act.setStatusTip(self.tr('Clear Breakpoints'))
+        act.setWhatsThis(self.tr(
+            """<b>Clear Breakpoints</b>"""
+            """<p>Clear breakpoints of all editors.</p>"""
+        ))
+        act.triggered.connect(self.__clearBreakpoints)
+        self.actions.append(act)
+
+        self.debugActGrp.setEnabled(False)
+        self.dbgSetBpActGrp.setEnabled(False)
+        self.runAct.setEnabled(False)
+        self.runProjectAct.setEnabled(False)
+        self.profileAct.setEnabled(False)
+        self.profileProjectAct.setEnabled(False)
+        self.coverageAct.setEnabled(False)
+        self.coverageProjectAct.setEnabled(False)
+        self.debugAct.setEnabled(False)
+        self.debugProjectAct.setEnabled(False)
+        self.restartAct.setEnabled(False)
+        self.stopAct.setEnabled(False)
+        
+    def initMenus(self):
+        """
+        Public slot to initialize the project menu.
+        
+        @return the generated menu
+        """
+        dmenu = QMenu(self.tr('&Debug'), self.parent())
+        dmenu.setTearOffEnabled(True)
+        smenu = QMenu(self.tr('Sta&rt'), self.parent())
+        smenu.setTearOffEnabled(True)
+        self.breakpointsMenu = QMenu(self.tr('&Breakpoints'), dmenu)
+        
+        smenu.addAction(self.restartAct)
+        smenu.addAction(self.stopAct)
+        smenu.addSeparator()
+        smenu.addAction(self.runAct)
+        smenu.addAction(self.runProjectAct)
+        smenu.addSeparator()
+        smenu.addAction(self.debugAct)
+        smenu.addAction(self.debugProjectAct)
+        smenu.addSeparator()
+        smenu.addAction(self.profileAct)
+        smenu.addAction(self.profileProjectAct)
+        smenu.addSeparator()
+        smenu.addAction(self.coverageAct)
+        smenu.addAction(self.coverageProjectAct)
+        
+        dmenu.addActions(self.debugActGrp.actions())
+        dmenu.addSeparator()
+        dmenu.addActions(self.dbgSetBpActGrp.actions())
+        self.menuBreakpointsAct = dmenu.addMenu(self.breakpointsMenu)
+        dmenu.addSeparator()
+        dmenu.addAction(self.dbgFilterAct)
+        dmenu.addAction(self.excFilterAct)
+        dmenu.addAction(self.excIgnoreFilterAct)
+        
+        self.breakpointsMenu.aboutToShow.connect(self.__showBreakpointsMenu)
+        self.breakpointsMenu.triggered.connect(self.__breakpointSelected)
+        dmenu.aboutToShow.connect(self.__showDebugMenu)
+        
+        return smenu, dmenu
+        
+    def initToolbars(self, toolbarManager):
+        """
+        Public slot to initialize the debug toolbars.
+        
+        @param toolbarManager reference to a toolbar manager object
+            (E5ToolBarManager)
+        @return the generated toolbars (list of QToolBar)
+        """
+        starttb = QToolBar(self.tr("Start"), self.ui)
+        starttb.setIconSize(UI.Config.ToolBarIconSize)
+        starttb.setObjectName("StartToolbar")
+        starttb.setToolTip(self.tr('Start'))
+        
+        starttb.addAction(self.restartAct)
+        starttb.addAction(self.stopAct)
+        starttb.addSeparator()
+        starttb.addAction(self.runAct)
+        starttb.addAction(self.runProjectAct)
+        starttb.addSeparator()
+        starttb.addAction(self.debugAct)
+        starttb.addAction(self.debugProjectAct)
+        
+        debugtb = QToolBar(self.tr("Debug"), self.ui)
+        debugtb.setIconSize(UI.Config.ToolBarIconSize)
+        debugtb.setObjectName("DebugToolbar")
+        debugtb.setToolTip(self.tr('Debug'))
+        
+        debugtb.addActions(self.debugActGrp.actions())
+        debugtb.addSeparator()
+        debugtb.addAction(self.dbgToggleBpAct)
+        debugtb.addAction(self.dbgEditBpAct)
+        debugtb.addAction(self.dbgNextBpAct)
+        debugtb.addAction(self.dbgPrevBpAct)
+        
+        toolbarManager.addToolBar(starttb, starttb.windowTitle())
+        toolbarManager.addToolBar(debugtb, debugtb.windowTitle())
+        toolbarManager.addAction(self.profileAct, starttb.windowTitle())
+        toolbarManager.addAction(self.profileProjectAct, starttb.windowTitle())
+        toolbarManager.addAction(self.coverageAct, starttb.windowTitle())
+        toolbarManager.addAction(self.coverageProjectAct,
+                                 starttb.windowTitle())
+        
+        return [starttb, debugtb]
+
+    def setArgvHistory(self, argsStr, clearHistories=False, history=None):
+        """
+        Public slot to initialize the argv history.
+        
+        @param argsStr the commandline arguments (string)
+        @param clearHistories flag indicating, that the list should
+            be cleared (boolean)
+        @param history list of history entries to be set (list of strings)
+        """
+        if clearHistories:
+            del self.argvHistory[1:]
+        elif history is not None:
+            self.argvHistory = history[:]
+        else:
+            if argsStr in self.argvHistory:
+                self.argvHistory.remove(argsStr)
+            self.argvHistory.insert(0, argsStr)
+
+    def setWdHistory(self, wdStr, clearHistories=False, history=None):
+        """
+        Public slot to initialize the wd history.
+        
+        @param wdStr the working directory (string)
+        @param clearHistories flag indicating, that the list should
+            be cleared (boolean)
+        @param history list of history entries to be set (list of strings)
+        """
+        if clearHistories:
+            del self.wdHistory[1:]
+        elif history is not None:
+            self.wdHistory = history[:]
+        else:
+            if wdStr in self.wdHistory:
+                self.wdHistory.remove(wdStr)
+            self.wdHistory.insert(0, wdStr)
+        
+    def setEnvHistory(self, envStr, clearHistories=False, history=None):
+        """
+        Public slot to initialize the env history.
+        
+        @param envStr the environment settings (string)
+        @param clearHistories flag indicating, that the list should
+            be cleared (boolean)
+        @param history list of history entries to be set (list of strings)
+        """
+        if clearHistories:
+            del self.envHistory[1:]
+        elif history is not None:
+            self.envHistory = history[:]
+        else:
+            if envStr in self.envHistory:
+                self.envHistory.remove(envStr)
+            self.envHistory.insert(0, envStr)
+        
+    def setExceptionReporting(self, exceptions):
+        """
+        Public slot to initialize the exception reporting flag.
+        
+        @param exceptions flag indicating exception reporting status (boolean)
+        """
+        self.exceptions = exceptions
+
+    def setExcList(self, excList):
+        """
+        Public slot to initialize the exceptions type list.
+        
+        @param excList list of exception types (list of strings)
+        """
+        self.excList = excList[:]   # keep a copy
+        
+    def setExcIgnoreList(self, excIgnoreList):
+        """
+        Public slot to initialize the ignored exceptions type list.
+        
+        @param excIgnoreList list of ignored exception types (list of strings)
+        """
+        self.excIgnoreList = excIgnoreList[:]   # keep a copy
+        
+    def setAutoClearShell(self, autoClearShell):
+        """
+        Public slot to initialize the autoClearShell flag.
+        
+        @param autoClearShell flag indicating, that the interpreter window
+            should be cleared (boolean)
+        """
+        self.autoClearShell = autoClearShell
+
+    def setTracePython(self, tracePython):
+        """
+        Public slot to initialize the trace Python flag.
+        
+        @param tracePython flag indicating if the Python library should be
+            traced as well (boolean)
+        """
+        self.tracePython = tracePython
+
+    def setAutoContinue(self, autoContinue):
+        """
+        Public slot to initialize the autoContinue flag.
+        
+        @param autoContinue flag indicating, that the debugger should not
+            stop at the first executable line (boolean)
+        """
+        self.autoContinue = autoContinue
+
+    def __editorOpened(self, fn):
+        """
+        Private slot to handle the editorOpened signal.
+        
+        @param fn filename of the opened editor
+        """
+        self.editorOpen = True
+        
+        editor = self.viewmanager.getOpenEditor(fn) if fn else None
+        self.__checkActions(editor)
+        
+    def __lastEditorClosed(self):
+        """
+        Private slot to handle the closeProgram signal.
+        """
+        self.editorOpen = False
+        self.debugAct.setEnabled(False)
+        self.runAct.setEnabled(False)
+        self.profileAct.setEnabled(False)
+        self.coverageAct.setEnabled(False)
+        self.debugActGrp.setEnabled(False)
+        self.dbgSetBpActGrp.setEnabled(False)
+        self.lastAction = -1
+        if not self.projectOpen:
+            self.restartAct.setEnabled(False)
+            self.lastDebuggedFile = None
+            self.lastStartAction = 0
+            self.clientType = ""
+        
+    def __checkActions(self, editor):
+        """
+        Private slot to check some actions for their enable/disable status.
+        
+        @param editor editor window
+        """
+        fn = editor.getFileName() if editor else None
+        
+        cap = 0
+        if fn:
+            for language in self.debugServer.getSupportedLanguages():
+                exts = self.debugServer.getExtensions(language)
+                if fn.endswith(exts):
+                    cap = self.debugServer.getClientCapabilities(language)
+                    break
+            else:
+                if editor.isPy3File():
+                    cap = self.debugServer.getClientCapabilities('Python3')
+                elif editor.isRubyFile():
+                    cap = self.debugServer.getClientCapabilities('Ruby')
+        
+            if not self.passive:
+                self.runAct.setEnabled(cap & HasInterpreter)
+                self.coverageAct.setEnabled(cap & HasCoverage)
+                self.profileAct.setEnabled(cap & HasProfiler)
+                self.debugAct.setEnabled(cap & HasDebugger)
+            self.dbgSetBpActGrp.setEnabled(cap & HasDebugger)
+            if editor.curLineHasBreakpoint():
+                self.dbgEditBpAct.setEnabled(True)
+            else:
+                self.dbgEditBpAct.setEnabled(False)
+            if editor.hasBreakpoints():
+                self.dbgNextBpAct.setEnabled(True)
+                self.dbgPrevBpAct.setEnabled(True)
+            else:
+                self.dbgNextBpAct.setEnabled(False)
+                self.dbgPrevBpAct.setEnabled(False)
+        else:
+            self.runAct.setEnabled(False)
+            self.coverageAct.setEnabled(False)
+            self.profileAct.setEnabled(False)
+            self.debugAct.setEnabled(False)
+            self.dbgSetBpActGrp.setEnabled(False)
+        
+    def __cursorChanged(self, editor):
+        """
+        Private slot handling the cursorChanged signal of the viewmanager.
+        
+        @param editor editor window
+        """
+        if editor is None:
+            return
+        
+        if editor.isPyFile() or editor.isRubyFile():
+            if editor.curLineHasBreakpoint():
+                self.dbgEditBpAct.setEnabled(True)
+            else:
+                self.dbgEditBpAct.setEnabled(False)
+            if editor.hasBreakpoints():
+                self.dbgNextBpAct.setEnabled(True)
+                self.dbgPrevBpAct.setEnabled(True)
+            else:
+                self.dbgNextBpAct.setEnabled(False)
+                self.dbgPrevBpAct.setEnabled(False)
+        
+    def __projectOpened(self):
+        """
+        Private slot to handle the projectOpened signal.
+        """
+        self.projectOpen = True
+        cap = self.debugServer.getClientCapabilities(
+            self.project.getProjectLanguage())
+        if not self.passive:
+            self.debugProjectAct.setEnabled(cap & HasDebugger)
+            self.runProjectAct.setEnabled(cap & HasInterpreter)
+            self.profileProjectAct.setEnabled(cap & HasProfiler)
+            self.coverageProjectAct.setEnabled(cap & HasCoverage)
+        
+    def __projectClosed(self):
+        """
+        Private slot to handle the projectClosed signal.
+        """
+        self.projectOpen = False
+        self.runProjectAct.setEnabled(False)
+        self.profileProjectAct.setEnabled(False)
+        self.coverageProjectAct.setEnabled(False)
+        self.debugProjectAct.setEnabled(False)
+        
+        if not self.editorOpen:
+            self.restartAct.setEnabled(False)
+            self.lastDebuggedFile = None
+            self.lastStartAction = 0
+            self.clientType = ""
+        
+    def clearHistories(self):
+        """
+        Public method to clear the various debug histories.
+        """
+        self.argvHistory = []
+        self.wdHistory = []
+        self.envHistory = []
+        self.multiprocessNoDebugHistory = []
+        
+        Preferences.Prefs.settings.setValue(
+            'DebugInfo/ArgumentsHistory', self.argvHistory)
+        Preferences.Prefs.settings.setValue(
+            'DebugInfo/WorkingDirectoryHistory', self.wdHistory)
+        Preferences.Prefs.settings.setValue(
+            'DebugInfo/EnvironmentHistory', self.envHistory)
+        Preferences.Prefs.settings.setValue(
+            'DebugInfo/MultiprocessNoDebugHistory',
+            self.multiprocessNoDebugHistory)
+        
+    def shutdown(self):
+        """
+        Public method to perform shutdown actions.
+        """
+        # Just save the 10 most recent entries
+        del self.argvHistory[10:]
+        del self.wdHistory[10:]
+        del self.envHistory[10:]
+        
+        Preferences.Prefs.settings.setValue(
+            'DebugInfo/VirtualEnvironment', self.lastUsedVenvName)
+        Preferences.Prefs.settings.setValue(
+            'DebugInfo/ArgumentsHistory', self.argvHistory)
+        Preferences.Prefs.settings.setValue(
+            'DebugInfo/WorkingDirectoryHistory', self.wdHistory)
+        Preferences.Prefs.settings.setValue(
+            'DebugInfo/EnvironmentHistory', self.envHistory)
+        Preferences.Prefs.settings.setValue(
+            'DebugInfo/Exceptions', self.excList)
+        Preferences.Prefs.settings.setValue(
+            'DebugInfo/IgnoredExceptions', self.excIgnoreList)
+        Preferences.Prefs.settings.setValue(
+            'DebugInfo/ReportExceptions', self.exceptions)
+        Preferences.Prefs.settings.setValue(
+            'DebugInfo/AutoClearShell', self.autoClearShell)
+        Preferences.Prefs.settings.setValue(
+            'DebugInfo/TracePython', self.tracePython)
+        Preferences.Prefs.settings.setValue(
+            'DebugInfo/AutoContinue', self.autoContinue)
+        Preferences.Prefs.settings.setValue(
+            'DebugInfo/EnableMultiprocess', self.enableMultiprocess)
+        Preferences.Prefs.settings.setValue(
+            'DebugInfo/MultiprocessNoDebugHistory',
+            self.multiprocessNoDebugHistory)
+        Preferences.Prefs.settings.setValue(
+            'DebugInfo/OverrideGlobal',
+            self.overrideGlobalConfig["enable"])
+        Preferences.Prefs.settings.setValue(
+            'DebugInfo/RedirectStdinStdout',
+            self.overrideGlobalConfig["redirect"])
+        
+    def shutdownServer(self):
+        """
+        Public method to shut down the debug server.
+        
+        This is needed to cleanly close the sockets on Win OS.
+        
+        @return always true
+        """
+        self.debugServer.shutdownServer()
+        return True
+        
+    def __resetUI(self, fullReset=True):
+        """
+        Private slot to reset the user interface.
+        
+        @param fullReset flag indicating a full reset is required
+        @type bool
+        """
+        self.lastAction = -1
+        self.debugActGrp.setEnabled(False)
+        self.__clientDebuggerIds.clear()
+        
+        if not self.passive:
+            if self.editorOpen:
+                editor = self.viewmanager.activeWindow()
+            else:
+                editor = None
+            self.__checkActions(editor)
+            
+            self.debugProjectAct.setEnabled(self.projectOpen)
+            self.runProjectAct.setEnabled(self.projectOpen)
+            self.profileProjectAct.setEnabled(self.projectOpen)
+            self.coverageProjectAct.setEnabled(self.projectOpen)
+            if (
+                self.lastDebuggedFile is not None and
+                (self.editorOpen or self.projectOpen)
+            ):
+                self.restartAct.setEnabled(True)
+            else:
+                self.restartAct.setEnabled(False)
+            self.stopAct.setEnabled(False)
+        
+        self.resetUI.emit(fullReset)
+    
+    def __clientDebuggerId(self, debuggerId):
+        """
+        Private slot to track the list of connected debuggers.
+        
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.__clientDebuggerIds.add(debuggerId)
+    
+    def __clientLine(self, fn, line, debuggerId, threadName, forStack):
+        """
+        Private method to handle a change to the current line.
+        
+        @param fn filename
+        @type str
+        @param line linenumber
+        @type int
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param threadName name of the thread signaling the event
+        @type str
+        @param forStack flag indicating this is for a stack dump
+        @type bool
+        """
+        self.ui.raise_()
+        self.ui.activateWindow()
+        if self.ui.getViewProfile() != "debug":
+            self.ui.setDebugProfile()
+        self.viewmanager.setFileLine(fn, line)
+        if not forStack:
+            self.__getThreadList(debuggerId)
+            self.__getClientVariables(debuggerId)
+
+        self.debugActGrp.setEnabled(True)
+    
+    @pyqtSlot(str)
+    def __clientDisconnected(self, debuggerId):
+        """
+        Private slot to handle a debug client disconnecting its control
+        socket.
+        
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.__clientDebuggerIds.discard(debuggerId)
+        
+        if len(self.__clientDebuggerIds) == 0:
+            self.viewmanager.exit()
+            self.__resetUI(fullReset=False)
+    
+    @pyqtSlot(str, int, str, bool, str)
+    def __clientExit(self, program, status, message, quiet, debuggerId):
+        """
+        Private slot to handle the debugged program terminating.
+        
+        @param program name of the exited program
+        @type str
+        @param status exit code of the debugged program
+        @type int
+        @param message exit message of the debugged program
+        @type str
+        @param quiet flag indicating to suppress exit info display
+        @type bool
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.__clientDisconnected(debuggerId)
+        
+        if not quiet:
+            if not program:
+                program = self.ui.currentProg
+            
+            if message:
+                info = self.tr("Message: {0}").format(
+                    Utilities.html_uencode(message))
+            else:
+                info = ""
+            if program is None:
+                msg = self.tr(
+                    '<p>The program has terminated with an exit status of'
+                    ' {0}.</p><p>{1}</p>').format(status, info)
+            else:
+                msg = self.tr(
+                    '<p><b>{0}</b> has terminated with an exit status of'
+                    ' {1}.</p><p>{2}</p>').format(
+                    os.path.basename(program), status, info)
+            if status != 0:
+                timeout = 0
+                kind = NotificationTypes.WARNING
+            else:
+                timeout = None
+                kind = NotificationTypes.INFORMATION
+            self.ui.showNotification(
+                UI.PixmapCache.getPixmap("debug48"),
+                self.tr("Program terminated"), msg, kind=kind,
+                timeout=timeout)
+    
+    def __lastClientExited(self):
+        """
+        Private slot handling the exit of the last client.
+        """
+        self.viewmanager.exit()
+        self.__resetUI()
+    
+    def __clientSyntaxError(self, message, filename, lineNo, characterNo):
+        """
+        Private method to handle a syntax error in the debugged program.
+        
+        @param message message of the syntax error (string)
+        @param filename translated filename of the syntax error position
+            (string)
+        @param lineNo line number of the syntax error position (integer)
+        @param characterNo character number of the syntax error position
+            (integer)
+        """
+        self.__resetUI()
+        self.ui.raise_()
+        self.ui.activateWindow()
+        
+        if message is None:
+            E5MessageBox.critical(
+                self.ui, Program,
+                self.tr(
+                    'The program being debugged contains an unspecified'
+                    ' syntax error.'))
+            return
+            
+        if not os.path.isabs(filename):
+            if os.path.exists(os.path.join(self.project.getProjectPath(),
+                              filename)):
+                filename = os.path.join(self.project.getProjectPath(),
+                                        filename)
+            else:
+                ms = self.project.getMainScript(normalized=True)
+                if ms is not None:
+                    d = os.path.dirname(ms)
+                    if os.path.exists(os.path.join(d, filename)):
+                        filename = os.path.join(d, filename)
+        self.viewmanager.setFileLine(filename, lineNo, True, True)
+        E5MessageBox.critical(
+            self.ui, Program,
+            self.tr('<p>The file <b>{0}</b> contains the syntax error'
+                    ' <b>{1}</b> at line <b>{2}</b>, character <b>{3}</b>.'
+                    '</p>')
+                .format(filename, message, lineNo, characterNo))
+        
+    def __clientException(self, exceptionType, exceptionMessage, stackTrace,
+                          debuggerId):
+        """
+        Private method to handle an exception of the debugged program.
+        
+        @param exceptionType type of exception raised
+        @type str
+        @param exceptionMessage message given by the exception
+        @type (str
+        @param stackTrace list of stack entries
+        @type list of str
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.ui.raise_()
+        QApplication.processEvents()
+        if not exceptionType:
+            E5MessageBox.critical(
+                self.ui, Program,
+                self.tr('An unhandled exception occured.'
+                        ' See the shell window for details.'))
+            return
+        
+        if (
+            (self.exceptions and
+             exceptionType not in self.excIgnoreList and
+             (not len(self.excList) or
+              (len(self.excList) and exceptionType in self.excList)
+              )
+             ) or exceptionType.startswith('unhandled')
+        ):
+            res = None
+            if stackTrace:
+                with contextlib.suppress(UnicodeError, OSError):
+                    file, line = stackTrace[0][:2]
+                    source, encoding = Utilities.readEncodedFile(file)
+                    source = source.splitlines(True)
+                    if len(source) >= line:
+                        lineFlags = Utilities.extractLineFlags(
+                            source[line - 1].strip())
+                        with contextlib.suppress(IndexError):
+                            lineFlags += Utilities.extractLineFlags(
+                                source[line].strip(), flagsLine=True)
+                        if "__IGNORE_EXCEPTION__" in lineFlags:
+                            res = E5MessageBox.No
+                if res != E5MessageBox.No:
+                    self.viewmanager.setFileLine(
+                        stackTrace[0][0], stackTrace[0][1], True)
+            if res != E5MessageBox.No:
+                self.ui.activateWindow()
+                if Preferences.getDebugger("BreakAlways"):
+                    res = E5MessageBox.Yes
+                else:
+                    if stackTrace:
+                        if exceptionType.startswith('unhandled'):
+                            buttons = E5MessageBox.StandardButtons(
+                                E5MessageBox.No |
+                                E5MessageBox.Yes)
+                        else:
+                            buttons = E5MessageBox.StandardButtons(
+                                E5MessageBox.No |
+                                E5MessageBox.Yes |
+                                E5MessageBox.Ignore)
+                        res = E5MessageBox.critical(
+                            self.ui, Program,
+                            self.tr(
+                                '<p>The debugged program raised the exception'
+                                ' <b>{0}</b><br>"<b>{1}</b>"<br>'
+                                'File: <b>{2}</b>, Line: <b>{3}</b></p>'
+                                '<p>Break here?</p>')
+                            .format(
+                                exceptionType,
+                                Utilities.html_encode(exceptionMessage),
+                                stackTrace[0][0],
+                                stackTrace[0][1]),
+                            buttons,
+                            E5MessageBox.No)
+                    else:
+                        res = E5MessageBox.critical(
+                            self.ui, Program,
+                            self.tr(
+                                '<p>The debugged program raised the exception'
+                                ' <b>{0}</b><br>"<b>{1}</b>"</p>')
+                            .format(
+                                exceptionType,
+                                Utilities.html_encode(exceptionMessage)))
+            if res == E5MessageBox.Yes:
+                self.debugServer.setDebugging(True)
+                self.exceptionInterrupt.emit()
+                stack = []
+                for fn, ln, func, args in stackTrace:
+                    stack.append((fn, ln, func, args))
+                self.clientStack.emit(stack, debuggerId)
+                self.__getClientVariables(debuggerId)
+                self.__getClientDisassembly(debuggerId)
+                self.ui.setDebugProfile()
+                self.debugActGrp.setEnabled(True)
+                return
+            elif (
+                res == E5MessageBox.Ignore and
+                exceptionType not in self.excIgnoreList
+            ):
+                self.excIgnoreList.append(exceptionType)
+        
+        if self.lastAction != -1:
+            if self.lastAction == 2:
+                self.__specialContinue(debuggerId)
+            else:
+                self.debugActions[self.lastAction](debuggerId)
+        else:
+            self.__continue(debuggerId)
+        
+    def __clientSignal(self, message, filename, lineNo, funcName, funcArgs,
+                       debuggerId):
+        """
+        Private method to handle a signal generated on the client side.
+        
+        @param message message of the syntax error
+        @type str
+        @param filename translated filename of the syntax error position
+        @type str
+        @param lineNo line number of the syntax error position
+        @type int
+        @param funcName name of the function causing the signal
+        @type str
+        @param funcArgs function arguments
+        @type str
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.ui.raise_()
+        self.ui.activateWindow()
+        QApplication.processEvents()
+        self.viewmanager.setFileLine(filename, lineNo, True)
+        E5MessageBox.critical(
+            self.ui, Program,
+            self.tr("""<p>The program generate the signal "{0}".<br/>"""
+                    """File: <b>{1}</b>, Line: <b>{2}</b></p>""").format(
+                message, filename, lineNo))
+        
+    def __clientGone(self, unplanned):
+        """
+        Private method to handle the disconnection of the debugger client.
+        
+        @param unplanned True if the client died, False otherwise
+        """
+        self.__resetUI()
+        if unplanned:
+            E5MessageBox.information(
+                self.ui, Program,
+                self.tr('The program being debugged has terminated'
+                        ' unexpectedly.'))
+        
+    def __getThreadList(self, debuggerId):
+        """
+        Private method to get the list of threads from the client.
+        
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.debugServer.remoteThreadList(debuggerId)
+        
+    def __clientThreadSet(self, debuggerId):
+        """
+        Private method to handle a change of the client's current thread.
+        
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.debugServer.remoteClientVariables(
+            debuggerId, 0, self.__localsVarFilterList)
+    
+    def __getClientVariables(self, debuggerId):
+        """
+        Private method to request the global and local variables.
+        
+        In the first step, the global variables are requested from the client.
+        Once these have been received, the local variables are requested.
+        This happens in the method '__clientVariables'.
+        
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        # get globals first
+        self.debugServer.remoteClientVariables(
+            debuggerId, 1, self.__globalsVarFilterList)
+        # the local variables are requested once we have received the globals
+        
+    def __clientVariables(self, scope, variables, debuggerId):
+        """
+        Private method to write the clients variables to the user interface.
+        
+        @param scope scope of the variables (-1 = empty locals, 1 = global,
+            0 = local)
+        @type int
+        @param variables the list of variables from the client
+        @type list
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        if debuggerId == self.getSelectedDebuggerId():
+            self.ui.activateDebugViewer()
+            if scope > 0:
+                self.debugViewer.showVariables(variables, True)
+                if scope == 1:
+                    # now get the local variables
+                    self.debugServer.remoteClientVariables(
+                        self.getSelectedDebuggerId(),
+                        0, self.__localsVarFilterList)
+            elif scope == 0:
+                self.debugViewer.showVariables(variables, False)
+            elif scope == -1:
+                vlist = [(self.tr('No locals available.'), '', '')]
+                self.debugViewer.showVariables(vlist, False)
+        
+    def __clientVariable(self, scope, variables, debuggerId):
+        """
+        Private method to write the contents of a clients classvariable to
+        the user interface.
+        
+        @param scope scope of the variables (-1 = empty locals, 1 = global,
+            0 = local)
+        @type int
+        @param variables the list of variables from the client
+        @type list
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        if debuggerId == self.getSelectedDebuggerId():
+            self.ui.activateDebugViewer()
+            if scope == 1:
+                self.debugViewer.showVariable(variables, True)
+            elif scope == 0:
+                self.debugViewer.showVariable(variables, False)
+    
+    def __getClientDisassembly(self, debuggerId):
+        """
+        Private method to ask the client for the latest traceback disassembly.
+        
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.debugServer.remoteClientDisassembly(debuggerId)
+    
+    def __clientBreakConditionError(self, filename, lineno, debuggerId):
+        """
+        Private method to handle a condition error of a breakpoint.
+        
+        @param filename filename of the breakpoint
+        @type str
+        @param lineno line umber of the breakpoint
+        @type int
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        E5MessageBox.critical(
+            self.ui,
+            self.tr("Breakpoint Condition Error"),
+            self.tr(
+                """<p>The condition of the breakpoint <b>{0}, {1}</b>"""
+                """ contains a syntax error.</p>""")
+            .format(filename, lineno))
+        
+        model = self.debugServer.getBreakPointModel()
+        index = model.getBreakPointIndex(filename, lineno)
+        if not index.isValid():
+            return
+        
+        bp = model.getBreakPointByIndex(index)
+        if not bp:
+            return
+        
+        fn, line, cond, temp, enabled, count = bp[:6]
+        
+        from .EditBreakpointDialog import EditBreakpointDialog
+        dlg = EditBreakpointDialog(
+            (fn, line), (cond, temp, enabled, count),
+            [], self.ui, modal=True)
+        if dlg.exec() == QDialog.DialogCode.Accepted:
+            cond, temp, enabled, count = dlg.getData()
+            model.setBreakPointByIndex(index, fn, line,
+                                       (cond, temp, enabled, count))
+        
+    def __clientWatchConditionError(self, cond, debuggerId):
+        """
+        Private method to handle a expression error of a watch expression.
+        
+        Note: This can only happen for normal watch expressions
+        
+        @param cond expression of the watch expression
+        @type str
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        E5MessageBox.critical(
+            self.ui,
+            self.tr("Watch Expression Error"),
+            self.tr("""<p>The watch expression <b>{0}</b>"""
+                    """ contains a syntax error.</p>""")
+            .format(cond))
+        
+        model = self.debugServer.getWatchPointModel()
+        index = model.getWatchPointIndex(cond)
+        if not index.isValid():
+            return
+        
+        wp = model.getWatchPointByIndex(index)
+        if not wp:
+            return
+        
+        cond, special, temp, enabled, count = wp[:5]
+        
+        from .EditWatchpointDialog import EditWatchpointDialog
+        dlg = EditWatchpointDialog(
+            (cond, temp, enabled, count, special), self.ui)
+        if dlg.exec() == QDialog.DialogCode.Accepted:
+            cond, temp, enabled, count, special = dlg.getData()
+            
+            # check for duplicates
+            idx = model.getWatchPointIndex(cond, special)
+            duplicate = (idx.isValid() and
+                         idx.internalPointer() != index.internalPointer())
+            if duplicate:
+                if not special:
+                    msg = self.tr(
+                        """<p>A watch expression '<b>{0}</b>'"""
+                        """ already exists.</p>"""
+                    ).format(Utilities.html_encode(cond))
+                else:
+                    msg = self.tr(
+                        """<p>A watch expression '<b>{0}</b>'"""
+                        """ for the variable <b>{1}</b> already"""
+                        """ exists.</p>"""
+                    ).format(special, Utilities.html_encode(cond))
+                E5MessageBox.warning(
+                    self.ui,
+                    self.tr("Watch expression already exists"),
+                    msg)
+                model.deleteWatchPointByIndex(index)
+            else:
+                model.setWatchPointByIndex(index, cond, special,
+                                           (temp, enabled, count))
+        
+    def __configureVariablesFilters(self):
+        """
+        Private slot for displaying the variables filter configuration dialog.
+        """
+        from .VariablesFilterDialog import VariablesFilterDialog
+        dlg = VariablesFilterDialog(self.ui, 'Filter Dialog', True)
+        dlg.setSelection(self.__localsVarFilterList,
+                         self.__globalsVarFilterList)
+        if dlg.exec() == QDialog.DialogCode.Accepted:
+            self.__localsVarFilterList, self.__globalsVarFilterList = (
+                dlg.getSelection()
+            )
+            self.debugViewer.setVariablesFilter(
+                self.__globalsVarFilterList, self.__localsVarFilterList)
+
+    def __configureExceptionsFilter(self):
+        """
+        Private slot for displaying the exception filter dialog.
+        """
+        from .ExceptionsFilterDialog import ExceptionsFilterDialog
+        dlg = ExceptionsFilterDialog(self.excList, ignore=False)
+        if dlg.exec() == QDialog.DialogCode.Accepted:
+            self.excList = dlg.getExceptionsList()[:]   # keep a copy
+        
+    def __configureIgnoredExceptions(self):
+        """
+        Private slot for displaying the ignored exceptions dialog.
+        """
+        from .ExceptionsFilterDialog import ExceptionsFilterDialog
+        dlg = ExceptionsFilterDialog(self.excIgnoreList, ignore=True)
+        if dlg.exec() == QDialog.DialogCode.Accepted:
+            self.excIgnoreList = dlg.getExceptionsList()[:]   # keep a copy
+        
+    def __toggleBreakpoint(self):
+        """
+        Private slot to handle the 'Set/Reset breakpoint' action.
+        """
+        self.viewmanager.activeWindow().menuToggleBreakpoint()
+        
+    def __editBreakpoint(self):
+        """
+        Private slot to handle the 'Edit breakpoint' action.
+        """
+        self.viewmanager.activeWindow().menuEditBreakpoint()
+        
+    def __nextBreakpoint(self):
+        """
+        Private slot to handle the 'Next breakpoint' action.
+        """
+        self.viewmanager.activeWindow().menuNextBreakpoint()
+        
+    def __previousBreakpoint(self):
+        """
+        Private slot to handle the 'Previous breakpoint' action.
+        """
+        self.viewmanager.activeWindow().menuPreviousBreakpoint()
+        
+    def __clearBreakpoints(self):
+        """
+        Private slot to handle the 'Clear breakpoints' action.
+        """
+        self.debugServer.getBreakPointModel().deleteAll()
+        
+    def __showDebugMenu(self):
+        """
+        Private method to set up the debug menu.
+        """
+        bpCount = self.debugServer.getBreakPointModel().rowCount()
+        self.menuBreakpointsAct.setEnabled(bpCount > 0)
+        
+    def __showBreakpointsMenu(self):
+        """
+        Private method to handle the show breakpoints menu signal.
+        """
+        self.breakpointsMenu.clear()
+        
+        model = self.debugServer.getBreakPointModel()
+        for row in range(model.rowCount()):
+            index = model.index(row, 0)
+            filename, line, cond = model.getBreakPointByIndex(index)[:3]
+            formattedCond = " : {0}".format(cond[:20]) if cond else ""
+            bpSuffix = " : {0:d}{1}".format(line, formattedCond)
+            act = self.breakpointsMenu.addAction(
+                "{0}{1}".format(
+                    Utilities.compactPath(
+                        filename,
+                        self.ui.maxMenuFilePathLen - len(bpSuffix)),
+                    bpSuffix))
+            act.setData([filename, line])
+    
+    def __breakpointSelected(self, act):
+        """
+        Private method to handle the breakpoint selected signal.
+        
+        @param act reference to the action that triggered (QAction)
+        """
+        qvList = act.data()
+        filename = qvList[0]
+        line = qvList[1]
+        self.viewmanager.openSourceFile(filename, line)
+        
+    def __compileChangedProjectFiles(self):
+        """
+        Private method to signal compilation of changed forms and resources
+        is wanted.
+        """
+        if Preferences.getProject("AutoCompileForms"):
+            self.compileForms.emit()
+        if Preferences.getProject("AutoCompileResources"):
+            self.compileResources.emit()
+        if Preferences.getProject("AutoExecuteMake"):
+            self.executeMake.emit()
+        QApplication.processEvents()
+        
+    def __coverageScript(self):
+        """
+        Private slot to handle the coverage of script action.
+        """
+        self.__doCoverage(False)
+        
+    def __coverageProject(self):
+        """
+        Private slot to handle the coverage of project action.
+        """
+        self.__compileChangedProjectFiles()
+        self.__doCoverage(True)
+        
+    def __doCoverage(self, runProject):
+        """
+        Private method to handle the coverage actions.
+        
+        @param runProject flag indicating coverage of the current project
+            (True) or script (false)
+        """
+        from .StartDialog import StartDialog
+        
+        self.__resetUI()
+        doNotStart = False
+        
+        # Get the command line arguments, the working directory and the
+        # exception reporting flag.
+        cap = (
+            self.tr("Coverage of Project")
+            if runProject else
+            self.tr("Coverage of Script")
+        )
+        dlg = StartDialog(
+            cap, self.lastUsedVenvName, self.argvHistory, self.wdHistory,
+            self.envHistory, self.exceptions, self.ui, 2,
+            autoClearShell=self.autoClearShell,
+            configOverride=self.overrideGlobalConfig)
+        if dlg.exec() == QDialog.DialogCode.Accepted:
+            (lastUsedVenvName, argv, wd, env, exceptions, clearShell,
+             console) = dlg.getData()
+            configOverride = dlg.getGlobalOverrideData()
+            eraseCoverage = dlg.getCoverageData()
+            
+            if runProject:
+                fn = self.project.getMainScript(True)
+                if fn is None:
+                    E5MessageBox.critical(
+                        self.ui,
+                        self.tr("Coverage of Project"),
+                        self.tr(
+                            "There is no main script defined for the"
+                            " current project. Aborting"))
+                    return
+                    
+                if (
+                    Preferences.getDebugger("Autosave") and
+                    not self.project.saveAllScripts(reportSyntaxErrors=True)
+                ):
+                    doNotStart = True
+                
+                # save the info for later use
+                self.project.setDbgInfo(
+                    lastUsedVenvName, argv, wd, env, exceptions, self.excList,
+                    self.excIgnoreList, clearShell,
+                    configOverride=configOverride
+                )
+                
+                self.lastStartAction = 6
+                self.clientType = self.project.getProjectLanguage()
+            else:
+                editor = self.viewmanager.activeWindow()
+                if editor is None:
+                    return
+                
+                if (
+                    not self.viewmanager.checkDirty(
+                        editor, Preferences.getDebugger("Autosave")) or
+                    editor.getFileName() is None
+                ):
+                    return
+                    
+                fn = editor.getFileName()
+                self.lastStartAction = 5
+                self.clientType = editor.determineFileType()
+                
+            # save the filename for use by the restart method
+            self.lastDebuggedFile = fn
+            self.restartAct.setEnabled(True)
+            
+            # save the most recently used virtual environment
+            self.lastUsedVenvName = lastUsedVenvName
+            
+            # This moves any previous occurrence of these arguments to the head
+            # of the list.
+            self.setArgvHistory(argv)
+            self.setWdHistory(wd)
+            self.setEnvHistory(env)
+            
+            # Save the exception flags
+            self.exceptions = exceptions
+            
+            # Save the erase coverage flag
+            self.eraseCoverage = eraseCoverage
+            
+            # Save the clear interpreter flag
+            self.autoClearShell = clearShell
+            
+            # Save the run in console flag
+            self.runInConsole = console
+            
+            # Save the global config override data
+            self.overrideGlobalConfig = copy.deepcopy(configOverride)
+            
+            # Hide all error highlights
+            self.viewmanager.unhighlight()
+            
+            if not doNotStart:
+                if runProject and self.project.getProjectType() in [
+                        "E6Plugin"]:
+                    argv = '--plugin="{0}" {1}'.format(fn, argv)
+                    fn = os.path.join(getConfig('ericDir'), "eric6.py")
+                
+                self.debugViewer.initCallStackViewer(runProject)
+                
+                # Ask the client to open the new program.
+                self.debugServer.remoteCoverage(
+                    lastUsedVenvName, fn, argv, wd, env,
+                    autoClearShell=self.autoClearShell, erase=eraseCoverage,
+                    forProject=runProject, runInConsole=console,
+                    clientType=self.clientType,
+                    configOverride=self.overrideGlobalConfig)
+                
+                self.stopAct.setEnabled(True)
+        
+        if dlg.clearHistories():
+            self.setArgvHistory("", clearHistories=True)
+            self.setWdHistory("", clearHistories=True)
+            self.setEnvHistory("", clearHistories=True)
+            self.setMultiprocessNoDebugHistory("", clearHistories=True)
+        elif dlg.historiesModified():
+            argvHistory, wdHistory, envHistory, _ = dlg.getHistories()
+            self.setArgvHistory("", history=argvHistory)
+            self.setWdHistory("", history=wdHistory)
+            self.setEnvHistory("", history=envHistory)
+    
+    def __profileScript(self):
+        """
+        Private slot to handle the profile script action.
+        """
+        self.__doProfile(False)
+        
+    def __profileProject(self):
+        """
+        Private slot to handle the profile project action.
+        """
+        self.__compileChangedProjectFiles()
+        self.__doProfile(True)
+        
+    def __doProfile(self, runProject):
+        """
+        Private method to handle the profile actions.
+        
+        @param runProject flag indicating profiling of the current project
+            (True) or script (False)
+        """
+        from .StartDialog import StartDialog
+        
+        self.__resetUI()
+        doNotStart = False
+        
+        # Get the command line arguments, the working directory and the
+        # exception reporting flag.
+        cap = (
+            self.tr("Profile of Project")
+            if runProject else
+            self.tr("Profile of Script")
+        )
+        dlg = StartDialog(
+            cap, self.lastUsedVenvName, self.argvHistory, self.wdHistory,
+            self.envHistory, self.exceptions, self.ui, 3,
+            autoClearShell=self.autoClearShell,
+            configOverride=self.overrideGlobalConfig)
+        if dlg.exec() == QDialog.DialogCode.Accepted:
+            (lastUsedVenvName, argv, wd, env, exceptions, clearShell,
+             console) = dlg.getData()
+            configOverride = dlg.getGlobalOverrideData()
+            eraseTimings = dlg.getProfilingData()
+            
+            if runProject:
+                fn = self.project.getMainScript(True)
+                if fn is None:
+                    E5MessageBox.critical(
+                        self.ui,
+                        self.tr("Profile of Project"),
+                        self.tr(
+                            "There is no main script defined for the"
+                            " current project. Aborting"))
+                    return
+                    
+                if (
+                    Preferences.getDebugger("Autosave") and
+                    not self.project.saveAllScripts(reportSyntaxErrors=True)
+                ):
+                    doNotStart = True
+                
+                # save the info for later use
+                self.project.setDbgInfo(
+                    lastUsedVenvName, argv, wd, env, exceptions, self.excList,
+                    self.excIgnoreList, clearShell,
+                    configOverride=configOverride
+                )
+                
+                self.lastStartAction = 8
+                self.clientType = self.project.getProjectLanguage()
+            else:
+                editor = self.viewmanager.activeWindow()
+                if editor is None:
+                    return
+                
+                if (
+                    not self.viewmanager.checkDirty(
+                        editor, Preferences.getDebugger("Autosave")) or
+                    editor.getFileName() is None
+                ):
+                    return
+                    
+                fn = editor.getFileName()
+                self.lastStartAction = 7
+                self.clientType = editor.determineFileType()
+                
+            # save the filename for use by the restart method
+            self.lastDebuggedFile = fn
+            self.restartAct.setEnabled(True)
+            
+            # save the most recently used virtual environment
+            self.lastUsedVenvName = lastUsedVenvName
+            
+            # This moves any previous occurrence of these arguments to the head
+            # of the list.
+            self.setArgvHistory(argv)
+            self.setWdHistory(wd)
+            self.setEnvHistory(env)
+            
+            # Save the exception flags
+            self.exceptions = exceptions
+            
+            # Save the erase timing flag
+            self.eraseTimings = eraseTimings
+            
+            # Save the clear interpreter flag
+            self.autoClearShell = clearShell
+            
+            # Save the run in console flag
+            self.runInConsole = console
+            
+            # Save the global config override data
+            self.overrideGlobalConfig = copy.deepcopy(configOverride)
+            
+            # Hide all error highlights
+            self.viewmanager.unhighlight()
+            
+            if not doNotStart:
+                if runProject and self.project.getProjectType() in [
+                        "E6Plugin"]:
+                    argv = '--plugin="{0}" {1}'.format(fn, argv)
+                    fn = os.path.join(getConfig('ericDir'), "eric6.py")
+                
+                self.debugViewer.initCallStackViewer(runProject)
+                
+                # Ask the client to open the new program.
+                self.debugServer.remoteProfile(
+                    lastUsedVenvName, fn, argv, wd, env,
+                    autoClearShell=self.autoClearShell, erase=eraseTimings,
+                    forProject=runProject, runInConsole=console,
+                    clientType=self.clientType,
+                    configOverride=self.overrideGlobalConfig)
+                
+                self.stopAct.setEnabled(True)
+        
+        if dlg.clearHistories():
+            self.setArgvHistory("", clearHistories=True)
+            self.setWdHistory("", clearHistories=True)
+            self.setEnvHistory("", clearHistories=True)
+            self.setMultiprocessNoDebugHistory("", clearHistories=True)
+        elif dlg.historiesModified():
+            argvHistory, wdHistory, envHistory, _ = dlg.getHistories()
+            self.setArgvHistory("", history=argvHistory)
+            self.setWdHistory("", history=wdHistory)
+            self.setEnvHistory("", history=envHistory)
+    
+    def __runScript(self):
+        """
+        Private slot to handle the run script action.
+        """
+        self.__doRun(False)
+        
+    def __runProject(self):
+        """
+        Private slot to handle the run project action.
+        """
+        self.__compileChangedProjectFiles()
+        self.__doRun(True)
+        
+    def __doRun(self, runProject):
+        """
+        Private method to handle the run actions.
+        
+        @param runProject flag indicating running the current project (True)
+                or script (False)
+        """
+        from .StartDialog import StartDialog
+        
+        self.__resetUI()
+        doNotStart = False
+        
+        # Get the command line arguments, the working directory and the
+        # exception reporting flag.
+        cap = (
+            self.tr("Run Project")
+            if runProject else
+            self.tr("Run Script")
+        )
+        dlg = StartDialog(
+            cap, self.lastUsedVenvName, self.argvHistory, self.wdHistory,
+            self.envHistory, self.exceptions, self.ui, 1,
+            autoClearShell=self.autoClearShell,
+            configOverride=self.overrideGlobalConfig)
+        if dlg.exec() == QDialog.DialogCode.Accepted:
+            (lastUsedVenvName, argv, wd, env, exceptions, clearShell,
+             console) = dlg.getData()
+            configOverride = dlg.getGlobalOverrideData()
+            
+            if runProject:
+                fn = self.project.getMainScript(True)
+                if fn is None:
+                    E5MessageBox.critical(
+                        self.ui,
+                        self.tr("Run Project"),
+                        self.tr(
+                            "There is no main script defined for the"
+                            " current project. Aborting"))
+                    return
+                    
+                if (
+                    Preferences.getDebugger("Autosave") and
+                    not self.project.saveAllScripts(reportSyntaxErrors=True)
+                ):
+                    doNotStart = True
+                
+                # save the info for later use
+                self.project.setDbgInfo(
+                    lastUsedVenvName, argv, wd, env, exceptions, self.excList,
+                    self.excIgnoreList, clearShell,
+                    configOverride=configOverride
+                )
+                
+                self.lastStartAction = 4
+                self.clientType = self.project.getProjectLanguage()
+            else:
+                editor = self.viewmanager.activeWindow()
+                if editor is None:
+                    return
+                
+                if (
+                    not self.viewmanager.checkDirty(
+                        editor,
+                        Preferences.getDebugger("Autosave")) or
+                    editor.getFileName() is None
+                ):
+                    return
+                    
+                fn = editor.getFileName()
+                self.lastStartAction = 3
+                self.clientType = editor.determineFileType()
+                
+            # save the filename for use by the restart method
+            self.lastDebuggedFile = fn
+            self.restartAct.setEnabled(True)
+            
+            # save the most recently used virtual environment
+            self.lastUsedVenvName = lastUsedVenvName
+            
+            # This moves any previous occurrence of these arguments to the head
+            # of the list.
+            self.setArgvHistory(argv)
+            self.setWdHistory(wd)
+            self.setEnvHistory(env)
+            
+            # Save the exception flags
+            self.exceptions = exceptions
+            
+            # Save the clear interpreter flag
+            self.autoClearShell = clearShell
+            
+            # Save the run in console flag
+            self.runInConsole = console
+            
+            # Save the global config override data
+            self.overrideGlobalConfig = copy.deepcopy(configOverride)
+            
+            # Hide all error highlights
+            self.viewmanager.unhighlight()
+            
+            if not doNotStart:
+                if runProject and self.project.getProjectType() in [
+                        "E6Plugin"]:
+                    argv = '--plugin="{0}" {1}'.format(fn, argv)
+                    fn = os.path.join(getConfig('ericDir'), "eric6.py")
+                
+                self.debugViewer.initCallStackViewer(runProject)
+                
+                # Ask the client to open the new program.
+                self.debugServer.remoteRun(
+                    lastUsedVenvName, fn, argv, wd, env,
+                    autoClearShell=self.autoClearShell, forProject=runProject,
+                    runInConsole=console, clientType=self.clientType,
+                    configOverride=self.overrideGlobalConfig)
+                
+                self.stopAct.setEnabled(True)
+        
+        if dlg.clearHistories():
+            self.setArgvHistory("", clearHistories=True)
+            self.setWdHistory("", clearHistories=True)
+            self.setEnvHistory("", clearHistories=True)
+            self.setMultiprocessNoDebugHistory("", clearHistories=True)
+        elif dlg.historiesModified():
+            argvHistory, wdHistory, envHistory, _ = dlg.getHistories()
+            self.setArgvHistory("", history=argvHistory)
+            self.setWdHistory("", history=wdHistory)
+            self.setEnvHistory("", history=envHistory)
+    
+    def __debugScript(self):
+        """
+        Private slot to handle the debug script action.
+        """
+        self.__doDebug(False)
+        
+    def __debugProject(self):
+        """
+        Private slot to handle the debug project action.
+        """
+        self.__compileChangedProjectFiles()
+        self.__doDebug(True)
+        
+    def __doDebug(self, debugProject):
+        """
+        Private method to handle the debug actions.
+        
+        @param debugProject flag indicating debugging the current project
+            (True) or script (False)
+        """
+        from .StartDialog import StartDialog
+        
+        self.__resetUI()
+        doNotStart = False
+        
+        # Get the command line arguments, the working directory and the
+        # exception reporting flag.
+        cap = (
+            self.tr("Debug Project")
+            if debugProject else
+            self.tr("Debug Script")
+        )
+        dlg = StartDialog(
+            cap, self.lastUsedVenvName, self.argvHistory, self.wdHistory,
+            self.envHistory, self.exceptions, self.ui, 0,
+            tracePython=self.tracePython, autoClearShell=self.autoClearShell,
+            autoContinue=self.autoContinue,
+            enableMultiprocess=self.enableMultiprocess,
+            multiprocessNoDebugHistory=self.multiprocessNoDebugHistory,
+            configOverride=self.overrideGlobalConfig)
+        if dlg.exec() == QDialog.DialogCode.Accepted:
+            (lastUsedVenvName, argv, wd, env, exceptions, clearShell,
+             console) = dlg.getData()
+            configOverride = dlg.getGlobalOverrideData()
+            (tracePython, autoContinue, enableMultiprocess,
+             multiprocessNoDebug) = dlg.getDebugData()
+            
+            if debugProject:
+                fn = self.project.getMainScript(True)
+                if fn is None:
+                    E5MessageBox.critical(
+                        self.ui,
+                        self.tr("Debug Project"),
+                        self.tr(
+                            "There is no main script defined for the"
+                            " current project. No debugging possible."))
+                    return
+                    
+                if (
+                    Preferences.getDebugger("Autosave") and
+                    not self.project.saveAllScripts(reportSyntaxErrors=True)
+                ):
+                    doNotStart = True
+                
+                # save the info for later use
+                self.project.setDbgInfo(
+                    lastUsedVenvName, argv, wd, env, exceptions, self.excList,
+                    self.excIgnoreList, clearShell, tracePython=tracePython,
+                    autoContinue=autoContinue,
+                    enableMultiprocess=enableMultiprocess,
+                    multiprocessNoDebug=multiprocessNoDebug,
+                    configOverride=configOverride
+                )
+                
+                self.lastStartAction = 2
+                self.clientType = self.project.getProjectLanguage()
+            else:
+                editor = self.viewmanager.activeWindow()
+                if editor is None:
+                    return
+                
+                if (
+                    not self.viewmanager.checkDirty(
+                        editor, Preferences.getDebugger("Autosave")) or
+                    editor.getFileName() is None
+                ):
+                    return
+                    
+                fn = editor.getFileName()
+                self.lastStartAction = 1
+                self.clientType = editor.determineFileType()
+            
+            # save the filename for use by the restart method
+            self.lastDebuggedFile = fn
+            self.restartAct.setEnabled(True)
+            
+            # save the most recently used virtual environment
+            self.lastUsedVenvName = lastUsedVenvName
+            
+            # This moves any previous occurrence of these arguments to the head
+            # of the list.
+            self.setArgvHistory(argv)
+            self.setWdHistory(wd)
+            self.setEnvHistory(env)
+            
+            # Save the exception flags
+            self.exceptions = exceptions
+            
+            # Save the tracePython flag
+            self.tracePython = tracePython
+            
+            # Save the clear interpreter flag
+            self.autoClearShell = clearShell
+            
+            # Save the run in console flag
+            self.runInConsole = console
+            
+            # Save the auto continue flag
+            self.autoContinue = autoContinue
+            
+            # Save the multiprocess debugging data
+            self.enableMultiprocess = enableMultiprocess
+            self.setMultiprocessNoDebugHistory(multiprocessNoDebug)
+            
+            # Save the global config override data
+            self.overrideGlobalConfig = copy.deepcopy(configOverride)
+            
+            # Hide all error highlights
+            self.viewmanager.unhighlight()
+            
+            if not doNotStart:
+                if debugProject and self.project.getProjectType() in [
+                        "E6Plugin"]:
+                    argv = '--plugin="{0}" {1}'.format(fn, argv)
+                    fn = os.path.join(getConfig('ericDir'), "eric6.py")
+                    tracePython = True  # override flag because it must be true
+                
+                self.debugViewer.initCallStackViewer(debugProject)
+                
+                # Ask the client to send call trace info
+                enableCallTrace = self.debugViewer.isCallTraceEnabled()
+                self.debugViewer.clearCallTrace()
+                self.debugViewer.setCallTraceToProjectMode(debugProject)
+                
+                # Ask the client to open the new program.
+                self.debugServer.remoteLoad(
+                    lastUsedVenvName, fn, argv, wd, env,
+                    autoClearShell=self.autoClearShell,
+                    tracePython=tracePython,
+                    autoContinue=autoContinue, forProject=debugProject,
+                    runInConsole=console, clientType=self.clientType,
+                    enableCallTrace=enableCallTrace,
+                    enableMultiprocess=enableMultiprocess,
+                    multiprocessNoDebug=multiprocessNoDebug,
+                    configOverride=self.overrideGlobalConfig)
+                
+                if (
+                    self.debugServer.isClientProcessUp() and
+                    self.debugServer.getClientType() == self.clientType
+                ):
+                    # Signal that we have started a debugging session
+                    self.debuggingStarted.emit(fn)
+                    
+                    self.stopAct.setEnabled(True)
+        
+        if dlg.clearHistories():
+            self.setArgvHistory("", clearHistories=True)
+            self.setWdHistory("", clearHistories=True)
+            self.setEnvHistory("", clearHistories=True)
+            self.setMultiprocessNoDebugHistory("", clearHistories=True)
+        elif dlg.historiesModified():
+            (argvHistory, wdHistory, envHistory,
+             noDebugHistory) = dlg.getHistories()
+            self.setArgvHistory("", history=argvHistory)
+            self.setWdHistory("", history=wdHistory)
+            self.setEnvHistory("", history=envHistory)
+            self.setMultiprocessNoDebugHistory("", history=noDebugHistory)
+    
+    def __doRestart(self):
+        """
+        Private slot to handle the restart action to restart the last
+        debugged file.
+        """
+        self.__resetUI()
+        doNotStart = False
+        
+        # first save any changes
+        if self.lastStartAction in [1, 3, 5, 7, 9]:
+            editor = self.viewmanager.getOpenEditor(self.lastDebuggedFile)
+            if (
+                editor and
+                not self.viewmanager.checkDirty(
+                    editor, Preferences.getDebugger("Autosave"))
+            ):
+                return
+            forProject = False
+        elif self.lastStartAction in [2, 4, 6, 8, 10]:
+            if (
+                Preferences.getDebugger("Autosave") and
+                not self.project.saveAllScripts(reportSyntaxErrors=True)
+            ):
+                doNotStart = True
+            self.__compileChangedProjectFiles()
+            forProject = True
+        else:
+            return      # should not happen
+                    
+        # get the saved stuff
+        venvName = self.lastUsedVenvName
+        wd = self.wdHistory[0]
+        argv = self.argvHistory[0]
+        fn = self.lastDebuggedFile
+        env = self.envHistory[0]
+        
+        # Hide all error highlights
+        self.viewmanager.unhighlight()
+        
+        if not doNotStart:
+            if forProject and self.project.getProjectType() in [
+                    "E6Plugin"]:
+                argv = '--plugin="{0}" {1}'.format(fn, argv)
+                fn = os.path.join(getConfig('ericDir'), "eric6.py")
+            
+            self.debugViewer.initCallStackViewer(forProject)
+            
+            if self.lastStartAction in [1, 2]:
+                # Ask the client to send call trace info
+                enableCallTrace = self.debugViewer.isCallTraceEnabled()
+                self.debugViewer.clearCallTrace()
+                self.debugViewer.setCallTraceToProjectMode(forProject)
+                multiprocessNoDebug = self.multiprocessNoDebugHistory[0]
+                
+                # Ask the client to debug the new program.
+                self.debugServer.remoteLoad(
+                    venvName, fn, argv, wd, env,
+                    autoClearShell=self.autoClearShell,
+                    tracePython=self.tracePython,
+                    autoContinue=self.autoContinue,
+                    forProject=forProject,
+                    runInConsole=self.runInConsole,
+                    clientType=self.clientType,
+                    enableCallTrace=enableCallTrace,
+                    enableMultiprocess=self.enableMultiprocess,
+                    multiprocessNoDebug=multiprocessNoDebug,
+                    configOverride=self.overrideGlobalConfig)
+                
+                # Signal that we have started a debugging session
+                self.debuggingStarted.emit(fn)
+            
+            elif self.lastStartAction in [3, 4]:
+                # Ask the client to run the new program.
+                self.debugServer.remoteRun(
+                    venvName, fn, argv, wd, env,
+                    autoClearShell=self.autoClearShell,
+                    forProject=forProject,
+                    runInConsole=self.runInConsole,
+                    clientType=self.clientType,
+                    configOverride=self.overrideGlobalConfig)
+            
+            elif self.lastStartAction in [5, 6]:
+                # Ask the client to coverage run the new program.
+                self.debugServer.remoteCoverage(
+                    venvName, fn, argv, wd, env,
+                    autoClearShell=self.autoClearShell,
+                    erase=self.eraseCoverage,
+                    forProject=forProject,
+                    runInConsole=self.runInConsole,
+                    clientType=self.clientType,
+                    configOverride=self.overrideGlobalConfig)
+            
+            elif self.lastStartAction in [7, 8]:
+                # Ask the client to profile run the new program.
+                self.debugServer.remoteProfile(
+                    venvName, fn, argv, wd, env,
+                    autoClearShell=self.autoClearShell,
+                    erase=self.eraseTimings,
+                    forProject=forProject,
+                    runInConsole=self.runInConsole,
+                    clientType=self.clientType,
+                    configOverride=self.overrideGlobalConfig)
+            
+            self.stopAct.setEnabled(True)
+        
+    def __stopScript(self):
+        """
+        Private slot to stop the running script.
+        """
+        self.debugServer.startClient(False)
+        
+    def __passiveDebugStarted(self, fn, exc):
+        """
+        Private slot to handle a passive debug session start.
+        
+        @param fn filename of the debugged script
+        @param exc flag to enable exception reporting of the IDE (boolean)
+        """
+        # Hide all error highlights
+        self.viewmanager.unhighlight()
+        
+        # Set filename of script being debugged
+        self.ui.currentProg = fn
+        
+        # Set exception reporting
+        self.setExceptionReporting(exc)
+        
+        # Signal that we have started a debugging session
+        self.debuggingStarted.emit(fn)
+        
+        # Initialize the call stack viewer
+        self.debugViewer.initCallStackViewer(False)
+        
+    def __continue(self, debuggerId=""):
+        """
+        Private method to handle the Continue action.
+        
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        if not debuggerId:
+            debuggerId = self.getSelectedDebuggerId()
+        
+        self.lastAction = 0
+        self.__enterRemote()
+        self.debugServer.remoteContinue(debuggerId)
+
+    def __specialContinue(self, debuggerId=""):
+        """
+        Private method to handle the Special Continue action.
+        
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        if not debuggerId:
+            debuggerId = self.getSelectedDebuggerId()
+        
+        self.lastAction = 2
+        self.__enterRemote()
+        self.debugServer.remoteContinue(debuggerId, 1)
+
+    def __step(self, debuggerId=""):
+        """
+        Private method to handle the Step action.
+        
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        if not debuggerId:
+            debuggerId = self.getSelectedDebuggerId()
+        
+        self.lastAction = 1
+        self.__enterRemote()
+        self.debugServer.remoteStep(debuggerId)
+
+    def __stepOver(self, debuggerId=""):
+        """
+        Private method to handle the Step Over action.
+        
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        if not debuggerId:
+            debuggerId = self.getSelectedDebuggerId()
+        
+        self.lastAction = 2
+        self.__enterRemote()
+        self.debugServer.remoteStepOver(debuggerId)
+
+    def __stepOut(self, debuggerId=""):
+        """
+        Private method to handle the Step Out action.
+        
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        if not debuggerId:
+            debuggerId = self.getSelectedDebuggerId()
+        
+        self.lastAction = 3
+        self.__enterRemote()
+        self.debugServer.remoteStepOut(debuggerId)
+
+    def __stepQuit(self, debuggerId=""):
+        """
+        Private method to handle the Step Quit action.
+        
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        if not debuggerId:
+            debuggerId = self.getSelectedDebuggerId()
+        
+        self.lastAction = 4
+        self.__enterRemote()
+        self.debugServer.remoteStepQuit(debuggerId)
+        self.__resetUI()
+
+    def __runToCursor(self, debuggerId=""):
+        """
+        Private method to handle the Run to Cursor action.
+        
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        if not debuggerId:
+            debuggerId = self.getSelectedDebuggerId()
+        
+        self.lastAction = 0
+        aw = self.viewmanager.activeWindow()
+        line = aw.getCursorPosition()[0] + 1
+        self.__enterRemote()
+        self.debugServer.remoteBreakpoint(
+            self.getSelectedDebuggerId(),
+            aw.getFileName(), line, 1, None, 1)
+        self.debugServer.remoteContinue(debuggerId)
+
+    def __runUntil(self, debuggerId=""):
+        """
+        Private method to handle the Run Until action.
+        
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        if not debuggerId:
+            debuggerId = self.getSelectedDebuggerId()
+        
+        self.lastAction = 0
+        aw = self.viewmanager.activeWindow()
+        line = aw.getCursorPosition()[0] + 1
+        self.__enterRemote()
+        self.debugServer.remoteContinueUntil(debuggerId, line)
+
+    def __moveInstructionPointer(self, debuggerId=""):
+        """
+        Private method to move the instruction pointer to a different line.
+        
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        if not debuggerId:
+            debuggerId = self.getSelectedDebuggerId()
+        
+        self.lastAction = 0
+        aw = self.viewmanager.activeWindow()
+        line = aw.getCursorPosition()[0] + 1
+        self.debugServer.remoteMoveIP(debuggerId, line)
+
+    def __enterRemote(self):
+        """
+        Private method to update the user interface.
+
+        This method is called just prior to executing some of
+        the program being debugged.
+        """
+        # Disable further debug commands from the user.
+        self.debugActGrp.setEnabled(False)
+        
+        self.viewmanager.unhighlight(True)
+
+    def getActions(self):
+        """
+        Public method to get a list of all actions.
+        
+        @return list of all actions (list of E5Action)
+        """
+        return self.actions[:]
+    
+    def getSelectedDebuggerId(self):
+        """
+        Public method to get the currently selected debugger ID.
+        
+        @return selected debugger ID
+        @rtype str
+        """
+        return self.debugViewer.getSelectedDebuggerId()
+    
+    def setDebugActionsEnabled(self, enable):
+        """
+        Public method to set the enabled state of the debug actions.
+        
+        @param enable enable state to be set
+        @type bool
+        """
+        self.debugActGrp.setEnabled(enable)
+
+    def setMultiprocessNoDebugHistory(self, noDebugList, clearHistories=False,
+                                      history=None):
+        """
+        Public slot to initialize the no debug list history.
+        
+        @param noDebugList whitespace separated list of programs not to be
+            debugged
+        @type str
+        @param clearHistories flag indicating, that the list should be cleared
+        @type bool
+        @param history list of history entries to be set
+        @type list of str
+        """
+        if clearHistories:
+            del self.multiprocessNoDebugHistory[1:]
+        elif history is not None:
+            self.multiprocessNoDebugHistory = history[:]
+        else:
+            if noDebugList in self.multiprocessNoDebugHistory:
+                self.multiprocessNoDebugHistory.remove(noDebugList)
+            self.multiprocessNoDebugHistory.insert(0, noDebugList)
+    
+    def setEnableMultiprocess(self, enableMultiprocess):
+        """
+        Public slot to initialize the enableMultiprocess flag.
+        
+        @param enableMultiprocess flag indicating, that the debugger should be
+            run in multi process mode
+        @type bool
+        """
+        self.enableMultiprocess = enableMultiprocess
+    
+    def setEnableGlobalConfigOverride(self, overrideData):
+        """
+        Public method to initialize the global config override data.
+        
+        @param overrideData dictionary containing a flag indicating to enable
+            global config override and a flag indicating to redirect
+            stdin/stdout/stderr
+        @type dict
+        """
+        self.overrideGlobalConfig = copy.deepcopy(overrideData)

eric ide

mercurial