mirror of
https://github.com/TagStudioDev/TagStudio.git
synced 2026-01-28 22:01:24 +00:00
ci: add mypy check (#161)
* ci: add mypy check * fix remaining mypy issues * ignore whole methods
This commit is contained in:
33
.github/workflows/mypy.yaml
vendored
Normal file
33
.github/workflows/mypy.yaml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: MyPy
|
||||
|
||||
on: [ push, pull_request ]
|
||||
|
||||
|
||||
jobs:
|
||||
mypy:
|
||||
name: Run MyPy
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
# pyside 6.6.3 has some issue in their .pyi files
|
||||
pip install PySide6==6.6.2
|
||||
pip install -r requirements.txt
|
||||
pip install mypy==1.10.0
|
||||
|
||||
- name: Run MyPy
|
||||
run: |
|
||||
cd tagstudio
|
||||
mkdir -p .mypy_cache
|
||||
mypy --install-types --non-interactive
|
||||
mypy --config-file ../pyproject.toml .
|
||||
@@ -1,2 +1,8 @@
|
||||
[tool.ruff]
|
||||
exclude = ["main_window.py", "home_ui.py", "resources.py", "resources_rc.py"]
|
||||
|
||||
[tool.mypy]
|
||||
strict_optional = false
|
||||
disable_error_code = ["union-attr", "annotation-unchecked", "import-untyped"]
|
||||
explicit_package_bases = true
|
||||
warn_unused_ignores = true
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
ruff==0.4.2
|
||||
pre-commit==3.7.0
|
||||
Pyinstaller==6.6.0
|
||||
mypy==1.10.0
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# type: ignore
|
||||
# Copyright (C) 2024 Travis Abendshien (CyanVoxel).
|
||||
# Licensed under the GPL-3.0 License.
|
||||
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio
|
||||
|
||||
@@ -19,7 +19,7 @@ class FieldTemplate:
|
||||
|
||||
def to_compressed_obj(self) -> dict:
|
||||
"""An alternative to __dict__ that only includes fields containing non-default data."""
|
||||
obj = {}
|
||||
obj: dict = {}
|
||||
# All Field fields (haha) are mandatory, so no value checks are done.
|
||||
obj["id"] = self.id
|
||||
obj["name"] = self.name
|
||||
|
||||
@@ -8,6 +8,7 @@ class JsonLibary(TypedDict("", {"ts-version": str})):
|
||||
fields: list # TODO
|
||||
macros: "list[JsonMacro]"
|
||||
entries: "list[JsonEntry]"
|
||||
ignored_extensions: list[str]
|
||||
|
||||
|
||||
class JsonBase(TypedDict):
|
||||
|
||||
@@ -6,14 +6,19 @@
|
||||
|
||||
import datetime
|
||||
import glob
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
import typing
|
||||
import xml.etree.ElementTree as ET
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from typing import cast, Generator
|
||||
|
||||
from typing_extensions import Self
|
||||
|
||||
import ujson
|
||||
|
||||
from src.core.json_typing import JsonCollation, JsonEntry, JsonLibary, JsonTag
|
||||
@@ -42,7 +47,7 @@ class Entry:
|
||||
self.id = int(id)
|
||||
self.filename = filename
|
||||
self.path = path
|
||||
self.fields = fields
|
||||
self.fields: list[dict] = fields
|
||||
self.type = None
|
||||
|
||||
# Optional Fields ======================================================
|
||||
@@ -75,6 +80,7 @@ class Entry:
|
||||
return self.__str__()
|
||||
|
||||
def __eq__(self, __value: object) -> bool:
|
||||
__value = cast(Self, object)
|
||||
if os.name == "nt":
|
||||
return (
|
||||
int(self.id) == int(__value.id)
|
||||
@@ -129,18 +135,16 @@ class Entry:
|
||||
)
|
||||
t.remove(tag_id)
|
||||
elif field_index < 0:
|
||||
t: list[int] = library.get_field_attr(f, "content")
|
||||
t = library.get_field_attr(f, "content")
|
||||
while tag_id in t:
|
||||
t.remove(tag_id)
|
||||
|
||||
def add_tag(
|
||||
self, library: "Library", tag_id: int, field_id: int, field_index: int = None
|
||||
self, library: "Library", tag_id: int, field_id: int, field_index: int = -1
|
||||
):
|
||||
# field_index: int = -1
|
||||
# if self.fields:
|
||||
# if field_index != -1:
|
||||
# logging.info(f'[LIBRARY] ADD TAG to E:{self.id}, F-DI:{field_id}, F-INDEX:{field_index}')
|
||||
field_index = -1 if field_index is None else field_index
|
||||
for i, f in enumerate(self.fields):
|
||||
if library.get_field_attr(f, "id") == field_id:
|
||||
field_index = i
|
||||
@@ -183,7 +187,7 @@ class Tag:
|
||||
self.shorthand = shorthand
|
||||
self.aliases = aliases
|
||||
# Ensures no duplicates while retaining order.
|
||||
self.subtag_ids = []
|
||||
self.subtag_ids: list[int] = []
|
||||
for s in subtags_ids:
|
||||
if int(s) not in self.subtag_ids:
|
||||
self.subtag_ids.append(int(s))
|
||||
@@ -275,17 +279,19 @@ class Collation:
|
||||
def __repr__(self) -> str:
|
||||
return self.__str__()
|
||||
|
||||
@typing.no_type_check
|
||||
def __eq__(self, __value: object) -> bool:
|
||||
__value = cast(Self, __value)
|
||||
if os.name == "nt":
|
||||
return (
|
||||
int(self.id) == int(__value.id_)
|
||||
int(self.id) == int(__value.id)
|
||||
and self.filename.lower() == __value.filename.lower()
|
||||
and self.path.lower() == __value.path.lower()
|
||||
and self.fields == __value.fields
|
||||
)
|
||||
else:
|
||||
return (
|
||||
int(self.id) == int(__value.id_)
|
||||
int(self.id) == int(__value.id)
|
||||
and self.filename == __value.filename
|
||||
and self.path == __value.path
|
||||
and self.fields == __value.fields
|
||||
@@ -342,7 +348,7 @@ class Library:
|
||||
self.files_not_in_library: list[str] = []
|
||||
self.missing_files: list[str] = []
|
||||
self.fixed_files: list[str] = [] # TODO: Get rid of this.
|
||||
self.missing_matches = {}
|
||||
self.missing_matches: dict = {}
|
||||
# Duplicate Files
|
||||
# Defined by files that are exact or similar copies to others. Generated by DupeGuru.
|
||||
# (Filepath, Matched Filepath, Match Percentage)
|
||||
@@ -393,7 +399,7 @@ class Library:
|
||||
# Tag(id=1, name='Favorite', shorthand='', aliases=['Favorited, Favorites, Likes, Liked, Loved'], subtags_ids=[], color='yellow'),
|
||||
# ]
|
||||
|
||||
self.default_fields = [
|
||||
self.default_fields: list[dict] = [
|
||||
{"id": 0, "name": "Title", "type": "text_line"},
|
||||
{"id": 1, "name": "Author", "type": "text_line"},
|
||||
{"id": 2, "name": "Artist", "type": "text_line"},
|
||||
@@ -512,8 +518,8 @@ class Library:
|
||||
),
|
||||
"r",
|
||||
encoding="utf-8",
|
||||
) as f:
|
||||
json_dump: JsonLibary = ujson.load(f)
|
||||
) as file:
|
||||
json_dump: JsonLibary = ujson.load(file)
|
||||
self.library_dir = str(path)
|
||||
self.verify_ts_folders()
|
||||
major, minor, patch = json_dump["ts-version"].split(".")
|
||||
@@ -591,7 +597,7 @@ class Library:
|
||||
|
||||
filename = entry.get("filename", "")
|
||||
e_path = entry.get("path", "")
|
||||
fields = []
|
||||
fields: list = []
|
||||
if "fields" in entry:
|
||||
# Cast JSON str keys to ints
|
||||
for f in entry["fields"]:
|
||||
@@ -688,14 +694,14 @@ class Library:
|
||||
self._next_collation_id = id + 1
|
||||
|
||||
title = collation.get("title", "")
|
||||
e_ids_and_pages = collation.get("e_ids_and_pages", "")
|
||||
sort_order = collation.get("sort_order", [])
|
||||
cover_id = collation.get("cover_id", [])
|
||||
e_ids_and_pages = collation.get("e_ids_and_pages", [])
|
||||
sort_order = collation.get("sort_order", "")
|
||||
cover_id = collation.get("cover_id", -1)
|
||||
|
||||
c = Collation(
|
||||
id=id,
|
||||
title=title,
|
||||
e_ids_and_pages=e_ids_and_pages,
|
||||
e_ids_and_pages=e_ids_and_pages, # type: ignore
|
||||
sort_order=sort_order,
|
||||
cover_id=cover_id,
|
||||
)
|
||||
@@ -861,33 +867,33 @@ class Library:
|
||||
self.is_legacy_library = False
|
||||
|
||||
self.entries.clear()
|
||||
self._next_entry_id: int = 0
|
||||
self._next_entry_id = 0
|
||||
# self.filtered_entries.clear()
|
||||
self._entry_id_to_index_map.clear()
|
||||
|
||||
self._collation_id_to_index_map.clear()
|
||||
|
||||
self.missing_matches = {}
|
||||
self.dir_file_count: int = -1
|
||||
self.dir_file_count = -1
|
||||
self.files_not_in_library.clear()
|
||||
self.missing_files.clear()
|
||||
self.fixed_files.clear()
|
||||
self.filename_to_entry_id_map: dict[str, int] = {}
|
||||
self.filename_to_entry_id_map = {}
|
||||
self.ignored_extensions = self.default_ext_blacklist
|
||||
|
||||
self.tags.clear()
|
||||
self._next_tag_id: int = 1000
|
||||
self._tag_strings_to_id_map: dict[str, list[int]] = {}
|
||||
self._tag_id_to_cluster_map: dict[int, list[int]] = {}
|
||||
self._tag_id_to_index_map: dict[int, int] = {}
|
||||
self._next_tag_id = 1000
|
||||
self._tag_strings_to_id_map = {}
|
||||
self._tag_id_to_cluster_map = {}
|
||||
self._tag_id_to_index_map = {}
|
||||
self._tag_entry_ref_map.clear()
|
||||
|
||||
def refresh_dir(self):
|
||||
def refresh_dir(self) -> Generator:
|
||||
"""Scans a directory for files, and adds those relative filenames to internal variables."""
|
||||
|
||||
# Reset file interfacing variables.
|
||||
# -1 means uninitialized, aka a scan like this was never attempted before.
|
||||
self.dir_file_count: int = 0
|
||||
self.dir_file_count = 0
|
||||
self.files_not_in_library.clear()
|
||||
|
||||
# Scans the directory for files, keeping track of:
|
||||
@@ -1210,6 +1216,7 @@ class Library:
|
||||
# (int, str)
|
||||
|
||||
self._map_filenames_to_entry_ids()
|
||||
# TODO - the type here doesnt match but I cant reproduce calling this
|
||||
self.remove_missing_matches(fixed_indices)
|
||||
|
||||
# for i in fixed_indices:
|
||||
@@ -1330,10 +1337,11 @@ class Library:
|
||||
return self.collations[self._collation_id_to_index_map[int(collation_id)]]
|
||||
|
||||
# @deprecated('Use new Entry ID system.')
|
||||
def get_entry_from_index(self, index: int) -> Entry:
|
||||
def get_entry_from_index(self, index: int) -> Entry | None:
|
||||
"""Returns a Library Entry object given its index in the unfiltered Entries list."""
|
||||
if self.entries:
|
||||
return self.entries[int(index)]
|
||||
return None
|
||||
|
||||
# @deprecated('Use new Entry ID system.')
|
||||
def get_entry_id_from_filepath(self, filename):
|
||||
@@ -1368,7 +1376,7 @@ class Library:
|
||||
|
||||
if query:
|
||||
# start_time = time.time()
|
||||
query: str = query.strip().lower()
|
||||
query = query.strip().lower()
|
||||
query_words: list[str] = query.split(" ")
|
||||
all_tag_terms: list[str] = []
|
||||
only_untagged: bool = "untagged" in query or "no tags" in query
|
||||
@@ -1548,7 +1556,7 @@ class Library:
|
||||
else:
|
||||
for entry in self.entries:
|
||||
added = False
|
||||
allowed_ext: bool = (
|
||||
allowed_ext = (
|
||||
os.path.splitext(entry.filename)[1][1:].lower()
|
||||
not in self.ignored_extensions
|
||||
)
|
||||
@@ -1756,7 +1764,7 @@ class Library:
|
||||
|
||||
# if context and id_weights:
|
||||
# time.sleep(3)
|
||||
[final.append(idw[0]) for idw in id_weights if idw[0] not in final]
|
||||
[final.append(idw[0]) for idw in id_weights if idw[0] not in final] # type: ignore
|
||||
# print(f'Final IDs: \"{[self.get_tag_from_id(id).display_name(self) for id in final]}\"')
|
||||
# print('')
|
||||
return final
|
||||
@@ -1774,7 +1782,7 @@ class Library:
|
||||
|
||||
return subtag_ids
|
||||
|
||||
def filter_field_templates(self: str, query) -> list[int]:
|
||||
def filter_field_templates(self, query: str) -> list[int]:
|
||||
"""Returns a list of Field Template IDs returned from a string query."""
|
||||
|
||||
matches: list[int] = []
|
||||
@@ -2127,12 +2135,12 @@ class Library:
|
||||
def mirror_entry_fields(self, entry_ids: list[int]) -> None:
|
||||
"""Combines and mirrors all fields across a list of given Entry IDs."""
|
||||
|
||||
all_fields = []
|
||||
all_ids = [] # Parallel to all_fields
|
||||
all_fields: list = []
|
||||
all_ids: list = [] # Parallel to all_fields
|
||||
# Extract and merge all fields from all given Entries.
|
||||
for id in entry_ids:
|
||||
if id:
|
||||
entry: Entry = self.get_entry(id)
|
||||
entry = self.get_entry(id)
|
||||
if entry and entry.fields:
|
||||
for field in entry.fields:
|
||||
# First checks if their are matching tag_boxes to append to
|
||||
@@ -2153,7 +2161,7 @@ class Library:
|
||||
|
||||
# Replace each Entry's fields with the new merged ones.
|
||||
for id in entry_ids:
|
||||
entry: Entry = self.get_entry(id)
|
||||
entry = self.get_entry(id)
|
||||
if entry:
|
||||
entry.fields = all_fields
|
||||
|
||||
@@ -2181,7 +2189,7 @@ class Library:
|
||||
# pass
|
||||
# # TODO: Implement.
|
||||
|
||||
def get_field_attr(self, entry_field, attribute: str):
|
||||
def get_field_attr(self, entry_field: dict, attribute: str):
|
||||
"""Returns the value of a specified attribute inside an Entry field."""
|
||||
if attribute.lower() == "id":
|
||||
return list(entry_field.keys())[0]
|
||||
@@ -2236,7 +2244,7 @@ class Library:
|
||||
self._tag_strings_to_id_map[shorthand].append(tag.id)
|
||||
|
||||
for alias in tag.aliases:
|
||||
alias: str = strip_punctuation(alias).lower()
|
||||
alias = strip_punctuation(alias).lower()
|
||||
if alias not in self._tag_strings_to_id_map:
|
||||
self._tag_strings_to_id_map[alias] = []
|
||||
self._tag_strings_to_id_map[alias].append(tag.id)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class ColorType(Enum):
|
||||
class ColorType(int, Enum):
|
||||
PRIMARY = 0
|
||||
TEXT = 1
|
||||
BORDER = 2
|
||||
@@ -278,7 +278,7 @@ _TAG_COLORS = {
|
||||
}
|
||||
|
||||
|
||||
def get_tag_color(type: ColorType, color: str):
|
||||
def get_tag_color(type, color):
|
||||
color = color.lower()
|
||||
try:
|
||||
if type == ColorType.TEXT:
|
||||
|
||||
@@ -300,7 +300,7 @@ class TagStudioCore:
|
||||
# input()
|
||||
pass
|
||||
|
||||
def build_url(self, entry_id: int, source: str) -> str:
|
||||
def build_url(self, entry_id: int, source: str):
|
||||
"""Tries to rebuild a source URL given a specific filename structure."""
|
||||
|
||||
source = source.lower().replace("-", " ").replace("_", " ")
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio
|
||||
|
||||
|
||||
from types import FunctionType
|
||||
|
||||
from PySide6.QtCore import Signal, QObject
|
||||
from typing import Callable
|
||||
|
||||
|
||||
class FunctionIterator(QObject):
|
||||
@@ -13,7 +12,7 @@ class FunctionIterator(QObject):
|
||||
|
||||
value = Signal(object)
|
||||
|
||||
def __init__(self, function: FunctionType):
|
||||
def __init__(self, function: Callable):
|
||||
super().__init__()
|
||||
self.iterable = function
|
||||
|
||||
|
||||
@@ -36,18 +36,18 @@ logging.basicConfig(format="%(message)s", level=logging.INFO)
|
||||
|
||||
def folders_to_tags(library: Library):
|
||||
logging.info("Converting folders to Tags")
|
||||
tree = dict(dirs={})
|
||||
tree: dict = dict(dirs={})
|
||||
|
||||
def add_tag_to_tree(list: list[Tag]):
|
||||
def add_tag_to_tree(items: list[Tag]):
|
||||
branch = tree
|
||||
for tag in list:
|
||||
for tag in items:
|
||||
if tag.name not in branch["dirs"]:
|
||||
branch["dirs"][tag.name] = dict(dirs={}, tag=tag)
|
||||
branch = branch["dirs"][tag.name]
|
||||
|
||||
def add_folders_to_tree(list: list[str]) -> Tag:
|
||||
branch = tree
|
||||
for folder in list:
|
||||
def add_folders_to_tree(items: list[str]) -> Tag:
|
||||
branch: dict = tree
|
||||
for folder in items:
|
||||
if folder not in branch["dirs"]:
|
||||
new_tag = Tag(
|
||||
-1,
|
||||
@@ -97,18 +97,18 @@ def reverse_tag(library: Library, tag: Tag, list: list[Tag]) -> list[Tag]:
|
||||
|
||||
|
||||
def generate_preview_data(library: Library):
|
||||
tree = dict(dirs={}, files=[])
|
||||
tree: dict = dict(dirs={}, files=[])
|
||||
|
||||
def add_tag_to_tree(list: list[Tag]):
|
||||
branch = tree
|
||||
for tag in list:
|
||||
def add_tag_to_tree(items: list[Tag]):
|
||||
branch: dict = tree
|
||||
for tag in items:
|
||||
if tag.name not in branch["dirs"]:
|
||||
branch["dirs"][tag.name] = dict(dirs={}, tag=tag, files=[])
|
||||
branch = branch["dirs"][tag.name]
|
||||
|
||||
def add_folders_to_tree(list: list[str]) -> Tag:
|
||||
branch = tree
|
||||
for folder in list:
|
||||
def add_folders_to_tree(items: list[str]) -> dict:
|
||||
branch: dict = tree
|
||||
for folder in items:
|
||||
if folder not in branch["dirs"]:
|
||||
new_tag = Tag(-1, folder, "", [], [], "green")
|
||||
branch["dirs"][folder] = dict(dirs={}, tag=new_tag, files=[])
|
||||
@@ -220,7 +220,7 @@ class FoldersToTagsModal(QWidget):
|
||||
self.apply_button.setMinimumWidth(100)
|
||||
self.apply_button.clicked.connect(self.on_apply)
|
||||
|
||||
self.showEvent = self.on_open
|
||||
self.showEvent = self.on_open # type: ignore
|
||||
|
||||
self.root_layout.addWidget(self.title_widget)
|
||||
self.root_layout.addWidget(self.desc_widget)
|
||||
|
||||
@@ -125,7 +125,7 @@ class MirrorEntriesModal(QWidget):
|
||||
)
|
||||
|
||||
def mirror_entries_runnable(self):
|
||||
mirrored = []
|
||||
mirrored: list = []
|
||||
for i, dupe in enumerate(self.lib.dupe_files):
|
||||
# pb.setValue(i)
|
||||
# pb.setLabelText(f'Mirroring {i}/{len(self.lib.dupe_files)} Entries')
|
||||
|
||||
@@ -292,9 +292,9 @@ class Pagination(QWidget, QObject):
|
||||
).widget().setHidden(False)
|
||||
self.start_buffer_layout.itemAt(
|
||||
i - start_offset
|
||||
).widget().setText(str(i + 1))
|
||||
).widget().setText(str(i + 1)) # type: ignore
|
||||
self._assign_click(
|
||||
self.start_buffer_layout.itemAt(i - start_offset).widget(),
|
||||
self.start_buffer_layout.itemAt(i - start_offset).widget(), # type: ignore
|
||||
i,
|
||||
)
|
||||
sbc += 1
|
||||
@@ -319,11 +319,12 @@ class Pagination(QWidget, QObject):
|
||||
self.end_buffer_layout.itemAt(
|
||||
i - end_offset
|
||||
).widget().setHidden(False)
|
||||
self.end_buffer_layout.itemAt(i - end_offset).widget().setText(
|
||||
self.end_buffer_layout.itemAt(i - end_offset).widget().setText( # type: ignore
|
||||
str(i + 1)
|
||||
)
|
||||
self._assign_click(
|
||||
self.end_buffer_layout.itemAt(i - end_offset).widget(), i
|
||||
self.end_buffer_layout.itemAt(i - end_offset).widget(), # type: ignore
|
||||
i,
|
||||
)
|
||||
else:
|
||||
# if self.start_buffer_layout.itemAt(i-1):
|
||||
|
||||
@@ -13,6 +13,7 @@ import math
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import typing
|
||||
import webbrowser
|
||||
from datetime import datetime as dt
|
||||
from pathlib import Path
|
||||
@@ -117,7 +118,7 @@ class NavigationState:
|
||||
scrollbar_pos: int,
|
||||
page_index: int,
|
||||
page_count: int,
|
||||
search_text: str = None,
|
||||
search_text: str | None = None,
|
||||
thumb_size=None,
|
||||
spacing=None,
|
||||
) -> None:
|
||||
@@ -165,11 +166,16 @@ class QtDriver(QObject):
|
||||
|
||||
SIGTERM = Signal()
|
||||
|
||||
def __init__(self, core, args):
|
||||
preview_panel: PreviewPanel
|
||||
|
||||
def __init__(self, core: TagStudioCore, args):
|
||||
super().__init__()
|
||||
self.core: TagStudioCore = core
|
||||
self.lib = self.core.lib
|
||||
self.args = args
|
||||
self.frame_dict: dict = {}
|
||||
self.nav_frames: list[NavigationState] = []
|
||||
self.cur_frame_idx: int = -1
|
||||
|
||||
# self.main_window = None
|
||||
# self.main_window = Ui_MainWindow()
|
||||
@@ -193,10 +199,13 @@ class QtDriver(QObject):
|
||||
f"[QT DRIVER] Config File does not exist creating {str(path)}"
|
||||
)
|
||||
logging.info(f"[QT DRIVER] Using Config File {str(path)}")
|
||||
self.settings = QSettings(str(path), QSettings.IniFormat)
|
||||
self.settings = QSettings(str(path), QSettings.Format.IniFormat)
|
||||
else:
|
||||
self.settings = QSettings(
|
||||
QSettings.IniFormat, QSettings.UserScope, "TagStudio", "TagStudio"
|
||||
QSettings.Format.IniFormat,
|
||||
QSettings.Scope.UserScope,
|
||||
"TagStudio",
|
||||
"TagStudio",
|
||||
)
|
||||
logging.info(
|
||||
f"[QT DRIVER] Config File not specified, defaulting to {self.settings.fileName()}"
|
||||
@@ -230,7 +239,7 @@ class QtDriver(QObject):
|
||||
signal(SIGTERM, self.signal_handler)
|
||||
signal(SIGQUIT, self.signal_handler)
|
||||
|
||||
def start(self):
|
||||
def start(self) -> None:
|
||||
"""Launches the main Qt window."""
|
||||
|
||||
loader = QUiLoader()
|
||||
@@ -257,7 +266,7 @@ class QtDriver(QObject):
|
||||
# self.main_window = loader.load(home_path)
|
||||
self.main_window = Ui_MainWindow()
|
||||
self.main_window.setWindowTitle(self.base_title)
|
||||
self.main_window.mousePressEvent = self.mouse_navigation
|
||||
self.main_window.mousePressEvent = self.mouse_navigation # type: ignore
|
||||
# self.main_window.setStyleSheet(
|
||||
# f'QScrollBar::{{background:red;}}'
|
||||
# )
|
||||
@@ -273,13 +282,13 @@ class QtDriver(QObject):
|
||||
|
||||
splash_pixmap = QPixmap(":/images/splash.png")
|
||||
splash_pixmap.setDevicePixelRatio(self.main_window.devicePixelRatio())
|
||||
self.splash = QSplashScreen(splash_pixmap, Qt.WindowStaysOnTopHint)
|
||||
self.splash = QSplashScreen(splash_pixmap, Qt.WindowStaysOnTopHint) # type: ignore
|
||||
# self.splash.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
|
||||
self.splash.show()
|
||||
|
||||
if os.name == "nt":
|
||||
appid = "cyanvoxel.tagstudio.9"
|
||||
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(appid)
|
||||
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(appid) # type: ignore
|
||||
|
||||
if sys.platform != "darwin":
|
||||
icon = QIcon()
|
||||
@@ -392,7 +401,7 @@ class QtDriver(QObject):
|
||||
check_action = QAction("Open library on start", self)
|
||||
check_action.setCheckable(True)
|
||||
check_action.setChecked(
|
||||
self.settings.value(SettingItems.START_LOAD_LAST, True, type=bool)
|
||||
self.settings.value(SettingItems.START_LOAD_LAST, True, type=bool) # type: ignore
|
||||
)
|
||||
check_action.triggered.connect(
|
||||
lambda checked: self.settings.setValue(
|
||||
@@ -447,15 +456,14 @@ class QtDriver(QObject):
|
||||
self.sort_fields_action.setToolTip("Alt+S")
|
||||
macros_menu.addAction(self.sort_fields_action)
|
||||
|
||||
folders_to_tags_action = QAction("Create Tags From Folders", menu_bar)
|
||||
show_libs_list_action = QAction("Show Recent Libraries", menu_bar)
|
||||
show_libs_list_action.setCheckable(True)
|
||||
show_libs_list_action.setChecked(
|
||||
self.settings.value(SettingItems.WINDOW_SHOW_LIBS, True, type=bool)
|
||||
self.settings.value(SettingItems.WINDOW_SHOW_LIBS, True, type=bool) # type: ignore
|
||||
)
|
||||
show_libs_list_action.triggered.connect(
|
||||
lambda checked: (
|
||||
self.settings.setValue(SettingItems.WINDOW_SHOW_LIBS, checked),
|
||||
self.settings.setValue(SettingItems.WINDOW_SHOW_LIBS, checked), # type: ignore
|
||||
self.toggle_libs_list(checked),
|
||||
)
|
||||
)
|
||||
@@ -557,9 +565,9 @@ class QtDriver(QObject):
|
||||
)
|
||||
)
|
||||
|
||||
self.nav_frames: list[NavigationState] = []
|
||||
self.cur_frame_idx: int = -1
|
||||
self.cur_query: str = ""
|
||||
self.nav_frames = []
|
||||
self.cur_frame_idx = -1
|
||||
self.cur_query = ""
|
||||
self.filter_items()
|
||||
# self.update_thumbs()
|
||||
|
||||
@@ -650,9 +658,9 @@ class QtDriver(QObject):
|
||||
title_text = f"{self.base_title}"
|
||||
self.main_window.setWindowTitle(title_text)
|
||||
|
||||
self.nav_frames: list[NavigationState] = []
|
||||
self.cur_frame_idx: int = -1
|
||||
self.cur_query: str = ""
|
||||
self.nav_frames = []
|
||||
self.cur_frame_idx = -1
|
||||
self.cur_query = ""
|
||||
self.selected.clear()
|
||||
self.preview_panel.update_widgets()
|
||||
self.filter_items()
|
||||
@@ -1016,8 +1024,10 @@ class QtDriver(QObject):
|
||||
self.update_thumbs()
|
||||
# logging.info(f'Refresh: {[len(x.contents) for x in self.nav_stack]}, Index {self.cur_page_idx}')
|
||||
|
||||
@typing.no_type_check
|
||||
def purge_item_from_navigation(self, type: ItemType, id: int):
|
||||
# logging.info(self.nav_frames)
|
||||
# TODO - types here are ambiguous
|
||||
for i, frame in enumerate(self.nav_frames, start=0):
|
||||
while (type, id) in frame.contents:
|
||||
logging.info(f"Removing {id} from nav stack frame {i}")
|
||||
@@ -1061,7 +1071,7 @@ class QtDriver(QObject):
|
||||
sa.setWidgetResizable(True)
|
||||
sa.setWidget(self.flow_container)
|
||||
|
||||
def select_item(self, type: int, id: int, append: bool, bridge: bool):
|
||||
def select_item(self, type: ItemType, id: int, append: bool, bridge: bool):
|
||||
"""Selects one or more items in the Thumbnail Grid."""
|
||||
if append:
|
||||
# self.selected.append((thumb_index, page_index))
|
||||
@@ -1284,14 +1294,14 @@ class QtDriver(QObject):
|
||||
self.nav_forward([(ItemType.ENTRY, x[0]) for x in collation_entries])
|
||||
# self.update_thumbs()
|
||||
|
||||
def get_frame_contents(self, index=0, query: str = None):
|
||||
def get_frame_contents(self, index=0, query: str = ""):
|
||||
return (
|
||||
[] if not self.frame_dict[query] else self.frame_dict[query][index],
|
||||
index,
|
||||
len(self.frame_dict[query]),
|
||||
)
|
||||
|
||||
def filter_items(self, query=""):
|
||||
def filter_items(self, query: str = ""):
|
||||
if self.lib:
|
||||
# logging.info('Filtering...')
|
||||
self.main_window.statusbar.showMessage(
|
||||
@@ -1303,7 +1313,7 @@ class QtDriver(QObject):
|
||||
# self.filtered_items = self.lib.search_library(query)
|
||||
# 73601 Entries at 500 size should be 246
|
||||
all_items = self.lib.search_library(query)
|
||||
frames = []
|
||||
frames: list[list[tuple[ItemType, int]]] = []
|
||||
frame_count = math.ceil(len(all_items) / self.max_results)
|
||||
for i in range(0, frame_count):
|
||||
frames.append(
|
||||
@@ -1349,7 +1359,8 @@ class QtDriver(QObject):
|
||||
self.settings.endGroup()
|
||||
self.settings.sync()
|
||||
|
||||
def update_libs_list(self, path: str | Path):
|
||||
@typing.no_type_check
|
||||
def update_libs_list(self, path: Path):
|
||||
"""add library to list in SettingItems.LIBS_LIST"""
|
||||
ITEMS_LIMIT = 5
|
||||
path = Path(path)
|
||||
@@ -1404,9 +1415,9 @@ class QtDriver(QObject):
|
||||
title_text = f"{self.base_title} - Library '{self.lib.library_dir}'"
|
||||
self.main_window.setWindowTitle(title_text)
|
||||
|
||||
self.nav_frames: list[NavigationState] = []
|
||||
self.cur_frame_idx: int = -1
|
||||
self.cur_query: str = ""
|
||||
self.nav_frames = []
|
||||
self.cur_frame_idx = -1
|
||||
self.cur_query = ""
|
||||
self.selected.clear()
|
||||
self.preview_panel.update_widgets()
|
||||
self.filter_items()
|
||||
@@ -1444,7 +1455,7 @@ class QtDriver(QObject):
|
||||
# ('Stretch to Fill','Stretches the media file to fill the entire collage square.'),
|
||||
# ('Keep Aspect Ratio','Keeps the original media file\'s aspect ratio, filling the rest of the square with black bars.')
|
||||
# ], prompt='', required=True)
|
||||
keep_aspect = 0
|
||||
keep_aspect = False
|
||||
|
||||
if mode in [1, 2, 3]:
|
||||
# TODO: Choose data visualization options here.
|
||||
|
||||
@@ -86,7 +86,7 @@ class CollageIconRenderer(QObject):
|
||||
color = "#e22c3c" # Red
|
||||
|
||||
if data_only_mode:
|
||||
pic: Image = Image.new("RGB", size, color)
|
||||
pic = Image.new("RGB", size, color)
|
||||
# collage.paste(pic, (y*thumb_size, x*thumb_size))
|
||||
self.rendered.emit(pic)
|
||||
if not data_only_mode:
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
|
||||
import math
|
||||
import os
|
||||
from types import FunctionType
|
||||
from types import FunctionType, MethodType
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from typing import Optional, cast, Callable, Any
|
||||
|
||||
from PIL import Image, ImageQt
|
||||
from PySide6.QtCore import Qt, QEvent
|
||||
@@ -48,7 +48,7 @@ class FieldContainer(QWidget):
|
||||
# self.editable:bool = editable
|
||||
self.copy_callback: FunctionType = None
|
||||
self.edit_callback: FunctionType = None
|
||||
self.remove_callback: FunctionType = None
|
||||
self.remove_callback: Callable = None
|
||||
button_size = 24
|
||||
# self.setStyleSheet('border-style:solid;border-color:#1e1a33;border-radius:8px;border-width:2px;')
|
||||
|
||||
@@ -129,7 +129,7 @@ class FieldContainer(QWidget):
|
||||
|
||||
# self.set_inner_widget(mode)
|
||||
|
||||
def set_copy_callback(self, callback: Optional[FunctionType]):
|
||||
def set_copy_callback(self, callback: Optional[MethodType]):
|
||||
try:
|
||||
self.copy_button.clicked.disconnect()
|
||||
except RuntimeError:
|
||||
@@ -138,7 +138,7 @@ class FieldContainer(QWidget):
|
||||
self.copy_callback = callback
|
||||
self.copy_button.clicked.connect(callback)
|
||||
|
||||
def set_edit_callback(self, callback: Optional[FunctionType]):
|
||||
def set_edit_callback(self, callback: Optional[MethodType]):
|
||||
try:
|
||||
self.edit_button.clicked.disconnect()
|
||||
except RuntimeError:
|
||||
@@ -147,7 +147,7 @@ class FieldContainer(QWidget):
|
||||
self.edit_callback = callback
|
||||
self.edit_button.clicked.connect(callback)
|
||||
|
||||
def set_remove_callback(self, callback: Optional[FunctionType]):
|
||||
def set_remove_callback(self, callback: Optional[Callable]):
|
||||
try:
|
||||
self.remove_button.clicked.disconnect()
|
||||
except RuntimeError:
|
||||
@@ -168,7 +168,7 @@ class FieldContainer(QWidget):
|
||||
|
||||
def get_inner_widget(self) -> Optional["FieldWidget"]:
|
||||
if self.field_layout.itemAt(0):
|
||||
return self.field_layout.itemAt(0).widget()
|
||||
return cast(FieldWidget, self.field_layout.itemAt(0).widget())
|
||||
return None
|
||||
|
||||
def set_title(self, title: str):
|
||||
|
||||
@@ -181,7 +181,7 @@ class ItemThumb(FlowWidget):
|
||||
lambda ts, i, s, ext: (
|
||||
self.update_thumb(ts, image=i),
|
||||
self.update_size(ts, size=s),
|
||||
self.set_extension(ext),
|
||||
self.set_extension(ext), # type: ignore
|
||||
)
|
||||
)
|
||||
self.thumb_button.setFlat(True)
|
||||
@@ -388,7 +388,7 @@ class ItemThumb(FlowWidget):
|
||||
self.thumb_button.setMinimumSize(size)
|
||||
self.thumb_button.setMaximumSize(size)
|
||||
|
||||
def update_clickable(self, clickable: FunctionType = None):
|
||||
def update_clickable(self, clickable: typing.Callable):
|
||||
"""Updates attributes of a thumbnail element."""
|
||||
# logging.info(f'[GUI] Updating Click Event for element {id(element)}: {id(clickable) if clickable else None}')
|
||||
try:
|
||||
|
||||
@@ -19,9 +19,9 @@ class PanelModal(QWidget):
|
||||
widget: "PanelWidget",
|
||||
title: str,
|
||||
window_title: str,
|
||||
done_callback: FunctionType = None,
|
||||
done_callback: Callable = None,
|
||||
# cancel_callback:FunctionType=None,
|
||||
save_callback: FunctionType = None,
|
||||
save_callback: Callable = None,
|
||||
has_save: bool = False,
|
||||
):
|
||||
# [Done]
|
||||
|
||||
@@ -6,7 +6,6 @@ import logging
|
||||
import os
|
||||
import time
|
||||
import typing
|
||||
from types import FunctionType
|
||||
from datetime import datetime as dt
|
||||
|
||||
import cv2
|
||||
@@ -67,8 +66,8 @@ class PreviewPanel(QWidget):
|
||||
self.isOpen: bool = False
|
||||
# self.filepath = None
|
||||
# self.item = None # DEPRECATED, USE self.selected
|
||||
self.common_fields = []
|
||||
self.mixed_fields = []
|
||||
self.common_fields: list = []
|
||||
self.mixed_fields: list = []
|
||||
self.selected: list[tuple[ItemType, int]] = [] # New way of tracking items
|
||||
self.tag_callback = None
|
||||
self.containers: list[QWidget] = []
|
||||
@@ -174,7 +173,7 @@ class PreviewPanel(QWidget):
|
||||
info_layout.addWidget(scroll_area)
|
||||
|
||||
# keep list of rendered libraries to avoid needless re-rendering
|
||||
self.render_libs = set()
|
||||
self.render_libs: set = set()
|
||||
self.libs_layout = QVBoxLayout()
|
||||
self.fill_libs_widget(self.libs_layout)
|
||||
|
||||
@@ -182,7 +181,8 @@ class PreviewPanel(QWidget):
|
||||
self.libs_flow_container.setObjectName("librariesList")
|
||||
self.libs_flow_container.setLayout(self.libs_layout)
|
||||
self.libs_flow_container.setSizePolicy(
|
||||
QSizePolicy.Preferred, QSizePolicy.Maximum
|
||||
QSizePolicy.Preferred, # type: ignore
|
||||
QSizePolicy.Maximum, # type: ignore
|
||||
)
|
||||
|
||||
# set initial visibility based on settings
|
||||
@@ -233,7 +233,7 @@ class PreviewPanel(QWidget):
|
||||
settings.beginGroup(SettingItems.LIBS_LIST)
|
||||
lib_items: dict[str, tuple[str, str]] = {}
|
||||
for item_tstamp in settings.allKeys():
|
||||
val = settings.value(item_tstamp)
|
||||
val: str = settings.value(item_tstamp) # type: ignore
|
||||
cut_val = val
|
||||
if len(val) > 45:
|
||||
cut_val = f"{val[0:10]} ... {val[-10:]}"
|
||||
@@ -261,13 +261,13 @@ class PreviewPanel(QWidget):
|
||||
if child.widget() is not None:
|
||||
child.widget().deleteLater()
|
||||
elif child.layout() is not None:
|
||||
clear_layout(child.layout())
|
||||
clear_layout(child.layout()) # type: ignore
|
||||
|
||||
# remove any potential previous items
|
||||
clear_layout(layout)
|
||||
|
||||
label = QLabel("Recent Libraries")
|
||||
label.setAlignment(Qt.AlignCenter)
|
||||
label.setAlignment(Qt.AlignCenter) # type: ignore
|
||||
|
||||
row_layout = QHBoxLayout()
|
||||
row_layout.addWidget(label)
|
||||
@@ -348,8 +348,8 @@ class PreviewPanel(QWidget):
|
||||
# logging.info(f'')
|
||||
# self.preview_img.setMinimumSize(64,64)
|
||||
|
||||
adj_width = size[0]
|
||||
adj_height = size[1]
|
||||
adj_width: float = size[0]
|
||||
adj_height: float = size[1]
|
||||
# Landscape
|
||||
if self.image_ratio > 1:
|
||||
# logging.info('Landscape')
|
||||
@@ -371,8 +371,8 @@ class PreviewPanel(QWidget):
|
||||
|
||||
# self.preview_img.setMinimumSize(s)
|
||||
# self.preview_img.setMaximumSize(s_max)
|
||||
adj_size = QSize(adj_width, adj_height)
|
||||
self.img_button_size = (adj_width, adj_height)
|
||||
adj_size = QSize(int(adj_width), int(adj_height))
|
||||
self.img_button_size = (int(adj_width), int(adj_height))
|
||||
self.preview_img.setMaximumSize(adj_size)
|
||||
self.preview_img.setIconSize(adj_size)
|
||||
# self.preview_img.setMinimumSize(adj_size)
|
||||
@@ -466,7 +466,7 @@ class PreviewPanel(QWidget):
|
||||
)
|
||||
self.file_label.setFilePath(filepath)
|
||||
window_title = filepath
|
||||
ratio: float = self.devicePixelRatio()
|
||||
ratio = self.devicePixelRatio()
|
||||
self.tr.render_big(time.time(), filepath, (512, 512), ratio)
|
||||
self.file_label.setText("\u200b".join(filepath))
|
||||
self.file_label.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||
@@ -575,7 +575,7 @@ class PreviewPanel(QWidget):
|
||||
)
|
||||
self.preview_img.setCursor(Qt.CursorShape.ArrowCursor)
|
||||
|
||||
ratio: float = self.devicePixelRatio()
|
||||
ratio = self.devicePixelRatio()
|
||||
self.tr.render_big(time.time(), "", (512, 512), ratio, True)
|
||||
try:
|
||||
self.preview_img.clicked.disconnect()
|
||||
@@ -796,7 +796,6 @@ class PreviewPanel(QWidget):
|
||||
# container.set_editable(True)
|
||||
container.set_inline(False)
|
||||
# Normalize line endings in any text content.
|
||||
text: str = ""
|
||||
if not mixed:
|
||||
text = self.lib.get_field_attr(field, "content").replace("\r", "\n")
|
||||
else:
|
||||
@@ -836,7 +835,6 @@ class PreviewPanel(QWidget):
|
||||
# container.set_editable(True)
|
||||
container.set_inline(False)
|
||||
# Normalize line endings in any text content.
|
||||
text: str = ""
|
||||
if not mixed:
|
||||
text = self.lib.get_field_attr(field, "content").replace("\r", "\n")
|
||||
else:
|
||||
@@ -877,7 +875,7 @@ class PreviewPanel(QWidget):
|
||||
self.lib.get_field_attr(field, "content")
|
||||
)
|
||||
title = f"{self.lib.get_field_attr(field, 'name')} (Collation)"
|
||||
text: str = f"{collation.title} ({len(collation.e_ids_and_pages)} Items)"
|
||||
text = f"{collation.title} ({len(collation.e_ids_and_pages)} Items)"
|
||||
if len(self.selected) == 1:
|
||||
text += f" - Page {collation.e_ids_and_pages[[x[0] for x in collation.e_ids_and_pages].index(self.selected[0][1])][1]}"
|
||||
inner_container = TextWidget(title, text)
|
||||
@@ -953,7 +951,7 @@ class PreviewPanel(QWidget):
|
||||
container.setHidden(False)
|
||||
self.place_add_field_button()
|
||||
|
||||
def remove_field(self, field: object):
|
||||
def remove_field(self, field: dict):
|
||||
"""Removes a field from all selected Entries, given a field object."""
|
||||
for item_pair in self.selected:
|
||||
if item_pair[0] == ItemType.ENTRY:
|
||||
@@ -975,7 +973,7 @@ class PreviewPanel(QWidget):
|
||||
)
|
||||
pass
|
||||
|
||||
def update_field(self, field: object, content):
|
||||
def update_field(self, field: dict, content):
|
||||
"""Removes a field from all selected Entries, given a field object."""
|
||||
field = dict(field)
|
||||
for item_pair in self.selected:
|
||||
@@ -991,7 +989,7 @@ class PreviewPanel(QWidget):
|
||||
)
|
||||
pass
|
||||
|
||||
def remove_message_box(self, prompt: str, callback: FunctionType) -> int:
|
||||
def remove_message_box(self, prompt: str, callback: typing.Callable) -> None:
|
||||
remove_mb = QMessageBox()
|
||||
remove_mb.setText(prompt)
|
||||
remove_mb.setWindowTitle("Remove Field")
|
||||
|
||||
@@ -78,7 +78,7 @@ class TagBoxWidget(FieldWidget):
|
||||
tsp.tag_chosen.connect(lambda x: self.add_tag_callback(x))
|
||||
self.add_modal = PanelModal(tsp, title, "Add Tags")
|
||||
self.add_button.clicked.connect(
|
||||
lambda: (tsp.update_tags(), self.add_modal.show())
|
||||
lambda: (tsp.update_tags(), self.add_modal.show()) # type: ignore
|
||||
)
|
||||
|
||||
self.set_tags(tags)
|
||||
@@ -137,7 +137,6 @@ class TagBoxWidget(FieldWidget):
|
||||
has_save=True,
|
||||
)
|
||||
# self.edit_modal.widget.update_display_name.connect(lambda t: self.edit_modal.title_widget.setText(t))
|
||||
panel: BuildTagPanel = self.edit_modal.widget
|
||||
self.edit_modal.saved.connect(lambda: self.lib.update_tag(btp.build_tag()))
|
||||
# panel.tag_updated.connect(lambda tag: self.lib.update_tag(tag))
|
||||
self.edit_modal.show()
|
||||
@@ -149,7 +148,7 @@ class TagBoxWidget(FieldWidget):
|
||||
f"[TAG BOX WIDGET] ADD TAG CALLBACK: T:{tag_id} to E:{self.item.id}"
|
||||
)
|
||||
logging.info(f"[TAG BOX WIDGET] SELECTED T:{self.driver.selected}")
|
||||
id = list(self.field.keys())[0]
|
||||
id: int = list(self.field.keys())[0] # type: ignore
|
||||
for x in self.driver.selected:
|
||||
self.driver.lib.get_entry(x[1]).add_tag(
|
||||
self.driver.lib, tag_id, field_id=id, field_index=-1
|
||||
@@ -170,9 +169,9 @@ class TagBoxWidget(FieldWidget):
|
||||
def edit_tag_callback(self, tag: Tag):
|
||||
self.lib.update_tag(tag)
|
||||
|
||||
def remove_tag(self, tag_id):
|
||||
def remove_tag(self, tag_id: int):
|
||||
logging.info(f"[TAG BOX WIDGET] SELECTED T:{self.driver.selected}")
|
||||
id = list(self.field.keys())[0]
|
||||
id: int = list(self.field.keys())[0] # type: ignore
|
||||
for x in self.driver.selected:
|
||||
index = self.driver.lib.get_field_index_in_entry(
|
||||
self.driver.lib.get_entry(x[1]), id
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
from PySide6 import QtCore
|
||||
from PySide6.QtCore import QEvent
|
||||
from PySide6.QtGui import QEnterEvent, QPainter, QColor, QPen, QPainterPath
|
||||
from PySide6.QtGui import QEnterEvent, QPainter, QColor, QPen, QPainterPath, QPaintEvent
|
||||
from PySide6.QtWidgets import QWidget, QPushButton
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ class ThumbButton(QPushButton):
|
||||
|
||||
# self.clicked.connect(lambda checked: self.set_selected(True))
|
||||
|
||||
def paintEvent(self, event: QEvent) -> None:
|
||||
def paintEvent(self, event: QPaintEvent) -> None:
|
||||
super().paintEvent(event)
|
||||
if self.hovered or self.selected:
|
||||
painter = QPainter()
|
||||
|
||||
@@ -103,9 +103,7 @@ class ThumbRenderer(QObject):
|
||||
adj_size: int = 1
|
||||
image = None
|
||||
pixmap = None
|
||||
final = None
|
||||
extension: str = None
|
||||
broken_thumb = False
|
||||
# adj_font_size = math.floor(12 * pixelRatio)
|
||||
if ThumbRenderer.font_pixel_ratio != pixelRatio:
|
||||
ThumbRenderer.font_pixel_ratio = pixelRatio
|
||||
@@ -283,7 +281,7 @@ class ThumbRenderer(QObject):
|
||||
filepath,
|
||||
base_size: tuple[int, int],
|
||||
pixelRatio: float,
|
||||
isLoading=False,
|
||||
isLoading: bool = False,
|
||||
):
|
||||
"""Renders a large, non-square entry/element thumbnail for the GUI."""
|
||||
adj_size: int = 1
|
||||
@@ -291,8 +289,6 @@ class ThumbRenderer(QObject):
|
||||
pixmap: QPixmap = None
|
||||
final: Image.Image = None
|
||||
extension: str = None
|
||||
broken_thumb = False
|
||||
img_ratio = 1
|
||||
# adj_font_size = math.floor(12 * pixelRatio)
|
||||
if ThumbRenderer.font_pixel_ratio != pixelRatio:
|
||||
ThumbRenderer.font_pixel_ratio = pixelRatio
|
||||
@@ -308,7 +304,7 @@ class ThumbRenderer(QObject):
|
||||
|
||||
if isLoading:
|
||||
adj_size = math.ceil((512 * pixelRatio))
|
||||
final: Image.Image = ThumbRenderer.thumb_loading_512.resize(
|
||||
final = ThumbRenderer.thumb_loading_512.resize(
|
||||
(adj_size, adj_size), resample=Image.Resampling.BILINEAR
|
||||
)
|
||||
qim = ImageQt.ImageQt(final)
|
||||
@@ -452,7 +448,9 @@ class ThumbRenderer(QObject):
|
||||
# final.paste(hl_add, mask=hl_add.getchannel(3))
|
||||
scalar = 4
|
||||
rec: Image.Image = Image.new(
|
||||
"RGB", tuple([d * scalar for d in image.size]), "black"
|
||||
"RGB",
|
||||
tuple([d * scalar for d in image.size]), # type: ignore
|
||||
"black",
|
||||
)
|
||||
draw = ImageDraw.Draw(rec)
|
||||
draw.rounded_rectangle(
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"""TagStudio launcher."""
|
||||
|
||||
from src.core.ts_core import TagStudioCore
|
||||
from src.cli.ts_cli import CliDriver
|
||||
from src.cli.ts_cli import CliDriver # type: ignore
|
||||
from src.qt.ts_qt import QtDriver
|
||||
import argparse
|
||||
import traceback
|
||||
|
||||
Reference in New Issue
Block a user