Debugger/CallStackViewer.py

changeset 2622
08cc2f31c983
child 2677
3d4277929fb3
child 2732
c14fe50c113e
equal deleted inserted replaced
2621:ab5918079c38 2622:08cc2f31c983
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2013 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the Call Stack viewer widget.
8 """
9
10 from PyQt4.QtCore import pyqtSignal, Qt, QFileInfo
11 from PyQt4.QtGui import QTreeWidget, QTreeWidgetItem, QMenu
12
13 from E5Gui.E5Application import e5App
14 from E5Gui import E5FileDialog, E5MessageBox
15
16 import Utilities
17
18
19 class CallStackViewer(QTreeWidget):
20 """
21 Class implementing the Call Stack viewer widget.
22
23 @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
25 """
26 sourceFile = pyqtSignal(str, int)
27 frameSelected = pyqtSignal(int)
28
29 FilenameRole = Qt.UserRole + 1
30 LinenoRole = Qt.UserRole + 2
31
32 def __init__(self, debugServer, parent=None):
33 """
34 Constructor
35
36 @param debugServer reference to the debug server object (DebugServer)
37 @param parent reference to the parent widget (QWidget)
38 """
39 super().__init__(parent)
40
41 self.setHeaderHidden(True)
42 self.setAlternatingRowColors(True)
43 self.setItemsExpandable(False)
44 self.setRootIsDecorated(False)
45
46 self.__menu = QMenu(self)
47 self.__sourceAct = self.__menu.addAction(
48 self.trUtf8("Show source"), self.__openSource)
49 self.__menu.addAction(self.trUtf8("Clear"), self.clear)
50 self.__menu.addSeparator()
51 self.__menu.addAction(self.trUtf8("Save"), self.__saveStackTrace)
52 self.setContextMenuPolicy(Qt.CustomContextMenu)
53 self.customContextMenuRequested.connect(self.__showContextMenu)
54
55 self.__dbs = debugServer
56
57 # file name, line number, function name, arguments
58 self.__entryFormat = self.trUtf8("File: {0}\nLine: {1}\n{2}{3}")
59 # file name, line number
60 self.__entryFormatShort = self.trUtf8("File: {0}\nLine: {1}")
61
62 self.__projectMode = False
63 self.__project = None
64
65 self.__dbs.clientStack.connect(self.__showCallStack)
66 self.itemDoubleClicked.connect(self.__itemDoubleClicked)
67
68 def setDebugger(self, debugUI):
69 """
70 Public method to set a reference to the Debug UI.
71
72 @param debugUI reference to the DebugUI object (DebugUI)
73 """
74 debugUI.clientStack.connect(self.__showCallStack)
75
76 def setProjectMode(self, enabled):
77 """
78 Public slot to set the call trace viewer to project mode.
79
80 In project mode the call trace info is shown with project relative
81 path names.
82
83 @param enabled flag indicating to enable the project mode (boolean)
84 """
85 self.__projectMode = enabled
86 if enabled and self.__project is None:
87 self.__project = e5App().getObject("Project")
88
89 def __showContextMenu(self, coord):
90 """
91 Private slot to show the context menu.
92
93 @param coord the position of the mouse pointer (QPoint)
94 """
95 if self.topLevelItemCount() > 0:
96 itm = self.currentItem()
97 self.__sourceAct.setEnabled(itm is not None)
98 self.__menu.popup(self.mapToGlobal(coord))
99
100 def __showCallStack(self, stack):
101 """
102 Public slot to show the call stack of the program being debugged.
103
104 @param stack list of tuples with call stack data (file name, line number,
105 function name, formatted argument/values list)
106 """
107 self.clear()
108 for fname, fline, ffunc, fargs in stack:
109 if self.__projectMode:
110 dfname = self.__project.getRelativePath(fname)
111 else:
112 dfname = fname
113 if ffunc and not ffunc.startswith("<"):
114 # use normal format
115 itm = QTreeWidgetItem(self,
116 [self.__entryFormat.format(dfname, fline, ffunc, fargs)])
117 else:
118 # use short format
119 itm = QTreeWidgetItem(self,
120 [self.__entryFormatShort.format(dfname, fline)])
121 itm.setData(0, self.FilenameRole, fname)
122 itm.setData(0, self.LinenoRole, fline)
123
124 self.resizeColumnToContents(0)
125
126 def __itemDoubleClicked(self, itm):
127 """
128 Private slot to handle a double click of a stack entry.
129
130 @param itm reference to the double clicked item (QTreeWidgetItem)
131 """
132 fname = itm.data(0, self.FilenameRole)
133 fline = itm.data(0, self.LinenoRole)
134 if self.__projectMode:
135 fname = self.__project.getAbsolutePath(fname)
136 self.sourceFile.emit(fname, fline)
137
138 index = self.indexOfTopLevelItem(itm)
139 self.frameSelected.emit(index)
140
141 def __openSource(self):
142 """
143 Private slot to show the source for the selected stack entry.
144 """
145 itm = self.currentItem()
146 if itm:
147 self.__itemDoubleClicked(itm)
148
149 def __saveStackTrace(self):
150 """
151 Private slot to save the stack trace info to a file.
152 """
153 if self.topLevelItemCount() > 0:
154 fname, selectedFilter = E5FileDialog.getSaveFileNameAndFilter(
155 self,
156 self.trUtf8("Save Call Stack Info"),
157 "",
158 self.trUtf8("Text Files (*.txt);;All Files (*)"),
159 None,
160 E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
161 if fname:
162 ext = QFileInfo(fname).suffix()
163 if not ext:
164 ex = selectedFilter.split("(*")[1].split(")")[0]
165 if ex:
166 fname += ex
167 if QFileInfo(fname).exists():
168 res = E5MessageBox.yesNo(self,
169 self.trUtf8("Save Call Stack Info"),
170 self.trUtf8("<p>The file <b>{0}</b> already exists."
171 " Overwrite it?</p>").format(fname),
172 icon=E5MessageBox.Warning)
173 if not res:
174 return
175 fname = Utilities.toNativeSeparators(fname)
176
177 try:
178 f = open(fname, "w", encoding="utf-8")
179 itm = self.topLevelItem(0)
180 while itm is not None:
181 f.write("{0}\n".format(itm.text(0)))
182 f.write(78 * "=" + "\n")
183 itm = self.itemBelow(itm)
184 f.close()
185 except IOError as err:
186 E5MessageBox.critical(self,
187 self.trUtf8("Error saving Call Stack Info"),
188 self.trUtf8("""<p>The call stack info could not be written"""
189 """ to <b>{0}</b></p><p>Reason: {1}</p>""")\
190 .format(fname, str(err)))

eric ide

mercurial