首页
GitHub
QML热加载精简实现方法

# 简介

通过c++端侦听qml文件实时刷新界面而无须重新运行应用程序。

  • 当前在Mac OSX 系统上测试效果比较好,在Windows上效果不理想,有时候会出现多个窗口。
  • PS: 当前代码直接在github编辑器上编辑的,尚未在本地IDE上编辑,可能会有个别地方编辑有误,有空再补充。

# 运行效果

demo

# 代码

  • QmlListener.h
#ifndef QMLLISTENER_H
#define QMLLISTENER_H

#include <QObject>

class QQuickView;
class QQmlApplicationEngine;
class QmlListenerPrivate;
class QmlListener : public QObject
{
    Q_OBJECT
    Q_DISABLE_COPY(QmlListener)
    Q_DECLARE_PRIVATE(QmlListener)
    QScopedPointer<QmlListenerPrivate> const d_ptr;
public:
    explicit QmlListener(const QString &mainQml = QString(), QObject *parent = nullptr);
    ~QmlListener();
    
    void setIndexQml(const QString &qmlFile);
    
    void listen(QQuickView *view);
    void listen(QQmlApplicationEngine *engine);
    
    void addPaths(const QStringList &paths);
    void addPath(const QString &path);
    
signals:

public slots:
    void reload(const QString &file);
}
#endif // QMLLISTENER_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
  • QmlListener.cpp
#include "QmlListener.h"

#include <QFileSystemWatcher>
#include <QQmlApplicationEngine>
#include <QQmlEngine>
#include <QQuickView>
#include <QQmlContext>
#include <QQmlComponent>

class QmlListenerPrivate
{
    Q_DECLARE_PUBLIC(QmlListener)
    QmlListener *q_ptr;
public:
    QmlListenerPrivate(QmlListener *parent);
    
    void watching();
    
    QQuickView *view;
    QQmlApplicationEngine *engine;
    QFileSystemWatcher *watcher;
    QList<QObject *> qmlObjects;
    QString mainQml;
}

QmlListenerPrivate::QmlListenerPrivate(QmlListener *parent)
    : q_ptr(parent)
    , watcher(new QFileSystemWatcher())
{
}

QmlListenerPrivate::watching()
{
    Q_Q(QmlListener);
    if(!engine && !view)
        return ;
    
    const QString basePath = engine ? engine->baseUrl().toString() : view->engine()->baseUrl().toString();
    watcher->addPath(basePath);
    watcher->addPath(mainQml);
    QObject::connect(watcher, &QFileSystemWatcher::fileChanged, q, &QmlListener::reload);
}

QmlListener::QmlListener(const QString mainQml, QObject *parent)
    : QObject(parent)
    , d_ptr(new QmlListenerPrivate(this))
{
    Q_D(QmlListener);
    d->mainQml = mainQml;
}

QmlListener::~QmlListener()
{
}

void QmlListener::setIndexQml(const QString &qmlFile)
{
    Q_D(QmlListener);
    if(qmlFile == d->mainQml)
        return;

    d->mainQml = qmlFile;
}

void QmlListener::listen(QQuickView *view)
{
    Q_D(QmlListener);
    d->view = view;
    d->watching();
}

void QmlListener::listen(QQmlApplicationEngine *engine)
{
    Q_D(QmlListener);
    d->engine = engine;
    d->watching();
}

void QmlListener::addPaths(const QStringList &paths)
{
    Q_D(QmlListener);
    d->watcher->addPaths(paths);
}

void QmlListener::addPath(const QString &path)
{
    Q_D(QmlListener);
    d->watcher->addPath(path);
}

void QmlListener::reload(const QString &file)
{
    Q_D(QmlListener);
    if(d->view)
    {
        d->view->setSource(QUrl());
        d->view->engine()->clearComponentCache();
        d->view->setSource(QUrl(d->mainQml));
    }
    else if(d->engine)
    {
        foreach(QObject *obj, d->engine->rootObjects())
        {
            if(d->qmlObjects.contains(obj))
                continue;

            d->qmlObjects.append(obj);
            d->engine->setObjectOwnership(obj, QQmlApplicationEngine::CppOwnership);
            obj->deleteLater();
        }
        d->engine->clearComponentCache();
        d->engine->load(d->mainQml);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114

# 使用

  • main.cpp
#include "QmlListener.h"

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QDir>
#include <QDirIterator>

static const QString UI_PATH = "path/to/your/project/qml";

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);
    
    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

#ifdef QT_DEBUG
    // scan all qml files
    QDir dir(UI_PATH);
    dir.setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
    dir.setSorting(QDir::Name);

    QStringList files;
    QDirIterator it(dir, QDirIterator::Subdirectories);
    while (it.hasNext())
    {
        it.next();
        QFileInfo info = it.fileInfo();
        if(info.isFile())
        {
            files << info.filePath();
        }
    }
    
    QmlListener listtener;
    listtener.setIndexQml(UI_PATH + "/main.qml");
    listtener.addPaths(files);
    listtener.listen(&engine);
#endif

    return app.exec();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

# 待续