I have a dll that is relying extensively on Qt and that exports certain async functions returning QFuture, e.g.
QFuture<QString> loadUserAsync();
I want to export such functions (through pybind11) so that customers can write scripts in python. I don't want to leak the async Qt interface into python though, hence I am writing a wrapping API in C++, something like that:
class API_EXPORT API {
public:
std::string loadUsername();
//...
};
std::string API::loadUsername() {
Future<QString> future = _core->loadUserAsync();
return future.result().toStdString();
}
which then gets exported through pybind11:
py::class_<API>(m, "api")
.def(py::init<>())
.def("loadUsername", &API::loadUsername);
Well, this has multiple issues and I am struggling how to approach this correctly.
First, I most certainly need to instantiate a QCoreApplication so that signal/slot and events within the library are working correctly. This seems to work but I am really not sure if this is considered best practise and if I have to call the exec function (I cannot call exec on the calling thread, else it will block):
API::API() {
if (!QCoreApplication::instance()) {
int argc = 1;
char* argv[] = {"api"};
_qt = std::make_shared<QCoreApplication>(argc, argv);
}
}
Second, future.result().toStdString(); deadlocks. I could "fix" this instantiating my own QEventLoop but I am not sure if this is the way to go:
QFutureWatcher<void> watcher;
QEventLoop loop;
watcher.connect(&watcher, SIGNAL(finished()), &loop, SLOT(quit()), Qt::QueuedConnection);
watcher.setFuture(future);
loop.exec();
Third, somewhere within the dll a QTimer is instantiated so that I am getting nasty warnings printed in python and I am puzzled what to do about it:
QObject::startTimer: Timers can only be used with threads started with QThread
from Use async c++/qt functions in exported python module
No comments:
Post a Comment