fix: renderer type fixes (#1114)

* fix: type fixes

* fix: address review feedback
This commit is contained in:
Jann Stute
2025-09-11 18:39:32 +02:00
committed by GitHub
parent c1599c98f1
commit c6f66973a4
4 changed files with 61 additions and 53 deletions

View File

@@ -5,7 +5,7 @@
import typing
from pathlib import Path
from time import gmtime
from typing import override
from typing import cast, override
import structlog
from PIL import Image, ImageDraw
@@ -411,11 +411,11 @@ class MediaPlayer(QGraphicsView):
self.player.play()
def load_toggle_play_icon(self, playing: bool) -> None:
icon = self.driver.rm.pause_icon if playing else self.driver.rm.play_icon
icon = cast(bytes, self.driver.rm.pause_icon if playing else self.driver.rm.play_icon)
self.play_pause.load(icon)
def load_mute_unmute_icon(self, muted: bool) -> None:
icon = self.driver.rm.volume_mute_icon if muted else self.driver.rm.volume_icon
icon = cast(bytes, self.driver.rm.volume_mute_icon if muted else self.driver.rm.volume_icon)
self.mute_unmute.load(icon)
def slider_value_changed(self, value: int) -> None:

View File

@@ -5,6 +5,7 @@
import traceback
from pathlib import Path
from typing import cast
import structlog
from PySide6.QtCore import QObject, Qt, QThreadPool, Signal
@@ -61,8 +62,8 @@ class JsonMigrationModal(QObject):
self.path: Path = path
self.stack: list[PagedPanelState] = []
self.json_lib: JsonLibrary = None
self.sql_lib: SqliteLibrary = None
self.json_lib: JsonLibrary = None # pyright: ignore[reportAttributeAccessIssue]
self.sql_lib: SqliteLibrary = None # pyright: ignore[reportAttributeAccessIssue]
self.is_migration_initialized: bool = False
self.discrepancies: list[str] = []
@@ -72,7 +73,7 @@ class JsonMigrationModal(QObject):
self.old_entry_count: int = 0
self.old_tag_count: int = 0
self.old_ext_count: int = 0
self.old_ext_type: bool = None
self.old_ext_type: bool = None # pyright: ignore[reportAttributeAccessIssue]
self.field_parity: bool = False
self.path_parity: bool = False
@@ -367,7 +368,7 @@ class JsonMigrationModal(QObject):
minimum=0,
maximum=0,
)
pb.setCancelButton(None)
pb.setCancelButton(None) # pyright: ignore[reportArgumentType]
self.body_wrapper_01.layout().addWidget(pb)
try:
@@ -390,7 +391,7 @@ class JsonMigrationModal(QObject):
pb.setMinimum(1), # type: ignore
pb.setValue(1), # type: ignore
# Enable the finish button
self.stack[1].buttons[4].setDisabled(False),
cast(QPushButtonWrapper, self.stack[1].buttons[4]).setDisabled(False),
)
)
QThreadPool.globalInstance().start(r)
@@ -474,7 +475,7 @@ class JsonMigrationModal(QObject):
)
self.update_sql_value(
self.ext_type_row,
self.sql_lib.prefs(LibraryPrefs.IS_EXCLUDE_LIST),
self.sql_lib.prefs(LibraryPrefs.IS_EXCLUDE_LIST), # pyright: ignore[reportArgumentType]
self.old_ext_type,
)
logger.info("Parity check complete!")
@@ -636,18 +637,15 @@ class JsonMigrationModal(QObject):
def check_subtag_parity(self) -> bool:
"""Check if all JSON parent tags match the new SQL parent tags."""
sql_parent_tags: set[int] = None
json_parent_tags: set[int] = None
with Session(self.sql_lib.engine) as session:
for tag in self.sql_lib.tags:
tag_id = tag.id # Tag IDs start at 0
sql_parent_tags = set(
sql_parent_tags: set[int] = set(
session.scalars(select(TagParent.parent_id).where(TagParent.child_id == tag.id))
)
# JSON tags allowed self-parenting; SQL tags no longer allow this.
json_parent_tags = set(self.json_lib.get_tag(tag_id).subtag_ids)
json_parent_tags: set[int] = set(self.json_lib.get_tag(tag_id).subtag_ids)
json_parent_tags.discard(tag_id)
logger.info(
@@ -677,16 +675,15 @@ class JsonMigrationModal(QObject):
def check_alias_parity(self) -> bool:
"""Check if all JSON aliases match the new SQL aliases."""
sql_aliases: set[str] = None
json_aliases: set[str] = None
with Session(self.sql_lib.engine) as session:
for tag in self.sql_lib.tags:
tag_id = tag.id # Tag IDs start at 0
sql_aliases = set(
sql_aliases: set[str] = set(
session.scalars(select(TagAlias.name).where(TagAlias.tag_id == tag.id))
)
json_aliases = set([x for x in self.json_lib.get_tag(tag_id).aliases if x])
json_aliases: set[str] = set(
[x for x in self.json_lib.get_tag(tag_id).aliases if x]
)
logger.info(
"[Alias Parity]",

View File

@@ -5,10 +5,10 @@
"""A pagination widget created for TagStudio."""
from typing import override
from typing import cast, override
from PIL import Image, ImageQt
from PySide6.QtCore import QObject, QSize, Signal
from PySide6.QtCore import QSize, Signal
from PySide6.QtGui import QIntValidator, QPixmap
from PySide6.QtWidgets import QHBoxLayout, QLabel, QLineEdit, QSizePolicy, QWidget
@@ -17,7 +17,7 @@ from tagstudio.qt.resource_manager import ResourceManager
from tagstudio.qt.views.qbutton_wrapper import QPushButtonWrapper
class Pagination(QWidget, QObject):
class Pagination(QWidget):
"""Widget containing controls for navigating between pages of items."""
index = Signal(int)
@@ -160,8 +160,8 @@ class Pagination(QWidget, QObject):
srt_scale = max(1, (7 - (end_page - index)))
if page_count >= 8:
end_size = self.button_size.width() * end_scale + (3 * (end_scale - 1))
srt_size = self.button_size.width() * srt_scale + (3 * (srt_scale - 1))
end_size = self.button_size.width() * end_scale + (3 * (end_scale - 1)) # pyright: ignore[reportPossiblyUnboundVariable]
srt_size = self.button_size.width() * srt_scale + (3 * (srt_scale - 1)) # pyright: ignore[reportPossiblyUnboundVariable]
self.end_ellipses.setMinimumWidth(end_size)
self.end_ellipses.setMaximumWidth(end_size)
self.start_ellipses.setMinimumWidth(srt_size)
@@ -223,7 +223,10 @@ class Pagination(QWidget, QObject):
str(i + 1)
)
self._assign_click(
self.start_buffer_layout.itemAt(i - start_offset).widget(),
cast(
QPushButtonWrapper,
self.start_buffer_layout.itemAt(i - start_offset).widget(),
),
i,
)
sbc += 1
@@ -239,7 +242,10 @@ class Pagination(QWidget, QObject):
str(i + 1)
)
self._assign_click(
self.end_buffer_layout.itemAt(i - end_offset).widget(),
cast(
QPushButtonWrapper,
self.end_buffer_layout.itemAt(i - end_offset).widget(),
),
i,
)
else:

View File

@@ -21,12 +21,14 @@ import cv2
import numpy as np
import pillow_avif # noqa: F401 # pyright: ignore[reportUnusedImport]
import py7zr
import py7zr.io
import rarfile
import rawpy
import srctools
import structlog
from cv2.typing import MatLike
from mutagen import MutagenError, flac, id3, mp4
from mutagen import flac, id3, mp4
from mutagen._util import MutagenError
from PIL import (
Image,
ImageChops,
@@ -119,7 +121,7 @@ class _TarFile(tarfile.TarFile):
return self.getnames()
def read(self, name: str) -> bytes:
return self.extractfile(name).read()
return unwrap(self.extractfile(name)).read()
type _Archive_T = (
@@ -435,9 +437,9 @@ class ThumbRenderer(QObject):
)
# Get icon by name
icon: Image.Image | None = self.rm.get(name)
icon: Image.Image | None = self.rm.get(name) # pyright: ignore[reportAssignmentType]
if not icon:
icon = self.rm.get("file_generic")
icon = self.rm.get("file_generic") # pyright: ignore[reportAssignmentType]
if not icon:
icon = Image.new(mode="RGBA", size=(32, 32), color="magenta")
@@ -533,9 +535,9 @@ class ThumbRenderer(QObject):
)
# Get icon by name
icon: Image.Image | None = self.rm.get(name)
icon: Image.Image | None = self.rm.get(name) # pyright: ignore[reportAssignmentType]
if not icon:
icon = self.rm.get("file_generic")
icon = self.rm.get("file_generic") # pyright: ignore[reportAssignmentType]
if not icon:
icon = Image.new(mode="RGBA", size=(32, 32), color="magenta")
@@ -664,14 +666,14 @@ class ThumbRenderer(QObject):
artwork = Image.open(BytesIO(flac_covers[0].data))
elif ext in [".mp4", ".m4a", ".aac"]:
mp4_tags: mp4.MP4 = mp4.MP4(filepath)
mp4_covers: list = unwrap(mp4_tags.get("covr"))
mp4_covers: list | None = mp4_tags.get("covr") # pyright: ignore[reportAssignmentType]
if mp4_covers:
artwork = Image.open(BytesIO(mp4_covers[0]))
if artwork:
image = artwork
except (
FileNotFoundError,
id3.ID3NoHeaderError,
id3.ID3NoHeaderError, # pyright: ignore[reportPrivateImportUsage]
mp4.MP4MetadataError,
mp4.MP4StreamInfoError,
MutagenError,
@@ -768,7 +770,7 @@ class ThumbRenderer(QObject):
return im
@staticmethod
def _blender(filepath: Path) -> Image.Image:
def _blender(filepath: Path) -> Image.Image | None:
"""Get an emended thumbnail from a Blender file, if a thumbnail is present.
Args:
@@ -956,21 +958,21 @@ class ThumbRenderer(QObject):
cover = comic_info.find(f"./*Page[@Type='{cover_type}']")
if cover is not None:
pages = [f for f in archive.namelist() if f != "ComicInfo.xml"]
page_name = pages[int(cover.get("Image"))]
page_name = pages[int(unwrap(cover.get("Image")))]
if page_name.endswith((".png", ".jpg", ".jpeg", ".gif", ".bmp", ".svg")):
image_data = archive.read(page_name)
im = Image.open(BytesIO(image_data))
return im
def _font_short_thumb(self, filepath: Path, size: int) -> Image.Image:
def _font_short_thumb(self, filepath: Path, size: int) -> Image.Image | None:
"""Render a small font preview ("Aa") thumbnail from a font file.
Args:
filepath (Path): The path of the file.
size (tuple[int,int]): The size of the thumbnail.
"""
im: Image.Image = None
im: Image.Image | None = None
try:
bg = Image.new("RGB", (size, size), color="#000000")
raw = Image.new("RGB", (size * 3, size * 3), color="#000000")
@@ -1024,7 +1026,7 @@ class ThumbRenderer(QObject):
return im
@staticmethod
def _font_long_thumb(filepath: Path, size: int) -> Image.Image:
def _font_long_thumb(filepath: Path, size: int) -> Image.Image | None:
"""Render a large font preview ("Alphabet") thumbnail from a font file.
Args:
@@ -1033,7 +1035,7 @@ class ThumbRenderer(QObject):
"""
# Scale the sample font sizes to the preview image
# resolution,assuming the sizes are tuned for 256px.
im: Image.Image = None
im: Image.Image | None = None
try:
scaled_sizes: list[int] = [math.floor(x * (size / 256)) for x in FONT_SAMPLE_SIZES]
bg = Image.new("RGBA", (size, size), color="#00000000")
@@ -1044,7 +1046,10 @@ class ThumbRenderer(QObject):
for font_size in scaled_sizes:
font = ImageFont.truetype(filepath, size=font_size)
text_wrapped: str = wrap_full_text(
FONT_SAMPLE_TEXT, font=font, width=size, draw=draw
FONT_SAMPLE_TEXT,
font=font, # pyright: ignore[reportArgumentType]
width=size,
draw=draw,
)
draw.multiline_text((0, y_offset), text_wrapped, font=font)
y_offset += (len(text_wrapped.split("\n")) + lines_of_padding) * draw.textbbox(
@@ -1056,13 +1061,13 @@ class ThumbRenderer(QObject):
return im
@staticmethod
def _image_raw_thumb(filepath: Path) -> Image.Image:
def _image_raw_thumb(filepath: Path) -> Image.Image | None:
"""Render a thumbnail for a RAW image type.
Args:
filepath (Path): The path of the file.
"""
im: Image.Image = None
im: Image.Image | None = None
try:
with rawpy.imread(str(filepath)) as raw:
rgb = raw.postprocess(use_camera_wb=True)
@@ -1074,8 +1079,8 @@ class ThumbRenderer(QObject):
)
except (
DecompressionBombError,
rawpy._rawpy.LibRawIOError,
rawpy._rawpy.LibRawFileUnsupportedError,
rawpy._rawpy.LibRawIOError, # pyright: ignore[reportAttributeAccessIssue]
rawpy._rawpy.LibRawFileUnsupportedError, # pyright: ignore[reportAttributeAccessIssue]
) as e:
logger.error("Couldn't render thumbnail", filepath=filepath, error=type(e).__name__)
return im
@@ -1111,13 +1116,13 @@ class ThumbRenderer(QObject):
return im
@staticmethod
def _image_thumb(filepath: Path) -> Image.Image:
def _image_thumb(filepath: Path) -> Image.Image | None:
"""Render a thumbnail for a standard image type.
Args:
filepath (Path): The path of the file.
"""
im: Image.Image = None
im: Image.Image | None = None
try:
im = Image.open(filepath)
if im.mode != "RGB" and im.mode != "RGBA":
@@ -1144,7 +1149,7 @@ class ThumbRenderer(QObject):
filepath (Path): The path of the file.
size (tuple[int,int]): The size of the thumbnail.
"""
im: Image.Image = None
im: Image.Image | None = None
# Create an image to draw the svg to and a painter to do the drawing
q_image: QImage = QImage(size, size, QImage.Format.Format_ARGB32)
q_image.fill("#1e1e1e")
@@ -1174,7 +1179,7 @@ class ThumbRenderer(QObject):
return im
@staticmethod
def _iwork_thumb(filepath: Path) -> Image.Image:
def _iwork_thumb(filepath: Path) -> Image.Image | None:
"""Extract and render a thumbnail for an Apple iWork (Pages, Numbers, Keynote) file.
Args:
@@ -1212,7 +1217,7 @@ class ThumbRenderer(QObject):
return im
@staticmethod
def _model_stl_thumb(filepath: Path, size: int) -> Image.Image:
def _model_stl_thumb(filepath: Path, size: int) -> Image.Image | None:
"""Render a thumbnail for an STL file.
Args:
@@ -1223,7 +1228,7 @@ class ThumbRenderer(QObject):
# The following commented code describes a method for rendering via
# matplotlib.
# This implementation did not play nice with multithreading.
im: Image.Image = None
im: Image.Image | None = None
# # Create a new plot
# matplotlib.use('agg')
# figure = plt.figure()
@@ -1245,13 +1250,13 @@ class ThumbRenderer(QObject):
return im
@staticmethod
def _pdf_thumb(filepath: Path, size: int) -> Image.Image:
def _pdf_thumb(filepath: Path, size: int) -> Image.Image | None:
"""Render a thumbnail for a PDF file.
filepath (Path): The path of the file.
size (int): The size of the icon.
"""
im: Image.Image = None
im: Image.Image | None = None
file: QFile = QFile(filepath)
success: bool = file.open(