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>selector showing all connected debugger backends</li> |
11 <li>selector showing all connected debugger backends with associated |
|
12 threads</li> |
12 <li>variables viewer for global variables for the selected debug client</li> |
13 <li>variables viewer for global variables for the selected debug client</li> |
13 <li>variables viewer for local variables for the selected debug client</li> |
14 <li>variables viewer for local variables for the selected debug client</li> |
14 <li>call stack viewer for the selected debug client</li> |
15 <li>call stack viewer for the selected debug client</li> |
15 <li>call trace viewer</li> |
16 <li>call trace viewer</li> |
16 <li>viewer for breakpoints</li> |
17 <li>viewer for breakpoints</li> |
17 <li>viewer for watch expressions</li> |
18 <li>viewer for watch expressions</li> |
18 <li>viewer for exceptions</li> |
19 <li>viewer for exceptions</li> |
19 <li>viewer for threads for the selected debug client</li> |
20 <li>viewer for a code disassembly for an exception<li> |
20 </ul> |
21 </ul> |
21 """ |
22 """ |
22 |
23 |
23 import os |
24 import os |
24 |
25 |
25 from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt |
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, QSplitter |
29 QComboBox, QLabel, QTreeWidget, QTreeWidgetItem, QHeaderView, QSplitter |
29 ) |
30 ) |
30 |
31 |
49 sourceFile = pyqtSignal(str, int) |
50 sourceFile = pyqtSignal(str, int) |
50 preferencesChanged = pyqtSignal() |
51 preferencesChanged = pyqtSignal() |
51 |
52 |
52 ThreadIdRole = Qt.UserRole + 1 |
53 ThreadIdRole = Qt.UserRole + 1 |
53 DebuggerStateRole = Qt.UserRole + 2 |
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 } |
54 |
75 |
55 def __init__(self, debugServer, parent=None): |
76 def __init__(self, debugServer, parent=None): |
56 """ |
77 """ |
57 Constructor |
78 Constructor |
58 |
79 |
452 # just show base filename to make it readable |
473 # just show base filename to make it readable |
453 s = (os.path.basename(s[0]), s[1], s[2]) |
474 s = (os.path.basename(s[0]), s[1], s[2]) |
454 self.stackComboBox.addItem('{0}:{1}:{2}'.format(*s)) |
475 self.stackComboBox.addItem('{0}:{1}:{2}'.format(*s)) |
455 self.stackComboBox.blockSignals(block) |
476 self.stackComboBox.blockSignals(block) |
456 |
477 |
457 def __clientLine(self, fn, line, debuggerId): |
478 def __clientLine(self, fn, line, debuggerId, threadName): |
458 """ |
479 """ |
459 Private method to handle a change to the current line. |
480 Private method to handle a change to the current line. |
460 |
481 |
461 @param fn filename |
482 @param fn filename |
462 @type str |
483 @type str |
463 @param line linenumber |
484 @param line linenumber |
464 @type int |
485 @type int |
465 @param debuggerId ID of the debugger backend |
486 @param debuggerId ID of the debugger backend |
466 @type str |
487 @type str |
467 """ |
488 @param threadName name of the thread signaling the event |
468 self.__setDebuggerIconAndState(debuggerId, "break", "broken") |
489 @type str |
|
490 """ |
|
491 self.__setDebuggerIconAndState(debuggerId, "broken") |
|
492 self.__setThreadIconAndState(debuggerId, threadName, "broken") |
469 if debuggerId != self.getSelectedDebuggerId(): |
493 if debuggerId != self.getSelectedDebuggerId(): |
470 self.__setCurrentDebugger(debuggerId) |
494 self.__setCurrentDebugger(debuggerId) |
471 |
495 |
472 @pyqtSlot(str, int, str, bool, str) |
496 @pyqtSlot(str, int, str, bool, str) |
473 def __clientExit(self, program, status, message, quiet, debuggerId): |
497 def __clientExit(self, program, status, message, quiet, debuggerId): |
497 self.stackComboBox.clear() |
521 self.stackComboBox.clear() |
498 |
522 |
499 self.__removeDebugger(debuggerId) |
523 self.__removeDebugger(debuggerId) |
500 |
524 |
501 def __clientSyntaxError(self, message, filename, lineNo, characterNo, |
525 def __clientSyntaxError(self, message, filename, lineNo, characterNo, |
502 debuggerId): |
526 debuggerId, threadName): |
503 """ |
527 """ |
504 Private method to handle a syntax error in the debugged program. |
528 Private method to handle a syntax error in the debugged program. |
505 |
529 |
506 @param message message of the syntax error |
530 @param message message of the syntax error |
507 @type str |
531 @type str |
511 @type int |
535 @type int |
512 @param characterNo character number of the syntax error position |
536 @param characterNo character number of the syntax error position |
513 @type int |
537 @type int |
514 @param debuggerId ID of the debugger backend |
538 @param debuggerId ID of the debugger backend |
515 @type str |
539 @type str |
516 """ |
540 @param threadName name of the thread signaling the event |
517 self.__setDebuggerIconAndState(debuggerId, "syntaxError22", |
541 @type str |
518 "exception") |
542 """ |
|
543 self.__setDebuggerIconAndState(debuggerId, "syntax") |
|
544 self.__setThreadIconAndState(debuggerId, threadName, "syntax") |
519 |
545 |
520 def __clientException(self, exceptionType, exceptionMessage, stackTrace, |
546 def __clientException(self, exceptionType, exceptionMessage, stackTrace, |
521 debuggerId): |
547 debuggerId, threadName): |
522 """ |
548 """ |
523 Private method to handle an exception of the debugged program. |
549 Private method to handle an exception of the debugged program. |
524 |
550 |
525 @param exceptionType type of exception raised |
551 @param exceptionType type of exception raised |
526 @type str |
552 @type str |
528 @type (str |
554 @type (str |
529 @param stackTrace list of stack entries |
555 @param stackTrace list of stack entries |
530 @type list of str |
556 @type list of str |
531 @param debuggerId ID of the debugger backend |
557 @param debuggerId ID of the debugger backend |
532 @type str |
558 @type str |
533 """ |
559 @param threadName name of the thread signaling the event |
534 self.__setDebuggerIconAndState(debuggerId, "exceptions", "exception") |
560 @type str |
|
561 """ |
|
562 self.__setDebuggerIconAndState(debuggerId, "exception") |
|
563 self.__setThreadIconAndState(debuggerId, threadName, "exception") |
535 |
564 |
536 def setVariablesFilter(self, globalsFilter, localsFilter): |
565 def setVariablesFilter(self, globalsFilter, localsFilter): |
537 """ |
566 """ |
538 Public slot to set the local variables filter. |
567 Public slot to set the local variables filter. |
539 |
568 |
758 # it is a thread item |
787 # it is a thread item |
759 return itm.parent().data(0, self.DebuggerStateRole) |
788 return itm.parent().data(0, self.DebuggerStateRole) |
760 else: |
789 else: |
761 return "" |
790 return "" |
762 |
791 |
763 def __setDebuggerIconAndState(self, debuggerId, iconName, state): |
792 def __setDebuggerIconAndState(self, debuggerId, state): |
764 """ |
793 """ |
765 Private method to set the icon for a specific debugger ID. |
794 Private method to set the icon for a specific debugger ID. |
766 |
795 |
767 @param debuggerId ID of the debugger backend (empty ID means the |
796 @param debuggerId ID of the debugger backend (empty ID means the |
768 currently selected one) |
797 currently selected one) |
769 @type str |
|
770 @param iconName name of the icon to be used |
|
771 @type str |
798 @type str |
772 @param state state of the debugger (broken, exception, running) |
799 @param state state of the debugger (broken, exception, running) |
773 @type str |
800 @type str |
774 """ |
801 """ |
775 debuggerItem = None |
802 debuggerItem = None |
779 if foundItems: |
806 if foundItems: |
780 debuggerItem = foundItems[0] |
807 debuggerItem = foundItems[0] |
781 if debuggerItem is None: |
808 if debuggerItem is None: |
782 debuggerItem = self.__debuggersList.currentItem() |
809 debuggerItem = self.__debuggersList.currentItem() |
783 if debuggerItem is not None: |
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) |
784 debuggerItem.setIcon(0, UI.PixmapCache.getIcon(iconName)) |
819 debuggerItem.setIcon(0, UI.PixmapCache.getIcon(iconName)) |
785 debuggerItem.setData(0, self.DebuggerStateRole, state) |
820 debuggerItem.setData(0, self.DebuggerStateRole, state) |
786 if state == "broken": |
821 debuggerItem.setText(1, stateText) |
787 debuggerItem.setText(1, self.tr("waiting at breakpoint")) |
|
788 elif state == "exception": |
|
789 debuggerItem.setText(1, self.tr("waiting at exception")) |
|
790 elif state == "running": |
|
791 debuggerItem.setText(1, self.tr("running")) |
|
792 else: |
|
793 debuggerItem.setText( |
|
794 1, self.tr("unknown state ({0})").format(state)) |
|
795 |
822 |
796 self.__debuggersList.header().resizeSections( |
823 self.__debuggersList.header().resizeSections( |
797 QHeaderView.ResizeToContents) |
824 QHeaderView.ResizeToContents) |
798 |
825 |
799 def __removeDebugger(self, debuggerId): |
826 def __removeDebugger(self, debuggerId): |
836 currentChild = "" |
863 currentChild = "" |
837 self.__doDebuggersListUpdate = False |
864 self.__doDebuggersListUpdate = False |
838 debuggerItem.takeChildren() |
865 debuggerItem.takeChildren() |
839 for thread in threadList: |
866 for thread in threadList: |
840 if thread.get('except', False): |
867 if thread.get('except', False): |
841 state = self.tr("waiting at exception") |
868 stateText = DebugViewer.StateMessage["exception"] |
842 icon = "exceptions" |
869 iconName = DebugViewer.StateIcon["exception"] |
843 debugStatus = 1 |
870 debugStatus = 1 |
844 elif thread['broken']: |
871 elif thread['broken']: |
845 state = self.tr("waiting at breakpoint") |
872 stateText = DebugViewer.StateMessage["broken"] |
846 icon = "break" |
873 iconName = DebugViewer.StateIcon["broken"] |
847 if debugStatus < 1: |
874 if debugStatus < 1: |
848 debugStatus = 0 |
875 debugStatus = 0 |
849 else: |
876 else: |
850 state = self.tr("running") |
877 stateText = DebugViewer.StateMessage["running"] |
851 icon = "mediaPlaybackStart" |
878 iconName = DebugViewer.StateIcon["running"] |
852 itm = QTreeWidgetItem(debuggerItem, [thread['name'], state]) |
879 itm = QTreeWidgetItem(debuggerItem, |
|
880 [thread['name'], stateText]) |
853 itm.setData(0, self.ThreadIdRole, thread['id']) |
881 itm.setData(0, self.ThreadIdRole, thread['id']) |
854 itm.setIcon(0, UI.PixmapCache.getIcon(icon)) |
882 itm.setIcon(0, UI.PixmapCache.getIcon(iconName)) |
855 if currentChild == thread['name']: |
883 if currentChild == thread['name']: |
856 self.__debuggersList.setCurrentItem(itm) |
884 self.__debuggersList.setCurrentItem(itm) |
857 if thread['id'] == currentID: |
885 if thread['id'] == currentID: |
858 font = debuggerItem.font(0) |
886 font = debuggerItem.font(0) |
859 font.setItalic(True) |
887 font.setItalic(True) |
865 QHeaderView.ResizeToContents) |
893 QHeaderView.ResizeToContents) |
866 self.__debuggersList.header().setStretchLastSection(True) |
894 self.__debuggersList.header().setStretchLastSection(True) |
867 self.__doDebuggersListUpdate = True |
895 self.__doDebuggersListUpdate = True |
868 |
896 |
869 if debugStatus == -1: |
897 if debugStatus == -1: |
870 icon = "mediaPlaybackStart" |
898 debuggerState = "running" |
871 state = "running" |
|
872 elif debugStatus == 0: |
899 elif debugStatus == 0: |
873 icon = "break" |
900 debuggerState = "broken" |
874 state = "broken" |
|
875 else: |
901 else: |
876 icon = "exceptions" |
902 debuggerState = "exception" |
877 state = "exception" |
903 self.__setDebuggerIconAndState(debuggerId, debuggerState) |
878 self.__setDebuggerIconAndState(debuggerId, icon, state) |
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) |