[Solved] C++ Signal --> using Connections --> QML Slot



  • Hello guys,

    it seems that I am too stupid to understand how to connect a c++ signal to a qml slot.
    (The other way seems to be more easy for me ... qml signal --> c++ slot)

    I just tried to build a counter. I want to click on my gui (at the moment it doesnt matter where I click) and want to count how many times I already clicked on the screen.

    My first exmaple doesn't really count. It should send the value "3" to the main.qml file after the main.qml sent a signal "count()" to my count.cpp file.

    Problem
    If I want to run the project, I'll get the following error message:
    file:// ... /main.qml:10:5: QML Connections: Cannot assign to non-existent property "onClickValueChaged"

    First question
    If I am using the Connections component, do I need to "QObject::connect()" the (c++) signal and the (qml) slot?
    The documentation ("QT5.0 QtQuick2 Connections":http://qt-project.org/doc/qt-5.0/qtqml/qml-qtquick2-connections.html) doesn't use it.
    //I just tried in in line 17 of the cpp file, doesn't solved my problem, but maybe I used connect() in a wrong way.

    Second question
    When do I need to set the "target:" of the Connections object?
    If I understood it correctly, I can have a Button{ id: button } and use the onClick-signal of the Button somewhere else for example changing a text. Text{ id: myText text: "hello" Connections { target: button onClicked: { change the text to "blabla" }}
    Is this correct?

    @
    //counter.h
    #ifndef COUNTER_H
    #define COUNTER_H

    #include <QObject>

    class Counter : public QObject
    {
    Q_OBJECT
    public:
    explicit Counter(QObject *parent = 0);

    signals:
    void clickValueChanged(int newValue);

    public slots:
    void count();
    };

    #endif // COUNTER_H
    @

    @
    //counter.cpp
    #include "counter.h"
    #include <QDebug>
    #include <QObject>
    #include <QQuickView>
    #include <QQuickItem>

    Counter::Counter(QObject *parent) :
    QObject(parent)
    {

    QQuickView view(QUrl::fromLocalFile&#40;"main.qml"&#41;&#41;;
    QObject *item = view.rootObject(&#41;;
    Counter counter;
    
    connect(item, SIGNAL(count(&#41;), &counter, SLOT(count()));
    //connect(&counter, SIGNAL(clickValueChanged(int)), item, SLOT(clickValueChaged(int)));
    

    }

    void Counter::count() {
    qDebug() << "count()";
    clickValueChanged(3);
    }@

    @
    //main.qml
    import QtQuick 2.0

    Rectangle {
    id: root
    width: 360
    height: 360

    signal count()
    
    Connections {
        target: area
        onClickValueChanged:
            console.log("clickValueChaged: " + newValue);
    }
    
    Text {
        text: qsTr("Hello World")
        anchors.centerIn: parent
    }
    
    MouseArea {
        id: area
        anchors.fill: parent
        onClicked: {
            console.log("count()")
            count()
        }
    }
    

    }
    @

    @
    //main.cpp
    #include <QtGui/QGuiApplication>
    #include "qtquick2applicationviewer.h"

    int main(int argc, char *argv[])
    {
    QGuiApplication app(argc, argv);

    QtQuick2ApplicationViewer viewer;
    viewer.setMainQmlFile(QStringLiteral("qml/Test53/main.qml"&#41;&#41;;
    viewer.showExpanded(&#41;;
    
    return app.exec(&#41;;
    

    }
    @

    Thanks in advance for your help.
    I know I am a newbie :P



  • you need to have an objects of your Counter in QML first, then it all works easily like described in the doc :D

    Without an object how would you connect the signal to a slot? Also you don't need any connect calls in c++, because the slot will be in QML and the QML engine does the connect for you in the background.

    How I would do it, register the QObject to be available in QML, in you main.cpp add (after QGuiApplication or before I don't know if that matters, but before you create the QML view obviously):
    @qmlRegisterType<Counter>("Counter", 1, 0, "Counter");@

    that's all you have to do to make the QObject with all public slots and signals available to QML, so remove the code from your Counter constructor that is not necessary).

    then you can create an object in QML simply like this:
    @
    import Counter 1.0
    ..
    Counter {
    // connect the signal
    onClickValueChanged: console.log("clickValueChaged: " + newValue)
    }
    @

    you can use the Connections with your Counter object if you want, but as mentioned in the doc that is usually not necessary, unless you want to connect the slot somewhere else and not in the object itself.

    Also you don't need a real connection from QMl to C++ slots like you did, in most cases it is easier if you give your object in QMl and id (eg "id: counter") and then call it like a function from your MouseArea of whatever:
    @counter.count()@

    then remove the dependency in your C++ from QMl completely! otherwise it would be very hard to have more then one Counter object in QML or anywhere else without very complicated code in c++ :p



  • I didn't went through your code but here is how to work with connection from C++ to QML and vice versa.

    Signal in C++ and Slot in QML

    C++ :-

    @class ABC : public QObject
    {
    Q_OBJECT
    .
    .
    .
    signals:
    void countChanged(int value);

    };

    // Somewhere in the code
    ABC* abc_instance = new ABC(...)

    // Get the qml context from the viewer. After the following statement
    // your class's instance shall be available in QML as AbcInstance
    qmlContext->setContextProperty("abcInstance", abc_instance);@

    QML : -

    @YourComponent {
    .
    .
    .
    Connections {
    target: abcInstance
    onCountChanged : {
    console.log(value)
    }

      }
    

    .
    .
    }
    @

    Slot in C++ and Signal in QML

    C++ :-

    @class ABC : public QObject
    {
    Q_OBJECT
    .
    public:
    Q_INVOKABLE void setQmlItem(QQuickItem* item)
    {
    if(item) {
    _item = item;
    connect(_item,SIGNAL(itemClicked()),this,SLOT(onItemClcked()));
    }
    }
    .
    private slots:
    void onItemClcked();

    private:
    QQuickItem* _item;
    };

    // Somewhere in the code
    ABC* abc_instance = new ABC(...)

    // Get the qml context from the viewer. After the following statement
    // your class's instance shall be available in QML as AbcInstance
    qmlContext->setContextProperty("abcInstance", abc_instance);@

    QML : -

    @YourComponent {
    id : itemId
    .
    signal itemClicked
    .
    MouseArea {
    onClicked{
    itemClicked
    }
    }

    Componenet.onCompleted()
    {
         abcInstance.setQmlItem(itemID)
    }
    

    .
    .
    }
    @



    • Thank you very much for these informations.
      It helped me a lot!
      I just successfully used Xander's code in my program.

Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.