From 202d853bbea8ecb2164b9c9dc69e2129b45f05f8 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 7 Jan 2014 11:30:17 +0100 Subject: qt: Move initialization/shutdown to a thread Move AppInit2 and Shutdown to a thread. This allows a more responsive splash screen, prevents 'process does not respond' messages from the window system and will allow for showing a user friendly window while shutting down. --- src/qt/Makefile.am | 2 +- src/qt/bitcoin.cpp | 411 +++++++++++++++++++++++++++++++++++------------- src/qt/splashscreen.cpp | 2 + 3 files changed, 301 insertions(+), 114 deletions(-) diff --git a/src/qt/Makefile.am b/src/qt/Makefile.am index abd8a9f118..99fdecb03a 100644 --- a/src/qt/Makefile.am +++ b/src/qt/Makefile.am @@ -108,7 +108,7 @@ QT_MOC_CPP = moc_aboutdialog.cpp moc_addressbookpage.cpp \ BITCOIN_MM = macdockiconhandler.mm macnotificationhandler.mm -QT_MOC = intro.moc overviewpage.moc rpcconsole.moc +QT_MOC = intro.moc overviewpage.moc rpcconsole.moc bitcoin.moc QT_QRC_CPP = qrc_bitcoin.cpp QT_QRC = bitcoin.qrc diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 794d12f69b..d4a834acb6 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -30,6 +30,8 @@ #include #include #include +#include +#include #if defined(QT_STATICPLUGIN) #include @@ -52,18 +54,18 @@ Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin); // Declare meta types used for QMetaObject::invokeMethod Q_DECLARE_METATYPE(bool*) -// Need a global reference for the notifications to find the GUI -static BitcoinGUI *guiref; -static SplashScreen *splashref; +// Need a global reference for the notifications to find the GUI and splash screen +static QWeakPointer guiref; +static QWeakPointer splashref; static bool ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style) { - if(guiref) + if(!guiref.isNull()) { bool modal = (style & CClientUIInterface::MODAL); bool ret = false; // In case of modal message, use blocking connection to wait for user to click a button - QMetaObject::invokeMethod(guiref, "message", + QMetaObject::invokeMethod(guiref.data(), "message", modal ? GUIUtil::blockingGUIThreadConnection() : Qt::QueuedConnection, Q_ARG(QString, QString::fromStdString(caption)), Q_ARG(QString, QString::fromStdString(message)), @@ -81,10 +83,13 @@ static bool ThreadSafeMessageBox(const std::string& message, const std::string& static void InitMessage(const std::string &message) { - if(splashref) + if(!splashref.isNull()) { - splashref->showMessage(QString::fromStdString(message), Qt::AlignBottom|Qt::AlignHCenter, QColor(55,55,55)); - qApp->processEvents(); + QMetaObject::invokeMethod(splashref.data(), "showMessage", + Qt::QueuedConnection, + Q_ARG(QString, QString::fromStdString(message)), + Q_ARG(int, Qt::AlignBottom|Qt::AlignHCenter), + Q_ARG(QColor, QColor(55,55,55))); } LogPrintf("init message: %s\n", message.c_str()); } @@ -97,15 +102,6 @@ static std::string Translate(const char* psz) return QCoreApplication::translate("bitcoin-core", psz).toStdString(); } -/* Handle runaway exceptions. Shows a message box with the problem and quits the program. - */ -static void handleRunawayException(std::exception *e) -{ - PrintExceptionContinue(e, "Runaway exception"); - QMessageBox::critical(0, "Runaway exception", BitcoinGUI::tr("A fatal error occurred. Bitcoin can no longer continue safely and will quit.") + QString("\n\n") + QString::fromStdString(strMiscWarning)); - exit(1); -} - /** Set up translations */ static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTranslator, QTranslator &translatorBase, QTranslator &translator) { @@ -162,6 +158,265 @@ void DebugMessageHandler(QtMsgType type, const QMessageLogContext& context, cons } #endif +/** Class encapsulating Bitcoin Core startup and shutdown. + * Allows running startup and shutdown in a different thread from the UI thread. + */ +class BitcoinCore: public QObject +{ + Q_OBJECT +public: + explicit BitcoinCore(); + +public slots: + void initialize(); + void shutdown(); + +signals: + void initializeResult(int retval); + void shutdownResult(int retval); + void runawayException(const QString &message); + +private: + boost::thread_group threadGroup; + + /// Pass fatal exception message to UI thread + void handleRunawayException(std::exception *e); +}; + +/** Main Bitcoin application object */ +class BitcoinApplication: public QApplication +{ + Q_OBJECT +public: + explicit BitcoinApplication(int &argc, char **argv); + ~BitcoinApplication(); + + /// Create payment server + void createPaymentServer(); + /// Create options model + void createOptionsModel(); + /// Create main window + void createWindow(bool isaTestNet); + + /// Request core initialization + void requestInitialize(); + /// Request core shutdown + void requestShutdown(); + + /// Get process return value + int getReturnValue() { return returnValue; } + +public slots: + void initializeResult(int retval); + void shutdownResult(int retval); + /// Handle runaway exceptions. Shows a message box with the problem and quits the program. + void handleRunawayException(const QString &message); + +signals: + void requestedInitialize(); + void requestedShutdown(); + void stopThread(); + +private: + QThread *coreThread; + PaymentServer* paymentServer; + OptionsModel *optionsModel; + ClientModel *clientModel; + BitcoinGUI *window; + WalletModel *walletModel; + int returnValue; + + void startThread(); +}; + +#include "bitcoin.moc" + +BitcoinCore::BitcoinCore(): + QObject() +{ +} + +void BitcoinCore::handleRunawayException(std::exception *e) +{ + PrintExceptionContinue(e, "Runaway exception"); + emit runawayException(QString::fromStdString(strMiscWarning)); +} + +void BitcoinCore::initialize() +{ + try + { + LogPrintf("Running AppInit2 in thread\n"); + int rv = AppInit2(threadGroup); + emit initializeResult(rv); + } catch (std::exception& e) { + handleRunawayException(&e); + } catch (...) { + handleRunawayException(NULL); + } +} + +void BitcoinCore::shutdown() +{ + try + { + LogPrintf("Running Shutdown in thread\n"); + threadGroup.interrupt_all(); + threadGroup.join_all(); + Shutdown(); + LogPrintf("Shutdown finished\n"); + emit shutdownResult(1); + } catch (std::exception& e) { + handleRunawayException(&e); + } catch (...) { + handleRunawayException(NULL); + } +} + +BitcoinApplication::BitcoinApplication(int &argc, char **argv): + QApplication(argc, argv), + coreThread(0), + paymentServer(0), + optionsModel(0), + clientModel(0), + window(0), + walletModel(0), + returnValue(0) +{ + setQuitOnLastWindowClosed(false); + startThread(); +} + +BitcoinApplication::~BitcoinApplication() +{ + LogPrintf("Stopping thread\n"); + emit stopThread(); + coreThread->wait(); + LogPrintf("Stopped thread\n"); +} + +void BitcoinApplication::createPaymentServer() +{ + paymentServer = new PaymentServer(this); +} + +void BitcoinApplication::createOptionsModel() +{ + optionsModel = new OptionsModel(); +} + +void BitcoinApplication::createWindow(bool isaTestNet) +{ + window = new BitcoinGUI(isaTestNet, 0); + guiref = window; + + QTimer* pollShutdownTimer = new QTimer(window); + connect(pollShutdownTimer, SIGNAL(timeout()), window, SLOT(detectShutdown())); + pollShutdownTimer->start(200); +} + +void BitcoinApplication::startThread() +{ + coreThread = new QThread(this); + BitcoinCore *executor = new BitcoinCore(); + executor->moveToThread(coreThread); + + /* communication to and from thread */ + connect(executor, SIGNAL(initializeResult(int)), this, SLOT(initializeResult(int))); + connect(executor, SIGNAL(shutdownResult(int)), this, SLOT(shutdownResult(int))); + connect(executor, SIGNAL(runawayException(QString)), this, SLOT(handleRunawayException(QString))); + connect(this, SIGNAL(requestedInitialize()), executor, SLOT(initialize())); + connect(this, SIGNAL(requestedShutdown()), executor, SLOT(shutdown())); + /* make sure executor object is deleted in its own thread */ + connect(this, SIGNAL(stopThread()), executor, SLOT(deleteLater())); + connect(this, SIGNAL(stopThread()), coreThread, SLOT(quit())); + + coreThread->start(); +} + +void BitcoinApplication::requestInitialize() +{ + LogPrintf("Requesting initialize\n"); + emit requestedInitialize(); +} + +void BitcoinApplication::requestShutdown() +{ + LogPrintf("Requesting shutdown\n"); + window->hide(); + window->setClientModel(0); + window->removeAllWallets(); + guiref.clear(); + delete walletModel; + emit requestedShutdown(); +} + +void BitcoinApplication::initializeResult(int retval) +{ + LogPrintf("Initialization result: %i\n", retval); + /// Set exit result: 0 if successful, 1 if failure + returnValue = retval ? 0 : 1; + if(retval) + { + optionsModel->Upgrade(); // Must be done after AppInit2 + + PaymentServer::LoadRootCAs(); + paymentServer->setOptionsModel(optionsModel); + + if (!splashref.isNull()) + splashref.data()->finish(window); + + clientModel = new ClientModel(optionsModel); + window->setClientModel(clientModel); + + if(pwalletMain) + { + walletModel = new WalletModel(pwalletMain, optionsModel); + + window->addWallet("~Default", walletModel); + window->setCurrentWallet("~Default"); + + connect(walletModel, SIGNAL(coinsSent(CWallet*,SendCoinsRecipient,QByteArray)), + paymentServer, SLOT(fetchPaymentACK(CWallet*,const SendCoinsRecipient&,QByteArray))); + } + + // If -min option passed, start window minimized. + if(GetBoolArg("-min", false)) + { + window->showMinimized(); + } + else + { + window->show(); + } + + // Now that initialization/startup is done, process any command-line + // bitcoin: URIs or payment requests: + connect(paymentServer, SIGNAL(receivedPaymentRequest(SendCoinsRecipient)), + window, SLOT(handlePaymentRequest(SendCoinsRecipient))); + connect(window, SIGNAL(receivedURI(QString)), + paymentServer, SLOT(handleURIOrFile(QString))); + connect(paymentServer, SIGNAL(message(QString,QString,unsigned int)), + window, SLOT(message(QString,QString,unsigned int))); + QTimer::singleShot(100, paymentServer, SLOT(uiReady())); + + } else { + quit(); // Exit main loop + } +} + +void BitcoinApplication::shutdownResult(int retval) +{ + LogPrintf("Shutdown result: %i\n", retval); + quit(); // Exit main loop after shutdown finished +} + +void BitcoinApplication::handleRunawayException(const QString &message) +{ + QMessageBox::critical(0, "Runaway exception", BitcoinGUI::tr("A fatal error occurred. Bitcoin can no longer continue safely and will quit.") + QString("\n\n") + message); + ::exit(1); +} + #ifndef BITCOIN_QT_TEST int main(int argc, char *argv[]) { @@ -189,7 +444,7 @@ int main(int argc, char *argv[]) #endif Q_INIT_RESOURCE(bitcoin); - QApplication app(argc, argv); + BitcoinApplication app(argc, argv); #if QT_VERSION > 0x050100 // Generate high-dpi pixmaps QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); @@ -254,7 +509,7 @@ int main(int argc, char *argv[]) // Start up the payment server early, too, so impatient users that click on // bitcoin: links repeatedly have their payment requests routed to this process: - PaymentServer* paymentServer = new PaymentServer(&app); + app.createPaymentServer(); /// 8. Main GUI initialization // Install global event filter that makes sure that long tooltips can be word-wrapped @@ -266,24 +521,29 @@ int main(int argc, char *argv[]) qInstallMessageHandler(DebugMessageHandler); #endif // Load GUI settings from QSettings - OptionsModel optionsModel; + app.createOptionsModel(); // Subscribe to global signals from core uiInterface.ThreadSafeMessageBox.connect(ThreadSafeMessageBox); uiInterface.InitMessage.connect(InitMessage); uiInterface.Translate.connect(Translate); - // Show splash screen if appropriate - SplashScreen splash(QPixmap(), 0, isaTestNet); - if (GetBoolArg("-splash", true) && !GetBoolArg("-min", false)) + // Show help message immediately after parsing command-line options (for "-lang") and setting locale, + // but before showing splash screen. + if (mapArgs.count("-?") || mapArgs.count("--help")) { - splash.show(); - splash.setAutoFillBackground(true); - splashref = &splash; + GUIUtil::HelpMessageBox help; + help.showOrPrint(); + return 1; } - app.processEvents(); - app.setQuitOnLastWindowClosed(false); + if (GetBoolArg("-splash", true) && !GetBoolArg("-min", false)) + { + SplashScreen *splash = new SplashScreen(QPixmap(), 0, isaTestNet); + splash->setAttribute(Qt::WA_DeleteOnClose); + splash->show(); + splashref = splash; + } try { @@ -293,93 +553,18 @@ int main(int argc, char *argv[]) if (GUIUtil::GetStartOnSystemStartup()) GUIUtil::SetStartOnSystemStartup(true); #endif - - boost::thread_group threadGroup; - - BitcoinGUI window(isaTestNet, 0); - guiref = &window; - - QTimer* pollShutdownTimer = new QTimer(guiref); - QObject::connect(pollShutdownTimer, SIGNAL(timeout()), guiref, SLOT(detectShutdown())); - pollShutdownTimer->start(200); - - if(AppInit2(threadGroup)) - { - { - // Put this in a block, so that the Model objects are cleaned up before - // calling Shutdown(). - - optionsModel.Upgrade(); // Must be done after AppInit2 - - PaymentServer::LoadRootCAs(); - paymentServer->setOptionsModel(&optionsModel); - - if (splashref) - splash.finish(&window); - - ClientModel clientModel(&optionsModel); - window.setClientModel(&clientModel); - - WalletModel *walletModel = 0; - if(pwalletMain) - walletModel = new WalletModel(pwalletMain, &optionsModel); - - if(walletModel) - { - window.addWallet("~Default", walletModel); - window.setCurrentWallet("~Default"); - } - - // If -min option passed, start window minimized. - if(GetBoolArg("-min", false)) - { - window.showMinimized(); - } - else - { - window.show(); - } - - // Now that initialization/startup is done, process any command-line - // bitcoin: URIs or payment requests: - QObject::connect(paymentServer, SIGNAL(receivedPaymentRequest(SendCoinsRecipient)), - &window, SLOT(handlePaymentRequest(SendCoinsRecipient))); - QObject::connect(&window, SIGNAL(receivedURI(QString)), - paymentServer, SLOT(handleURIOrFile(QString))); - if(walletModel) - { - QObject::connect(walletModel, SIGNAL(coinsSent(CWallet*,SendCoinsRecipient,QByteArray)), - paymentServer, SLOT(fetchPaymentACK(CWallet*,const SendCoinsRecipient&,QByteArray))); - } - QObject::connect(paymentServer, SIGNAL(message(QString,QString,unsigned int)), - guiref, SLOT(message(QString,QString,unsigned int))); - QTimer::singleShot(100, paymentServer, SLOT(uiReady())); - - app.exec(); - - window.hide(); - window.setClientModel(0); - window.removeAllWallets(); - guiref = 0; - delete walletModel; - } - // Shutdown the core and its threads, but don't exit the GUI here - threadGroup.interrupt_all(); - threadGroup.join_all(); - Shutdown(); - } - else - { - threadGroup.interrupt_all(); - threadGroup.join_all(); - Shutdown(); - return 1; - } + app.createWindow(isaTestNet); + app.requestInitialize(); + app.exec(); + app.requestShutdown(); + app.exec(); } catch (std::exception& e) { - handleRunawayException(&e); + PrintExceptionContinue(&e, "Runaway exception"); + app.handleRunawayException(QString::fromStdString(strMiscWarning)); } catch (...) { - handleRunawayException(NULL); + PrintExceptionContinue(NULL, "Runaway exception"); + app.handleRunawayException(QString::fromStdString(strMiscWarning)); } - return 0; + return app.getReturnValue(); } #endif // BITCOIN_QT_TEST diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index 6fb834c045..0e998c6d7e 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -13,6 +13,8 @@ SplashScreen::SplashScreen(const QPixmap &pixmap, Qt::WindowFlags f, bool isTestNet) : QSplashScreen(pixmap, f) { + setAutoFillBackground(true); + // set reference point, paddings int paddingRight = 50; int paddingTop = 50; -- cgit v1.2.3 From 55fe4de96056cf7b6bdf708a2912927dc9857207 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 7 Jan 2014 16:04:47 +0100 Subject: qt: Show window while shutting down Makes it clear to the user that the application is still wrapping up and the computer should not be turned off until it is finished. --- src/qt/bitcoin.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index d4a834acb6..8bcf187ae7 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -32,6 +32,8 @@ #include #include #include +#include +#include #if defined(QT_STATICPLUGIN) #include @@ -347,17 +349,34 @@ void BitcoinApplication::requestShutdown() window->setClientModel(0); window->removeAllWallets(); guiref.clear(); + delete walletModel; + + // Show a simple window indicating shutdown status + QWidget *shutdownWindow = new QWidget(); + QVBoxLayout *layout = new QVBoxLayout(); + layout->addWidget(new QLabel( + tr("Bitcoin Core is shutting down...\n") + + tr("Do not shut down the computer until this window disappears."))); + shutdownWindow->setLayout(layout); + + // Center shutdown window at where main window was + const QPoint global = window->mapToGlobal(window->rect().center()); + shutdownWindow->move(global.x() - shutdownWindow->width() / 2, global.y() - shutdownWindow->height() / 2); + shutdownWindow->show(); + + // Request shutdown from core thread emit requestedShutdown(); } void BitcoinApplication::initializeResult(int retval) { LogPrintf("Initialization result: %i\n", retval); - /// Set exit result: 0 if successful, 1 if failure + // Set exit result: 0 if successful, 1 if failure returnValue = retval ? 0 : 1; if(retval) { + // Miscellaneous initialization after core is initialized optionsModel->Upgrade(); // Must be done after AppInit2 PaymentServer::LoadRootCAs(); -- cgit v1.2.3 From 35ecf854c084c248ad640c6af030a9d1ed726c47 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 8 Jan 2014 08:59:24 +0100 Subject: qt: Remove global references in bitcoin.cpp Remove the need for global references `guiref` and `splashref` by making the BitcoinGUI and SplashScreen classes register for the UI interface signals themselves. --- src/qt/bitcoin.cpp | 69 +++++++++++++++---------------------------------- src/qt/bitcoingui.cpp | 32 +++++++++++++++++++++++ src/qt/bitcoingui.h | 5 ++++ src/qt/splashscreen.cpp | 34 ++++++++++++++++++++++++ src/qt/splashscreen.h | 11 ++++++++ 5 files changed, 103 insertions(+), 48 deletions(-) diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 8bcf187ae7..098b17490e 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -56,43 +55,8 @@ Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin); // Declare meta types used for QMetaObject::invokeMethod Q_DECLARE_METATYPE(bool*) -// Need a global reference for the notifications to find the GUI and splash screen -static QWeakPointer guiref; -static QWeakPointer splashref; - -static bool ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style) -{ - if(!guiref.isNull()) - { - bool modal = (style & CClientUIInterface::MODAL); - bool ret = false; - // In case of modal message, use blocking connection to wait for user to click a button - QMetaObject::invokeMethod(guiref.data(), "message", - modal ? GUIUtil::blockingGUIThreadConnection() : Qt::QueuedConnection, - Q_ARG(QString, QString::fromStdString(caption)), - Q_ARG(QString, QString::fromStdString(message)), - Q_ARG(unsigned int, style), - Q_ARG(bool*, &ret)); - return ret; - } - else - { - LogPrintf("%s: %s\n", caption.c_str(), message.c_str()); - fprintf(stderr, "%s: %s\n", caption.c_str(), message.c_str()); - return false; - } -} - static void InitMessage(const std::string &message) { - if(!splashref.isNull()) - { - QMetaObject::invokeMethod(splashref.data(), "showMessage", - Qt::QueuedConnection, - Q_ARG(QString, QString::fromStdString(message)), - Q_ARG(int, Qt::AlignBottom|Qt::AlignHCenter), - Q_ARG(QColor, QColor(55,55,55))); - } LogPrintf("init message: %s\n", message.c_str()); } @@ -199,6 +163,8 @@ public: void createOptionsModel(); /// Create main window void createWindow(bool isaTestNet); + /// Create splash screen + void createSplashScreen(bool isaTestNet); /// Request core initialization void requestInitialize(); @@ -218,6 +184,7 @@ signals: void requestedInitialize(); void requestedShutdown(); void stopThread(); + void splashFinished(QWidget *window); private: QThread *coreThread; @@ -295,6 +262,10 @@ BitcoinApplication::~BitcoinApplication() emit stopThread(); coreThread->wait(); LogPrintf("Stopped thread\n"); + + delete window; + delete paymentServer; + delete optionsModel; } void BitcoinApplication::createPaymentServer() @@ -310,13 +281,20 @@ void BitcoinApplication::createOptionsModel() void BitcoinApplication::createWindow(bool isaTestNet) { window = new BitcoinGUI(isaTestNet, 0); - guiref = window; QTimer* pollShutdownTimer = new QTimer(window); connect(pollShutdownTimer, SIGNAL(timeout()), window, SLOT(detectShutdown())); pollShutdownTimer->start(200); } +void BitcoinApplication::createSplashScreen(bool isaTestNet) +{ + SplashScreen *splash = new SplashScreen(QPixmap(), 0, isaTestNet); + splash->setAttribute(Qt::WA_DeleteOnClose); + splash->show(); + connect(this, SIGNAL(splashFinished(QWidget*)), splash, SLOT(slotFinish(QWidget*))); +} + void BitcoinApplication::startThread() { coreThread = new QThread(this); @@ -348,9 +326,11 @@ void BitcoinApplication::requestShutdown() window->hide(); window->setClientModel(0); window->removeAllWallets(); - guiref.clear(); delete walletModel; + walletModel = 0; + delete clientModel; + clientModel = 0; // Show a simple window indicating shutdown status QWidget *shutdownWindow = new QWidget(); @@ -382,8 +362,7 @@ void BitcoinApplication::initializeResult(int retval) PaymentServer::LoadRootCAs(); paymentServer->setOptionsModel(optionsModel); - if (!splashref.isNull()) - splashref.data()->finish(window); + emit splashFinished(window); clientModel = new ClientModel(optionsModel); window->setClientModel(clientModel); @@ -489,6 +468,7 @@ int main(int argc, char *argv[]) // Now that QSettings are accessible, initialize translations QTranslator qtTranslatorBase, qtTranslator, translatorBase, translator; initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator); + uiInterface.Translate.connect(Translate); // Show help message immediately after parsing command-line options (for "-lang") and setting locale, // but before showing splash screen. @@ -543,9 +523,7 @@ int main(int argc, char *argv[]) app.createOptionsModel(); // Subscribe to global signals from core - uiInterface.ThreadSafeMessageBox.connect(ThreadSafeMessageBox); uiInterface.InitMessage.connect(InitMessage); - uiInterface.Translate.connect(Translate); // Show help message immediately after parsing command-line options (for "-lang") and setting locale, // but before showing splash screen. @@ -557,12 +535,7 @@ int main(int argc, char *argv[]) } if (GetBoolArg("-splash", true) && !GetBoolArg("-min", false)) - { - SplashScreen *splash = new SplashScreen(QPixmap(), 0, isaTestNet); - splash->setAttribute(Qt::WA_DeleteOnClose); - splash->show(); - splashref = splash; - } + app.createSplashScreen(isaTestNet); try { diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 6be5a64015..f8daa08c4d 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -170,10 +170,16 @@ BitcoinGUI::BitcoinGUI(bool fIsTestnet, QWidget *parent) : // Initially wallet actions should be disabled setWalletActionsEnabled(false); + + // Subscribe to notifications from core + subscribeToCoreSignals(); } BitcoinGUI::~BitcoinGUI() { + // Unsubscribe from notifications from core + unsubscribeFromCoreSignals(); + GUIUtil::saveWindowGeometry("nWindow", this); if(trayIcon) // Hide tray icon, as deleting will let it linger until quit (on Ubuntu) trayIcon->hide(); @@ -851,3 +857,29 @@ void BitcoinGUI::detectShutdown() if (ShutdownRequested()) QMetaObject::invokeMethod(QCoreApplication::instance(), "quit", Qt::QueuedConnection); } + +static bool ThreadSafeMessageBox(BitcoinGUI *gui, const std::string& message, const std::string& caption, unsigned int style) +{ + bool modal = (style & CClientUIInterface::MODAL); + bool ret = false; + // In case of modal message, use blocking connection to wait for user to click a button + QMetaObject::invokeMethod(gui, "message", + modal ? GUIUtil::blockingGUIThreadConnection() : Qt::QueuedConnection, + Q_ARG(QString, QString::fromStdString(caption)), + Q_ARG(QString, QString::fromStdString(message)), + Q_ARG(unsigned int, style), + Q_ARG(bool*, &ret)); + return ret; +} + +void BitcoinGUI::subscribeToCoreSignals() +{ + // Connect signals to client + uiInterface.ThreadSafeMessageBox.connect(boost::bind(ThreadSafeMessageBox, this, _1, _2, _3)); +} + +void BitcoinGUI::unsubscribeFromCoreSignals() +{ + // Disconnect signals from client + uiInterface.ThreadSafeMessageBox.disconnect(boost::bind(ThreadSafeMessageBox, this, _1, _2, _3)); +} diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 75c61d2a8a..f5d5bb9762 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -111,6 +111,11 @@ private: /** Enable or disable all wallet-related actions */ void setWalletActionsEnabled(bool enabled); + /** Connect core signals to GUI client */ + void subscribeToCoreSignals(); + /** Disconnect core signals from GUI client */ + void unsubscribeFromCoreSignals(); + signals: /** Signal raised when a URI was entered or dragged to the GUI */ void receivedURI(const QString &uri); diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index 0e998c6d7e..8b16496c18 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -6,6 +6,7 @@ #include "clientversion.h" #include "util.h" +#include "ui_interface.h" #include #include @@ -85,4 +86,37 @@ SplashScreen::SplashScreen(const QPixmap &pixmap, Qt::WindowFlags f, bool isTest pixPaint.end(); this->setPixmap(newPixmap); + + subscribeToCoreSignals(); +} + +SplashScreen::~SplashScreen() +{ + unsubscribeFromCoreSignals(); +} + +void SplashScreen::slotFinish(QWidget *mainWin) +{ + finish(mainWin); +} + +static void InitMessage(SplashScreen *splash, const std::string &message) +{ + QMetaObject::invokeMethod(splash, "showMessage", + Qt::QueuedConnection, + Q_ARG(QString, QString::fromStdString(message)), + Q_ARG(int, Qt::AlignBottom|Qt::AlignHCenter), + Q_ARG(QColor, QColor(55,55,55))); +} + +void SplashScreen::subscribeToCoreSignals() +{ + // Connect signals to client + uiInterface.InitMessage.connect(boost::bind(InitMessage, this, _1)); +} + +void SplashScreen::unsubscribeFromCoreSignals() +{ + // Disconnect signals from client + uiInterface.InitMessage.disconnect(boost::bind(InitMessage, this, _1)); } diff --git a/src/qt/splashscreen.h b/src/qt/splashscreen.h index 070e376c95..6bc10e60ab 100644 --- a/src/qt/splashscreen.h +++ b/src/qt/splashscreen.h @@ -15,6 +15,17 @@ class SplashScreen : public QSplashScreen public: explicit SplashScreen(const QPixmap &pixmap, Qt::WindowFlags f, bool isTestNet); + ~SplashScreen(); + +public slots: + /** Slot to call finish() method as it's not defined as slot */ + void slotFinish(QWidget *mainWin); + +private: + /** Connect core signals to splash screen */ + void subscribeToCoreSignals(); + /** Disconnect core signals to splash screen */ + void unsubscribeFromCoreSignals(); }; #endif // SPLASHSCREEN_H -- cgit v1.2.3 From 9a2305a1b3075d110f4127a3d3c38b014237968a Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 8 Jan 2014 10:45:00 +0100 Subject: qt: Stop shutdown detection timer during shutdown Stop the shutdown timer from exiting the main loop when shutdown is already in progress. Fixes seeming hanging window after typing 'stop' in debug console. Also hide the debug console during shutdown as it is useless without a core to connect to. --- src/qt/bitcoin.cpp | 5 ++++- src/qt/bitcoingui.cpp | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 098b17490e..45551657d7 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -193,6 +193,7 @@ private: ClientModel *clientModel; BitcoinGUI *window; WalletModel *walletModel; + QTimer *pollShutdownTimer; int returnValue; void startThread(); @@ -250,6 +251,7 @@ BitcoinApplication::BitcoinApplication(int &argc, char **argv): clientModel(0), window(0), walletModel(0), + pollShutdownTimer(0), returnValue(0) { setQuitOnLastWindowClosed(false); @@ -282,7 +284,7 @@ void BitcoinApplication::createWindow(bool isaTestNet) { window = new BitcoinGUI(isaTestNet, 0); - QTimer* pollShutdownTimer = new QTimer(window); + pollShutdownTimer = new QTimer(window); connect(pollShutdownTimer, SIGNAL(timeout()), window, SLOT(detectShutdown())); pollShutdownTimer->start(200); } @@ -326,6 +328,7 @@ void BitcoinApplication::requestShutdown() window->hide(); window->setClientModel(0); window->removeAllWallets(); + pollShutdownTimer->stop(); delete walletModel; walletModel = 0; diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index f8daa08c4d..ee27fe90a6 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -855,7 +855,11 @@ void BitcoinGUI::toggleHidden() void BitcoinGUI::detectShutdown() { if (ShutdownRequested()) - QMetaObject::invokeMethod(QCoreApplication::instance(), "quit", Qt::QueuedConnection); + { + if(rpcConsole) + rpcConsole->hide(); + qApp->quit(); + } } static bool ThreadSafeMessageBox(BitcoinGUI *gui, const std::string& message, const std::string& caption, unsigned int style) -- cgit v1.2.3