首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何通过QSqlQueryModel异步查询?

如何通过QSqlQueryModel异步查询?
EN

Stack Overflow用户
提问于 2014-03-18 14:38:47
回答 1查看 1.4K关注 0票数 2

我希望通过QSqlQueryModel (PyqQt 5/QT5.2)异步查询一个SQL数据库,这样用户界面就不会阻塞。如何才能做到这一点?也许是通过多线程?请提供如何做到这一点的代码。如果异步使用QSqlQueryModel不实用,可以随意提供替代方案(尽管应该可以使用QTableView )。

我的(同步)代码当前看起来如下面所示。主脚本bin/app.py加载gui/_init_..py并执行其main方法。这反过来又使用gui.models.Table从数据库加载数据。问题是gui.models.Table同步查询数据库并同时锁定GUI。

bin/app.py:

代码语言:javascript
复制
import os.path
import sys

sys.path.insert(0, os.path.abspath(os.path.join(
    os.path.dirname(__file__), "..")))

import gui


if __name__ == "__main__":
    gui.main()

gui/__init__.py:

代码语言:javascript
复制
import sys
import os.path
from PyQt5 import uic
from PyQt5 import QtCore, QtWidgets

from gui import models


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        uic.loadUi(os.path.join(os.path.dirname(__file__), 'app.ui'), self)
        self.tableView.setModel(models.Table(self))


def main():
    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    app.exec_()

gui/models.py:

代码语言:javascript
复制
import os.path
from PyQt5.QtCore import *
from PyQt5.QtSql import *


class Table(QSqlQueryModel):
    def __init__(self, parent=None):
        super(Table, self).__init__(parent)

        pth = os.path.abspath(os.path.join(os.path.dirname(__file__), "..",
                                           "test.sqlite"))
        db = QSqlDatabase.addDatabase("QSQLITE")
        db.setDatabaseName(pth)
        if not db.open():
            raise Exception("Couldn't open database '{}'".format(pth))
        try:
            self.setQuery("select * from Test")
        finally:
            db.close()
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-03-18 18:16:13

不幸的是,Qt (或其他人)使用的一个典型的数据库驱动程序是同步的。不幸的是,Qt视图不知道如何处理外部线程中的模型。

因此,该解决方案需要一个shim模型,子类为QIdentityProxyModel。实现的第一步是通过阻塞QMetaObject::invokeMethod调用来缓冲所有源模型的方法调用。这是需要的,只是为了正确,如果还不是异步的。它只是向另一个线程中的模型公开一个安全的接口。

下一步是提供一些功能的异步贴面。假设您希望使data方法异步。你所做的是:

  1. 对于每个角色,都有一个由模型索引键确定的变量值的缓存。
  2. 在源模型的dataChanged信号上,缓存跨所有角色更改的所有值。data调用需要在模型的线程中排队-稍后会有更多介绍。
  3. data中,如果缓存命中,则返回它。否则,返回一个空变量并在模型的线程中对data调用进行排队。

您的代理应该有一个名为cacheData的私有方法,它将从排队的调用中调用。在另一个答案中,我有详细说明如何在另一个线程中对函子调用进行队列处理。。利用这一点,您的数据调用队列方法可以如下所示:

代码语言:javascript
复制
void ThreadsafeProxyModel::queueDataCall(const QModelIndex & index, int role) {
  int row = index.row();
  int column = index.column();
  void * data = index.internalPointer();
  postMetacall(sourceModel()->thread(), [this, row, column, data, role]{
    QVariant data = sourceModel()->data(createIndex(row, column, data), role);
    QMetaObject::invoke(this, "cacheData", 
                        Q_ARG(QVariant, data), Q_ARG(int, role),
                        Q_ARG(int, row), Q_ARG(int, column), Q_ARG(void*, data));
  });
}

这只是个素描。它是相当复杂的,但当然是可行的,并且仍然维护一个真实模型的语义。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/22482599

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档