Quantcast
Channel: Active questions tagged python - Stack Overflow
Viewing all articles
Browse latest Browse all 23276

Advancing to the next video using PyQt6

$
0
0

I am trying to run a PyQt6 example , but the method to advance the video player to the next clip gets the UI stuck (unresponsive). How can I fix that?

The code to move to the next clip is

@Slot()def next_clicked(self):    print(f"next_from {self._playlist_index}")    self._playlist_index = (self._playlist_index + 1) % len(self._playlist)    self._player.setSource(self._playlist[self._playlist_index])

I have checked that I have a valid playlist containing more than one clip, and the index correctly progresses from n-1 to 0, etc. Once setSource() is executed, the UI becomes unresponsive and needs to be killed.

I am running this on Windows 11 with the latest patches and Python 3.11.

UPDATE: The code works better with shorter clips (~20s) and on a faster drive (C: vs external USB), so the problems might be due to synchronization, i.e., I should not attempt to play a clip before it is fully loaded. But how can I do that, i.e., which event do I need to handle? (Hypothetically: onMediaLoaded() self._player.play()).

The code posted below is a complete example. It should run for you as is, and allow you to open a clip from a directory containing several clips in MP4 format. When you press the "Next clip" button, it may become unresponsive.

Running with QT_FFMPEG_DEBUG=1 does not print any debug messages.

My code, which is virtually identical to the example:

# see https://doc.qt.io/qtforpython-6/examples/example_multimedia_player.html"""PySide6 Multimedia player example"""import sysimport osimport globfrom PySide6.QtCore import QStandardPaths, Qt, Slot, QUrlfrom PySide6.QtGui import QAction, QIcon, QKeySequencefrom PySide6.QtWidgets import (QApplication, QDialog, QFileDialog,                               QMainWindow, QSlider, QStyle, QToolBar)from PySide6.QtMultimedia import (QAudioOutput, QMediaFormat,                                  QMediaPlayer)from PySide6.QtMultimediaWidgets import QVideoWidgetAVI = "video/x-msvideo"  # AVIMP4 = '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 resultdef is_directory(path):    return os.path.isdir(path)def list_files_with_extension(directory, extension):    if not extension.startswith('.'):        extension = '.'+ extension    search_path = os.path.join(directory, '*'+ extension)    return glob.glob(search_path)class MainWindow(QMainWindow):    def __init__(self):        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)        #{ Add a Delete button        icon = QIcon.fromTheme(QIcon.ThemeIcon.MediaRecord,                               style.standardIcon(QStyle.SP_MediaVolumeMuted))        self._delete_action = tool_bar.addAction(icon, "Delete")        self._delete_action.triggered.connect(self._delete_media)        play_menu.addAction(self._delete_action)        #} Add a Delete button        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):        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)        #efault_mimetype = AVI if is_windows else MP4        default_mimetype = 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]            print(f"Opening {url.toString()}")            if url.isLocalFile():                file_url = url.toLocalFile()                if is_directory(file_url):                    self.append_files(file_url)                else:                    dir_url = os.path.dirname(file_url)                    self.append_files(dir_url)            self._player.play()    def append_file(self, url):        self._playlist.append(url)        self._playlist_index = len(self._playlist) - 1        self._player.setSource(url)    def append_files(self, dir_url):        files = list_files_with_extension(dir_url, 'mp4')        urls = [QUrl.fromLocalFile(file) for file in files]        for url in urls:            self.append_file(url)    @Slot()    def _ensure_stopped(self):        if self._player.playbackState() != QMediaPlayer.StoppedState:            self._player.stop()    @Slot()    def _delete_media(self):        playing_now = self._playlist[self._playlist_index]        print(f"Delete {playing_now}")    @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 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)    @Slot()    def next_clicked(self):        print(f"next_from {self._playlist_index}")        self._playlist_index = (self._playlist_index + 1) % len(self._playlist)        self._player.setSource(self._playlist[self._playlist_index])        #self._player.play()    @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)if __name__ == '__main__':    app = QApplication(sys.argv)    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())

Viewing all articles
Browse latest Browse all 23276

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>