45

I'm trying to make a little program with Qt. I have a main.cpp with the following code:

#include <QtGui/QApplication>
#include "qmlapplicationviewer.h"

Q_DECL_EXPORT int main(int argc, char *argv[])
{
    QScopedPointer<QApplication> app(createApplication(argc, argv));

    QmlApplicationViewer viewer;
    viewer.setOrientation(QmlApplicationViewer::ScreenOrientationAuto);
    viewer.setMainQmlFile(QLatin1String("qml/tw_looptijden_berekenen/main.qml"));
    viewer.showExpanded();

    return app->exec();
}

int reken_tijden_uit(){
    return true;
}

and I have a .qml file:

import QtQuick 1.1

Rectangle {

width: 360
height: 360
Text {
    text: qsTr("Hello World")
    anchors.centerIn: parent
}
MouseArea {
    anchors.fill: parent
    onClicked: {
        Qt.quit();
    }
}
}

Now, when I click on the MouseArea, the program quits. What I want is that it calls the function reken_tijden_uit in the main.cpp file.

I've googled a lot, and searched on this site to. I've found a couple of answers, but I didn't get one working.

So what code do I put where so I can call the function reken_tijden_uit in C++?

Thanks in advance.


The header file looks like this:

#ifndef EIGEN_FUNCTION_HEADER_H
#define EIGEN_FUNCTION_HEADER_H

class MyObject : public QObject{
   Q_OBJECT
public:
    explicit MyObject (QObject* parent = 0) : QObject(parent) {}
    Q_INVOKABLE int reken_tijden_uit(){
    return 1;
    }
};

#endif // EIGEN_FUNCTION_HEADER_H

main.cpp:

#include <QtGui/QApplication>
#include "qmlapplicationviewer.h"
#include "eigen_function_header.h"

QScopedPointer<QApplication> app(createApplication(argc, argv));

qmlRegisterType<MyObject>("com.myself", 1, 0, "MyObject");

Q_DECL_EXPORT int main(int argc, char *argv[])
{
    QScopedPointer<QApplication> app(createApplication(argc, argv));

    QmlApplicationViewer viewer;
    viewer.setOrientation(QmlApplicationViewer::ScreenOrientationAuto);
    viewer.setMainQmlFile(QLatin1String("qml/tw_looptijden_berekenen/main.qml"));
    viewer.showExpanded();

    return app->exec();
}

and the QML file:

import QtQuick 1.1
import com.myself 1.0

Rectangle {
    width: 360
    height: 360
    Text {
        text: qsTr("Hello World")
        anchors.centerIn: parent
    }
    MyObject {
        id: myobject
    }
    MouseArea {
        anchors.fill: parent
        onClicked: {
            myobject.reken_tijden_uit()
        }
    }
}

And the errors are as follow:

D:\*\main.cpp:6: error: 'argc' was not declared in this scope
D:\*\main.cpp:6: error: 'argv' was not declared in this scope
D:\*\main.cpp:8: error: expected constructor, destructor, or type conversion before '<' token

So what did I do wrong?

NG_
  • 6,895
  • 7
  • 45
  • 67
Mathlight
  • 6,436
  • 17
  • 62
  • 107
  • please check the following thread this might help you http://stackoverflow.com/questions/5709820/communication-between-c-and-qml – shofee Feb 29 '12 at 14:06
  • hy, i checked it, but it's not precisly what i want... i want to add a onclick event to the mousearea the calls the cpp function. (in javascript is it just reken_tijden_uit(); so how does it works in c++ and qml... – Mathlight Feb 29 '12 at 14:16

2 Answers2

65

For any C++ code to be called from QML, it must reside inside a QObject.

What you need to do is create a QObject descended class with your function, register it to QML, instantiate it in your QML and call the function. Note also that you have to mark your function with Q_INVOKABLE.

Code:

#ifndef EIGEN_FUNCTION_HEADER_H
#define EIGEN_FUNCTION_HEADER_H

#include <QObject>

class MyObject : public QObject{
   Q_OBJECT
public:
    explicit MyObject (QObject* parent = 0) : QObject(parent) {}
    Q_INVOKABLE int reken_tijden_uit(){
    return 1;
    }
};

#endif // EIGEN_FUNCTION_HEADER_H

main.cpp:

#include <QtGui/QApplication>
#include <QtDeclarative>

#include "qmlapplicationviewer.h"
#include "eigen_function_header.h"

Q_DECL_EXPORT int main(int argc, char *argv[])
{
    QScopedPointer<QApplication> app(createApplication(argc, argv));
    qmlRegisterType<MyObject>("com.myself", 1, 0, "MyObject");

    QmlApplicationViewer viewer;
    viewer.setOrientation(QmlApplicationViewer::ScreenOrientationAuto);
    viewer.setMainQmlFile(QLatin1String("qml/tw_looptijden_berekenen/main.qml"));
    viewer.showExpanded();

    return app->exec();
}

QML:

import QtQuick 1.1
import com.myself 1.0

Rectangle {

    width: 360
    height: 360
    Text {
        text: qsTr("Hello World")
        anchors.centerIn: parent
    }
    MyObject {
       id: myobject
    }

    MouseArea {
        anchors.fill: parent
        onClicked: {
            console.log(myobject.reken_tijden_uit())
        }
    }
}
Nejat
  • 31,784
  • 12
  • 106
  • 138
Chris Browet
  • 4,156
  • 20
  • 17
  • Best, in a separate .cpp/.h added to your project. Do not forget to include the .h in main.cpp before doing the qmlRegisterType – Chris Browet Mar 01 '12 at 09:41
  • alright, i've got a couple of errors, i will post a new answer so it's clear y what the problem is... – Mathlight Mar 01 '12 at 10:37
  • 1) You have twice "QScopedPointer...". Remove the first one 2) Move "qmlRegisterType..." just after the second "QScopedPointer...", i.e. inside main{} 3) do a "console.log(myobject.reken_tijden_uit())" to check that it works (will appear in console in qt creator) – Chris Browet Mar 01 '12 at 10:52
  • In the QML? I.e. onClicked: { console.debug(myobject.reken_tijden_uit()) } – Chris Browet Mar 01 '12 at 12:19
  • alrght, i got still errors, and put the console.log on the onClicked in the qml. the errors are as follow: D:\*\main.cpp:-1: In function 'int qMain(int, char**)': AND D:\*\main.cpp:14: error: 'qmlRegisterType' was not declared in this scope AND D:\*\main.cpp:14: error: expected primary-expression before '>' token – Mathlight Mar 01 '12 at 12:54
  • and 3 warnings.... 1); D:\*\main.cpp:14: warning: left-hand operand of comma has no effect 2); D:\*\main.cpp:14: warning: right-hand operand of comma has no effect 3);D:\*\main.cpp:14: warning: right-hand operand of comma has no effect – Mathlight Mar 01 '12 at 12:55
  • alright, i think it's working now, because i've got now 15 errors (A) the first one is: D:\qt\tw_looptijden_berekenen\tw_looptijden_berekenen-build-desktop-Qt_4_8_0_for_Desktop_-_MinGW__Qt_SDK__Debug\debug\moc_eigen_function_header.cpp:12: error: #error "The header file 'eigen_function_header.h' doesn't include ." – Mathlight Mar 01 '12 at 13:40
  • Indeed. Edited for #include – Chris Browet Mar 01 '12 at 13:48
  • yeeey :P srr, i thought i had it but didn't ;P it works now. Thanks for the help ;) – Mathlight Mar 01 '12 at 13:51
  • If you instantiate it in QML, then how can you give this instance access to other objects you have in C++? For example, if an invokable function requires pointers to other objects in your C++ environment, you'd have to give the instance some way of accessing them. How? – johnbakers Dec 13 '13 at 15:08
  • @hellofunk why is function `main` declared as `Q_DECL_EXPORT`? – KernelPanic Aug 11 '15 at 09:09
  • @MarkoFrelih see this: http://stackoverflow.com/questions/13911387/q-decl-export-keyword-meaning i can't remember the reasoning in this particular case, but i don't use Qt anymore and this was a couple years ago – johnbakers Aug 11 '15 at 11:28
  • Some important caveat: the function marker with Q_INVOKABLE has to be **public** (or at least, not private, haven't checked for protected). – Vincent Fourmond Aug 21 '17 at 12:47
15

As an alternative to qmlRegisterType() in main.cpp, you can also use context properties to make QObject variables available in QML. (In case you don't require to create different instances of your object with later QML).

Q_DECL_EXPORT int main(int argc, char *argv[])
{
    QScopedPointer<QApplication> app(createApplication(argc, argv));

    QmlApplicationViewer viewer;
    viewer.setOrientation(QmlApplicationViewer::ScreenOrientationAuto);
    viewer.setMainQmlFile(QLatin1String("qml/tw_looptijden_berekenen/main.qml"));
    viewer.showExpanded();

    // add single instance of your object to the QML context as a property
    // the object will be available in QML with name "myObject"
    MyObject* myObject = new MyObject(); 
    viewer.engine()->rootContext()->setContextProperty("myObject", myObject); 

    return app->exec();
}

In QML, you can then access the object from anywhere in your code with the given name specified in main.cpp. No additional declarations required:

MouseArea {
    anchors.fill: parent
    onClicked: {
        myObject.reken_tijden_uit()
    }
}

You can find more information on QML<->C++ communication possibilities here: https://v-play.net/cross-platform-development/how-to-expose-a-qt-cpp-class-with-signals-and-slots-to-qml

GDevT
  • 241
  • 3
  • 4
  • 1
    There is a [flowchart in Qt docs] (https://doc.qt.io/qt-5/qtqml-cppintegration-overview.html#choosing-the-correct-integration-method-between-c-and-qml) that helps to decide when better to use _qmlRegisterTypes_ approach and when _setContextProperty_ – Mikolasan Jun 27 '19 at 17:27