Каждая постоянная анимация вела свой таймер: значок «Работает» в заголовке (62 кадра в секунду), точка и сцена статуса на главной, блик Premium. Их кадры шли вразнобой, и на каждый Qt отдельно перерисовывал окно и отдельно отдавал его на экран, а без видеокарты кадр собирал ещё и dwm.exe. Теперь один общий такт (ui/frame_clock.py): кадры всех анимаций приходят в одном обороте цикла событий и сливаются в одну перерисовку окна. Стенд главной (значок + сцена обхода): 87 перерисовок окна в секунду -> 30, 8,3% ядра -> около 4%. - Значок «Работает» и точка статуса — 30 кадров в секунду, фаза по времени. Кадры значка совпадают со старыми до пикселя (сравнено 96 кадров: фазы, наведение, темы). Цвета значка считаются один раз на фазу и тему. - Блик Premium оставлен на 60 кадрах, но тоже на общем такте. - Такт на точном таймере: на Windows обычный таймер на 33 мс давал 20 кадров рывками (интервалы 42–63 мс), точный — ровные 30 (замер на win10, Qt 6.11). - Сеанс заблокирован или отключён, дисплей выключен — такт стоит (WM_WTSSESSION_CHANGE, GUID_CONSOLE_DISPLAY_STATE); активация окна снимает паузу на случай потерянного уведомления. - nativeEvent разбирает структуру сообщения Windows один раз и зовёт обработчики только для своих номеров, а не на каждое движение мыши.
76 lines
3 KiB
Python
76 lines
3 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
import unittest
|
|
from unittest import mock
|
|
|
|
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
|
|
|
from PyQt6.QtWidgets import QApplication, QHBoxLayout, QWidget
|
|
|
|
import ui.subscription_title_badge as badge_module
|
|
from donater.premium_display import TIER_ACTIVE, TIER_FREE, PremiumDisplay
|
|
from ui.subscription_title_badge import SubscriptionTitleBadge
|
|
|
|
|
|
class SubscriptionBadgeShineTests(unittest.TestCase):
|
|
@classmethod
|
|
def setUpClass(cls) -> None:
|
|
cls._app = QApplication.instance() or QApplication([])
|
|
|
|
def setUp(self) -> None:
|
|
patcher = mock.patch.object(badge_module, "are_live_animations_enabled", return_value=True)
|
|
patcher.start()
|
|
self.addCleanup(patcher.stop)
|
|
self.host = QWidget()
|
|
self.addCleanup(self.host.deleteLater)
|
|
self.badge = SubscriptionTitleBadge(self.host, language_provider=lambda: "ru")
|
|
QHBoxLayout(self.host).addWidget(self.badge)
|
|
self.host.show()
|
|
|
|
def test_premium_badge_schedules_shine_and_repeats(self) -> None:
|
|
self.badge.set_display(PremiumDisplay(tier=TIER_ACTIVE, days=37))
|
|
QApplication.processEvents()
|
|
self.assertTrue(self.badge._shine_timer.isActive())
|
|
|
|
self.badge.play_shine()
|
|
self.assertTrue(self.badge.is_shining())
|
|
# Кадр на общем такте после конца вспышки завершает её.
|
|
with mock.patch.object(
|
|
self.badge._shine, "elapsed_ms", return_value=badge_module.PREMIUM_SHINE_DURATION_MS
|
|
):
|
|
self.badge._on_shine_frame()
|
|
self.assertFalse(self.badge.is_shining())
|
|
self.assertEqual(self.badge._shine_t, 0.0)
|
|
# После вспышки ждёт следующую только одиночный таймер.
|
|
self.assertTrue(self.badge._shine_timer.isActive())
|
|
self.assertEqual(self.badge._shine_timer.interval(), badge_module.PREMIUM_SHINE_INTERVAL_MS)
|
|
|
|
def test_free_badge_never_shines(self) -> None:
|
|
self.badge.set_display(PremiumDisplay(tier=TIER_FREE))
|
|
QApplication.processEvents()
|
|
self.assertFalse(self.badge._shine_timer.isActive())
|
|
self.badge.play_shine()
|
|
self.assertFalse(self.badge.is_shining())
|
|
|
|
def test_hidden_badge_stops_shining(self) -> None:
|
|
self.badge.set_display(PremiumDisplay(tier=TIER_ACTIVE, days=37))
|
|
QApplication.processEvents()
|
|
self.badge.play_shine()
|
|
|
|
self.host.hide()
|
|
|
|
self.assertFalse(self.badge.is_shining())
|
|
self.assertFalse(self.badge._shine_timer.isActive())
|
|
|
|
def test_no_shine_when_live_animations_are_off(self) -> None:
|
|
with mock.patch.object(badge_module, "are_live_animations_enabled", return_value=False):
|
|
self.badge.set_display(PremiumDisplay(tier=TIER_ACTIVE, days=37))
|
|
QApplication.processEvents()
|
|
self.badge.play_shine()
|
|
self.assertFalse(self.badge.is_shining())
|
|
self.assertFalse(self.badge._shine_timer.isActive())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|