【问题标题】:PyQt Color Based CheckBox not setting model dataPyQt 基于颜色的复选框未设置模型数据
【发布时间】:2020-01-17 15:43:25
【问题描述】:

我对更好的选择持开放态度,但我在这里得到的是一个使用绿色和红色颜色代码而不是“已选中”和“未选中”的复选框。

我将它与自定义 QStyledItemDelegate、QTableView 和 QAbstractTableModel 一起使用,目的是存储“复选框”的状态。

问题是鼠标按下时未调用委托 setModelData。 请注意,此问题在我的任何其他自定义小部件(例如自定义 QDateEdit 和 QLineEdit)中都不存在,为此我无需执行任何操作(在小部件代码中)以使 setModelData 调用发生。

这就是为什么我确定问题是我在这个类代码中遗漏了一些东西。我的猜测是在 QToolButton 的代码中默认为无操作的信号或某些方法的实现。不过是哪一个?

编辑:请求的完整示例,相关代码是 ColorCheckBox,其余部分是使其运行所必需的。 当您单击该框时,它应该会打印更改的值,但在开始时会打印两次,它不会。

from PyQt5 import QtCore, QtWidgets
import sys


def validate(value, expected_type):
    if type(value) == QtCore.QVariant:
        value = None if value.isNull() or not value.isValid() else value.value()
    if expected_type is bool and not value:
        return False
    value = expected_type() if value is None else expected_type(str(value))
    return value


class ColorCheckBox(QtWidgets.QToolButton):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.bool = False
        self.setStyleSheet("background-color: red")

    def data(self):
        return self.bool

    def set_data(self, value):
        self.set_checked(validate(value, bool))
        self.update()

    def set_checked(self, value):
        self.bool = value
        if self.bool:
            self.setStyleSheet("background-color: green")
        else:
            self.setStyleSheet("background-color: red")

    def mousePressEvent(self, event):
        super().mousePressEvent(event)
        if event.button() == QtCore.Qt.LeftButton:
            self.set_data(not self.bool)


class MyWindow(QtWidgets.QWidget):
    def __init__(self, *args):
        super().__init__(*args)
        tableview = TableView()
        layout = QtWidgets.QVBoxLayout(self)
        layout.addWidget(tableview)
        self.setLayout(layout)


class Delegate(QtWidgets.QStyledItemDelegate):
    def __init__(self, model):
        super().__init__()
        self.model = model

    def createEditor(self, parent, option, index):
        widget = ColorCheckBox(parent)
        widget.set_data(False)
        return widget

    def setModelData(self, widget, model, index):
        self.model.setData(index, widget.data())

    def setEditorData(self, widget, index):
        widget.set_data(index.data())


class Model(QtCore.QAbstractTableModel):
    def __init__(self, parent=None):
        QtCore.QAbstractTableModel.__init__(self, parent=parent)

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

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if not index.isValid() or role != QtCore.Qt.DisplayRole:
            return QtCore.QVariant()
        return QtCore.QVariant('False')

    def setData(self, index, value, role=QtCore.Qt.EditRole):
        print("data[{}][{}] = {}".format(index.row(), index.column(), value))
        return True

    def rowCount(self, parent=QtCore.QModelIndex()):
        return 1

    def columnCount(self, parent=QtCore.QModelIndex()):
        return 1


class TableView(QtWidgets.QTableView):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.model = Model(self)
        delegate = Delegate(self.model)
        self.setItemDelegate(delegate)
        self.setEditTriggers(self.CurrentChanged)
        self.setModel(self.model)
        for row in range(self.model.rowCount()):
            for column in range(self.model.columnCount()):
                index = self.model.index(row, column)
                self.openPersistentEditor(index)


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    w = MyWindow()
    w.show()
    sys.exit(app.exec_())

【问题讨论】:

  • 您是否使用 ColorCheckBox 作为代理的编辑器?您能否也为您的委托向我们展示一个可重现的最小代码?
  • @musicamante 我已根据您的要求添加了我的代表。我不确定你的第一个问题是什么。如果你问我是否有委托处理这个小部件,那么答案是肯定的。我要做的就是让 pandas 数据框被可视化和修改,我正在使用自定义 QTableView 和小部件来为数据框提供更好的输入和可视化。
  • @Dirich 我们在问你,因为我也加入了请求,minimal reproducible example 来测试你的代码并分析问题,你的代码不是 MRE,所以如果你需要帮助,请提供你所拥有的要求。
  • 我已将代码更改为一个完整的工作示例,这通常意味着失败,包括自定义委托、qtableview 和模型在内的任何内容,即使是极简主义,你最终总是超过 100 行,而人们只是不要不想看那个。

标签: python pyqt pyqt5


【解决方案1】:

默认情况下,setData() 方法在编辑器关闭时调用,因为发出了commitDatacloseEditor 信号,该逻辑是为某些默认小部件实现的,但在您的自定义小部件的情况下没有,所以解决方案是在按下按钮时发出commitData 信号。

另一方面,不要重新发明轮子,因为 QToolButton 是可检查的,因为它继承自 QAbstractButton。

综合以上,解决办法是:

# ...

class ColorCheckBox(QtWidgets.QToolButton):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setStyleSheet(
            """
        ColorCheckBox{
            border: 2px solid red;
            background-color: red
        }
        ColorCheckBox:checked{
            border: 2px solid green;
            background-color: green
        }
        """
        )
        self.setCheckable(True)

    def set_data(self, value):
        self.setChecked(validate(value, bool))

    def data(self):
        return self.isChecked()

# ...

class Delegate(QtWidgets.QStyledItemDelegate):
    def createEditor(self, parent, option, index):
        widget = ColorCheckBox(parent)
        widget.toggled.connect(self._commit)
        return widget

    def setModelData(self, widget, model, index):
        model.setData(index, widget.data())

    def setEditorData(self, widget, index):
        widget.set_data(index.data())

    @QtCore.pyqtSlot()
    def _commit(self):
        widget = self.sender()
        self.commitData.emit(widget)


class Model(QtCore.QAbstractTableModel):
    # ...

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if not index.isValid() or role != QtCore.Qt.DisplayRole:
            return QtCore.QVariant()
        return QtCore.QVariant(False) # <---
    # ...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-12-22
    • 1970-01-01
    • 1970-01-01
    • 2014-12-14
    • 1970-01-01
    • 1970-01-01
    • 2020-07-18
    相关资源
    最近更新 更多