Routinely change QML property value from within a threaded c++ loop - c++

I'm brand new to c++ and QML, and am struggling to call and run a threaded loop. I'd like to add an integer to a QML propery value every x milliseconds.
I'll omit my main.qml code for now, as I'm able to access the desired property; this question pertains more to c++.
void fn(QQuickItem x)
{
for (;;)
{
std::this_thread::sleep_for(std::chrono.milliseconds(100));
x->setProperty("value", x->property("value").toReal() + 10);
}
}
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
//QScopedPointer<TachometerDemo> Tacho(new TachometerDemo);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
QQuickItem *item = engine.rootObjects().at(0)->findChild<QQuickItem*>
("circularGauge");
thread t1(fn, item);
return app.exec();
}
Any guidance on the most efficient means for achieving the above desired functionality would be appreciated. Whilst I'm currently testing on a win32 platform, a cross-platform approach is required.

Better use Qt's event loop mechanism and SIGNAL/SLOTS. And QThreads if needed. Also if you need to update value in QML in msecconds duration don't do it over setProperty, this function call takes too long. You can update your value directly in QML using JS. But if you need to update QML value from C++ you can do it this way:
test.h
#include <QObject>
#include <QTimer>
class Test : public QObject
{
Q_OBJECT
public:
Test();
Q_PROPERTY(int value READ value NOTIFY valueChanged)
int value(){return this->m_value;}
signals:
void valueChanged();
private slots:
void timeout();
private:
int m_value;
QTimer * m_timer;
};
test.cpp
#include "test.h"
Test::Test():m_value(0)
{
this->m_timer = new QTimer(this);
this->m_timer->setInterval(100);
connect(this->m_timer, &QTimer::timeout, this, &Test::timeout);
this->m_timer->start();
}
void Test::timeout()
{
this->m_value += 10;
emit valueChanged();
}
in your main.cpp
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty(QStringLiteral("Test"), new Test());
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
Somewhere in your QML:
Label
{
text: Test.value
anchors.centerIn: parent
}
This is the fastest way to update QML value from C++

Related

QML Reference Error - <thing> is not defined during a c++/QML integration

I am trying to build a mixed c++/QML application but I came across a problem when trying to make both parts communicate and interact.
The objective is to use embedded C++ object in QML via the setContextProperties method, using QQmlApplicationEngine.
I've been looking at this post QT 5.7 QML - Reference Error: Class is not defined since the problem is quite similar, but unfortunately the solution doesn't apply here. I'm still new to Qt so maybe the solution is obvious but I couldn't figure it out.
So I have 3 files, main.cpp, thing.h and main.qml.
main.cpp:
#include "thing.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
Thing thing;
engine.rootContext()->setContextProperty("thing", &thing);
thing.setColor(Qt::green);
return app.exec();
}
which calls thing.h:
class Thing : public QObject
{
Q_OBJECT
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
public:
Thing() : _color(Qt::black), _text("text") { }
Q_INVOKABLE void clicked() { setColor(Qt::blue); }
QColor color() const {return _color;}
void setColor(const QColor &color) {
_color = color;
emit colorChanged();
}
signals:
void colorChanged();
private:
QColor _color;
};
and main.qml:
Window {id: main
width: 100; height: 100
color: thing.color
MouseArea {
anchors.fill: parent
onClicked: thing.clicked();
}
}
When running this code, I get 'qrc:/main.qml:6: ReferenceError: thing is not defined' which refers to the execution color: thing.color in main.qml. How can I get it work ?
You can try to expose your root context property "thing" before loading your main component. It will ensure that your "thing" property will be available once the component instance is created and its bindings are evaluated for the first time.
#include "thing.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
Thing thing;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("thing", &thing);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
thing.setColor(Qt::green);
return app.exec();
}

How to check how long a button is pressed?

I'm looking for a way to check how long a button is pressed! My idea was starting a counter which starts when pressed() is emitted and stops when released() is emitted. But I currently don't know how to wait for the released- or similar events. Normally I would connect() a signal with a slot but in this case both are slots. Maybe you've got a better idea maybe this one is good enough.
When pressed. Start a timer. When released check for how long the timer has been running (and stop it). Save the elapsed time in a variable in the class.
That's one way to do it at least.
When the pressed signal is sent, start the timer. When the relesed signal is sent, read how much time has elapsed on the timer.
This is a complete example using Qt 5 and a C++ compiler that's not prehistoric:
// https://github.com/KubaO/stackoverflown/tree/master/questions/button-timer-38645219
#include <QtWidgets>
int main(int argc, char ** argv) {
QApplication app{argc, argv};
QWidget w;
QFormLayout layout{&w};
QPushButton button{"Press Me"};
QLabel label;
layout.addRow(&button);
layout.addRow(&label);
QElapsedTimer timer;
QObject::connect(&button, &QPushButton::pressed, [&]{ timer.start(); });
QObject::connect(&button, &QPushButton::released, [&]{
label.setText(QStringLiteral("Pressed for %1 ms").arg(timer.elapsed()));
});
w.show();
return app.exec();
}
Of course you can shove all the variables into a class. You'll notice that C++11 makes this transformation almost a no-brainer: it's by design, not by coincidence.
#include <QtWidgets>
class Widget : public QWidget {
QFormLayout layout{this};
QPushButton button{"Press Me"};
QLabel label;
QElapsedTimer timer;
public:
Widget() {
layout.addRow(&button);
layout.addRow(&label);
connect(&button, &QPushButton::pressed, [&]{ timer.start(); });
connect(&button, &QPushButton::released, [&]{
label.setText(QStringLiteral("Pressed for %1 ms").arg(timer.elapsed()));
});
}
};
int main(int argc, char ** argv) {
QApplication app{argc, argv};
Widget w;
w.show();
return app.exec();
}
It is generally frowned upon to have "business logic" in UI objects, so we can separate out the functionality into that of a Timer, a Widget, and of a Controller. The code external to all of these objects sets up the requisite connections.
#include <QtWidgets>
class Timer : public QObject {
Q_OBJECT
QElapsedTimer timer;
public:
Q_SLOT void start() { timer.start(); }
Q_SLOT void stop() { emit elapsed(timer.elapsed()); }
Q_SIGNAL void elapsed(qint64);
};
class Widget : public QWidget {
Q_OBJECT
QFormLayout layout{this};
QPushButton button{"Press Me"};
QLabel label;
public:
Widget() {
layout.addRow(&button);
layout.addRow(&label);
connect(&button, &QPushButton::pressed, this, &Widget::pressed);
connect(&button, &QPushButton::released, this, &Widget::released);
}
Q_SIGNAL void pressed();
Q_SIGNAL void released();
Q_SLOT void setText(const QString & text) { label.setText(text); }
};
class Controller : public QObject {
Q_OBJECT
public:
Q_SLOT void elapsed(qint64 ms) {
emit hasText(QStringLiteral("Pressed for %1 ms").arg(ms));
}
Q_SIGNAL void hasText(const QString &);
};
int main(int argc, char ** argv) {
QApplication app{argc, argv};
Timer t;
Widget w;
Controller c;
w.show();
//
QObject::connect(&w, &Widget::pressed, &t, &Timer::start);
QObject::connect(&w, &Widget::released, &t, &Timer::stop);
QObject::connect(&t, &Timer::elapsed, &c, &Controller::elapsed);
QObject::connect(&c, &Controller::hasText, &w, &Widget::setText);
return app.exec();
}
#include "main.moc"
Of course you can scream that the controller, even if not really coupled to the other code, is still somewhat dependent on the design of the rest of the code. Thankfully, it doesn't have to be so: lambdas make simple adaptations simple.
We can use a "foreign" controller, provided by our big business vendor, too:
// use Timer and Widget from preceding example
#include <sstream>
#include <string>
#include <functional>
class Controller {
public:
using callback_t = std::function<void(const std::string&)>;
Controller(callback_t && callback) : callback{std::move(callback)} {}
void onElapsed(int ms) {
std::stringstream s;
s << "Pressed for " << ms << " ms";
callback(s.str());
}
private:
callback_t callback;
};
int main(int argc, char ** argv) {
QApplication app{argc, argv};
Timer t;
Widget w;
Controller c{ [&](const std::string & s){ w.setText(QString::fromStdString(s)); } };
QObject::connect(&w, &Widget::pressed, &t, &Timer::start);
QObject::connect(&w, &Widget::released, &t, &Timer::stop);
QObject::connect(&t, &Timer::elapsed, [&](qint64 ms) { c.onElapsed(ms); });
w.show();
return app.exec();
}
#include "main.moc"
All of the examples above are compileable: put any one of them into main.cpp, rebuild the project, and go.
When you're posting test cases for questions, you should go backwards: you start somewhere towards the end of this answer, and work your way towards the beginning, minimal setup. It'll help you understand your own code better, too, since the verbosity is minimized. It's telling, I hope, that both the first and the last version have same functionality.

Exchange data from C++ to QML

And sorry about the vague title, i don't know how to express it better, so pardon me.
I got a class to store data, inherit from QObject, and a loginSignal
class UserData : public QObject {
Q_OBJECT
public:
Q_INVOKABLE QString login(const QString p_user, const QString p_password, const bool p_remember);
UserData(QObject* parent = 0);
signals:
void userLogin();
};
Register class to QML
qmlRegisterType<UserData>("UserData",1,0,"UserData");
Connect to a QML Element
ApplicationWindow {
UserData {
id:userData
}
Rectangle {
Connections {
target: userData
onUserLogin : {
doSomething()
}
}
}
}
However if i call emit(userLogin()) signal inside login() function, nothing happend in QML
int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine(QUrl("qrc:///mainControl.qml"));
QQmlContext* context = engine.rootContext();
UserData userData;
context->setContextProperty("userData",&userData);
userData.login(user, pass, true);
return app.exec();
}
Maybe QML Component hasn't complete finished yet?
And thank for dropping by
Edit : Look like i accident create a second instance in QML, and use it. Everything working fine now :D

Update QLineEdit with Signal and Slot QT

I am writing to ask for advice on how best to implement my code using QT library. I have a class called Action class that every one second retrieve the PC time (with gettimeofday), this value shall be displayed in the GUI. So I have a class widget that defines all the widgets necessary for the GUI. The value (expressed in seconds) will be displayed with a QLineEdit.
So my question is, how I have to implement Signal and slot to update the value in QLineEdit?
Should I emit a signal every time the function retreiveTimetoSend is called?
action.h
class Action: public object
{
Q_OBJECT
private:
Qtimer timer;
unisgned int timetosend;
private:
void retreiveTimetoSend();
public:
Action();
~Action();
public slots:
void doWork();
}
action.cpp
void retreiveTimetoSend()
{
struct timeval Now;
unsigned int Sec;
gettimeofday(&Now, NULL);
Sec = Now.tv_sec;
time.value =Sec;
}
void Action::Action()
{
timer.setInterval(1000);
connect(&timer, SIGNAL(timeout()), this, SLOT (doWork()));
timer.start();
}
void Action::doWork()
{
retreiveTimetoSend()
}
widget.h
class widgets: public QWidget
{
Q_OBJECT
private:
QLineEdit *displayTime;
public:
widget(action *w_test);
}
widget.cpp
widgets::widgets(action *w_test)
{
displayTime= new QLineEdit();
displayTime->setText(QString::number(w_test->timetosend,10));
displayTC->setStyleSheet("color: blue; background-color: red");
}
main.cpp
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Action *test = new Action;
Thread *threadtest = new QThread;
test->moveToThread(threadtest);
QObject::connect(threadtest, SIGNAL(started()), test ,SLOT(doWork()));
widget *mainwindows = new widget(test);
mywindow->show();
threadtest->start();
return app.exec();
}
Instead using gettimeofday use QTime::currentTime then convert it to string (chose the format) and emit result. This signal should be connected to slot QLineEdit::setText.
Using thread is completely obsolete here.

Using QQmlContext::setContextObject to make a C++ object visible to QML

Edit: Problem solved. See my edit below
I am having trouble using QQmlContext::setContextObject to make a C++ object visible to QML. I have read the documentation for QQmlContext at link, which suggests that I can use setContextObject to make the Q_PROPERTY's of a QObject-derived class visible to QML. The following code illustrates the problem.
main.cpp
#include <QObject>
#include <QQmlEngine>
#include <QGuiApplication>
class MyClass : public QObject
{
Q_OBJECT
Q_PROPERTY(QString myProperty READ prop NOTIFY propChanged)
public:
MyClass(QObject * parent = 0) : QObject(parent) {}
QString prop() { return QString("Hello from MyClass"); }
Q_SIGNALS:
void propChanged(void);
};
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlEngine engine;
QQmlContext *objectContext = new QQmlContext(engine.rootContext());
MyClass myClass;
objectContext->setContextObject(&myClass);
QQmlComponent component(&engine, "main.qml");
QObject *object = component.create(objectContext);
return app.exec();
}
main.qml
import QtQuick 2.1
import QtQuick.Controls 1.0
ApplicationWindow
{
Text
{
text: myProperty
}
}
When I run this program I get the error
file:///C:/Path/to/main.qml:8: ReferenceError: myProperty is not defined
Thank you in advance for any help.
Environment. I am using Qt 5.1.1 on Windows 7, with MSVC2010 compiler
Edit. Answering my own question. A clean rebuild showed that my build output folder clearly had some out-of-date objects in it.
One point of note: MyClass has to be in a separate file, or else the moc compiler cannot do its magic.
My tidied-up main.cpp now looks like this
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlEngine engine;
QQmlContext * context = new QQmlContext(engine.rootContext());
QObject::connect(&engine, SIGNAL(quit()), QCoreApplication::instance(), SLOT(quit ()));
MyClass myClass;
context->setContextObject(&myClass);
QQmlComponent component(&engine, "main.qml");
QQuickWindow * topLevel = qobject_cast<QQuickWindow*>(component.create(context));
topLevel->show();
int rc = app.exec();
delete topLevel;
delete context;
return rc;
}
You can try to add Q_INVOKABLE macro in you getter function decalration. If it will not help you can consider using QQmlContext::setContextProperty to do this. I have never seen that somebody is doing this kind of integration using ::setContextObject.