如何在 QTableView 中恢复 QComboBox 委托的索引?

How to restore the index of a QComboBox delegate in a QTableView?

有一个QTableView(),其中一列是QComboBox。 问题是如何根据从字典

中获取的数据QTableView()中的组合框select项

我知道我应该应用 self.combo.setCurrentIndex(self.combo.findText( status_str)) 但无法理解如何在 comboBox 中获取该变量 status_str 或将其放置在代码中应用它的位置。 我也无法理解如何使 comboBox 仅在双击后出现。如果单元格没有被双击,它看起来一定和其他任何单元格一样。

代码示例:

data = {"first":{"status":"closed"},"second":{"status":"expired"},"third":{ "status":"cancelled"}}
class ComboDelegate(QItemDelegate):
    def __init__(self, parent):
        QItemDelegate.__init__(self, parent)
    def paint(self, painter, option, index):
        self.combo = QComboBox(self.parent())
        li = []
        li.append("closed")
        li.append("expired")
        li.append("cancelled")
        li.append("waiting")
        self.combo.addItems(li)
        #self.combo.setCurrentIndex(self.combo.findText( status_str ))
        if not self.parent().indexWidget(index):
            self.parent().setIndexWidget(           index,          self.combo          )

class TableView(QTableView):
    def __init__(self, *args, **kwargs):
        QTableView.__init__(self, *args, **kwargs)
        self.setItemDelegateForColumn(1, ComboDelegate(self))

class MainFrame(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        table = TableView(self)
        self.model = QStandardItemModel()
        table.setModel(self.model)
        MainWindow = QVBoxLayout()
        MainWindow.addWidget(table)
        self.setLayout(MainWindow)
        self.fillModel()

    def fillModel(self):
        for i in data:
            print i
            name_str = i
            status_str = data[i]["status"]
            name = QStandardItem(name_str)
            status = QStandardItem(status_str)
            items = [name, status]
            self.model.appendRow(items)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    main = MainFrame()
    main.show()
    main.move(app.desktop().screen().rect().center() -     main.rect().center())
    sys.exit(app.exec_())

为了不总是在单元格中看到组合框,您需要子class QAbstracItemModel 或类似的,并将其设置为您的QTableView 的模型,这样您可以覆盖其 data() 函数,该函数描述了每列中应显示的内容。

然后,要使组合框在点击时出现,并使其正确显示所选索引,我建议您看看QStyledItemDelegate

您最好自己阅读并了解详细信息,但这里有一个关于如何使用它的粗略概述:

  1. Subclass QStyledItemDelegate 并且在那个 subclass 中有你的 QComboBox 作为成员。

  2. 将其 createEditor() 功能覆盖到 return 组合框。因此,当您将此委托设置为列的项目委托时,将显示组合框以供编辑。

  3. Subclass QAbstractListModel 并将其设置为组合框的模型。它将保存您的组合框的条目,并确保始终显示正确的索引,而无需您搜索索引。

再次阅读如何正确完成这些步骤中的每一个,例如如何正确地子class 抽象模型。您可以在每个 class 文档的 "details" 部分找到所有信息。

重写 QItemDelegate.paint 不是创建委托的推荐方法。 QItemDelegate 具有诸如 createEditorsetEditorData 之类的方法,您应该改写这些方法。 Qt 会适当地调用这些方法。

createEditor 中,您应该创建自己的 comboBox,然后 return。例如:

def createEditor(self, parent, option, index):
    editor = QComboBox(parent)
    li = []
    li.append("closed")
    li.append("expired")
    li.append("cancelled")
    li.append("waiting")
    editor.addItems(li)
    return editor

setEditorData 中,您查询模型以获取组合框的当前索引。这将被称为例如:

def setEditorData(self, editor, index):
    value = index.model().data(index, Qt.EditRole)
    editor.setCurrentIndex(editor.findText(value))

请注意,在此示例中,我依赖 QItemDelegate.setModelData() 的默认实现将 combobox 的当前文本保存到 EditRole 中。如果你想做一些更复杂的事情(例如保存 combobox 索引而不是文本),你可以 save/restore 数据到不同的角色(例如 Qt.UserRole),在这种情况下你将修改您在 setEditorData 方法中获得角色的位置,并像这样覆盖 setModelData

def setEditorData(self, editor, index):
    value = index.model().data(index, Qt.UserRole)
    editor.setCurrentIndex(int(value))

def setModelData(self, editor, model, index):
    model.setData(index, editor.currentIndex(), Qt.UserRole)

这是上述代码的最小工作示例!请注意,我已经使用 sip 关闭了对 QVariant 的支持,因此模型 return 的原生 Python 类型。

import sys
import sip
sip.setapi('QVariant', 2)
from PyQt4.QtGui import *
from PyQt4.QtCore import *


data = {"first":{"status":"closed"},"second":{"status":"expired"},"third":{ "status":"cancelled"}}

class ComboDelegate(QItemDelegate):

    def createEditor(self, parent, option, index):
        editor = QComboBox(parent)
        li = []
        li.append("closed")
        li.append("expired")
        li.append("cancelled")
        li.append("waiting")
        editor.addItems(li)
        return editor

    def setEditorData(self, editor, index):
        value = index.model().data(index, Qt.EditRole)
        editor.setCurrentIndex(editor.findText(value))


class Example(QMainWindow):

    def __init__(self):
        super(Example, self).__init__()

        self.tableview = QTableView()
        self.tableview.setItemDelegateForColumn(1, ComboDelegate())

        self.setCentralWidget(self.tableview)

        self.model = QStandardItemModel()
        self.tableview.setModel(self.model)
        self.fillModel()

        self.show()

    def fillModel(self):
        for i in data:
            name_str = i
            status_str = data[i]["status"]
            name = QStandardItem(name_str)
            status = QStandardItem(status_str)
            items = [name, status]
            self.model.appendRow(items)


if __name__ == '__main__':    
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

编辑

我刚刚注意到您提出的另一个关于在双击后自动显示 comboBox 的问题。我有一个 hack 来做我以前用过的事情。它依赖于将视图传递给委托并将以下行添加到 createEditor 方法:

        editor.activated.connect(lambda index, editor=editor: self._view.commitData(editor))
        editor.activated.connect(lambda index, editor=editor: self._view.closeEditor(editor,QAbstractItemDelegate.NoHint))
        QTimer.singleShot(10,editor.showPopup)

完整的工作示例:

import sys
import sip
sip.setapi('QVariant', 2)
from PyQt4.QtGui import *
from PyQt4.QtCore import *


data = {"first":{"status":"closed"},"second":{"status":"expired"},"third":{ "status":"cancelled"}}

class ComboDelegate(QItemDelegate):
    def __init__(self, view):
        QItemDelegate.__init__(self)
        self._view = view


    def createEditor(self, parent, option, index):
        editor = QComboBox(parent)
        li = []
        li.append("closed")
        li.append("expired")
        li.append("cancelled")
        li.append("waiting")
        editor.addItems(li)


        editor.activated.connect(lambda index, editor=editor: self._view.commitData(editor))
        editor.activated.connect(lambda index, editor=editor: self._view.closeEditor(editor,QAbstractItemDelegate.NoHint))
        QTimer.singleShot(10,editor.showPopup)

        return editor

    def setEditorData(self, editor, index):
        value = index.model().data(index, Qt.EditRole)
        editor.setCurrentIndex(editor.findText(value))


class Example(QMainWindow):

    def __init__(self):
        super(Example, self).__init__()

        self.tableview = QTableView()
        self.tableview.setItemDelegateForColumn(1, ComboDelegate(self.tableview))

        self.setCentralWidget(self.tableview)

        self.model = QStandardItemModel()
        self.tableview.setModel(self.model)
        self.fillModel()

        self.show()

    def fillModel(self):
        for i in data:
            name_str = i
            status_str = data[i]["status"]
            name = QStandardItem(name_str)
            status = QStandardItem(status_str)
            items = [name, status]
            self.model.appendRow(items)


if __name__ == '__main__':    
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())