eric6/Debugger/DebugViewer.py

changeset 7986
2971d5d19951
parent 7933
ca1b44d522b9
child 8043
0acf98cd089a
child 8143
2c730d5fd177
equal deleted inserted replaced
7985:3a1ca2452c30 7986:2971d5d19951
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)

eric ide

mercurial