Reload a Loader on button click
-
Simplest way to reload a loader:
// In some JS function, like button click handler: loaderRecord.source = "" loaderRecord.source = "Record/Record.qml"
But I suspect you are asking about something else, right?
-
@sierdzio I haven't tested it, but shouldn't this be the same, as setting the active property to false, and to true again ?
Edit:
jup made a small test:ApplicationWindow { id:root visible:true width:500; height:500 Button { id:btn anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right height: 50 onClicked: { load.active = !load.active load.active = !load.active } } Loader{ id:load anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom anchors.top: btn.bottom source: "TestFile.qml" } } //TestFile.qml import QtQuick 2.0 Rectangle { color: "blue" Component.onCompleted: console.log("completed") Component.onDestruction: console.log("Destructed") }
works fine enough, completed and destructed is printed each time
-
Thanks for your response !
Both of your solution work, but it seems that the application keeps the initial qml file in memory. If I change my code live and reload it, my app will still load the file without the new modification.
-
@Dylan_Alt. that's because the component cache is not cleared.
But I don't know how to force this from QML side. Maybe forcing the garbage collector to run in-between ?
gc()
usually you would call
clearComponentCache
on yourQQmlApplicationEngine
inside you main.cpp for that.Or that's where I do it ;)
-
Note that the cached properties will be cleared if the source or sourceComponent is changed after calling this function but prior to setting the loader active.
That's from https://doc.qt.io/qt-5/qml-qtquick-loader.html. So the solution might be to get the right combination of
active: false
, then set source, then 'active: true`, but it sounds very hacky. What @J-Hilk proposed seems better. -
Would creating dynamic QML objects work better than using a Loader?
https://doc.qt.io/qt-5/qtqml-javascript-dynamicobjectcreation.html -
Hi,
sorry for the late response, I had a lot of other stuff to do.
I tried with gc() and the hacky way, and a combination of both, and the result is the same as before.
I haven't try fcarney's solution yet, I will do it when I will have a some time, and tell you if it worked.Thanks
-
Here's how I do it,
maybe it's of help for your case://QuickWidget.h #ifndef QUICKWINDOW_H #define QUICKWINDOW_H #include <QQuickWindow> #include <QIcon> class QuickWindow : public QQuickWindow { Q_OBJECT public: explicit QuickWindow(QQuickWindow *parent = nullptr) : QQuickWindow(parent) {} signals: Q_INVOKABLE void reloadQML(); }; #endif // QUICKWINDOW_H
//main.cpp #include <QApplication> #include <QQmlApplicationEngine> #include "quickwindow.h" void clearAndReload( QQmlApplicationEngine &engine){ for(QObject *obj : engine.rootObjects()){ engine.rootObjects().removeOne(obj); obj->deleteLater(); } engine.clearComponentCache(); engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); for(QObject *obj : engine.rootObjects()){ QuickWindow *window = qobject_cast<QuickWindow*>(obj); if(window)QObject::connect(window, &QuickWindow::reloadQML, &engine,[&engine]{clearAndReload(engine);}); } } int main(int argc, char *argv[]) { QApplication app(argc, argv); qmlRegisterType<QuickWindow>("QuickWindow", 1, 0, "QuickWindow"); QQmlApplicationEngine engine; engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); if (engine.rootObjects().isEmpty()) return -1; for(QObject *obj : engine.rootObjects()){ QuickWindow *window = qobject_cast<QuickWindow*>(obj); if(window) QObject::connect(window, &QuickWindow::reloadQML, &engine,[&engine]{clearAndReload(engine);}); } return app.exec(); }
//main.qml import QtQuick 2.12 import QtQuick.Controls 2.5 import QuickWindow 1.0 QuickWindow { id:root visible:true width:500; height:500 Component.onCompleted: console.log("Window created") Shortcut{ sequence: "F5" onActivated: { console.log("Reload") reloadQML() } } }
-
@J.Hilk I cannot use any C++ code :( I'm using QML in an external software, I can't link both C++ and QML.
I tried to create / delete my objects dynamically like that :
main.qml
[...] StackLayout { [...] Loader{ id: loaderTestReload source: "tab1.qml" } [...] }
tab1.qml is use to create my object :
Item { id: tab1 property var compo Component.onCompleted: init() function init() { var component = Qt.createComponent("tab1_qml.qml") component.createObject(tab1) compo = component } function reload() { compo.destroy() init() } }
and then I have the main component, with 2 buttons and a text :
Item { id: tab1qml property int value: 0 Button{ id: button1 text: tab1qml.value onClicked: value++ } Button{ y: button1.height text: "reload" onClicked: tab1.reload() } Text{ y: button1.height*2 text: "reset ?" } }
When I press the reload button, the component is reloaded, but as before, my app keeps the file in memory. If I change my text, it will remain as it was.
I will take a look at the example, it uses createQmlObject() instead of createComponent(). Maybe I can store the content of the file and send it via createQmlObject(). -
Indulge me for a bit, as I have not done this before. How do you start a QML application without a main- function?
-
@J.Hilk I don't how it's made behind. The software uses blocks to code (like Unreal Engine for example, but at a much higher level than simple instructions). One of these blocks is a QML Viewer, where I can set a main.qml file, up to 32 input property and as many outputs as I want. I don't have any control of what happens when the full application is started.
-
@Dylan_Alt.
alight,that's unfortunate, as it limits your options quite heavily.
-
I took my time to test several things and I found something that worked :
I used createQmlObject(), to load the content of the file, instead of createComponent(). Here is how it looks like :Tab1.qml :
Item { id: tab1 property var compo Component.onCompleted: init() function init() { var xhr = new XMLHttpRequest; var response xhr.open("GET", "tab1_qml.qml"); xhr.onreadystatechange = function() { if (xhr.readyState == XMLHttpRequest.DONE) { response = xhr.responseText; compo = Qt.createQmlObject(response, tab1,"tab1_qml.qml") } }; xhr.send(); // begin the request } function reload() { compo.destroy() init() } }
tab1_qml.qml :
Item { id: tab1qml property int value: 0 Button{ id: button1 text: tab1qml.value onClicked: value++ } Button{ y: button1.height text: "reload" onClicked: tab1.reload() } Text{ y: button1.height*2 text: "reset ?" } }
When I modify my tab1_qml.qml and clic the reload button, it works.
But bad news, it works well on my software, but it doesn't work on QtCreator. As far as I understand, it's because the file is loaded from the ressource, the solution may be to take an external qml file.
Anyway, my problem is solved.Thanks everyone who responsed.
-
@Dylan_Alt I realize this is a pretty old thread and this is a pretty hacky response, but it appears to work for both local and remote files.
onClicked { myLoader.source = "MyLocalOrRemoteFile.qml?"+Math.random() }