如何在 QTableView 中为单个单元格设置委托?

问题描述

QTableView 类为 setDelegate 提供了 3 个接口:

  • setItemDelegate -- 为整个 QTableView 设置委托

  • setItemDelegateForRow -- 为给定行设置委托

  • setItemDelegateForColumn -- 为给定列设置委托

问题:如果我只想为一个单元格设置委托,我该怎么做?

例如,我得到了一个包含两列的 Qtableview,第一列设置了一个自定义的 QComboBox 委托,其中包含人类和植物项目。第二列也设置了 QComboBox 委托,但可选项目取决于第一列的选择。这意味着第二列中的每个单元格可能有不同的代表。

例如,如果 data(row=1,col=1) 从组合框委托中选择为人类,则 cell(row=1,col=2) 有一个组合框委托,其中包含项目 header、body、hand、foot ;如果从组合框委托中选择 data(row=2,col=1) 作为植物,则 cell(row=2,col=2) 有一个包含项目根、叶、花的组合框委托。

有类似的问题但尚未回答,Set Delegate for each cell in a QTableView?

解决方法

对此没有直接的解决方案,至少(据我所知)来自 PyQt5。

问题是所有项目视图类都使用私有(并且无法从 PyQt 访问)delegateForIndex 函数,该函数首先检查该行并返回该行的委托(如果存在),然后执行相同的操作列(同样,如果存在)并最终返回默认委托(内置委托或具有泛型 setItemDelegate() 的委托)。

因此,如果您想确保委托首先始终基于行/列对(然后“回退”到某些行或列相关行为),唯一的解决方案是使用唯一委托,然后根据行/列位置实现相关功能。

,

我认为您有一个 XY problem。该逻辑与每个单元格的委托无关,而是实现基于 QModelIndex(提供行和列)的逻辑。

import random
import sys

from PyQt5.QtCore import Qt
from PyQt5.QtGui import QStandardItem,QStandardItemModel
from PyQt5.QtWidgets import (
    QApplication,QComboBox,QStyledItemDelegate,QTableView,QWidget,)

OPTIONS = {
    "human": ["header","body","hand","foot"],"plant": ["root","leaf","flower"],}


class Delegate(QStyledItemDelegate):
    def createEditor(self,parent,option,index):
        editor = QComboBox(parent)
        editor.currentTextChanged.connect(self.handle_commit_close_editor)
        return editor

    def setEditorData(self,editor,index):
        if index.column() == 0:
            option = index.data()
            editor.clear()
            editor.addItems(list(OPTIONS.keys()))
            editor.setCurrentText(option)

        elif index.column() == 1:
            option = index.sibling(index.row(),0).data()
            options = OPTIONS.get(option,[])
            editor.clear()
            editor.addItems(options)

    def setModelData(self,model,index):
        if index.column() == 0:
            option = editor.currentText()
            model.setData(index,Qt.DisplayRole)
            options = OPTIONS.get(option,[])
            model.setData(
                index.sibling(index.row(),1),options[0] if options else "",Qt.DisplayRole,)
        elif index.column() == 1:
            option = editor.currentText()
            model.setData(index,Qt.DisplayRole)

    def handle_commit_close_editor(self):
        editor = self.sender()
        if isinstance(editor,QWidget):
            self.commitData.emit(editor)
            self.closeEditor.emit(editor)


def main():
    app = QApplication(sys.argv)

    app.setStyle("fusion")

    model = QStandardItemModel(0,2)

    for i in range(4):
        option = random.choice(list(OPTIONS.keys()))
        item_1 = QStandardItem(option)
        item_2 = QStandardItem(random.choice(list(OPTIONS[option])))
        model.appendRow([item_1,item_2])

    view = QTableView()
    view.setModel(model)
    view.resize(640,480)
    view.show()

    delegate = Delegate(view)
    view.setItemDelegate(delegate)

    sys.exit(app.exec_())


if __name__ == "__main__":
    main()