mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 08:08:42 +00:00
RideFile reading refactoring
With the introduction of the rideSelected signal the RideFile was opened (as previously) by the RideSummaryWindow::htmlSummary() member. In some cases, this signal was processed by RideSummary window AFTER the other charts (AllPlot etc) this results in 'No data' being shown on other charts. This patch moves the file reading to RideItem::ride() which was previously a public RideFile * (that is now a protected member ride_). As a happy by product it removes the need to check if the file has already been read across all other functions ensuring in-core values are not accidentally overwritten. The read errors are made available by a new RideItem::errors() member. This modification is required to support the RideImportWizard in freeing loaded RideFiles during batch import to ensure virtual memory is not exhausted when large numbers of files are imported at once. This modification is also included in this patch.
This commit is contained in:
committed by
Sean Rhea
parent
71d67e2203
commit
feb111a4ff
@@ -431,8 +431,8 @@ AllPlot::refreshIntervalMarkers()
|
||||
}
|
||||
d_mrk.clear();
|
||||
QRegExp wkoAuto("^(Peak *[0-9]*(s|min)|Entire workout|Find #[0-9]*) *\\([^)]*\\)$");
|
||||
if (rideItem->ride) {
|
||||
foreach(const RideFileInterval &interval, rideItem->ride->intervals()) {
|
||||
if (rideItem->ride()) {
|
||||
foreach(const RideFileInterval &interval, rideItem->ride()->intervals()) {
|
||||
// skip WKO autogenerated peak intervals
|
||||
if (wkoAuto.exactMatch(interval.name))
|
||||
continue;
|
||||
@@ -449,7 +449,7 @@ AllPlot::refreshIntervalMarkers()
|
||||
mrk->setValue(interval.start / 60.0, 0.0);
|
||||
else
|
||||
mrk->setValue((useMetricUnits ? 1 : MILES_PER_KM) *
|
||||
rideItem->ride->timeToDistance(interval.start), 0.0);
|
||||
rideItem->ride()->timeToDistance(interval.start), 0.0);
|
||||
mrk->setLabel(text);
|
||||
}
|
||||
}
|
||||
@@ -520,7 +520,7 @@ AllPlot::setData(RideItem *_rideItem)
|
||||
|
||||
wattsArray.clear();
|
||||
|
||||
RideFile *ride = rideItem->ride;
|
||||
RideFile *ride = rideItem->ride();
|
||||
if (ride && ride->deviceType() != QString("Manual CSV")) {
|
||||
setTitle(ride->startTime().toString(GC_DATETIME_FORMAT));
|
||||
|
||||
|
||||
@@ -232,8 +232,8 @@ AllPlotWindow::setSmoothingFromLineEdit()
|
||||
void
|
||||
AllPlotWindow::setAllPlotWidgets(RideItem *ride)
|
||||
{
|
||||
if (ride->ride && ride->ride->deviceType() != QString("Manual CSV")) {
|
||||
const RideFileDataPresent *dataPresent = ride->ride->areDataPresent();
|
||||
if (ride->ride() && ride->ride()->deviceType() != QString("Manual CSV")) {
|
||||
const RideFileDataPresent *dataPresent = ride->ride()->areDataPresent();
|
||||
showPower->setEnabled(dataPresent->watts);
|
||||
showHr->setEnabled(dataPresent->hr);
|
||||
showSpeed->setEnabled(dataPresent->kph);
|
||||
@@ -345,7 +345,7 @@ AllPlotWindow::setEndSelection(double xValue, bool newInterval, QString name)
|
||||
x2 *= KM_PER_MILE;
|
||||
}
|
||||
|
||||
foreach (const RideFilePoint *point, ride->ride->dataPoints()) {
|
||||
foreach (const RideFilePoint *point, ride->ride()->dataPoints()) {
|
||||
if ((allPlot->byDistance()==true && point->km>=x1 && point->km<x2) ||
|
||||
(allPlot->byDistance()==false && point->secs/60>=x1 && point->secs/60<x2)) {
|
||||
|
||||
@@ -356,7 +356,7 @@ AllPlotWindow::setEndSelection(double xValue, bool newInterval, QString name)
|
||||
duration2 = point->secs;
|
||||
|
||||
if (point->kph > 0.0)
|
||||
secsMoving += ride->ride->recIntSecs();
|
||||
secsMoving += ride->ride()->recIntSecs();
|
||||
wattsTotal += point->watts;
|
||||
bpmTotal += point->hr;
|
||||
++arrayLength;
|
||||
@@ -405,7 +405,7 @@ AllPlotWindow::setEndSelection(double xValue, bool newInterval, QString name)
|
||||
// add average power to the end of the selection name
|
||||
name += QString("(%1 watts)").arg(round((wattsTotal && arrayLength) ? wattsTotal/arrayLength : 0));
|
||||
|
||||
QTreeWidgetItem *last = new IntervalItem(ride->ride, name, duration1, duration2, distance1, distance2);
|
||||
QTreeWidgetItem *last = new IntervalItem(ride->ride(), name, duration1, duration2, distance1, distance2);
|
||||
allIntervals->addChild(last);
|
||||
|
||||
// now update the RideFileIntervals and all the plots etc
|
||||
|
||||
@@ -200,7 +200,7 @@ HistogramWindow::setHistWidgets(RideItem *rideItem)
|
||||
{
|
||||
int count = 0;
|
||||
assert(rideItem);
|
||||
RideFile *ride = rideItem->ride;
|
||||
RideFile *ride = rideItem->ride();
|
||||
|
||||
// prevent selection from changing during reconstruction of options
|
||||
disconnect(histParameterCombo, SIGNAL(currentIndexChanged(int)),
|
||||
|
||||
@@ -521,7 +521,7 @@ MainWindow::currentRide()
|
||||
|| (treeWidget->selectedItems().first()->type() != RIDE_TYPE)) {
|
||||
return NULL;
|
||||
}
|
||||
return ((RideItem*) treeWidget->selectedItems().first())->ride;
|
||||
return ((RideItem*) treeWidget->selectedItems().first())->ride();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -578,7 +578,7 @@ MainWindow::exportCSV()
|
||||
return;
|
||||
}
|
||||
|
||||
ride->ride->writeAsCsv(file, useMetricUnits);
|
||||
ride->ride()->writeAsCsv(file, useMetricUnits);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -678,17 +678,17 @@ MainWindow::findPowerPeaks()
|
||||
return;
|
||||
}
|
||||
|
||||
addIntervalForPowerPeaksForSecs(ride->ride, 5, "Peak 5s");
|
||||
addIntervalForPowerPeaksForSecs(ride->ride, 10, "Peak 10s");
|
||||
addIntervalForPowerPeaksForSecs(ride->ride, 20, "Peak 20s");
|
||||
addIntervalForPowerPeaksForSecs(ride->ride, 30, "Peak 30s");
|
||||
addIntervalForPowerPeaksForSecs(ride->ride, 60, "Peak 1min");
|
||||
addIntervalForPowerPeaksForSecs(ride->ride, 120, "Peak 2min");
|
||||
addIntervalForPowerPeaksForSecs(ride->ride, 300, "Peak 5min");
|
||||
addIntervalForPowerPeaksForSecs(ride->ride, 600, "Peak 10min");
|
||||
addIntervalForPowerPeaksForSecs(ride->ride, 1200, "Peak 20min");
|
||||
addIntervalForPowerPeaksForSecs(ride->ride, 1800, "Peak 30min");
|
||||
addIntervalForPowerPeaksForSecs(ride->ride, 3600, "Peak 60min");
|
||||
addIntervalForPowerPeaksForSecs(ride->ride(), 5, "Peak 5s");
|
||||
addIntervalForPowerPeaksForSecs(ride->ride(), 10, "Peak 10s");
|
||||
addIntervalForPowerPeaksForSecs(ride->ride(), 20, "Peak 20s");
|
||||
addIntervalForPowerPeaksForSecs(ride->ride(), 30, "Peak 30s");
|
||||
addIntervalForPowerPeaksForSecs(ride->ride(), 60, "Peak 1min");
|
||||
addIntervalForPowerPeaksForSecs(ride->ride(), 120, "Peak 2min");
|
||||
addIntervalForPowerPeaksForSecs(ride->ride(), 300, "Peak 5min");
|
||||
addIntervalForPowerPeaksForSecs(ride->ride(), 600, "Peak 10min");
|
||||
addIntervalForPowerPeaksForSecs(ride->ride(), 1200, "Peak 20min");
|
||||
addIntervalForPowerPeaksForSecs(ride->ride(), 1800, "Peak 30min");
|
||||
addIntervalForPowerPeaksForSecs(ride->ride(), 3600, "Peak 60min");
|
||||
|
||||
// now update the RideFileIntervals
|
||||
updateRideFileIntervals();
|
||||
@@ -725,7 +725,7 @@ MainWindow::rideTreeWidgetSelectionChanged()
|
||||
|
||||
// now add the intervals for the current ride
|
||||
if (ride) { // only if we have a ride pointer
|
||||
RideFile *selected = ride->ride;
|
||||
RideFile *selected = ride->ride();
|
||||
if (selected) {
|
||||
// get all the intervals in the currently selected RideFile
|
||||
QList<RideFileInterval> intervals = selected->intervals();
|
||||
@@ -743,7 +743,7 @@ MainWindow::rideTreeWidgetSelectionChanged()
|
||||
}
|
||||
|
||||
// turn off tabs that don't make sense for manual file entry
|
||||
if (ride->ride && ride->ride->deviceType() == QString("Manual CSV")) {
|
||||
if (ride->ride() && ride->ride()->deviceType() == QString("Manual CSV")) {
|
||||
tabWidget->setTabEnabled(3,false); // Power Histogram
|
||||
tabWidget->setTabEnabled(4,false); // PF/PV Plot
|
||||
}
|
||||
@@ -783,7 +783,7 @@ MainWindow::updateRideFileIntervals()
|
||||
// iterate over allIntervals as they are now defined
|
||||
// and update the RideFile->intervals
|
||||
RideItem *which = (RideItem *)treeWidget->selectedItems().first();
|
||||
RideFile *current = which->ride;
|
||||
RideFile *current = which->ride();
|
||||
current->clearIntervals();
|
||||
for (int i=0; i < allIntervals->childCount(); i++) {
|
||||
// add the intervals as updated
|
||||
|
||||
@@ -313,7 +313,7 @@ PfPvPlot::setData(RideItem *_rideItem)
|
||||
|
||||
rideItem = _rideItem;
|
||||
|
||||
RideFile *ride = rideItem->ride;
|
||||
RideFile *ride = rideItem->ride();
|
||||
|
||||
if (ride) {
|
||||
setTitle(ride->startTime().toString(GC_DATETIME_FORMAT));
|
||||
@@ -409,7 +409,7 @@ PfPvPlot::showIntervals(RideItem *_rideItem)
|
||||
|
||||
rideItem = _rideItem;
|
||||
|
||||
RideFile *ride = rideItem->ride;
|
||||
RideFile *ride = rideItem->ride();
|
||||
|
||||
if (ride) {
|
||||
// due to the discrete power and cadence values returned by the
|
||||
|
||||
@@ -409,7 +409,7 @@ PowerHist::setData(RideItem *_rideItem)
|
||||
{
|
||||
rideItem = _rideItem;
|
||||
|
||||
RideFile *ride = rideItem->ride;
|
||||
RideFile *ride = rideItem->ride();
|
||||
|
||||
if (ride) {
|
||||
setTitle(ride->startTime().toString(GC_DATETIME_FORMAT));
|
||||
@@ -649,7 +649,7 @@ void
|
||||
PowerHist::fixSelection() {
|
||||
|
||||
Selection s = selected;
|
||||
RideFile *ride = rideItem->ride;
|
||||
RideFile *ride = rideItem->ride();
|
||||
|
||||
if (ride)
|
||||
do
|
||||
@@ -701,7 +701,7 @@ bool PowerHist::shadeZones() const
|
||||
{
|
||||
return (
|
||||
rideItem &&
|
||||
rideItem->ride &&
|
||||
rideItem->ride() &&
|
||||
selected == wattsShaded
|
||||
);
|
||||
}
|
||||
|
||||
@@ -663,6 +663,9 @@ RideImportWizard::abortClicked()
|
||||
if (source.copy(fulltarget)) {
|
||||
tableWidget->item(i,5)->setText(tr("File Saved"));
|
||||
mainWindow->addRide(QFileInfo(fulltarget).fileName(), true); // add to tree view
|
||||
// free immediately otherwise all imported rides are cached
|
||||
// and with large imports this can lead to memory exhaustion
|
||||
mainWindow->rideItem()->freeMemory();
|
||||
} else
|
||||
tableWidget->item(i,5)->setText(tr("Error - copy failed"));
|
||||
}
|
||||
|
||||
@@ -26,10 +26,9 @@
|
||||
RideItem::RideItem(int type,
|
||||
QString path, QString fileName, const QDateTime &dateTime,
|
||||
const Zones *zones, QString notesFileName) :
|
||||
QTreeWidgetItem(type), path(path), fileName(fileName),
|
||||
dateTime(dateTime), ride(NULL), zones(zones), notesFileName(notesFileName)
|
||||
QTreeWidgetItem(type), ride_(NULL), isdirty(false), path(path), fileName(fileName),
|
||||
dateTime(dateTime), zones(zones), notesFileName(notesFileName)
|
||||
{
|
||||
isdirty = false;
|
||||
setText(0, dateTime.toString("ddd"));
|
||||
setText(1, dateTime.toString("MMM d, yyyy"));
|
||||
setText(2, dateTime.toString("h:mm AP"));
|
||||
@@ -46,6 +45,15 @@ RideItem::~RideItem()
|
||||
}
|
||||
}
|
||||
|
||||
RideFile *RideItem::ride()
|
||||
{
|
||||
if (ride_) return ride_;
|
||||
|
||||
// open the ride file
|
||||
QFile file(path + "/" + fileName);
|
||||
ride_ = RideFileFactory::instance().openRideFile(file, errors_);
|
||||
return ride_;
|
||||
}
|
||||
void
|
||||
RideItem::setDirty(bool val)
|
||||
{
|
||||
@@ -93,7 +101,7 @@ int RideItem::numZones()
|
||||
double RideItem::timeInZone(int zone)
|
||||
{
|
||||
computeMetrics();
|
||||
if (!ride)
|
||||
if (!ride())
|
||||
return 0.0;
|
||||
assert(zone < numZones());
|
||||
return time_in_zone[zone];
|
||||
@@ -102,9 +110,9 @@ double RideItem::timeInZone(int zone)
|
||||
void
|
||||
RideItem::freeMemory()
|
||||
{
|
||||
if (ride) {
|
||||
delete ride;
|
||||
ride = NULL;
|
||||
if (ride_) {
|
||||
delete ride_;
|
||||
ride_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,13 +125,7 @@ RideItem::computeMetrics()
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ride) {
|
||||
QFile file(path + "/" + fileName);
|
||||
QStringList errors;
|
||||
ride = RideFileFactory::instance().openRideFile(file, errors);
|
||||
if (!ride)
|
||||
return;
|
||||
}
|
||||
if (!ride()) return;
|
||||
|
||||
computeMetricsTime = QDateTime::currentDateTime();
|
||||
|
||||
@@ -135,8 +137,8 @@ RideItem::computeMetrics()
|
||||
time_in_zone.resize(num_zones);
|
||||
}
|
||||
|
||||
double secs_delta = ride->recIntSecs();
|
||||
foreach (const RideFilePoint *point, ride->dataPoints()) {
|
||||
double secs_delta = ride()->recIntSecs();
|
||||
foreach (const RideFilePoint *point, ride()->dataPoints()) {
|
||||
if (point->watts >= 0.0) {
|
||||
if (num_zones > 0) {
|
||||
int zone = zones->whichZone(zone_range, point->watts);
|
||||
@@ -169,7 +171,7 @@ RideItem::computeMetrics()
|
||||
if (!metrics.contains(deps[j]))
|
||||
goto later;
|
||||
RideMetric *metric = factory.newMetric(name);
|
||||
metric->compute(ride, zones, zone_range, metrics);
|
||||
metric->compute(ride(), zones, zone_range, metrics);
|
||||
metrics.insert(name, metric);
|
||||
i.remove();
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ class RideItem : public QTreeWidgetItem {
|
||||
protected:
|
||||
|
||||
QVector<double> time_in_zone;
|
||||
RideFile *ride_;
|
||||
QStringList errors_;
|
||||
bool isdirty;
|
||||
|
||||
public:
|
||||
@@ -38,7 +40,8 @@ class RideItem : public QTreeWidgetItem {
|
||||
QString fileName;
|
||||
QDateTime dateTime;
|
||||
QDateTime computeMetricsTime;
|
||||
RideFile *ride;
|
||||
RideFile *ride();
|
||||
const QStringList errors() { return errors_; }
|
||||
const Zones *zones;
|
||||
QString notesFileName;
|
||||
|
||||
|
||||
@@ -161,18 +161,18 @@ RideSummaryWindow::htmlSummary() const
|
||||
QString summary;
|
||||
|
||||
RideItem *rideItem = mainWindow->rideItem();
|
||||
QFile file(rideItem->path + "/" + rideItem->fileName);
|
||||
QStringList errors;
|
||||
RideFile *ride = rideItem->ride;
|
||||
if (!ride)
|
||||
ride = RideFileFactory::instance().openRideFile(file, errors);
|
||||
RideFile *ride = rideItem->ride();
|
||||
|
||||
// ridefile read errors?
|
||||
if (!ride) {
|
||||
summary = "<p>Couldn't read file \"" + file.fileName() + "\":";
|
||||
QListIterator<QString> i(errors);
|
||||
summary = tr("<p>Couldn't read file \"");
|
||||
summary += rideItem->fileName + "\":";
|
||||
QListIterator<QString> i(mainWindow->rideItem()->errors());
|
||||
while (i.hasNext())
|
||||
summary += "<br>" + i.next();
|
||||
return summary;
|
||||
}
|
||||
|
||||
summary = ("<p><center><h2>"
|
||||
+ rideItem->dateTime.toString(tr("dddd MMMM d, yyyy, h:mm AP"))
|
||||
+ "</h2><h3>" + tr("Device Type: ") + ride->deviceType() + "</h3>");
|
||||
@@ -354,9 +354,9 @@ RideSummaryWindow::htmlSummary() const
|
||||
summary += "</table>";
|
||||
}
|
||||
|
||||
if (!errors.empty()) {
|
||||
if (!rideItem->errors().empty()) {
|
||||
summary += tr("<p><h2>Errors reading file:</h2><ul>");
|
||||
QStringListIterator i(errors);
|
||||
QStringListIterator i(rideItem->errors());
|
||||
while(i.hasNext())
|
||||
summary += " <li>" + i.next();
|
||||
summary += "</ul>";
|
||||
|
||||
@@ -141,7 +141,7 @@ MainWindow::saveSilent(RideItem *rideItem)
|
||||
|
||||
// save in GC format
|
||||
GcFileReader reader;
|
||||
reader.writeRideFile(rideItem->ride, savedFile);
|
||||
reader.writeRideFile(rideItem->ride(), savedFile);
|
||||
|
||||
// rename the file and update the rideItem list to reflect the change
|
||||
if (convert) {
|
||||
|
||||
@@ -200,7 +200,7 @@ WeeklySummaryWindow::refresh()
|
||||
) {
|
||||
|
||||
item->computeMetrics(); // generates item->ride
|
||||
if (!item->ride)
|
||||
if (!item->ride())
|
||||
continue;
|
||||
|
||||
RideMetric *m;
|
||||
|
||||
Reference in New Issue
Block a user