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