将 Q_GADGET 作为信号参数从 C++ 传递到 QML

2022-01-19 00:00:00 qt qtquick2 qml c++ qgadget

无法在 QML 代码中获取 C++ 对象的属性.对象作为参数传递给信号.

Can't get a property of a C++ object inside a QML code. Object is passed as a parameter to the signal.

期望在QML中,可以提取Record对象的text属性.并且值应该是abc.QML 将对象视为 QVariant(Record),并将其属性 text 视为 undefined.

Expected that in QML, the property text of the Record object can be extracted. And the value should be abc. QML sees the object as QVariant(Record), and its property text as undefined.

Record是类似QPoint的值类型,所以它使用Q_GADGET声明.

Record is a value-type like QPoint, so it uses Q_GADGET declaration.

hpp:

#ifndef LISTENP_HPP_
#define LISTENP_HPP_

#include <QObject>

#include "Record.hpp"

class ListenP: public QObject
{
Q_OBJECT

public:
    ListenP();
    virtual ~ListenP();

    void emitGotRecord();

signals:
    void gotRecord(Record r);
};

#endif /* LISTENP_HPP_ */

cpp:

#include "ListenP.hpp"

ListenP::ListenP() :
        QObject()
{
}

ListenP::~ListenP()
{
}

void ListenP::emitGotRecord()
{
    emit gotRecord(Record("abc"));
}

用于记录的hpp:

#ifndef RECORD_HPP_
#define RECORD_HPP_

#include <QObject>
#include <QMetaType>

class Record
{
Q_GADGET

Q_PROPERTY(QString text READ text WRITE setText)

public:
    Record(const QString& text = "");
    ~Record();

    QString text() const
    {
        return m_text;
    }

    void setText(const QString& text)
    {
        m_text = text;
    }

private:
    QString m_text;
};

Q_DECLARE_METATYPE(Record)

#endif /* RECORD_HPP_ */

用于记录的 cpp:

#include "Record.hpp"

Record::Record(const QString& text) :
        m_text(text)
{
}

Record::~Record()
{
}

namespace
{
const int RecordMetaTypeId = qMetaTypeId<Record>();
}

QML 片断:

Connections {
    target: listenPModel
    onGotRecord: {
        console.log(r)
        console.log(r.text)
    }
}

主要部分:

QGuiApplication app(argc, argv);

auto listenP = std::make_shared<ListenP>();
QQuickView view;
view.rootContext()->setContextProperty("listenPModel", &*listenP);
view.setSource(QStringLiteral("src/qml/main.qml"));
view.show();

QtConcurrent::run([=]
{
    QThread::sleep(3);
    listenP->emitGotRecord();
});

return app.exec();

日志显示:

qml: QVariant(Record)
qml: undefined

推荐答案

发行说明Qt 5.5 对新功能的说明:

The release notes for Qt 5.5 says for the new features:

  • Qt 核心
    • 您现在可以在 Q_GADGET 中包含 Q_PROPERTY 和 Q_INVOKABLE,并且可以使用 QMetaType 系统查询此类小工具的 QMetaObject
    • Qt Core
      • You can now have Q_PROPERTY and Q_INVOKABLE within a Q_GADGET, and there is a way to query the QMetaObject of such gadget using the QMetaType system

    确实,使用 Qt 5.4 编译和运行您的示例会得到与您的结果相同的结果 而 使用 Qt 5.5 我正确识别了 Record,即我得到的结果是:

    Indeed, compiling and running your example with Qt 5.4 gives the same result as yours whereas with Qt 5.5 I got Record correctly recognised, i.e. I got as a result:

    qml: Record(abc)
    qml: abc
    

    另外,如 Q_DECLARE_METATYPE 文档,传递给宏的类型 - 在这种情况下为 Record,应该提供 (1) 公共默认构造函数,(2) 公共复制构造函数和 (3) 公共析构函数.由于 Record 是一个非常简单的类,所以不需要提供复制构造函数,默认的就足够了.

    Also, as stated in the Q_DECLARE_METATYPE documentation, the type passed to the macro - Record in this case, should provide (1) a public default constructor, (2) a public copy constructor and (3) a public destructor. Since Record is a very simple class, there's no need to provide a copy constructor as the default one is sufficient.

相关文章