forked from CastroFidel/winehelper
optimization of icon animation in the _start_icon_fade_animation method
This commit is contained in:
@ -13,7 +13,7 @@ from functools import partial
|
||||
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,QPushButton, QLabel, QTabWidget,
|
||||
QTextEdit, QFileDialog, QMessageBox, QLineEdit, QCheckBox, QStackedWidget, QScrollArea,
|
||||
QListWidget, QListWidgetItem, QGridLayout, QFrame, QDialog, QTextBrowser)
|
||||
from PyQt5.QtCore import Qt, QProcess, QSize, QTimer, QProcessEnvironment
|
||||
from PyQt5.QtCore import Qt, QProcess, QSize, QTimer, QProcessEnvironment, QPropertyAnimation, QEasingCurve
|
||||
from PyQt5.QtGui import QIcon, QFont, QTextCursor, QPixmap, QPainter
|
||||
|
||||
|
||||
@ -908,68 +908,79 @@ class WineHelperGUI(QMainWindow):
|
||||
print(f"Ошибка чтения файла для извлечения PROG_URL: {str(e)}")
|
||||
return None
|
||||
|
||||
def _ease_in_out_quad(self, t):
|
||||
"""Простая квадратичная функция сглаживания (ease-in-out)."""
|
||||
# t - значение от 0.0 до 1.0
|
||||
if t < 0.5:
|
||||
return 2 * t * t
|
||||
return 1 - pow(-2 * t + 2, 2) / 2
|
||||
|
||||
def _start_icon_fade_animation(self, button):
|
||||
"""Запускает анимацию плавного перехода для иконки на кнопке."""
|
||||
"""Запускает анимацию плавного перехода для иконки на кнопке с помощью QPropertyAnimation."""
|
||||
if button not in self.icon_animators:
|
||||
return
|
||||
|
||||
anim_data = self.icon_animators[button]
|
||||
|
||||
# Получаем или создаем объект анимации один раз
|
||||
animation = anim_data.get('animation')
|
||||
if not animation:
|
||||
# Устанавливаем динамическое свойство, чтобы избежать предупреждений
|
||||
button.setProperty("fadeProgress", 0.0)
|
||||
animation = QPropertyAnimation(button, b"fadeProgress", self)
|
||||
animation.setDuration(700)
|
||||
animation.setEasingCurve(QEasingCurve.InOutQuad)
|
||||
|
||||
# Сигналы подключаются только один раз при создании
|
||||
animation.valueChanged.connect(
|
||||
lambda value, b=button: self._update_icon_frame(b, value)
|
||||
)
|
||||
animation.finished.connect(
|
||||
lambda b=button: self._on_fade_animation_finished(b)
|
||||
)
|
||||
anim_data['animation'] = animation
|
||||
|
||||
# Останавливаем предыдущую анимацию, если она еще идет
|
||||
if anim_data.get('fade_timer') and anim_data['fade_timer'].isActive():
|
||||
anim_data['fade_timer'].stop()
|
||||
if animation.state() == QPropertyAnimation.Running:
|
||||
animation.stop()
|
||||
|
||||
# Определяем текущую и следующую иконки
|
||||
current_icon_path = anim_data['icons'][anim_data['current_index']]
|
||||
next_icon_index = (anim_data['current_index'] + 1) % len(anim_data['icons'])
|
||||
next_icon_path = anim_data['icons'][next_icon_index]
|
||||
|
||||
current_pixmap = QPixmap(current_icon_path)
|
||||
next_pixmap = QPixmap(next_icon_path)
|
||||
# Сохраняем QPixmap для использования в функции обновления кадра
|
||||
anim_data['pixmaps'] = (QPixmap(current_icon_path), QPixmap(next_icon_path))
|
||||
|
||||
# Запускаем таймер для покадровой анимации
|
||||
anim_data['fade_step'] = 0
|
||||
fade_timer = QTimer(button)
|
||||
anim_data['fade_timer'] = fade_timer
|
||||
fade_timer.timeout.connect(
|
||||
lambda b=button, p1=current_pixmap, p2=next_pixmap: self._update_fade_frame(b, p1, p2)
|
||||
)
|
||||
fade_timer.start(16) # ~60 кадров в секунду
|
||||
# Устанавливаем начальное и конечное значения и запускаем
|
||||
animation.setStartValue(0.0)
|
||||
animation.setEndValue(1.0)
|
||||
animation.start() # Без DeleteWhenStopped
|
||||
|
||||
def _update_fade_frame(self, button, old_pixmap, new_pixmap):
|
||||
"""Обновляет кадр анимации перехода иконок."""
|
||||
if button not in self.icon_animators:
|
||||
def _on_fade_animation_finished(self, button):
|
||||
"""Вызывается по завершении анимации для обновления индекса иконки."""
|
||||
if button in self.icon_animators:
|
||||
anim_data = self.icon_animators[button]
|
||||
anim_data['current_index'] = (anim_data['current_index'] + 1) % len(anim_data['icons'])
|
||||
|
||||
def _update_icon_frame(self, button, progress):
|
||||
"""Обновляет кадр анимации, смешивая две иконки в зависимости от прогресса."""
|
||||
anim_data = self.icon_animators.get(button)
|
||||
if not anim_data or 'pixmaps' not in anim_data:
|
||||
return
|
||||
|
||||
anim_data = self.icon_animators[button]
|
||||
# Длительность анимации: 44 шага * 16 мс ~= 700 мс
|
||||
FADE_DURATION_STEPS = 44
|
||||
old_pixmap, new_pixmap = anim_data['pixmaps']
|
||||
|
||||
anim_data['fade_step'] += 1
|
||||
progress_linear = anim_data['fade_step'] / FADE_DURATION_STEPS
|
||||
# Применяем функцию сглаживания для более плавного старта и завершения
|
||||
progress = self._ease_in_out_quad(progress_linear)
|
||||
|
||||
if progress_linear >= 1.0:
|
||||
anim_data['fade_timer'].stop()
|
||||
anim_data['current_index'] = (anim_data['current_index'] + 1) % len(anim_data['icons'])
|
||||
# На последнем кадре просто устанавливаем новую иконку
|
||||
if progress >= 1.0:
|
||||
button.setIcon(QIcon(new_pixmap))
|
||||
return
|
||||
|
||||
# Создаем холст для отрисовки смешанной иконки
|
||||
canvas = QPixmap(button.iconSize())
|
||||
canvas.fill(Qt.transparent)
|
||||
painter = QPainter(canvas)
|
||||
painter.setRenderHint(QPainter.Antialiasing, True)
|
||||
painter.setRenderHint(QPainter.SmoothPixmapTransform, True)
|
||||
|
||||
# Плавно скрываем старую иконку
|
||||
painter.setOpacity(1.0 - progress)
|
||||
painter.drawPixmap(canvas.rect(), old_pixmap)
|
||||
|
||||
# Плавно проявляем новую иконку
|
||||
painter.setOpacity(progress)
|
||||
painter.drawPixmap(canvas.rect(), new_pixmap)
|
||||
painter.end()
|
||||
|
Reference in New Issue
Block a user