I am creating an modular application. It has a Core, and a few modules, amongs which is a gui module. These modules can be started via the command line:
myApp gui=qml=qmlFile.qml connection=serial=/dev/ttyS1
will start the app with one (multiple is also possible) gui and one serial connection. There is always one Router object, which handles the connection. Guis are loaded by a class that looks like this:
class Gui :QObject{
Core* core;
public:
QQmlApplicationEngine engine;
public slots:
void start(){
engine.load("qrc:/gui/from/command/line.qml");
}
In the gui i have a nice bit of qml called ConnectionController{} which is an easy way of adding/removing connections. But this is only the first of it's kind: in the end every module should have a qml component for controlling it.
This all works fine, but now i am trying to add multiple threads. I want to have a RouterThread and a GuiThread. This in itself was simply a matter of changing the creation of the Router (by the Core) to this:
m_router = new Router(this /*not as parent, just as pointer*/);
m_router->moveToThread(&routerThread);
(gui's still live in the main thread for now)
After this everything still works fine, except for qml property bindings.
QQmlEngine: Illegal attempt to connect to SerialConnection(0x7f58d00013a0) that is in a different thread than the QML engine
Because Gui and it's child QQmlEngine engine live in the guiThread while the SerialConnection lives in the routerThread.
Signals and slots between Router and Gui (without qml) do work, because they are handled by default as asynchronous by qt. (as long as the appropriate types are defined.)
Well, no, you cant access threaded objects from your gui and should not try to either.
You need to build a c++ model that lives in the main thread that is exposed to qml. Inside this model you can connect to signals emitted by your threads via a queuedconnection and link them to the models' own signals.
Also you should make sure that when you read data from an object owned by another thread, everything is properly mutexed or you will be reading garbage.
Related
I'm familiar with Qt from some time back, but I could use a little guidence with QML/QTQuick interactions with C++. I feel I'm missing something simple here, but am a little stuck.
I'm developing an embedded system displays the status of distributed switches communicating on a serial bus. The serial bus runs as a separate thread in C++ (has always been Qt) and automatically polls devices in a round robin fashion to get updates from the devices.
Looking into this, I found a pretty simple example using QAbstractList model to share the status via QML properties from the backed C++. https://www.youtube.com/watch?v=9BcAYDlpuT8&list=PLo12gBvZwC78nnrZHCBowKf36ZAi7iOLW&index=4&t=0s
Initially the model looked great, I simply loaded a list with the information I needed and can see it from the UI. The question is, how do I access the model from C++ to have updates bubble up to the UI when they change in the background.
What I've done so far:
Register model:
qmlReisterType<ModelDerrivedClass>("DeviceListModel",1,0,"DeviceList")
Define roles:
enum {
OpenRole = Qt::UserRole,
StatusRole
}
Define Hash table for model
QHash<int,QByteArray> ModelDerrivedClass::roleNames() const
{
QHash<int, QByteArray> names;
names[OpenRole] = "openstatus";
names[StatusRole] = "devicestatus";
return names;
}
Create simple list of structures with the proper information, implent then necessary methods, etc...works like a charm from top down.
How do I access the model from the bottom up? These devices will update status according to external input the UI doesn't need to be aware of, but these events need to be able to drive the front end. It almost looks like this scenario isn't accounted for.
I've also looked into registering a device type with QML, but can't figure out how to link a QML object to a C++ object so the the READ/WRITE/NOTIFY properties work with individual QML objects in a list. In this scenario, I would register OPEN and STATUS as properties of a QML type that could be used in the QML code directly, but I would need to associate the object instance int C++ with the QML object instance. This is something the QAbstractListModel appers to work around.
Any assistance would be appreciated.
You have two options, but I think the first is the better:
option 1: rootContext
You can set a property on the rootContext of a QQMlEngine, which will be available throughout every QML file loaded by that qml engine (see Qt docs):
QQuickView view;
view.rootContext()->setContextProperty("currentDateTime",
QDateTime::currentDateTime());
view.setSource(QUrl::fromLocalFile("MyItem.qml"));
view.show();
You should have an instance of the model:
ModelDerivedClass devices;
QQuickView view;
view.rootContext()->setContextProperty("devices", &devices);
view.setSource(QUrl::fromLocalFile("MyItem.qml"));
view.show();
You can now make changes to the ModelDerivedClass using setData or your own methods.
BTW, in this option you should not register as type, but as uncreatable type:
qmlRegisterUncreatableType<ModelDerrivedClass>("DeviceListModel",1,0,"DeviceList", "available as rootContext property 'devices'");
Option 2: singleton
You can make the ModelDerivedClass a Singleton, which is only available once. Add an instance function to the class:
class ModelDerivedClass
{
...
ModelDerivedClass* instance() {
static ModelDerivedClass theInstance;
return &theInstance;
}
...
}
From C++ you can use ModelDerivedClass::instance() to operate the changes.
You should also register it to QML as singleton:
qmlRegisterSingletonType<ModelDerivedClass>("DeviceListModel", 1, 0, "DeviceList",
[](QQmlEngine *eng, QJSEngine *js) -> QObject*
{
return ModelDerivedClass::instance();
});
But in this case you don't have much control over the lifetime of the object, which is arguably not good (lots of discussion can be found on this subject). So I would advice to take option 1.
The main thing to realize is that your main thread is a special thread that must be the only thread that accesses and changes anything about your GUI state. Most everything about Qt GUI and QML is single threaded.
Therefore, you'll want to communicate the change from your serial bus thread over to the main thread. This can be done connecting a slot/signal from the serial bus thread to an appropriate object on the main thread.
Qt knows when you've crossed thread boundaries with a connection like that and will post it in a way to the main thread that ensures it gets handled in a single-threaded fashion.
I am using a 3D rendering C++ API and want to use Qt to display GUIs on top of it.
My rendering API runs in the main() application thread, just like Qt wants.
At first I tried to run Qt in it's own std::thread and it worked perfectly fine - and I have no idea what Qt's doc means with
As mentioned, each program has one thread when it is started. This
thread is called the "main thread" (also known as the "GUI thread" in
Qt applications). The Qt GUI must run in this thread
https://doc.qt.io/qt-5/thread-basics.html
This is either plain wrong or poorly written...
My std::thread worker looks something like this:
int SomeClass::qt_app_worker(size_t width, size_t height, const std::string& title, const std::string& contents) {
int argc = 0;
QApplication app(argc, NULL);
// QDialog here
return app.exec();
}
The problem is then I can't do anything with this Qt app because if I try to create say another QDialog from the main() thread, Qt will complain I can only do this from the thread owning my QApplication (qt_app_worker).
So I'm either permanently locked out of the qt_app_worker thread, or I have to implement my own message queue for EVERYTHING Qt-related.
I sketched it with a derived QApplication class using startTimer() and then handling custom messages in timerEvent(...) but this is just too much hassle.
I just don't understand why Qt won't let a user run it in a separate thread, hopefully I'm just missing something.
There are (at least) two built-in thread-safe ways to communicate with the GUI thread (which is usually the "main thread" of Qt application, which is why it is often called main thread, and for Qt point of view it is).
You can post events (including your own custom event subclasses) to the thread using QCoreApplication::postEvent.
You can invoke methods of objects in another thread, provided you do so with Qt::QueuedConnection or Qt::BlockingQueuedConnection connection type using QMetaObject::invokeMethod (and its different overloads).
Communication the other way, from Qt to you own main thread, can happen in a few ways. I believe using Qt::BlockingQueuedConnection also allow getting a return value, but you probably don't want that blocking.... So you can just use any of the usual methods of communicating between threads, not limited by Qt, such as simply atomic or mutex-protected variables which are set from Qt, and your threads polls or otherwise reads when relevant (such as at start of every frame). Or make a simple message queue, if you want to be able to track every change and not just the state at the start of a frame or whatever.
I have the follow constellation:
A qt gui thread with MainWindow
Another thread which essentially is a CameraManager...everytime a camera is added/removed the MainWindow will be informed.
It roughly looks like this:
Mainwindow derives from ICameraAddedConsumer
MainWindow implements ConsumeCameraAdded and creates widget inside this function. It subscribes itselv as a consumer to the CameraManager
CameraManager calls ConsumeCameraAdded of all it's consumers (MainWindow) when a new camera is added.
The problem is that CameraManager lives in a different thread and Qt will obviously complain about this since a widget is not created in the same thread as the mainwindow was.
Any suggestions how I can solve this?
As per comments, using signals/slots between QObjects in different threads should take care of the issue "automagically."
Barring that, and assuming MainWindow/ICameraAddedConsumer is a QObject, one idea could be to use something like:
QMetaObject::invokeMethod(consumer, "ConsumeCameraAdded", Qt::QueuedConnection, ...)
where consumer is a pointer to the MainWindow/ICameraAddedConsumer instance.
There's QWaitCondition but I'm not sure that makes sense in this case (though it could be adapted I suppose).
Otherwise... don't create the widget in ConsumeCameraAdded() but set some flag there (and return) and then use a QTimer or QObject::timerEvent() to periodically check the flag and create widget if it is set. Unfortunately I'm pretty sure you will not be able to create or start a timer within ConsumeCameraAdded() itself because of threading issues.
Since creating widgets takes a lot of time, I try to create widgets in different threads and add them to the main layout, but that fails. When creating widgets and then adding them sequentially, the program works normally. Notifications I received: "QObject::setParent: Cannot set parent, new parent is in a different thread"
Is there a way to do it?
No, there is no way to do it.
Qt GUI classes including QWidget must be used only from the main thread.
Quoting Qt documentation:
Although QObject is reentrant, the GUI classes, notably QWidget and
all its subclasses, are not reentrant. They can only be used from the
main thread. As noted earlier, QCoreApplication::exec() must also be
called from that thread.
This is enforced in Qt code by a Q_ASSERT_X when you construct a QWidget:
Q_ASSERT_X(q->thread() == qApp->thread(), "QWidget",
"Widgets must be created in the GUI thread.");
So, even if you would find some work around to make it work, you would not have any guarantee that your code will work in a reproducible way and that any Qt update will not break your code.
Regarding your specific problem, creating widgets should not be time consuming. I can think of 2 reasons why it would be time consuming:
Your widgets are doing heavy computation when you create them. Then you shoul put the computation, and only the computation, in another thread.
You are creating a lot of widgets in one shot. The you can deffer the creation using the event loop. Basically, you create some widgets then post an event or set a timer that will create some more widget, etc. until you meet some stop conditions.
I'm looking at using QScriptEngine in a project as a simple way to interact with a console application. With a few lines I was able to get everything wired up and it works great (I'm able to call slots on my objects, access properties, etc) except it seems that whenever I invoke a slot through a script command, it is called on the thread that QScriptEngine is on instead of the thread that the QObject is on. I have 3 threads in my application and the slots on the QObjects must be called on their respective threads. Is there a simple way to tell the script engine to fire the slot into the QObjects' threads' event loop?