Qt qml 实现简易的热加载,保存文件即可加载

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代码也可以实现相应效果。大功告成。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值