/* * Copyright (c) 2009 Sean C. Rhea (srhea@srhea.net) * Copyright (c) 2014 Mark Liversedge (liversedge@gmail.com) * * 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 "Context.h" #include "Context.h" #include "Athlete.h" #include "TabView.h" #include "AllPlotWindow.h" #include "AllPlot.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 #include #include #include #include #include #include #include #include #include #include #include #include // span slider specials #include #include #include #include // tooltip #include "LTMWindow.h" // help #include "HelpWhatsThis.h" // overlay helper #include "GcOverlayWidget.h" #include "IntervalSummaryWindow.h" static const int stackZoomWidth[8] = { 5, 10, 15, 20, 30, 45, 60, 120 }; AllPlotWindow::AllPlotWindow(Context *context) : GcChartWindow(context), current(NULL), context(context), active(false), stale(true), setupStack(false), setupSeriesStack(false), compareStale(true), firstShow(true) { // basic setup setContentsMargins(0,0,0,0); QWidget *c = new QWidget; HelpWhatsThis *helpConfig = new HelpWhatsThis(c); c->setWhatsThis(helpConfig->getWhatsThisText(HelpWhatsThis::ChartRides_Performance)); setControls(c); QVBoxLayout *clv = new QVBoxLayout(c); // all the controls QFormLayout *mainControls = new QFormLayout; // basic stuff at top; power, slider etc // aside from basic settings, other stuff is now // in a tab widget as we have so many data series ! QTabWidget *st = new QTabWidget(this); clv->addWidget(st); // gui controls QWidget *basic = new QWidget(this); // show stack etc QVBoxLayout *basicControls = new QVBoxLayout(basic); basicControls->addLayout(mainControls); QFormLayout *guiControls = new QFormLayout; // show stack etc BUT ALSO ACCEL etc basicControls->addLayout(guiControls); basicControls->addStretch(); st->addTab(basic, tr("Basic")); HelpWhatsThis *basicHelp = new HelpWhatsThis(basic); basic->setWhatsThis(basicHelp->getWhatsThisText(HelpWhatsThis::ChartRides_Performance_Config_Basic)); // data series QWidget *series = new QWidget(this); // data series selection QHBoxLayout *seriesControls = new QHBoxLayout(series); QFormLayout *seriesLeft = new QFormLayout(); // ride side series QFormLayout *seriesRight = new QFormLayout(); // ride side series seriesControls->addLayout(seriesLeft); seriesControls->addLayout(seriesRight); // ack I swapped them around ! st->addTab(series, tr("Curves")); HelpWhatsThis *seriesHelp = new HelpWhatsThis(series); series->setWhatsThis(seriesHelp->getWhatsThisText(HelpWhatsThis::ChartRides_Performance_Config_Series)); // 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")); rBySeries = new QCheckBox(tr("by series")); rFull = new QCheckBox(tr("Fullplot")); rHelp = new QCheckBox(tr("Overlay")); // 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; QHBoxLayout *s = new QHBoxLayout; QHBoxLayout *t = new QHBoxLayout; s->addWidget(rStack); s->addWidget(rBySeries); v->addLayout(s); t->addWidget(rFull); t->addWidget(rHelp); v->addLayout(t); 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("View"), c); showStack = new QCheckBox(tr("Stack"), this); showStack->setCheckState(Qt::Unchecked); guiControls->addRow(showLabel, showStack); showBySeries = new QCheckBox(tr("By Series"), this); showBySeries->setCheckState(Qt::Unchecked); guiControls->addRow(new QLabel("", this), showBySeries); guiControls->addRow(new QLabel("", this), new QLabel("",this)); // spacer stackWidth = 20; stackZoomSlider = new QSlider(Qt::Horizontal,this); stackZoomSlider->setMinimum(0); stackZoomSlider->setMaximum(7); stackZoomSlider->setTickInterval(1); stackZoomSlider->setValue(3); guiControls->addRow(new QLabel(tr("Stack Zoom")), stackZoomSlider); showFull = new QCheckBox(tr("Full plot"), this); showFull->setCheckState(Qt::Checked); guiControls->addRow(new QLabel(""), showFull); showHelp = new QCheckBox(tr("Overlay"), this); showHelp->setCheckState(Qt::Unchecked); guiControls->addRow(new QLabel(""), showHelp); paintBrush = new QCheckBox(tr("Fill Curves"), this); paintBrush->setCheckState(Qt::Unchecked); guiControls->addRow(new QLabel(""), paintBrush); showGrid = new QCheckBox(tr("Grid"), this); showGrid->setCheckState(Qt::Checked); guiControls->addRow(new QLabel(""), showGrid); guiControls->addRow(new QLabel(""), new QLabel("")); showAccel = new QCheckBox(tr("Acceleration"), this); showAccel->setCheckState(Qt::Checked); seriesRight->addRow(new QLabel(tr("Delta Series")), showAccel); showPowerD = new QCheckBox(QString(tr("Power %1").arg(deltaChar)), this); showPowerD->setCheckState(Qt::Unchecked); seriesRight->addRow(new QLabel(""), showPowerD); showCadD = new QCheckBox(QString(tr("Cadence %1").arg(deltaChar)), this); showCadD->setCheckState(Qt::Unchecked); seriesRight->addRow(new QLabel(""), showCadD); showTorqueD = new QCheckBox(QString(tr("Torque %1").arg(deltaChar)), this); showTorqueD->setCheckState(Qt::Unchecked); seriesRight->addRow(new QLabel(""), showTorqueD); showHrD = new QCheckBox(QString(tr("Heartrate %1").arg(deltaChar)), this); showHrD->setCheckState(Qt::Unchecked); seriesRight->addRow(new QLabel(""), showHrD); seriesRight->addRow(new QLabel(""), new QLabel("")); showBalance = new QCheckBox(tr("Balance"), this); showBalance->setCheckState(Qt::Checked); seriesRight->addRow(new QLabel(tr("Left/Right")), showBalance); showTE = new QCheckBox(tr("Torque Effectiveness")); showTE->setCheckState(Qt::Unchecked); seriesRight->addRow(new QLabel(""), showTE); showPS = new QCheckBox(tr("Smoothness"), this); showPS->setCheckState(Qt::Unchecked); seriesRight->addRow(new QLabel(""), showPS); // running ! seriesRight->addRow(new QLabel(""), new QLabel("")); showRV = new QCheckBox(tr("Vertical Oscillation"), this); showRV->setCheckState(Qt::Checked); seriesRight->addRow(new QLabel(tr("Running")), showRV); showRGCT = new QCheckBox(tr("Ground Contact Time"), this); showRGCT->setCheckState(Qt::Checked); seriesRight->addRow(new QLabel(""), showRGCT); showRCad = new QCheckBox(tr("Cadence"), this); showRCad->setCheckState(Qt::Checked); seriesRight->addRow(new QLabel(""), showRCad); seriesRight->addRow(new QLabel(""), new QLabel("")); showSmO2 = new QCheckBox(tr("SmO2"), this); showSmO2->setCheckState(Qt::Checked); seriesRight->addRow(new QLabel(tr("Moxy")), showSmO2); showtHb = new QCheckBox(tr("tHb"), this); showtHb->setCheckState(Qt::Checked); seriesRight->addRow(new QLabel(""), showtHb); showO2Hb = new QCheckBox(tr("O2Hb"), this); showO2Hb->setCheckState(Qt::Checked); seriesRight->addRow(new QLabel(""), showO2Hb); showHHb = new QCheckBox(tr("HHb"), this); showHHb->setCheckState(Qt::Checked); seriesRight->addRow(new QLabel(""), showHHb); // "standard" showHr = new QCheckBox(tr("Heart Rate"), this); showHr->setCheckState(Qt::Checked); seriesLeft->addRow(new QLabel(tr("Data series")), showHr); showSpeed = new QCheckBox(tr("Speed"), this); showSpeed->setCheckState(Qt::Checked); seriesLeft->addRow(new QLabel(""), showSpeed); showCad = new QCheckBox(tr("Cadence"), this); showCad->setCheckState(Qt::Checked); seriesLeft->addRow(new QLabel(""), showCad); showAlt = new QCheckBox(tr("Altitude"), this); showAlt->setCheckState(Qt::Checked); seriesLeft->addRow(new QLabel(""), showAlt); showTemp = new QCheckBox(tr("Temperature"), this); showTemp->setCheckState(Qt::Checked); seriesLeft->addRow(new QLabel(""), showTemp); showWind = new QCheckBox(tr("Headwind"), this); showWind->setCheckState(Qt::Checked); seriesLeft->addRow(new QLabel(""), showWind); showTorque = new QCheckBox(tr("Torque"), this); showTorque->setCheckState(Qt::Checked); seriesLeft->addRow(new QLabel(""), showTorque); showGear = new QCheckBox(tr("Gear Ratio"), this); showGear->setCheckState(Qt::Checked); seriesLeft->addRow(new QLabel(""), showGear); showSlope = new QCheckBox(tr("Slope"), this); showSlope->setCheckState(Qt::Checked); seriesLeft->addRow(new QLabel(""), showSlope); seriesLeft->addRow(new QLabel(""), new QLabel("")); showAltSlope = new QComboBox(this); showAltSlope->addItem(tr("No Alt/Slope")); showAltSlope->addItem(tr("0.1km|mi - 1min")); showAltSlope->addItem(tr("0.5km|mi - 5min")); showAltSlope->addItem(tr("1.0km|mi - 10min")); seriesLeft->addRow(new QLabel(tr("Alt/Slope")), showAltSlope); showAltSlope->setCurrentIndex(0); seriesLeft->addRow(new QLabel(""), new QLabel("")); showANTISS = new QCheckBox(tr("Anaerobic TISS"), this); showANTISS->setCheckState(Qt::Unchecked); seriesLeft->addRow(new QLabel(tr("Metrics")), showANTISS); showATISS = new QCheckBox(tr("Aerobic TISS"), this); showATISS->setCheckState(Qt::Unchecked); seriesLeft->addRow(new QLabel(""), showATISS); showNP = new QCheckBox(tr("Normalized Power"), this); showNP->setCheckState(Qt::Unchecked); seriesLeft->addRow(new QLabel(""), showNP); showXP = new QCheckBox(tr("Skiba xPower"), this); showXP->setCheckState(Qt::Unchecked); seriesLeft->addRow(new QLabel(""), showXP); showAP = new QCheckBox(tr("Altitude Power"), this); showAP->setCheckState(Qt::Unchecked); seriesLeft->addRow(new QLabel(""), showAP); showW = new QCheckBox(tr("W' balance"), this); showW->setCheckState(Qt::Unchecked); seriesLeft->addRow(new QLabel(""), showW); showPower = new QComboBox(this); showPower->addItem(tr("Power + shade")); showPower->addItem(tr("Power - shade")); showPower->addItem(tr("No Power")); mainControls->addRow(new QLabel(tr("Shading")), showPower); showPower->setCurrentIndex(0); comboDistance = new QComboBox(this); comboDistance->addItem(tr("Time")); comboDistance->addItem(tr("Distance")); mainControls->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, this); 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); mainControls->addRow(smoothLabel, smoothLayout); QPalette palette; palette.setBrush(QPalette::Background, QBrush(GColor(CRIDEPLOTBACKGROUND))); allPlot = new AllPlot(this, this, 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); allStack = new QStackedWidget(this); allStack->addWidget(allPlot); allStack->setCurrentIndex(0); HelpWhatsThis *help = new HelpWhatsThis(allPlot); allPlot->setWhatsThis(help->getWhatsThisText(HelpWhatsThis::ChartRides_Performance)); // 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, QwtAxisId(QwtAxis::yLeft, 2).id, 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(context, SIGNAL(intervalHover(RideFileInterval)), allPlot, SLOT(intervalHover(RideFileInterval))); 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->standard->allMarker1=allMarker1; QwtPlotMarker* allMarker2 = new QwtPlotMarker(); allMarker2->setLineStyle(QwtPlotMarker::VLine); allMarker2->attach(allPlot); allMarker2->setLabelAlignment(Qt::AlignTop|Qt::AlignRight); allPlot->standard->allMarker2=allMarker2; // // stack view // stackPlotLayout = new QVBoxLayout(); stackPlotLayout->setSpacing(0); stackPlotLayout->setContentsMargins(0,0,0,0); stackWidget = new QWidget(this); stackWidget->setAutoFillBackground(false); stackWidget->setLayout(stackPlotLayout); stackWidget->setPalette(palette); stackFrame = new QScrollArea(this); #ifdef Q_OS_WIN QStyle *cde = QStyleFactory::create(OS_STYLE); stackFrame->setStyle(cde); #endif stackFrame->hide(); stackFrame->setAutoFillBackground(false); stackFrame->setWidgetResizable(true); stackFrame->setWidget(stackWidget); stackFrame->setFrameStyle(QFrame::NoFrame); 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(this); seriesstackWidget->setAutoFillBackground(true); seriesstackWidget->setLayout(seriesstackPlotLayout); seriesstackWidget->setPalette(palette); seriesstackFrame = new QScrollArea(this); #ifdef Q_OS_WIN cde = QStyleFactory::create(OS_STYLE); seriesstackFrame->setStyle(cde); #endif seriesstackFrame->hide(); seriesstackFrame->setAutoFillBackground(false); seriesstackFrame->setWidgetResizable(true); seriesstackFrame->setWidget(seriesstackWidget); seriesstackFrame->setFrameStyle(QFrame::NoFrame); seriesstackFrame->setContentsMargins(0,0,0,0); seriesstackFrame->setPalette(palette); // // Compare - AllPlots stack // comparePlotLayout = new QVBoxLayout(); comparePlotLayout->setSpacing(0); comparePlotLayout->setContentsMargins(0,0,0,0); comparePlotWidget = new QWidget(this); comparePlotWidget->setAutoFillBackground(false); comparePlotWidget->setLayout(comparePlotLayout); comparePlotWidget->setPalette(palette); comparePlotFrame = new QScrollArea(this); #ifdef Q_OS_WIN cde = QStyleFactory::create(OS_STYLE); comparePlotWidget->setStyle(cde); #endif comparePlotFrame->hide(); comparePlotFrame->setAutoFillBackground(false); comparePlotFrame->setWidgetResizable(true); comparePlotFrame->setWidget(comparePlotWidget); comparePlotFrame->setFrameStyle(QFrame::NoFrame); comparePlotFrame->setContentsMargins(0,0,0,0); comparePlotFrame->setPalette(palette); allStack->addWidget(comparePlotFrame); // // allPlot view // allPlotLayout = new QVBoxLayout; allPlotLayout->setSpacing(0); allPlotLayout->setContentsMargins(0,0,0,0); allPlotFrame = new QScrollArea(this); #ifdef Q_OS_WIN cde = QStyleFactory::create(OS_STYLE); allPlotFrame->setStyle(cde); #endif allPlotFrame->setFrameStyle(QFrame::NoFrame); allPlotFrame->setAutoFillBackground(false); allPlotFrame->setContentsMargins(0,0,0,0); spanSlider = new QxtSpanSlider(Qt::Horizontal, this); spanSlider->setFocusPolicy(Qt::NoFocus); spanSlider->setHandleMovementMode(QxtSpanSlider::NoOverlapping); spanSlider->setLowerValue(0); spanSlider->setUpperValue(15); QFont small; small.setPointSize(6); scrollLeft = new QPushButton("<", this); scrollLeft->setFont(small); scrollLeft->setAutoRepeat(true); scrollLeft->setFixedHeight(16); scrollLeft->setFixedWidth(16); scrollLeft->setContentsMargins(0,0,0,0); scrollRight = new QPushButton(">", this); 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 #if QT_VERSION > 0x5000 QStyle *style = QStyleFactory::create("fusion"); #else QStyle *style = QStyleFactory::create("Cleanlooks"); #endif spanSlider->setStyle(style); scrollLeft->setStyle(style); scrollRight->setStyle(style); #endif fullPlot = new AllPlot(this, this, context); fullPlot->standard->grid->enableY(false); fullPlot->setFixedHeight(100); fullPlot->setCanvasBackground(GColor(CRIDEPLOTBACKGROUND)); fullPlot->setHighlightIntervals(false); static_cast(fullPlot->canvas())->setBorderRadius(0); fullPlot->setWantAxis(false); fullPlot->setContentsMargins(0,0,0,0); HelpWhatsThis *helpFull = new HelpWhatsThis(fullPlot); fullPlot->setWhatsThis(helpFull->getWhatsThisText(HelpWhatsThis::ChartRides_Performance)); // allPlotStack contains the allPlot and the stack by series // because both want the optional fullplot at the bottom allPlotStack = new QStackedWidget(this); allPlotStack->addWidget(allStack); allPlotStack->addWidget(seriesstackFrame); allPlotStack->setCurrentIndex(0); HelpWhatsThis *helpStack = new HelpWhatsThis(allPlotStack); allPlotStack->setWhatsThis(helpStack->getWhatsThisText(HelpWhatsThis::ChartRides_Performance)); allPlotLayout->addWidget(allPlotStack); 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); // put a helper on the screen for mouse over intervals... overlayIntervals = new IntervalSummaryWindow(context); addHelper(tr("Intervals"), overlayIntervals); //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(showCad, SIGNAL(stateChanged(int)), this, SLOT(setShowCad(int))); connect(showTorque, SIGNAL(stateChanged(int)), this, SLOT(setShowTorque(int))); connect(showHr, SIGNAL(stateChanged(int)), this, SLOT(setShowHr(int))); connect(showPowerD, SIGNAL(stateChanged(int)), this, SLOT(setShowPowerD(int))); connect(showCadD, SIGNAL(stateChanged(int)), this, SLOT(setShowCadD(int))); connect(showTorqueD, SIGNAL(stateChanged(int)), this, SLOT(setShowTorqueD(int))); connect(showHrD, SIGNAL(stateChanged(int)), this, SLOT(setShowHrD(int))); connect(showNP, SIGNAL(stateChanged(int)), this, SLOT(setShowNP(int))); connect(showRV, SIGNAL(stateChanged(int)), this, SLOT(setShowRV(int))); connect(showRCad, SIGNAL(stateChanged(int)), this, SLOT(setShowRCad(int))); connect(showRGCT, SIGNAL(stateChanged(int)), this, SLOT(setShowRGCT(int))); connect(showGear, SIGNAL(stateChanged(int)), this, SLOT(setShowGear(int))); connect(showSmO2, SIGNAL(stateChanged(int)), this, SLOT(setShowSmO2(int))); connect(showtHb, SIGNAL(stateChanged(int)), this, SLOT(setShowtHb(int))); connect(showO2Hb, SIGNAL(stateChanged(int)), this, SLOT(setShowO2Hb(int))); connect(showHHb, SIGNAL(stateChanged(int)), this, SLOT(setShowHHb(int))); connect(showATISS, SIGNAL(stateChanged(int)), this, SLOT(setShowATISS(int))); connect(showANTISS, SIGNAL(stateChanged(int)), this, SLOT(setShowANTISS(int))); connect(showXP, SIGNAL(stateChanged(int)), this, SLOT(setShowXP(int))); connect(showAP, SIGNAL(stateChanged(int)), this, SLOT(setShowAP(int))); connect(showSpeed, SIGNAL(stateChanged(int)), this, SLOT(setShowSpeed(int))); connect(showAccel, SIGNAL(stateChanged(int)), this, SLOT(setShowAccel(int))); connect(showAlt, SIGNAL(stateChanged(int)), this, SLOT(setShowAlt(int))); connect(showSlope, SIGNAL(stateChanged(int)), this, SLOT(setShowSlope(int))); connect(showAltSlope, SIGNAL(currentIndexChanged(int)), this, SLOT(setShowAltSlope(int))); connect(showTemp, SIGNAL(stateChanged(int)), this, SLOT(setShowTemp(int))); connect(showWind, SIGNAL(stateChanged(int)), this, SLOT(setShowWind(int))); connect(showW, SIGNAL(stateChanged(int)), this, SLOT(setShowW(int))); connect(showBalance, SIGNAL(stateChanged(int)), this, SLOT(setShowBalance(int))); connect(showPS, SIGNAL(stateChanged(int)), this, SLOT(setShowPS(int))); connect(showTE, SIGNAL(stateChanged(int)), this, SLOT(setShowTE(int))); connect(showGrid, SIGNAL(stateChanged(int)), this, SLOT(setShowGrid(int))); connect(showFull, SIGNAL(stateChanged(int)), this, SLOT(setShowFull(int))); connect(showHelp, SIGNAL(stateChanged(int)), this, SLOT(setShowHelp(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(rHelp, SIGNAL(stateChanged(int)), this, SLOT(setShowHelp(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(scrollLeft, SIGNAL(clicked()), this, SLOT(moveLeft())); connect(scrollRight, SIGNAL(clicked()), this, SLOT(moveRight())); connect(stackZoomSlider, SIGNAL(valueChanged(int)), this, SLOT(stackZoomSliderChanged())); // GC signals connect(this, SIGNAL(rideItemChanged(RideItem*)), this, SLOT(rideSelected())); connect(context, SIGNAL(rideDirty(RideItem*)), this, SLOT(rideSelected())); connect(context, SIGNAL(configChanged()), this, SLOT(configChanged())); connect(context->athlete, SIGNAL(zonesChanged()), this, SLOT(zonesChanged())); connect(context, SIGNAL(intervalsChanged()), this, SLOT(intervalsChanged())); connect(context, SIGNAL(intervalZoom(IntervalItem*)), this, SLOT(zoomInterval(IntervalItem*))); connect(context, SIGNAL(zoomOut()), this, SLOT(zoomOut())); connect(context, SIGNAL(intervalSelected()), this, SLOT(intervalSelected())); connect(context, SIGNAL(rideDeleted(RideItem*)), this, SLOT(rideDeleted(RideItem*))); // comparing things connect(context, SIGNAL(compareIntervalsStateChanged(bool)), this, SLOT(compareChanged())); connect(context, SIGNAL(compareIntervalsChanged()), this, SLOT(compareChanged())); // set initial colors configChanged(); } void AllPlotWindow::configChanged() { setUpdatesEnabled(false); setProperty("color", GColor(CRIDEPLOTBACKGROUND)); // 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, QBrush(GColor(CRIDEPLOTBACKGROUND))); setPalette(palette); // propagates to children // set style sheets #ifndef Q_OS_MAC allPlotFrame->setStyleSheet(TabView::ourStyleSheet()); comparePlotFrame->setStyleSheet(TabView::ourStyleSheet()); seriesstackFrame->setStyleSheet(TabView::ourStyleSheet()); stackFrame->setStyleSheet(TabView::ourStyleSheet()); overlayIntervals->setStyleSheet(TabView::ourStyleSheet()); #endif // set palettes allStack->setPalette(palette); allPlotStack->setPalette(palette); allPlotFrame->setPalette(palette); allPlotFrame->viewport()->setPalette(palette); // its a scroll area comparePlotFrame->setPalette(palette); comparePlotWidget->setPalette(palette); seriesstackFrame->setPalette(palette); seriesstackWidget->setPalette(palette); stackFrame->setPalette(palette); stackWidget->setPalette(palette); stackZoomSlider->setPalette(palette); scrollLeft->setPalette(palette); scrollRight->setPalette(palette); fullPlot->setCanvasBackground(GColor(CRIDEPLOTBACKGROUND)); fullPlot->setPalette(palette); fullPlot->configChanged(); fullPlot->update(); // allPlot of course allPlot->setCanvasBackground(GColor(CRIDEPLOTBACKGROUND)); allPlot->setPalette(palette); allPlot->configChanged(); allPlot->update(); // and then the stacked plot foreach (AllPlot *plot, allPlots) { plot->setCanvasBackground(GColor(CRIDEPLOTBACKGROUND)); plot->setPalette(palette); plot->configChanged(); plot->update(); } // and then the series plots foreach (AllPlot *plot, seriesPlots) { plot->setCanvasBackground(GColor(CRIDEPLOTBACKGROUND)); plot->setPalette(palette); plot->configChanged(); plot->update(); } // and then the compaer plots foreach (AllPlot *plot, allComparePlots) { plot->setCanvasBackground(GColor(CRIDEPLOTBACKGROUND)); plot->setPalette(palette); plot->configChanged(); plot->update(); } setUpdatesEnabled(true); // 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; // reset the charts etc if (isCompare()) { compareChanged(); } else { // ok replot with the new config! redrawFullPlot(); redrawAllPlot(); redrawStackPlot(); } } bool AllPlotWindow::event(QEvent *event) { // nasty nasty nasty hack to move widgets as soon as the widget geometry // is set properly by the layout system, by default the width is 100 and // we wait for it to be set properly then put our helper widget on the RHS if (event->type() == QEvent::Resize && geometry().width() != 100) { // put somewhere nice on first show if (firstShow) { firstShow = false; helperWidget()->move(mainWidget()->geometry().width()-275, 50); helperWidget()->raise(); if (isShowHelp()) helperWidget()->show(); else helperWidget()->hide(); } // if off the screen move on screen if (helperWidget()->geometry().x() > geometry().width()) { helperWidget()->move(mainWidget()->geometry().width()-275, 50); } } return QWidget::event(event); } void AllPlotWindow::compareChanged() { if (!amVisible()) { if (context->isCompareIntervals) compareStale = true; // switched on else stale = true; // switched off return; } // we get busy so lets turn off updates till we're done setUpdatesEnabled(false); // clean up old foreach(AllPlotObject *p, compareIntervalCurves) delete p; compareIntervalCurves.clear(); // new ones .. if (context->isCompareIntervals) { // first, lets init fullPlot, just in case its never // been set (ie, switched to us before ever plotting a ride if (myRideItem) fullPlot->setDataFromRide(myRideItem); // and even if the current ride is blank, we're not // going to be blank !! setIsBlank(false); // // SETUP FULLPLOT FOR COMPARE MODE // int maxKM=0, maxSECS=0; fullPlot->standard->setVisible(false); if (fullPlot->smooth < 1) fullPlot->smooth = 1; foreach(CompareInterval ci, context->compareIntervals) { AllPlotObject *po = new AllPlotObject(fullPlot); if (ci.isChecked()) fullPlot->setDataFromRideFile(ci.data, po); // what was the maximum x value? if (po->maxKM > maxKM) maxKM = po->maxKM; if (po->maxSECS > maxSECS) maxSECS = po->maxSECS; // prettify / hide unnecessary guff po->setColor(ci.color); po->hideUnwanted(); // remember compareIntervalCurves << po; } // what is the longest compareInterval? if (fullPlot->bydist) fullPlot->setAxisScale(QwtPlot::xBottom, 0, maxKM); else fullPlot->setAxisScale(QwtPlot::xBottom, 0, maxSECS/60); // now set it it in all the compare objects so they all get set // to the same time / duration foreach (AllPlotObject *po, compareIntervalCurves) { po->maxKM = maxKM; po->maxSECS = maxSECS; } if (fullPlot->bydist == false) { spanSlider->setMinimum(0); spanSlider->setMaximum(maxSECS); } else { spanSlider->setMinimum(0); spanSlider->setMaximum(maxKM * 1000); } spanSlider->setLowerValue(spanSlider->minimum()); spanSlider->setUpperValue(spanSlider->maximum()); // redraw for red/no-red title fullPlot->replot(); // // SETUP ALLPLOT AS A STACK FOR EACH INTERVAL // allStack->setCurrentIndex(1); // first lets wipe the old ones foreach (AllPlot *ap, allComparePlots) delete ap; allComparePlots.clear(); if (comparePlotLayout->count() == 1) { comparePlotLayout->takeAt(0); // remove the stretch } // now lets throw up one for each interval that // is checked int i=0; foreach(CompareInterval ci, context->compareIntervals) { // create a new one using the interval data object and // referencing fullPlot for the user prefs etc AllPlot *ap = new AllPlot(this, this, context); ap->bydist = fullPlot->bydist; ap->setShadeZones(showPower->currentIndex() == 0); ap->setDataFromObject(compareIntervalCurves[i], fullPlot); // simpler to keep the indexes aligned if (!ci.isChecked()) ap->hide(); // tooltip on hover over point -- consider moving this to AllPlot (!) ap->tooltip = new LTMToolTip(QwtPlot::xBottom, QwtAxisId(QwtAxis::yLeft, 2).id, QwtPicker::VLineRubberBand, QwtPicker::AlwaysOn, ap->canvas(), ""); ap->tooltip->setRubberBand(QwtPicker::VLineRubberBand); ap->tooltip->setMousePattern(QwtEventPattern::MouseSelect1, Qt::LeftButton); ap->tooltip->setTrackerPen(QColor(Qt::black)); QColor inv(Qt::white); inv.setAlpha(0); ap->tooltip->setRubberBandPen(inv); ap->tooltip->setEnabled(true); ap->_canvasPicker = new LTMCanvasPicker(ap); connect(ap->_canvasPicker, SIGNAL(pointHover(QwtPlotCurve*, int)), ap, SLOT(pointHover(QwtPlotCurve*, int))); // format it for our purposes if (fullPlot->bydist) ap->setAxisScale(QwtPlot::xBottom, 0, maxKM); else ap->setAxisScale(QwtPlot::xBottom, 0, maxSECS/60.00f); ap->setFixedHeight(120 + (stackWidth *4)); // add to layout comparePlotLayout->addWidget(ap); allComparePlots << ap; i++; } comparePlotLayout->addStretch(); // // SETUP STACK SERIES // // Wipe away whatever is there foreach(AllPlot *plot, seriesPlots) delete plot; seriesPlots.clear(); // and the stretch while (seriesstackPlotLayout->count()) { delete seriesstackPlotLayout->takeAt(0); } // work out what we want to see QList wanted; SeriesWanted s; if (showPower->currentIndex() < 2) { s.one = RideFile::watts; s.two = RideFile::none; wanted << s;}; if (showPowerD->isChecked()) { s.one = RideFile::wattsd; s.two = RideFile::none; wanted << s;}; if (showHr->isChecked()) { s.one = RideFile::hr; s.two = RideFile::none; wanted << s;}; if (showHrD->isChecked()) { s.one = RideFile::hrd; s.two = RideFile::none; wanted << s;}; if (showSpeed->isChecked()) { s.one = RideFile::kph; s.two = RideFile::none; wanted << s;}; if (showAccel->isChecked()) { s.one = RideFile::kphd; s.two = RideFile::none; wanted << s;}; if (showCad->isChecked()) { s.one = RideFile::cad; s.two = RideFile::none; wanted << s;}; if (showCadD->isChecked()) { s.one = RideFile::cadd; s.two = RideFile::none; wanted << s;}; if (showTorque->isChecked()) { s.one = RideFile::nm; s.two = RideFile::none; wanted << s;}; if (showTorqueD->isChecked()) { s.one = RideFile::nmd; s.two = RideFile::none; wanted << s;}; if (showAlt->isChecked()) { s.one = RideFile::alt; s.two = RideFile::none; wanted << s;}; if (showSlope->isChecked()) { s.one = RideFile::slope; s.two = RideFile::none; wanted << s;}; if (showAltSlope->currentIndex() > 0) { s.one = RideFile::alt; s.two = RideFile::slope; wanted << s;}; // ALT/SLOPE ! if (showTemp->isChecked()) { s.one = RideFile::temp; s.two = RideFile::none; wanted << s;}; if (showWind->isChecked()) { s.one = RideFile::headwind; s.two = RideFile::none; wanted << s;}; if (showNP->isChecked()) { s.one = RideFile::NP; s.two = RideFile::none; wanted << s;}; if (showRV->isChecked()) { s.one = RideFile::rvert; s.two = RideFile::none; wanted << s;}; if (showRCad->isChecked()) { s.one = RideFile::rcad; s.two = RideFile::none; wanted << s;}; if (showRGCT->isChecked()) { s.one = RideFile::rcontact; s.two = RideFile::none; wanted << s;}; if (showGear->isChecked()) { s.one = RideFile::gear; s.two = RideFile::none; wanted << s;}; if (showSmO2->isChecked()) { s.one = RideFile::smo2; s.two = RideFile::none; wanted << s;}; if (showtHb->isChecked()) { s.one = RideFile::thb; s.two = RideFile::none; wanted << s;}; if (showO2Hb->isChecked()) { s.one = RideFile::o2hb; s.two = RideFile::none; wanted << s;}; if (showHHb->isChecked()) { s.one = RideFile::hhb; s.two = RideFile::none; wanted << s;}; if (showATISS->isChecked()) { s.one = RideFile::aTISS; s.two = RideFile::none; wanted << s;}; if (showANTISS->isChecked()) { s.one = RideFile::anTISS; s.two = RideFile::none; wanted << s;}; if (showXP->isChecked()) { s.one = RideFile::xPower; s.two = RideFile::none; wanted << s;}; if (showAP->isChecked()) { s.one = RideFile::aPower; s.two = RideFile::none; wanted << s;}; if (showW->isChecked()) { s.one = RideFile::wprime; s.two = RideFile::none; wanted << s;}; if (showBalance->isChecked()) { s.one = RideFile::lrbalance; s.two = RideFile::none; wanted << s;}; // create blank and add to gui QPalette palette; palette.setBrush(QPalette::Background, Qt::NoBrush); foreach(SeriesWanted x, wanted) { // create and setup with normal gui stuff AllPlot *plot = new AllPlot(this, this, context, x.one, x.two, false); plot->setPalette(palette); plot->setAutoFillBackground(false); plot->setFixedHeight(120+(stackWidth*4)); // tooltip on hover over point -- consider moving this to AllPlot (!) plot->tooltip = new LTMToolTip(QwtPlot::xBottom, QwtAxisId(QwtAxis::yLeft, 2).id, QwtPicker::VLineRubberBand, QwtPicker::AlwaysOn, plot->canvas(), ""); plot->tooltip->setRubberBand(QwtPicker::VLineRubberBand); plot->tooltip->setMousePattern(QwtEventPattern::MouseSelect1, Qt::LeftButton); plot->tooltip->setTrackerPen(QColor(Qt::black)); QColor inv(Qt::white); inv.setAlpha(0); plot->tooltip->setRubberBandPen(inv); plot->tooltip->setEnabled(true); plot->_canvasPicker = new LTMCanvasPicker(plot); connect(plot->_canvasPicker, SIGNAL(pointHover(QwtPlotCurve*, int)), plot, SLOT(pointHover(QwtPlotCurve*, int))); // No x axis titles plot->bydist = fullPlot->bydist; plot->showAltSlopeState = fullPlot->showAltSlopeState; if (x.one == RideFile::watts) plot->setShadeZones(showPower->currentIndex() == 0); else plot->setShadeZones(false); plot->setAxisVisible(QwtPlot::xBottom, true); plot->enableAxis(QwtPlot::xBottom, true); plot->setAxisTitle(QwtPlot::xBottom,NULL); // common y axis QwtScaleDraw *sd = new QwtScaleDraw; sd->setTickLength(QwtScaleDiv::MajorTick, 3); sd->enableComponent(QwtScaleDraw::Ticks, false); sd->enableComponent(QwtScaleDraw::Backbone, false); plot->setAxisScaleDraw(QwtPlot::yLeft, sd); // y-axis title and colour if (x.one == RideFile::alt && x.two == RideFile::slope) { plot->setAxisTitle(QwtPlot::yLeft, tr("Alt/Slope")); } else { plot->setAxisTitle(QwtPlot::yLeft, RideFile::seriesName(x.one)); } QPalette pal; pal.setColor(QPalette::WindowText, RideFile::colorFor(x.one)); pal.setColor(QPalette::Text, RideFile::colorFor(x.one)); plot->axisWidget(QwtPlot::yLeft)->setPalette(pal); // remember them seriesstackPlotLayout->addWidget(plot); seriesPlots << plot; } seriesstackPlotLayout->addStretch(); // now lets get each of them loaded up with data and in the right format! foreach(AllPlot *compare, seriesPlots) { // update the one already there compare->standard->setVisible(false); compare->setDataFromPlots(allComparePlots); // format it for our purposes compare->bydist = fullPlot->bydist; if (fullPlot->bydist) compare->setAxisScale(QwtPlot::xBottom, 0, maxKM); else compare->setAxisScale(QwtPlot::xBottom, 0, maxSECS/60.00f); compare->setXTitle(); compare->replot(); } // now remove any series plots that are empty for(int i=0; icompares.count() == 0 || seriesPlots[i]->compares[0]->data()->size() == 0) { delete seriesPlots[i]; seriesPlots.removeAt(i); } else { i++; } } // ok, we're done } else { // reset to normal view? fullPlot->standard->setVisible(true); // back to allplot allStack->setCurrentIndex(0); // reset by just calling ride selected // and setting stale to true.. simpler this way stale = true; rideSelected(); } // we're not stale anymore compareStale = false; // set the widgets straight setAllPlotWidgets(NULL); // Now we're done lets paint setUpdatesEnabled(true); repaint(); } void AllPlotWindow::redrawAllPlot() { // 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; 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 performed since the 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)); static_cast(fullPlot->canvas())->setBorderRadius(0); fullPlot->standard->grid->enableY(false); // use the ride to decide if (fullPlot->bydist) fullPlot->setAxisScale(QwtPlot::xBottom, ride->ride()->dataPoints().first()->km * (context->athlete->useMetricUnits ? 1 : MILES_PER_KM), ride->ride()->dataPoints().last()->km * (context->athlete->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); // 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); } } void AllPlotWindow::zoomChanged() { if (isCompare()) { // zoom in all the compare plots foreach (AllPlot *plot, allComparePlots) { if (fullPlot->bydist) plot->setAxisScale(QwtPlot::xBottom, spanSlider->lowerValue()/1000.00f, spanSlider->upperValue()/1000.00f); else plot->setAxisScale(QwtPlot::xBottom, spanSlider->lowerValue() / 60.00f, spanSlider->upperValue() / 60.00f); plot->replot(); } // and the series plots foreach (AllPlot *plot, seriesPlots) { if (fullPlot->bydist) plot->setAxisScale(QwtPlot::xBottom, spanSlider->lowerValue()/1000.00f, spanSlider->upperValue()/1000.00f); else plot->setAxisScale(QwtPlot::xBottom, spanSlider->lowerValue() / 60.00f, spanSlider->upperValue() / 60.00f); plot->replot(); } } else { redrawAllPlot(); redrawStackPlot(); // series stacks! } } 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() { // compare mode ignores ride selection if (isVisible() && isCompare()) { if (compareStale) compareChanged(); return; } RideItem *ride = myRideItem; if (ride == NULL) current = NULL; // ignore if not active if (!amVisible()) { stale = true; setupSeriesStack = setupStack = false; 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! //XXX the !ride->isDirty() code below makes GC crash when selecting //XXX on the canvas ?!??!? No idea why, but commenting out for now //XXX if (!ride->isDirty() && ride == current && stale == false) return; 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(); // we need to reset the stacks as the ride has changed // but it may ignore if not in stacked mode. setupSeriesStack = setupStack = false; setupStackPlots(); setupSeriesStackPlots(); stale = false; } void AllPlotWindow::rideDeleted(RideItem *ride) { if (ride == myRideItem) { // we have nothing to show setProperty("ride", QVariant::fromValue(NULL)); // notify all the plots, because when zones are redrawn // they will try and reference AllPlot::rideItem if (!isCompare()) { 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(); } // and then the series plots foreach (AllPlot *plot, seriesPlots) { plot->refreshIntervalMarkers(); plot->replot(); } } void AllPlotWindow::intervalSelected() { if (isCompare() || !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()) { if (showBySeries->isChecked()) { foreach (AllPlot *plot, seriesPlots) plot->replot(); } else { foreach (AllPlot *plot, allPlots) plot->replot(); } } else { fullPlot->replot(); allPlot->replot(); } } void 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 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 (!isCompare()) { // set for currently selected ride 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")) { showPowerD->setEnabled(dataPresent->watts); showCadD->setEnabled(dataPresent->cad); showTorqueD->setEnabled(dataPresent->nm); showHrD->setEnabled(dataPresent->hr); showPower->setEnabled(dataPresent->watts); showCad->setEnabled(dataPresent->cad); showTorque->setEnabled(dataPresent->nm); showHr->setEnabled(dataPresent->hr); showSpeed->setEnabled(dataPresent->kph); showAccel->setEnabled(dataPresent->kph); showAlt->setEnabled(dataPresent->alt); showAltSlope->setEnabled(dataPresent->alt); showSlope->setEnabled(dataPresent->slope); showTemp->setEnabled(dataPresent->temp); showWind->setEnabled(dataPresent->headwind); showBalance->setEnabled(dataPresent->lrbalance); } else { showAccel->setEnabled(false); showPowerD->setEnabled(false); showCadD->setEnabled(false); showTorqueD->setEnabled(false); showHrD->setEnabled(false); showPower->setEnabled(false); showHr->setEnabled(false); showSpeed->setEnabled(false); showCad->setEnabled(false); showAlt->setEnabled(false); showAltSlope->setEnabled(false); showSlope->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 (!isCompare() && showStack->isChecked() && !showBySeries->isChecked()) { // ALL PLOT STACK ORIGINAL AS CODED BY DAMIEN // hide normal view allPlotFrame->hide(); allPlot->hide(); fullPlot->hide(); controlsLayout->setRowStretch(0, 0); controlsLayout->setRowStretch(1, 0); // show stacked view stackFrame->show(); } else { // hide stack plots (segmented by time/distance) stackFrame->hide(); // show normal view allPlotFrame->show(); allPlot->show(); if (showStack->isChecked() && (isCompare() || showBySeries->isChecked())) { // STACK SERIES LANES SHOWING allPlotStack->setCurrentIndex(1); } else { // ALLPLOT allPlotStack->setCurrentIndex(0); if (isCompare()) { // COMPARE ALL PLOTS allStack->setCurrentIndex(1); // compare mode stack of all plots } else { // NORMAL SINGLE ALL PLOT allStack->setCurrentIndex(0); // normal single allplot } } // FIXUP FULL PLOT! if (showFull->isChecked()) { fullPlot->show(); controlsLayout->setRowStretch(0, 100); controlsLayout->setRowStretch(1, 20); } else { fullPlot->hide(); if (isCompare()) { controlsLayout->setRowStretch(0, 100); controlsLayout->setRowStretch(1, 00); } else { controlsLayout->setRowStretch(0, 100); controlsLayout->setRowStretch(1, 00); } } } } void AllPlotWindow::zoomOut() { // we don't do that in compare mode if (isCompare()) return; // set them to maximums to avoid overlapping // when we set them below, daft but works spanSlider->setLowerValue(spanSlider->minimum()); spanSlider->setUpperValue(spanSlider->maximum()); zoomChanged(); } void AllPlotWindow::zoomInterval(IntervalItem *which) { // we don't do that in compare mode if (isCompare()) return; // 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) { // we don't do selection in compare mode if (isCompare()) return; QwtPlotPicker* pick = qobject_cast(sender()); AllPlot* plot = qobject_cast(pick->plot()); double xValue = plot->invTransform(QwtPlot::xBottom, pos.x()); setStartSelection(plot, xValue); } void AllPlotWindow::plotPickerMoved(const QPoint &pos) { // we don't do selection in compare mode if (isCompare()) return; QString name = QString(tr("Selection #%1 ")).arg(selection); // which picker and plot send this signal? QwtPlotPicker* pick = qobject_cast(sender()); AllPlot* plot = qobject_cast(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, (showBySeries->isChecked() ? seriesPlots : allPlots)) { // mark the start of selection on every plot _plot->standard->allMarker1->setValue(plot->standard->allMarker1->value()); if (_plot->y()<=plot->y() && posY<_plot->y()){ if (_plot->transform(QwtPlot::xBottom, _plot->standard->allMarker2->xValue())>0) { setEndSelection(_plot, 0, false, name); _plot->standard->allMarker2->setLabel(QString("")); } } else if (_plot->y()>=plot->y() && posY>_plot->y()+_plot->height()) { if (_plot->transform(QwtPlot::xBottom, _plot->standard->allMarker2->xValue())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->standard->timeArray[fullPlot->standard->timeArray.size()-1])) { posX = _plot->transform(QwtPlot::xBottom, fullPlot->standard->timeArray[fullPlot->standard->timeArray.size()-1]); } else if (plot->bydist && pos.x()>_plot->transform(QwtPlot::xBottom, fullPlot->standard->distanceArray[fullPlot->standard->distanceArray.size()-1])) { posX = fullPlot->transform(QwtPlot::xBottom, fullPlot->standard->distanceArray[fullPlot->standard->distanceArray.size()-1]); } setEndSelection(_plot, _plot->invTransform(QwtPlot::xBottom, posX), true, name); if (plot->y()<_plot->y()) { plot->standard->allMarker1->setLabel(_plot->standard->allMarker1->label()); plot->standard->allMarker2->setLabel(_plot->standard->allMarker2->label()); } } else { _plot->standard->allMarker1->hide(); _plot->standard->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) { // we don't do selection in compare mode if (isCompare()) return; QwtPlotMarker* allMarker1 = plot->standard->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) { // we don't do selection in compare mode if (isCompare()) return; active = true; QwtPlotMarker* allMarker1 = plot->standard->allMarker1; QwtPlotMarker* allMarker2 = plot->standard->allMarker2; 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 && context->athlete->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->kmbydist==false && point->secs/60>=x1 && point->secs/60km; 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(context->athlete->useMetricUnits ? distance2-distance1 : (distance2-distance1)*MILES_PER_KM, 0, 'f', 2); s = s.arg((context->athlete->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((context->athlete->useMetricUnits ? 1 : MILES_PER_KM) * (distance2-distance1)/secsMoving*3600, 0, 'f', 1); s = s.arg((context->athlete->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()xValue()) { allMarker1->setLabel(label); allMarker2->setLabel(QString("")); } else { allMarker2->setLabel(label); allMarker1->setLabel(QString("")); } if (newInterval) { QTreeWidgetItem *allIntervals = context->athlete->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 --WTAF?????? NO! NO! NO! context->athlete->intervalTreeWidget()->setItemSelected(last, true); // now update the RideFileIntervals and all the plots etc context->athlete->updateRideFileIntervals(); } } active = false; } void AllPlotWindow::clearSelection() { // we don't do selection in compare mode if (isCompare()) return; selection = 0; allPlot->standard->allMarker1->setVisible(false); allPlot->standard->allMarker2->setVisible(false); foreach (AllPlot *plot, allPlots) { plot->standard->allMarker1->setVisible(false); plot->standard->allMarker2->setVisible(false); } } void AllPlotWindow::hideSelection() { // we don't do selection in compare mode if (isCompare()) return; if (showStack->isChecked()) { foreach (AllPlot *plot, allPlots) { plot->standard->allMarker1->setVisible(false); plot->standard->allMarker2->setVisible(false); plot->replot(); } } else { fullPlot->replot(); allPlot->standard->allMarker1->setVisible(false); allPlot->standard->allMarker2->setVisible(false); allPlot->replot(); } } void AllPlotWindow::setShowPower(int value) { showPower->setCurrentIndex(value); // compare mode selfcontained update if (isCompare()) { compareChanged(); 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... } // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowHr(int value) { showHr->setChecked(value); // compare mode selfcontained update if (isCompare()) { compareChanged(); return; } bool checked = ( ( value == Qt::Checked ) && showHr->isEnabled()) ? true : false; allPlot->setShowHr(checked); foreach (AllPlot *plot, allPlots) plot->setShowHr(checked); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowNP(int value) { showNP->setChecked(value); // compare mode selfcontained update if (isCompare()) { compareChanged(); return; } bool checked = ( ( value == Qt::Checked ) && showNP->isEnabled()) ? true : false; // recalc only does it if it needs to if (value && current && current->ride()) current->ride()->recalculateDerivedSeries(); allPlot->setShowNP(checked); foreach (AllPlot *plot, allPlots) plot->setShowNP(checked); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowANTISS(int value) { showANTISS->setChecked(value); // compare mode selfcontained update if (isCompare()) { compareChanged(); return; } bool checked = ( ( value == Qt::Checked ) && showANTISS->isEnabled()) ? true : false; // recalc only does it if it needs to if (value && current && current->ride()) current->ride()->recalculateDerivedSeries(); allPlot->setShowANTISS(checked); foreach (AllPlot *plot, allPlots) plot->setShowANTISS(checked); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowATISS(int value) { showATISS->setChecked(value); // compare mode selfcontained update if (isCompare()) { compareChanged(); return; } bool checked = ( ( value == Qt::Checked ) && showATISS->isEnabled()) ? true : false; // recalc only does it if it needs to if (value && current && current->ride()) current->ride()->recalculateDerivedSeries(); allPlot->setShowATISS(checked); foreach (AllPlot *plot, allPlots) plot->setShowATISS(checked); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowXP(int value) { showXP->setChecked(value); // compare mode selfcontained update if (isCompare()) { compareChanged(); return; } bool checked = ( ( value == Qt::Checked ) && showXP->isEnabled()) ? true : false; // recalc only does it if it needs to if (value && current && current->ride()) current->ride()->recalculateDerivedSeries(); allPlot->setShowXP(checked); foreach (AllPlot *plot, allPlots) plot->setShowXP(checked); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowAP(int value) { showAP->setChecked(value); // compare mode selfcontained update if (isCompare()) { compareChanged(); return; } bool checked = ( ( value == Qt::Checked ) && showAP->isEnabled()) ? true : false; // recalc only does it if it needs to if (value && current && current->ride()) current->ride()->recalculateDerivedSeries(); allPlot->setShowAP(checked); foreach (AllPlot *plot, allPlots) plot->setShowAP(checked); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowSpeed(int value) { showSpeed->setChecked(value); // compare mode selfcontained update if (isCompare()) { compareChanged(); return; } bool checked = ( ( value == Qt::Checked ) && showSpeed->isEnabled()) ? true : false; allPlot->setShowSpeed(checked); foreach (AllPlot *plot, allPlots) plot->setShowSpeed(checked); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowAccel(int value) { showAccel->setChecked(value); // compare mode selfcontained update if (isCompare()) { compareChanged(); return; } bool checked = ( ( value == Qt::Checked ) && showAccel->isEnabled()) ? true : false; allPlot->setShowAccel(checked); foreach (AllPlot *plot, allPlots) plot->setShowAccel(checked); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowPowerD(int value) { showPowerD->setChecked(value); // compare mode selfcontained update if (isCompare()) { compareChanged(); return; } bool checked = ( ( value == Qt::Checked ) && showPowerD->isEnabled()) ? true : false; allPlot->setShowPowerD(checked); foreach (AllPlot *plot, allPlots) plot->setShowPowerD(checked); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowCadD(int value) { showCadD->setChecked(value); // compare mode selfcontained update if (isCompare()) { compareChanged(); return; } bool checked = ( ( value == Qt::Checked ) && showCadD->isEnabled()) ? true : false; allPlot->setShowCadD(checked); foreach (AllPlot *plot, allPlots) plot->setShowCadD(checked); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowTorqueD(int value) { showTorqueD->setChecked(value); // compare mode selfcontained update if (isCompare()) { compareChanged(); return; } bool checked = ( ( value == Qt::Checked ) && showTorqueD->isEnabled()) ? true : false; allPlot->setShowTorqueD(checked); foreach (AllPlot *plot, allPlots) plot->setShowTorqueD(checked); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowHrD(int value) { showHrD->setChecked(value); // compare mode selfcontained update if (isCompare()) { compareChanged(); return; } bool checked = ( ( value == Qt::Checked ) && showHrD->isEnabled()) ? true : false; allPlot->setShowHrD(checked); foreach (AllPlot *plot, allPlots) plot->setShowHrD(checked); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowCad(int value) { showCad->setChecked(value); // compare mode selfcontained update if (isCompare()) { compareChanged(); return; } bool checked = ( ( value == Qt::Checked ) && showCad->isEnabled()) ? true : false; allPlot->setShowCad(checked); foreach (AllPlot *plot, allPlots) plot->setShowCad(checked); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowAlt(int value) { showAlt->setChecked(value); // compare mode selfcontained update if (isCompare()) { compareChanged(); return; } bool checked = ( ( value == Qt::Checked ) && showAlt->isEnabled()) ? true : false; allPlot->setShowAlt(checked); foreach (AllPlot *plot, allPlots) plot->setShowAlt(checked); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowSlope(int value) { showSlope->setChecked(value); // compare mode selfcontained update if (isCompare()) { compareChanged(); return; } bool checked = ( ( value == Qt::Checked ) && showSlope->isEnabled()) ? true : false; allPlot->setShowSlope (checked); foreach (AllPlot *plot, allPlots) plot->setShowSlope(checked); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowAltSlope(int value) { showAltSlope->setCurrentIndex(value); // compare mode selfcontained update if (isCompare()) { // transfer changes of setting here (which is more than on/off) also to other plot settings fullPlot->showAltSlopeState = value; fullPlot->setAltSlopePlotStyle(fullPlot->standard->altSlopeCurve); allPlot->showAltSlopeState = value; allPlot->setAltSlopePlotStyle(allPlot->standard->altSlopeCurve); compareChanged(); active = false; return; } fullPlot->setShowAltSlope(value); allPlot->setShowAltSlope(value); foreach (AllPlot *plot, allPlots) plot->setShowAltSlope(value); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowTemp(int value) { showTemp->setChecked(value); // compare mode selfcontained update if (isCompare()) { compareChanged(); return; } bool checked = ( ( value == Qt::Checked ) && showTemp->isEnabled()) ? true : false; allPlot->setShowTemp(checked); foreach (AllPlot *plot, allPlots) plot->setShowTemp(checked); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowRV(int value) { showRV->setChecked(value); // compare mode selfcontained update if (isCompare()) { compareChanged(); return; } bool checked = (( value == Qt::Checked ) && showRV->isEnabled()) ? true : false; allPlot->setShowRV(checked); foreach (AllPlot *plot, allPlots) plot->setShowRV(checked); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowRGCT(int value) { showRGCT->setChecked(value); // compare mode selfcontained update if (isCompare()) { compareChanged(); return; } bool checked = (( value == Qt::Checked ) && showRGCT->isEnabled()) ? true : false; allPlot->setShowRGCT(checked); foreach (AllPlot *plot, allPlots) plot->setShowRGCT(checked); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowRCad(int value) { showRCad->setChecked(value); // compare mode selfcontained update if (isCompare()) { compareChanged(); return; } bool checked = (( value == Qt::Checked ) && showRCad->isEnabled()) ? true : false; allPlot->setShowRCad(checked); foreach (AllPlot *plot, allPlots) plot->setShowRCad(checked); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowSmO2(int value) { showSmO2->setChecked(value); // compare mode selfcontained update if (isCompare()) { compareChanged(); return; } bool checked = (( value == Qt::Checked ) && showSmO2->isEnabled()) ? true : false; allPlot->setShowSmO2(checked); foreach (AllPlot *plot, allPlots) plot->setShowSmO2(checked); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowtHb(int value) { showtHb->setChecked(value); // compare mode selfcontained update if (isCompare()) { compareChanged(); return; } bool checked = (( value == Qt::Checked ) && showtHb->isEnabled()) ? true : false; allPlot->setShowtHb(checked); foreach (AllPlot *plot, allPlots) plot->setShowtHb(checked); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowO2Hb(int value) { showO2Hb->setChecked(value); // compare mode selfcontained update if (isCompare()) { compareChanged(); return; } bool checked = (( value == Qt::Checked ) && showO2Hb->isEnabled()) ? true : false; allPlot->setShowO2Hb(checked); foreach (AllPlot *plot, allPlots) plot->setShowO2Hb(checked); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowHHb(int value) { showHHb->setChecked(value); // compare mode selfcontained update if (isCompare()) { compareChanged(); return; } bool checked = (( value == Qt::Checked ) && showHHb->isEnabled()) ? true : false; allPlot->setShowHHb(checked); foreach (AllPlot *plot, allPlots) plot->setShowHHb(checked); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowGear(int value) { showGear->setChecked(value); // compare mode selfcontained update if (isCompare()) { compareChanged(); return; } bool checked = (( value == Qt::Checked ) && showGear->isEnabled()) ? true : false; allPlot->setShowGear(checked); foreach (AllPlot *plot, allPlots) plot->setShowGear(checked); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowWind(int value) { showWind->setChecked(value); // compare mode selfcontained update if (isCompare()) { compareChanged(); return; } bool checked = (( value == Qt::Checked ) && showWind->isEnabled()) ? true : false; allPlot->setShowWind(checked); foreach (AllPlot *plot, allPlots) plot->setShowWind(checked); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowW(int value) { showW->setChecked(value); // compare mode selfcontained update if (isCompare()) { compareChanged(); return; } bool checked = ( ( value == Qt::Checked ) && showW->isEnabled()) ? true : false; allPlot->setShowW(checked); foreach (AllPlot *plot, allPlots) plot->setShowW(checked); // refresh W' data if needed if (checked && current && current->ride()) { // redraw redrawFullPlot(); redrawAllPlot(); redrawStackPlot(); } // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowTorque(int value) { showTorque->setChecked(value); bool checked = ( ( value == Qt::Checked ) && showTorque->isEnabled()) ? true : false; // compare mode selfcontained update if (isCompare()) { compareChanged(); return; } allPlot->setShowTorque(checked); foreach (AllPlot *plot, allPlots) plot->setShowTorque(checked); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowBalance(int value) { showBalance->setChecked(value); // compare mode selfcontained update if (isCompare()) { compareChanged(); return; } bool checked = ( ( value == Qt::Checked ) && showBalance->isEnabled()) ? true : false; allPlot->setShowBalance(checked); foreach (AllPlot *plot, allPlots) plot->setShowBalance(checked); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowPS(int value) { showPS->setChecked(value); // compare mode selfcontained update if (isCompare()) { compareChanged(); return; } bool checked = ( ( value == Qt::Checked ) && showPS->isEnabled()) ? true : false; allPlot->setShowPS(checked); foreach (AllPlot *plot, allPlots) plot->setShowPS(checked); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowTE(int value) { showTE->setChecked(value); // compare mode selfcontained update if (isCompare()) { compareChanged(); return; } bool checked = ( ( value == Qt::Checked ) && showTE->isEnabled()) ? true : false; allPlot->setShowTE(checked); foreach (AllPlot *plot, allPlots) plot->setShowTE(checked); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setShowHelp(int value) { rHelp->setChecked(value); showHelp->setChecked(value); if (showHelp->isChecked()) helperWidget()->show(); else helperWidget()->hide(); } void AllPlotWindow::setShowFull(int value) { rFull->setChecked(value); showFull->setChecked(value); if (showFull->isChecked()) { fullPlot->show(); allPlotLayout->setStretch(1,20); } else { fullPlot->hide(); allPlotLayout->setStretch(1,0); } } void AllPlotWindow::setShowGrid(int value) { showGrid->setChecked(value); if (isCompare()) { // XXX return; } else { allPlot->setShowGrid(value); foreach (AllPlot *plot, allPlots) plot->setShowGrid(value); // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } } void AllPlotWindow::setPaintBrush(int value) { if (active == true) return; if (isCompare()) { // XXX return; } active = true; paintBrush->setChecked(value); //if (!current) return; allPlot->setPaintBrush(value); foreach (AllPlot *plot, allPlots) plot->setPaintBrush(value); active = false; // and the series stacks too forceSetupSeriesStackPlots(); // scope changed so force redraw } void AllPlotWindow::setByDistance(int value) { if (value <0 || value >1 || active == true) return; active = true; comboDistance->setCurrentIndex(value); // compare mode is self contained if (isCompare()) { fullPlot->bydist = value; compareChanged(); active = false; return; } fullPlot->setByDistance(value); allPlot->setByDistance(value); // refresh controls, specifically spanSlider setAllPlotWidgets(fullPlot->rideItem); // refresh setupSeriesStack = setupStack = false; redrawFullPlot(); redrawAllPlot(); setupStackPlots(); setupSeriesStackPlots(); active = false; } void AllPlotWindow::setSmoothing(int value) { //if (!current) return; smoothSlider->setValue(value); // Compare has LOTS of rides to smooth... if (context->isCompareIntervals) { // no zero smoothing when comparing -- we need the // arrays to be initialised for all series if (value < 1) value = 1; fullPlot->setSmoothing(value); setUpdatesEnabled(false); // smooth each on full plots ... foreach(AllPlotObject *po, compareIntervalCurves) { fullPlot->recalc(po); } // .. and all plots too! int i=0; foreach (AllPlot *plot, allComparePlots) { plot->setDataFromObject(compareIntervalCurves[i], allPlot); i++; } // and series plots.. foreach (AllPlot *plot, seriesPlots) { plot->setDataFromPlots(allComparePlots); plot->replot(); // straight away this time } setUpdatesEnabled(true); } else { // recalculate etc fullPlot->setSmoothing(value); // redraw redrawFullPlot(); redrawAllPlot(); redrawStackPlot(); } } void AllPlotWindow::resetSeriesStackedDatas() { if (!current) return; // just reset from AllPlot foreach(AllPlot *p, seriesPlots) { p->setDataFromPlot(allPlot); } } // // 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( (context->athlete->useMetricUnits ? 1 : KM_PER_MILE) * _stackWidth*i); stopIndex = allPlot->rideItem->ride()->distanceIndex( (context->athlete->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(""); } } void AllPlotWindow::stackZoomSliderChanged() { // slider moved! setStackWidth(stackZoomWidth[stackZoomSlider->value()]); } void AllPlotWindow::setStackWidth(int width) { // derive width from allowed values -- this is for backward // compatibility to ensure we always use a size that we are // expecting and set the slider to the appropriate value int i=0; for (; i<8; i++) // there are 8 pre-set sizes if (width <= stackZoomWidth[i]) break; // we never found it if (i == 8) i=7; // did anything actually change? if (stackZoomWidth[i] == stackWidth) return; // set the value and the slider stackWidth = stackZoomWidth[i]; stackZoomSlider->setValue(i); resizeSeriesPlots(); // its only the size that needs to change // no need to replot resizeComparePlots(); // now lets do the plots... setupStack = false; // force resize setupStackPlots(); } void AllPlotWindow::showStackChanged(int value) { showStack->setCheckState((Qt::CheckState)value); rStack->setCheckState((Qt::CheckState)value); if (isCompare()) { setAllPlotWidgets(NULL); // no need to do anything fancy return; } if (!current) return; // enables / disable as needed if (showStack->isChecked()) { showBySeries->setEnabled(true); rBySeries->setEnabled(true); // all plot... allPlotStack->setCurrentIndex(0); if (isCompare()) allStack->setCurrentIndex(1); // compare mode stack of all plots else allStack->setCurrentIndex(0); // normal single allplot } 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' // 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) { if (!showBySeries->isChecked()) { // BY TIME / DISTANCE // 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(); // now they are all set, lets replot them foreach(AllPlot *plot, seriesPlots) plot->replot(); } } else { // refresh plots redrawAllPlot(); } // reset the view setAllPlotWidgets(current); } void AllPlotWindow::showBySeriesChanged(int value) { if (!current) return; showBySeries->setCheckState((Qt::CheckState)value); rBySeries->setCheckState((Qt::CheckState)value); showStackChanged(showStack->checkState()); // force replot etc } void AllPlotWindow::forceSetupSeriesStackPlots() { setupSeriesStack = false; setupSeriesStackPlots(); } void AllPlotWindow::resizeSeriesPlots() { // calling setupdates enabled when its disabled // causes all paint events to be flushed. We want // to avoid that, since it causes a flicker. bool update = seriesstackFrame->updatesEnabled(); if (update) seriesstackFrame->setUpdatesEnabled(false); foreach (AllPlot *plot, seriesPlots) plot->setFixedHeight(120 + (stackWidth *4)); if (update) seriesstackFrame->setUpdatesEnabled(true); } void AllPlotWindow::resizeComparePlots() { comparePlotFrame->setUpdatesEnabled(false); foreach (AllPlot *plot, allComparePlots) plot->setFixedHeight(120 + (stackWidth *4)); comparePlotFrame->setUpdatesEnabled(true); } void AllPlotWindow::setupSeriesStackPlots() { if (!isCompare() && (!showStack->isChecked() || !showBySeries->isChecked() || setupSeriesStack)) return; QVBoxLayout *newLayout = new QVBoxLayout; // this is NOT a memory leak (see ZZZ below) seriesPlots.clear(); bool addHeadwind = false; RideItem* rideItem = current; if (!rideItem || !rideItem->ride() || rideItem->ride()->dataPoints().isEmpty()) return; // 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); QPalette palette; palette.setBrush(QPalette::Background, Qt::NoBrush); QList serieslist; SeriesWanted s; // lets get a list of what we need to plot -- plot is same order as options in settings if (showPower->currentIndex() < 2 && rideItem->ride()->areDataPresent()->watts) { s.one = RideFile::watts; s.two = RideFile::none; serieslist << s; } if (showPowerD->isChecked() && rideItem->ride()->areDataPresent()->watts) { s.one = RideFile::wattsd;s.two = RideFile::none; serieslist << s; } if (showHr->isChecked() && rideItem->ride()->areDataPresent()->hr) { s.one = RideFile::hr; s.two = RideFile::none; serieslist << s; } if (showHrD->isChecked() && rideItem->ride()->areDataPresent()->hr) { s.one = RideFile::hrd; s.two = RideFile::none; serieslist << s; } if (showSmO2->isChecked() && rideItem->ride()->areDataPresent()->smo2) { s.one = RideFile::smo2; s.two = RideFile::none; serieslist << s; } if (showtHb->isChecked() && rideItem->ride()->areDataPresent()->thb) { s.one = RideFile::thb; s.two = RideFile::none; serieslist << s; } if (showO2Hb->isChecked() && rideItem->ride()->areDataPresent()->o2hb) { s.one = RideFile::o2hb; s.two = RideFile::none; serieslist << s; } if (showHHb->isChecked() && rideItem->ride()->areDataPresent()->hhb) { s.one = RideFile::hhb; s.two = RideFile::none; serieslist << s; } if (showSpeed->isChecked() && rideItem->ride()->areDataPresent()->kph) { s.one = RideFile::kph; s.two = RideFile::none; serieslist << s; } if (showAccel->isChecked() && rideItem->ride()->areDataPresent()->kph) { s.one = RideFile::kphd; s.two = RideFile::none; serieslist << s; } if (showCad->isChecked() && rideItem->ride()->areDataPresent()->cad) { s.one = RideFile::cad; s.two = RideFile::none; serieslist << s; } if (showCadD->isChecked() && rideItem->ride()->areDataPresent()->cad) { s.one = RideFile::cadd; s.two = RideFile::none; serieslist << s; } if (showTorque->isChecked() && rideItem->ride()->areDataPresent()->nm) { s.one = RideFile::nm; s.two = RideFile::none; serieslist << s; } if (showTorqueD->isChecked() && rideItem->ride()->areDataPresent()->nm) { s.one = RideFile::nmd; s.two = RideFile::none; serieslist << s; } if (showAlt->isChecked() && rideItem->ride()->areDataPresent()->alt) { s.one = RideFile::alt; s.two = RideFile::none; serieslist << s; } if (showAltSlope->currentIndex() > 0 && rideItem->ride()->areDataPresent()->alt) { s.one = RideFile::alt; s.two = RideFile::slope; serieslist << s; } if (showSlope->isChecked() && rideItem->ride()->areDataPresent()->slope) { s.one = RideFile::slope; s.two = RideFile::none; serieslist << s; } if (showTemp->isChecked() && rideItem->ride()->areDataPresent()->temp) { s.one = RideFile::temp; s.two = RideFile::none; serieslist << s; } if (showWind->isChecked() && rideItem->ride()->areDataPresent()->headwind) addHeadwind=true; //serieslist << RideFile::headwind; if (showNP->isChecked() && rideItem->ride()->areDataPresent()->watts) { s.one = RideFile::NP; s.two = RideFile::none; serieslist << s; } if (showRV->isChecked() && rideItem->ride()->areDataPresent()->rvert) { s.one = RideFile::rvert; s.two = RideFile::none; serieslist << s; } if (showRCad->isChecked() && rideItem->ride()->areDataPresent()->rcad) { s.one = RideFile::rcad; s.two = RideFile::none; serieslist << s; } if (showRGCT->isChecked() && rideItem->ride()->areDataPresent()->rcontact) { s.one = RideFile::rcontact; s.two = RideFile::none; serieslist << s; } if (showGear->isChecked() && rideItem->ride()->areDataPresent()->gear) { s.one = RideFile::gear; s.two = RideFile::none; serieslist << s; } if (showATISS->isChecked() && rideItem->ride()->areDataPresent()->watts) { s.one = RideFile::aTISS; s.two = RideFile::none; serieslist << s; } if (showANTISS->isChecked() && rideItem->ride()->areDataPresent()->watts) { s.one = RideFile::anTISS; s.two = RideFile::none; serieslist << s; } if (showXP->isChecked() && rideItem->ride()->areDataPresent()->watts) { s.one = RideFile::xPower; s.two = RideFile::none; serieslist << s; } if (showAP->isChecked() && rideItem->ride()->areDataPresent()->watts) { s.one = RideFile::aPower; s.two = RideFile::none; serieslist << s; } if (showW->isChecked() && rideItem->ride()->areDataPresent()->watts) { s.one = RideFile::wprime; s.two = RideFile::none; serieslist << s; } if (showBalance->isChecked() && rideItem->ride()->areDataPresent()->lrbalance) { s.one = RideFile::lrbalance; s.two = RideFile::none; serieslist << s; } if (showTE->isChecked() && rideItem->ride()->areDataPresent()->lte) { s.one = RideFile::lte; s.two = RideFile::none; serieslist << s; s.one = RideFile::rte; s.two = RideFile::none; serieslist << s; } if (showPS->isChecked() && rideItem->ride()->areDataPresent()->lps) { s.one = RideFile::lps; s.two = RideFile::none; serieslist << s; s.one = RideFile::rps; s.two = RideFile::none; serieslist << s; } bool first = true; foreach(SeriesWanted x, serieslist) { // create that plot AllPlot *_allPlot = new AllPlot(this, this, context, x.one, (addHeadwind && x.one == RideFile::kph ? RideFile::headwind : x.two), first); _allPlot->setAutoFillBackground(false); _allPlot->setPalette(palette); _allPlot->setPaintBrush(paintBrush->checkState()); _allPlot->setDataFromPlot(allPlot); // will clone all settings and data for the series // being plotted, only works for one series plotting if (x.one == RideFile::watts) _allPlot->setShadeZones(showPower->currentIndex() == 0); else _allPlot->setShadeZones(false); first = false; // add to the list seriesPlots.append(_allPlot); addPickers(_allPlot); newLayout->addWidget(_allPlot); _allPlot->setFixedHeight(120+(stackWidth*4)); // 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,1), 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(); // lets make sure the size matches the user preferences resizeSeriesPlots(); // << checks if updates enabled and doesn't reenable // set new widgets QPalette old = seriesstackWidget->palette(); seriesstackWidget = new QWidget(this); seriesstackWidget->setPalette(old); seriesstackWidget->setAutoFillBackground(false); seriesstackWidget->setLayout(newLayout); seriesstackFrame->setWidget(seriesstackWidget); // lets remember the layout seriesstackPlotLayout = newLayout; // 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() { // 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. if (!showStack->isChecked() || showBySeries->isChecked() || setupStack) return; // since we cache out above we need to // track and manage via below setupStack = true; // ************************************ // 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 = (context->athlete->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((context->athlete->useMetricUnits ? 1 : KM_PER_MILE) * _stackWidth*i); stopIndex = fullPlot->rideItem->ride()->distanceIndex((context->athlete->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, this, context); _allPlot->setAutoFillBackground(false); _allPlot->setPalette(palette); _allPlot->setPaintBrush(paintBrush->checkState()); // 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*4); // 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,1), 0); _allPlot->setAxisMaxMinor(QwtPlot::yRight, 0); _allPlot->setAxisMaxMinor(QwtAxisId(QwtAxis::yRight,2).id, 0); _allPlot->setAxisMaxMinor(QwtAxisId(QwtAxis::yRight,3).id, 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->setShowAccel((showAccel->isEnabled()) ? ( showAccel->checkState() == Qt::Checked ) : false ); _allPlot->setShowPowerD((showPowerD->isEnabled()) ? ( showPowerD->checkState() == Qt::Checked ) : false ); _allPlot->setShowCadD((showCadD->isEnabled()) ? ( showCadD->checkState() == Qt::Checked ) : false ); _allPlot->setShowTorqueD((showTorqueD->isEnabled()) ? ( showTorqueD->checkState() == Qt::Checked ) : false ); _allPlot->setShowHrD((showHrD->isEnabled()) ? ( showHrD->checkState() == Qt::Checked ) : false ); _allPlot->setShowCad((showCad->isEnabled()) ? ( showCad->checkState() == Qt::Checked ) : false ); _allPlot->setShowAlt((showAlt->isEnabled()) ? ( showAlt->checkState() == Qt::Checked ) : false ); _allPlot->setShowSlope((showSlope->isEnabled()) ? ( showSlope->checkState() == Qt::Checked ) : false ); _allPlot->setShowAltSlope(showAltSlope->currentIndex()); _allPlot->setShowTemp((showTemp->isEnabled()) ? ( showTemp->checkState() == Qt::Checked ) : false ); _allPlot->setShowTorque((showTorque->isEnabled()) ? ( showTorque->checkState() == Qt::Checked ) : false ); _allPlot->setShowW((showW->isEnabled()) ? ( showW->checkState() == Qt::Checked ) : false ); _allPlot->setShowATISS((showATISS->isEnabled()) ? ( showATISS->checkState() == Qt::Checked ) : false ); _allPlot->setShowANTISS((showANTISS->isEnabled()) ? ( showANTISS->checkState() == Qt::Checked ) : false ); _allPlot->setShowGrid(showGrid->checkState() == Qt::Checked); _allPlot->setPaintBrush(paintBrush->checkState()); _allPlot->setSmoothing(smoothSlider->value()); _allPlot->setByDistance(comboDistance->currentIndex()); _allPlot->setShowBalance((showBalance->isEnabled()) ? ( showBalance->checkState() == Qt::Checked ) : false ); _allPlot->setShowNP((showNP->isEnabled()) ? ( showNP->checkState() == Qt::Checked ) : false ); _allPlot->setShowRV((showRV->isEnabled()) ? ( showRV->checkState() == Qt::Checked ) : false ); _allPlot->setShowRCad((showRCad->isEnabled()) ? ( showRCad->checkState() == Qt::Checked ) : false ); _allPlot->setShowRGCT((showRGCT->isEnabled()) ? ( showRGCT->checkState() == Qt::Checked ) : false ); _allPlot->setShowGear((showGear->isEnabled()) ? ( showGear->checkState() == Qt::Checked ) : false ); _allPlot->setShowSmO2((showSmO2->isEnabled()) ? ( showSmO2->checkState() == Qt::Checked ) : false ); _allPlot->setShowtHb((showtHb->isEnabled()) ? ( showtHb->checkState() == Qt::Checked ) : false ); _allPlot->setShowO2Hb((showO2Hb->isEnabled()) ? ( showO2Hb->checkState() == Qt::Checked ) : false ); _allPlot->setShowHHb((showHHb->isEnabled()) ? ( showHHb->checkState() == Qt::Checked ) : false ); _allPlot->setShowXP((showXP->isEnabled()) ? ( showXP->checkState() == Qt::Checked ) : false ); _allPlot->setShowAP((showAP->isEnabled()) ? ( showAP->checkState() == Qt::Checked ) : false ); _allPlot->setShowTE((showTE->isEnabled()) ? ( showTE->checkState() == Qt::Checked ) : false ); _allPlot->setShowPS((showPS->isEnabled()) ? ( showPS->checkState() == Qt::Checked ) : false ); _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 QPalette old = stackWidget->palette(); stackWidget = new QWidget(this); stackWidget->setPalette(old); 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->standard->allMarker1 = allMarker1; QwtPlotMarker* allMarker2 = new QwtPlotMarker(); allMarker2->setLineStyle(QwtPlotMarker::VLine); allMarker2->attach(_allPlot); allMarker2->setLabelAlignment(Qt::AlignTop|Qt::AlignRight); _allPlot->standard->allMarker2 = allMarker2; // use the tooltip picker rather than a standard picker _allPlot->tooltip = new LTMToolTip(QwtPlot::xBottom, QwtAxisId(QwtAxis::yLeft, 2).id, 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(context, SIGNAL(intervalHover(RideFileInterval)), _allPlot, SLOT(intervalHover(RideFileInterval))); 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 &))); }