无法在QML TableView中显示QSqlQueryModel的数据

3 投票
1 回答
3700 浏览
提问于 2025-04-20 04:06

我正在尝试使用QML中的TableView组件来显示来自MySQL数据库的数据。

最开始,我试着从QSqlQuery对象创建一个QSqlQueryModel对象,并把它作为属性传递给QML上下文。但我从Qt文档了解到,我必须实现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
}

但是它什么都不显示,只是一个空白窗口

Ubuntu 14.04中的Qt5空白窗口

我可以看到我的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()

工作中的Qt5代码

有没有人能帮我解决这个问题?非常感谢。

1 个回答

4

好的,你的评论让我想起了,确实需要重新实现 data() 方法,以适应 QML 的模型。为什么呢?因为 QML 的模型会用 roleName() 提供的角色来调用 data()。它不会像在 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]
        }
    }

}

撰写回答