eric6/Debugger/CallStackViewer.py

changeset 7927
866ddf957461
parent 7923
91e843545d9a
child 8043
0acf98cd089a
child 8143
2c730d5fd177
equal deleted inserted replaced
7926:5357e5ffebf1 7927:866ddf957461
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 @type list of tuples of (str, str, str, str) 130 @type list of tuples of (str, str, str, str)
108 """ 131 @param debuggerId ID of the debugger backend
109 self.clear() 132 @type str
133 """
134 self.__debuggerLabel.setText(debuggerId)
135
136 self.__callStackList.clear()
110 for fname, fline, ffunc, fargs in stack: 137 for fname, fline, ffunc, fargs in stack:
111 if self.__projectMode: 138 if self.__projectMode:
112 dfname = self.__project.getRelativePath(fname) 139 dfname = self.__project.getRelativePath(fname)
113 else: 140 else:
114 dfname = fname 141 dfname = fname
115 if ffunc and not ffunc.startswith("<"): 142 if ffunc and not ffunc.startswith("<"):
116 # use normal format 143 # use normal format
117 itm = QTreeWidgetItem( 144 itm = QTreeWidgetItem(
118 self, 145 self.__callStackList,
119 [self.__entryFormat.format(dfname, fline, ffunc, fargs)]) 146 [self.__entryFormat.format(dfname, fline, ffunc, fargs)]
147 )
120 else: 148 else:
121 # use short format 149 # use short format
122 itm = QTreeWidgetItem( 150 itm = QTreeWidgetItem(
123 self, [self.__entryFormatShort.format(dfname, fline)]) 151 self.__callStackList,
152 [self.__entryFormatShort.format(dfname, fline)]
153 )
124 itm.setData(0, self.FilenameRole, fname) 154 itm.setData(0, self.FilenameRole, fname)
125 itm.setData(0, self.LinenoRole, fline) 155 itm.setData(0, self.LinenoRole, fline)
126 156
127 self.resizeColumnToContents(0) 157 self.__callStackList.resizeColumnToContents(0)
128 158
129 def __itemDoubleClicked(self, itm): 159 def __itemDoubleClicked(self, itm):
130 """ 160 """
131 Private slot to handle a double click of a stack entry. 161 Private slot to handle a double click of a stack entry.
132 162
133 @param itm reference to the double clicked item (QTreeWidgetItem) 163 @param itm reference to the double clicked item
164 @type QTreeWidgetItem
134 """ 165 """
135 fname = itm.data(0, self.FilenameRole) 166 fname = itm.data(0, self.FilenameRole)
136 fline = itm.data(0, self.LinenoRole) 167 fline = itm.data(0, self.LinenoRole)
137 if self.__projectMode: 168 if self.__projectMode:
138 fname = self.__project.getAbsolutePath(fname) 169 fname = self.__project.getAbsolutePath(fname)
139 self.sourceFile.emit(fname, fline) 170 self.sourceFile.emit(fname, fline)
140 171
141 index = self.indexOfTopLevelItem(itm) 172 index = self.__callStackList.indexOfTopLevelItem(itm)
142 self.frameSelected.emit(index) 173 self.frameSelected.emit(index)
143 174
144 def __openSource(self): 175 def __openSource(self):
145 """ 176 """
146 Private slot to show the source for the selected stack entry. 177 Private slot to show the source for the selected stack entry.
147 """ 178 """
148 itm = self.currentItem() 179 itm = self.__callStackList.currentItem()
149 if itm: 180 if itm:
150 self.__itemDoubleClicked(itm) 181 self.__itemDoubleClicked(itm)
151 182
152 def __saveStackTrace(self): 183 def __saveStackTrace(self):
153 """ 184 """
154 Private slot to save the stack trace info to a file. 185 Private slot to save the stack trace info to a file.
155 """ 186 """
156 if self.topLevelItemCount() > 0: 187 if self.__callStackList.topLevelItemCount() > 0:
157 fname, selectedFilter = E5FileDialog.getSaveFileNameAndFilter( 188 fname, selectedFilter = E5FileDialog.getSaveFileNameAndFilter(
158 self, 189 self,
159 self.tr("Save Call Stack Info"), 190 self.tr("Save Call Stack Info"),
160 "", 191 "",
161 self.tr("Text Files (*.txt);;All Files (*)"), 192 self.tr("Text Files (*.txt);;All Files (*)"),
177 if not res: 208 if not res:
178 return 209 return
179 fname = Utilities.toNativeSeparators(fname) 210 fname = Utilities.toNativeSeparators(fname)
180 211
181 try: 212 try:
213 title = self.tr("Call Stack of '{0}'").format(
214 self.__debuggerLabel.text())
182 with open(fname, "w", encoding="utf-8") as f: 215 with open(fname, "w", encoding="utf-8") as f:
183 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)
184 while itm is not None: 219 while itm is not None:
185 f.write("{0}\n".format(itm.text(0))) 220 f.write("{0}\n".format(itm.text(0)))
186 f.write(78 * "=" + "\n") 221 f.write("{0}\n".format(78 * "="))
187 itm = self.itemBelow(itm) 222 itm = self.__callStackList.itemBelow(itm)
188 except OSError as err: 223 except OSError as err:
189 E5MessageBox.critical( 224 E5MessageBox.critical(
190 self, 225 self,
191 self.tr("Error saving Call Stack Info"), 226 self.tr("Error saving Call Stack Info"),
192 self.tr("""<p>The call stack info could not be""" 227 self.tr("""<p>The call stack info could not be"""

eric ide

mercurial