我希望通过QSqlQueryModel(PyqQt 5 / Qt 5.2)异步查询SQL数据库,这样GUI就不会阻塞。如何实现这一目标?也许通过多线程?请提供如何执行此操作的代码。如果异步使用QSqlQueryModel并不实用,请随意提供备选方案(尽管应该可以与QTableView一起使用)。
我的(同步)代码目前看起来如下所示。主脚本bin / app.py加载gui / __ init__.py并执行其main
方法。然后使用gui.models.Table
从数据库加载数据。问题是gui.models.Table
同步查询数据库并同时锁定GUI。
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()
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_()
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()
答案 0 :(得分:0)
不幸的是,Qt(或其他任何人真正使用)的典型数据库驱动程序是同步的。遗憾的是,Qt视图不知道如何处理外部线程中的模型。
因此,解决方案需要一个垫片代理模型,子类化QIdentityProxyModel
。实现的第一步是使用阻塞QMetaObject::invokeMethod
调用来填充所有源模型的方法调用。如果不是异步的话,只需正确即可。它只是为了存在于另一个线程中的模型的安全接口。
下一步是为某些功能提供异步胶合代码。假设您要使data
方法异步。你做的是:
对于每个角色,都有一个由模型索引键入的变量值缓存。
在来自源模型的dataChanged
信号上,缓存所有角色中已更改的所有值。 data
调用需要在模型的线程中排队 - 稍后会更多。
在data
中,如果有缓存命中,请将其返回。否则返回null变量并在模型的线程中对data
调用进行排队。
您的代理应该有一个名为cacheData
的私有方法,该方法将从排队的调用中调用。在另一个答案中,我detailed how to queue functor calls in another thread。利用它,您的数据调用排队方法可能如下所示:
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));
});
}
这只是一个草图。它是相当复杂的,但肯定是可行的,并且仍然保持着真实模型的语义。