eric6/Debugger/DebugViewer.py

Sun, 06 Dec 2020 17:53:05 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sun, 06 Dec 2020 17:53:05 +0100
branch
multi_processing
changeset 7863
6725d2549801
parent 7802
eefe954f01e8
parent 7862
817ef8e0fa66
child 7876
9164a5c0d4a6
permissions
-rw-r--r--

Merged with the default branch.

# -*- coding: utf-8 -*-

# Copyright (c) 2002 - 2020 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing a widget containing various debug related views.

The views avaliable are:
<ul>
  <li>selector showing all connected debugger backends</li>
  <li>variables viewer for global variables for the selected debug client</li>
  <li>variables viewer for local variables for the selected debug client</li>
  <li>call stack viewer for the selected debug client</li>
  <li>call trace viewer</li>
  <li>viewer for breakpoints</li>
  <li>viewer for watch expressions</li>
  <li>viewer for exceptions</li>
  <li>viewer for threads for the selected debug client</li>
</ul>
"""

import os

from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt
from PyQt5.QtWidgets import (
    QWidget, QVBoxLayout, QHBoxLayout, QLineEdit, QSizePolicy, QPushButton,
    QComboBox, QLabel, QTreeWidget, QTreeWidgetItem, QHeaderView, QFrame
)

import UI.PixmapCache
import Preferences

from E5Gui.E5TabWidget import E5TabWidget


class DebugViewer(QWidget):
    """
    Class implementing a widget containing various debug related views.
    
    The individual tabs contain the interpreter shell (optional),
    the filesystem browser (optional), the two variables viewers
    (global and local), a breakpoint viewer, a watch expression viewer and
    the exception logger. Additionally a list of all threads is shown.
    
    @signal sourceFile(string, int) emitted to open a source file at a line
    @signal preferencesChanged() emitted to react on changed preferences
    """
    sourceFile = pyqtSignal(str, int)
    preferencesChanged = pyqtSignal()
    
    ThreadIdRole = Qt.UserRole + 1
    
    def __init__(self, debugServer, parent=None):
        """
        Constructor
        
        @param debugServer reference to the debug server object (DebugServer)
        @param parent parent widget (QWidget)
        """
        super(DebugViewer, self).__init__(parent)
        
        self.debugServer = debugServer
        self.debugUI = None
        
        self.setWindowIcon(UI.PixmapCache.getIcon("eric"))
        
        self.__mainLayout = QVBoxLayout()
        self.__mainLayout.setContentsMargins(0, 3, 0, 0)
        self.setLayout(self.__mainLayout)
        
        # add the viewer showing the connected debug backends
        self.__debuggersLayout = QHBoxLayout()
        self.__debuggersLayout.addWidget(QLabel(self.tr("Debuggers:")))
        self.__debuggersCombo = QComboBox(self)
        self.__debuggersCombo.setSizePolicy(
            QSizePolicy.Expanding, QSizePolicy.Fixed)
        self.__debuggersLayout.addWidget(self.__debuggersCombo)
        self.__mainLayout.addLayout(self.__debuggersLayout)
        
        self.__debuggersCombo.currentIndexChanged[str].connect(
            self.__debuggerSelected)
        
        # add a line to separate debugger selector from debugger specific parts
        hline = QFrame(self)
        hline.setFrameStyle(QFrame.Sunken)
        hline.setFrameShape(QFrame.HLine)
        self.__mainLayout.addWidget(hline)
        
        # add the tab widget containing various debug related views
        self.__tabWidget = E5TabWidget()
        self.__mainLayout.addWidget(self.__tabWidget)
        
        from .VariablesViewer import VariablesViewer
        # add the global variables viewer
        self.glvWidget = QWidget()
        self.glvWidgetVLayout = QVBoxLayout(self.glvWidget)
        self.glvWidgetVLayout.setContentsMargins(0, 0, 0, 0)
        self.glvWidgetVLayout.setSpacing(3)
        self.glvWidget.setLayout(self.glvWidgetVLayout)
        
        self.globalsViewer = VariablesViewer(self, True, self.glvWidget)
        self.glvWidgetVLayout.addWidget(self.globalsViewer)
        
        self.glvWidgetHLayout = QHBoxLayout()
        self.glvWidgetHLayout.setContentsMargins(3, 3, 3, 3)
        
        self.globalsFilterEdit = QLineEdit(self.glvWidget)
        self.globalsFilterEdit.setSizePolicy(
            QSizePolicy.Expanding, QSizePolicy.Fixed)
        self.glvWidgetHLayout.addWidget(self.globalsFilterEdit)
        self.globalsFilterEdit.setToolTip(
            self.tr("Enter regular expression patterns separated by ';'"
                    " to define variable filters. "))
        self.globalsFilterEdit.setWhatsThis(
            self.tr("Enter regular expression patterns separated by ';'"
                    " to define variable filters. All variables and"
                    " class attributes matched by one of the expressions"
                    " are not shown in the list above."))
        
        self.setGlobalsFilterButton = QPushButton(
            self.tr('Set'), self.glvWidget)
        self.glvWidgetHLayout.addWidget(self.setGlobalsFilterButton)
        self.glvWidgetVLayout.addLayout(self.glvWidgetHLayout)
        
        index = self.__tabWidget.addTab(
            self.glvWidget,
            UI.PixmapCache.getIcon("globalVariables"), '')
        self.__tabWidget.setTabToolTip(index, self.globalsViewer.windowTitle())
        
        self.setGlobalsFilterButton.clicked.connect(
            self.setGlobalsFilter)
        self.globalsFilterEdit.returnPressed.connect(self.setGlobalsFilter)
        
        # add the local variables viewer
        self.lvWidget = QWidget()
        self.lvWidgetVLayout = QVBoxLayout(self.lvWidget)
        self.lvWidgetVLayout.setContentsMargins(0, 0, 0, 0)
        self.lvWidgetVLayout.setSpacing(3)
        self.lvWidget.setLayout(self.lvWidgetVLayout)
        
        self.lvWidgetHLayout1 = QHBoxLayout()
        self.lvWidgetHLayout1.setContentsMargins(3, 3, 3, 3)
        
        self.stackComboBox = QComboBox(self.lvWidget)
        self.stackComboBox.setSizePolicy(
            QSizePolicy.Expanding, QSizePolicy.Fixed)
        self.lvWidgetHLayout1.addWidget(self.stackComboBox)

        self.sourceButton = QPushButton(self.tr('Source'), self.lvWidget)
        self.lvWidgetHLayout1.addWidget(self.sourceButton)
        self.sourceButton.setEnabled(False)
        self.lvWidgetVLayout.addLayout(self.lvWidgetHLayout1)

        self.localsViewer = VariablesViewer(self, False, self.lvWidget)
        self.lvWidgetVLayout.addWidget(self.localsViewer)
        
        self.lvWidgetHLayout2 = QHBoxLayout()
        self.lvWidgetHLayout2.setContentsMargins(3, 3, 3, 3)
        
        self.localsFilterEdit = QLineEdit(self.lvWidget)
        self.localsFilterEdit.setSizePolicy(
            QSizePolicy.Expanding, QSizePolicy.Fixed)
        self.lvWidgetHLayout2.addWidget(self.localsFilterEdit)
        self.localsFilterEdit.setToolTip(
            self.tr(
                "Enter regular expression patterns separated by ';' to define "
                "variable filters. "))
        self.localsFilterEdit.setWhatsThis(
            self.tr(
                "Enter regular expression patterns separated by ';' to define "
                "variable filters. All variables and class attributes matched"
                " by one of the expressions are not shown in the list above."))
        
        self.setLocalsFilterButton = QPushButton(
            self.tr('Set'), self.lvWidget)
        self.lvWidgetHLayout2.addWidget(self.setLocalsFilterButton)
        self.lvWidgetVLayout.addLayout(self.lvWidgetHLayout2)
        
        index = self.__tabWidget.addTab(
            self.lvWidget,
            UI.PixmapCache.getIcon("localVariables"), '')
        self.__tabWidget.setTabToolTip(index, self.localsViewer.windowTitle())
        
        self.sourceButton.clicked.connect(self.__showSource)
        self.stackComboBox.currentIndexChanged[int].connect(
            self.__frameSelected)
        self.setLocalsFilterButton.clicked.connect(self.setLocalsFilter)
        self.localsFilterEdit.returnPressed.connect(self.setLocalsFilter)
        
        self.preferencesChanged.connect(self.handlePreferencesChanged)
        self.preferencesChanged.connect(self.globalsViewer.preferencesChanged)
        self.preferencesChanged.connect(self.localsViewer.preferencesChanged)
        
        from .CallStackViewer import CallStackViewer
        # add the call stack viewer
        self.callStackViewer = CallStackViewer(self.debugServer)
        index = self.__tabWidget.addTab(
            self.callStackViewer,
            UI.PixmapCache.getIcon("callStack"), "")
        self.__tabWidget.setTabToolTip(
            index, self.callStackViewer.windowTitle())
        self.callStackViewer.sourceFile.connect(self.sourceFile)
        self.callStackViewer.frameSelected.connect(
            self.__callStackFrameSelected)
        
        from .CallTraceViewer import CallTraceViewer
        # add the call trace viewer
        self.callTraceViewer = CallTraceViewer(self.debugServer, self)
        index = self.__tabWidget.addTab(
            self.callTraceViewer,
            UI.PixmapCache.getIcon("callTrace"), "")
        self.__tabWidget.setTabToolTip(
            index, self.callTraceViewer.windowTitle())
        self.callTraceViewer.sourceFile.connect(self.sourceFile)
        
        from .BreakPointViewer import BreakPointViewer
        # add the breakpoint viewer
        self.breakpointViewer = BreakPointViewer()
        self.breakpointViewer.setModel(self.debugServer.getBreakPointModel())
        index = self.__tabWidget.addTab(
            self.breakpointViewer,
            UI.PixmapCache.getIcon("breakpoints"), '')
        self.__tabWidget.setTabToolTip(
            index, self.breakpointViewer.windowTitle())
        self.breakpointViewer.sourceFile.connect(self.sourceFile)
        
        from .WatchPointViewer import WatchPointViewer
        # add the watch expression viewer
        self.watchpointViewer = WatchPointViewer()
        self.watchpointViewer.setModel(self.debugServer.getWatchPointModel())
        index = self.__tabWidget.addTab(
            self.watchpointViewer,
            UI.PixmapCache.getIcon("watchpoints"), '')
        self.__tabWidget.setTabToolTip(
            index, self.watchpointViewer.windowTitle())
        
        from .ExceptionLogger import ExceptionLogger
        # add the exception logger
        self.exceptionLogger = ExceptionLogger()
        index = self.__tabWidget.addTab(
            self.exceptionLogger,
            UI.PixmapCache.getIcon("exceptions"), '')
        self.__tabWidget.setTabToolTip(
            index, self.exceptionLogger.windowTitle())
        
        from UI.PythonDisViewer import PythonDisViewer, PythonDisViewerModes
        # add the Python disassembly viewer
        self.disassemblyViewer = PythonDisViewer(
            None, mode=PythonDisViewerModes.TracebackMode)
        index = self.__tabWidget.addTab(
            self.disassemblyViewer,
            UI.PixmapCache.getIcon("disassembly"), '')
        self.__tabWidget.setTabToolTip(
            index, self.disassemblyViewer.windowTitle())
        
        self.__tabWidget.setCurrentWidget(self.glvWidget)
        
        # add the threads viewer
        self.__mainLayout.addWidget(QLabel(self.tr("Threads:")))
        self.__threadList = QTreeWidget()
        self.__threadList.setHeaderLabels(
            [self.tr("Name"), self.tr("State"), ""])
        self.__threadList.setSortingEnabled(True)
        self.__mainLayout.addWidget(self.__threadList)
        
        self.__doThreadListUpdate = True
        
        self.__threadList.currentItemChanged.connect(self.__threadSelected)
        
        self.__mainLayout.setStretchFactor(self.__tabWidget, 5)
        self.__mainLayout.setStretchFactor(self.__threadList, 1)
        
        self.currentStack = None
        self.framenr = 0
        
        self.__autoViewSource = Preferences.getDebugger("AutoViewSourceCode")
        self.sourceButton.setVisible(not self.__autoViewSource)
        
        # connect somer debug server signals
        self.debugServer.clientStack.connect(
            self.handleClientStack)
        self.debugServer.clientThreadList.connect(
            self.showThreadList)
        self.debugServer.clientDebuggerId.connect(
            self.__clientDebuggerId)
        self.debugServer.passiveDebugStarted.connect(
            self.handleDebuggingStarted)
        self.debugServer.clientLine.connect(
            self.__clientLine)
        self.debugServer.clientSyntaxError.connect(
            self.__clientSyntaxError)
        self.debugServer.clientException.connect(
            self.__clientException)
        self.debugServer.clientExit.connect(
            self.__clientExit)
        
        self.debugServer.clientException.connect(
            self.exceptionLogger.addException)
        self.debugServer.passiveDebugStarted.connect(
            self.exceptionLogger.debuggingStarted)
        
        self.debugServer.clientLine.connect(
            self.breakpointViewer.highlightBreakpoint)
    
    def handlePreferencesChanged(self):
        """
        Public slot to handle the preferencesChanged signal.
        """
        self.__autoViewSource = Preferences.getDebugger("AutoViewSourceCode")
        self.sourceButton.setVisible(not self.__autoViewSource)
        
    def setDebugger(self, debugUI):
        """
        Public method to set a reference to the Debug UI.
        
        @param debugUI reference to the DebugUI object (DebugUI)
        """
        self.debugUI = debugUI
        self.callStackViewer.setDebugger(debugUI)
        
        # connect some debugUI signals
        self.debugUI.clientStack.connect(self.handleClientStack)
        self.debugUI.debuggingStarted.connect(
            self.exceptionLogger.debuggingStarted)
        self.debugUI.debuggingStarted.connect(
            self.handleDebuggingStarted)
    
    def handleResetUI(self):
        """
        Public method to reset the SBVviewer.
        """
        self.globalsViewer.handleResetUI()
        self.localsViewer.handleResetUI()
        self.setGlobalsFilter()
        self.setLocalsFilter()
        self.sourceButton.setEnabled(False)
        self.currentStack = None
        self.stackComboBox.clear()
        self.__threadList.clear()
        self.__tabWidget.setCurrentWidget(self.glvWidget)
        self.breakpointViewer.handleResetUI()
        self.__debuggersCombo.clear()
        self.disassemblyViewer.clear()
        
    def initCallStackViewer(self, projectMode):
        """
        Public method to initialize the call stack viewer.
        
        @param projectMode flag indicating to enable the project mode (boolean)
        """
        self.callStackViewer.clear()
        self.callStackViewer.setProjectMode(projectMode)
        
    def isCallTraceEnabled(self):
        """
        Public method to get the state of the call trace function.
        
        @return flag indicating the state of the call trace function (boolean)
        """
        return self.callTraceViewer.isCallTraceEnabled()
        
    def clearCallTrace(self):
        """
        Public method to clear the recorded call trace.
        """
        self.callTraceViewer.clear()
        
    def setCallTraceToProjectMode(self, enabled):
        """
        Public slot to set the call trace viewer to project mode.
        
        In project mode the call trace info is shown with project relative
        path names.
        
        @param enabled flag indicating to enable the project mode (boolean)
        """
        self.callTraceViewer.setProjectMode(enabled)
        
    def showVariables(self, vlist, showGlobals):
        """
        Public method to show the variables in the respective window.
        
        @param vlist list of variables to display
        @param showGlobals flag indicating global/local state
        """
        if showGlobals:
            self.globalsViewer.showVariables(vlist, self.framenr)
        else:
            self.localsViewer.showVariables(vlist, self.framenr)
            
    def showVariable(self, vlist, showGlobals):
        """
        Public method to show the variables in the respective window.
        
        @param vlist list of variables to display
        @param showGlobals flag indicating global/local state
        """
        if showGlobals:
            self.globalsViewer.showVariable(vlist)
        else:
            self.localsViewer.showVariable(vlist)
            
    def showVariablesTab(self, showGlobals):
        """
        Public method to make a variables tab visible.
        
        @param showGlobals flag indicating global/local state
        """
        if showGlobals:
            self.__tabWidget.setCurrentWidget(self.glvWidget)
        else:
            self.__tabWidget.setCurrentWidget(self.lvWidget)
        
    def handleClientStack(self, stack, debuggerId):
        """
        Public slot to show the call stack of the program being debugged.
        
        @param stack list of tuples with call stack data (file name,
            line number, function name, formatted argument/values list)
        @type list of tuples of (str, str, str, str)
        @param debuggerId ID of the debugger backend
        @type str
        """
        if debuggerId == self.__debuggersCombo.currentText():
            block = self.stackComboBox.blockSignals(True)
            self.framenr = 0
            self.stackComboBox.clear()
            self.currentStack = stack
            self.sourceButton.setEnabled(len(stack) > 0)
            for s in stack:
                # just show base filename to make it readable
                s = (os.path.basename(s[0]), s[1], s[2])
                self.stackComboBox.addItem('{0}:{1}:{2}'.format(*s))
            self.stackComboBox.blockSignals(block)
    
    def __clientLine(self, fn, line, debuggerId):
        """
        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
        """
        self.__setDebuggerIconAndState(debuggerId, "break", "broken")
        if debuggerId != self.getSelectedDebuggerId():
            self.__debuggersCombo.setCurrentText(debuggerId)
    
    @pyqtSlot(str, int, str, bool, str)
    def __clientExit(self, program, status, message, quiet, debuggerId):
        """
        Private method 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
        """
        if debuggerId == self.getSelectedDebuggerId():
            # the current client has exited
            self.globalsViewer.handleResetUI()
            self.localsViewer.handleResetUI()
            self.setGlobalsFilter()
            self.setLocalsFilter()
            self.sourceButton.setEnabled(False)
            self.currentStack = None
            self.stackComboBox.clear()
            self.__threadList.clear()
        index = self.__debuggersCombo.findText(debuggerId, Qt.MatchExactly)
        self.__debuggersCombo.removeItem(index)
    
    def __clientSyntaxError(self, message, filename, lineNo, characterNo,
                            debuggerId):
        """
        Private method to handle a syntax error in the debugged program.
        
        @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 characterNo character number of the syntax error position
        @type int
        @param debuggerId ID of the debugger backend
        @type str
        """
        self.__setDebuggerIconAndState(debuggerId, "syntaxError22",
                                       "exception")
    
    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.__setDebuggerIconAndState(debuggerId, "exceptions", "exception")
    
    def setVariablesFilter(self, globalsFilter, localsFilter):
        """
        Public slot to set the local variables filter.
        
        @param globalsFilter filter list for global variable types
        @type list of str
        @param localsFilter filter list for local variable types
        @type list of str
        """
        self.__globalsFilter = globalsFilter
        self.__localsFilter = localsFilter
        
    def __showSource(self):
        """
        Private slot to handle the source button press to show the selected
        file.
        """
        index = self.stackComboBox.currentIndex()
        if index > -1 and self.currentStack:
            s = self.currentStack[index]
            self.sourceFile.emit(s[0], int(s[1]))
        
    def __frameSelected(self, frmnr):
        """
        Private slot to handle the selection of a new stack frame number.
        
        @param frmnr frame number (0 is the current frame) (int)
        """
        if frmnr >= 0:
            self.framenr = frmnr
            if self.debugServer.isDebugging():
                self.debugServer.remoteClientVariables(
                    self.getSelectedDebuggerId(), 0, self.__localsFilter,
                    frmnr)
            
            if self.__autoViewSource:
                self.__showSource()
        
    def setGlobalsFilter(self):
        """
        Public slot to set the global variable filter.
        """
        if self.debugServer.isDebugging():
            filterStr = self.globalsFilterEdit.text()
            self.debugServer.remoteClientSetFilter(
                self.getSelectedDebuggerId(), 1, filterStr)
            self.debugServer.remoteClientVariables(
                self.getSelectedDebuggerId(), 2, self.__globalsFilter)
        
    def setLocalsFilter(self):
        """
        Public slot to set the local variable filter.
        """
        if self.debugServer.isDebugging():
            filterStr = self.localsFilterEdit.text()
            self.debugServer.remoteClientSetFilter(
                self.getSelectedDebuggerId(), 0, filterStr)
            if self.currentStack:
                self.debugServer.remoteClientVariables(
                    self.getSelectedDebuggerId(), 0, self.__localsFilter,
                    self.framenr)
        
    def handleDebuggingStarted(self):
        """
        Public slot to handle the start of a debugging session.
        
        This slot sets the variables filter expressions.
        """
        self.setGlobalsFilter()
        self.setLocalsFilter()
        self.showVariablesTab(False)
        
        self.disassemblyViewer.clear()
        
    def currentWidget(self):
        """
        Public method to get a reference to the current widget.
        
        @return reference to the current widget (QWidget)
        """
        return self.__tabWidget.currentWidget()
        
    def setCurrentWidget(self, widget):
        """
        Public slot to set the current page based on the given widget.
        
        @param widget reference to the widget (QWidget)
        """
        self.__tabWidget.setCurrentWidget(widget)
        
    def showThreadList(self, currentID, threadList, debuggerId):
        """
        Public method to show the thread list.
        
        @param currentID id of the current thread
        @type int
        @param threadList list of dictionaries containing the thread data
        @type list of dict
        @param debuggerId ID of the debugger backend
        @type str
        """
        debugStatus = -1    # i.e. running
        
        if debuggerId == self.__debuggersCombo.currentText():
            citm = None
            
            self.__threadList.clear()
            for thread in threadList:
                if thread.get('except', False):
                    state = self.tr("waiting at exception")
                    icon = "exceptions"
                    debugStatus = 1
                elif thread['broken']:
                    state = self.tr("waiting at breakpoint")
                    icon = "break"
                    if debugStatus < 1:
                        debugStatus = 0
                else:
                    state = self.tr("running")
                    icon = "mediaPlaybackStart"
                itm = QTreeWidgetItem(self.__threadList,
                                      [thread['name'], state])
                itm.setData(0, self.ThreadIdRole, thread['id'])
                itm.setIcon(0, UI.PixmapCache.getIcon(icon))
                if thread['id'] == currentID:
                    citm = itm
            
            self.__threadList.header().resizeSections(
                QHeaderView.ResizeToContents)
            self.__threadList.header().setStretchLastSection(True)
            
            if citm:
                self.__doThreadListUpdate = False
                self.__threadList.setCurrentItem(citm)
                self.__doThreadListUpdate = True
        else:
            for thread in threadList:
                if thread.get('except', False):
                    debugStatus = 1
                elif thread['broken']:
                    if debugStatus < 1:
                        debugStatus = 0
        
        if debugStatus == -1:
            icon = "mediaPlaybackStart"
            state = "running"
        elif debugStatus == 0:
            icon = "break"
            state = "broken"
        else:
            icon = "exceptions"
            state = "exception"
        self.__setDebuggerIconAndState(debuggerId, icon, state)
    
    def __threadSelected(self, current, previous):
        """
        Private slot to handle the selection of a thread in the thread list.
        
        @param current reference to the new current item (QTreeWidgetItem)
        @param previous reference to the previous current item
            (QTreeWidgetItem)
        """
        if current is not None and self.__doThreadListUpdate:
            tid = current.data(0, self.ThreadIdRole)
            self.debugServer.remoteSetThread(self.getSelectedDebuggerId(), tid)
    
    def __callStackFrameSelected(self, frameNo):
        """
        Private slot to handle the selection of a call stack entry of the
        call stack viewer.
        
        @param frameNo frame number (index) of the selected entry (integer)
        """
        if frameNo >= 0:
            self.stackComboBox.setCurrentIndex(frameNo)
    
    def __debuggerSelected(self, debuggerId):
        """
        Private slot to handle the selection of a debugger backend ID.
        
        @param debuggerId ID of the selected debugger backend
        @type str
        """
        if debuggerId:
            self.globalsViewer.handleResetUI()
            self.localsViewer.handleResetUI()
            self.currentStack = None
            self.stackComboBox.clear()
            self.__threadList.clear()
            self.callStackViewer.clear()
            
            self.debugUI.getDebuggerData(debuggerId)
            self.debugUI.setDebugActionsEnabled(
                self.getSelectedDebuggerState() != "running")
            self.__showSource()
    
    def __clientDebuggerId(self, debuggerId):
        """
        Private slot to receive the ID of a newly connected debugger backend.
        
        @param debuggerId ID of a newly connected debugger backend
        @type str
        """
        self.__debuggersCombo.addItem(debuggerId)
    
    def getSelectedDebuggerId(self):
        """
        Public method to get the currently selected debugger ID.
        
        @return selected debugger ID
        @rtype str
        """
        return self.__debuggersCombo.currentText()
    
    def getSelectedDebuggerState(self):
        """
        Public method to get the currently selected debugger's state.
        
        @return selected debugger's state (broken, exception, running)
        @rtype str
        """
        return self.__debuggersCombo.currentData()
    
    def __setDebuggerIconAndState(self, debuggerId, iconName, state):
        """
        Private method to set the icon for a specific debugger ID.
        
        @param debuggerId ID of the debugger backend (empty ID means the
            currently selected one)
        @type str
        @param iconName name of the icon to be used
        @type str
        @param state state of the debugger (broken, exception, running)
        @type str
        """
        if debuggerId:
            index = self.__debuggersCombo.findText(debuggerId, Qt.MatchExactly)
        else:
            index = self.__debuggersCombo.currentIndex()
        if index >= 0:
            self.__debuggersCombo.setItemIcon(
                index, UI.PixmapCache.getIcon(iconName))
            self.__debuggersCombo.setItemData(index, state)

eric ide

mercurial