QML Dynamic Component Destroy/Create with WebEngineView

In this topic I’ll show how to create and destroy components dynamically on QML with JavaScript for WebEngineView.

Sample project code can be accessed from my GitHub repository.

First of all I want to talk about Qt’ s WebEngine. It’s your in app web browser which uses The Chromium Project. Nowadays it is possible to notice web pages are popped out via apps in app screen (mostly mobile apps use this feature). Qt’s WebEngine provides this feature. It makes it possible to interact Qt based applications with webapps.

It is quite possible that user wants to close webapp, for that it can be hidden or closed. For WebEngineView we don’t have a close option, instead of it’s object can be destroyed to close page. If user destroys it’s web view then user may be in need of reopening that page. For reopening web view page it has to be recreated dynamically.

1st method is creating an object from a string. That method needs whole component of yours has to be typed as a string which I don’t find it useful for my example case.

2nd method is define your own seperated .qml file for your component. It has two tiny steps: create component and create object from your new component. In documentation example createSpriteObjects function creates dynamic component from .qml file and finishCreation creates object from component.

createObject function has 2 parameters; 1st parameter is it’s parent component, kind of okay I’m creating this instance, but where? parent parameter is answer for that. 2nd parameter is getting your components attributes. It also makes possible to assign read-only parameters be initiated in this object parameter (tested with url property of WebEngineView.

Enough of theory, let’s combine ingredients to fed our belly. at first webengine kit has to be included to our project.

QT += quick webengine

CONFIG += c++11

DEFINES += QT_DEPRECATED_WARNINGS

SOURCES += \
        main.cpp

RESOURCES += qml.qrc

qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

in main function QtWebEngine::initialize before QGuiApplication call.

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtWebEngine>

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QtWebEngine::initialize();
    QGuiApplication app(argc, argv);

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

createSpriteObjects and finishCreation functions are defined in main.qml file.

function createSpriteObjects() {
        component = Qt.createComponent("WebEngineview.qml");
        if (component.status === Component.Ready)
            finishCreation();
        else
            component.statusChanged.connect(finishCreation);
    }

    function finishCreation() {
        if (component.status === Component.Ready) {
            sprite = component.createObject(appWindow, {"x": 0, "y": 0, "url":webSiteUrl});
            if (sprite === null) {
                // Error Handling
                console.log("Error creating object");
            }
        } else if (component.status === Component.Error) {
            // Error Handling
            console.log("Error loading component:", component.errorString());
        }
    }

Destroy method is under main.qml file defined in destroyButton. It destroys sprite object instead of component, this is why I prefered 2nd method instead of 1st one to create dynamic components in Quick Applications.

Button {
        z: 1
        id: destroyButton
        anchors.left: parent.left
        anchors.top: parent.top
        width: 150
        text: "close window"
        visible: buttonToggle
        onClicked: {
            sprite.destroy()
            sprite = null
            buttonToggle = false
        }
    }

If object is destroyed then it only needs to call finishCreation method to open up web page in createButton.

Button {
        z: 1
        id: createButton
        anchors.left: parent.left
        anchors.top: parent.top
        width: 150
        text: "open window"
        visible: !buttonToggle
        onClicked: {
            finishCreation()
            buttonToggle = true
        }
    }

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *