I have a small problem. Can someone show me how can I update a qml text from c++. I have an example using threads but I don't want to apply this method because I don't know how to set a
parameter in run() function.To fully understand me here is my code.In the main function when I start the thread I want to put my custom text or a string variable that has a text.
thread.h
#ifndef THREAD_H
#define THREAD_H
#include <QThread>
class Thread : public QThread
{
Q_OBJECT
public:
Thread(QObject *parent=nullptr);
~Thread() override;
Q_SLOT void stop();
Q_SIGNAL bool textChanged(const QString & text);
protected:
void run() override;
};
#endif // THREAD_H
thread.c
#include "thread.h"
#include <QDebug>
Thread::Thread(QObject *parent):
QThread(parent)
{
}
Thread::~Thread()
{
}
void Thread::stop()
{
requestInterruption();
wait();
}
void Thread::run() //here I want to specify a QString variable such that in main function to call the function with my text, and not specified the text from here
{
int i=0;
while(!isInterruptionRequested()){
QString text;
text = QString("I changed the text"); // I don't want to declare from here the text.
Q_EMIT textChanged(text);
QThread::msleep(20);
qDebug() <<i++;
}
}
main.cpp
...
Thread thread;
QQmlApplicationEngine engine;
QObject::connect(&app, &QGuiApplication::aboutToQuit, &thread, &Thread::stop);
thread.start();
engine.rootContext()->setContextProperty("thread", &thread);
engine.load(QUrl("qrc:/main.qml"));
thread.stop();
...
main.qml
.....
Text {
objectName: "myLabel"
id: txt
width: 200
height: 29
color: "#faf9f9"
text: qsTr("Text")
font.pixelSize: 12
}
Connections{
target: thread
onTextChanged: txt.text = text
}
.....
You can send data from C++ to qml using signals, like this:
//main.cpp
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
// Class init
YourClass yourObject;
// Embedding C++ Objects into QML with Context Properties
QQmlContext* ctx = engine.rootContext();
ctx->setContextProperty("yourObject", &yourObject);
return app.exec();
}
//main.qml
import QtQuick 2.6
Window {
id: mainWindow
Connections {
target: gapi
onSignalData: {
console.log("Data: " + data)
textToChange.text = "Changed to: " + data
}
}
Text {
id: textToChange
text: "beforeChange"
}
}
//yourClass.h
class YourClass : public QObject
{
signals:
// Signal from YourClass to QML
void signalData(QString data);
}
//yourClass.cpp
emit signalData("Hello QML"); // Signal from GAPI to QML
This page https://felgo.com/cross-platform-development/how-to-expose-a-qt-cpp-class-with-signals-and-slots-to-qml have a complete tutorial about "How to Expose a Qt C++ Class with Signals and Slots to QML"
Related
I got this basic project:
main.qml
Rectangle{
Text {
id: text1
objectName: "showStr"
property string _textField: "passive"
text: _textField
}
Button{
id: _button
signal buttClick(string str)
anchors.top: text1.bottom
text: "Button"
onClicked:
{
_button.buttClick("state: active")
}
}
}
myClass.cpp
class myClass : public QObject{
Q_OBJECT
public:
explicit myClass(QObject *parent = nullptr): QObject(parent) {}
public slots:
void setTextProperty(const QString& s) {this->str = s;}
void getStrToQObj()
{
//TODO: set updated str into qml
}
signals:
void strChanged(const QString& time);
private:
QString str;
};
main.cpp
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
QQmlComponent component(&engine,
QUrl(QLatin1String("../myProj/qml/main.qml")));
QObject* item = component.create();
QObject* showTime = item->findChild<QObject*>("showTime");
QObject* butt = item->findChild<QObject*>("start");
myClass mc(item);
QObject::connect(butt, SIGNAL(buttClick(QString)),
&mc, SLOT(setTextProperty(QString)));
return app.exec();
}
Connection of Qml signal buttClick to setTextProperty(const QString& s) works fine and myClass::str is changed.The question is how to connect Text property _textField to update every time, when myClass::str is changed?
Do not export objects from QML to C++, instead do the opposite: export the C ++ objects to QML since this way the connection is simple and there is no need to use the old connection syntax:
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
myClass mc;
engine.rootContext()->setContextProperty("mc", &mc);
QQmlComponent component(&engine,
QUrl(QLatin1String("../myProj/qml/main.qml")));
QObject* item = component.create();
return app.exec();
}
Rectangle {
Text {
id: text1
text: "passive"
}
Button {
id: _button
anchors.top: text1.bottom
text: "Button"
onClicked: {
mc.setTextProperty("state: active");
}
}
Connections {
function onTimeChanged(text) {
text1.text = text;
}
target: mc
}
}
It is old syntax of connection, but according to separations logiv from GUI, and for ones who do not know JS well,turns out you can call parent() method to you class because it is inherit QObject class. This return to you Qobject* you put in constructor of your class, and then you can findChild and work with it:
void myClass::f(){
//do fancy stuff with your myClass::str
emit activityChanged(this->parent())
}
void getStrToQObj(QObject* qo)
{
auto tmp = qo->findChild<QObject*>("showTime");
tmp->setProperty("_textField", this->str);
}
And connect this in main.cpp like:
QObject::connect(&mc, SIGNAL(strChanged(QObject*)),
&mc, SLOT(getStrToQObj(QObject*)));
I have a larger project that crashes randomly after a few hours. I believe it is due to qml accessing Q_PROPERTY in my C++ QObject, where the Q_PROPERTY is set in another thread, causing a data race.
I have a simple toy project here. In it main.qml has a label that accesses a QProperty test.testChild.time. test.testChild.time is set in another thread when a QTimer expires and calls Test::timerExpired(). Test::timerExpired() has a QThread::usleep(100000) to simulate a long operation. When I comment the moveToThread(&m_workerThread) line below, the ui is much less responsive than before but everything is in the same thread so I believe data race cannot occur. If I don't comment moveToThread(&m_workerThread) and have timer.setInterval(0), the ui is very responsive but a crash occurs usually within a few minutes on my Ubuntu VM.
The code below is a section from test.cpp in my simple toy project here
Test::Test(QObject *parent) : QObject(parent)
// , testChild(this) // uncommenting this line will cause testChild to move to m_workerThread
{
uiThread = QThread::currentThreadId();
// move this object to a separate thread so does not interrupt UI thread
moveToThread(&m_workerThread); // commenting this line will make test::timerExpired() run on UI thread and slow down UI
timer.setInterval(0); // with interval 0, more likely to seg fault due to data race
connect(&timer, SIGNAL(timeout()), this, SLOT(timerExpired()));
connect(&m_workerThread, SIGNAL(started()), &timer, SLOT(start()));
m_workerThread.start();
}
void Test::timerExpired()
{
if (uiThread == QThread::currentThreadId())
qCritical() << "timerExpired() Controller thread is same as UI thread";
QMutexLocker locker(&mutex);// prevent UI thread from accessing objects below while they update with a QMutexLocker
QString time;
time = QDateTime::currentDateTime().toString("hh:mm:ss.zzz");
QThread::usleep(100000); // long operation
setTime(time);
testChild.setTime(time);
}
How can I safely have two threads, one for qml and another for long operations, while qml can access QProperties that update in the long operations thread?
I believe I have solved this using a model so the QML can safely communicate with the another thread.
The model is instantiated in the UI thread. The model receives data using signals and slots from the other thread (which is threadsafe). The model has QProperties that QML can interface with. When the model receives new data from another thread, QProperties are updated and QML receives new data.
The project is here but is also included below
TestQPropertyThread.pro
QT += quick
CONFIG += c++11
DEFINES += QT_DEPRECATED_WARNINGS
QMAKE_CXXFLAGS += -g -Wall -Werror
SOURCES += \
controller.cpp \
controllerchild.cpp \
main.cpp \
model.cpp
RESOURCES += qml.qrc
HEADERS += \
controller.h \
controllerchild.h \
model.h
main.cpp
#include "controller.h"
#include "model.h"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
Controller test;
Model model(test);
engine.rootContext()->setContextProperty(QStringLiteral("model"), &model);
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();
}
controller.cpp
#include "controller.h"
Controller::Controller(QObject *parent) : QObject(parent), testChild(this), timer(this) // testChild to move to m_workerThread
{
uiThread = QThread::currentThreadId();
// move this object to a separate thread so does not interrupt UI thread
moveToThread(&m_workerThread); // commenting this line will make test::timerExpired() run on UI thread and slow down UI
timer.setInterval(0); // with interval 0, more likely to seg fault due to data race
connect(&timer, SIGNAL(timeout()), this, SLOT(timerExpired()));
connect(&m_workerThread, SIGNAL(started()), &timer, SLOT(start()));
m_workerThread.start();
}
void Controller::timerExpired()
{
if (uiThread == QThread::currentThreadId())
qCritical() << "Controller::timerExpired() Controller thread is same as UI thread";
// prevent UI thread from accessing objects below while they update with a QMutexLocker
QMutexLocker locker(&mutex);
QString time;
time = QDateTime::currentDateTime().toString("hh:mm:ss.zzz");
qDebug() << "Controller::timerExpired()" << time; // this can impact preformance when usleep is commented out
QThread::usleep(100000); // long operation
testChild.setTime(time);
}
controller.h
#ifndef TEST_H
#define TEST_H
#include <QObject>
#include <QTimer>
#include <QThread>
#include <QMutex>
#include <QDebug>
#include <QDateTime>
#include "controllerchild.h"
class Controller : public QObject
{
Q_OBJECT
public:
explicit Controller(QObject *parent = nullptr);
ControllerChild testChild;
public slots:
void timerExpired();
private:
QTimer timer;
QThread m_workerThread;
Qt::HANDLE uiThread;
QMutex mutex;
};
#endif // TEST_H
controllerchild.cpp
#include "controllerchild.h"
ControllerChild::ControllerChild(QObject *parent) : QObject(parent)
{
}
void ControllerChild::setTime(const QString &value)
{
time = value;
emit timeChanged(time);
}
void ControllerChild::onButtonPress(const QString &value)
{
qDebug() << "ControllerChild::onButtonPress()" << value << QThread::currentThreadId() << QDateTime::currentDateTime().toString("hh:mm:ss.zzz");
emit onBoolChanged(value); // send value back to UI label
}
controllerchild.h
#ifndef TESTCHILD_H
#define TESTCHILD_H
#include <QObject>
#include <QDebug>
#include <QThread>
#include <QDateTime>
class ControllerChild : public QObject
{
Q_OBJECT
public:
explicit ControllerChild(QObject *parent = nullptr);
void setTime(const QString &value);
public slots:
void onButtonPress(const QString &value);
signals:
void timeChanged(const QString &value);
void onBoolChanged(QString value);
private:
QString time;
};
#endif // TESTCHILD_H
model.cpp
#include "model.h"
Model::Model(Controller &test, QObject *parent) : QObject(parent), test(test)
{
connect(&test.testChild, &ControllerChild::timeChanged, this, [=](const QString &time){ this->setTime(time); });
connect(this, &Model::onButtonPressChanged, &test.testChild, &ControllerChild::onButtonPress);
connect(&test.testChild, &ControllerChild::onBoolChanged, this, [=](const QString &value){ this->setBoolValue(value); });
}
QString Model::getTime() const
{
// return QString("test Child %1").arg(time);
return time;
}
void Model::setTime(const QString &value)
{
time = value;
emit timeChanged();
}
void Model::onButtonPress(const QString &value)
{
qDebug() << "Model::onButtonPress()" << value << QThread::currentThreadId() << QDateTime::currentDateTime().toString("hh:mm:ss.zzz");
emit onButtonPressChanged(value);
}
void Model::onBool(const QString &value)
{
qDebug() << "Model::onBool()" << value << QThread::currentThreadId() << QDateTime::currentDateTime().toString("hh:mm:ss.zzz");
emit onBoolChanged(value);
}
void Model::setBoolValue(const QString &value)
{
boolValue = value;
qDebug() << "Model::setBoolValue()" << value << QThread::currentThreadId() << QDateTime::currentDateTime().toString("hh:mm:ss.zzz");
emit onBoolChanged(value);
}
model.h
#ifndef MODEL_H
#define MODEL_H
#include <QObject>
#include "controller.h"
class Model : public QObject
{
Q_OBJECT
Q_PROPERTY(QString time READ getTime WRITE setTime NOTIFY timeChanged)
Q_PROPERTY(QString boolValue MEMBER boolValue NOTIFY onBoolChanged)
public:
explicit Model(Controller &test, QObject *parent = nullptr);
QString getTime() const;
void setTime(const QString &value);
void setBoolValue(const QString &value);
public slots:
void onButtonPress(const QString &value);
void onBool(const QString &value);
signals:
void timeChanged();
void onButtonPressChanged(const QString &value);
void onBoolChanged(const QString &value);
private:
QString time;
Controller &test;
QString boolValue;
};
#endif // MODEL_H
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Label {
id: time1
text: model.time
}
BusyIndicator {
id: busyIndicator
x: 0
y: 23
}
PathView {
id: pathView
x: 0
y: 135
width: 640
height: 130
delegate: Column {
spacing: 5
Rectangle {
width: 40
height: 40
color: colorCode
anchors.horizontalCenter: parent.horizontalCenter
}
Text {
x: 5
text: name
anchors.horizontalCenter: parent.horizontalCenter
font.bold: true
}
}
path: Path {
startY: 100
startX: 120
PathQuad {
x: 120
y: 25
controlY: 75
controlX: 260
}
PathQuad {
x: 120
y: 100
controlY: 75
controlX: -20
}
}
model: ListModel {
ListElement {
name: "Grey"
colorCode: "grey"
}
ListElement {
name: "Red"
colorCode: "red"
}
ListElement {
name: "Blue"
colorCode: "blue"
}
ListElement {
name: "Green"
colorCode: "green"
}
}
}
Button {
id: button
x: 0
y: 271
text: qsTr("Press me!!")
checkable: true
onToggled: model.onButtonPress(button.checked)
}
Label {
id: label
x: 106
y: 294
text: model.boolValue
}
}
I have some issues changing the text of a QML window in Qt. I have a C++ file that calls a thread and from there I'm trying to change the value of the text label. The thread is running correctly but the text value from the QML is not changing. Below is part of my code:
main.cpp:
int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:///template.qml")));
QQuickItem *label = engine.rootObjects().at(0)->findChild<QQuickItem*>("myLabel");
thread2 thread(label);
thread.start();
}
Thread.cpp:
thread2::thread2(QQuickItem *label) {
this->label = label;
}
void thread2::run() {
int test = 0;
char buffer[10];
int speed = 100;
while(1) {
speed++;
sprintf(buffer,"%d km/h",speed);
this->label->setProperty("text", QString(buffer));
QThread::msleep(1000);
qDebug()<<"tic: "<<buffer<<endl;
}
template.qml:
Window {
id: window
visible: true
width: 360
height: 360
Component {
id: fruitDelegate
Row {
spacing: 10
Text { text: name }
Text { text: '$' + cost }
}
}
Text {
width: 99
height: 19
text: qsTr("Speed: ")
anchors.verticalCenterOffset: 1
anchors.horizontalCenterOffset: 0
anchors.centerIn: parent
objectName: "lab"
}
Text {
width: 38
height: 19
text: qsTr(" 0 ")
anchors.verticalCenterOffset: 1
anchors.horizontalCenterOffset: 46
anchors.centerIn: parent
objectName: "myLabel"
}
}
Can anyone tell me why is not working? Where is my mistake?
Thanks!
You have 2 errors:
You should not update the GUI from another thread, the run method is executed in another thread so Qt does not guarantee that it works correctly.
Do not export an element from QML to C++ because it brings several problems since it is many times impossible to obtain the object through the objectname, another inconvenient is that the life cycle of the Item is determined by QML so at a given moment it could be eliminated so that label could point to an unreserved memory making its use, etc. Instead it exports the C++ object to QML.
Considering the above the solution is:
thread.h
#ifndef THREAD_H
#define THREAD_H
#include <QThread>
class Thread : public QThread
{
Q_OBJECT
public:
Thread(QObject *parent=nullptr);
~Thread() override;
Q_SLOT void stop();
Q_SIGNAL void textChanged(const QString & text);
protected:
void run() override;
};
#endif // THREAD_H
thread.cpp
#include "thread.h"
#include <QDebug>
Thread::Thread(QObject *parent):
QThread(parent)
{
}
Thread::~Thread()
{
}
void Thread::stop()
{
requestInterruption();
wait();
}
void Thread::run()
{
int speed = 100;
QString text;
while(!isInterruptionRequested()) {
speed++;
text = QString("%1 km/h").arg(speed);
Q_EMIT textChanged(text);
QThread::msleep(1000);
qDebug()<<"tic: "<< text;
}
}
main.cpp
#include "thread.h"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
Thread thread;
QObject::connect(&app, &QGuiApplication::aboutToQuit, &thread, &Thread::stop);
thread.start();
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("thread", &thread);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml
// ...
Text {
id: txt
width: 38
height: 19
text: qsTr(" 0 ")
anchors.verticalCenterOffset: 1
anchors.horizontalCenterOffset: 46
anchors.centerIn: parent
objectName: "myLabel"
}
Connections{
target: thread
onTextChanged: txt.text = text
}
// ...
For more information read:
https://doc.qt.io/qt-5/qtquick-bestpractices.html#interacting-with-qml-from-c
https://doc.qt.io/qt-5/thread-basics.html#gui-thread-and-worker-thread
You shouldn't modify the UI from another thread. Use signal/slot instead.
You should not create a child class from QThread, also (create a worker and move it in another thread).
Item {
id: rooItem
visible: true
anchors.fill: parent
signal change(string s);
onChange: foobar.text = s
Text {
id: foobar
text: "Empty"
}
}
class Worker: public QObject
{
Q_OBJECT
public slots:
void run()
{
while(1) {
QThread::msleep(1000);
emit changed(QDateTime::currentDateTime().toString());
}
}
signals:
void changed(QString);
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QThread* th = new QThread();
Worker* worker = new Worker();
worker->moveToThread(th);
QObject::connect(th, &QThread::started, worker, &Worker::run);
th->start();
QQuickView view(QStringLiteral("qrc:/Main.qml"));
QObject* o = view.rootObject();
QObject::connect(worker, SIGNAL(changed(QString)), o, SIGNAL(change(QString)));
view.showMaximized();
return app.exec();
}
You are using the wrong mechanism to update a qml property, take a look at QQmlProperty for the right way. You could also export a QObject instance into the qml engine and bind the labels text property to some property of that object. Always keep in mind that qml/qt quick are essentially hacks. There is a way to update the gui safely from a non-gui thread without using signals. instead you can use events to do the work.
I am working on a program which uses an user interface created in QML. The program at it's core have two threads: the main thread in which UI is running and the second thread which will handle all the rest of the work. So program have once class for interfacing with UI and a second class which it's the backend process. The problem is with connecting the slots/signals from UI class to the second class on the second thread.
The code is the following:
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "QmlInterface.h"
#include "MainClass.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
qmlRegisterType<QmlInterface>("CppInterface",1,0,"CppInterface");
/* Ui Stuff */
QmlInterface qml;
qml.SetupUI();
/* Main class */
MainClass work;
QObject::connect(&qml, SIGNAL(onButtonClicked()), &work, SLOT(on_ButtonClicked()) );
return app.exec();
}
QmlInterface.h
#ifndef QMLINTERFACE_H
#define QMLINTERFACE_H
#include <QObject>
#include <QDebug>
#include <QQmlApplicationEngine>
class QmlInterface : public QObject
{
Q_OBJECT
public:
explicit QmlInterface();
virtual ~QmlInterface();
void SetupUI();
public slots:
Q_INVOKABLE void buttonClicked();
signals:
void onButtonClicked();
private:
QQmlApplicationEngine *engine;
};
#endif // QMLINTERFACE_H
QmlInterface.cpp
#include "QmlInterface.h"
QmlInterface::QmlInterface()
{
}
QmlInterface::~QmlInterface()
{
}
void QmlInterface::SetupUI()
{
engine = new QQmlApplicationEngine;
engine->load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine->rootObjects().isEmpty())
{
qDebug() << "Failed to load UI";
}
}
void QmlInterface::buttonClicked()
{
qDebug() << "Button clicked! Signal to be emited!";
emit onButtonClicked();
}
MainClass.h
#ifndef MAINCLASS_H
#define MAINCLASS_H
#include <QThread>
#include <QtDebug>
class MainClass : public QThread
{
Q_OBJECT
public:
MainClass();
virtual ~MainClass() {}
public slots:
void on_ButtonClicked();
private:
void run();
};
#endif // MAINCLASS_H
MainClass.cpp
#include "MainClass.h"
MainClass::MainClass()
{
}
void MainClass::on_ButtonClicked()
{
qDebug() << "Button click received in main class!";
}
void MainClass::run()
{
while(1)
{
QThread::sleep(1);
}
}
And finally main.qml
import QtQuick 2.11
import QtQuick 2.8
import QtQuick.Controls 2.1
import QtQuick.Window 2.1
import CppInterface 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
CppInterface
{
id: cpp
}
Button
{
text: "Click me"
onPressed: {
cpp.buttonClicked();
}
}
}
The connection between QML and QmlInterface works fine! The problem is with connection between QmlInterface and MainClass.
To be more specific, the problem is that connect() function called in main.cpp seems not to be able to link given signal with given slot from MainClass:
QObject::connect(&qml, SIGNAL(onButtonClicked()), &work, SLOT(on_ButtonClicked()) );
The problem is that you have created 2 instances of QmlInterface:
QmlInterface qml;
qml.SetupUI();
and
CppInterface
{
id: cpp
}
And you have connected the signal of the first instance, and you are emitting the signal using the second instance.
So instead of creating 2 QmlInterface just create one, for convenience you do not create the object in QML and only export the object created in C++ using setContextProperty():
// ...
#include <QQmlContext>
// ...
void QmlInterface::SetupUI()
{
engine = new QQmlApplicationEngine;
engine->load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine->rootObjects().isEmpty())
qDebug() << "Failed to load UI";
else
// export
engine->rootContext()->setContextProperty("cpp", this);
}
*.qml
import QtQuick 2.11
import QtQuick.Controls 2.1
import QtQuick.Window 2.1
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Button
{
text: "Click me"
onPressed: cpp.buttonClicked();
}
}
On the other hand it is not necessary to register as a type to QmlInterface so you can remove it:
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
/* Ui Stuff */
QmlInterface qml;
qml.SetupUI();
/* Main class */
MainClass work;
QObject::connect(&qml, &QmlInterface::onButtonClicked, &work, &MainClass::on_ButtonClicked );
return app.exec();
}
I am trying to send parameters to my Model from the QML View but I'm stuck.
Please note that right now I am able to connect the QML View with the Model, but in order to not repeat my code I want to send some parameter (a QString) from the View and in the Model decide what to send back to the View.
fileparser.h
#ifndef FILEPARSER_H
#define FILEPARSER_H
#include <QObject>
class FileParser : public QObject
{
Q_OBJECT
Q_PROPERTY(QString file READ file WRITE setFile NOTIFY fileChanged)
public:
explicit FileParser(QObject *parent = 0);
FileParser(const QString &file, QObject *parent=0);
QString file() const;
void setFile(const QString &file);
QString fileFinder(QString file);
signals:
void fileChanged();
private:
QString m_file;
};
#endif // FILEPARSER_H
fileparser.cpp
#include "FileParser.h"
FileParser::FileParser(QObject *parent) :
QObject(parent)
{
}
FileParser::FileParser(const QString &file, QObject *parent)
: QObject(parent), m_file(file)
{
}
QString FileParser::file() const
{
return m_file;
}
void FileParser::setFile(const QString &file)
{
if (m_file != file){
m_file = file;
emit fileChanged();
}
}
QString FileParser::fileFinder(QString file)
{
if (file == “fileA“){
return “test file A“;
}
return “test file B“;
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <qqmlcontext.h>
#include <QtQuick/qquickview.h>
#include <QString>
#include "fileparser.h"
QString getFile(QString file)
{
FileParser *fileParser = new FileParser();
return fileParser->fileFinder(file);
}
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQuickView view;
view.setResizeMode(QQuickView::SizeRootObjectToView);
QQmlContext *ctxt = view.rootContext();
ctxt->setContextProperty("_parsedText", getFile(“Default“));
view.setSource(QUrl("qrc:main.qml"));
view.show();
return app.exec();
}
And finally the QML looks like this:
main.qml
import QtQuick 2.2
Item {
width: 400; height: 400
Text {
x: 0; y: parent.height
anchors.fill: parent
text: _parsedText // returns "test file B", now how can I send a parameter???
}
}
You are exposing just the string returned from getFile() as context property. Try to set as context property the whole parser and, in QML code, access to file property.
// main code
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQuickView view;
view.setResizeMode(QQuickView::SizeRootObjectToView);
QQmlContext *ctxt = view.rootContext();
FileParser parser("Default");
ctxt->setContextProperty("parser", &parser);
view.setSource(QUrl("qrc:main.qml"));
view.show();
return app.exec();
}
This way QML code can bind file property to other properties and watch for file changes. When you want to change file property, just asign a new value to it.
// QML code
import QtQuick 2.2
Item {
width: 400; height: 400
TextInput { // TextInput to allow an user change the text
x: 0; y: parent.height
anchors.fill: parent
text: parser.file // Read and watch for changes
onTextChanged: parser.file = text // this will call parser.setFile()
}
}