Load trophies in another thread

This commit is contained in:
msuih 2018-10-31 20:38:48 +02:00 committed by Megamouse
parent f505ac7b63
commit bf0e6ca174
2 changed files with 105 additions and 19 deletions

View file

@ -29,6 +29,7 @@
#include <QUrl> #include <QUrl>
#include <QScrollBar> #include <QScrollBar>
#include <QWheelEvent> #include <QWheelEvent>
#include <QProgressDialog>
namespace namespace
{ {
@ -321,7 +322,22 @@ trophy_manager_dialog::trophy_manager_dialog(std::shared_ptr<gui_settings> gui_s
m_game_combo->setCurrentText(m_game_table->item(m_game_table->selectedItems().first()->row(), GameColumns::GameName)->text()); m_game_combo->setCurrentText(m_game_table->item(m_game_table->selectedItems().first()->row(), GameColumns::GameName)->text());
}); });
RepaintUI(true); RepaintUI();
StartTrophyLoadThread();
}
trophy_manager_dialog::~trophy_manager_dialog()
{
if (m_thread_state != TrophyThreadState::CLOSED)
{
TrophyThreadState expected = TrophyThreadState::RUNNING;
m_thread_state.compare_exchange_strong(expected, TrophyThreadState::CLOSING);
while (m_thread_state != TrophyThreadState::CLOSED)
{
std::this_thread::yield();
}
}
} }
bool trophy_manager_dialog::LoadTrophyFolderToDB(const std::string& trop_name) bool trophy_manager_dialog::LoadTrophyFolderToDB(const std::string& trop_name)
@ -395,7 +411,7 @@ bool trophy_manager_dialog::LoadTrophyFolderToDB(const std::string& trop_name)
return true; return true;
} }
void trophy_manager_dialog::RepaintUI(bool refresh_trophies) void trophy_manager_dialog::RepaintUI()
{ {
if (m_gui_settings->GetValue(gui::m_enableUIColors).toBool()) if (m_gui_settings->GetValue(gui::m_enableUIColors).toBool())
{ {
@ -406,11 +422,6 @@ void trophy_manager_dialog::RepaintUI(bool refresh_trophies)
m_game_icon_color = gui::utils::get_label_color("gamelist_icon_background_color"); m_game_icon_color = gui::utils::get_label_color("gamelist_icon_background_color");
} }
if (refresh_trophies)
{
PopulateTrophyDB();
}
PopulateGameTable(); PopulateGameTable();
if (!restoreGeometry(m_gui_settings->GetValue(gui::tr_geometry).toByteArray())) if (!restoreGeometry(m_gui_settings->GetValue(gui::tr_geometry).toByteArray()))
@ -581,18 +592,62 @@ void trophy_manager_dialog::ShowContextMenu(const QPoint& loc)
menu->exec(globalPos); menu->exec(globalPos);
} }
void trophy_manager_dialog::PopulateTrophyDB() void trophy_manager_dialog::StartTrophyLoadThread()
{ {
m_trophies_db.clear(); auto progressDialog = new QProgressDialog(
tr("Loading trophy data, please wait..."), tr("Cancel"), 0, 1, this,
QDirIterator dir_iter(qstr(vfs::get(m_trophy_dir)), QDir::Dirs | QDir::NoDotAndDotDot); Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint);
while (dir_iter.hasNext()) progressDialog->setWindowTitle(tr("Loading trophies"));
connect(progressDialog, &QProgressDialog::canceled, [this]()
{ {
dir_iter.next(); TrophyThreadState expected = TrophyThreadState::RUNNING;
std::string dirName = sstr(dir_iter.fileName()); m_thread_state.compare_exchange_strong(expected, TrophyThreadState::CLOSING);
LOG_TRACE(GENERAL, "Loading trophy dir: %s", dirName); this->close(); // It's pointless to show an empty window
LoadTrophyFolderToDB(dirName); });
progressDialog->show();
auto trophyThread = new trophy_manager_dialog::trophy_load_thread(this);
connect(trophyThread, &QThread::finished, trophyThread, &QThread::deleteLater);
connect(trophyThread, &QThread::finished, progressDialog, &QProgressDialog::deleteLater);
connect(trophyThread, &trophy_manager_dialog::trophy_load_thread::TotalCountChanged, progressDialog, &QProgressDialog::setMaximum);
connect(trophyThread, &trophy_manager_dialog::trophy_load_thread::ProcessedCountChanged, progressDialog, &QProgressDialog::setValue);
connect(trophyThread, &trophy_manager_dialog::trophy_load_thread::FinishedSuccessfully, this, &trophy_manager_dialog::HandleRepaintUiRequest);
m_thread_state = TrophyThreadState::RUNNING;
trophyThread->start();
}
void trophy_manager_dialog::trophy_load_thread::run()
{
m_manager->m_trophies_db.clear();
QDir trophy_dir(qstr(vfs::get(m_manager->m_trophy_dir)));
const auto folder_list = trophy_dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
const int count = folder_list.count();
Q_EMIT TotalCountChanged(count);
for (int i = 0; m_manager->m_thread_state == TrophyThreadState::RUNNING && i < count; i++)
{
std::string dir_name = sstr(folder_list.value(i));
LOG_TRACE(GENERAL, "Loading trophy dir: %s", dir_name);
try
{
m_manager->LoadTrophyFolderToDB(dir_name);
}
catch (const std::exception& e)
{
// TODO: Add error checks & throws to LoadTrophyFolderToDB so that they can be caught here.
// Also add a way of showing the number of corrupted/invalid folders in UI somewhere.
LOG_ERROR(GENERAL, "Exception occurred while parsing folder %s for trophies: %s", dir_name, e.what());
}
Q_EMIT ProcessedCountChanged(i + 1);
} }
if (m_manager->m_thread_state == TrophyThreadState::RUNNING)
{
Q_EMIT FinishedSuccessfully();
}
m_manager->m_thread_state = TrophyThreadState::CLOSED;
} }
void trophy_manager_dialog::PopulateGameTable() void trophy_manager_dialog::PopulateGameTable()

View file

@ -13,6 +13,7 @@
#include <QTableWidget> #include <QTableWidget>
#include <QSlider> #include <QSlider>
#include <QSplitter> #include <QSplitter>
#include <QThread>
struct GameTrophiesData struct GameTrophiesData
{ {
@ -44,8 +45,17 @@ enum GameColumns
GameColumnsCount GameColumnsCount
}; };
enum TrophyThreadState
{
RUNNING,
CLOSING,
CLOSED
};
class trophy_manager_dialog : public QWidget class trophy_manager_dialog : public QWidget
{ {
Q_OBJECT
const QString Bronze = "Bronze"; const QString Bronze = "Bronze";
const QString Silver = "Silver"; const QString Silver = "Silver";
const QString Gold = "Gold"; const QString Gold = "Gold";
@ -53,7 +63,8 @@ class trophy_manager_dialog : public QWidget
public: public:
explicit trophy_manager_dialog(std::shared_ptr<gui_settings> gui_settings); explicit trophy_manager_dialog(std::shared_ptr<gui_settings> gui_settings);
void RepaintUI(bool refresh_trophies = false); ~trophy_manager_dialog() override;
void RepaintUI();
public Q_SLOTS: public Q_SLOTS:
void HandleRepaintUiRequest(); void HandleRepaintUiRequest();
@ -71,8 +82,8 @@ private:
*/ */
bool LoadTrophyFolderToDB(const std::string& trop_name); bool LoadTrophyFolderToDB(const std::string& trop_name);
/** Populate the trophy database */ /** Populate the trophy database (in another thread). */
void PopulateTrophyDB(); void StartTrophyLoadThread();
/** Fills game table with information. /** Fills game table with information.
Takes results from LoadTrophyFolderToDB and puts it into the UI. Takes results from LoadTrophyFolderToDB and puts it into the UI.
@ -99,6 +110,9 @@ private:
QTableWidget* m_trophy_table; //! UI element to display trophy stuff. QTableWidget* m_trophy_table; //! UI element to display trophy stuff.
QTableWidget* m_game_table; //! UI element to display games. QTableWidget* m_game_table; //! UI element to display games.
class trophy_load_thread; //Qt cannot parse nested classes, declaration is below
std::atomic<TrophyThreadState> m_thread_state = TrophyThreadState::CLOSED;
bool m_show_hidden_trophies = false; bool m_show_hidden_trophies = false;
bool m_show_unlocked_trophies = true; bool m_show_unlocked_trophies = true;
bool m_show_locked_trophies = true; bool m_show_locked_trophies = true;
@ -118,3 +132,20 @@ private:
QSlider* m_game_icon_slider = nullptr; QSlider* m_game_icon_slider = nullptr;
QColor m_game_icon_color; QColor m_game_icon_color;
}; };
class trophy_manager_dialog::trophy_load_thread : public QThread
{
Q_OBJECT
public:
explicit trophy_load_thread(trophy_manager_dialog *manager) : m_manager(manager) {}
void run() override;
Q_SIGNALS:
void TotalCountChanged(int count);
void ProcessedCountChanged(int processed);
void FinishedSuccessfully();
private:
trophy_manager_dialog *m_manager;
};