--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Debugger/DebugUI.py Mon Dec 28 16:03:33 2009 +0000 @@ -0,0 +1,1992 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2002 - 2009 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing the debugger UI. +""" + +import os + +from PyQt4.QtCore import * +from PyQt4.QtGui import * + +from UI.Info import * +from VariablesFilterDialog import * +from ExceptionsFilterDialog import * +from StartDialog import * +from EditBreakpointDialog import EditBreakpointDialog + +from DebugClientCapabilities import * +import Preferences +import Utilities +import UI.PixmapCache +import UI.Config + +from E4Gui.E4Action import E4Action, createActionGroup + +from eric4config import getConfig + +class DebugUI(QObject): + """ + Class implementing the debugger part of the UI. + + @signal clientStack(stack) 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 debuggingStarted(filename) emitted when a debugging session was started + @signal resetUI() emitted to reset the UI + @signal exceptionInterrupt() emitted after the execution was interrupted by an + exception and acknowledged by the user + """ + 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 + """ + QObject.__init__(self, 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 + + # Generate the variables filter dialog + self.dbgFilterDialog = VariablesFilterDialog(self.ui, 'Filter Dialog', True) + + # read the saved debug info values + self.argvHistory = \ + Preferences.Prefs.settings \ + .value('DebugInfo/ArgumentsHistory').toStringList() + self.wdHistory = \ + Preferences.Prefs.settings \ + .value('DebugInfo/WorkingDirectoryHistory').toStringList() + self.envHistory = \ + Preferences.Prefs.settings \ + .value('DebugInfo/EnvironmentHistory').toStringList() + self.excList = \ + Preferences.Prefs.settings \ + .value('DebugInfo/Exceptions').toStringList() + self.excIgnoreList = \ + Preferences.Prefs.settings \ + .value('DebugInfo/IgnoredExceptions').toStringList() + self.exceptions = \ + Preferences.Prefs.settings.value('DebugInfo/ReportExceptions', + QVariant(True)).toBool() + self.autoClearShell = Preferences.Prefs.settings.value('DebugInfo/AutoClearShell', + QVariant(True)).toBool() + self.tracePython = Preferences.Prefs.settings.value('DebugInfo/TracePython', + QVariant(False)).toBool() + self.autoContinue = Preferences.Prefs.settings.value('DebugInfo/AutoContinue', + QVariant(True)).toBool() + self.forkAutomatically = Preferences.Prefs.settings.value( + 'DebugInfo/ForkAutomatically', QVariant(False)).toBool() + self.forkIntoChild = Preferences.Prefs.settings.value('DebugInfo/ForkIntoChild', + QVariant(False)).toBool() + + self.evalHistory = [] + self.execHistory = [] + self.lastDebuggedFile = None + self.lastStartAction = 0 # 0=None, 1=Script, 2=Project + self.lastAction = -1 + self.debugActions = [self.__continue, self.__step,\ + self.__stepOver, self.__stepOut,\ + self.__stepQuit, self.__runToCursor] + self.localsVarFilter, self.globalsVarFilter = Preferences.getVarFilters() + self.debugViewer.setVariablesFilter(self.globalsVarFilter, self.localsVarFilter) + + # Connect the signals emitted by the debug-server + self.connect(debugServer, SIGNAL('clientGone'), self.__clientGone) + self.connect(debugServer, SIGNAL('clientLine'), self.__clientLine) + self.connect(debugServer, SIGNAL('clientExit(int)'), self.__clientExit) + self.connect(debugServer, SIGNAL('clientSyntaxError'), self.__clientSyntaxError) + self.connect(debugServer, SIGNAL('clientException'), self.__clientException) + self.connect(debugServer, SIGNAL('clientVariables'), self.__clientVariables) + self.connect(debugServer, SIGNAL('clientVariable'), self.__clientVariable) + self.connect(debugServer, SIGNAL('clientBreakConditionError'), + self.__clientBreakConditionError) + self.connect(debugServer, SIGNAL('clientWatchConditionError'), + self.__clientWatchConditionError) + self.connect(debugServer, SIGNAL('passiveDebugStarted'), + self.__passiveDebugStarted) + self.connect(debugServer, SIGNAL('clientThreadSet'), self.__clientThreadSet) + + self.connect(debugServer, SIGNAL('clientRawInput'), debugViewer.handleRawInput) + self.connect(debugServer, SIGNAL('clientRawInputSent'), + debugViewer.restoreCurrentPage) + self.connect(debugServer, SIGNAL('clientThreadList'), debugViewer.showThreadList) + + # Connect the signals emitted by the viewmanager + self.connect(vm, SIGNAL('editorOpened'), self.__editorOpened) + self.connect(vm, SIGNAL('lastEditorClosed'), self.__lastEditorClosed) + self.connect(vm, SIGNAL('checkActions'), self.__checkActions) + self.connect(vm, SIGNAL('cursorChanged'), self.__cursorChanged) + self.connect(vm, SIGNAL('breakpointToggled'), self.__cursorChanged) + + # Connect the signals emitted by the project + self.connect(project, SIGNAL('projectOpened'), self.__projectOpened) + self.connect(project, SIGNAL('newProject'), self.__projectOpened) + self.connect(project, SIGNAL('projectClosed'), self.__projectClosed) + self.connect(project, SIGNAL('projectSessionLoaded'), + self.__projectSessionLoaded) + + # Set a flag for the passive debug mode + self.passive = Preferences.getDebugger("PassiveDbgEnabled") + + 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 (list of integers) + """ + if scope: + return self.globalsVarFilter[:] + else: + return self.localsVarFilter[:] + + def initActions(self): + """ + Method defining the user interface actions. + """ + self.actions = [] + + self.runAct = E4Action(self.trUtf8('Run Script'), + UI.PixmapCache.getIcon("runScript.png"), + self.trUtf8('&Run Script...'),Qt.Key_F2,0,self,'dbg_run_script') + self.runAct.setStatusTip(self.trUtf8('Run the current Script')) + self.runAct.setWhatsThis(self.trUtf8( + """<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.connect(self.runAct, SIGNAL('triggered()'), self.__runScript) + self.actions.append(self.runAct) + + self.runProjectAct = E4Action(self.trUtf8('Run Project'), + UI.PixmapCache.getIcon("runProject.png"), + self.trUtf8('Run &Project...'),Qt.SHIFT + Qt.Key_F2,0,self, + 'dbg_run_project') + self.runProjectAct.setStatusTip(self.trUtf8('Run the current Project')) + self.runProjectAct.setWhatsThis(self.trUtf8( + """<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.connect(self.runProjectAct, SIGNAL('triggered()'), self.__runProject) + self.actions.append(self.runProjectAct) + + self.coverageAct = E4Action(self.trUtf8('Coverage run of Script'), + UI.PixmapCache.getIcon("coverageScript.png"), + self.trUtf8('Coverage run of Script...'),0,0,self,'dbg_coverage_script') + self.coverageAct.setStatusTip(\ + self.trUtf8('Perform a coverage run of the current Script')) + self.coverageAct.setWhatsThis(self.trUtf8( + """<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.connect(self.coverageAct, SIGNAL('triggered()'), self.__coverageScript) + self.actions.append(self.coverageAct) + + self.coverageProjectAct = E4Action(self.trUtf8('Coverage run of Project'), + UI.PixmapCache.getIcon("coverageProject.png"), + self.trUtf8('Coverage run of Project...'),0,0,self,'dbg_coverage_project') + self.coverageProjectAct.setStatusTip(\ + self.trUtf8('Perform a coverage run of the current Project')) + self.coverageProjectAct.setWhatsThis(self.trUtf8( + """<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.connect(self.coverageProjectAct, SIGNAL('triggered()'), self.__coverageProject) + self.actions.append(self.coverageProjectAct) + + self.profileAct = E4Action(self.trUtf8('Profile Script'), + UI.PixmapCache.getIcon("profileScript.png"), + self.trUtf8('Profile Script...'),0,0,self,'dbg_profile_script') + self.profileAct.setStatusTip(self.trUtf8('Profile the current Script')) + self.profileAct.setWhatsThis(self.trUtf8( + """<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.connect(self.profileAct, SIGNAL('triggered()'), self.__profileScript) + self.actions.append(self.profileAct) + + self.profileProjectAct = E4Action(self.trUtf8('Profile Project'), + UI.PixmapCache.getIcon("profileProject.png"), + self.trUtf8('Profile Project...'),0,0,self,'dbg_profile_project') + self.profileProjectAct.setStatusTip(self.trUtf8('Profile the current Project')) + self.profileProjectAct.setWhatsThis(self.trUtf8( + """<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.connect(self.profileProjectAct, SIGNAL('triggered()'), self.__profileProject) + self.actions.append(self.profileProjectAct) + + self.debugAct = E4Action(self.trUtf8('Debug Script'), + UI.PixmapCache.getIcon("debugScript.png"), + self.trUtf8('&Debug Script...'),Qt.Key_F5,0,self,'dbg_debug_script') + self.debugAct.setStatusTip(self.trUtf8('Debug the current Script')) + self.debugAct.setWhatsThis(self.trUtf8( + """<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.connect(self.debugAct, SIGNAL('triggered()'), self.__debugScript) + self.actions.append(self.debugAct) + + self.debugProjectAct = E4Action(self.trUtf8('Debug Project'), + UI.PixmapCache.getIcon("debugProject.png"), + self.trUtf8('Debug &Project...'),Qt.SHIFT + Qt.Key_F5,0,self, + 'dbg_debug_project') + self.debugProjectAct.setStatusTip(self.trUtf8('Debug the current Project')) + self.debugProjectAct.setWhatsThis(self.trUtf8( + """<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.connect(self.debugProjectAct, SIGNAL('triggered()'), self.__debugProject) + self.actions.append(self.debugProjectAct) + + self.restartAct = E4Action(self.trUtf8('Restart Script'), + UI.PixmapCache.getIcon("restart.png"), + self.trUtf8('Restart Script'),Qt.Key_F4,0,self,'dbg_restart_script') + self.restartAct.setStatusTip(self.trUtf8('Restart the last debugged script')) + self.restartAct.setWhatsThis(self.trUtf8( + """<b>Restart Script</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.connect(self.restartAct, SIGNAL('triggered()'), self.__doRestart) + self.actions.append(self.restartAct) + + self.stopAct = E4Action(self.trUtf8('Stop Script'), + UI.PixmapCache.getIcon("stopScript.png"), + self.trUtf8('Stop Script'),Qt.SHIFT + Qt.Key_F10,0, + self,'dbg_stop_script') + self.stopAct.setStatusTip(self.trUtf8("""Stop the running script.""")) + self.stopAct.setWhatsThis(self.trUtf8( + """<b>Stop Script</b>""" + """<p>This stops the script running in the debugger backend.</p>""" + )) + self.connect(self.stopAct, SIGNAL('triggered()'), self.__stopScript) + self.actions.append(self.stopAct) + + self.debugActGrp = createActionGroup(self) + + act = E4Action(self.trUtf8('Continue'), + UI.PixmapCache.getIcon("continue.png"), + self.trUtf8('&Continue'),Qt.Key_F6,0, + self.debugActGrp,'dbg_continue') + act.setStatusTip(\ + self.trUtf8('Continue running the program from the current line')) + act.setWhatsThis(self.trUtf8( + """<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>""" + )) + self.connect(act, SIGNAL('triggered()'), self.__continue) + self.actions.append(act) + + act = E4Action(self.trUtf8('Continue to Cursor'), + UI.PixmapCache.getIcon("continueToCursor.png"), + self.trUtf8('Continue &To Cursor'),Qt.SHIFT + Qt.Key_F6,0, + self.debugActGrp,'dbg_continue_to_cursor') + act.setStatusTip(self.trUtf8("""Continue running the program from the""" + """ current line to the current cursor position""")) + act.setWhatsThis(self.trUtf8( + """<b>Continue To Cursor</b>""" + """<p>Continue running the program from the current line to the""" + """ current cursor position.</p>""" + )) + self.connect(act, SIGNAL('triggered()'), self.__runToCursor) + self.actions.append(act) + + act = E4Action(self.trUtf8('Single Step'), + UI.PixmapCache.getIcon("step.png"), + self.trUtf8('Sin&gle Step'),Qt.Key_F7,0, + self.debugActGrp,'dbg_single_step') + act.setStatusTip(self.trUtf8('Execute a single Python statement')) + act.setWhatsThis(self.trUtf8( + """<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>""" + )) + self.connect(act, SIGNAL('triggered()'), self.__step) + self.actions.append(act) + + act = E4Action(self.trUtf8('Step Over'), + UI.PixmapCache.getIcon("stepOver.png"), + self.trUtf8('Step &Over'),Qt.Key_F8,0, + self.debugActGrp,'dbg_step_over') + act.setStatusTip(self.trUtf8("""Execute a single Python statement staying""" + """ in the current frame""")) + act.setWhatsThis(self.trUtf8( + """<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>""" + )) + self.connect(act, SIGNAL('triggered()'), self.__stepOver) + self.actions.append(act) + + act = E4Action(self.trUtf8('Step Out'), + UI.PixmapCache.getIcon("stepOut.png"), + self.trUtf8('Step Ou&t'),Qt.Key_F9,0, + self.debugActGrp,'dbg_step_out') + act.setStatusTip(self.trUtf8("""Execute Python statements until leaving""" + """ the current frame""")) + act.setWhatsThis(self.trUtf8( + """<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>""" + )) + self.connect(act, SIGNAL('triggered()'), self.__stepOut) + self.actions.append(act) + + act = E4Action(self.trUtf8('Stop'), + UI.PixmapCache.getIcon("stepQuit.png"), + self.trUtf8('&Stop'),Qt.Key_F10,0, + self.debugActGrp,'dbg_stop') + act.setStatusTip(self.trUtf8('Stop debugging')) + act.setWhatsThis(self.trUtf8( + """<b>Stop</b>""" + """<p>Stop the running debugging session.</p>""" + )) + self.connect(act, SIGNAL('triggered()'), self.__stepQuit) + self.actions.append(act) + + self.debugActGrp2 = createActionGroup(self) + + act = E4Action(self.trUtf8('Evaluate'), + self.trUtf8('E&valuate...'), + 0,0,self.debugActGrp2,'dbg_evaluate') + act.setStatusTip(self.trUtf8('Evaluate in current context')) + act.setWhatsThis(self.trUtf8( + """<b>Evaluate</b>""" + """<p>Evaluate an expression in the current context of the""" + """ debugged program. The result is displayed in the""" + """ shell window.</p>""" + )) + self.connect(act, SIGNAL('triggered()'), self.__eval) + self.actions.append(act) + + act = E4Action(self.trUtf8('Execute'), + self.trUtf8('E&xecute...'), + 0,0,self.debugActGrp2,'dbg_execute') + act.setStatusTip(\ + self.trUtf8('Execute a one line statement in the current context')) + act.setWhatsThis(self.trUtf8( + """<b>Execute</b>""" + """<p>Execute a one line statement in the current context""" + """ of the debugged program.</p>""" + )) + self.connect(act, SIGNAL('triggered()'), self.__exec) + self.actions.append(act) + + self.dbgFilterAct = E4Action(self.trUtf8('Variables Type Filter'), + self.trUtf8('Varia&bles Type Filter...'), 0, 0, self, + 'dbg_variables_filter') + self.dbgFilterAct.setStatusTip(self.trUtf8('Configure variables type filter')) + self.dbgFilterAct.setWhatsThis(self.trUtf8( + """<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.connect(self.dbgFilterAct, SIGNAL('triggered()'), + self.__configureVariablesFilters) + self.actions.append(self.dbgFilterAct) + + self.excFilterAct = E4Action(self.trUtf8('Exceptions Filter'), + self.trUtf8('&Exceptions Filter...'), 0, 0, self, 'dbg_exceptions_filter') + self.excFilterAct.setStatusTip(self.trUtf8('Configure exceptions filter')) + self.excFilterAct.setWhatsThis(self.trUtf8( + """<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.connect(self.excFilterAct, SIGNAL('triggered()'), + self.__configureExceptionsFilter) + self.actions.append(self.excFilterAct) + + self.excIgnoreFilterAct = E4Action(self.trUtf8('Ignored Exceptions'), + self.trUtf8('&Ignored Exceptions...'), 0, 0, + self, 'dbg_ignored_exceptions') + self.excIgnoreFilterAct.setStatusTip(self.trUtf8('Configure ignored exceptions')) + self.excIgnoreFilterAct.setWhatsThis(self.trUtf8( + """<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.connect(self.excIgnoreFilterAct, SIGNAL('triggered()'), + self.__configureIgnoredExceptions) + self.actions.append(self.excIgnoreFilterAct) + + self.dbgSetBpActGrp = createActionGroup(self) + + self.dbgToggleBpAct = E4Action(self.trUtf8('Toggle Breakpoint'), + UI.PixmapCache.getIcon("breakpointToggle.png"), + self.trUtf8('Toggle Breakpoint'), + QKeySequence(self.trUtf8("Shift+F11","Debug|Toggle Breakpoint")), 0, + self.dbgSetBpActGrp, 'dbg_toggle_breakpoint') + self.dbgToggleBpAct.setStatusTip(self.trUtf8('Toggle Breakpoint')) + self.dbgToggleBpAct.setWhatsThis(self.trUtf8( + """<b>Toggle Breakpoint</b>""" + """<p>Toggles a breakpoint at the current line of the""" + """ current editor.</p>""" + )) + self.connect(self.dbgToggleBpAct, SIGNAL('triggered()'), self.__toggleBreakpoint) + self.actions.append(self.dbgToggleBpAct) + + self.dbgEditBpAct = E4Action(self.trUtf8('Edit Breakpoint'), + UI.PixmapCache.getIcon("cBreakpointToggle.png"), + self.trUtf8('Edit Breakpoint...'), + QKeySequence(self.trUtf8("Shift+F12","Debug|Edit Breakpoint")), 0, + self.dbgSetBpActGrp, 'dbg_edit_breakpoint') + self.dbgEditBpAct.setStatusTip(self.trUtf8('Edit Breakpoint')) + self.dbgEditBpAct.setWhatsThis(self.trUtf8( + """<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.connect(self.dbgEditBpAct, SIGNAL('triggered()'), self.__editBreakpoint) + self.actions.append(self.dbgEditBpAct) + + self.dbgNextBpAct = E4Action(self.trUtf8('Next Breakpoint'), + UI.PixmapCache.getIcon("breakpointNext.png"), + self.trUtf8('Next Breakpoint'), + QKeySequence(self.trUtf8("Ctrl+Shift+PgDown","Debug|Next Breakpoint")), 0, + self.dbgSetBpActGrp, 'dbg_next_breakpoint') + self.dbgNextBpAct.setStatusTip(self.trUtf8('Next Breakpoint')) + self.dbgNextBpAct.setWhatsThis(self.trUtf8( + """<b>Next Breakpoint</b>""" + """<p>Go to next breakpoint of the current editor.</p>""" + )) + self.connect(self.dbgNextBpAct, SIGNAL('triggered()'), self.__nextBreakpoint) + self.actions.append(self.dbgNextBpAct) + + self.dbgPrevBpAct = E4Action(self.trUtf8('Previous Breakpoint'), + UI.PixmapCache.getIcon("breakpointPrevious.png"), + self.trUtf8('Previous Breakpoint'), + QKeySequence(self.trUtf8("Ctrl+Shift+PgUp","Debug|Previous Breakpoint")), + 0, self.dbgSetBpActGrp, 'dbg_previous_breakpoint') + self.dbgPrevBpAct.setStatusTip(self.trUtf8('Previous Breakpoint')) + self.dbgPrevBpAct.setWhatsThis(self.trUtf8( + """<b>Previous Breakpoint</b>""" + """<p>Go to previous breakpoint of the current editor.</p>""" + )) + self.connect(self.dbgPrevBpAct, SIGNAL('triggered()'), self.__previousBreakpoint) + self.actions.append(self.dbgPrevBpAct) + + act = E4Action(self.trUtf8('Clear Breakpoints'), + self.trUtf8('Clear Breakpoints'), + QKeySequence(self.trUtf8("Ctrl+Shift+C","Debug|Clear Breakpoints")), 0, + self.dbgSetBpActGrp, 'dbg_clear_breakpoint') + act.setStatusTip(self.trUtf8('Clear Breakpoints')) + act.setWhatsThis(self.trUtf8( + """<b>Clear Breakpoints</b>""" + """<p>Clear breakpoints of all editors.</p>""" + )) + self.connect(act, SIGNAL('triggered()'), self.__clearBreakpoints) + self.actions.append(act) + + self.debugActGrp.setEnabled(False) + self.debugActGrp2.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.trUtf8('&Debug'), self.parent()) + dmenu.setTearOffEnabled(True) + smenu = QMenu(self.trUtf8('&Start'), self.parent()) + smenu.setTearOffEnabled(True) + self.breakpointsMenu = QMenu(self.trUtf8('&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.debugActGrp2.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.connect(self.breakpointsMenu, SIGNAL('aboutToShow()'), + self.__showBreakpointsMenu) + self.connect(self.breakpointsMenu, SIGNAL('triggered(QAction *)'), + self.__breakpointSelected) + self.connect(dmenu, SIGNAL('aboutToShow()'), self.__showDebugMenu) + + return smenu, dmenu + + def initToolbars(self, toolbarManager): + """ + Public slot to initialize the debug toolbars. + + @param toolbarManager reference to a toolbar manager object (E4ToolBarManager) + @return the generated toolbars (list of QToolBar) + """ + starttb = QToolBar(self.trUtf8("Start"), self.parent()) + starttb.setIconSize(UI.Config.ToolBarIconSize) + starttb.setObjectName("StartToolbar") + starttb.setToolTip(self.trUtf8('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.trUtf8("Debug"), self.parent()) + debugtb.setIconSize(UI.Config.ToolBarIconSize) + debugtb.setObjectName("DebugToolbar") + debugtb.setToolTip(self.trUtf8('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): + """ + Public slot to initialize the argv history. + + @param argsStr the commandline arguments (string) + @param clearHistories flag indicating, that the list should + be cleared (boolean) + """ + if clearHistories: + self.argvHistory = [] + else: + if argsStr in self.argvHistory: + self.argvHistory.remove(argsStr) + self.argvHistory.insert(0, argsStr) + + def setWdHistory(self, wdStr, clearHistories = False): + """ + Public slot to initialize the wd history. + + @param wdStr the working directory (string) + @param clearHistories flag indicating, that the list should + be cleared (boolean) + """ + if clearHistories: + self.wdHistory = [] + else: + if wdStr in self.wdHistory: + self.wdHistory.remove(wdStr) + self.wdHistory.insert(0, wdStr) + + def setEnvHistory(self, envStr, clearHistories = False): + """ + Public slot to initialize the env history. + + @param envStr the environment settings (string) + @param clearHistories flag indicating, that the list should + be cleared (boolean) + """ + if clearHistories: + self.envHistory = [] + 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 + + if fn: + editor = self.viewmanager.getOpenEditor(fn) + else: + editor = 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.debugActGrp2.setEnabled(False) + self.dbgSetBpActGrp.setEnabled(False) + self.lastAction = -1 + if not self.projectOpen: + self.restartAct.setEnabled(False) + self.lastDebuggedFile = None + self.lastStartAction = 0 + + def __checkActions(self, editor): + """ + Private slot to check some actions for their enable/disable status. + + @param editor editor window + """ + if editor: + fn = editor.getFileName() + else: + fn = 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.isPyFile(): + cap = self.debugServer.getClientCapabilities('Python') + elif 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.isPy3File() 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.pdata["PROGLANGUAGE"][0]) + 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 + + def __projectSessionLoaded(self): + """ + Private slot to handle the projectSessionLoaded signal. + """ + fn = self.project.getMainScript(True) + if fn is not None: + self.lastStartAction = 2 + self.lastDebuggedFile = fn + self.restartAct.setEnabled(True) + + 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/ArgumentsHistory', + QVariant(self.argvHistory)) + Preferences.Prefs.settings.setValue('DebugInfo/WorkingDirectoryHistory', + QVariant(self.wdHistory)) + Preferences.Prefs.settings.setValue('DebugInfo/EnvironmentHistory', + QVariant(self.envHistory)) + Preferences.Prefs.settings.setValue('DebugInfo/Exceptions', + QVariant(self.excList)) + Preferences.Prefs.settings.setValue('DebugInfo/IgnoredExceptions', + QVariant(self.excIgnoreList)) + Preferences.Prefs.settings.setValue('DebugInfo/ReportExceptions', + QVariant(self.exceptions)) + Preferences.Prefs.settings.setValue('DebugInfo/AutoClearShell', + QVariant(self.autoClearShell)) + Preferences.Prefs.settings.setValue('DebugInfo/TracePython', + QVariant(self.tracePython)) + Preferences.Prefs.settings.setValue('DebugInfo/AutoContinue', + QVariant(self.autoContinue)) + Preferences.Prefs.settings.setValue('DebugInfo/ForkAutomatically', + QVariant(self.forkAutomatically)) + Preferences.Prefs.settings.setValue('DebugInfo/ForkIntoChild', + QVariant(self.forkIntoChild)) + + 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): + """ + Private slot to reset the user interface. + """ + self.lastAction = -1 + self.debugActGrp.setEnabled(False) + self.debugActGrp2.setEnabled(False) + 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.emit(SIGNAL('resetUI')) + + def __clientLine(self, fn, line, forStack): + """ + Private method to handle a change to the current line. + + @param fn filename (string) + @param line linenumber (int) + @param forStack flag indicating this is for a stack dump (boolean) + """ + self.ui.raise_() + self.ui.activateWindow() + if self.ui.getViewProfile() != "debug": + self.ui.setDebugProfile() + self.viewmanager.setFileLine(fn, line) + if not forStack: + self.__getThreadList() + self.__getClientVariables() + + def __clientExit(self, status): + """ + Private method to handle the debugged program terminating. + + @param status exit code of the debugged program (int) + """ + self.viewmanager.exit() + + self.__resetUI() + + if not Preferences.getDebugger("SuppressClientExit") or status != 0: + if self.ui.currentProg is None: + QMessageBox.information(self.ui,Program, + self.trUtf8('<p>The program has terminated with an exit' + ' status of {0}.</p>').format(status)) + else: + QMessageBox.information(self.ui,Program, + self.trUtf8('<p><b>{0}</b> has terminated with an exit' + ' status of {1}.</p>') + .format(Utilities.normabspath(self.ui.currentProg), status)) + + 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: + QMessageBox.critical(self.ui,Program, + self.trUtf8('The program being debugged contains an unspecified' + ' syntax error.')) + return + + self.viewmanager.setFileLine(filename, lineNo, True, True) + QMessageBox.critical(self.ui,Program, + self.trUtf8('<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): + """ + Private method to handle an exception of the debugged program. + + @param exceptionType type of exception raised (string) + @param exceptionMessage message given by the exception (string) + @param stackTrace list of stack entries. + """ + self.ui.raise_() + self.ui.activateWindow() + QApplication.processEvents() + if exceptionType is None: + QMessageBox.critical(self.ui,Program, + self.trUtf8('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'): + if stackTrace: + self.viewmanager.setFileLine(stackTrace[0][0], stackTrace[0][1], True) + if Preferences.getDebugger("BreakAlways"): + res = QMessageBox.Yes + else: + if stackTrace: + if exceptionType.startswith('unhandled'): + buttons = QMessageBox.StandardButtons(\ + QMessageBox.No | \ + QMessageBox.Yes) + else: + buttons = QMessageBox.StandardButtons(\ + QMessageBox.No | \ + QMessageBox.Yes | \ + QMessageBox.Ignore) + res = QMessageBox.critical(self.ui, Program, + self.trUtf8('<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, + QMessageBox.No) + else: + res = QMessageBox.critical(self.ui, Program, + self.trUtf8('<p>The debugged program raised the exception' + ' <b>{0}</b><br>"<b>{1}</b>"</p>') + .format(exceptionType, + Utilities.html_encode(exceptionMessage))) + if res == QMessageBox.Yes: + self.emit(SIGNAL('exceptionInterrupt')) + stack = [] + for fn, ln in stackTrace: + stack.append((fn, ln, '')) + self.emit(SIGNAL('clientStack'), stack) + self.__getClientVariables() + self.ui.setDebugProfile() + return + elif res == QMessageBox.Ignore: + if exceptionType not in self.excIgnoreList: + self.excIgnoreList.append(exceptionType) + + if self.lastAction != -1: + if self.lastAction == 2: + self.__specialContinue() + else: + self.debugActions[self.lastAction]() + else: + self.__continue() + + def __clientGone(self,unplanned): + """ + Private method to handle the disconnection of the debugger client. + + @param unplanned 1 if the client died, 0 otherwise + """ + self.__resetUI() + if unplanned: + QMessageBox.information(self.ui,Program, + self.trUtf8('The program being debugged has terminated unexpectedly.')) + + def __getThreadList(self): + """ + Private method to get the list of threads from the client. + """ + self.debugServer.remoteThreadList() + + def __clientThreadSet(self): + """ + Private method to handle a change of the client's current thread. + """ + self.debugServer.remoteClientVariables(0, self.localsVarFilter) + + def __getClientVariables(self): + """ + 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'. + """ + # get globals first + self.debugServer.remoteClientVariables(1, self.globalsVarFilter) + # the local variables are requested once we have received the globals + + def __clientVariables(self, scope, variables): + """ + Private method to write the clients variables to the user interface. + + @param scope scope of the variables (-1 = empty global, 1 = global, 0 = local) + @param variables the list of variables from the client + """ + if scope > 0: + self.debugViewer.showVariables(variables, True) + if scope == 1: + # now get the local variables + self.debugServer.remoteClientVariables(0, self.localsVarFilter) + elif scope == 0: + self.debugViewer.showVariables(variables, False) + elif scope == -1: + vlist = [('None','','')] + self.debugViewer.showVariables(vlist, False) + + if scope < 1: + self.debugActGrp.setEnabled(True) + self.debugActGrp2.setEnabled(True) + + def __clientVariable(self, scope, variables): + """ + Private method to write the contents of a clients classvariable to the user + interface. + + @param scope scope of the variables (-1 = empty global, 1 = global, 0 = local) + @param variables the list of members of a classvariable from the client + """ + if scope == 1: + self.debugViewer.showVariable(variables, 1) + elif scope == 0: + self.debugViewer.showVariable(variables, 0) + + def __clientBreakConditionError(self, filename, lineno): + """ + Private method to handle a condition error of a breakpoint. + + @param filename filename of the breakpoint (string) + @param lineno linenumber of the breakpoint (integer) + """ + QMessageBox.critical(None, + self.trUtf8("Breakpoint Condition Error"), + self.trUtf8("""<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] + + dlg = EditBreakpointDialog((fn, line), (cond, temp, enabled, count), + [], self.ui, modal = True) + if dlg.exec_() == QDialog.Accepted: + cond, temp, enabled, count = dlg.getData() + model.setBreakPointByIndex(index, fn, line, (cond, temp, enabled, count)) + + def __clientWatchConditionError(self, cond): + """ + Public 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 (string) + """ + QMessageBox.critical(None, + self.trUtf8("Watch Expression Error"), + self.trUtf8("""<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] + + dlg = EditWatchpointDialog(\ + (cond, temp, enabled, count, special), self) + if dlg.exec_() == QDialog.Accepted: + cond, temp, enabled, count, special = dlg.getData() + + # check for duplicates + idx = self.__model.getWatchPointIndex(cond, special) + duplicate = idx.isValid() and idx.internalPointer() != index.internalPointer() + if duplicate: + if not special: + msg = self.trUtf8("""<p>A watch expression '<b>{0}</b>'""" + """ already exists.</p>""")\ + .format(Utilities.html_encode(cond)) + else: + msg = self.trUtf8("""<p>A watch expression '<b>{0}</b>'""" + """ for the variable <b>{1}</b> already exists.</p>""")\ + .format(special, + Utilities.html_encode(cond)) + QMessageBox.warning(None, + self.trUtf8("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. + """ + result = self.dbgFilterDialog.exec_() + if result == QDialog.Accepted: + self.localsVarFilter, self.globalsVarFilter = \ + self.dbgFilterDialog.getSelection() + else: + self.dbgFilterDialog.setSelection( + self.localsVarFilter, self.globalsVarFilter) + self.debugViewer.setVariablesFilter(self.globalsVarFilter, self.localsVarFilter) + + def __configureExceptionsFilter(self): + """ + Private slot for displaying the exception filter dialog. + """ + dlg = ExceptionsFilterDialog(self.excList, ignore = False) + if dlg.exec_() == QDialog.Accepted: + self.excList = dlg.getExceptionsList()[:] # keep a copy + + def __configureIgnoredExceptions(self): + """ + Private slot for displaying the ignored exceptions dialog. + """ + dlg = ExceptionsFilterDialog(self.excIgnoreList, ignore = True) + if dlg.exec_() == QDialog.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] + if not cond: + formattedCond = "" + else: + formattedCond = " : %s" % cond[:20] + bpSuffix = " : %d%s" % (line, formattedCond) + act = self.breakpointsMenu.addAction( + "%s%s" % ( + Utilities.compactPath( + filename, + self.ui.maxMenuFilePathLen - len(bpSuffix)), + bpSuffix)) + act.setData(QVariant([QVariant(filename), QVariant(line)])) + + def __breakpointSelected(self, act): + """ + Private method to handle the breakpoint selected signal. + + @param act reference to the action that triggered (QAction) + """ + try: + qvList = act.data().toPyObject() + filename = qvList[0] + line = qvList[1] + except AttributeError: + qvList = act.data().toList() + filename = qvList[0].toString() + line = qvList[1].toInt()[0] + 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.emit(SIGNAL('compileForms')) + if Preferences.getProject("AutoCompileResources"): + self.emit(SIGNAL('compileResources')) + 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) + """ + self.__resetUI() + doNotStart = False + + # Get the command line arguments, the working directory and the + # exception reporting flag. + if runProject: + cap = self.trUtf8("Coverage of Project") + else: + cap = self.trUtf8("Coverage of Script") + dlg = StartDialog(cap, self.argvHistory, self.wdHistory, self.envHistory, + self.exceptions, self.ui, 2, autoClearShell = self.autoClearShell) + if dlg.exec_() == QDialog.Accepted: + argv, wd, env, exceptions, clearShell, clearHistories, console = dlg.getData() + eraseCoverage = dlg.getCoverageData() + + if runProject: + fn = self.project.getMainScript(1) + if fn is None: + QMessageBox.critical(self.ui, + self.trUtf8("Coverage of Project"), + self.trUtf8("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(argv, wd, env, exceptions, self.excList, + self.excIgnoreList, clearShell) + + self.lastStartAction = 6 + 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 + + # save the filename for use by the restart method + self.lastDebuggedFile = fn + self.restartAct.setEnabled(True) + + # This moves any previous occurrence of these arguments to the head + # of the list. + self.setArgvHistory(argv, clearHistories) + self.setWdHistory(wd, clearHistories) + self.setEnvHistory(env, clearHistories) + + # 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 + + # Hide all error highlights + self.viewmanager.unhighlight() + + if not doNotStart: + if runProject and self.project.getProjectType() == "E4Plugin": + argv.insert(0, "--plugin=%s " % fn) + fn = os.path.join(getConfig('ericDir'), "eric4.py") + + # Ask the client to open the new program. + self.debugServer.remoteCoverage(fn, argv, wd, env, + autoClearShell = self.autoClearShell, erase = eraseCoverage, + forProject = runProject, runInConsole = console) + + self.stopAct.setEnabled(True) + + 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) + """ + self.__resetUI() + doNotStart = False + + # Get the command line arguments, the working directory and the + # exception reporting flag. + if runProject: + cap = self.trUtf8("Profile of Project") + else: + cap = self.trUtf8("Profile of Script") + dlg = StartDialog(cap, self.argvHistory, self.wdHistory, self.envHistory, + self.exceptions, self.ui, 3, + autoClearShell = self.autoClearShell) + if dlg.exec_() == QDialog.Accepted: + argv, wd, env, exceptions, clearShell, clearHistories, console = dlg.getData() + eraseTimings = dlg.getProfilingData() + + if runProject: + fn = self.project.getMainScript(1) + if fn is None: + QMessageBox.critical(self.ui, + self.trUtf8("Profile of Project"), + self.trUtf8("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(argv, wd, env, exceptions, self.excList, + self.excIgnoreList, clearShell) + + self.lastStartAction = 8 + 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 + + # save the filename for use by the restart method + self.lastDebuggedFile = fn + self.restartAct.setEnabled(True) + + # This moves any previous occurrence of these arguments to the head + # of the list. + self.setArgvHistory(argv, clearHistories) + self.setWdHistory(wd, clearHistories) + self.setEnvHistory(env, clearHistories) + + # 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 + + # Hide all error highlights + self.viewmanager.unhighlight() + + if not doNotStart: + if runProject and self.project.getProjectType() == "E4Plugin": + argv.insert(0, "--plugin=%s " % fn) + fn = os.path.join(getConfig('ericDir'), "eric4.py") + + # Ask the client to open the new program. + self.debugServer.remoteProfile(fn, argv, wd, env, + autoClearShell = self.autoClearShell, erase = eraseTimings, + forProject = runProject, runInConsole = console) + + self.stopAct.setEnabled(True) + + 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) + """ + self.__resetUI() + doNotStart = False + + # Get the command line arguments, the working directory and the + # exception reporting flag. + if runProject: + cap = self.trUtf8("Run Project") + else: + cap = self.trUtf8("Run Script") + dlg = StartDialog(cap, self.argvHistory, self.wdHistory, self.envHistory, + self.exceptions, self.ui, 1, + autoClearShell = self.autoClearShell) + if dlg.exec_() == QDialog.Accepted: + argv, wd, env, exceptions, clearShell, clearHistories, console = dlg.getData() + + if runProject: + fn = self.project.getMainScript(1) + if fn is None: + QMessageBox.critical(self.ui, + self.trUtf8("Run Project"), + self.trUtf8("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(argv, wd, env, exceptions, self.excList, + self.excIgnoreList, clearShell) + + self.lastStartAction = 4 + 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 + + # save the filename for use by the restart method + self.lastDebuggedFile = fn + self.restartAct.setEnabled(True) + + # This moves any previous occurrence of these arguments to the head + # of the list. + self.setArgvHistory(argv, clearHistories) + self.setWdHistory(wd, clearHistories) + self.setEnvHistory(env, clearHistories) + + # Save the exception flags + self.exceptions = exceptions + + # Save the clear interpreter flag + self.autoClearShell = clearShell + + # Save the run in console flag + self.runInConsole = console + + # Hide all error highlights + self.viewmanager.unhighlight() + + if not doNotStart: + if runProject and self.project.getProjectType() == "E4Plugin": + argv.insert(0, "--plugin=%s " % fn) + fn = os.path.join(getConfig('ericDir'), "eric4.py") + + # Ask the client to open the new program. + self.debugServer.remoteRun(fn, argv, wd, env, + autoClearShell = self.autoClearShell, forProject = runProject, + runInConsole = console) + + self.stopAct.setEnabled(True) + + 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) + """ + self.__resetUI() + doNotStart = False + + # Get the command line arguments, the working directory and the + # exception reporting flag. + if debugProject: + cap = self.trUtf8("Debug Project") + else: + cap = self.trUtf8("Debug Script") + dlg = StartDialog(cap, self.argvHistory, self.wdHistory, self.envHistory, + self.exceptions, self.ui, 0, tracePython = self.tracePython, + autoClearShell = self.autoClearShell, autoContinue = self.autoContinue, + autoFork = self.forkAutomatically, forkChild = self.forkIntoChild) + if dlg.exec_() == QDialog.Accepted: + argv, wd, env, exceptions, clearShell, clearHistories, console = dlg.getData() + tracePython, autoContinue, forkAutomatically, forkIntoChild = \ + dlg.getDebugData() + + if debugProject: + fn = self.project.getMainScript(True) + if fn is None: + QMessageBox.critical(self.ui, + self.trUtf8("Debug Project"), + self.trUtf8("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(argv, wd, env, exceptions, self.excList, + self.excIgnoreList, clearShell, tracePython = tracePython, + autoContinue = self.autoContinue) + + self.lastStartAction = 2 + 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 + + # save the filename for use by the restart method + self.lastDebuggedFile = fn + self.restartAct.setEnabled(True) + + # This moves any previous occurrence of these arguments to the head + # of the list. + self.setArgvHistory(argv, clearHistories) + self.setWdHistory(wd, clearHistories) + self.setEnvHistory(env, clearHistories) + + # 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 forking flags + self.forkAutomatically = forkAutomatically + self.forkIntoChild = forkIntoChild + + # Hide all error highlights + self.viewmanager.unhighlight() + + if not doNotStart: + if debugProject and self.project.getProjectType() == "E4Plugin": + argv.insert(0, "--plugin=%s " % fn) + fn = os.path.join(getConfig('ericDir'), "eric4.py") + tracePython = True # override flag because it must be true + + # Ask the client to open the new program. + self.debugServer.remoteLoad(fn, argv, wd, env, + autoClearShell = self.autoClearShell, tracePython = tracePython, + autoContinue = autoContinue, forProject = debugProject, + runInConsole = console, autoFork = forkAutomatically, + forkChild = forkIntoChild) + + # Signal that we have started a debugging session + self.emit(SIGNAL('debuggingStarted'), fn) + + self.stopAct.setEnabled(True) + + 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 + 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() == "E4Plugin": + argv.insert(0, "--plugin=%s " % fn) + fn = os.path.join(getConfig('ericDir'), "eric4.py") + + if self.lastStartAction in [1, 2]: + # Ask the client to debug the new program. + self.debugServer.remoteLoad(fn, argv, wd, env, + autoClearShell = self.autoClearShell, tracePython = self.tracePython, + autoContinue = self.autoContinue, forProject = forProject, + runInConsole = self.runInConsole) + + # Signal that we have started a debugging session + self.emit(SIGNAL('debuggingStarted'), fn) + elif self.lastStartAction in [3, 4]: + # Ask the client to run the new program. + self.debugServer.remoteRun(fn, argv, wd, env, + autoClearShell = self.autoClearShell, forProject = forProject, + runInConsole = self.runInConsole) + elif self.lastStartAction in [5, 6]: + # Ask the client to coverage run the new program. + self.debugServer.remoteCoverage(fn, argv, wd, env, + autoClearShell = self.autoClearShell, erase = self.eraseCoverage, + forProject = forProject, runInConsole = self.runInConsole) + elif self.lastStartAction in [7, 8]: + # Ask the client to profile run the new program. + self.debugServer.remoteProfile(fn, argv, wd, env, + autoClearShell = self.autoClearShell, erase = self.eraseTimings, + forProject = forProject, runInConsole = self.runInConsole) + + 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.emit(SIGNAL('debuggingStarted'), fn) + + def __continue(self): + """ + Private method to handle the Continue action. + """ + self.lastAction = 0 + self.__enterRemote() + self.debugServer.remoteContinue() + + def __specialContinue(self): + """ + Private method to handle the Special Continue action. + """ + self.lastAction = 2 + self.__enterRemote() + self.debugServer.remoteContinue(1) + + def __step(self): + """ + Private method to handle the Step action. + """ + self.lastAction = 1 + self.__enterRemote() + self.debugServer.remoteStep() + + def __stepOver(self): + """ + Private method to handle the Step Over action. + """ + self.lastAction = 2 + self.__enterRemote() + self.debugServer.remoteStepOver() + + def __stepOut(self): + """ + Private method to handle the Step Out action. + """ + self.lastAction = 3 + self.__enterRemote() + self.debugServer.remoteStepOut() + + def __stepQuit(self): + """ + Private method to handle the Step Quit action. + """ + self.lastAction = 4 + self.__enterRemote() + self.debugServer.remoteStepQuit() + self.__resetUI() + + def __runToCursor(self): + """ + Private method to handle the Run to Cursor action. + """ + self.lastAction = 0 + aw = self.viewmanager.activeWindow() + line = aw.getCursorPosition()[0] + 1 + self.__enterRemote() + self.debugServer.remoteBreakpoint(aw.getFileName(), + line, 1, None, 1) + self.debugServer.remoteContinue() + + def __eval(self): + """ + Private method to handle the Eval action. + """ + # Get the command line arguments. + if len(self.evalHistory) > 0: + curr = 0 + else: + curr = -1 + + arg, ok = QInputDialog.getItem(\ + self.ui, + self.trUtf8("Evaluate"), + self.trUtf8("Enter the statement to evaluate"), + self.evalHistory, + curr, True) + + if ok: + if arg.isNull(): + return + + # This moves any previous occurrence of this expression to the head + # of the list. + if arg in self.evalHistory: + self.evalHistory.remove(arg) + self.evalHistory.insert(0, arg) + + self.debugServer.remoteEval(arg) + + def __exec(self): + """ + Private method to handle the Exec action. + """ + # Get the command line arguments. + if len(self.execHistory) > 0: + curr = 0 + else: + curr = -1 + + stmt, ok = QInputDialog.getItem(\ + self.ui, + self.trUtf8("Execute"), + self.trUtf8("Enter the statement to execute"), + self.execHistory, + curr, True) + + if ok: + if stmt.isNull(): + return + + # This moves any previous occurrence of this statement to the head + # of the list. + if stmt in self.execHistory: + self.execHistory.remove(stmt) + self.execHistory.insert(0, stmt) + + self.debugServer.remoteExec(stmt) + + 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.debugActGrp2.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 E4Action) + """ + return self.actions[:]