0
votes

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.

2

2 Answers

0
votes

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.

0
votes

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.