From 01e9a424821d34723d22b8c3989eff98611a868e Mon Sep 17 00:00:00 2001 From: mrbesen Date: Sun, 27 Aug 2023 16:51:02 +0200 Subject: [PATCH] add loading screen; make RestClient a QObject --- include/blitzapi.h | 2 ++ include/clientapi.h | 2 ++ include/datadragon.h | 21 ++++++++++++-- include/loadingwindow.h | 36 ++++++++++++++++++++++++ include/mainwindow.h | 6 +++- include/restclient.h | 10 +++++-- lolautoaccept.pro | 3 ++ src/datadragon.cpp | 43 +++++++++++++++++++++------- src/loadingwindow.cpp | 27 ++++++++++++++++++ src/main.cpp | 5 +--- src/mainwindow.cpp | 62 +++++++++++++++++++++++++++-------------- ui/loadingwindow.ui | 47 +++++++++++++++++++++++++++++++ ui/mainwindow.ui | 4 +++ 13 files changed, 226 insertions(+), 42 deletions(-) create mode 100644 include/loadingwindow.h create mode 100644 src/loadingwindow.cpp create mode 100644 ui/loadingwindow.ui diff --git a/include/blitzapi.h b/include/blitzapi.h index 5e25baa..83cd189 100644 --- a/include/blitzapi.h +++ b/include/blitzapi.h @@ -8,6 +8,8 @@ #include "position.h" class BlitzAPI : public RestClient { + Q_OBJECT + public: BlitzAPI(); diff --git a/include/clientapi.h b/include/clientapi.h index 3365351..cfae024 100644 --- a/include/clientapi.h +++ b/include/clientapi.h @@ -10,6 +10,8 @@ #include "runestyle.h" class ClientAPI : public RestClient { + Q_OBJECT + public: enum class ReadyCheckState : uint32_t { INVALID = 0, diff --git a/include/datadragon.h b/include/datadragon.h index eda7b33..3720709 100644 --- a/include/datadragon.h +++ b/include/datadragon.h @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -15,7 +14,11 @@ #include "memoryimagecache.h" #include "restclient.h" +class QThread; + class DataDragon : public RestClient { + Q_OBJECT + public: using notifyImgfunc_t = std::function; @@ -56,7 +59,20 @@ public: std::vector resolveChampIDs(const std::vector& champnames); + void startThread(); + static const ChampData EMPTYCHAMP; + +public slots: + void stop(); + +signals: + // loading progress in 0.0 - 1.0 + void loading(float); + + // which champion is currently prefretched + void fetchingChamp(QString); + protected: QString getImageUrl(const QString& champid, ImageType type); QString getCDNString() const; @@ -64,7 +80,6 @@ protected: void prefetchChampImage(const QString& champid, ImageType imgtype = ImageType::SQUARE); void getVersionInternal(); void getChampsInternal(); - void startThread(); void stopThread(); void stopAndJoinThread(); void threadLoop(); @@ -90,7 +105,7 @@ private: std::list tasks; std::mutex tasksmutex; std::condition_variable tasksnotemptycv; - std::thread bgthread; + QThread* bgthread; bool shouldrun = true; }; diff --git a/include/loadingwindow.h b/include/loadingwindow.h new file mode 100644 index 0000000..8b095b1 --- /dev/null +++ b/include/loadingwindow.h @@ -0,0 +1,36 @@ +#ifndef LOADINGWINDOW_H +#define LOADINGWINDOW_H + +#include + +class QCloseEvent; + +namespace Ui { + class LoadingWindow; +} + +class LoadingWindow : public QWidget { + Q_OBJECT + +public: + explicit LoadingWindow( QWidget* parent = nullptr ); + ~LoadingWindow(); + +public slots: + void setChampion(QString championName); + void setText(QString text); + + // should be 0.0 to 1.0 + void setProgress(float val); + +signals: + void closed(); + +protected: + virtual void closeEvent(QCloseEvent*) override; + +private: + Ui::LoadingWindow* ui; +}; + +#endif // LOADINGWINDOW_H diff --git a/include/mainwindow.h b/include/mainwindow.h index fac174e..c1e66d5 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -14,6 +14,8 @@ QT_END_NAMESPACE class QTimer; +class LoadingWindow; + class MainWindow : public QMainWindow { Q_OBJECT @@ -32,6 +34,8 @@ public slots: void resetSaveTimer(); private slots: + void loadingStatus(float); + void toggleMainswitch(bool); void aatoggled(bool); void smitewarntoggled(bool); @@ -52,11 +56,11 @@ private: bool loading; Ui::MainWindow *ui; QTimer* saveTimer; - QTimer* initDoneTimer; std::thread lolaathread; DataDragon dd; Config conf; LolAutoAccept lolaa; + LoadingWindow* lwin; }; #endif // MAINWINDOW_H diff --git a/include/restclient.h b/include/restclient.h index 52b7abc..0c06b57 100644 --- a/include/restclient.h +++ b/include/restclient.h @@ -1,15 +1,19 @@ #pragma once -#include -#include #include +#include +#include +#include + #ifdef Q_OS_WIN #undef DELETE #endif -class RestClient { +class RestClient : public QObject { + Q_OBJECT + public: RestClient(const QString& base); RestClient(const RestClient&) = delete; diff --git a/lolautoaccept.pro b/lolautoaccept.pro index df51390..b49e083 100644 --- a/lolautoaccept.pro +++ b/lolautoaccept.pro @@ -49,6 +49,7 @@ SOURCES += \ src/datadragonimagecache.cpp \ src/files.cpp \ src/json.cpp \ + src/loadingwindow.cpp \ src/lolautoaccept.cpp \ src/main.cpp \ src/mainwindow.cpp \ @@ -84,6 +85,7 @@ HEADERS += \ include/defer.h \ include/files.h \ include/json.h \ + include/loadingwindow.h \ include/lolautoaccept.h \ include/mainwindow.h \ include/memoryimagecache.h \ @@ -102,6 +104,7 @@ HEADERS += \ FORMS += \ ui/championsearch.ui \ ui/clipboardpopup.ui \ + ui/loadingwindow.ui \ ui/mainwindow.ui \ ui/runeaspektbutton.ui \ ui/runedisplay.ui \ diff --git a/src/datadragon.cpp b/src/datadragon.cpp index 307030a..f7e3675 100644 --- a/src/datadragon.cpp +++ b/src/datadragon.cpp @@ -6,8 +6,10 @@ #include #include +#include #include #include +#include #include // std::max, champ matching @@ -17,7 +19,7 @@ static const QString BASEURL = "https://ddragon.leagueoflegends.com/"; const DataDragon::ChampData DataDragon::EMPTYCHAMP; DataDragon::DataDragon(const QString& locale) : RestClient(BASEURL), locale(locale), cache({{"square", ".png"}, {"loading", "_0.jpg"}, {"splash", "_0.jpg"}}) { - startThread(); + this->setObjectName("DataDragon"); } DataDragon::~DataDragon() { @@ -220,6 +222,23 @@ std::vector DataDragon::resolveChampIDs(const std::vector& ch return out; } +void DataDragon::startThread() { + shouldrun = true; + bgthread = QThread::create(&DataDragon::threadLoop, this); + bgthread->setObjectName("DataDragonThread"); + bgthread->start(); + this->moveToThread(bgthread); +} + +void DataDragon::stop() { + qDebug() << "stop DataDragon"; + shouldrun = false; + std::unique_lock lock(tasksmutex); + + tasks.clear(); + notDownloadedImages.clear(); // this is a possible race condition! +} + QString DataDragon::getImageUrl(const QString& champid, ImageType type) { switch(type) { case ImageType::SQUARE: { @@ -317,11 +336,6 @@ void DataDragon::getChampsInternal() { cachedatacv.notify_all(); } -void DataDragon::startThread() { - shouldrun = true; - bgthread = std::thread(&DataDragon::threadLoop, this); -} - void DataDragon::stopThread() { shouldrun = false; tasksnotemptycv.notify_all(); @@ -330,11 +344,12 @@ void DataDragon::stopThread() { void DataDragon::stopAndJoinThread() { stopThread(); - if(bgthread.joinable()) - bgthread.join(); + bgthread->wait(); } void DataDragon::threadLoop() { + QEventLoop loop; + // init version and champ list getVersionInternal(); getChampsInternal(); @@ -350,7 +365,7 @@ void DataDragon::threadLoop() { } else { Log::note << "DataDragon background thread is idleing - prefetching champion images TODO: " << notDownloadedImages.size(); - while(!notDownloadedImages.empty() && tasks.empty()) { + while(shouldrun && !notDownloadedImages.empty() && tasks.empty()) { lock.unlock(); auto it = notDownloadedImages.begin(); @@ -359,6 +374,9 @@ void DataDragon::threadLoop() { prefetchChampImage(champid, ImageType::SQUARE); + emit this->loading(1.0 - (notDownloadedImages.size() / (float) champs.size())); + emit this->fetchingChamp(champid); + lock.lock(); } @@ -368,23 +386,28 @@ void DataDragon::threadLoop() { if(!once) { once = true; Log::note << "all champs are prefetched now"; + emit this->loading( 1.0 ); } tasksnotemptycv.wait(lock); } } } + if(tasks.empty()) continue; t = tasks.front(); tasks.pop_front(); } + loop.processEvents(); + QPixmap img = getImage(t.champid, t.type); t.func(img); } -} + qDebug() << "DataDragon Thread terminated"; +} std::ostream& operator<<(std::ostream& str, const DataDragon::ChampData& cd) { return str << "[n: " << cd.name.toStdString() << " " << " k: " << cd.key << " id: " << cd.id.toStdString() << "]"; diff --git a/src/loadingwindow.cpp b/src/loadingwindow.cpp new file mode 100644 index 0000000..9af6526 --- /dev/null +++ b/src/loadingwindow.cpp @@ -0,0 +1,27 @@ +#include "loadingwindow.h" +#include "ui_loadingwindow.h" + +LoadingWindow::LoadingWindow(QWidget* parent) : QWidget(parent), ui(new Ui::LoadingWindow) { + ui->setupUi(this); +} + +LoadingWindow::~LoadingWindow() { + delete ui; +} + +void LoadingWindow::setChampion(QString championName) { + this->setText(LoadingWindow::tr("Loading Champion: %0").arg(championName)); +} + +void LoadingWindow::setText(QString text) { + ui->label->setText(text); +} + +void LoadingWindow::setProgress(float val) { + ui->progressBar->setValue(val * 100); +} + +void LoadingWindow::closeEvent(QCloseEvent* event) { + QWidget::closeEvent(event); + emit this->closed(); +} diff --git a/src/main.cpp b/src/main.cpp index eb4e082..5ca7e02 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,9 +11,9 @@ #include #include "arg.h" -#include "mainwindow.h" #include "clientaccess.h" #include "clientapi.h" +#include "mainwindow.h" int main(int argc, char** argv) { Log::init(); @@ -40,10 +40,7 @@ int main(int argc, char** argv) { qWarning() << "translation not found"; } MainWindow win; - QIcon icon(":/lolautoaccept.png"); - win.setWindowIcon(icon); - win.show(); int ret = app.exec(); Log::stop(); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index dcbbe67..8214d54 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1,14 +1,24 @@ #include "mainwindow.h" #include "ui_mainwindow.h" +#include #include #include +#include "loadingwindow.h" + MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), loading(true), ui(new Ui::MainWindow), saveTimer(new QTimer(this)), dd(QLocale().name()), - lolaa(conf.getConfig(), dd) { + lolaa(conf.getConfig(), dd), lwin(new LoadingWindow(nullptr)) { ui->setupUi(this); + QObject::connect(&dd, &DataDragon::fetchingChamp, lwin, &LoadingWindow::setChampion); + QObject::connect(&dd, &DataDragon::loading, lwin, &LoadingWindow::setProgress); + QObject::connect(&dd, &DataDragon::loading, this, &MainWindow::loadingStatus); + QObject::connect(lwin, &LoadingWindow::closed, &dd, &DataDragon::stop); + QObject::connect(lwin, &LoadingWindow::closed, qApp, &QCoreApplication::quit, Qt::ConnectionType::QueuedConnection); + dd.startThread(); + QObject::connect(&lolaa, &LolAutoAccept::statusChanged, this, &MainWindow::lolaaStatusChanged); QObject::connect(&lolaa, &LolAutoAccept::positionChanged, this, &MainWindow::onPosChange); @@ -19,19 +29,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), loading(true), ui ui->copyrightlabel->setText(ui->copyrightlabel->text().arg(LOLAA_VERSION)); conf.load(); - - // for all tabs - set their config and datadragon Config::RootConfig& rc = conf.getConfig(); - for(int32_t tabnr = 0; tabnr < ui->tabWidget->count(); ++tabnr) { - SettingsTab* tab = (SettingsTab*) ui->tabWidget->widget(tabnr); - tab->setup(*rc.getPositionConfig(tab->getPosition()), &dd); - - QObject::connect(tab, &SettingsTab::changed, this, &MainWindow::tabchanged); - QObject::connect(tab, &SettingsTab::toggled, this, &MainWindow::tabtoggled); - } - - ui->runesPage->setDataDragon(dd); - ui->runesPage->setConfig(conf); ui->enableAll->setChecked(rc.enabledAutoAccept); lolaa.setEnabled(rc.enabledAutoAccept, LolAutoAccept::State::LOBBY); @@ -45,12 +43,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), loading(true), ui resizeEvent(nullptr); - // a timer to delay the loading flag a short time until all other signals are processed - initDoneTimer = new QTimer(this); - initDoneTimer->setSingleShot(true); - initDoneTimer->setInterval(std::chrono::milliseconds(1)); // schedule for first event loop - QObject::connect(initDoneTimer, &QTimer::timeout, this, &MainWindow::initDone, Qt::QueuedConnection); - initDoneTimer->start(); + lwin->show(); } MainWindow::~MainWindow() { @@ -70,6 +63,35 @@ void MainWindow::resetSaveTimer() { qDebug() << "resetTimer"; } +void MainWindow::loadingStatus(float f) { + if(f >= 1.0 && lwin) { + // loading complete + + // for all tabs - set their config and datadragon + Config::RootConfig& rc = conf.getConfig(); + for(int32_t tabnr = 0; tabnr < ui->tabWidget->count(); ++tabnr) { + SettingsTab* tab = (SettingsTab*) ui->tabWidget->widget(tabnr); + tab->setup(*rc.getPositionConfig(tab->getPosition()), &dd); + + QObject::connect(tab, &SettingsTab::changed, this, &MainWindow::tabchanged); + QObject::connect(tab, &SettingsTab::toggled, this, &MainWindow::tabtoggled); + } + + // load runepage images + ui->runesPage->setDataDragon(dd); + ui->runesPage->setConfig(conf); + + // switch from loading window to main window + lwin->hide(); + this->show(); + lwin->deleteLater(); + lwin = nullptr; + + // a timer to delay the loading flag a short time until all other signals are processed + QTimer::singleShot(std::chrono::milliseconds(1), this, &MainWindow::initDone); + } +} + void MainWindow::toggleMainswitch(bool state) { qDebug() << "mainswitch toggled: " << state; @@ -156,8 +178,6 @@ void MainWindow::saveConfig() { void MainWindow::initDone() { loading = false; - initDoneTimer->deleteLater(); - initDoneTimer = nullptr; qDebug() << "loading done"; } diff --git a/ui/loadingwindow.ui b/ui/loadingwindow.ui new file mode 100644 index 0000000..ddefe30 --- /dev/null +++ b/ui/loadingwindow.ui @@ -0,0 +1,47 @@ + + + LoadingWindow + + + + 0 + 0 + 739 + 176 + + + + LoL-Auto-Accept + + + + :/lolautoaccept.svg:/lolautoaccept.svg + + + + + + 0 + + + Qt::Horizontal + + + + + + + Qt::AlignCenter + + + Qt::NoTextInteraction + + + + + + + + + + diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui index 03c98a2..ec84771 100644 --- a/ui/mainwindow.ui +++ b/ui/mainwindow.ui @@ -19,6 +19,10 @@ LoL-Auto-Accept + + + :/lolautoaccept.svg:/lolautoaccept.svg +