eric6/Debugger/CallStackViewer.py

branch
maintenance
changeset 8043
0acf98cd089a
parent 7924
8a96736d465e
parent 7927
866ddf957461
child 8176
31965986ecd1
equal deleted inserted replaced
7991:866adc8c315b 8043:0acf98cd089a
6 """ 6 """
7 Module implementing the Call Stack viewer widget. 7 Module implementing the Call Stack viewer widget.
8 """ 8 """
9 9
10 from PyQt5.QtCore import pyqtSignal, Qt, QFileInfo 10 from PyQt5.QtCore import pyqtSignal, Qt, QFileInfo
11 from PyQt5.QtWidgets import QTreeWidget, QTreeWidgetItem, QMenu 11 from PyQt5.QtWidgets import (
12 QTreeWidget, QTreeWidgetItem, QMenu, QWidget, QVBoxLayout, QLabel
13 )
12 14
13 from E5Gui.E5Application import e5App 15 from E5Gui.E5Application import e5App
14 from E5Gui import E5FileDialog, E5MessageBox 16 from E5Gui import E5FileDialog, E5MessageBox
15 17
16 import Utilities 18 import Utilities
17 19
18 20
19 class CallStackViewer(QTreeWidget): 21 class CallStackViewer(QWidget):
20 """ 22 """
21 Class implementing the Call Stack viewer widget. 23 Class implementing the Call Stack viewer widget.
22 24
23 @signal sourceFile(str, int) emitted to show the source of a stack entry 25 @signal sourceFile(str, int) emitted to show the source of a stack entry
24 @signal frameSelected(int) emitted to signal the selection of a frame entry 26 @signal frameSelected(int) emitted to signal the selection of a frame entry
31 33
32 def __init__(self, debugServer, parent=None): 34 def __init__(self, debugServer, parent=None):
33 """ 35 """
34 Constructor 36 Constructor
35 37
36 @param debugServer reference to the debug server object (DebugServer) 38 @param debugServer reference to the debug server object
37 @param parent reference to the parent widget (QWidget) 39 @type DebugServer
40 @param parent reference to the parent widget
41 @type QWidget
38 """ 42 """
39 super(CallStackViewer, self).__init__(parent) 43 super(CallStackViewer, self).__init__(parent)
40 44
41 self.setHeaderHidden(True) 45 self.__layout = QVBoxLayout(self)
42 self.setAlternatingRowColors(True) 46 self.setLayout(self.__layout)
43 self.setItemsExpandable(False) 47 self.__debuggerLabel = QLabel(self)
44 self.setRootIsDecorated(False) 48 self.__layout.addWidget(self.__debuggerLabel)
49 self.__callStackList = QTreeWidget(self)
50 self.__layout.addWidget(self.__callStackList)
51
52 self.__callStackList.setHeaderHidden(True)
53 self.__callStackList.setAlternatingRowColors(True)
54 self.__callStackList.setItemsExpandable(False)
55 self.__callStackList.setRootIsDecorated(False)
45 self.setWindowTitle(self.tr("Call Stack")) 56 self.setWindowTitle(self.tr("Call Stack"))
46 57
47 self.__menu = QMenu(self) 58 self.__menu = QMenu(self.__callStackList)
48 self.__sourceAct = self.__menu.addAction( 59 self.__sourceAct = self.__menu.addAction(
49 self.tr("Show source"), self.__openSource) 60 self.tr("Show source"), self.__openSource)
50 self.__menu.addAction(self.tr("Clear"), self.clear) 61 self.__menu.addAction(self.tr("Clear"), self.__callStackList.clear)
51 self.__menu.addSeparator() 62 self.__menu.addSeparator()
52 self.__menu.addAction(self.tr("Save"), self.__saveStackTrace) 63 self.__menu.addAction(self.tr("Save"), self.__saveStackTrace)
53 self.setContextMenuPolicy(Qt.CustomContextMenu) 64 self.__callStackList.setContextMenuPolicy(Qt.CustomContextMenu)
54 self.customContextMenuRequested.connect(self.__showContextMenu) 65 self.__callStackList.customContextMenuRequested.connect(
66 self.__showContextMenu)
55 67
56 self.__dbs = debugServer 68 self.__dbs = debugServer
57 69
58 # file name, line number, function name, arguments 70 # file name, line number, function name, arguments
59 self.__entryFormat = self.tr("File: {0}\nLine: {1}\n{2}{3}") 71 self.__entryFormat = self.tr("File: {0}\nLine: {1}\n{2}{3}")
62 74
63 self.__projectMode = False 75 self.__projectMode = False
64 self.__project = None 76 self.__project = None
65 77
66 self.__dbs.clientStack.connect(self.__showCallStack) 78 self.__dbs.clientStack.connect(self.__showCallStack)
67 self.itemDoubleClicked.connect(self.__itemDoubleClicked) 79 self.__callStackList.itemDoubleClicked.connect(
80 self.__itemDoubleClicked)
68 81
69 def setDebugger(self, debugUI): 82 def setDebugger(self, debugUI):
70 """ 83 """
71 Public method to set a reference to the Debug UI. 84 Public method to set a reference to the Debug UI.
72 85
73 @param debugUI reference to the DebugUI object (DebugUI) 86 @param debugUI reference to the DebugUI object
87 @type DebugUI
74 """ 88 """
75 debugUI.clientStack.connect(self.__showCallStack) 89 debugUI.clientStack.connect(self.__showCallStack)
76 90
77 def setProjectMode(self, enabled): 91 def setProjectMode(self, enabled):
78 """ 92 """
79 Public slot to set the call trace viewer to project mode. 93 Public slot to set the call trace viewer to project mode.
80 94
81 In project mode the call trace info is shown with project relative 95 In project mode the call trace info is shown with project relative
82 path names. 96 path names.
83 97
84 @param enabled flag indicating to enable the project mode (boolean) 98 @param enabled flag indicating to enable the project mode
99 @type bool
85 """ 100 """
86 self.__projectMode = enabled 101 self.__projectMode = enabled
87 if enabled and self.__project is None: 102 if enabled and self.__project is None:
88 self.__project = e5App().getObject("Project") 103 self.__project = e5App().getObject("Project")
89 104
90 def __showContextMenu(self, coord): 105 def __showContextMenu(self, coord):
91 """ 106 """
92 Private slot to show the context menu. 107 Private slot to show the context menu.
93 108
94 @param coord the position of the mouse pointer (QPoint) 109 @param coord the position of the mouse pointer
95 """ 110 @type QPoint
96 if self.topLevelItemCount() > 0: 111 """
97 itm = self.currentItem() 112 if self.__callStackList.topLevelItemCount() > 0:
113 itm = self.__callStackList.currentItem()
98 self.__sourceAct.setEnabled(itm is not None) 114 self.__sourceAct.setEnabled(itm is not None)
99 self.__menu.popup(self.mapToGlobal(coord)) 115 self.__menu.popup(self.__callStackList.mapToGlobal(coord))
100 116
101 def __showCallStack(self, stack): 117 def clear(self):
118 """
119 Public method to clear the stack viewer data.
120 """
121 self.__debuggerLabel.clear()
122 self.__callStackList.clear()
123
124 def __showCallStack(self, stack, debuggerId):
102 """ 125 """
103 Private slot to show the call stack of the program being debugged. 126 Private slot to show the call stack of the program being debugged.
104 127
105 @param stack list of tuples with call stack data (file name, 128 @param stack list of tuples with call stack data (file name,
106 line number, function name, formatted argument/values list) 129 line number, function name, formatted argument/values list)
107 """ 130 @type list of tuples of (str, str, str, str)
108 self.clear() 131 @param debuggerId ID of the debugger backend
132 @type str
133 """
134 self.__debuggerLabel.setText(debuggerId)
135
136 self.__callStackList.clear()
109 for fname, fline, ffunc, fargs in stack: 137 for fname, fline, ffunc, fargs in stack:
110 if self.__projectMode: 138 if self.__projectMode:
111 dfname = self.__project.getRelativePath(fname) 139 dfname = self.__project.getRelativePath(fname)
112 else: 140 else:
113 dfname = fname 141 dfname = fname
114 if ffunc and not ffunc.startswith("<"): 142 if ffunc and not ffunc.startswith("<"):
115 # use normal format 143 # use normal format
116 itm = QTreeWidgetItem( 144 itm = QTreeWidgetItem(
117 self, 145 self.__callStackList,
118 [self.__entryFormat.format(dfname, fline, ffunc, fargs)]) 146 [self.__entryFormat.format(dfname, fline, ffunc, fargs)]
147 )
119 else: 148 else:
120 # use short format 149 # use short format
121 itm = QTreeWidgetItem( 150 itm = QTreeWidgetItem(
122 self, [self.__entryFormatShort.format(dfname, fline)]) 151 self.__callStackList,
152 [self.__entryFormatShort.format(dfname, fline)]
153 )
123 itm.setData(0, self.FilenameRole, fname) 154 itm.setData(0, self.FilenameRole, fname)
124 itm.setData(0, self.LinenoRole, fline) 155 itm.setData(0, self.LinenoRole, fline)
125 156
126 self.resizeColumnToContents(0) 157 self.__callStackList.resizeColumnToContents(0)
127 158
128 def __itemDoubleClicked(self, itm): 159 def __itemDoubleClicked(self, itm):
129 """ 160 """
130 Private slot to handle a double click of a stack entry. 161 Private slot to handle a double click of a stack entry.
131 162
132 @param itm reference to the double clicked item (QTreeWidgetItem) 163 @param itm reference to the double clicked item
164 @type QTreeWidgetItem
133 """ 165 """
134 fname = itm.data(0, self.FilenameRole) 166 fname = itm.data(0, self.FilenameRole)
135 fline = itm.data(0, self.LinenoRole) 167 fline = itm.data(0, self.LinenoRole)
136 if self.__projectMode: 168 if self.__projectMode:
137 fname = self.__project.getAbsolutePath(fname) 169 fname = self.__project.getAbsolutePath(fname)
138 self.sourceFile.emit(fname, fline) 170 self.sourceFile.emit(fname, fline)
139 171
140 index = self.indexOfTopLevelItem(itm) 172 index = self.__callStackList.indexOfTopLevelItem(itm)
141 self.frameSelected.emit(index) 173 self.frameSelected.emit(index)
142 174
143 def __openSource(self): 175 def __openSource(self):
144 """ 176 """
145 Private slot to show the source for the selected stack entry. 177 Private slot to show the source for the selected stack entry.
146 """ 178 """
147 itm = self.currentItem() 179 itm = self.__callStackList.currentItem()
148 if itm: 180 if itm:
149 self.__itemDoubleClicked(itm) 181 self.__itemDoubleClicked(itm)
150 182
151 def __saveStackTrace(self): 183 def __saveStackTrace(self):
152 """ 184 """
153 Private slot to save the stack trace info to a file. 185 Private slot to save the stack trace info to a file.
154 """ 186 """
155 if self.topLevelItemCount() > 0: 187 if self.__callStackList.topLevelItemCount() > 0:
156 fname, selectedFilter = E5FileDialog.getSaveFileNameAndFilter( 188 fname, selectedFilter = E5FileDialog.getSaveFileNameAndFilter(
157 self, 189 self,
158 self.tr("Save Call Stack Info"), 190 self.tr("Save Call Stack Info"),
159 "", 191 "",
160 self.tr("Text Files (*.txt);;All Files (*)"), 192 self.tr("Text Files (*.txt);;All Files (*)"),
176 if not res: 208 if not res:
177 return 209 return
178 fname = Utilities.toNativeSeparators(fname) 210 fname = Utilities.toNativeSeparators(fname)
179 211
180 try: 212 try:
213 title = self.tr("Call Stack of '{0}'").format(
214 self.__debuggerLabel.text())
181 with open(fname, "w", encoding="utf-8") as f: 215 with open(fname, "w", encoding="utf-8") as f:
182 itm = self.topLevelItem(0) 216 f.write("{0}\n".format(title))
217 f.write("{0}\n\n".format(len(title) * "="))
218 itm = self.__callStackList.topLevelItem(0)
183 while itm is not None: 219 while itm is not None:
184 f.write("{0}\n".format(itm.text(0))) 220 f.write("{0}\n".format(itm.text(0)))
185 f.write(78 * "=" + "\n") 221 f.write("{0}\n".format(78 * "="))
186 itm = self.itemBelow(itm) 222 itm = self.__callStackList.itemBelow(itm)
187 except OSError as err: 223 except OSError as err:
188 E5MessageBox.critical( 224 E5MessageBox.critical(
189 self, 225 self,
190 self.tr("Error saving Call Stack Info"), 226 self.tr("Error saving Call Stack Info"),
191 self.tr("""<p>The call stack info could not be""" 227 self.tr("""<p>The call stack info could not be"""

eric ide

mercurial