簡體   English   中英

如何將帶有自定義對象的容器從C ++傳遞到QML?

[英]How to pass container with custom objects from C++ to QML?

我對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 ++代碼
mydata.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

mydata.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;
}

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

根據docs ,從QObject繼承的類不能具有復制構造函數或賦值運算符:

沒有復制構造函數或賦值運算符

QObject既沒有復制構造函數,也沒有賦值運算符。 這是設計使然。 實際上,它們是通過宏Q_DISABLE_COPY()的私有部分聲明的。 實際上,所有從QObject派生的Qt類(直接或間接)都使用此宏將其復制構造函數和賦值運算符聲明為私有。 可以在“ Qt對象模型”頁面上有關“身份與價值”的討論中找到其理由。

主要結果是您應該使用指向QObject(或指向QObject子類)的指針,否則可能會引誘您將QObject子類用作值。 例如,沒有復制構造函數,就不能使用QObject的子類作為要存儲在容器類之一中的值。 您必須存儲指針。

另一方面,如果您希望從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*

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM