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( |
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 |
|