3

I am developing a basic qml-cpp application to understand how one interacts with another. I have a MessageSetter C++ class and one main.qml. Since I wish to understand two-way communication, I exposed MessageSetter properties to qml using setContextProperty and also registered MessageSetter class with qml (instantiable registration). Exposed properties work fine. Now when a qml button is clicked, then the signal (qmlBtnClicked) is successfully caught in a MessageSetter slot(onQmlButtonClicked). This slot further emits another MessageSetter signal (colorChanged). This new (C++) signal should be caught in qml registered MessageSetter's signal handler (onColorChanged) but it does not arrive here in any case. Below is main.cpp code:

int main(int argc, char *argv[])
{
   QGuiApplication app(argc, argv);
   QQmlApplicationEngine engine;
   qmlRegisterType<MessageSetter>("com.SkillLotto.MessageSetter", 1, 0, "SetMessage");

   MessageSetter data;
   engine.rootContext()->setContextProperty("msgSetter", &data);
   QQmlComponent component(&engine, QUrl::fromLocalFile("main.qml"));

   QObject *object = component.create()->findChild<QObject*>("setTextBtn");
   QObject::connect(object, SIGNAL(qmlBtnClicked()), &data, SLOT(onQmlButtonClicked()));

   return app.exec();
}

This is MessageSetter slot that emits another signal:

void MessageSetter::onQmlButtonClicked()
{
   emit colorChanged("red");
}

This is qml code, this signal handler never gets called:

SetMessage{
    onColorChanged: {
        rect.color = color    //rect is some rectangle in this file.
    }
}

As I stated, qml signal is successfully caught in C++ slot but I am unable to catch this C++ signal in qml signal handler. Any help please.

This question, as I see, is focussed on qmlRegisterType() and should not be duplicate of this question? I also want to know whether qmlRegisterType() and setContextProperty() cant be used simultaneously or not ?

Community
  • 1
  • 1
Jatin
  • 276
  • 6
  • 12
  • This is the link I went through to do it this way. Please let me know if I did not get it right (Exposing Signals Section). http://doc.qt.io/qt-5/qtqml-cppintegration-exposecppattributes.html – Jatin Dec 02 '15 at 11:25
  • You're right. This question is not solved [here](http://stackoverflow.com/questions/8834147/c-signal-to-qml-slot-in-qt). I've just answered with an example. – Tarod Dec 02 '15 at 13:12

2 Answers2

2

Because you are using two different instances of your MessageSetter, one is data in main.cpp and other is new instance SetMessage. Use only one to connect both signals/slots.

You are expecting onColorChanged signal from SetMessage but the signal is coming from data (in main.cpp).

Why do you need instantiable if you want to create a context property?


Add this in your main.qml file

Connections {
  target: msgSetter
  onColorChanged: {
   console.log("received color changed signal"); 
  }
}
ramtheconqueror
  • 1,907
  • 1
  • 22
  • 35
  • I thank you for your post. Well, it's not any project, just a learning application. And i want to know how to catch C++ signal in QML signal handler. That is why doing it both ways: setContext and qmlRegisterType. – Jatin Dec 02 '15 at 11:54
  • i got that, you are emitting signal from one object and expecting from the other one. that is the problem. – ramtheconqueror Dec 02 '15 at 11:59
  • You are right! I appreciate your help. Do we have a way if I had to do it without Connections{} and using qmlRegisterType only ? – Jatin Dec 02 '15 at 12:08
  • you don't want to use `Connections`? any specific reason? it is more like `connect` from `QObject`. – ramtheconqueror Dec 02 '15 at 15:13
2

I think your code should work well.

I don't have the whole code so I don't know if you have the right methods implemented.

In order to get the signal using qmlRegisterType you need some requirements. Check if you have the Q_PROPERTY macro call implemented. Any property that is writable should have an associated NOTIFY signal that is emitted whenever the property value has changed.

If so, when you change the color property in the SetMessage component, the signal onColorChanged should be triggered.

Here you have an example where two signals are emitted: the first one when the size property is updated and the second one if the C++ mouseClick method is called using a MouseArea.

By the way, you don't need setContextProperty to integrate your C++ class with QML. qmlRegisterType should be enough. Or vice versa, depending on your needs. You can use both, but then you will have two different objects to work with. It really depends on what you want to achieve.

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "customitem.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    qmlRegisterType<CustomItem>("CustomItem", 1,0, "CustomItem");

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    return app.exec();
}

customitem.h

#ifndef CUSTOMITEM_H
#define CUSTOMITEM_H

#include <QObject>

class CustomItem: public QObject
{
    Q_OBJECT

    /*
     * Any property that is writable should have an associated NOTIFY signal.
     * Ref: http://doc.qt.io/qt-5/qtqml-cppintegration-exposecppattributes.html#exposing-properties
     */

    Q_PROPERTY(int size READ size WRITE setSize NOTIFY sizeChanged)

public:
    CustomItem(QObject *parent = 0);

    int size() const;
    void setSize(int);

    Q_INVOKABLE void mouseClick();

private:
    int m_size;

signals:
    void sizeChanged(int size);
    void clicked();

public slots:
};

#endif // CUSTOMITEM_H

customitem.cpp

#include "customitem.h"
#include <QDebug>

CustomItem::CustomItem(QObject *parent)
: QObject(parent), m_size(0)
{
}

int CustomItem::size() const
{
    return m_size;
}

void CustomItem::setSize(int size)
{
    m_size = size;
    emit sizeChanged(m_size);
}

void CustomItem::mouseClick()
{
    qDebug() << "CustomItem::mouseClick()";

    emit clicked();
}

main.qml

import QtQuick 2.5
import QtQuick.Window 2.2
import CustomItem 1.0

Window {
    visible: true

    TextInput  {
        id: mySize
        x: 0
        y: 0
        text: "100"
    }

    CustomItem {
        id: customItem
        size: mySize.text

        onSizeChanged: console.log("size changed:", size)
        onClicked: console.log("onClicked!")
    }

    Rectangle {
        id: rect
        x: 50
        y: 50
        width: customItem.size
        height: customItem.size
        color: "red"

        MouseArea {
            anchors.fill: parent
            onClicked: { customItem.mouseClick() }
        }
    }
}
Tarod
  • 6,732
  • 5
  • 44
  • 50
  • Well, as I wanted, your answer is focussed on qmlRegisterType and is useful. But one issue I face now; when I execute your code in Qt 5.4.2 it works fine. And it does not work in Qt 5.5.0. The problem is same as with my code. C++ slot gets called but not QML slot. Why does it behave so? I work on Qt 5.5.0. – Jatin Dec 03 '15 at 05:09
  • @Jatin Glad to know it was useful. Well, I work on Qt 5.5.0 too. The code was compiled in Windows 7 with that Qt version. I [uploaded](https://github.com/ftena/qml-c/tree/master/adding-types) the code to GitHub. You could download it and try again. Could you compile & run the application on a different computer? – Tarod Dec 03 '15 at 08:20
  • Thanks for the GitHub upload. I think you are right, my code should work. But the problem might be with Fedora 22, my OS. And I also tried your code on same OS but different computer. There, too, it had same problem 'QML slot not getting called'. I also face problems like 'qDebug' and 'console.log' not printing on this OS. Anyways, happy with your support.! – Jatin Dec 03 '15 at 16:42
  • I am sorry Tarod. It was my carelessness. Your code as well as my code work fine on the same machine! It was the id missing, that is, the code works only if I set id of 'SetMessage' to 'msgSetter'. If id is not set, as was the case previously, then it does not work. Therefore, I find one thing, if 'qmlRegisterType' and 'setContextProperty' are used simultaneously, then the id of registered type and name of context object should match (here 'msgSetter') as they both refer the same C++ object. I hope I am right in this conclusion. Thanks brother.! – Jatin Dec 04 '15 at 07:09
  • Wow, wonderful then. Thanks to you! :) A very good job, @Jatin. Please, mark the ramtheconqueror's answer or mine as the right one if you think the question was resolved ;) Happy coding! – Tarod Dec 04 '15 at 07:23
  • Yes, I will mark. If what I concluded in previous comment is right, then could you please verify yourself and then edit your post to include that statement? It will be kind of you and helpful to others as well :) – Jatin Dec 04 '15 at 07:30
  • I've been testing your idea, but I'm not sure about your conclusion. The problem is we can't know if using that unique `id` we're referring to the registered type or the single global class. In my opinion, if you set the `id` of `SetMessage` to `msgSetter`, and you use `msgSetter` in the QML code, you're using the registered type, not the class instantiated in `main.cpp`. Maybe I'm wrong. In any case, [here](http://stackoverflow.com/questions/32044270/qt-qml-qmlregistertype-vs-setcontextproperty-difference) you have more information. – Tarod Dec 04 '15 at 08:27