Как передать контейнер с пользовательскими объектами из C ++ в QML? - PullRequest
0 голосов
/ 25 октября 2018

Я кое-что знаю о C ++, но я довольно плохо знаком с QML.
Я хочу передать несколько пользовательских объектов C ++ в контейнере в QML, но у меня возникают проблемы при этом.
Предоставленный код суженк основам.

Я могу общаться с QML и с ним в одном объекте, который я регистрирую в setContextProperty, это прекрасно работает.Но если я пытаюсь сделать это с помощью 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.Кажется, я не могу получить доступ ко всем функциям QMap из QML.

Например:

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

1 Ответ

0 голосов
/ 25 октября 2018

Согласно документам классы, которые наследуются от QObject, не могут иметь конструктор копирования или оператор присваивания:

Нет конструктора копирования или оператора присваивания

QObject не имеет ни конструктора копирования, ни оператора присваивания.Это по замыслу.На самом деле они объявлены, но в закрытом разделе с макросом Q_DISABLE_COPY ().Фактически, все классы Qt, производные от QObject (прямые или косвенные), используют этот макрос, чтобы объявить их конструктор копирования и оператор присваивания закрытыми.Обоснование можно найти в обсуждении Identity vs Value на странице объектной модели Qt.

Основным следствием является то, что вы должны использовать указатели на QObject (или на ваш подкласс QObject), где у вас может возникнуть искушение использоватьВаш подкласс QObject в качестве значения.Например, без конструктора копирования вы не можете использовать подкласс QObject в качестве значения, которое будет храниться в одном из классов контейнера.Вы должны хранить указатели.

С другой стороны, если вы хотите, чтобы класс, унаследованный от QObject, поддерживал QMetaType, чтобы использовать его как QVariant, вы должны передать его указателю, потому что, как было отмечено,QObjects не имеют конструктора копирования, но указатели копируются.

//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*.

...