Fail To Connect Qml signal to C++ Slot - c++

I have been trying to connect signal between Qml file and c++, but public slot in c++ doesn't seem to receive the signal.
What might be wrong with my program?
main.qml
Item{
id:item
signal qml_signal
Button{
onClicked: {
item.qml_signal();
}
}
}
main.cpp
QQuickView view(QUrl("qrc:/main.qml"));
QObject *item = view.rootObject();
Myclass myclass;
QObject::connect(item, SIGNAL(qml_signal()), &myclass,SLOT(cppSlot()));
myclass.h
void cppSlot() ;
myclass.cpp
void Myclass::cppSlot(){
qDebug() << "Called the C++ slot with message:";
}

When you want objects to interact between C++ and QML, you must do it on the QML side, since obtaining a QML object from C++ can cause you many problems, as in this case, the signal created in QML can not be handled in C++.
The solution is to export your object myclass to QML and make the connection there:
main.cpp
#include "myclass.h"
#include <QGuiApplication>
#include <QQuickView>
#include <QQmlContext>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQuickView view(QUrl("qrc:/main.qml"));
Myclass myclass;
view.rootContext()->setContextProperty("myclass", &myclass);
view.show();
return app.exec();
}
main.qml
import QtQuick 2.9
import QtQuick.Controls 1.4
Item{
id:item
signal qml_signal
Button{
onClicked: item.qml_signal()
}
onQml_signal: myclass.cppSlot()
}

Related

QML instantiates C++ objects. How do I access their methods?

Here's what my main.cpp looks like:
int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);
QCoreApplication::addLibraryPath("./");
QQuickView view;
view.setResizeMode(QQuickView::SizeRootObjectToView);
view.setSource(QUrl("qrc:/myqml.qml"));
view.show();
return app.exec();
}
As you can see, it creates things from myqml. Well, myqml instantiates a C++ class MyClass.
How do I access this C++ methods from the object QQuickView view? For example, I'd like to do something of the type view.MyClassInstance.myMethod1()
You want to obtain an object created in QML by C++, here it does not matter if the target has a prototype created in C ++ or not. If you want this you must obtain the object through findChild since all the objects created in QML have a kinship relationship with the window.
main.cpp
#include <QtQuick>
#include <QtGui>
class MyClass: public QObject
{
Q_OBJECT
public:
using QObject::QObject;
Q_INVOKABLE void invokable(){
qDebug()<< "invokable";
}
Q_SLOT void slot(){
qDebug()<< "slot";
}
void function(){
qDebug()<< "function";
}
};
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
qmlRegisterType<MyClass>("foo", 1, 0, "MyClass");
QGuiApplication app(argc, argv);
QQuickView view;
view.setResizeMode(QQuickView::SizeRootObjectToView);
view.setSource(QUrl(QStringLiteral("qrc:/main.qml")));
view.show();
if(MyClass* myclass_instance = view.findChild<MyClass *>("myclass_instance")){
myclass_instance->invokable();
myclass_instance->slot();
myclass_instance->function();
}
return app.exec();
}
#include "main.moc"
main.qml
import QtQuick 2.9
import foo 1.0
Rectangle {
color: "salmon"
width: 640
height: 480
MyClass{
objectName: "myclass_instance"
}
}
But this method has several drawbacks such as who manages the life cycle of the object is QML, not C ++ so the pointer could at some point point to an unreserved address. Another disadvantage is that there is a dependency of C++ to QML since if the objectName is changed in QML the code in C ++ would have to be changed.
Another approach is to create a helper class that is exported to QML with setContextProperty() and that interacts with the MyClass object.
main.cpp
#include <QtQuick>
#include <QtGui>
class MyClass: public QObject
{
Q_OBJECT
public:
using QObject::QObject;
Q_INVOKABLE void invokable(){
qDebug()<< "invokable";
}
Q_SLOT void slot(){
qDebug()<< "slot";
}
void function(){
qDebug()<< "function";
}
};
class Helper: public QObject
{
Q_OBJECT
public:
using QObject::QObject;
void call_function(){
emit called();
}
Q_SIGNAL void called();
};
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
qmlRegisterType<MyClass>("foo", 1, 0, "MyClass");
QGuiApplication app(argc, argv);
Helper helper;
QQuickView view;
view.rootContext()->setContextProperty("helper", &helper);
view.setResizeMode(QQuickView::SizeRootObjectToView);
view.setSource(QUrl(QStringLiteral("qrc:/main.qml")));
view.show();
helper.call_function();
return app.exec();
}
#include "main.moc"
main.qml
import QtQuick 2.9
import foo 1.0
Rectangle {
color: "salmon"
width: 640
height: 480
MyClass{
id: myclass
}
Connections{
target: helper
onCalled:{
myclass.invokable()
myclass.slot()
}
}
}
The advantage of this method is that there is no dependence between the logic of C++ and QML, besides the life cycle does not generate problems since the myclass objects are not handled directly in QML. The disadvantage is that you write a little more code and you can only call the Q_INVOKABLE or Q_SLOT.

How can I connect qml objects, signals with mechanics and slots in c++?

I want to write mechanics in c++ and objects, signals from qml, how can I connect my main.qml with main.cpp ?
The best option will be (if it is possible) declaration, adding something like directory which make qml and c++ as one database without everytime slot and signal reference between two files
From the documentation, here's one way to connect QML objects to C++:
// MyItem.qml
import QtQuick 2.0
Item {
id: item
width: 100; height: 100
signal qmlSignal(string msg)
MouseArea {
anchors.fill: parent
onClicked: item.qmlSignal("Hello from QML")
}
}
class MyClass : public QObject
{
Q_OBJECT
public slots:
void cppSlot(const QString &msg) {
qDebug() << "Called the C++ slot with message:" << msg;
}
};
int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
QQuickView view(QUrl::fromLocalFile("MyItem.qml"));
QObject *item = view.rootObject();
MyClass myClass;
QObject::connect(item, SIGNAL(qmlSignal(QString)),
&myClass, SLOT(cppSlot(QString)));
view.show();
return app.exec();
}

setMainQmlFile`, rootObject and showExpanded are not members of QQmlApplicationEngine

I've written this piece of code following some guys tutorial but I can't get it to run. The error says:
setMainQmlFile`, rootObject and showExpanded are not members of
QQmlApplicationEngine
What it's supposed to do is get a signal from QML and print out a message (in console). Basically I'm trying to integrate C++ and QML.
EDIT
I've tried to replace some of the functions with some others that seemed appropriate (at least to me). I've also tried to find what to include so that these functions would work but with no luck.
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "qtquickglobal.h"
#include <QQmlContext>
#include "myclass.h"
#include <QtCore>
#include <QtDebug>
#include <QQuickWindow>
int main(int argc, char *argv[]){
//Q_OBJECT;
QGuiApplication app(argc, argv);
QQmlApplicationEngine viewer;
viewer.load(QUrl(QStringLiteral("Qt/Versuch2/main.qml")));
myclass data;
viewer.rootContext() ->setContextProperty("myclassData", &data);
viewer.setMainQmlFile(QStringLiteral("qml/Versuch2/main.qml"));
QObject *viewerobject = viewer.rootObject();
QObject::connect(viewerobject, SIGNAL(qmlSignal(QString)), &data, SLOT(cppSlot(QString)));
viewer.showExpanded();
return app.exec();
}
void myclass::cppSlot(QString msg) {
qDebug() <<QString ("Called the cpp slot with message: %1").arg(msg);
}
Thank You.
I don't know where did you find you tutorial but, regarding Qt documentation, there is no such methods as setMainQmlFile nor showExpanded for QQmlApplicationEngine.
For setMainQmlFile(...), try to use instead setSource(...).
For showExpanded(), it's a QWidget function, and QQmlApplicationEngine do not inherit QWidget.
Regarding rootObject(), it might be a typo, you can use rootObjects() which return a QList<QObject*>.
Edit: Looks like you'll have to use the Qt Quick 2 Application wizard of Qt Creator in order to re-create that QtQuick2ApplicationViewer class used in the tutorial you found.
Using Qt 5.4.0 and Qt Creator 3.3.0, create New Project:
Click New Project
Qt Quick Application
Click Choose...
Name the project and select where to place it
Click Next
Select Qt Quick 2.4 from the drop-down menu
Click Next
Select desired Kit(s)
Click Next
Click Finish
Now You should see open main.qml file with following code:
import QtQuick 2.4
import QtQuick.Window 2.2
Window {
visible: true
MainForm {
anchors.fill: parent
mouseArea.onClicked: {
Qt.quit();
}
}
}
Make changes to the file so that it looks like the following:
import QtQuick 2.4
import QtQuick.Window 2.2
Window {
visible: true
//### New Code ###
signal myQmlSignal(string msg)
//################
MainForm {
anchors.fill: parent
mouseArea.onClicked: {
//### New Code ###
//Replace "Qt.quit();" with
console.log("Sending myQmlSignal from QML...");
myQmlSignal("Hello from QML")
//################
}
}
}
Add new class to Your project:
Right Mouse Click project name in Projects viewer
Click Add New...
Select C++ Class if not already selected
Click Choose...
In Class name filed enter "MyCppClass"
Set Base class to QObject
Click Next
Click Finish
Open mycppclass.h file, it should look like the following:
#ifndef MYCPPCLASS_H
#define MYCPPCLASS_H
#include <QObject>
class MyCppClass : public QObject
{
Q_OBJECT
public:
explicit MyCppClass(QObject *parent = 0);
~MyCppClass();
signals:
public slots:
};
#endif // MYCPPCLASS_H
Make changes to mycppclass.h so it looks like this:
#ifndef MYCPPCLASS_H
#define MYCPPCLASS_H
#include <QObject>
//### New Code ###
#include <QDebug>
//################
class MyCppClass : public QObject
{
Q_OBJECT
public:
explicit MyCppClass(QObject *parent = 0);
~MyCppClass();
signals:
public slots:
//### New Code ###
void myCppSlot(const QString &msg);
//################
};
#endif // MYCPPCLASS_H
Open mycppclass.cpp, which should look like this:
#include "mycppclass.h"
MyCppClass::MyCppClass(QObject *parent) : QObject(parent)
{
}
MyCppClass::~MyCppClass()
{
}
Change it to this:
#include "mycppclass.h"
MyCppClass::MyCppClass(QObject *parent) : QObject(parent)
{
}
MyCppClass::~MyCppClass()
{
}
void MyCppClass::myCppSlot(const QString &msg)
{
//### New Code ###
qDebug() << "Signal was received by C++. It contains follwoing message: " << msg;
//################
}
Open main.cpp, which looks like this:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
And make following changes:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
//### New Code ###
#include "mycppclass.h"
//################
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
//### New Code ###
MyCppClass myCppClass;
QObject::connect(engine.rootObjects().takeFirst(), SIGNAL(myQmlSignal(QString)), &myCppClass, SLOT(myCppSlot(QString)));
//################
return app.exec();
}
Click big green triangle to compile and run Your application. Watch Application Output area, click Hello World, You should see following message being printed out:
qml: Sending myQmlSignal from QML...
Signal was received by C++. It contains follwoing message: "Hello from QML"

QT Quick Application Window embed C++ object

I have a QT Quick 2.2 ApplicationWindow and I want to use inside this ApplicationWindow a C++ object.
I know QQuickView view, but this work only for objects which are derived from QQuickItem (not for ApplicationWindow).
I also know qmlRegisterType, but this adds only a general C++ class in QML. I want only to have one C++ object (instantiated in C++ code) in the ApplicationWindow.
Is there a possibility to use a C++ Object in a QT Quick 2.2 ApplicationWindow?
main.cpp
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQuickView>
#include <QQmlContext>
#include "myclass.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MyClass myClass;
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
return app.exec();
}
myclass.h
#include <QObject>
#include <QString>
class MyClass : public QObject
{
Q_OBJECT
public:
MyClass(QObject *parent = 0) {};
Q_INVOKABLE QString getValue();
};
myclass.cpp
#include "myclass.h"
#include <QString>
QString MyClass::getValue() {
return "42";
}
qrc:///main.qml
import QtQuick 2.2
import QtQuick.Controls 1.1
ApplicationWindow {
visible: true
Text {
text: myClass.getValue()
anchors.centerIn: parent
}
}
Thanks
It depends or your purpose. If you want to define QML type from C++, you should do this in your main.cpp for example :
qmlRegisterType< MyClass >("com.example.myclass", 1, 0, "MyClass");
now in your .qml file, first you need to import your newly created data type using import statement :
import com.example.myclass 1.0
and then you can create an item from your own data type:
import QtQuick 2.2
import QtQuick.Controls 1.1
import com.example.myclass 1.0
ApplicationWindow {
visible: true
Text {
text: myClass.getValue()
anchors.centerIn: parent
}
MyClass {
}
}
But you have another solution. You can pass a QObject object from c++ to QML.
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("myClass", new MyClass);
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
Now inside of your qml file you can access myClass object.

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.