Фильтр QListView по атрибуту данных (не по тексту)

Я хотел бы иметь представления, которые отображают разные подмножества одних и тех же данных на основе внутренних атрибутов этих данных, конечно, имея представления, использующие одну и ту же модель.

Вот пример со скриншотами:

1 — табличное представление, в котором отображается список элементов с именем и статусом.

2 - Представление списка, в котором отображаются элементы, отфильтрованные по статусу, но без отображения статуса (в идеале я бы хотел, чтобы в этом представлении списка рядом с именем был цвет статуса, но это может быть другой вопрос).

Моя цель — создать 2 способа просмотра данных: как глобальную (нефильтрованную) таблицу, а также как своего рода канбан-доску со списком для каждого статуса (или любого другого атрибута).

Таблица, показывающая все

введите здесь описание изображения

Список, показывающий отфильтрованные данные

введите здесь описание изображения

Вот код моей модели и то, как я строю данные.

Мне комфортно с Qt, но я новичок в его классах Model-View, поэтому, если есть какие-либо предложения о том, как сделать это лучше, они более чем приветствуются!


import sys
from PySide2.QtWidgets import *
from PySide2 import QtCore, QtGui
from PySide2.QtCore import Qt


class TestModel(QtCore.QAbstractTableModel):
    def __init__(self, items=None, parent=None):
        super(TestModel, self).__init__(parent)
        self.__items = items or []

    def headerData(self, section, orientation, role):
        headers = ('Name', 'Status')
        if role == Qt.DisplayRole and orientation == Qt.Horizontal:
                return headers[section]

    def columnCount(self, parent):
        return 2

    def rowCount(self, parent):
        return len(self.__items)

    def data(self, index, role):
        column = index.column()
        item = self.__items[index.row()]
        # Name
        if column == 0:
            if role == Qt.DisplayRole:
                return item.name
            if role == Qt.EditRole:
                return item.name
        # Status
        elif column == 1:
            if role == Qt.DisplayRole:
                return item.status
            if role == Qt.EditRole:
                return item.status
            if role == Qt.DecorationRole:
                pixmap = QtGui.QPixmap(26, 26)
                pixmap.fill(item.status_color)
                icon = QtGui.QIcon(pixmap)
                return icon

        if role == Qt.ToolTipRole:
            return item.__repr__()


class Item(object):
    def __init__(self, name='', status=None):
        self._valid_statuses = ('active', 'hold', 'closed', 'done')
        self._color_map = {self._valid_statuses[0]: '#56FF51',  # valid
                           self._valid_statuses[1]: '#B8E5EC', # hold
                           self._valid_statuses[2]: '#F42525',
                           self._valid_statuses[3]: '#E7F64D'
                           } # closed
        self.name = name
        self._status = status or self._valid_statuses[0]

    @property
    def status(self):
        return self._status

    @status.setter
    def status(self, status):
        if status not in self._valid_statuses:
            status = self._valid_statuses[0]
        self._status = status

    @property
    def status_color(self):
        # return black if invalid
        return self._color_map.get(self._status, '#000000')


app = QApplication([])

data = [
    Item(name='one', status='active'),
    Item(name='two', status='active'),
    Item(name='three', status='hold'),
    Item(name='four', status='closed'),
    Item(name='five', status='done'),
]

model = TestModel(data)

# Table
table_view = QTableView()
table_view.show()
table_view.setModel(model)

# List with the wanted filters i.e.: by 'approved' status
list_view = QListView()
list_view.show()
list_view.setModel(model)

sys.exit(app.exec_())

person Fernando Ortega    schedule 22.02.2021    source источник
comment
Рассматривали ли вы возможность поиска QSortFilterProxyModel?   -  person musicamante    schedule 23.02.2021
comment
@musicamante Да, но, насколько я мог видеть, он работает с текстом, но список, который я хочу отфильтровать, не отображает текст, который я фильтрую. Я мог бы что-то упустить, у вас есть пример того, как я могу это использовать?   -  person Fernando Ortega    schedule 23.02.2021


Ответы (1)


В этом случае решение состоит в том, чтобы использовать QSortFilterProxyModel и создать собственный фильтр:

class StatusFilterModel(QtCore.QSortFilterProxyModel):
    def __init__(self, parent=None):
        super(StatusFilterModel, self).__init__(parent)
        self._status = None

    @property
    def status(self):
        return self._status

    @status.setter
    def status(self, status):
        self._status = status
        self.invalidateFilter()

    def filterAcceptsRow(self, source_row, source_parent):
        if self.status is None:
            return True
        source_index = self.sourceModel().index(source_row, 1, source_parent)
        status = source_index.data()
        return self.status == status
list_view = QListView()
list_view.show()
proxy_model = StatusFilterModel()
proxy_model.setSourceModel(model)
proxy_model.status = "active"
list_view.setModel(proxy_model)
person eyllanesc    schedule 22.02.2021
comment
Это именно то, что я искал. Спасибо! - person Fernando Ortega; 23.02.2021