eric6/Debugger/DebugViewer.py

changeset 7920
322d1ee01510
parent 7915
e68f5c568aee
child 7923
91e843545d9a
equal deleted inserted replaced
7917:e04b7a25e241 7920:322d1ee01510
48 """ 48 """
49 sourceFile = pyqtSignal(str, int) 49 sourceFile = pyqtSignal(str, int)
50 preferencesChanged = pyqtSignal() 50 preferencesChanged = pyqtSignal()
51 51
52 ThreadIdRole = Qt.UserRole + 1 52 ThreadIdRole = Qt.UserRole + 1
53 DebuggerStateRole = Qt.UserRole + 2
53 54
54 def __init__(self, debugServer, parent=None): 55 def __init__(self, debugServer, parent=None):
55 """ 56 """
56 Constructor 57 Constructor
57 58
76 77
77 # add the viewer showing the connected debug backends 78 # add the viewer showing the connected debug backends
78 self.__debuggersWidget = QWidget() 79 self.__debuggersWidget = QWidget()
79 self.__debuggersLayout = QVBoxLayout(self.__debuggersWidget) 80 self.__debuggersLayout = QVBoxLayout(self.__debuggersWidget)
80 self.__debuggersLayout.setContentsMargins(0, 0, 0, 0) 81 self.__debuggersLayout.setContentsMargins(0, 0, 0, 0)
81 self.__debuggersLayout.addWidget(QLabel(self.tr("Debuggers:"))) 82 self.__debuggersLayout.addWidget(
83 QLabel(self.tr("Debuggers and Threads:")))
82 self.__debuggersList = QTreeWidget() 84 self.__debuggersList = QTreeWidget()
83 self.__debuggersList.setHeaderLabels( 85 self.__debuggersList.setHeaderLabels(
84 [self.tr("ID"), self.tr("State"), ""]) 86 [self.tr("ID"), self.tr("State"), ""])
85 self.__debuggersList.header().setStretchLastSection(True) 87 self.__debuggersList.header().setStretchLastSection(True)
86 self.__debuggersList.setSortingEnabled(True) 88 self.__debuggersList.setSortingEnabled(True)
87 self.__debuggersList.setRootIsDecorated(False) 89 self.__debuggersList.setRootIsDecorated(True)
88 self.__debuggersList.setAlternatingRowColors(True) 90 self.__debuggersList.setAlternatingRowColors(True)
89 self.__debuggersLayout.addWidget(self.__debuggersList) 91 self.__debuggersLayout.addWidget(self.__debuggersList)
90 self.__mainSplitter.addWidget(self.__debuggersWidget) 92 self.__mainSplitter.addWidget(self.__debuggersWidget)
91 93
92 self.__debuggersList.currentItemChanged.connect( 94 self.__debuggersList.currentItemChanged.connect(
269 index, 271 index,
270 self.tr("Shows a code disassembly in case of an exception.")) 272 self.tr("Shows a code disassembly in case of an exception."))
271 273
272 self.__tabWidget.setCurrentWidget(self.glvWidget) 274 self.__tabWidget.setCurrentWidget(self.glvWidget)
273 275
274 # add the threads viewer 276 self.__doDebuggersListUpdate = True
275 self.__threadWidget = QWidget() 277
276 self.__threadWidgetLayout = QVBoxLayout(self.__threadWidget) 278 self.__mainSplitter.setSizes([100, 700])
277 self.__threadWidgetLayout.setContentsMargins(0, 0, 0, 0)
278 self.__threadWidgetLayout.addWidget(QLabel(self.tr("Threads:")))
279 self.__threadList = QTreeWidget()
280 self.__threadList.setHeaderLabels(
281 [self.tr("Name"), self.tr("State"), ""])
282 self.__threadList.setSortingEnabled(True)
283 self.__threadList.setRootIsDecorated(False)
284 self.__threadList.setAlternatingRowColors(True)
285 self.__threadWidgetLayout.addWidget(self.__threadList)
286 self.__mainSplitter.addWidget(self.__threadWidget)
287
288 self.__doThreadListUpdate = True
289
290 self.__threadList.currentItemChanged.connect(self.__threadSelected)
291
292 self.__mainSplitter.setSizes([50, 700, 50])
293 279
294 self.currentStack = None 280 self.currentStack = None
295 self.framenr = 0 281 self.framenr = 0
296 282
297 self.__autoViewSource = Preferences.getDebugger("AutoViewSourceCode") 283 self.__autoViewSource = Preferences.getDebugger("AutoViewSourceCode")
298 self.sourceButton.setVisible(not self.__autoViewSource) 284 self.sourceButton.setVisible(not self.__autoViewSource)
299 285
300 # connect somer debug server signals 286 # connect some debug server signals
301 self.debugServer.clientStack.connect( 287 self.debugServer.clientStack.connect(
302 self.handleClientStack) 288 self.handleClientStack)
303 self.debugServer.clientThreadList.connect( 289 self.debugServer.clientThreadList.connect(
304 self.showThreadList) 290 self.__addThreadList)
305 self.debugServer.clientDebuggerId.connect( 291 self.debugServer.clientDebuggerId.connect(
306 self.__clientDebuggerId) 292 self.__clientDebuggerId)
307 self.debugServer.passiveDebugStarted.connect( 293 self.debugServer.passiveDebugStarted.connect(
308 self.handleDebuggingStarted) 294 self.handleDebuggingStarted)
309 self.debugServer.clientLine.connect( 295 self.debugServer.clientLine.connect(
356 self.setGlobalsFilter() 342 self.setGlobalsFilter()
357 self.setLocalsFilter() 343 self.setLocalsFilter()
358 self.sourceButton.setEnabled(False) 344 self.sourceButton.setEnabled(False)
359 self.currentStack = None 345 self.currentStack = None
360 self.stackComboBox.clear() 346 self.stackComboBox.clear()
361 self.__threadList.clear()
362 self.__tabWidget.setCurrentWidget(self.glvWidget) 347 self.__tabWidget.setCurrentWidget(self.glvWidget)
363 self.breakpointViewer.handleResetUI() 348 self.breakpointViewer.handleResetUI()
364 self.__debuggersList.clear() 349 self.__debuggersList.clear()
365 self.disassemblyViewer.clear() 350 self.disassemblyViewer.clear()
366 351
502 self.setGlobalsFilter() 487 self.setGlobalsFilter()
503 self.setLocalsFilter() 488 self.setLocalsFilter()
504 self.sourceButton.setEnabled(False) 489 self.sourceButton.setEnabled(False)
505 self.currentStack = None 490 self.currentStack = None
506 self.stackComboBox.clear() 491 self.stackComboBox.clear()
507 self.__threadList.clear()
508 492
509 self.__removeDebugger(debuggerId) 493 self.__removeDebugger(debuggerId)
510 494
511 def __clientSyntaxError(self, message, filename, lineNo, characterNo, 495 def __clientSyntaxError(self, message, filename, lineNo, characterNo,
512 debuggerId): 496 debuggerId):
633 617
634 @param widget reference to the widget 618 @param widget reference to the widget
635 @type QWidget 619 @type QWidget
636 """ 620 """
637 self.__tabWidget.setCurrentWidget(widget) 621 self.__tabWidget.setCurrentWidget(widget)
638 622
639 def showThreadList(self, currentID, threadList, debuggerId): 623 def __callStackFrameSelected(self, frameNo):
640 """ 624 """
641 Public method to show the thread list. 625 Private slot to handle the selection of a call stack entry of the
626 call stack viewer.
627
628 @param frameNo frame number (index) of the selected entry
629 @type int
630 """
631 if frameNo >= 0:
632 self.stackComboBox.setCurrentIndex(frameNo)
633
634 def __debuggerSelected(self, current, previous):
635 """
636 Private slot to handle the selection of a debugger backend in the
637 debuggers list.
638
639 @param current reference to the new current item
640 @type QTreeWidgetItem
641 @param previous reference to the previous current item
642 @type QTreeWidgetItem
643 """
644 if current is not None and self.__doDebuggersListUpdate:
645 if current.parent() is None:
646 # it is a debugger item
647 debuggerId = current.text(0)
648 self.globalsViewer.handleResetUI()
649 self.localsViewer.handleResetUI()
650 self.currentStack = None
651 self.stackComboBox.clear()
652 self.callStackViewer.clear()
653
654 self.debugUI.getDebuggerData(debuggerId)
655 self.debugUI.setDebugActionsEnabled(
656 self.getSelectedDebuggerState() != "running")
657 self.__showSource()
658 else:
659 # it is a thread item
660 tid = current.data(0, self.ThreadIdRole)
661 self.debugServer.remoteSetThread(
662 self.getSelectedDebuggerId(), tid)
663
664 def __clientDebuggerId(self, debuggerId):
665 """
666 Private slot to receive the ID of a newly connected debugger backend.
667
668 @param debuggerId ID of a newly connected debugger backend
669 @type str
670 """
671 itm = QTreeWidgetItem(self.__debuggersList, [debuggerId])
672 if self.__debuggersList.topLevelItemCount() > 1:
673 self.debugUI.showNotification(
674 self.tr("<p>Debugger with ID <b>{0}</b> has been connected."
675 "</p>")
676 .format(debuggerId))
677
678 self.__debuggersList.header().resizeSections(
679 QHeaderView.ResizeToContents)
680
681 if self.__debuggersList.topLevelItemCount() == 1:
682 # it is the only item, select it as the current one
683 self.__debuggersList.setCurrentItem(itm)
684
685 def __setCurrentDebugger(self, debuggerId):
686 """
687 Private method to set the current debugger based on the given ID.
688
689 @param debuggerId ID of the debugger to set as current debugger
690 @type str
691 """
692 debuggerItems = self.__debuggersList.findItems(
693 debuggerId, Qt.MatchExactly)
694 if debuggerItems:
695 debuggerItem = debuggerItems[0]
696 currentItem = self.__debuggersList.currentItem()
697 if currentItem is debuggerItem:
698 # nothing to do
699 return
700
701 currentParent = currentItem.parent()
702 if currentParent is None:
703 # current is a debugger item
704 self.__debuggersList.setCurrentItem(debuggerItem)
705 elif currentParent is debuggerItem:
706 # nothing to do
707 return
708 else:
709 self.__debuggersList.setCurrentItem(debuggerItem)
710
711 def isOnlyDebugger(self):
712 """
713 Public method to test, if only one debugger is connected.
714
715 @return flag indicating that only one debugger is connected
716 @rtype bool
717 """
718 return self.__debuggersList.topLevelItemCount() == 1
719
720 def getSelectedDebuggerId(self):
721 """
722 Public method to get the currently selected debugger ID.
723
724 @return selected debugger ID
725 @rtype str
726 """
727 itm = self.__debuggersList.currentItem()
728 if itm:
729 if itm.parent() is None:
730 # it is a debugger item
731 return itm.text(0)
732 else:
733 # it is a thread item
734 return itm.parent().text(0)
735 else:
736 return ""
737
738 def getSelectedDebuggerState(self):
739 """
740 Public method to get the currently selected debugger's state.
741
742 @return selected debugger's state (broken, exception, running)
743 @rtype str
744 """
745 itm = self.__debuggersList.currentItem()
746 if itm:
747 if itm.parent() is None:
748 # it is a debugger item
749 return itm.data(0, self.DebuggerStateRole)
750 else:
751 # it is a thread item
752 return itm.parent().data(0, self.DebuggerStateRole)
753 else:
754 return ""
755
756 def __setDebuggerIconAndState(self, debuggerId, iconName, state):
757 """
758 Private method to set the icon for a specific debugger ID.
759
760 @param debuggerId ID of the debugger backend (empty ID means the
761 currently selected one)
762 @type str
763 @param iconName name of the icon to be used
764 @type str
765 @param state state of the debugger (broken, exception, running)
766 @type str
767 """
768 debuggerItem = None
769 if debuggerId:
770 foundItems = self.__debuggersList.findItems(
771 debuggerId, Qt.MatchExactly)
772 if foundItems:
773 debuggerItem = foundItems[0]
774 if debuggerItem is None:
775 debuggerItem = self.__debuggersList.currentItem()
776 if debuggerItem is not None:
777 debuggerItem.setIcon(0, UI.PixmapCache.getIcon(iconName))
778 debuggerItem.setData(0, self.DebuggerStateRole, state)
779 if state == "broken":
780 debuggerItem.setText(1, self.tr("waiting at breakpoint"))
781 elif state == "exception":
782 debuggerItem.setText(1, self.tr("waiting at exception"))
783 elif state == "running":
784 debuggerItem.setText(1, self.tr("running"))
785 else:
786 debuggerItem.setText(
787 1, self.tr("unknown state ({0})").format(state))
788
789 self.__debuggersList.header().resizeSections(
790 QHeaderView.ResizeToContents)
791
792 def __removeDebugger(self, debuggerId):
793 """
794 Private method to remove a debugger given its ID.
795
796 @param debuggerId ID of the debugger to be removed from the list
797 @type str
798 """
799 foundItems = self.__debuggersList.findItems(
800 debuggerId, Qt.MatchExactly)
801 if foundItems:
802 index = self.__debuggersList.indexOfTopLevelItem(foundItems[0])
803 itm = self.__debuggersList.takeTopLevelItem(index)
804 # __IGNORE_WARNING__
805 del itm
806
807 def __addThreadList(self, currentID, threadList, debuggerId):
808 """
809 Private method to add the list of threads to a debugger entry.
642 810
643 @param currentID id of the current thread 811 @param currentID id of the current thread
644 @type int 812 @type int
645 @param threadList list of dictionaries containing the thread data 813 @param threadList list of dictionaries containing the thread data
646 @type list of dict 814 @type list of dict
647 @param debuggerId ID of the debugger backend 815 @param debuggerId ID of the debugger backend
648 @type str 816 @type str
649 """ 817 """
650 debugStatus = -1 # i.e. running 818 debugStatus = -1 # i.e. running
651 819
652 if debuggerId == self.getSelectedDebuggerId(): 820 debuggerItems = self.__debuggersList.findItems(
653 citm = None 821 debuggerId, Qt.MatchExactly)
654 822 if debuggerItems:
655 self.__threadList.clear() 823 debuggerItem = debuggerItems[0]
824
825 currentItem = self.__debuggersList.currentItem()
826 if currentItem.parent() is debuggerItem:
827 currentChild = currentItem.text(0)
828 else:
829 currentChild = ""
830 self.__doDebuggersListUpdate = False
831 debuggerItem.takeChildren()
656 for thread in threadList: 832 for thread in threadList:
657 if thread.get('except', False): 833 if thread.get('except', False):
658 state = self.tr("waiting at exception") 834 state = self.tr("waiting at exception")
659 icon = "exceptions" 835 icon = "exceptions"
660 debugStatus = 1 836 debugStatus = 1
664 if debugStatus < 1: 840 if debugStatus < 1:
665 debugStatus = 0 841 debugStatus = 0
666 else: 842 else:
667 state = self.tr("running") 843 state = self.tr("running")
668 icon = "mediaPlaybackStart" 844 icon = "mediaPlaybackStart"
669 itm = QTreeWidgetItem(self.__threadList, 845 itm = QTreeWidgetItem(debuggerItem, [thread['name'], state])
670 [thread['name'], state])
671 itm.setData(0, self.ThreadIdRole, thread['id']) 846 itm.setData(0, self.ThreadIdRole, thread['id'])
672 itm.setIcon(0, UI.PixmapCache.getIcon(icon)) 847 itm.setIcon(0, UI.PixmapCache.getIcon(icon))
848 if currentChild == thread['name']:
849 self.__debuggersList.setCurrentItem(itm)
673 if thread['id'] == currentID: 850 if thread['id'] == currentID:
674 citm = itm 851 font = debuggerItem.font(0)
675 852 font.setItalic(True)
676 self.__threadList.header().resizeSections( 853 itm.setFont(0, font)
677 QHeaderView.ResizeToContents) 854
678 self.__threadList.header().setStretchLastSection(True) 855 debuggerItem.setExpanded(debuggerItem.childCount() > 0)
679
680 if citm:
681 self.__doThreadListUpdate = False
682 self.__threadList.setCurrentItem(citm)
683 self.__doThreadListUpdate = True
684 else:
685 for thread in threadList:
686 if thread.get('except', False):
687 debugStatus = 1
688 elif thread['broken']:
689 if debugStatus < 1:
690 debugStatus = 0
691
692 if debugStatus == -1:
693 icon = "mediaPlaybackStart"
694 state = "running"
695 elif debugStatus == 0:
696 icon = "break"
697 state = "broken"
698 else:
699 icon = "exceptions"
700 state = "exception"
701 self.__setDebuggerIconAndState(debuggerId, icon, state)
702
703 def __threadSelected(self, current, previous):
704 """
705 Private slot to handle the selection of a thread in the thread list.
706
707 @param current reference to the new current item
708 @type QTreeWidgetItem
709 @param previous reference to the previous current item
710 @type QTreeWidgetItem
711 """
712 if current is not None and self.__doThreadListUpdate:
713 tid = current.data(0, self.ThreadIdRole)
714 self.debugServer.remoteSetThread(self.getSelectedDebuggerId(), tid)
715
716 def __callStackFrameSelected(self, frameNo):
717 """
718 Private slot to handle the selection of a call stack entry of the
719 call stack viewer.
720
721 @param frameNo frame number (index) of the selected entry
722 @type int
723 """
724 if frameNo >= 0:
725 self.stackComboBox.setCurrentIndex(frameNo)
726
727 def __debuggerSelected(self, current, previous):
728 """
729 Private slot to handle the selection of a debugger backend in the
730 debuggers list.
731
732 @param current reference to the new current item
733 @type QTreeWidgetItem
734 @param previous reference to the previous current item
735 @type QTreeWidgetItem
736 """
737 if current is not None:
738 debuggerId = current.text(0)
739 else:
740 debuggerId = ""
741 if debuggerId:
742 self.globalsViewer.handleResetUI()
743 self.localsViewer.handleResetUI()
744 self.currentStack = None
745 self.stackComboBox.clear()
746 self.__threadList.clear()
747 self.callStackViewer.clear()
748
749 self.debugUI.getDebuggerData(debuggerId)
750 self.debugUI.setDebugActionsEnabled(
751 self.getSelectedDebuggerState() != "running")
752 self.__showSource()
753
754 def __clientDebuggerId(self, debuggerId):
755 """
756 Private slot to receive the ID of a newly connected debugger backend.
757
758 @param debuggerId ID of a newly connected debugger backend
759 @type str
760 """
761 itm = QTreeWidgetItem(self.__debuggersList, [debuggerId])
762 if self.__debuggersList.topLevelItemCount() > 1:
763 self.debugUI.showNotification(
764 self.tr("<p>Debugger with ID <b>{0}</b> has been connected."
765 "</p>")
766 .format(debuggerId))
767
768 self.__debuggersList.header().resizeSections(
769 QHeaderView.ResizeToContents)
770
771 if self.__debuggersList.topLevelItemCount() == 1:
772 # it is the only item, select it as the current one
773 self.__debuggersList.setCurrentItem(itm)
774
775 def __setCurrentDebugger(self, debuggerId):
776 """
777 Private method to set the current debugger based on the given ID.
778
779 @param debuggerId ID of the debugger to set as current debugger
780 @type str
781 """
782 foundItems = self.__debuggersList.findItems(debuggerId,
783 Qt.MatchExactly)
784 if foundItems:
785 self.__debuggersList.setCurrentItem(foundItems[0])
786
787 def isOnlyDebugger(self):
788 """
789 Public method to test, if only one debugger is connected.
790
791 @return flag indicating that only one debugger is connected
792 @rtype bool
793 """
794 return self.__debuggersList.topLevelItemCount() == 1
795
796 def getSelectedDebuggerId(self):
797 """
798 Public method to get the currently selected debugger ID.
799
800 @return selected debugger ID
801 @rtype str
802 """
803 itm = self.__debuggersList.currentItem()
804 if itm:
805 return itm.text(0)
806 else:
807 return ""
808
809 def getSelectedDebuggerState(self):
810 """
811 Public method to get the currently selected debugger's state.
812
813 @return selected debugger's state (broken, exception, running)
814 @rtype str
815 """
816 itm = self.__debuggersList.currentItem()
817 if itm:
818 return itm.data(0, Qt.UserRole)
819 else:
820 return ""
821
822 def __setDebuggerIconAndState(self, debuggerId, iconName, state):
823 """
824 Private method to set the icon for a specific debugger ID.
825
826 @param debuggerId ID of the debugger backend (empty ID means the
827 currently selected one)
828 @type str
829 @param iconName name of the icon to be used
830 @type str
831 @param state state of the debugger (broken, exception, running)
832 @type str
833 """
834 debuggerItem = None
835 if debuggerId:
836 foundItems = self.__debuggersList.findItems(
837 debuggerId, Qt.MatchExactly)
838 if foundItems:
839 debuggerItem = foundItems[0]
840 if debuggerItem is None:
841 debuggerItem = self.__debuggersList.currentItem()
842 if debuggerItem is not None:
843 debuggerItem.setIcon(0, UI.PixmapCache.getIcon(iconName))
844 debuggerItem.setData(0, Qt.UserRole, state)
845 if state == "broken":
846 debuggerItem.setText(1, self.tr("waiting at breakpoint"))
847 elif state == "exception":
848 debuggerItem.setText(1, self.tr("waiting at exception"))
849 elif state == "running":
850 debuggerItem.setText(1, self.tr("running"))
851 else:
852 debuggerItem.setText(
853 1, self.tr("unknown state ({0})").format(state))
854 856
855 self.__debuggersList.header().resizeSections( 857 self.__debuggersList.header().resizeSections(
856 QHeaderView.ResizeToContents) 858 QHeaderView.ResizeToContents)
857 859 self.__debuggersList.header().setStretchLastSection(True)
858 def __removeDebugger(self, debuggerId): 860 self.__doDebuggersListUpdate = True
859 """ 861
860 Private method to remove a debugger given its ID. 862 if debugStatus == -1:
861 863 icon = "mediaPlaybackStart"
862 @param debuggerId ID of the debugger to be removed from the list 864 state = "running"
863 @type str 865 elif debugStatus == 0:
864 """ 866 icon = "break"
865 foundItems = self.__debuggersList.findItems( 867 state = "broken"
866 debuggerId, Qt.MatchExactly) 868 else:
867 if foundItems: 869 icon = "exceptions"
868 index = self.__debuggersList.indexOfTopLevelItem(foundItems[0]) 870 state = "exception"
869 itm = self.__debuggersList.takeTopLevelItem(index) 871 self.__setDebuggerIconAndState(debuggerId, icon, state)
870 # __IGNORE_WARNING__
871 del itm

eric ide

mercurial