pyjamas
pyjamas

Reputation: 5389

PyQt QListView checkbox click toggle?

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?

enter image description here

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

Answers (1)

eyllanesc
eyllanesc

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

Related Questions