diff --git a/src/AddIntervalDialog.cpp b/src/AddIntervalDialog.cpp new file mode 100644 index 000000000..1c44c7129 --- /dev/null +++ b/src/AddIntervalDialog.cpp @@ -0,0 +1,662 @@ +/* + * Copyright (c) 2012 Damien Grauser (Damien.Grauser@pev-geneve.ch) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "AddIntervalDialog.h" +#include "MainWindow.h" +#include "RideFile.h" +#include +#include +#include + +AddIntervalDialog::AddIntervalDialog(MainWindow *mainWindow) : + mainWindow(mainWindow) +{ + setAttribute(Qt::WA_DeleteOnClose); + setWindowTitle("Add Intervals"); + QVBoxLayout *mainLayout = new QVBoxLayout(this); + + intervalMethodWidget = new QWidget(); + QHBoxLayout *intervalMethodLayout = new QHBoxLayout; + QLabel *intervalMethodLabel = new QLabel(tr("Method: "), this); + intervalMethodLayout->addWidget(intervalMethodLabel); + QButtonGroup *methodButtonGroup = new QButtonGroup(this); + methodFirst = new QRadioButton(tr("First")); + methodFirst->setChecked(true); + methodButtonGroup->addButton(methodFirst); + intervalMethodLayout->addWidget(methodFirst); + methodBestPower = new QRadioButton(tr("Peak Power")); + methodButtonGroup->addButton(methodBestPower); + intervalMethodLayout->addWidget(methodBestPower); + //mainLayout->addLayout(intervalMethodLayout); + intervalMethodWidget->setLayout(intervalMethodLayout); + mainLayout->addWidget(intervalMethodWidget); + + intervalPeakPowerWidget = new QWidget(); + intervalPeakPowerTypeLayout = new QHBoxLayout; + QLabel *intervalPeakPowerTypeLabel = new QLabel(tr("Type: "), this); + intervalPeakPowerTypeLayout->addWidget(intervalPeakPowerTypeLabel); + QButtonGroup *peakPowerTypeButtonGroup = new QButtonGroup(this); + peakPowerStandard = new QRadioButton(tr("Standard")); + peakPowerStandard->setChecked(true); + peakPowerTypeButtonGroup->addButton(peakPowerStandard); + intervalPeakPowerTypeLayout->addWidget(peakPowerStandard); + peakPowerCustom = new QRadioButton(tr("Custom")); + peakPowerTypeButtonGroup->addButton(peakPowerCustom); + intervalPeakPowerTypeLayout->addWidget(peakPowerCustom); + //mainLayout->addLayout(intervalPeakPowerTypeLayout); + intervalPeakPowerWidget->setLayout(intervalPeakPowerTypeLayout); + intervalPeakPowerWidget->hide(); + mainLayout->addWidget(intervalPeakPowerWidget); + + intervalTypeWidget = new QWidget(); + QHBoxLayout *intervalTypeLayout = new QHBoxLayout; + QLabel *intervalTypeLabel = new QLabel(tr("Interval length: "), this); + intervalTypeLayout->addWidget(intervalTypeLabel); + QButtonGroup *typeButtonGroup = new QButtonGroup(this); + typeTime = new QRadioButton(tr("By time")); + typeTime->setChecked(true); + typeButtonGroup->addButton(typeTime); + intervalTypeLayout->addWidget(typeTime); + typeDistance = new QRadioButton(tr("By distance")); + typeButtonGroup->addButton(typeDistance); + intervalTypeLayout->addWidget(typeDistance); + //mainLayout->addLayout(intervalTypeLayout); + intervalTypeWidget->setLayout(intervalTypeLayout); + mainLayout->addWidget(intervalTypeWidget); + + intervalTimeWidget = new QWidget(); + QHBoxLayout *intervalTimeLayout = new QHBoxLayout; + QLabel *intervalTimeLabel = new QLabel(tr("Time: "), this); + intervalTimeLayout->addWidget(intervalTimeLabel); + hrsSpinBox = new QDoubleSpinBox(this); + hrsSpinBox->setDecimals(0); + hrsSpinBox->setMinimum(0.0); + hrsSpinBox->setSuffix(" hrs"); + hrsSpinBox->setSingleStep(1.0); + hrsSpinBox->setAlignment(Qt::AlignRight); + intervalTimeLayout->addWidget(hrsSpinBox); + minsSpinBox = new QDoubleSpinBox(this); + minsSpinBox->setDecimals(0); + minsSpinBox->setRange(0.0, 59.0); + minsSpinBox->setSuffix(" mins"); + minsSpinBox->setSingleStep(1.0); + minsSpinBox->setWrapping(true); + minsSpinBox->setAlignment(Qt::AlignRight); + minsSpinBox->setValue(1.0); + intervalTimeLayout->addWidget(minsSpinBox); + secsSpinBox = new QDoubleSpinBox(this); + secsSpinBox->setDecimals(0); + secsSpinBox->setRange(0.0, 59.0); + secsSpinBox->setSuffix(" secs"); + secsSpinBox->setSingleStep(1.0); + secsSpinBox->setWrapping(true); + secsSpinBox->setAlignment(Qt::AlignRight); + intervalTimeLayout->addWidget(secsSpinBox); + //mainLayout->addLayout(intervalLengthLayout); + intervalTimeWidget->setLayout(intervalTimeLayout); + mainLayout->addWidget(intervalTimeWidget); + + intervalDistanceWidget = new QWidget(); + QHBoxLayout *intervalDistanceLayout = new QHBoxLayout; + QLabel *intervalDistanceLabel = new QLabel(tr("Distance: "), this); + intervalDistanceLayout->addWidget(intervalDistanceLabel); + kmsSpinBox = new QDoubleSpinBox(this); + kmsSpinBox->setDecimals(0); + kmsSpinBox->setRange(0.0, 999); + kmsSpinBox->setValue(5.0); + kmsSpinBox->setSuffix(" km"); + kmsSpinBox->setSingleStep(1.0); + kmsSpinBox->setAlignment(Qt::AlignRight); + intervalDistanceLayout->addWidget(kmsSpinBox); + msSpinBox = new QDoubleSpinBox(this); + msSpinBox->setDecimals(0); + msSpinBox->setRange(0.0, 999); + msSpinBox->setSuffix(" m"); + msSpinBox->setSingleStep(1.0); + msSpinBox->setAlignment(Qt::AlignRight); + intervalDistanceLayout->addWidget(msSpinBox); + //mainLayout->addLayout(intervalDistanceLayout); + intervalDistanceWidget->setLayout(intervalDistanceLayout); + intervalDistanceWidget->hide(); + mainLayout->addWidget(intervalDistanceWidget); + + intervalCountWidget = new QWidget(); + QHBoxLayout *intervalCountLayout = new QHBoxLayout; + QLabel *intervalCountLabel = new QLabel(tr("How many to find: "), this); + intervalCountLayout->addWidget(intervalCountLabel); + countSpinBox = new QDoubleSpinBox(this); + countSpinBox->setDecimals(0); + countSpinBox->setMinimum(1.0); + countSpinBox->setValue(5.0); // lets default to the top 5 powers + countSpinBox->setSingleStep(1.0); + countSpinBox->setAlignment(Qt::AlignRight); + intervalCountLayout->addWidget(countSpinBox); + //mainLayout->addLayout(intervalCountLayout); + intervalCountWidget->setLayout(intervalCountLayout); + mainLayout->addWidget(intervalCountWidget); + + + + + + + + QLabel *resultsLabel = new QLabel(tr("Results:"), this); + mainLayout->addWidget(resultsLabel); + + // user can select from the results to add + // to the ride intervals + resultsTable = new QTableWidget; + mainLayout->addWidget(resultsTable); + resultsTable->setColumnCount(5); + resultsTable->setColumnHidden(3, true); // has start time in secs + resultsTable->setColumnHidden(4, true); // has stop time in secs + resultsTable->horizontalHeader()->hide(); +// resultsTable->verticalHeader()->hide(); + resultsTable->setShowGrid(false); + + QHBoxLayout *buttonLayout = new QHBoxLayout; + createButton = new QPushButton(tr("&Create Intervals"), this); + buttonLayout->addWidget(createButton); + doneButton = new QPushButton(tr("&Done"), this); + buttonLayout->addWidget(doneButton); + addButton = new QPushButton(tr("&Add to Intervals")); + buttonLayout->addWidget(addButton); + mainLayout->addLayout(buttonLayout); + + connect(methodFirst, SIGNAL(clicked()), this, SLOT(methodFirstClicked())); + connect(methodBestPower, SIGNAL(clicked()), this, SLOT(methodBestPowerClicked())); + connect(peakPowerStandard, SIGNAL(clicked()), this, SLOT(peakPowerStandardClicked())); + connect(peakPowerCustom, SIGNAL(clicked()), this, SLOT(peakPowerCustomClicked())); + connect(typeTime, SIGNAL(clicked()), this, SLOT(typeTimeClicked())); + connect(typeDistance, SIGNAL(clicked()), this, SLOT(typeDistanceClicked())); + + + + + connect(createButton, SIGNAL(clicked()), this, SLOT(createClicked())); + connect(doneButton, SIGNAL(clicked()), this, SLOT(doneClicked())); + connect(addButton, SIGNAL(clicked()), this, SLOT(addClicked())); +} + +void +AddIntervalDialog::methodFirstClicked() +{ + intervalPeakPowerWidget->hide(); + intervalTypeWidget->show(); + if (typeDistance->isChecked()) + typeDistanceClicked(); + else + typeTimeClicked(); + intervalCountWidget->show(); +} + +void +AddIntervalDialog::methodBestPowerClicked() +{ + intervalPeakPowerWidget->show(); + if (peakPowerCustom->isChecked()) + peakPowerCustomClicked(); + else + peakPowerStandardClicked(); +} + +void +AddIntervalDialog::peakPowerStandardClicked() +{ + intervalTypeWidget->hide(); + intervalTimeWidget->hide(); + intervalDistanceWidget->hide(); + intervalCountWidget->hide(); +} + +void +AddIntervalDialog::peakPowerCustomClicked() +{ + intervalTypeWidget->show(); + if (typeDistance->isChecked()) + typeDistanceClicked(); + else + typeTimeClicked(); + intervalCountWidget->show(); +} + +void +AddIntervalDialog::typeTimeClicked() +{ + intervalDistanceWidget->hide(); + intervalTimeWidget->show(); +} + +void +AddIntervalDialog::typeDistanceClicked() +{ + intervalDistanceWidget->show(); + intervalTimeWidget->hide(); +} + + + + +// little helper function +static void +clearResultsTable(QTableWidget *resultsTable) +{ + // zap the 3 main cols and two hidden ones + for (int i=0; irowCount(); i++) { + for (int j=0; jcolumnCount(); j++) + delete resultsTable->takeItem(i,j); + } +} + +static double +intervalDuration(const RideFilePoint *start, const RideFilePoint *stop, const RideFile *ride) +{ + return stop->secs - start->secs + ride->recIntSecs(); +} + +static double +intervalDistance(const RideFilePoint *start, const RideFilePoint *stop, const RideFile *ride) +{ + return 1000*(stop->km - start->km);// + (ride->recIntSecs()*stop->kph/3600)); +} + +static bool +intervalsOverlap(const AddIntervalDialog::AddedInterval &a, + const AddIntervalDialog::AddedInterval &b) +{ + if ((a.start <= b.start) && (a.stop > b.start)) + return true; + if ((b.start <= a.start) && (b.stop > a.start)) + return true; + return false; +} + +struct CompareBests { + // Sort by decreasing power and increasing start time. + bool operator()(const AddIntervalDialog::AddedInterval &a, + const AddIntervalDialog::AddedInterval &b) const { + if (a.avg > b.avg) + return true; + if (b.avg > a.avg) + return false; + return a.start < b.start; + } +}; + +void +AddIntervalDialog::createClicked() +{ + const RideFile *ride = mainWindow->currentRide(); + if (!ride) { + QMessageBox::critical(this, tr("Select Ride"), tr("No ride selected!")); + return; + } + + int maxIntervals = (int) countSpinBox->value(); + + double windowSizeSecs = (hrsSpinBox->value() * 3600.0 + + minsSpinBox->value() * 60.0 + + secsSpinBox->value()); + + double windowSizeMeters = (kmsSpinBox->value() * 1000.0 + + msSpinBox->value()); + + if (windowSizeSecs == 0.0) { + QMessageBox::critical(this, tr("Bad Interval Length"), + tr("Interval length must be greater than zero!")); + return; + } + + bool byTime = typeTime->isChecked(); + + QList results; + if (methodBestPower->isChecked()) { + if (peakPowerStandard->isChecked()) + findPeakPowerStandard(ride, results); + else + findBests(byTime, ride, (byTime?windowSizeSecs:windowSizeMeters), maxIntervals, results, ""); + } + else + findFirsts(byTime, ride, (byTime?windowSizeSecs:windowSizeMeters), maxIntervals, results); + + // clear the table + clearResultsTable(resultsTable); + + // populate the table + resultsTable->setRowCount(results.size()); + int row = 0; + foreach (const AddedInterval &interval, results) { + + double secs = interval.start; + double mins = floor(secs / 60); + secs = secs - mins * 60.0; + double hrs = floor(mins / 60); + mins = mins - hrs * 60.0; + + // check box + QCheckBox *c = new QCheckBox; + c->setCheckState(Qt::Checked); + resultsTable->setCellWidget(row, 0, c); + + // start time + QString start = "%1:%2:%3"; + start = start.arg(hrs, 0, 'f', 0); + start = start.arg(mins, 2, 'f', 0, QLatin1Char('0')); + start = start.arg(round(secs), 2, 'f', 0, QLatin1Char('0')); + + QTableWidgetItem *t = new QTableWidgetItem; + t->setText(start); + t->setFlags(t->flags() & (~Qt::ItemIsEditable)); + resultsTable->setItem(row, 1, t); + + QTableWidgetItem *n = new QTableWidgetItem; + n->setText(interval.name); + n->setFlags(n->flags() | (Qt::ItemIsEditable)); + resultsTable->setItem(row, 2, n); + + // hidden columns - start, stop + QString strt = QString("%1").arg(interval.start); // can't use secs as it gets modified + QTableWidgetItem *st = new QTableWidgetItem; + st->setText(strt); + resultsTable->setItem(row, 3, st); + + QString stp = QString("%1").arg(interval.stop); // was interval.start+x + QTableWidgetItem *sp = new QTableWidgetItem; + sp->setText(stp); + resultsTable->setItem(row, 4, sp); + + row++; + } + resultsTable->resizeColumnToContents(0); + resultsTable->resizeColumnToContents(1); + resultsTable->setColumnWidth(2,200); +} + +void +AddIntervalDialog::findFirsts(bool typeTime, const RideFile *ride, double windowSize, + int maxIntervals, QList &results) +{ + QList bests; + + double secsDelta = ride->recIntSecs(); + double totalWatts = 0.0; + QList window; + + // ride is shorter than the window size! + if (typeTime) + if (windowSize > ride->dataPoints().last()->secs + secsDelta) return; + else + if (windowSize > ride->dataPoints().last()->km*1000) return; + + double rest = 0; + // We're looking for intervals with durations in [windowSizeSecs, windowSizeSecs + secsDelta). + foreach (const RideFilePoint *point, ride->dataPoints()) { + + // Add points until interval duration is >= windowSizeSecs. + totalWatts += point->watts; + window.append(point); + double duration = intervalDuration(window.first(), window.last(), ride); + double distance = intervalDistance(window.first(), window.last(), ride); + + if ((typeTime && duration >= (windowSize - rest)) || + (!typeTime && distance >= (windowSize - rest))) { + double start = window.first()->secs; + double stop = start + duration - (typeTime?0:ride->recIntSecs()); // correction for distance + double avg = totalWatts * secsDelta / duration; + bests.append(AddedInterval(start, stop, avg)); + rest = (typeTime?duration-windowSize+rest:distance-windowSize+rest); + // Discard points + window.clear(); + totalWatts = 0; + + // For distance the last point of an interval is also the first point of the next + if (!typeTime) { + totalWatts += point->watts; + window.append(point); + } + } + } + + while (!bests.empty() && (results.size() < maxIntervals)) { + AddedInterval candidate = bests.takeFirst(); + + QString name = "#%1 %2%3 (%4w)"; + name = name.arg(results.count()+1); + + if (typeTime) { + // best n mins + if (windowSize < 60) { + // whole seconds + name = name.arg(windowSize); + name = name.arg("sec"); + } else if (windowSize >= 60 && !(((int)windowSize)%60)) { + // whole minutes + name = name.arg(windowSize/60); + name = name.arg("min"); + } else { + double secs = windowSize; + double mins = ((int) secs) / 60; + secs = secs - mins * 60.0; + double hrs = ((int) mins) / 60; + mins = mins - hrs * 60.0; + QString tm = "%1:%2:%3"; + tm = tm.arg(hrs, 0, 'f', 0); + tm = tm.arg(mins, 2, 'f', 0, QLatin1Char('0')); + tm = tm.arg(secs, 2, 'f', 0, QLatin1Char('0')); + + // mins and secs + name = name.arg(tm); + name = name.arg(""); + } + } else { + // best n mins + if (windowSize < 1000) { + // whole seconds + name = name.arg(windowSize); + name = name.arg("m"); + } else { + double dist = windowSize; + double kms = ((int) dist) / 1000; + dist = dist - kms * 1000.0; + double ms = dist; + + QString tm = "%1,%2"; + tm = tm.arg(kms); + tm = tm.arg(ms); + + // km and m + name = name.arg(tm); + name = name.arg("km"); + } + } + name = name.arg(round(candidate.avg)); + candidate.name = name; + + results.append(candidate); + } + +} + +void +AddIntervalDialog::findPeakPowerStandard(const RideFile *ride, QList &results) +{ + findBests(true, ride, 5, 1, results, "Peak 5s"); + findBests(true, ride, 10, 1, results, "Peak 10s"); + findBests(true, ride, 20, 1, results, "Peak 20s"); + findBests(true, ride, 30, 1, results, "Peak 30s"); + findBests(true, ride, 60, 1, results, "Peak 1min"); + findBests(true, ride, 120, 1, results, "Peak 2min"); + findBests(true, ride, 300, 1, results, "Peak 10min"); + findBests(true, ride, 600, 1, results, "Peak 10min"); + findBests(true, ride, 1200, 1, results, "Peak 20min"); + findBests(true, ride, 1800, 1, results, "Peak 30min"); + findBests(true, ride, 3600, 1, results, "Peak 60min"); +} + +void +AddIntervalDialog::findBests(bool typeTime, const RideFile *ride, double windowSize, + int maxIntervals, QList &results, QString prefix) +{ + QList bests; + QList _results; + + double secsDelta = ride->recIntSecs(); + double totalWatts = 0.0; + QList window; + + // ride is shorter than the window size! + if (typeTime) + if (windowSize > ride->dataPoints().last()->secs + secsDelta) return; + else + if (windowSize > ride->dataPoints().last()->km*1000) return; + + // We're looking for intervals with durations in [windowSizeSecs, windowSizeSecs + secsDelta). + foreach (const RideFilePoint *point, ride->dataPoints()) { + // Discard points until interval duration is < windowSizeSecs + secsDelta. + while ((typeTime && !window.empty() && intervalDuration(window.first(), point, ride) >= windowSize + secsDelta) || + (!typeTime && window.length()>1 && intervalDistance(window.at(1), point, ride) >= windowSize)) { + totalWatts -= window.first()->watts; + window.takeFirst(); + } + // Add points until interval duration or distance is >= windowSize. + totalWatts += point->watts; + window.append(point); + double duration = intervalDuration(window.first(), window.last(), ride); + double distance = intervalDistance(window.first(), window.last(), ride); + + if ((typeTime && duration >= windowSize) || + (!typeTime && distance >= windowSize)) { + double start = window.first()->secs; + double stop = window.last()->secs; //start + duration; + double avg = totalWatts * secsDelta / duration; + bests.append(AddedInterval(start, stop, avg)); + } + } + + std::sort(bests.begin(), bests.end(), CompareBests()); + + while (!bests.empty() && (_results.size() < maxIntervals)) { + AddedInterval candidate = bests.takeFirst(); + bool overlaps = false; + foreach (const AddedInterval &existing, _results) { + if (intervalsOverlap(candidate, existing)) { + overlaps = true; + break; + } + } + if (!overlaps) { + QString name = prefix; + if (prefix == "") { + name = "Best %2%3 #%1"; + name = name.arg(_results.count()+1); + if (typeTime) { + // best n mins + if (windowSize < 60) { + // whole seconds + name = name.arg(windowSize); + name = name.arg("sec"); + } else if (windowSize >= 60 && !(((int)windowSize)%60)) { + // whole minutes + name = name.arg(windowSize/60); + name = name.arg("min"); + } else { + double secs = windowSize; + double mins = ((int) secs) / 60; + secs = secs - mins * 60.0; + double hrs = ((int) mins) / 60; + mins = mins - hrs * 60.0; + QString tm = "%1:%2:%3"; + tm = tm.arg(hrs, 0, 'f', 0); + tm = tm.arg(mins, 2, 'f', 0, QLatin1Char('0')); + tm = tm.arg(secs, 2, 'f', 0, QLatin1Char('0')); + + // mins and secs + name = name.arg(tm); + name = name.arg(""); + } + } else { + // best n mins + if (windowSize < 1000) { + // whole seconds + name = name.arg(windowSize); + name = name.arg("m"); + } else { + double dist = windowSize; + double kms = ((int) dist) / 1000; + dist = dist - kms * 1000.0; + double ms = dist; + + QString tm = "%1,%2"; + tm = tm.arg(kms); + tm = tm.arg(ms); + + // km and m + name = name.arg(tm); + name = name.arg("km"); + } + } + } + name += " (%4w)"; + name = name.arg(round(candidate.avg)); + candidate.name = name; + name = ""; + _results.append(candidate); + } + } + results.append(_results); +} + +void +AddIntervalDialog::doneClicked() +{ + clearResultsTable(resultsTable); // clear out that table! + done(0); +} + +void +AddIntervalDialog::addClicked() +{ + // run through the table row by row + // and when the checkbox is shown + // get name from column 2 + // get start in secs as a string from column 3 + // get stop in secs as a string from column 4 + for (int i=0; irowCount(); i++) { + + // is it checked? + QCheckBox *c = (QCheckBox *)resultsTable->cellWidget(i,0); + if (c->isChecked()) { + double start = resultsTable->item(i,3)->text().toDouble(); + double stop = resultsTable->item(i,4)->text().toDouble(); + QString name = resultsTable->item(i,2)->text(); + const RideFile *ride = mainWindow->currentRide(); + + QTreeWidgetItem *allIntervals = mainWindow->mutableIntervalItems(); + QTreeWidgetItem *last = + new IntervalItem(ride, name, start, stop, + ride->timeToDistance(start), + ride->timeToDistance(stop), + allIntervals->childCount()+1); + last->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled); + // add + allIntervals->addChild(last); + } + } + mainWindow->updateRideFileIntervals(); +} diff --git a/src/AddIntervalDialog.h b/src/AddIntervalDialog.h new file mode 100644 index 000000000..cadec9ad0 --- /dev/null +++ b/src/AddIntervalDialog.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2012 Damien Grauser (Damien.Grauser@pev-geneve.ch) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _GC_AddIntervalDialog_h +#define _GC_AddIntervalDialog_h 1 +#include "GoldenCheetah.h" + +#include + +class MainWindow; +class RideFile; + +class AddIntervalDialog : public QDialog +{ + Q_OBJECT + G_OBJECT + + + public: + + struct AddedInterval { + QString name; + double start, stop, avg; + AddedInterval(double start, double stop, double avg) : + start(start), stop(stop), avg(avg) {} + }; + + AddIntervalDialog(MainWindow *mainWindow); + + static void findPeakPowerStandard(const RideFile *ride, QList &results); + + static void findBests(bool typeTime, const RideFile *ride, double windowSizeSecs, + int maxIntervals, QList &results, QString name); + + static void findFirsts(bool typeTime, const RideFile *ride, double windowSizeSecs, + int maxIntervals, QList &results); + + private slots: + void createClicked(); + void doneClicked(); + void addClicked(); // add to inverval selections + + void methodFirstClicked(); + void methodBestPowerClicked(); + void peakPowerStandardClicked(); + void peakPowerCustomClicked(); + void typeTimeClicked(); + void typeDistanceClicked(); + + private: + + MainWindow *mainWindow; + QWidget *intervalMethodWidget, *intervalPeakPowerWidget, *intervalTypeWidget, *intervalTimeWidget, *intervalDistanceWidget, *intervalCountWidget; + + QHBoxLayout *intervalPeakPowerTypeLayout; + QPushButton *createButton, *doneButton, *addButton; + QDoubleSpinBox *hrsSpinBox, *minsSpinBox, *secsSpinBox, *countSpinBox,*kmsSpinBox, *msSpinBox; + QRadioButton *methodFirst, *methodBestPower, *typeDistance, *typeTime, *peakPowerStandard, *peakPowerCustom; + QTableWidget *resultsTable; +}; + +#endif // _GC_AddIntervalDialog_h + diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index a45b89f95..a7768ac93 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -17,6 +17,7 @@ */ #include "MainWindow.h" +#include "AddIntervalDialog.h" #include "AthleteTool.h" #include "BestIntervalDialog.h" #include "ChooseCyclistDialog.h" @@ -715,8 +716,7 @@ MainWindow::MainWindow(const QDir &home) : optionsMenu->addAction(tr("Refresh Calendar"), this, SLOT(refreshCalendar()), tr ("")); #endif optionsMenu->addSeparator(); - optionsMenu->addAction(tr("Find &best intervals..."), this, SLOT(findBestIntervals()), tr ("Ctrl+B")); - optionsMenu->addAction(tr("Find power &peaks..."), this, SLOT(findPowerPeaks()), tr ("Ctrl+P")); + optionsMenu->addAction(tr("Find intervals..."), this, SLOT(addIntervals()), tr ("")); // Add all the data processors to the tools menu const DataProcessorFactory &factory = DataProcessorFactory::instance(); @@ -1801,6 +1801,14 @@ MainWindow::findBestIntervals() p->exec(); } +void +MainWindow::addIntervals() +{ + AddIntervalDialog *p = new AddIntervalDialog(this); + p->setWindowModality(Qt::ApplicationModal); // don't allow select other ride or it all goes wrong! + p->exec(); +} + void MainWindow::addIntervalForPowerPeaksForSecs(RideFile *ride, int windowSizeSecs, QString name) { diff --git a/src/MainWindow.h b/src/MainWindow.h index 7726ecf46..2f1140455 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -246,6 +246,7 @@ class MainWindow : public QMainWindow #endif void importFile(); void findBestIntervals(); + void addIntervals(); void addIntervalForPowerPeaksForSecs(RideFile *ride, int windowSizeSecs, QString name); void findPowerPeaks(); void splitRide(); diff --git a/src/src.pro b/src/src.pro index f69139acd..f252f3270 100644 --- a/src/src.pro +++ b/src/src.pro @@ -186,6 +186,7 @@ HEADERS += ../qxt/src/qxtspanslider.h \ HEADERS += \ AddDeviceWizard.h \ + AddIntervalDialog.h \ Aerolab.h \ AerolabWindow.h \ AthleteTool.h \ @@ -361,6 +362,7 @@ LEXSOURCES = JsonRideFile.l WithingsParser.l SOURCES += \ AddDeviceWizard.cpp \ + AddIntervalDialog.cpp \ AerobicDecoupling.cpp \ Aerolab.cpp \ AerolabWindow.cpp \