mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
Add control to Video Player to select among several Meter Widget layouts (#3515)
The video layout file is extended to contain possibly several named layouts. The file is read to list the layouts and offer a selection in the Video Player chart settings menu. The file is then read again to instantiate the selected layout.
This commit is contained in:
@@ -97,6 +97,7 @@ class Context : public QObject
|
||||
DateRange dr_;
|
||||
ErgFile *workout; // the currently selected workout file
|
||||
VideoSyncFile *videosync; // the currently selected videosync file
|
||||
QString videoFilename;
|
||||
long now; // point in time during train session
|
||||
SpecialFields specialFields;
|
||||
|
||||
@@ -155,7 +156,7 @@ class Context : public QObject
|
||||
void notifyVideoSyncFileSelected(VideoSyncFile *x) { videosync=x; videoSyncFileSelected(x); }
|
||||
ErgFile *currentErgFile() { return workout; }
|
||||
VideoSyncFile *currentVideoSyncFile() { return videosync; }
|
||||
void notifyMediaSelected( QString x) { mediaSelected(x); }
|
||||
void notifyMediaSelected( QString x) { videoFilename = x; mediaSelected(x); }
|
||||
void notifySelectVideo(QString x) { selectMedia(x); }
|
||||
void notifySelectWorkout(QString x) { selectWorkout(x); }
|
||||
void notifySelectVideoSync(QString x) { selectVideoSync(x); }
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<layout name="preset_1">
|
||||
<layouts>
|
||||
<layout name="Graphical Meters">
|
||||
<meter name="Speedometer" container="Video" source="Speed" type="NeedleMeter">
|
||||
<RelativeSize Width="15.0" Height="15.0" />
|
||||
<RelativePosition X="12.0" Y="77.0" />
|
||||
@@ -83,13 +84,85 @@
|
||||
<MainColor R="255" G="0" B="0" A="180" />
|
||||
<Text>load</Text>
|
||||
</meter>
|
||||
<!-- START Changes for elevation widget -->
|
||||
<meter name="elevation" container="Video" source="Elevation" type="Elevation">
|
||||
<RelativeSize Width="40.0" Height="15.0" />
|
||||
<RelativePosition X="20.0" Y="92.0" />
|
||||
<BackgroundColor R="200" G="200" B="200" A="125" />
|
||||
<OutlineColor R="255" G="255" B="255" A="250" />
|
||||
<MainColor R="255" G="0" B="0" A="250" />
|
||||
</meter>
|
||||
<!-- END Changes for elevation widget -->
|
||||
<meter name="elevation" container="Video" source="Elevation" type="Elevation">
|
||||
<RelativeSize Width="40.0" Height="15.0" />
|
||||
<RelativePosition X="20.0" Y="92.0" />
|
||||
<BackgroundColor R="200" G="200" B="200" A="125" />
|
||||
<OutlineColor R="255" G="255" B="255" A="250" />
|
||||
<MainColor R="255" G="0" B="0" A="250" />
|
||||
</meter>
|
||||
</layout>
|
||||
|
||||
<layout name="Text Meters">
|
||||
<meter name="MeterPanel" container="Video" source="None" type="Text" Background="true">
|
||||
<RelativeSize Width="15.0" Height="50.0" />
|
||||
<RelativePosition X="10.0" Y="25.0" />
|
||||
<MainColor R="250" G="250" B="250" A="200" />
|
||||
<MainFont Name="Consolas" Size="64" />
|
||||
<AltFont Name="Consolas" Size="64" />
|
||||
<BackgroundColor R="50" G="50" B="50" A="100" />
|
||||
<Text></Text>
|
||||
</meter>
|
||||
<meter name="Slope" container="MeterPanel" source="Load" type="Text" alignment="AlignLeft" textWidth="5">
|
||||
<RelativeSize Width="100.0" Height="10.0" />
|
||||
<RelativePosition X="50.0" Y="8.0" />
|
||||
<MainColor R="250" G="250" B="250" A="200" />
|
||||
<MainFont Name="Consolas" Size="64" />
|
||||
<AltFont Name="Consolas" Size="64" />
|
||||
</meter>
|
||||
<meter name="Speed" container="MeterPanel" source="Speed" type="Text" alignment="AlignLeft" textWidth="5">
|
||||
<RelativeSize Width="100.0" Height="10.0" />
|
||||
<RelativePosition X="50.0" Y="20.0" />
|
||||
<MainColor R="250" G="250" B="250" A="200" />
|
||||
<MainFont Name="Consolas" Size="64" />
|
||||
<AltFont Name="Consolas" Size="64" />
|
||||
</meter>
|
||||
<meter name="Distance" container="MeterPanel" source="Distance" type="Text" alignment="AlignLeft" textWidth="5">
|
||||
<RelativeSize Width="100.0" Height="10.0" />
|
||||
<RelativePosition X="50.0" Y="32.0" />
|
||||
<MainColor R="250" G="250" B="250" A="200" />
|
||||
<MainFont Name="Consolas" Size="64" />
|
||||
<AltFont Name="Consolas" Size="64" />
|
||||
</meter>
|
||||
<meter name="Wattmeter" container="MeterPanel" source="Watt" type="Text" alignment="AlignLeft" textWidth="5">
|
||||
<RelativeSize Width="100.0" Height="10.0" />
|
||||
<RelativePosition X="50.0" Y="44.0" />
|
||||
<MainColor R="250" G="250" B="250" A="200" />
|
||||
<MainFont Name="Consolas" Size="64" />
|
||||
<AltFont Name="Consolas" Size="64" />
|
||||
<AltText> Watts</AltText>
|
||||
</meter>
|
||||
<meter name="Altitude" container="MeterPanel" source="Altitude" type="Text" alignment="AlignLeft" textWidth="5">
|
||||
<RelativeSize Width="100.0" Height="10.0" />
|
||||
<RelativePosition X="50.0" Y="56.0" />
|
||||
<MainColor R="250" G="250" B="250" A="200" />
|
||||
<MainFont Name="Consolas" Size="64" />
|
||||
<AltFont Name="Consolas" Size="64" />
|
||||
</meter>
|
||||
<meter name="Cadencemeter" container="MeterPanel" source="Cadence" type="Text" alignment="AlignLeft" textWidth="5">
|
||||
<RelativeSize Width="100.0" Height="10.0" />
|
||||
<RelativePosition X="50.0" Y="68.0" />
|
||||
<MainColor R="250" G="250" B="250" A="200" />
|
||||
<MainFont Name="Consolas" Size="64" />
|
||||
<AltFont Name="Consolas" Size="64" />
|
||||
<AltText> RPM</AltText>
|
||||
</meter>
|
||||
<meter name="Elapsed" container="MeterPanel" source="Time" type="Text">
|
||||
<RelativeSize Width="100.0" Height="10.0" />
|
||||
<RelativePosition X="50.0" Y="80.0" />
|
||||
<MainColor R="250" G="250" B="250" A="200" />
|
||||
<MainFont Name="Consolas" Size="64" />
|
||||
<AltFont Name="Consolas" Size="64" />
|
||||
</meter>
|
||||
<meter name="elevation" container="Video" source="Elevation" type="Elevation">
|
||||
<RelativeSize Width="40.0" Height="15.0" />
|
||||
<RelativePosition X="20.0" Y="92.0" />
|
||||
<BackgroundColor R="200" G="200" B="200" A="125" />
|
||||
<OutlineColor R="255" G="255" B="255" A="250" />
|
||||
<MainColor R="255" G="0" B="0" A="250" />
|
||||
</meter>
|
||||
</layout>
|
||||
|
||||
<layout name="No Meters">
|
||||
</layout>
|
||||
</layouts>
|
||||
|
||||
@@ -26,10 +26,12 @@
|
||||
#include "VideoLayoutParser.h"
|
||||
#include "MeterWidget.h"
|
||||
|
||||
VideoLayoutParser::VideoLayoutParser (QList<MeterWidget*>* metersWidget, QWidget* VideoContainer)
|
||||
: metersWidget(metersWidget), VideoContainer(VideoContainer)
|
||||
VideoLayoutParser::VideoLayoutParser (QList<MeterWidget*>* metersWidget, QList<QString>* layoutNames, QWidget* VideoContainer)
|
||||
: metersWidget(metersWidget), layoutNames(layoutNames), VideoContainer(VideoContainer)
|
||||
{
|
||||
nonameindex = 0;
|
||||
skipLayout = false;
|
||||
layoutPosition = 0;
|
||||
meterWidget = NULL;
|
||||
}
|
||||
|
||||
@@ -75,9 +77,18 @@ bool VideoLayoutParser::startElement( const QString&, const QString&,
|
||||
const QString& qName,
|
||||
const QXmlAttributes& qAttributes)
|
||||
{
|
||||
if(skipLayout) return true;
|
||||
|
||||
buffer.clear();
|
||||
|
||||
if(qName == "meter")
|
||||
if(qName == "layout")
|
||||
{
|
||||
int i = qAttributes.index("name");
|
||||
layoutNames->append(i >= 0 ? qAttributes.value(i) : QString("noname_") + QString::number(layoutPosition));
|
||||
|
||||
if(layoutPositionSelected != layoutPosition) skipLayout = true;
|
||||
}
|
||||
else if(qName == "meter")
|
||||
{
|
||||
int i = qAttributes.index("name"); // To be used to enclose another sub meter (typ. text within NeedleMeter)
|
||||
if(i >= 0)
|
||||
@@ -207,6 +218,15 @@ bool VideoLayoutParser::startElement( const QString&, const QString&,
|
||||
|
||||
bool VideoLayoutParser::endElement( const QString&, const QString&, const QString& qName)
|
||||
{
|
||||
if(qName == "layout")
|
||||
{
|
||||
layoutPosition++;
|
||||
skipLayout = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(skipLayout) return true;
|
||||
|
||||
if (meterWidget)
|
||||
{
|
||||
if (qName == "Angle")
|
||||
@@ -231,6 +251,8 @@ bool VideoLayoutParser::endElement( const QString&, const QString&, const QStrin
|
||||
|
||||
bool VideoLayoutParser::characters( const QString& str )
|
||||
{
|
||||
if(skipLayout) return true;
|
||||
|
||||
buffer += str;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -32,21 +32,25 @@
|
||||
class VideoLayoutParser : public QXmlDefaultHandler
|
||||
{
|
||||
public:
|
||||
VideoLayoutParser(QList<MeterWidget*>* metersWidget, QWidget* VideoContainer);
|
||||
VideoLayoutParser(QList<MeterWidget*>* metersWidget, QList<QString>* layoutNames, QWidget* VideoContainer);
|
||||
|
||||
bool startElement( const QString&, const QString&, const QString&, const QXmlAttributes& );
|
||||
bool endElement( const QString&, const QString&, const QString& );
|
||||
|
||||
bool characters( const QString& );
|
||||
void SetDefaultValues();
|
||||
int layoutPositionSelected;
|
||||
|
||||
private:
|
||||
QList<MeterWidget*>* metersWidget;
|
||||
QList<QString>* layoutNames;
|
||||
QWidget* VideoContainer;
|
||||
|
||||
QString buffer;
|
||||
|
||||
int nonameindex;
|
||||
int layoutPosition;
|
||||
bool skipLayout;
|
||||
|
||||
QString source;
|
||||
QString meterName;
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
VideoWindow::VideoWindow(Context *context) :
|
||||
GcChartWindow(context), context(context), m_MediaChanged(false)
|
||||
{
|
||||
setControls(NULL);
|
||||
QWidget *c = NULL;
|
||||
setProperty("color", QColor(Qt::black));
|
||||
|
||||
QHBoxLayout *layout = new QHBoxLayout();
|
||||
@@ -80,38 +80,25 @@ VideoWindow::VideoWindow(Context *context) :
|
||||
#endif
|
||||
|
||||
#if defined(WIN32) || defined(Q_OS_LINUX)
|
||||
// Video Overlays Initialization: if video config file is not present
|
||||
// copy a default one to be used as a model by the user.
|
||||
// An empty video-layout.xml file disables video overlays
|
||||
QString filename = context->athlete->home->config().canonicalPath() + "/" + "video-layout.xml";
|
||||
QFile file(filename);
|
||||
if (!file.exists())
|
||||
{
|
||||
file.setFileName(":/xml/video-layout.xml");
|
||||
file.copy(filename);
|
||||
QFile::setPermissions(filename, QFileDevice::ReadUser|QFileDevice::WriteUser);
|
||||
}
|
||||
if (file.exists())
|
||||
{
|
||||
// clean previous layout
|
||||
foreach(MeterWidget* p_meterWidget, m_metersWidget)
|
||||
{
|
||||
m_metersWidget.removeAll(p_meterWidget);
|
||||
delete p_meterWidget;
|
||||
}
|
||||
|
||||
VideoLayoutParser handler(&m_metersWidget, container);
|
||||
// Read the video layouts just to list the names for the layout selector
|
||||
readVideoLayout(-1);
|
||||
|
||||
QXmlInputSource source (&file);
|
||||
QXmlSimpleReader reader;
|
||||
reader.setContentHandler (&handler);
|
||||
// Create the layout selector form
|
||||
c = new QWidget;
|
||||
QVBoxLayout *cl = new QVBoxLayout(c);
|
||||
QFormLayout *controlsLayout = new QFormLayout();
|
||||
controlsLayout->setSpacing(0);
|
||||
controlsLayout->setContentsMargins(5,5,5,5);
|
||||
cl->addLayout(controlsLayout);
|
||||
QLabel *layoutLabel = new QLabel(tr("Video meters layout"), this);
|
||||
layoutLabel->setAutoFillBackground(true);
|
||||
layoutSelector = new QComboBox(this);
|
||||
|
||||
reader.parse (source);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << qPrintable(QString("file" + filename + " (video layout XML file) not found"));
|
||||
for(int i = 0; i < layoutNames.length(); i++) {
|
||||
layoutSelector->addItem(layoutNames[i], i);
|
||||
}
|
||||
controlsLayout->addRow(layoutLabel, layoutSelector);
|
||||
#endif
|
||||
} else {
|
||||
|
||||
@@ -131,6 +118,8 @@ VideoWindow::VideoWindow(Context *context) :
|
||||
layout->addWidget(wd);
|
||||
#endif
|
||||
|
||||
setControls(c);
|
||||
|
||||
if (init) {
|
||||
// get updates..
|
||||
connect(context, SIGNAL(telemetryUpdate(RealtimeData)), this, SLOT(telemetryUpdate(RealtimeData)));
|
||||
@@ -140,6 +129,15 @@ VideoWindow::VideoWindow(Context *context) :
|
||||
connect(context, SIGNAL(seek(long)), this, SLOT(seekPlayback(long)));
|
||||
connect(context, SIGNAL(unpause()), this, SLOT(resumePlayback()));
|
||||
connect(context, SIGNAL(mediaSelected(QString)), this, SLOT(mediaSelected(QString)));
|
||||
connect(context, SIGNAL(configChanged(qint32)), this, SLOT(layoutChanged()));
|
||||
connect(layoutSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(layoutChanged()));
|
||||
|
||||
// The video file may have been already selected
|
||||
mediaSelected(context->videoFilename);
|
||||
// We may add a video player while training is already running!
|
||||
if(context->isRunning) startPlayback();
|
||||
// Instantiate a layout as initial default
|
||||
layoutChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,6 +167,62 @@ VideoWindow::~VideoWindow()
|
||||
#endif
|
||||
}
|
||||
|
||||
void VideoWindow::layoutChanged()
|
||||
{
|
||||
readVideoLayout(videoLayout());
|
||||
}
|
||||
|
||||
void VideoWindow::readVideoLayout(int pos)
|
||||
{
|
||||
// Video Overlays Initialization: if video config file is not present
|
||||
// copy a default one to be used as a model by the user.
|
||||
// An empty video-layout.xml file disables video overlays
|
||||
QString filename = context->athlete->home->config().canonicalPath() + "/" + "video-layout.xml";
|
||||
QFile file(filename);
|
||||
if (!file.exists())
|
||||
{
|
||||
file.setFileName(":/xml/video-layout.xml");
|
||||
file.copy(filename);
|
||||
QFile::setPermissions(filename, QFileDevice::ReadUser|QFileDevice::WriteUser);
|
||||
}
|
||||
if (file.exists())
|
||||
{
|
||||
// clean previous layout
|
||||
foreach(MeterWidget* p_meterWidget, m_metersWidget)
|
||||
{
|
||||
m_metersWidget.removeAll(p_meterWidget);
|
||||
p_meterWidget->deleteLater();
|
||||
}
|
||||
layoutNames.clear();
|
||||
|
||||
VideoLayoutParser handler(&m_metersWidget, &layoutNames, container);
|
||||
QXmlInputSource source(&file);
|
||||
QXmlSimpleReader reader;
|
||||
handler.layoutPositionSelected = pos;
|
||||
reader.setContentHandler(&handler);
|
||||
reader.parse(source);
|
||||
qDebug() << "Video Layout parsing: " << layoutNames;
|
||||
if(context->isRunning) showMeters();
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << qPrintable(QString("file" + filename + " (video layout XML file) not found"));
|
||||
}
|
||||
}
|
||||
|
||||
void VideoWindow::showMeters()
|
||||
{
|
||||
foreach(MeterWidget* p_meterWidget , m_metersWidget)
|
||||
{
|
||||
p_meterWidget->setWindowOpacity(1); // Show the widget
|
||||
p_meterWidget->AdjustSizePos();
|
||||
p_meterWidget->update();
|
||||
p_meterWidget->raise();
|
||||
p_meterWidget->show();
|
||||
}
|
||||
prevPosition = mapToGlobal(pos());
|
||||
}
|
||||
|
||||
void VideoWindow::resizeEvent(QResizeEvent * )
|
||||
{
|
||||
foreach(MeterWidget* p_meterWidget , m_metersWidget)
|
||||
@@ -178,7 +232,7 @@ void VideoWindow::resizeEvent(QResizeEvent * )
|
||||
|
||||
void VideoWindow::startPlayback()
|
||||
{
|
||||
if (context->currentVideoSyncFile()) {
|
||||
if ((!context->isRunning) && context->currentVideoSyncFile()) {
|
||||
context->currentVideoSyncFile()->manualOffset = 0.0;
|
||||
context->currentVideoSyncFile()->km = 0.0;
|
||||
}
|
||||
@@ -210,21 +264,12 @@ void VideoWindow::startPlayback()
|
||||
mp->play();
|
||||
#endif
|
||||
|
||||
foreach(MeterWidget* p_meterWidget , m_metersWidget)
|
||||
{
|
||||
p_meterWidget->setWindowOpacity(1); // Show the widget
|
||||
p_meterWidget->AdjustSizePos();
|
||||
p_meterWidget->update();
|
||||
|
||||
p_meterWidget->raise();
|
||||
p_meterWidget->show();
|
||||
}
|
||||
prevPosition = mapToGlobal(pos());
|
||||
showMeters();
|
||||
}
|
||||
|
||||
void VideoWindow::stopPlayback()
|
||||
{
|
||||
if (context->currentVideoSyncFile())
|
||||
if ((!context->isRunning) && context->currentVideoSyncFile())
|
||||
context->currentVideoSyncFile()->manualOffset = 0.0;
|
||||
|
||||
#ifdef GC_VIDEO_VLC
|
||||
@@ -239,7 +284,6 @@ void VideoWindow::stopPlayback()
|
||||
#endif
|
||||
foreach(MeterWidget* p_meterWidget , m_metersWidget)
|
||||
p_meterWidget->hide();
|
||||
|
||||
}
|
||||
|
||||
void VideoWindow::pausePlayback()
|
||||
@@ -591,6 +635,7 @@ void VideoWindow::mediaSelected(QString filename)
|
||||
mc = QMediaContent(QUrl::fromLocalFile(filename));
|
||||
mp->setMedia(mc);
|
||||
#endif
|
||||
if(context->isRunning) startPlayback();
|
||||
}
|
||||
|
||||
MediaHelper::MediaHelper()
|
||||
|
||||
@@ -143,14 +143,19 @@ class VideoWindow : public GcChartWindow
|
||||
Q_OBJECT
|
||||
G_OBJECT
|
||||
|
||||
// which layout to use
|
||||
Q_PROPERTY(int videoLayout READ videoLayout WRITE setVideoLayout USER true)
|
||||
|
||||
public:
|
||||
|
||||
VideoWindow(Context *);
|
||||
~VideoWindow();
|
||||
int videoLayout() const { return layoutSelector->currentIndex(); }
|
||||
|
||||
|
||||
public slots:
|
||||
|
||||
void layoutChanged();
|
||||
void startPlayback();
|
||||
void stopPlayback();
|
||||
void pausePlayback();
|
||||
@@ -162,6 +167,7 @@ class VideoWindow : public GcChartWindow
|
||||
protected:
|
||||
|
||||
void resizeEvent(QResizeEvent *);
|
||||
void setVideoLayout(int x) { layoutSelector->setCurrentIndex(x); }
|
||||
|
||||
// current data
|
||||
int curPosition;
|
||||
@@ -176,6 +182,11 @@ class VideoWindow : public GcChartWindow
|
||||
QList<MeterWidget*> m_metersWidget;
|
||||
QPoint prevPosition;
|
||||
|
||||
private:
|
||||
QList<QString> layoutNames;
|
||||
void readVideoLayout(int x);
|
||||
void showMeters();
|
||||
|
||||
#ifdef GC_VIDEO_VLC
|
||||
|
||||
// vlc for older QT
|
||||
@@ -193,6 +204,7 @@ class VideoWindow : public GcChartWindow
|
||||
#endif
|
||||
|
||||
QWidget *container;
|
||||
QComboBox *layoutSelector;
|
||||
|
||||
bool init; // we initialised ok ?
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user