fix(present): black video flashes and other bugs linked to Qt (#465)

* Relies on Pixel Format instead of Size

* chore(lib): remove deprecated warning

* chore(deps): update lockfiles

* chore(lib): cleanup code

* chore(ci): run fmt

* chore: update changelog

* chore: typo

* chore(docs): remove ref. to black screen

* fix(deps): issue on Windows

---------

Co-authored-by: PeculiarProgrammer <179261820+PeculiarProgrammer@users.noreply.github.com>
This commit is contained in:
Jérome Eertmans
2024-08-26 17:29:39 +02:00
committed by GitHub
parent 3b23c7739d
commit 5b6f5eb1e4
7 changed files with 215 additions and 206 deletions

View File

@ -11,23 +11,6 @@ from ..commons import config_path_option, folder_path_option, verbosity_option
from ..config import Config, PresentationConfig
from ..logger import logger
PREFERRED_QT_VERSIONS = ("6.5.1", "6.5.2")
def warn_if_non_desirable_pyside6_version() -> None:
from qtpy import API, QT_VERSION
if sys.version_info < (3, 12) and (
API != "pyside6" or QT_VERSION not in PREFERRED_QT_VERSIONS
):
logger.warn(
f"You are using {API = }, {QT_VERSION = }, "
"but we recommend installing 'PySide6==6.5.2', mainly to avoid "
"flashing screens between slides, "
"see issue https://github.com/jeertmans/manim-slides/issues/293. "
"You can do so with `pip install 'manim-slides[pyside6]'`."
)
@click.command()
@folder_path_option
@ -302,8 +285,6 @@ def present(
if start_at[1]:
start_at_slide_number = start_at[1]
warn_if_non_desirable_pyside6_version()
from qtpy.QtCore import Qt
from qtpy.QtGui import QScreen

View File

@ -226,6 +226,8 @@ class Player(QMainWindow): # type: ignore[misc]
self.icon = QIcon(":/icon.png")
self.setWindowIcon(self.icon)
self.frame = QVideoFrame()
self.audio_output = QAudioOutput()
self.video_widget = QVideoWidget()
self.video_sink = self.video_widget.videoSink()
@ -240,18 +242,6 @@ class Player(QMainWindow): # type: ignore[misc]
self.presentation_changed.connect(self.presentation_changed_callback)
self.slide_changed.connect(self.slide_changed_callback)
old_frame = None
def frame_changed(frame: QVideoFrame) -> None:
nonlocal old_frame
if old_frame and (frame.size() != old_frame.size()):
self.video_sink.setVideoFrame(old_frame)
frame = old_frame
self.info.video_sink.setVideoFrame(frame)
old_frame = frame
self.info = Info(
full_screen=full_screen,
aspect_ratio_mode=aspect_ratio_mode,
@ -259,7 +249,7 @@ class Player(QMainWindow): # type: ignore[misc]
)
self.info.close_event.connect(self.closeEvent)
self.info.key_press_event.connect(self.keyPressEvent)
self.video_sink.videoFrameChanged.connect(frame_changed)
self.video_sink.videoFrameChanged.connect(self.frame_changed)
self.hide_info_window = hide_info_window
# Connecting key callbacks
@ -555,6 +545,34 @@ class Player(QMainWindow): # type: ignore[misc]
else:
self.setCursor(Qt.BlankCursor)
def frame_changed(self, frame: QVideoFrame) -> None:
"""
Slot to handle possibly invalid frames.
This slot cannot be decorated with ``@Slot`` as
the video sinks are handled in different threads.
As of Qt>=6.5.3, the last frame of every video is "flushed",
resulting in a short black screen between each slide.
To avoid this issue, we check every frame, and avoid playing
invalid ones.
References
----------
1. https://github.com/jeertmans/manim-slides/issues/293
2. https://github.com/jeertmans/manim-slides/pull/464
:param frame: The most recent frame.
"""
if frame.isValid():
self.frame = frame
else:
self.video_sink.setVideoFrame(self.frame) # Reuse previous frame
self.info.video_sink.setVideoFrame(self.frame)
def closeEvent(self, event: QCloseEvent) -> None: # noqa: N802
self.close()