eric6/Debugger/DebugViewer.py

changeset 6942
2602857055c5
parent 6645
ad476851d7e0
child 6987
3371a03ed0a7
child 7012
cc3f83d1a605
equal deleted inserted replaced
6941:f99d60d6b59b 6942:2602857055c5
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2002 - 2019 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>call trace viewer</li>
14 <li>viewer for breakpoints</li>
15 <li>viewer for watch expressions</li>
16 <li>viewer for exceptions</li>
17 <li>viewer for threads</li>
18 <li>a file browser (optional)</li>
19 <li>an interpreter shell (optional)</li>
20 </ul>
21 """
22
23 from __future__ import unicode_literals
24
25 import os
26
27 from PyQt5.QtCore import pyqtSignal
28 from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLineEdit, \
29 QSizePolicy, QPushButton, QComboBox, QLabel, QTreeWidget, \
30 QTreeWidgetItem, QHeaderView
31
32 import UI.PixmapCache
33 import Preferences
34
35 from E5Gui.E5TabWidget import E5TabWidget
36
37
38 class DebugViewer(QWidget):
39 """
40 Class implementing a widget containing various debug related views.
41
42 The individual tabs contain the interpreter shell (optional),
43 the filesystem browser (optional), the two variables viewers
44 (global and local), a breakpoint viewer, a watch expression viewer and
45 the exception logger. Additionally a list of all threads is shown.
46
47 @signal sourceFile(string, int) emitted to open a source file at a line
48 """
49 sourceFile = pyqtSignal(str, int)
50
51 def __init__(self, debugServer, docked, vm, parent=None):
52 """
53 Constructor
54
55 @param debugServer reference to the debug server object (DebugServer)
56 @param docked flag indicating a dock window
57 @param vm reference to the viewmanager object
58 @param parent parent widget (QWidget)
59 """
60 super(DebugViewer, self).__init__(parent)
61
62 self.debugServer = debugServer
63 self.debugUI = None
64
65 self.setWindowIcon(UI.PixmapCache.getIcon("eric.png"))
66
67 self.__mainLayout = QVBoxLayout()
68 self.__mainLayout.setContentsMargins(0, 0, 0, 0)
69 self.setLayout(self.__mainLayout)
70
71 self.__tabWidget = E5TabWidget()
72 self.__mainLayout.addWidget(self.__tabWidget)
73
74 from .VariablesViewer import VariablesViewer
75 # add the global variables viewer
76 self.glvWidget = QWidget()
77 self.glvWidgetVLayout = QVBoxLayout(self.glvWidget)
78 self.glvWidgetVLayout.setContentsMargins(0, 0, 0, 0)
79 self.glvWidgetVLayout.setSpacing(3)
80 self.glvWidget.setLayout(self.glvWidgetVLayout)
81
82 self.globalsViewer = VariablesViewer(self, True, self.glvWidget)
83 self.glvWidgetVLayout.addWidget(self.globalsViewer)
84
85 self.glvWidgetHLayout = QHBoxLayout()
86 self.glvWidgetHLayout.setContentsMargins(3, 3, 3, 3)
87
88 self.globalsFilterEdit = QLineEdit(self.glvWidget)
89 self.globalsFilterEdit.setSizePolicy(
90 QSizePolicy.Expanding, QSizePolicy.Fixed)
91 self.glvWidgetHLayout.addWidget(self.globalsFilterEdit)
92 self.globalsFilterEdit.setToolTip(
93 self.tr("Enter regular expression patterns separated by ';'"
94 " to define variable filters. "))
95 self.globalsFilterEdit.setWhatsThis(
96 self.tr("Enter regular expression patterns separated by ';'"
97 " to define variable filters. All variables and"
98 " class attributes matched by one of the expressions"
99 " are not shown in the list above."))
100
101 self.setGlobalsFilterButton = QPushButton(
102 self.tr('Set'), self.glvWidget)
103 self.glvWidgetHLayout.addWidget(self.setGlobalsFilterButton)
104 self.glvWidgetVLayout.addLayout(self.glvWidgetHLayout)
105
106 index = self.__tabWidget.addTab(
107 self.glvWidget,
108 UI.PixmapCache.getIcon("globalVariables.png"), '')
109 self.__tabWidget.setTabToolTip(index, self.globalsViewer.windowTitle())
110
111 self.setGlobalsFilterButton.clicked.connect(
112 self.setGlobalsFilter)
113 self.globalsFilterEdit.returnPressed.connect(self.setGlobalsFilter)
114
115 # add the local variables viewer
116 self.lvWidget = QWidget()
117 self.lvWidgetVLayout = QVBoxLayout(self.lvWidget)
118 self.lvWidgetVLayout.setContentsMargins(0, 0, 0, 0)
119 self.lvWidgetVLayout.setSpacing(3)
120 self.lvWidget.setLayout(self.lvWidgetVLayout)
121
122 self.lvWidgetHLayout1 = QHBoxLayout()
123 self.lvWidgetHLayout1.setContentsMargins(3, 3, 3, 3)
124
125 self.stackComboBox = QComboBox(self.lvWidget)
126 self.stackComboBox.setSizePolicy(
127 QSizePolicy.Expanding, QSizePolicy.Fixed)
128 self.lvWidgetHLayout1.addWidget(self.stackComboBox)
129
130 self.sourceButton = QPushButton(self.tr('Source'), self.lvWidget)
131 self.lvWidgetHLayout1.addWidget(self.sourceButton)
132 self.sourceButton.setEnabled(False)
133 self.lvWidgetVLayout.addLayout(self.lvWidgetHLayout1)
134
135 self.localsViewer = VariablesViewer(self, False, self.lvWidget)
136 self.lvWidgetVLayout.addWidget(self.localsViewer)
137
138 self.lvWidgetHLayout2 = QHBoxLayout()
139 self.lvWidgetHLayout2.setContentsMargins(3, 3, 3, 3)
140
141 self.localsFilterEdit = QLineEdit(self.lvWidget)
142 self.localsFilterEdit.setSizePolicy(
143 QSizePolicy.Expanding, QSizePolicy.Fixed)
144 self.lvWidgetHLayout2.addWidget(self.localsFilterEdit)
145 self.localsFilterEdit.setToolTip(
146 self.tr(
147 "Enter regular expression patterns separated by ';' to define "
148 "variable filters. "))
149 self.localsFilterEdit.setWhatsThis(
150 self.tr(
151 "Enter regular expression patterns separated by ';' to define "
152 "variable filters. All variables and class attributes matched"
153 " by one of the expressions are not shown in the list above."))
154
155 self.setLocalsFilterButton = QPushButton(
156 self.tr('Set'), self.lvWidget)
157 self.lvWidgetHLayout2.addWidget(self.setLocalsFilterButton)
158 self.lvWidgetVLayout.addLayout(self.lvWidgetHLayout2)
159
160 index = self.__tabWidget.addTab(
161 self.lvWidget,
162 UI.PixmapCache.getIcon("localVariables.png"), '')
163 self.__tabWidget.setTabToolTip(index, self.localsViewer.windowTitle())
164
165 self.sourceButton.clicked.connect(self.__showSource)
166 self.stackComboBox.currentIndexChanged[int].connect(
167 self.__frameSelected)
168 self.setLocalsFilterButton.clicked.connect(self.setLocalsFilter)
169 self.localsFilterEdit.returnPressed.connect(self.setLocalsFilter)
170
171 from .CallStackViewer import CallStackViewer
172 # add the call stack viewer
173 self.callStackViewer = CallStackViewer(self.debugServer)
174 index = self.__tabWidget.addTab(
175 self.callStackViewer,
176 UI.PixmapCache.getIcon("step.png"), "")
177 self.__tabWidget.setTabToolTip(
178 index, self.callStackViewer.windowTitle())
179 self.callStackViewer.sourceFile.connect(self.sourceFile)
180 self.callStackViewer.frameSelected.connect(
181 self.__callStackFrameSelected)
182
183 from .CallTraceViewer import CallTraceViewer
184 # add the call trace viewer
185 self.callTraceViewer = CallTraceViewer(self.debugServer)
186 index = self.__tabWidget.addTab(
187 self.callTraceViewer,
188 UI.PixmapCache.getIcon("callTrace.png"), "")
189 self.__tabWidget.setTabToolTip(
190 index, self.callTraceViewer.windowTitle())
191 self.callTraceViewer.sourceFile.connect(self.sourceFile)
192
193 from .BreakPointViewer import BreakPointViewer
194 # add the breakpoint viewer
195 self.breakpointViewer = BreakPointViewer()
196 self.breakpointViewer.setModel(self.debugServer.getBreakPointModel())
197 index = self.__tabWidget.addTab(
198 self.breakpointViewer,
199 UI.PixmapCache.getIcon("breakpoints.png"), '')
200 self.__tabWidget.setTabToolTip(
201 index, self.breakpointViewer.windowTitle())
202 self.breakpointViewer.sourceFile.connect(self.sourceFile)
203
204 from .WatchPointViewer import WatchPointViewer
205 # add the watch expression viewer
206 self.watchpointViewer = WatchPointViewer()
207 self.watchpointViewer.setModel(self.debugServer.getWatchPointModel())
208 index = self.__tabWidget.addTab(
209 self.watchpointViewer,
210 UI.PixmapCache.getIcon("watchpoints.png"), '')
211 self.__tabWidget.setTabToolTip(
212 index, self.watchpointViewer.windowTitle())
213
214 from .ExceptionLogger import ExceptionLogger
215 # add the exception logger
216 self.exceptionLogger = ExceptionLogger()
217 index = self.__tabWidget.addTab(
218 self.exceptionLogger,
219 UI.PixmapCache.getIcon("exceptions.png"), '')
220 self.__tabWidget.setTabToolTip(
221 index, self.exceptionLogger.windowTitle())
222
223 self.__tabWidget.setCurrentWidget(self.glvWidget)
224
225 # add the threads viewer
226 self.__mainLayout.addWidget(QLabel(self.tr("Threads:")))
227 self.__threadList = QTreeWidget()
228 self.__threadList.setHeaderLabels(
229 [self.tr("ID"), self.tr("Name"),
230 self.tr("State"), ""])
231 self.__threadList.setSortingEnabled(True)
232 self.__mainLayout.addWidget(self.__threadList)
233
234 self.__doThreadListUpdate = True
235
236 self.__threadList.currentItemChanged.connect(self.__threadSelected)
237
238 self.__mainLayout.setStretchFactor(self.__tabWidget, 5)
239 self.__mainLayout.setStretchFactor(self.__threadList, 1)
240
241 self.currentStack = None
242 self.framenr = 0
243
244 self.debugServer.clientStack.connect(self.handleClientStack)
245
246 self.__autoViewSource = Preferences.getDebugger("AutoViewSourceCode")
247 self.sourceButton.setVisible(not self.__autoViewSource)
248
249 def preferencesChanged(self):
250 """
251 Public slot to handle the preferencesChanged signal.
252 """
253 self.__autoViewSource = Preferences.getDebugger("AutoViewSourceCode")
254 self.sourceButton.setVisible(not self.__autoViewSource)
255
256 def setDebugger(self, debugUI):
257 """
258 Public method to set a reference to the Debug UI.
259
260 @param debugUI reference to the DebugUI object (DebugUI)
261 """
262 self.debugUI = debugUI
263 self.debugUI.clientStack.connect(self.handleClientStack)
264 self.callStackViewer.setDebugger(debugUI)
265
266 def handleResetUI(self):
267 """
268 Public method to reset the SBVviewer.
269 """
270 self.globalsViewer.handleResetUI()
271 self.localsViewer.handleResetUI()
272 self.setGlobalsFilter()
273 self.setLocalsFilter()
274 self.sourceButton.setEnabled(False)
275 self.currentStack = None
276 self.stackComboBox.clear()
277 self.__threadList.clear()
278 self.__tabWidget.setCurrentWidget(self.glvWidget)
279 self.breakpointViewer.handleResetUI()
280
281 def initCallStackViewer(self, projectMode):
282 """
283 Public method to initialize the call stack viewer.
284
285 @param projectMode flag indicating to enable the project mode (boolean)
286 """
287 self.callStackViewer.clear()
288 self.callStackViewer.setProjectMode(projectMode)
289
290 def isCallTraceEnabled(self):
291 """
292 Public method to get the state of the call trace function.
293
294 @return flag indicating the state of the call trace function (boolean)
295 """
296 return self.callTraceViewer.isCallTraceEnabled()
297
298 def clearCallTrace(self):
299 """
300 Public method to clear the recorded call trace.
301 """
302 self.callTraceViewer.clear()
303
304 def setCallTraceToProjectMode(self, enabled):
305 """
306 Public slot to set the call trace viewer to project mode.
307
308 In project mode the call trace info is shown with project relative
309 path names.
310
311 @param enabled flag indicating to enable the project mode (boolean)
312 """
313 self.callTraceViewer.setProjectMode(enabled)
314
315 def showVariables(self, vlist, showGlobals):
316 """
317 Public method to show the variables in the respective window.
318
319 @param vlist list of variables to display
320 @param showGlobals flag indicating global/local state
321 """
322 if showGlobals:
323 self.globalsViewer.showVariables(vlist, self.framenr)
324 else:
325 self.localsViewer.showVariables(vlist, self.framenr)
326
327 def showVariable(self, vlist, showGlobals):
328 """
329 Public method to show the variables in the respective window.
330
331 @param vlist list of variables to display
332 @param showGlobals flag indicating global/local state
333 """
334 if showGlobals:
335 self.globalsViewer.showVariable(vlist)
336 else:
337 self.localsViewer.showVariable(vlist)
338
339 def showVariablesTab(self, showGlobals):
340 """
341 Public method to make a variables tab visible.
342
343 @param showGlobals flag indicating global/local state
344 """
345 if showGlobals:
346 self.__tabWidget.setCurrentWidget(self.glvWidget)
347 else:
348 self.__tabWidget.setCurrentWidget(self.lvWidget)
349
350 def handleClientStack(self, stack):
351 """
352 Public slot to show the call stack of the program being debugged.
353
354 @param stack list of tuples with call stack data (file name,
355 line number, function name, formatted argument/values list)
356 """
357 block = self.stackComboBox.blockSignals(True)
358 self.framenr = 0
359 self.stackComboBox.clear()
360 self.currentStack = stack
361 self.sourceButton.setEnabled(len(stack) > 0)
362 for s in stack:
363 # just show base filename to make it readable
364 s = (os.path.basename(s[0]), s[1], s[2])
365 self.stackComboBox.addItem('{0}:{1}:{2}'.format(*s))
366 self.stackComboBox.blockSignals(block)
367
368 def setVariablesFilter(self, globalsFilter, localsFilter):
369 """
370 Public slot to set the local variables filter.
371
372 @param globalsFilter filter list for global variable types
373 (list of int)
374 @param localsFilter filter list for local variable types (list of int)
375 """
376 self.globalsFilter = globalsFilter
377 self.localsFilter = localsFilter
378
379 def __showSource(self):
380 """
381 Private slot to handle the source button press to show the selected
382 file.
383 """
384 index = self.stackComboBox.currentIndex()
385 if index > -1 and self.currentStack:
386 s = self.currentStack[index]
387 self.sourceFile.emit(s[0], int(s[1]))
388
389 def __frameSelected(self, frmnr):
390 """
391 Private slot to handle the selection of a new stack frame number.
392
393 @param frmnr frame number (0 is the current frame) (int)
394 """
395 self.framenr = frmnr
396 if self.debugServer.isDebugging():
397 self.debugServer.remoteClientVariables(0, self.localsFilter, frmnr)
398
399 if self.__autoViewSource:
400 self.__showSource()
401
402 def setGlobalsFilter(self):
403 """
404 Public slot to set the global variable filter.
405 """
406 if self.debugServer.isDebugging():
407 filterStr = self.globalsFilterEdit.text()
408 self.debugServer.remoteClientSetFilter(1, filterStr)
409 self.debugServer.remoteClientVariables(2, self.globalsFilter)
410
411 def setLocalsFilter(self):
412 """
413 Public slot to set the local variable filter.
414 """
415 if self.debugServer.isDebugging():
416 filterStr = self.localsFilterEdit.text()
417 self.debugServer.remoteClientSetFilter(0, filterStr)
418 if self.currentStack:
419 self.debugServer.remoteClientVariables(
420 0, self.localsFilter, self.framenr)
421
422 def handleDebuggingStarted(self):
423 """
424 Public slot to handle the start of a debugging session.
425
426 This slot sets the variables filter expressions.
427 """
428 self.setGlobalsFilter()
429 self.setLocalsFilter()
430 self.showVariablesTab(False)
431
432 def currentWidget(self):
433 """
434 Public method to get a reference to the current widget.
435
436 @return reference to the current widget (QWidget)
437 """
438 return self.__tabWidget.currentWidget()
439
440 def setCurrentWidget(self, widget):
441 """
442 Public slot to set the current page based on the given widget.
443
444 @param widget reference to the widget (QWidget)
445 """
446 self.__tabWidget.setCurrentWidget(widget)
447
448 def showThreadList(self, currentID, threadList):
449 """
450 Public method to show the thread list.
451
452 @param currentID id of the current thread (integer)
453 @param threadList list of dictionaries containing the thread data
454 """
455 citm = None
456
457 self.__threadList.clear()
458 for thread in threadList:
459 if thread['broken']:
460 state = self.tr("waiting at breakpoint")
461 else:
462 state = self.tr("running")
463 itm = QTreeWidgetItem(self.__threadList,
464 ["{0:d}".format(thread['id']),
465 thread['name'], state])
466 if thread['id'] == currentID:
467 citm = itm
468
469 self.__threadList.header().resizeSections(QHeaderView.ResizeToContents)
470 self.__threadList.header().setStretchLastSection(True)
471
472 if citm:
473 self.__doThreadListUpdate = False
474 self.__threadList.setCurrentItem(citm)
475 self.__doThreadListUpdate = True
476
477 def __threadSelected(self, current, previous):
478 """
479 Private slot to handle the selection of a thread in the thread list.
480
481 @param current reference to the new current item (QTreeWidgetItem)
482 @param previous reference to the previous current item
483 (QTreeWidgetItem)
484 """
485 if current is not None and self.__doThreadListUpdate:
486 tid = int(current.text(0))
487 self.debugServer.remoteSetThread(tid)
488
489 def __callStackFrameSelected(self, frameNo):
490 """
491 Private slot to handle the selection of a call stack entry of the
492 call stack viewer.
493
494 @param frameNo frame number (index) of the selected entry (integer)
495 """
496 if frameNo >= 0:
497 self.stackComboBox.setCurrentIndex(frameNo)

eric ide

mercurial