PyQt-QTableview中的QCombobox

时间:2018-08-21 08:53:09

标签: pyqt qtableview qcombobox qitemdelegate

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

我找到了this SO answer,并试图在我的模型/视图设置中实现它,但是我遇到了问题(因此是后续工作)。

这是一个完整的迷你示例:

#!/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()

在这种情况下,我想在“公司”列上使用QCombobox。它应该一直显示,所以我叫openPersistentEditor。

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

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

任何帮助,我们将不胜感激。 (我已经阅读了documentation on QAbstractItemDelegate,但我认为它没有什么特别的帮助。)

1 个答案:

答案 0 :(得分:1)

借助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())

我希望这可以帮助某人。