c++ - 如何将带有自定义对象的容器从 C++ 传递到 QML?

标签 c++ qt qml qt5

我对 C++ 有所了解,但对 QML 还是个新手。
我想将容器中的多个自定义 C++ 对象传递给 QML,但我在这样做时遇到了问题。
所提供的代码仅限于基础。

我可以在使用 setContextProperty 注册的单个对象中与 QML 进行通信,这工作正常。但是,如果我尝试使用 QHash 这样做,则会出现错误:

‘QVariant::QVariant(void*)’ is private within this context‘

也许你可以帮我解决问题或给我一个方向? 非常感谢。

更新:
谢谢 derM,这是我的尝试:

我在头文件末尾添加了:Q_DECLARE_METATYPE(MyData);。我已将容器更改为 QVariantMap。
如果我尝试:QVariant qvtest1(test1); 我收到错误:

no matching function for call to ‘QVariant::QVariant(MyData&)’

然而这是可行的:

QVariant qvtest1, qvtest2;  
qvtest1.setValue(test1);  
qvtest2.setValue(test2);  

但是我再次遇到错误:setContextProperty("mymap", &mymap); 错误:

calling a private constructor of class 'QVariant'

代码相应调整。

更新 2
谢谢 eyllanesc,您的方法奏效了!
但是,我现在遇到了 QML 中的相关问题。 看来我无法从 QML 访问所有 QMap 函数。

例如:

var test_data = mymap["three"]  // works fine  
var test_data2 = mymap.find("two").value()  // results in: Property 'find' of object [object Object] is not a function

同样的问题:

var tmp1 = mydata_qml_object // object was created before  
mymap["four"] = tmp1 // works fine  
mymap.insert("four", tmp1) // Property 'insert' of object [object Object] is not a function  

我正在使用 Qt 5.11.1
这是一个错误还是我遗漏了什么?

C++代码
我的数据.hpp:

#ifndef MYDATA_HPP  
#define MYDATA_HPP

#include <QObject>
#include <QString>

class MyData : public QObject
{
    Q_OBJECT

    Q_PROPERTY(QString name READ get_name WRITE set_name NOTIFY nameChanged)

  public:
    explicit MyData(QObject *parent = nullptr);
    MyData(QString name);
    MyData(const MyData &other);
    MyData(MyData &&other) = delete;
    MyData &operator=(const MyData &other);
    MyData operator=(MyData &&other) = delete;
    ~MyData() override = default;

  signals:
    void nameChanged();

  public slots:
    void set_name(const QString &name);
    QString get_name();

  private:
    QString _name;
};

Q_DECLARE_METATYPE(MyData);

#endif // MYDATA_HPP

我的数据.cpp:

#include "mydata.hpp"

MyData::MyData(QObject *parent)
    : QObject(parent)
{
}

MyData::MyData(QString name)
    : _name(name)
{
}

MyData::MyData(const MyData &other)
{
    _name = other._name;
}

MyData &MyData::operator=(const MyData &other)
{
    if (this != &other)
    {
        _name = other._name;
        return *this;
    }
}

void MyData::set_name(const QString &name)
{
    _name = name;
}
QString MyData::get_name()
{
    return _name;
}

主要.cpp:

#include <mydata.hpp>

#include <QGuiApplication>
#include <QMap>
#include <QQmlApplicationEngine>
#include <QQmlComponent>
#include <QQmlContext>
#include <QQuickView>

#include <iostream>

int main(int argc, char *argv[])
{
    MyData test1("Hi");
    MyData test2("Hello");
    QMap<QString, QVariant> mymap; // QVariantMap

    QVariant qvtest1(test1); // error: no matching function for call to ‘QVariant::QVariant(MyData&)’

    //working:
    QVariant qvtest1, qvtest2;
    qvtest1.setValue(test1);
    qvtest2.setValue(test2);


    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);
    auto *engine = new QQmlEngine;
    QQuickView view;
    QQmlContext *ctxt = view.rootContext();

    // this is working:
    qmlRegisterType<MyData>("MyData", 1, 0, "MyData");
    engine->rootContext()->setContextProperty("test1", &test1);


    // this produces an error: calling a private constructor of class 'QVariant'
    engine->rootContext()->setContextProperty("mymap", &mymap);

    QQmlComponent component(engine, QUrl("qrc:/main.qml"));
    QQmlEngine::setObjectOwnership(engine, QQmlEngine::CppOwnership);
    QObject *object = component.create();

    return app.exec();
}

最佳答案

根据 the docs ,继承自 QObject 的类不能有复制构造函数或赋值运算符:

No Copy Constructor or Assignment Operator

QObject has neither a copy constructor nor an assignment operator. This is by design. Actually, they are declared, but in a private section with the macro Q_DISABLE_COPY(). In fact, all Qt classes derived from QObject (direct or indirect) use this macro to declare their copy constructor and assignment operator to be private. The reasoning is found in the discussion on Identity vs Value on the Qt Object Model page.

The main consequence is that you should use pointers to QObject (or to your QObject subclass) where you might otherwise be tempted to use your QObject subclass as a value. For example, without a copy constructor, you can't use a subclass of QObject as the value to be stored in one of the container classes. You must store pointers.

另一方面,如果你想让一个继承自 QObject 的类支持 QMetaType 以将其用作 QVariant,你必须将它传递给指针,因为如前所述,QObject 没有复制构造函数,但是指针是可复制的。

//mydata.h
#ifndef MYDATA_H
#define MYDATA_H

#include <QObject>
class MyData : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString name READ get_name WRITE set_name NOTIFY nameChanged)
public:
    explicit MyData(QObject *parent = nullptr);
    MyData(const QString & name);
    ~MyData() override = default;
signals:
    void nameChanged();
public slots:
    void set_name(const QString &name);
    QString get_name();
private:
    QString _name;
};
Q_DECLARE_METATYPE(MyData*)
#endif // MYDATA_H

//mydata.cpp

#include "mydata.h"

MyData::MyData(QObject *parent) : QObject(parent)
{}
MyData::MyData(const QString & name)
    : _name(name){}
void MyData::set_name(const QString &name)
{
    if(_name == name) return;
    _name = name;
    emit nameChanged();
}
QString MyData::get_name()
{return _name;}

你指出 setValue 有效,你买了它有效吗?除了这个方法用于不支持 QVariant 的类型所以他们可能会接受任何类型的数据,必须做的是传递指针:

MyData test1("Hi");
MyData test2("Hello");
QMap<QString, QVariant> mymap;
QVariant qvtest1 = QVariant::fromValue(&test1); 
QVariant qvtest_1, qvtest_2;
qvtest_1.setValue(&test1);
qvtest_2.setValue(&test2);

另一方面是 setContextProperty,它接受 QVariant 或指向 QObject 的指针,在第一种情况下你传递 QObject,正确的做法是传递指针,在第二种情况下你传递 QVariant,所以没有问题,因为它可以被复制。

engine->rootContext()->setContextProperty("test1", &test1);
engine->rootContext()->setContextProperty("mymap", mymap);

总而言之,您必须将可复制的对象传递给 setContextProperty。


qmlRegisterType 只记录一个类型可以通过信号传输,但这并不能保证它有效,这是一个必要条件但还不够,因为它不可复制,所以你必须使用MyData*.

关于c++ - 如何将带有自定义对象的容器从 C++ 传递到 QML?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52988921/

相关文章:

Qt、GCC、SSE 和堆栈对齐

qt - 将整个文件读取到 QString

c++ - QCompleter 在 NetworkRequest 完成后无法正常工作

qt - QML不显示SVG图片

c++ - 尝试在 C++ 中实现固定大小队列时出现段错误

c++ - 为 CUDA 应用程序在自定义类 int2_、float2_ 和 double2_ 之间重载 operator=

c++ - std::string::assign() 导致段错误

c++ - 如何告诉 qml 绑定(bind)附加依赖项?

c++ - 无法加载 QML 插件 : cannot protect module as it was never registered

c++ - 将大括号括起来的初始值设定项列表转换为类型时出错的含义?