From c93cce6918f8063fb7986aecad381d7b315a740c Mon Sep 17 00:00:00 2001 From: Ales Erjavec Date: Fri, 17 Mar 2023 00:45:37 +0100 Subject: [PATCH] tableview: Automaticaly switch header sorting on/off --- Orange/widgets/data/owtable.py | 15 ------- Orange/widgets/data/utils/tablesummary.py | 9 ---- Orange/widgets/data/utils/tableview.py | 54 ++++++++++++++++++++--- Orange/widgets/obsolete/owtable.py | 13 ------ 4 files changed, 48 insertions(+), 43 deletions(-) diff --git a/Orange/widgets/data/owtable.py b/Orange/widgets/data/owtable.py index b8430cf8865..638f42179b4 100644 --- a/Orange/widgets/data/owtable.py +++ b/Orange/widgets/data/owtable.py @@ -196,12 +196,6 @@ def _setup_table_view(self): else: view.setItemDelegate(TableDataDelegate(view)) - # Enable/disable view sorting based on data's type - view.setSortingEnabled(is_sortable(data)) - header = view.horizontalHeader() - header.setSectionsClickable(is_sortable(data)) - header.setSortIndicatorShown(is_sortable(data)) - view.setModel(datamodel) vheader = view.verticalHeader() @@ -393,15 +387,6 @@ def send_report(self): self.report_table(self.view) -def is_sortable(table): - if isinstance(table, SqlTable): - return False - elif isinstance(table, Orange.data.Table): - return True - else: - return False - - if __name__ == "__main__": # pragma: no cover WidgetPreview(OWTable).run( input_data=Table("iris"), diff --git a/Orange/widgets/data/utils/tablesummary.py b/Orange/widgets/data/utils/tablesummary.py index 22c0788b446..edc0b0898e0 100644 --- a/Orange/widgets/data/utils/tablesummary.py +++ b/Orange/widgets/data/utils/tablesummary.py @@ -93,15 +93,6 @@ def parts(density, col_dist): return Summary(n_instances, domain, X_part, Y_part, M_part) -def is_sortable(table: Table) -> bool: - if isinstance(table, SqlTable): - return False - elif isinstance(table, Table): - return True - else: - return False - - def format_summary(summary: Union[ApproxSummary, Summary]) -> List[str]: def format_part(part: Optional[_ArrayStat]) -> str: if isinstance(part, DenseArray): diff --git a/Orange/widgets/data/utils/tableview.py b/Orange/widgets/data/utils/tableview.py index cbd365a10de..5d59c0ac6a0 100644 --- a/Orange/widgets/data/utils/tableview.py +++ b/Orange/widgets/data/utils/tableview.py @@ -1,18 +1,21 @@ import sys from itertools import chain, starmap -from typing import Sequence, Tuple, cast +from typing import Sequence, Tuple, cast, Optional import numpy as np from AnyQt.QtCore import ( Qt, QObject, QEvent, QSize, QAbstractProxyModel, QItemSelection, - QItemSelectionModel, QItemSelectionRange + QItemSelectionModel, QItemSelectionRange, QAbstractItemModel ) from AnyQt.QtGui import QPainter from AnyQt.QtWidgets import ( QStyle, QWidget, QStyleOptionHeader, QAbstractButton ) +import Orange.data +import Orange.data.sql.table + from Orange.widgets.data.utils.models import RichTableModel from Orange.widgets.utils.itemmodels import TableModel from Orange.widgets.utils.itemselectionmodel import ( @@ -99,13 +102,33 @@ def cornerButtonClicked(self): self.selectAll() +def source_model(model: QAbstractItemModel) -> Optional[QAbstractItemModel]: + while isinstance(model, QAbstractProxyModel): + model = model.sourceModel() + return model + + +def is_table_sortable(table): + if isinstance(table, Orange.data.sql.table.SqlTable): + return False + elif isinstance(table, Orange.data.Table): + return True + else: + return False + + class RichTableView(DataTableView): """ The preferred table view for RichTableModel. Handles the display of variable's labels keys in top left corner. """ - def setModel(self, model: RichTableModel): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + header = self.horizontalHeader() + header.setSortIndicator(-1, Qt.AscendingOrder) + + def setModel(self, model: QAbstractItemModel): current = self.model() if current is not None: current.headerDataChanged.disconnect(self.__headerDataChanged) @@ -117,15 +140,34 @@ def setModel(self, model: RichTableModel): sel_model = BlockSelectionModel(model, selectBlocks=not select_rows) self.setSelectionModel(sel_model) + sortable = self.isModelSortable(model) + self.setSortingEnabled(sortable) + header = self.horizontalHeader() + header.setSectionsClickable(sortable) + header.setSortIndicatorShown(sortable) + + def isModelSortable(self, model: QAbstractItemModel) -> bool: + """ + Should the `model` be sortable via the view header click. + + This predicate is called when a model is set on the view and + enables/disables the model sorting and header section sort indicators. + """ + model = source_model(model) + if isinstance(model, TableModel): + table = model.source + return is_table_sortable(table) + return False + def __headerDataChanged( self, orientation: Qt.Orientation, ) -> None: if orientation == Qt.Horizontal: model = self.model() - while isinstance(model, QAbstractProxyModel): - model = model.sourceModel() - if model.richHeaderFlags() & RichTableModel.Labels: + model = source_model(model) + if isinstance(model, RichTableModel) and \ + model.richHeaderFlags() & RichTableModel.Labels: items = model.headerData( 0, Qt.Horizontal, RichTableModel.LabelsItemsRole ) diff --git a/Orange/widgets/obsolete/owtable.py b/Orange/widgets/obsolete/owtable.py index 302856efc29..fc36a99d8d4 100644 --- a/Orange/widgets/obsolete/owtable.py +++ b/Orange/widgets/obsolete/owtable.py @@ -259,11 +259,7 @@ def _setup_table_view(self, view, data): else: view.setItemDelegate(TableDataDelegate(view)) - # Enable/disable view sorting based on data's type - view.setSortingEnabled(is_sortable(data)) header = view.horizontalHeader() - header.setSectionsClickable(is_sortable(data)) - header.setSortIndicatorShown(is_sortable(data)) header.sortIndicatorChanged.connect(self.update_selection) view.setModel(datamodel) @@ -526,15 +522,6 @@ def send_report(self): self.report_table(view) -def is_sortable(table): - if isinstance(table, SqlTable): - return False - elif isinstance(table, Orange.data.Table): - return True - else: - return False - - if __name__ == "__main__": # pragma: no cover WidgetPreview(OWDataTable).run( insert_dataset=[