Animate widget visibility and fix fades
Use animated show/hide for playlist widgets and harden fade animations. Replace direct setVisible signal handlers with lambdas that call set_widget_visible_animated, and add set_widget_visible_animated to route to fade-in/out. Fade methods now stop opposing animations, reuse existing QGraphicsOpacityEffect when present, preserve current opacity as start values, and set effect opacity when creating a new effect. Also catch possible RuntimeError when stopping animations and keep animation references on widgets to avoid premature GC.
This commit is contained in:
@@ -510,10 +510,10 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin, AnalysisMixin):
|
|||||||
self.signals.update_progress.connect(self.update_progress_bar)
|
self.signals.update_progress.connect(self.update_progress_bar)
|
||||||
|
|
||||||
# Connect new signals
|
# Connect new signals
|
||||||
self.signals.playlist_info_label_visible.connect(self.playlist_info_label.setVisible)
|
self.signals.playlist_info_label_visible.connect(lambda v: self.set_widget_visible_animated(self.playlist_info_label, v))
|
||||||
self.signals.playlist_info_label_text.connect(self.playlist_info_label.setText)
|
self.signals.playlist_info_label_text.connect(self.playlist_info_label.setText)
|
||||||
self.signals.selected_subs_label_text.connect(self.selected_subs_label.setText)
|
self.signals.selected_subs_label_text.connect(self.selected_subs_label.setText)
|
||||||
self.signals.playlist_select_btn_visible.connect(self.playlist_select_btn.setVisible)
|
self.signals.playlist_select_btn_visible.connect(lambda v: self.set_widget_visible_animated(self.playlist_select_btn, v))
|
||||||
self.signals.playlist_select_btn_text.connect(self.playlist_select_btn.setText)
|
self.signals.playlist_select_btn_text.connect(self.playlist_select_btn.setText)
|
||||||
|
|
||||||
# Disable analysis-dependent controls until video is analyzed
|
# Disable analysis-dependent controls until video is analyzed
|
||||||
@@ -1461,15 +1461,25 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin, AnalysisMixin):
|
|||||||
|
|
||||||
def animate_widget_fade_in(self, widget: QWidget, duration: int = 300) -> None:
|
def animate_widget_fade_in(self, widget: QWidget, duration: int = 300) -> None:
|
||||||
"""Fade in a widget using opacity animation."""
|
"""Fade in a widget using opacity animation."""
|
||||||
if widget.isVisible():
|
# Stop fade out if running
|
||||||
|
if hasattr(widget, '_fade_out_anim'):
|
||||||
|
try:
|
||||||
|
if widget._fade_out_anim.state() == QPropertyAnimation.State.Running:
|
||||||
|
widget._fade_out_anim.stop()
|
||||||
|
except RuntimeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if widget.isVisible() and widget.graphicsEffect() is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Setup opacity effect if not present
|
# Setup opacity effect if not present
|
||||||
effect = QGraphicsOpacityEffect(widget)
|
effect = widget.graphicsEffect()
|
||||||
widget.setGraphicsEffect(effect)
|
if not effect or not isinstance(effect, QGraphicsOpacityEffect):
|
||||||
|
effect = QGraphicsOpacityEffect(widget)
|
||||||
|
widget.setGraphicsEffect(effect)
|
||||||
|
|
||||||
# Determine start and end values
|
# Determine start value (current opacity if previously animating)
|
||||||
start_val = 0.0
|
start_val = effect.opacity() if widget.isVisible() else 0.0
|
||||||
end_val = 1.0
|
end_val = 1.0
|
||||||
|
|
||||||
# Setup animation
|
# Setup animation
|
||||||
@@ -1488,15 +1498,28 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin, AnalysisMixin):
|
|||||||
|
|
||||||
def animate_widget_fade_out(self, widget: QWidget, duration: int = 300) -> None:
|
def animate_widget_fade_out(self, widget: QWidget, duration: int = 300) -> None:
|
||||||
"""Fade out a widget and then hide it."""
|
"""Fade out a widget and then hide it."""
|
||||||
|
# Stop fade in if running
|
||||||
|
if hasattr(widget, '_fade_anim'):
|
||||||
|
try:
|
||||||
|
if widget._fade_anim.state() == QPropertyAnimation.State.Running:
|
||||||
|
widget._fade_anim.stop()
|
||||||
|
except RuntimeError:
|
||||||
|
pass
|
||||||
|
|
||||||
if not widget.isVisible():
|
if not widget.isVisible():
|
||||||
return
|
return
|
||||||
|
|
||||||
effect = QGraphicsOpacityEffect(widget)
|
effect = widget.graphicsEffect()
|
||||||
widget.setGraphicsEffect(effect)
|
if not effect or not isinstance(effect, QGraphicsOpacityEffect):
|
||||||
|
effect = QGraphicsOpacityEffect(widget)
|
||||||
|
widget.setGraphicsEffect(effect)
|
||||||
|
effect.setOpacity(1.0)
|
||||||
|
|
||||||
|
start_val = effect.opacity()
|
||||||
|
|
||||||
anim = QPropertyAnimation(effect, b"opacity", widget)
|
anim = QPropertyAnimation(effect, b"opacity", widget)
|
||||||
anim.setDuration(duration)
|
anim.setDuration(duration)
|
||||||
anim.setStartValue(1.0)
|
anim.setStartValue(start_val)
|
||||||
anim.setEndValue(0.0)
|
anim.setEndValue(0.0)
|
||||||
anim.setEasingCurve(QEasingCurve.Type.InQuad)
|
anim.setEasingCurve(QEasingCurve.Type.InQuad)
|
||||||
|
|
||||||
@@ -1509,6 +1532,14 @@ class YTSageApp(QMainWindow, FormatTableMixin, VideoInfoMixin, AnalysisMixin):
|
|||||||
|
|
||||||
widget._fade_out_anim = anim # Keep reference
|
widget._fade_out_anim = anim # Keep reference
|
||||||
|
|
||||||
|
def set_widget_visible_animated(self, widget: QWidget, visible: bool) -> None:
|
||||||
|
"""Toggle widget visibility with fade animation."""
|
||||||
|
if visible:
|
||||||
|
self.animate_widget_fade_in(widget)
|
||||||
|
else:
|
||||||
|
self.animate_widget_fade_out(widget)
|
||||||
|
|
||||||
|
|
||||||
def set_status_message_animated(self, message: str) -> None:
|
def set_status_message_animated(self, message: str) -> None:
|
||||||
"""Update status label with a cross-fade animation."""
|
"""Update status label with a cross-fade animation."""
|
||||||
if self.status_label.text() == message:
|
if self.status_label.text() == message:
|
||||||
|
|||||||
Reference in New Issue
Block a user