1. 问题描述
由于编写qml组件代码时,常常会改动布局或代码逻辑,多次编译运行,比较耗时,因而可以尝试利用Loader组件实现自动加载已修改的代码。
2.方法
使用qml中Loader组件,即可实现部分效果。
Button
{
width: 100
height: 100
onClicked:
{
loader.source = ""
loader.source = "qrc:/TestBtn.qml"
}
}
Loader
{
id:loader
}
但是,运行程序,并修改 TestBtn.qml 文件里的代码,发现程序没有改变,解决问题的方法是在于没有刷新TestBtn.qml 以及qrc:使用的是经编译后的文件,故可以修改source
loader.source = ""
loader.source = "qrc:/TestBtn.qml?v=" + new Date().getTime()
以及将qrc: 改为绝对路径
loader.source = "file:///M:/Codes/QtCodes/DemoLoader/TestBtn.qml?v=" + new Date().getTime();
其次为了避免每次都需要Button来改变Loader, 可以使用QFileSystemWatcher来监视文件改动。
filewatch.h
#ifndef FILEWATCHER_H
#define FILEWATCHER_H
#include <QObject>
#include <QFileSystemWatcher>
#include <QString>
#include <QUrl>
#include <QDebug>
class FileWatcher : public QObject
{
Q_OBJECT
Q_PROPERTY(QString pathWatched READ pathWatched WRITE setPathWatched NOTIFY pathWatchedChanged)
public:
explicit FileWatcher(QObject *parent = nullptr)
: QObject{parent}
{
}
~FileWatcher()
{
if(m_fileWatcher != nullptr)
{
QObject::disconnect(m_fileWatcher, &QFileSystemWatcher::fileChanged, this, &FileWatcher::pathWatchedChanged);
delete m_fileWatcher;
m_fileWatcher = nullptr;
}
}
QString pathWatched() const
{
return m_pathWatched;
}
void setPathWatched(const QUrl& url)
{
if(url.isEmpty())
return;
QString newPathWatched = url.toLocalFile(); // 将qml里file:///转化为Qt里本地路径
if(m_pathWatched == newPathWatched)
return;
m_pathWatched = newPathWatched;
if(m_fileWatcher == nullptr)
{
m_fileWatcher = new QFileSystemWatcher;
m_fileWatcher->addPath(m_pathWatched); // 添加监听路径
QObject::connect(m_fileWatcher, &QFileSystemWatcher::fileChanged, this, &FileWatcher::pathWatchedChanged);
}
else
{
if(!m_fileWatcher->files().isEmpty())
{
QObject::disconnect(m_fileWatcher, &QFileSystemWatcher::fileChanged, this, &FileWatcher::pathWatchedChanged); // 删除已连接的信号槽
m_fileWatcher->removePaths(m_fileWatcher->files()); // 删除监听的路径
}
m_fileWatcher->addPath(m_pathWatched); // 添加监听路径
QObject::connect(m_fileWatcher, &QFileSystemWatcher::fileChanged, this, &FileWatcher::pathWatchedChanged);
}
}
signals:
void pathWatchedChanged(const QString& path);
private:
QString m_pathWatched;
QFileSystemWatcher *m_fileWatcher = nullptr;
};
#endif // FILEWATCHER_H
main.cpp 添加 qmlRegisterType<FileWatcher>("CppImport", 1, 0, "CppFileWatcher"); 实现注册cpp类型到qml里。
HeatLoader.qml里利用信号pathWatchedChanged来修改Loader里的source
import QtQuick 2.12
import CppImport 1.0
Item
{
property string source
onSourceChanged:
{
loader.source = ""
loader.source = source
}
CppFileWatcher
{
pathWatched: loader.source
onPathWatchedChanged:
{
loader.source = ""
loader.source = "file:///" + path + "?v=" + new Date().getTime()
}
}
Loader
{
id: loader
source: parent.source
}
}
测试代码main.qml, 其中source 为qml绝对路径,前面需要添加前缀 "file:///"
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
Window {
width: 640
height: 480
visible: true
id: root
title: qsTr("Hello World")
Row
{
width: 200
height: 200
Button
{
id: button
width: 100
height: 100
property bool btn: true
onClicked:
{
if(btn){
loader2.source = "file:///M:/Codes/QtCodes/DemoLoader/TestBtn.qml" + "?v=" + new Date().getTime()
// 设置source为带有时间戳的文件路径, 实现刷新, 否则显示未修改的TestBtn.qml
btn = false
button.text = "01"
root.title = "01"
}
else
{
loader2.source = "file:///M:/Codes/QtCodes/DemoLoader/TestBtn2.qml" +"?v=" + new Date().getTime()
btn =true;
button.text = "02"
root.title = "02"
}
}
}
HeatLoader
{
id: loader2
width: 100
height: 100
// source: "file:///M:/Codes/QtCodes/DemoLoader/TestBtn.qml"
}
}
}
TestBtn.qml TestBtn.qml为两个Rectangle
点击按钮运行效果如下
切换TestBtn.qml 并将按钮按到01,修改Rectangle为Button并保存,效果如下
切换TestBtn.qml和按钮02, 修改qml代码也可以实现相应效果。大功告成。