--- a/eric6/Debugger/DebugViewer.py Mon Dec 28 19:43:35 2020 +0100 +++ b/eric6/Debugger/DebugViewer.py Tue Dec 29 20:06:50 2020 +0100 @@ -50,6 +50,7 @@ preferencesChanged = pyqtSignal() ThreadIdRole = Qt.UserRole + 1 + DebuggerStateRole = Qt.UserRole + 2 def __init__(self, debugServer, parent=None): """ @@ -78,13 +79,14 @@ self.__debuggersWidget = QWidget() self.__debuggersLayout = QVBoxLayout(self.__debuggersWidget) self.__debuggersLayout.setContentsMargins(0, 0, 0, 0) - self.__debuggersLayout.addWidget(QLabel(self.tr("Debuggers:"))) + self.__debuggersLayout.addWidget( + QLabel(self.tr("Debuggers and Threads:"))) self.__debuggersList = QTreeWidget() self.__debuggersList.setHeaderLabels( [self.tr("ID"), self.tr("State"), ""]) self.__debuggersList.header().setStretchLastSection(True) self.__debuggersList.setSortingEnabled(True) - self.__debuggersList.setRootIsDecorated(False) + self.__debuggersList.setRootIsDecorated(True) self.__debuggersList.setAlternatingRowColors(True) self.__debuggersLayout.addWidget(self.__debuggersList) self.__mainSplitter.addWidget(self.__debuggersWidget) @@ -271,25 +273,9 @@ self.__tabWidget.setCurrentWidget(self.glvWidget) - # add the threads viewer - self.__threadWidget = QWidget() - self.__threadWidgetLayout = QVBoxLayout(self.__threadWidget) - self.__threadWidgetLayout.setContentsMargins(0, 0, 0, 0) - self.__threadWidgetLayout.addWidget(QLabel(self.tr("Threads:"))) - self.__threadList = QTreeWidget() - self.__threadList.setHeaderLabels( - [self.tr("Name"), self.tr("State"), ""]) - self.__threadList.setSortingEnabled(True) - self.__threadList.setRootIsDecorated(False) - self.__threadList.setAlternatingRowColors(True) - self.__threadWidgetLayout.addWidget(self.__threadList) - self.__mainSplitter.addWidget(self.__threadWidget) + self.__doDebuggersListUpdate = True - self.__doThreadListUpdate = True - - self.__threadList.currentItemChanged.connect(self.__threadSelected) - - self.__mainSplitter.setSizes([50, 700, 50]) + self.__mainSplitter.setSizes([100, 700]) self.currentStack = None self.framenr = 0 @@ -297,11 +283,11 @@ self.__autoViewSource = Preferences.getDebugger("AutoViewSourceCode") self.sourceButton.setVisible(not self.__autoViewSource) - # connect somer debug server signals + # connect some debug server signals self.debugServer.clientStack.connect( self.handleClientStack) self.debugServer.clientThreadList.connect( - self.showThreadList) + self.__addThreadList) self.debugServer.clientDebuggerId.connect( self.__clientDebuggerId) self.debugServer.passiveDebugStarted.connect( @@ -358,7 +344,6 @@ self.sourceButton.setEnabled(False) self.currentStack = None self.stackComboBox.clear() - self.__threadList.clear() self.__tabWidget.setCurrentWidget(self.glvWidget) self.breakpointViewer.handleResetUI() self.__debuggersList.clear() @@ -504,7 +489,6 @@ self.sourceButton.setEnabled(False) self.currentStack = None self.stackComboBox.clear() - self.__threadList.clear() self.__removeDebugger(debuggerId) @@ -635,83 +619,6 @@ @type 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.getSelectedDebuggerId(): - 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 - @type QTreeWidgetItem - @param previous reference to the previous current item - @type 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): """ @@ -734,22 +641,25 @@ @param previous reference to the previous current item @type QTreeWidgetItem """ - if current is not None: - debuggerId = current.text(0) - else: - debuggerId = "" - 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() + if current is not None and self.__doDebuggersListUpdate: + if current.parent() is None: + # it is a debugger item + debuggerId = current.text(0) + self.globalsViewer.handleResetUI() + self.localsViewer.handleResetUI() + self.currentStack = None + self.stackComboBox.clear() + self.callStackViewer.clear() + + self.debugUI.getDebuggerData(debuggerId) + self.debugUI.setDebugActionsEnabled( + self.getSelectedDebuggerState() != "running") + self.__showSource() + else: + # it is a thread item + tid = current.data(0, self.ThreadIdRole) + self.debugServer.remoteSetThread( + self.getSelectedDebuggerId(), tid) def __clientDebuggerId(self, debuggerId): """ @@ -779,10 +689,24 @@ @param debuggerId ID of the debugger to set as current debugger @type str """ - foundItems = self.__debuggersList.findItems(debuggerId, - Qt.MatchExactly) - if foundItems: - self.__debuggersList.setCurrentItem(foundItems[0]) + debuggerItems = self.__debuggersList.findItems( + debuggerId, Qt.MatchExactly) + if debuggerItems: + debuggerItem = debuggerItems[0] + currentItem = self.__debuggersList.currentItem() + if currentItem is debuggerItem: + # nothing to do + return + + currentParent = currentItem.parent() + if currentParent is None: + # current is a debugger item + self.__debuggersList.setCurrentItem(debuggerItem) + elif currentParent is debuggerItem: + # nothing to do + return + else: + self.__debuggersList.setCurrentItem(debuggerItem) def isOnlyDebugger(self): """ @@ -802,7 +726,12 @@ """ itm = self.__debuggersList.currentItem() if itm: - return itm.text(0) + if itm.parent() is None: + # it is a debugger item + return itm.text(0) + else: + # it is a thread item + return itm.parent().text(0) else: return "" @@ -815,7 +744,12 @@ """ itm = self.__debuggersList.currentItem() if itm: - return itm.data(0, Qt.UserRole) + if itm.parent() is None: + # it is a debugger item + return itm.data(0, self.DebuggerStateRole) + else: + # it is a thread item + return itm.parent().data(0, self.DebuggerStateRole) else: return "" @@ -841,7 +775,7 @@ debuggerItem = self.__debuggersList.currentItem() if debuggerItem is not None: debuggerItem.setIcon(0, UI.PixmapCache.getIcon(iconName)) - debuggerItem.setData(0, Qt.UserRole, state) + debuggerItem.setData(0, self.DebuggerStateRole, state) if state == "broken": debuggerItem.setText(1, self.tr("waiting at breakpoint")) elif state == "exception": @@ -869,3 +803,69 @@ itm = self.__debuggersList.takeTopLevelItem(index) # __IGNORE_WARNING__ del itm + + def __addThreadList(self, currentID, threadList, debuggerId): + """ + Private method to add the list of threads to a debugger entry. + + @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 + + debuggerItems = self.__debuggersList.findItems( + debuggerId, Qt.MatchExactly) + if debuggerItems: + debuggerItem = debuggerItems[0] + + currentItem = self.__debuggersList.currentItem() + if currentItem.parent() is debuggerItem: + currentChild = currentItem.text(0) + else: + currentChild = "" + self.__doDebuggersListUpdate = False + debuggerItem.takeChildren() + 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(debuggerItem, [thread['name'], state]) + itm.setData(0, self.ThreadIdRole, thread['id']) + itm.setIcon(0, UI.PixmapCache.getIcon(icon)) + if currentChild == thread['name']: + self.__debuggersList.setCurrentItem(itm) + if thread['id'] == currentID: + font = debuggerItem.font(0) + font.setItalic(True) + itm.setFont(0, font) + + debuggerItem.setExpanded(debuggerItem.childCount() > 0) + + self.__debuggersList.header().resizeSections( + QHeaderView.ResizeToContents) + self.__debuggersList.header().setStretchLastSection(True) + self.__doDebuggersListUpdate = True + + if debugStatus == -1: + icon = "mediaPlaybackStart" + state = "running" + elif debugStatus == 0: + icon = "break" + state = "broken" + else: + icon = "exceptions" + state = "exception" + self.__setDebuggerIconAndState(debuggerId, icon, state)