Debugger/CallStackViewer.py

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

eric ide

mercurial