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) |
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 """ |
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) |