From ef94c6fdfc23083035ad0dbd92703576fface6e0 Mon Sep 17 00:00:00 2001 From: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com> Date: Mon, 27 Jul 2026 13:06:12 -0700 Subject: [PATCH] refactor: rename ambiguous variables --- src/tagstudio/qt/previews/renderer.py | 179 ++++++++++++-------------- src/tagstudio/qt/qt_file_renderer.py | 10 +- 2 files changed, 85 insertions(+), 104 deletions(-) diff --git a/src/tagstudio/qt/previews/renderer.py b/src/tagstudio/qt/previews/renderer.py index 9857a507..41fb1f45 100644 --- a/src/tagstudio/qt/previews/renderer.py +++ b/src/tagstudio/qt/previews/renderer.py @@ -56,7 +56,7 @@ logger = structlog.get_logger(__name__) class FileRenderer: - """A class for rendering image and file thumbnails.""" + """A class for rendering image previews and thumbnails from files.""" rm: ResourceManager = ResourceManager() cached_img_ext: str = ".webp" @@ -107,6 +107,7 @@ class FileRenderer: return "file_generic" + # NOTE: This method will be replaced with frontend specific decorations (Qt painting) def _get_mask( self, size: tuple[int, int], pixel_ratio: float, scale_radius: bool = False ) -> Image.Image: @@ -130,6 +131,7 @@ class FileRenderer: self.thumb_masks[(*size, pixel_ratio, radius_scale)] = item return item + # NOTE: This method will be replaced with frontend specific decorations (Qt painting) def _get_edge( self, size: tuple[int, int], pixel_ratio: float ) -> tuple[Image.Image, Image.Image]: @@ -153,7 +155,7 @@ class FileRenderer: color: UiColor, size: tuple[int, int], theme: Theme, - pixel_ratio: float = 1.0, + dpi_scale: float = 1.0, bg_image: Image.Image | None = None, draw_edge: bool = True, is_corner: bool = False, @@ -165,7 +167,7 @@ class FileRenderer: color (str): The color to use for the icon. size (tuple[int,int]): The size of the icon. theme (Theme): A theme enum to determine the light/dark theme. - pixel_ratio (float): The screen pixel ratio. + dpi_scale (float): The screen pixel ratio. bg_image (Image.Image): Optional background image to go behind the icon. draw_edge (bool): Flag for is the raised edge should be drawn. is_corner (bool): Flag for is the icon should render with the "corner" style @@ -174,23 +176,24 @@ class FileRenderer: if name == "thumb_loading": draw_border = False - item: Image.Image | None = self.icons.get((name, color, *size, pixel_ratio)) + item: Image.Image | None = self.icons.get((name, color, *size, dpi_scale)) if not item: item_flat: Image.Image = ( - self._render_corner_icon(name, color, size, pixel_ratio, theme, bg_image) + self._render_corner_icon(name, color, size, dpi_scale, theme, bg_image) if is_corner else self._render_center_icon( - name, color, size, pixel_ratio, theme, draw_border, bg_image + name, color, size, dpi_scale, theme, draw_border, bg_image ) ) if draw_edge: - edge: tuple[Image.Image, Image.Image] = self._get_edge(size, pixel_ratio) + edge: tuple[Image.Image, Image.Image] = self._get_edge(size, dpi_scale) item = self._apply_edge(item_flat, edge, theme, faded=True) - self.icons[(name, color, *size, pixel_ratio)] = item + self.icons[(name, color, *size, dpi_scale)] = item else: item = item_flat return item + # NOTE: This method will be replaced with frontend specific decorations (Qt painting) def _render_mask( self, size: tuple[int, int], pixel_ratio: float, radius_scale: float = 1 ) -> Image.Image: @@ -221,6 +224,7 @@ class FileRenderer: ) return im + # NOTE: This method will be replaced with frontend specific decorations (Qt painting) def _render_edge( self, size: tuple[int, int], pixel_ratio: float ) -> tuple[Image.Image, Image.Image]: @@ -248,10 +252,7 @@ class FileRenderer: outline="white", width=width, ) - im_hl = im_hl.resize( - size, - resample=Image.Resampling.BILINEAR, - ) + im_hl = im_hl.resize(size, resample=Image.Resampling.BILINEAR) # Shadow im_sh: Image.Image = Image.new( @@ -267,10 +268,7 @@ class FileRenderer: outline="black", width=width, ) - im_sh = im_sh.resize( - size, - resample=Image.Resampling.BILINEAR, - ) + im_sh = im_sh.resize(size, resample=Image.Resampling.BILINEAR) return (im_hl, im_sh) @@ -347,10 +345,7 @@ class FileRenderer: ) # Resize image to final size - im = im.resize( - size, - resample=Image.Resampling.BILINEAR, - ) + im = im.resize(size, resample=Image.Resampling.BILINEAR) fg: Image.Image = Image.new("RGB", size=size, color="#00FF00") # Get icon by name @@ -436,15 +431,8 @@ class FileRenderer: primary_color = colors.get(ColorType.PRIMARY) # Resize image to final size - im = im.resize( - size, - resample=Image.Resampling.BILINEAR, - ) - fg: Image.Image = Image.new( - "RGB", - size=size, - color=primary_color, - ) + im = im.resize(size, resample=Image.Resampling.BILINEAR) + fg: Image.Image = Image.new("RGB", size=size, color=primary_color) # Get icon by name icon = self.rm.get(name) @@ -453,21 +441,11 @@ class FileRenderer: icon = self.rm.file_generic # Resize icon to fit icon_ratio - icon = icon.resize( - ( - math.ceil(size[0] // icon_ratio), - math.ceil(size[1] // icon_ratio), - ) - ) + icon = icon.resize((math.ceil(size[0] // icon_ratio), math.ceil(size[1] // icon_ratio))) # Paste icon im.paste( - im=fg.resize( - ( - math.ceil(size[0] // icon_ratio), - math.ceil(size[1] // icon_ratio), - ) - ), + im=fg.resize((math.ceil(size[0] // icon_ratio), math.ceil(size[1] // icon_ratio))), box=(size[0] // padding_factor, size[1] // padding_factor), mask=icon.getchannel(3), ) @@ -516,6 +494,7 @@ class FileRenderer: return bg + # NOTE: This method will be replaced with frontend specific decorations (Qt painting) def _apply_edge( self, image: Image.Image, @@ -557,11 +536,11 @@ class FileRenderer: cache: CacheManager | None, timestamp: float, filepath: Path | str, - base_size: tuple[int, int], - pixel_ratio: float, + size: tuple[int, int], + dpi_scale: float, theme: Theme = Theme.DARK, is_loading: bool = False, - is_grid_thumb: bool = False, + is_thumb: bool = False, ): """Render a thumbnail or preview image. @@ -569,39 +548,38 @@ class FileRenderer: cache (CacheManager | None): A cache manager instance. timestamp (float): The timestamp for which this job was dispatched. filepath (str | Path): The path of the file to render a thumbnail for. - base_size (tuple[int,int]): The unmodified base size of the thumbnail. - pixel_ratio (float): The screen pixel ratio. + size (tuple[int, int]): The unmodified base size of the thumbnail. + dpi_scale (float): The screen pixel ratio. theme (Theme): A theme enum to determine the light/dark theme. is_loading (bool): Is this a loading graphic? - is_grid_thumb (bool): Is this a thumbnail for the thumbnail grid? - Or else the Preview Pane? + is_thumb (bool): Is this specifically a thumbnail? Use for specifying small variants. update_on_ratio_change (bool): Should an updated ratio signal be sent? """ render_mask_and_edge: bool = True - adj_size = math.ceil(max(base_size[0], base_size[1]) * pixel_ratio) + scaled_size = math.ceil(max(size[0], size[1]) * dpi_scale) theme_color: UiColor = UiColor.THEME_LIGHT if theme == Theme.LIGHT else UiColor.THEME_DARK if isinstance(filepath, str): filepath = Path(filepath) - def render_default(size: tuple[int, int], pixel_ratio: float) -> Image.Image: + def render_default(size: tuple[int, int], dpi_scale: float) -> Image.Image: im = self._get_icon( name=self._get_resource_id(filepath), color=theme_color, size=size, theme=theme, - pixel_ratio=pixel_ratio, + dpi_scale=dpi_scale, ) return im def render_unlinked( - size: tuple[int, int], pixel_ratio: float, cached_im: Image.Image | None = None + size: tuple[int, int], dpi_scale: float, cached_im: Image.Image | None = None ) -> Image.Image: im = self._get_icon( name="broken_link_icon", color=UiColor.RED, size=size, theme=theme, - pixel_ratio=pixel_ratio, + dpi_scale=dpi_scale, bg_image=cached_im, draw_edge=not cached_im, is_corner=False, @@ -614,9 +592,7 @@ class FileRenderer: im_ = im icon: Image.Image = self.rm.ignored - icon = icon.resize((math.ceil(size[0] // icon_ratio), math.ceil(size[1] // icon_ratio))) - im_.paste( im=icon.resize( (math.ceil(size[0] // icon_ratio), math.ceil(size[1] // icon_ratio)) @@ -640,13 +616,13 @@ class FileRenderer: raise UnidentifiedImageError # pyright: ignore[reportUnreachable] except Exception as e: logger.error( - "[ThumbRenderer] Couldn't open cached thumbnail!", path=cached_path, error=e + "[FileRenderer] Couldn't open cached thumbnail!", path=cached_path, error=e ) return image image: Image.Image | None = None # Try to get a non-loading thumbnail for the grid. - if not is_loading and is_grid_thumb and filepath and filepath != Path("."): + if not is_loading and is_thumb and filepath and filepath != Path("."): # Attempt to retrieve cached image from disk mod_time: str = "" with contextlib.suppress(Exception): @@ -668,35 +644,35 @@ class FileRenderer: # TODO: Audio waveforms are dynamically sized based on the base_size, so hardcoding # the resolution breaks that. image = self._render( - cache, - filepath, - (thumb_res, thumb_res), - 1, - theme, - is_grid_thumb, - save_to_file=file_name, + cache=cache, + filepath=filepath, + size=(thumb_res, thumb_res), + dpi_scale=1, + theme=theme, + is_thumb=is_thumb, + cache_filename=file_name, ) # If the normal renderer failed, fallback the defaults # (with native non-cached sizing!) if not image: image = ( - render_unlinked((adj_size, adj_size), pixel_ratio) + render_unlinked((scaled_size, scaled_size), dpi_scale) if not filepath.exists() or filepath.is_dir() - else render_default((adj_size, adj_size), pixel_ratio) + else render_default((scaled_size, scaled_size), dpi_scale) ) render_mask_and_edge = False # Apply the mask and edge if image: - image = self._resize_image(image, (adj_size, adj_size)) + image = self._resize_image(image, (scaled_size, scaled_size)) if render_mask_and_edge: - mask = self._get_mask((adj_size, adj_size), pixel_ratio) + mask = self._get_mask((scaled_size, scaled_size), dpi_scale) edge: tuple[Image.Image, Image.Image] = self._get_edge( - (adj_size, adj_size), pixel_ratio + (scaled_size, scaled_size), dpi_scale ) image = self._apply_edge( - four_corner_gradient(image, (adj_size, adj_size), mask), edge, theme + four_corner_gradient(image, (scaled_size, scaled_size), mask), edge, theme ) # Check if the file is supposed to be ignored and render an overlay if needed @@ -708,7 +684,7 @@ class FileRenderer: filepath.relative_to(unwrap(self.lib.library_dir)) ) ): - image = render_ignored((adj_size, adj_size), image) + image = render_ignored((scaled_size, scaled_size), image) except TypeError: pass @@ -716,13 +692,15 @@ class FileRenderer: elif is_loading: # Initialize "Loading" thumbnail loading_thumb: Image.Image = self._get_icon( - "thumb_loading", theme_color, (adj_size, adj_size), theme, pixel_ratio + "thumb_loading", theme_color, (scaled_size, scaled_size), theme, dpi_scale + ) + image = loading_thumb.resize( + (scaled_size, scaled_size), resample=Image.Resampling.BILINEAR ) - image = loading_thumb.resize((adj_size, adj_size), resample=Image.Resampling.BILINEAR) # A full preview image (never cached) - elif not is_grid_thumb: - image = self._render(cache, filepath, base_size, pixel_ratio, theme) + elif not is_thumb: + image = self._render(cache, filepath, size, dpi_scale, theme) if not image: image = ( render_unlinked((512, 512), 2) @@ -730,7 +708,7 @@ class FileRenderer: else render_default((512, 512), 2) ) render_mask_and_edge = False - mask = self._get_mask(image.size, pixel_ratio, scale_radius=True) + mask = self._get_mask(image.size, dpi_scale, scale_radius=True) bg = Image.new("RGBA", image.size, (0, 0, 0, 0)) bg.paste(image, mask=mask.getchannel(0)) image = bg @@ -741,7 +719,7 @@ class FileRenderer: return ( image, - (math.ceil(adj_size / pixel_ratio), math.ceil(image.size[1] / pixel_ratio)), + (math.ceil(scaled_size / dpi_scale), math.ceil(image.size[1] / dpi_scale)), timestamp, ) @@ -749,11 +727,11 @@ class FileRenderer: self, cache: CacheManager | None, filepath: str | Path, - base_size: tuple[int, int], - pixel_ratio: float, + size: tuple[int, int], + dpi_scale: float, theme: Theme = Theme.DARK, - is_grid_thumb: bool = False, - save_to_file: Path | None = None, + is_thumb: bool = False, + cache_filename: Path | None = None, ) -> Image.Image | None: """Render a thumbnail or preview image. @@ -761,18 +739,17 @@ class FileRenderer: cache (CacheManager | None): A cache manager instance. timestamp (float): The timestamp for which this job was dispatched. filepath (str | Path): The path of the file to render a thumbnail for. - base_size (tuple[int,int]): The unmodified base size of the thumbnail. - pixel_ratio (float): The screen pixel ratio. + size (tuple[int, int]): The unmodified base size of the thumbnail. + dpi_scale (float): The screen pixel ratio. theme (Theme): A theme enum to determine the light/dark theme. - is_grid_thumb (bool): Is this a thumbnail for the thumbnail grid? - Or else the Preview Pane? - save_to_file(Path | None): A filepath to optionally save the output to. + is_thumb (bool): Is this specifically a thumbnail? Use for specifying small variants. + cache_filename (Path | None): An optional filename to use to save to the cache. """ - adj_size = math.ceil(max(base_size[0], base_size[1]) * pixel_ratio) + scaled_size = math.ceil(max(size[0], size[1]) * dpi_scale) image: Image.Image | None = None filepath_: Path = Path(filepath) - savable_media_type: bool = True + is_savable_type: bool = True if filepath_ and filepath_.is_file(): try: @@ -810,7 +787,7 @@ class FileRenderer: elif MediaCategories.is_ext_in_category( ext, MediaCategories.IMAGE_VECTOR_TYPES, mime_fallback=True ): - image = vector_image_thumb(filepath_, adj_size) + image = vector_image_thumb(filepath_, scaled_size) # EXR Images ------------------------------------------------------------------- elif ext in [".exr"]: image = exr_image_thumb(filepath_) @@ -845,22 +822,22 @@ class FileRenderer: elif MediaCategories.is_ext_in_category( ext, MediaCategories.FONT_TYPES, mime_fallback=True ): - if is_grid_thumb: + if is_thumb: # Short (Aa) Preview - image = font_small_thumb(filepath_, adj_size) + image = font_small_thumb(filepath_, scaled_size) if image is not None: image = self._apply_overlay_color(image, UiColor.BLUE, theme) else: # Large (Full Alphabet) Preview - image = font_full_preview(filepath_, adj_size) + image = font_full_preview(filepath_, scaled_size) # Audio ======================================================== elif MediaCategories.is_ext_in_category( ext, MediaCategories.AUDIO_TYPES, mime_fallback=True ): image = audio_album_thumb(filepath_, ext) if image is None: - image = audio_waveform_thumb(filepath_, ext, adj_size, pixel_ratio) - savable_media_type = False + image = audio_waveform_thumb(filepath_, ext, scaled_size, dpi_scale) + is_savable_type = False if image is not None: image = self._apply_overlay_color(image, UiColor.GREEN, theme) # Blender ====================================================== @@ -872,7 +849,7 @@ class FileRenderer: elif MediaCategories.is_ext_in_category( ext, MediaCategories.PDF_TYPES, mime_fallback=True ): - image = pdf_thumb(filepath_, adj_size, ext) + image = pdf_thumb(filepath_, scaled_size, ext) # Archives ===================================================== elif MediaCategories.is_ext_in_category(ext, MediaCategories.ARCHIVE_TYPES): image = archive_thumb(filepath_, ext) @@ -887,10 +864,10 @@ class FileRenderer: raise NoRendererError if image: - image = self._resize_image(image, (adj_size, adj_size)) + image = self._resize_image(image, (scaled_size, scaled_size)) - if save_to_file and savable_media_type and image and cache: - cache.save_image(image, save_to_file, mode="RGBA") + if cache_filename and is_savable_type and image and cache: + cache.save_image(image, cache_filename, mode="RGBA") except ( AssertionError, @@ -899,7 +876,11 @@ class FileRenderer: UnidentifiedImageError, ValueError, ) as e: - logger.error("Couldn't render thumbnail", filepath=filepath, error=type(e).__name__) + logger.error( + "[FileRenderer] Couldn't render thumbnail", + filepath=filepath, + error=type(e).__name__, + ) image = None except NoRendererError: image = None diff --git a/src/tagstudio/qt/qt_file_renderer.py b/src/tagstudio/qt/qt_file_renderer.py index 9a9e8fac..b6751f00 100644 --- a/src/tagstudio/qt/qt_file_renderer.py +++ b/src/tagstudio/qt/qt_file_renderer.py @@ -14,11 +14,11 @@ from tagstudio.qt.previews.renderer import FileRenderer class QtFileRenderer(QObject): + """A Qt-specific wrapper for rendering image previews and thumbnails from files.""" + updated = Signal(float, QPixmap, QSize, Path) updated_ratio = Signal(float) - """A Qt-specific entry point for rendering file previews and thumbnails.""" - def __init__(self, library: Library, settings: GlobalSettings) -> None: super().__init__() self.renderer = FileRenderer(library, settings) @@ -43,11 +43,11 @@ class QtFileRenderer(QObject): cache=cache, timestamp=timestamp, filepath=filepath, - base_size=base_size, - pixel_ratio=pixel_ratio, + size=base_size, + dpi_scale=pixel_ratio, theme=self.theme, is_loading=is_loading, - is_grid_thumb=is_grid_thumb, + is_thumb=is_grid_thumb, ) qim = ImageQt.ImageQt(image) pixmap = QPixmap.fromImage(qim)