--- a/eric6/Debugger/DebugViewer.py Fri Jan 15 19:46:04 2021 +0100 +++ b/eric6/Debugger/DebugViewer.py Fri Jan 15 19:49:36 2021 +0100 @@ -8,7 +8,8 @@ The views avaliable are: <ul> - <li>selector showing all connected debugger backends</li> + <li>selector showing all connected debugger backends with associated + threads</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> @@ -16,13 +17,13 @@ <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> + <li>viewer for a code disassembly for an exception<li> </ul> """ import os -from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt +from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QCoreApplication from PyQt5.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QLineEdit, QSizePolicy, QPushButton, QComboBox, QLabel, QTreeWidget, QTreeWidgetItem, QHeaderView, QSplitter @@ -52,6 +53,26 @@ ThreadIdRole = Qt.UserRole + 1 DebuggerStateRole = Qt.UserRole + 2 + # Map debug state to icon name + StateIcon = { + "broken": "break", + "exception": "exceptions", + "running": "mediaPlaybackStart", + "syntax": "syntaxError22", + } + + # Map debug state to user message + StateMessage = { + "broken": QCoreApplication.translate( + "DebugViewer", "waiting at breakpoint"), + "exception": QCoreApplication.translate( + "DebugViewer", "waiting at exception"), + "running": QCoreApplication.translate( + "DebugViewer", "running"), + "syntax": QCoreApplication.translate( + "DebugViewer", "syntax error"), + } + def __init__(self, debugServer, parent=None): """ Constructor @@ -454,7 +475,7 @@ self.stackComboBox.addItem('{0}:{1}:{2}'.format(*s)) self.stackComboBox.blockSignals(block) - def __clientLine(self, fn, line, debuggerId): + def __clientLine(self, fn, line, debuggerId, threadName): """ Private method to handle a change to the current line. @@ -464,8 +485,11 @@ @type int @param debuggerId ID of the debugger backend @type str + @param threadName name of the thread signaling the event + @type str """ - self.__setDebuggerIconAndState(debuggerId, "break", "broken") + self.__setDebuggerIconAndState(debuggerId, "broken") + self.__setThreadIconAndState(debuggerId, threadName, "broken") if debuggerId != self.getSelectedDebuggerId(): self.__setCurrentDebugger(debuggerId) @@ -499,7 +523,7 @@ self.__removeDebugger(debuggerId) def __clientSyntaxError(self, message, filename, lineNo, characterNo, - debuggerId): + debuggerId, threadName): """ Private method to handle a syntax error in the debugged program. @@ -513,12 +537,14 @@ @type int @param debuggerId ID of the debugger backend @type str + @param threadName name of the thread signaling the event + @type str """ - self.__setDebuggerIconAndState(debuggerId, "syntaxError22", - "exception") + self.__setDebuggerIconAndState(debuggerId, "syntax") + self.__setThreadIconAndState(debuggerId, threadName, "syntax") def __clientException(self, exceptionType, exceptionMessage, stackTrace, - debuggerId): + debuggerId, threadName): """ Private method to handle an exception of the debugged program. @@ -530,8 +556,11 @@ @type list of str @param debuggerId ID of the debugger backend @type str + @param threadName name of the thread signaling the event + @type str """ - self.__setDebuggerIconAndState(debuggerId, "exceptions", "exception") + self.__setDebuggerIconAndState(debuggerId, "exception") + self.__setThreadIconAndState(debuggerId, threadName, "exception") def setVariablesFilter(self, globalsFilter, localsFilter): """ @@ -760,15 +789,13 @@ else: return "" - def __setDebuggerIconAndState(self, debuggerId, iconName, state): + def __setDebuggerIconAndState(self, debuggerId, 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 """ @@ -781,17 +808,17 @@ if debuggerItem is None: debuggerItem = self.__debuggersList.currentItem() if debuggerItem is not None: + try: + iconName = DebugViewer.StateIcon[state] + except KeyError: + iconName = "question" + try: + stateText = DebugViewer.StateMessage[state] + except KeyError: + stateText = self.tr("unknown state ({0})").format(state) debuggerItem.setIcon(0, UI.PixmapCache.getIcon(iconName)) debuggerItem.setData(0, self.DebuggerStateRole, state) - if state == "broken": - debuggerItem.setText(1, self.tr("waiting at breakpoint")) - elif state == "exception": - debuggerItem.setText(1, self.tr("waiting at exception")) - elif state == "running": - debuggerItem.setText(1, self.tr("running")) - else: - debuggerItem.setText( - 1, self.tr("unknown state ({0})").format(state)) + debuggerItem.setText(1, stateText) self.__debuggersList.header().resizeSections( QHeaderView.ResizeToContents) @@ -838,20 +865,21 @@ debuggerItem.takeChildren() for thread in threadList: if thread.get('except', False): - state = self.tr("waiting at exception") - icon = "exceptions" + stateText = DebugViewer.StateMessage["exception"] + iconName = DebugViewer.StateIcon["exception"] debugStatus = 1 elif thread['broken']: - state = self.tr("waiting at breakpoint") - icon = "break" + stateText = DebugViewer.StateMessage["broken"] + iconName = DebugViewer.StateIcon["broken"] if debugStatus < 1: debugStatus = 0 else: - state = self.tr("running") - icon = "mediaPlaybackStart" - itm = QTreeWidgetItem(debuggerItem, [thread['name'], state]) + stateText = DebugViewer.StateMessage["running"] + iconName = DebugViewer.StateIcon["running"] + itm = QTreeWidgetItem(debuggerItem, + [thread['name'], stateText]) itm.setData(0, self.ThreadIdRole, thread['id']) - itm.setIcon(0, UI.PixmapCache.getIcon(icon)) + itm.setIcon(0, UI.PixmapCache.getIcon(iconName)) if currentChild == thread['name']: self.__debuggersList.setCurrentItem(itm) if thread['id'] == currentID: @@ -867,12 +895,53 @@ self.__doDebuggersListUpdate = True if debugStatus == -1: - icon = "mediaPlaybackStart" - state = "running" + debuggerState = "running" elif debugStatus == 0: - icon = "break" - state = "broken" + debuggerState = "broken" else: - icon = "exceptions" - state = "exception" - self.__setDebuggerIconAndState(debuggerId, icon, state) + debuggerState = "exception" + self.__setDebuggerIconAndState(debuggerId, debuggerState) + + def __setThreadIconAndState(self, debuggerId, threadName, state): + """ + Private method to set the icon for a specific thread name and + debugger ID. + + @param debuggerId ID of the debugger backend (empty ID means the + currently selected one) + @type str + @param threadName name of the thread signaling the event + @type str + @param state state of the debugger (broken, exception, running) + @type str + """ + debuggerItem = None + if debuggerId: + foundItems = self.__debuggersList.findItems( + debuggerId, Qt.MatchExactly) + if foundItems: + debuggerItem = foundItems[0] + if debuggerItem is None: + debuggerItem = self.__debuggersList.currentItem() + if debuggerItem is not None: + for index in range(debuggerItem.childCount()): + childItem = debuggerItem.child(index) + if childItem.text(0) == threadName: + break + else: + childItem = None + + if childItem is not None: + try: + iconName = DebugViewer.StateIcon[state] + except KeyError: + iconName = "question" + try: + stateText = DebugViewer.StateMessage[state] + except KeyError: + stateText = self.tr("unknown state ({0})").format(state) + childItem.setIcon(0, UI.PixmapCache.getIcon(iconName)) + childItem.setText(1, stateText) + + self.__debuggersList.header().resizeSections( + QHeaderView.ResizeToContents)