Moved QApplication instance to main Application class

This commit is contained in:
Nav
2023-05-29 22:40:11 +01:00
parent f3b64beaff
commit 659b3c3679
4 changed files with 64 additions and 105 deletions

View File

@@ -1,6 +1,7 @@
#include "Application.hpp" #include "Application.hpp"
#include <iostream> #include <iostream>
#include <QTimer>
#include <QFile> #include <QFile>
#include <QJsonDocument> #include <QJsonDocument>
#include <unistd.h> #include <unistd.h>
@@ -20,6 +21,15 @@ namespace Bloom
Application::Application(std::vector<std::string>&& arguments) Application::Application(std::vector<std::string>&& arguments)
: arguments(std::move(arguments)) : arguments(std::move(arguments))
, qtApplication(
(
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true),
#ifndef BLOOM_DEBUG_BUILD
QCoreApplication::addLibraryPath(QString::fromStdString(Services::PathService::applicationDirPath() + "/plugins")),
#endif
QApplication(this->qtApplicationArgc, this->qtApplicationArgv.data())
)
)
{} {}
int Application::run() { int Application::run() {
@@ -57,21 +67,22 @@ namespace Bloom
this->startup(); this->startup();
#ifndef EXCLUDE_INSIGHT #ifndef EXCLUDE_INSIGHT
this->insightActivationPending = this->insightConfig->activateOnStartup; if (this->insightConfig->activateOnStartup) {
#endif this->activateInsight();
// Main event loop
while (Thread::getThreadState() == ThreadState::READY) {
#ifndef EXCLUDE_INSIGHT
if (this->insightActivationPending) {
this->insightActivationPending = false;
this->startInsight();
continue;
} }
#endif #endif
this->applicationEventListener->waitAndDispatch(); /*
} * We can't run our own event loop here - we have to use Qt's event loop. But we still need to be able to
* process our events. To address this, we use a QTimer to dispatch our events on an interval.
*
* This allows us to use Qt's event loop whilst still being able to process our own events.
*/
auto* eventDispatchTimer = new QTimer(&(this->qtApplication));
QObject::connect(eventDispatchTimer, &QTimer::timeout, this, &Application::dispatchEvents);
eventDispatchTimer->start(100);
this->qtApplication.exec();
} catch (const InvalidConfig& exception) { } catch (const InvalidConfig& exception) {
Logger::error("Invalid project configuration (bloom.yaml) - " + exception.getMessage()); Logger::error("Invalid project configuration (bloom.yaml) - " + exception.getMessage());
@@ -176,6 +187,7 @@ namespace Bloom
this->stopSignalHandler(); this->stopSignalHandler();
this->saveProjectSettings(); this->saveProjectSettings();
this->qtApplication.exit(0);
Thread::threadState = ThreadState::STOPPED; Thread::threadState = ThreadState::STOPPED;
} }
@@ -498,8 +510,12 @@ namespace Bloom
} }
} }
void Application::dispatchEvents() {
this->applicationEventListener->dispatchCurrentEvents();
}
#ifndef EXCLUDE_INSIGHT #ifndef EXCLUDE_INSIGHT
void Application::startInsight() { void Application::activateInsight() {
assert(!this->insight); assert(!this->insight);
this->insight = std::make_unique<Insight>( this->insight = std::make_unique<Insight>(
@@ -507,29 +523,21 @@ namespace Bloom
this->projectConfig.value(), this->projectConfig.value(),
this->environmentConfig.value(), this->environmentConfig.value(),
this->insightConfig.value(), this->insightConfig.value(),
this->projectSettings.value().insightSettings this->projectSettings.value().insightSettings,
&(this->qtApplication)
); );
/* this->insight->activate();
* Before letting Insight occupy the main thread, process any pending events that accumulated
* during startup.
*/
this->applicationEventListener->dispatchCurrentEvents();
if (Thread::getThreadState() == ThreadState::READY) {
this->insight->run();
Logger::debug("Insight closed");
}
} }
void Application::onInsightActivationRequest(const Events::InsightActivationRequested&) { void Application::onInsightActivationRequest(const Events::InsightActivationRequested&) {
if (this->insight) { if (this->insight) {
// Insight has already been started // Insight has already been activated
this->insight->showMainWindow(); this->insight->showMainWindow();
return; return;
} }
this->insightActivationPending = true; this->activateInsight();
} }
#endif #endif
@@ -540,7 +548,7 @@ namespace Bloom
void Application::onTargetControllerThreadStateChanged(const Events::TargetControllerThreadStateChanged& event) { void Application::onTargetControllerThreadStateChanged(const Events::TargetControllerThreadStateChanged& event) {
if (event.getState() == ThreadState::STOPPED || event.getState() == ThreadState::SHUTDOWN_INITIATED) { if (event.getState() == ThreadState::STOPPED || event.getState() == ThreadState::SHUTDOWN_INITIATED) {
// TargetController has unexpectedly shutdown - it must have encountered a fatal error. // TargetController has unexpectedly shutdown.
this->shutdown(); this->shutdown();
} }
} }

View File

@@ -6,6 +6,7 @@
#include <optional> #include <optional>
#include <functional> #include <functional>
#include <QtCore/QtCore> #include <QtCore/QtCore>
#include <QApplication>
#include <thread> #include <thread>
#include "src/Helpers/Thread.hpp" #include "src/Helpers/Thread.hpp"
@@ -35,8 +36,10 @@ namespace Bloom
* This is the main entry-point of execution for the Bloom program. The methods within will run on the main * This is the main entry-point of execution for the Bloom program. The methods within will run on the main
* thread. If Insight is enabled, execution will be passed over to Insight::run() upon start up. * thread. If Insight is enabled, execution will be passed over to Insight::run() upon start up.
*/ */
class Application: public Thread class Application: public QObject, public Thread
{ {
Q_OBJECT
public: public:
static const inline VersionNumber VERSION = VersionNumber(std::string(BLOOM_VERSION)); static const inline VersionNumber VERSION = VersionNumber(std::string(BLOOM_VERSION));
@@ -52,6 +55,11 @@ namespace Bloom
private: private:
std::vector<std::string> arguments; std::vector<std::string> arguments;
std::string qtApplicationName = "Bloom";
std::array<char*, 1> qtApplicationArgv = {this->qtApplicationName.data()};
int qtApplicationArgc = 1;
QApplication qtApplication;
EventListenerPointer applicationEventListener = std::make_shared<EventListener>("ApplicationEventListener"); EventListenerPointer applicationEventListener = std::make_shared<EventListener>("ApplicationEventListener");
/** /**
@@ -238,13 +246,18 @@ namespace Bloom
*/ */
void stopDebugServer(); void stopDebugServer();
/**
* Dispatches any pending events. This function will be called periodically, via a QTimer.
*/
void dispatchEvents();
#ifndef EXCLUDE_INSIGHT #ifndef EXCLUDE_INSIGHT
/** /**
* Starts the Insight GUI. * Activate the Insight GUI.
* *
* This function should never be called more than once. * This function should never be called more than once.
*/ */
void startInsight(); void activateInsight();
/** /**
* Handles requests to start the Insight GUI. * Handles requests to start the Insight GUI.

View File

@@ -28,52 +28,24 @@ namespace Bloom
const ProjectConfig& projectConfig, const ProjectConfig& projectConfig,
const EnvironmentConfig& environmentConfig, const EnvironmentConfig& environmentConfig,
const InsightConfig& insightConfig, const InsightConfig& insightConfig,
InsightProjectSettings& insightProjectSettings InsightProjectSettings& insightProjectSettings,
QApplication* parent
) )
: eventListener(eventListener) : QObject(parent)
, eventListener(eventListener)
, projectConfig(projectConfig) , projectConfig(projectConfig)
, environmentConfig(environmentConfig) , environmentConfig(environmentConfig)
, insightConfig(insightConfig) , insightConfig(insightConfig)
, insightProjectSettings(insightProjectSettings) , insightProjectSettings(insightProjectSettings)
, application(
(
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true),
#ifndef BLOOM_DEBUG_BUILD
QCoreApplication::addLibraryPath(QString::fromStdString(Services::PathService::applicationDirPath() + "/plugins")),
#endif
QApplication(this->qtApplicationArgc, this->qtApplicationArgv.data())
)
)
{} {}
void Insight::run() {
try {
this->startup();
this->threadState = ThreadState::READY;
Logger::info("Insight ready");
this->application.exec();
} catch (const Exception& exception) {
Logger::error("Insight encountered a fatal error. See below for errors:");
Logger::error(exception.getMessage());
} catch (const std::exception& exception) {
Logger::error("Insight encountered a fatal error. See below for errors:");
Logger::error(std::string(exception.what()));
}
this->shutdown();
}
void Insight::showMainWindow() { void Insight::showMainWindow() {
this->mainWindow->show(); this->mainWindow->show();
this->mainWindow->activateWindow(); this->mainWindow->activateWindow();
} }
void Insight::startup() { void Insight::activate() {
Logger::info("Starting Insight"); Logger::info("Starting Insight");
this->threadState = ThreadState::STARTING;
this->eventListener.registerCallbackForEventType<Events::TargetExecutionStopped>( this->eventListener.registerCallbackForEventType<Events::TargetExecutionStopped>(
std::bind(&Insight::onTargetStoppedEvent, this, std::placeholders::_1) std::bind(&Insight::onTargetStoppedEvent, this, std::placeholders::_1)
@@ -116,8 +88,6 @@ namespace Bloom
throw Exception("Failed to open global stylesheet file"); throw Exception("Failed to open global stylesheet file");
} }
this->application.setStyleSheet(globalStylesheet.readAll());
qRegisterMetaType<Bloom::Targets::TargetDescriptor>(); qRegisterMetaType<Bloom::Targets::TargetDescriptor>();
qRegisterMetaType<Bloom::Targets::TargetPinDescriptor>(); qRegisterMetaType<Bloom::Targets::TargetPinDescriptor>();
qRegisterMetaType<Bloom::Targets::TargetPinState>(); qRegisterMetaType<Bloom::Targets::TargetPinState>();
@@ -168,16 +138,6 @@ namespace Bloom
QString::fromStdString(Services::PathService::resourcesDirPath() + "/fonts/Ubuntu/Ubuntu-Th.ttf") QString::fromStdString(Services::PathService::resourcesDirPath() + "/fonts/Ubuntu/Ubuntu-Th.ttf")
); );
/*
* We can't run our own event loop here - we have to use Qt's event loop. But we still need to be able to
* process our events. To address this, we use a QTimer to dispatch our events on an interval.
*
* This allows us to use Qt's event loop whilst still being able to process our own events.
*/
auto* eventDispatchTimer = new QTimer(&(this->application));
QObject::connect(eventDispatchTimer, &QTimer::timeout, this, &Insight::dispatchEvents);
eventDispatchTimer->start(100);
QObject::connect( QObject::connect(
this->mainWindow, this->mainWindow,
&InsightWindow::activatedSignal, &InsightWindow::activatedSignal,
@@ -185,6 +145,8 @@ namespace Bloom
&Insight::onInsightWindowActivated &Insight::onInsightWindowActivated
); );
this->mainWindow->setStyleSheet(globalStylesheet.readAll());
this->mainWindow->setInsightConfig(this->insightConfig); this->mainWindow->setInsightConfig(this->insightConfig);
this->mainWindow->setEnvironmentConfig(this->environmentConfig); this->mainWindow->setEnvironmentConfig(this->environmentConfig);
@@ -212,10 +174,6 @@ namespace Bloom
} }
void Insight::shutdown() { void Insight::shutdown() {
if (this->getThreadState() == ThreadState::STOPPED) {
return;
}
Logger::info("Shutting down Insight"); Logger::info("Shutting down Insight");
this->mainWindow->close(); this->mainWindow->close();
@@ -230,9 +188,6 @@ namespace Bloom
workerThread->wait(); workerThread->wait();
} }
} }
this->application.exit(0);
this->threadState = ThreadState::STOPPED;
} }
void Insight::checkBloomVersion() { void Insight::checkBloomVersion() {

View File

@@ -35,7 +35,7 @@ namespace Bloom
* *
* The Insight component occupies the Bloom's main thread. See Application::run() for more. * The Insight component occupies the Bloom's main thread. See Application::run() for more.
*/ */
class Insight: public QObject, public Thread class Insight: public QObject
{ {
Q_OBJECT Q_OBJECT
@@ -53,13 +53,14 @@ namespace Bloom
const ProjectConfig& projectConfig, const ProjectConfig& projectConfig,
const EnvironmentConfig& environmentConfig, const EnvironmentConfig& environmentConfig,
const InsightConfig& insightConfig, const InsightConfig& insightConfig,
InsightProjectSettings& insightProjectSettings InsightProjectSettings& insightProjectSettings,
QApplication* parent
); );
/** /**
* Entry point for Insight. * Entry point for Insight.
*/ */
void run(); void activate();
/** /**
* Opens main window and obtains focus. * Opens main window and obtains focus.
@@ -73,9 +74,6 @@ namespace Bloom
private: private:
static constexpr std::uint8_t INSIGHT_WORKER_COUNT = 3; static constexpr std::uint8_t INSIGHT_WORKER_COUNT = 3;
std::string qtApplicationName = "Bloom";
std::array<char*, 1> qtApplicationArgv = {this->qtApplicationName.data()};
int qtApplicationArgc = 1;
ProjectConfig projectConfig; ProjectConfig projectConfig;
EnvironmentConfig environmentConfig; EnvironmentConfig environmentConfig;
@@ -86,8 +84,6 @@ namespace Bloom
EventListener& eventListener; EventListener& eventListener;
Services::TargetControllerService targetControllerService = Services::TargetControllerService(); Services::TargetControllerService targetControllerService = Services::TargetControllerService();
QApplication application;
std::map<decltype(InsightWorker::id), std::pair<InsightWorker*, QThread*>> insightWorkersById; std::map<decltype(InsightWorker::id), std::pair<InsightWorker*, QThread*>> insightWorkersById;
InsightWindow* mainWindow = new InsightWindow( InsightWindow* mainWindow = new InsightWindow(
this->environmentConfig, this->environmentConfig,
@@ -102,8 +98,6 @@ namespace Bloom
InsightSignals* insightSignals = InsightSignals::instance(); InsightSignals* insightSignals = InsightSignals::instance();
void startup();
/** /**
* Queries the Bloom server for the latest version number. If the current version number doesn't match the * Queries the Bloom server for the latest version number. If the current version number doesn't match the
* latest version number returned by the server, we'll display a warning in the logs to instruct the user to * latest version number returned by the server, we'll display a warning in the logs to instruct the user to
@@ -111,17 +105,6 @@ namespace Bloom
*/ */
void checkBloomVersion(); void checkBloomVersion();
/**
* Dispatches any events currently in the queue.
*
* Because Insight is effectively a Qt application, we cannot use our own event loop. We must use Qt's event
* loop. We do this by utilizing a QTimer instance to call this method on an interval.
* See Insight::startup() for more.
*/
void dispatchEvents() {
this->eventListener.dispatchCurrentEvents();
};
void onInsightWindowActivated(); void onInsightWindowActivated();
void onTargetStoppedEvent(const Events::TargetExecutionStopped& event); void onTargetStoppedEvent(const Events::TargetExecutionStopped& event);
void onTargetResumedEvent(const Events::TargetExecutionResumed& event); void onTargetResumedEvent(const Events::TargetExecutionResumed& event);