mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
Cloud Auto Download - 3 of 3
.. Gui notifications of background activity .. a small notification area at the bottom of the analysis sidebar shows checking and download status. .. also fixed up the ride list to not select them, since that is irritating if your in the middle of doing something.
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
#include <QFileIconProvider>
|
||||
#include <QMessageBox>
|
||||
#include <QHeaderView>
|
||||
#include <QDesktopWidget>
|
||||
|
||||
#include "../qzip/zipwriter.h"
|
||||
#include "../qzip/zipreader.h"
|
||||
@@ -1589,6 +1590,11 @@ CloudServiceAutoDownload::autoDownload()
|
||||
{
|
||||
if (initial) {
|
||||
initial = false;
|
||||
|
||||
// Start means we are looking for downloads to do
|
||||
context->notifyAutoDownloadStart();
|
||||
|
||||
// starts a thread
|
||||
start();
|
||||
}
|
||||
}
|
||||
@@ -1599,7 +1605,6 @@ CloudServiceAutoDownload::run()
|
||||
// this is a separate thread and can run in parallel with the main gui
|
||||
// so we can loop through services and download the data needed.
|
||||
// we notify the main gui via the usual signals.
|
||||
context->notifyAutoDownloadStart();
|
||||
|
||||
// get a list of services to sync from
|
||||
QStringList worklist;
|
||||
@@ -1609,9 +1614,6 @@ CloudServiceAutoDownload::run()
|
||||
}
|
||||
}
|
||||
|
||||
// update progress indicator
|
||||
context->notifyAutoDownloadProgress(0.0f);
|
||||
|
||||
//
|
||||
// generate a worklist to process
|
||||
//
|
||||
@@ -1709,6 +1711,9 @@ CloudServiceAutoDownload::run()
|
||||
double inc = 100.0f / double(downloadlist.count());
|
||||
for(int i=0; i<downloadlist.count(); i++) {
|
||||
|
||||
// update progress indicator
|
||||
context->notifyAutoDownloadProgress(progress);
|
||||
|
||||
CloudServiceDownloadEntry download= downloadlist[i];
|
||||
|
||||
// we block on read completing
|
||||
@@ -1724,12 +1729,14 @@ CloudServiceAutoDownload::run()
|
||||
// block on timeout or readComplete...
|
||||
loop.exec();
|
||||
|
||||
// notify progress
|
||||
context->notifyAutoDownloadProgress(progress += inc);
|
||||
// update progress
|
||||
progress += inc;
|
||||
|
||||
// if last one we need to signal done.
|
||||
if ((i+1) == downloadlist.count()) context->notifyAutoDownloadProgress(progress);
|
||||
}
|
||||
|
||||
// all done, close the sync notification
|
||||
context->notifyAutoDownloadProgress(100);
|
||||
// all done, close the sync notification, regardless of if anything was downloaded
|
||||
context->notifyAutoDownloadEnd();
|
||||
|
||||
// remove providers
|
||||
@@ -1747,10 +1754,8 @@ CloudServiceAutoDownload::run()
|
||||
}
|
||||
|
||||
void
|
||||
CloudServiceAutoDownload::readComplete(QByteArray*data,QString name,QString status)
|
||||
CloudServiceAutoDownload::readComplete(QByteArray*data,QString name,QString)
|
||||
{
|
||||
qDebug()<<"received:"<<name<<status; // whilst developing .. will remove when done
|
||||
|
||||
// find the entry I belong too
|
||||
CloudServiceDownloadEntry entry;
|
||||
bool found=false;
|
||||
@@ -1810,7 +1815,101 @@ CloudServiceAutoDownload::readComplete(QByteArray*data,QString name,QString stat
|
||||
// delete temporary in-memory copy
|
||||
delete ride;
|
||||
|
||||
// add to the ride list
|
||||
context->athlete->addRide(fileinfo.fileName(), true);
|
||||
// add to the ride list -- but don't select it
|
||||
context->athlete->addRide(fileinfo.fileName(), true, false);
|
||||
|
||||
}
|
||||
|
||||
|
||||
CloudServiceAutoDownloadWidget::CloudServiceAutoDownloadWidget(Context *context,QWidget *parent) :
|
||||
QWidget(parent), context(context), state(Dormant)
|
||||
{
|
||||
connect(context, SIGNAL(autoDownloadStart()), this, SLOT(downloadStart()));
|
||||
connect(context, SIGNAL(autoDownloadEnd()), this, SLOT(downloadFinish()));
|
||||
connect(context, SIGNAL(autoDownloadProgress(double)), this, SLOT(downloadProgress(double)));
|
||||
|
||||
// just a small little thing
|
||||
setFixedHeight(dpiYFactor * 25);
|
||||
hide();
|
||||
|
||||
// animating checking
|
||||
animator= new QPropertyAnimation(this, "transition");
|
||||
animator->setStartValue(0);
|
||||
animator->setEndValue(100);
|
||||
animator->setDuration(1000);
|
||||
animator->setEasingCurve(QEasingCurve::Linear);
|
||||
}
|
||||
|
||||
void
|
||||
CloudServiceAutoDownloadWidget::downloadStart()
|
||||
{
|
||||
state = Checking;
|
||||
animator->start();
|
||||
show();
|
||||
}
|
||||
|
||||
void
|
||||
CloudServiceAutoDownloadWidget::downloadFinish()
|
||||
{
|
||||
state = Dormant;
|
||||
animator->stop();
|
||||
hide();
|
||||
}
|
||||
|
||||
void
|
||||
CloudServiceAutoDownloadWidget::downloadProgress(double x)
|
||||
{
|
||||
state = Downloading;
|
||||
animator->stop();
|
||||
show();
|
||||
progress = x;
|
||||
repaint();
|
||||
}
|
||||
|
||||
void
|
||||
CloudServiceAutoDownloadWidget::paintEvent(QPaintEvent*)
|
||||
{
|
||||
QPainter painter(this);
|
||||
QBrush brush(GColor(CPLOTBACKGROUND));
|
||||
painter.fillRect(0,0,width(),height(), brush);
|
||||
|
||||
QString statusstring;
|
||||
switch(state) {
|
||||
case Dormant: statusstring=""; break;
|
||||
case Downloading: statusstring=tr("Downloading"); break;
|
||||
case Checking: statusstring=tr("Checking"); break;
|
||||
}
|
||||
|
||||
// smallest font we can
|
||||
QFont font;
|
||||
QFontMetrics fm(font);
|
||||
painter.setFont(font);
|
||||
painter.setPen(GCColor::invertColor(GColor(CPLOTBACKGROUND)));
|
||||
QRectF textbox = QRectF(0,0, fm.width(statusstring), height());
|
||||
painter.drawText(textbox, Qt::AlignVCenter | Qt::AlignCenter, statusstring);
|
||||
|
||||
// rectangle
|
||||
QRectF pr(textbox.width()+(5.0f*dpiXFactor), textbox.top()+(8.0f*dpiXFactor), width()-(10.0f*dpiXFactor)-textbox.width(), height()-(16*dpiXFactor));
|
||||
|
||||
// progress rect
|
||||
QColor col = GColor(CPLOTMARKER);
|
||||
col.setAlpha(150);
|
||||
brush= QBrush(col);
|
||||
|
||||
if (state == Downloading) {
|
||||
QRectF bar(pr.left(), pr.top(), (pr.width() / 100.00f * progress), pr.height());
|
||||
painter.fillRect(bar, brush);
|
||||
} else if (state == Checking) {
|
||||
// bounce
|
||||
QRectF lbar(pr.left()+ ((pr.width() *0.8f) / 100.0f * transition), pr.top(), pr.width() * 0.2f, pr.height());
|
||||
QRectF rbar(pr.left()+ (pr.width()*0.8f) - ((pr.width() *0.8f) / 100.0f * transition), pr.top(), pr.width() * 0.2f, pr.height());
|
||||
painter.fillRect(lbar, brush);
|
||||
painter.fillRect(rbar, brush);
|
||||
|
||||
// if we ran out of juice start again
|
||||
if (transition == 100) { animator->stop(); animator->start(); }
|
||||
}
|
||||
|
||||
// border of progress bar
|
||||
painter.drawRect(pr);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QProgressBar>
|
||||
#include <QPropertyAnimation>
|
||||
|
||||
#include "Context.h"
|
||||
#include "Athlete.h"
|
||||
@@ -603,4 +604,38 @@ class CloudServiceFactory {
|
||||
|
||||
};
|
||||
|
||||
class CloudServiceAutoDownloadWidget : public QWidget
|
||||
{
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(int transition READ getTransition WRITE setTransition)
|
||||
|
||||
public:
|
||||
CloudServiceAutoDownloadWidget(Context *context,QWidget *parent);
|
||||
|
||||
// transition animation 0-255
|
||||
int getTransition() const {return transition;}
|
||||
void setTransition(int x) { if (transition !=x) {transition=x; update();}}
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent*);
|
||||
|
||||
public slots:
|
||||
|
||||
void downloadStart();
|
||||
void downloadFinish();
|
||||
void downloadProgress(double x);
|
||||
|
||||
private:
|
||||
|
||||
Context *context;
|
||||
enum { Checking, Downloading, Dormant } state;
|
||||
double progress;
|
||||
|
||||
// animating checking
|
||||
QPropertyAnimation *animator;
|
||||
int transition;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -274,9 +274,9 @@ void Athlete::selectRideFile(QString fileName)
|
||||
}
|
||||
|
||||
void
|
||||
Athlete::addRide(QString name, bool dosignal, bool useTempActivities, bool planned)
|
||||
Athlete::addRide(QString name, bool dosignal, bool select, bool useTempActivities, bool planned)
|
||||
{
|
||||
rideCache->addRide(name, dosignal, useTempActivities, planned);
|
||||
rideCache->addRide(name, dosignal, select, useTempActivities, planned);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -151,7 +151,7 @@ class Athlete : public QObject
|
||||
|
||||
// ride collection
|
||||
void selectRideFile(QString);
|
||||
void addRide(QString name, bool bSelect=true, bool useTempActivities=false, bool planned=false);
|
||||
void addRide(QString name, bool signal, bool select=true, bool useTempActivities=false, bool planned=false);
|
||||
void removeCurrentRide();
|
||||
|
||||
// zones etc
|
||||
|
||||
@@ -202,7 +202,7 @@ RideCache::itemChanged()
|
||||
emit itemChanged(item);
|
||||
|
||||
// current ride changed is more relevant for the charts lets notify
|
||||
// them the ride they're showing has changed
|
||||
// them the ride they're showing has changed
|
||||
if (item == context->currentRideItem()) {
|
||||
|
||||
context->notifyRideChanged(item);
|
||||
@@ -211,7 +211,7 @@ RideCache::itemChanged()
|
||||
|
||||
// add a new ride
|
||||
void
|
||||
RideCache::addRide(QString name, bool dosignal, bool useTempActivities, bool planned)
|
||||
RideCache::addRide(QString name, bool dosignal, bool select, bool useTempActivities, bool planned)
|
||||
{
|
||||
RideItem *prior = context->ride;
|
||||
|
||||
@@ -249,7 +249,7 @@ RideCache::addRide(QString name, bool dosignal, bool useTempActivities, bool pla
|
||||
model_->endReset();
|
||||
}
|
||||
|
||||
// refresh metrics for *this ride only*
|
||||
// refresh metrics for *this ride only*
|
||||
last->refresh();
|
||||
|
||||
if (dosignal) context->notifyRideAdded(last); // here so emitted BEFORE rideSelected is emitted!
|
||||
@@ -260,8 +260,13 @@ RideCache::addRide(QString name, bool dosignal, bool useTempActivities, bool pla
|
||||
if (prior) prior->close();
|
||||
|
||||
// notify everyone to select it
|
||||
context->ride = last;
|
||||
context->notifyRideSelected(last);
|
||||
if (select) {
|
||||
context->ride = last;
|
||||
context->notifyRideSelected(last);
|
||||
} else{
|
||||
// notify everyone to select the one we were already on
|
||||
context->notifyRideSelected(prior);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -464,7 +469,7 @@ RideCache::refresh()
|
||||
foreach(RideItem *item, rides_) {
|
||||
|
||||
// ok set stale so we refresh
|
||||
if (item->checkStale())
|
||||
if (item->checkStale())
|
||||
staleCount++;
|
||||
}
|
||||
|
||||
@@ -479,7 +484,9 @@ RideCache::refresh()
|
||||
|
||||
// nothing to do, notify its started and done immediately
|
||||
context->notifyRefreshStart();
|
||||
context->notifyRefreshEnd();
|
||||
|
||||
// wait five seconds, so mainwindow can get up and running...
|
||||
QTimer::singleShot(5000, context, SLOT(notifyRefreshEnd()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -570,7 +577,7 @@ RideCache::getAggregate(QString name, Specification spec, bool useMetricUnits, b
|
||||
|
||||
} else result = metric->toString(useMetricUnits);
|
||||
|
||||
// 0 temp from aggregate means no values
|
||||
// 0 temp from aggregate means no values
|
||||
if ((metric->symbol() == "average_temp" || metric->symbol() == "max_temp") && result == "0.0") result = "-";
|
||||
return result;
|
||||
}
|
||||
@@ -585,7 +592,7 @@ bool rideCachesummaryBestLowerThan(const AthleteBest &s1, const AthleteBest &s2)
|
||||
return s1.nvalue < s2.nvalue;
|
||||
}
|
||||
|
||||
QList<AthleteBest>
|
||||
QList<AthleteBest>
|
||||
RideCache::getBests(QString symbol, int n, Specification specification, bool useMetricUnits)
|
||||
{
|
||||
QList<AthleteBest> results;
|
||||
@@ -665,7 +672,7 @@ class RollingBests {
|
||||
returning.fill(0.0f, size);
|
||||
|
||||
// get largest values
|
||||
for(int i=0; i<buffer.count(); i++)
|
||||
for(int i=0; i<buffer.count(); i++)
|
||||
for (int j=0; j<buffer[i].count(); j++)
|
||||
if(buffer[i].at(j) > returning[j])
|
||||
returning[j] = buffer[i].at(j);
|
||||
@@ -781,7 +788,7 @@ RideCache::refreshCPModelMetrics()
|
||||
if (add.CP && add.WPrime) add.EI = add.WPrime / add.CP ;
|
||||
|
||||
// so long as the important model derived values are sensible ...
|
||||
if (add.WPrime > 1000 && add.CP > 100)
|
||||
if (add.WPrime > 1000 && add.CP > 100)
|
||||
context->athlete->PDEstimates_ << add;
|
||||
|
||||
//qDebug()<<add.to<<add.from<<model->code()<< "W'="<< model->WPrime() <<"CP="<< model->CP() <<"pMax="<<model->PMax();
|
||||
@@ -801,7 +808,7 @@ RideCache::refreshCPModelMetrics()
|
||||
if (add.CP && add.WPrime) add.EI = add.WPrime / add.CP ;
|
||||
|
||||
// so long as the model derived values are sensible ...
|
||||
if ((!model->hasWPrime() || add.WPrime > 10.0f) &&
|
||||
if ((!model->hasWPrime() || add.WPrime > 10.0f) &&
|
||||
(!model->hasCP() || add.CP > 1.0f) &&
|
||||
(!model->hasPMax() || add.PMax > 1.0f) &&
|
||||
(!model->hasFTP() || add.FTP > 1.0f))
|
||||
@@ -826,7 +833,7 @@ RideCache::refreshCPModelMetrics()
|
||||
emit modelProgress(0, 0); // all done
|
||||
}
|
||||
|
||||
QList<QDateTime>
|
||||
QList<QDateTime>
|
||||
RideCache::getAllDates()
|
||||
{
|
||||
QList<QDateTime> returning;
|
||||
@@ -836,7 +843,7 @@ RideCache::getAllDates()
|
||||
return returning;
|
||||
}
|
||||
|
||||
QStringList
|
||||
QStringList
|
||||
RideCache::getAllFilenames()
|
||||
{
|
||||
QStringList returning;
|
||||
@@ -876,7 +883,7 @@ RideCache::getRankedValues(QString field)
|
||||
int count = returning.value(value,0);
|
||||
returning.insert(value,++count);
|
||||
}
|
||||
}
|
||||
}
|
||||
return returning;
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ class RideCache : public QObject
|
||||
QVector<RideItem*>&rides() { return rides_; }
|
||||
|
||||
// add/remove a ride to the list
|
||||
void addRide(QString name, bool dosignal, bool useTempActivities, bool planned);
|
||||
void addRide(QString name, bool dosignal, bool select, bool useTempActivities, bool planned);
|
||||
void removeCurrentRide();
|
||||
|
||||
// export metrics in CSV format
|
||||
|
||||
@@ -49,6 +49,9 @@ AnalysisSidebar::AnalysisSidebar(Context *context) : QWidget(context->mainWindow
|
||||
splitter = new GcSplitter(Qt::Vertical);
|
||||
mainLayout->addWidget(splitter);
|
||||
|
||||
autowidget = new CloudServiceAutoDownloadWidget(context, this);
|
||||
mainLayout->addWidget(autowidget);
|
||||
|
||||
// calendar
|
||||
calendarWidget = new GcMultiCalendar(context);
|
||||
calendarItem = new GcSplitterItem(tr("Calendar"), iconFromPNG(":images/sidebar/calendar.png"), this);
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "RideItem.h"
|
||||
#include "IntervalTreeView.h"
|
||||
#include "IntervalSummaryWindow.h"
|
||||
#include "CloudService.h"
|
||||
|
||||
#include <QTreeWidgetItem>
|
||||
#include <QTreeWidget>
|
||||
@@ -104,6 +105,8 @@ class AnalysisSidebar : public QWidget
|
||||
|
||||
IntervalTreeView *intervalTree; // the interval tree
|
||||
QMap<RideFileInterval::intervaltype, QTreeWidgetItem*> trees;
|
||||
|
||||
CloudServiceAutoDownloadWidget *autowidget;
|
||||
};
|
||||
|
||||
#endif // _GC_AnalysisSidebar_h
|
||||
|
||||
@@ -546,7 +546,7 @@ ManualRideDialog::okClicked()
|
||||
if (success) {
|
||||
|
||||
// refresh metric db etc
|
||||
context->athlete->addRide(basename + ".json");
|
||||
context->athlete->addRide(basename + ".json", true);
|
||||
accept();
|
||||
|
||||
} else {
|
||||
|
||||
@@ -124,6 +124,7 @@ RideNavigator::RideNavigator(Context *context, bool mainwindow) : GcChartWindow(
|
||||
// refresh when rides added/removed
|
||||
connect(context, SIGNAL(rideAdded(RideItem*)), this, SLOT(refresh()));
|
||||
connect(context, SIGNAL(rideDeleted(RideItem*)), this, SLOT(rideDeleted(RideItem*)));
|
||||
connect(context, SIGNAL(rideSelected(RideItem*)), this, SLOT(setRide(RideItem*)));
|
||||
|
||||
// user selected a ride on the ride list, we should reflect that too..
|
||||
connect(tableView, SIGNAL(rowSelected(QItemSelection)), this, SLOT(selectionChanged(QItemSelection)));
|
||||
@@ -949,7 +950,8 @@ RideNavigator::showColumnChooser()
|
||||
void
|
||||
RideNavigator::setRide(RideItem*rideItem)
|
||||
{
|
||||
if (currentItem == rideItem) return;
|
||||
// if we have a selection and its this one just ignore.
|
||||
if (currentItem == rideItem && tableView->selectionModel()->selectedRows().count() != 0) return;
|
||||
|
||||
for (int i=0; i<tableView->model()->rowCount(); i++) {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user