diff options
Diffstat (limited to 'examples/multimedia/player/player.py')
-rw-r--r-- | examples/multimedia/player/player.py | 314 |
1 files changed, 176 insertions, 138 deletions
diff --git a/examples/multimedia/player/player.py b/examples/multimedia/player/player.py index bffcb3906..e0e328b5d 100644 --- a/examples/multimedia/player/player.py +++ b/examples/multimedia/player/player.py @@ -1,157 +1,195 @@ - -############################################################################# -## -## Copyright (C) 2017 The Qt Company Ltd. -## Contact: http://www.qt.io/licensing/ -## -## This file is part of the Qt for Python examples of the Qt Toolkit. -## -## $QT_BEGIN_LICENSE:BSD$ -## You may use this file under the terms of the BSD license as follows: -## -## "Redistribution and use in source and binary forms, with or without -## modification, are permitted provided that the following conditions are -## met: -## * Redistributions of source code must retain the above copyright -## notice, this list of conditions and the following disclaimer. -## * Redistributions in binary form must reproduce the above copyright -## notice, this list of conditions and the following disclaimer in -## the documentation and/or other materials provided with the -## distribution. -## * Neither the name of The Qt Company Ltd nor the names of its -## contributors may be used to endorse or promote products derived -## from this software without specific prior written permission. -## -## -## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -## -## $QT_END_LICENSE$ -## -############################################################################# +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +from __future__ import annotations """PySide6 Multimedia player example""" import sys -from PySide6.QtCore import QStandardPaths, Qt +from PySide6.QtCore import QStandardPaths, Qt, Slot from PySide6.QtGui import QAction, QIcon, QKeySequence from PySide6.QtWidgets import (QApplication, QDialog, QFileDialog, - QMainWindow, QSlider, QStyle, QToolBar) -from PySide6.QtMultimedia import QMediaPlayer, QMediaPlaylist + QMainWindow, QSlider, QStyle, QToolBar) +from PySide6.QtMultimedia import (QAudioOutput, QMediaFormat, + QMediaPlayer) from PySide6.QtMultimediaWidgets import QVideoWidget + +AVI = "video/x-msvideo" # AVI + + +MP4 = 'video/mp4' + + +def get_supported_mime_types(): + result = [] + for f in QMediaFormat().supportedFileFormats(QMediaFormat.Decode): + mime_type = QMediaFormat(f).mimeType() + result.append(mime_type.name()) + return result + + class MainWindow(QMainWindow): def __init__(self): - super(MainWindow, self).__init__() - - self.playlist = QMediaPlaylist() - self.player = QMediaPlayer() - - toolBar = QToolBar() - self.addToolBar(toolBar) - - fileMenu = self.menuBar().addMenu("&File") - openAction = QAction(QIcon.fromTheme("document-open"), - "&Open...", self, shortcut=QKeySequence.Open, - triggered=self.open) - fileMenu.addAction(openAction) - exitAction = QAction(QIcon.fromTheme("application-exit"), "E&xit", - self, shortcut="Ctrl+Q", triggered=self.close) - fileMenu.addAction(exitAction) - - playMenu = self.menuBar().addMenu("&Play") - playIcon = self.style().standardIcon(QStyle.SP_MediaPlay) - self.playAction = toolBar.addAction(playIcon, "Play") - self.playAction.triggered.connect(self.player.play) - playMenu.addAction(self.playAction) - - previousIcon = self.style().standardIcon(QStyle.SP_MediaSkipBackward) - self.previousAction = toolBar.addAction(previousIcon, "Previous") - self.previousAction.triggered.connect(self.previousClicked) - playMenu.addAction(self.previousAction) - - pauseIcon = self.style().standardIcon(QStyle.SP_MediaPause) - self.pauseAction = toolBar.addAction(pauseIcon, "Pause") - self.pauseAction.triggered.connect(self.player.pause) - playMenu.addAction(self.pauseAction) - - nextIcon = self.style().standardIcon(QStyle.SP_MediaSkipForward) - self.nextAction = toolBar.addAction(nextIcon, "Next") - self.nextAction.triggered.connect(self.playlist.next) - playMenu.addAction(self.nextAction) - - stopIcon = self.style().standardIcon(QStyle.SP_MediaStop) - self.stopAction = toolBar.addAction(stopIcon, "Stop") - self.stopAction.triggered.connect(self.player.stop) - playMenu.addAction(self.stopAction) - - self.volumeSlider = QSlider() - self.volumeSlider.setOrientation(Qt.Horizontal) - self.volumeSlider.setMinimum(0) - self.volumeSlider.setMaximum(100) - self.volumeSlider.setFixedWidth(app.desktop().availableGeometry(self).width() / 10) - self.volumeSlider.setValue(self.player.volume()) - self.volumeSlider.setTickInterval(10) - self.volumeSlider.setTickPosition(QSlider.TicksBelow) - self.volumeSlider.setToolTip("Volume") - self.volumeSlider.valueChanged.connect(self.player.setVolume) - toolBar.addWidget(self.volumeSlider) - - aboutMenu = self.menuBar().addMenu("&About") - aboutQtAct = QAction("About &Qt", self, triggered=qApp.aboutQt) - aboutMenu.addAction(aboutQtAct) - - self.videoWidget = QVideoWidget() - self.setCentralWidget(self.videoWidget) - self.player.setPlaylist(self.playlist) - self.player.stateChanged.connect(self.updateButtons) - self.player.setVideoOutput(self.videoWidget) - - self.updateButtons(self.player.state()) - + super().__init__() + + self._playlist = [] # FIXME 6.3: Replace by QMediaPlaylist? + self._playlist_index = -1 + self._audio_output = QAudioOutput() + self._player = QMediaPlayer() + self._player.setAudioOutput(self._audio_output) + + self._player.errorOccurred.connect(self._player_error) + + tool_bar = QToolBar() + self.addToolBar(tool_bar) + + file_menu = self.menuBar().addMenu("&File") + icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentOpen) + open_action = QAction(icon, "&Open...", self, + shortcut=QKeySequence.Open, triggered=self.open) + file_menu.addAction(open_action) + tool_bar.addAction(open_action) + icon = QIcon.fromTheme(QIcon.ThemeIcon.ApplicationExit) + exit_action = QAction(icon, "E&xit", self, + shortcut="Ctrl+Q", triggered=self.close) + file_menu.addAction(exit_action) + + play_menu = self.menuBar().addMenu("&Play") + style = self.style() + icon = QIcon.fromTheme(QIcon.ThemeIcon.MediaPlaybackStart, + style.standardIcon(QStyle.SP_MediaPlay)) + self._play_action = tool_bar.addAction(icon, "Play") + self._play_action.triggered.connect(self._player.play) + play_menu.addAction(self._play_action) + + icon = QIcon.fromTheme(QIcon.ThemeIcon.MediaSkipBackward, + style.standardIcon(QStyle.SP_MediaSkipBackward)) + self._previous_action = tool_bar.addAction(icon, "Previous") + self._previous_action.triggered.connect(self.previous_clicked) + play_menu.addAction(self._previous_action) + + icon = QIcon.fromTheme(QIcon.ThemeIcon.MediaPlaybackPause, + style.standardIcon(QStyle.SP_MediaPause)) + self._pause_action = tool_bar.addAction(icon, "Pause") + self._pause_action.triggered.connect(self._player.pause) + play_menu.addAction(self._pause_action) + + icon = QIcon.fromTheme(QIcon.ThemeIcon.MediaSkipForward, + style.standardIcon(QStyle.SP_MediaSkipForward)) + self._next_action = tool_bar.addAction(icon, "Next") + self._next_action.triggered.connect(self.next_clicked) + play_menu.addAction(self._next_action) + + icon = QIcon.fromTheme(QIcon.ThemeIcon.MediaPlaybackStop, + style.standardIcon(QStyle.SP_MediaStop)) + self._stop_action = tool_bar.addAction(icon, "Stop") + self._stop_action.triggered.connect(self._ensure_stopped) + play_menu.addAction(self._stop_action) + + self._volume_slider = QSlider() + self._volume_slider.setOrientation(Qt.Horizontal) + self._volume_slider.setMinimum(0) + self._volume_slider.setMaximum(100) + available_width = self.screen().availableGeometry().width() + self._volume_slider.setFixedWidth(available_width / 10) + self._volume_slider.setValue(self._audio_output.volume()) + self._volume_slider.setTickInterval(10) + self._volume_slider.setTickPosition(QSlider.TicksBelow) + self._volume_slider.setToolTip("Volume") + self._volume_slider.valueChanged.connect(self._audio_output.setVolume) + tool_bar.addWidget(self._volume_slider) + + icon = QIcon.fromTheme(QIcon.ThemeIcon.HelpAbout) + about_menu = self.menuBar().addMenu("&About") + about_qt_act = QAction(icon, "About &Qt", self, triggered=qApp.aboutQt) # noqa: F821 + about_menu.addAction(about_qt_act) + + self._video_widget = QVideoWidget() + self.setCentralWidget(self._video_widget) + self._player.playbackStateChanged.connect(self.update_buttons) + self._player.setVideoOutput(self._video_widget) + + self.update_buttons(self._player.playbackState()) + self._mime_types = [] + + def closeEvent(self, event): + self._ensure_stopped() + event.accept() + + @Slot() def open(self): - fileDialog = QFileDialog(self) - supportedMimeTypes = QMediaPlayer.supportedMimeTypes() - if not supportedMimeTypes: - supportedMimeTypes.append("video/x-msvideo") # AVI - fileDialog.setMimeTypeFilters(supportedMimeTypes) - moviesLocation = QStandardPaths.writableLocation(QStandardPaths.MoviesLocation) - fileDialog.setDirectory(moviesLocation) - if fileDialog.exec_() == QDialog.Accepted: - self.playlist.addMedia(fileDialog.selectedUrls()[0]) - self.player.play() - - def previousClicked(self): + self._ensure_stopped() + file_dialog = QFileDialog(self) + + is_windows = sys.platform == 'win32' + if not self._mime_types: + self._mime_types = get_supported_mime_types() + if (is_windows and AVI not in self._mime_types): + self._mime_types.append(AVI) + elif MP4 not in self._mime_types: + self._mime_types.append(MP4) + + file_dialog.setMimeTypeFilters(self._mime_types) + + default_mimetype = AVI if is_windows else MP4 + if default_mimetype in self._mime_types: + file_dialog.selectMimeTypeFilter(default_mimetype) + + movies_location = QStandardPaths.writableLocation(QStandardPaths.MoviesLocation) + file_dialog.setDirectory(movies_location) + if file_dialog.exec() == QDialog.Accepted: + url = file_dialog.selectedUrls()[0] + self._playlist.append(url) + self._playlist_index = len(self._playlist) - 1 + self._player.setSource(url) + self._player.play() + + @Slot() + def _ensure_stopped(self): + if self._player.playbackState() != QMediaPlayer.StoppedState: + self._player.stop() + + @Slot() + def previous_clicked(self): # Go to previous track if we are within the first 5 seconds of playback # Otherwise, seek to the beginning. - if self.player.position() <= 5000: - self.playlist.previous() + if self._player.position() <= 5000 and self._playlist_index > 0: + self._playlist_index -= 1 + self._playlist.previous() + self._player.setSource(self._playlist[self._playlist_index]) else: - self.player.setPosition(0) + self._player.setPosition(0) + + @Slot() + def next_clicked(self): + if self._playlist_index < len(self._playlist) - 1: + self._playlist_index += 1 + self._player.setSource(self._playlist[self._playlist_index]) + + @Slot("QMediaPlayer::PlaybackState") + def update_buttons(self, state): + media_count = len(self._playlist) + self._play_action.setEnabled(media_count > 0 and state != QMediaPlayer.PlayingState) + self._pause_action.setEnabled(state == QMediaPlayer.PlayingState) + self._stop_action.setEnabled(state != QMediaPlayer.StoppedState) + self._previous_action.setEnabled(self._player.position() > 0) + self._next_action.setEnabled(media_count > 1) + + def show_status_message(self, message): + self.statusBar().showMessage(message, 5000) + + @Slot("QMediaPlayer::Error", str) + def _player_error(self, error, error_string): + print(error_string, file=sys.stderr) + self.show_status_message(error_string) - def updateButtons(self, state): - mediaCount = self.playlist.mediaCount() - self.playAction.setEnabled(mediaCount > 0 - and state != QMediaPlayer.PlayingState) - self.pauseAction.setEnabled(state == QMediaPlayer.PlayingState) - self.stopAction.setEnabled(state != QMediaPlayer.StoppedState) - self.previousAction.setEnabled(self.player.position() > 0) - self.nextAction.setEnabled(mediaCount > 1) if __name__ == '__main__': app = QApplication(sys.argv) - mainWin = MainWindow() - availableGeometry = app.desktop().availableGeometry(mainWin) - mainWin.resize(availableGeometry.width() / 3, availableGeometry.height() / 2) - mainWin.show() - sys.exit(app.exec_()) + main_win = MainWindow() + available_geometry = main_win.screen().availableGeometry() + main_win.resize(available_geometry.width() / 3, + available_geometry.height() / 2) + main_win.show() + sys.exit(app.exec()) |