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:
Mark Liversedge
2017-04-15 17:52:04 +01:00
parent 8c12cefa8a
commit 70593976cb
10 changed files with 183 additions and 34 deletions

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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 {

View File

@@ -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++) {