diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index f7625843dfb44f..7db77634ea6903 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -504,6 +504,20 @@ namespace GUIUtil return string.split(separator, QString::SkipEmptyParts); #endif } + + /** + * Queue a function to run in an object's event loop. This can be + * replaced by a call to the QMetaObject::invokeMethod functor overload after Qt 5.10, but + * for now use a QObject::connect for compatibility with older Qt versions, based on + * https://stackoverflow.com/questions/21646467/how-to-execute-a-functor-or-a-lambda-in-a-given-thread-in-qt-gcd-style + */ + template + void ObjectInvoke(QObject* object, Fn&& function, Qt::ConnectionType connection = Qt::QueuedConnection) + { + QObject source; + QObject::connect(&source, &QObject::destroyed, object, std::forward(function), connection); + } + } // namespace GUIUtil #endif // BITCOIN_QT_GUIUTIL_H diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp index bf38880d9804a2..28d51783631bee 100644 --- a/src/qt/walletcontroller.cpp +++ b/src/qt/walletcontroller.cpp @@ -129,10 +129,20 @@ WalletModel* WalletController::getOrCreateWallet(std::unique_ptrmoveToThread(thread()); - wallet_model->setParent(this); + // setParent(parent) must be called in the thread which created the parent object. More details in #18948. + GUIUtil::ObjectInvoke(this, [wallet_model, this] { + wallet_model->setParent(this); + }, GUIUtil::blockingGUIThreadConnection()); + m_wallets.push_back(wallet_model); // WalletModel::startPollBalance needs to be called in a thread managed by @@ -158,7 +168,6 @@ WalletModel* WalletController::getOrCreateWallet(std::unique_ptr