2021-09-22 QML 实时预览

7 篇文章 0 订阅

在学习qml过程中,可能大家都希望能有实时预览效果,在网上找了找,找到个解决方案,原理就是使用 QFileSystemWatcher 实时监控本地文件,一旦文件有变化,利用Qml 中的 Loader 加载qml文件

注意main.cpp中必须要设置本地绝对路径

engine.hotLoad("E:/QTCloud/QmlLearn/LivePreview/qml/main.qml");

左侧文件列表使用的是QFileSystemModel 这个qt官方有案例,本地文件增删时,列表也随之增删
其中qml 源码案例来自 qmlbook http://qmlbook.github.io/index.html

效果:
在这里插入图片描述

github https://github.com/tianxiaofan/LivePreview

以下是关键原码
main.cpp

#include "qmlliveengine.h"

#include <QGuiApplication>
#include <QQmlApplicationEngine>


int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif

    QGuiApplication app(argc, argv);

    QmlLiveEngine engine;
    engine.hotLoad("E:/QTCloud/QmlLearn/LivePreview/qml/main.qml");

    return app.exec();
}

qmlliveengine.h

#pragma once

#include <QFileSystemModel>
#include <QQmlApplicationEngine>
#include <QStringListModel>

class QFileSystemWatcher;
class QTimer;

class DisplayFileSystemModel : public QFileSystemModel
{
    Q_OBJECT
public:
    explicit DisplayFileSystemModel(QObject* parent = nullptr) : QFileSystemModel(parent) { }

    enum Roles {
        SizeRole                       = Qt::UserRole + 4,
        DisplayableFilePermissionsRole = Qt::UserRole + 5,
        LastModifiedRole               = Qt::UserRole + 6,
        UrlStringRole                  = Qt::UserRole + 7
    };
    Q_ENUM(Roles)

    QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override
    {
        if (index.isValid() && role >= SizeRole) {
            switch (role) {
            case UrlStringRole:
                return QVariant(QUrl::fromLocalFile(filePath(index)).toString());
            default:
                break;
            }
        }
        return QFileSystemModel::data(index, role);
    }

    QHash<int, QByteArray> roleNames() const override
    {
        QHash<int, QByteArray> result = QFileSystemModel::roleNames();
        return result;
    }
};

class QmlLiveEngine : public QQmlApplicationEngine
{
    Q_OBJECT
public:
    explicit QmlLiveEngine(QObject* parent = nullptr);

    void hotLoad(const QString& mainQml, bool integratedConsole = true);

    Q_INVOKABLE void    clearCache() { clearComponentCache(); }
    Q_INVOKABLE QString getMainQml() { return m_mainQml; };

private slots:
    void onFileSystemChanged();
    void onReloadRequested();

private:
    void watchFileSystemRecursively(const QString& dir);
    void unwatchAll();

    QFileSystemWatcher* m_watcher;
    QTimer*             m_timer;

    QObject*               m_window;
    QString                m_mainQml;
    QString                m_dir;
    DisplayFileSystemModel m_qmlFiles;
};

qmlliveengine.cpp

#include "qmlliveengine.h"
#include <QFileInfo>
#include <QTimer>
#include <QFileSystemWatcher>
#include <QQmlContext>
#include <QDir>

QmlLiveEngine::QmlLiveEngine(QObject* parent) :
    QQmlApplicationEngine(parent), m_watcher(nullptr), m_timer(nullptr), m_window(nullptr)
{
    qmlRegisterUncreatableType<DisplayFileSystemModel>(
            "io.qt.examples.quick.controls.filesystembrowser", 1, 0, "FileSystemModel",
            "Cannot create a FileSystemModel instance.");
}

void QmlLiveEngine::hotLoad(const QString &mainQml, bool integratedConsole)
{
    m_mainQml = mainQml;
    m_dir = m_mainQml.left(m_mainQml.lastIndexOf('/'));

    m_watcher = new QFileSystemWatcher(this);
    m_timer   = new QTimer(this);

    m_qmlFiles.setRootPath(m_dir);
    m_qmlFiles.setNameFilterDisables(false);
    m_qmlFiles.setNameFilters({ "*.qml" });
    watchFileSystemRecursively(m_dir);

    m_timer->setInterval(500);
    m_timer->setSingleShot(true);

    connect(m_watcher, &QFileSystemWatcher::directoryChanged, this
        , &QmlLiveEngine::onFileSystemChanged);
    connect(m_watcher, &QFileSystemWatcher::fileChanged, this
        , &QmlLiveEngine::onFileSystemChanged);
    connect(m_timer, &QTimer::timeout, this, &QmlLiveEngine::onReloadRequested);

    rootContext()->setContextProperty("QmlLiveEngine", this);
    rootContext()->setContextProperty("QmlDir", m_dir);
    rootContext()->setContextProperty("fileSystemModel", &m_qmlFiles);
    rootContext()->setContextProperty("rootPathIndex", m_qmlFiles.index(m_qmlFiles.rootPath()));

    load(mainQml);

    m_window = rootObjects().first();
}

void QmlLiveEngine::onFileSystemChanged()
{
    if (!m_timer) return;
    if (!m_timer->isActive()) m_timer->start();
}

void QmlLiveEngine::onReloadRequested()
{

    unwatchAll();
    watchFileSystemRecursively(m_dir);

    if (m_window) {
        QMetaObject::invokeMethod(m_window, "_reload");
    }
}

void QmlLiveEngine::watchFileSystemRecursively(const QString &dir)
{
    QDir d(dir);
    QStringList files = d.entryList(QStringList() << "*.qml", QDir::Files);
    QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot);

    m_watcher->addPath(dir);
    for (QString& file : files) {
        m_watcher->addPath(dir + '/' + file);
    }

    for (QString& subdir : dirs) {
        watchFileSystemRecursively(dir + '/' + subdir);
    }
}

void QmlLiveEngine::unwatchAll()
{
    QStringList dirs = m_watcher->directories();
    QStringList files = m_watcher->files();
    QStringList fails;

    for (QString &dir: dirs) {
        if (!m_watcher->removePath(dir)) {
            fails << dir;
        }
    }
    for (QString &file: files) {
        if (!m_watcher->removePath(file)) {
            fails << file;
        }
    }

    if (!fails.empty()) {
        qWarning() << tr("The following directories or files "
                         "cannot be removed from file system watcher:");
        for (QString &fail: fails) {
            qWarning() << "\t" << fail;
        }
    }
}


main.qml

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
import Qt.labs.folderlistmodel 2.15
import QtQuick.Controls 1.5
import QtQml.Models 2.12
import io.qt.examples.quick.controls.filesystembrowser 1.0

ApplicationWindow {
    id:root
    width: 800
    height: 600
    visible: true
    Rectangle{
        id:fileList
        width: 200
        height: root.height

        color: "lightgrey"

        Button{
            id: clearBtn
            text: "clear"
            width: 100
            height: 40
            anchors.bottom: parent.bottom
            anchors.right: parent.right
            onClicked: {
                clearQml()
            }
        }
        ItemSelectionModel {
            id: sel
            model: fileSystemModel
        }
        TreeView{
            anchors.left: parent.left
            anchors.top: parent.top
            anchors.right: parent.right
            anchors.bottom: clearBtn.top
            model: fileSystemModel
            rootIndex: rootPathIndex
            selection: sel
            TableViewColumn {
                role: "fileName"
                resizable: true
            }
            onActivated: {
                var url = fileSystemModel.data(index, FileSystemModel.UrlStringRole)
                loadQml(url)
            }
        }

    }
    property string currentQml
    Rectangle{
        id:title
        anchors.left: fileList.right
        anchors.leftMargin: 2
        anchors.right: parent.right
        anchors.top: parent.top
        height: 20
        Text {
            text: currentQml
            anchors.fill: parent
            horizontalAlignment: Text.AlignHCenter
            verticalAlignment: Text.AlignVCenter
        }
        color: "mediumaquamarine"
    }

    Rectangle{
        id: showView
        anchors.left: fileList.right
        anchors.leftMargin: 2
        anchors.right: parent.right
        anchors.top: title.bottom
        anchors.bottom: parent.bottom
        Canvas{
            anchors.fill: parent
            visible: true
            property int img_w: 16
            property int img_h: 16
            property string imageData: "qrc:/image/lb_transparent.png"
            onPaint: {
                var ctx = getContext("2d");
                for(let i = 0; i <= Math.floor(width/img_w);i++)
                {
                    for (let j = 0; j <= Math.floor(height/img_h); j++)
                    {
                        ctx.drawImage(imageData,i*img_w,j*img_h);
                    }
                }
            }
            Component.onCompleted: {
                loadImage(imageData)
            }
        }

        Loader{
            anchors.centerIn: parent
            id: pageLoader
            asynchronous: true
            focus: true //这里需要开启,不然对于加载的qml,如果需要焦点事件,则无法接收
            visible: true
        }

    }


    function loadQml(name)
    {
        if(name.length <= 0)
            return

        pageLoader.source = "";

        //需要先清空,不然会
        QmlLiveEngine.clearCache();

        pageLoader.setSource(name)
        pageLoader.visible = true;

        if(currentQml !== name)
            currentQml = name;

        console.log(pageLoader.source)
    }
    function clearQml()
    {
        pageLoader.source = "";
        pageLoader.visible = false;
    }

    function _reload(){
        if(currentQml.length <= 0)
            return;

        loadQml(currentQml)
    }

}

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: Qt6 QML实时开发是基于Qt6平台的快速开发框架,它利用Qt Quick语言和QML技术,为开发人员提供了设计和开发图形用户界面(GUI)所需的各种工具。QML是一种描述用户界面的语言,借助它可以轻松地创建各种精美的用户界面。 Qt6 QML实时开发的主要优点是其实时性和高效性。它可以让开发人员在软件开发过程中实时预览结果,使开发效率大大提高。同时,Qt6 QML实时开发还支持快速迭代和动态调整,便于开发人员随时调整和修改界面,同时保证了UI的高度灵活性和可扩展性。 Qt6 QML实时开发还提供了丰富的控件和组件库,这些库都是经过优化和测试的,能够让开发人员快速构建和布局GUI和用户交互界面。此外,Qt6 QML实时开发还支持跨平台开发,可用于开发Android、iOS、Windows、macOS和Linux等平台的应用程序。 综上所述,Qt6 QML实时开发是一种快速、高效、灵活的开发框架,在图形用户界面开发中应用广泛,也是未来GUI开发的一个趋势。它不仅可以大大提高开发效率,同时还提供了丰富的控件和组件,支持跨平台开发,是一款非常有价值的开发工具。 ### 回答2: Qt6 QML是一个开发桌面和移动应用程序的框架,其实时开发方案也得到了广泛的支持和认可。Qt6 QML通过其强大的功能和易于使用的界面设计,使开发人员可以更加方便和快速地开发出高质量的应用程序。 在Qt6 QML实时开发中,其主要特点包括: 1. 设计驱动型开发 Qt6 QML采用的是设计驱动型开发,开发人员可以直接在设计界面中进行开发与调试,而不需要手动写代码。这种开发方式可以大大提高开发效率,同时也可以有效降低错误发生的概率。 2. 实时预览界面效果 Qt6 QML具有实时预览界面效果的功能,这意味着开发人员可以在编写代码的同时观察到其效果,从而快速定位和修复问题。这种实时预览的功能也能够提高开发效率,并且可以让开发过程更加享受。 3. 基于组件的开发 Qt6 QML支持基于组件的开发,可以通过在不同的组件之间进行组合,以构建更高级的组件和应用程序。这种基于组件的开发方式,可以大大提高代码的可重用性,减少代码的冗余度。 综上所述,Qt6 QML实时开发是一个高效、方便和灵活的开发工具,可以使开发人员更加轻松和快速地开发出高质量的应用程序。同时,其具有丰富的功能和易于使用的界面设计,也可以让开发过程更加有趣和愉悦。 ### 回答3: Qt6 QML实时开发,指的是使用Qt6和QML技术进行实时开发的过程。Qt6是一种跨平台的应用程序框架,它可以帮助开发者快速创建高性能、现代化的应用程序,而QML则是一种基于JavaScript的用户界面语言,可以帮助开发者快速构建交互式的用户界面。因此,Qt6 QML实时开发可用于开发实时交互应用程序,如数据可视化、游戏、嵌入式系统等领域。 在Qt6 QML实时开发中,开发者可以使用Qt Creator等集成开发环境(IDE)轻松创建QML应用程序。QML语言支持丰富的界面元素和动画效果,使得界面设计非常灵活。此外,Qt6提供了丰富的C++类库和工具,方便开发者实现高性能的后台逻辑和各种设备的接口。 Qt6 QML实时开发的优势在于快速迭代。开发者可以使用实时预览功能,即在编辑代码时即时看到修改后的效果,从而提高开发效率和设计灵活性。此外,由于Qt6和QML都为开发者提供了丰富的功能和现成的库文件,因此可以极大地减少开发时间和成本。 总之,Qt6 QML实时开发是一种高效的技术和方法,可用于实现高性能、现代化的应用程序,为开发者提供快速迭代和灵活性,是目前最流行的开发方式之一。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值