Skip to content

Commit

Permalink
[gui] Brand new QgsMediaWidget to for playing audio or video files
Browse files Browse the repository at this point in the history
  • Loading branch information
nirvn authored and nyalldawson committed Jan 26, 2023
1 parent 4bc4412 commit 7b102b9
Show file tree
Hide file tree
Showing 8 changed files with 473 additions and 1 deletion.
85 changes: 85 additions & 0 deletions python/gui/auto_generated/qgsmediawidget.sip.in
@@ -0,0 +1,85 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/qgsmediawidget.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/





class QgsMediaWidget : QWidget
{
%Docstring(signature="appended")
The :py:class:`QgsMediaWidget` class creates a widget for playing back audio and video media files.

.. versionadded:: 3.30
%End

%TypeHeaderCode
#include "qgsmediawidget.h"
%End
public:

enum Mode
{
Audio,
Video,
};

explicit QgsMediaWidget( QWidget *parent /TransferThis/ = 0 );
%Docstring
Constructor
%End

QString mediaPath() const;
%Docstring
Returns the media path.
%End

void setMediaPath( const QString &path );
%Docstring
Sets the media ``path``.
%End

Mode mode() const;
%Docstring
Returns the media widget mode.
%End

void setMode( Mode mode );
%Docstring
Sets the media widget ``mode``.
%End

int videoHeight() const;
%Docstring
Returns the video frame height.
%End

void setVideoHeight( int height );
%Docstring
Sets the video frame height.

.. note::

setting the height to 0 is interpreted as a video frame that will
expand to fill available height in the widget's parent layout.
%End

QMediaPlayer *mediaPlayer();
%Docstring
Returns the QMediaPlayer object.
%End

};

/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/qgsmediawidget.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
1 change: 1 addition & 0 deletions python/gui/gui.sip.in
Expand Up @@ -10,6 +10,7 @@ ${DEFAULTDOCSTRINGSIGNATURE}
%Import QtCore/QtCoremod.sip
%Import QtGui/QtGuimod.sip
%Import QtXml/QtXmlmod.sip
%Import QtMultimedia/QtMultimediamod.sip
%Import ${QSCI_SIP_MOD_NAME}

%Include gui_auto.sip
1 change: 1 addition & 0 deletions python/gui/gui_auto.sip
Expand Up @@ -139,6 +139,7 @@
%Include auto_generated/qgsmaptoolidentifyfeature.sip
%Include auto_generated/qgsmaptoolpan.sip
%Include auto_generated/qgsmaptoolzoom.sip
%Include auto_generated/qgsmediawidget.sip
%Include auto_generated/qgsmenuheader.sip
%Include auto_generated/qgsmessagebar.sip
%Include auto_generated/qgsmessagebaritem.sip
Expand Down
6 changes: 5 additions & 1 deletion src/gui/CMakeLists.txt
Expand Up @@ -593,6 +593,7 @@ set(QGIS_GUI_SRCS
qgsmaptoolzoom.cpp
qgsmaplayerconfigwidget.cpp
qgsmaterialsettingswidget.cpp
qgsmediawidget.cpp
qgsmenuheader.cpp
qgsmessagebar.cpp
qgsmessagebaritem.cpp
Expand Down Expand Up @@ -859,6 +860,7 @@ set(QGIS_GUI_HDRS
qgsmaptoolzoom.h
qgsmaskingwidget.h
qgsmaterialsettingswidget.h
qgsmediawidget.h
qgsmenuheader.h
qgsmessagebar.h
qgsmessagebaritem.h
Expand Down Expand Up @@ -1408,7 +1410,7 @@ set(QGIS_GUI_PRIVATE_HDRS
attributetable/qgsfeaturefilterwidget_p.h
)

find_package(${QT_VERSION_BASE} COMPONENTS Qml QuickWidgets UiTools REQUIRED)
find_package(${QT_VERSION_BASE} COMPONENTS Multimedia MultimediaWidgets Qml QuickWidgets UiTools REQUIRED)

if(${QT_VERSION_BASE}Qml_FOUND)
add_definitions(-DWITH_QML)
Expand Down Expand Up @@ -1690,6 +1692,8 @@ target_link_libraries(qgis_gui
${QWT_LIBRARY}
${QSCINTILLA_LIBRARY}
${QT_VERSION_BASE}::QuickWidgets
${QT_VERSION_BASE}::Multimedia
${QT_VERSION_BASE}::MultimediaWidgets
)

if (BUILD_WITH_QT6)
Expand Down
208 changes: 208 additions & 0 deletions src/gui/qgsmediawidget.cpp
@@ -0,0 +1,208 @@
/***************************************************************************
qgsmediawidget.cpp
---------------------
begin : 2023.01.24
copyright : (C) 2023 by Mathieu Pellerin
email : mathieu at opengis dot 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. *
* *
***************************************************************************/

#include "qgsmediawidget.h"
#include "qgsapplication.h"

#include <QLabel>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QPushButton>
#include <QSlider>
#include <QVideoWidget>


QgsMediaWidget::QgsMediaWidget( QWidget *parent )
: QWidget( parent )
{
mLayout = new QVBoxLayout();
mLayout->setContentsMargins( 0, 0, 0, 0 );

mVideoWidget = new QVideoWidget( this );
mVideoWidget->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
mVideoWidget->setMinimumHeight( 0 );
mVideoWidget->setMaximumHeight( 9999 );
mLayout->addWidget( mVideoWidget );

QHBoxLayout *controlsLayout = new QHBoxLayout();
controlsLayout->setContentsMargins( 0, 0, 0, 0 );

mPlayButton = new QPushButton( this );
mPlayButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred );
mPlayButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionPlay.svg" ) ) );
mPlayButton->setCheckable( true );
controlsLayout->addWidget( mPlayButton );

mPositionSlider = new QSlider( this );
mPositionSlider->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
mPositionSlider->setOrientation( Qt::Horizontal );
controlsLayout->addWidget( mPositionSlider );

mDurationLabel = new QLabel( this );
mDurationLabel->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred );
mDurationLabel->setAlignment( Qt::AlignHCenter );
mDurationLabel->setText( QStringLiteral( "-" ) );
QFontMetrics fm( mDurationLabel->font() );
mDurationLabel->setMinimumWidth( fm.boundingRect( QStringLiteral( "00:00:00" ) ).width() );
controlsLayout->addWidget( mDurationLabel );

QWidget *controls = new QWidget();
controls->setLayout( controlsLayout );

mLayout->addWidget( controls );
setLayout( mLayout );

adjustControls();
setControlsEnabled( false );

mMediaPlayer.setVideoOutput( mVideoWidget );

connect( &mMediaPlayer, &QMediaPlayer::mediaStatusChanged, this, &QgsMediaWidget::mediaStatusChanged );
connect( &mMediaPlayer, &QMediaPlayer::positionChanged, this, [ = ]()
{
mPositionSlider->setValue( static_cast<int>( mMediaPlayer.position() / 1000 ) );
} );

connect( mPlayButton, &QAbstractButton::clicked, this, [ = ]()
{
#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
if ( mMediaPlayer.playbackState() == QMediaPlayer::PlayingState )
#else
if ( mMediaPlayer.state() == QMediaPlayer::PlayingState )
#endif
{
mMediaPlayer.pause();
}
else
{
mMediaPlayer.play();
}
} );
connect( mPositionSlider, &QAbstractSlider::sliderReleased, this, [ = ]()
{
mMediaPlayer.setPosition( mPositionSlider->value() * 1000 );
} );
}

void QgsMediaWidget::setMediaPath( const QString &path )
{
if ( mMediaPath == path )
return;

mMediaPath = path;
#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
mMediaPlayer.setSource( QUrl::fromLocalFile( path ) );
#else
mMediaPlayer.setMedia( QUrl::fromLocalFile( path ) );
#endif
}

void QgsMediaWidget::setMode( Mode mode )
{
if ( mMode == mode )
return;

mMode = mode;
adjustControls();
}

int QgsMediaWidget::videoHeight() const
{
return mVideoWidget->minimumHeight();
}

void QgsMediaWidget::setVideoHeight( int height )
{
mVideoWidget->setMinimumHeight( height );
mVideoWidget->setMaximumHeight( height > 0 ? height : 9999 );
}

void QgsMediaWidget::adjustControls()
{
switch ( mMode )
{
case Audio:
{
mVideoWidget->setVisible( false );
break;
}
case Video:
{
mVideoWidget->setVisible( true );
break;
}
}
}

void QgsMediaWidget::setControlsEnabled( bool enabled )
{
mPlayButton->setEnabled( enabled );
mPositionSlider->setEnabled( enabled );
}

void QgsMediaWidget::mediaStatusChanged( QMediaPlayer::MediaStatus status )
{
switch ( status )
{
case QMediaPlayer::LoadedMedia:
{
setControlsEnabled( true );
mPositionSlider->setMinimum( 0 );
mPositionSlider->setMaximum( static_cast<int>( mMediaPlayer.duration() / 1000 ) );
mPositionSlider->setValue( 0 );
int seconds = std::floor( mMediaPlayer.duration() / 1000 );
const int hours = std::floor( seconds / 3600 );
seconds -= hours * 3600;
const int minutes = std::floor( seconds / 60 );
seconds -= minutes * 60;
mDurationLabel->setText( QStringLiteral( "%1:%2:%3" ).arg( QString::number( hours ), 2, '0' )
.arg( QString::number( minutes ), 2, '0' )
.arg( QString::number( seconds ), 2, '0' ) );
break;
}

case QMediaPlayer::EndOfMedia:
{
mPlayButton->setChecked( false );
break;
}

case QMediaPlayer::LoadingMedia:
case QMediaPlayer::StalledMedia:
case QMediaPlayer::BufferingMedia:
case QMediaPlayer::BufferedMedia:
{
break;
}

case QMediaPlayer::InvalidMedia:
{
setControlsEnabled( false );
mDurationLabel->setText( tr( "invalid" ) );
break;
}

case QMediaPlayer::NoMedia:
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
case QMediaPlayer::UnknownMediaStatus:
#endif
{
setControlsEnabled( false );
mDurationLabel->setText( QStringLiteral( "-" ) );
break;
}
}
}

0 comments on commit 7b102b9

Please sign in to comment.