PyQt - QTableview 中的 QCombobox [英] PyQt - QCombobox in QTableview

查看:136
本文介绍了PyQt - QTableview 中的 QCombobox的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

我找到了 this SO answer 并试图在我的模型上实现它/视图设置,但我遇到了问题(所以这是后续操作).

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

#!/usr/bin/python3# -*- 编码:utf-8 -*-从 PyQt5 导入 QtSql从 PyQt5.QtWidgets 导入(QWidget、QTableView、QApplication、QHBoxLayout、QItemDelegate, QComboBox)从 PyQt5.QtCore 导入 pyqtSlot导入系统类 ComboDelegate(QItemDelegate):"""一个在每个地方放置一个功能齐全的 QComboBox 的委托应用它的列的单元格来源:https://gist.github.com/Riateche/5984815"""def __init__(self, parent, items):self.items = 物品QItemDelegate.__init__(self, parent)def createEditor(self, parent, option, index):组合 = QComboBox(父)里 = []对于 self.items 中的项目:li.append(item)组合.addItems(li)组合.currentIndexChanged.connect(self.currentIndexChanged)返回组合def setEditorData(self, editor, index):editor.blockSignals(真)# editor.setCurrentIndex(int(index.model().data(index))) #来自原始代码editor.setCurrentIndex(index.row()) # 替换editor.blockSignals(False)def setModelData(self, editor, model, index):model.setData(index, editor.currentIndex())@pyqtSlot()def currentIndexChanged(self):self.commitData.emit(self.sender())类示例(QWidget):def __init__(self):super().__init__()self.resize(400, 150)self.createConnection()self.fillTable() # 注释掉以跳过重新创建 SQL 表self.createModel()self.initUI()def createConnection(self):self.db = QtSql.QSqlDatabase.addDatabase("QSQLITE")self.db.setDatabaseName("test.db")如果不是 self.db.open():print("无法建立数据库连接")返回错误定义填充表(自我):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("汽车")self.model.select()定义 initUI(self):布局 = QHBoxLayout()self.setLayout(布局)视图 = QTableView()layout.addWidget(视图)view.setModel(self.model)view.setItemDelegateForColumn(0, ComboDelegate(self, ["VW", "Honda"]))对于范围内的行(0,self.model.rowCount()):view.openPersistentEditor(self.model.index(row, 0))def closeEvent(self, e):对于范围内的行(self.model.rowCount()):print("row {}: company = {}".format(row, self.model.data(self.model.index(row, 0))))如果(self.db.open()):self.db.close()定义主():app = QApplication(sys.argv)ex = 示例()例如.show()sys.exit(app.exec_())如果 __name__ == '__main__':主要的()

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

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

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

非常感谢任何帮助.(我已经阅读了关于 QAbstractItemDelegate 的文档,但我觉得它不是特别有用.)

解决方案

使用 Python 和 Qt 进行快速 GUI 编程:

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

def setEditorData(self, editor, index):editor.blockSignals(真)text = index.model().data(index, Qt.DisplayRole)尝试:i = self.items.index(text)除了值错误:我 = 0editor.setCurrentIndex(i)editor.blockSignals(False)def setModelData(self, editor, model, index):model.setData(index, editor.currentText())

我希望这能帮助某人.

I am displaying data from an SQLite database in a QTableView using a QSqlTableModel. Letting the user edit this data works fine. However, for some columns I want to use QComboboxes instead of free text cells, to restrict the list of possible answers.

I have found this SO answer and am trying to implement it on my model/view setting, but I'm running into problems (so this is a follow-up).

Here's a full mini-example:

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

In this case, I want to use a QCombobox on the "Company" column. It should be displayed all the time, so I'm calling openPersistentEditor.

Problem 1: default values I would expect that this shows the non-edited field's content when not edited (i.e. the company as it is listed in the model), but instead it apparently shows the ith element of the combobox's choices. How can I make each combobox show the model's actual content for this field by default?

Problem 2: editing When you comment out "self.fill_table()" you can check whether the edits arrive in the SQL database. I would expect that choosing any field in the dropdown list would replace the original value. But (a) I have to make every choice twice (the first time, the value displayed in the cell remains the same), and (b) the data appears in the model weirdly (changing the first column to 'VW', 'Honda', 'Honda' results in ('1', 'VW', '1' in the model). I think this is because the code uses editor.currentIndex() in the delegate's setModelData, but I have not found a way to use the editor's content instead. How can I make the code report the user's choices correctly back to the model? (And how do I make this work on first click, instead of needing 2 clicks?)

Any help greatly appreciated. (I have read the documentation on QAbstractItemDelegate, but I don't find it particularly helpful.)

解决方案

Found the solution with the help of the book Rapid GUI Programming with Python and Qt:

createEditor and setEditorData do not work as I expected (I was misguided because the example code looked like it was using the text content but instead was dealing with index numbers). Instead, they should look like this:

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

I hope this helps someone down the line.

这篇关于PyQt - QTableview 中的 QCombobox的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆