fix: move PySide6 imports to lazy in controllers for CI compat

CI installs httpx + Pillow + pytest but not PySide6. The Phase C
tests import pure functions from controller modules, which had
top-level PySide6 imports (QTimer, QPixmap, QApplication, QMessageBox).
Move these to lazy imports inside the methods that need them so the
module-level pure functions remain importable without Qt.
This commit is contained in:
pax 2026-04-10 15:39:50 -05:00
parent f9977b61e6
commit de6961da37
4 changed files with 9 additions and 10 deletions

View File

@ -7,8 +7,6 @@ import logging
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from PySide6.QtGui import QPixmap
from ..core.cache import download_image, cache_size_bytes, evict_oldest, evict_oldest_thumbnails from ..core.cache import download_image, cache_size_bytes, evict_oldest, evict_oldest_thumbnails
if TYPE_CHECKING: if TYPE_CHECKING:
@ -265,6 +263,7 @@ class MediaController:
if _is_video(path): if _is_video(path):
return 0, 0 return 0, 0
try: try:
from PySide6.QtGui import QPixmap
pix = QPixmap(path) pix = QPixmap(path)
if not pix.isNull(): if not pix.isNull():
return pix.width(), pix.height() return pix.width(), pix.height()

View File

@ -6,8 +6,6 @@ import logging
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from PySide6.QtWidgets import QMessageBox
from ..core.cache import download_image from ..core.cache import download_image
if TYPE_CHECKING: if TYPE_CHECKING:
@ -163,6 +161,7 @@ class PostActionsController:
self._app._popout_ctrl.update_state() self._app._popout_ctrl.update_state()
def blacklist_tag_from_popout(self, tag: str) -> None: def blacklist_tag_from_popout(self, tag: str) -> None:
from PySide6.QtWidgets import QMessageBox
reply = QMessageBox.question( reply = QMessageBox.question(
self._app, "Blacklist Tag", self._app, "Blacklist Tag",
f"Blacklist tag \"{tag}\"?\nPosts with this tag will be hidden.", f"Blacklist tag \"{tag}\"?\nPosts with this tag will be hidden.",
@ -178,6 +177,7 @@ class PostActionsController:
def blacklist_post_from_popout(self) -> None: def blacklist_post_from_popout(self) -> None:
post, idx = self.get_preview_post() post, idx = self.get_preview_post()
if post: if post:
from PySide6.QtWidgets import QMessageBox
reply = QMessageBox.question( reply = QMessageBox.question(
self._app, "Blacklist Post", self._app, "Blacklist Post",
f"Blacklist post #{post.id}?\nThis post will be hidden from results.", f"Blacklist post #{post.id}?\nThis post will be hidden from results.",

View File

@ -6,10 +6,6 @@ import asyncio
import logging import logging
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from PySide6.QtCore import QTimer
from PySide6.QtGui import QPixmap
from PySide6.QtWidgets import QApplication
from .search_state import SearchState from .search_state import SearchState
if TYPE_CHECKING: if TYPE_CHECKING:
@ -293,6 +289,7 @@ class SearchController:
self._app._next_page_btn.setVisible(not at_end) self._app._next_page_btn.setVisible(not at_end)
thumbs = self._app._grid.set_posts(len(posts)) thumbs = self._app._grid.set_posts(len(posts))
self._app._grid.scroll_to_top() self._app._grid.scroll_to_top()
from PySide6.QtCore import QTimer
QTimer.singleShot(100, self.clear_loading) QTimer.singleShot(100, self.clear_loading)
from ..core.config import saved_dir from ..core.config import saved_dir
@ -410,6 +407,7 @@ class SearchController:
def on_scroll_range_changed(self, _min: int, max_val: int) -> None: def on_scroll_range_changed(self, _min: int, max_val: int) -> None:
"""Scrollbar range changed (resize/splitter) -- check if viewport needs filling.""" """Scrollbar range changed (resize/splitter) -- check if viewport needs filling."""
if max_val == 0 and self._infinite_scroll and self._app._posts: if max_val == 0 and self._infinite_scroll and self._app._posts:
from PySide6.QtCore import QTimer
QTimer.singleShot(100, self.check_viewport_fill) QTimer.singleShot(100, self.check_viewport_fill)
def check_viewport_fill(self) -> None: def check_viewport_fill(self) -> None:
@ -417,6 +415,7 @@ class SearchController:
if not self._infinite_scroll or self._loading or self._search.infinite_exhausted: if not self._infinite_scroll or self._loading or self._search.infinite_exhausted:
return return
self._app._grid.widget().updateGeometry() self._app._grid.widget().updateGeometry()
from PySide6.QtWidgets import QApplication
QApplication.processEvents() QApplication.processEvents()
sb = self._app._grid.verticalScrollBar() sb = self._app._grid.verticalScrollBar()
if sb.maximum() == 0 and self._app._posts: if sb.maximum() == 0 and self._app._posts:
@ -434,6 +433,7 @@ class SearchController:
ss.infinite_exhausted = True ss.infinite_exhausted = True
self._app._status.showMessage(f"{len(self._app._posts)} results (end)") self._app._status.showMessage(f"{len(self._app._posts)} results (end)")
else: else:
from PySide6.QtCore import QTimer
QTimer.singleShot(100, self.check_viewport_fill) QTimer.singleShot(100, self.check_viewport_fill)
return return
if ss.infinite_last_page > self._current_page: if ss.infinite_last_page > self._current_page:
@ -501,6 +501,7 @@ class SearchController:
self._app._run_async(_download) self._app._run_async(_download)
def on_thumb_done(self, index: int, path: str) -> None: def on_thumb_done(self, index: int, path: str) -> None:
from PySide6.QtGui import QPixmap
thumbs = self._app._grid._thumbs thumbs = self._app._grid._thumbs
if 0 <= index < len(thumbs): if 0 <= index < len(thumbs):
pix = QPixmap(path) pix = QPixmap(path)

View File

@ -8,8 +8,6 @@ import os
import subprocess import subprocess
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from PySide6.QtCore import QTimer
if TYPE_CHECKING: if TYPE_CHECKING:
from .main_window import BooruApp from .main_window import BooruApp
@ -252,6 +250,7 @@ class WindowStateController:
return return
# Slight delay so the window is registered before we try to find # Slight delay so the window is registered before we try to find
# its address. The popout uses the same pattern. # its address. The popout uses the same pattern.
from PySide6.QtCore import QTimer
QTimer.singleShot( QTimer.singleShot(
50, lambda: self.hyprctl_apply_main_state(x, y, w, h, was_floating) 50, lambda: self.hyprctl_apply_main_state(x, y, w, h, was_floating)
) )