使用委托向 QTableView 单元格中的文本添加复选框

Add a checkbox to text in a QTableView cell using delegate

我想在我的 QTableView 中向带有文本的单元格添加一个复选框:

仅供参考 - 我还打算下一步做:激活复选框时 - 单元格将拒绝更新其中文本的信号,如果未选中复选框 - 发出特殊信号时将更新文本。

我知道我应该子类化 QStyledItemDelegate 并重新实现 paint 方法,但作为一个初学者,我发现很难弄清楚这个任务。

您可以使用此示例编写和测试代码:

from PyQt5 import QtCore, QtGui, QtWidgets
import sys
import pickle

class Mainwindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        self.table = QtWidgets.QTableView()
        self.setCentralWidget(self.table)

        self.list = ['item_1', 'item_2','item_3']
        self.data = [
            [1, 'Blocks γ=500 GOST 31359-2007', self.list[0], 0.18, 0.22],
            [2, 'Blocks γ=600 GOST 31359-2008', self.list[0], 0.25, 0.27],
            [3, 'Insulation', self.list[0], 0.041, 0.042],
            [3, 'Insulation', self.list[0], 0.041, 0.042]
            ]

        self.model = Materials(self.data)
        self.table.setModel(self.model)

        self.table.setSelectionBehavior(self.table.SelectRows)
        self.table.setSelectionMode(self.table.SingleSelection)


        for row in range(len(self.model.materials)):
            index = self.table.model().index(row,2)
            self.table.setIndexWidget(index, self.setting_combobox(index))



    def setting_combobox(self, index):
        widget = QtWidgets.QComboBox()
        list = self.list
        widget.addItems(list)
        widget.setCurrentIndex(0)
        widget.currentTextChanged.connect(lambda value: self.model.setData(index, value))
        return widget


class Materials(QtCore.QAbstractTableModel):

    def __init__(self, materials = [[]], parent = None):
        super(Materials, self).__init__()
        self.materials = materials


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

    def columnCount(self, parent):
        return len(self.materials[0])

    def data(self, index, role):

        if role == QtCore.Qt.DisplayRole:
            row = index.row()
            column = index.column()
            value = self.materials[row][column]
            return value

        if role == QtCore.Qt.EditRole:
            row = index.row()
            column = index.column()
            value = self.materials[row][column]
            return value

        if role == QtCore.Qt.FontRole:
            if index.column() == 0:
                boldfont = QtGui.QFont()
                boldfont.setBold(True)
                return boldfont

    def setData(self, index, value, role = QtCore.Qt.EditRole):

        if role == QtCore.Qt.EditRole:
            row = index.row()
            column = index.column()
            self.materials[row][column] = value
            self.dataChanged.emit(index, index)
            return True
        return False


    def flags(self, index):
        return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable


if __name__ == '__main__':
    app = QtWidgets.QApplication([])
    application = Mainwindow()
    application.show()


    sys.exit(app.exec())

一个可能的解决方案是启用角色 Qt.CheckStateRole 并使用委托修改复选框的位置。为此,还必须修改模型以存储复选框的状态。

import sys

from PyQt5 import QtCore, QtGui, QtWidgets


class CustomDelegate(QtWidgets.QStyledItemDelegate):
    def initStyleOption(self, option, index):
        value = index.data(QtCore.Qt.CheckStateRole)
        if value is None:
            model = index.model()
            model.setData(index, QtCore.Qt.Unchecked, QtCore.Qt.CheckStateRole)
        super().initStyleOption(option, index)
        option.direction = QtCore.Qt.RightToLeft
        option.displayAlignment = QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter


class Mainwindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        self.table = QtWidgets.QTableView()
        self.setCentralWidget(self.table)

        self.list = ["item_1", "item_2", "item_3"]
        self.data = [
            [1, "Blocks γ=500 GOST 31359-2007", self.list[0], 0.18, 0.22],
            [2, "Blocks γ=600 GOST 31359-2008", self.list[0], 0.25, 0.27],
            [3, "Insulation", self.list[0], 0.041, 0.042],
            [3, "Insulation", self.list[0], 0.041, 0.042],
        ]

        self.model = Materials(self.data)
        self.table.setModel(self.model)

        self.table.setSelectionBehavior(self.table.SelectRows)
        self.table.setSelectionMode(self.table.SingleSelection)

        for row in range(len(self.model.materials)):
            index = self.table.model().index(row, 2)
            self.table.setIndexWidget(index, self.setting_combobox(index))

        delegate = CustomDelegate(self.table)
        self.table.setItemDelegateForColumn(4, delegate)

        self.resize(640, 480)

    def setting_combobox(self, index):
        widget = QtWidgets.QComboBox()
        list = self.list
        widget.addItems(list)
        widget.setCurrentIndex(0)
        widget.currentTextChanged.connect(
            lambda value: self.model.setData(index, value)
        )
        return widget


class Materials(QtCore.QAbstractTableModel):
    def __init__(self, materials=[[]], parent=None):
        super(Materials, self).__init__()
        self.materials = materials

        self.check_states = dict()

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

    def columnCount(self, parent):
        return len(self.materials[0])

    def data(self, index, role):

        if role == QtCore.Qt.DisplayRole:
            row = index.row()
            column = index.column()
            value = self.materials[row][column]
            return value

        if role == QtCore.Qt.EditRole:
            row = index.row()
            column = index.column()
            value = self.materials[row][column]
            return value

        if role == QtCore.Qt.FontRole:
            if index.column() == 0:
                boldfont = QtGui.QFont()
                boldfont.setBold(True)
                return boldfont

        if role == QtCore.Qt.CheckStateRole:
            value = self.check_states.get(QtCore.QPersistentModelIndex(index))
            if value is not None:
                return value

    def setData(self, index, value, role=QtCore.Qt.EditRole):
        if role == QtCore.Qt.EditRole:
            row = index.row()
            column = index.column()
            self.materials[row][column] = value
            self.dataChanged.emit(index, index, (role,))
            return True
        if role == QtCore.Qt.CheckStateRole:
            self.check_states[QtCore.QPersistentModelIndex(index)] = value
            self.dataChanged.emit(index, index, (role,))
            return True
        return False

    def flags(self, index):
        return (
            QtCore.Qt.ItemIsEditable
            | QtCore.Qt.ItemIsEnabled
            | QtCore.Qt.ItemIsSelectable
            | QtCore.Qt.ItemIsUserCheckable
        )


if __name__ == "__main__":
    app = QtWidgets.QApplication([])
    application = Mainwindow()
    application.show()

    sys.exit(app.exec())