Reputation: 130
I'd like to have views that display different subsets of the same data based on internal attributes of that data, having the views share the same model of course.
Here's an example with screenshots:
1 - A table view that shows a list of items with "name" and "status"
2 - A list view that shows items filtered by "status", but without showing the status (ideally I'd like this list view to have the status color next to the name, but that can be a different question).
My goal is to create 2 ways to view the data: as a global (unfiltered) table, and also as a sort of kanban board with a list for each "status" (or any other attribute).
Table view showing all
List view showing filtered data
Here's the code of my model and how I'm constructing the data.
I'm comfortable with Qt, but I'm new to its Model-View classes, so if there are any suggestions on how to do this better they are more than welcome!
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_())
Upvotes: 1
Views: 525
Reputation: 243965
In this case the solution is to use QSortFilterProxyModel and make a custom filter:
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)
Upvotes: 2