File Export (part 2 of 2)

Added a function for Batch Export of current
activity history. The user can select files
to export, the target directory and format to
use.

This completes the updates to improve export
functionality.

Fixes #476.
This commit is contained in:
Mark Liversedge
2011-10-12 18:00:58 +01:00
parent cc0fbdf47d
commit 6f116ab07d
8 changed files with 341 additions and 7 deletions

250
src/BatchExportDialog.cpp Normal file
View File

@@ -0,0 +1,250 @@
/*
* Copyright (c) 2011 Mark Liversedge (liversedge@gmail.com)
*
* 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 "BatchExportDialog.h"
BatchExportDialog::BatchExportDialog(MainWindow *main) : QDialog(main), main(main)
{
setAttribute(Qt::WA_DeleteOnClose);
setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
setWindowTitle(tr("Activity Batch Export"));
// make the dialog a resonable size
setMinimumWidth(550);
setMinimumHeight(400);
QVBoxLayout *layout = new QVBoxLayout;
setLayout(layout);
files = new QTreeWidget;
files->headerItem()->setText(0, tr(""));
files->headerItem()->setText(1, tr("Filename"));
files->headerItem()->setText(2, tr("Date"));
files->headerItem()->setText(3, tr("Time"));
files->headerItem()->setText(4, tr("Action"));
files->setColumnCount(5);
files->setColumnWidth(0, 30); // selector
files->setColumnWidth(1, 190); // filename
files->setColumnWidth(2, 95); // date
files->setColumnWidth(3, 90); // time
files->setSelectionMode(QAbstractItemView::SingleSelection);
files->setEditTriggers(QAbstractItemView::SelectedClicked); // allow edit
files->setUniformRowHeights(true);
files->setIndentation(0);
// populate with each ride in the ridelist
const QTreeWidgetItem *allRides = main->allRideItems();
for (int i=0; i<allRides->childCount(); i++) {
RideItem *rideItem = static_cast<RideItem*>(allRides->child(i));
QTreeWidgetItem *add = new QTreeWidgetItem(files->invisibleRootItem());
add->setFlags(add->flags() | Qt::ItemIsEditable);
// selector
QCheckBox *checkBox = new QCheckBox("", this);
checkBox->setChecked(true);
files->setItemWidget(add, 0, checkBox);
// we will wipe the original file
add->setText(1, rideItem->fileName);
add->setText(2, rideItem->dateTime.toString(tr("dd MMM yyyy")));
add->setText(3, rideItem->dateTime.toString(tr("hh:mm:ss ap")));
// interval action
add->setText(4, "Export");
}
// format and directory
QGridLayout *grid = new QGridLayout;
formatLabel = new QLabel("Export as", this);
format = new QComboBox(this);
const RideFileFactory &rff = RideFileFactory::instance();
foreach(QString suffix, rff.writeSuffixes()) format->addItem(rff.description(suffix));
selectDir = new QPushButton("Browse", this);
dirLabel = new QLabel ("Export to", this);
dirName = new QLabel(QDir::home().absolutePath(), this);
all = new QCheckBox("check/uncheck all", this);
all->setChecked(true);
grid->addWidget(formatLabel, 0,0, Qt::AlignLeft);
grid->addWidget(format, 0,1, Qt::AlignLeft);
grid->addWidget(dirLabel, 1,0, Qt::AlignLeft);
grid->addWidget(dirName, 1,1, Qt::AlignLeft);
grid->addWidget(selectDir, 1,2, Qt::AlignLeft);
grid->addWidget(all, 2,0, Qt::AlignLeft);
grid->setColumnStretch(0, 1);
grid->setColumnStretch(1, 10);
// buttons
QHBoxLayout *buttons = new QHBoxLayout;
status = new QLabel("", this);
status->hide();
overwrite = new QCheckBox("Overwrite existing files", this);
cancel = new QPushButton("Cancel", this);
ok = new QPushButton("Export", this);
buttons->addWidget(overwrite);
buttons->addWidget(status);
buttons->addStretch();
buttons->addWidget(cancel);
buttons->addWidget(ok);
layout->addLayout(grid);
layout->addWidget(files);
layout->addLayout(buttons);
exports = fails = 0;
// connect signals and slots up..
connect(selectDir, SIGNAL(clicked()), this, SLOT(selectClicked()));
connect(ok, SIGNAL(clicked()), this, SLOT(okClicked()));
connect(all, SIGNAL(stateChanged(int)), this, SLOT(allClicked()));
connect(cancel, SIGNAL(clicked()), this, SLOT(cancelClicked()));
}
void
BatchExportDialog::selectClicked()
{
QString dir = QFileDialog::getExistingDirectory(this, tr("Select Target Directory"),
dirName->text(),
QFileDialog::ShowDirsOnly
| QFileDialog::DontResolveSymlinks);
if (dir!="") dirName->setText(dir);
return;
}
void
BatchExportDialog::allClicked()
{
// set/uncheck all rides according to the "all"
bool checked = all->isChecked();
for(int i=0; i<files->invisibleRootItem()->childCount(); i++) {
QTreeWidgetItem *current = files->invisibleRootItem()->child(i);
static_cast<QCheckBox*>(files->itemWidget(current,0))->setChecked(checked);
}
}
void
BatchExportDialog::okClicked()
{
if (ok->text() == "Export") {
aborted = false;
overwrite->hide();
status->setText("Exporting...");
status->show();
cancel->hide();
ok->setText("Abort");
exportFiles();
status->setText(QString("%1 activities exported, %2 failed or skipped.").arg(exports).arg(fails));
ok->setText("Finish");
} else if (ok->text() == "Abort") {
aborted = true;
} else if (ok->text() == "Finish") {
accept(); // our work is done!
}
}
void
BatchExportDialog::cancelClicked()
{
reject();
}
void
BatchExportDialog::exportFiles()
{
// what format to export as?
QString type = RideFileFactory::instance().writeSuffixes().at(format->currentIndex());
// loop through the table and export all selected
for(int i=0; i<files->invisibleRootItem()->childCount(); i++) {
// give user a chance to abort..
QApplication::processEvents();
// did they?
if (aborted == true) return; // user aborted!
QTreeWidgetItem *current = files->invisibleRootItem()->child(i);
// is it selected
if (static_cast<QCheckBox*>(files->itemWidget(current,0))->isChecked()) {
files->setCurrentItem(current); QApplication::processEvents();
QString filename = dirName->text() + "/" + QFileInfo(current->text(1)).baseName() + "." + type;
if (QFile(filename).exists()) {
if (overwrite->isChecked() == false) {
// skip existing files
current->setText(4, "Exists - not exported"); QApplication::processEvents();
fails++;
continue;
} else {
// remove existing
QFile(filename).remove();
current->setText(4, "Removing..."); QApplication::processEvents();
}
}
// this one then
current->setText(4, "Reading..."); QApplication::processEvents();
// open it..
QStringList errors;
QList<RideFile*> rides;
QFile thisfile(QString(main->home.absolutePath()+"/"+current->text(1)));
RideFile *ride = RideFileFactory::instance().openRideFile(main, thisfile, errors, &rides);
// open success?
if (ride) {
current->setText(4, "Writing..."); QApplication::processEvents();
QFile out(filename);
bool success = RideFileFactory::instance().writeRideFile(main, ride, out, type);
if (success) {
exports++;
current->setText(4, "Exported"); QApplication::processEvents();
} else {
fails++;
current->setText(4, "Write failed"); QApplication::processEvents();
}
delete ride; // free memory!
// open failed
} else {
current->setText(4, "Read error"); QApplication::processEvents();
}
}
}
}

80
src/BatchExportDialog.h Normal file
View File

@@ -0,0 +1,80 @@
/*
* Copyright (c) 2011 Mark Liversedge (liversedge@gmail.com)
*
* 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 _BatchExportDialog_h
#define _BatchExportDialog_h
#include "GoldenCheetah.h"
#include "MainWindow.h"
#include "Settings.h"
#include "Units.h"
#include "RideItem.h"
#include "RideFile.h"
#include <QtGui>
#include <QTableWidget>
#include <QProgressBar>
#include <QList>
#include <QLabel>
#include <QListIterator>
#include <QDebug>
// Dialog class to show filenames, import progress and to capture user input
// of ride date and time
class BatchExportDialog : public QDialog
{
Q_OBJECT
G_OBJECT
public:
BatchExportDialog(MainWindow *main);
QTreeWidget *files; // choose files to export
signals:
private slots:
void cancelClicked();
void okClicked();
void selectClicked();
void exportFiles();
void allClicked();
private:
MainWindow *main;
bool aborted;
QCheckBox *all;
QComboBox *format;
QLabel *formatLabel;
QPushButton *selectDir;
QLabel *dirLabel, *dirName;
QCheckBox *overwrite;
QPushButton *cancel, *ok;
int exports, fails;
QLabel *status;
};
#endif // _BatchExportDialog_h

View File

@@ -28,7 +28,7 @@
static int gcFileReaderRegistered =
RideFileFactory::instance().registerReader(
"gc", "GoldenCheetah XML Format", new GcFileReader());
"gc", "GoldenCheetah XML", new GcFileReader());
RideFile *
GcFileReader::openRideFile(QFile &file, QStringList &errors, QList<RideFile*>*) const

View File

@@ -226,7 +226,7 @@ string: STRING { JsonString = unprotect(JsonRideFiletex
static int jsonFileReaderRegistered =
RideFileFactory::instance().registerReader(
"json", "GoldenCheetah Json Format", new JsonFileReader());
"json", "GoldenCheetah Json", new JsonFileReader());
RideFile *
JsonFileReader::openRideFile(QFile &file, QStringList &errors, QList<RideFile*>*) const

View File

@@ -62,7 +62,7 @@ using kmldom::StyleMapPtr;
static int kmlFileReaderRegistered =
RideFileFactory::instance().registerReader(
"kml", "Google Earth KML Format", new KmlFileReader());
"kml", "Google Earth KML", new KmlFileReader());
//
// Utility functions
//

View File

@@ -52,6 +52,7 @@
#include "ToolsDialog.h"
#include "MetricAggregator.h"
#include "SplitActivityWizard.h"
#include "BatchExportDialog.h"
#include "TwitterDialog.h"
#include "WithingsDownload.h"
#include "CalendarDownload.h"
@@ -440,8 +441,8 @@ MainWindow::MainWindow(const QDir &home) :
rideMenu->addAction(tr("&Import from file..."), this, SLOT (importFile()), tr ("Ctrl+I"));
rideMenu->addAction(tr("&Manual activity entry..."), this, SLOT(manualRide()), tr("Ctrl+M"));
rideMenu->addSeparator ();
rideMenu->addAction(tr("&Export ..."), this, SLOT(exportRide()), tr("Ctrl+E"));
rideMenu->addAction(tr("&Batch export ..."), this, SLOT(exportBatch()), tr("Ctrl+B"));
rideMenu->addAction(tr("&Export..."), this, SLOT(exportRide()), tr("Ctrl+E"));
rideMenu->addAction(tr("&Batch export..."), this, SLOT(exportBatch()), tr("Ctrl+B"));
rideMenu->addAction(tr("Export Metrics as CSV..."), this, SLOT(exportMetrics()), tr(""));
#ifdef GC_HAVE_SOAP
rideMenu->addSeparator ();
@@ -1090,7 +1091,8 @@ MainWindow::currentRide()
void
MainWindow::exportBatch()
{
// XXX todo
BatchExportDialog *d = new BatchExportDialog(this);
d->exec();
}
void

View File

@@ -30,7 +30,7 @@
static int tcxFileReaderRegistered =
RideFileFactory::instance().registerReader(
"tcx", "Garmin Training Centre", new TcxFileReader());
"tcx", "Garmin Training Centre TCX", new TcxFileReader());
RideFile *TcxFileReader::openRideFile(QFile &file, QStringList &errors, QList<RideFile*>*list) const
{

View File

@@ -163,6 +163,7 @@ HEADERS += \
ANTMessages.h \
ANTlocalController.h \
ANTplusController.h \
BatchExportDialog.h \
BestIntervalDialog.h \
BinRideFile.h \
BingMap.h \
@@ -325,6 +326,7 @@ SOURCES += \
ANTlocalController.cpp \
ANTplusController.cpp \
BasicRideMetrics.cpp \
BatchExportDialog.cpp \
BestIntervalDialog.cpp \
BikeScore.cpp \
BinRideFile.cpp \