Train View: Coloring by powerzones in ErgFilePlot (#4479)

Added support to color sections according to their power zone
* Optional coloring: Never (default), Always, Workout is stopped
* Optional tooltip giving information about current section (independent
  of coloring): Never (default), Workout is stopped
* Single sections covering multiple zones are split (for coloring /
  tooltip only)
* Tooltip shows starttime, duration, power (range if applicable), zone,
  W'bal-range
This commit is contained in:
Joachim Kohlhammer
2024-04-22 16:48:07 +02:00
committed by GitHub
parent 02835e3eb5
commit 0b144cc57b
6 changed files with 606 additions and 95 deletions

View File

@@ -1175,6 +1175,64 @@ ErgFile::Sections()
return returning;
}
// convert points to set of sections, every section will be strictly in one zone
QList<ErgFileZoneSection>
ErgFile::ZoneSections()
{
QList<ErgFileZoneSection> ret;
const Zones *zones = context->athlete->zones("Bike");
int zoneRange = zones->whichRange(QDate::currentDate());
QList<QString> zoneNames = zones->getZoneNames(zoneRange);
if (hasWatts() && Duration > 0) {
for (int i = 0; i < Points.size() - 1; ++i) {
const ErgFilePoint &pl = Points[i];
const ErgFilePoint &pr = Points[i + 1];
if (long(pl.x) != long(pr.x)) {
int zoneL = 0;
int zoneR = 0;
if (zoneRange >= 0) {
zoneL = zones->whichZone(zoneRange, pl.y);
zoneR = zones->whichZone(zoneRange, pr.y);
}
if (zoneL == zoneR) {
ret << ErgFileZoneSection(pl.x, pl.y, pr.x, pr.y, zoneL);
} else {
double m = (pr.y - pl.y) / (pr.x - pl.x);
int oldZone = 0;
if (zoneRange >= 0) {
oldZone = zones->whichZone(zoneRange, pl.y);
}
double oldTime = pl.x;
double oldWatts = pl.y;
double sectionStartTime = pl.x;
double sectionStartWatts = pl.y;
for (double i = pl.x; i < pr.x; i += 1000) {
double watts = m * (i - pl.x) + pl.y;
int newZone = 0;
if (zoneRange >= 0) {
newZone = zones->whichZone(zoneRange, watts);
}
if (newZone != oldZone) {
ret << ErgFileZoneSection(sectionStartTime, sectionStartWatts, oldTime, oldWatts, oldZone);
sectionStartTime = oldTime;
sectionStartWatts = watts;
oldZone = newZone;
}
oldTime = i;
oldWatts = watts;
}
ret << ErgFileZoneSection(sectionStartTime, sectionStartWatts, pr.x, pr.y, oldZone);
}
}
}
}
return ret;
}
bool
ErgFile::save(QStringList &errors)
{

View File

@@ -74,6 +74,20 @@ class ErgFileSection
double start, end;
};
class ErgFileZoneSection
: public ErgFileSection
{
public:
ErgFileZoneSection() : ErgFileSection(), startValue(0), endValue(0), zone(0) {}
ErgFileZoneSection(int startMSecs, int startValue, int endMSecs, int endValue, int zone)
: ErgFileSection(endMSecs - startMSecs, startMSecs, endMSecs), startValue(startValue), endValue(endValue), zone(zone)
{}
int startValue;
int endValue;
int zone;
};
class ErgFileText
{
public:
@@ -156,6 +170,7 @@ public:
// turn the ergfile into a series of sections rather
// than a list of points
QList<ErgFileSection> Sections();
QList<ErgFileZoneSection> ZoneSections();
QString Version, // version number / identifer
Units, // units used

View File

@@ -21,8 +21,15 @@
#include "Context.h"
#include "Units.h"
#include <qwt_picker_machine.h>
#include <unordered_map>
static const int sectionAlphaHovered = 128;
static const int sectionAlphaNeutral = 255;
// Bridge between QwtPlot and ErgFile to avoid having to
// create a separate array for the ergfile data, we plot
// directly from the ErgFile points array
@@ -66,7 +73,7 @@ QRectF ErgFileData::boundingRect() const
}
// Now bar
double NowData::x(size_t) const {
double NowData::x(size_t) const {
if (!bydist || GlobalContext::context()->useMetricUnits) return context->getNow();
else return context->getNow() * MILES_PER_KM;
}
@@ -92,6 +99,8 @@ QPointF NowData::sample(size_t i) const
ErgFilePlot::ErgFilePlot(Context *context) : context(context)
{
workoutActive = context->isRunning;
//insertLegend(new QwtLegend(), QwtPlot::BottomLegend);
setCanvasBackground(GColor(CTRAINPLOTBACKGROUND));
static_cast<QwtPlotCanvas*>(canvas())->setFrameStyle(QFrame::NoFrame);
@@ -161,6 +170,7 @@ ErgFilePlot::ErgFilePlot(Context *context) : context(context)
LodCurve = new QwtPlotCurve("Course Load");
LodCurve->setSamples(lodData);
LodCurve->attach(this);
LodCurve->setVisible(workoutActive);
LodCurve->setBaseline(-1000);
LodCurve->setYAxis(QwtAxis::YLeft);
@@ -254,6 +264,12 @@ ErgFilePlot::ErgFilePlot(Context *context) : context(context)
CPMarker->setYValue(274);
CPMarker->attach(this);
// Dummy curve for ensuring headroom in Ergmode
powerHeadroom = new QwtPlotCurve("Dummy Headroom");
powerHeadroom->setYAxis(QwtAxis::YLeft);
powerHeadroom->setPen(QColor(0, 0, 0, 0));
powerHeadroom->attach(this);
powerHeadroom->setVisible(false);
// Now pointer
NowCurve = new QwtPlotCurve("Now");
@@ -263,17 +279,30 @@ ErgFilePlot::ErgFilePlot(Context *context) : context(context)
NowCurve->attach(this);
NowCurve->setYAxis(QwtAxis::YLeft);
tooltip = new penTooltip(static_cast<QwtPlotCanvas*>(canvas()));
tooltip->setMousePattern(QwtEventPattern::MouseSelect1, Qt::LeftButton, Qt::ShiftModifier);
picker = new QwtPlotPicker(QwtAxis::XBottom, QwtAxis::YLeft, canvas());
picker->setTrackerMode(QwtPlotPicker::AlwaysOff);
picker->setStateMachine(new QwtPickerTrackerMachine());
connect(picker, SIGNAL(moved(const QPoint&)), this, SLOT(hover(const QPoint&)));
connect(context, SIGNAL(start()), this, SLOT(startWorkout()));
connect(context, SIGNAL(stop()), this, SLOT(stopWorkout()));
bydist = false;
ergFile = NULL;
selectTooltip();
setAutoReplot(false);
setData(ergFile);
setData(ergFile);
configChanged(CONFIG_ZONES);
connect(context, SIGNAL(configChanged(qint32)), this, SLOT(configChanged(qint32)));
}
void
ErgFilePlot::configChanged(qint32)
{
@@ -339,7 +368,7 @@ public:
//
// Build mapping from lapRangeId to index of first/last lap markers in the
// group.
//
//
// Map provides instant access to start and end of rangeid.
int lapCount = laps.count();
@@ -417,6 +446,11 @@ void
ErgFilePlot::setData(ErgFile *ergfile)
{
reset();
powerHeadroom->setVisible(false);
for (int i = 0; i < powerSectionCurves.length(); ++i) {
delete powerSectionCurves[i];
}
powerSectionCurves.clear();
ergFile = ergfile;
// clear the previous marks (if any)
@@ -454,8 +488,32 @@ ErgFilePlot::setData(ErgFile *ergfile)
LodCurve->setBrush(linearGradient); // fill below the line
QPen Lodpen = QPen(Qt::gray, 1.0);
LodCurve->setPen(Lodpen);
LodCurve->show();
} else {
QList<ErgFileZoneSection> zoneSections = ergFile->ZoneSections();
bool antiAlias = appsettings->value(this, GC_ANTIALIAS, false).toBool();
for (int i = 0; i < zoneSections.length(); ++i) {
QVector<QPointF> sectionData;
sectionData << QPointF(zoneSections[i].start, zoneSections[i].startValue)
<< QPointF(zoneSections[i].end, zoneSections[i].endValue);
QColor color = QColor(zoneColor(zoneSections[i].zone, 0));
color.setAlpha(sectionAlphaNeutral);
QwtPlotCurve *sectionCurve = new QwtPlotCurve("Course Load");
sectionCurve->setSamples(sectionData);
sectionCurve->setBaseline(-1000);
sectionCurve->setYAxis(QwtAxis::YLeft);
sectionCurve->setZ(-100);
sectionCurve->setPen(QColor(0, 0, 0, 0));
sectionCurve->setBrush(color);
sectionCurve->setRenderHint(QwtPlotItem::RenderAntialiased, antiAlias);
sectionCurve->attach(this);
sectionCurve->hide();
powerSectionCurves.append(sectionCurve);
}
selectCurves();
powerHeadroom->setVisible(true);
powerHeadroom->setSamples(QVector<QPointF> { dynamic_cast<QwtPointArrayData<double>*>(lodData)->boundingRect().bottomLeft() });
QColor brush_color1 = QColor(GColor(CTPOWER));
brush_color1.setAlpha(200);
@@ -470,8 +528,8 @@ ErgFilePlot::setData(ErgFile *ergfile)
LodCurve->setBrush(linearGradient); // fill below the line
QPen Lodpen = QPen(GColor(CTPOWER), 1.0);
LodCurve->setPen(Lodpen);
}
selectTooltip();
LapRowDistributor lapRowDistributor(ergFile->Laps);
@@ -512,7 +570,7 @@ ErgFilePlot::setData(ErgFile *ergfile)
// Literal row translation. We loves ascii art...
QString prefix = (row > 0) ? QString("\n").repeated(row) : "";
QwtText text(prefix + decoratedName);
text.setFont(QFont("Helvetica", 10, QFont::Bold));
text.setColor(GColor(CPLOTMARKER));
@@ -524,7 +582,7 @@ ErgFilePlot::setData(ErgFile *ergfile)
// convert to imperial according to settings
double unitsFactor = (!bydist || GlobalContext::context()->useMetricUnits) ? 1.0 : MILES_PER_KM;
add->setValue(lap.x * unitsFactor, 0);
add->setLabel(text);
add->attach(this);
@@ -619,6 +677,53 @@ ErgFilePlot::setNow(long /*msecs*/)
replot(); // and update
}
bool
ErgFilePlot::eventFilter
(QObject *obj, QEvent *event)
{
if (obj == canvas() && event->type() == QEvent::Leave) {
highlightSectionCurve(nullptr);
tooltip->setText("");
}
return false;
}
int
ErgFilePlot::showColorZones
() const
{
return _showColorZones;
}
void
ErgFilePlot::setShowColorZones
(int index)
{
_showColorZones = index;
selectCurves();
}
int
ErgFilePlot::showTooltip
() const
{
return _showTooltip;
}
void
ErgFilePlot::setShowTooltip
(int index)
{
_showTooltip = index;
selectTooltip();
}
void
ErgFilePlot::performancePlot(RealtimeData rtdata)
{
@@ -714,6 +819,221 @@ ErgFilePlot::reset()
speedCurve->setSamples(speedData->x(), speedData->y(), speedData->count());
}
void
ErgFilePlot::hover
(const QPoint &point)
{
if ( bydist
|| _showTooltip == 0
|| ( _showTooltip == 1
&& workoutActive)
|| ergFile == nullptr
|| ergFile->Duration == 0) {
tooltip->setText("");
return;
}
double xvalue = invTransform(QwtAxis::XBottom, point.x());
double yvalue = invTransform(QwtAxis::YLeft, point.y());
const int fullSecs = std::min(std::max(0, int(xvalue)), int(ergFile->Duration) - 1000) / 1000;
int duration = 0;
int startPower = 0;
int endPower = 0;
QwtPlotCurve *hoverCurve = nullptr;
for (QwtPlotCurve*& curve : powerSectionCurves) {
if (curve->minXValue() <= xvalue && xvalue <= curve->maxXValue()) {
duration = (curve->maxXValue() - curve->minXValue()) / 1000;
startPower = curve->sample(0).y();
endPower = curve->sample(1).y();
hoverCurve = curve;
break;
}
}
int watts = startPower;
if (hoverCurve != nullptr && startPower != endPower) {
QPointF pl = hoverCurve->sample(0);
QPointF pr = hoverCurve->sample(1);
watts = (pr.y() - pl.y()) / (pr.x() - pl.x()) * (xvalue - pl.x()) + pl.y();
}
if (watts == 0 || yvalue > watts) {
highlightSectionCurve(nullptr);
tooltip->setText("");
return;
}
if (hoverCurve->brush().color().alpha() != sectionAlphaNeutral) {
return;
}
double sectionStart = hoverCurve->sample(0).x();
double sectionEnd = hoverCurve->sample(1).x();
highlightSectionCurve(hoverCurve);
QString tooltipText;
tooltipText = QString("%1\n%4: ")
.arg(tr("Section of %1 starts at %2")
.arg(secsToString(duration))
.arg(secsToString(sectionStart / 1000)))
.arg(tr("Power"));
if (startPower == endPower) {
tooltipText = QString("%1%2 %3")
.arg(tooltipText)
.arg(startPower)
.arg(tr("watts"));
} else {
tooltipText = QString("%1%2..%3 %4")
.arg(tooltipText)
.arg(startPower)
.arg(endPower)
.arg(tr("watts"));
}
const Zones *zones = context->athlete->zones("Bike");
int zoneRange = zones->whichRange(QDate::currentDate());
if (zoneRange >= 0) {
tooltipText = QString("%1 (%2)")
.arg(tooltipText)
.arg(zones->getZoneNames(zoneRange)[zones->whichZone(zoneRange, startPower)]);
}
if (wbalCurvePredict != nullptr && wbalCurvePredict->dataSize() >= fullSecs) {
int secsStart = std::min(int(sectionStart / 1000), int(wbalCurvePredict->dataSize() - 1));
int secsEnd = std::min(int(sectionEnd / 1000), int(wbalCurvePredict->dataSize() - 1));
double wbalIn = wbalCurvePredict->sample(secsStart).y();
double wbalOut = wbalCurvePredict->sample(secsEnd).y();
if (int(wbalIn / 100) == int(wbalOut / 100)) {
tooltipText = QString("%1\n%2: %3 %4")
.arg(tooltipText)
.arg(tr("W' Balance"))
.arg(wbalIn / 1000.0 , 0, 'f', 1)
.arg(tr("kJ"));
} else {
tooltipText = QString("%1\n%2: %3..%4 (%5) %6")
.arg(tooltipText)
.arg(tr("W' Balance"))
.arg(wbalIn / 1000.0 , 0, 'f', 1)
.arg(wbalOut / 1000.0 , 0, 'f', 1)
.arg((wbalOut - wbalIn) / 1000.0 , 0, 'f', 1)
.arg(tr("kJ"));
}
}
tooltip->setText(tooltipText);
}
void
ErgFilePlot::startWorkout
()
{
workoutActive = true;
selectTooltip();
selectCurves();
}
void
ErgFilePlot::stopWorkout
()
{
workoutActive = false;
selectCurves();
selectTooltip();
}
void
ErgFilePlot::selectCurves
()
{
bool showColored = ergFile
&& ! bydist
&& ( _showColorZones == 1
|| (_showColorZones == 2 && ! workoutActive));
if (showColored) {
LodCurve->hide();
for (int i = 0; i < powerSectionCurves.size(); ++i) {
powerSectionCurves[i]->show();
}
} else {
LodCurve->show();
for (int i = 0; i < powerSectionCurves.size(); ++i) {
powerSectionCurves[i]->hide();
}
}
replot();
}
void
ErgFilePlot::selectTooltip
()
{
if ( ergFile
&& ! bydist
&& (_showTooltip == 1 && ! workoutActive)) {
installEventFilter(canvas());
picker->setEnabled(true);
} else {
removeEventFilter(canvas());
picker->setEnabled(false);
}
}
QString
ErgFilePlot::secsToString
(int fullSecs) const
{
int secs = fullSecs % 60;
int mins = (fullSecs / 60) % 60;
int hours = fullSecs / 3600;
QString time;
if (hours > 0) {
if (secs > 0) {
time = QString("%1h %2m %3s").arg(hours).arg(mins).arg(secs);
} else {
time = QString("%1h %2m").arg(hours).arg(mins);
}
} else if (mins > 0) {
if (secs > 0) {
time = QString("%1m %2s").arg(mins).arg(secs);
} else {
time = QString("%2m").arg(mins);
}
} else {
time = QString("%1s").arg(secs);
}
return time;
}
void
ErgFilePlot::highlightSectionCurve
(QwtPlotCurve const * const highlightedCurve)
{
bool needsReplot = false;
for (QwtPlotCurve*& curve : powerSectionCurves) {
QBrush brush = curve->brush();
QColor color = brush.color();
if (curve != highlightedCurve) {
if (color.alpha() != sectionAlphaNeutral) {
color.setAlpha(sectionAlphaNeutral);
brush.setColor(color);
curve->setBrush(brush);
needsReplot = true;
}
} else {
if (color.alpha() == sectionAlphaNeutral) {
color.setAlpha(sectionAlphaHovered);
brush.setColor(color);
curve->setBrush(brush);
needsReplot = true;
}
}
}
if (needsReplot) {
replot();
}
}
// curve data.. code snaffled in from the Qwt example (realtime_plot)
CurveData::CurveData(): d_count(0) { }

View File

@@ -40,6 +40,7 @@
#include "Settings.h"
#include "Colors.h"
#include "PowerHist.h" // for penTooltip
#include "RealtimeData.h"
#include <qwt_series_data.h>
@@ -50,83 +51,89 @@
class ErgFileData : public QwtPointArrayData<double>
{
public:
ErgFileData (Context *context) : QwtPointArrayData(QVector<double>(), QVector<double>()), context(context) {}
double x(size_t i) const ;
double y(size_t i) const ;
size_t size() const ;
void setByDist(bool bd) { bydist = bd; };
bool byDist() const { return bydist; };
ErgFileData (Context *context) : QwtPointArrayData(QVector<double>(), QVector<double>()), context(context) {}
double x(size_t i) const;
double y(size_t i) const;
size_t size() const;
void setByDist(bool bd) { bydist = bd; };
bool byDist() const { return bydist; };
private:
Context *context;
bool bydist = false;
Context *context;
bool bydist = false;
virtual QPointF sample(size_t i) const;
virtual QRectF boundingRect() const;
virtual QPointF sample(size_t i) const;
virtual QRectF boundingRect() const;
};
class NowData : public QwtPointArrayData<double>
{
public:
NowData (Context *context) : QwtPointArrayData(QVector<double>(), QVector<double>()), context(context) {}
double x(size_t i) const ;
double y(size_t i) const ;
size_t size() const ;
void setByDist(bool bd) { bydist = bd; };
bool byDist() const { return bydist; };
NowData (Context *context) : QwtPointArrayData(QVector<double>(), QVector<double>()), context(context) {}
double x(size_t i) const;
double y(size_t i) const;
size_t size() const;
void setByDist(bool bd) { bydist = bd; };
bool byDist() const { return bydist; };
void init();
void init() ;
private:
Context *context;
bool bydist = false;
Context *context;
bool bydist = false;
virtual QPointF sample(size_t i) const;
//virtual QRectF boundingRect() const;
virtual QPointF sample(size_t i) const;
//virtual QRectF boundingRect() const;
};
// incremental data, for each curve
class CurveData
{
// A container class for growing data
public:
public:
CurveData();
CurveData();
void append(double *x, double *y, int count);
void clear();
void append(double *x, double *y, int count);
void clear();
int count() const;
int size() const;
const double *x() const;
const double *y() const;
int count() const;
int size() const;
const double *x() const;
const double *y() const;
private:
int d_count;
QVector<double> d_x;
QVector<double> d_y;
private:
int d_count;
QVector<double> d_x;
QVector<double> d_y;
};
class DistScaleDraw: public QwtScaleDraw
{
public:
DistScaleDraw() { }
public:
DistScaleDraw() { }
// we do 100m for <= a kilo
virtual QwtText label(double v) const { if (v<1000) return QString("%1").arg(v/1000, 0, 'g', 1);
else return QString("%1").arg(round(v/1000)); }
// we do 100m for <= a kilo
virtual QwtText label(double v) const { if (v<1000) return QString("%1").arg(v/1000, 0, 'g', 1);
else return QString("%1").arg(round(v/1000)); }
};
class HourTimeScaleDraw: public QwtScaleDraw
{
public:
HourTimeScaleDraw() { }
public:
HourTimeScaleDraw() { }
virtual QwtText label(double v) const {
v /= 1000;
QTime t = QTime(0,0,0,0).addSecs(v);
if (scaleMap().sDist() > 5)
return t.toString("hh:mm");
return t.toString("hh:mm:ss");
}
virtual QwtText label(double v) const {
v /= 1000;
QTime t = QTime(0,0,0,0).addSecs(v);
if (scaleMap().sDist() > 5)
return t.toString("hh:mm");
return t.toString("hh:mm:ss");
}
};
class ErgFilePlot : public QwtPlot
@@ -134,64 +141,81 @@ class ErgFilePlot : public QwtPlot
Q_OBJECT
G_OBJECT
public:
ErgFilePlot(Context *context);
ErgFilePlot(Context *context);
QList<QwtPlotMarker *> Marks;
QList<QwtPlotMarker *> Marks;
void setData(ErgFile *); // set the course
void reset(); // reset counters etc when plot changes
void setNow(long); // set point we're add for progress pointer
void setData(ErgFile *); // set the course
void reset(); // reset counters etc when plot changes
void setNow(long); // set point we're add for progress pointer
bool eventFilter(QObject *obj, QEvent *event);
public slots:
void performancePlot(RealtimeData);
void configChanged(qint32);
void start();
void hover(const QPoint &point);
void startWorkout();
void stopWorkout();
void selectCurves();
void selectTooltip();
void performancePlot(RealtimeData);
void configChanged(qint32);
void start();
int showColorZones() const;
void setShowColorZones(int index);
int showTooltip() const;
void setShowTooltip(int index);
private:
WPrime calculator;
Context *context;
bool bydist;
ErgFile *ergFile;
QwtPlotMarker *CPMarker;
WPrime calculator;
Context *context;
bool bydist;
ErgFile *ergFile;
QwtPlotMarker *CPMarker;
int _showColorZones = 0;
int _showTooltip = 0;
QwtPlotGrid *grid;
QwtPlotCurve *LodCurve;
QwtPlotCurve *wbalCurve;
QwtPlotCurve *wbalCurvePredict;
QwtPlotCurve *wattsCurve;
QwtPlotCurve *hrCurve;
QwtPlotCurve *cadCurve;
QwtPlotCurve *speedCurve;
QwtPlotCurve *NowCurve;
QwtPlotGrid *grid;
QList<QwtPlotCurve*> powerSectionCurves;
QwtPlotCurve *LodCurve;
QwtPlotCurve *wbalCurve;
QwtPlotCurve *wbalCurvePredict;
QwtPlotCurve *wattsCurve;
QwtPlotCurve *hrCurve;
QwtPlotCurve *cadCurve;
QwtPlotCurve *speedCurve;
QwtPlotCurve *NowCurve;
QwtPlotCurve *powerHeadroom;
CurveData *wattsData,
*hrData,
*cadData,
*wbalData,
*speedData;
CurveData *wattsData,
*hrData,
*cadData,
*wbalData,
*speedData;
double counter;
double wattssum,
hrsum,
cadsum,
wbalsum,
speedsum;
double counter;
double wattssum,
hrsum,
cadsum,
wbalsum,
speedsum;
ErgFileData *lodData;
NowData *nowData;
ErgFileData *lodData;
NowData *nowData;
DistScaleDraw *distdraw;
HourTimeScaleDraw *timedraw;
DistScaleDraw *distdraw;
HourTimeScaleDraw *timedraw;
ErgFilePlot();
QwtPlotPicker *picker;
penTooltip *tooltip;
bool workoutActive = false;
void highlightSectionCurve(QwtPlotCurve const * const highlightedCurve);
QString secsToString(int fullSecs) const;
};
#endif

View File

@@ -21,14 +21,45 @@
#include "Context.h"
#include "HelpWhatsThis.h"
#include <QFormLayout>
#include <QGroupBox>
WorkoutPlotWindow::WorkoutPlotWindow(Context *context) :
GcChartWindow(context), context(context)
{
HelpWhatsThis *helpContents = new HelpWhatsThis(this);
this->setWhatsThis(helpContents->getWhatsThisText(HelpWhatsThis::ChartTrain_Workout));
// Chart settings
QWidget *settingsWidget = new QWidget(this);
settingsWidget->setContentsMargins(0, 0, 0, 0);
QVBoxLayout *commonLayout = new QVBoxLayout(settingsWidget);
ctrlsGroupBox = new QGroupBox(tr("Ergmode specific settings"));
commonLayout->addWidget(ctrlsGroupBox);
commonLayout->addStretch();
QFormLayout *ergmodeLayout = new QFormLayout(ctrlsGroupBox);
ctrlsSituationLabel = new QLabel(tr("Color power zones"));
ctrlsSituation = new QComboBox();
ctrlsSituation->addItem(tr("Never"));
ctrlsSituation->addItem(tr("Always"));
ctrlsSituation->addItem(tr("When stopped"));
connect(ctrlsSituation, SIGNAL(currentIndexChanged(int)), this, SLOT(setShowColorZones(int)));
ergmodeLayout->addRow(ctrlsSituationLabel, ctrlsSituation);
ctrlsShowTooltipLabel = new QLabel(tr("Show tooltip"));
ctrlsShowTooltip = new QComboBox();
ctrlsShowTooltip->addItem(tr("Never"));
ctrlsShowTooltip->addItem(tr("When stopped"));
connect(ctrlsShowTooltip, SIGNAL(currentIndexChanged(int)), this, SLOT(setShowTooltip(int)));
ergmodeLayout->addRow(ctrlsShowTooltipLabel, ctrlsShowTooltip);
setContentsMargins(0,0,0,0);
setControls(NULL);
setControls(settingsWidget);
setProperty("color", GColor(CTRAINPLOTBACKGROUND));
QVBoxLayout *layout = new QVBoxLayout;
@@ -69,5 +100,50 @@ void
WorkoutPlotWindow::configChanged(qint32)
{
setProperty("color", GColor(CTRAINPLOTBACKGROUND));
ctrlsGroupBox->setTitle(tr("Ergmode specific settings"));
ctrlsSituationLabel->setText(tr("Color power zones"));
ctrlsSituation->setItemText(0, tr("Never"));
ctrlsSituation->setItemText(1, tr("Always"));
ctrlsSituation->setItemText(2, tr("When stopped"));
ctrlsShowTooltipLabel->setText(tr("Show tooltip"));
ctrlsShowTooltip->setItemText(0, tr("Never"));
ctrlsShowTooltip->setItemText(1, tr("When stopped"));
repaint();
}
int
WorkoutPlotWindow::showColorZones
() const
{
return ctrlsSituation->currentIndex();
}
void
WorkoutPlotWindow::setShowColorZones
(int index)
{
ctrlsSituation->setCurrentIndex(index);
ergPlot->setShowColorZones(index);
}
int
WorkoutPlotWindow::showTooltip
() const
{
return ctrlsShowTooltip->currentIndex();
}
void
WorkoutPlotWindow::setShowTooltip
(int index)
{
ctrlsShowTooltip->setCurrentIndex(index);
ergPlot->setShowTooltip(index);
}

View File

@@ -32,26 +32,44 @@
#include "Settings.h"
#include "Colors.h"
#include <QGroupBox>
#include <QLabel>
#include <QComboBox>
class WorkoutPlotWindow : public GcChartWindow
{
Q_OBJECT
G_OBJECT
Q_PROPERTY(int showColorZones READ showColorZones WRITE setShowColorZones USER true)
Q_PROPERTY(int showTooltip READ showTooltip WRITE setShowTooltip USER true)
public:
WorkoutPlotWindow(Context *context);
public slots:
public slots:
// trap signals
void setNow(long now);
void ergFileSelected(ErgFile *);
void configChanged(qint32);
int showColorZones() const;
void setShowColorZones(int index);
int showTooltip() const;
void setShowTooltip(int index);
private:
Context *context;
ErgFilePlot *ergPlot;
QGroupBox *ctrlsGroupBox;
QLabel *ctrlsSituationLabel;
QComboBox *ctrlsSituation;
QLabel *ctrlsShowTooltipLabel;
QComboBox *ctrlsShowTooltip;
};
#endif // _GC_WorkoutPlotWindow_h