【问题标题】:Can't display data from QSqlQueryModel in a QML TableView无法在 QML TableView 中显示来自 QSqlQueryModel 的数据
【发布时间】:2018-09-24 02:20:44
【问题描述】:

我正在尝试借助 QML 的 TableViewcomponent 在表中显示来自 MySQL 数据库的数据。

最初我尝试从QSqlQuery 对象创建QSqlQueryModel 对象并将其作为属性传递给QML 上下文。但是我从Qt documentation 得知我必须实现roleNames() 以提供列到角色映射到TableView,所以我像这样子类化QSqlQueryModel

import sys
from PyQt5.QtCore import QUrl, Qt
from PyQt5.QtGui import QGuiApplication
from PyQt5.QtQuick import QQuickView
from PyQt5.QtSql import QSqlDatabase, QSqlQuery, QSqlQueryModel


class QtTabModel(QSqlQueryModel):
    def __init__(self):
        super(QtTabModel, self).__init__()

    @staticmethod
    def roleNames():
        roles = {
            Qt.UserRole + 1 : "id",
            Qt.UserRole + 2 : "name"
        }
        return roles

app = QGuiApplication(sys.argv)
view = QQuickView()
db = QSqlDatabase.addDatabase("QMYSQL")
db.setHostName("localhost")
db.setDatabaseName("qtdb")
db.setUserName("abc")
db.setPassword("xyz")
qry = QSqlQuery()
if db.open():
    qry = db.exec("SELECT id, name FROM qttab")
tabmodel = QtTabModel()
tabmodel.setQuery(qry)
ctx = view.rootContext()
ctx.setContextProperty("tabmodel", tabmodel)
view.setSource(QUrl.fromLocalFile("sqltabletest.qml"))
view.show()
app.exec()

我的 QML 是

import QtQuick 2.2
import QtQuick.Controls 1.1

TableView {
    width: 200
    height: 300
    model: tabmodel
}

但它什么也没显示,只是一个空白窗口

我可以看到我的QSqlQuery 正在工作,因为我可以使用该查询中的value(n) 方法从数据库中打印数据。我还检查了 rolenames() 成员函数,但最终结果是一样的。

def roleNames(self):
    roles = {
        Qt.UserRole + 1 : "id",
        Qt.UserRole + 2 : "name"
    }
    return roles

更新:

QSqlQueryModel 适用于小部件类,我已经使用 QTableView 小部件对其进行了测试。但我需要让它与 QML 一起工作。

import sys

from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QTableView
from PyQt5.QtSql import QSqlDatabase, QSqlQuery, QSqlQueryModel


app = QApplication(sys.argv)
db = QSqlDatabase.addDatabase("QMYSQL")
db.setHostName("localhost")
db.setDatabaseName("qtdb")
db.setUserName("abc")
db.setPassword("xyz")
qry = QSqlQuery()
if db.open():
    qry = db.exec("SELECT id, name FROM qttab")
tabmodel = QSqlQueryModel()
tabmodel.setQuery(qry)
tabmodel.setHeaderData(0, Qt.Horizontal, "ID")
tabmodel.setHeaderData(1, Qt.Horizontal, "Name")
tabview = QTableView()
tabview.setModel(tabmodel)
tabview.show()
db.close()
app.exec()

谁能帮我解决这个问题?提前致谢。

【问题讨论】:

  • 我认为 roleNames 不应该是静态方法。
  • @fxam 我也尝试过不使用装饰器,使其成为成员方法;同样的结果:(
  • 也许你可以从 here 那里得到一些想法,虽然它是针对 PyQt4 的。
  • 我已经解决了这个问题,不幸的是,有问题的代码直接继承了QAbstractItemModel,问题在于rowCountdata方法的实现以及关于从QML访问特定成员.在我的问题中,我继承了QSqlQueryModel 类,该类已经实现了datarowCount。我也没有尝试显式访问该派生类的任何成员。如果我在这里遗漏了什么,你能解释一下吗?

标签: python mysql pyqt qml pyqt5


【解决方案1】:

好的,您的评论提醒我,为了 QML 的模型,您确实需要重新实现 data()。为什么?因为 QML 的模型调用 data() 并使用 roleName() 给出的角色。它不像在 QWidget 世界中那样用Qt::DisplayRole 调用data()。此外,您需要使用角色名称定义TableViewColumn,否则模型将不会调用data()。这是一个如何重新实现 data() 的示例:

import sys
from PyQt5.QtCore import QUrl, Qt, QVariant
from PyQt5.QtCore import QObject, pyqtSlot
from PyQt5.QtGui import QGuiApplication
from PyQt5.QtQuick import QQuickView
from PyQt5.QtSql import QSqlDatabase, QSqlQuery, QSqlQueryModel

class QtTabModel(QSqlQueryModel):
    def __init__(self):
        super(QtTabModel, self).__init__()

    def roleNames(self):
        roles = {
            Qt.UserRole + 1 : 'id',
            Qt.UserRole + 2 : 'name'
        }
        return roles

    def data(self, index, role):
        if role < Qt.UserRole:
            # caller requests non-UserRole data, just pass to papa
            return super(QtTabModel, self).data(index, role)

        # caller requests UserRole data, convert role to column (role - Qt.UserRole -1) to return correct data
        return super(QtTabModel, self).data(self.index(index.row(), role - Qt.UserRole -1), Qt.DisplayRole)

    @pyqtSlot(result=QVariant)  # don't know how to return a python array/list, so just use QVariant
    def roleNameArray(self):
        # This method is used to return a list that QML understands
        list = []
        # list = self.roleNames().items()
        for key, value in self.roleNames().items():
            list.append(value)

        return QVariant(list)

TableViewColumn 添加到TableView。请记住,角色区分大小写。它们必须与 roleNames() 返回的内容完全匹配:

import QtQuick 2.2
import QtQuick.Controls 1.1

TableView {
    width: 200
    height: 300
    model: tabmodel
    TableViewColumn {
        role: "id" // case-sensitive, must match a role returned by roleNames()
    }
    TableViewColumn {
        role: "name"
    }

}

这里有一种自动生成TableViewColumn的方法。它调用上面python代码中定义的roleNameArray槽来获取角色名称列表。我们不在这里调用 roleNames() 因为我不知道如何让 QML 理解它返回的结果:),所以我们必须将其转换为列表。最后我们遍历列表并调用 TableView.addColumn 来创建列:

TableView {
    width: 200
    height: 300
    model: tabmodel
    Component.onCompleted: {
        var roles = model.roleNameArray()
        for (var i=0; i<roles.length; i++) {
          var column = addColumn( Qt.createQmlObject(
            "import QtQuick.Controls 1.1; TableViewColumn {}",
            this) )
          column.role = roles[i]
          column.title = roles[i]
        }
    }

}

【讨论】:

  • 谢谢,这个解决方案可以正常工作。但是正如您所提到的,我还需要在 QML 中手动创建表列,哪种放弃了我的数据模型和数据视图之间的分离,对吗?在小部件类的情况下,我只需要将模型传递给视图,列将从查询结果的列名自动创建(忽略我手动传递列名的情况:P)。有什么方法可以从 QML 中实现吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-08
相关资源
最近更新 更多