I have main.qml
and dynamic.qml
files that i want to load dynamic.qml
on main.qml
using Loader {}
.
Content of dynamic.qml
file is dynamic and another program may change its content and overwrite it.
So i wrote some C++ code for detecting changes on file and fires Signal.
My problem is that I don't know how can i force Loader to reload file.
This is my current work:
MainController {
id: mainController
onInstallationHelpChanged: {
helpLoader.source = "";
helpLoader.source = "../dynamic.qml";
}
}
Loader {
id: helpLoader
anchors.fill: parent
anchors.margins: 60
source: "../dynamic.qml"
}
I think that QML Engine caches dynamic.qml
file. So whenever I want to reload Loader, it shows old content. Any suggestion?
You need to call trimComponentCache()
on QQmlEngine after you have set the Loaders source
property to an empty string. In other words:
helpLoader.source = "";
// call trimComponentCache() here!!!
helpLoader.source = "../dynamic.qml";
In order to do that, you'll need to expose some C++ object to QML which has a reference to your QQmlEngine (lots of examples in Qt and on StackOverflow to help with that).
trimComponentCache tells QML to forget about all the components it's not current using and does just what you want.
Update - explaining in a bit more detail:
For example, somewhere you define a class that takes a pointer to your QQmlEngine and exposes the trimComponentCache method:
class ComponentCacheManager : public QObject {
Q_OBJECT
public:
ComponentCacheManager(QQmlEngine *engine) : engine(engine) { }
Q_INVOKABLE void trim() { engine->trimComponentCache(); }
private:
QQmlEngine *engine;
};
Then when you create your QQuickView, bind one of the above as a context property:
QQuickView *view = new QQuickView(...);
...
view->rootContext()->setContextProperty(QStringLiteral("componentCache", new ComponentCacheManager(view->engine());
Then in your QML you can do something like:
helpLoader.source = "";
componentCache.trim();
helpLoader.source = "../dynamic.qml";
I was hoping for a pure QML solution. I noticed that loader.source
is a url (file:///
) and remembered how with HTML, you can avoid HTTP caching using ?t=Date.now()
in your requests. Tried adding ?t=1234
to the end of loader.source
, and sure enough, it works.
import QtQuick 2.0
Item {
Loader {
id: loader
anchors.fill: parent
property string filename: "User.qml"
source: filename
function reload() {
source = filename + "?t=" + Date.now()
}
}
Timer {
id: reloadTimer
interval: 2000
repeat: true
running: true
onTriggered: {
loader.reload();
}
}
}
I also wrote another example that will check for changes in the file contents before triggering a reload using an XMLHttpRequest.
import QtQuick 2.0
Item {
Loader {
id: loader
anchors.fill: parent
property string filename: "AppletUser.qml"
property string fileContents: ""
source: ""
function reload() {
source = filename + "?t=" + Date.now()
}
function checkForChange() {
var req = new XMLHttpRequest();
req.onreadystatechange = function() {
if (req.readyState === 4) {
if (loader.fileContents != req.responseText) {
loader.fileContents = req.responseText;
loader.reload();
}
}
}
req.open("GET", loader.filename, true);
req.send();
}
onLoaded: {
console.log(source)
}
Timer {
id: reloadTimer
interval: 2000
repeat: true
running: true
onTriggered: loader.checkForChange()
}
Component.onCompleted: {
loader.checkForChange()
}
}
}