eric7/Unittest/UTTestResultsTree.py

branch
unittest
changeset 9065
39405e6eba20
parent 9064
339bb8c8007d
diff -r 339bb8c8007d -r 39405e6eba20 eric7/Unittest/UTTestResultsTree.py
--- a/eric7/Unittest/UTTestResultsTree.py	Sun May 15 18:08:31 2022 +0200
+++ b/eric7/Unittest/UTTestResultsTree.py	Mon May 16 17:22:43 2022 +0200
@@ -16,10 +16,11 @@
 from operator import attrgetter
 
 from PyQt6.QtCore import (
-    pyqtSignal, pyqtSlot, Qt, QAbstractItemModel, QCoreApplication, QModelIndex
+    pyqtSignal, pyqtSlot, Qt, QAbstractItemModel, QCoreApplication,
+    QModelIndex, QPoint
 )
 from PyQt6.QtGui import QBrush, QColor
-from PyQt6.QtWidgets import QTreeView
+from PyQt6.QtWidgets import QMenu, QTreeView
 
 from EricWidgets.EricApplication import ericApp
 
@@ -43,7 +44,7 @@
         QCoreApplication.translate("TestResultsModel", "Status"),
         QCoreApplication.translate("TestResultsModel", "Name"),
         QCoreApplication.translate("TestResultsModel", "Message"),
-        QCoreApplication.translate("TestResultsModel", "Duration (ms)"),
+        QCoreApplication.translate("TestResultsModel", "Duration [ms]"),
     ]
     
     StatusColumn = 0
@@ -142,7 +143,7 @@
                 )
         elif role == Qt.ItemDataRole.ToolTipRole:
             if idx == TopLevelId and column == TestResultsModel.NameColumn:
-                return self.testresults[row].name
+                return self.__testResults[row].name
         elif role == Qt.ItemDataRole.FontRole:
             if idx != TopLevelId:
                 return Preferences.getEditorOtherFonts("MonospacedFont")
@@ -156,7 +157,7 @@
                 return Qt.AlignmentFlag.AlignRight
         elif role == Qt.ItemDataRole.UserRole:      # __IGNORE_WARNING_Y102__
             if idx == TopLevelId:
-                testresult = self.testresults[row]
+                testresult = self.__testResults[row]
                 return (testresult.filename, testresult.lineno)
         
         return None
@@ -427,8 +428,11 @@
         self.header().setDefaultAlignment(Qt.AlignmentFlag.AlignCenter)
         self.header().setSortIndicatorShown(False)
         
+        self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
+        
         # connect signals and slots
         self.doubleClicked.connect(self.__gotoTestDefinition)
+        self.customContextMenuRequested.connect(self.__showContextMenu)
         
         self.header().sortIndicatorChanged.connect(self.sortByColumn)
         self.header().sortIndicatorChanged.connect(
@@ -479,17 +483,6 @@
             bottomRight = bottomRight.parent()
         self.spanFirstColumn(topLeft.row(), bottomRight.row())
     
-    @pyqtSlot(QModelIndex)
-    def __gotoTestDefinition(self, index):
-        """
-        Private slot to show the test definition.
-        
-        @param index index for the double-clicked item
-        @type QModelIndex
-        """
-        # TODO: not implemented yet (__gotoTestDefinition)
-        pass
-    
     def resizeColumns(self):
         """
         Public method to resize the columns to their contents.
@@ -514,6 +507,105 @@
             index = model.index(row, 0)
             for i in range(model.rowCount(index)):
                 self.setFirstColumnSpanned(i, index, True)
+    
+    def __canonicalIndex(self, index):
+        """
+        Private method to create the canonical index for a given index.
+        
+        The canonical index is the index of the first column of the test
+        result entry (i.e. the top-level item). If the index is invalid,
+        None is returned.
+        
+        @param index index to determine the canonical index for
+        @type QModelIndex
+        @return index of the firt column of the associated top-level item index
+        @rtype QModelIndex
+        """
+        if not index.isValid():
+            return None
+        
+        while index.parent().isValid():  # find the top-level node
+            index = index.parent()
+        index = index.sibling(index.row(), 0)  # go to first column
+        return index
+    
+    @pyqtSlot(QModelIndex)
+    def __gotoTestDefinition(self, index):
+        """
+        Private slot to show the test definition.
+        
+        @param index index for the double-clicked item
+        @type QModelIndex
+        """
+        cindex = self.__canonicalIndex(index)
+        filename, lineno = self.model().data(cindex, Qt.ItemDataRole.UserRole)
+        if filename is not None:
+            if lineno is None:
+                lineno = 1
+            self.goto.emit(filename, lineno)
+    
+    @pyqtSlot(QPoint)
+    def __showContextMenu(self, pos):
+        """
+        Private slot to show the context menu.
+        
+        @param pos relative position for the context menu
+        @type QPoint
+        """
+        index = self.indexAt(pos)
+        cindex = self.__canonicalIndex(index)
+        
+        contextMenu = (
+            self.__createContextMenu(cindex)
+            if cindex else
+            self.__createBackgroundContextMenu()
+        )
+        contextMenu.exec(self.mapToGlobal(pos))
+    
+    def __createContextMenu(self, index):
+        """
+        Private method to create a context menu for the item pointed to by the
+        given index.
+        
+        @param index index of the item
+        @type QModelIndex
+        @return created context menu
+        @rtype QMenu
+        """
+        menu = QMenu(self)
+        if self.isExpanded(index):
+            menu.addAction(self.tr("Collapse"),
+                           lambda: self.collapse(index))
+        else:
+            act = menu.addAction(self.tr("Expand"),
+                                 lambda: self.expand(index))
+            act.setEnabled(self.model().hasChildren(index))
+        menu.addSeparator()
+        
+        act = menu.addAction(self.tr("Show Source"),
+                             lambda: self.__gotoTestDefinition(index))
+        act.setEnabled(
+            self.model().data(index, Qt.ItemDataRole.UserRole) is not None
+        )
+        menu.addSeparator()
+        
+        menu.addAction(self.tr("Collapse All"), self.collapseAll)
+        menu.addAction(self.tr("Expand All"), self.expandAll)
+        
+        return menu
+    
+    def __createBackgroundContextMenu(self):
+        """
+        Private method to create a context menu for the background.
+        
+        @return created context menu
+        @rtype QMenu
+        """
+        menu = QMenu(self)
+        menu.addAction(self.tr("Collapse All"), self.collapseAll)
+        menu.addAction(self.tr("Expand All"), self.expandAll)
+        
+        return menu
 
 #
 # eflag: noqa = M821, M822

eric ide

mercurial