From a4fc05f70ba8a6ce0415f23ced49bbf50e67bbb2 Mon Sep 17 00:00:00 2001 From: Mark Liversedge Date: Sun, 22 Dec 2013 17:23:19 +0000 Subject: [PATCH] AllPlot Stacked by Series Extending the stack display to have a single plot for each data series. This will be useful when comparing activities and also makes it a bit easier to see the data when there are lots of data series. This is a checkpoint commit as I need to fixup some of the series that have multiple curves and also reference lines are not currently working. But you can zoom using the fullplot and highlight intervals etc. --- src/AllPlot.cpp | 284 ++++++++++++++++++++++++++++++++++++++++- src/AllPlot.h | 13 +- src/AllPlotWindow.cpp | 289 +++++++++++++++++++++++++++++++++++++++--- src/AllPlotWindow.h | 18 ++- 4 files changed, 575 insertions(+), 29 deletions(-) diff --git a/src/AllPlot.cpp b/src/AllPlot.cpp index 2fd7b0f6d..519379489 100644 --- a/src/AllPlot.cpp +++ b/src/AllPlot.cpp @@ -228,7 +228,7 @@ class TimeScaleDraw: public QwtScaleDraw static inline double max(double a, double b) { if (a > b) return a; else return b; } -AllPlot::AllPlot(AllPlotWindow *parent, Context *context): +AllPlot::AllPlot(AllPlotWindow *parent, Context *context, RideFile::SeriesType scope): QwtPlot(parent), rideItem(NULL), shade_zones(true), @@ -245,6 +245,7 @@ AllPlot::AllPlot(AllPlotWindow *parent, Context *context): showTorque(true), showBalance(true), bydist(false), + scope(scope), context(context), parent(parent) { @@ -990,7 +991,7 @@ AllPlot::recalc() } void -AllPlot::refreshIntervalMarkers() +AllPlot::refreshIntervalMarkers(bool withtext) { foreach(QwtPlotMarker *mrk, d_mrk) { mrk->detach(); @@ -1014,7 +1015,7 @@ AllPlot::refreshIntervalMarkers() QString name(interval.name); if (interval.name.startsWith(tr("Match"))) name = QString("\n%1").arg(interval.name); - QwtText text(name); + QwtText text(withtext ? name : ""); text.setFont(QFont("Helvetica", 10, QFont::Bold)); if (interval.name.startsWith(tr("Match"))) text.setColor(GColor(CWBAL)); @@ -1031,7 +1032,7 @@ AllPlot::refreshIntervalMarkers() } void -AllPlot::refreshCalibrationMarkers() +AllPlot::refreshCalibrationMarkers(bool withtext) { foreach(QwtPlotMarker *mrk, cal_mrk) { mrk->detach(); @@ -1047,7 +1048,7 @@ AllPlot::refreshCalibrationMarkers() mrk->setLineStyle(QwtPlotMarker::VLine); mrk->setLabelAlignment(Qt::AlignRight | Qt::AlignTop); mrk->setLinePen(QPen(GColor(CPLOTMARKER), 0, Qt::DashDotLine)); - QwtText text("\n\n"+calibration.name); + QwtText text(withtext ? ("\n\n"+calibration.name) : ""); text.setFont(QFont("Helvetica", 9, QFont::Bold)); text.setColor(GColor(CPLOTMARKER)); if (!bydist) @@ -1680,6 +1681,279 @@ AllPlot::setDataFromPlot(AllPlot *plot, int startidx, int stopidx) //replot(); } +void +AllPlot::setDataFromPlot(AllPlot *plot, bool first) +{ +qDebug()<<"set data from plot"; + if (plot == NULL) { + rideItem = NULL; + return; + } + + referencePlot = plot; + +qDebug()<<"check it has data"; + // You got to give me some data first! + //if (!plot->distanceArray.count() || !plot->timeArray.count()) return; + + // reference the plot for data and state + rideItem = plot->rideItem; + bydist = plot->bydist; + +qDebug()<<"set invisible"; + // remove all curves from the plot + wCurve->detach(); + mCurve->detach(); + wattsCurve->detach(); + npCurve->detach(); + xpCurve->detach(); + apCurve->detach(); + hrCurve->detach(); + speedCurve->detach(); + cadCurve->detach(); + altCurve->detach(); + tempCurve->detach(); + windCurve->detach(); + torqueCurve->detach(); + balanceLCurve->detach(); + balanceRCurve->detach(); + + wCurve->setVisible(false); + mCurve->setVisible(false); + wattsCurve->setVisible(false); + npCurve->setVisible(false); + xpCurve->setVisible(false); + apCurve->setVisible(false); + hrCurve->setVisible(false); + speedCurve->setVisible(false); + cadCurve->setVisible(false); + altCurve->setVisible(false); + tempCurve->setVisible(false); + windCurve->setVisible(false); + torqueCurve->setVisible(false); + balanceLCurve->setVisible(false); + balanceRCurve->setVisible(false); + +#if 0 + // attach appropriate curves + //if (this->legend()) this->legend()->hide(); + if (showW && parent->wpData->TAU > 0) { + + // center the curve title + curveTitle.setYValue(30); + curveTitle.setXValue(2); + + // matches cost + double burnt=0; + int count=0; + foreach(struct Match match, parent->wpData->matches) + if (match.cost > 2000) { //XXX how to decide the threshold for a match? + burnt += match.cost; + count++; + } + + QwtText text(QString("Tau=%1, CP=%2, W'=%3, %4 matches >2kJ (%5 kJ)").arg(parent->wpData->TAU) + .arg(parent->wpData->CP) + .arg(parent->wpData->WPRIME) + .arg(count) + .arg(burnt/1000.00, 0, 'f', 1)); + + text.setFont(QFont("Helvetica", 10, QFont::Bold)); + text.setColor(GColor(CWBAL)); + curveTitle.setLabel(text); + } else { + curveTitle.setLabel(QwtText("")); + } +#endif + +qDebug()<<"clone curves.."; + QwtPlotCurve *ourCurve = NULL, *thereCurve = NULL; + QString title; + + // which curve are we interested in ? + switch (scope) { + + case RideFile::cad: + { + ourCurve = cadCurve; + thereCurve = referencePlot->cadCurve; + title = tr("Cadence"); + } + break; + + case RideFile::hr: + { + ourCurve = hrCurve; + thereCurve = referencePlot->hrCurve; + title = tr("Heartrate"); + } + break; + + case RideFile::kph: + { + ourCurve = speedCurve; + thereCurve = referencePlot->speedCurve; + title = tr("Speed"); + } + break; + + case RideFile::nm: + { + ourCurve = torqueCurve; + thereCurve = referencePlot->torqueCurve; + title = tr("Torque"); + } + break; + + case RideFile::watts: + { + ourCurve = wattsCurve; + thereCurve = referencePlot->wattsCurve; + title = tr("Power"); + } + break; + + case RideFile::alt: + { + ourCurve = altCurve; + thereCurve = referencePlot->altCurve; + title = tr("Altitude"); + } + break; + + case RideFile::headwind: + { + //XXX fixme! QwtPlotIntervalCurve *windCurve; + title = tr("Headwind"); + } + break; + + case RideFile::temp: + { + ourCurve = tempCurve; + thereCurve = referencePlot->tempCurve; + title = tr("Temperature"); + } + break; + + case RideFile::NP: + { + ourCurve = npCurve; + thereCurve = referencePlot->npCurve; + title = tr("NP"); + } + break; + + case RideFile::xPower: + { + ourCurve = xpCurve; + thereCurve = referencePlot->xpCurve; + title = tr("xPower"); + } + break; + + case RideFile::lrbalance: + { + //XXX fixme ourCurve = balanceLCurve; + //XXX fixme ourCurve = balanceRCurve; + title = tr("L/R Balance"); + } + break; + + case RideFile::aPower: + { + ourCurve = apCurve; + thereCurve = referencePlot->apCurve; + title = tr("aPower"); + } + break; + + default: + case RideFile::interval: + case RideFile::slope: + case RideFile::vam: + case RideFile::wattsKg: + case RideFile::km: + case RideFile::lon: + case RideFile::lat: + case RideFile::none: + break; + } + + // lets clone ! + if (ourCurve && thereCurve) { + + // no way to get values, so we run through them + ourCurve->setVisible(true); + ourCurve->attach(this); + + // lets clone the data + QVector array; + for (int i=0; idata()->size(); i++) array << thereCurve->data()->sample(i); + + ourCurve->setSamples(array); + + // x-axis + setAxisScale(QwtPlot::xBottom, thereCurve->minXValue(), thereCurve->maxXValue()); + enableAxis(QwtPlot::xBottom, true); + setAxisVisible(QwtPlot::xBottom, true); + + // y-axis yLeft + ourCurve->setYAxis(yLeft); + setAxisVisible(yLeft, true); + setAxisScale(QwtPlot::yLeft, thereCurve->minYValue(), 1.1f * thereCurve->maxYValue()); + QwtScaleDraw *sd = new QwtScaleDraw; + sd->setTickLength(QwtScaleDiv::MajorTick, 3); + sd->enableComponent(QwtScaleDraw::Ticks, false); + sd->enableComponent(QwtScaleDraw::Backbone, false); + setAxisScaleDraw(QwtPlot::yLeft, sd); + + // title and colour + setAxisTitle(yLeft, title); + QPalette pal; + pal.setColor(QPalette::WindowText, thereCurve->pen().color()); + pal.setColor(QPalette::Text, thereCurve->pen().color()); + axisWidget(QwtPlot::yLeft)->setPalette(pal); + + // hide other y axes + setAxisVisible(QwtAxisId(QwtAxis::yLeft, 1), false); + setAxisVisible(yRight, false); + setAxisVisible(QwtAxisId(QwtAxis::yRight, 1), false); + setAxisVisible(QwtAxisId(QwtAxis::yRight, 2), false); + + // plot grid + grid->setVisible(referencePlot->grid->isVisible()); + + // symbol when zoomed in super close + if (array.size() < 150) { + QwtSymbol *sym = new QwtSymbol; + sym->setPen(QPen(GColor(CPLOTMARKER))); + sym->setStyle(QwtSymbol::Ellipse); + sym->setSize(3); + ourCurve->setSymbol(sym); + } else { + QwtSymbol *sym = new QwtSymbol; + sym->setStyle(QwtSymbol::NoSymbol); + sym->setSize(0); + ourCurve->setSymbol(sym); + } + + // intervals and calibration + refreshIntervalMarkers(first); + refreshCalibrationMarkers(first); + +#if 0 + QwtPlotCurve *wCurve; + QwtPlotCurve *mCurve; + QwtPlotCurve *lrCurve; + //XXX setgrids, shade zones etc etc etc etc + refreshReferenceLines(); + refreshZoneLabels(); +#endif + + } +} + void AllPlot::setDataFromRide(RideItem *_rideItem) { diff --git a/src/AllPlot.h b/src/AllPlot.h index 9fd944a71..7429294b5 100644 --- a/src/AllPlot.h +++ b/src/AllPlot.h @@ -34,6 +34,8 @@ #include #include +#include "RideFile.h" + class QwtPlotCurve; class QwtPlotIntervalCurve; class QwtPlotGrid; @@ -58,13 +60,15 @@ class AllPlot : public QwtPlot public: - AllPlot(AllPlotWindow *parent, Context *context); + // you can declare which series to plot, none means do them all + AllPlot(AllPlotWindow *parent, Context *context, RideFile::SeriesType series = RideFile::none); bool eventFilter(QObject *object, QEvent *e); // set the curve data e.g. when a ride is selected void setDataFromRide(RideItem *_rideItem); void setDataFromPlot(AllPlot *plot, int startidx, int stopidx); + void setDataFromPlot(AllPlot *plot, bool first = false); // used for single series plotting // convert from time/distance to index in *smoothed* datapoints int timeIndex(double) const; @@ -73,8 +77,8 @@ class AllPlot : public QwtPlot // plot redraw functions bool shadeZones() const; void refreshZoneLabels(); - void refreshIntervalMarkers(); - void refreshCalibrationMarkers(); + void refreshIntervalMarkers(bool withtext=true); + void refreshCalibrationMarkers(bool withtext=true); void refreshReferenceLines(); void refreshReferenceLinesForAllPlots(); void setAxisTitle(QwtAxisId axis, QString label); @@ -207,6 +211,9 @@ class AllPlot : public QwtPlot int smooth; bool bydist; + // scope of plot (none means all, or just for a specific series + RideFile::SeriesType scope; + private: Context *context; diff --git a/src/AllPlotWindow.cpp b/src/AllPlotWindow.cpp index 6fdca3622..a8d09fb9d 100644 --- a/src/AllPlotWindow.cpp +++ b/src/AllPlotWindow.cpp @@ -56,7 +56,7 @@ #include "WPrime.h" AllPlotWindow::AllPlotWindow(Context *context) : - GcChartWindow(context), current(NULL), context(context), active(false), stale(true), setupStack(false) + GcChartWindow(context), current(NULL), context(context), active(false), stale(true), setupStack(false), setupSeriesStack(false) { QWidget *c = new QWidget; QVBoxLayout *clv = new QVBoxLayout(c); @@ -92,6 +92,7 @@ AllPlotWindow::AllPlotWindow(Context *context) : rSmoothSlider->setMinimum(1); rSmoothSlider->setMaximum(100); rStack = new QCheckBox(tr("Stacked")); + rBySeries = new QCheckBox(tr("by series")); rFull = new QCheckBox(tr("Fullplot")); // layout reveal controls @@ -102,7 +103,10 @@ AllPlotWindow::AllPlotWindow(Context *context) : r->addWidget(rSmoothEdit); r->addWidget(rSmoothSlider); QVBoxLayout *v = new QVBoxLayout; - v->addWidget(rStack); + QHBoxLayout *s = new QHBoxLayout; + s->addWidget(rStack); + s->addWidget(rBySeries); + v->addLayout(s); v->addWidget(rFull); r->addSpacing(20); r->addLayout(v); @@ -120,6 +124,10 @@ AllPlotWindow::AllPlotWindow(Context *context) : showStack->setCheckState(Qt::Unchecked); cl1->addRow(showLabel, showStack); + showBySeries = new QCheckBox(tr("By Series"), this); + showBySeries->setCheckState(Qt::Unchecked); + cl1->addRow(new QLabel("", this), showBySeries); + stackWidth = 15; stackZoomUp = new QwtArrowButton(1, Qt::UpArrow,this); stackZoomUp->setFixedHeight(15); @@ -233,6 +241,7 @@ AllPlotWindow::AllPlotWindow(Context *context) : allPlot->setContentsMargins(0,0,0,0); allPlot->enableAxis(QwtPlot::xBottom, true); allPlot->setAxisVisible(QwtPlot::xBottom, true); + //allPlot->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); //allPlot->axisWidget(QwtPlot::yLeft)->installEventFilter(this); // sort out default values @@ -318,6 +327,26 @@ AllPlotWindow::AllPlotWindow(Context *context) : stackFrame->setContentsMargins(0,0,0,0); stackFrame->setPalette(palette); + // + // series stack view + // + seriesstackPlotLayout = new QVBoxLayout(); + seriesstackPlotLayout->setSpacing(0); + seriesstackPlotLayout->setContentsMargins(0,0,0,0); + seriesstackWidget = new QWidget(); + seriesstackWidget->setAutoFillBackground(false); + seriesstackWidget->setLayout(seriesstackPlotLayout); + seriesstackWidget->setPalette(palette); + + seriesstackFrame = new QScrollArea(); + seriesstackFrame->hide(); + seriesstackFrame->setAutoFillBackground(false); + seriesstackFrame->setWidgetResizable(true); + seriesstackFrame->setWidget(seriesstackWidget); + seriesstackFrame->setFrameStyle(QFrame::NoFrame); + seriesstackFrame->setContentsMargins(0,0,0,0); + seriesstackFrame->setPalette(palette); + // // allPlot view // @@ -378,7 +407,14 @@ AllPlotWindow::AllPlotWindow(Context *context) : //fullPlot->setTitle(""); fullPlot->setContentsMargins(0,0,0,0); - allPlotLayout->addWidget(allPlot); + // allPlotStack contains the allPlot and the stack by series + // because both want the optional fullplot at the botton + allPlotStack = new QStackedWidget(this); + allPlotStack->addWidget(allPlot); + allPlotStack->addWidget(seriesstackFrame); + allPlotStack->setCurrentIndex(0); + + allPlotLayout->addWidget(allPlotStack); allPlotFrame->setLayout(allPlotLayout); // controls... @@ -434,6 +470,8 @@ AllPlotWindow::AllPlotWindow(Context *context) : connect(showFull, SIGNAL(stateChanged(int)), this, SLOT(setShowFull(int))); connect(showStack, SIGNAL(stateChanged(int)), this, SLOT(showStackChanged(int))); connect(rStack, SIGNAL(stateChanged(int)), this, SLOT(showStackChanged(int))); + connect(showBySeries, SIGNAL(stateChanged(int)), this, SLOT(showBySeriesChanged(int))); + connect(rBySeries, SIGNAL(stateChanged(int)), this, SLOT(showBySeriesChanged(int))); connect(rFull, SIGNAL(stateChanged(int)), this, SLOT(setShowFull(int))); connect(paintBrush, SIGNAL(stateChanged(int)), this, SLOT(setPaintBrush(int))); connect(comboDistance, SIGNAL(currentIndexChanged(int)), this, SLOT(setByDistance(int))); @@ -501,7 +539,9 @@ AllPlotWindow::configChanged() void AllPlotWindow::redrawAllPlot() { - if (!showStack->isChecked() && current) { + // refresh if shown or used as basis for stacked series (ie zooming off fullPlot) + if (((showStack->isChecked() && showBySeries->isChecked()) || !showStack->isChecked()) && current) { + RideItem *ride = current; int startidx, stopidx; @@ -570,11 +610,26 @@ AllPlotWindow::redrawStackPlot() // to screen flicker stackFrame->setUpdatesEnabled(false); - // remove current plots - then recreate - resetStackedDatas(); - // now they are all set, lets replot - foreach(AllPlot *plot, allPlots) plot->replot(); + // plot stack view .. + if (!showBySeries->isChecked()) { + + // BY TIME / DISTANCE + + // remove current plots - then recreate + resetStackedDatas(); + + // now they are all set, lets replot + foreach(AllPlot *plot, allPlots) plot->replot(); + + } else { + + // BY SERIES + resetSeriesStackedDatas(); + + // now they are all set, lets replot + foreach(AllPlot *plot, seriesPlots) plot->replot(); + } // we're done, go update the display now stackFrame->setUpdatesEnabled(true); @@ -585,6 +640,7 @@ void AllPlotWindow::zoomChanged() { redrawAllPlot(); + redrawStackPlot(); // series stacks! } void @@ -625,7 +681,7 @@ AllPlotWindow::rideSelected() // ignore if not active if (!amVisible()) { stale = true; - setupStack = false; + setupSeriesStack = setupStack = false; return; } @@ -678,8 +734,10 @@ AllPlotWindow::rideSelected() // we need to reset the stacks as the ride has changed // but it may ignore if not in stacked mode. - setupStack = false; + setupSeriesStack = setupStack = false; + setupStackPlots(); + setupSeriesStackPlots(); stale = false; } @@ -731,6 +789,13 @@ AllPlotWindow::intervalsChanged() plot->refreshIntervalMarkers(); plot->replot(); } + + // and then the series plots + foreach (AllPlot *plot, seriesPlots) { + plot->refreshIntervalMarkers(); + plot->replot(); + } + } void @@ -749,8 +814,14 @@ AllPlotWindow::intervalSelected() // which mode we are in... hideSelection(); if (showStack->isChecked()) { - foreach (AllPlot *plot, allPlots) - plot->replot(); + + if (showBySeries->isChecked()) { + foreach (AllPlot *plot, seriesPlots) + plot->replot(); + } else { + foreach (AllPlot *plot, allPlots) + plot->replot(); + } } else { fullPlot->replot(); allPlot->replot(); @@ -762,6 +833,22 @@ AllPlotWindow::setStacked(int value) { showStack->setChecked(value); rStack->setChecked(value); + + // enables / disable as needed + if (showStack->isChecked()) { + showBySeries->setEnabled(true); + rBySeries->setEnabled(true); + } else { + showBySeries->setEnabled(false); + rBySeries->setEnabled(false); + } +} + +void +AllPlotWindow::setBySeries(int value) +{ + showBySeries->setChecked(value); + rBySeries->setChecked(value); } void @@ -896,7 +983,7 @@ AllPlotWindow::setAllPlotWidgets(RideItem *ride) // now set the visible plots, depending upon whether // we are in stacked mode or not - if (showStack->isChecked()) { + if (showStack->isChecked() && !showBySeries->isChecked()) { // hide normal view allPlotFrame->hide(); @@ -925,6 +1012,9 @@ AllPlotWindow::setAllPlotWidgets(RideItem *ride) allPlotFrame->show(); allPlot->show(); + if (showStack->isChecked() && showBySeries->isChecked()) allPlotStack->setCurrentIndex(1); + else allPlotStack->setCurrentIndex(0); + if (showFull->isChecked()) { fullPlot->show(); controlsLayout->setRowStretch(0, 100); @@ -1497,6 +1587,7 @@ AllPlotWindow::setByDistance(int value) redrawFullPlot(); redrawAllPlot(); setupStackPlots(); + setupSeriesStackPlots(); active = false; } @@ -1516,6 +1607,18 @@ AllPlotWindow::setSmoothing(int value) redrawStackPlot(); } +void +AllPlotWindow::resetSeriesStackedDatas() +{ + if (!current) return; +qDebug()<<"reset series stacked datas!"; + // just reset from AllPlot + bool first = true; + foreach(AllPlot *p, seriesPlots) { + p->setDataFromPlot(allPlot, first); + first = false; + } +} // // Runs through the stacks and updates their contents // @@ -1583,6 +1686,7 @@ AllPlotWindow::setStackZoomUp() stackWidth = ceil(stackWidth * 1.25); setupStackPlots(); + setupSeriesStackPlots(); stackZoomUp->setEnabled(stackZoomUpShouldEnable(stackWidth)); stackZoomDown->setEnabled(stackZoomDownShouldEnable(stackWidth)); } @@ -1597,6 +1701,7 @@ AllPlotWindow::setStackZoomDown() stackWidth = floor(stackWidth / 1.25); setupStackPlots(); + setupSeriesStackPlots(); stackZoomUp->setEnabled(stackZoomUpShouldEnable(stackWidth)); stackZoomDown->setEnabled(stackZoomDownShouldEnable(stackWidth)); } @@ -1610,6 +1715,17 @@ AllPlotWindow::showStackChanged(int value) showStack->setCheckState((Qt::CheckState)value); rStack->setCheckState((Qt::CheckState)value); + // enables / disable as needed + if (showStack->isChecked()) { + showBySeries->setEnabled(true); + rBySeries->setEnabled(true); + allPlotStack->setCurrentIndex(0); + } else { + showBySeries->setEnabled(false); + rBySeries->setEnabled(false); + allPlotStack->setCurrentIndex(1); + } + // when we swap from normal to // stacked view, save the mode so // we can startup with the 'preferred' @@ -1620,14 +1736,33 @@ AllPlotWindow::showStackChanged(int value) // and the right widgets are hidden/shown. if (value) { - // setup the stack plots if needed - setupStackPlots(); + if (!showBySeries->isChecked()) { - // refresh plots - resetStackedDatas(); + // BY TIME / DISTANCE - // now they are all set, lets replot them - foreach(AllPlot *plot, allPlots) plot->replot(); + // setup the stack plots if needed + setupStackPlots(); + + // refresh plots + resetStackedDatas(); + + // now they are all set, lets replot them + foreach(AllPlot *plot, allPlots) plot->replot(); + + } else { + + // BY SERIES + + // setup the stack plots if needed + setupSeriesStackPlots(); + + // refresh plots + resetSeriesStackedDatas(); +qDebug()<<"replotting!"; + // now they are all set, lets replot them + foreach(AllPlot *plot, seriesPlots) plot->replot(); + + } } else { // refresh plots @@ -1638,6 +1773,120 @@ AllPlotWindow::showStackChanged(int value) setAllPlotWidgets(current); } +void +AllPlotWindow::showBySeriesChanged(int value) +{ + if (!current) return; + + showBySeries->setCheckState((Qt::CheckState)value); + rBySeries->setCheckState((Qt::CheckState)value); + + // XXX ??? + showStackChanged(showStack->checkState()); // force replot etc +} + +void +AllPlotWindow::setupSeriesStackPlots() +{ + if (!showStack->isChecked() || !showBySeries->isChecked() || setupSeriesStack) return; + + qDebug()<<"setup series stack plots just 5 for now"<ride() || rideItem->ride()->dataPoints().isEmpty()) return; + + QPalette palette; + palette.setBrush(QPalette::Background, Qt::NoBrush); + + QList serieslist; + + // lets get a list of what we need to plot + if (showPower->currentIndex() < 2 && rideItem->ride()->areDataPresent()->watts) serieslist << RideFile::watts; + if (showHr->isChecked() && rideItem->ride()->areDataPresent()->hr) serieslist << RideFile::hr; + if (showCad->isChecked() && rideItem->ride()->areDataPresent()->cad) serieslist << RideFile::cad; + if (showSpeed->isChecked() && rideItem->ride()->areDataPresent()->kph) serieslist << RideFile::kph; + if (showTorque->isChecked() && rideItem->ride()->areDataPresent()->nm) serieslist << RideFile::nm; + if (showAlt->isChecked() && rideItem->ride()->areDataPresent()->alt) serieslist << RideFile::alt; + if (showTemp->isChecked() && rideItem->ride()->areDataPresent()->temp) serieslist << RideFile::temp; + if (showWind->isChecked() && rideItem->ride()->areDataPresent()->headwind) serieslist << RideFile::headwind; + if (showBalance->isChecked() && rideItem->ride()->areDataPresent()->lrbalance) serieslist << RideFile::lrbalance; + if (showNP->isChecked() && rideItem->ride()->areDataPresent()->watts) serieslist << RideFile::NP; + if (showXP->isChecked() && rideItem->ride()->areDataPresent()->watts) serieslist << RideFile::xPower; + if (showAP->isChecked() && rideItem->ride()->areDataPresent()->watts) serieslist << RideFile::aPower; + //if (showW->isChecked() && rideItem->ride()->areDataPresent()->watts) serieslist << RideFile::bpm; + + bool first = true; + foreach(RideFile::SeriesType x, serieslist) { + +qDebug()<<"added a plot"<setAutoFillBackground(false); + _allPlot->setPalette(palette); + _allPlot->setDataFromPlot(allPlot, first); // will clone all settings and data for the series + // being plotted, only works for one series plotting + + first = false; + + // add to the list + seriesPlots.append(_allPlot); + addPickers(_allPlot); + newLayout->addWidget(_allPlot); + _allPlot->setFixedHeight(120); + + // No x axis titles + _allPlot->setAxisVisible(QwtPlot::xBottom, true); + _allPlot->enableAxis(QwtPlot::xBottom, true); + _allPlot->setAxisTitle(QwtPlot::xBottom,NULL); + _allPlot->setAxisMaxMinor(QwtPlot::xBottom, 0); + _allPlot->setAxisMaxMinor(QwtPlot::yLeft, 0); + _allPlot->setAxisMaxMinor(QwtAxisId(QwtAxis::yLeft,2), 0); + _allPlot->setAxisMaxMinor(QwtPlot::yRight, 0); + _allPlot->setAxisMaxMinor(QwtAxisId(QwtAxis::yRight,2).id, 0); + _allPlot->setAxisMaxMinor(QwtAxisId(QwtAxis::yRight,3).id, 0); + + // controls + _allPlot->replot(); + } + newLayout->addStretch(); + + // the refresh takes a while and is prone + // to lots of flicker, we turn off updates + // whilst we switch from the old to the new + // stackWidget and plots + seriesstackFrame->setUpdatesEnabled(false); + + // set new widgets + QWidget *stackWidget = new QWidget; + stackWidget->setPalette(palette); + stackWidget->setAutoFillBackground(true); + stackWidget->setLayout(newLayout); + seriesstackFrame->setWidget(stackWidget); + + // ZZZZ zap old widgets - is NOT required + // since setWidget above will destroy + // the ScrollArea's children + // not neccessary --> foreach (AllPlot *plot, oldPlots) delete plot; + // not neccessary --> delete stackPlotLayout; + // not neccessary --> delete stackWidget; + + // we are now happy for a screen refresh to take place + seriesstackFrame->setUpdatesEnabled(true); + +#if 0 + // first lets wipe out the current plots + // since we cache out above we need to + // track and manage via below +#endif + setupSeriesStack = true; // we're clean +} + void AllPlotWindow::setupStackPlots() { @@ -1648,7 +1897,7 @@ AllPlotWindow::setupStackPlots() // with a new one. This is to avoid some // nasty flickers and simplifies the code // for refreshing. - if (!showStack->isChecked() || setupStack) return; + if (!showStack->isChecked() || showBySeries->isChecked() || setupStack) return; // since we cache out above we need to // track and manage via below diff --git a/src/AllPlotWindow.h b/src/AllPlotWindow.h index 604a5010e..d9be08115 100644 --- a/src/AllPlotWindow.h +++ b/src/AllPlotWindow.h @@ -53,6 +53,7 @@ class AllPlotWindow : public GcChartWindow // in this example we might show a stacked plot and a ride // plot at the same time. Q_PROPERTY(bool stacked READ isStacked WRITE setStacked USER true) + Q_PROPERTY(bool byseries READ isBySeries WRITE setBySeries USER true) Q_PROPERTY(int showGrid READ isShowGrid WRITE setShowGrid USER true) Q_PROPERTY(int showFull READ isShowFull WRITE setShowFull USER true) Q_PROPERTY(int showHr READ isShowHr WRITE setShowHr USER true) @@ -86,6 +87,7 @@ class AllPlotWindow : public GcChartWindow // get properties - the setters are below bool isStacked() const { return showStack->isChecked(); } + bool isBySeries() const { return showBySeries->isChecked(); } int isShowGrid() const { return showGrid->checkState(); } int isShowFull() const { return showFull->checkState(); } int isShowHr() const { return showHr->checkState(); } @@ -140,6 +142,7 @@ class AllPlotWindow : public GcChartWindow void setSmoothing(int value); void setByDistance(int value); void setStacked(int value); + void setBySeries(int value); // trap widget signals void zoomChanged(); @@ -148,6 +151,7 @@ class AllPlotWindow : public GcChartWindow void moveLeft(); void moveRight(); void showStackChanged(int state); + void showBySeriesChanged(int state); protected: @@ -169,6 +173,7 @@ class AllPlotWindow : public GcChartWindow AllPlot *allPlot; AllPlot *fullPlot; QList allPlots; + QList seriesPlots; QwtPlotPanner *allPanner; QwtPlotZoomer *allZoomer; @@ -176,6 +181,12 @@ class AllPlotWindow : public GcChartWindow QScrollArea *stackFrame; QVBoxLayout *stackPlotLayout; QWidget *stackWidget; + + // series stack view + QScrollArea *seriesstackFrame; + QVBoxLayout *seriesstackPlotLayout; + QWidget *seriesstackWidget; + QwtArrowButton *stackZoomDown; QwtArrowButton *stackZoomUp; @@ -186,6 +197,7 @@ class AllPlotWindow : public GcChartWindow // Common controls QGridLayout *controlsLayout; QCheckBox *showStack; + QCheckBox *showBySeries; QCheckBox *showGrid; QCheckBox *showFull; QCheckBox *paintBrush; @@ -212,21 +224,25 @@ class AllPlotWindow : public GcChartWindow QLabel *rSmooth; QSlider *rSmoothSlider; QLineEdit *rSmoothEdit; - QCheckBox *rStack, *rFull; + QCheckBox *rStack, *rBySeries, *rFull; + QStackedWidget *allPlotStack; // reset/redraw all the plots void setupStackPlots(); + void setupSeriesStackPlots(); void redrawAllPlot(); void redrawFullPlot(); void redrawStackPlot(); void showInfo(QString); void resetStackedDatas(); + void resetSeriesStackedDatas(); int stackWidth; bool active; bool stale; bool setupStack; // we optimise this out, its costly + bool setupSeriesStack; // we optimise this out, its costly private slots: