eric6/Debugger/DebugViewer.py

branch
maintenance
changeset 8043
0acf98cd089a
parent 7924
8a96736d465e
parent 7986
2971d5d19951
child 8176
31965986ecd1
equal deleted inserted replaced
7991:866adc8c315b 8043:0acf98cd089a
6 """ 6 """
7 Module implementing a widget containing various debug related views. 7 Module implementing a widget containing various debug related views.
8 8
9 The views avaliable are: 9 The views avaliable are:
10 <ul> 10 <ul>
11 <li>variables viewer for global variables</li> 11 <li>selector showing all connected debugger backends with associated
12 <li>variables viewer for local variables</li> 12 threads</li>
13 <li>variables viewer for global variables for the selected debug client</li>
14 <li>variables viewer for local variables for the selected debug client</li>
15 <li>call stack viewer for the selected debug client</li>
13 <li>call trace viewer</li> 16 <li>call trace viewer</li>
14 <li>viewer for breakpoints</li> 17 <li>viewer for breakpoints</li>
15 <li>viewer for watch expressions</li> 18 <li>viewer for watch expressions</li>
16 <li>viewer for exceptions</li> 19 <li>viewer for exceptions</li>
17 <li>viewer for threads</li> 20 <li>viewer for a code disassembly for an exception<li>
18 <li>a file browser (optional)</li>
19 <li>an interpreter shell (optional)</li>
20 </ul> 21 </ul>
21 """ 22 """
22 23
23 import os 24 import os
24 25
25 from PyQt5.QtCore import pyqtSignal 26 from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QCoreApplication
26 from PyQt5.QtWidgets import ( 27 from PyQt5.QtWidgets import (
27 QWidget, QVBoxLayout, QHBoxLayout, QLineEdit, QSizePolicy, QPushButton, 28 QWidget, QVBoxLayout, QHBoxLayout, QLineEdit, QSizePolicy, QPushButton,
28 QComboBox, QLabel, QTreeWidget, QTreeWidgetItem, QHeaderView 29 QComboBox, QLabel, QTreeWidget, QTreeWidgetItem, QHeaderView, QSplitter
29 ) 30 )
30 31
31 import UI.PixmapCache 32 import UI.PixmapCache
32 import Preferences 33 import Preferences
33 34
47 @signal preferencesChanged() emitted to react on changed preferences 48 @signal preferencesChanged() emitted to react on changed preferences
48 """ 49 """
49 sourceFile = pyqtSignal(str, int) 50 sourceFile = pyqtSignal(str, int)
50 preferencesChanged = pyqtSignal() 51 preferencesChanged = pyqtSignal()
51 52
53 ThreadIdRole = Qt.UserRole + 1
54 DebuggerStateRole = Qt.UserRole + 2
55
56 # Map debug state to icon name
57 StateIcon = {
58 "broken": "break",
59 "exception": "exceptions",
60 "running": "mediaPlaybackStart",
61 "syntax": "syntaxError22",
62 }
63
64 # Map debug state to user message
65 StateMessage = {
66 "broken": QCoreApplication.translate(
67 "DebugViewer", "waiting at breakpoint"),
68 "exception": QCoreApplication.translate(
69 "DebugViewer", "waiting at exception"),
70 "running": QCoreApplication.translate(
71 "DebugViewer", "running"),
72 "syntax": QCoreApplication.translate(
73 "DebugViewer", "syntax error"),
74 }
75
52 def __init__(self, debugServer, parent=None): 76 def __init__(self, debugServer, parent=None):
53 """ 77 """
54 Constructor 78 Constructor
55 79
56 @param debugServer reference to the debug server object (DebugServer) 80 @param debugServer reference to the debug server object
57 @param parent parent widget (QWidget) 81 @type DebugServer
82 @param parent parent widget
83 @type QWidget
58 """ 84 """
59 super(DebugViewer, self).__init__(parent) 85 super(DebugViewer, self).__init__(parent)
60 86
61 self.debugServer = debugServer 87 self.debugServer = debugServer
62 self.debugUI = None 88 self.debugUI = None
63 89
64 self.setWindowIcon(UI.PixmapCache.getIcon("eric")) 90 self.setWindowIcon(UI.PixmapCache.getIcon("eric"))
65 91
66 self.__mainLayout = QVBoxLayout() 92 self.__mainLayout = QVBoxLayout()
67 self.__mainLayout.setContentsMargins(0, 0, 0, 0) 93 self.__mainLayout.setContentsMargins(0, 3, 0, 0)
68 self.setLayout(self.__mainLayout) 94 self.setLayout(self.__mainLayout)
69 95
96 self.__mainSplitter = QSplitter(Qt.Vertical, self)
97 self.__mainLayout.addWidget(self.__mainSplitter)
98
99 # add the viewer showing the connected debug backends
100 self.__debuggersWidget = QWidget()
101 self.__debuggersLayout = QVBoxLayout(self.__debuggersWidget)
102 self.__debuggersLayout.setContentsMargins(0, 0, 0, 0)
103 self.__debuggersLayout.addWidget(
104 QLabel(self.tr("Debuggers and Threads:")))
105 self.__debuggersList = QTreeWidget()
106 self.__debuggersList.setHeaderLabels(
107 [self.tr("ID"), self.tr("State"), ""])
108 self.__debuggersList.header().setStretchLastSection(True)
109 self.__debuggersList.setSortingEnabled(True)
110 self.__debuggersList.setRootIsDecorated(True)
111 self.__debuggersList.setAlternatingRowColors(True)
112 self.__debuggersLayout.addWidget(self.__debuggersList)
113 self.__mainSplitter.addWidget(self.__debuggersWidget)
114
115 self.__debuggersList.currentItemChanged.connect(
116 self.__debuggerSelected)
117
118 # add the tab widget containing various debug related views
70 self.__tabWidget = E5TabWidget() 119 self.__tabWidget = E5TabWidget()
71 self.__mainLayout.addWidget(self.__tabWidget) 120 self.__mainSplitter.addWidget(self.__tabWidget)
72 121
73 from .VariablesViewer import VariablesViewer 122 from .VariablesViewer import VariablesViewer
74 # add the global variables viewer 123 # add the global variables viewer
75 self.glvWidget = QWidget() 124 self.glvWidget = QWidget()
76 self.glvWidgetVLayout = QVBoxLayout(self.glvWidget) 125 self.glvWidgetVLayout = QVBoxLayout(self.glvWidget)
103 self.glvWidgetVLayout.addLayout(self.glvWidgetHLayout) 152 self.glvWidgetVLayout.addLayout(self.glvWidgetHLayout)
104 153
105 index = self.__tabWidget.addTab( 154 index = self.__tabWidget.addTab(
106 self.glvWidget, 155 self.glvWidget,
107 UI.PixmapCache.getIcon("globalVariables"), '') 156 UI.PixmapCache.getIcon("globalVariables"), '')
108 self.__tabWidget.setTabToolTip(index, self.globalsViewer.windowTitle()) 157 self.__tabWidget.setTabToolTip(
158 index,
159 self.tr("Shows the list of global variables and their values."))
109 160
110 self.setGlobalsFilterButton.clicked.connect( 161 self.setGlobalsFilterButton.clicked.connect(
111 self.setGlobalsFilter) 162 self.setGlobalsFilter)
112 self.globalsFilterEdit.returnPressed.connect(self.setGlobalsFilter) 163 self.globalsFilterEdit.returnPressed.connect(self.setGlobalsFilter)
113 164
157 self.lvWidgetVLayout.addLayout(self.lvWidgetHLayout2) 208 self.lvWidgetVLayout.addLayout(self.lvWidgetHLayout2)
158 209
159 index = self.__tabWidget.addTab( 210 index = self.__tabWidget.addTab(
160 self.lvWidget, 211 self.lvWidget,
161 UI.PixmapCache.getIcon("localVariables"), '') 212 UI.PixmapCache.getIcon("localVariables"), '')
162 self.__tabWidget.setTabToolTip(index, self.localsViewer.windowTitle()) 213 self.__tabWidget.setTabToolTip(
214 index,
215 self.tr("Shows the list of local variables and their values."))
163 216
164 self.sourceButton.clicked.connect(self.__showSource) 217 self.sourceButton.clicked.connect(self.__showSource)
165 self.stackComboBox.currentIndexChanged[int].connect( 218 self.stackComboBox.currentIndexChanged[int].connect(
166 self.__frameSelected) 219 self.__frameSelected)
167 self.setLocalsFilterButton.clicked.connect(self.setLocalsFilter) 220 self.setLocalsFilterButton.clicked.connect(self.setLocalsFilter)
176 self.callStackViewer = CallStackViewer(self.debugServer) 229 self.callStackViewer = CallStackViewer(self.debugServer)
177 index = self.__tabWidget.addTab( 230 index = self.__tabWidget.addTab(
178 self.callStackViewer, 231 self.callStackViewer,
179 UI.PixmapCache.getIcon("callStack"), "") 232 UI.PixmapCache.getIcon("callStack"), "")
180 self.__tabWidget.setTabToolTip( 233 self.__tabWidget.setTabToolTip(
181 index, self.callStackViewer.windowTitle()) 234 index,
235 self.tr("Shows the current call stack."))
182 self.callStackViewer.sourceFile.connect(self.sourceFile) 236 self.callStackViewer.sourceFile.connect(self.sourceFile)
183 self.callStackViewer.frameSelected.connect( 237 self.callStackViewer.frameSelected.connect(
184 self.__callStackFrameSelected) 238 self.__callStackFrameSelected)
185 239
186 from .CallTraceViewer import CallTraceViewer 240 from .CallTraceViewer import CallTraceViewer
187 # add the call trace viewer 241 # add the call trace viewer
188 self.callTraceViewer = CallTraceViewer(self.debugServer) 242 self.callTraceViewer = CallTraceViewer(self.debugServer, self)
189 index = self.__tabWidget.addTab( 243 index = self.__tabWidget.addTab(
190 self.callTraceViewer, 244 self.callTraceViewer,
191 UI.PixmapCache.getIcon("callTrace"), "") 245 UI.PixmapCache.getIcon("callTrace"), "")
192 self.__tabWidget.setTabToolTip( 246 self.__tabWidget.setTabToolTip(
193 index, self.callTraceViewer.windowTitle()) 247 index,
248 self.tr("Shows a trace of the program flow."))
194 self.callTraceViewer.sourceFile.connect(self.sourceFile) 249 self.callTraceViewer.sourceFile.connect(self.sourceFile)
195 250
196 from .BreakPointViewer import BreakPointViewer 251 from .BreakPointViewer import BreakPointViewer
197 # add the breakpoint viewer 252 # add the breakpoint viewer
198 self.breakpointViewer = BreakPointViewer() 253 self.breakpointViewer = BreakPointViewer()
199 self.breakpointViewer.setModel(self.debugServer.getBreakPointModel()) 254 self.breakpointViewer.setModel(self.debugServer.getBreakPointModel())
200 index = self.__tabWidget.addTab( 255 index = self.__tabWidget.addTab(
201 self.breakpointViewer, 256 self.breakpointViewer,
202 UI.PixmapCache.getIcon("breakpoints"), '') 257 UI.PixmapCache.getIcon("breakpoints"), '')
203 self.__tabWidget.setTabToolTip( 258 self.__tabWidget.setTabToolTip(
204 index, self.breakpointViewer.windowTitle()) 259 index,
260 self.tr("Shows a list of defined breakpoints."))
205 self.breakpointViewer.sourceFile.connect(self.sourceFile) 261 self.breakpointViewer.sourceFile.connect(self.sourceFile)
206 262
207 from .WatchPointViewer import WatchPointViewer 263 from .WatchPointViewer import WatchPointViewer
208 # add the watch expression viewer 264 # add the watch expression viewer
209 self.watchpointViewer = WatchPointViewer() 265 self.watchpointViewer = WatchPointViewer()
210 self.watchpointViewer.setModel(self.debugServer.getWatchPointModel()) 266 self.watchpointViewer.setModel(self.debugServer.getWatchPointModel())
211 index = self.__tabWidget.addTab( 267 index = self.__tabWidget.addTab(
212 self.watchpointViewer, 268 self.watchpointViewer,
213 UI.PixmapCache.getIcon("watchpoints"), '') 269 UI.PixmapCache.getIcon("watchpoints"), '')
214 self.__tabWidget.setTabToolTip( 270 self.__tabWidget.setTabToolTip(
215 index, self.watchpointViewer.windowTitle()) 271 index,
272 self.tr("Shows a list of defined watchpoints."))
216 273
217 from .ExceptionLogger import ExceptionLogger 274 from .ExceptionLogger import ExceptionLogger
218 # add the exception logger 275 # add the exception logger
219 self.exceptionLogger = ExceptionLogger() 276 self.exceptionLogger = ExceptionLogger()
220 index = self.__tabWidget.addTab( 277 index = self.__tabWidget.addTab(
221 self.exceptionLogger, 278 self.exceptionLogger,
222 UI.PixmapCache.getIcon("exceptions"), '') 279 UI.PixmapCache.getIcon("exceptions"), '')
223 self.__tabWidget.setTabToolTip( 280 self.__tabWidget.setTabToolTip(
224 index, self.exceptionLogger.windowTitle()) 281 index,
282 self.tr("Shows a list of raised exceptions."))
225 283
226 from UI.PythonDisViewer import PythonDisViewer, PythonDisViewerModes 284 from UI.PythonDisViewer import PythonDisViewer, PythonDisViewerModes
227 # add the Python disassembly viewer 285 # add the Python disassembly viewer
228 self.disassemblyViewer = PythonDisViewer( 286 self.disassemblyViewer = PythonDisViewer(
229 None, mode=PythonDisViewerModes.TracebackMode) 287 None, mode=PythonDisViewerModes.TracebackMode)
230 index = self.__tabWidget.addTab( 288 index = self.__tabWidget.addTab(
231 self.disassemblyViewer, 289 self.disassemblyViewer,
232 UI.PixmapCache.getIcon("disassembly"), '') 290 UI.PixmapCache.getIcon("disassembly"), '')
233 self.__tabWidget.setTabToolTip( 291 self.__tabWidget.setTabToolTip(
234 index, self.disassemblyViewer.windowTitle()) 292 index,
293 self.tr("Shows a code disassembly in case of an exception."))
235 294
236 self.__tabWidget.setCurrentWidget(self.glvWidget) 295 self.__tabWidget.setCurrentWidget(self.glvWidget)
237 296
238 # add the threads viewer 297 self.__doDebuggersListUpdate = True
239 self.__mainLayout.addWidget(QLabel(self.tr("Threads:"))) 298
240 self.__threadList = QTreeWidget() 299 self.__mainSplitter.setSizes([100, 700])
241 self.__threadList.setHeaderLabels(
242 [self.tr("ID"), self.tr("Name"),
243 self.tr("State"), ""])
244 self.__threadList.setSortingEnabled(True)
245 self.__mainLayout.addWidget(self.__threadList)
246
247 self.__doThreadListUpdate = True
248
249 self.__threadList.currentItemChanged.connect(self.__threadSelected)
250
251 self.__mainLayout.setStretchFactor(self.__tabWidget, 5)
252 self.__mainLayout.setStretchFactor(self.__threadList, 1)
253 300
254 self.currentStack = None 301 self.currentStack = None
255 self.framenr = 0 302 self.framenr = 0
256 303
257 self.debugServer.clientStack.connect(self.handleClientStack)
258
259 self.__autoViewSource = Preferences.getDebugger("AutoViewSourceCode") 304 self.__autoViewSource = Preferences.getDebugger("AutoViewSourceCode")
260 self.sourceButton.setVisible(not self.__autoViewSource) 305 self.sourceButton.setVisible(not self.__autoViewSource)
261 306
307 # connect some debug server signals
308 self.debugServer.clientStack.connect(
309 self.handleClientStack)
310 self.debugServer.clientThreadList.connect(
311 self.__addThreadList)
312 self.debugServer.clientDebuggerId.connect(
313 self.__clientDebuggerId)
314 self.debugServer.passiveDebugStarted.connect(
315 self.handleDebuggingStarted)
316 self.debugServer.clientLine.connect(
317 self.__clientLine)
318 self.debugServer.clientSyntaxError.connect(
319 self.__clientSyntaxError)
320 self.debugServer.clientException.connect(
321 self.__clientException)
322 self.debugServer.clientExit.connect(
323 self.__clientExit)
324 self.debugServer.clientDisconnected.connect(
325 self.__removeDebugger)
326
327 self.debugServer.clientException.connect(
328 self.exceptionLogger.addException)
329 self.debugServer.passiveDebugStarted.connect(
330 self.exceptionLogger.debuggingStarted)
331
332 self.debugServer.clientLine.connect(
333 self.breakpointViewer.highlightBreakpoint)
334
262 def handlePreferencesChanged(self): 335 def handlePreferencesChanged(self):
263 """ 336 """
264 Public slot to handle the preferencesChanged signal. 337 Public slot to handle the preferencesChanged signal.
265 """ 338 """
266 self.__autoViewSource = Preferences.getDebugger("AutoViewSourceCode") 339 self.__autoViewSource = Preferences.getDebugger("AutoViewSourceCode")
268 341
269 def setDebugger(self, debugUI): 342 def setDebugger(self, debugUI):
270 """ 343 """
271 Public method to set a reference to the Debug UI. 344 Public method to set a reference to the Debug UI.
272 345
273 @param debugUI reference to the DebugUI object (DebugUI) 346 @param debugUI reference to the DebugUI object
347 @type DebugUI
274 """ 348 """
275 self.debugUI = debugUI 349 self.debugUI = debugUI
350 self.callStackViewer.setDebugger(debugUI)
351
352 # connect some debugUI signals
276 self.debugUI.clientStack.connect(self.handleClientStack) 353 self.debugUI.clientStack.connect(self.handleClientStack)
277 self.callStackViewer.setDebugger(debugUI) 354 self.debugUI.debuggingStarted.connect(
278 355 self.exceptionLogger.debuggingStarted)
279 def handleResetUI(self): 356 self.debugUI.debuggingStarted.connect(
280 """ 357 self.handleDebuggingStarted)
281 Public method to reset the SBVviewer. 358
359 def handleResetUI(self, fullReset):
360 """
361 Public method to reset the viewer.
362
363 @param fullReset flag indicating a full reset is required
364 @type bool
282 """ 365 """
283 self.globalsViewer.handleResetUI() 366 self.globalsViewer.handleResetUI()
284 self.localsViewer.handleResetUI() 367 self.localsViewer.handleResetUI()
285 self.setGlobalsFilter() 368 self.setGlobalsFilter()
286 self.setLocalsFilter() 369 self.setLocalsFilter()
287 self.sourceButton.setEnabled(False) 370 self.sourceButton.setEnabled(False)
288 self.currentStack = None 371 self.currentStack = None
289 self.stackComboBox.clear() 372 self.stackComboBox.clear()
290 self.__threadList.clear()
291 self.__tabWidget.setCurrentWidget(self.glvWidget) 373 self.__tabWidget.setCurrentWidget(self.glvWidget)
292 self.breakpointViewer.handleResetUI() 374 self.breakpointViewer.handleResetUI()
375 if fullReset:
376 self.__debuggersList.clear()
293 self.disassemblyViewer.clear() 377 self.disassemblyViewer.clear()
294 378
295 def initCallStackViewer(self, projectMode): 379 def initCallStackViewer(self, projectMode):
296 """ 380 """
297 Public method to initialize the call stack viewer. 381 Public method to initialize the call stack viewer.
298 382
299 @param projectMode flag indicating to enable the project mode (boolean) 383 @param projectMode flag indicating to enable the project mode
384 @type bool
300 """ 385 """
301 self.callStackViewer.clear() 386 self.callStackViewer.clear()
302 self.callStackViewer.setProjectMode(projectMode) 387 self.callStackViewer.setProjectMode(projectMode)
303 388
304 def isCallTraceEnabled(self): 389 def isCallTraceEnabled(self):
305 """ 390 """
306 Public method to get the state of the call trace function. 391 Public method to get the state of the call trace function.
307 392
308 @return flag indicating the state of the call trace function (boolean) 393 @return flag indicating the state of the call trace function
394 @rtype bool
309 """ 395 """
310 return self.callTraceViewer.isCallTraceEnabled() 396 return self.callTraceViewer.isCallTraceEnabled()
311 397
312 def clearCallTrace(self): 398 def clearCallTrace(self):
313 """ 399 """
320 Public slot to set the call trace viewer to project mode. 406 Public slot to set the call trace viewer to project mode.
321 407
322 In project mode the call trace info is shown with project relative 408 In project mode the call trace info is shown with project relative
323 path names. 409 path names.
324 410
325 @param enabled flag indicating to enable the project mode (boolean) 411 @param enabled flag indicating to enable the project mode
412 @type bool
326 """ 413 """
327 self.callTraceViewer.setProjectMode(enabled) 414 self.callTraceViewer.setProjectMode(enabled)
328 415
329 def showVariables(self, vlist, showGlobals): 416 def showVariables(self, vlist, showGlobals):
330 """ 417 """
331 Public method to show the variables in the respective window. 418 Public method to show the variables in the respective window.
332 419
333 @param vlist list of variables to display 420 @param vlist list of variables to display
421 @type list
334 @param showGlobals flag indicating global/local state 422 @param showGlobals flag indicating global/local state
423 @type bool
335 """ 424 """
336 if showGlobals: 425 if showGlobals:
337 self.globalsViewer.showVariables(vlist, self.framenr) 426 self.globalsViewer.showVariables(vlist, self.framenr)
338 else: 427 else:
339 self.localsViewer.showVariables(vlist, self.framenr) 428 self.localsViewer.showVariables(vlist, self.framenr)
341 def showVariable(self, vlist, showGlobals): 430 def showVariable(self, vlist, showGlobals):
342 """ 431 """
343 Public method to show the variables in the respective window. 432 Public method to show the variables in the respective window.
344 433
345 @param vlist list of variables to display 434 @param vlist list of variables to display
435 @type list
346 @param showGlobals flag indicating global/local state 436 @param showGlobals flag indicating global/local state
437 @type bool
347 """ 438 """
348 if showGlobals: 439 if showGlobals:
349 self.globalsViewer.showVariable(vlist) 440 self.globalsViewer.showVariable(vlist)
350 else: 441 else:
351 self.localsViewer.showVariable(vlist) 442 self.localsViewer.showVariable(vlist)
353 def showVariablesTab(self, showGlobals): 444 def showVariablesTab(self, showGlobals):
354 """ 445 """
355 Public method to make a variables tab visible. 446 Public method to make a variables tab visible.
356 447
357 @param showGlobals flag indicating global/local state 448 @param showGlobals flag indicating global/local state
449 @type bool
358 """ 450 """
359 if showGlobals: 451 if showGlobals:
360 self.__tabWidget.setCurrentWidget(self.glvWidget) 452 self.__tabWidget.setCurrentWidget(self.glvWidget)
361 else: 453 else:
362 self.__tabWidget.setCurrentWidget(self.lvWidget) 454 self.__tabWidget.setCurrentWidget(self.lvWidget)
363 455
364 def handleClientStack(self, stack): 456 def handleClientStack(self, stack, debuggerId):
365 """ 457 """
366 Public slot to show the call stack of the program being debugged. 458 Public slot to show the call stack of the program being debugged.
367 459
368 @param stack list of tuples with call stack data (file name, 460 @param stack list of tuples with call stack data (file name,
369 line number, function name, formatted argument/values list) 461 line number, function name, formatted argument/values list)
370 """ 462 @type list of tuples of (str, str, str, str)
371 block = self.stackComboBox.blockSignals(True) 463 @param debuggerId ID of the debugger backend
372 self.framenr = 0 464 @type str
373 self.stackComboBox.clear() 465 """
374 self.currentStack = stack 466 if debuggerId == self.getSelectedDebuggerId():
375 self.sourceButton.setEnabled(len(stack) > 0) 467 block = self.stackComboBox.blockSignals(True)
376 for s in stack: 468 self.framenr = 0
377 # just show base filename to make it readable 469 self.stackComboBox.clear()
378 s = (os.path.basename(s[0]), s[1], s[2]) 470 self.currentStack = stack
379 self.stackComboBox.addItem('{0}:{1}:{2}'.format(*s)) 471 self.sourceButton.setEnabled(len(stack) > 0)
380 self.stackComboBox.blockSignals(block) 472 for s in stack:
381 473 # just show base filename to make it readable
474 s = (os.path.basename(s[0]), s[1], s[2])
475 self.stackComboBox.addItem('{0}:{1}:{2}'.format(*s))
476 self.stackComboBox.blockSignals(block)
477
478 def __clientLine(self, fn, line, debuggerId, threadName):
479 """
480 Private method to handle a change to the current line.
481
482 @param fn filename
483 @type str
484 @param line linenumber
485 @type int
486 @param debuggerId ID of the debugger backend
487 @type str
488 @param threadName name of the thread signaling the event
489 @type str
490 """
491 self.__setDebuggerIconAndState(debuggerId, "broken")
492 self.__setThreadIconAndState(debuggerId, threadName, "broken")
493 if debuggerId != self.getSelectedDebuggerId():
494 self.__setCurrentDebugger(debuggerId)
495
496 @pyqtSlot(str, int, str, bool, str)
497 def __clientExit(self, program, status, message, quiet, debuggerId):
498 """
499 Private method to handle the debugged program terminating.
500
501 @param program name of the exited program
502 @type str
503 @param status exit code of the debugged program
504 @type int
505 @param message exit message of the debugged program
506 @type str
507 @param quiet flag indicating to suppress exit info display
508 @type bool
509 @param debuggerId ID of the debugger backend
510 @type str
511 """
512 if not self.isOnlyDebugger():
513 if debuggerId == self.getSelectedDebuggerId():
514 # the current client has exited
515 self.globalsViewer.handleResetUI()
516 self.localsViewer.handleResetUI()
517 self.setGlobalsFilter()
518 self.setLocalsFilter()
519 self.sourceButton.setEnabled(False)
520 self.currentStack = None
521 self.stackComboBox.clear()
522
523 self.__removeDebugger(debuggerId)
524
525 def __clientSyntaxError(self, message, filename, lineNo, characterNo,
526 debuggerId, threadName):
527 """
528 Private method to handle a syntax error in the debugged program.
529
530 @param message message of the syntax error
531 @type str
532 @param filename translated filename of the syntax error position
533 @type str
534 @param lineNo line number of the syntax error position
535 @type int
536 @param characterNo character number of the syntax error position
537 @type int
538 @param debuggerId ID of the debugger backend
539 @type str
540 @param threadName name of the thread signaling the event
541 @type str
542 """
543 self.__setDebuggerIconAndState(debuggerId, "syntax")
544 self.__setThreadIconAndState(debuggerId, threadName, "syntax")
545
546 def __clientException(self, exceptionType, exceptionMessage, stackTrace,
547 debuggerId, threadName):
548 """
549 Private method to handle an exception of the debugged program.
550
551 @param exceptionType type of exception raised
552 @type str
553 @param exceptionMessage message given by the exception
554 @type (str
555 @param stackTrace list of stack entries
556 @type list of str
557 @param debuggerId ID of the debugger backend
558 @type str
559 @param threadName name of the thread signaling the event
560 @type str
561 """
562 self.__setDebuggerIconAndState(debuggerId, "exception")
563 self.__setThreadIconAndState(debuggerId, threadName, "exception")
564
382 def setVariablesFilter(self, globalsFilter, localsFilter): 565 def setVariablesFilter(self, globalsFilter, localsFilter):
383 """ 566 """
384 Public slot to set the local variables filter. 567 Public slot to set the local variables filter.
385 568
386 @param globalsFilter filter list for global variable types 569 @param globalsFilter filter list for global variable types
387 (list of int) 570 @type list of str
388 @param localsFilter filter list for local variable types (list of int) 571 @param localsFilter filter list for local variable types
389 """ 572 @type list of str
390 self.globalsFilter = globalsFilter 573 """
391 self.localsFilter = localsFilter 574 self.__globalsFilter = globalsFilter
575 self.__localsFilter = localsFilter
392 576
393 def __showSource(self): 577 def __showSource(self):
394 """ 578 """
395 Private slot to handle the source button press to show the selected 579 Private slot to handle the source button press to show the selected
396 file. 580 file.
402 586
403 def __frameSelected(self, frmnr): 587 def __frameSelected(self, frmnr):
404 """ 588 """
405 Private slot to handle the selection of a new stack frame number. 589 Private slot to handle the selection of a new stack frame number.
406 590
407 @param frmnr frame number (0 is the current frame) (int) 591 @param frmnr frame number (0 is the current frame)
408 """ 592 @type int
409 self.framenr = frmnr 593 """
410 if self.debugServer.isDebugging(): 594 if frmnr >= 0:
411 self.debugServer.remoteClientVariables(0, self.localsFilter, frmnr) 595 self.framenr = frmnr
412 596 if self.debugServer.isDebugging():
413 if self.__autoViewSource: 597 self.debugServer.remoteClientVariables(
414 self.__showSource() 598 self.getSelectedDebuggerId(), 0, self.__localsFilter,
599 frmnr)
600
601 if self.__autoViewSource:
602 self.__showSource()
415 603
416 def setGlobalsFilter(self): 604 def setGlobalsFilter(self):
417 """ 605 """
418 Public slot to set the global variable filter. 606 Public slot to set the global variable filter.
419 """ 607 """
420 if self.debugServer.isDebugging(): 608 if self.debugServer.isDebugging():
421 filterStr = self.globalsFilterEdit.text() 609 filterStr = self.globalsFilterEdit.text()
422 self.debugServer.remoteClientSetFilter(1, filterStr) 610 self.debugServer.remoteClientSetFilter(
423 self.debugServer.remoteClientVariables(2, self.globalsFilter) 611 self.getSelectedDebuggerId(), 1, filterStr)
612 self.debugServer.remoteClientVariables(
613 self.getSelectedDebuggerId(), 2, self.__globalsFilter)
424 614
425 def setLocalsFilter(self): 615 def setLocalsFilter(self):
426 """ 616 """
427 Public slot to set the local variable filter. 617 Public slot to set the local variable filter.
428 """ 618 """
429 if self.debugServer.isDebugging(): 619 if self.debugServer.isDebugging():
430 filterStr = self.localsFilterEdit.text() 620 filterStr = self.localsFilterEdit.text()
431 self.debugServer.remoteClientSetFilter(0, filterStr) 621 self.debugServer.remoteClientSetFilter(
622 self.getSelectedDebuggerId(), 0, filterStr)
432 if self.currentStack: 623 if self.currentStack:
433 self.debugServer.remoteClientVariables( 624 self.debugServer.remoteClientVariables(
434 0, self.localsFilter, self.framenr) 625 self.getSelectedDebuggerId(), 0, self.__localsFilter,
626 self.framenr)
435 627
436 def handleDebuggingStarted(self): 628 def handleDebuggingStarted(self):
437 """ 629 """
438 Public slot to handle the start of a debugging session. 630 Public slot to handle the start of a debugging session.
439 631
447 639
448 def currentWidget(self): 640 def currentWidget(self):
449 """ 641 """
450 Public method to get a reference to the current widget. 642 Public method to get a reference to the current widget.
451 643
452 @return reference to the current widget (QWidget) 644 @return reference to the current widget
645 @rtype QWidget
453 """ 646 """
454 return self.__tabWidget.currentWidget() 647 return self.__tabWidget.currentWidget()
455 648
456 def setCurrentWidget(self, widget): 649 def setCurrentWidget(self, widget):
457 """ 650 """
458 Public slot to set the current page based on the given widget. 651 Public slot to set the current page based on the given widget.
459 652
460 @param widget reference to the widget (QWidget) 653 @param widget reference to the widget
654 @type QWidget
461 """ 655 """
462 self.__tabWidget.setCurrentWidget(widget) 656 self.__tabWidget.setCurrentWidget(widget)
463
464 def showThreadList(self, currentID, threadList):
465 """
466 Public method to show the thread list.
467
468 @param currentID id of the current thread (integer)
469 @param threadList list of dictionaries containing the thread data
470 """
471 citm = None
472
473 self.__threadList.clear()
474 for thread in threadList:
475 if thread['broken']:
476 state = self.tr("waiting at breakpoint")
477 else:
478 state = self.tr("running")
479 itm = QTreeWidgetItem(self.__threadList,
480 ["{0:d}".format(thread['id']),
481 thread['name'], state])
482 if thread['id'] == currentID:
483 citm = itm
484
485 self.__threadList.header().resizeSections(QHeaderView.ResizeToContents)
486 self.__threadList.header().setStretchLastSection(True)
487
488 if citm:
489 self.__doThreadListUpdate = False
490 self.__threadList.setCurrentItem(citm)
491 self.__doThreadListUpdate = True
492
493 def __threadSelected(self, current, previous):
494 """
495 Private slot to handle the selection of a thread in the thread list.
496
497 @param current reference to the new current item (QTreeWidgetItem)
498 @param previous reference to the previous current item
499 (QTreeWidgetItem)
500 """
501 if current is not None and self.__doThreadListUpdate:
502 tid = int(current.text(0))
503 self.debugServer.remoteSetThread(tid)
504 657
505 def __callStackFrameSelected(self, frameNo): 658 def __callStackFrameSelected(self, frameNo):
506 """ 659 """
507 Private slot to handle the selection of a call stack entry of the 660 Private slot to handle the selection of a call stack entry of the
508 call stack viewer. 661 call stack viewer.
509 662
510 @param frameNo frame number (index) of the selected entry (integer) 663 @param frameNo frame number (index) of the selected entry
664 @type int
511 """ 665 """
512 if frameNo >= 0: 666 if frameNo >= 0:
513 self.stackComboBox.setCurrentIndex(frameNo) 667 self.stackComboBox.setCurrentIndex(frameNo)
668
669 def __debuggerSelected(self, current, previous):
670 """
671 Private slot to handle the selection of a debugger backend in the
672 debuggers list.
673
674 @param current reference to the new current item
675 @type QTreeWidgetItem
676 @param previous reference to the previous current item
677 @type QTreeWidgetItem
678 """
679 if current is not None and self.__doDebuggersListUpdate:
680 if current.parent() is None:
681 # it is a debugger item
682 debuggerId = current.text(0)
683 self.globalsViewer.handleResetUI()
684 self.localsViewer.handleResetUI()
685 self.currentStack = None
686 self.stackComboBox.clear()
687 self.callStackViewer.clear()
688
689 self.debugServer.remoteSetThread(debuggerId, -1)
690 self.__showSource()
691 else:
692 # it is a thread item
693 tid = current.data(0, self.ThreadIdRole)
694 self.debugServer.remoteSetThread(
695 self.getSelectedDebuggerId(), tid)
696
697 def __clientDebuggerId(self, debuggerId):
698 """
699 Private slot to receive the ID of a newly connected debugger backend.
700
701 @param debuggerId ID of a newly connected debugger backend
702 @type str
703 """
704 itm = QTreeWidgetItem(self.__debuggersList, [debuggerId])
705 if self.__debuggersList.topLevelItemCount() > 1:
706 self.debugUI.showNotification(
707 self.tr("<p>Debugger with ID <b>{0}</b> has been connected."
708 "</p>")
709 .format(debuggerId))
710
711 self.__debuggersList.header().resizeSections(
712 QHeaderView.ResizeToContents)
713
714 if self.__debuggersList.topLevelItemCount() == 1:
715 # it is the only item, select it as the current one
716 self.__debuggersList.setCurrentItem(itm)
717
718 def __setCurrentDebugger(self, debuggerId):
719 """
720 Private method to set the current debugger based on the given ID.
721
722 @param debuggerId ID of the debugger to set as current debugger
723 @type str
724 """
725 debuggerItems = self.__debuggersList.findItems(
726 debuggerId, Qt.MatchExactly)
727 if debuggerItems:
728 debuggerItem = debuggerItems[0]
729 currentItem = self.__debuggersList.currentItem()
730 if currentItem is debuggerItem:
731 # nothing to do
732 return
733
734 if currentItem:
735 currentParent = currentItem.parent()
736 else:
737 currentParent = None
738 if currentParent is None:
739 # current is a debugger item
740 self.__debuggersList.setCurrentItem(debuggerItem)
741 elif currentParent is debuggerItem:
742 # nothing to do
743 return
744 else:
745 self.__debuggersList.setCurrentItem(debuggerItem)
746
747 def isOnlyDebugger(self):
748 """
749 Public method to test, if only one debugger is connected.
750
751 @return flag indicating that only one debugger is connected
752 @rtype bool
753 """
754 return self.__debuggersList.topLevelItemCount() == 1
755
756 def getSelectedDebuggerId(self):
757 """
758 Public method to get the currently selected debugger ID.
759
760 @return selected debugger ID
761 @rtype str
762 """
763 itm = self.__debuggersList.currentItem()
764 if itm:
765 if itm.parent() is None:
766 # it is a debugger item
767 return itm.text(0)
768 else:
769 # it is a thread item
770 return itm.parent().text(0)
771 else:
772 return ""
773
774 def getSelectedDebuggerState(self):
775 """
776 Public method to get the currently selected debugger's state.
777
778 @return selected debugger's state (broken, exception, running)
779 @rtype str
780 """
781 itm = self.__debuggersList.currentItem()
782 if itm:
783 if itm.parent() is None:
784 # it is a debugger item
785 return itm.data(0, self.DebuggerStateRole)
786 else:
787 # it is a thread item
788 return itm.parent().data(0, self.DebuggerStateRole)
789 else:
790 return ""
791
792 def __setDebuggerIconAndState(self, debuggerId, state):
793 """
794 Private method to set the icon for a specific debugger ID.
795
796 @param debuggerId ID of the debugger backend (empty ID means the
797 currently selected one)
798 @type str
799 @param state state of the debugger (broken, exception, running)
800 @type str
801 """
802 debuggerItem = None
803 if debuggerId:
804 foundItems = self.__debuggersList.findItems(
805 debuggerId, Qt.MatchExactly)
806 if foundItems:
807 debuggerItem = foundItems[0]
808 if debuggerItem is None:
809 debuggerItem = self.__debuggersList.currentItem()
810 if debuggerItem is not None:
811 try:
812 iconName = DebugViewer.StateIcon[state]
813 except KeyError:
814 iconName = "question"
815 try:
816 stateText = DebugViewer.StateMessage[state]
817 except KeyError:
818 stateText = self.tr("unknown state ({0})").format(state)
819 debuggerItem.setIcon(0, UI.PixmapCache.getIcon(iconName))
820 debuggerItem.setData(0, self.DebuggerStateRole, state)
821 debuggerItem.setText(1, stateText)
822
823 self.__debuggersList.header().resizeSections(
824 QHeaderView.ResizeToContents)
825
826 def __removeDebugger(self, debuggerId):
827 """
828 Private method to remove a debugger given its ID.
829
830 @param debuggerId ID of the debugger to be removed from the list
831 @type str
832 """
833 foundItems = self.__debuggersList.findItems(
834 debuggerId, Qt.MatchExactly)
835 if foundItems:
836 index = self.__debuggersList.indexOfTopLevelItem(foundItems[0])
837 itm = self.__debuggersList.takeTopLevelItem(index)
838 # __IGNORE_WARNING__
839 del itm
840
841 def __addThreadList(self, currentID, threadList, debuggerId):
842 """
843 Private method to add the list of threads to a debugger entry.
844
845 @param currentID id of the current thread
846 @type int
847 @param threadList list of dictionaries containing the thread data
848 @type list of dict
849 @param debuggerId ID of the debugger backend
850 @type str
851 """
852 debugStatus = -1 # i.e. running
853
854 debuggerItems = self.__debuggersList.findItems(
855 debuggerId, Qt.MatchExactly)
856 if debuggerItems:
857 debuggerItem = debuggerItems[0]
858
859 currentItem = self.__debuggersList.currentItem()
860 if currentItem.parent() is debuggerItem:
861 currentChild = currentItem.text(0)
862 else:
863 currentChild = ""
864 self.__doDebuggersListUpdate = False
865 debuggerItem.takeChildren()
866 for thread in threadList:
867 if thread.get('except', False):
868 stateText = DebugViewer.StateMessage["exception"]
869 iconName = DebugViewer.StateIcon["exception"]
870 debugStatus = 1
871 elif thread['broken']:
872 stateText = DebugViewer.StateMessage["broken"]
873 iconName = DebugViewer.StateIcon["broken"]
874 if debugStatus < 1:
875 debugStatus = 0
876 else:
877 stateText = DebugViewer.StateMessage["running"]
878 iconName = DebugViewer.StateIcon["running"]
879 itm = QTreeWidgetItem(debuggerItem,
880 [thread['name'], stateText])
881 itm.setData(0, self.ThreadIdRole, thread['id'])
882 itm.setIcon(0, UI.PixmapCache.getIcon(iconName))
883 if currentChild == thread['name']:
884 self.__debuggersList.setCurrentItem(itm)
885 if thread['id'] == currentID:
886 font = debuggerItem.font(0)
887 font.setItalic(True)
888 itm.setFont(0, font)
889
890 debuggerItem.setExpanded(debuggerItem.childCount() > 0)
891
892 self.__debuggersList.header().resizeSections(
893 QHeaderView.ResizeToContents)
894 self.__debuggersList.header().setStretchLastSection(True)
895 self.__doDebuggersListUpdate = True
896
897 if debugStatus == -1:
898 debuggerState = "running"
899 elif debugStatus == 0:
900 debuggerState = "broken"
901 else:
902 debuggerState = "exception"
903 self.__setDebuggerIconAndState(debuggerId, debuggerState)
904
905 def __setThreadIconAndState(self, debuggerId, threadName, state):
906 """
907 Private method to set the icon for a specific thread name and
908 debugger ID.
909
910 @param debuggerId ID of the debugger backend (empty ID means the
911 currently selected one)
912 @type str
913 @param threadName name of the thread signaling the event
914 @type str
915 @param state state of the debugger (broken, exception, running)
916 @type str
917 """
918 debuggerItem = None
919 if debuggerId:
920 foundItems = self.__debuggersList.findItems(
921 debuggerId, Qt.MatchExactly)
922 if foundItems:
923 debuggerItem = foundItems[0]
924 if debuggerItem is None:
925 debuggerItem = self.__debuggersList.currentItem()
926 if debuggerItem is not None:
927 for index in range(debuggerItem.childCount()):
928 childItem = debuggerItem.child(index)
929 if childItem.text(0) == threadName:
930 break
931 else:
932 childItem = None
933
934 if childItem is not None:
935 try:
936 iconName = DebugViewer.StateIcon[state]
937 except KeyError:
938 iconName = "question"
939 try:
940 stateText = DebugViewer.StateMessage[state]
941 except KeyError:
942 stateText = self.tr("unknown state ({0})").format(state)
943 childItem.setIcon(0, UI.PixmapCache.getIcon(iconName))
944 childItem.setText(1, stateText)
945
946 self.__debuggersList.header().resizeSections(
947 QHeaderView.ResizeToContents)

eric ide

mercurial