mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
Workout Library Part 3 of 3
Linked up the training db to the train tool to allow users to select from the db. Further enhancements to the list could be made, but leaving this till we move to OpenTrainer. Still need to add drag and drop to copy into the library or add reference to the file. Fixes #301.
This commit is contained in:
@@ -375,7 +375,7 @@ LibrarySearchDialog::removeDirectory()
|
||||
void
|
||||
LibrarySearchDialog::updateDB()
|
||||
{
|
||||
trainDB->connection().transaction();
|
||||
trainDB->startLUW();
|
||||
|
||||
// workouts
|
||||
foreach(QString ergFile, workoutsFound) {
|
||||
@@ -391,7 +391,7 @@ LibrarySearchDialog::updateDB()
|
||||
trainDB->importVideo(video);
|
||||
}
|
||||
|
||||
trainDB->connection().commit();
|
||||
trainDB->endLUW();
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
// Rev Date Who What Changed
|
||||
// 01 21 Dec 2012 Mark Liversedge Initial Build
|
||||
|
||||
static int TrainDBSchemaVersion = 2;
|
||||
static int TrainDBSchemaVersion = 1;
|
||||
TrainDB *trainDB;
|
||||
|
||||
TrainDB::TrainDB(QDir home) : home(home)
|
||||
@@ -91,12 +91,16 @@ bool TrainDB::createVideoTable()
|
||||
// we need to create it!
|
||||
if (rc && createTables) {
|
||||
|
||||
QString createVideoTable = "create table videos (filename varchar primary key,"
|
||||
QString createVideoTable = "create table videos (filepath varchar primary key,"
|
||||
"filename varchar,"
|
||||
"timestamp integer,"
|
||||
"length integer);";
|
||||
|
||||
rc = query.exec(createVideoTable);
|
||||
|
||||
// insert the 'DVD' record for playing currently loaded DVD
|
||||
//XXX rc = query.exec("INSERT INTO videos (filepath, filename) values (\"\", \"DVD\");");
|
||||
|
||||
// add row to version database
|
||||
query.exec("DELETE FROM version where table_name = \"videos\"");
|
||||
|
||||
@@ -131,7 +135,8 @@ bool TrainDB::createWorkoutTable()
|
||||
// we need to create it!
|
||||
if (rc && createTables) {
|
||||
|
||||
QString createMetricTable = "create table workouts (filename varchar primary key,"
|
||||
QString createMetricTable = "create table workouts (filepath varchar primary key,"
|
||||
"filename,"
|
||||
"timestamp integer,"
|
||||
"description varchar,"
|
||||
"source varchar,"
|
||||
@@ -144,6 +149,14 @@ bool TrainDB::createWorkoutTable()
|
||||
|
||||
rc = query.exec(createMetricTable);
|
||||
|
||||
QString manualErg = QString("INSERT INTO workouts (filepath, filename) values (\"//1\", \"%1\");")
|
||||
.arg(tr("Manual Erg Mode"));
|
||||
rc = query.exec(manualErg);
|
||||
|
||||
QString manualCrs = QString("INSERT INTO workouts (filepath, filename) values (\"//2\", \"%1\");")
|
||||
.arg(tr("Manual Slope Mode"));
|
||||
rc = query.exec(manualCrs);
|
||||
|
||||
// add row to version database
|
||||
query.exec("DELETE FROM version where table_name = \"workouts\"");
|
||||
|
||||
@@ -257,12 +270,13 @@ bool TrainDB::importWorkout(QString pathname, ErgFile *ergFile)
|
||||
QDateTime timestamp = QDateTime::currentDateTime();
|
||||
|
||||
// zap the current row - if there is one
|
||||
query.prepare("DELETE FROM workouts WHERE filename = ?;");
|
||||
query.prepare("DELETE FROM workouts WHERE filepath = ?;");
|
||||
query.addBindValue(pathname);
|
||||
query.exec();
|
||||
|
||||
// construct an insert statement
|
||||
QString insertStatement = "insert into workouts ( filename, "
|
||||
QString insertStatement = "insert into workouts ( filepath, "
|
||||
"filename,"
|
||||
"timestamp,"
|
||||
"description,"
|
||||
"source,"
|
||||
@@ -271,11 +285,12 @@ bool TrainDB::importWorkout(QString pathname, ErgFile *ergFile)
|
||||
"coggan_tss,"
|
||||
"coggan_if,"
|
||||
"elevation,"
|
||||
"grade ) values ( ?,?,?,?,?,?,?,?,?,? );";
|
||||
"grade ) values ( ?,?,?,?,?,?,?,?,?,?,? );";
|
||||
query.prepare(insertStatement);
|
||||
|
||||
// filename, timestamp, ride date
|
||||
query.addBindValue(pathname);
|
||||
query.addBindValue(QFileInfo(pathname).fileName());
|
||||
query.addBindValue(timestamp);
|
||||
query.addBindValue(ergFile->Name);
|
||||
query.addBindValue(ergFile->Source);
|
||||
@@ -298,16 +313,17 @@ bool TrainDB::importVideo(QString pathname)
|
||||
QDateTime timestamp = QDateTime::currentDateTime();
|
||||
|
||||
// zap the current row - if there is one
|
||||
query.prepare("DELETE FROM videos WHERE filename = ?;");
|
||||
query.prepare("DELETE FROM videos WHERE filepath = ?;");
|
||||
query.addBindValue(pathname);
|
||||
query.exec();
|
||||
|
||||
// construct an insert statement
|
||||
QString insertStatement = "insert into videos ( filename ) values ( ? );";
|
||||
QString insertStatement = "insert into videos ( filepath,filename ) values ( ?,? );";
|
||||
query.prepare(insertStatement);
|
||||
|
||||
// filename, timestamp, ride date
|
||||
query.addBindValue(pathname);
|
||||
query.addBindValue(QFileInfo(pathname).fileName());
|
||||
|
||||
// go do it!
|
||||
bool rc = query.exec();
|
||||
|
||||
@@ -26,9 +26,11 @@
|
||||
#include <QtSql>
|
||||
|
||||
class ErgFile;
|
||||
class TrainDB
|
||||
class TrainDB : public QObject
|
||||
{
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
// get connection name
|
||||
@@ -44,9 +46,15 @@ class TrainDB
|
||||
TrainDB(QDir home);
|
||||
~TrainDB();
|
||||
|
||||
void startLUW() { dbconn.transaction(); }
|
||||
void endLUW() { dbconn.commit(); emit dataChanged(); }
|
||||
|
||||
bool importWorkout(QString pathname, ErgFile *ergFile);
|
||||
bool importVideo(QString pathname); //XXX simple for now
|
||||
|
||||
signals:
|
||||
void dataChanged();
|
||||
|
||||
private:
|
||||
QDir home;
|
||||
QSqlDatabase db;
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#endif
|
||||
|
||||
#include <math.h> // isnan and isinf
|
||||
#include "TrainDB.h"
|
||||
|
||||
TrainTool::TrainTool(MainWindow *parent, const QDir &home) : GcWindow(parent), home(home), main(parent)
|
||||
{
|
||||
@@ -79,16 +80,35 @@ TrainTool::TrainTool(MainWindow *parent, const QDir &home) : GcWindow(parent), h
|
||||
#endif
|
||||
|
||||
#if defined Q_OS_MAC || defined GC_HAVE_VLC
|
||||
mediaTree = new QTreeWidget;
|
||||
mediaTree->setFrameStyle(QFrame::NoFrame);
|
||||
mediaTree->setColumnCount(1);
|
||||
mediaTree->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
videoModel = new QSqlTableModel(this, trainDB->connection());
|
||||
videoModel->setTable("videos");
|
||||
videoModel->setEditStrategy(QSqlTableModel::OnManualSubmit);
|
||||
videoModel->select();
|
||||
while (videoModel->canFetchMore(QModelIndex())) videoModel->fetchMore(QModelIndex());
|
||||
|
||||
mediaTree = new QTreeView;
|
||||
mediaTree->setModel(videoModel);
|
||||
|
||||
// hide unwanted columns and header
|
||||
for(int i=0; i<mediaTree->header()->count(); i++)
|
||||
mediaTree->setColumnHidden(i, true);
|
||||
mediaTree->setColumnHidden(1, false); // show filename
|
||||
mediaTree->header()->hide();
|
||||
mediaTree->setAlternatingRowColors (false);
|
||||
mediaTree->setIndentation(5);
|
||||
allMedia = new QTreeWidgetItem(mediaTree, HEAD_TYPE);
|
||||
allMedia->setText(0, tr("Video / Media"));
|
||||
mediaTree->expandItem(allMedia);
|
||||
|
||||
mediaTree->setSortingEnabled(true);
|
||||
mediaTree->setAlternatingRowColors(false);
|
||||
mediaTree->setEditTriggers(QAbstractItemView::NoEditTriggers); // read-only
|
||||
mediaTree->expandAll();
|
||||
mediaTree->header()->setCascadingSectionResizes(true); // easier to resize this way
|
||||
mediaTree->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
mediaTree->header()->setStretchLastSection(true);
|
||||
mediaTree->header()->setMinimumSectionSize(0);
|
||||
mediaTree->header()->setFocusPolicy(Qt::NoFocus);
|
||||
mediaTree->setFrameStyle(QFrame::NoFrame);
|
||||
#ifdef Q_OS_MAC
|
||||
mediaTree->header()->setSortIndicatorShown(false); // blue looks nasty
|
||||
mediaTree->setAttribute(Qt::WA_MacShowFocusRect, 0);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
deviceTree = new QTreeWidget;
|
||||
@@ -106,17 +126,34 @@ TrainTool::TrainTool(MainWindow *parent, const QDir &home) : GcWindow(parent), h
|
||||
deviceTree->expandItem(allDevices);
|
||||
deviceTree->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
workoutTree = new QTreeWidget;
|
||||
workoutTree->setFrameStyle(QFrame::NoFrame);
|
||||
workoutTree->setColumnCount(1);
|
||||
workoutTree->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
workoutTree->header()->hide();
|
||||
workoutTree->setAlternatingRowColors (false);
|
||||
workoutTree->setIndentation(5);
|
||||
workoutModel = new QSqlTableModel(this, trainDB->connection());
|
||||
workoutModel->setTable("workouts");
|
||||
workoutModel->setEditStrategy(QSqlTableModel::OnManualSubmit);
|
||||
workoutModel->select();
|
||||
while (workoutModel->canFetchMore(QModelIndex())) workoutModel->fetchMore(QModelIndex());
|
||||
|
||||
allWorkouts = new QTreeWidgetItem(workoutTree, HEAD_TYPE);
|
||||
allWorkouts->setText(0, tr("Workout Library"));
|
||||
workoutTree->expandItem(allWorkouts);
|
||||
workoutTree = new QTreeView;
|
||||
workoutTree->setModel(workoutModel);
|
||||
|
||||
// hide unwanted columns and header
|
||||
for(int i=0; i<workoutTree->header()->count(); i++)
|
||||
workoutTree->setColumnHidden(i, true);
|
||||
workoutTree->setColumnHidden(1, false); // show filename
|
||||
workoutTree->header()->hide();
|
||||
workoutTree->setFrameStyle(QFrame::NoFrame);
|
||||
workoutTree->setAlternatingRowColors(false);
|
||||
workoutTree->setEditTriggers(QAbstractItemView::NoEditTriggers); // read-only
|
||||
workoutTree->expandAll();
|
||||
workoutTree->header()->setCascadingSectionResizes(true); // easier to resize this way
|
||||
workoutTree->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
workoutTree->header()->setStretchLastSection(true);
|
||||
workoutTree->header()->setMinimumSectionSize(0);
|
||||
workoutTree->header()->setFocusPolicy(Qt::NoFocus);
|
||||
workoutTree->setFrameStyle(QFrame::NoFrame);
|
||||
#ifdef Q_OS_MAC
|
||||
workoutTree->header()->setSortIndicatorShown(false); // blue looks nasty
|
||||
workoutTree->setAttribute(Qt::WA_MacShowFocusRect, 0);
|
||||
#endif
|
||||
|
||||
// TOOLBAR BUTTONS ETC
|
||||
QHBoxLayout *toolallbuttons=new QHBoxLayout; // on toolbar
|
||||
@@ -274,21 +311,16 @@ TrainTool::TrainTool(MainWindow *parent, const QDir &home) : GcWindow(parent), h
|
||||
//connect(serverTree,SIGNAL(itemSelectionChanged()), this, SLOT(serverTreeWidgetSelectionChanged()));
|
||||
connect(deviceTree,SIGNAL(itemSelectionChanged()), this, SLOT(deviceTreeWidgetSelectionChanged()));
|
||||
connect(deviceTree,SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(deviceTreeMenuPopup(const QPoint &)));
|
||||
connect(workoutTree,SIGNAL(itemSelectionChanged()), this, SLOT(workoutTreeWidgetSelectionChanged()));
|
||||
#if defined Q_OS_MAC || defined GC_HAVE_VLC
|
||||
connect(mediaTree,SIGNAL(itemSelectionChanged()), this, SLOT(mediaTreeWidgetSelectionChanged()));
|
||||
connect(mediaTree->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
|
||||
this, SLOT(mediaTreeWidgetSelectionChanged()));
|
||||
#endif
|
||||
connect(main, SIGNAL(configChanged()), this, SLOT(configChanged()));
|
||||
connect(trainDB, SIGNAL(dataChanged()), this, SLOT(refresh()));
|
||||
|
||||
connect(workoutTree->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(workoutTreeWidgetSelectionChanged()));
|
||||
// add a watch on all directories
|
||||
QVariant workoutDir = appsettings->value(NULL, GC_WORKOUTDIR);
|
||||
#if 0 //XXX Performance issues with this
|
||||
watcher = boost::shared_ptr<QFileSystemWatcher>(new QFileSystemWatcher());
|
||||
watcher->addPaths(workoutDir.toStringList());
|
||||
|
||||
connect(&*watcher,SIGNAL(directoryChanged(QString)),this,SLOT(configChanged()));
|
||||
connect(&*watcher,SIGNAL(fileChanged(QString)),this,SLOT(configChanged()));
|
||||
#endif
|
||||
|
||||
// set home
|
||||
main = parent;
|
||||
@@ -337,6 +369,15 @@ TrainTool::TrainTool(MainWindow *parent, const QDir &home) : GcWindow(parent), h
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
TrainTool::refresh()
|
||||
{
|
||||
videoModel->select();
|
||||
while (videoModel->canFetchMore(QModelIndex())) videoModel->fetchMore(QModelIndex());
|
||||
workoutModel->select();
|
||||
while (workoutModel->canFetchMore(QModelIndex())) workoutModel->fetchMore(QModelIndex());
|
||||
}
|
||||
|
||||
void
|
||||
TrainTool::configChanged()
|
||||
{
|
||||
@@ -394,46 +435,6 @@ TrainTool::configChanged()
|
||||
deviceTree->setCurrentItem(allDevices->child(0));
|
||||
}
|
||||
|
||||
// WORKOUTS
|
||||
// zap whats there
|
||||
QList<QTreeWidgetItem *> workouts = allWorkouts->takeChildren();
|
||||
for (int i=0; i<workouts.count(); i++) delete workouts.at(i);
|
||||
|
||||
// standard workouts - ergo and slope
|
||||
QTreeWidgetItem *ergomode = new QTreeWidgetItem(allWorkouts, WORKOUT_TYPE);
|
||||
ergomode->setText(0, tr("Manual Ergo Mode"));
|
||||
QTreeWidgetItem *slopemode = new QTreeWidgetItem(allWorkouts, WORKOUT_TYPE);
|
||||
slopemode->setText(0, tr("Manual Slope Mode"));
|
||||
|
||||
// add all the workouts in the library
|
||||
QVariant workoutDir = appsettings->value(this, GC_WORKOUTDIR);
|
||||
QStringListIterator w(listWorkoutFiles(workoutDir.toString()));
|
||||
while (w.hasNext()) {
|
||||
QString name = w.next();
|
||||
QTreeWidgetItem *work = new QTreeWidgetItem(allWorkouts, WORKOUT_TYPE);
|
||||
work->setText(0, name);
|
||||
}
|
||||
|
||||
#if defined Q_OS_MAC || defined GC_HAVE_VLC
|
||||
// MEDIA
|
||||
QList<QTreeWidgetItem *> media = allMedia->takeChildren();
|
||||
for (int i=0; i<media.count(); i++) delete media.at(i);
|
||||
|
||||
#ifndef Q_OS_MAC
|
||||
// add dvd playback via VLC
|
||||
QTreeWidgetItem *dvd = new QTreeWidgetItem(allMedia, WORKOUT_TYPE);
|
||||
dvd->setText(0, "DVD");
|
||||
#endif
|
||||
|
||||
MediaHelper mediaHelper;
|
||||
foreach(QString video, mediaHelper.listMedia(QDir(workoutDir.toString()))) {
|
||||
|
||||
// add a media line for the video (it might be a song though...)
|
||||
QTreeWidgetItem *media = new QTreeWidgetItem(allMedia, WORKOUT_TYPE);
|
||||
media->setText(0, video);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Athlete
|
||||
FTP=285; // default to 285 if zones are not set
|
||||
int range = main->zones()->whichRange(QDate::currentDate());
|
||||
@@ -501,18 +502,8 @@ TrainTool::devices()
|
||||
void
|
||||
TrainTool::workoutTreeWidgetSelectionChanged()
|
||||
{
|
||||
assert(workoutTree->selectedItems().size() <= 1);
|
||||
if (workoutTree->selectedItems().isEmpty())
|
||||
workout = NULL;
|
||||
else {
|
||||
QTreeWidgetItem *which = workoutTree->selectedItems().first();
|
||||
if (which->type() != WORKOUT_TYPE)
|
||||
workout = NULL;
|
||||
else
|
||||
workout = which;
|
||||
}
|
||||
|
||||
//int mode;
|
||||
QModelIndex current = workoutTree->currentIndex();
|
||||
QString filename = workoutModel->data(workoutModel->index(current.row(), 0), Qt::DisplayRole).toString();
|
||||
|
||||
// wip away the current selected workout
|
||||
if (ergFile) {
|
||||
@@ -520,15 +511,8 @@ TrainTool::workoutTreeWidgetSelectionChanged()
|
||||
ergFile = NULL;
|
||||
}
|
||||
|
||||
// which one is selected?
|
||||
if (currentWorkout() == NULL || currentWorkout()->type() != WORKOUT_TYPE) {
|
||||
main->notifyErgFileSelected(NULL);
|
||||
setLabels();
|
||||
return;
|
||||
}
|
||||
|
||||
// is it the auto mode?
|
||||
int index = workoutItems()->indexOfChild((QTreeWidgetItem *)currentWorkout());
|
||||
int index = current.row();
|
||||
if (index == 0) {
|
||||
// ergo mode
|
||||
main->notifyErgFileSelected(NULL);
|
||||
@@ -545,10 +529,7 @@ TrainTool::workoutTreeWidgetSelectionChanged()
|
||||
//ergPlot->setVisible(false);
|
||||
} else {
|
||||
// workout mode
|
||||
QVariant workoutDir = appsettings->value(this, GC_WORKOUTDIR);
|
||||
QString fileName = workoutDir.toString() + "/" + currentWorkout()->text(0); // filename
|
||||
|
||||
ergFile = new ErgFile(fileName, mode, main);
|
||||
ergFile = new ErgFile(filename, mode, main);
|
||||
if (ergFile->isValid()) {
|
||||
|
||||
status |= RT_WORKOUT;
|
||||
@@ -604,26 +585,9 @@ TrainTool::listWorkoutFiles(const QDir &dir) const
|
||||
void
|
||||
TrainTool::mediaTreeWidgetSelectionChanged()
|
||||
{
|
||||
assert(mediaTree->selectedItems().size() <= 1);
|
||||
if (mediaTree->selectedItems().isEmpty())
|
||||
media = NULL;
|
||||
else {
|
||||
QTreeWidgetItem *which = mediaTree->selectedItems().first();
|
||||
if (which->type() != WORKOUT_TYPE)
|
||||
media = NULL;
|
||||
else
|
||||
media = which;
|
||||
}
|
||||
|
||||
// which one is selected?
|
||||
if (currentMedia() == NULL || currentMedia()->type() != WORKOUT_TYPE) {
|
||||
main->notifyMediaSelected("");
|
||||
return;
|
||||
}
|
||||
|
||||
QVariant workoutDir = appsettings->value(this, GC_WORKOUTDIR);
|
||||
QString fileName = workoutDir.toString() + "/" + currentMedia()->text(0); // filename
|
||||
main->notifyMediaSelected(fileName);
|
||||
QModelIndex current = mediaTree->currentIndex();
|
||||
QString filename = videoModel->data(videoModel->index(current.row(), 0), Qt::DisplayRole).toString();
|
||||
main->notifyMediaSelected(filename);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------------
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "math.h" // for round()
|
||||
#include "Units.h" // for MILES_PER_KM
|
||||
#include <QDebug>
|
||||
#include <QSqlTableModel>
|
||||
|
||||
// Status settings
|
||||
#define RT_MODE_ERGO 0x0001 // load generation modes
|
||||
@@ -84,7 +85,6 @@ class TrainTool : public GcWindow
|
||||
const QTreeWidgetItem *currentWorkout() { return workout; }
|
||||
const QTreeWidgetItem *currentMedia() { return media; }
|
||||
const QTreeWidgetItem *workoutItems() { return allWorkouts; }
|
||||
const QTreeWidgetItem *mediaItems() { return allMedia; }
|
||||
const QTreeWidgetItem *currentServer() { return server; }
|
||||
const QTreeWidgetItem *serverItems() { return allServers; }
|
||||
|
||||
@@ -133,6 +133,8 @@ class TrainTool : public GcWindow
|
||||
void deviceTreeMenuPopup(const QPoint &);
|
||||
void deleteDevice();
|
||||
|
||||
void refresh(); // when TrainDB is updated...
|
||||
|
||||
//XXX void workoutTreeMenuPopup(const QPoint &);
|
||||
//XXX void mediaTreeMenuPopup(const QPoint &);
|
||||
|
||||
@@ -174,13 +176,15 @@ class TrainTool : public GcWindow
|
||||
|
||||
QWidget *toolbarButtons;
|
||||
|
||||
QTreeWidget *workoutTree;
|
||||
QSqlTableModel *videoModel;
|
||||
QSqlTableModel *workoutModel;
|
||||
|
||||
QTreeWidget *deviceTree;
|
||||
QTreeWidget *serverTree;
|
||||
QTreeWidget *mediaTree;
|
||||
QTreeView *workoutTree;
|
||||
QTreeView *mediaTree;
|
||||
|
||||
QTreeWidgetItem *allServers;
|
||||
QTreeWidgetItem *allMedia;
|
||||
QTreeWidgetItem *allDevices;
|
||||
QTreeWidgetItem *server;
|
||||
QTreeWidgetItem *allWorkouts;
|
||||
|
||||
Reference in New Issue
Block a user