zapretgui/tests/test_combo_popup_guard.py
loop-uh fa7fb76f3f
All checks were successful
Zapret source guards / validate (push) Successful in 9s
Windows-сборка / Dev Dev 21.1.7.92 опубликован
Сборка страниц ещё на треть быстрее: убраны перехватчики событий на каждом элементе, «Оформление» достраивает нижние разделы позже
Продолжение блочной сборки (8bfc2dd3).

- Стили qfluentwidgets: у виджета со стилем больше нет двух перехватчиков
  событий библиотеки (ждали смены «своего стиля» и «ленивой» смены темы).
  Это было два вызова функции на Python на каждое событие каждого такого
  виджета. «Свой стиль» применяется там, где его задают; смена темы
  обновляет стили сразу (ленивой сменой программа не пользовалась).
- Закрытие выпадающих списков при потере активности слушает один сигнал
  приложения вместо перехватчика, через который шло каждое событие каждого
  объекта.
- Имя и состояние элемента для экранного диктора: служебные дочерние кнопки
  убираются из Tab-порядка за один обход детей вместо четырёх (на главной
  335 обходов дерева при сборке → 91).
- «Оформление»: новогоднее оформление, прозрачность, акцентный цвет и
  производительность — блок ниже первого экрана (ui.block_build). При
  создании страницы строится 52 элемента вместо 135. Блок, собранный позже,
  перечитывает настройки: прозрачность меняют и из меню в трее.
- У заглушки блока и слоя появления убрана лишняя установка фокуса
  (значение по умолчанию), из-за неё оба файла попадали под проверку
  доступности.

Замер на хосте против 21.1.7.85, семь страниц: сборка 255 → 171 мс, показ
75 → 54 мс, открытие рядом с занятым фоновым потоком 5644 → 3478 мс
(«Оформление» 989 → 125 мс).

Тест test_appearance_page_builds_all_sections_in_initial_pass заменён:
решение владельца от 2026-10-08 — не строить сразу то, чего не видно.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 16:31:34 +03:00

58 lines
2.3 KiB
Python

"""Выпадающие списки закрываются, когда программа теряет активность."""
from __future__ import annotations
import os
import unittest
from unittest import mock
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
from PyQt6.QtCore import QEvent, Qt
from PyQt6.QtWidgets import QApplication, QLabel
from ui.combo_popup_guard import GlobalComboPopupCloser
class ComboPopupGuardTests(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls._app = QApplication.instance() or QApplication([])
def _closer(self) -> GlobalComboPopupCloser:
closer = GlobalComboPopupCloser(self._app)
self.addCleanup(closer.deleteLater)
self.addCleanup(closer.cleanup)
return closer
def test_popups_close_when_the_application_becomes_inactive(self) -> None:
closer = self._closer()
with mock.patch.object(closer, "close_all_popups") as close:
self._app.applicationStateChanged.emit(Qt.ApplicationState.ApplicationInactive)
close.assert_called_once()
close.reset_mock()
self._app.applicationStateChanged.emit(Qt.ApplicationState.ApplicationActive)
close.assert_not_called()
def test_closer_does_not_watch_every_event_of_the_application(self) -> None:
# Раньше это был перехватчик на всё приложение: через функцию на Python
# проходило каждое событие каждого объекта.
closer = self._closer()
self.assertNotIn("eventFilter", vars(GlobalComboPopupCloser))
with mock.patch.object(closer, "close_all_popups") as close:
label = QLabel("текст")
self.addCleanup(label.deleteLater)
QApplication.sendEvent(label, QEvent(QEvent.Type.ApplicationDeactivate))
close.assert_not_called()
def test_cleanup_stops_listening(self) -> None:
closer = GlobalComboPopupCloser(self._app)
self.addCleanup(closer.deleteLater)
closer.cleanup()
with mock.patch.object(closer, "close_all_popups") as close:
self._app.applicationStateChanged.emit(Qt.ApplicationState.ApplicationInactive)
close.assert_not_called()
if __name__ == "__main__":
unittest.main()