C++ does not find QML StackView - c++

I need to push a qml page to stackview from other qml page.
So i am using c++-qml interacting for this problem.
my c++ code finds page item, but does not find stackView.
main.cpp:
int main(int argc, char *argv[])
{
...
QQmlEngine engine;
QQmlComponent component(&engine, QUrl("qrc:/qml/main.qml"));
QObject* object = component.create();
Settings::defineObject(object);
return app.exec();
}
settings.h:
class Settings : public QObject
{
Q_OBJECT
private:
static QObject* object;
public:
...
Q_INVOKABLE void push(QString fileName);
static void defineObject(QObject* obj);
};
settings.cpp:
QObject* Settings::object = nullptr;
...
void Settings::push(QString fileName)
{
if (Settings::object == nullptr) QCoreApplication::exit(-3);
QObject* page = Settings::object->findChild<QObject*>("page");
if (!page) QCoreApplication::exit(-4);
QObject* stackView = page->findChild<QObject*>("stackView");
if (!stackView) QCoreApplication::exit(-5);
QMetaObject::invokeMethod(stackView, "push", Q_ARG(QString, fileName));
}
void Settings::defineObject(QObject* obj)
{
object = obj;
}
main.qml:
ApplicationWindow {
...
Page {
id: page
...
StackView {
id: stackView
initialItem: "Home.qml"
anchors.fill: parent
}
}
}
My program always exits -5 code.

Related

Connect qml object property changes to signal/slot mechanism

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*)));

Best way to create Elements dynamically in QML based on C++ QMap

This seemed like a simple task at first, but honestly I am struggling. I have a Map of profiles I load at startup, and profiles can be added and deleted at runtime. Those profiles need to be listed as RadioButtons or some other element for selection/editing in my UI.
I've tried using createQMLObject() while looping trough a QVariantMap getter:
profile.h
#ifndef PROFILE_H
#define PROFILE_H
#include <QObject>
class Profile final : public QObject
{
Q_OBJECT
public:
Profile(QObject *parent = nullptr);
~Profile(){}
Profile(QString profileName = "default", QObject *parent = nullptr);
QString getProfileName() const;
void setProfileName(const QString &value);
private:
QString profileName;
};
#endif // PROFILE_H
profile.cpp
#include "profile.h"
Profile::Profile(QString profileName, QObject *parent)
: QObject(parent), profileName{ profileName }{}
QString Profile::getProfileName() const
{
return profileName;
}
void Profile::setProfileName(const QString &value)
{
profileName = value;
}
controller.h
#ifndef CONTROLLER_H
#define CONTROLLER_H
#include "profile.h"
#include <QObject>
#include <QQmlEngine>
class Controller : public QObject
{
Q_OBJECT
public:
Controller(QObject *parent = nullptr);
~Controller(){}
static QObject* controllerSingletonProvider(QQmlEngine *engine, QJSEngine *scriptEngine);
static Controller* instance();
Q_INVOKABLE void addProfile(QString profileName);
Q_INVOKABLE QVariantList getLoadedProfiles(){
QVariantList rval;
foreach (QString key, loadedProfiles.keys()) {
rval.append(QVariant::fromValue<Profile*>(loadedProfiles[key].get()));
}
return rval;
};
private:
QMap<QString, std::shared_ptr<Profile>> loadedProfiles;
signals:
};
#endif // CONTROLLER_H
controller.cpp
#include "controller.h"
Controller::Controller(QObject *parent) : QObject(parent){}
void Controller::addProfile(QString profileName)
{
std::shared_ptr<Profile> profile{std::make_shared<Profile>(profileName)};
loadedProfiles[profile->getProfileName()] = profile;
}
QObject* Controller::controllerSingletonProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
return Controller::instance();
}
Controller* Controller::instance(){
static Controller* controller = new Controller();
return controller;
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <controller.h>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
qmlRegisterSingletonType<Controller>("com.controller", 1, 0, "Controller", Controller::controllerSingletonProvider);
Controller::instance()->addProfile("Profile1");
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 com.controller 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Column {
id: radioButtonColumn
Component.onCompleted: {
for(var profile in Controller.getLoadedProfiles()){
var obj = Qt.createQmlObject('import QtQuick 2.12; RadioButton { id: '+ profile.getProfileName() +';
text: '+ profile.getProfileName() +'; }', radioButtonColumn, "dynamicSnippet1");
}
}
}
}
I've tried using a signal sent from my Controller Singleton using Connections, but the parameter of the signal does not arrive. I can however call my Controllers c++ functions from qml. I'll only post the changes to the previous code sample:
main.qml changes:
Connections {
target: Controller
onProfileAdded: {
var obj = Qt.createQmlObject('import QtQuick 2.12; RadioButton { id: '+ profileName +';
text: '+ profileName +'; }', radioButtonColumn, "dynamicSnippet1");
console.log("Value changed")
}
}
controller.h changes:
signals:
void profileAdded(QString &profileName);
};
controller.cpp changes:
void Controller::addProfile(QString profileName)
{
std::shared_ptr<Profile> profile{std::make_shared<Profile>(profileName)};
loadedProfiles[profile->getProfileName()] = profile;
emit profileAdded(profileName);
}
main.cpp changes:
engine.load(url);
Controller::instance()->addProfile("Profile2");
return app.exec();
Edit: Curious, that I had to change my onProfileAdded in controller.h to profileAdded for the signal to work. However, I still do not receive the parameter profileName in my qml.
I would like to ask what is the best way to handle a requirement like this. Maybe there is another, simpler way?
Im new to both c++ and QT, so please bear with my perhaps trivial question, but my research didnt yield me anything, that I could understand at least.

How to update a QML text with c++

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"

Accesing C++ model from QML

I have created a minimal working example. I hope it will be understandable. My problem is that I cannot create a model for my top level item in order to access items further. This is how the classes objects architecture looks like:
CTop
x times CMiddle
y times CBottom
This is a tree architecture. Here is the code:
CBottom.h:
#ifndef CBOTTOM_H
#define CBOTTOM_H
#include <QObject>
#include <QtQml>
class CBottom : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
public:
CBottom(QObject *parent = nullptr) : QObject(parent)
{
}
CBottom(const QString& name, QObject *parent = nullptr) : QObject(parent)
{
qmlRegisterType<CBottom>("Bottom", 1, 0, "Bottom");
m_name = name;
}
QString name()
{
return m_name;
}
void setName(const QString& name)
{
if (name != m_name)
{
m_name = name;
emit nameChanged();
}
}
signals:
void nameChanged();
private:
QString m_name;
};
#endif // CBOTTOM_H
CMiddle.h:
#ifndef CMIDDLE_H
#define CMIDDLE_H
#include <QObject>
#include <QtQml>
#include <QVector>
#include "cbottom.h"
class CMiddle : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
public:
CMiddle(QObject *parent = nullptr) : QObject(parent)
{
}
CMiddle(const QString& name, const QStringList& bottoms, QObject *parent = nullptr) : QObject(parent)
{
qmlRegisterType<CMiddle>("Middle", 1, 0, "Middle");
m_name = name;
foreach (auto bottom, bottoms)
{
m_bottoms.append(new CBottom(bottom));
}
}
QString name()
{
return m_name;
}
void setName(const QString& name)
{
if (name != m_name)
{
m_name = name;
emit nameChanged();
}
}
signals:
void nameChanged();
private:
QString m_name;
QVector<CBottom*> m_bottoms;
};
#endif // CMIDDLE_H
CTop.h:
#ifndef CTOP_H
#define CTOP_H
#include <QObject>
#include <QtQml>
#include "cmiddle.h"
class CTop : public QObject
{
Q_OBJECT
public:
CTop(QObject *parent = nullptr) : QObject(parent)
{
}
CTop(const QStringList& middles, QObject *parent = nullptr) : QObject(parent)
{
qmlRegisterType<CTop>("Top", 1, 0, "Top");
int i = 0;
foreach (auto middle, middles)
{
QStringList bottoms;
bottoms.append("A" + QString(i));
bottoms.append("B" + QString(i));
bottoms.append("C" + QString(i));
i++;
m_middles.append(new CMiddle(middle, bottoms));
}
}
Q_INVOKABLE QVector<CMiddle*>& middles()
{
return m_middles;
}
Q_INVOKABLE CMiddle* middle(const int index)
{
return m_middles[index];
}
private:
QVector<CMiddle*> m_middles;
};
#endif // CTOP_H
main.c:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QString>
#include <QVector>
#include <QStringList>
#include <QVariant>
#include <QQmlContext>
#include "ctop.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QStringList middles;
middles.append("FirstMiddle");
middles.append("SecondMiddle");
CTop* top = new CTop(middles);
QQmlApplicationEngine engine;
QQmlContext *ctxt = engine.rootContext();
ctxt->setContextProperty("theTop", QVariant::fromValue(top));
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml:
import QtQuick 2.9
import QtQuick.Window 2.2
import Top 1.0
Window
{
width: 600
height: 400
visible: true
Repeater
{
model: theTop.middles();
delegate: txtComp;
}
Component
{
id: txtComp;
Text
{
text: name;
}
}
}
The question is: Why isn't the name of the one of the CMiddle objects displayed in the QML code? I wanted to obtain the CMiddle components (to act as a model) from the exported to QML CTop components. If this code worked, I would then go further and made another model for accessing the CBottom objects within each CMiddle object.
For instance, I noticed that this QML code works:
import QtQuick 2.9
import QtQuick.Window 2.2
import Top 1.0
Window
{
width: 600
height: 400
visible: true
Repeater
{
model: theTop;
delegate: txtComp;
}
Component
{
id: txtComp;
Text
{
text: middle(0).name;
}
}
}
It doesnt make much sense, but shows that the CTop component is exposed correctly to the QML part.
Why cant the output Qvector of CMiddle pointers act as a model in the QML part?
QML does not know QList<CMiddle *>, instead you should use QList<QObject *>:
CTop.h
// ...
Q_INVOKABLE QList<QObject*> middles(){
QList<QObject*> objects;
for(CMiddle *m : qAsConst(m_middles)){
objects << m;
}
return objects;
}
// ...
Also another error is that top should not necessarily be a pointer, and you can pass it without using QVariant::fromValue() since it is a QObject:
main.cpp
// ...
CTop top(middles);
QQmlApplicationEngine engine;
QQmlContext *ctxt = engine.rootContext();
ctxt->setContextProperty("theTop", &top);
Also in a Repeater to obtain the item of the model using modelData:
main.qml
//...
Component{
id: txtComp;
Text{
text: modelData.name
}
}
//...
The complete project can be found here

How to interact with the value of a Slider in Sailfish Silica / QML?

I seem to be totally lost in the declaration of forms in QML. I have a C++ object with properly set Q_PROPERTies, I have access to an object of that class in the QML, and I want to use sliders in the QML. How exactly do I make the value of the Slider update the information of the properties in the object, and vise-versa?
If it's anything like regular QML, then the following approaches will work.
Context property
Use an explicit Binding or use Slider's valueChanged signal:
#include <QGuiApplication>
#include <QtQml>
class Object : public QObject
{
Q_OBJECT
Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged)
public:
explicit Object(QObject *parent = 0) :
QObject(parent),
mValue(0)
{
}
qreal value() const {
return mValue;
}
void setValue(qreal value) {
if (value != mValue) {
mValue = value;
emit valueChanged();
}
}
signals:
qreal valueChanged();
private:
qreal mValue;
};
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
Object object;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("object", &object);
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
return app.exec();
}
#include "main.moc"
main.qml:
import QtQuick 2.3
import QtQuick.Controls 1.2
ApplicationWindow {
width: 400
height: 400
visible: true
Binding {
target: object
property: "value"
value: slider.value
}
Slider {
id: slider
// You can also react to the valueChanged signal of Slider:
//onValueChanged: object.value = value
}
}
Registered QML Type
Use a simple binding:
#include <QGuiApplication>
#include <QtQml>
class Object : public QObject
{
Q_OBJECT
Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged)
public:
explicit Object(QObject *parent = 0) :
QObject(parent),
mValue(0)
{
}
qreal value() const {
return mValue;
}
void setValue(qreal value) {
if (value != mValue) {
mValue = value;
emit valueChanged();
}
}
signals:
qreal valueChanged();
private:
qreal mValue;
};
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterType<Object>("Test", 1, 0, "Object");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
return app.exec();
}
#include "main.moc"
main.qml:
import QtQuick 2.3
import QtQuick.Controls 1.2
import Test 1.0
ApplicationWindow {
width: 400
height: 400
visible: true
Object {
id: object
value: slider.value
onValueChanged: print(value)
}
Slider {
id: slider
}
}