QML Loader не показывает изменения в файле.qml

Я имею main.qml а также dynamic.qml файлы, которые я хочу загрузить dynamic.qml на main.qml с помощью Loader {},
Содержание dynamic.qml Файл является динамическим, и другая программа может изменить его содержимое и перезаписать его. Поэтому я написал код на C++ для обнаружения изменений в файле и запуска сигнала.
Моя проблема в том, что я не знаю, как заставить загрузчик перезагрузить файл.

Это моя текущая работа:

MainController {
    id: mainController
    onInstallationHelpChanged: {
        helpLoader.source = "";
        helpLoader.source = "../dynamic.qml";
    }
}

Loader {
    id: helpLoader

    anchors.fill: parent
    anchors.margins: 60
    source: "../dynamic.qml"
}



Я думаю, что QML Engine кеширует dynamic.qml файл. Поэтому всякий раз, когда я хочу перезагрузить Loader, он показывает старый контент. Любое предложение?

2 ответа

Решение

Вам нужно позвонить trimComponentCache() на QQmlEngine после того, как вы установили Loaders source свойство пустой строки. Другими словами:

helpLoader.source = "";
// call trimComponentCache() here!!!
helpLoader.source = "../dynamic.qml";

Чтобы сделать это, вам нужно представить некоторый объект C++ для QML, который имеет ссылку на ваш QQmlEngine (множество примеров в Qt и в Stackru помогут в этом).

trimComponentCache говорит QML забыть обо всех компонентах, которые не используются в данный момент, и выполняет только то, что вы хотите.

Обновление - объясняя немного подробнее:

Например, где-то вы определяете класс, который берет указатель на ваш QQmlEngine и предоставляет метод trimComponentCache:

class ComponentCacheManager : public QObject {
    Q_OBJECT
public:
    ComponentCacheManager(QQmlEngine *engine) : engine(engine) { }

    Q_INVOKABLE void trim() { engine->trimComponentCache(); }

private:
    QQmlEngine *engine;
};

Затем, когда вы создаете свой QQuickView, свяжите одно из вышеперечисленного как свойство контекста:

QQuickView *view = new QQuickView(...);
...
view->rootContext()->setContextProperty(QStringLiteral("componentCache", new ComponentCacheManager(view->engine());

Тогда в вашем QML вы можете сделать что-то вроде:

helpLoader.source = "";
componentCache.trim();
helpLoader.source = "../dynamic.qml";

Я надеялся на чистое решение QML. Я заметил, что loader.source это URL (file:///) и вспомнил, как с помощью HTML вы можете избежать HTTP-кэширования, используя ?t=Date.now() в ваших запросах. Пробовал добавлять ?t=1234 до конца loader.sourceи, конечно же, это работает.

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

Я также написал другой пример, который проверит изменения содержимого файла, прежде чем запускать перезагрузку с использованием 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()
        }
    }

}
Другие вопросы по тегам