diff -r 866adc8c315b -r 0acf98cd089a eric6/Debugger/CallStackViewer.py --- a/eric6/Debugger/CallStackViewer.py Sun Jan 17 13:53:08 2021 +0100 +++ b/eric6/Debugger/CallStackViewer.py Mon Feb 01 10:38:16 2021 +0100 @@ -8,7 +8,9 @@ """ from PyQt5.QtCore import pyqtSignal, Qt, QFileInfo -from PyQt5.QtWidgets import QTreeWidget, QTreeWidgetItem, QMenu +from PyQt5.QtWidgets import ( + QTreeWidget, QTreeWidgetItem, QMenu, QWidget, QVBoxLayout, QLabel +) from E5Gui.E5Application import e5App from E5Gui import E5FileDialog, E5MessageBox @@ -16,7 +18,7 @@ import Utilities -class CallStackViewer(QTreeWidget): +class CallStackViewer(QWidget): """ Class implementing the Call Stack viewer widget. @@ -33,25 +35,35 @@ """ Constructor - @param debugServer reference to the debug server object (DebugServer) - @param parent reference to the parent widget (QWidget) + @param debugServer reference to the debug server object + @type DebugServer + @param parent reference to the parent widget + @type QWidget """ super(CallStackViewer, self).__init__(parent) - self.setHeaderHidden(True) - self.setAlternatingRowColors(True) - self.setItemsExpandable(False) - self.setRootIsDecorated(False) + self.__layout = QVBoxLayout(self) + self.setLayout(self.__layout) + self.__debuggerLabel = QLabel(self) + self.__layout.addWidget(self.__debuggerLabel) + self.__callStackList = QTreeWidget(self) + self.__layout.addWidget(self.__callStackList) + + self.__callStackList.setHeaderHidden(True) + self.__callStackList.setAlternatingRowColors(True) + self.__callStackList.setItemsExpandable(False) + self.__callStackList.setRootIsDecorated(False) self.setWindowTitle(self.tr("Call Stack")) - self.__menu = QMenu(self) + self.__menu = QMenu(self.__callStackList) self.__sourceAct = self.__menu.addAction( self.tr("Show source"), self.__openSource) - self.__menu.addAction(self.tr("Clear"), self.clear) + self.__menu.addAction(self.tr("Clear"), self.__callStackList.clear) self.__menu.addSeparator() self.__menu.addAction(self.tr("Save"), self.__saveStackTrace) - self.setContextMenuPolicy(Qt.CustomContextMenu) - self.customContextMenuRequested.connect(self.__showContextMenu) + self.__callStackList.setContextMenuPolicy(Qt.CustomContextMenu) + self.__callStackList.customContextMenuRequested.connect( + self.__showContextMenu) self.__dbs = debugServer @@ -64,13 +76,15 @@ self.__project = None self.__dbs.clientStack.connect(self.__showCallStack) - self.itemDoubleClicked.connect(self.__itemDoubleClicked) + self.__callStackList.itemDoubleClicked.connect( + self.__itemDoubleClicked) def setDebugger(self, debugUI): """ Public method to set a reference to the Debug UI. - @param debugUI reference to the DebugUI object (DebugUI) + @param debugUI reference to the DebugUI object + @type DebugUI """ debugUI.clientStack.connect(self.__showCallStack) @@ -81,7 +95,8 @@ In project mode the call trace info is shown with project relative path names. - @param enabled flag indicating to enable the project mode (boolean) + @param enabled flag indicating to enable the project mode + @type bool """ self.__projectMode = enabled if enabled and self.__project is None: @@ -91,21 +106,34 @@ """ Private slot to show the context menu. - @param coord the position of the mouse pointer (QPoint) + @param coord the position of the mouse pointer + @type QPoint """ - if self.topLevelItemCount() > 0: - itm = self.currentItem() + if self.__callStackList.topLevelItemCount() > 0: + itm = self.__callStackList.currentItem() self.__sourceAct.setEnabled(itm is not None) - self.__menu.popup(self.mapToGlobal(coord)) + self.__menu.popup(self.__callStackList.mapToGlobal(coord)) - def __showCallStack(self, stack): + def clear(self): + """ + Public method to clear the stack viewer data. + """ + self.__debuggerLabel.clear() + self.__callStackList.clear() + + def __showCallStack(self, stack, debuggerId): """ Private 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 """ - self.clear() + self.__debuggerLabel.setText(debuggerId) + + self.__callStackList.clear() for fname, fline, ffunc, fargs in stack: if self.__projectMode: dfname = self.__project.getRelativePath(fname) @@ -114,22 +142,26 @@ if ffunc and not ffunc.startswith("<"): # use normal format itm = QTreeWidgetItem( - self, - [self.__entryFormat.format(dfname, fline, ffunc, fargs)]) + self.__callStackList, + [self.__entryFormat.format(dfname, fline, ffunc, fargs)] + ) else: # use short format itm = QTreeWidgetItem( - self, [self.__entryFormatShort.format(dfname, fline)]) + self.__callStackList, + [self.__entryFormatShort.format(dfname, fline)] + ) itm.setData(0, self.FilenameRole, fname) itm.setData(0, self.LinenoRole, fline) - self.resizeColumnToContents(0) + self.__callStackList.resizeColumnToContents(0) def __itemDoubleClicked(self, itm): """ Private slot to handle a double click of a stack entry. - @param itm reference to the double clicked item (QTreeWidgetItem) + @param itm reference to the double clicked item + @type QTreeWidgetItem """ fname = itm.data(0, self.FilenameRole) fline = itm.data(0, self.LinenoRole) @@ -137,14 +169,14 @@ fname = self.__project.getAbsolutePath(fname) self.sourceFile.emit(fname, fline) - index = self.indexOfTopLevelItem(itm) + index = self.__callStackList.indexOfTopLevelItem(itm) self.frameSelected.emit(index) def __openSource(self): """ Private slot to show the source for the selected stack entry. """ - itm = self.currentItem() + itm = self.__callStackList.currentItem() if itm: self.__itemDoubleClicked(itm) @@ -152,7 +184,7 @@ """ Private slot to save the stack trace info to a file. """ - if self.topLevelItemCount() > 0: + if self.__callStackList.topLevelItemCount() > 0: fname, selectedFilter = E5FileDialog.getSaveFileNameAndFilter( self, self.tr("Save Call Stack Info"), @@ -178,12 +210,16 @@ fname = Utilities.toNativeSeparators(fname) try: + title = self.tr("Call Stack of '{0}'").format( + self.__debuggerLabel.text()) with open(fname, "w", encoding="utf-8") as f: - itm = self.topLevelItem(0) + f.write("{0}\n".format(title)) + f.write("{0}\n\n".format(len(title) * "=")) + itm = self.__callStackList.topLevelItem(0) while itm is not None: f.write("{0}\n".format(itm.text(0))) - f.write(78 * "=" + "\n") - itm = self.itemBelow(itm) + f.write("{0}\n".format(78 * "=")) + itm = self.__callStackList.itemBelow(itm) except OSError as err: E5MessageBox.critical( self,