├── .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 | --------------------------------------------------------------------------------