Debugger/DebugViewer.py

changeset 0
de9c2efb9d02
child 7
c679fb30c8f3
equal deleted inserted replaced
-1:000000000000 0:de9c2efb9d02
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2002 - 2009 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a widget containing various debug related views.
8
9 The views avaliable are:
10 <ul>
11 <li>variables viewer for global variables</li>
12 <li>variables viewer for local variables</li>
13 <li>viewer for breakpoints</li>
14 <li>viewer for watch expressions</li>
15 <li>viewer for exceptions</li>
16 <li>viewer for threads</li>
17 <li>a file browser (optional)</li>
18 <li>an interpreter shell (optional)</li>
19 </ul>
20 """
21
22 import os
23
24 from PyQt4.QtCore import *
25 from PyQt4.QtGui import *
26
27 from QScintilla.Shell import Shell
28 from VariablesViewer import VariablesViewer
29 from ExceptionLogger import ExceptionLogger
30 from BreakPointViewer import BreakPointViewer
31 from WatchPointViewer import WatchPointViewer
32
33 import Utilities
34 import UI.PixmapCache
35
36 from E4Gui.E4TabWidget import E4TabWidget
37
38 class DebugViewer(QWidget):
39 """
40 Class implementing a widget conatining various debug related views.
41
42 The individual tabs contain the interpreter shell (optional),
43 the filesystem browser (optional), the two variables viewers (global and local),
44 a breakpoint viewer, a watch expression viewer and the exception logger. Additionally
45 a list of all threads is shown.
46
47 @signal sourceFile(string, int) emitted to open a source file at a line
48 """
49 def __init__(self, debugServer, docked, vm, parent = None,
50 embeddedShell = True, embeddedBrowser = True):
51 """
52 Constructor
53
54 @param debugServer reference to the debug server object
55 @param docked flag indicating a dock window
56 @param vm reference to the viewmanager object
57 @param parent parent widget (QWidget)
58 @param embeddedShell flag indicating whether the shell should be included.
59 This flag is set to False by those layouts, that have the interpreter
60 shell in a separate window.
61 @param embeddedBrowser flag indicating whether the file browser should
62 be included. This flag is set to False by those layouts, that
63 have the file browser in a separate window or embedded
64 in the project browser instead.
65 """
66 QWidget.__init__(self, parent)
67
68 self.debugServer = debugServer
69 self.debugUI = None
70
71 self.setWindowIcon(UI.PixmapCache.getIcon("eric.png"))
72
73 self.__mainLayout = QVBoxLayout()
74 self.__mainLayout.setMargin(0)
75 self.setLayout(self.__mainLayout)
76
77 self.__tabWidget = E4TabWidget()
78 self.__mainLayout.addWidget(self.__tabWidget)
79
80 self.embeddedShell = embeddedShell
81 if embeddedShell:
82 # add the interpreter shell
83 self.shell = Shell(debugServer, vm)
84 index = self.__tabWidget.addTab(self.shell,
85 UI.PixmapCache.getIcon("shell.png"), '')
86 self.__tabWidget.setTabToolTip(index, self.shell.windowTitle())
87
88 self.embeddedBrowser = embeddedBrowser
89 if embeddedBrowser:
90 from UI.Browser import Browser
91 # add the browser
92 self.browser = Browser()
93 index = self.__tabWidget.addTab(self.browser,
94 UI.PixmapCache.getIcon("browser.png"), '')
95 self.__tabWidget.setTabToolTip(index, self.browser.windowTitle())
96
97 # add the global variables viewer
98 self.glvWidget = QWidget()
99 self.glvWidgetVLayout = QVBoxLayout(self.glvWidget)
100 self.glvWidgetVLayout.setMargin(0)
101 self.glvWidgetVLayout.setSpacing(3)
102 self.glvWidget.setLayout(self.glvWidgetVLayout)
103
104 self.globalsViewer = VariablesViewer(self.glvWidget, True)
105 self.glvWidgetVLayout.addWidget(self.globalsViewer)
106
107 self.glvWidgetHLayout = QHBoxLayout()
108 self.glvWidgetHLayout.setMargin(3)
109
110 self.globalsFilterEdit = QLineEdit(self.glvWidget)
111 self.globalsFilterEdit.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
112 self.glvWidgetHLayout.addWidget(self.globalsFilterEdit)
113 self.globalsFilterEdit.setToolTip(\
114 self.trUtf8("Enter regular expression patterns separated by ';' to define "
115 "variable filters. "))
116 self.globalsFilterEdit.setWhatsThis(\
117 self.trUtf8("Enter regular expression patterns separated by ';' to define "
118 "variable filters. All variables and class attributes matched by one of "
119 "the expressions are not shown in the list above."))
120
121 self.setGlobalsFilterButton = QPushButton(self.trUtf8('Set'), self.glvWidget)
122 self.glvWidgetHLayout.addWidget(self.setGlobalsFilterButton)
123 self.glvWidgetVLayout.addLayout(self.glvWidgetHLayout)
124
125 index = self.__tabWidget.addTab(self.glvWidget,
126 UI.PixmapCache.getIcon("globalVariables.png"), '')
127 self.__tabWidget.setTabToolTip(index, self.globalsViewer.windowTitle())
128
129 self.connect(self.setGlobalsFilterButton, SIGNAL('clicked()'),
130 self.__setGlobalsFilter)
131 self.connect(self.globalsFilterEdit, SIGNAL('returnPressed()'),
132 self.__setGlobalsFilter)
133
134 # add the local variables viewer
135 self.lvWidget = QWidget()
136 self.lvWidgetVLayout = QVBoxLayout(self.lvWidget)
137 self.lvWidgetVLayout.setMargin(0)
138 self.lvWidgetVLayout.setSpacing(3)
139 self.lvWidget.setLayout(self.lvWidgetVLayout)
140
141 self.lvWidgetHLayout1 = QHBoxLayout()
142 self.lvWidgetHLayout1.setMargin(3)
143
144 self.stackComboBox = QComboBox(self.lvWidget)
145 self.stackComboBox.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
146 self.lvWidgetHLayout1.addWidget(self.stackComboBox)
147
148 self.sourceButton = QPushButton(self.trUtf8('Source'), self.lvWidget)
149 self.lvWidgetHLayout1.addWidget(self.sourceButton)
150 self.sourceButton.setEnabled(False)
151 self.lvWidgetVLayout.addLayout(self.lvWidgetHLayout1)
152
153 self.localsViewer = VariablesViewer(self.lvWidget, False)
154 self.lvWidgetVLayout.addWidget(self.localsViewer)
155
156 self.lvWidgetHLayout2 = QHBoxLayout()
157 self.lvWidgetHLayout2.setMargin(3)
158
159 self.localsFilterEdit = QLineEdit(self.lvWidget)
160 self.localsFilterEdit.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
161 self.lvWidgetHLayout2.addWidget(self.localsFilterEdit)
162 self.localsFilterEdit.setToolTip(\
163 self.trUtf8("Enter regular expression patterns separated by ';' to define "
164 "variable filters. "))
165 self.localsFilterEdit.setWhatsThis(\
166 self.trUtf8("Enter regular expression patterns separated by ';' to define "
167 "variable filters. All variables and class attributes matched by one of "
168 "the expressions are not shown in the list above."))
169
170 self.setLocalsFilterButton = QPushButton(self.trUtf8('Set'), self.lvWidget)
171 self.lvWidgetHLayout2.addWidget(self.setLocalsFilterButton)
172 self.lvWidgetVLayout.addLayout(self.lvWidgetHLayout2)
173
174 index = self.__tabWidget.addTab(self.lvWidget,
175 UI.PixmapCache.getIcon("localVariables.png"), '')
176 self.__tabWidget.setTabToolTip(index, self.localsViewer.windowTitle())
177
178 self.connect(self.sourceButton, SIGNAL('clicked()'),
179 self.__showSource)
180 self.connect(self.stackComboBox, SIGNAL('activated(int)'),
181 self.__frameSelected)
182 self.connect(self.setLocalsFilterButton, SIGNAL('clicked()'),
183 self.__setLocalsFilter)
184 self.connect(self.localsFilterEdit, SIGNAL('returnPressed()'),
185 self.__setLocalsFilter)
186
187 # add the breakpoint viewer
188 self.breakpointViewer = BreakPointViewer()
189 self.breakpointViewer.setModel(self.debugServer.getBreakPointModel())
190 index = self.__tabWidget.addTab(self.breakpointViewer,
191 UI.PixmapCache.getIcon("breakpoints.png"), '')
192 self.__tabWidget.setTabToolTip(index, self.breakpointViewer.windowTitle())
193 self.connect(self.breakpointViewer, SIGNAL("sourceFile"),
194 self, SIGNAL("sourceFile"))
195
196 # add the watch expression viewer
197 self.watchpointViewer = WatchPointViewer()
198 self.watchpointViewer.setModel(self.debugServer.getWatchPointModel())
199 index = self.__tabWidget.addTab(self.watchpointViewer,
200 UI.PixmapCache.getIcon("watchpoints.png"), '')
201 self.__tabWidget.setTabToolTip(index, self.watchpointViewer.windowTitle())
202
203 # add the exception logger
204 self.exceptionLogger = ExceptionLogger()
205 index = self.__tabWidget.addTab(self.exceptionLogger,
206 UI.PixmapCache.getIcon("exceptions.png"), '')
207 self.__tabWidget.setTabToolTip(index, self.exceptionLogger.windowTitle())
208
209 if self.embeddedShell:
210 self.__tabWidget.setCurrentWidget(self.shell)
211 else:
212 if self.embeddedBrowser:
213 self.__tabWidget.setCurrentWidget(self.browser)
214 else:
215 self.__tabWidget.setCurrentWidget(self.lvWidget)
216
217 # add the threads viewer
218 self.__mainLayout.addWidget(QLabel(self.trUtf8("Threads:")))
219 self.__threadList = QTreeWidget()
220 self.__threadList.setHeaderLabels([self.trUtf8("ID"), self.trUtf8("Name"),
221 self.trUtf8("State"), ""])
222 self.__threadList.setSortingEnabled(True)
223 self.__mainLayout.addWidget(self.__threadList)
224
225 self.__doThreadListUpdate = True
226
227 self.connect(self.__threadList,
228 SIGNAL('currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)'),
229 self.__threadSelected)
230
231 self.__mainLayout.setStretchFactor(self.__tabWidget, 5)
232 self.__mainLayout.setStretchFactor(self.__threadList, 1)
233
234 self.currPage = None
235 self.currentStack = None
236 self.framenr = 0
237
238 self.connect(self.debugServer, SIGNAL('clientStack'), self.handleClientStack)
239
240 def setDebugger(self, debugUI):
241 """
242 Public method to set a reference to the Debug UI.
243
244 @param debugUI reference to the DebugUI objectTrees
245 """
246 self.debugUI = debugUI
247 self.connect(self.debugUI, SIGNAL('clientStack'), self.handleClientStack)
248
249 def handleResetUI(self):
250 """
251 Public method to reset the SBVviewer.
252 """
253 self.globalsViewer.handleResetUI()
254 self.localsViewer.handleResetUI()
255 self.stackComboBox.clear()
256 self.sourceButton.setEnabled(False)
257 self.currentStack = None
258 self.__threadList.clear()
259 if self.embeddedShell:
260 self.__tabWidget.setCurrentWidget(self.shell)
261 else:
262 if self.embeddedBrowser:
263 self.__tabWidget.setCurrentWidget(self.browser)
264 else:
265 self.__tabWidget.setCurrentWidget(self.lvWidget)
266 self.breakpointViewer.handleResetUI()
267
268 def handleRawInput(self):
269 """
270 Pulic slot to handle the switch to the shell in raw input mode.
271 """
272 if self.embeddedShell:
273 self.saveCurrentPage()
274 self.__tabWidget.setCurrentWidget(self.shell)
275
276 def showVariables(self, vlist, globals):
277 """
278 Public method to show the variables in the respective window.
279
280 @param vlist list of variables to display
281 @param globals flag indicating global/local state
282 """
283 if globals:
284 self.globalsViewer.showVariables(vlist, self.framenr)
285 else:
286 self.localsViewer.showVariables(vlist, self.framenr)
287
288 def showVariable(self, vlist, globals):
289 """
290 Public method to show the variables in the respective window.
291
292 @param vlist list of variables to display
293 @param globals flag indicating global/local state
294 """
295 if globals:
296 self.globalsViewer.showVariable(vlist)
297 else:
298 self.localsViewer.showVariable(vlist)
299
300 def showVariablesTab(self, globals):
301 """
302 Public method to make a variables tab visible.
303
304 @param globals flag indicating global/local state
305 """
306 if globals:
307 self.__tabWidget.setCurrentWidget(self.glvWidget)
308 else:
309 self.__tabWidget.setCurrentWidget(self.lvWidget)
310
311 def saveCurrentPage(self):
312 """
313 Public slot to save the current page.
314 """
315 self.currPage = self.__tabWidget.currentWidget()
316
317 def restoreCurrentPage(self):
318 """
319 Public slot to restore the previously saved page.
320 """
321 if self.currPage is not None:
322 self.__tabWidget.setCurrentWidget(self.currPage)
323
324 def handleClientStack(self, stack):
325 """
326 Public slot to show the call stack of the program being debugged.
327 """
328 self.framenr = 0
329 self.stackComboBox.clear()
330 self.currentStack = stack
331 self.sourceButton.setEnabled(len(stack) > 0)
332 for s in stack:
333 # just show base filename to make it readable
334 s = (os.path.basename(s[0]), s[1], s[2])
335 self.stackComboBox.addItem('%s:%s:%s' % s)
336
337 def setVariablesFilter(self, globalsFilter, localsFilter):
338 """
339 Public slot to set the local variables filter.
340
341 @param globalsFilter filter list for global variable types (list of int)
342 @param localsFilter filter list for local variable types (list of int)
343 """
344 self.globalsFilter = globalsFilter
345 self.localsFilter = localsFilter
346
347 def __showSource(self):
348 """
349 Private slot to handle the source button press to show the selected file.
350 """
351 s = self.currentStack[self.stackComboBox.currentIndex()]
352 self.emit(SIGNAL('sourceFile'), s[0], int(s[1]))
353
354 def __frameSelected(self, frmnr):
355 """
356 Private slot to handle the selection of a new stack frame number.
357
358 @param frmnr frame number (0 is the current frame) (int)
359 """
360 self.framenr = frmnr
361 self.debugServer.remoteClientVariables(0, self.localsFilter, frmnr)
362
363 def __setGlobalsFilter(self):
364 """
365 Private slot to set the global variable filter
366 """
367 filter = self.globalsFilterEdit.text()
368 self.debugServer.remoteClientSetFilter(1, filter)
369 if self.currentStack:
370 self.debugServer.remoteClientVariables(2, self.globalsFilter)
371
372 def __setLocalsFilter(self):
373 """
374 Private slot to set the local variable filter
375 """
376 filter = self.localsFilterEdit.text()
377 self.debugServer.remoteClientSetFilter(0, filter)
378 if self.currentStack:
379 self.debugServer.remoteClientVariables(0, self.localsFilter, self.framenr)
380
381 def handleDebuggingStarted(self):
382 """
383 Public slot to handle the start of a debugging session.
384
385 This slot sets the variables filter expressions.
386 """
387 self.__setGlobalsFilter()
388 self.__setLocalsFilter()
389 self.showVariablesTab(False)
390
391 def currentWidget(self):
392 """
393 Public method to get a reference to the current widget.
394
395 @return reference to the current widget (QWidget)
396 """
397 return self.__tabWidget.currentWidget()
398
399 def showThreadList(self, currentID, threadList):
400 """
401 Public method to show the thread list.
402
403 @param currentID id of the current thread (integer)
404 @param threadList list of dictionaries containing the thread data
405 """
406 citm = None
407
408 self.__threadList.clear()
409 for thread in threadList:
410 if thread['broken']:
411 state = self.trUtf8("waiting at breakpoint")
412 else:
413 state = self.trUtf8("running")
414 itm = QTreeWidgetItem(self.__threadList,
415 ["%d" % thread['id'], thread['name'], state])
416 if thread['id'] == currentID:
417 citm = itm
418
419 self.__threadList.header().resizeSections(QHeaderView.ResizeToContents)
420 self.__threadList.header().setStretchLastSection(True)
421
422 if citm:
423 self.__doThreadListUpdate = False
424 self.__threadList.setCurrentItem(citm)
425 self.__doThreadListUpdate = True
426
427 def __threadSelected(self, current, previous):
428 """
429 Private slot to handle the selection of a thread in the thread list.
430
431 @param current reference to the new current item (QTreeWidgetItem)
432 @param previous reference to the previous current item (QTreeWidgetItem)
433 """
434 if current is not None and self.__doThreadListUpdate:
435 tid, ok = current.text(0).toLong()
436 if ok:
437 self.debugServer.remoteSetThread(tid)

eric ide

mercurial