Unsolved 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...
-
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).
-
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
-
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();