Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Is there a way to forward a signal so that it assigns a Q_PROPERTY value automatically?



  • I've made a Controller that creates a separate thread, this thread manages a value. I need a way to send this value to QML.

    Right now, I create a signal that is emitted with the value. The controller listens for this signal, and then emits it's own equivalent signal, but the controllers signal is attached to a Q_PROPERTY NOTIFY (so QML picks up on that signal and acts accordingly).

    This creates two problems for me...

    1. I have to create a READ or MEMBER call with Q_PROPERTY, where I'd much rather it somehow auto update the value with the signals parameter and that somehow is fed into QML (without me having to create getter functions for each property within the controller).

    2. In my READ function I access the threads value in a non-thread safe way... Again, ideally I can somehow have the threads signal just forward the value for auto updating... I know I could create a local copy of the value within my controller that is updated when the thread emits the update signal... but then I also have to create a SLOT function to receive the signal, and then emit the change for QML.

    Is this possible either through Q_PROPERTY or another method to accomplish what I want... or do I have to stick to defining a SIGNAL in thread, that connects to a SLOT in the Controller, that emits a NOTIFY SIGNAL to QML for every single value? It's not a problem to do with 1 value, but in my real application there's a few and it can get tedious.

    I've made the little application to demonstrate....

    SampleController.h

    #ifndef SAMPLECONTROLLER_H
    #define SAMPLECONTROLLER_H
    
    #include <QObject>
    #include "SampleThread.h"
    
    class SampleController : public QObject
    {
        Q_OBJECT
    
        Q_PROPERTY(int sampleValue READ sampleValue NOTIFY sampleValueChanged)
    
    public:
        explicit SampleController(
                QObject *parent = nullptr);
    
        int sampleValue() const;
    
        Q_INVOKABLE void startThread();
    
    signals:
    
        void sampleValueChanged(
                const int new_sample_value);
    private:
        SampleThread m_sample_thread;
    };
    
    
    #endif // SAMPLECONTROLLER_H
    

    SampleController.cpp

    #include <QDebug>
    
    #include "SampleController.h"
    
    SampleController::SampleController(
            QObject *parent) :
        QObject(parent)
    {
        connect(&m_sample_thread, &SampleThread::sampleValueChanged, this, &SampleController::sampleValueChanged);
    }
    
    // Is there a way that we can forward SampleThread's signal to Q_PROPERTY in order to assign a value in QML?
    // So we don't have directly access m_sample_value in a non-thread-safe manner...
    int SampleController::sampleValue() const
    {
        return m_sample_thread.m_sample_value;
        //return 0;
    }
    
    void SampleController::startThread()
    {
        m_sample_thread.start();
    }
    

    SampleThread.h

    #ifndef SAMPLETHREAD_H
    #define SAMPLETHREAD_H
    
    #include <QThread>
    #include <QObject>
    
    class SampleThread : public QThread
    {
        Q_OBJECT
    public:
        explicit SampleThread(
                QObject *parent = nullptr);
    
        int m_sample_value;
    
    signals:
        void sampleValueChanged(
                const int new_sample_value);
    
    protected:
        void run() override;
    
    private:
    };
    
    #endif // SAMPLETHREAD_H
    
    

    SampleThread.cpp

    #include "SampleThread.h"
    
    SampleThread::SampleThread(
            QObject *parent) :
        QThread(parent),
        m_sample_value(0)
    {
    
    }
    
    
    void SampleThread::run()
    {
        m_sample_value++;
        emit sampleValueChanged(m_sample_value);
    }
    
    

    Main.cpp

    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <QQmlContext>
    
    #include "SampleController.h"
    
    int main(int argc, char *argv[])
    {
    #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    #endif
    
        QGuiApplication app(argc, argv);
    
    
        SampleController sampleController;
    
        QQmlApplicationEngine engine;
        engine.rootContext()->setContextProperty("sampleController", &sampleController);
    
        const QUrl url(QStringLiteral("qrc:/main.qml"));
        QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                         &app, [url](QObject *obj, const QUrl &objUrl) {
            if (!obj && url == objUrl)
                QCoreApplication::exit(-1);
        }, Qt::QueuedConnection);
        engine.load(url);
    
        return app.exec();
    }
    
    

    Main.qml

    import QtQuick 2.12
    import QtQuick.Window 2.12
    import QtQuick.Controls 2.12
    
    Window {
        width: 640
        height: 480
        visible: true
        title: qsTr("Hello World")
    
        Column {
            Text {
                id: sampleText
                width: parent.width
                text: "Sample Value: " + sampleController.sampleValue
            }
    
            Button {
                id: sampleBtn
                text: "Click Me"
                onClicked: sampleController.startThread()
            }
        }
    }
    
    


  • If anyone else is looking for an answer to this... I found a way to accomplish exactly what I need.... use QML_READONLY_PROPERTY.

    In .h File

    QML_READONLY_PROPERTY(int, propertyName)
    

    In .cpp File

    connect(&someSignalObject, &SomeSignalClass::someSignal, this, &SomeSlotClass::update_propertyName);
    

    The "update_propertyName" seems to be automatically created for us!



  • @KidTrent said in Is there a way to forward a signal so that it assigns a Q_PROPERTY value automatically?:

    If anyone else is looking for an answer to this... I found a way to accomplish exactly what I need.... use QML_READONLY_PROPERTY.

    Where is this defined? There's no mention of it in the Qt 6 or Qt 5 documentation.



  • @jeremy_k said in Is there a way to forward a signal so that it assigns a Q_PROPERTY value automatically?:

    QML_READONLY_PROPERTY

    Sorry, I forgot to mention it's not included in QT. However, it's just a helper macro... Found it in several projects I was looking through, such as this one https://invent.kde.org/rolisteam/rcharactersheet/-/blob/fa9452f4fbe467ef18d81e1a40ffd2dc1a270369/qqmlhelpers.cpp and for the .h... https://invent.kde.org/rolisteam/rcharactersheet/-/blob/fa9452f4fbe467ef18d81e1a40ffd2dc1a270369/qqmlhelpers.h


  • Qt Champions 2018

    It's a macro from an external lib (originateing from this : http://gitlab.unique-conception.org/qt-qml-tricks/qt-supermacros ), roughly expanding to this:

    private:
    Q_PROPERTY(int propertyName READ propertyName NOTIFY propertyNameChanged)
    int m_propertyName;
    
    public:
    int propertyName()
    {
        return m_propertyName;
    }
    void update_propertyName(int propertyName)
    {
        if (m_propertyName == propertyName)
            return;
        m_propertyName = propertyName;
        Q_EMIT propertyNameChanged();
    }
    
    Q_SIGNAL propertyNameChanged();
    
    

Log in to reply