eric7/Unittest/UTTestResultsTree.py

branch
unittest
changeset 9065
39405e6eba20
parent 9064
339bb8c8007d
equal deleted inserted replaced
9064:339bb8c8007d 9065:39405e6eba20
14 14
15 from collections import Counter 15 from collections import Counter
16 from operator import attrgetter 16 from operator import attrgetter
17 17
18 from PyQt6.QtCore import ( 18 from PyQt6.QtCore import (
19 pyqtSignal, pyqtSlot, Qt, QAbstractItemModel, QCoreApplication, QModelIndex 19 pyqtSignal, pyqtSlot, Qt, QAbstractItemModel, QCoreApplication,
20 QModelIndex, QPoint
20 ) 21 )
21 from PyQt6.QtGui import QBrush, QColor 22 from PyQt6.QtGui import QBrush, QColor
22 from PyQt6.QtWidgets import QTreeView 23 from PyQt6.QtWidgets import QMenu, QTreeView
23 24
24 from EricWidgets.EricApplication import ericApp 25 from EricWidgets.EricApplication import ericApp
25 26
26 import Preferences 27 import Preferences
27 28
41 42
42 Headers = [ 43 Headers = [
43 QCoreApplication.translate("TestResultsModel", "Status"), 44 QCoreApplication.translate("TestResultsModel", "Status"),
44 QCoreApplication.translate("TestResultsModel", "Name"), 45 QCoreApplication.translate("TestResultsModel", "Name"),
45 QCoreApplication.translate("TestResultsModel", "Message"), 46 QCoreApplication.translate("TestResultsModel", "Message"),
46 QCoreApplication.translate("TestResultsModel", "Duration (ms)"), 47 QCoreApplication.translate("TestResultsModel", "Duration [ms]"),
47 ] 48 ]
48 49
49 StatusColumn = 0 50 StatusColumn = 0
50 NameColumn = 1 51 NameColumn = 1
51 MessageColumn = 2 52 MessageColumn = 2
140 if duration is None else 141 if duration is None else
141 locale.format_string("%.2f", duration, grouping=True) 142 locale.format_string("%.2f", duration, grouping=True)
142 ) 143 )
143 elif role == Qt.ItemDataRole.ToolTipRole: 144 elif role == Qt.ItemDataRole.ToolTipRole:
144 if idx == TopLevelId and column == TestResultsModel.NameColumn: 145 if idx == TopLevelId and column == TestResultsModel.NameColumn:
145 return self.testresults[row].name 146 return self.__testResults[row].name
146 elif role == Qt.ItemDataRole.FontRole: 147 elif role == Qt.ItemDataRole.FontRole:
147 if idx != TopLevelId: 148 if idx != TopLevelId:
148 return Preferences.getEditorOtherFonts("MonospacedFont") 149 return Preferences.getEditorOtherFonts("MonospacedFont")
149 elif role == Qt.ItemDataRole.BackgroundRole: 150 elif role == Qt.ItemDataRole.BackgroundRole:
150 if idx == TopLevelId: 151 if idx == TopLevelId:
154 elif role == Qt.ItemDataRole.TextAlignmentRole: 155 elif role == Qt.ItemDataRole.TextAlignmentRole:
155 if idx == TopLevelId and column == TestResultsModel.DurationColumn: 156 if idx == TopLevelId and column == TestResultsModel.DurationColumn:
156 return Qt.AlignmentFlag.AlignRight 157 return Qt.AlignmentFlag.AlignRight
157 elif role == Qt.ItemDataRole.UserRole: # __IGNORE_WARNING_Y102__ 158 elif role == Qt.ItemDataRole.UserRole: # __IGNORE_WARNING_Y102__
158 if idx == TopLevelId: 159 if idx == TopLevelId:
159 testresult = self.testresults[row] 160 testresult = self.__testResults[row]
160 return (testresult.filename, testresult.lineno) 161 return (testresult.filename, testresult.lineno)
161 162
162 return None 163 return None
163 164
164 def headerData(self, section, orientation, 165 def headerData(self, section, orientation,
425 self.setSortingEnabled(True) 426 self.setSortingEnabled(True)
426 427
427 self.header().setDefaultAlignment(Qt.AlignmentFlag.AlignCenter) 428 self.header().setDefaultAlignment(Qt.AlignmentFlag.AlignCenter)
428 self.header().setSortIndicatorShown(False) 429 self.header().setSortIndicatorShown(False)
429 430
431 self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
432
430 # connect signals and slots 433 # connect signals and slots
431 self.doubleClicked.connect(self.__gotoTestDefinition) 434 self.doubleClicked.connect(self.__gotoTestDefinition)
435 self.customContextMenuRequested.connect(self.__showContextMenu)
432 436
433 self.header().sortIndicatorChanged.connect(self.sortByColumn) 437 self.header().sortIndicatorChanged.connect(self.sortByColumn)
434 self.header().sortIndicatorChanged.connect( 438 self.header().sortIndicatorChanged.connect(
435 lambda column, order: self.header().setSortIndicatorShown(True)) 439 lambda column, order: self.header().setSortIndicatorShown(True))
436 440
477 topLeft = topLeft.parent() 481 topLeft = topLeft.parent()
478 while bottomRight.parent().isValid(): 482 while bottomRight.parent().isValid():
479 bottomRight = bottomRight.parent() 483 bottomRight = bottomRight.parent()
480 self.spanFirstColumn(topLeft.row(), bottomRight.row()) 484 self.spanFirstColumn(topLeft.row(), bottomRight.row())
481 485
482 @pyqtSlot(QModelIndex)
483 def __gotoTestDefinition(self, index):
484 """
485 Private slot to show the test definition.
486
487 @param index index for the double-clicked item
488 @type QModelIndex
489 """
490 # TODO: not implemented yet (__gotoTestDefinition)
491 pass
492
493 def resizeColumns(self): 486 def resizeColumns(self):
494 """ 487 """
495 Public method to resize the columns to their contents. 488 Public method to resize the columns to their contents.
496 """ 489 """
497 for column in range(self.model().columnCount()): 490 for column in range(self.model().columnCount()):
512 model = self.model() 505 model = self.model()
513 for row in range(startRow, endRow + 1): 506 for row in range(startRow, endRow + 1):
514 index = model.index(row, 0) 507 index = model.index(row, 0)
515 for i in range(model.rowCount(index)): 508 for i in range(model.rowCount(index)):
516 self.setFirstColumnSpanned(i, index, True) 509 self.setFirstColumnSpanned(i, index, True)
510
511 def __canonicalIndex(self, index):
512 """
513 Private method to create the canonical index for a given index.
514
515 The canonical index is the index of the first column of the test
516 result entry (i.e. the top-level item). If the index is invalid,
517 None is returned.
518
519 @param index index to determine the canonical index for
520 @type QModelIndex
521 @return index of the firt column of the associated top-level item index
522 @rtype QModelIndex
523 """
524 if not index.isValid():
525 return None
526
527 while index.parent().isValid(): # find the top-level node
528 index = index.parent()
529 index = index.sibling(index.row(), 0) # go to first column
530 return index
531
532 @pyqtSlot(QModelIndex)
533 def __gotoTestDefinition(self, index):
534 """
535 Private slot to show the test definition.
536
537 @param index index for the double-clicked item
538 @type QModelIndex
539 """
540 cindex = self.__canonicalIndex(index)
541 filename, lineno = self.model().data(cindex, Qt.ItemDataRole.UserRole)
542 if filename is not None:
543 if lineno is None:
544 lineno = 1
545 self.goto.emit(filename, lineno)
546
547 @pyqtSlot(QPoint)
548 def __showContextMenu(self, pos):
549 """
550 Private slot to show the context menu.
551
552 @param pos relative position for the context menu
553 @type QPoint
554 """
555 index = self.indexAt(pos)
556 cindex = self.__canonicalIndex(index)
557
558 contextMenu = (
559 self.__createContextMenu(cindex)
560 if cindex else
561 self.__createBackgroundContextMenu()
562 )
563 contextMenu.exec(self.mapToGlobal(pos))
564
565 def __createContextMenu(self, index):
566 """
567 Private method to create a context menu for the item pointed to by the
568 given index.
569
570 @param index index of the item
571 @type QModelIndex
572 @return created context menu
573 @rtype QMenu
574 """
575 menu = QMenu(self)
576 if self.isExpanded(index):
577 menu.addAction(self.tr("Collapse"),
578 lambda: self.collapse(index))
579 else:
580 act = menu.addAction(self.tr("Expand"),
581 lambda: self.expand(index))
582 act.setEnabled(self.model().hasChildren(index))
583 menu.addSeparator()
584
585 act = menu.addAction(self.tr("Show Source"),
586 lambda: self.__gotoTestDefinition(index))
587 act.setEnabled(
588 self.model().data(index, Qt.ItemDataRole.UserRole) is not None
589 )
590 menu.addSeparator()
591
592 menu.addAction(self.tr("Collapse All"), self.collapseAll)
593 menu.addAction(self.tr("Expand All"), self.expandAll)
594
595 return menu
596
597 def __createBackgroundContextMenu(self):
598 """
599 Private method to create a context menu for the background.
600
601 @return created context menu
602 @rtype QMenu
603 """
604 menu = QMenu(self)
605 menu.addAction(self.tr("Collapse All"), self.collapseAll)
606 menu.addAction(self.tr("Expand All"), self.expandAll)
607
608 return menu
517 609
518 # 610 #
519 # eflag: noqa = M821, M822 611 # eflag: noqa = M821, M822

eric ide

mercurial