Как поймать сигнал С++ в обработчике сигналов QML после регистрации типа?

Я разрабатываю базовое приложение qml-cpp, чтобы понять, как одно взаимодействует с другим. У меня есть класс MessageSetter C++ и один файл main.qml. Поскольку я хочу понять двустороннюю связь, я представил свойства MessageSetter для qml, используя setContextProperty, а также зарегистрировал класс MessageSetter с qml (мгновенная регистрация). Открытые свойства работают нормально. Теперь при нажатии кнопки qml сигнал (qmlBtnClicked) успешно перехватывается в слоте MessageSetter (onQmlButtonClicked). Этот слот далее испускает другой сигнал MessageSetter (colorChanged). Этот новый (C++) сигнал должен быть перехвачен в зарегистрированном в qml обработчике сигналов MessageSetter (onColorChanged), но в любом случае он не поступает сюда. Ниже приведен код main.cpp:

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();
}

Это слот MessageSetter, который испускает другой сигнал:

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

Это код qml, этот обработчик сигналов никогда не вызывается:

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

Как я уже сказал, сигнал qml успешно перехватывается в слоте C++, но я не могу перехватить этот сигнал C++ в обработчике сигналов qml. Любая помощь, пожалуйста.

Этот вопрос, как я вижу, сосредоточен на qmlRegisterType() и не должен дублировать этот вопрос? Я также хочу знать, нельзя ли одновременно использовать qmlRegisterType() и setContextProperty()?


person Jatin    schedule 02.12.2015    source источник
comment
Это ссылка, по которой я прошел, чтобы сделать это таким образом. Пожалуйста, дайте мне знать, если я не понял это правильно (Раздел «Разоблачение сигналов»). doc.qt.io/qt-5/qtqml-cppintegration-exposecppattributes. html   -  person Jatin    schedule 02.12.2015
comment
Ты прав. Этот вопрос не решен здесь. Я только что ответил примером.   -  person Tarod    schedule 02.12.2015


Ответы (2)


Я думаю, что ваш код должен работать хорошо.

У меня нет всего кода, поэтому я не знаю, реализованы ли у вас правильные методы.

Чтобы получить сигнал с помощью qmlRegisterType, вам потребуются некоторые требования. Проверьте, реализован ли у вас вызов макроса Q_PROPERTY. Любое свойство, доступное для записи, должно иметь связанный с ним сигнал NOTIFY, который выдается всякий раз, когда значение свойства изменяется.

Если да, то при изменении свойства color в компоненте SetMessage должен срабатывать сигнал onColorChanged.

Здесь у вас есть пример, где испускаются два сигнала: первый, когда обновляется свойство size, и второй, если метод C++ mouseClick вызывается с использованием MouseArea.

Кстати, вам не нужно setContextProperty для интеграции вашего класса C++ с QML. qmlRegisterType должно быть достаточно. Или наоборот, в зависимости от ваших потребностей. Вы можете использовать оба, но тогда у вас будет два разных объекта для работы. Это действительно зависит от того, чего вы хотите достичь.

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() }
        }
    }
}
person Tarod    schedule 02.12.2015
comment
Что ж, как я и хотел, ваш ответ сосредоточен на qmlRegisterType и полезен. Но сейчас я сталкиваюсь с одной проблемой; когда я выполняю ваш код в Qt 5.4.2, он работает нормально. И это не работает в Qt 5.5.0. Проблема такая же, как с моим кодом. Вызывается слот C++, но не слот QML. Почему так себя ведет? Я работаю на Qt 5.5.0. - person Jatin; 03.12.2015
comment
@Jatin Рад узнать, что это было полезно. Ну, я тоже работаю на Qt 5.5.0. Код был скомпилирован в Windows 7 с этой версией Qt. Я загрузил код на GitHub. Вы можете скачать его и попробовать еще раз. Не могли бы вы скомпилировать и запустить приложение на другом компьютере? - person Tarod; 03.12.2015
comment
Спасибо за загрузку на GitHub. Я думаю, вы правы, мой код должен работать. Но проблема может быть в Fedora 22, моей ОС. И я также попробовал ваш код на той же ОС, но на другом компьютере. Там тоже была такая же проблема: «Слот QML не вызывается». Я также сталкиваюсь с такими проблемами, как «qDebug» и «console.log», которые не печатаются в этой ОС. В любом случае, рад вашей поддержке.! - person Jatin; 03.12.2015
comment
Мне жаль Тарода. Это была моя невнимательность. Ваш код и мой код отлично работают на одной машине! Это был отсутствующий идентификатор, то есть код работает, только если я установил идентификатор «SetMessage» на «msgSetter». Если id не задан, как было раньше, то не работает. Поэтому я нахожу одну вещь: если «qmlRegisterType» и «setContextProperty» используются одновременно, то идентификатор зарегистрированного типа и имя объекта контекста должны совпадать (здесь «msgSetter»), поскольку они оба ссылаются на один и тот же объект C++. Надеюсь, я прав в этом выводе. Спасибо брат.! - person Jatin; 04.12.2015
comment
Вау, замечательно тогда. Благодаря вам! :) Очень хорошая работа, @Jatin. Пожалуйста, отметьте ответ ramtheconqueror или мой как правильный, если вы считаете, что вопрос решен ;) Удачного кодирования! - person Tarod; 04.12.2015
comment
Да, отмечу. Если то, что я сделал в предыдущем комментарии, верно, то не могли бы вы проверить себя, а затем отредактировать свой пост, включив в него это утверждение? Это будет любезно с вашей стороны и полезно для других :) - person Jatin; 04.12.2015
comment
Я проверял вашу идею, но я не уверен в вашем выводе. Проблема в том, что мы не можем знать, ссылаемся ли мы, используя этот уникальный id, на зарегистрированный тип или на единственный глобальный класс. На мой взгляд, если вы устанавливаете id из SetMessage на msgSetter и используете msgSetter в коде QML, вы используете зарегистрированный тип, а не класс, созданный в main.cpp. Может быть, я ошибаюсь. В любом случае здесь у вас есть дополнительная информация. - person Tarod; 04.12.2015

Поскольку вы используете два разных экземпляра вашего MessageSetter, один — data в main.cpp, а другой — новый экземпляр SetMessage. Используйте только один для подключения обоих сигналов/слотов.

Вы ожидаете сигнал onColorChanged от SetMessage, но сигнал поступает от data (в main.cpp).

Зачем вам нужен экземпляр, если вы хотите создать свойство контекста?


Добавьте это в свой файл main.qml

Connections {
  target: msgSetter
  onColorChanged: {
   console.log("received color changed signal"); 
  }
}
person ramtheconqueror    schedule 02.12.2015
comment
Я благодарю вас за ваш пост. Ну, это не какой-то проект, а просто обучающее приложение. И я хочу знать, как поймать сигнал С++ в обработчике сигналов QML. Поэтому делаем это двумя способами: setContext и qmlRegisterType. - person Jatin; 02.12.2015
comment
Я понял, вы излучаете сигнал от одного объекта и ожидаете от другого. это проблема. - person ramtheconqueror; 02.12.2015
comment
Ты прав! Я ценю вашу помощь. Есть ли у нас способ, если бы мне пришлось делать это без Connections {} и используя только qmlRegisterType? - person Jatin; 02.12.2015
comment
вы не хотите использовать Connections? какая-то конкретная причина? это больше похоже на connect из QObject. - person ramtheconqueror; 02.12.2015