eric6/Debugger/CallStackViewer.py

branch
maintenance
changeset 8043
0acf98cd089a
parent 7924
8a96736d465e
parent 7927
866ddf957461
child 8176
31965986ecd1
--- 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,

eric ide

mercurial