├── .gitignore
├── old_code
├── README.md
└── QmlThread
│ ├── qml.qrc
│ ├── deployment.pri
│ ├── qml
│ ├── worker.js
│ └── main.qml
│ ├── QmlThread.pro
│ ├── src
│ ├── qmlthread
│ │ ├── qmlthreadengine.h
│ │ ├── qmlthreadhelper.h
│ │ ├── qmlthread.h
│ │ ├── qmlthread.cpp
│ │ ├── qmlthreadengine.cpp
│ │ └── qmlthread_p.h
│ └── test
│ │ └── foo.h
│ ├── main.cpp
│ ├── README.md
│ └── doc
│ └── readme.md
├── thread_in_qml
├── src
│ ├── qmlthread
│ │ ├── QmlThread
│ │ ├── qmlthread.h
│ │ └── qmlthread.cpp
│ ├── qmlthread.pri
│ └── utility
│ │ ├── dirsize.h
│ │ └── dirsize.cpp
├── qml.qrc
├── deployment.pri
├── thread_in_qml.pro
├── qml
│ ├── thread
│ │ └── thread_dir_size.qml
│ └── main.qml
├── main.cpp
└── README.md
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | *.user
--------------------------------------------------------------------------------
/old_code/README.md:
--------------------------------------------------------------------------------
1 | # 本目录保留的是废弃的代码
2 |
3 |
--------------------------------------------------------------------------------
/thread_in_qml/src/qmlthread/QmlThread:
--------------------------------------------------------------------------------
1 | #include "qmlthread.h"
2 |
--------------------------------------------------------------------------------
/old_code/QmlThread/qml.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | qml/main.qml
4 | qml/worker.js
5 |
6 |
7 |
--------------------------------------------------------------------------------
/thread_in_qml/qml.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | qml/main.qml
4 | qml/thread/thread_dir_size.qml
5 |
6 |
7 |
--------------------------------------------------------------------------------
/thread_in_qml/src/qmlthread.pri:
--------------------------------------------------------------------------------
1 | INCLUDEPATH += $$PWD/
2 |
3 | HEADERS += $$PWD/qmlthread/qmlthread.h \
4 | $$PWD/utility/dirsize.h
5 |
6 | SOURCES += \
7 | $$PWD/qmlthread/qmlthread.cpp \
8 | $$PWD/utility/dirsize.cpp
9 |
10 |
--------------------------------------------------------------------------------
/thread_in_qml/deployment.pri:
--------------------------------------------------------------------------------
1 | unix:!android {
2 | isEmpty(target.path) {
3 | qnx {
4 | target.path = /tmp/$${TARGET}/bin
5 | } else {
6 | target.path = /opt/$${TARGET}/bin
7 | }
8 | export(target.path)
9 | }
10 | INSTALLS += target
11 | }
12 |
13 | export(INSTALLS)
14 |
15 |
--------------------------------------------------------------------------------
/old_code/QmlThread/deployment.pri:
--------------------------------------------------------------------------------
1 | unix:!android {
2 | isEmpty(target.path) {
3 | qnx {
4 | target.path = /tmp/$${TARGET}/bin
5 | } else {
6 | target.path = /opt/$${TARGET}/bin
7 | }
8 | export(target.path)
9 | }
10 | INSTALLS += target
11 | }
12 |
13 | export(INSTALLS)
14 |
15 |
--------------------------------------------------------------------------------
/thread_in_qml/thread_in_qml.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = app
2 |
3 | QT += qml quick widgets
4 | CONFIG += c++11
5 |
6 | SOURCES += main.cpp
7 |
8 | RESOURCES += qml.qrc
9 |
10 | # Additional import path used to resolve QML modules in Qt Creator's code model
11 | QML_IMPORT_PATH =
12 |
13 | # Default rules for deployment.
14 | include(deployment.pri)
15 | include(src/qmlthread.pri)
16 |
17 | OTHER_FILES += *.md
18 |
--------------------------------------------------------------------------------
/thread_in_qml/src/utility/dirsize.h:
--------------------------------------------------------------------------------
1 | #ifndef QYVLIK_DIRSIZE_H
2 | #define QYVLIK_DIRSIZE_H
3 |
4 | #include
5 |
6 | namespace qyvlik {
7 |
8 | class DirSize : public QObject
9 | {
10 | Q_OBJECT
11 | public:
12 | explicit DirSize(QObject *parent = 0);
13 |
14 | Q_INVOKABLE qint64 dirSize(const QString &path);
15 | };
16 |
17 | } // namespace qyvlik
18 |
19 | #endif // QYVLIK_DIRSIZE_H
20 |
--------------------------------------------------------------------------------
/thread_in_qml/qml/thread/thread_dir_size.qml:
--------------------------------------------------------------------------------
1 | // thread only
2 | // 这里你不能使用到主线程中的QQmlEngine的上下文!!!!
3 | // 不能使用 Window 和 Item 相关类
4 |
5 | import qyvlik.thread.runnable 0.1
6 | import qyvlik.utility 0.1
7 |
8 |
9 | QmlRunnable {
10 | id: runnable
11 |
12 | readonly property var __: DirSize {
13 | id: dirsize
14 | }
15 |
16 | onMessageReceived: {
17 | console.time("dirsize")
18 | sendMessage(dirsize.dirSize(message));
19 | console.timeEnd("dirsize")
20 | }
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/old_code/QmlThread/qml/worker.js:
--------------------------------------------------------------------------------
1 | // This is for QmlThread
2 | // Don't support Qt object
3 | // Don''t support .import
4 |
5 | Worker.onMessage = function(message) {
6 | var i
7 |
8 | try {
9 | // FooHelperFactory 是在 QML 中使用 helper 注册的
10 | var foo = FooHelperFactory.create();
11 | // c++ native method
12 | i = foo.helper(message.OK);
13 | } catch(e) {
14 | console.log(e);
15 | i = 0;
16 | }
17 | //var xhr = new XMLHttpRequest;
18 |
19 | Worker.sendMessageToThread({"OK": i});
20 | }
21 |
--------------------------------------------------------------------------------
/old_code/QmlThread/QmlThread.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = app
2 |
3 | QT += qml quick widgets
4 |
5 | SOURCES += main.cpp
6 | RESOURCES += qml.qrc
7 |
8 | # Additional import path used to resolve QML modules in Qt Creator's code model
9 | QML_IMPORT_PATH =
10 |
11 | # Default rules for deployment.
12 | include(deployment.pri)
13 |
14 | HEADERS += \
15 | src/test/foo.h
16 |
17 | CONFIG += C++11
18 |
19 | SOURCES += src/qmlthread/qmlthread.cpp \
20 | src/qmlthread/qmlthreadengine.cpp
21 |
22 | HEADERS += \
23 | src/qmlthread/qmlthread.h \
24 | src/qmlthread/qmlthread_p.h \
25 | src/qmlthread/qmlthreadhelper.h \
26 | src/qmlthread/qmlthreadengine.h
27 |
--------------------------------------------------------------------------------
/thread_in_qml/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include
6 | #include
7 |
8 | int main(int argc, char *argv[])
9 | {
10 | QGuiApplication app(argc, argv);
11 |
12 | // only for xxx.thread.qml
13 | qmlRegisterType("qyvlik.thread.runnable", 0, 1, "QmlRunnable");
14 | qmlRegisterType("qyvlik.thread", 0, 1, "QmlThread");
15 |
16 | qmlRegisterType("qyvlik.utility", 0, 1, "DirSize");
17 |
18 | QQmlApplicationEngine engine;
19 | engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml")));
20 |
21 | return app.exec();
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/thread_in_qml/src/utility/dirsize.cpp:
--------------------------------------------------------------------------------
1 | #include "dirsize.h"
2 |
3 | #include
4 | #include
5 |
6 | namespace qyvlik {
7 |
8 | DirSize::DirSize(QObject *parent) :
9 | QObject(parent)
10 | {
11 |
12 | }
13 |
14 | qint64 DirSize::dirSize(const QString &path)
15 | {
16 | QString path_ = path;
17 | QDir dir(path_.replace("file:///", ""));
18 |
19 | // qDebug() << path_ << " exists:" << dir.exists();
20 |
21 | qint64 size = 0;
22 |
23 | Q_FOREACH (QFileInfo fileInfo, dir.entryInfoList(QDir::Files)) {
24 | size += fileInfo.size();
25 | }
26 |
27 | Q_FOREACH (QString subDir, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
28 | size += dirSize(path + QDir::separator() + subDir);
29 | }
30 | return size;
31 | }
32 |
33 | } // namespace qyvlik
34 |
35 |
--------------------------------------------------------------------------------
/old_code/QmlThread/src/qmlthread/qmlthreadengine.h:
--------------------------------------------------------------------------------
1 | #ifndef QMLTHREADENGINE_H
2 | #define QMLTHREADENGINE_H
3 |
4 |
5 | #include
6 | #include "qmlthreadhelper.h"
7 |
8 | // don't have console or XMLHttpRequest
9 | class Console : public QObject
10 | {
11 | Q_OBJECT
12 | public:
13 | Console(QObject *parent = Q_NULLPTR);
14 | Q_INVOKABLE void print(const QJSValue& value);
15 | };
16 |
17 | class QmlThreadEngine : public QJSEngine
18 | {
19 | Q_OBJECT
20 | public:
21 | QmlThreadEngine(QObject *parent);
22 |
23 | void setContextProperty(const QString& name, const QJSValue& value);
24 |
25 | Q_SIGNALS:
26 | void registerHelper(const QString& helperName, QmlThreadHelper* threadHelper);
27 |
28 | private Q_SLOTS:
29 | void onRegisterHelper(const QString& helperName, QmlThreadHelper* threadHelper);
30 |
31 | private:
32 | QMap threadHelperFactoryMap;
33 | };
34 |
35 |
36 | #endif // QMLTHREADENGINE_H
37 |
--------------------------------------------------------------------------------
/thread_in_qml/qml/main.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.5
2 | import QtQuick.Window 2.2
3 | import QtQuick.Controls 1.4
4 | import QtQuick.Dialogs 1.2
5 |
6 | import qyvlik.thread 0.1
7 |
8 | Window {
9 | visible: true
10 |
11 | id: app
12 |
13 | Flow {
14 | anchors.fill: parent
15 |
16 | Button {
17 | text: "open file dialog"
18 | onClicked: {
19 | fileDialog.open()
20 | }
21 | }
22 | }
23 |
24 | FileDialog {
25 | id: fileDialog
26 | title: "Please choose a file"
27 | selectFolder: true
28 | onAccepted: {
29 | console.log("You chose: " + fileDialog.fileUrl);
30 | var sendResult = thread.sendMessage(fileDialog.fileUrl.toString())
31 | }
32 | }
33 |
34 | Text {
35 | text: qsTr("Hello World")
36 | anchors.centerIn: parent
37 | }
38 |
39 | QmlThread {
40 | id: thread
41 | runnableSource: "./thread/thread_dir_size.qml"
42 | onMessageReceived: {
43 | console.log(message.toString())
44 | }
45 | }
46 | }
47 |
48 |
--------------------------------------------------------------------------------
/old_code/QmlThread/src/test/foo.h:
--------------------------------------------------------------------------------
1 | #ifndef FOO
2 | #define FOO
3 |
4 | #include "src/qmlthread/qmlthreadhelper.h"
5 |
6 | class Foo : public QObject
7 | {
8 | Q_OBJECT
9 | public:
10 | Foo(QObject *parent = Q_NULLPTR):
11 | QObject(parent)
12 | { }
13 | Q_INVOKABLE int helper(int i) {
14 | if(i<=0) {
15 | return 1;
16 | } else {
17 | return helper(i-1) + helper(i-2);
18 | }
19 | }
20 | };
21 |
22 |
23 | class FooHelperFactory : public QmlThreadHelperFactory
24 | {
25 | Q_OBJECT
26 | public:
27 | FooHelperFactory(QObject *parent, QJSEngine* engine):
28 | QmlThreadHelperFactory(parent, engine)
29 | { }
30 |
31 | QJSValue create() {
32 | static Foo object;
33 | return getJsEngine()->newQObject(&object);
34 | }
35 | };
36 |
37 |
38 | class FooHelper : public QmlThreadHelper
39 | {
40 | Q_OBJECT
41 | public:
42 | FooHelper(QObject *parent = Q_NULLPTR):
43 | QmlThreadHelper(parent)
44 | { this->setFactoryName("FooHelperFactory");}
45 |
46 | QmlThreadHelperFactory* helperFactory(QObject* factoryParent, QJSEngine* engine) {
47 | return new FooHelperFactory(factoryParent, engine);
48 | }
49 | };
50 |
51 |
52 | #endif // FOO
53 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # QmlThread
2 |
3 | More powerful Thread module in QML.
4 |
5 | > [old_code](old_code/QmlThread/README.md)
6 |
7 | ## 新的想法
8 |
9 | > [thread_in_qml](thread_in_qml/README.md)
10 |
11 | 之前的设计思路是将 `QJSEngine` 移动到新线程,并为线程中的 `QJSEngine` 设计一套注册机制。现在看来拙劣至极。
12 |
13 | 直接将 `QQmlEngine` 移动到新的线程,qml 的类型注册与 `QQmlEngine` 实例化无关。然后 `qml` 的语法补全和智能提示又比 `JavaScript` 强太多了。所以决定使用 `qml` 作为线程处理的代码。
14 |
15 | 线程连接器:
16 |
17 | ```
18 | Thread {
19 | id: thread
20 | source: "./thread/thread_dir_size.qml"
21 | onMessageReceived: {
22 | console.log(message.toString())
23 | }
24 | }
25 | ```
26 |
27 | > 接口设计可能参照 `WebScoket` 那样,就像主线程异步连接到了网络中一台功能强劲的电脑,将你的请求转化为运算,然后再将结果发送回主线程。
28 |
29 | ```
30 | // thread_dir_size.qml
31 | // 约定文件名为小写开头,这样 QtCreator 的语法树模型就不会将主线程中的变量映射到这边。
32 | import qyvlik.thread.runnable 0.1
33 | import qyvlik.utility 0.1
34 |
35 |
36 | QmlRunnable {
37 | id: runnable
38 |
39 | property var __: DirSize {
40 | id: dirsize
41 | }
42 |
43 | onMessageReceived: {
44 | console.time("dirsize")
45 | sendMessage(dirsize.dirSize(message));
46 | console.timeEnd("dirsize")
47 | }
48 | }
49 | ```
50 |
51 | [讨论](http://qtdream.com/topic/533/qml-%E4%B8%8B%E7%9A%84%E5%A4%9A%E7%BA%BF%E7%A8%8B)
52 |
--------------------------------------------------------------------------------
/old_code/QmlThread/src/qmlthread/qmlthreadhelper.h:
--------------------------------------------------------------------------------
1 | #ifndef QMLTHREADHELPER
2 | #define QMLTHREADHELPER
3 |
4 | #include
5 | #include
6 |
7 | class QmlThreadHelperFactory : public QObject
8 | {
9 | Q_OBJECT
10 | public:
11 | QmlThreadHelperFactory(QObject *parent, QJSEngine* engine):
12 | QObject(parent),
13 | jsEngine(engine)
14 | { }
15 |
16 | Q_INVOKABLE virtual QJSValue create() = 0;
17 |
18 | inline QJSEngine *getJsEngine() const
19 | { return jsEngine; }
20 |
21 | protected:
22 | QJSEngine* jsEngine;
23 | };
24 |
25 | class QmlThreadHelper : public QObject
26 | {
27 | Q_OBJECT
28 | Q_PROPERTY(QString factoryName READ factoryName CONSTANT FINAL)
29 | public:
30 | QmlThreadHelper(QObject *parent = Q_NULLPTR):
31 | QObject(parent),
32 | m_factoryName("ThreadHelperFactory")
33 | { }
34 |
35 | virtual QmlThreadHelperFactory* helperFactory(QObject* parent, QJSEngine* jsEngine) = 0;
36 | // { return Q_NULLPTR; }
37 |
38 | QString factoryName() const
39 | { return m_factoryName; }
40 |
41 | protected:
42 | void setFactoryName(const QString &name)
43 | { m_factoryName = name; }
44 |
45 | private:
46 | QString m_factoryName;
47 | QString m_helperInfo;
48 | };
49 |
50 |
51 | #endif // QMLTHREADHELPER
52 |
--------------------------------------------------------------------------------
/old_code/QmlThread/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | #include "src/qmlthread/qmlthread.h"
12 | #include "src/test/foo.h"
13 |
14 | int main(int argc, char *argv[])
15 | {
16 | QApplication app(argc, argv);
17 |
18 |
19 | qmlRegisterUncreatableType("Core",
20 | 0, 1,
21 | "ThreadHelper",
22 | "ThreadHelper is Uncreatable Type");
23 |
24 | qmlRegisterType("Core", 0, 1, "Thread");
25 |
26 | qmlRegisterType("Core", 0, 1, "FooHelper");
27 |
28 | // QJSEngine jsEngine;
29 | // QJSValue global = jsEngine.globalObject();
30 | // QJSValueIterator it(global);
31 | // while (it.hasNext()) {
32 | // it.next();
33 | // qDebug() << it.name() << ": " << it.value().toString();
34 | // }
35 |
36 |
37 | QQmlApplicationEngine engine;
38 | engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml")));
39 |
40 | // // remove qrc...
41 | // QFile file(":/main.qml");
42 | // if(!file.open(QFile::ReadOnly | QFile::Text) ) {
43 | // qDebug() << file.errorString();
44 | // } else {
45 | // qDebug()<< file.readAll();
46 | // }
47 |
48 | return app.exec();
49 | }
50 |
51 |
--------------------------------------------------------------------------------
/old_code/QmlThread/src/qmlthread/qmlthread.h:
--------------------------------------------------------------------------------
1 | #ifndef QMLTHREAD_H
2 | #define QMLTHREAD_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | #include "qmlthreadhelper.h"
12 |
13 | class QmlThreadPrivate;
14 |
15 | class QmlThread : public QObject
16 | {
17 | Q_OBJECT
18 | Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
19 | Q_PROPERTY(QQmlListProperty helpers READ helpers)
20 |
21 | public:
22 | explicit QmlThread(QObject *parent = 0);
23 |
24 | ~QmlThread();
25 |
26 | QUrl source() const;
27 |
28 | void setSource(const QUrl& value);
29 |
30 | QQmlListProperty helpers();
31 |
32 | public Q_SLOTS:
33 | void sendMessageToWorker(QJsonValue message);
34 |
35 | Q_SIGNALS:
36 | void sourceChanged();
37 | void message(QJsonValue messageObject);
38 |
39 | protected:
40 | /*Q_INVOKABLE*/
41 | void registerThreadHelper(const QString& name, QmlThreadHelper* threadHelper);
42 |
43 | static void helpers_append(QQmlListProperty *list, QmlThreadHelper *value);
44 |
45 | static QmlThreadHelper* helpers_at(QQmlListProperty *list, int index);
46 |
47 | static void helpers_clear(QQmlListProperty *list);
48 |
49 | static int helpers_count(QQmlListProperty *list);
50 |
51 | private:
52 | QmlThreadPrivate* d_ptr;
53 | QList m_threadHelpers;
54 | };
55 |
56 | #endif // QMLTHREAD_H
57 |
--------------------------------------------------------------------------------
/old_code/QmlThread/qml/main.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.5
2 | import QtQuick.Controls 1.4
3 | import QtQuick.Layouts 1.1
4 | import Core 0.1
5 |
6 | ApplicationWindow {
7 | visible: true
8 | width: 640
9 | height: 480
10 | title: qsTr("Hello World")
11 |
12 | menuBar: MenuBar {
13 | Menu {
14 | title: qsTr("File")
15 | MenuItem {
16 | text: qsTr("&Open")
17 | onTriggered: console.log("Open action triggered");
18 | }
19 | MenuItem {
20 | text: qsTr("Exit")
21 | onTriggered: {
22 | Qt.quit();
23 | new Date().toJSON();
24 | DataView ;
25 | }
26 | }
27 | }
28 | }
29 |
30 | ColumnLayout {
31 | anchors.fill: parent
32 | anchors.margins: 100
33 | TextField {
34 | id: input
35 | Layout.fillWidth: true
36 | text: "15"
37 | }
38 | Button {
39 | text: qsTr("Start")
40 | Layout.fillWidth: true
41 | onClicked: {
42 | thread.sendMessageToWorker({"OK":input.text})
43 | }
44 | }
45 | Text {
46 | id: info
47 | }
48 | }
49 |
50 | FooHelper {
51 | id: foo
52 | }
53 |
54 | Thread {
55 | id: thread
56 | source: "./worker.js"
57 |
58 | // 帮助对象列表
59 | helpers: [foo ]
60 |
61 | onMessage: {
62 | console.log(messageObject.OK)
63 | info.text = "result:" + messageObject.OK;
64 | }
65 | }
66 | }
67 |
68 |
--------------------------------------------------------------------------------
/thread_in_qml/README.md:
--------------------------------------------------------------------------------
1 | # QmlThread
2 |
3 | More powerful Thread module in QML.
4 |
5 | 直接将 `QQmlEngine` 移动到新的线程,qml 的类型注册与 `QQmlEngine` 实例化无关。然后 `qml` 的语法补全和智能提示又比 `JavaScript` 强太多了。所以决定使用 `qml` 作为线程处理的代码。
6 |
7 | 线程连接器:
8 |
9 | ```
10 | Button {
11 | text: "dirsize"
12 | onClicked: thread.sendMessage("C:/")
13 | }
14 | Thread {
15 | id: thread
16 | source: "./thread/thread_dir_size.qml"
17 | onMessageReceived: {
18 | console.log(message.toString())
19 | }
20 | }
21 | ```
22 |
23 | > 接口设计参照 `WebScoket` 那样,就像主线程异步连接到了网络中一台功能强劲的电脑,将你的请求转化为运算,然后再将结果发送回主线程。
24 |
25 | ```
26 | // thread_dir_size.qml
27 | // 约定文件名为小写开头,这样 QtCreator 的语法树模型就不会将主线程中的变量映射到这边。
28 | import qyvlik.thread.runnable 0.1
29 | import qyvlik.utility 0.1
30 |
31 |
32 | QmlRunnable {
33 | id: runnable
34 |
35 | property var __: DirSize {
36 | id: dirsize
37 | }
38 |
39 | onMessageReceived: {
40 | console.time("dirsize")
41 | sendMessage(dirsize.dirSize(message));
42 | console.timeEnd("dirsize")
43 | }
44 | }
45 | ```
46 |
47 | ```
48 | +-------- Main Thread ----------+ +-------------- Thread -------------+
49 | | Thread.sendMessage | | |
50 | | | ---> | QmlRunnable.onMessageReceived |
51 | | | | dirsize.dirSize(message) |
52 | | | <--- | QmlRunnable.sendMessage |
53 | | Thread.onMessageReceived | | |
54 | +-------------------------------+ +-----------------------------------+
55 | ```
56 |
--------------------------------------------------------------------------------
/thread_in_qml/src/qmlthread/qmlthread.h:
--------------------------------------------------------------------------------
1 | #ifndef QYVLIK_CHILDTHREADCONNECTOR_H
2 | #define QYVLIK_CHILDTHREADCONNECTOR_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | class QThread;
10 | class QQmlEngine;
11 |
12 | namespace qyvlik {
13 |
14 | // moveToThread
15 | class Worker : public QObject
16 | {
17 | Q_OBJECT
18 | public:
19 | explicit Worker(QObject* parent = nullptr);
20 |
21 | public Q_SLOTS:
22 | void doWork(const QUrl& thread_qml_file);
23 |
24 | Q_SIGNALS:
25 | void sendMessage(const QJsonValue& message);
26 |
27 | void messageReceived(const QJsonValue& message);
28 |
29 | private:
30 | QQmlEngine* mEngine;
31 | };
32 |
33 | class QmlRunnable : public QObject, public QQmlParserStatus
34 | {
35 | Q_OBJECT
36 | Q_INTERFACES(QQmlParserStatus)
37 | public:
38 | explicit QmlRunnable(QObject* parent = nullptr);
39 |
40 | void classBegin() override;
41 | void componentComplete() override;
42 |
43 | Q_SIGNALS:
44 | void sendMessage(const QJsonValue& message);
45 | void messageReceived(const QJsonValue& message);
46 | };
47 |
48 | class QmlThreadPrivate;
49 | class QmlThread : public QObject
50 | {
51 | Q_OBJECT
52 | Q_PROPERTY(QUrl runnableSource READ runnableSource WRITE setRunnableSource NOTIFY runnableSourceChanged FINAL)
53 | public:
54 | explicit QmlThread(QObject *parent = 0);
55 | ~QmlThread();
56 |
57 | Q_INVOKABLE bool sendMessage(const QJsonValue& message);
58 |
59 | public:
60 | QUrl runnableSource() const;
61 | void setRunnableSource(const QUrl &newValue);
62 |
63 | Q_SIGNALS:
64 | void runnableSourceChanged(const QUrl runnableSource);
65 | void messageReceived(const QJsonValue& message);
66 |
67 | private:
68 | QmlThreadPrivate* d_ptr;
69 | };
70 |
71 | } // namespace qyvlik
72 |
73 | #endif // QYVLIK_CHILDTHREADCONNECTOR_H
74 |
--------------------------------------------------------------------------------
/old_code/QmlThread/README.md:
--------------------------------------------------------------------------------
1 | # 自定义的 QML 多线程支持
2 |
3 | More powerful Thread module in QML. Like WorkerScript but can use c++ native method in WorkerScript.
4 |
5 |
6 | 支持向线程脚本中注册 `C++` 对象。
7 |
8 | 以下为伪码,主要讲一下 `QmlThread` 的框架。
9 |
10 | ### 框架解析
11 |
12 | 算不上框架解析,只能是思路。
13 |
14 | ```
15 | // QmlThread
16 | QtObject {
17 | id: worker
18 |
19 | property url source
20 | property list helpers
21 | signal message(messageObject)
22 | function sendMessageToThread(message) {
23 | // emit
24 | d_ptr.sendMessageToThread(message);
25 | }
26 |
27 | QmlThreadPrivate {
28 | id: d_ptr
29 | source: worker.source
30 | }
31 |
32 | Component.onCompleted: {
33 | for(var iter in helpers) {
34 | d_ptr.registerHelpter(helpers.name, helpers[iter]);
35 | }
36 | }
37 | }
38 | ```
39 |
40 | 定义了一个 `QmlThread` 对象,其中包含一个 `QmlThreadPrivate` 对象和一个重要的链表属性 `helpers`。
41 |
42 | 在初始化完成后,向 `d_ptr` 注册带有名称的帮助类。
43 |
44 | ```
45 | // QmlThreadPrivate
46 | QtObject {
47 | signal sendMessageToThread(message)
48 | function registerHelpter(name, helper) {
49 | // emit
50 | worker.registerHelpter(name, helper)
51 | }
52 |
53 | Worker { id: worker }
54 |
55 | Thread { id: thread }
56 |
57 | Component.onCompleted: {
58 | worker.moveToThread(thread)
59 | worker.start();
60 | }
61 | }
62 | ```
63 |
64 | `QmlThreadPrivate` 对象包含一个工作实例和线程实例。在初始化完成后,将 `worker` 移动到 `thread` 线程。(worker 的 parent 设置为空)
65 |
66 | 在将 `Worker` 暴露出来的信号链接到 `QmlThreadPrivate` 的对应信号上或者方法上,以便调用。注意,只有信号是线程安全的,除非其他方法有设置线程保护。
67 |
68 | ```
69 | // Worker
70 |
71 | QtObject {
72 | id: worker
73 |
74 | signal sendMessageToThread(message)
75 | signal sendEvaluateToEngine(QString,QString,int)
76 | signal registerHelpter(name, helper);
77 |
78 | JSEngine {
79 | id: engine
80 | function evaluate(){}
81 | }
82 | function init(){
83 | // set console etc.
84 | engine.setSomeGlobalObject();
85 | }
86 | function clear(){
87 | init();
88 | }
89 | }
90 | ```
91 |
92 | 一个 `Worker` 有一个内置的,自定义的 `QJSEngine`。
93 |
94 | [更多信息](doc/readme.md)
95 |
96 | ## TODO
97 |
98 | 1. 优化设计。
99 |
100 | 2. 错误处理。
101 |
102 | 3. 本人懒癌晚期,有空再说。
--------------------------------------------------------------------------------
/old_code/QmlThread/src/qmlthread/qmlthread.cpp:
--------------------------------------------------------------------------------
1 | #include "qmlthread.h"
2 | #include "qmlthread_p.h"
3 |
4 | QmlThread::QmlThread(QObject *parent)
5 | : QObject(parent),
6 | d_ptr(new QmlThreadPrivate(this))
7 | {
8 | connect(d_ptr, &QmlThreadPrivate::sendMessageToThread,
9 | this, &QmlThread::message);
10 | }
11 |
12 |
13 | QmlThread::~QmlThread()
14 | {
15 | d_ptr->deleteLater();
16 | }
17 |
18 |
19 | QUrl QmlThread::source() const
20 | {
21 | return d_ptr->getSource();
22 | }
23 |
24 |
25 | void QmlThread::setSource(const QUrl &value)
26 | {
27 | if(d_ptr->getSource() != value) {
28 | d_ptr->setSource(value);
29 | Q_EMIT sourceChanged();
30 | }
31 | }
32 |
33 | QQmlListProperty QmlThread::helpers()
34 | {
35 | return QQmlListProperty(this,
36 | &this->m_threadHelpers,
37 | &QmlThread::helpers_append,
38 | &QmlThread::helpers_count,
39 | &QmlThread::helpers_at,
40 | &QmlThread::helpers_clear
41 | );
42 | }
43 |
44 |
45 | void QmlThread::sendMessageToWorker(QJsonValue message)
46 | {
47 | //#ifdef QT_DEBUG
48 | // qDebug() << Q_FUNC_INFO;
49 | //#endif
50 | Q_EMIT d_ptr->startWork(message);
51 | }
52 |
53 |
54 | void QmlThread::registerThreadHelper(const QString &name, QmlThreadHelper *threadHelper)
55 | {
56 | d_ptr->registerHelper(name, threadHelper);
57 | }
58 |
59 |
60 | void QmlThread::helpers_append(QQmlListProperty *list, QmlThreadHelper *value)
61 | {
62 | QmlThread* root = qobject_cast(list->object);
63 | if(root) {
64 | root->m_threadHelpers.append(value);
65 | //
66 | root->registerThreadHelper(value->factoryName(), value);
67 | }
68 | }
69 |
70 |
71 | QmlThreadHelper *QmlThread::helpers_at(QQmlListProperty *list, int index)
72 | {
73 | QmlThread* root = qobject_cast(list->object);
74 | if(root && root->m_threadHelpers.size() > index) {
75 | return root->m_threadHelpers.at(index);
76 | } else {
77 | return Q_NULLPTR;
78 | }
79 | }
80 |
81 |
82 | void QmlThread::helpers_clear(QQmlListProperty *list)
83 | {
84 | QmlThread* root = qobject_cast(list->object);
85 | if(root) {
86 | root->m_threadHelpers.clear();
87 | }
88 | }
89 |
90 |
91 | int QmlThread::helpers_count(QQmlListProperty *list)
92 | {
93 | QmlThread* root = qobject_cast(list->object);
94 | if(root) {
95 | return root->m_threadHelpers.size();
96 | } else {
97 | return 0;
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/thread_in_qml/src/qmlthread/qmlthread.cpp:
--------------------------------------------------------------------------------
1 | #include "qmlthread.h"
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | namespace qyvlik {
8 |
9 | Worker::Worker(QObject *parent) :
10 | QObject(parent),
11 | mEngine(nullptr)
12 | {
13 | }
14 |
15 | void Worker::doWork(const QUrl &thread_qml_file)
16 | {
17 | if(mEngine == nullptr) {
18 | mEngine = new QQmlEngine(this);
19 | qmlInfo(mEngine) << tr("thread qml file: ") << thread_qml_file
20 | << tr("current thread id: ")<< QThread::currentThreadId();
21 | }
22 |
23 | QQmlComponent component(mEngine, thread_qml_file, QQmlComponent::PreferSynchronous);
24 |
25 | if(component.status() != QQmlComponent::Ready) {
26 | qmlInfo(&component) << component.errors();
27 | return;
28 | }
29 |
30 | QObject *myObject = component.create();
31 |
32 | QmlRunnable *runnable = qobject_cast(myObject);
33 | if(runnable == nullptr) {
34 | qmlInfo(myObject) << tr("qml file is not QmlRunnable") << component.errors();
35 | myObject->deleteLater();
36 | return;
37 | }
38 |
39 | QQmlEngine::setObjectOwnership(runnable, QQmlEngine::JavaScriptOwnership);
40 |
41 | connect(this, &Worker::messageReceived, runnable, &QmlRunnable::messageReceived);
42 | connect(runnable, &QmlRunnable::sendMessage, this, &Worker::sendMessage);
43 | }
44 |
45 |
46 | QmlRunnable::QmlRunnable(QObject *parent) :
47 | QObject(parent)
48 | {}
49 |
50 | void QmlRunnable::classBegin()
51 | {
52 | QThread* mainThread = QCoreApplication::instance()->thread();
53 | QThread* currentThread = QThread::currentThread();
54 |
55 | if(mainThread == currentThread) {
56 |
57 | qmlInfo(this) << "mainThread: " << (void*)mainThread
58 | << "currentThread: " << (void*)currentThread;
59 | // abort();
60 | }
61 | }
62 |
63 | void QmlRunnable::componentComplete()
64 | {
65 | }
66 |
67 |
68 | class QmlThreadPrivate
69 | {
70 | public:
71 | QmlThreadPrivate(QThread* thread, Worker* worker):
72 | mThread(thread),
73 | mWorker(worker)
74 | {}
75 | QThread* mThread;
76 | Worker* mWorker;
77 | QUrl mRunnableSource;
78 | };
79 |
80 | QmlThread::QmlThread(QObject *parent) :
81 | QObject(parent),
82 | d_ptr(new QmlThreadPrivate(new QThread(this), new Worker))
83 | {
84 |
85 | d_ptr->mWorker->moveToThread(d_ptr->mThread);
86 |
87 | connect(this, &QmlThread::runnableSourceChanged, d_ptr->mWorker, &Worker::doWork);
88 | connect(d_ptr->mThread, &QThread::destroyed, d_ptr->mWorker, &QObject::deleteLater);
89 |
90 | connect(d_ptr->mWorker, &Worker::sendMessage, this, &QmlThread::messageReceived);
91 |
92 | d_ptr->mThread->start();
93 | }
94 |
95 | QmlThread::~QmlThread()
96 | {
97 | d_ptr->mThread->quit();
98 | d_ptr->mThread->wait();
99 | delete d_ptr;
100 | }
101 |
102 | bool QmlThread::sendMessage(const QJsonValue& message)
103 | {
104 | if(!d_ptr->mThread->isRunning()) {
105 | qmlInfo(d_ptr->mWorker) << "thread is running";
106 | return false;
107 | }
108 |
109 | Q_EMIT d_ptr->mWorker->messageReceived(message);
110 |
111 | return true;
112 | }
113 |
114 | QUrl QmlThread::runnableSource() const
115 | {
116 | return d_ptr->mRunnableSource;
117 | }
118 |
119 | void QmlThread::setRunnableSource(const QUrl &newValue)
120 | {
121 | if(d_ptr->mRunnableSource != newValue) {
122 | d_ptr->mRunnableSource = newValue;
123 | Q_EMIT runnableSourceChanged(d_ptr->mRunnableSource);
124 | }
125 | }
126 |
127 |
128 |
129 | } // namespace qyvlik
130 |
131 |
--------------------------------------------------------------------------------
/old_code/QmlThread/src/qmlthread/qmlthreadengine.cpp:
--------------------------------------------------------------------------------
1 | #include "qmlthreadengine.h"
2 |
3 | Console::Console(QObject *parent) :
4 | QObject(parent)
5 | {}
6 |
7 | void Console::print(const QJSValue &value) {
8 | printf(qPrintable(value.toString()));
9 | fflush(stdout);
10 | }
11 |
12 | QmlThreadEngine::QmlThreadEngine(QObject *parent):
13 | QJSEngine(parent)
14 | {
15 | globalObject().setProperty("console", newQObject(new Console(this)));
16 | globalObject()
17 | .property("console")
18 | .setProperty("log", evaluate("function(){ \n"
19 | " console.print('QmlThread:') \n"
20 | " for(var iter in arguments) { \n"
21 | " console.print(arguments[iter]+' ');\n"
22 | " } \n"
23 | " console.print('\\n') \n"
24 | " }\n"));
25 |
26 | connect( this, SIGNAL(registerHelper(QString,QmlThreadHelper*)),
27 | this, SLOT(onRegisterHelper(QString,QmlThreadHelper*)) );
28 | }
29 |
30 | void QmlThreadEngine::setContextProperty(const QString &name, const QJSValue &value)
31 | {
32 | this->globalObject().setProperty(name, value);
33 | }
34 |
35 | void QmlThreadEngine::onRegisterHelper(const QString &helperName,
36 | QmlThreadHelper *threadHelper)
37 | {
38 | auto find = threadHelperFactoryMap.find(helperName);
39 | auto end = threadHelperFactoryMap.end();
40 | if(find == end) {
41 | threadHelperFactoryMap.insert(helperName, threadHelper->helperFactory(this, this));
42 | this->setContextProperty(helperName,
43 | this->newQObject(threadHelperFactoryMap[helperName]));
44 | } else {
45 | threadHelperFactoryMap[helperName]->deleteLater();
46 | threadHelperFactoryMap[helperName] = threadHelper->helperFactory(this, this);
47 | this->setContextProperty(helperName,
48 | this->newQObject(threadHelperFactoryMap[helperName]));
49 | }
50 | }
51 |
52 |
53 |
54 | /*
55 | "Object" : "function() { [code] }"
56 | "String" : "function() { [code] }"
57 | "Number" : "function() { [code] }"
58 | "Boolean" : "function() { [code] }"
59 | "Array" : "function() { [code] }"
60 | "Function" : "function() { [code] }"
61 | "Date" : "function() { [code] }"
62 | "RegExp" : "function() { [code] }"
63 | "Error" : "function() { [code] }"
64 | "EvalError" : "function() { [code] }"
65 | "RangeError" : "function() { [code] }"
66 | "ReferenceError" : "function() { [code] }"
67 | "SyntaxError" : "function() { [code] }"
68 | "TypeError" : "function() { [code] }"
69 | "URIError" : "function() { [code] }"
70 | "ArrayBuffer" : "function() { [code] }"
71 | "DataView" : "function() { [code] }"
72 | "Int8Array" : "function() { [code] }"
73 | "Uint8Array" : "function() { [code] }"
74 | "Uint8ClampedArray" : "function() { [code] }"
75 | "Int16Array" : "function() { [code] }"
76 | "Uint16Array" : "function() { [code] }"
77 | "Int32Array" : "function() { [code] }"
78 | "Uint32Array" : "function() { [code] }"
79 | "Float32Array" : "function() { [code] }"
80 | "Float64Array" : "function() { [code] }"
81 | "Math" : "[object Math]"
82 | "JSON" : "[object JSON]"
83 | "undefined" : "undefined"
84 | "NaN" : "NaN"
85 | "Infinity" : "Infinity"
86 | "eval" : "function() { [code] }"
87 | "parseInt" : "function() { [code] }"
88 | "parseFloat" : "function() { [code] }"
89 | "isNaN" : "function() { [code] }"
90 | "isFinite" : "function() { [code] }"
91 | "decodeURI" : "function() { [code] }"
92 | "decodeURIComponent" : "function() { [code] }"
93 | "encodeURI" : "function() { [code] }"
94 | "encodeURIComponent" : "function() { [code] }"
95 | "escape" : "function() { [code] }"
96 | "unescape" : "function() { [code] }"
97 | */
98 |
--------------------------------------------------------------------------------
/old_code/QmlThread/doc/readme.md:
--------------------------------------------------------------------------------
1 | # QML 中的多线程
2 |
3 | ## WorkerScript
4 |
5 | QML 中的 WorkerScript 是通过一个线程来创建一个定制过的 QQmlEngine 来实现的。
6 |
7 | 大体结构就是:
8 |
9 | ```
10 | WorkerScript {
11 | id: worker
12 | function sendMessage(msg) {
13 | thread.sendMessage(msg);
14 | }
15 | signal message(messageObject)
16 |
17 | Thread {
18 | id: thread
19 | signal sendMessage(msg)
20 | signal message(messageObject)
21 | MyQmlEngine {
22 | id: engine
23 | signal sendMessage(msg)
24 | signal message(messageObject)
25 | }
26 | }
27 |
28 | Component.onCompleted: {
29 | thread.message.connect(worker.message);
30 | engine.sendMessage.connect(thread.message);
31 | thread.sendMessage(engine.message);
32 | }
33 | }
34 | ```
35 |
36 | 整体意思就是在一个线程中有一个独立的 `QQmlEngine` 实例,所有其他线程对 `QQmlEngine` 的操作都要通过 `Thread` 。
37 |
38 | 但是 `WorkerScript` 不能向线程传递一个函数或者 `QObjec*` 的实例,只能是 JSON 的数据或者 `ListModel`。
39 |
40 | ## 自定义的 QML 多线程支持
41 |
42 | 支持向线程脚本中注册 `C++` 对象。
43 |
44 | 以下为伪码,主要讲一下 `QmlThread` 的框架。
45 |
46 | ### 框架解析
47 |
48 | 算不上框架解析,只能是思路。
49 |
50 | ```
51 | // QmlThread
52 | QtObject {
53 | id: worker
54 |
55 | property url source
56 | property list helpers
57 | signal message(messageObject)
58 | function sendMessageToThread(message) {
59 | // emit
60 | d_ptr.sendMessageToThread(message);
61 | }
62 |
63 | QmlThreadPrivate {
64 | id: d_ptr
65 | source: worker.source
66 | }
67 |
68 | Component.onCompleted: {
69 | for(var iter in helpers) {
70 | d_ptr.registerHelpter(helpers.name, helpers[iter]);
71 | }
72 | }
73 | }
74 | ```
75 |
76 | 定义了一个 `QmlThread` 对象,其中包含一个 `QmlThreadPrivate` 对象和一个重要的链表属性 `helpers`。
77 |
78 | 在初始化完成后,向 `d_ptr` 注册带有名称的帮助类。
79 |
80 | ```
81 | // QmlThreadPrivate
82 | QtObject {
83 | signal sendMessageToThread(message)
84 | function registerHelpter(name, helper) {
85 | // emit
86 | worker.registerHelpter(name, helper)
87 | }
88 |
89 | Worker { id: worker }
90 |
91 | Thread { id: thread }
92 |
93 | Component.onCompleted: {
94 | worker.moveToThread(thread)
95 | worker.start();
96 | }
97 | }
98 | ```
99 |
100 | `QmlThreadPrivate` 对象包含一个工作实例和线程实例。在初始化完成后,将 `worker` 移动到 `thread` 线程。(worker 的 parent 设置为空)
101 |
102 | 在将 `Worker` 暴露出来的信号链接到 `QmlThreadPrivate` 的对应信号上或者方法上,以便调用。注意,只有信号是线程安全的,除非其他方法有设置线程保护。
103 |
104 | ```
105 | // Worker
106 |
107 | QtObject {
108 | id: worker
109 |
110 | signal sendMessageToThread(message)
111 | signal sendEvaluateToEngine(QString,QString,int)
112 | signal registerHelpter(name, helper);
113 |
114 | JSEngine {
115 | id: engine
116 | function evaluate(){}
117 | }
118 | function init(){
119 | // set console etc.
120 | engine.setSomeGlobalObject();
121 | }
122 | function clear(){
123 | init();
124 | }
125 | }
126 | ```
127 |
128 | 一个 `Worker` 有一个内置的,自定义的 `QJSEngine`。
129 |
130 | ### 运作流程
131 |
132 | 首先初始化。
133 |
134 | ```
135 | // script.js
136 | Worker.onMessage = function(message) {
137 | var i = message.OK + 1;
138 | Worker.sendMessageToThread({"OK": i});
139 | }
140 | ```
141 |
142 | 在设定 `Thread` 的 `source` 后,就会触发 `Worker::sendEvaluateToEngine` 的信号,将读取的代码发送给 `QJSEngine` 执行。注意在执行读取到的线程脚本时,就已经对 `QJSEngine` 设置了一些必要的对象了,例如全局对象 `Worker`。
143 |
144 | 上述函数将一个函数保存到全局对象 `Worker` 的 `onMessage` 中去。
145 |
146 | 接下来等待触发 `Thread::sendMessageToWorker` 这个函数,这个函数一旦被调用,就会触发 `d_ptr->startWork`,开始触发线程中 `Worker` 的工作函数 `onMessage`。
147 |
148 | 触发栈如下:
149 |
150 | ```
151 | QmlThread::sendMessageToWorker -->
152 | QmlThreadPrivate::startWork -->
153 | Worker::doWork -->
154 | Worker::work -->
155 | Worker.onMessage
156 | ```
157 |
158 | 在完成任务后,全局对象 `Worker` 触发 `sendMessageToThread` 信号,这个信号通过 `QmlThreadPrivate::sendMessageToThread` 信号转发给 `Thread::message`。将处理好的数据送出。
159 |
160 | ```
161 | Worker.sendMessageToThread -->
162 | QmlThreadPrivate::sendMessageToThread -->
163 | QmlThread::message
164 | ```
165 |
166 | ~~改进,应该是 `JSEngine` 拥有 `Worker`。所以需要大改。不过暴露出来的接口改动却不会很大。~~
167 |
168 | ---
169 |
170 | ### 注册帮助类到 `Thread`
171 |
172 | 先看怎么使用吧。
173 |
174 | ```qml
175 | Thread {
176 | id: thread
177 | source: "./worker.js"
178 | helpers: [
179 | FooHelper {
180 | id: foo // foo.name == FooHelper
181 | }
182 | ]
183 | onMessage: {
184 | console.log(messageObject.OK)
185 | print("result:" + messageObject.OK)
186 | }
187 | }
188 | ```
189 |
190 | 在 qml 文档中,在 `Thread::helpers` 列表中写入一些帮助类(继承自 `QmlThreadHelper`)。
191 |
192 | ```js
193 | //~ worker.js
194 | Worker.onMessage = function(message) {
195 | var i;
196 |
197 | try {
198 | var foo = FooHelperFactory.create();
199 | i = foo.helper(message.OK);
200 | } catch(e) {
201 | console.log(e);
202 | i = 0;
203 | }
204 |
205 | Worker.sendMessageToThread({"OK": i});
206 | }
207 | ```
208 |
209 | 在 `JavaScript` 代码中,只能在函数内部使用 `FooHelper`,否则直接使用会提示 `undefined`。因为 `QmlThread` 的实现是先加载这个 `js` 文件,后注册帮助类。
210 |
211 | ```cpp
212 | class QmlThreadHelper : public QObject
213 | {
214 | Q_OBJECT
215 | Q_PROPERTY(QString factoryName READ factoryName CONSTANT FINAL)
216 | ...
217 | virtual QmlThreadHelperFactory* helperFactory(QObject* parent, QJSEngine* jsEngine) = 0;
218 | ...
219 | };
220 | ```
221 |
222 | 看看部分 `QmlThreadHelper` 的定义,定义了一个 `QmlThreadHelperFactory* QmlThreadHelper::helperFactory(QObject* parent, QJSEngine* jsEngine)` 纯虚函数,需要在继承后提供一个实现,这个函数主要是用来返回一个工厂实例,这个返回的工厂实例是属于工作线程中 `JSEngine` 的。然后将实例化的工厂类设置到 `JSEngine` 的属性中。
223 |
224 | > 因为是多线程,所以不能直接在另一个线程的 `JSEngine` 中使用 `QmlThreadHelper` 的方法,来获取一个帮助对象实例(这个实例对象必须在线程中创建,不能在主线程创建,在主线程创建,然后在线程中使用,由于没有设置线程安全机制,容易出问题),但是可以在另一个线程使用 `QmlThreadHelper::helperFactory` 来获取一个工厂实例,由于是在线程中创建的,并且工厂实例属于 `JSEngine`,就没有线程限制。
225 |
226 | ```cpp
227 | class QmlThreadHelperFactory : public QObject
228 | {
229 | QmlThreadHelperFactory(QObject *parent, QJSEngine* engine)
230 |
231 | Q_INVOKABLE virtual QJSValue create() = 0;
232 |
233 | QJSEngine *getJsEngine() const;
234 | ...
235 | };
236 | ```
237 |
238 | 然后重新实例化 `QmlThreadHelperFactory`,`QJSValue QmlThreadHelperFactory::create()` 是纯虚函数,需要继承后提供一个实现。
239 |
240 | 由于是将 `QmlThreadHelper::factoryName` 和 `QmlThreadHelper::helperFactory` 一起绑定,设置到 `JSEngine` 中,所以使用的时候就如下:
241 |
242 | ```
243 | var helper = HelperFactory.create()
244 | ```
245 |
246 | 上述的 `HelperFactory` 其实是 `HelperFactory`,通过调用 `create()` 返回一个 `QJSValue` 到 `JSEngine` 的运行上下文。
247 |
248 |
--------------------------------------------------------------------------------
/old_code/QmlThread/src/qmlthread/qmlthread_p.h:
--------------------------------------------------------------------------------
1 | #ifndef QMLTHREAD_P_H
2 | #define QMLTHREAD_P_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | #include "qmlthreadengine.h"
15 |
16 | class Worker : public QObject
17 | {
18 | Q_OBJECT
19 | Q_PROPERTY(QJSValue onMessage READ onMessage WRITE setOnMessage NOTIFY onMessageChanged)
20 | public:
21 | Worker(QObject *parent=0):
22 | QObject(parent),
23 | jsEngine(new QmlThreadEngine(this))
24 | {
25 | this->init();
26 |
27 | connect(this, SIGNAL(sendEvaluateToEngine(QString,QString,int)),
28 | this, SLOT(onSendEvaluateToEngine(QString,QString,int)) );
29 |
30 | connect(this, SIGNAL(registerHelper(QString,QmlThreadHelper*)),
31 | jsEngine, SIGNAL(registerHelper(QString,QmlThreadHelper*)) );
32 | }
33 |
34 | ~Worker()
35 | {
36 | }
37 |
38 | QJSValue onMessage() const
39 | {
40 | return m_onMessage;
41 | }
42 |
43 | void setOnMessage(const QJSValue &onMessage)
44 | {
45 | m_onMessage = onMessage;
46 | Q_EMIT onMessageChanged();
47 | }
48 |
49 | void clear()
50 | {
51 | this->init();
52 | }
53 |
54 | QmlThreadEngine *getJsEngine()
55 | { return jsEngine; }
56 |
57 | Q_SIGNALS:
58 | void onMessageChanged();
59 | void sendMessageToThread(QJsonValue messageObject);
60 | void finished();
61 | void sendEvaluateToEngine(const QString &program, const QString &fileName,
62 | int lineNumber);
63 | void registerHelper(const QString& helperName, QmlThreadHelper* threadHelper);
64 | void unregisterHelper(const QString& helperName);
65 | void error(const QString& errorString);
66 |
67 | public Q_SLOTS:
68 | void doWork(const QJsonValue& message)
69 | {
70 | //#ifdef QT_DEBUG
71 | // qDebug() << Q_FUNC_INFO;
72 | //#endif
73 | QJSValue result = this->work(message);
74 | if(result.isError()) {
75 | qDebug() << result.property("stack").toString() + ":"
76 | << result.property("lineNumber").toInt()
77 | << ":" << result.toString();
78 | } else {
79 | Q_EMIT finished();
80 | }
81 | }
82 |
83 | protected:
84 | void init()
85 | {
86 | jsEngine->setContextProperty("Worker", jsEngine->newQObject(this));
87 | setOnMessage(jsEngine->evaluate("(function(msg){\n"
88 | " console.log('this is default onMessage!');\n"
89 | "})\n"));
90 | }
91 |
92 | QJSValue work(const QJsonValue& message)
93 | {
94 | QJsonDocument jsonDocument;
95 | if(message.isArray()) {
96 | jsonDocument.setArray(message.toArray());
97 | } else if(message.isObject()) {
98 | jsonDocument.setObject(message.toObject());
99 | }
100 | QJSValue msg = jsEngine->evaluate("(JSON.parse('"
101 | +jsonDocument.toJson(QJsonDocument::Compact)
102 | +"'));");
103 | QJSValueList args;
104 | args << msg;
105 | return m_onMessage.call(args);
106 | }
107 |
108 | private Q_SLOTS:
109 | void onSendEvaluateToEngine(const QString &program, const QString &fileName, int lineNumber)
110 | {
111 | QJSValue result = this->getJsEngine()->evaluate(program, fileName, lineNumber);
112 | if(result.isError()) {
113 | qDebug() << result.property("stack").toString()
114 | << result.property("fileName").toString()
115 | << result.property("lineNumber").toInt()
116 | << ":" << result.toString();
117 | Q_EMIT error(fileName
118 | + ":"
119 | + result.property("lineNumber").toString()
120 | + result.toString()
121 | );
122 | }
123 | }
124 |
125 | private:
126 | QmlThreadEngine *jsEngine;
127 | QJSValue m_onMessage;
128 | };
129 |
130 | ///////////////////////////////////////////////////////////
131 |
132 | class QmlThreadPrivate : public QObject
133 | {
134 | Q_OBJECT
135 | public:
136 | QmlThreadPrivate(QObject* parent):
137 | QObject(parent),
138 | worker(new Worker)
139 | {
140 | worker->moveToThread(&thread);
141 |
142 | connect(this, &QmlThreadPrivate::startWork,
143 | worker, &Worker::doWork);
144 |
145 | connect(worker, &Worker::sendMessageToThread,
146 | this, &QmlThreadPrivate::sendMessageToThread);
147 |
148 | connect(worker, &Worker::finished,
149 | this, &QmlThreadPrivate::finished);
150 |
151 | connect(worker, &Worker::error,
152 | this, &QmlThreadPrivate::error);
153 |
154 | connect(this, &QmlThreadPrivate::registerHelper,
155 | worker, &Worker::registerHelper);
156 |
157 | connect(this, &QmlThreadPrivate::unregisterHelper,
158 | worker, &Worker::unregisterHelper);
159 |
160 | thread.start();
161 | }
162 |
163 | ~QmlThreadPrivate()
164 | {
165 | thread.quit();
166 | // TODO
167 | thread.wait(10);
168 | worker->deleteLater();
169 | }
170 |
171 | QString executeString(const QString& sourceFile) {
172 | QString fileName = sourceFile;
173 | if(fileName.contains("qrc")) {
174 | fileName.remove("qrc");
175 | }
176 | QFile file(fileName);
177 | if(!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
178 | // qDebug() << Q_FUNC_INFO << file.errorString()
179 | // << sourceFile;
180 | return QString();
181 | }
182 | return QString(file.readAll());
183 | }
184 |
185 | QUrl getSource() const
186 | { return source; }
187 |
188 | void setSource(const QUrl &value)
189 | {
190 | source = value;
191 | worker->clear();
192 | // here use signal handle
193 | worker->sendEvaluateToEngine(executeString(source.toString()),
194 | source.toString(),
195 | 1);
196 | }
197 |
198 | bool isFinished() const
199 | { return thread.isFinished(); }
200 |
201 | bool isRunning() const
202 | { return thread.isRunning(); }
203 |
204 | Q_SIGNALS:
205 | void finished();
206 | void error(const QString& errorString);
207 | void startWork(QJsonValue message);
208 | void sendMessageToThread(QJsonValue message);
209 | void registerHelper(const QString& helperName, QmlThreadHelper* threadHelper);
210 | void unregisterHelper(const QString& helperName);
211 |
212 | private:
213 | QUrl source;
214 | QThread thread;
215 | Worker* worker;
216 | };
217 |
218 |
219 | #endif // QMLTHREAD_P_H
220 |
--------------------------------------------------------------------------------