From 8a174cba58bcdd7bb860d262219e8363b3c139c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Sun, 26 Sep 2021 15:16:19 +0100 Subject: [PATCH 1/5] qml: Decouple InitExecutor from NodeModel --- src/qml/bitcoin.cpp | 8 ++------ src/qml/nodemodel.cpp | 10 ---------- src/qml/nodemodel.h | 5 ----- src/qml/pages/stub.qml | 8 +++++++- 4 files changed, 9 insertions(+), 22 deletions(-) diff --git a/src/qml/bitcoin.cpp b/src/qml/bitcoin.cpp index 5e035677dd..dc7a40ce9e 100644 --- a/src/qml/bitcoin.cpp +++ b/src/qml/bitcoin.cpp @@ -159,16 +159,11 @@ int QmlGuiMain(int argc, char* argv[]) NodeModel node_model{*node}; InitExecutor init_executor{*node}; - QObject::connect(&node_model, &NodeModel::requestedInitialize, &init_executor, &InitExecutor::initialize); - QObject::connect(&node_model, &NodeModel::requestedShutdown, &init_executor, &InitExecutor::shutdown); - QObject::connect(&init_executor, &InitExecutor::initializeResult, &node_model, &NodeModel::initializeResult); - QObject::connect(&init_executor, &InitExecutor::shutdownResult, qGuiApp, &QGuiApplication::quit, Qt::QueuedConnection); - // QObject::connect(&init_executor, &InitExecutor::runawayException, &node_model, &NodeModel::handleRunawayException); qGuiApp->setQuitOnLastWindowClosed(false); QObject::connect(qGuiApp, &QGuiApplication::lastWindowClosed, [&] { node->startShutdown(); - node_model.startNodeShutdown(); + init_executor.shutdown(); }); GUIUtil::LoadFont(":/fonts/inter/regular"); @@ -180,6 +175,7 @@ int QmlGuiMain(int argc, char* argv[]) assert(!network_style.isNull()); engine.addImageProvider(QStringLiteral("images"), new ImageProvider{network_style.data()}); + engine.rootContext()->setContextProperty("initExecutor", &init_executor); engine.rootContext()->setContextProperty("nodeModel", &node_model); engine.load(QUrl(QStringLiteral("qrc:///qml/pages/stub.qml"))); diff --git a/src/qml/nodemodel.cpp b/src/qml/nodemodel.cpp index 45601791df..d4679320d9 100644 --- a/src/qml/nodemodel.cpp +++ b/src/qml/nodemodel.cpp @@ -23,16 +23,6 @@ void NodeModel::setBlockTipHeight(int new_height) } } -void NodeModel::startNodeInitializionThread() -{ - Q_EMIT requestedInitialize(); -} - -void NodeModel::startNodeShutdown() -{ - Q_EMIT requestedShutdown(); -} - void NodeModel::initializeResult([[maybe_unused]] bool success, interfaces::BlockAndHeaderTipInfo tip_info) { // TODO: Handle the `success` parameter, diff --git a/src/qml/nodemodel.h b/src/qml/nodemodel.h index b3190a0199..15b346a63e 100644 --- a/src/qml/nodemodel.h +++ b/src/qml/nodemodel.h @@ -28,16 +28,11 @@ class NodeModel : public QObject int blockTipHeight() const { return m_block_tip_height; } void setBlockTipHeight(int new_height); - Q_INVOKABLE void startNodeInitializionThread(); - void startNodeShutdown(); - public Q_SLOTS: void initializeResult(bool success, interfaces::BlockAndHeaderTipInfo tip_info); Q_SIGNALS: void blockTipHeightChanged(); - void requestedInitialize(); - void requestedShutdown(); private: // Properties that are exposed to QML. diff --git a/src/qml/pages/stub.qml b/src/qml/pages/stub.qml index d7c6fdbdfa..d08025cdde 100644 --- a/src/qml/pages/stub.qml +++ b/src/qml/pages/stub.qml @@ -16,7 +16,13 @@ ApplicationWindow { color: "black" visible: true - Component.onCompleted: nodeModel.startNodeInitializionThread(); + Component.onCompleted: initExecutor.initialize() + + Connections { + target: initExecutor + onInitializeResult: nodeModel.initializeResult(success, tip_info) + } + ColumnLayout { anchors.centerIn: parent From 3aa399a00074e642488e36643f4b7ecad10800fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Tue, 28 Sep 2021 00:46:48 +0100 Subject: [PATCH 2/5] qt: Expose ready property in InitExecutor --- src/qt/initexecutor.cpp | 5 +++++ src/qt/initexecutor.h | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/qt/initexecutor.cpp b/src/qt/initexecutor.cpp index 24ae7ba73d..bdb6b24791 100644 --- a/src/qt/initexecutor.cpp +++ b/src/qt/initexecutor.cpp @@ -21,6 +21,11 @@ InitExecutor::InitExecutor(interfaces::Node& node) { m_context.moveToThread(&m_thread); m_thread.start(); + + connect(this, &InitExecutor::initializeResult, this, [=](bool success, interfaces::BlockAndHeaderTipInfo tip_info) { + m_ready = success; + Q_EMIT readyChanged(); + }); } InitExecutor::~InitExecutor() diff --git a/src/qt/initexecutor.h b/src/qt/initexecutor.h index 410c44fa2d..8017bc48c2 100644 --- a/src/qt/initexecutor.h +++ b/src/qt/initexecutor.h @@ -22,15 +22,19 @@ QT_END_NAMESPACE class InitExecutor : public QObject { Q_OBJECT + Q_PROPERTY(bool ready READ ready NOTIFY readyChanged) public: explicit InitExecutor(interfaces::Node& node); ~InitExecutor(); + bool ready() const { return m_ready; } + public Q_SLOTS: void initialize(); void shutdown(); Q_SIGNALS: + void readyChanged(); void initializeResult(bool success, interfaces::BlockAndHeaderTipInfo tip_info); void shutdownResult(); void runawayException(const QString& message); @@ -42,6 +46,7 @@ public Q_SLOTS: interfaces::Node& m_node; QObject m_context; QThread m_thread; + bool m_ready{false}; }; #endif // BITCOIN_QT_INITEXECUTOR_H From f3c1e6a92eaafac593696d3dba753bce896cef34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Tue, 28 Sep 2021 00:46:09 +0100 Subject: [PATCH 3/5] qml: Factor out Engine class --- src/Makefile.qt.include | 5 ++++- src/qml/bitcoin.cpp | 4 ++-- src/qml/engine.cpp | 26 ++++++++++++++++++++++++++ src/qml/engine.h | 30 ++++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 src/qml/engine.cpp create mode 100644 src/qml/engine.h diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 2f2d69847b..7da0c2504c 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -35,6 +35,7 @@ QT_FORMS_UI = \ qt/forms/transactiondescdialog.ui QT_MOC_CPP = \ + qml/moc_engine.cpp \ qml/moc_nodemodel.cpp \ qt/moc_addressbookpage.cpp \ qt/moc_addresstablemodel.cpp \ @@ -108,6 +109,7 @@ QT_QRC_LOCALE = qt/bitcoin_locale.qrc BITCOIN_QT_H = \ qml/bitcoin.h \ + qml/engine.h \ qml/imageprovider.h \ qml/nodemodel.h \ qml/util.h \ @@ -286,6 +288,7 @@ BITCOIN_QT_WALLET_CPP = \ BITCOIN_QML_BASE_CPP = \ qml/bitcoin.cpp \ + qml/engine.cpp \ qml/imageprovider.cpp \ qml/nodemodel.cpp \ qml/util.cpp @@ -444,7 +447,7 @@ ui_%.h: %.ui $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(MOC) $(DEFAULT_INCLUDES) $(QT_INCLUDES_UNSUPPRESSED) $(MOC_DEFS) $< > $@ moc_%.cpp: %.h - $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(MOC) $(DEFAULT_INCLUDES) $(QT_INCLUDES_UNSUPPRESSED) $(MOC_DEFS) $< > $@ + $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(MOC) $(DEFAULT_INCLUDES) $(QT_INCLUDES_UNSUPPRESSED) $(QT_QUICK_CFLAGS) $(MOC_DEFS) $< > $@ %.qm: %.ts @test -f $(LRELEASE) diff --git a/src/qml/bitcoin.cpp b/src/qml/bitcoin.cpp index dc7a40ce9e..7d6c61da51 100644 --- a/src/qml/bitcoin.cpp +++ b/src/qml/bitcoin.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -29,7 +30,6 @@ #include #include -#include #include #include #include @@ -169,7 +169,7 @@ int QmlGuiMain(int argc, char* argv[]) GUIUtil::LoadFont(":/fonts/inter/regular"); GUIUtil::LoadFont(":/fonts/inter/semibold"); - QQmlApplicationEngine engine; + Engine engine(*node); QScopedPointer network_style{NetworkStyle::instantiate(Params().NetworkIDString())}; assert(!network_style.isNull()); diff --git a/src/qml/engine.cpp b/src/qml/engine.cpp new file mode 100644 index 0000000000..f8bd591cec --- /dev/null +++ b/src/qml/engine.cpp @@ -0,0 +1,26 @@ +// Copyright (c) 2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include +#include + +#include + +Engine::Engine(interfaces::Node& node) + : m_node(node) +{ +} + +Engine::~Engine() +{ +} + +interfaces::Node& Engine::node(QObject* object) +{ + auto context = Assert(QQmlEngine::contextForObject(object)); + auto engine = Assert(context->engine()); + return Assert(qobject_cast(engine))->node(); +} diff --git a/src/qml/engine.h b/src/qml/engine.h new file mode 100644 index 0000000000..e8ebf31728 --- /dev/null +++ b/src/qml/engine.h @@ -0,0 +1,30 @@ +// Copyright (c) 2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_QML_ENGINE_H +#define BITCOIN_QML_ENGINE_H + +#include + +namespace interfaces { +class Node; +} + +class Engine : public QQmlApplicationEngine +{ + Q_OBJECT + +public: + explicit Engine(interfaces::Node& node); + ~Engine(); + + interfaces::Node& node() const { return m_node; } + + static interfaces::Node& node(QObject* object); + +private: + interfaces::Node& m_node; +}; + +#endif // BITCOIN_QML_ENGINE_H From 640818c563bdcf150c1161ca533b07ade3685140 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Tue, 28 Sep 2021 00:50:48 +0100 Subject: [PATCH 4/5] qml: Instantiate NodeModel in qml --- src/qml/bitcoin.cpp | 4 +-- src/qml/nodemodel.cpp | 16 ++++++------ src/qml/nodemodel.h | 15 +++++------ src/qml/pages/stub.qml | 59 ++++++++++++++++++++++++++---------------- 4 files changed, 53 insertions(+), 41 deletions(-) diff --git a/src/qml/bitcoin.cpp b/src/qml/bitcoin.cpp index 7d6c61da51..249e74887a 100644 --- a/src/qml/bitcoin.cpp +++ b/src/qml/bitcoin.cpp @@ -157,7 +157,6 @@ int QmlGuiMain(int argc, char* argv[]) handler_message_box.disconnect(); - NodeModel node_model{*node}; InitExecutor init_executor{*node}; qGuiApp->setQuitOnLastWindowClosed(false); @@ -169,6 +168,8 @@ int QmlGuiMain(int argc, char* argv[]) GUIUtil::LoadFont(":/fonts/inter/regular"); GUIUtil::LoadFont(":/fonts/inter/semibold"); + qmlRegisterType("BitcoinCore", 1, 0, "NodeModel"); + Engine engine(*node); QScopedPointer network_style{NetworkStyle::instantiate(Params().NetworkIDString())}; @@ -176,7 +177,6 @@ int QmlGuiMain(int argc, char* argv[]) engine.addImageProvider(QStringLiteral("images"), new ImageProvider{network_style.data()}); engine.rootContext()->setContextProperty("initExecutor", &init_executor); - engine.rootContext()->setContextProperty("nodeModel", &node_model); engine.load(QUrl(QStringLiteral("qrc:///qml/pages/stub.qml"))); if (engine.rootObjects().isEmpty()) { diff --git a/src/qml/nodemodel.cpp b/src/qml/nodemodel.cpp index d4679320d9..e8a23ee803 100644 --- a/src/qml/nodemodel.cpp +++ b/src/qml/nodemodel.cpp @@ -5,14 +5,14 @@ #include #include +#include #include #include -NodeModel::NodeModel(interfaces::Node& node) - : m_node{node} +NodeModel::NodeModel(QObject* parent) + : QObject(parent) { - ConnectToBlockTipSignal(); } void NodeModel::setBlockTipHeight(int new_height) @@ -23,16 +23,16 @@ void NodeModel::setBlockTipHeight(int new_height) } } -void NodeModel::initializeResult([[maybe_unused]] bool success, interfaces::BlockAndHeaderTipInfo tip_info) +void NodeModel::classBegin() { - // TODO: Handle the `success` parameter, - setBlockTipHeight(tip_info.block_height); } -void NodeModel::ConnectToBlockTipSignal() +void NodeModel::componentComplete() { + auto& node = Engine::node(this); + setBlockTipHeight(node.getNumBlocks()); assert(!m_handler_notify_block_tip); - m_handler_notify_block_tip = m_node.handleNotifyBlockTip( + m_handler_notify_block_tip = node.handleNotifyBlockTip( [this](SynchronizationState state, interfaces::BlockTip tip, double verification_progress) { setBlockTipHeight(tip.block_height); }); diff --git a/src/qml/nodemodel.h b/src/qml/nodemodel.h index 15b346a63e..d704235a00 100644 --- a/src/qml/nodemodel.h +++ b/src/qml/nodemodel.h @@ -6,30 +6,32 @@ #define BITCOIN_QML_NODEMODEL_H #include -#include #include #include +#include namespace interfaces { class Node; } /** Model for Bitcoin network client. */ -class NodeModel : public QObject +class NodeModel : public QObject, public QQmlParserStatus { Q_OBJECT + Q_INTERFACES(QQmlParserStatus) Q_PROPERTY(int blockTipHeight READ blockTipHeight NOTIFY blockTipHeightChanged) public: - explicit NodeModel(interfaces::Node& node); + explicit NodeModel(QObject* parent = nullptr); int blockTipHeight() const { return m_block_tip_height; } void setBlockTipHeight(int new_height); -public Q_SLOTS: - void initializeResult(bool success, interfaces::BlockAndHeaderTipInfo tip_info); + // QQmlParserStatus + void classBegin() override; + void componentComplete() override; Q_SIGNALS: void blockTipHeightChanged(); @@ -38,10 +40,7 @@ public Q_SLOTS: // Properties that are exposed to QML. int m_block_tip_height{0}; - interfaces::Node& m_node; std::unique_ptr m_handler_notify_block_tip; - - void ConnectToBlockTipSignal(); }; #endif // BITCOIN_QML_NODEMODEL_H diff --git a/src/qml/pages/stub.qml b/src/qml/pages/stub.qml index d08025cdde..d6ddb2e8b7 100644 --- a/src/qml/pages/stub.qml +++ b/src/qml/pages/stub.qml @@ -2,6 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +import BitcoinCore 1.0 import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.11 @@ -23,32 +24,44 @@ ApplicationWindow { onInitializeResult: nodeModel.initializeResult(success, tip_info) } + BusyIndicator { + running: !initExecutor.ready + visible: running + anchors.centerIn: parent + } - ColumnLayout { + Loader { + id: loader + active: initExecutor.ready anchors.centerIn: parent - spacing: 15 - width: 400 - Image { - Layout.alignment: Qt.AlignCenter - source: "image://images/app" - sourceSize.width: 64 - sourceSize.height: 64 - } - BlockCounter { - Layout.alignment: Qt.AlignCenter - blockHeight: nodeModel.blockTipHeight - } - ProgressIndicator { - id: indicator - Layout.fillWidth: true - progress: 0.666 - background: MouseArea { - onClicked: indicator.progress = mouseX / width + sourceComponent: ColumnLayout { + spacing: 15 + width: 400 + NodeModel { + id: nodeModel + } + Image { + Layout.alignment: Qt.AlignCenter + source: "image://images/app" + sourceSize.width: 64 + sourceSize.height: 64 + } + BlockCounter { + Layout.alignment: Qt.AlignCenter + blockHeight: nodeModel.blockTipHeight + } + ProgressIndicator { + id: indicator + Layout.fillWidth: true + progress: 0.666 + background: MouseArea { + onClicked: indicator.progress = mouseX / width + } + } + ConnectionOptions { + Layout.preferredWidth: 400 + focus: true } - } - ConnectionOptions { - Layout.preferredWidth: 400 - focus: true } } } From cf8a278e7a8bb5aaabef60b9345deff4c3d3c792 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Barbosa?= Date: Tue, 28 Sep 2021 11:19:47 +0100 Subject: [PATCH 5/5] qml: Update block tip in the gui thread --- src/qml/nodemodel.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/qml/nodemodel.cpp b/src/qml/nodemodel.cpp index e8a23ee803..07fab41898 100644 --- a/src/qml/nodemodel.cpp +++ b/src/qml/nodemodel.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -34,6 +35,8 @@ void NodeModel::componentComplete() assert(!m_handler_notify_block_tip); m_handler_notify_block_tip = node.handleNotifyBlockTip( [this](SynchronizationState state, interfaces::BlockTip tip, double verification_progress) { - setBlockTipHeight(tip.block_height); + GUIUtil::ObjectInvoke(this, [=] { + setBlockTipHeight(tip.block_height); + }); }); }