【问题标题】:No background for selected cell in QItemDelegate modified cellQItemDelegate 修改单元格中选定单元格没有背景
【发布时间】:2021-01-24 10:04:57
【问题描述】:

我正在使用子类 QItemDelegate 来显示和编辑 QTableView 中的单元格。编辑部分完美运行。
我遇到的问题是,对于每个可编辑的单元格,在选择行时背景保持白色(我使用行选择行为)。
I have tried playing with the paint() method but whatever I do the background for those cells does not change when the row is selected.
我的互联网研究使我认为,因为用于编辑单元格的不同小部件占据了整个单元格矩形(因为这是在 updateEditorGeometry() 方法中定义的方式),所以如果隐藏单元格的背景。
我该如何解决这个问题,以便用于编辑单元格的小部件的背景与行的其余部分获得相同的选定背景。
这是显示问题的图像:

仍然具有白色背景的每个单元格都是可编辑的,并且在 QItemDelegate 类中定义。
这是委托类的代码:

class SqlAlchemyItemDelegate(QtWidgets.QItemDelegate):
    def __init__(self):
        super().__init__()

    def paint(self, painter: QtGui.QPainter, option: 'QStyleOptionViewItem', index: QtCore.QModelIndex) -> None:

        # Checkboxes
        check_state = index.data(QtCore.Qt.CheckStateRole)
        if isinstance(check_state, int):
            self.drawCheck(painter, option, option.rect, check_state)
            self.drawFocus(painter, option, option.rect)
            return
        super().paint(painter, option, index)

    def editorEvent(self, event, model, option, index):
        if event.type() == QtCore.QEvent.MouseButtonRelease and isinstance(index.data(QtCore.Qt.DisplayRole), bool):
            value = bool(model.data(index, QtCore.Qt.CheckStateRole))
            model.setData(index, not value)
            event.accept()
        return super().editorEvent(event, model, option, index)

    def createEditor(self, parent: QtWidgets.QWidget, option: 'QStyleOptionViewItem', index: QtCore.QModelIndex) -> QtWidgets.QWidget:
        data = index.model().data(index, QtCore.Qt.EditRole)
        if isinstance(data, datetime.datetime):
            return QtWidgets.QDateTimeEdit(parent)
        if isinstance(data, str):
            return QtWidgets.QLineEdit(parent)

    def setEditorData(self, editor: QtWidgets.QWidget, index: QtCore.QModelIndex) -> None:
        data = index.model().data(index, QtCore.Qt.EditRole)
        if isinstance(editor, QtWidgets.QDateTimeEdit):
            editor.setDateTime(data)
        if isinstance(editor, QtWidgets.QLineEdit):
            editor.setText(data)

    def setModelData(self, editor: QtWidgets.QWidget, model: QtCore.QAbstractItemModel, index: QtCore.QModelIndex) -> None:
        if isinstance(editor, QtWidgets.QDateTimeEdit):
            model.setData(index, editor.dateTime().toPyDateTime(), QtCore.Qt.EditRole)
        if isinstance(editor, QtWidgets.QLineEdit):
            model.setData(index, editor.text(), QtCore.Qt.EditRole)

    def updateEditorGeometry(self, editor: QtWidgets.QWidget, option: 'QStyleOptionViewItem', index: QtCore.QModelIndex) -> None:
        editor.setGeometry(option.rect)

编辑
这是一个重现该问题的最小工作示例:

from PyQt5 import QtWidgets, QtCore, QtGui
import datetime


class Gui(QtWidgets.QDialog):
    def __init__(self):
        super().__init__()
        self.setMinimumWidth(750)

        self.table_view = QtWidgets.QTableView()
        self.table_view.setSelectionBehavior(self.table_view.SelectRows)
        self.table_model = SqlAlchemyTableModel()
        self.table_model.from_complex_query(
            table_data=[
                [1, 'Alex', 'Knight', '28', '0123456789', True, datetime.datetime(year=2021, month=5, day=12, hour=2, minute=30, second=30)],
                [2, 'Alex', 'Knight', '28', '0123456789', True, datetime.datetime(year=2021, month=5, day=12, hour=2, minute=30, second=30)],
                [3, 'Alex', 'Knight', '28', '0123456789', True, datetime.datetime(year=2021, month=5, day=12, hour=2, minute=30, second=30)]
            ],
            headers=['id', 'first', 'last', 'age', 'phone', 'checked', 'date'],
            editable_columns=[4, 5, 6]
        )
        self.table_view.setModel(self.table_model)
        self.delegate = SqlAlchemyItemDelegate()
        self.table_view.setItemDelegate(self.delegate)

        self.layout = QtWidgets.QVBoxLayout(self)
        self.layout.addWidget(self.table_view)
        self.setLayout(self.layout)
        self.show()


class SqlAlchemyItemDelegate(QtWidgets.QItemDelegate):
    def __init__(self):
        super().__init__()

    def paint(self, painter: QtGui.QPainter, option: 'QStyleOptionViewItem', index: QtCore.QModelIndex) -> None:

        # Checkboxes
        check_state = index.data(QtCore.Qt.CheckStateRole)
        if isinstance(check_state, int):
            self.drawCheck(painter, option, option.rect, check_state)
            self.drawFocus(painter, option, option.rect)
            return
        super().paint(painter, option, index)

    def editorEvent(self, event, model, option, index):
        if event.type() == QtCore.QEvent.MouseButtonRelease and isinstance(index.data(QtCore.Qt.DisplayRole), bool):
            value = bool(model.data(index, QtCore.Qt.CheckStateRole))
            model.setData(index, not value)
            event.accept()
        return super().editorEvent(event, model, option, index)

    def createEditor(self, parent: QtWidgets.QWidget, option: 'QStyleOptionViewItem', index: QtCore.QModelIndex) -> QtWidgets.QWidget:
        data = index.model().data(index, QtCore.Qt.EditRole)
        if isinstance(data, datetime.datetime):
            return QtWidgets.QDateTimeEdit(parent)
        if isinstance(data, str):
            return QtWidgets.QLineEdit(parent)

    def setEditorData(self, editor: QtWidgets.QWidget, index: QtCore.QModelIndex) -> None:
        data = index.model().data(index, QtCore.Qt.EditRole)
        if isinstance(editor, QtWidgets.QDateTimeEdit):
            editor.setDateTime(data)
        if isinstance(editor, QtWidgets.QLineEdit):
            editor.setText(data)

    def setModelData(self, editor: QtWidgets.QWidget, model: QtCore.QAbstractItemModel, index: QtCore.QModelIndex) -> None:
        if isinstance(editor, QtWidgets.QDateTimeEdit):
            model.setData(index, editor.dateTime().toPyDateTime(), QtCore.Qt.EditRole)
        if isinstance(editor, QtWidgets.QLineEdit):
            model.setData(index, editor.text(), QtCore.Qt.EditRole)

    def updateEditorGeometry(self, editor: QtWidgets.QWidget, option: 'QStyleOptionViewItem', index: QtCore.QModelIndex) -> None:
        editor.setGeometry(option.rect)


class SqlAlchemyTableModel(QtCore.QAbstractTableModel):
    def __init__(self, **kwargs):
        super().__init__()

    def rowCount(self, *args):
        return len(self.table_data)

    def columnCount(self, *args):
        return len(self.headers)

    def data(self, index, role):
        row = index.row()
        col = index.column()

        # EditRole is handled by SqlAlchemyItemDelegate
        if role == QtCore.Qt.EditRole:
            return self.table_data[row][col]

        # For Booleans, display checkbox
        if isinstance(self.table_data[row][col], bool):
            if role == QtCore.Qt.CheckStateRole:
                if self.table_data[row][col]:
                    return QtCore.QVariant(QtCore.Qt.Checked)
                else:
                    return QtCore.QVariant(QtCore.Qt.Unchecked)
            return self.table_data[row][col]

        # For DateTime, display QDateTime
        if isinstance(self.table_data[row][col], datetime.datetime) and role == QtCore.Qt.DisplayRole:
            return  QtCore.QDateTime(self.table_data[row][col])

        # For other data, display itself
        if role == QtCore.Qt.DisplayRole:
            return self.table_data[row][col]

    def headerData(self, section, orientation, role):
        if role == QtCore.Qt.DisplayRole and orientation == QtCore.Qt.Horizontal:
            return self.headers[section]

    def flags(self, index):
        if self.editable_columns and index.column() in self.editable_columns and isinstance(self.table_data[index.row()][index.column()], bool):
            return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsUserCheckable
        if self.editable_columns and index.column() in self.editable_columns and isinstance(self.table_data[index.row()][index.column()], (str, datetime.datetime)):
            return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable
        return super().flags(index)

    def setData(self, index, value, role):
        row = index.row()
        col = index.column()

        if role == QtCore.Qt.EditRole:
            self.table_data[row][col] = value
            self.dataChanged.emit(index, index)
            return True
        return super().setData(index, value, role)

    def from_complex_query(self, table_data, headers, editable_columns=None):
        self.table_data = [list(row) for row in table_data]
        self.headers = headers
        self.editable_columns = editable_columns


if __name__ == '__main__':
    app = QtWidgets.QApplication([])
    win = Gui()
    app.exec()

【问题讨论】:

  • 在显示可检查项目时,绘制肯定缺少drawBackground 方法,并且通常建议使用QStyledItemDelegate,而不是更基本和有限的QItemDelegate。也就是说,请提供minimal, reproducible example
  • @musicamante 谢谢你的回答,我用一个完整的例子更新了这个问题。我使用 QItemDelegate 因为它有 drawCheck() 方法(QStyledItemDelegate 没有)允许我将复选框居中。

标签: python python-3.x qt pyqt5


【解决方案1】:

问题不是(完全)在委托上,而是在模型的不完整实现中:flags() 应该返回QtCore.Qt.ItemIsSelectable 以允许(和显示)选择。

    def flags(self, index):
        if self.editable_columns and index.column() in self.editable_columns and isinstance(self.table_data[index.row()][index.column()], bool):
            return (QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsUserCheckable | 
                QtCore.Qt.ItemIsSelectable)
        if self.editable_columns and index.column() in self.editable_columns and isinstance(self.table_data[index.row()][index.column()], (str, datetime.datetime)):
            return (QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable | 
                QtCore.Qt.ItemIsSelectable)
        return super().flags(index)

那么,使用 QItemDelegate,drawBackground() 也是必需的:

    def paint(self, painter: QtGui.QPainter, option: 'QStyleOptionViewItem', index: QtCore.QModelIndex) -> None:
        check_state = index.data(QtCore.Qt.CheckStateRole)
        if isinstance(check_state, int):
            self.drawBackground(painter, option, index)
            # ...

请注意,您的代码存在我目前无法解决的其他问题。例如,setData() 应该将角色提供为 可选参数(事实上,您的代码在单击复选框时会引发错误),并且使用 setData 更改用于检查状态的值应该使用合适的角色。

【讨论】:

  • 您,先生,是救生员!您的解决方案完美运行。关于在实际代码中处理的复选框,由于事件返回,它当前作为 EditRole 工作。但是,我会将角色更改为可选并按照您的建议更改回报。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-11
  • 1970-01-01
  • 2021-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多