View QStandardItemModel with column of checkboxes

2019-06-02 08:32发布

问题:

I have a QTreeView of a QStandardItemModel, and want one column to be checkboxes only (no text, just checkboxes). This has been discussed for QAbstractItemModel. I have tried to implement the solution there (see SSCCE below), but only a single checkbox shows up, in the first row of the view. I am not sure what extra magic needs to be added for the checkbox to show up in every row.

Note, as discussed at this related question, I want a column of checkboxes with no text fields, so simply applying setCheckable(True) to each item in a column will not be enough, as that leaves an (empty) text field next to the checkbox.

SSCCE

# -*- coding: utf-8 -*-
from PySide import QtGui, QtCore
import sys

class CheckBoxDelegate(QtGui.QStyledItemDelegate):
    def __init__(self, parent = None):
        QtGui.QStyledItemDelegate.__init__(self, parent) 
    def createEditor(self, parent, option, index):
        return None
    def paint(self, painter, option, index):
        checked = bool(index.model().data(index, QtCore.Qt.DisplayRole))
        check_box_style_option = QtGui.QStyleOptionButton()
        if (index.flags() & QtCore.Qt.ItemIsEditable) > 0:
            check_box_style_option.state |= QtGui.QStyle.State_Enabled
        else:
            check_box_style_option.state |= QtGui.QStyle.State_ReadOnly
        if checked:
            check_box_style_option.state |= QtGui.QStyle.State_On
        else:
            check_box_style_option.state |= QtGui.QStyle.State_Off
        check_box_style_option.rect = self.getCheckBoxRect(option)
        QtGui.QApplication.style().drawControl(QtGui.QStyle.CE_CheckBox, check_box_style_option, painter)
    def editorEvent(self, event, model, option, index):
        if not (index.flags() & QtCore.Qt.ItemIsEditable) > 0:
            return False
        # Do not change the checkbox-state
        if event.type() == QtCore.QEvent.MouseButtonRelease or event.type() == QtCore.QEvent.MouseButtonDblClick:
            if event.button() != QtCore.Qt.LeftButton or not self.getCheckBoxRect(option).contains(event.pos()):
                return False
            if event.type() == QtCore.QEvent.MouseButtonDblClick:
                return True
        elif event.type() == QtCore.QEvent.KeyPress:
            if event.key() != QtCore.Qt.Key_Space and event.key() != QtCore.Qt.Key_Select:
                return False
        else:
            return False
        # Change the checkbox-state
        self.setModelData(None, model, index)
        return True
    def setModelData (self, editor, model, index):
        newValue = not bool(index.model().data(index, QtCore.Qt.DisplayRole))
        model.setData(index, newValue, QtCore.Qt.EditRole)
    def getCheckBoxRect(self, option):
        check_box_style_option = QtGui.QStyleOptionButton()
        check_box_rect = QtGui.QApplication.style().subElementRect(QtGui.QStyle.SE_CheckBoxIndicator, check_box_style_option, None)
        check_box_point = QtCore.QPoint (option.rect.x() +
                             option.rect.width() / 2 -
                             check_box_rect.width() / 2,
                             option.rect.y() +
                             option.rect.height() / 2 -
                             check_box_rect.height() / 2)
        return QtCore.QRect(check_box_point, check_box_rect.size())       


#BUILD THE TREE
app = QtGui.QApplication(sys.argv)
model = QtGui.QStandardItemModel()
model.setHorizontalHeaderLabels(['Title', 'Summary', 'Checkbox'])
rootItem = model.invisibleRootItem()
#Adding branches and leaves to root
item0 = [QtGui.QStandardItem('Title0'), QtGui.QStandardItem('Summary0')]
item00 = [QtGui.QStandardItem('Title00'), QtGui.QStandardItem('Summary00')]
rootItem.appendRow(item0)
item0[0].appendRow(item00)
treeView= QtGui.QTreeView()
treeView.setModel(model)
treeView.expandAll()
treeView.show()
#PUT THE CHECKBOX IN COLUMN 2
myDelegate = CheckBoxDelegate()
treeView.setItemDelegateForColumn(2, myDelegate)
sys.exit(app.exec_())

回答1:

There is a problem with the number of columns: there's only two columns, except for the top level items. The delegate is set on the third column, so it's only set for the top level items.

You define you lines with:

item1 = [QtGui.QStandardItem('Title1'), QtGui.QStandardItem('Summary1')]

Here, clearly, there's only two columns. But before that, you set the header labels with:

model.setHorizontalHeaderLabels(['Title', 'Summary', 'Checkbox'])

I guess this line set that there is three columns for the top level items, but it doesn't have any effect on the child items.

If you directly create lines with three items, your problem is solved:

item0 = [QtGui.QStandardItem('Title0'), QtGui.QStandardItem('Summary0'), QtGui.QStandardItem()]
item00 = [QtGui.QStandardItem('Title00'), QtGui.QStandardItem('Summary00'), QtGui.QStandardItem()]


回答2:

I think you don't need an item delegate at all. Simply consider using QStandardItem::setFlags() function along with the Qt::ItemIsUserCheckable flag for each QStandardItem you add to your tree.



标签: qt pyqt pyside