Set loader component by calling C++ function - c++

I'm just wondering if anything like this is possible :
Loader {
id: loader
objectName: "loader"
anchors.centerIn: parent
sourceComponent: cppWrapper.getCurrentDisplay();
}
And in C++ :
QDeclarativeComponent currentDisplay;
and
Q_INVOKABLE QDeclarativeComponent getCurrentDisplay() const
{ return currentDisplay; }
I have trouble compiling it (it fails in the moc file compilation), but if it's possible, it can be a real shortcut for me

Sure, you can create Component in C++ part (as QQmlComponent) and return it to QML part.
Simple example (I use Qt 5.4):
First of all I create a class to use it as singleton (just for ease using)
common.h
#include <QObject>
#include <QQmlComponent>
class Common : public QObject
{
Q_OBJECT
public:
explicit Common(QObject *parent = 0);
~Common();
Q_INVOKABLE QQmlComponent *getComponent(QObject *parent);
};
common.cpp
QQmlComponent *Common::getComponent(QObject *parent) {
QQmlEngine *engine = qmlEngine(parent);
if(engine) {
QQmlComponent *component = new QQmlComponent(engine, QUrl("qrc:/Test.qml"));
return component;
}
return NULL;
}
Now I create and register my singleton:
main.cpp
#include "common.h"
static QObject *qobject_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
static Common *common = new Common();
return common;
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
qmlRegisterSingletonType<Common>("Test", 1, 0, "Common", qobject_singletontype_provider);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
Ok, now we have a singleton and it is very simple to use it in QML:
main.qml
import QtQuick 2.3
import Test 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
id: mainWindow
Loader {
id: loader
objectName: "loader"
anchors.centerIn: parent
sourceComponent: Common.getComponent(mainWindow)
}
}
Also our component we created in C++:
Test.qml
import QtQuick 2.3
Rectangle {
width: 100
height: 100
color: "green"
border.color: "yellow"
border.width: 3
radius: 10
}
Pay attention: all our QML files are in resource but it's just for example and you can put it in any place you want in your case

Related

Access an QObject class from another QObject class by reference and parallel using in qml [duplicate]

I tried to operate a part of a qt project in Qt\Examples\Qt-5.9\quick\views, I am new to qml and I am trying to open each time a different QDialog window depending on qml pathview component that has been clicked. First of all, I started with creating a class (interfacageQML) which will serve to interface the qml Mainform and the QDialog (qtinterface), the necessary files are included among which interfacageqml.h.
here is the main.cpp :
#include "interfacageqml.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterType<interfacageQML>("Interfacage", 1, 0,"Component:MouseArea");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
And here's interfacageqml.h :
#ifndef INTERFACAGEQML_H
#define INTERFACAGEQML_H
#include <QObject>
#include "qtinterface.h"
class interfacageQML : public QObject
{
Q_OBJECT
public:
interfacageQML(QObject *parent);
~interfacageQML();
Q_INVOKABLE void mouseClick();
signals:
void clicked();
};
#endif // INTERFACAGEQML_H
interfacageqml.cpp :
#include "interfacageqml.h"
#include <QDebug>
#include <QApplication>
interfacageQML::interfacageQML(QObject *parent)
: QObject(parent)
{
}
interfacageQML::~interfacageQML()
{
}
void interfacageQML::mouseClick()
{
qDebug() << "qmlinterface::mouseClick()";
emit clicked();
}
My project is organised this way :
the qmlinterface.qrc file contains these paths:
main.qml :
import QtQuick 2.6
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
MainForm{
anchors.fill: parent
}
}
MainForm.qml :
import QtQuick 2.6
import QtQuick.Controls 2.0 as QQC2
import Interfacage 1.0
Rectangle {
width: 800
height: 800
color: "white"
ListModel {
id: appModel
ListElement {
name: "Contacts"
icon: "pics/Resources/AddressBook_48.png"
}
ListElement {
name: "Music"
icon: "pics/Resources/AudioPlayer_48.png"
}
ListElement {
name: "Movies"
icon: "pics/Resources/VideoPlayer_48.png"
}
ListElement {
name: "Camera"
icon: "pics/Resources/Camera_48.png"
}
ListElement {
name: "Calendar"
icon: "pics/Resources/DateBook_48.png"
}
ListElement {
name: "Todo List"
icon: "pics/Resources/TodoList_48.png"
}
}
Component {
id: appDelegate
Item {
width: 100
height: 100
scale: PathView.iconScale
Image {
id: myIcon
y: 20
anchors.horizontalCenter: parent.horizontalCenter
source: icon
}
Text {
anchors {
top: myIcon.bottom
horizontalCenter: parent.horizontalCenter
}
text: name
}
MouseArea {
anchors.fill: parent
onClicked: {
view.currentIndex = index
Interfacage.mouseClick
}
}
}
}
Component {
id: appHighlight
Rectangle {
width: 100
height: 80
color: "lightsteelblue"
}
}
PathView {
id: view
anchors.fill: parent
highlight: appHighlight
preferredHighlightBegin: 0.5
preferredHighlightEnd: 0.5
focus: true
model: appModel
delegate: appDelegate
path: Path {
startX: 50
startY: 80
PathAttribute {
name: "iconScale"
value: 2.0
}
PathQuad {
x: 250
y: 200
controlX: 50
controlY: 200
}
PathAttribute {
name: "iconScale"
value: 2.0
}
PathQuad {
x: 600
y: 50
controlX: 400
controlY: 200
}
PathAttribute {
name: "iconScale"
value: 2.0
}
}
}
}
When I run this project, i got an error :
error:C2280
However, when I comment this line : qmlRegisterType<interfacageQML>("Interfacage", 1, 0, "Component:MouseArea"); the project runs and I can navigate between the pathview components in the MainForm.
When you use qmlRegisterType you are registering a new data type in QML, it is not an object, in that case the name "Component: MouseArea" is not suitable.
qmlRegisterType<interfacageQML>("Interfacage", 1, 0, "InterfacageQML");
Another error is that you must pass a parent by default, in this case 0 or nullptr since the items may not have parents.
class interfacageQML : public QObject
{
Q_OBJECT
public:
explicit interfacageQML(QObject *parent = nullptr);
[...]
As I said in the first lines, this is a new type, it is not an object so you must create it.
import QtQuick 2.6
import QtQuick.Controls 2.0 as QQC2
import Interfacage 1.0
Rectangle {
width: 800
height: 800
color: "white"
InterfacageQML{
id: myitem
}
[...]
And in the end if you want to use it you must call the function through the item.
MouseArea {
anchors.fill: parent
onClicked: {
view.currentIndex = index
myitem.mouseClick()
}
}
Since you want to connect your QDialog with the QML through that class, you can not do it since they will be different objects, one solution for this is to use a singleton, for this you must do the following:
interfacageqml.h
#ifndef INTERFACAGEQML_H
#define INTERFACAGEQML_H
#include <QObject>
#include <QQmlEngine>
class interfacageQML : public QObject
{
Q_OBJECT
static interfacageQML* instance;
explicit interfacageQML(QObject *parent = nullptr);
public:
static interfacageQML *getInstance();
~interfacageQML();
Q_INVOKABLE void mouseClick();
signals:
void clicked();
};
#endif // INTERFACAGEQML_H
interfacageqml.cpp
#include "interfacageqml.h"
#include <QDebug>
interfacageQML* interfacageQML::instance = 0;
interfacageQML *interfacageQML::getInstance()
{
if (instance == 0)
instance = new interfacageQML;
return instance;
}
interfacageQML::interfacageQML(QObject *parent) : QObject(parent)
{
}
interfacageQML::~interfacageQML()
{
}
void interfacageQML::mouseClick()
{
qDebug() << "qmlinterface::mouseClick()";
emit clicked();
}
main.cpp
#include "interfacageqml.h"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
static QObject *singletonTypeProvider(QQmlEngine *, QJSEngine *)
{
return interfacageQML::getInstance();
}
int main(int argc, char *argv[])
{
qmlRegisterSingletonType<interfacageQML>("Interfacage", 1, 0, "InterfacageQML", singletonTypeProvider);
// test
interfacageQML *obj = qobject_cast<interfacageQML*>(interfacageQML::getInstance());
QObject::connect(obj, &interfacageQML::clicked,[]{
qDebug()<<"clicked";
});
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
As it is a singleton it is not necessary to create an item, you can do it directly:
import Interfacage 1.0
[...]
MouseArea {
anchors.fill: parent
onClicked: {
view.currentIndex = index
InterfacageQML.mouseClick()
}
}
This last example can be found in the following link.

Control Qt QML SwipeView with Signals/Slots

I'm very new to Qt, and right now struggling to figure out how to change the index of the SwipeView through a different C++ class. I'd like to make some kind of a signal that emits from the C++ class saying "Swipe Right" or something like that, and having the SwipeView respond. I'm also new to signals and slots, so I might be misunderstanding how they work.
When you want to control a QML element from C ++ the best strategy is to create a QObject that has Q_PROPERTY, slot and signals and export it to QML through setContextProperty(), and perform the direct binding or use Connections to connect the signals, in this case it is only necessary to emit a signal and connect it in QML.
In the following example I show how to change to the right or left using QPushButton:
main.cpp
#include <QApplication>
#include <QPushButton>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QHBoxLayout>
class SwipeManager: public QObject{
Q_OBJECT
public:
using QObject::QObject;
public slots:
void moveToRight(){
emit toRight();
}
void moveToLeft(){
emit toLeft();
}
signals:
void toRight();
void toLeft();
};
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc, argv);
SwipeManager manager;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("manager", &manager);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
QWidget w;
QPushButton left("to left");
QPushButton right("to right");
QHBoxLayout *lay = new QHBoxLayout(&w);
lay->addWidget(&left);
lay->addWidget(&right);
QObject::connect(&left, &QPushButton::clicked, &manager, &SwipeManager::moveToLeft);
QObject::connect(&right, &QPushButton::clicked, &manager, &SwipeManager::moveToRight);
w.show();
return app.exec();
}
#include "main.moc"
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.4
Window {
visible: true
width: 640
height: 480
title: qsTr("SwipeView Test")
SwipeView {
id: view
anchors.fill: parent
currentIndex: 4
Repeater {
model: 10
Loader {
active: SwipeView.isCurrentItem || SwipeView.isNextItem || SwipeView.isPreviousItem
sourceComponent: Item{
Text {
text: "Page: "+index
anchors.centerIn: parent
}
}
}
}
}
Connections{
target: manager
onToRight: if(view.currentIndex + 1 != view.count) view.currentIndex += 1
onToLeft: if(view.currentIndex != 0) view.currentIndex -= 1
}
PageIndicator {
id: indicator
count: view.count
currentIndex: view.currentIndex
anchors.bottom: view.bottom
anchors.horizontalCenter: parent.horizontalCenter
}
}
The complete example can be found in the following link.

Get user input from qml to c++

I'm trying to get user input from qml TextField to c++, but it only works if text property value is hardcoded (const value).
c++
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QObject *rootObject = engine.rootObjects().first();
QObject *serverField1 = rootObject->findChild<QObject*>("serverField1");
qDebug() << serverField1->property("text"); //Here I expect to get user input
Qml
ApplicationWindow {
id: applicationWindow
visible: true
width: 300
height: 550
TextField {
id: serverField1
objectName: "serverField1"
width: 200
height: 110
// text: "hardcoded value" //If text is const value, qDebug will get data from this property
}
}
You are asking for the text of the TextField when the window is displayed so what you will get is the text you have initially set, what you should do is get it every time it changes. I think that you have some class that will handle some processing with that data, that class will be called Backend, it must inherit from QObject so that it can have a qproperty and embed an object of that class to QML through setContextProperty, so every time it changes the text in QML will also change the text in the Backend class object.
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQmlProperty>
#include <QDebug>
class Backend: public QObject{
Q_OBJECT
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
public:
QString text() const{
return mText;
}
void setText(const QString &text){
if(text == mText)
return;
mText = text;
emit textChanged(mText);
}
signals:
void textChanged(const QString & text);
private:
QString mText;
};
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
Backend backend;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("backend", &backend);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
// test
QObject::connect(&backend, &Backend::textChanged, [](const QString & text){
qDebug() << text;
});
return app.exec();
}
#include "main.moc"
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.4
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
TextField {
id: serverField1
x: 15
y: 46
width: 120
height: 45
topPadding: 8
font.pointSize: 14
bottomPadding: 16
placeholderText: "Server Ip"
renderType: Text.QtRendering
onTextChanged: backend.text = text
}
}
You can find the complete code in the following link.

Changing QML object from C++ slot

I want to change object defined in QML from a slot in C++. In slot startButtonClicked() I start timer which every second calls slot getData(). How can I change the label defined in QML from C++ slot genData()? Now I am able to change in only from main.cpp
class LogicClass : public QObject
{
Q_OBJECT
public:
LogicClass();
~LogicClass();
public slots:
void startButtonClicked(const QVariant &v);
void getData();
};
main:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
class LogicClass logicClass;
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
QObject *rootObject = engine.rootObjects().first();
QObject *qmlObject = rootObject->findChild<QObject*>("startButton");
QObject::connect(qmlObject, SIGNAL(qmlSignal(QVariant)),&logicClass, SLOT(startButtonClicked(QVariant)));
return app.exec();
}
qml:
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Dialogs 1.2
ApplicationWindow {
id: window
objectName: "window"
visible: true
width: 640
height: 520
title: qsTr("MY app")
Button {
id: startButton
objectName: "startButton"
x: 25
text: qsTr("Start")
signal qmlSignal(var anObject)
MouseArea {
anchors.fill: parent
onClicked: startButton.qmlSignal(startButton)
}
}
Label {
objectName: "latitudeLabelValue"
id: latitudeLabelValue
y: 478
width: 50
text: qsTr("")
}
}
}
You have to use the setProperty method:
QObject *lblLatitute = rootObject->findChild<QObject*>("latitudeLabelValue");
lblLatitute->setProperty("text", "234.234");
But consider to use the model/view/delegate paradigm.
Passing a pointer to rootObject to LogicClass() can be a solution.
QObject *rootObject = engine.rootObjects().first();
class LogicClass logicClass(rootObject);
Save it as a aparameter of a class, and use it. this->rootObject->rootObject->findChild<QObject*>("latitudeLabelValue");
and then the setProperty() function.

How to access properties of another file from main.qml after they are modified by C++ code first

I’m having problems with QML and C++. If someone could take a look at this piece of code and tell me what I am doing wrong. I ‘m just trying to print “msg.author” on a window (main window) but everytime I try to access it from main.qml there’s an error saying msg is not defined. Thank you for your time.
main.qml
import QtQuick 2.2
import QtQuick.Window 2.1
Window {
property alias name: value
visible: true
id: main_window
width: 500; height: width
color:"black"
}
MyItem.qml
import QtQuick 2.0
import QtQuick.Window 2.0
Text {
id: text1
visible: true
width: 100; height: 100
text: msg.author // invokes Message::author() to get this value
color: "green"
font.pixelSize: 20
Component.onCompleted: {
msg.author = "Jonah" // invokes Message::setAuthor()
}
message.h
#ifndef MESSAGE
#define MESSAGE
#include <QObject>
#include <QString>
class Message : public QObject
{
Q_OBJECT
Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
public:
void setAuthor(const QString &a) {
if (a != m_author) {
m_author = a;
emit authorChanged();
}
}
QString author() const {
return m_author;
}
signals:
void authorChanged();
private:
QString m_author;
};
#endif // MESSAGE
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlEngine>
#include <QQmlContext>
#include <QQmlComponent>
#include <QDebug>
#include "message.h"
int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine_e;
engine_e.load(QUrl(QStringLiteral("qrc:/main.qml")));
QQmlEngine engine;
Message msg;
engine.rootContext()->setContextProperty("msg", &msg);
QQmlComponent component(&engine, QUrl::fromLocalFile("main.qml"));
if(component.status()!=component.Ready){
if(component.Error){
qDebug()<<"Error: "<<component.errorString();
}
}
return app.exec();
}
The code is not working, and the main has two engines. The second is not properly set-up and it should also be noted, from QQmlApplicationEngine documentation, that:
This class combines a QQmlEngine and QQmlComponent to provide a convenient way to load a single QML file. It also exposes some central application functionality to QML, which a C++/QML hybrid application would normally control from C++.
Hence, QQmlApplicationEngine can perfectly suit your needs and load the QML file. Now, it should also be noted that context properties should always be set before loading a QML file. The final correct code is the following:
int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine_e;
Message msg;
engine_e.rootContext()->setContextProperty("msg", &msg);
engine_e.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
EDIT
For what concerns QML files, they should be placed inside the resources (as I hope you have done). My test setting is the following:
main.qml:
import QtQuick 2.3
import QtQuick.Window 2.1
Window {
// property alias name: value
visible: true
id: main_window
width: 500; height: width
// color:"black"
MyItem {
anchors.centerIn: parent // <--- added to the main window to see the result
}
}
MyItem.qml
import QtQuick 2.0
Text {
id: text1
visible: true
width: 100; height: 100
text: msg.author // invokes Message::author() to get this value
color: "green"
font.pixelSize: 20
Component.onCompleted: {
msg.author = "Jonah" // invokes Message::setAuthor()
}
}