1""" GUI utils for widgets """ 2from typing import Sequence 3from numbers import Real, Integral 4from collections import namedtuple 5 6from AnyQt.QtGui import QTextDocument, QAbstractTextDocumentLayout 7from AnyQt.QtCore import Qt, QSize, QSortFilterProxyModel 8from AnyQt.QtWidgets import QFrame, QStyle, QApplication, QStyledItemDelegate, QStyleOptionViewItem 9 10from .gene_sets import GeneSetsSelection 11from .gene_scoring import GeneScoringWidget, gene_scoring_method 12from .list_completer import TokenListCompleter 13from .label_selection import ( 14 RowGroup, 15 ColumnGroup, 16 LabelSelectionWidget, 17 itemselection, 18 group_candidates, 19 standarditem_from, 20 group_selection_mask, 21) 22 23__all__ = ( 24 'GeneSetsSelection', 25 'GeneScoringWidget', 26 'gene_scoring_method', 27 'TokenListCompleter', 28 'RowGroup', 29 'ColumnGroup', 30 'LabelSelectionWidget', 31 'itemselection', 32 'group_candidates', 33 'standarditem_from', 34 'group_selection_mask', 35) 36 37 38# Creates line separator 39def horizontal_line(): 40 line = QFrame() 41 line.setFrameShape(QFrame.HLine) 42 line.setFrameShadow(QFrame.Sunken) 43 return line 44 45 46class FilterProxyModel(QSortFilterProxyModel): 47 """ 48 A simple filter proxy model with settable filter predicates 49 Example 50 ------- 51 >>> proxy = FilterProxyModel() 52 >>> proxy.set_filters([ 53 ... FilterProxyModel.Filter(0, Qt.DisplayRole, lambda value: value < 1) 54 ... ]) 55 """ 56 57 Filter = namedtuple("Filter", ["column", "role", "predicate"]) 58 59 def __init__(self, *args, **kwargs): 60 super().__init__(*args, **kwargs) 61 self.__filters = [] 62 63 def reset_filters(self): 64 self.__filters = [] 65 self.invalidateFilter() 66 67 def set_filters(self, filters): 68 # type: (Sequence[FilterProxyModel.Filter]) -> None 69 70 filters = [FilterProxyModel.Filter(f.column, f.role, f.predicate) for f in filters] 71 self.__filters = filters 72 self.invalidateFilter() 73 74 def filterAcceptsRow(self, row, parent): 75 source = self.sourceModel() 76 77 def apply(f): 78 index = source.index(row, f.column, parent) 79 data = source.data(index, f.role) 80 try: 81 return f.predicate(data) 82 except (TypeError, ValueError): 83 return False 84 85 return all(apply(f) for f in self.__filters) 86 87 88class NumericalColumnDelegate(QStyledItemDelegate): 89 """ 90 An Item delegate for displaying numerical columns 91 """ 92 93 def __init__(self, parent=None, precision=4, notation='f'): 94 super().__init__(parent) 95 self.precision = precision 96 self.notation = notation 97 98 def displayText(self, value, locale): 99 if isinstance(value, Integral): 100 return locale.toString(int(value)) 101 elif isinstance(value, Real): 102 return locale.toString(float(value), self.notation, self.precision) 103 else: 104 return super().displayText(value, locale) 105 106 def initStyleOption(self, option, index): 107 super().initStyleOption(option, index) 108 align = index.data(Qt.TextAlignmentRole) 109 data = index.data(Qt.DisplayRole) 110 if align is None and isinstance(data, Real): 111 # Right align if the model does not specify otherwise 112 option.displayAlignment = Qt.AlignRight | Qt.AlignVCenter 113 114 115class HTMLDelegate(QStyledItemDelegate): 116 """ 117 https://stackoverflow.com/questions/1956542/how-to-make-item-view-render-rich-html-text-in-qt 118 https://stackoverflow.com/questions/2375763/how-to-open-an-url-in-a-qtableview 119 """ 120 121 def sizeHint(self, option, index): 122 options = QStyleOptionViewItem(option) 123 gene_obj = index.data(Qt.DisplayRole) 124 self.initStyleOption(options, index) 125 126 doc = QTextDocument() 127 doc.setHtml(gene_obj.to_html()) 128 doc.setTextWidth(options.rect.width() - 10) 129 130 return QSize(doc.idealWidth(), doc.size().height()) 131 132 def paint(self, painter, option, index): 133 options = QStyleOptionViewItem(option) 134 row_obj = index.data(Qt.DisplayRole) 135 self.initStyleOption(options, index) 136 # print(option.rect.width()) 137 style = QApplication.style() if options.widget is None else options.widget.style() 138 139 doc = QTextDocument() 140 doc.setHtml(row_obj.to_html()) 141 doc.setTextWidth(option.rect.width() - 10) 142 143 # doc.setPageSize(300) 144 # print(doc.loadResource(3)) 145 146 options.text = "" 147 style.drawControl(QStyle.CE_ItemViewItem, options, painter) 148 149 ctx = QAbstractTextDocumentLayout.PaintContext() 150 151 text_rect = style.subElementRect(QStyle.SE_ItemViewItemText, options) 152 painter.save() 153 painter.translate(text_rect.topLeft()) 154 painter.setClipRect(text_rect.translated(-text_rect.topLeft())) 155 doc.documentLayout().draw(painter, ctx) 156 157 painter.restore() 158