Dynamically loading and unloading 2 qml files from cpp

1.8k Views Asked by At

I have requirement to launch 2 different Qml files "mainwindow.qml" and "basic.qml" ,wheras both are independent of each other.

Initially i need to launch qml window based on flag bSettingMainWindow, based on this i will be launching any one of the qml file.

After launching I need to switch between these 2 qml files anytime. It should be like loading and unloading of "mainwindow.qml" and "basic.qml", based on the user action. Since because memory concern , i need load any of them at a time. and i dont want to play with visible true or false.

whereas from this below code I can able to load any one of the qml file based on the flag bSettingMainWindow. and also after loading i can able to switch to another qml file window.

Assume first I have launched in Mainwindow.qml then i clicked the mouse button over Rectangle of mainwindow.qml and moved to basic.qml and now if i click on the Rectangle of basic.qml it is not switching to mainwindow.qml. and vice versa. Only one time i can able to switch b/w these 2 . I want to switch multiple times. Below is the code , requesting to please provide your answers

//** windowLoader.hpp   **//
class WindowLoader : public QObject{
    Q_OBJECT
    QQmlApplicationEngine loadQMlEngine;

public:
    explicit WindowLoader(QObject * parent = 0);
    void loadWindow();


public slots:
    void loadMainWindow();
    void loadBasicWindow();
    void connectToMain(QObject *object = nullptr, const QUrl &url = QUrl(""));
    void connectToBasic(QObject *object = nullptr, const QUrl &url = QUrl(""));

    private:
};

//** windowLoader.cpp   **//
WindowLoader::WindowLoader(QObject *parent) : QObject(parent) {

}

void WindowLoader::loadWindow()  {
    if(bSettingMainWindow){ //this will be from a internal flag, this check is only one time during launch
        connect(&loadQMlEngine,SIGNAL(objectCreated(QObject *, const QUrl &)),this,SLOT(connectToBasic(QObject *, const QUrl &)),Qt::QueuedConnection);
        loadQMlEngine.rootContext()->setContextProperty( "interface", m_interface );
        loadQMlEngine.load(QUrl(QStringLiteral("qrc:/Qml/mainWindow.qml")));
    } else {
        connect(&loadQMlEngine,SIGNAL(objectCreated(QObject *, const QUrl &)),this,SLOT(connectToMain(QObject *, const QUrl &)),Qt::QueuedConnection);
        loadQMlEngine.rootContext()->setContextProperty( "interface", m_interface );
        loadQMlEngine.load(QUrl(QStringLiteral("qrc:/Qml/basic.qml")));     
    }
}

void WindowLoader::connectToBasic(QObject *object, const QUrl &url) {
    if(object){
        connect(object, SIGNAL(switchToBasicSignal()), this, SLOT(loadBasicWindow()));
    }
}

void WindowLoader::connectToMain(QObject *object, const QUrl &url) {
    if(object){
        connect(object, SIGNAL(switchToMainSignal()), this, SLOT(loadMainWindow()));
    }
}

void WindowLoader::loadBasicWindow() {
    loadQMlEngine.rootObjects()[0]->deleteLater();
    loadQMlEngine.destroyed();

    loadQMlEngine.rootContext()->setContextProperty( "interface", m_interface );
    loadQMlEngine.load(QUrl(QStringLiteral("qrc:/Qml/basic.qml")));
}

void WindowLoader::loadMainWindow() {
    loadQMlEngine.rootObjects()[0]->deleteLater();
    loadQMlEngine.destroyed();

    loadQMlEngine.rootContext()->setContextProperty( "interface", m_interface );
    loadQMlEngine.load(QUrl(QStringLiteral("qrc:/Qml/mainWindow.qml")));

}


//** main.cpp **//
int main( int argc, char *argv[] ) {
    QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QApplication app(argc, argv);
    WindowLoader root;
    root.loadWindow();
    return app.exec();
}



// ** mainWindow.qml **//
ApplicationWindow {
    visible: true
    width: 1200
    height: 800
    title: qsTr("MainWindow")

    signal switchToBasicSignal()

    Rectangle {
        anchors.fill: parent
        MouseArea{
            anchors.fill: parent
            onClicked: {
                switchToBasicSignal()
            }
        }
    }
}

//** basic.qml **//
ApplicationWindow {
    visible: true
    width: 640
    height: 480
    title: qsTr("basic")

    signal switchToMainSignal()

    Rectangle {
        anchors.fill: parent
        MouseArea{
            anchors.fill: parent
            onClicked: {
                switchToMainSignal()
            }
        }
    }
}
2

There are 2 best solutions below

0
Ramkumar R On

Loading and Unloading of the complete window will not give a good user experience. If you have memory concerns and don't want to use visibility property you can try with Loader concepts.

I have tried the below sample code it works. Based on the bool flag I tried to switch between the windows. Please check the sample attached below. I hope this helps to solve your issue.

/* main.cpp */
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>

#include "backend.h"

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

    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    Backend backend(&engine);

    engine.rootContext()->setContextProperty("backend", &backend);

    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}

/* backend.h */
#ifndef BACKEND_H
#define BACKEND_H

#include <QObject>

class QQmlApplicationEngine;
class Backend : public QObject
{
    Q_OBJECT
public:
    explicit Backend(QQmlApplicationEngine *engine, QObject *parent = nullptr);

    Q_INVOKABLE void changeWindow();

private:
    QQmlApplicationEngine *_engine = nullptr;
    bool _main = true;
};

#endif // BACKEND_H

/*backend.cpp*/
#include "backend.h"
#include <QQmlApplicationEngine>
#include <QQuickWindow>

Backend::Backend(QQmlApplicationEngine *engine, QObject *parent) : QObject(parent)
{
    _engine = engine;
}

void Backend::changeWindow()
{
    QObject *qObject = _engine->rootObjects().first();
    Q_ASSERT( qObject != NULL );

    QQuickWindow* mainWindow = qobject_cast<QQuickWindow*>(qObject);
    Q_ASSERT( mainWindow );

    _main = !_main;
    if (_main)
    {
        _engine->load(QUrl(QStringLiteral("qrc:/main.qml")));
    } else
    {
        _engine->load(QUrl(QStringLiteral("qrc:/samplewindow.qml")));
    }
    mainWindow->close();
    qObject->deleteLater();
}

/* main.qml */
import QtQuick 2.9
import QtQuick.Window 2.2

Window {
    visible: true
    width: 640
    height: 480

    MouseArea {
        anchors.fill: parent
        onClicked: backend.changeWindow()
    }

    Text {
        text: qsTr("main window")
        anchors.centerIn: parent
    }
}

/* samplewindow.qml */
import QtQuick 2.9
import QtQuick.Window 2.2

Window {
    visible: true
    width: 640
    height: 480

    MouseArea {
        anchors.fill: parent
        onClicked: backend.changeWindow()
    }

    Text {
        text: qsTr("sample window")
        anchors.centerIn: parent
    }
}
0
Rajeesh Raveendran On

Its so easy in QML.!!! You dont need to create your own window loader as QML has already Loader. consider this sample. Hope this will work for you.

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "backendcppclass.h"

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

    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    BackendCPPClass backendCPPClass; /// create instance of backend class.
    engine.rootContext()->setContextProperty("backend", &backendCPPClass); /// Set context propert so that we can access in QML.

    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.14
import QtQuick.Window 2.14

Window {
    visible: true
    width: 640
    height: 480
    Loader {
        id: windowLoader
        anchors.fill: parent
        source: backend.bSettingMainWindow ? "qrc:/mainwindow.qml" : "qrc:/basic.qml"
    }
}

qml.qrc

<RCC>
    <qresource prefix="/">
        <file>main.qml</file>
        <file>basic.qml</file>
        <file>mainwindow.qml</file>
    </qresource>
</RCC>

backendcppclass.h

#ifndef BACKENDCPPCLASS_H
#define BACKENDCPPCLASS_H

#include <QObject>

class BackendCPPClass : public QObject
{
    Q_OBJECT
public:
    explicit BackendCPPClass(QObject *parent = nullptr) {};
    Q_PROPERTY(MEMBER bSettingMainWindow NOTIFY bSettingMainWindowChanged)

signals:
    void bSettingMainWindowChanged();

private:
    bool bSettingMainWindow = false;

};

#endif // BACKENDCPPCLASS_H

backendcppclass.cpp

#include "backendcppclass.h"

BackendCPPClass::BackendCPPClass(QObject *parent) : QObject(parent)
{
    /// update any logic to change the variable bSettingMainWindow.
    /// QML will automatically validate it.
    bSettingMainWindow = true;
    emit bSettingMainWindowChanged();
}

And Basic.qml and mainwindow.qml can be ur custom specific qmls.