Files
GoldenCheetah/src/AllPlotWindow.cpp
Mark Liversedge 440bbd203f Refresh all curve when spikes edited and saved
.. will refresh after the metrics and cpx have been refreshed

.. it invalidates the data rather than refreshing immediately
   since thats expensive and better wait until refresh is needed.
2013-03-29 19:33:43 +00:00

1672 lines
53 KiB
C++

/*
* Copyright (c) 2009 Sean C. Rhea (srhea@srhea.net)
*
* 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 "MainWindow.h"
#include "AllPlotWindow.h"
#include "AllPlot.h"
#include "MainWindow.h"
#include "RideFile.h"
#include "RideItem.h"
#include "IntervalItem.h"
#include "IntervalTreeView.h"
#include "TimeUtils.h"
#include "Settings.h"
#include "Units.h" // for MILES_PER_KM
#include "Colors.h" // for MILES_PER_KM
#include <qwt_plot_layout.h>
#include <qwt_plot_panner.h>
#include <qwt_plot_zoomer.h>
#include <qwt_plot_picker.h>
#include <qwt_plot_marker.h>
#include <qwt_arrow_button.h>
#include <qwt_plot_curve.h>
#include <qwt_plot_grid.h>
#include <qwt_text.h>
#include <qwt_legend.h>
#include <qwt_series_data.h>
// span slider specials
#include <qxtspanslider.h>
#include <QCleanlooksStyle>
// tooltip
#include "LTMWindow.h"
AllPlotWindow::AllPlotWindow(MainWindow *mainWindow) :
GcChartWindow(mainWindow), current(NULL), mainWindow(mainWindow), active(false), stale(true)
{
setInstanceName("Ride Plot Window");
QWidget *c = new QWidget;
QVBoxLayout *clv = new QVBoxLayout(c);
QHBoxLayout *cl = new QHBoxLayout;
QFormLayout *cl1 = new QFormLayout;
QFormLayout *cl2 = new QFormLayout;
QFormLayout *cl3 = new QFormLayout;
cl->addLayout(cl1);
cl->addLayout(cl2);
clv->addLayout(cl3);
clv->addWidget(new QLabel("")); //spacer
clv->addLayout(cl);
clv->addStretch();
setControls(c);
setContentsMargins(0,0,0,0);
// Main layout
//QGridLayout *mainLayout = new QGridLayout();
//mainLayout->setContentsMargins(2,2,2,2);
//
// reveal controls widget
//
// reveal controls
rSmooth = new QLabel(tr("Smooth"));
rSmoothEdit = new QLineEdit();
rSmoothEdit->setFixedWidth(30);
rSmoothSlider = new QSlider(Qt::Horizontal);
rSmoothSlider->setTickPosition(QSlider::TicksBelow);
rSmoothSlider->setTickInterval(10);
rSmoothSlider->setMinimum(1);
rSmoothSlider->setMaximum(100);
rStack = new QCheckBox(tr("Stacked"));
rFull = new QCheckBox(tr("Fullplot"));
// layout reveal controls
QHBoxLayout *r = new QHBoxLayout;
r->setContentsMargins(0,0,0,0);
r->addStretch();
r->addWidget(rSmooth);
r->addWidget(rSmoothEdit);
r->addWidget(rSmoothSlider);
QVBoxLayout *v = new QVBoxLayout;
v->addWidget(rStack);
v->addWidget(rFull);
r->addSpacing(20);
r->addLayout(v);
r->addStretch();
setRevealLayout(r);
//revealControls->setLayout(r);
// hide them initially
//revealControls->hide();
// setup the controls
QLabel *showLabel = new QLabel(tr("Show"), c);
showStack = new QCheckBox(tr("Stacked view"), this);
showStack->setCheckState(Qt::Unchecked);
cl1->addRow(showLabel, showStack);
stackWidth = 15;
stackZoomUp = new QwtArrowButton(1, Qt::UpArrow,this);
stackZoomUp->setFixedHeight(15);
stackZoomUp->setFixedWidth(15);
stackZoomUp->setEnabled(false);
stackZoomUp->setContentsMargins(0,0,0,0);
stackZoomUp->setFlat(true);
cl1->addRow(new QLabel(""),stackZoomUp);
stackZoomDown = new QwtArrowButton(1, Qt::DownArrow,this);
stackZoomDown->setFixedHeight(15);
stackZoomDown->setFixedWidth(15);
stackZoomDown->setEnabled(false);
stackZoomDown->setContentsMargins(0,0,0,0);
stackZoomDown->setFlat(true);
cl1->addRow(new QLabel(""), stackZoomDown);
showFull = new QCheckBox(tr("Full plot"), this);
showFull->setCheckState(Qt::Checked);
cl1->addRow(new QLabel(""), showFull);
paintBrush = new QCheckBox(tr("Fill Curves"), this);
paintBrush->setCheckState(Qt::Unchecked);
cl1->addRow(new QLabel(""), paintBrush);
showGrid = new QCheckBox(tr("Grid"), this);
showGrid->setCheckState(Qt::Checked);
cl1->addRow(new QLabel(""), showGrid);
showHr = new QCheckBox(tr("Heart Rate"), this);
showHr->setCheckState(Qt::Checked);
cl2->addRow(new QLabel(tr("Data series")), showHr);
showSpeed = new QCheckBox(tr("Speed"), this);
showSpeed->setCheckState(Qt::Checked);
cl2->addRow(new QLabel(""), showSpeed);
showCad = new QCheckBox(tr("Cadence"), this);
showCad->setCheckState(Qt::Checked);
cl2->addRow(new QLabel(""), showCad);
showAlt = new QCheckBox(tr("Altitude"), this);
showAlt->setCheckState(Qt::Checked);
cl2->addRow(new QLabel(""), showAlt);
showTemp = new QCheckBox(tr("Temperature"), this);
showTemp->setCheckState(Qt::Checked);
cl2->addRow(new QLabel(""), showTemp);
showWind = new QCheckBox(tr("Headwind"), this);
showWind->setCheckState(Qt::Checked);
cl2->addRow(new QLabel(""), showWind);
showTorque = new QCheckBox(tr("Torque"), this);
showTorque->setCheckState(Qt::Checked);
cl2->addRow(new QLabel(""), showTorque);
showBalance = new QCheckBox(tr("Power balance"), this);
showBalance->setCheckState(Qt::Checked);
cl2->addRow(new QLabel(""), showBalance);
showPower = new QComboBox();
showPower->addItem(tr("Power + shade"));
showPower->addItem(tr("Power - shade"));
showPower->addItem(tr("No Power"));
cl3->addRow(new QLabel(tr("Shading")), showPower);
showPower->setCurrentIndex(0);
comboDistance = new QComboBox();
comboDistance->addItem(tr("Time"));
comboDistance->addItem(tr("Distance"));
cl3->addRow(new QLabel(tr("X Axis")), comboDistance);
QLabel *smoothLabel = new QLabel(tr("Smooth"), this);
smoothLineEdit = new QLineEdit(this);
smoothLineEdit->setFixedWidth(40);
smoothSlider = new QSlider(Qt::Horizontal);
smoothSlider->setTickPosition(QSlider::TicksBelow);
smoothSlider->setTickInterval(10);
smoothSlider->setMinimum(1);
smoothSlider->setMaximum(600);
smoothLineEdit->setValidator(new QIntValidator(smoothSlider->minimum(),
smoothSlider->maximum(),
smoothLineEdit));
QHBoxLayout *smoothLayout = new QHBoxLayout;
smoothLayout->addWidget(smoothLineEdit);
smoothLayout->addWidget(smoothSlider);
cl3->addRow(smoothLabel, smoothLayout);
allPlot = new AllPlot(this, mainWindow);
allPlot->setInstanceName("allPlot");
allPlot->setContentsMargins(0,0,0,0);
// sort out default values
smoothSlider->setValue(allPlot->smooth);
smoothLineEdit->setText(QString("%1").arg(allPlot->smooth));
rSmoothSlider->setValue(allPlot->smooth);
rSmoothEdit->setText(QString("%1").arg(allPlot->smooth));
allZoomer = new QwtPlotZoomer(allPlot->canvas());
allZoomer->setRubberBand(QwtPicker::RectRubberBand);
allZoomer->setRubberBandPen(GColor(CPLOTSELECT));
allZoomer->setTrackerMode(QwtPicker::AlwaysOff);
allZoomer->setEnabled(true);
// TODO: Hack for OS X one-button mouse
// allZoomer->initMousePattern(1);
// RightButton: zoom out by 1
// Ctrl+RightButton: zoom out to full size
allZoomer->setMousePattern(QwtEventPattern::MouseSelect1,
Qt::LeftButton, Qt::ShiftModifier);
allZoomer->setMousePattern(QwtEventPattern::MouseSelect2,
Qt::RightButton, Qt::ControlModifier);
allZoomer->setMousePattern(QwtEventPattern::MouseSelect3,
Qt::RightButton);
allPanner = new QwtPlotPanner(allPlot->canvas());
allPanner->setMouseButton(Qt::MidButton);
// TODO: zoomer doesn't interact well with automatic axis resizing
// tooltip on hover over point
allPlot->tooltip = new LTMToolTip(QwtPlot::xBottom, QwtPlot::yLeft,
QwtPicker::VLineRubberBand,
QwtPicker::AlwaysOn,
allPlot->canvas(),
"");
allPlot->tooltip->setRubberBand(QwtPicker::VLineRubberBand);
allPlot->tooltip->setMousePattern(QwtEventPattern::MouseSelect1, Qt::LeftButton);
allPlot->tooltip->setTrackerPen(QColor(Qt::black));
QColor inv(Qt::white);
inv.setAlpha(0);
allPlot->tooltip->setRubberBandPen(inv);
allPlot->tooltip->setEnabled(true);
allPlot->_canvasPicker = new LTMCanvasPicker(allPlot);
connect(allPlot->_canvasPicker, SIGNAL(pointHover(QwtPlotCurve*, int)), allPlot, SLOT(pointHover(QwtPlotCurve*, int)));
connect(allPlot->tooltip, SIGNAL(moved(const QPoint &)), this, SLOT(plotPickerMoved(const QPoint &)));
connect(allPlot->tooltip, SIGNAL(appended(const QPoint &)), this, SLOT(plotPickerSelected(const QPoint &)));
QwtPlotMarker* allMarker1 = new QwtPlotMarker();
allMarker1->setLineStyle(QwtPlotMarker::VLine);
allMarker1->attach(allPlot);
allMarker1->setLabelAlignment(Qt::AlignTop|Qt::AlignRight);
allPlot->allMarker1=allMarker1;
QwtPlotMarker* allMarker2 = new QwtPlotMarker();
allMarker2->setLineStyle(QwtPlotMarker::VLine);
allMarker2->attach(allPlot);
allMarker2->setLabelAlignment(Qt::AlignTop|Qt::AlignRight);
allPlot->allMarker2=allMarker2;
// Container widgets should not paint
// since they tend to use naff defaults and
// 'complicate' or 'make busy' the general
// look and feel
QPalette palette;
palette.setBrush(QPalette::Background, Qt::NoBrush);
//
// stack view
//
stackPlotLayout = new QVBoxLayout();
stackPlotLayout->setSpacing(0);
stackPlotLayout->setContentsMargins(0,0,0,0);
stackWidget = new QWidget();
stackWidget->setAutoFillBackground(false);
stackWidget->setPalette(palette);
stackWidget->setLayout(stackPlotLayout);
stackFrame = new QScrollArea();
stackFrame->hide();
stackFrame->setPalette(palette);
stackFrame->setAutoFillBackground(false);
stackFrame->setWidgetResizable(true);
stackFrame->setWidget(stackWidget);
stackFrame->setFrameStyle(QFrame::NoFrame);
stackFrame->setContentsMargins(0,0,0,0);
//
// allPlot view
//
allPlotLayout = new QVBoxLayout;
allPlotLayout->setSpacing(0);
allPlotLayout->setContentsMargins(0,0,0,0);
allPlotFrame = new QScrollArea();
allPlotFrame->setFrameStyle(QFrame::NoFrame);
allPlotFrame->setAutoFillBackground(false);
allPlotFrame->setPalette(palette);
allPlotFrame->setContentsMargins(0,0,0,0);
spanSlider = new QxtSpanSlider(Qt::Horizontal);
spanSlider->setHandleMovementMode(QxtSpanSlider::NoOverlapping);
spanSlider->setLowerValue(0);
spanSlider->setUpperValue(15);
QFont small;
small.setPointSize(6);
scrollLeft = new QPushButton("<");
scrollLeft->setFont(small);
scrollLeft->setAutoRepeat(true);
scrollLeft->setFixedHeight(16);
scrollLeft->setFixedWidth(16);
scrollLeft->setContentsMargins(0,0,0,0);
scrollRight = new QPushButton(">");
scrollRight->setFont(small);
scrollRight->setAutoRepeat(true);
scrollRight->setFixedHeight(16);
scrollRight->setFixedWidth(16);
scrollRight->setContentsMargins(0,0,0,0);
#ifdef Q_OS_MAC
// BUG in QMacStyle and painting of spanSlider
// so we use a plain style to avoid it, but only
// on a MAC, since win and linux are fine
QCleanlooksStyle *style = new QCleanlooksStyle();
spanSlider->setStyle(style);
scrollLeft->setStyle(style);
scrollRight->setStyle(style);
#endif
fullPlot = new AllPlot(this, mainWindow);
fullPlot->setInstanceName("fullPlot");
fullPlot->grid->enableY(false);
fullPlot->setFixedHeight(100);
QPalette def;
//fullPlot->setCanvasBackground(def.color(QPalette::Window));
fullPlot->setCanvasBackground(Qt::white);
fullPlot->setCanvasLineWidth(0);
fullPlot->enableAxis(QwtPlot::yLeft, false);
fullPlot->enableAxis(QwtPlot::yLeft2, false);
fullPlot->enableAxis(QwtPlot::yRight, false);
fullPlot->enableAxis(QwtPlot::yRight2, false);
fullPlot->enableAxis(QwtPlot::xBottom, false);
//fullPlot->legend()->clear();
//fullPlot->setTitle("");
fullPlot->setContentsMargins(0,0,0,0);
allPlotLayout->addWidget(allPlot);
allPlotFrame->setLayout(allPlotLayout);
// controls...
controlsLayout = new QGridLayout;
controlsLayout->setSpacing(0);
controlsLayout->setContentsMargins(5,5,5,5);
controlsLayout->addWidget(fullPlot, 0,1);
controlsLayout->addWidget(spanSlider, 1,1);
controlsLayout->addWidget(scrollLeft,1,0);
controlsLayout->addWidget(scrollRight,1,2);
controlsLayout->setRowStretch(0, 10);
controlsLayout->setRowStretch(1, 1);
controlsLayout->setContentsMargins(0,0,0,0);
#ifdef Q_OS_MAC
// macs dpscing is weird
//controlsLayout->setSpacing(5);
#else
controlsLayout->setSpacing(0);
#endif
allPlotLayout->addLayout(controlsLayout);
allPlotLayout->setStretch(0,100);
allPlotLayout->setStretch(1,20);
QVBoxLayout *vlayout = new QVBoxLayout;
vlayout->setContentsMargins(2,0,2,2);
vlayout->setSpacing(0);
vlayout->addWidget(allPlotFrame);
vlayout->addWidget(stackFrame);
vlayout->setSpacing(1);
//mainLayout->addLayout(vlayout,0,0);
//mainLayout->addWidget(revealBackground,0,0, Qt::AlignTop);
//mainLayout->addWidget(revealControls,0,0, Qt::AlignTop);
//revealBackground->raise();
//revealControls->raise();
setChartLayout(vlayout);
// common controls
connect(showPower, SIGNAL(currentIndexChanged(int)), this, SLOT(setShowPower(int)));
connect(showHr, SIGNAL(stateChanged(int)), this, SLOT(setShowHr(int)));
connect(showSpeed, SIGNAL(stateChanged(int)), this, SLOT(setShowSpeed(int)));
connect(showCad, SIGNAL(stateChanged(int)), this, SLOT(setShowCad(int)));
connect(showAlt, SIGNAL(stateChanged(int)), this, SLOT(setShowAlt(int)));
connect(showTemp, SIGNAL(stateChanged(int)), this, SLOT(setShowTemp(int)));
connect(showWind, SIGNAL(stateChanged(int)), this, SLOT(setShowWind(int)));
connect(showTorque, SIGNAL(stateChanged(int)), this, SLOT(setShowTorque(int)));
connect(showBalance, SIGNAL(stateChanged(int)), this, SLOT(setShowBalance(int)));
connect(showGrid, SIGNAL(stateChanged(int)), this, SLOT(setShowGrid(int)));
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(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)));
connect(smoothSlider, SIGNAL(valueChanged(int)), this, SLOT(setSmoothingFromSlider()));
connect(smoothLineEdit, SIGNAL(editingFinished()), this, SLOT(setSmoothingFromLineEdit()));
connect(rSmoothSlider, SIGNAL(valueChanged(int)), this, SLOT(setrSmoothingFromSlider()));
connect(rSmoothEdit, SIGNAL(editingFinished()), this, SLOT(setrSmoothingFromLineEdit()));
// normal view
connect(spanSlider, SIGNAL(lowerPositionChanged(int)), this, SLOT(zoomChanged()));
connect(spanSlider, SIGNAL(upperPositionChanged(int)), this, SLOT(zoomChanged()));
// stacked view
connect(stackZoomUp, SIGNAL(clicked()), this, SLOT(setStackZoomUp()));
connect(stackZoomDown, SIGNAL(clicked()), this, SLOT(setStackZoomDown()));
connect(scrollLeft, SIGNAL(clicked()), this, SLOT(moveLeft()));
connect(scrollRight, SIGNAL(clicked()), this, SLOT(moveRight()));
// GC signals
//connect(mainWindow, SIGNAL(rideSelected()), this, SLOT(rideSelected()));
connect(this, SIGNAL(rideItemChanged(RideItem*)), this, SLOT(rideSelected()));
connect(mainWindow, SIGNAL(rideDirty(RideItem*)), this, SLOT(rideSelected()));
connect(mainWindow, SIGNAL(zonesChanged()), this, SLOT(zonesChanged()));
connect(mainWindow, SIGNAL(intervalsChanged()), this, SLOT(intervalsChanged()));
connect(mainWindow, SIGNAL(intervalZoom(IntervalItem*)), this, SLOT(zoomInterval(IntervalItem*)));
connect(mainWindow, SIGNAL(intervalSelected()), this, SLOT(intervalSelected()));
connect(mainWindow, SIGNAL(configChanged()), allPlot, SLOT(configChanged()));
connect(mainWindow, SIGNAL(configChanged()), this, SLOT(configChanged()));
connect(mainWindow, SIGNAL(rideDeleted(RideItem*)), this, SLOT(rideDeleted(RideItem*)));
// set initial colors
configChanged();
}
void
AllPlotWindow::configChanged()
{
// we're going to replot, but only if we're active
// and all the other guff
RideItem *ride = myRideItem;
if (!amVisible()) {
stale = true;
return;
}
// ignore if null, or manual / empty
if (!ride || !ride->ride() || !ride->ride()->dataPoints().count()) return;
// ok replot with the new config!
redrawFullPlot();
redrawAllPlot();
redrawStackPlot();
}
void
AllPlotWindow::redrawAllPlot()
{
if (!showStack->isChecked() && current) {
RideItem *ride = current;
int startidx, stopidx;
if (fullPlot->bydist == true) {
startidx =ride->ride()->distanceIndex((double)spanSlider->lowerValue()/(double)1000);
stopidx = ride->ride()->distanceIndex((double)spanSlider->upperValue()/(double)1000);
} else {
startidx = ride->ride()->timeIndex(spanSlider->lowerValue());
stopidx = ride->ride()->timeIndex(spanSlider->upperValue());
}
// need more than 1 sample to plot
if (stopidx - startidx > 1) {
allPlot->setDataFromPlot(fullPlot, startidx, stopidx);
allZoomer->setZoomBase();
//allPlot->setTitle("");
allPlot->replot();
}
}
}
void
AllPlotWindow::redrawFullPlot()
{
// always peformed sincethe data is used
// by both the stack plots and the allplot
RideItem *ride = current;
// null rides are possible on new cyclist
if (!ride) return;
// hide the usual plot decorations etc
fullPlot->setShowPower(1);
//We now use the window background color
//fullPlot->setCanvasBackground(GColor(CPLOTTHUMBNAIL));
fullPlot->setCanvasLineWidth(0);
fullPlot->grid->enableY(false);
fullPlot->enableAxis(QwtPlot::yLeft, false);
fullPlot->enableAxis(QwtPlot::yLeft2, false);
fullPlot->enableAxis(QwtPlot::yRight, false);
fullPlot->enableAxis(QwtPlot::yRight2, false);
fullPlot->enableAxis(QwtPlot::xBottom, false);
//fullPlot->legend()->clear();
//fullPlot->setTitle("");
if (fullPlot->bydist)
fullPlot->setAxisScale(QwtPlot::xBottom,
ride->ride()->dataPoints().first()->km * (fullPlot->useMetricUnits ? 1 : MILES_PER_KM),
ride->ride()->dataPoints().last()->km * (fullPlot->useMetricUnits ? 1 : MILES_PER_KM));
else
fullPlot->setAxisScale(QwtPlot::xBottom, ride->ride()->dataPoints().first()->secs/60,
ride->ride()->dataPoints().last()->secs/60);
fullPlot->replot();
}
void
AllPlotWindow::redrawStackPlot()
{
if (showStack->isChecked()) {
// turn off display updates whilst we
// do this, it takes a while and is prone
// 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();
// we're done, go update the display now
stackFrame->setUpdatesEnabled(true);
}
}
void
AllPlotWindow::zoomChanged()
{
redrawAllPlot();
}
void
AllPlotWindow::moveLeft()
{
// move across by 5% of the span, or to zero if not much left
int span = spanSlider->upperValue() - spanSlider->lowerValue();
int delta = span / 20;
if (delta > (spanSlider->lowerValue() - spanSlider->minimum()))
delta = spanSlider->lowerValue() - spanSlider->minimum();
spanSlider->setLowerValue(spanSlider->lowerValue()-delta);
spanSlider->setUpperValue(spanSlider->upperValue()-delta);
zoomChanged();
}
void
AllPlotWindow::moveRight()
{
// move across by 5% of the span, or to zero if not much left
int span = spanSlider->upperValue() - spanSlider->lowerValue();
int delta = span / 20;
if (delta > (spanSlider->maximum() - spanSlider->upperValue()))
delta = spanSlider->maximum() - spanSlider->upperValue();
spanSlider->setLowerValue(spanSlider->lowerValue()+delta);
spanSlider->setUpperValue(spanSlider->upperValue()+delta);
zoomChanged();
}
void
AllPlotWindow::rideSelected()
{
RideItem *ride = myRideItem;
if (ride == NULL) current = NULL;
// ignore if not active
if (!amVisible()) {
stale = true;
return;
}
// ignore if null, or manual / empty
if (!ride || !ride->ride() || !ride->ride()->dataPoints().count()) {
current = NULL;
setIsBlank(true);
return;
}
else
setIsBlank(false);
// we already plotted it!
if (ride == current && stale == false) return;
// ok, its now the current ride
current = ride;
// clear any previous selections
clearSelection();
// setup the control widgets, dependant on
// data present in this ride, needs to happen
// before we set the plots below...
setAllPlotWidgets(ride);
// setup the charts to reflect current ride selection
fullPlot->setDataFromRide(ride);
// Fixup supplied by Josef Gebel
int startidx, stopidx;
if ( fullPlot->bydist == true ) {
startidx = ride->ride()->distanceIndex( ( double ) spanSlider->lowerValue() / 1000.0 );
stopidx = ride->ride()->distanceIndex( ( double ) spanSlider->upperValue() / 1000.0 );
} else {
startidx = ride->ride()->timeIndex( spanSlider->lowerValue() );
stopidx = ride->ride()->timeIndex( spanSlider->upperValue() );
}
allPlot->setDataFromPlot( fullPlot, startidx, stopidx );
// redraw all the plots, they will check
// to see if they are currently visible
// and only redraw if neccessary
redrawFullPlot();
redrawAllPlot();
setupStackPlots();
stale = false;
}
void
AllPlotWindow::rideDeleted(RideItem *ride)
{
if (ride == myRideItem) {
// we have nothing to show
setProperty("ride", QVariant::fromValue<RideItem*>(NULL));
// notify all the plots, because when zones are redrawn
// they will try and reference AllPlot::rideItem
setAllPlotWidgets(NULL);
fullPlot->setDataFromRide(NULL);
}
}
void
AllPlotWindow::zonesChanged()
{
if (!amVisible()) {
stale = true;
return;
}
allPlot->refreshZoneLabels();
allPlot->replot();
}
void
AllPlotWindow::intervalsChanged()
{
if (!amVisible()) {
stale = true;
return;
}
// show selection on fullplot too
fullPlot->refreshIntervalMarkers();
fullPlot->replot();
// allPlot of course
allPlot->refreshIntervalMarkers();
allPlot->replot();
// and then the stacked plot
foreach (AllPlot *plot, allPlots) {
plot->refreshIntervalMarkers();
plot->replot();
}
}
void
AllPlotWindow::intervalSelected()
{
if (!amVisible()) {
stale = true;
return;
}
if (active == true) return;
// the intervals are highlighted
// in the plot automatically, we just
// need to replot, depending upon
// which mode we are in...
hideSelection();
if (showStack->isChecked()) {
foreach (AllPlot *plot, allPlots)
plot->replot();
} else {
fullPlot->replot();
allPlot->replot();
}
}
void
AllPlotWindow::setStacked(int value)
{
showStack->setChecked(value);
rStack->setChecked(value);
}
void
AllPlotWindow::setSmoothingFromSlider()
{
// active tells us we have been triggered by
// the setSmoothingFromLineEdit which will also
// recalculates smoothing, lets not double up...
if (active) return;
else active = true;
if (allPlot->smooth != smoothSlider->value()) {
setSmoothing(smoothSlider->value());
smoothLineEdit->setText(QString("%1").arg(fullPlot->smooth));
rSmoothEdit->setText(QString("%1").arg(fullPlot->smooth));
rSmoothSlider->setValue(rSmoothSlider->value());
}
active = false;
}
void
AllPlotWindow::setrSmoothingFromSlider()
{
// active tells us we have been triggered by
// the setSmoothingFromLineEdit which will also
// recalculates smoothing, lets not double up...
if (active) return;
else active = true;
if (allPlot->smooth != rSmoothSlider->value()) {
setSmoothing(rSmoothSlider->value());
rSmoothEdit->setText(QString("%1").arg(fullPlot->smooth));
smoothSlider->setValue(rSmoothSlider->value());
smoothLineEdit->setText(QString("%1").arg(fullPlot->smooth));
}
active = false;
}
void
AllPlotWindow::setSmoothingFromLineEdit()
{
// active tells us we have been triggered by
// the setSmoothingFromSlider which will also
// recalculates smoothing, lets not double up...
if (active) return;
else active = true;
int value = smoothLineEdit->text().toInt();
if (value != fullPlot->smooth) {
smoothSlider->setValue(value);
rSmoothSlider->setValue(value);
setSmoothing(value);
}
active = false;
}
void
AllPlotWindow::setrSmoothingFromLineEdit()
{
// active tells us we have been triggered by
// the setSmoothingFromSlider which will also
// recalculates smoothing, lets not double up...
if (active) return;
else active = true;
int value = rSmoothEdit->text().toInt();
if (value != fullPlot->smooth) {
rSmoothSlider->setValue(value);
smoothSlider->setValue(value);
setSmoothing(value);
}
active = false;
}
void
AllPlotWindow::setAllPlotWidgets(RideItem *ride)
{
// this routine sets up the display widgets
// depending upon what data is available in the
// ride. It also hides/shows widgets depending
// upon wether we are in 'normal' mode or
// stacked plot mode
if (!ride) return;
// checkboxes to show/hide specific data series...
const RideFileDataPresent *dataPresent = ride->ride()->areDataPresent();
if (ride->ride() && ride->ride()->deviceType() != QString("Manual CSV")) {
showPower->setEnabled(dataPresent->watts);
showHr->setEnabled(dataPresent->hr);
showSpeed->setEnabled(dataPresent->kph);
showCad->setEnabled(dataPresent->cad);
showAlt->setEnabled(dataPresent->alt);
showTemp->setEnabled(dataPresent->temp);
showWind->setEnabled(dataPresent->headwind);
showTorque->setEnabled(dataPresent->nm);
showBalance->setEnabled(dataPresent->lrbalance);
} else {
showPower->setEnabled(false);
showHr->setEnabled(false);
showSpeed->setEnabled(false);
showCad->setEnabled(false);
showAlt->setEnabled(false);
showTemp->setEnabled(false);
showWind->setEnabled(false);
showTorque->setEnabled(false);
showBalance->setEnabled(false);
}
// turn on/off shading, if it's not available
bool shade;
if (dataPresent->watts) shade = (showPower->currentIndex() == 0);
else shade = false;
allPlot->setShadeZones(shade);
foreach (AllPlot *plot, allPlots) plot->setShadeZones(shade);
allPlot->setShowGrid(showGrid->checkState() == Qt::Checked);
foreach (AllPlot *plot, allPlots) plot->setShowGrid(showGrid->checkState() == Qt::Checked);
// set the SpanSlider for the ride length, by default
// show the entire ride (the user can adjust later)
if (fullPlot->bydist == false) {
spanSlider->setMinimum(ride->ride()->dataPoints().first()->secs);
spanSlider->setMaximum(ride->ride()->dataPoints().last()->secs);
spanSlider->setLowerValue(spanSlider->minimum());
spanSlider->setUpperValue(spanSlider->maximum());
} else {
spanSlider->setMinimum(ride->ride()->dataPoints().first()->km * 1000);
spanSlider->setMaximum(ride->ride()->dataPoints().last()->km * 1000);
spanSlider->setLowerValue(spanSlider->minimum());
spanSlider->setUpperValue(spanSlider->maximum());
}
// now set the visible plots, depending upon whether
// we are in stacked mode or not
if (showStack->isChecked()) {
// hide normal view
allPlotFrame->hide();
allPlot->hide();
fullPlot->hide();
controlsLayout->setRowStretch(0, 0);
controlsLayout->setRowStretch(1, 0);
spanSlider->hide();
scrollLeft->hide();
scrollRight->hide();
stackZoomUp->setEnabled(stackZoomUpShouldEnable(stackWidth));
stackZoomDown->setEnabled(stackZoomDownShouldEnable(stackWidth));
// show stacked view
stackFrame->show();
} else {
// hide stack view
stackZoomDown->setEnabled(false);
stackZoomUp->setEnabled(false);
stackFrame->hide();
// show normal view
allPlotFrame->show();
allPlot->show();
if (showFull->isChecked()) {
fullPlot->show();
controlsLayout->setRowStretch(0, 100);
controlsLayout->setRowStretch(1, 20);
spanSlider->show();
scrollLeft->show();
scrollRight->show();
} else {
fullPlot->hide();
controlsLayout->setRowStretch(0, 100);
controlsLayout->setRowStretch(1, 00);
spanSlider->hide();
scrollLeft->hide();
scrollRight->hide();
}
stackZoomUp->setEnabled(false);
stackZoomDown->setEnabled(false);
}
}
void
AllPlotWindow::zoomInterval(IntervalItem *which)
{
// use the span slider to highlight
// when we are in normal mode.
// set them to maximums to avoid overlapping
// when we set them below, daft but works
spanSlider->setLowerValue(spanSlider->minimum());
spanSlider->setUpperValue(spanSlider->maximum());
if (!allPlot->bydist) {
spanSlider->setLowerValue(which->start);
spanSlider->setUpperValue(which->stop);
} else {
spanSlider->setLowerValue(which->startKM * (double)1000);
spanSlider->setUpperValue(which->stopKM * (double)1000);
}
zoomChanged();
}
void
AllPlotWindow::plotPickerSelected(const QPoint &pos)
{
QwtPlotPicker* pick = qobject_cast<QwtPlotPicker *>(sender());
AllPlot* plot = qobject_cast<AllPlot *>(pick->plot());
double xValue = plot->invTransform(QwtPlot::xBottom, pos.x());
setStartSelection(plot, xValue);
}
void
AllPlotWindow::plotPickerMoved(const QPoint &pos)
{
QString name = QString("Selection #%1 ").arg(selection);
// which picker and plot send this signal?
QwtPlotPicker* pick = qobject_cast<QwtPlotPicker *>(sender());
AllPlot* plot = qobject_cast<AllPlot *>(pick->plot());
int posX = pos.x();
int posY = plot->y()+pos.y()+50;
if (showStack->isChecked()) {
// need to highlight across stacked plots
foreach (AllPlot *_plot, allPlots) {
// mark the start of selection on every plot
_plot->allMarker1->setValue(plot->allMarker1->value());
if (_plot->y()<=plot->y() && posY<_plot->y()){
if (_plot->transform(QwtPlot::xBottom, _plot->allMarker2->xValue())>0) {
setEndSelection(_plot, 0, false, name);
_plot->allMarker2->setLabel(QString(""));
}
} else if (_plot->y()>=plot->y() && posY>_plot->y()+_plot->height()) {
if (_plot->transform(QwtPlot::xBottom, _plot->allMarker2->xValue())<plot->width()){
setEndSelection(_plot, _plot->transform(QwtPlot::xBottom, plot->width()), false, name);
}
}
else if (posY>_plot->y() && posY<_plot->y()+_plot->height()) {
if (pos.x()<6) {
posX = 6;
} else if (!_plot->bydist && pos.x()>_plot->transform(QwtPlot::xBottom,
fullPlot->timeArray[fullPlot->timeArray.size()-1])) {
posX = _plot->transform(QwtPlot::xBottom, fullPlot->timeArray[fullPlot->timeArray.size()-1]);
} else if (plot->bydist && pos.x()>_plot->transform(QwtPlot::xBottom,
fullPlot->distanceArray[fullPlot->distanceArray.size()-1])) {
posX = fullPlot->transform(QwtPlot::xBottom,
fullPlot->distanceArray[fullPlot->distanceArray.size()-1]);
}
setEndSelection(_plot, _plot->invTransform(QwtPlot::xBottom, posX), true, name);
if (plot->y()<_plot->y()) {
plot->allMarker1->setLabel(_plot->allMarker1->label());
plot->allMarker2->setLabel(_plot->allMarker2->label());
}
} else {
_plot->allMarker1->hide();
_plot->allMarker2->hide();
}
}
} else {
// working on AllPlot
double xValue = plot->invTransform(QwtPlot::xBottom, pos.x());
setEndSelection(plot, xValue, true, name);
}
}
void
AllPlotWindow::setStartSelection(AllPlot* plot, double xValue)
{
QwtPlotMarker* allMarker1 = plot->allMarker1;
selection++;
if (!allMarker1->isVisible() || allMarker1->xValue() != xValue) {
allMarker1->setValue(xValue, plot->bydist ? 0 : 100);
allMarker1->show();
}
}
void
AllPlotWindow::setEndSelection(AllPlot* plot, double xValue, bool newInterval, QString name)
{
active = true;
QwtPlotMarker* allMarker1 = plot->allMarker1;
QwtPlotMarker* allMarker2 = plot->allMarker2;
bool useMetricUnits = plot->useMetricUnits;
if (!allMarker2->isVisible() || allMarker2->xValue() != xValue) {
allMarker2->setValue(xValue, plot->bydist ? 0 : 100);
allMarker2->show();
double x1, x2; // time or distance depending upon mode selected
// swap to low-high if neccessary
if (allMarker1->xValue()>allMarker2->xValue()){
x2 = allMarker1->xValue();
x1 = allMarker2->xValue();
} else {
x1 = allMarker1->xValue();
x2 = allMarker2->xValue();
}
RideItem *ride = current;
double distance1 = -1;
double distance2 = -1;
double duration1 = -1;
double duration2 = -1;
double secsMoving = 0;
double wattsTotal = 0;
double bpmTotal = 0;
int arrayLength = 0;
// if we are in distance mode then x1 and x2 are distances
// we need to make sure they are in KM for the rest of this
// code.
if (plot->bydist && useMetricUnits == false) {
x1 *= KM_PER_MILE;
x2 *= KM_PER_MILE;
}
foreach (const RideFilePoint *point, ride->ride()->dataPoints()) {
if ((plot->bydist==true && point->km>=x1 && point->km<x2) ||
(plot->bydist==false && point->secs/60>=x1 && point->secs/60<x2)) {
if (distance1 == -1) distance1 = point->km;
distance2 = point->km;
if (duration1 == -1) duration1 = point->secs;
duration2 = point->secs;
if (point->kph > 0.0)
secsMoving += ride->ride()->recIntSecs();
wattsTotal += point->watts;
bpmTotal += point->hr;
++arrayLength;
}
}
QString s("\n%1%2 %3 %4\n%5%6 %7%8 %9%10");
s = s.arg(useMetricUnits ? distance2-distance1 : (distance2-distance1)*MILES_PER_KM, 0, 'f', 2);
s = s.arg((useMetricUnits? "km":"mi"));
s = s.arg(time_to_string(duration2-duration1));
if (duration2-duration1-secsMoving>1)
s = s.arg("("+time_to_string(secsMoving)+")");
else
s = s.arg("");
s = s.arg((useMetricUnits ? 1 : MILES_PER_KM) * (distance2-distance1)/secsMoving*3600, 0, 'f', 1);
s = s.arg((useMetricUnits? "km/h":"mph"));
if (wattsTotal>0) {
s = s.arg(wattsTotal/arrayLength, 0, 'f', 1);
s = s.arg("W");
}
else{
s = s.arg("");
s = s.arg("");
}
if (bpmTotal>0) {
s = s.arg(bpmTotal/arrayLength, 0, 'f', 0);
s = s.arg("bpm");
}
else {
s = s.arg("");
s = s.arg("");
}
QwtText label;
label.setColor(GColor(CPLOTMARKER));
label.setFont(QFont());
label.setText(s);
if (allMarker1->xValue()<allMarker2->xValue()) {
allMarker1->setLabel(label);
allMarker2->setLabel(QString(""));
}
else {
allMarker2->setLabel(label);
allMarker1->setLabel(QString(""));
}
if (newInterval) {
QTreeWidgetItem *allIntervals = mainWindow->mutableIntervalItems();
int count = allIntervals->childCount();
// are we adjusting an existing interval? - if so delete it and readd it
if (count > 0) {
IntervalItem *bottom = (IntervalItem *) allIntervals->child(count-1);
if (bottom->text(0).startsWith(name)) delete allIntervals->takeChild(count-1);
}
// add average power to the end of the selection name
name += QString("(%1 watts)").arg(round((wattsTotal && arrayLength) ? wattsTotal/arrayLength : 0));
QTreeWidgetItem *last = new IntervalItem(ride->ride(), name, duration1, duration2, distance1, distance2,
allIntervals->childCount()+1);
last->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled);
allIntervals->addChild(last);
// select this new interval
mainWindow->intervalTreeWidget()->setItemSelected(last, true);
// now update the RideFileIntervals and all the plots etc
mainWindow->updateRideFileIntervals();
}
}
active = false;
}
void
AllPlotWindow::clearSelection()
{
selection = 0;
allPlot->allMarker1->setVisible(false);
allPlot->allMarker2->setVisible(false);
foreach (AllPlot *plot, allPlots) {
plot->allMarker1->setVisible(false);
plot->allMarker2->setVisible(false);
}
}
void
AllPlotWindow::hideSelection()
{
if (showStack->isChecked()) {
foreach (AllPlot *plot, allPlots) {
plot->allMarker1->setVisible(false);
plot->allMarker2->setVisible(false);
plot->replot();
}
} else {
fullPlot->replot();
allPlot->allMarker1->setVisible(false);
allPlot->allMarker2->setVisible(false);
allPlot->replot();
}
}
void
AllPlotWindow::setShowPower(int value)
{
showPower->setCurrentIndex(value);
//if (!current) return;
// we only show the power shading on the
// allPlot / stack plots, not on the fullPlot
allPlot->setShowPower(value);
if (!showStack->isChecked())
allPlot->replot();
// redraw
foreach (AllPlot *plot, allPlots)
plot->setShowPower(value);
// now replot 'em - to avoid flicker
if (showStack->isChecked()) {
stackFrame->setUpdatesEnabled(false); // don't repaint whilst we do this...
foreach (AllPlot *plot, allPlots)
plot->replot();
stackFrame->setUpdatesEnabled(true); // don't repaint whilst we do this...
}
}
void
AllPlotWindow::setShowHr(int value)
{
showHr->setChecked(value);
//if (!current) return;
bool checked = ( ( value == Qt::Checked ) && showHr->isEnabled()) ? true : false;
allPlot->setShowHr(checked);
foreach (AllPlot *plot, allPlots)
plot->setShowHr(checked);
}
void
AllPlotWindow::setShowSpeed(int value)
{
showSpeed->setChecked(value);
//if (!current) return;
bool checked = ( ( value == Qt::Checked ) && showSpeed->isEnabled()) ? true : false;
allPlot->setShowSpeed(checked);
foreach (AllPlot *plot, allPlots)
plot->setShowSpeed(checked);
}
void
AllPlotWindow::setShowCad(int value)
{
showCad->setChecked(value);
//if (!current) return;
bool checked = ( ( value == Qt::Checked ) && showCad->isEnabled()) ? true : false;
allPlot->setShowCad(checked);
foreach (AllPlot *plot, allPlots)
plot->setShowCad(checked);
}
void
AllPlotWindow::setShowAlt(int value)
{
showAlt->setChecked(value);
//if (!current) return;
bool checked = ( ( value == Qt::Checked ) && showAlt->isEnabled()) ? true : false;
allPlot->setShowAlt(checked);
foreach (AllPlot *plot, allPlots)
plot->setShowAlt(checked);
}
void
AllPlotWindow::setShowTemp(int value)
{
showTemp->setChecked(value);
//if (!current) return;
bool checked = ( ( value == Qt::Checked ) && showTemp->isEnabled()) ? true : false;
allPlot->setShowTemp(checked);
foreach (AllPlot *plot, allPlots)
plot->setShowTemp(checked);
}
void
AllPlotWindow::setShowWind(int value)
{
showWind->setChecked(value);
//if (!current) return;
bool checked = ( ( value == Qt::Checked ) && showWind->isEnabled()) ? true : false;
allPlot->setShowWind(checked);
foreach (AllPlot *plot, allPlots)
plot->setShowWind(checked);
}
void
AllPlotWindow::setShowTorque(int value)
{
showTorque->setChecked(value);
bool checked = ( ( value == Qt::Checked ) && showTorque->isEnabled()) ? true : false;
//if (!current) return;
allPlot->setShowTorque(checked);
foreach (AllPlot *plot, allPlots)
plot->setShowTorque(checked);
}
void
AllPlotWindow::setShowBalance(int value)
{
showBalance->setChecked(value);
bool checked = ( ( value == Qt::Checked ) && showBalance->isEnabled()) ? true : false;
allPlot->setShowBalance(checked);
foreach (AllPlot *plot, allPlots)
plot->setShowBalance(checked);
}
void
AllPlotWindow::setShowFull(int value)
{
rFull->setChecked(value);
showFull->setChecked(value);
if (showFull->isChecked()) {
fullPlot->show();
spanSlider->show();
scrollLeft->show();
scrollRight->show();
allPlotLayout->setStretch(1,20);
}
else {
fullPlot->hide();
spanSlider->hide();
scrollLeft->hide();
scrollRight->hide();
allPlotLayout->setStretch(1,0);
}
}
void
AllPlotWindow::setShowGrid(int value)
{
showGrid->setChecked(value);
//if (!current) return;
allPlot->setShowGrid(value);
foreach (AllPlot *plot, allPlots)
plot->setShowGrid(value);
}
void
AllPlotWindow::setPaintBrush(int value)
{
if (active == true) return;
active = true;
paintBrush->setChecked(value);
//if (!current) return;
allPlot->setPaintBrush(value);
foreach (AllPlot *plot, allPlots)
plot->setPaintBrush(value);
active = false;
}
void
AllPlotWindow::setByDistance(int value)
{
if (value <0 || value >1 || active == true) return;
active = true;
comboDistance->setCurrentIndex(value);
fullPlot->setByDistance(value);
allPlot->setByDistance(value);
// refresh controls, specifically spanSlider
setAllPlotWidgets(fullPlot->rideItem);
// refresh
redrawFullPlot();
redrawAllPlot();
setupStackPlots();
active = false;
}
void
AllPlotWindow::setSmoothing(int value)
{
//if (!current) return;
smoothSlider->setValue(value);
// recalculate etc
fullPlot->setSmoothing(value);
// redraw
redrawFullPlot();
redrawAllPlot();
redrawStackPlot();
}
//
// Runs through the stacks and updates their contents
//
void
AllPlotWindow::resetStackedDatas()
{
if (!current) return;
int _stackWidth = stackWidth;
if (allPlot->bydist)
_stackWidth = stackWidth/3;
for(int i = 0 ; i < allPlots.count() ; i++)
{
AllPlot *plot = allPlots[i];
int startIndex, stopIndex;
if (plot->bydist) {
startIndex = allPlot->rideItem->ride()->distanceIndex(
(allPlot->useMetricUnits ? 1 : KM_PER_MILE) * _stackWidth*i);
stopIndex = allPlot->rideItem->ride()->distanceIndex(
(allPlot->useMetricUnits ? 1 : KM_PER_MILE) * _stackWidth*(i+1));
} else {
startIndex = allPlot->rideItem->ride()->timeIndex(60*_stackWidth*i);
stopIndex = allPlot->rideItem->ride()->timeIndex(60*_stackWidth*(i+1));
}
// Update AllPlot for stacked view
plot->setDataFromPlot(fullPlot, startIndex, stopIndex);
if (i != 0) plot->setTitle("");
}
}
bool
AllPlotWindow::stackZoomUpShouldEnable(int sw)
{
if (!current) return false;
if (sw >= 200 || sw >= current->ride()->dataPoints().last()->secs/60) {
return false;
}
else {
return true;
}
}
bool
AllPlotWindow::stackZoomDownShouldEnable(int sw)
{
if (sw <= 4) {
return false;
}
else {
return true;
}
}
void
AllPlotWindow::setStackZoomUp()
{
if (!current) return;
if (stackWidth<200 && stackWidth<current->ride()->dataPoints().last()->secs/60) {
stackWidth = ceil(stackWidth * 1.25);
setupStackPlots();
stackZoomUp->setEnabled(stackZoomUpShouldEnable(stackWidth));
stackZoomDown->setEnabled(stackZoomDownShouldEnable(stackWidth));
}
}
void
AllPlotWindow::setStackZoomDown()
{
if (!current) return;
if (stackWidth>4) {
stackWidth = floor(stackWidth / 1.25);
setupStackPlots();
stackZoomUp->setEnabled(stackZoomUpShouldEnable(stackWidth));
stackZoomDown->setEnabled(stackZoomDownShouldEnable(stackWidth));
}
}
void
AllPlotWindow::showStackChanged(int value)
{
if (!current) return;
showStack->setCheckState((Qt::CheckState)value);
rStack->setCheckState((Qt::CheckState)value);
// when we swap from normal to
// stacked view, save the mode so
// we can startup with the 'preferred'
// view next time. And replot for the
// target mode since it is probably
// out of date. Then call setAllPlotWidgets
// to make sure all the controls are setup
// and the right widgets are hidden/shown.
if (value) {
// refresh plots
resetStackedDatas();
// now they are all set, lets replot them
foreach(AllPlot *plot, allPlots) plot->replot();
} else {
// refresh plots
redrawAllPlot();
}
// reset the view
setAllPlotWidgets(current);
}
void
AllPlotWindow::setupStackPlots()
{
// recreate all the stack plots
// they are all attached to the
// stackWidget, which we ultimately
// destroy in this function and replace
// with a new one. This is to avoid some
// nasty flickers and simplifies the code
// for refreshing.
// ************************************
// EDIT WITH CAUTION -- THIS HAS BEEN
// OPTIMISED FOR PERFORMANCE AS WELL AS
// FOR SCREEN FLICKER. IT IS A LITTLE
// COMPLICATED
// ************************************
QVBoxLayout *newLayout = new QVBoxLayout;
// this is NOT a memory leak (see ZZZ below)
allPlots.clear();
int _stackWidth = stackWidth;
if (fullPlot->bydist) _stackWidth = stackWidth/3;
RideItem* rideItem = current;
// don't try and plot for null files
if (!rideItem || !rideItem->ride() || rideItem->ride()->dataPoints().isEmpty()) return;
double duration = rideItem->ride()->dataPoints().last()->secs;
double distance = (fullPlot->useMetricUnits ? 1 : MILES_PER_KM) * rideItem->ride()->dataPoints().last()->km;
int nbplot;
if (fullPlot->bydist)
nbplot = (int)floor(distance/_stackWidth)+1;
else
nbplot = (int)floor(duration/_stackWidth/60)+1;
QPalette palette;
palette.setBrush(QPalette::Background, Qt::NoBrush);
for(int i = 0 ; i < nbplot ; i++) {
// calculate the segment of ride this stack plot contains
int startIndex, stopIndex;
if (fullPlot->bydist) {
startIndex = fullPlot->rideItem->ride()->distanceIndex((fullPlot->useMetricUnits ?
1 : KM_PER_MILE) * _stackWidth*i);
stopIndex = fullPlot->rideItem->ride()->distanceIndex((fullPlot->useMetricUnits ?
1 : KM_PER_MILE) * _stackWidth*(i+1));
} else {
startIndex = fullPlot->rideItem->ride()->timeIndex(60*_stackWidth*i);
stopIndex = fullPlot->rideItem->ride()->timeIndex(60*_stackWidth*(i+1));
}
// we need at least one point to plot
if (stopIndex - startIndex < 2) break;
// create that plot
AllPlot *_allPlot = new AllPlot(this, mainWindow);
_allPlot->setInstanceName("stackPlot");
_allPlot->setAutoFillBackground(false);
_allPlot->setPalette(palette);
// add to the list
allPlots.append(_allPlot);
addPickers(_allPlot);
newLayout->addWidget(_allPlot);
// Update AllPlot for stacked view
_allPlot->setDataFromPlot(fullPlot, startIndex, stopIndex);
_allPlot->setAxisScale(QwtPlot::xBottom, _stackWidth*i, _stackWidth*(i+1), 15/stackWidth);
_allPlot->setFixedHeight(120+stackWidth*2);
// No x axis titles
_allPlot->setAxisTitle(QwtPlot::xBottom,NULL);
_allPlot->setAxisMaxMinor(QwtPlot::xBottom, 0);
_allPlot->setAxisMaxMinor(QwtPlot::yLeft, 0);
_allPlot->setAxisMaxMinor(QwtPlot::yLeft2, 0);
_allPlot->setAxisMaxMinor(QwtPlot::yRight, 0);
_allPlot->setAxisMaxMinor(QwtPlot::yRight2, 0);
// controls
_allPlot->setShadeZones(showPower->currentIndex() == 0);
_allPlot->setShowPower(showPower->currentIndex());
_allPlot->setShowHr( (showHr->isEnabled()) ? ( showHr->checkState() == Qt::Checked ) : false );
_allPlot->setShowSpeed((showSpeed->isEnabled()) ? ( showSpeed->checkState() == Qt::Checked ) : false );
_allPlot->setShowCad((showCad->isEnabled()) ? ( showCad->checkState() == Qt::Checked ) : false );
_allPlot->setShowAlt((showAlt->isEnabled()) ? ( showAlt->checkState() == Qt::Checked ) : false );
_allPlot->setShowTemp((showTemp->isEnabled()) ? ( showTemp->checkState() == Qt::Checked ) : false );
_allPlot->setShowTorque((showTorque->isEnabled()) ? ( showTorque->checkState() == Qt::Checked ) : false );
_allPlot->setShowGrid(showGrid->checkState() == Qt::Checked);
_allPlot->setPaintBrush(paintBrush->checkState());
_allPlot->setSmoothing(smoothSlider->value());
_allPlot->setByDistance(comboDistance->currentIndex());
_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
stackFrame->setUpdatesEnabled(false);
// set new widgets
QWidget *stackWidget = new QWidget;
stackWidget->setPalette(palette);
stackWidget->setAutoFillBackground(false);
stackWidget->setLayout(newLayout);
stackFrame->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
stackFrame->setUpdatesEnabled(true);
}
void
AllPlotWindow::addPickers(AllPlot *_allPlot)
{
QwtPlotMarker* allMarker1 = new QwtPlotMarker();
allMarker1->setLineStyle(QwtPlotMarker::VLine);
allMarker1->attach(_allPlot);
allMarker1->setLabelAlignment(Qt::AlignTop|Qt::AlignRight);
_allPlot->allMarker1 = allMarker1;
QwtPlotMarker* allMarker2 = new QwtPlotMarker();
allMarker2->setLineStyle(QwtPlotMarker::VLine);
allMarker2->attach(_allPlot);
allMarker2->setLabelAlignment(Qt::AlignTop|Qt::AlignRight);
_allPlot->allMarker2 = allMarker2;
// use the tooltip picker rather than a standard picker
_allPlot->tooltip = new LTMToolTip(QwtPlot::xBottom, QwtPlot::yLeft,
QwtPicker::VLineRubberBand,
QwtPicker::AlwaysOn,
_allPlot->canvas(),
"");
_allPlot->tooltip->setRubberBand(QwtPicker::VLineRubberBand);
_allPlot->tooltip->setMousePattern(QwtEventPattern::MouseSelect1, Qt::LeftButton);
_allPlot->tooltip->setTrackerPen(QColor(Qt::black));
QColor inv(Qt::white);
inv.setAlpha(0);
_allPlot->tooltip->setRubberBandPen(inv);
_allPlot->tooltip->setEnabled(true);
_allPlot->_canvasPicker = new LTMCanvasPicker(_allPlot);
connect(_allPlot->_canvasPicker, SIGNAL(pointHover(QwtPlotCurve*, int)), _allPlot, SLOT(pointHover(QwtPlotCurve*, int)));
connect(_allPlot->tooltip, SIGNAL(moved(const QPoint &)), this, SLOT(plotPickerMoved(const QPoint &)));
connect(_allPlot->tooltip, SIGNAL(appended(const QPoint &)), this, SLOT(plotPickerSelected(const QPoint &)));
}