Reputation: 5389
I am trying to make a QListView that shows a list of items and whether they're enabled or not. The code below does this, but I also want to be able to toggle the item's enabled state when the user clicks on the checkbox (not just anywhere on the list item, but specifically on the checkbox), how can I do that?
import sys
from dataclasses import dataclass
from typing import Dict, List, Union
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
@dataclass
class Filter:
expr: str
enabled: bool
class Store:
def __init__(self):
super().__init__()
self.filter_viewer: Union["FilterViewer", None] = None
self.filters: List[Filter] = []
def update(self):
if self.filter_viewer is not None:
self.filter_viewer.list_model.dataChanged.emit(QtCore.QModelIndex(), QtCore.QModelIndex())
def add_filter(self, expr: str, enabled=True):
filt = Filter(expr=expr, enabled=enabled)
self.filters.append(filt)
self.update()
def remove_filter(self, index: int):
self.filters.pop(index)
self.update()
def toggle_filter(self, index: int):
self.filters[index].enabled = not self.filters[index].enabled
self.update()
class FilterViewer(QtWidgets.QWidget):
def __init__(self, pgdf: Store):
super().__init__()
pgdf.filter_viewer = self
self.pgdf = pgdf
self.list_view = self.ListView()
self.list_model = self.ListModel(pgdf)
self.list_view.setModel(self.list_model)
self.text_input = QtWidgets.QLineEdit()
self.submit_button = QtWidgets.QPushButton("Add Filter")
self.submit_button.clicked.connect(self.add_filter)
self.text_input.returnPressed.connect(self.add_filter)
self.layout = QtWidgets.QVBoxLayout()
self.layout.addWidget(self.list_view)
self.layout.addWidget(self.text_input)
self.layout.addWidget(self.submit_button)
self.setLayout(self.layout)
def add_filter(self):
expr = self.text_input.text()
self.text_input.setText("")
self.pgdf.add_filter(expr=expr)
print(self.pgdf.filters)
class ListView(QtWidgets.QListView):
pass
class ListModel(QtCore.QAbstractListModel):
def __init__(self, pgdf: Store):
super().__init__()
self.pgdf = pgdf
def data(self, index: QtCore.QModelIndex, role: int):
row = index.row()
if role == Qt.DisplayRole:
filt = self.pgdf.filters[row]
return filt.expr
if role == Qt.CheckStateRole:
filt = self.pgdf.filters[row]
if filt.enabled:
return Qt.Checked
else:
return Qt.Unchecked
def rowCount(self, parent):
return len(self.pgdf.filters)
def flags(self, index):
return (QtCore.Qt.ItemIsEnabled |
QtCore.Qt.ItemIsUserCheckable)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
pgdf = Store()
pgdf.add_filter('Generation > 3', enabled=True)
pgdf.add_filter('Size > 50', enabled=False)
fv = FilterViewer(pgdf)
fv.show()
app.exec_()
Upvotes: 0
Views: 657
Reputation: 243955
The state of the QListView checkboxes is determined by the model, so to change the model information you must override the setData() method:
class ListModel(QtCore.QAbstractListModel):
def __init__(self, pgdf: Store):
super().__init__()
self.pgdf = pgdf
def data(self, index: QtCore.QModelIndex, role: int):
row = index.row()
if role == Qt.DisplayRole:
filt = self.pgdf.filters[row]
return filt.expr
if role == Qt.CheckStateRole:
filt = self.pgdf.filters[row]
if filt.enabled:
return Qt.Checked
else:
return Qt.Unchecked
def setData(self, index, value, role=QtCore.Qt.DisplayRole):
row = index.row()
if role == Qt.CheckStateRole:
filt = self.pgdf.filters[row]
filt.enabled = bool(value)
self.dataChanged.emit(index, index, (role,))
return True
return False
def rowCount(self, parent):
return len(self.pgdf.filters)
def flags(self, index):
return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsUserCheckable
Upvotes: 1