Лучший способ динамически создавать элементы в QML на основе C ++ QMap - PullRequest
0 голосов
/ 20 июня 2020

Сначала это казалось простой задачей, но, честно говоря, я борюсь. У меня есть карта профилей, которую я загружаю при запуске, и профили можно добавлять и удалять во время выполнения. Эти профили должны быть указаны как RadioButtons или какой-либо другой элемент для выбора / редактирования в моем пользовательском интерфейсе.

Я пробовал использовать createQMLObject () во время цикла через геттер QVariantMap:

profile.h

#ifndef PROFILE_H
    #define PROFILE_H
    
    #include <QObject>
    
    class Profile final :  public QObject
    {
        Q_OBJECT
    public:
        Profile(QObject *parent = nullptr);
        ~Profile(){}
        Profile(QString profileName = "default", QObject *parent = nullptr);
        
        QString getProfileName() const;
        void setProfileName(const QString &value);
    private:
        QString profileName;
    };
    
    #endif // PROFILE_H

profile. cpp

#include "profile.h"

Profile::Profile(QString profileName, QObject *parent)
    : QObject(parent), profileName{ profileName }{}

QString Profile::getProfileName() const
{
    return profileName;
}

void Profile::setProfileName(const QString &value)
{
    profileName = value;
}

controller.h

#ifndef CONTROLLER_H
#define CONTROLLER_H

#include "profile.h"
#include <QObject>
#include <QQmlEngine>

class Controller : public QObject
{
    Q_OBJECT
public:
    Controller(QObject *parent = nullptr);
    ~Controller(){}

    static QObject* controllerSingletonProvider(QQmlEngine *engine, QJSEngine *scriptEngine);
    static Controller* instance();

    Q_INVOKABLE void addProfile(QString profileName);

    Q_INVOKABLE QVariantList getLoadedProfiles(){
        QVariantList rval;
        foreach (QString key, loadedProfiles.keys()) {
            rval.append(QVariant::fromValue<Profile*>(loadedProfiles[key].get()));
        }
        return rval;
    };

private:
    QMap<QString, std::shared_ptr<Profile>> loadedProfiles;

signals:

};

#endif // CONTROLLER_H

контроллер. cpp

#include "controller.h"

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

void Controller::addProfile(QString profileName)
{
    std::shared_ptr<Profile> profile{std::make_shared<Profile>(profileName)};
    loadedProfiles[profile->getProfileName()] = profile;
}

QObject* Controller::controllerSingletonProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
    Q_UNUSED(engine)
    Q_UNUSED(scriptEngine)

    return Controller::instance();
}

Controller* Controller::instance(){
    static Controller* controller = new Controller();
    return controller;
}

основной. cpp

    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <controller.h>
    
    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    
        QGuiApplication app(argc, argv);
        QQmlApplicationEngine engine;
    
        qmlRegisterSingletonType<Controller>("com.controller", 1, 0, "Controller", Controller::controllerSingletonProvider);
        Controller::instance()->addProfile("Profile1");
    
        const QUrl url(QStringLiteral("qrc:/main.qml"));
        QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                         &app, [url](QObject *obj, const QUrl &objUrl) {
            if (!obj && url == objUrl)
                QCoreApplication::exit(-1);
        }, Qt::QueuedConnection);
        engine.load(url);
    
        return app.exec();
    }

main.qml

import QtQuick 2.12
import QtQuick.Window 2.12
import com.controller 1.0

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    Column {
        id: radioButtonColumn

        Component.onCompleted: {
            for(var profile in Controller.getLoadedProfiles()){
                var obj = Qt.createQmlObject('import QtQuick 2.12; RadioButton { id: '+ profile.getProfileName() +';
                                     text: '+ profile.getProfileName() +'; }', radioButtonColumn, "dynamicSnippet1");
            }
        }
    }
}

Я пробовал использовать сигнал, отправленный с моего синглтона контроллера с помощью Connections, но параметр сигнала не приходит. Однако я могу вызывать свои функции Controllers c ++ из qml. Я опубликую только изменения в предыдущем примере кода:

изменения main.qml:

Connections {
    target: Controller
    onProfileAdded: {
        var obj = Qt.createQmlObject('import QtQuick 2.12; RadioButton { id: '+ profileName +';
                             text: '+ profileName +'; }', radioButtonColumn, "dynamicSnippet1");
        console.log("Value changed")
    }
}

изменения controller.h:

    signals:
        void profileAdded(QString &profileName);
    };

контроллер. cpp изменения:

void Controller::addProfile(QString profileName)
{
    std::shared_ptr<Profile> profile{std::make_shared<Profile>(profileName)};
    loadedProfiles[profile->getProfileName()] = profile;
    emit profileAdded(profileName);
}

основной. cpp изменения:

    engine.load(url);

    Controller::instance()->addProfile("Profile2");

    return app.exec();

Edit: Любопытно, что мне пришлось изменить свой onProfileAdded в controller.h на profileAdded, чтобы сигнал работал. Однако я все еще не получаю параметр profileName в моем qml.

Я хотел бы спросить, как лучше всего справиться с таким требованием. Может быть, есть другой, более простой способ?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...