Load and control QML from C++

Ok folks, today we are talking about how to load custom QML file and integrate it into your main application? It is useful, for example, to create settings UI for different plugins, when each plugin requires its own UI

So, here is a code:

#ifndef QMLSETTINGSVIEW_H
#define QMLSETTINGSVIEW_H

#include <QObject>
#include <QQuickView>
#include <QQmlApplicationEngine>
#include <QQuickItem>

class QmlSettingsView: public QObject
{
    Q_OBJECT
public:
    QmlSettingsView(QObject *parent = 0);
    ~QmlSettingsView();
    QQmlApplicationEngine *engine() const;
    void setEngine(QQmlApplicationEngine *engine, const QString &qmlpath, const QString &finishedSignal);
    void showSettings();
    void hideSettings();
    bool setSettingsProperty(const char *name, const QVariant &value);
    QVariant settingsProperty(const char *name);

public slots:
    virtual void settingsFinished() = 0;

    QQuickWindow *parentWindow() const;

private:
    QQuickView *view_;
    QQmlApplicationEngine *engine_;
    QQuickItem *root_;
    QQuickWindow *parentWindow_;
};

#endif // QMLSETTINGSVIEW_H

 

#include "qmlsettingsview.h"
#include <QQmlContext>

QmlSettingsView::QmlSettingsView(QObject *parent):
    QObject(parent)
  , view_(0)
  , engine_(0)
  , root_(0)
  , parentWindow_(0){
}

QmlSettingsView::~QmlSettingsView() {
    if (view_)
        view_->deleteLater();
}

QQmlApplicationEngine *QmlSettingsView::engine() const {
    return engine_;
}

void QmlSettingsView::setEngine(QQmlApplicationEngine *engine, const QString &qmlpath, const QString &finishedSignal) {
    engine_ = engine;
    if (!engine_)
        return;

    if (view_) //already created
        return;

    engine_->clearComponentCache();
    if (engine_->rootObjects().count() <= 0)
        return;

    QObject *topLevel = engine_->rootObjects().value(0);
    parentWindow_ = qobject_cast<QQuickWindow *>(topLevel);

    if (!parentWindow_)
        return;

    view_ = new QQuickView(engine, parentWindow_);
    if (!view_)
        return;

    view_->setSource(QUrl(qmlpath));
    if (view_->status() != QQuickView::Ready) {
        return;
    }

    root_ = view_->rootObject();
    QString f_ = finishedSignal;
    f_ = f_.insert(0, "2");
    //settingsFinished() must be implemented in child class
    connect(root_, f_.toLatin1().data(), this, SLOT(settingsFinished()));
}

void QmlSettingsView::showSettings() {
    if (!view_)
        return;
    view_->show();
}

void QmlSettingsView::hideSettings() {
    if (!view_)
        return;
    view_->hide();
}

bool QmlSettingsView::setSettingsProperty(const char *name, const QVariant &value) {
    if (!root_)
        return false;

    return root_->setProperty(name, value);
}

QVariant QmlSettingsView::settingsProperty(const char *name) {
    if (!root_)
        return false;

    return root_->property(name);
}

QQuickWindow *QmlSettingsView::parentWindow() const {
    return parentWindow_;
}

The main purpose to create the code was to implement UI logic for separate plugins, that’s why its naming

Usage example:

#include "qmlsettingsview/qmlsettingsview.h"

class GrayScaleFilterPluginSettings: public QmlSettingsView {

    Q_OBJECT

public:
    GrayScaleFilterPluginSettings(QObject *parent = 0);
    virtual ~GrayScaleFilterPluginSettings();

    void settingsFinished();
signals:
    void finished();
};

define your new class somewhere in the main code:

GrayScaleFilterPluginSettings qmlsettingsview;

implementation

GrayScaleFilterPluginSettings::GrayScaleFilterPluginSettings(QObject *parent):
    QmlSettingsView(parent) {
}

GrayScaleFilterPluginSettings::~GrayScaleFilterPluginSettings() {
}

void GrayScaleFilterPluginSettings::settingsFinished() {
    emit finished();
}

connect finished signal to your main class slot, which will deal with the settings etc

connect(&qmlsettingsview, SIGNAL(finished()), this, SLOT(settingsClosed()));

here we show and hide our settings

void GrayScaleFilterPlugin::setupItem(QQmlApplicationEngine *engine) {
    if (!engine)
        return;

    qmlsettingsview.setEngine(engine, "qrc:/gssettings.qml", "finished()");
    qmlsettingsview.setSettingsProperty("width", qmlsettingsview.parentWindow()->width());
    qmlsettingsview.setSettingsProperty("height", qmlsettingsview.parentWindow()->width());
    qmlsettingsview.showSettings();
    return;
}

void GrayScaleFilterPlugin::settingsClosed() {
    qmlsettingsview.hideSettings();
}

setEngine() sets QML engine, path to your settings qml file and name of signal when the settings are gets closed.

You can read new properties in settingsClosed() slot and save them to file

 

That’s actually it at the moment

Feel free to ask a questions

 

Leave a Reply