PyQt - QTableview 中的 QCombobox

PyQt - QCombobox in QTableview

我正在使用 QSqlTableModel 在 QTableView 中显示来自 SQLite 数据库的数据。让用户编辑此数据效果很好。但是,对于某些列,我想使用 QComboboxes 而不是自由文本单元格,以限制可能答案的列表。

我已经找到 this SO answer 并且正在尝试在我的 model/view 设置上实施它,但是我 运行 遇到了问题(所以这是后续行动)。

这是一个完整的小例子:

#!/usr/bin/python3
# -*- coding: utf-8 -*-

from PyQt5 import QtSql
from PyQt5.QtWidgets import (QWidget, QTableView, QApplication, QHBoxLayout,
                             QItemDelegate, QComboBox)
from PyQt5.QtCore import pyqtSlot
import sys

class ComboDelegate(QItemDelegate):
    """
    A delegate that places a fully functioning QComboBox in every
    cell of the column to which it's applied

    source: https://gist.github.com/Riateche/5984815
    """
    def __init__(self, parent, items):
        self.items = items
        QItemDelegate.__init__(self, parent)

    def createEditor(self, parent, option, index):
        combo = QComboBox(parent)
        li = []
        for item in self.items:
            li.append(item)
        combo.addItems(li)
        combo.currentIndexChanged.connect(self.currentIndexChanged)
        return combo

    def setEditorData(self, editor, index):
        editor.blockSignals(True)
#         editor.setCurrentIndex(int(index.model().data(index))) #from original code
        editor.setCurrentIndex(index.row()) # replacement
        editor.blockSignals(False)

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

    @pyqtSlot()
    def currentIndexChanged(self):
        self.commitData.emit(self.sender())


class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.resize(400, 150)

        self.createConnection()
        self.fillTable() # comment out to skip re-creating the SQL table
        self.createModel()
        self.initUI()

    def createConnection(self):
        self.db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
        self.db.setDatabaseName("test.db")
        if not self.db.open():
            print("Cannot establish a database connection")
            return False

    def fillTable(self):
        self.db.transaction()
        q = QtSql.QSqlQuery()

        q.exec_("DROP TABLE IF EXISTS Cars;")
        q.exec_("CREATE TABLE Cars (Company TEXT, Model TEXT, Year NUMBER);")
        q.exec_("INSERT INTO Cars VALUES ('Honda', 'Civic', 2009);")
        q.exec_("INSERT INTO Cars VALUES ('VW', 'Golf', 2013);")
        q.exec_("INSERT INTO Cars VALUES ('VW', 'Polo', 1999);")

        self.db.commit()

    def createModel(self):
        self.model = QtSql.QSqlTableModel()
        self.model.setTable("Cars")
        self.model.select()

    def initUI(self):
        layout = QHBoxLayout()
        self.setLayout(layout)

        view = QTableView()
        layout.addWidget(view)

        view.setModel(self.model)

        view.setItemDelegateForColumn(0, ComboDelegate(self, ["VW", "Honda"]))
        for row in range(0, self.model.rowCount()):
            view.openPersistentEditor(self.model.index(row, 0))

    def closeEvent(self, e):
        for row in range(self.model.rowCount()):
            print("row {}: company = {}".format(row, self.model.data(self.model.index(row, 0))))
        if (self.db.open()):
            self.db.close()


def main():
    app = QApplication(sys.argv)
    ex = Example()
    ex.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

在这种情况下,我想在 "Company" 列上使用 QCombobox。它应该一直显示,所以我调用了 openPersistentEditor。

问题 1:默认值 我希望这会在未编辑时显示未编辑字段的内容(即模型中列出的公司),而是它显然显示了组合框选择的第 i 个元素。 如何让每个组合框默认显示该字段的模型实际内容?

问题 2:编辑 当您注释掉 "self.fill_table()" 时,您可以检查编辑是否到达 SQL 数据库。我希望选择下拉列表中的任何字段将替换原始值。但是 (a) 我必须对每个选择进行两次(第一次,单元格中显示的值保持不变),并且 (b) 数据奇怪地出现在模型中(将第一列更改为 'VW', 'Honda'、'Honda' 结果为(模型中的“1”、'VW'、“1”)。我认为这是因为代码在委托中使用了 editor.currentIndex() setModelData,但我还没有找到一种方法来代替使用编辑器的内容。如何让代码将用户的选择正确地报告回模型?(以及如何使它起作用第一次点击,而不是需要 2 次点击?)

非常感谢任何帮助。 (我 阅读了 documentation on QAbstractItemDelegate,但我觉得它不是特别有用。)

在书的帮助下找到了解决方案Rapid GUI Programming with Python and Qt:

createEditorsetEditorData 没有像我预期的那样工作(我被误导了,因为示例代码看起来像是在使用文本内容,但实际上是在处理索引号)。相反,它们应该如下所示:

def setEditorData(self, editor, index):
    editor.blockSignals(True)
    text = index.model().data(index, Qt.DisplayRole)
    try:
        i = self.items.index(text)
    except ValueError:
        i = 0
    editor.setCurrentIndex(i)
    editor.blockSignals(False)

def setModelData(self, editor, model, index):
    model.setData(index, editor.currentText())

我希望这对在线的人有所帮助。