Added a graphical splashscreen (#4468)

* Based on QSplashScreen
* Shrinking the image for low resolutions (screen width <= 1280, < 1024)
* Dynamically adding Golden Cheetahs version and build id
* Showing messages related to the current loading state (including the
  current counter)
* Closing the splashscreen as close as possible to showing the MainWindow
* The image (splashscreen.svg) was created using Inkscape
This commit is contained in:
Joachim Kohlhammer
2024-04-08 16:34:55 +02:00
committed by GitHub
parent d0e536306f
commit 8718974722
10 changed files with 332 additions and 61 deletions

View File

@@ -175,7 +175,9 @@ Athlete::Athlete(Context *context, const QDir &homeDir)
connect(rideCache, SIGNAL(loadComplete()), this, SLOT(loadComplete()));
// we need to block on load complete if first (before mainwindow ready)
if (context->mainWindow->progress) loop.exec();
if (context->mainWindow->isStarting()) {
loop.exec();
}
}
void

View File

@@ -137,6 +137,8 @@ Context::Context(MainWindow *mainWindow): mainWindow(mainWindow)
isCompareIntervals = isCompareDateRanges = false;
isRunning = isPaused = false;
connect(this, SIGNAL(loadProgress(QString, double)), mainWindow, SLOT(loadProgress(QString, double)));
#ifdef GC_HAS_CLOUD_DB
cdbChartListDialog = NULL;
cdbUserMetricListDialog = NULL;

View File

@@ -82,17 +82,8 @@ ride: '{' rideelement_list '}' {
jc->api->writeRideLine(jc->item, jc->request, jc->response);
#endif
} else {
double progress= double(jc->loading++) / double(jc->cache->rides().count()) * 100.0f;
if (jc->context->mainWindow->progress) {
// percentage progress
QString m = QString("%1%").arg(progress , 0, 'f', 0);
jc->context->mainWindow->progress->setText(m);
QApplication::processEvents();
} else {
jc->context->notifyLoadProgress(jc->folder,progress);
}
jc->context->notifyLoadProgress(jc->folder,progress);
// find entry and update it
int index=jc->cache->find(&jc->item);

View File

@@ -137,7 +137,7 @@ MainWindow::MainWindow(const QDir &home)
// create a splash to keep user informed on first load
// first one in middle of display, not middle of window
setSplash(true);
setSplash();
#if defined(_MSC_VER) && defined(_WIN64)
// set dbg/stacktrace directory for Windows to the athlete directory
@@ -154,9 +154,6 @@ MainWindow::MainWindow(const QDir &home)
context->athlete = new Athlete(context, home);
currentAthleteTab = new AthleteTab(context);
// get rid of splash when currentTab is shown
clearSplash();
setWindowIcon(QIcon(":images/gc.png"));
setWindowTitle(context->athlete->home->root().dirName());
setContentsMargins(0,0,0,0);
@@ -182,10 +179,11 @@ MainWindow::MainWindow(const QDir &home)
/*----------------------------------------------------------------------
* GUI setup
*--------------------------------------------------------------------*/
if (appsettings->contains(GC_SETTINGS_MAIN_GEOM)) {
splash->showMessage(tr("Setting up GUI..."));
if (appsettings->contains(GC_SETTINGS_MAIN_GEOM)) {
restoreGeometry(appsettings->value(this, GC_SETTINGS_MAIN_GEOM).toByteArray());
restoreState(appsettings->value(this, GC_SETTINGS_MAIN_STATE).toByteArray());
} else {
} else {
AppearanceSettings defaults = GSettings::defaultAppearanceSettings();
@@ -201,6 +199,7 @@ MainWindow::MainWindow(const QDir &home)
/*----------------------------------------------------------------------
* ScopeBar as sidebar from v3.6
*--------------------------------------------------------------------*/
splash->showMessage(tr("Setting up GUI: Scopebar..."));
sidebar = new NewSideBar(context, this);
HelpWhatsThis *helpNewSideBar = new HelpWhatsThis(sidebar);
@@ -238,6 +237,7 @@ MainWindow::MainWindow(const QDir &home)
/*----------------------------------------------------------------------
* What's this Context Help
*--------------------------------------------------------------------*/
splash->showMessage(tr("Setting up GUI: Context Help..."));
// Help for the whole window
HelpWhatsThis *help = new HelpWhatsThis(this);
@@ -250,6 +250,7 @@ MainWindow::MainWindow(const QDir &home)
/*----------------------------------------------------------------------
* Toolbar
*--------------------------------------------------------------------*/
splash->showMessage(tr("Setting up GUI: Toolbar..."));
head = new GcToolBar(this);
QStyle *toolStyle = QStyleFactory::create("fusion");
@@ -394,6 +395,7 @@ MainWindow::MainWindow(const QDir &home)
/*----------------------------------------------------------------------
* Central Widget
*--------------------------------------------------------------------*/
splash->showMessage(tr("Setting up GUI: Central Widget..."));
tabbar = new DragBar(this);
tabbar->setTabsClosable(false); // use athlete view
@@ -461,6 +463,7 @@ MainWindow::MainWindow(const QDir &home)
/*----------------------------------------------------------------------
* Application Menus
*--------------------------------------------------------------------*/
splash->showMessage(tr("Setting up GUI: Application Menus..."));
#ifdef WIN32
QString menuColorString = GColor(CTOOLBAR).name();
menuBar()->setStyleSheet(QString("QMenuBar { color: black; background: %1; }"
@@ -699,6 +702,7 @@ MainWindow::MainWindow(const QDir &home)
/*----------------------------------------------------------------------
* Lets go, choose latest ride and get GUI up and running
*--------------------------------------------------------------------*/
splash->showMessage(tr("Selecting ride..."));
showTabbar(appsettings->value(NULL, GC_TABBAR, "0").toBool());
@@ -725,12 +729,14 @@ MainWindow::MainWindow(const QDir &home)
*--------------------------------------------------------------------*/
#if !defined(OPENDATA_DISABLE)
splash->showMessage(tr("Checking for udates..."));
OpenData::check(currentAthleteTab->context);
#else
fprintf(stderr, "OpenData disabled, secret not defined.\n"); fflush(stderr);
#endif
#ifdef GC_HAS_CLOUD_DB
splash->showMessage(tr("Asking for telemetry..."));
telemetryClient = new CloudDBTelemetryClient();
if (appsettings->value(NULL, GC_ALLOW_TELEMETRY, "undefined").toString() == "undefined" ) {
// ask user if storing is allowed
@@ -752,6 +758,8 @@ MainWindow::MainWindow(const QDir &home)
#endif
// get rid of splash when currentTab is shown
clearSplash();
}
@@ -760,47 +768,17 @@ MainWindow::MainWindow(const QDir &home)
*--------------------------------------------------------------------*/
void
MainWindow::setSplash(bool first)
MainWindow::setSplash()
{
// new frameless widget
splash = new QWidget(NULL);
// modal dialog with no parent so we set it up as a 'splash'
// because QSplashScreen doesn't seem to work (!!)
splash->setAttribute(Qt::WA_DeleteOnClose);
splash->setWindowFlags(splash->windowFlags() | Qt::FramelessWindowHint);
#ifdef Q_OS_LINUX
splash->setWindowFlags(splash->windowFlags() | Qt::X11BypassWindowManagerHint);
#endif
// put widgets on it
progress = new QLabel(splash);
progress->setAlignment(Qt::AlignCenter);
QHBoxLayout *l = new QHBoxLayout(splash);
l->setSpacing(0);
l->addWidget(progress);
// lets go
splash->setFixedSize(100 *dpiXFactor, 80 *dpiYFactor);
if (first) {
// middle of screen
splash->move(QGuiApplication::primaryScreen()->availableGeometry().center()-QPoint(50, 25));
} else {
// middle of mainwindow is appropriate
splash->move(geometry().center()-QPoint(50, 25));
}
splash->show();
// reset the splash counter
loading=1;
QString versionText = QString(tr("%1 - build %2")).arg(VERSION_STRING).arg(VERSION_LATEST);
splash = new SplashScreen(":images/splashscreen.png", versionText, 533, 100);
}
void
MainWindow::clearSplash()
{
progress = NULL;
splash->close();
delete splash;
splash = nullptr;
}
void
@@ -1390,6 +1368,15 @@ MainWindow::selectTrends()
setToolButtons();
}
bool
MainWindow::isStarting
() const
{
return splash != nullptr;
}
void
MainWindow::setToolButtons()
{
@@ -2645,6 +2632,18 @@ MainWindow::actionClicked(int index)
}
}
void
MainWindow::loadProgress
(QString folder, double progress)
{
Q_UNUSED(folder)
if (splash) {
splash->showMessage(QString(tr("Loading activities: %1\%")).arg(static_cast<int>(progress)));
}
}
void
MainWindow::addIntervals()
{

View File

@@ -36,6 +36,8 @@
#include "CloudDBTelemetry.h"
#endif
#include "SplashScreen.h"
#ifdef Q_OS_MAC
// What versions are supported by this SDK?
#include <AvailabilityMacros.h>
@@ -89,10 +91,6 @@ class MainWindow : public QMainWindow
void byebye() { close(); } // go bye bye for a restart
bool init; // if constructor has completed set to true
// when loading athlete
QLabel *progress;
int loading;
// currently selected tab
AthleteTab *athleteTab() { return currentAthleteTab; }
NewSideBar *newSidebar() { return sidebar; }
@@ -103,6 +101,8 @@ class MainWindow : public QMainWindow
// switch perspective
void switchPerspective(int index);
bool isStarting() const;
protected:
// used by ChooseCyclistDialog to see which athletes
@@ -121,8 +121,8 @@ class MainWindow : public QMainWindow
virtual void dropEvent(QDropEvent *);
// working with splash screens
QWidget *splash;
void setSplash(bool first=false);
SplashScreen *splash;
void setSplash();
void clearSplash();
signals:
@@ -133,7 +133,6 @@ class MainWindow : public QMainWindow
void deletedAthlete(QString);
public slots:
bool eventFilter(QObject*,QEvent*);
// GUI
@@ -145,6 +144,9 @@ class MainWindow : public QMainWindow
void support();
void actionClicked(int);
void loadProgress(QString folder, double progress);
// perspective selected
void perspectiveSelected(int index);
void perspectivesChanged(); // when the list of perspectives is updated in PerspectivesDialog
@@ -298,6 +300,7 @@ class MainWindow : public QMainWindow
private:
// when loading athlete
NewSideBar *sidebar;
AthleteView *athleteView;

86
src/Gui/SplashScreen.cpp Normal file
View File

@@ -0,0 +1,86 @@
/*
* Copyright (c) 2024 Joachim Kohlhammer (joachim.kohlhammer@gmx.de)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "SplashScreen.h"
#include <QDebug>
#include <QPixmap>
#include <QPainter>
#include <QFont>
#include <QFontMetrics>
#include <QApplication>
#include <QScreen>
SplashScreen::SplashScreen
(const QString &pixmapPath, const QString &version, int versionX, int versionY)
: QSplashScreen()
{
QPixmap pixmap(pixmapPath);
QScreen *screen = QApplication::primaryScreen();
if (screen->geometry().width() <= 1280) {
// Scale pixmap for lower screen resolutions
int targetPixmapWidth = 420;
if (screen->geometry().width() < 1024) {
targetPixmapWidth = 320;
}
int origPixmapWidth = pixmap.rect().width();
int origPixmapHeight = pixmap.rect().height();
pixmap = pixmap.scaledToWidth(targetPixmapWidth, Qt::SmoothTransformation);
int newPixmapWidth = pixmap.rect().width();
int newPixmapHeight = pixmap.rect().height();
versionX *= newPixmapWidth / static_cast<double>(origPixmapWidth);
versionY *= newPixmapHeight / static_cast<double>(origPixmapHeight);
}
QPainter painter(&pixmap);
if (version.size() > 0) {
QFont f = font();
f.setPointSize(16);
painter.setFont(f);
QFontMetrics fm(f);
QRect versionRect = fm.boundingRect(version);
painter.drawText(versionX - versionRect.width(), versionY + versionRect.height(), version);
}
setPixmap(pixmap);
show();
}
SplashScreen::~SplashScreen
()
{
close();
}
void
SplashScreen::showMessage
(const QString &msg)
{
QSplashScreen::showMessage(msg);
QApplication::processEvents();
}
void
SplashScreen::clearMessage
()
{
QSplashScreen::clearMessage();
QApplication::processEvents();
}

38
src/Gui/SplashScreen.h Normal file
View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2024 Joachim Kohlhammer (joachim.kohlhammer@gmx.de)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef SPLASHSCREEN_H
#define SPLASHSCREEN_H
#include <QSplashScreen>
class SplashScreen : public QSplashScreen
{
Q_OBJECT
public:
SplashScreen(const QString &pixmapPath, const QString &version, int versionX, int versionY);
virtual ~SplashScreen();
public slots:
void showMessage(const QString &msg);
void clearMessage();
};
#endif

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 261 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 81 KiB

View File

@@ -665,7 +665,7 @@ HEADERS += Gui/AboutDialog.h Gui/AddIntervalDialog.h Gui/AnalysisSidebar.h Gui/C
Gui/Views.h Gui/BatchProcessingDialog.h Gui/DownloadRideDialog.h Gui/ManualRideDialog.h Gui/NewSideBar.h \
Gui/MergeActivityWizard.h Gui/RideImportWizard.h Gui/SplitActivityWizard.h Gui/SolverDisplay.h Gui/MetricSelect.h \
Gui/AddTileWizard.h Gui/NavigationModel.h Gui/AthleteView.h Gui/AthleteConfigDialog.h Gui/AthletePages.h Gui/Perspective.h \
Gui/PerspectiveDialog.h
Gui/PerspectiveDialog.h Gui/SplashScreen.h
# metrics and models
HEADERS += Metrics/Banister.h Metrics/CPSolver.h Metrics/Estimator.h Metrics/ExtendedCriticalPower.h Metrics/HrZones.h Metrics/PaceZones.h \
@@ -766,7 +766,7 @@ SOURCES += Gui/AboutDialog.cpp Gui/AddIntervalDialog.cpp Gui/AnalysisSidebar.cpp
Gui/BatchProcessingDialog.cpp Gui/DownloadRideDialog.cpp Gui/ManualRideDialog.cpp Gui/EditUserMetricDialog.cpp Gui/NewSideBar.cpp \
Gui/MergeActivityWizard.cpp Gui/RideImportWizard.cpp Gui/SplitActivityWizard.cpp Gui/SolverDisplay.cpp Gui/MetricSelect.cpp \
Gui/AddTileWizard.cpp Gui/NavigationModel.cpp Gui/AthleteView.cpp Gui/AthleteConfigDialog.cpp Gui/AthletePages.cpp Gui/Perspective.cpp \
Gui/PerspectiveDialog.cpp
Gui/PerspectiveDialog.cpp Gui/SplashScreen.cpp
## Models and Metrics
SOURCES += Metrics/aBikeScore.cpp Metrics/aCoggan.cpp Metrics/AerobicDecoupling.cpp Metrics/Banister.cpp Metrics/BasicRideMetrics.cpp \