Правильная перезагрузка QQmlApplicationEngine
У меня есть приложение на основе QML, которое загружает main.qml
файл из файловой системы примерно так:
myEngine->load("main.qml");
Это прекрасно работает, но я бы хотел "перезагрузить" движок, если файл main.qml был заменен более новой версией.
То, что я пытался до сих пор, называлось load()
снова, предполагая, что движок автоматически перезагрузится, как в других классах Qt.
К сожалению, это не случай. Если я вызову метод еще раз, появится другое окно с содержимым обновленного файла qml, а исходное окно останется открытым и продолжит отображать старый файл qml.
Чтобы это исправить я попытался позвонить load(QUrl())
, с последующим clearComponentCache()
и последний вызов загрузки для нового файла. Это приводит к тому же эффекту.
Любые идеи, как я могу "правильно" перезагрузить движок QML во время работы приложения?
3 ответа
Я бы попробовал хранить myEngine
в качестве указателя на кучу и удаления его после вызова quit (). Затем вы можете восстановить его, чтобы получить новую версию файла QML.
Если вы не хотите делать это по какой-либо причине (например, из-за того, что хотите сохранить окно или что-то в этом роде), вы можете попробовать использовать Loader
и загрузка файла QML таким образом. Ваш main.qml
будет выглядеть примерно так:
import QtQuick 2.0
Loader {
source: "changing.qml"
}
Всякий раз, когда вы обнаружите, что changing.qml
изменилось, просто переключите активное свойство Loader
запустить перезагрузку файла.
Просто видел это, но если вы все еще пытаетесь понять это - это трехэтапный процесс, и у вас есть кое-что из этого.
Вы ДОЛЖНЫ закрыть окно, созданное
QQmlApplicationEngine
первый. В моем случае я вытащил первый корневой объект сQQmlApplicationEngine
и бросить егоQQuickWindow
затем позвонитеclose()
,Теперь вы можете позвонить
clearComponentCache
наQQmlApplicationEngine
,
Это то, что делает мой код закрытия окна (обратите внимание, что я дал моему главному окну objectName
)
QObject* pRootObject = in_pQmlApplicationEngine->rootObjects().first();
Q_ASSERT( pRootObject != NULL );
Q_ASSERT( pRootObject->objectName() == "mainWindow" );
QQuickWindow* pMainWindow = qobject_cast<QQuickWindow*>(pRootObject);
Q_ASSERT( pMainWindow );
pMainWindow->close();
Третий шаг, конечно, загрузить ваш QML.
Позже я перешел к созданию QQuickView
окно вместо QQmlApplicationEngine
чтобы я мог просто позвонить clearComponentCache
а потом setSource
(Мне не понравилось, что пользователь видит, как окно пользовательского интерфейса исчезает, а затем снова появляется.)
Использование файлового наблюдателя:
main.py
DEBUG = True
class EntryPoint(qtc.QObject):
if DEBUG:
qmlFileChanged = qtc.Signal()
def __init__(self, parent=None):
super().__init__(parent)
self.qml_engine = qqml.QQmlApplicationEngine()
self.qml_entry = str(PATHS.playground.resolve())
self.qml_engine.load(self.qml_entry)
if DEBUG:
qml_files = []
for file in glob.iglob('**/*.qml', root_dir=PATHS.QML, recursive=True):
qml_files.append(str((PATHS.QML / file).resolve()))
self.file_watcher = QFileSystemWatcher(self)
self.file_watcher.addPaths(qml_files)
self.file_watcher.fileChanged.connect(self.on_qml_file_changed)
if DEBUG:
@slot
def on_qml_file_changed(self) -> None: # pragma: no cover
self.qml_engine.clearComponentCache()
window: QQuickItem = self.qml_engine.rootObjects()[0]
loader: QQuickItem = window.findChild(QQuickItem, 'debug_loader')
qtc.QEventLoop().processEvents(qtc.QEventLoop.ProcessEventsFlag.AllEvents, 1000)
prev = loader.property("source")
loader.setProperty('source', "")
loader.setProperty('source', prev)
площадка.qml
import QtQuick
import QtQuick.Controls.Material
Window {
id: root
width: 1200
height: 900
visible: true
flags: Qt.WindowCloseButtonHint | Qt.WindowMinimizeButtonHint | Qt.CustomizeWindowHint | Qt.WindowTitleHint
Material.theme: Material.Dark
Material.accent: Material.Cyan
Pane {
anchors.fill: parent
objectName: "_rootRect"
Loader{id: loader
objectName: "debug_loader"
anchors.fill: parent;
source:"anything.qml"
}
}
}