From b41a0e4e05b4bc1751f207ac7673f96684764e5c Mon Sep 17 00:00:00 2001 From: johnny9 <985648+johnny9@users.noreply.github.com> Date: Fri, 31 May 2024 11:43:53 -0400 Subject: [PATCH 1/3] qml: Introduce WalletModel and loadWallet functionality When a user selects a wallet from the WalletSelect menu the wallet controller can now load the wallet data in and the name and balance will appear in the WalletBadge --- src/Makefile.qt.include | 9 ++- src/qml/bitcoin.cpp | 29 ++++++++-- src/qml/models/walletlistmodel.cpp | 14 ----- src/qml/models/walletlistmodel.h | 9 --- src/qml/models/walletqmlmodel.cpp | 36 ++++++++++++ src/qml/models/walletqmlmodel.h | 34 ++++++++++++ src/qml/pages/wallet/DesktopWallets.qml | 3 +- src/qml/pages/wallet/WalletBadge.qml | 70 +---------------------- src/qml/pages/wallet/WalletSelect.qml | 3 +- src/qml/walletcontroller.cpp | 25 --------- src/qml/walletcontroller.h | 26 --------- src/qml/walletqmlcontroller.cpp | 74 +++++++++++++++++++++++++ src/qml/walletqmlcontroller.h | 45 +++++++++++++++ 13 files changed, 225 insertions(+), 152 deletions(-) create mode 100644 src/qml/models/walletqmlmodel.cpp create mode 100644 src/qml/models/walletqmlmodel.h delete mode 100644 src/qml/walletcontroller.cpp delete mode 100644 src/qml/walletcontroller.h create mode 100644 src/qml/walletqmlcontroller.cpp create mode 100644 src/qml/walletqmlcontroller.h diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 1e27ddee98..8272a7ef54 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -44,8 +44,9 @@ QT_MOC_CPP = \ qml/models/moc_peerdetailsmodel.cpp \ qml/models/moc_peerlistsortproxy.cpp \ qml/models/moc_walletlistmodel.cpp \ + qml/models/moc_walletqmlmodel.cpp \ qml/moc_appmode.cpp \ - qml/moc_walletcontroller.cpp \ + qml/moc_walletqmlcontroller.cpp \ qt/moc_addressbookpage.cpp \ qt/moc_addresstablemodel.cpp \ qt/moc_askpassphrasedialog.cpp \ @@ -126,12 +127,13 @@ BITCOIN_QT_H = \ qml/models/peerdetailsmodel.h \ qml/models/peerlistsortproxy.h \ qml/models/walletlistmodel.h \ + qml/models/walletqmlmodel.h \ qml/appmode.h \ qml/bitcoin.h \ qml/guiconstants.h \ qml/imageprovider.h \ qml/util.h \ - qml/walletcontroller.h \ + qml/walletqmlcontroller.h \ qt/addressbookpage.h \ qt/addresstablemodel.h \ qt/askpassphrasedialog.h \ @@ -317,9 +319,10 @@ BITCOIN_QML_BASE_CPP = \ qml/models/peerdetailsmodel.cpp \ qml/models/peerlistsortproxy.cpp \ qml/models/walletlistmodel.cpp \ + qml/models/walletqmlmodel.cpp \ qml/imageprovider.cpp \ qml/util.cpp \ - qml/walletcontroller.cpp + qml/walletqmlcontroller.cpp QML_RES_FONTS = \ qml/res/fonts/Inter-Regular.otf \ diff --git a/src/qml/bitcoin.cpp b/src/qml/bitcoin.cpp index 0e5d0f9ce7..078f07e3c6 100644 --- a/src/qml/bitcoin.cpp +++ b/src/qml/bitcoin.cpp @@ -28,9 +28,10 @@ #include #include #include +#include #include #include -#include +#include #include #include #include @@ -259,8 +260,17 @@ int QmlGuiMain(int argc, char* argv[]) NodeModel node_model{*node}; InitExecutor init_executor{*node}; +#ifdef ENABLE_WALLET + WalletQmlController wallet_controller(*node); + QObject::connect(&init_executor, &InitExecutor::initializeResult, &wallet_controller, &WalletQmlController::initialize); +#endif QObject::connect(&node_model, &NodeModel::requestedInitialize, &init_executor, &InitExecutor::initialize); - QObject::connect(&node_model, &NodeModel::requestedShutdown, &init_executor, &InitExecutor::shutdown); + QObject::connect(&node_model, &NodeModel::requestedShutdown, [&] { +#ifdef ENABLE_WALLET + wallet_controller.unloadWallets(); +#endif + init_executor.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); @@ -277,8 +287,12 @@ int QmlGuiMain(int argc, char* argv[]) QObject::connect(&node_model, &NodeModel::setTimeRatioList, &chain_model, &ChainModel::setTimeRatioList); QObject::connect(&node_model, &NodeModel::setTimeRatioListInitial, &chain_model, &ChainModel::setTimeRatioListInitial); + qGuiApp->setQuitOnLastWindowClosed(false); QObject::connect(qGuiApp, &QGuiApplication::lastWindowClosed, [&] { +#ifdef ENABLE_WALLET + wallet_controller.unloadWallets(); +#endif node->startShutdown(); }); @@ -289,23 +303,22 @@ int QmlGuiMain(int argc, char* argv[]) GUIUtil::LoadFont(":/fonts/inter/regular"); GUIUtil::LoadFont(":/fonts/inter/semibold"); - WalletController wallet_controller(*node); - QQmlApplicationEngine engine; QScopedPointer network_style{NetworkStyle::instantiate(Params().GetChainType())}; assert(!network_style.isNull()); engine.addImageProvider(QStringLiteral("images"), new ImageProvider{network_style.data()}); - WalletListModel wallet_list_model{*node, nullptr}; - engine.rootContext()->setContextProperty("networkTrafficTower", &network_traffic_tower); engine.rootContext()->setContextProperty("nodeModel", &node_model); engine.rootContext()->setContextProperty("chainModel", &chain_model); engine.rootContext()->setContextProperty("peerTableModel", &peer_model); engine.rootContext()->setContextProperty("peerListModelProxy", &peer_model_sort_proxy); +#ifdef ENABLE_WALLET + WalletListModel wallet_list_model{*node, nullptr}; engine.rootContext()->setContextProperty("walletController", &wallet_controller); engine.rootContext()->setContextProperty("walletListModel", &wallet_list_model); +#endif OptionsQmlModel options_model(*node, !need_onboarding.toBool()); engine.rootContext()->setContextProperty("optionsModel", &options_model); @@ -318,6 +331,10 @@ int QmlGuiMain(int argc, char* argv[]) qmlRegisterType("org.bitcoincore.qt", 1, 0, "LineGraph"); qmlRegisterUncreatableType("org.bitcoincore.qt", 1, 0, "PeerDetailsModel", ""); +#ifdef ENABLE_WALLET + qmlRegisterUncreatableType("org.bitcoincore.qt", 1, 0, "WalletQmlModel", + "WalletQmlModel cannot be instantiated from QML"); +#endif engine.load(QUrl(QStringLiteral("qrc:///qml/pages/main.qml"))); if (engine.rootObjects().isEmpty()) { diff --git a/src/qml/models/walletlistmodel.cpp b/src/qml/models/walletlistmodel.cpp index ecf97b025a..9bc0f90eae 100644 --- a/src/qml/models/walletlistmodel.cpp +++ b/src/qml/models/walletlistmodel.cpp @@ -12,7 +12,6 @@ WalletListModel::WalletListModel(interfaces::Node& node, QObject *parent) : QAbstractListModel(parent) , m_node(node) { - setSelectedWallet("Singlesig Wallet"); } void WalletListModel::listWalletDir() @@ -32,19 +31,6 @@ void WalletListModel::listWalletDir() } } -void WalletListModel::setSelectedWallet(QString wallet_name) -{ - if (m_selected_wallet != wallet_name) { - m_selected_wallet = wallet_name; - Q_EMIT selectedWalletChanged(); - } -} - -QString WalletListModel::selectedWallet() const -{ - return m_selected_wallet; -} - int WalletListModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); diff --git a/src/qml/models/walletlistmodel.h b/src/qml/models/walletlistmodel.h index ae1451b21a..c36cb2b4fc 100644 --- a/src/qml/models/walletlistmodel.h +++ b/src/qml/models/walletlistmodel.h @@ -16,7 +16,6 @@ class Node; class WalletListModel : public QAbstractListModel { Q_OBJECT - Q_PROPERTY(QString selectedWallet READ selectedWallet WRITE setSelectedWallet NOTIFY selectedWalletChanged) public: WalletListModel(interfaces::Node& node, QObject *parent = nullptr); @@ -30,15 +29,9 @@ class WalletListModel : public QAbstractListModel QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QHash roleNames() const override; - void setSelectedWallet(QString wallet_name); - QString selectedWallet() const; - public Q_SLOTS: void listWalletDir(); -Q_SIGNALS: - void selectedWalletChanged(); - private: struct Item { QString name; @@ -48,8 +41,6 @@ public Q_SLOTS: QList m_items; interfaces::Node& m_node; - QString m_selected_wallet; - }; #endif // BITCOIN_QML_MODELS_WALLETLISTMODEL_H diff --git a/src/qml/models/walletqmlmodel.cpp b/src/qml/models/walletqmlmodel.cpp new file mode 100644 index 0000000000..3e51b5f29b --- /dev/null +++ b/src/qml/models/walletqmlmodel.cpp @@ -0,0 +1,36 @@ +// Copyright (c) 2024 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 + +WalletQmlModel::WalletQmlModel(std::unique_ptr wallet, QObject *parent) + : QObject(parent) +{ + m_wallet = std::move(wallet); +} + +WalletQmlModel::WalletQmlModel(QObject *parent) + : QObject(parent) +{ +} + +QString WalletQmlModel::balance() const +{ + if (!m_wallet) { + return "0"; + } + return BitcoinUnits::format(BitcoinUnits::Unit::BTC, m_wallet->getBalance()); +} + +QString WalletQmlModel::name() const +{ + if (!m_wallet) { + return QString(); + } + return QString::fromStdString(m_wallet->getWalletName()); +} diff --git a/src/qml/models/walletqmlmodel.h b/src/qml/models/walletqmlmodel.h new file mode 100644 index 0000000000..1c66709947 --- /dev/null +++ b/src/qml/models/walletqmlmodel.h @@ -0,0 +1,34 @@ +// Copyright (c) 2024 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_MODELS_WALLETQMLMODEL_H +#define BITCOIN_QML_MODELS_WALLETQMLMODEL_H + +#include + +#include + +class WalletQmlModel : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString name READ name NOTIFY nameChanged) + Q_PROPERTY(QString balance READ balance NOTIFY balanceChanged) + +public: + WalletQmlModel(std::unique_ptr wallet, QObject *parent = nullptr); + WalletQmlModel(QObject *parent = nullptr); + ~WalletQmlModel() = default; + + QString name() const; + QString balance() const; + +Q_SIGNALS: + void nameChanged(); + void balanceChanged(); + +private: + std::unique_ptr m_wallet; +}; + +#endif // BITCOIN_QML_MODELS_WALLETQMLMODEL_H diff --git a/src/qml/pages/wallet/DesktopWallets.qml b/src/qml/pages/wallet/DesktopWallets.qml index 59a7ac15e4..1e45d8d248 100644 --- a/src/qml/pages/wallet/DesktopWallets.qml +++ b/src/qml/pages/wallet/DesktopWallets.qml @@ -24,7 +24,8 @@ Page { leftItem: WalletBadge { implicitWidth: 154 implicitHeight: 46 - text: walletListModel.selectedWallet + text: walletController.selectedWallet.name + balance: walletController.selectedWallet.balance MouseArea { anchors.fill: parent diff --git a/src/qml/pages/wallet/WalletBadge.qml b/src/qml/pages/wallet/WalletBadge.qml index fe28d58f47..befc49eddf 100644 --- a/src/qml/pages/wallet/WalletBadge.qml +++ b/src/qml/pages/wallet/WalletBadge.qml @@ -13,70 +13,6 @@ import "../../controls" Button { id: root - function formatSatoshis(satoshis) { - var highlightColor = Theme.color.neutral9 - var zeroColor = Theme.color.neutral7 - - if (root.checked || root.hovered) { - highlightColor = zeroColor = Theme.color.orange - } - - // Convert satoshis to bitcoins - var bitcoins = satoshis / 100000000; - - // Format bitcoins to a fixed 8 decimal places string - var bitcoinStr = bitcoins.toFixed(8); - - // Split the bitcoin string into integer and fractional parts - var parts = bitcoinStr.split('.'); - - // Add spaces for every 3 digits in the integer part - parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ' '); - - // Highlight the first significant digit and all following digits in the integer part - var significantFound = false; - parts[0] = parts[0].replace(/(\d)/g, function(match) { - if (!significantFound && match !== '0') { - significantFound = true; - } - if (significantFound) { - return '' + match + ''; - } - return match; - }); - - // Add spaces for every 3 digits in the decimal part - parts[1] = parts[1].replace(/\B(?=(\d{3})+(?!\d))/g, ' '); - if (significantFound) { - parts[1] = '' + parts[1] + ''; - } else { - // Highlight the first significant digit and all following digits in the fractional part - significantFound = false; - parts[1] = parts[1].replace(/(\d)/g, function(match) { - if (!significantFound && match !== '0') { - significantFound = true; - } - if (significantFound) { - return '' + match + ''; - } - return match; - }); - } - - // Concatenate the parts back together - var formattedBitcoins = parts.join('.'); - - // Format the text with the Bitcoin symbol - var formattedText = ` ${formattedBitcoins}`; - - // Highlight zero in a different color if satoshis are zero - if (satoshis === 0) { - formattedText = `₿ 0.00`; - } - - return formattedText; - } - property color bgActiveColor: Theme.color.neutral2 property color textColor: Theme.color.neutral7 property color textHoverColor: Theme.color.orange @@ -85,17 +21,17 @@ Button { property string iconSource: "" property bool showBalance: true property bool showIcon: true + property string balance: "0.0 000 000" checkable: true hoverEnabled: AppMode.isDesktop implicitHeight: 60 - implicitWidth: 220 + implicitWidth: contentItem.width bottomPadding: 0 topPadding: 0 clip: true contentItem: RowLayout { - anchors.fill: parent anchors.leftMargin: 5 anchors.rightMargin: 5 clip: true @@ -126,7 +62,7 @@ Button { CoreText { id: balanceText visible: root.showBalance - text: formatSatoshis(12300) + text: "₿ " + root.balance color: Theme.color.neutral7 } } diff --git a/src/qml/pages/wallet/WalletSelect.qml b/src/qml/pages/wallet/WalletSelect.qml index 6c1b4af5e8..9905bc242f 100644 --- a/src/qml/pages/wallet/WalletSelect.qml +++ b/src/qml/pages/wallet/WalletSelect.qml @@ -78,11 +78,12 @@ Popup { width: 220 height: 32 text: name + checked: walletController.selectedWallet.name == name ButtonGroup.group: buttonGroup showBalance: false showIcon: false onClicked: { - walletListModel.selectedWallet = name + walletController.setSelectedWallet(name) root.close() } } diff --git a/src/qml/walletcontroller.cpp b/src/qml/walletcontroller.cpp deleted file mode 100644 index 7726bf5187..0000000000 --- a/src/qml/walletcontroller.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2024 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 - - -WalletController::WalletController(interfaces::Node& node) - : m_node(node) -{ -} - -void WalletController::createSingleSigWallet(const QString& name, const QString& passphrase) -{ - uint64_t flags = 0; - std::vector warning_message; - SecureString secure_passphrase; - flags |= wallet::WALLET_FLAG_DESCRIPTORS; - secure_passphrase.assign(passphrase.toStdString()); - m_node.walletLoader().createWallet(name.toStdString(), secure_passphrase, flags, warning_message); -} diff --git a/src/qml/walletcontroller.h b/src/qml/walletcontroller.h deleted file mode 100644 index 9258895556..0000000000 --- a/src/qml/walletcontroller.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2024 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_WALLETCONTROLLER_H -#define BITCOIN_QML_WALLETCONTROLLER_H - -#include - -#include -#include - -class WalletController : public QObject -{ - Q_OBJECT - -public: - explicit WalletController(interfaces::Node& node); - Q_INVOKABLE void createSingleSigWallet(const QString& name, const QString& passphrase); - -private: - interfaces::Node& m_node; -}; - - -#endif // BITCOIN_QML_WALLETCONTROLLER_H diff --git a/src/qml/walletqmlcontroller.cpp b/src/qml/walletqmlcontroller.cpp new file mode 100644 index 0000000000..fc25476580 --- /dev/null +++ b/src/qml/walletqmlcontroller.cpp @@ -0,0 +1,74 @@ +// Copyright (c) 2024 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 + +WalletQmlController::WalletQmlController(interfaces::Node& node, QObject *parent) + : QObject(parent) + , m_node(node) +{ + m_selected_wallet = new WalletQmlModel(parent); +} + +WalletQmlController::~WalletQmlController() +{ + if (m_handler_load_wallet) { + m_handler_load_wallet->disconnect(); + } +} + +void WalletQmlController::setSelectedWallet(QString path) +{ + std::vector warning_message; + auto wallet{m_node.walletLoader().loadWallet(path.toStdString(), warning_message)}; + if (wallet.has_value()) { + m_selected_wallet = new WalletQmlModel(std::move(wallet.value())); + m_wallets.push_back(m_selected_wallet); + Q_EMIT selectedWalletChanged(); + } +} + +WalletQmlModel* WalletQmlController::selectedWallet() const +{ + return m_selected_wallet; +} + +void WalletQmlController::unloadWallets() +{ + m_handler_load_wallet->disconnect(); + for (WalletQmlModel* wallet : m_wallets) { + delete wallet; + } + m_wallets.clear(); +} + +void WalletQmlController::handleLoadWallet(std::unique_ptr wallet) +{ + if (!m_wallets.empty()) { + QString name = QString::fromStdString(wallet->getWalletName()); + for (WalletQmlModel* wallet_model : m_wallets) { + if (wallet_model->name() == name) { + return; + } + } + } + + m_selected_wallet = new WalletQmlModel(std::move(wallet)); + m_wallets.push_back(m_selected_wallet); + Q_EMIT selectedWalletChanged(); +} + +void WalletQmlController::initialize() +{ + m_handler_load_wallet = m_node.walletLoader().handleLoadWallet([this](std::unique_ptr wallet) { + handleLoadWallet(std::move(wallet)); + }); + + auto wallets = m_node.walletLoader().getWallets(); + for (auto& wallet : wallets) { + handleLoadWallet(std::move(wallet)); + } +} diff --git a/src/qml/walletqmlcontroller.h b/src/qml/walletqmlcontroller.h new file mode 100644 index 0000000000..5dffb36f49 --- /dev/null +++ b/src/qml/walletqmlcontroller.h @@ -0,0 +1,45 @@ +// Copyright (c) 2024 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_WALLETQMLCONTROLLER_H +#define BITCOIN_QML_WALLETQMLCONTROLLER_H + +#include +#include +#include +#include + +#include +#include + +class WalletQmlController : public QObject +{ + Q_OBJECT + Q_PROPERTY(WalletQmlModel* selectedWallet READ selectedWallet NOTIFY selectedWalletChanged) + +public: + explicit WalletQmlController(interfaces::Node& node, QObject *parent = nullptr); + ~WalletQmlController(); + + Q_INVOKABLE void setSelectedWallet(QString path); + + WalletQmlModel* selectedWallet() const; + void unloadWallets(); + +Q_SIGNALS: + void selectedWalletChanged(); + +public Q_SLOTS: + void initialize(); + +private: + void handleLoadWallet(std::unique_ptr wallet); + + interfaces::Node& m_node; + WalletQmlModel* m_selected_wallet; + std::vector m_wallets; + std::unique_ptr m_handler_load_wallet; +}; + +#endif // BITCOIN_QML_WALLETQMLCONTROLLER_H From ec3e68e8ef64b2eff8dfd8a6b834175ff5db49b4 Mon Sep 17 00:00:00 2001 From: johnny9 <985648+johnny9@users.noreply.github.com> Date: Wed, 27 Nov 2024 07:33:36 -0500 Subject: [PATCH 2/3] qml: Move setSelectedWallet work to worker QThread --- src/qml/walletqmlcontroller.cpp | 33 +++++++++++++++++++++++++-------- src/qml/walletqmlcontroller.h | 3 +++ 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/qml/walletqmlcontroller.cpp b/src/qml/walletqmlcontroller.cpp index fc25476580..7502c189db 100644 --- a/src/qml/walletqmlcontroller.cpp +++ b/src/qml/walletqmlcontroller.cpp @@ -5,12 +5,22 @@ #include #include +#include + +#include WalletQmlController::WalletQmlController(interfaces::Node& node, QObject *parent) : QObject(parent) , m_node(node) + , m_selected_wallet(new WalletQmlModel(parent)) + , m_worker(new QObject) + , m_worker_thread(new QThread(this)) { - m_selected_wallet = new WalletQmlModel(parent); + m_worker->moveToThread(m_worker_thread); + m_worker_thread->start(); + QTimer::singleShot(0, m_worker, []() { + util::ThreadRename("qml-walletctrl"); + }); } WalletQmlController::~WalletQmlController() @@ -18,17 +28,24 @@ WalletQmlController::~WalletQmlController() if (m_handler_load_wallet) { m_handler_load_wallet->disconnect(); } + m_worker_thread->quit(); + m_worker_thread->wait(); + delete m_worker; } void WalletQmlController::setSelectedWallet(QString path) { - std::vector warning_message; - auto wallet{m_node.walletLoader().loadWallet(path.toStdString(), warning_message)}; - if (wallet.has_value()) { - m_selected_wallet = new WalletQmlModel(std::move(wallet.value())); - m_wallets.push_back(m_selected_wallet); - Q_EMIT selectedWalletChanged(); - } + QTimer::singleShot(0, m_worker, [this, path = path.toStdString()]() { + std::vector warning_message; + auto wallet{m_node.walletLoader().loadWallet(path, warning_message)}; + if (wallet.has_value()) { + auto wallet_model = new WalletQmlModel(std::move(wallet.value())); + wallet_model->moveToThread(this->thread()); + m_selected_wallet = wallet_model; + m_wallets.push_back(m_selected_wallet); + Q_EMIT selectedWalletChanged(); + } + }); } WalletQmlModel* WalletQmlController::selectedWallet() const diff --git a/src/qml/walletqmlcontroller.h b/src/qml/walletqmlcontroller.h index 5dffb36f49..fb15debd07 100644 --- a/src/qml/walletqmlcontroller.h +++ b/src/qml/walletqmlcontroller.h @@ -11,6 +11,7 @@ #include #include +#include #include class WalletQmlController : public QObject @@ -38,6 +39,8 @@ public Q_SLOTS: interfaces::Node& m_node; WalletQmlModel* m_selected_wallet; + QObject* m_worker; + QThread* m_worker_thread; std::vector m_wallets; std::unique_ptr m_handler_load_wallet; }; From 50c5f87552b6bf9d56840b91cdf0ea58f1ec862d Mon Sep 17 00:00:00 2001 From: johnny9 <985648+johnny9@users.noreply.github.com> Date: Wed, 27 Nov 2024 13:47:29 -0500 Subject: [PATCH 3/3] qml: Protect m_wallets in WalletQmlController with QMutex --- src/qml/walletqmlcontroller.cpp | 27 +++++++++++++++++---------- src/qml/walletqmlcontroller.h | 2 ++ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/qml/walletqmlcontroller.cpp b/src/qml/walletqmlcontroller.cpp index 7502c189db..25941cb3e2 100644 --- a/src/qml/walletqmlcontroller.cpp +++ b/src/qml/walletqmlcontroller.cpp @@ -41,8 +41,11 @@ void WalletQmlController::setSelectedWallet(QString path) if (wallet.has_value()) { auto wallet_model = new WalletQmlModel(std::move(wallet.value())); wallet_model->moveToThread(this->thread()); - m_selected_wallet = wallet_model; - m_wallets.push_back(m_selected_wallet); + { + QMutexLocker locker(&m_wallets_mutex); + m_selected_wallet = wallet_model; + m_wallets.push_back(m_selected_wallet); + } Q_EMIT selectedWalletChanged(); } }); @@ -56,6 +59,7 @@ WalletQmlModel* WalletQmlController::selectedWallet() const void WalletQmlController::unloadWallets() { m_handler_load_wallet->disconnect(); + QMutexLocker locker(&m_wallets_mutex); for (WalletQmlModel* wallet : m_wallets) { delete wallet; } @@ -64,17 +68,20 @@ void WalletQmlController::unloadWallets() void WalletQmlController::handleLoadWallet(std::unique_ptr wallet) { - if (!m_wallets.empty()) { - QString name = QString::fromStdString(wallet->getWalletName()); - for (WalletQmlModel* wallet_model : m_wallets) { - if (wallet_model->name() == name) { - return; + { + QMutexLocker locker(&m_wallets_mutex); + if (!m_wallets.empty()) { + QString name = QString::fromStdString(wallet->getWalletName()); + for (WalletQmlModel* wallet_model : m_wallets) { + if (wallet_model->name() == name) { + return; + } } } - } - m_selected_wallet = new WalletQmlModel(std::move(wallet)); - m_wallets.push_back(m_selected_wallet); + m_selected_wallet = new WalletQmlModel(std::move(wallet)); + m_wallets.push_back(m_selected_wallet); + } Q_EMIT selectedWalletChanged(); } diff --git a/src/qml/walletqmlcontroller.h b/src/qml/walletqmlcontroller.h index fb15debd07..bd2e16d531 100644 --- a/src/qml/walletqmlcontroller.h +++ b/src/qml/walletqmlcontroller.h @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -41,6 +42,7 @@ public Q_SLOTS: WalletQmlModel* m_selected_wallet; QObject* m_worker; QThread* m_worker_thread; + QMutex m_wallets_mutex; std::vector m_wallets; std::unique_ptr m_handler_load_wallet; };