mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
Metric Histogram Plot
Update to the histogram plot to now plot long term metrics. It enables you to plot distribution of say, Intensity Factor for a season or cycle. You can select the y-axis metric too, so rather than just plotting duration you could plot say, TSS accumulated for different ride intensities. Fixes #560
This commit is contained in:
Binary file not shown.
@@ -681,10 +681,20 @@ below in the secion @emph{Adding and adjusting charts}.
|
||||
@center @image{"image/2-chartmenu",180pt}
|
||||
@center @emph{Figure 9: The `+' Add Chart Menu}
|
||||
@vskip 6pt
|
||||
|
||||
@strong{NOTE:} Only the windows that are relevant for the current view will be listed. You cannot, for example, add a long term
|
||||
metric chart to the analysis view, or a performance chart (all plot) to the diary view.
|
||||
@end itemize
|
||||
|
||||
@section Views & Sidebar
|
||||
@vskip 12pt
|
||||
@noindent @image{"image/2-sideactivity",70pt}
|
||||
@noindent @image{"image/2-sideinterval",70pt}
|
||||
@noindent @image{"image/2-sidediary",70pt}
|
||||
@noindent @image{"image/2-sidehome",70pt}
|
||||
@noindent @image{"image/2-sidesummary",70pt}
|
||||
@noindent @image{"image/2-sidetrain",70pt}
|
||||
@center @emph{Figure 10: Sidebars}
|
||||
@vskip 6pt
|
||||
scopebar, sidebar, tab/tiled, add chart menu
|
||||
|
||||
@section Searching and Filtering
|
||||
|
||||
@@ -53,7 +53,7 @@ class AerobicDecoupling : public RideMetric {
|
||||
setType(RideMetric::Average);
|
||||
setMetricUnits(tr("%"));
|
||||
setImperialUnits(tr("%"));
|
||||
setPrecision(2);
|
||||
setPrecision(1);
|
||||
}
|
||||
void compute(const RideFile *ride, const Zones *, int,
|
||||
const HrZones *, int,
|
||||
|
||||
@@ -56,7 +56,7 @@ ColorButton::clicked()
|
||||
// Color picker dialog
|
||||
QColorDialog picker(this);
|
||||
picker.setCurrentColor(color);
|
||||
QColor rcolor = picker.getColor();
|
||||
QColor rcolor = picker.getColor(color, this, tr("Choose Color"), QColorDialog::DontUseNativeDialog);
|
||||
|
||||
// if we got a good color use it and notify others
|
||||
if (rcolor.isValid()) {
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
// 41 27 Oct 2012 Mark Liversedge Lucene switched to StandardAnalyzer and search all texts by default
|
||||
// 42 03 Dec 2012 Mark Liversedge W/KG ridefilecache changes - force a rebuild.
|
||||
// 43 24 Jan 2012 Mark Liversedge TRIMP update
|
||||
// 44 19 Apr 2013 Mark Liversedge Aerobic Decoupling precision reduced to 1pt
|
||||
|
||||
static int DBSchemaVersion = 43;
|
||||
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
|
||||
#include "HistogramWindow.h"
|
||||
#include "MainWindow.h"
|
||||
#include "MetricAggregator.h"
|
||||
#include "SummaryMetrics.h"
|
||||
#include "ChartSettings.h"
|
||||
#include "ColorButton.h"
|
||||
#include "PowerHist.h"
|
||||
#include "RideFile.h"
|
||||
#include "RideFileCache.h"
|
||||
@@ -30,13 +34,35 @@
|
||||
#include "Zones.h"
|
||||
#include "HrZones.h"
|
||||
|
||||
HistogramWindow::HistogramWindow(MainWindow *mainWindow, bool rangemode) : GcChartWindow(mainWindow), mainWindow(mainWindow), stale(true), source(NULL), rangemode(rangemode), useCustom(false), useToToday(false)
|
||||
// predefined deltas for each series
|
||||
static const double wattsDelta = 1.0;
|
||||
static const double wattsKgDelta = 0.01;
|
||||
static const double nmDelta = 0.1;
|
||||
static const double hrDelta = 1.0;
|
||||
static const double kphDelta = 0.1;
|
||||
static const double cadDelta = 1.0;
|
||||
|
||||
// digits for text entry validator
|
||||
static const int wattsDigits = 0;
|
||||
static const int wattsKgDigits = 2;
|
||||
static const int nmDigits = 1;
|
||||
static const int hrDigits = 0;
|
||||
static const int kphDigits = 1;
|
||||
static const int cadDigits = 0;
|
||||
|
||||
|
||||
//
|
||||
// Constructor
|
||||
//
|
||||
HistogramWindow::HistogramWindow(MainWindow *mainWindow, bool rangemode) : GcChartWindow(mainWindow), mainWindow(mainWindow), stale(true), source(NULL), active(false), bactive(false), rangemode(rangemode), useCustom(false), useToToday(false), precision(99)
|
||||
{
|
||||
setInstanceName("Histogram Window");
|
||||
|
||||
QWidget *c = new QWidget;
|
||||
c->setContentsMargins(0,0,0,0);
|
||||
QFormLayout *cl = new QFormLayout(c);
|
||||
cl->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
|
||||
cl->setSpacing(5);
|
||||
setControls(c);
|
||||
|
||||
//
|
||||
@@ -46,7 +72,7 @@ HistogramWindow::HistogramWindow(MainWindow *mainWindow, bool rangemode) : GcCha
|
||||
// reveal controls
|
||||
rWidth = new QLabel(tr("Bin Width"));
|
||||
rBinEdit = new QLineEdit();
|
||||
rBinEdit->setFixedWidth(30);
|
||||
rBinEdit->setFixedWidth(40);
|
||||
rBinSlider = new QSlider(Qt::Horizontal);
|
||||
rBinSlider->setTickPosition(QSlider::TicksBelow);
|
||||
rBinSlider->setTickInterval(10);
|
||||
@@ -84,27 +110,148 @@ HistogramWindow::HistogramWindow(MainWindow *mainWindow, bool rangemode) : GcCha
|
||||
searchBox = new SearchFilterBox(this, mainWindow);
|
||||
connect(searchBox, SIGNAL(searchClear()), this, SLOT(clearFilter()));
|
||||
connect(searchBox, SIGNAL(searchResults(QStringList)), this, SLOT(setFilter(QStringList)));
|
||||
cl->addRow(new QLabel(tr("Filter")), searchBox);
|
||||
if (!rangemode) searchBox->hide();
|
||||
else {
|
||||
cl->addRow(new QLabel(tr("Filter")), searchBox);
|
||||
cl->addWidget(new QLabel(""));
|
||||
}
|
||||
#endif
|
||||
|
||||
// spacing if we have a range
|
||||
if (rangemode) cl->addWidget(new QLabel(""));
|
||||
|
||||
// date selection
|
||||
dateSetting = new DateSettingsEdit(this);
|
||||
if (rangemode) cl->addRow(new QLabel(tr("Date Range")), dateSetting);
|
||||
|
||||
if (rangemode) {
|
||||
|
||||
cl->addRow(new QLabel(tr("Date Range")), dateSetting);
|
||||
cl->addWidget(new QLabel("")); // spacing
|
||||
|
||||
// default to data series!
|
||||
data = new QRadioButton(tr("Ride Data Samples"));
|
||||
metric = new QRadioButton(tr("Ride Metrics"));
|
||||
data->setChecked(true);
|
||||
metric->setChecked(false);
|
||||
QHBoxLayout *radios = new QHBoxLayout;
|
||||
radios->addWidget(data);
|
||||
radios->addWidget(metric);
|
||||
cl->addRow(new QLabel(tr("Plot")), radios);
|
||||
|
||||
connect(data, SIGNAL(toggled(bool)), this, SLOT(dataToggled(bool)));
|
||||
connect(metric, SIGNAL(toggled(bool)), this, SLOT(metricToggled(bool)));
|
||||
}
|
||||
|
||||
// data series
|
||||
cl->addWidget(new QLabel("")); // spacing
|
||||
seriesCombo = new QComboBox();
|
||||
addSeries();
|
||||
cl->addRow(new QLabel(tr("Data Series")), seriesCombo);
|
||||
if (rangemode) comboLabel = new QLabel("");
|
||||
else comboLabel = new QLabel(tr("Data Series"));
|
||||
cl->addRow(comboLabel, seriesCombo);
|
||||
|
||||
if (rangemode) {
|
||||
|
||||
// TOTAL METRIC
|
||||
totalMetricTree = new QTreeWidget;
|
||||
#ifdef Q_OS_MAC
|
||||
totalMetricTree->setAttribute(Qt::WA_MacShowFocusRect, 0);
|
||||
#endif
|
||||
totalMetricTree->setColumnCount(2);
|
||||
totalMetricTree->setColumnHidden(1, true);
|
||||
totalMetricTree->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
totalMetricTree->header()->hide();
|
||||
//totalMetricTree->setFrameStyle(QFrame::NoFrame);
|
||||
//totalMetricTree->setAlternatingRowColors (true);
|
||||
totalMetricTree->setIndentation(5);
|
||||
totalMetricTree->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
// ALL METRIC
|
||||
distMetricTree = new QTreeWidget;
|
||||
#ifdef Q_OS_MAC
|
||||
distMetricTree->setAttribute(Qt::WA_MacShowFocusRect, 0);
|
||||
#endif
|
||||
distMetricTree->setColumnCount(2);
|
||||
distMetricTree->setColumnHidden(1, true);
|
||||
distMetricTree->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
distMetricTree->header()->hide();
|
||||
//distMetricTree->setFrameStyle(QFrame::NoFrame);
|
||||
distMetricTree->setIndentation(5);
|
||||
distMetricTree->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
// add them all
|
||||
const RideMetricFactory &factory = RideMetricFactory::instance();
|
||||
for (int i = 0; i < factory.metricCount(); ++i) {
|
||||
|
||||
const RideMetric *m = factory.rideMetric(factory.metricName(i));
|
||||
|
||||
QTextEdit processHTML(m->name()); // process html encoding of(TM)
|
||||
QString processed = processHTML.toPlainText();
|
||||
|
||||
QTreeWidgetItem *add;
|
||||
add = new QTreeWidgetItem(distMetricTree->invisibleRootItem());
|
||||
add->setText(0, processed);
|
||||
add->setText(1, m->symbol());
|
||||
|
||||
// we only want totalising metrics
|
||||
if (m->type() != RideMetric::Total) continue;
|
||||
|
||||
add = new QTreeWidgetItem(totalMetricTree->invisibleRootItem());
|
||||
add->setText(0, processed);
|
||||
add->setText(1, m->symbol());
|
||||
}
|
||||
|
||||
QHBoxLayout *labels = new QHBoxLayout;
|
||||
|
||||
metricLabel1 = new QLabel(tr("Total (x-axis)"));
|
||||
labels->addWidget(metricLabel1);
|
||||
metricLabel1->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
|
||||
metricLabel2 = new QLabel(tr("Distribution (y-axis)"));
|
||||
metricLabel2->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
labels->addWidget(metricLabel2);
|
||||
|
||||
cl->addRow((blankLabel1=new QLabel("")), labels);
|
||||
QHBoxLayout *trees = new QHBoxLayout;
|
||||
trees->addWidget(totalMetricTree);
|
||||
trees->addWidget(distMetricTree);
|
||||
cl->addRow((blankLabel2 = new QLabel("")), trees);
|
||||
|
||||
colorButton = new ColorButton(this, "Color", QColor(Qt::blue));
|
||||
colorLabel = new QLabel(tr("Color"));
|
||||
connect(colorButton, SIGNAL(colorChosen(QColor)), this, SLOT(updateChart()));
|
||||
cl->addRow(colorLabel, colorButton);
|
||||
|
||||
// by default select number of rides by duration
|
||||
// which are the metrics workout_time and ride_count
|
||||
selectTotal("ride_count");
|
||||
selectMetric("workout_time");
|
||||
}
|
||||
|
||||
showSumY = new QComboBox();
|
||||
showSumY->addItem(tr("Absolute Time"));
|
||||
showSumY->addItem(tr("Percentage Time"));
|
||||
|
||||
showLabel = new QLabel(tr("Show"));
|
||||
cl->addRow(showLabel, showSumY);
|
||||
|
||||
showLnY = new QCheckBox;
|
||||
showLnY->setText(tr("Log Y"));
|
||||
cl->addRow(blankLabel3 = new QLabel(""), showLnY);
|
||||
|
||||
showZeroes = new QCheckBox;
|
||||
showZeroes->setText(tr("With zeros"));
|
||||
cl->addRow(blankLabel4 = new QLabel(""), showZeroes);
|
||||
|
||||
shadeZones = new QCheckBox;
|
||||
shadeZones->setText(tr("Shade zones"));
|
||||
cl->addRow(blankLabel5 = new QLabel(""), shadeZones);
|
||||
|
||||
showInZones = new QCheckBox;
|
||||
showInZones->setText(tr("Show in zones"));
|
||||
cl->addRow(blankLabel6 = new QLabel(""), showInZones);
|
||||
|
||||
// bin width
|
||||
QHBoxLayout *binWidthLayout = new QHBoxLayout;
|
||||
QLabel *binWidthLabel = new QLabel(tr("Bin width"), this);
|
||||
binWidthLineEdit = new QLineEdit(this);
|
||||
binWidthLineEdit->setFixedWidth(30);
|
||||
binWidthLineEdit->setFixedWidth(40);
|
||||
|
||||
binWidthLayout->addWidget(binWidthLineEdit);
|
||||
binWidthSlider = new QSlider(Qt::Horizontal);
|
||||
@@ -115,39 +262,20 @@ HistogramWindow::HistogramWindow(MainWindow *mainWindow, bool rangemode) : GcCha
|
||||
binWidthLayout->addWidget(binWidthSlider);
|
||||
cl->addRow(binWidthLabel, binWidthLayout);
|
||||
|
||||
showSumY = new QComboBox();
|
||||
showSumY->addItem(tr("Absolute Time"));
|
||||
showSumY->addItem(tr("Percentage Time"));
|
||||
|
||||
cl->addWidget(new QLabel("")); // spacing
|
||||
cl->addRow(new QLabel("Show"), showSumY);
|
||||
|
||||
showLnY = new QCheckBox;
|
||||
showLnY->setText(tr("Log Y"));
|
||||
cl->addRow(new QLabel(""), showLnY);
|
||||
|
||||
showZeroes = new QCheckBox;
|
||||
showZeroes->setText(tr("With zeros"));
|
||||
cl->addRow(new QLabel(""), showZeroes);
|
||||
|
||||
shadeZones = new QCheckBox;
|
||||
shadeZones->setText(tr("Shade zones"));
|
||||
cl->addRow(new QLabel(""), shadeZones);
|
||||
|
||||
showInZones = new QCheckBox;
|
||||
showInZones->setText(tr("Show in zones"));
|
||||
cl->addRow(new QLabel(""), showInZones);
|
||||
|
||||
|
||||
// sort out default values
|
||||
setHistTextValidator();
|
||||
setBinEditors();
|
||||
showLnY->setChecked(powerHist->islnY());
|
||||
showZeroes->setChecked(powerHist->withZeros());
|
||||
shadeZones->setChecked(powerHist->shade);
|
||||
binWidthSlider->setValue(powerHist->binWidth());
|
||||
rBinSlider->setValue(powerHist->binWidth());
|
||||
rShade->setChecked(powerHist->shade);
|
||||
setHistBinWidthText();
|
||||
|
||||
// fixup series selected by default
|
||||
seriesChanged();
|
||||
|
||||
// hide/show according to default mode
|
||||
switchMode(); // does nothing if not in rangemode
|
||||
|
||||
// set the defaults etc
|
||||
updateChart();
|
||||
@@ -167,6 +295,13 @@ HistogramWindow::HistogramWindow(MainWindow *mainWindow, bool rangemode) : GcCha
|
||||
connect(dateSetting, SIGNAL(useCustomRange(DateRange)), this, SLOT(useCustomRange(DateRange)));
|
||||
connect(dateSetting, SIGNAL(useThruToday()), this, SLOT(useThruToday()));
|
||||
connect(dateSetting, SIGNAL(useStandardRange()), this, SLOT(useStandardRange()));
|
||||
connect(distMetricTree, SIGNAL(itemSelectionChanged()), this, SLOT(treeSelectionChanged()));
|
||||
connect(totalMetricTree, SIGNAL(itemSelectionChanged()), this, SLOT(treeSelectionChanged()));
|
||||
|
||||
lagger = new QTimer;
|
||||
lagger->setSingleShot(true);
|
||||
connect(lagger, SIGNAL(timeout()), this, SLOT(treeSelectionTimeout()));
|
||||
|
||||
} else {
|
||||
dateSetting->hide();
|
||||
connect(this, SIGNAL(rideItemChanged(RideItem*)), this, SLOT(rideSelected()));
|
||||
@@ -176,7 +311,7 @@ HistogramWindow::HistogramWindow(MainWindow *mainWindow, bool rangemode) : GcCha
|
||||
// if any of the controls change we pass the chart everything
|
||||
connect(showLnY, SIGNAL(stateChanged(int)), this, SLOT(updateChart()));
|
||||
connect(showZeroes, SIGNAL(stateChanged(int)), this, SLOT(updateChart()));
|
||||
connect(seriesCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateChart()));
|
||||
connect(seriesCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(seriesChanged()));
|
||||
connect(showInZones, SIGNAL(stateChanged(int)), this, SLOT(setZoned(int)));
|
||||
connect(showInZones, SIGNAL(stateChanged(int)), this, SLOT(updateChart()));
|
||||
connect(shadeZones, SIGNAL(stateChanged(int)), this, SLOT(setShade(int)));
|
||||
@@ -191,6 +326,311 @@ HistogramWindow::HistogramWindow(MainWindow *mainWindow, bool rangemode) : GcCha
|
||||
connect(mainWindow, SIGNAL(filterChanged(QStringList&)), this, SLOT(forceReplot()));
|
||||
}
|
||||
|
||||
//
|
||||
// Colors (used by metric plotting)
|
||||
//
|
||||
QString HistogramWindow::plotColor() const
|
||||
{
|
||||
if (!rangemode) return QColor(Qt::blue).name();
|
||||
else {
|
||||
return colorButton->getColor().name();
|
||||
}
|
||||
}
|
||||
|
||||
void HistogramWindow::setPlotColor(QString color)
|
||||
{
|
||||
if (rangemode) colorButton->setColor(QColor(color));
|
||||
}
|
||||
|
||||
//
|
||||
// Set Bin Width property by setting the slider
|
||||
//
|
||||
void HistogramWindow::setBin(double x)
|
||||
{
|
||||
if (bactive) return;
|
||||
bactive = true;
|
||||
binWidthSlider->setValue(x/getDelta());
|
||||
rBinSlider->setValue(x/getDelta());
|
||||
binWidthLineEdit->setText(QString("%1").arg(x, 0, 'f', getDigits()));
|
||||
rBinEdit->setText(QString("%1").arg(x, 0, 'f', getDigits()));
|
||||
powerHist->setBinWidth(x);
|
||||
bactive = false;
|
||||
|
||||
// redraw
|
||||
stale = true;
|
||||
updateChart();
|
||||
}
|
||||
|
||||
//
|
||||
// Get/Set the metric selections
|
||||
//
|
||||
QString HistogramWindow::distMetric() const
|
||||
{
|
||||
if (!rangemode) return "workout_time";
|
||||
else {
|
||||
// get current selection
|
||||
QList<QTreeWidgetItem *> select = distMetricTree->selectedItems();
|
||||
if (select.count() == 0) return "workout_time";
|
||||
else return select[0]->text(1);
|
||||
}
|
||||
}
|
||||
|
||||
void HistogramWindow::setDistMetric(QString value)
|
||||
{
|
||||
if (!rangemode) return;
|
||||
selectMetric(value);
|
||||
}
|
||||
|
||||
QString HistogramWindow::totalMetric() const
|
||||
{
|
||||
if (!rangemode) return "ride_count";
|
||||
else {
|
||||
// get current selection
|
||||
QList<QTreeWidgetItem *> select = totalMetricTree->selectedItems();
|
||||
if (select.count() == 0) return "ride_count";
|
||||
else return select[0]->text(1);
|
||||
}
|
||||
}
|
||||
|
||||
void HistogramWindow::setTotalMetric(QString value)
|
||||
{
|
||||
if (!rangemode) return;
|
||||
selectTotal(value);
|
||||
}
|
||||
|
||||
void
|
||||
HistogramWindow::selectTotal(QString symbol)
|
||||
{
|
||||
QList<QTreeWidgetItem *> found = totalMetricTree->findItems(symbol, Qt::MatchRecursive|Qt::MatchExactly, 1);
|
||||
if (found.count() == 0) return;
|
||||
|
||||
// clear the current selection
|
||||
foreach(QTreeWidgetItem *selected, totalMetricTree->selectedItems()) selected->setSelected(false);
|
||||
|
||||
// select the first one (there shouldn't be more than that!!!
|
||||
found[0]->setSelected(true);
|
||||
|
||||
// make sure it is the current item and visible
|
||||
totalMetricTree->setCurrentItem(found[0]);
|
||||
totalMetricTree->scrollToItem(totalMetricTree->currentItem());
|
||||
}
|
||||
|
||||
void
|
||||
HistogramWindow::selectMetric(QString symbol)
|
||||
{
|
||||
QList<QTreeWidgetItem *> found = distMetricTree->findItems(symbol, Qt::MatchRecursive|Qt::MatchExactly, 1);
|
||||
if (found.count() == 0) return;
|
||||
|
||||
// clear the current selection
|
||||
foreach(QTreeWidgetItem *selected, distMetricTree->selectedItems()) selected->setSelected(false);
|
||||
|
||||
// select the first one (there shouldn't be more than that!!!
|
||||
found[0]->setSelected(true);
|
||||
|
||||
// make sure it is the current item and visible
|
||||
distMetricTree->setCurrentItem(found[0]);
|
||||
distMetricTree->scrollToItem(distMetricTree->currentItem());
|
||||
}
|
||||
|
||||
//
|
||||
// get set mode (data series or metric?) -- only in rangemode
|
||||
//
|
||||
bool HistogramWindow::dataMode() const
|
||||
{
|
||||
if (!rangemode) return true;
|
||||
else return data->isChecked();
|
||||
}
|
||||
|
||||
void HistogramWindow::setDataMode(bool value)
|
||||
{
|
||||
if (!rangemode) return;
|
||||
else {
|
||||
active = true;
|
||||
data->setChecked(value);
|
||||
metric->setChecked(!value);
|
||||
active = false;
|
||||
switchMode();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// When user changes from data series to metric
|
||||
//
|
||||
void
|
||||
HistogramWindow::dataToggled(bool x)
|
||||
{
|
||||
if (active) return;
|
||||
|
||||
active = true;
|
||||
stale = true;
|
||||
metric->setChecked(!x);
|
||||
switchMode();
|
||||
active = false;
|
||||
}
|
||||
|
||||
void
|
||||
HistogramWindow::metricToggled(bool x)
|
||||
{
|
||||
if (active) return;
|
||||
|
||||
active = true;
|
||||
stale = true;
|
||||
data->setChecked(!x);
|
||||
switchMode();
|
||||
active = false;
|
||||
}
|
||||
|
||||
void
|
||||
HistogramWindow::switchMode()
|
||||
{
|
||||
if (!rangemode) return; // ! only valid in rangemode
|
||||
|
||||
if (data->isChecked()) {
|
||||
|
||||
// hide all the metric controls
|
||||
blankLabel1->hide();
|
||||
blankLabel2->hide();
|
||||
distMetricTree->hide();
|
||||
totalMetricTree->hide();
|
||||
metricLabel1->hide();
|
||||
metricLabel2->hide();
|
||||
colorLabel->hide();
|
||||
colorButton->hide();
|
||||
|
||||
// show all the data series controls
|
||||
comboLabel->show();
|
||||
seriesCombo->show();
|
||||
blankLabel3->show();
|
||||
blankLabel4->show();
|
||||
blankLabel5->show();
|
||||
blankLabel6->show();
|
||||
showSumY->show();
|
||||
showLabel->show();
|
||||
showLnY->show();
|
||||
showZeroes->show();
|
||||
shadeZones->show();
|
||||
showInZones->show();
|
||||
|
||||
// select the series..
|
||||
seriesChanged();
|
||||
|
||||
} else {
|
||||
|
||||
// hide all the data series controls
|
||||
comboLabel->hide();
|
||||
seriesCombo->hide();
|
||||
blankLabel3->hide();
|
||||
blankLabel4->hide();
|
||||
blankLabel5->hide();
|
||||
blankLabel6->hide();
|
||||
showSumY->hide();
|
||||
showLabel->hide();
|
||||
showLnY->hide();
|
||||
showZeroes->hide();
|
||||
shadeZones->hide();
|
||||
showInZones->hide();
|
||||
|
||||
// show all the metric controls
|
||||
blankLabel1->show();
|
||||
blankLabel2->show();
|
||||
metricLabel1->show();
|
||||
metricLabel2->show();
|
||||
distMetricTree->show();
|
||||
totalMetricTree->show();
|
||||
colorLabel->show();
|
||||
colorButton->show();
|
||||
|
||||
// select the series.. (but without the half second delay)
|
||||
treeSelectionTimeout();
|
||||
}
|
||||
mainWindow->chartSettings->adjustSize();
|
||||
|
||||
stale = true;
|
||||
updateChart(); // to whatever is currently selected.
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// When user selects a new metric
|
||||
//
|
||||
void
|
||||
HistogramWindow::treeSelectionChanged()
|
||||
{
|
||||
stale = true;
|
||||
lagger->start(500);
|
||||
}
|
||||
|
||||
void
|
||||
HistogramWindow::treeSelectionTimeout()
|
||||
{
|
||||
// new metric chosen, so set up all the bin width, line edit
|
||||
// delta, precision etc
|
||||
powerHist->setSeries(RideFile::none);
|
||||
powerHist->setDelta(getDelta());
|
||||
powerHist->setDigits(getDigits());
|
||||
|
||||
// now update the slider stepper and linedit
|
||||
setBinEditors();
|
||||
|
||||
// initial value -- but only if need to
|
||||
double minbinw = getDelta();
|
||||
double maxbinw = getDelta() * 100;
|
||||
double current = binWidthLineEdit->text().toDouble();
|
||||
if (current < minbinw || current > maxbinw) setBin(getDelta());
|
||||
|
||||
// replot
|
||||
updateChart();
|
||||
}
|
||||
|
||||
//
|
||||
// When user selects a different data series
|
||||
//
|
||||
void
|
||||
HistogramWindow::seriesChanged()
|
||||
{
|
||||
// series changed so tell power hist
|
||||
powerHist->setSeries(static_cast<RideFile::SeriesType>(seriesCombo->itemData(seriesCombo->currentIndex()).toInt()));
|
||||
powerHist->setDelta(getDelta());
|
||||
powerHist->setDigits(getDigits());
|
||||
|
||||
// now update the slider stepper and linedit
|
||||
setBinEditors();
|
||||
|
||||
// set an initial bin width value
|
||||
setBin(getDelta());
|
||||
|
||||
// replot
|
||||
stale = true;
|
||||
updateChart();
|
||||
}
|
||||
|
||||
//
|
||||
// We need to config / update the controls when data series/metrics change
|
||||
//
|
||||
void
|
||||
HistogramWindow::setBinEditors()
|
||||
{
|
||||
// the line edit validator
|
||||
QValidator *validator;
|
||||
if (getDigits() == 0) {
|
||||
|
||||
validator = new QIntValidator(binWidthSlider->minimum() * getDelta(),
|
||||
binWidthSlider->maximum() * getDelta(),
|
||||
binWidthLineEdit);
|
||||
} else {
|
||||
|
||||
validator = new QDoubleValidator(binWidthSlider->minimum() * getDelta(),
|
||||
binWidthSlider->maximum() * getDelta(),
|
||||
getDigits(),
|
||||
binWidthLineEdit);
|
||||
}
|
||||
binWidthLineEdit->setValidator(validator);
|
||||
rBinEdit->setValidator(validator);
|
||||
}
|
||||
|
||||
//
|
||||
// A new ride was selected
|
||||
//
|
||||
void
|
||||
HistogramWindow::rideSelected()
|
||||
{
|
||||
@@ -210,12 +650,9 @@ HistogramWindow::rideSelected()
|
||||
updateChart();
|
||||
}
|
||||
|
||||
void
|
||||
HistogramWindow::rideAddorRemove(RideItem *)
|
||||
{
|
||||
stale = true;
|
||||
}
|
||||
|
||||
//
|
||||
// User selected a new interval
|
||||
//
|
||||
void
|
||||
HistogramWindow::intervalSelected()
|
||||
{
|
||||
@@ -231,6 +668,12 @@ HistogramWindow::intervalSelected()
|
||||
updateChart();
|
||||
}
|
||||
|
||||
void
|
||||
HistogramWindow::rideAddorRemove(RideItem *)
|
||||
{
|
||||
stale = true;
|
||||
}
|
||||
|
||||
void
|
||||
HistogramWindow::zonesChanged()
|
||||
{
|
||||
@@ -299,80 +742,29 @@ void HistogramWindow::addSeries()
|
||||
void
|
||||
HistogramWindow::setBinWidthFromSlider()
|
||||
{
|
||||
if (powerHist->binWidth() != binWidthSlider->value()) {
|
||||
//RideFile::SeriesType series = static_cast<RideFile::SeriesType>(seriesCombo->itemData(seriesCombo->currentIndex()).toInt());
|
||||
powerHist->setBinWidth(binWidthSlider->value());
|
||||
setHistBinWidthText();
|
||||
rBinEdit->setText(binWidthLineEdit->text());
|
||||
rBinSlider->setValue(binWidthSlider->value());
|
||||
updateChart();
|
||||
}
|
||||
if (bactive) return;
|
||||
setBin(binWidthSlider->value() * getDelta());
|
||||
}
|
||||
|
||||
void
|
||||
HistogramWindow::setrBinWidthFromSlider()
|
||||
{
|
||||
if (powerHist->binWidth() != rBinSlider->value()) {
|
||||
powerHist->setBinWidth(rBinSlider->value());
|
||||
setHistBinWidthText();
|
||||
rBinEdit->setText(binWidthLineEdit->text());
|
||||
updateChart();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HistogramWindow::setHistBinWidthText()
|
||||
{
|
||||
binWidthLineEdit->setText(QString("%1").arg(powerHist->getBinWidthRealUnits(), 0, 'g', 3));
|
||||
rBinEdit->setText(QString("%1").arg(powerHist->getBinWidthRealUnits(), 0, 'g', 3));
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
HistogramWindow::setHistTextValidator()
|
||||
{
|
||||
double delta = powerHist->getDelta();
|
||||
int digits = powerHist->getDigits();
|
||||
|
||||
QValidator *validator;
|
||||
if (digits == 0) {
|
||||
|
||||
validator = new QIntValidator(binWidthSlider->minimum() * delta,
|
||||
binWidthSlider->maximum() * delta,
|
||||
binWidthLineEdit);
|
||||
} else {
|
||||
|
||||
validator = new QDoubleValidator(binWidthSlider->minimum() * delta,
|
||||
binWidthSlider->maximum() * delta,
|
||||
digits,
|
||||
binWidthLineEdit);
|
||||
}
|
||||
binWidthLineEdit->setValidator(validator);
|
||||
rBinEdit->setValidator(validator);
|
||||
if (bactive) return;
|
||||
setBin(rBinSlider->value() * getDelta());
|
||||
}
|
||||
|
||||
void
|
||||
HistogramWindow::setBinWidthFromLineEdit()
|
||||
{
|
||||
double value = binWidthLineEdit->text().toDouble();
|
||||
if (value != powerHist->binWidth()) {
|
||||
binWidthSlider->setValue(powerHist->setBinWidthRealUnits(value));
|
||||
rBinSlider->setValue(powerHist->setBinWidthRealUnits(value));
|
||||
setHistBinWidthText();
|
||||
|
||||
updateChart();
|
||||
}
|
||||
if (bactive) return;
|
||||
setBin(binWidthLineEdit->text().toDouble());
|
||||
}
|
||||
|
||||
void
|
||||
HistogramWindow::setrBinWidthFromLineEdit()
|
||||
{
|
||||
double value = rBinEdit->text().toDouble();
|
||||
if (value != powerHist->binWidth()) {
|
||||
rBinSlider->setValue(powerHist->setBinWidthRealUnits(value));
|
||||
binWidthSlider->setValue(powerHist->setBinWidthRealUnits(value));
|
||||
updateChart();
|
||||
}
|
||||
if (bactive) return;
|
||||
setBin(rBinEdit->text().toDouble());
|
||||
}
|
||||
|
||||
void
|
||||
@@ -400,13 +792,33 @@ HistogramWindow::forceReplot()
|
||||
void
|
||||
HistogramWindow::updateChart()
|
||||
{
|
||||
// refresh the ridefile cache if it is stale
|
||||
if (!amVisible()) {
|
||||
stale = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// What is the selected series?
|
||||
RideFile::SeriesType series = static_cast<RideFile::SeriesType>(seriesCombo->itemData(seriesCombo->currentIndex()).toInt());
|
||||
|
||||
// If no data present show the blank state page
|
||||
if (!rangemode) {
|
||||
RideFile::SeriesType baseSeries = (series == RideFile::wattsKg) ? RideFile::watts : series;
|
||||
if (rideItem() != NULL && rideItem()->ride()->isDataPresent(baseSeries))
|
||||
setIsBlank(false);
|
||||
else
|
||||
setIsBlank(true);
|
||||
} else {
|
||||
setIsBlank(false);
|
||||
}
|
||||
|
||||
// Lets get the data then
|
||||
if (stale) {
|
||||
|
||||
RideFileCache *old = source;
|
||||
|
||||
if (rangemode) {
|
||||
|
||||
// set the date range to the appropiate selection
|
||||
DateRange use;
|
||||
if (useCustom) {
|
||||
|
||||
@@ -423,68 +835,84 @@ HistogramWindow::updateChart()
|
||||
use = myDateRange;
|
||||
}
|
||||
|
||||
if (data->isChecked()) {
|
||||
|
||||
// plotting a data series, so refresh the ridefilecache
|
||||
|
||||
#ifdef GC_HAVE_LUCENE
|
||||
source = new RideFileCache(mainWindow, use.from, use.to, isFiltered, files);
|
||||
source = new RideFileCache(mainWindow, use.from, use.to, isFiltered, files);
|
||||
#else
|
||||
source = new RideFileCache(mainWindow, use.from, use.to);
|
||||
source = new RideFileCache(mainWindow, use.from, use.to);
|
||||
#endif
|
||||
cfrom = use.from;
|
||||
cto = use.to;
|
||||
stale = false;
|
||||
cfrom = use.from;
|
||||
cto = use.to;
|
||||
stale = false;
|
||||
|
||||
if (old) delete old; // guarantee source pointer changes
|
||||
}
|
||||
stale = false; // well we tried
|
||||
}
|
||||
if (old) delete old; // guarantee source pointer changes
|
||||
stale = false; // well we tried
|
||||
|
||||
// set data
|
||||
if (rangemode && source) {
|
||||
powerHist->setData(source);
|
||||
} else
|
||||
powerHist->setData(myRideItem, interval); // intervals selected forces data to
|
||||
// set the data on the plot
|
||||
powerHist->setData(source);
|
||||
|
||||
// and which series to plot
|
||||
powerHist->setSeries(series);
|
||||
|
||||
// and now the controls
|
||||
powerHist->setShading(shadeZones->isChecked() ? true : false);
|
||||
powerHist->setZoned(showInZones->isChecked() ? true : false);
|
||||
powerHist->setlnY(showLnY->isChecked() ? true : false);
|
||||
powerHist->setWithZeros(showZeroes->isChecked() ? true : false);
|
||||
powerHist->setSumY(showSumY->currentIndex()== 0 ? true : false);
|
||||
|
||||
} else {
|
||||
|
||||
if (last.from != use.from || last.to != use.to) {
|
||||
|
||||
// remember the last lot we collected
|
||||
last = use;
|
||||
|
||||
// plotting a metric, reread the metrics for the selected date range
|
||||
results = mainWindow->metricDB->getAllMetricsFor(use);
|
||||
|
||||
}
|
||||
|
||||
// setData using the summary metrics -- always reset since filters may
|
||||
// have changed, or perhaps the bin width...
|
||||
powerHist->setSeries(RideFile::none);
|
||||
powerHist->setDelta(getDelta());
|
||||
powerHist->setDigits(getDigits());
|
||||
powerHist->setData(results, totalMetric(), distMetric(), isFiltered, files);
|
||||
powerHist->setColor(colorButton->getColor());
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
powerHist->setData(myRideItem, interval); // intervals selected forces data to
|
||||
// be recomputed since interval selection
|
||||
// has changed.
|
||||
|
||||
// and which series to plot
|
||||
powerHist->setSeries(series);
|
||||
|
||||
// and now the controls
|
||||
powerHist->setShading(shadeZones->isChecked() ? true : false);
|
||||
powerHist->setZoned(showInZones->isChecked() ? true : false);
|
||||
powerHist->setlnY(showLnY->isChecked() ? true : false);
|
||||
powerHist->setWithZeros(showZeroes->isChecked() ? true : false);
|
||||
powerHist->setSumY(showSumY->currentIndex()== 0 ? true : false);
|
||||
// and now the controls
|
||||
powerHist->setShading(shadeZones->isChecked() ? true : false);
|
||||
powerHist->setZoned(showInZones->isChecked() ? true : false);
|
||||
powerHist->setlnY(showLnY->isChecked() ? true : false);
|
||||
powerHist->setWithZeros(showZeroes->isChecked() ? true : false);
|
||||
powerHist->setSumY(showSumY->currentIndex()== 0 ? true : false);
|
||||
|
||||
// Selected series
|
||||
RideFile::SeriesType series = static_cast<RideFile::SeriesType>(seriesCombo->itemData(seriesCombo->currentIndex()).toInt());
|
||||
// and which series to plot
|
||||
powerHist->setSeries(series);
|
||||
}
|
||||
|
||||
// is data present for selected series, when not in range mode
|
||||
if (!rangemode) {
|
||||
RideFile::SeriesType baseSeries = (series == RideFile::wattsKg) ? RideFile::watts : series;
|
||||
if (rideItem() != NULL && rideItem()->ride()->isDataPresent(baseSeries))
|
||||
setIsBlank(false);
|
||||
else
|
||||
setIsBlank(true);
|
||||
} else {
|
||||
setIsBlank(false);
|
||||
}
|
||||
powerHist->recalc(true); // interval changed? force recalc
|
||||
powerHist->replot();
|
||||
|
||||
// Correct binWidth if not valid for the selected series
|
||||
if (binWidthLineEdit->text().toDouble()<powerHist->getDelta())
|
||||
binWidthSlider->setValue(powerHist->getDelta());
|
||||
else
|
||||
powerHist->setBinWidth(binWidthLineEdit->text().toDouble());
|
||||
|
||||
// now go plot yourself
|
||||
//powerHist->setAxisTitle(int axis, QString label);
|
||||
powerHist->recalc(interval); // interval changed? force recalc
|
||||
powerHist->replot();
|
||||
|
||||
interval = false;// we force a recalc whem called coz intervals
|
||||
// have been selected. The recalc routine in
|
||||
// powerhist optimises out, but doesn't keep track
|
||||
// of interval selection -- simplifies the setters
|
||||
// and getters, so worth this 'hack'.
|
||||
interval = false;// we force a recalc whem called coz intervals
|
||||
// have been selected. The recalc routine in
|
||||
// powerhist optimises out, but doesn't keep track
|
||||
// of interval selection -- simplifies the setters
|
||||
// and getters, so worth this 'hack'.
|
||||
} // if stale
|
||||
}
|
||||
|
||||
#ifdef GC_HAVE_LUCENE
|
||||
@@ -505,4 +933,59 @@ HistogramWindow::setFilter(QStringList list)
|
||||
stale = true;
|
||||
updateChart();
|
||||
}
|
||||
|
||||
double
|
||||
HistogramWindow::getDelta()
|
||||
{
|
||||
if (rangemode && !data->isChecked()) {
|
||||
|
||||
// based upon the metric chosen
|
||||
const RideMetricFactory &factory = RideMetricFactory::instance();
|
||||
const RideMetric *m = factory.rideMetric(distMetric());
|
||||
|
||||
if (m) return 1.00F / pow(10, m->precision());
|
||||
else return 1;
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
// use the predefined delta
|
||||
switch (seriesCombo->currentIndex()) {
|
||||
case 0: return wattsDelta;
|
||||
case 1: return wattsKgDelta;
|
||||
case 2: return hrDelta;
|
||||
case 3: return kphDelta;
|
||||
case 4: return cadDelta;
|
||||
case 5: return nmDelta;
|
||||
default: return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
HistogramWindow::getDigits()
|
||||
{
|
||||
if (rangemode && !data->isChecked()) {
|
||||
|
||||
// based upon the metric chosen
|
||||
const RideMetricFactory &factory = RideMetricFactory::instance();
|
||||
const RideMetric *m = factory.rideMetric(distMetric());
|
||||
|
||||
if (m) return m->precision();
|
||||
else return 0;
|
||||
|
||||
} else {
|
||||
|
||||
// use predefined for data series
|
||||
switch (seriesCombo->currentIndex()) {
|
||||
case 0: return wattsDigits;
|
||||
case 1: return wattsKgDigits;
|
||||
case 2: return hrDigits;
|
||||
case 3: return kphDigits;
|
||||
case 4: return cadDigits;
|
||||
case 5: return nmDigits;
|
||||
default: return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#ifndef _GC_HistogramWindow_h
|
||||
#define _GC_HistogramWindow_h 1
|
||||
#include "GoldenCheetah.h"
|
||||
#include "SummaryMetrics.h"
|
||||
|
||||
#include "Season.h"
|
||||
#include "SeasonParser.h"
|
||||
@@ -33,6 +34,7 @@ class MainWindow;
|
||||
class PowerHist;
|
||||
class RideItem;
|
||||
class RideFileCache;
|
||||
class ColorButton;
|
||||
|
||||
class HistogramWindow : public GcChartWindow
|
||||
{
|
||||
@@ -41,7 +43,6 @@ class HistogramWindow : public GcChartWindow
|
||||
|
||||
Q_PROPERTY(int series READ series WRITE setSeries USER true)
|
||||
Q_PROPERTY(int percent READ percent WRITE setPercent USER true)
|
||||
Q_PROPERTY(double bin READ bin WRITE setBin USER true)
|
||||
Q_PROPERTY(bool logY READ logY WRITE setLogY USER true)
|
||||
Q_PROPERTY(bool zeroes READ zeroes WRITE setZeroes USER true)
|
||||
Q_PROPERTY(bool shade READ shade WRITE setShade USER true)
|
||||
@@ -55,6 +56,11 @@ class HistogramWindow : public GcChartWindow
|
||||
Q_PROPERTY(int lastN READ lastN WRITE setLastN USER true)
|
||||
Q_PROPERTY(int lastNX READ lastNX WRITE setLastNX USER true)
|
||||
Q_PROPERTY(int prevN READ prevN WRITE setPrevN USER true)
|
||||
Q_PROPERTY(QString plotColor READ plotColor WRITE setPlotColor USER true)
|
||||
Q_PROPERTY(QString distmetric READ distMetric WRITE setDistMetric USER true)
|
||||
Q_PROPERTY(QString totalmetric READ totalMetric WRITE setTotalMetric USER true)
|
||||
Q_PROPERTY(bool dataMode READ dataMode WRITE setDataMode USER true)
|
||||
Q_PROPERTY(double bin READ bin WRITE setBin USER true)
|
||||
Q_PROPERTY(int useSelected READ useSelected WRITE setUseSelected USER true) // !! must be last property !!
|
||||
|
||||
public:
|
||||
@@ -69,8 +75,8 @@ class HistogramWindow : public GcChartWindow
|
||||
void setSeries(int x) { seriesCombo->setCurrentIndex(x); }
|
||||
int percent() const { return showSumY->currentIndex(); }
|
||||
void setPercent(int x) { showSumY->setCurrentIndex(x); }
|
||||
double bin() const { return binWidthSlider->value(); }
|
||||
void setBin(double x) { binWidthSlider->setValue(x); }
|
||||
double bin() const { return binWidthLineEdit->text().toDouble(); }
|
||||
void setBin(double x);
|
||||
bool logY() const { return showLnY->isChecked(); }
|
||||
void setLogY(bool x) { showLnY->setChecked(x); }
|
||||
bool zeroes() const { return showZeroes->isChecked(); }
|
||||
@@ -98,6 +104,21 @@ class HistogramWindow : public GcChartWindow
|
||||
void setLastNX(int x) { dateSetting->setLastNX(x); }
|
||||
int prevN() { return dateSetting->prevN(); }
|
||||
void setPrevN(int x) { dateSetting->setPrevN(x); }
|
||||
bool dataMode() const;
|
||||
void setDataMode(bool);
|
||||
QString totalMetric() const;
|
||||
void setTotalMetric(QString);
|
||||
QString distMetric() const;
|
||||
void setDistMetric(QString);
|
||||
void setPlotColor(QString);
|
||||
QString plotColor() const;
|
||||
|
||||
// for metric or data series
|
||||
double getDelta();
|
||||
int getDigits();
|
||||
|
||||
// bin width editor
|
||||
void setBinEditors();
|
||||
|
||||
public slots:
|
||||
|
||||
@@ -115,6 +136,14 @@ class HistogramWindow : public GcChartWindow
|
||||
void useThruToday();
|
||||
void dateRangeChanged(DateRange);
|
||||
|
||||
// we changed the series to plot
|
||||
void seriesChanged();
|
||||
|
||||
// in rangemode we choose data series or metric
|
||||
void metricToggled(bool);
|
||||
void dataToggled(bool);
|
||||
void switchMode();
|
||||
|
||||
void setZoned(int);
|
||||
void setShade(int);
|
||||
|
||||
@@ -127,10 +156,10 @@ class HistogramWindow : public GcChartWindow
|
||||
void forceReplot();
|
||||
void updateChart();
|
||||
|
||||
private:
|
||||
void treeSelectionChanged();
|
||||
void treeSelectionTimeout();
|
||||
|
||||
void setHistTextValidator();
|
||||
void setHistBinWidthText();
|
||||
private:
|
||||
|
||||
MainWindow *mainWindow;
|
||||
PowerHist *powerHist;
|
||||
@@ -165,11 +194,42 @@ class HistogramWindow : public GcChartWindow
|
||||
QStringList files;
|
||||
#endif
|
||||
|
||||
bool active, // active switching mode between data series and metric
|
||||
bactive; // active setting binwidth
|
||||
bool rangemode;
|
||||
DateSettingsEdit *dateSetting;
|
||||
bool useCustom;
|
||||
bool useToToday;
|
||||
DateRange custom;
|
||||
int precision;
|
||||
|
||||
// labels we need to remember so we can show/hide
|
||||
// when switching between data series and range mode
|
||||
QLabel *comboLabel, *metricLabel1, *metricLabel2, *showLabel,
|
||||
*blankLabel1, *blankLabel2,
|
||||
*blankLabel3, *blankLabel4, *blankLabel5, *blankLabel6,
|
||||
*colorLabel;
|
||||
|
||||
// in range mode we can also plot a distribution chart
|
||||
// based upon metrics and not just data series
|
||||
QRadioButton *data, *metric;
|
||||
|
||||
// total value (y-axis)
|
||||
QTreeWidget *totalMetricTree;
|
||||
void selectTotal(QString);
|
||||
|
||||
// distribution value (y-axis)
|
||||
QTreeWidget *distMetricTree;
|
||||
void selectMetric(QString);
|
||||
|
||||
// One shot timer.. delay before refreshing as user
|
||||
// scrolls up and down metric/total treewidget. This is
|
||||
// to have a slight lag before redrawing since it is expensive
|
||||
// and users are likely to move up and down with the arrow keys
|
||||
QTimer *lagger;
|
||||
ColorButton *colorButton;
|
||||
QList<SummaryMetrics> results;
|
||||
DateRange last;
|
||||
};
|
||||
|
||||
#endif // _GC_HistogramWindow_h
|
||||
|
||||
@@ -76,6 +76,7 @@ LTMTool::LTMTool(MainWindow *parent, const QDir &home, bool multi) : QWidget(par
|
||||
presetPicker = new QComboBox;
|
||||
presetPicker->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||
QHBoxLayout *presetrow = new QHBoxLayout;
|
||||
presetrow->setSpacing(5);
|
||||
presetrow->addWidget(presetLabel);
|
||||
presetrow->addWidget(presetPicker);
|
||||
presetrow->addStretch();
|
||||
@@ -125,7 +126,7 @@ LTMTool::LTMTool(MainWindow *parent, const QDir &home, bool multi) : QWidget(par
|
||||
else
|
||||
metricTree->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
metricTree->header()->hide();
|
||||
metricTree->setFrameStyle(QFrame::NoFrame);
|
||||
//metricTree->setFrameStyle(QFrame::NoFrame);
|
||||
//metricTree->setAlternatingRowColors (true);
|
||||
metricTree->setIndentation(5);
|
||||
allMetrics = new QTreeWidgetItem(metricTree, ROOT_TYPE);
|
||||
|
||||
@@ -764,6 +764,9 @@ MainWindow::MainWindow(const QDir &home) :
|
||||
|
||||
// Chart Settings now in their own dialog box
|
||||
chartSettings = new ChartSettings(this, masterControls);
|
||||
chartSettings->setMaximumWidth(450);
|
||||
chartSettings->setMaximumHeight(600);
|
||||
|
||||
//toolBox->addItem(masterControls, QIcon(":images/settings.png"), "Chart Settings");
|
||||
chartSettings->hide();
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "IntervalItem.h"
|
||||
#include "RideFile.h"
|
||||
#include "RideFileCache.h"
|
||||
#include "SummaryMetrics.h"
|
||||
#include "Settings.h"
|
||||
#include "Zones.h"
|
||||
#include "HrZones.h"
|
||||
@@ -42,24 +43,9 @@
|
||||
|
||||
#include "LTMCanvasPicker.h" // for tooltip
|
||||
|
||||
// discritized unit for smoothing
|
||||
static const double wattsDelta = 1.0;
|
||||
static const double wattsKgDelta = 0.01;
|
||||
static const double nmDelta = 0.1;
|
||||
static const double hrDelta = 1.0;
|
||||
static const double kphDelta = 0.1;
|
||||
static const double cadDelta = 1.0;
|
||||
|
||||
// digits for text entry validator
|
||||
static const int wattsDigits = 0;
|
||||
static const int wattsKgDigits = 2;
|
||||
static const int nmDigits = 1;
|
||||
static const int hrDigits = 0;
|
||||
static const int kphDigits = 1;
|
||||
static const int cadDigits = 0;
|
||||
|
||||
PowerHist::PowerHist(MainWindow *mainWindow):
|
||||
minX(0),
|
||||
maxX(0),
|
||||
rideItem(NULL),
|
||||
mainWindow(mainWindow),
|
||||
series(RideFile::watts),
|
||||
@@ -129,29 +115,37 @@ PowerHist::configChanged()
|
||||
QPen pen;
|
||||
QColor brush_color;
|
||||
|
||||
switch (series) {
|
||||
case RideFile::watts:
|
||||
case RideFile::wattsKg:
|
||||
pen.setColor(GColor(CPOWER).darker(200));
|
||||
brush_color = GColor(CPOWER);
|
||||
break;
|
||||
case RideFile::nm:
|
||||
pen.setColor(GColor(CTORQUE).darker(200));
|
||||
brush_color = GColor(CTORQUE);
|
||||
break;
|
||||
case RideFile::kph:
|
||||
pen.setColor(GColor(CSPEED).darker(200));
|
||||
brush_color = GColor(CSPEED);
|
||||
break;
|
||||
case RideFile::cad:
|
||||
pen.setColor(GColor(CCADENCE).darker(200));
|
||||
brush_color = GColor(CCADENCE);
|
||||
break;
|
||||
default:
|
||||
case RideFile::hr:
|
||||
pen.setColor(GColor(CHEARTRATE).darker(200));
|
||||
brush_color = GColor(CHEARTRATE);
|
||||
break;
|
||||
if (source == Metric) {
|
||||
|
||||
pen.setColor(metricColor.darker(200));
|
||||
brush_color = metricColor;
|
||||
|
||||
} else {
|
||||
|
||||
switch (series) {
|
||||
case RideFile::watts:
|
||||
case RideFile::wattsKg:
|
||||
pen.setColor(GColor(CPOWER).darker(200));
|
||||
brush_color = GColor(CPOWER);
|
||||
break;
|
||||
case RideFile::nm:
|
||||
pen.setColor(GColor(CTORQUE).darker(200));
|
||||
brush_color = GColor(CTORQUE);
|
||||
break;
|
||||
case RideFile::kph:
|
||||
pen.setColor(GColor(CSPEED).darker(200));
|
||||
brush_color = GColor(CSPEED);
|
||||
break;
|
||||
case RideFile::cad:
|
||||
pen.setColor(GColor(CCADENCE).darker(200));
|
||||
brush_color = GColor(CCADENCE);
|
||||
break;
|
||||
default:
|
||||
case RideFile::hr:
|
||||
pen.setColor(GColor(CHEARTRATE).darker(200));
|
||||
brush_color = GColor(CHEARTRATE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
double width = appsettings->value(this, GC_LINEWIDTH, 2.0).toDouble();
|
||||
@@ -270,7 +264,6 @@ PowerHist::recalc(bool force)
|
||||
QVector<unsigned int> *array = NULL;
|
||||
QVector<unsigned int> *selectedArray = NULL;
|
||||
int arrayLength = 0;
|
||||
double delta = 0;
|
||||
|
||||
// lets make sure we need to recalculate
|
||||
if (force == false &&
|
||||
@@ -308,61 +301,60 @@ PowerHist::recalc(bool force)
|
||||
|
||||
if (source == Ride && !rideItem) return;
|
||||
|
||||
// make sure the interval length is set
|
||||
if (dt <= 0) return;
|
||||
// make sure the interval length is set if not plotting metrics
|
||||
if (source != Metric && dt <= 0) return;
|
||||
|
||||
if (series == RideFile::watts && zoned == false) {
|
||||
if (source == Metric) {
|
||||
|
||||
// we use the metricArray
|
||||
array = &metricArray;
|
||||
arrayLength = metricArray.size();
|
||||
selectedArray = NULL;
|
||||
|
||||
} else if (series == RideFile::watts && zoned == false) {
|
||||
|
||||
array = &wattsArray;
|
||||
delta = wattsDelta;
|
||||
arrayLength = wattsArray.size();
|
||||
selectedArray = &wattsSelectedArray;
|
||||
|
||||
} else if ((series == RideFile::watts || series == RideFile::wattsKg) && zoned == true) {
|
||||
|
||||
array = &wattsZoneArray;
|
||||
delta = 1;
|
||||
arrayLength = wattsZoneArray.size();
|
||||
selectedArray = &wattsZoneSelectedArray;
|
||||
|
||||
} else if (series == RideFile::wattsKg && zoned == false) {
|
||||
|
||||
array = &wattsKgArray;
|
||||
delta = wattsKgDelta;
|
||||
arrayLength = wattsKgArray.size();
|
||||
selectedArray = &wattsKgSelectedArray;
|
||||
|
||||
} else if (series == RideFile::nm) {
|
||||
|
||||
array = &nmArray;
|
||||
delta = nmDelta;
|
||||
arrayLength = nmArray.size();
|
||||
selectedArray = &nmSelectedArray;
|
||||
|
||||
} else if (series == RideFile::hr && zoned == false) {
|
||||
|
||||
array = &hrArray;
|
||||
delta = hrDelta;
|
||||
arrayLength = hrArray.size();
|
||||
selectedArray = &hrSelectedArray;
|
||||
|
||||
} else if (series == RideFile::hr && zoned == true) {
|
||||
|
||||
array = &hrZoneArray;
|
||||
delta = 1;
|
||||
arrayLength = hrZoneArray.size();
|
||||
selectedArray = &hrZoneSelectedArray;
|
||||
|
||||
} else if (series == RideFile::kph) {
|
||||
|
||||
array = &kphArray;
|
||||
delta = kphDelta;
|
||||
arrayLength = kphArray.size();
|
||||
selectedArray = &kphSelectedArray;
|
||||
|
||||
} else if (series == RideFile::cad) {
|
||||
array = &cadArray;
|
||||
delta = cadDelta;
|
||||
arrayLength = cadArray.size();
|
||||
selectedArray = &cadSelectedArray;
|
||||
}
|
||||
@@ -549,6 +541,7 @@ PowerHist::setYMax()
|
||||
{
|
||||
double MaxY = curve->maxYValue();
|
||||
if (MaxY < curveSelected->maxYValue()) MaxY = curveSelected->maxYValue();
|
||||
|
||||
static const double tmin = 1.0/60;
|
||||
setAxisScale(yLeft, (lny ? tmin : 0.0), MaxY * 1.1);
|
||||
|
||||
@@ -626,6 +619,136 @@ PowerHist::setData(RideFileCache *cache)
|
||||
curveSelected->hide();
|
||||
}
|
||||
|
||||
void
|
||||
PowerHist::setData(QList<SummaryMetrics>&results, QString totalMetric, QString distMetric,
|
||||
bool isFiltered, QStringList files)
|
||||
{
|
||||
// what metrics are we plotting?
|
||||
source = Metric;
|
||||
const RideMetricFactory &factory = RideMetricFactory::instance();
|
||||
const RideMetric *m = factory.rideMetric(distMetric);
|
||||
const RideMetric *tm = factory.rideMetric(totalMetric);
|
||||
if (m == NULL || tm == NULL) return;
|
||||
|
||||
// metricX, metricY
|
||||
metricX = distMetric;
|
||||
metricY = totalMetric;
|
||||
|
||||
// how big should the array be?
|
||||
double multiplier = pow(10, m->precision());
|
||||
int max = 0, min = 0;
|
||||
|
||||
// LOOP THRU VALUES -- REPEATED WITH CUT AND PASTE BELOW
|
||||
// SO PLEASE MAKE SAME CHANGES TWICE (SORRY)
|
||||
foreach(SummaryMetrics x, results) {
|
||||
|
||||
// skip filtered values
|
||||
if (isFiltered && !files.contains(x.getFileName())) continue;
|
||||
|
||||
// and global filter too
|
||||
if (mainWindow->isfiltered && !mainWindow->filters.contains(x.getFileName())) continue;
|
||||
|
||||
// get computed value
|
||||
double v = x.getForSymbol(distMetric, mainWindow->useMetricUnits);
|
||||
|
||||
// ignore no temp files
|
||||
if ((distMetric == "average_temp" || distMetric == "max_temp") && v == RideFile::noTemp) continue;
|
||||
|
||||
// clean up dodgy values
|
||||
if (isnan(v) || isinf(v)) v = 0;
|
||||
|
||||
// seconds to minutes
|
||||
if (m->units(mainWindow->useMetricUnits) == tr("seconds")) v /= 60;
|
||||
|
||||
// apply multiplier
|
||||
v *= multiplier;
|
||||
|
||||
if (v>max) max = v;
|
||||
if (v<min) min = v;
|
||||
}
|
||||
|
||||
// lets truncate the data if there are very high
|
||||
// or very low max/min values, to ensure we don't exhaust memory
|
||||
if (max > 100000) max = 100000;
|
||||
if (min < -100000) min = -100000;
|
||||
|
||||
// now run thru the data again, but this time
|
||||
// populate the metricArray
|
||||
metricArray.resize(max-min);
|
||||
metricArray.fill(0);
|
||||
|
||||
// LOOP THRU VALUES -- REPEATED WITH CUT AND PASTE ABOVE
|
||||
// SO PLEASE MAKE SAME CHANGES TWICE (SORRY)
|
||||
foreach(SummaryMetrics x, results) {
|
||||
|
||||
// skip filtered values
|
||||
if (isFiltered && !files.contains(x.getFileName())) continue;
|
||||
|
||||
// and global filter too
|
||||
if (mainWindow->isfiltered && !mainWindow->filters.contains(x.getFileName())) continue;
|
||||
|
||||
// get computed value
|
||||
double v = x.getForSymbol(distMetric, mainWindow->useMetricUnits);
|
||||
|
||||
// ignore no temp files
|
||||
if ((distMetric == "average_temp" || distMetric == "max_temp") && v == RideFile::noTemp) continue;
|
||||
|
||||
// clean up dodgy values
|
||||
if (isnan(v) || isinf(v)) v = 0;
|
||||
|
||||
// seconds to minutes
|
||||
if (m->units(mainWindow->useMetricUnits) == tr("seconds")) v /= 60;
|
||||
|
||||
// apply multiplier
|
||||
v *= multiplier;
|
||||
|
||||
// ignore out of bounds data
|
||||
if ((int)(v)<min || (int)(v)>max) continue;
|
||||
|
||||
// increment value, are intitialised to zero above
|
||||
// there will be some loss of precision due to totalising
|
||||
// a double in an int, but frankly that should be minimal
|
||||
// since most values of note are integer based anyway.
|
||||
double t = x.getForSymbol(totalMetric, mainWindow->useMetricUnits);
|
||||
|
||||
// totalise in minutes
|
||||
if (tm->units(mainWindow->useMetricUnits) == tr("seconds")) t /= 60;
|
||||
|
||||
// sum up
|
||||
metricArray[(int)(v)-min] += t;
|
||||
}
|
||||
|
||||
// we certainly don't want the interval curve when plotting
|
||||
// metrics across rides!
|
||||
curveSelected->hide();
|
||||
|
||||
// now set all the plot paramaters to match the data
|
||||
source = Metric;
|
||||
zoned = false;
|
||||
rideItem = NULL;
|
||||
lny = false;
|
||||
shade = false;
|
||||
withz = false;
|
||||
dt = 1;
|
||||
absolutetime = true;
|
||||
|
||||
// and the plot itself
|
||||
QString yunits = tm->units(mainWindow->useMetricUnits);
|
||||
if (yunits == tr("seconds")) yunits = tr("minutes");
|
||||
QString xunits = m->units(mainWindow->useMetricUnits);
|
||||
if (xunits == tr("seconds")) xunits = tr("minutes");
|
||||
|
||||
if (tm->units(mainWindow->useMetricUnits) != "")
|
||||
setAxisTitle(yLeft, QString(tr("Total %1 (%2)")).arg(tm->name()).arg(yunits));
|
||||
else
|
||||
setAxisTitle(yLeft, QString(tr("Total %1")).arg(tm->name()));
|
||||
|
||||
if (m->units(mainWindow->useMetricUnits) != "")
|
||||
setAxisTitle(xBottom, QString(tr("%1 of Activity (%2)")).arg(m->name()).arg(xunits));
|
||||
else
|
||||
setAxisTitle(xBottom, QString(tr("%1 of Activity")).arg(m->name()));
|
||||
}
|
||||
|
||||
void
|
||||
PowerHist::updatePlot()
|
||||
{
|
||||
@@ -636,6 +759,14 @@ PowerHist::updatePlot()
|
||||
void
|
||||
PowerHist::setData(RideItem *_rideItem, bool force)
|
||||
{
|
||||
// predefined deltas for each series
|
||||
static const double wattsDelta = 1.0;
|
||||
static const double wattsKgDelta = 0.01;
|
||||
static const double nmDelta = 0.1;
|
||||
static const double hrDelta = 1.0;
|
||||
static const double kphDelta = 0.1;
|
||||
static const double cadDelta = 1.0;
|
||||
|
||||
source = Ride;
|
||||
|
||||
// we set with this data already
|
||||
@@ -832,48 +963,6 @@ PowerHist::setZoned(bool value)
|
||||
zoned = value;
|
||||
}
|
||||
|
||||
double
|
||||
PowerHist::getDelta()
|
||||
{
|
||||
switch (series) {
|
||||
case RideFile::watts: return wattsDelta;
|
||||
case RideFile::wattsKg: return wattsKgDelta;
|
||||
case RideFile::nm: return nmDelta;
|
||||
case RideFile::hr: return hrDelta;
|
||||
case RideFile::kph: return kphDelta;
|
||||
case RideFile::cad: return cadDelta;
|
||||
default: return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
PowerHist::getDigits()
|
||||
{
|
||||
switch (series) {
|
||||
case RideFile::watts: return wattsDigits;
|
||||
case RideFile::wattsKg: return wattsKgDigits;
|
||||
case RideFile::nm: return nmDigits;
|
||||
case RideFile::hr: return hrDigits;
|
||||
case RideFile::kph: return kphDigits;
|
||||
case RideFile::cad: return cadDigits;
|
||||
default: return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
PowerHist::setBinWidthRealUnits(double value)
|
||||
{
|
||||
setBinWidth(round(value / getDelta()));
|
||||
if (!binw) binw = 1; // must be nonzero
|
||||
return binw;
|
||||
}
|
||||
|
||||
double
|
||||
PowerHist::getBinWidthRealUnits()
|
||||
{
|
||||
return binw * getDelta();
|
||||
}
|
||||
|
||||
void
|
||||
PowerHist::setWithZeros(bool value)
|
||||
{
|
||||
@@ -967,7 +1056,8 @@ PowerHist::setAxisTitle(int axis, QString label)
|
||||
}
|
||||
|
||||
void
|
||||
PowerHist::setSeries(RideFile::SeriesType x) {
|
||||
PowerHist::setSeries(RideFile::SeriesType x)
|
||||
{
|
||||
// user selected a different series to plot
|
||||
series = x;
|
||||
configChanged(); // set colors
|
||||
@@ -1017,12 +1107,20 @@ PowerHist::pointHover(QwtPlotCurve *curve, int index)
|
||||
|
||||
} else if (yvalue > 0) {
|
||||
|
||||
// output the tooltip
|
||||
text = QString("%1 %2\n%3 %4")
|
||||
.arg(xvalue, 0, 'f', getDigits())
|
||||
.arg(this->axisTitle(curve->xAxis()).text())
|
||||
.arg(yvalue, 0, 'f', 1)
|
||||
.arg(absolutetime ? tr("minutes") : tr("%"));
|
||||
if (source != Metric) {
|
||||
// output the tooltip
|
||||
text = QString("%1 %2\n%3 %4")
|
||||
.arg(xvalue, 0, 'f', digits)
|
||||
.arg(this->axisTitle(curve->xAxis()).text())
|
||||
.arg(yvalue, 0, 'f', 1)
|
||||
.arg(absolutetime ? tr("minutes") : tr("%"));
|
||||
} else {
|
||||
text = QString("%1 %2\n%3 %4")
|
||||
.arg(xvalue, 0, 'f', digits)
|
||||
.arg(this->axisTitle(curve->xAxis()).text())
|
||||
.arg(yvalue, 0, 'f', 1)
|
||||
.arg(this->axisTitle(curve->yAxis()).text());
|
||||
}
|
||||
|
||||
// set that text up
|
||||
zoomer->setText(text);
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <qwt_plot.h>
|
||||
#include <qwt_plot_zoomer.h>
|
||||
#include <qwt_compat.h>
|
||||
#include <qwt_scale_draw.h>
|
||||
#include <qsettings.h>
|
||||
#include <qvariant.h>
|
||||
|
||||
@@ -45,7 +46,7 @@ class HrHistBackground;
|
||||
class HrHistZoneLabel;
|
||||
class LTMCanvasPicker;
|
||||
class ZoneScaleDraw;
|
||||
|
||||
class SummaryMetrics;
|
||||
|
||||
class penTooltip: public QwtPlotZoomer
|
||||
{
|
||||
@@ -99,14 +100,24 @@ class PowerHist : public QwtPlot
|
||||
~PowerHist();
|
||||
|
||||
double minX;
|
||||
double maxX;
|
||||
|
||||
public slots:
|
||||
|
||||
// public setters
|
||||
void setShading(bool x) { shade=x; }
|
||||
void setSeries(RideFile::SeriesType series);
|
||||
|
||||
// set data from a ride
|
||||
void setData(RideItem *_rideItem, bool force=false);
|
||||
|
||||
// set data from a ridefile cache
|
||||
void setData(RideFileCache *source);
|
||||
|
||||
// set data from metrics
|
||||
void setData(QList<SummaryMetrics>&results, QString totalMetric, QString distMetric,
|
||||
bool isFiltered, QStringList files);
|
||||
|
||||
void setlnY(bool value);
|
||||
void setWithZeros(bool value);
|
||||
void setZoned(bool value);
|
||||
@@ -115,12 +126,11 @@ class PowerHist : public QwtPlot
|
||||
void setAxisTitle(int axis, QString label);
|
||||
void setYMax();
|
||||
void setBinWidth(double value);
|
||||
int setBinWidthRealUnits(double value);
|
||||
void setColor(QColor color) { metricColor = color; }
|
||||
|
||||
// public getters
|
||||
double getDelta();
|
||||
double getBinWidthRealUnits();
|
||||
int getDigits();
|
||||
void setDelta(double delta) { this->delta = delta; }
|
||||
void setDigits(int digits) { this->digits = digits; }
|
||||
inline bool islnY() const { return lny; }
|
||||
inline bool withZeros() const { return withz; }
|
||||
inline double binWidth() const { return binw; }
|
||||
@@ -168,13 +178,16 @@ class PowerHist : public QwtPlot
|
||||
QwtPlotCurve *curve, *curveSelected;
|
||||
QList <PowerHistZoneLabel *> zoneLabels;
|
||||
QList <HrHistZoneLabel *> hrzoneLabels;
|
||||
QString metricX, metricY;
|
||||
int digits;
|
||||
double delta;
|
||||
|
||||
// source cache
|
||||
RideFileCache *cache;
|
||||
|
||||
// storage for data counts
|
||||
QVector<unsigned int> wattsArray, wattsZoneArray, wattsKgArray, nmArray, hrArray,
|
||||
hrZoneArray, kphArray, cadArray;
|
||||
hrZoneArray, kphArray, cadArray, metricArray;
|
||||
|
||||
// storage for data counts in interval selected
|
||||
QVector<unsigned int> wattsSelectedArray, wattsZoneSelectedArray,
|
||||
@@ -183,7 +196,8 @@ class PowerHist : public QwtPlot
|
||||
hrZoneSelectedArray, kphSelectedArray,
|
||||
cadSelectedArray;
|
||||
|
||||
enum Source { Ride, Cache } source, LASTsource;
|
||||
enum Source { Ride, Cache, Metric } source, LASTsource;
|
||||
QColor metricColor;
|
||||
|
||||
// last plot settings - to avoid lots of uneeded recalcs
|
||||
RideItem *LASTrideItem;
|
||||
@@ -493,4 +507,19 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class HTimeScaleDraw: public QwtScaleDraw
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
HTimeScaleDraw() : QwtScaleDraw() {}
|
||||
|
||||
virtual QwtText label(double v) const
|
||||
{
|
||||
QTime t = QTime().addSecs(v*60.00);
|
||||
if (scaleMap().sDist() > 5)
|
||||
return t.toString("hh:mm");
|
||||
return t.toString("hh:mm:ss");
|
||||
}
|
||||
};
|
||||
#endif // _GC_PowerHist_h
|
||||
|
||||
@@ -147,6 +147,7 @@ public:
|
||||
switch(bezelStyle) {
|
||||
case QtMacButton::Recessed:
|
||||
[nsButton setButtonType:NSOnOffButton];
|
||||
[[nsButton cell] setGradientType:NSGradientConvexStrong ];
|
||||
[nsButton setShowsBorderOnlyWhileMouseInside:true ];
|
||||
[[nsButton cell] setBackgroundStyle:NSBackgroundStyleRaised];
|
||||
//[nsButton setButtonType:NSPushOnPushOffButton];
|
||||
|
||||
@@ -57,6 +57,19 @@ const RideMetric *metricForSymbol(QString symbol)
|
||||
return factory.rideMetric(symbol);
|
||||
}
|
||||
|
||||
double
|
||||
SummaryMetrics::getForSymbol(QString symbol, bool metric) const
|
||||
{
|
||||
if (metric) return value.value(symbol, 0.0);
|
||||
else {
|
||||
const RideMetric *m = metricForSymbol(symbol);
|
||||
double metricValue = value.value(symbol, 0.0);
|
||||
metricValue *= m->conversion();
|
||||
metricValue += m->conversionSum();
|
||||
return metricValue;
|
||||
}
|
||||
}
|
||||
|
||||
QString
|
||||
SummaryMetrics::getStringForSymbol(QString symbol, bool UseMetric) const
|
||||
{
|
||||
|
||||
@@ -47,7 +47,7 @@ class SummaryMetrics
|
||||
|
||||
// metric values
|
||||
void setForSymbol(QString symbol, double v) { value.insert(symbol, v); }
|
||||
double getForSymbol(QString symbol) const { return value.value(symbol, 0.0); }
|
||||
double getForSymbol(QString symbol, bool metric=true) const;
|
||||
|
||||
void setText(QString name, QString v) { text.insert(name, v); }
|
||||
QString getText(QString name, QString fallback) { return text.value(name, fallback); }
|
||||
|
||||
Reference in New Issue
Block a user