Add summary metrics list to preferences

Fixes #317.
This commit is contained in:
Damien
2011-07-13 21:53:21 +02:00
committed by Mark Liversedge
parent bea79092ab
commit 05346eda24
5 changed files with 295 additions and 2 deletions

View File

@@ -19,11 +19,13 @@ ConfigurationPage::ConfigurationPage(MainWindow *main) : main(main)
QWidget *config = new QWidget(this);
QVBoxLayout *configLayout = new QVBoxLayout(config);
colorsPage = new ColorsPage(main);
summaryMetrics = new SummaryMetricsPage;
intervalMetrics = new IntervalMetricsPage;
metadataPage = new MetadataPage(main);
tabs->addTab(config, tr("Basic Settings"));
tabs->addTab(colorsPage, tr("Colors"));
tabs->addTab(summaryMetrics, tr("Summary Metrics"));
tabs->addTab(intervalMetrics, tr("Interval Metrics"));
tabs->addTab(metadataPage, tr("Ride Data"));
@@ -227,6 +229,7 @@ void
ConfigurationPage::saveClicked()
{
colorsPage->saveClicked();
summaryMetrics->saveClicked();
intervalMetrics->saveClicked();
metadataPage->saveClicked();
}
@@ -930,6 +933,187 @@ IntervalMetricsPage::saveClicked()
settings->setValue(GC_SETTINGS_INTERVAL_METRICS, metrics.join(","));
}
SummaryMetricsPage::SummaryMetricsPage(QWidget *parent) :
QWidget(parent), changed(false)
{
availList = new QListWidget;
availList->setSortingEnabled(true);
availList->setSelectionMode(QAbstractItemView::SingleSelection);
QVBoxLayout *availLayout = new QVBoxLayout;
availLayout->addWidget(new QLabel(tr("Available Metrics")));
availLayout->addWidget(availList);
selectedList = new QListWidget;
selectedList->setSelectionMode(QAbstractItemView::SingleSelection);
QVBoxLayout *selectedLayout = new QVBoxLayout;
selectedLayout->addWidget(new QLabel(tr("Selected Metrics")));
selectedLayout->addWidget(selectedList);
upButton = new QPushButton("Move up");
downButton = new QPushButton("Move down");
leftButton = new QPushButton("Exclude");
rightButton = new QPushButton("Include");
QVBoxLayout *buttonGrid = new QVBoxLayout;
QHBoxLayout *upLayout = new QHBoxLayout;
QHBoxLayout *inexcLayout = new QHBoxLayout;
QHBoxLayout *downLayout = new QHBoxLayout;
upLayout->addStretch();
upLayout->addWidget(upButton);
upLayout->addStretch();
inexcLayout->addStretch();
inexcLayout->addWidget(leftButton);
inexcLayout->addWidget(rightButton);
inexcLayout->addStretch();
downLayout->addStretch();
downLayout->addWidget(downButton);
downLayout->addStretch();
buttonGrid->addStretch();
buttonGrid->addLayout(upLayout);
buttonGrid->addLayout(inexcLayout);
buttonGrid->addLayout(downLayout);
buttonGrid->addStretch();
QHBoxLayout *hlayout = new QHBoxLayout;
hlayout->addLayout(availLayout);
hlayout->addLayout(buttonGrid);
hlayout->addLayout(selectedLayout);
setLayout(hlayout);
QString s;
boost::shared_ptr<QSettings> settings = GetApplicationSettings();
if (settings->contains(GC_SETTINGS_SUMMARY_METRICS))
s = settings->value(GC_SETTINGS_SUMMARY_METRICS).toString();
else
s = GC_SETTINGS_SUMMARY_METRICS_DEFAULT;
QStringList selectedMetrics = s.split(",");
const RideMetricFactory &factory = RideMetricFactory::instance();
for (int i = 0; i < factory.metricCount(); ++i) {
QString symbol = factory.metricName(i);
if (selectedMetrics.contains(symbol))
continue;
QSharedPointer<RideMetric> m(factory.newMetric(symbol));
QString name = m->name();
name.replace(tr("&#8482;"), tr(" (TM)"));
QListWidgetItem *item = new QListWidgetItem(name);
item->setData(Qt::UserRole, symbol);
availList->addItem(item);
}
foreach (QString symbol, selectedMetrics) {
if (!factory.haveMetric(symbol))
continue;
QSharedPointer<RideMetric> m(factory.newMetric(symbol));
QString name = m->name();
name.replace(tr("&#8482;"), tr(" (TM)"));
QListWidgetItem *item = new QListWidgetItem(name);
item->setData(Qt::UserRole, symbol);
selectedList->addItem(item);
}
upButton->setEnabled(false);
downButton->setEnabled(false);
leftButton->setEnabled(false);
rightButton->setEnabled(false);
connect(upButton, SIGNAL(clicked()), this, SLOT(upClicked()));
connect(downButton, SIGNAL(clicked()), this, SLOT(downClicked()));
connect(leftButton, SIGNAL(clicked()), this, SLOT(leftClicked()));
connect(rightButton, SIGNAL(clicked()), this, SLOT(rightClicked()));
connect(availList, SIGNAL(itemSelectionChanged()),
this, SLOT(availChanged()));
connect(selectedList, SIGNAL(itemSelectionChanged()),
this, SLOT(selectedChanged()));
}
void
SummaryMetricsPage::upClicked()
{
assert(!selectedList->selectedItems().isEmpty());
QListWidgetItem *item = selectedList->selectedItems().first();
int row = selectedList->row(item);
assert(row > 0);
selectedList->takeItem(row);
selectedList->insertItem(row - 1, item);
selectedList->setCurrentItem(item);
changed = true;
}
void
SummaryMetricsPage::downClicked()
{
assert(!selectedList->selectedItems().isEmpty());
QListWidgetItem *item = selectedList->selectedItems().first();
int row = selectedList->row(item);
assert(row < selectedList->count() - 1);
selectedList->takeItem(row);
selectedList->insertItem(row + 1, item);
selectedList->setCurrentItem(item);
changed = true;
}
void
SummaryMetricsPage::leftClicked()
{
assert(!selectedList->selectedItems().isEmpty());
QListWidgetItem *item = selectedList->selectedItems().first();
selectedList->takeItem(selectedList->row(item));
availList->addItem(item);
changed = true;
}
void
SummaryMetricsPage::rightClicked()
{
assert(!availList->selectedItems().isEmpty());
QListWidgetItem *item = availList->selectedItems().first();
availList->takeItem(availList->row(item));
selectedList->addItem(item);
changed = true;
}
void
SummaryMetricsPage::availChanged()
{
rightButton->setEnabled(!availList->selectedItems().isEmpty());
}
void
SummaryMetricsPage::selectedChanged()
{
if (selectedList->selectedItems().isEmpty()) {
upButton->setEnabled(false);
downButton->setEnabled(false);
leftButton->setEnabled(false);
return;
}
QListWidgetItem *item = selectedList->selectedItems().first();
int row = selectedList->row(item);
if (row == 0)
upButton->setEnabled(false);
else
upButton->setEnabled(true);
if (row == selectedList->count() - 1)
downButton->setEnabled(false);
else
downButton->setEnabled(true);
leftButton->setEnabled(true);
}
void
SummaryMetricsPage::saveClicked()
{
if (!changed)
return;
QStringList metrics;
for (int i = 0; i < selectedList->count(); ++i)
metrics << selectedList->item(i)->data(Qt::UserRole).toString();
boost::shared_ptr<QSettings> settings = GetApplicationSettings();
settings->setValue(GC_SETTINGS_SUMMARY_METRICS, metrics.join(","));
}
MetadataPage::MetadataPage(MainWindow *main) : main(main)
{
QVBoxLayout *layout = new QVBoxLayout(this);

View File

@@ -35,6 +35,7 @@ class QHBoxLayout;
class QVBoxLayout;
class ColorsPage;
class IntervalMetricsPage;
class SummaryMetricsPage;
class MetadataPage;
class KeywordsPage;
class FieldsPage;
@@ -66,6 +67,7 @@ class ConfigurationPage : public QWidget
private:
MainWindow *main;
ColorsPage *colorsPage;
SummaryMetricsPage *summaryMetrics;
IntervalMetricsPage *intervalMetrics;
MetadataPage *metadataPage;
@@ -228,6 +230,36 @@ class IntervalMetricsPage : public QWidget
QPushButton *rightButton;
};
class SummaryMetricsPage : public QWidget
{
Q_OBJECT
public:
SummaryMetricsPage(QWidget *parent = NULL);
public slots:
void upClicked();
void downClicked();
void leftClicked();
void rightClicked();
void availChanged();
void selectedChanged();
void saveClicked();
protected:
bool changed;
QListWidget *availList;
QListWidget *selectedList;
QPushButton *upButton;
QPushButton *downButton;
QPushButton *leftButton;
QPushButton *rightButton;
};
class KeywordsPage : public QWidget
{
Q_OBJECT

View File

@@ -108,7 +108,25 @@ RideSummaryWindow::htmlSummary() const
NULL
};
const char *metricColumn[] = {
QString s;
if (settings->contains(GC_SETTINGS_SUMMARY_METRICS))
s = settings->value(GC_SETTINGS_SUMMARY_METRICS).toString();
else
s = GC_SETTINGS_SUMMARY_METRICS_DEFAULT;
QStringList metricColumnList = s.split(",");
char **metricColumnTmp;
// Copy QStringList to char **
metricColumnTmp = new char*[metricColumnList.size() + 1];
for (int i = 0; i < metricColumnList.size(); i++) {
metricColumnTmp[i] = new char[strlen(metricColumnList.at(i).toStdString().c_str())+1];
memcpy(metricColumnTmp[i], metricColumnList.at(i).toStdString().c_str(), strlen(metricColumnList.at(i).toStdString().c_str())+1);
}
metricColumnTmp[metricColumnList.size()] = NULL;
char const **metricColumn = (const char**)metricColumnTmp;
/*const char *metricColumn[] = {
"skiba_xpower",
"skiba_relative_intensity",
"skiba_bike_score",
@@ -117,7 +135,7 @@ RideSummaryWindow::htmlSummary() const
"trimp_points",
"aerobic_decoupling",
NULL
};
};*/
summary += "<table border=0 cellspacing=10><tr>";
for (int i = 0; i < columns; ++i) {
@@ -135,6 +153,7 @@ RideSummaryWindow::htmlSummary() const
for (int j = 0;; ++j) {
const char *symbol = metricsList[j];
if (!symbol) break;
RideMetricPtr m = rideItem->metrics.value(symbol);
QString name = m->name().replace(QRegExp(tr("^Average ")), "");
if (m->units(metricUnits) == "seconds" || m->units(metricUnits) == tr("seconds")) {

View File

@@ -31,11 +31,13 @@
#define GC_SETTINGS_SUMMARYSPLITTER_SIZES "mainwindow/summarysplittersizes"
#define GC_SETTINGS_CALENDAR_SIZES "mainwindow/calendarSizes"
#define GC_TABS_TO_HIDE "mainwindow/tabsToHide"
#define GC_SETTINGS_SUMMARY_METRICS "rideSummaryWindow/summaryMetrics"
#define GC_SETTINGS_INTERVAL_METRICS "rideSummaryWindow/intervalMetrics"
#define GC_RIDE_PLOT_SMOOTHING "ridePlot/Smoothing"
#define GC_RIDE_PLOT_STACK "ridePlot/Stack"
#define GC_PERF_MAN_METRIC "performanceManager/metric"
#define GC_HIST_BIN_WIDTH "histogamWindow/binWidth"
#define GC_SETTINGS_SUMMARY_METRICS_DEFAULT "skiba_xpower,skiba_relative_intensity,skiba_bike_score,daniels_points,daniels_equivalent_power,trimp_points,aerobic_decoupling"
#define GC_SETTINGS_INTERVAL_METRICS_DEFAULT "workout_time,total_distance,total_work,average_power,skiba_xpower,max_power,average_hr,ninety_five_percent_hr,average_cad,average_speed"
#define GC_DATETIME_FORMAT "ddd MMM dd, yyyy, hh:mm AP"
#define GC_UNIT "unit"

View File

@@ -293,6 +293,58 @@ public:
RideMetric *clone() const { return new TRIMPZonalPoints(*this); }
};
// RPE is the rate of percieved exercion (borg scale).
// Is a numerical value the riders give in "average" fatigue of the training session he percieved.
//
// Calculate the session RPE that is the product of RPE * time (minutes) of training/race ride. I
// We have 3 different "training load" parameters:
// - internal load (TRIMPS)
// - external load (bikescore/TSS)
// - perceived load (session RPE)
//
class SessionRPE : public RideMetric {
Q_DECLARE_TR_FUNCTIONS(TRIMPPoints)
double score;
public:
SessionRPE() : score(0.0)
{
setSymbol("session_rpe");
#ifdef ENABLE_METRICS_TRANSLATION
setInternalName("Session RPE");
}
void initialize() {
#endif
setName(tr("Session RPE"));
setMetricUnits("");
setImperialUnits("");
setType(RideMetric::Total);
}
void compute(const RideFile *rideFile,
const Zones *, int ,
const HrZones *hrZones, int hrZoneRange,
const QHash<QString,RideMetric*> &deps)
{
// use RPE value in ride metadata
double rpe = rideFile->getTag("RPE", "0.0").toDouble();
assert(deps.contains("workout_time"));
const RideMetric *workoutTimeMetric = deps.value("workout_time");
assert(workoutTimeMetric);
double secs = workoutTimeMetric->value(true);
// ok lets work the score out
score = ((secs == 0.0 || rpe == 0) ? 0.0 : secs/60 *rpe);
setValue(score);
}
RideMetric *clone() const { return new SessionRPE(*this); }
};
static bool added() {
QVector<QString> deps;
deps.append("workout_time");
@@ -312,6 +364,10 @@ static bool added() {
deps.append("time_in_zone_H4");
deps.append("time_in_zone_H5");
RideMetricFactory::instance().addMetric(TRIMPZonalPoints(), &deps);
deps.clear();
deps.append("workout_time");
RideMetricFactory::instance().addMetric(SessionRPE(), &deps);
return true;
}