9 |
9 |
10 import pathlib |
10 import pathlib |
11 |
11 |
12 from PyQt6.QtCore import pyqtSignal, Qt |
12 from PyQt6.QtCore import pyqtSignal, Qt |
13 from PyQt6.QtWidgets import ( |
13 from PyQt6.QtWidgets import ( |
14 QTreeWidget, QTreeWidgetItem, QMenu, QWidget, QVBoxLayout, QLabel |
14 QTreeWidget, |
|
15 QTreeWidgetItem, |
|
16 QMenu, |
|
17 QWidget, |
|
18 QVBoxLayout, |
|
19 QLabel, |
15 ) |
20 ) |
16 |
21 |
17 from EricWidgets.EricApplication import ericApp |
22 from EricWidgets.EricApplication import ericApp |
18 from EricWidgets import EricFileDialog, EricMessageBox |
23 from EricWidgets import EricFileDialog, EricMessageBox |
19 |
24 |
20 |
25 |
21 class CallStackViewer(QWidget): |
26 class CallStackViewer(QWidget): |
22 """ |
27 """ |
23 Class implementing the Call Stack viewer widget. |
28 Class implementing the Call Stack viewer widget. |
24 |
29 |
25 @signal sourceFile(str, int) emitted to show the source of a stack entry |
30 @signal sourceFile(str, int) emitted to show the source of a stack entry |
26 @signal frameSelected(int) emitted to signal the selection of a frame entry |
31 @signal frameSelected(int) emitted to signal the selection of a frame entry |
27 """ |
32 """ |
|
33 |
28 sourceFile = pyqtSignal(str, int) |
34 sourceFile = pyqtSignal(str, int) |
29 frameSelected = pyqtSignal(int) |
35 frameSelected = pyqtSignal(int) |
30 |
36 |
31 FilenameRole = Qt.ItemDataRole.UserRole + 1 |
37 FilenameRole = Qt.ItemDataRole.UserRole + 1 |
32 LinenoRole = Qt.ItemDataRole.UserRole + 2 |
38 LinenoRole = Qt.ItemDataRole.UserRole + 2 |
33 |
39 |
34 def __init__(self, debugServer, parent=None): |
40 def __init__(self, debugServer, parent=None): |
35 """ |
41 """ |
36 Constructor |
42 Constructor |
37 |
43 |
38 @param debugServer reference to the debug server object |
44 @param debugServer reference to the debug server object |
39 @type DebugServer |
45 @type DebugServer |
40 @param parent reference to the parent widget |
46 @param parent reference to the parent widget |
41 @type QWidget |
47 @type QWidget |
42 """ |
48 """ |
43 super().__init__(parent) |
49 super().__init__(parent) |
44 |
50 |
45 self.__layout = QVBoxLayout(self) |
51 self.__layout = QVBoxLayout(self) |
46 self.setLayout(self.__layout) |
52 self.setLayout(self.__layout) |
47 self.__debuggerLabel = QLabel(self) |
53 self.__debuggerLabel = QLabel(self) |
48 self.__layout.addWidget(self.__debuggerLabel) |
54 self.__layout.addWidget(self.__debuggerLabel) |
49 self.__callStackList = QTreeWidget(self) |
55 self.__callStackList = QTreeWidget(self) |
50 self.__layout.addWidget(self.__callStackList) |
56 self.__layout.addWidget(self.__callStackList) |
51 |
57 |
52 self.__callStackList.setHeaderHidden(True) |
58 self.__callStackList.setHeaderHidden(True) |
53 self.__callStackList.setAlternatingRowColors(True) |
59 self.__callStackList.setAlternatingRowColors(True) |
54 self.__callStackList.setItemsExpandable(False) |
60 self.__callStackList.setItemsExpandable(False) |
55 self.__callStackList.setRootIsDecorated(False) |
61 self.__callStackList.setRootIsDecorated(False) |
56 self.setWindowTitle(self.tr("Call Stack")) |
62 self.setWindowTitle(self.tr("Call Stack")) |
57 |
63 |
58 self.__menu = QMenu(self.__callStackList) |
64 self.__menu = QMenu(self.__callStackList) |
59 self.__sourceAct = self.__menu.addAction( |
65 self.__sourceAct = self.__menu.addAction( |
60 self.tr("Show source"), self.__openSource) |
66 self.tr("Show source"), self.__openSource |
|
67 ) |
61 self.__menu.addAction(self.tr("Clear"), self.__callStackList.clear) |
68 self.__menu.addAction(self.tr("Clear"), self.__callStackList.clear) |
62 self.__menu.addSeparator() |
69 self.__menu.addSeparator() |
63 self.__menu.addAction(self.tr("Save"), self.__saveStackTrace) |
70 self.__menu.addAction(self.tr("Save"), self.__saveStackTrace) |
64 self.__callStackList.setContextMenuPolicy( |
71 self.__callStackList.setContextMenuPolicy( |
65 Qt.ContextMenuPolicy.CustomContextMenu) |
72 Qt.ContextMenuPolicy.CustomContextMenu |
66 self.__callStackList.customContextMenuRequested.connect( |
73 ) |
67 self.__showContextMenu) |
74 self.__callStackList.customContextMenuRequested.connect(self.__showContextMenu) |
68 |
75 |
69 self.__dbs = debugServer |
76 self.__dbs = debugServer |
70 |
77 |
71 # file name, line number, function name, arguments |
78 # file name, line number, function name, arguments |
72 self.__entryFormat = self.tr("File: {0}\nLine: {1}\n{2}{3}") |
79 self.__entryFormat = self.tr("File: {0}\nLine: {1}\n{2}{3}") |
73 # file name, line number |
80 # file name, line number |
74 self.__entryFormatShort = self.tr("File: {0}\nLine: {1}") |
81 self.__entryFormatShort = self.tr("File: {0}\nLine: {1}") |
75 |
82 |
76 self.__projectMode = False |
83 self.__projectMode = False |
77 self.__project = None |
84 self.__project = None |
78 |
85 |
79 self.__dbs.clientStack.connect(self.__showCallStack) |
86 self.__dbs.clientStack.connect(self.__showCallStack) |
80 self.__callStackList.itemDoubleClicked.connect( |
87 self.__callStackList.itemDoubleClicked.connect(self.__itemDoubleClicked) |
81 self.__itemDoubleClicked) |
88 |
82 |
|
83 def setDebugger(self, debugUI): |
89 def setDebugger(self, debugUI): |
84 """ |
90 """ |
85 Public method to set a reference to the Debug UI. |
91 Public method to set a reference to the Debug UI. |
86 |
92 |
87 @param debugUI reference to the DebugUI object |
93 @param debugUI reference to the DebugUI object |
88 @type DebugUI |
94 @type DebugUI |
89 """ |
95 """ |
90 debugUI.clientStack.connect(self.__showCallStack) |
96 debugUI.clientStack.connect(self.__showCallStack) |
91 |
97 |
92 def setProjectMode(self, enabled): |
98 def setProjectMode(self, enabled): |
93 """ |
99 """ |
94 Public slot to set the call trace viewer to project mode. |
100 Public slot to set the call trace viewer to project mode. |
95 |
101 |
96 In project mode the call trace info is shown with project relative |
102 In project mode the call trace info is shown with project relative |
97 path names. |
103 path names. |
98 |
104 |
99 @param enabled flag indicating to enable the project mode |
105 @param enabled flag indicating to enable the project mode |
100 @type bool |
106 @type bool |
101 """ |
107 """ |
102 self.__projectMode = enabled |
108 self.__projectMode = enabled |
103 if enabled and self.__project is None: |
109 if enabled and self.__project is None: |
104 self.__project = ericApp().getObject("Project") |
110 self.__project = ericApp().getObject("Project") |
105 |
111 |
106 def __showContextMenu(self, coord): |
112 def __showContextMenu(self, coord): |
107 """ |
113 """ |
108 Private slot to show the context menu. |
114 Private slot to show the context menu. |
109 |
115 |
110 @param coord the position of the mouse pointer |
116 @param coord the position of the mouse pointer |
111 @type QPoint |
117 @type QPoint |
112 """ |
118 """ |
113 if self.__callStackList.topLevelItemCount() > 0: |
119 if self.__callStackList.topLevelItemCount() > 0: |
114 itm = self.__callStackList.currentItem() |
120 itm = self.__callStackList.currentItem() |
115 self.__sourceAct.setEnabled(itm is not None) |
121 self.__sourceAct.setEnabled(itm is not None) |
116 self.__menu.popup(self.__callStackList.mapToGlobal(coord)) |
122 self.__menu.popup(self.__callStackList.mapToGlobal(coord)) |
117 |
123 |
118 def clear(self): |
124 def clear(self): |
119 """ |
125 """ |
120 Public method to clear the stack viewer data. |
126 Public method to clear the stack viewer data. |
121 """ |
127 """ |
122 self.__debuggerLabel.clear() |
128 self.__debuggerLabel.clear() |
123 self.__callStackList.clear() |
129 self.__callStackList.clear() |
124 |
130 |
125 def __showCallStack(self, stack, debuggerId): |
131 def __showCallStack(self, stack, debuggerId): |
126 """ |
132 """ |
127 Private slot to show the call stack of the program being debugged. |
133 Private slot to show the call stack of the program being debugged. |
128 |
134 |
129 @param stack list of tuples with call stack data (file name, |
135 @param stack list of tuples with call stack data (file name, |
130 line number, function name, formatted argument/values list) |
136 line number, function name, formatted argument/values list) |
131 @type list of tuples of (str, str, str, str) |
137 @type list of tuples of (str, str, str, str) |
132 @param debuggerId ID of the debugger backend |
138 @param debuggerId ID of the debugger backend |
133 @type str |
139 @type str |
134 """ |
140 """ |
135 self.__debuggerLabel.setText(debuggerId) |
141 self.__debuggerLabel.setText(debuggerId) |
136 |
142 |
137 self.__callStackList.clear() |
143 self.__callStackList.clear() |
138 for fname, fline, ffunc, fargs in stack: |
144 for fname, fline, ffunc, fargs in stack: |
139 dfname = ( |
145 dfname = ( |
140 self.__project.getRelativePath(fname) |
146 self.__project.getRelativePath(fname) if self.__projectMode else fname |
141 if self.__projectMode else |
|
142 fname |
|
143 ) |
147 ) |
144 itm = ( |
148 itm = ( |
145 # use normal format |
149 # use normal format |
146 QTreeWidgetItem( |
150 QTreeWidgetItem( |
147 self.__callStackList, |
151 self.__callStackList, |
148 [self.__entryFormat.format(dfname, fline, ffunc, fargs)] |
152 [self.__entryFormat.format(dfname, fline, ffunc, fargs)], |
149 ) |
153 ) |
150 if ffunc and not ffunc.startswith("<") else |
154 if ffunc and not ffunc.startswith("<") |
|
155 else |
151 # use short format |
156 # use short format |
152 QTreeWidgetItem( |
157 QTreeWidgetItem( |
153 self.__callStackList, |
158 self.__callStackList, |
154 [self.__entryFormatShort.format(dfname, fline)] |
159 [self.__entryFormatShort.format(dfname, fline)], |
155 ) |
160 ) |
156 ) |
161 ) |
157 itm.setData(0, self.FilenameRole, fname) |
162 itm.setData(0, self.FilenameRole, fname) |
158 itm.setData(0, self.LinenoRole, fline) |
163 itm.setData(0, self.LinenoRole, fline) |
159 |
164 |
160 self.__callStackList.resizeColumnToContents(0) |
165 self.__callStackList.resizeColumnToContents(0) |
161 |
166 |
162 def __itemDoubleClicked(self, itm): |
167 def __itemDoubleClicked(self, itm): |
163 """ |
168 """ |
164 Private slot to handle a double click of a stack entry. |
169 Private slot to handle a double click of a stack entry. |
165 |
170 |
166 @param itm reference to the double clicked item |
171 @param itm reference to the double clicked item |
167 @type QTreeWidgetItem |
172 @type QTreeWidgetItem |
168 """ |
173 """ |
169 fname = itm.data(0, self.FilenameRole) |
174 fname = itm.data(0, self.FilenameRole) |
170 fline = itm.data(0, self.LinenoRole) |
175 fline = itm.data(0, self.LinenoRole) |
171 if self.__projectMode: |
176 if self.__projectMode: |
172 fname = self.__project.getAbsolutePath(fname) |
177 fname = self.__project.getAbsolutePath(fname) |
173 self.sourceFile.emit(fname, fline) |
178 self.sourceFile.emit(fname, fline) |
174 |
179 |
175 index = self.__callStackList.indexOfTopLevelItem(itm) |
180 index = self.__callStackList.indexOfTopLevelItem(itm) |
176 self.frameSelected.emit(index) |
181 self.frameSelected.emit(index) |
177 |
182 |
178 def __openSource(self): |
183 def __openSource(self): |
179 """ |
184 """ |
180 Private slot to show the source for the selected stack entry. |
185 Private slot to show the source for the selected stack entry. |
181 """ |
186 """ |
182 itm = self.__callStackList.currentItem() |
187 itm = self.__callStackList.currentItem() |
183 if itm: |
188 if itm: |
184 self.__itemDoubleClicked(itm) |
189 self.__itemDoubleClicked(itm) |
185 |
190 |
186 def __saveStackTrace(self): |
191 def __saveStackTrace(self): |
187 """ |
192 """ |
188 Private slot to save the stack trace info to a file. |
193 Private slot to save the stack trace info to a file. |
189 """ |
194 """ |
190 if self.__callStackList.topLevelItemCount() > 0: |
195 if self.__callStackList.topLevelItemCount() > 0: |