multithreading - How can I query asynchronously via a QSqlQueryModel? -
i wish query sql database via qsqlquerymodel (pyqqt 5/qt 5.2) asynchronously, gui doesn't block. how can accomplished? maybe through multithreading? please provide code of how this. if using qsqlquerymodel asynchronously isn't practical, feel free provide alternatives (should usable qtableview though).
my (synchronous) code looks shown beneath. main script bin/app.py loads gui/__init__.py , executes main method. in turn uses gui.models.table load data database. problem gui.models.table queries database synchronously , locks gui in meantime.
bin/app.py:
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:
import sys import os.path pyqt5 import uic pyqt5 import qtcore, qtwidgets 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:
import os.path pyqt5.qtcore import * 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 * test") finally: db.close()
unfortunately, typical database driver qt (or else, really) uses synchronous. qt views unfortunately don't know how deal models in foreign threads.
the solution requires shim proxy model, subclassing qidentityproxymodel. first step in implementation shim of source model's method calls blocking qmetaobject::invokemethod calls. needed correct, if not asynchronous yet. it' expose safe interface model lives in thread.
the next step provide asynchronous veneer on of functionality. suppose want make data method asynchronous. is:
for each role, have cache of variant values keyed model index.
on
datachangedsignal source model, cache values changed, across roles.datacall needs queued in model's thread - more on later.in
data, if there's cache hit, return it. otherwise return null variant , queuedatacall in model's thread.
your proxy should have private method called cachedata called queued calls. in answer, i've detailed how queue functor calls in thread. leveraging that, data call queuing method can like:
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)); }); } this sketch. it'd involved, doable, , still maintaining semantics of real model.
Comments
Post a Comment