Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions STYLE.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,19 @@ As of writing this section, the QT part of the code base is quite unstructured a
The general structure of the QT code base should look like this:
```
qt
├── controller
├── controllers
│ ├── widgets
│ │ └── preview_panel_controller.py
│ └── main_window_controller.py
├── view
├── views
│ ├── widgets
│ │ └── preview_panel_view.py
│ └── main_window_view.py
├── ts_qt.py
└── mixed.py
```

In this structure there are the `view` and `controller` sub-directories. They have the exact same structure and for every `<component>_view.py` there is a `<component>_controller.py` at the same location in the other subdirectory and vice versa.
In this structure there are the `views` and `controllers` sub-directories. They have the exact same structure and for every `<component>_view.py` there is a `<component>_controller.py` at the same location in the other subdirectory and vice versa.

Typically the classes should look like this:
```py
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ ignore_errors = true
qt_api = "pyside6"

[tool.pyright]
ignore = ["src/tagstudio/qt/helpers/vendored/pydub/", ".venv/**"]
ignore = ["src/tagstudio/qt/previews/vendored/pydub/", ".venv/**"]
include = ["src/tagstudio", "tests"]
reportAny = false
reportIgnoreCommentWithoutRule = false
Expand All @@ -109,7 +109,7 @@ ignore = ["D100", "D101", "D102", "D103", "D104", "D105", "D106", "D107"]

[tool.ruff.lint.per-file-ignores]
"tests/**" = ["D", "E402"]
"src/tagstudio/qt/helpers/vendored/**" = ["B", "E", "N", "UP", "SIM115"]
"src/tagstudio/qt/previews/vendored/**" = ["B", "E", "N", "UP", "SIM115"]

[tool.ruff.lint.pydocstyle]
convention = "google"
4 changes: 3 additions & 1 deletion src/tagstudio/core/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@

from tagstudio.core.constants import TS_FOLDER_NAME
from tagstudio.core.enums import SettingItems
from tagstudio.core.global_settings import GlobalSettings
from tagstudio.core.library.alchemy.library import LibraryStatus
from tagstudio.qt.global_settings import GlobalSettings

logger = structlog.get_logger(__name__)


class DriverMixin:
cached_values: QSettings
# TODO: GlobalSettings has become closely tied to Qt.
# Should there be a base Settings class?
settings: GlobalSettings

def evaluate_path(self, open_path: str | None) -> LibraryStatus:
Expand Down
28 changes: 0 additions & 28 deletions src/tagstudio/core/field_template.py

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@


@dataclass
class DupeRegistry:
class DupeFilesRegistry:
"""State handler for DupeGuru results."""

library: Library
Expand Down
2 changes: 1 addition & 1 deletion src/tagstudio/core/library/ignore.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from wcmatch import glob, pathlib

from tagstudio.core.constants import IGNORE_NAME, TS_FOLDER_NAME
from tagstudio.core.singleton import Singleton
from tagstudio.core.utils.singleton import Singleton

logger = structlog.get_logger()

Expand Down
3 changes: 1 addition & 2 deletions src/tagstudio/core/library/json/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@
)
from tagstudio.core.enums import OpenStatus
from tagstudio.core.library.json.fields import DEFAULT_FIELDS, TEXT_FIELDS
from tagstudio.core.utils.str import strip_punctuation
from tagstudio.core.utils.web import strip_web_protocol
from tagstudio.core.utils.str_formatting import strip_punctuation, strip_web_protocol

TYPE = ["file", "meta", "alt", "mask"]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@
from tagstudio.core.library.alchemy.library import Library
from tagstudio.core.library.alchemy.models import Entry
from tagstudio.core.library.ignore import PATH_GLOB_FLAGS, Ignore, ignore_to_glob
from tagstudio.qt.helpers.silent_popen import silent_run # pyright: ignore
from tagstudio.core.utils.silent_subprocess import silent_run # pyright: ignore

logger = structlog.get_logger(__name__)


@dataclass
class RefreshDirTracker:
class RefreshTracker:
library: Library
files_not_in_library: list[Path] = field(default_factory=list)

Expand Down
5 changes: 4 additions & 1 deletion src/tagstudio/core/ts_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@
import json
from pathlib import Path

import structlog

from tagstudio.core.constants import TS_FOLDER_NAME
from tagstudio.core.library.alchemy.fields import _FieldID
from tagstudio.core.library.alchemy.library import Library
from tagstudio.core.library.alchemy.models import Entry
from tagstudio.core.utils.unlinked_registry import logger

logger = structlog.get_logger(__name__)


class TagStudioCore:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,46 +1,51 @@
# Copyright (C) 2024 Travis Abendshien (CyanVoxel).
# Copyright (C) 2025
# Licensed under the GPL-3.0 License.
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio

# pyright: reportExplicitAny=false

import os
import subprocess
import sys
from collections.abc import Callable, Collection, Iterable
from typing import Any

"""Implementation of subprocess.Popen that does not spawn console windows or log output
and sanitizes pyinstall environment variables."""
and sanitizes pyinstaller environment variables."""


def silent_Popen( # noqa: N802
def silent_popen(
args,
bufsize=-1,
bufsize: int = -1,
executable=None,
stdin=None,
stdout=None,
stderr=None,
preexec_fn=None,
close_fds=True,
shell=False,
preexec_fn: Callable[[], Any] | None = None,
close_fds: bool = True,
shell: bool = False,
cwd=None,
env=None,
universal_newlines=None,
startupinfo=None,
creationflags=0,
restore_signals=True,
start_new_session=False,
pass_fds=(),
universal_newlines: bool | None = None,
startupinfo: Any | None = None,
creationflags: int = 0,
restore_signals: bool = True,
start_new_session: bool = False,
pass_fds: Collection[int] = (),
*,
group=None,
extra_groups=None,
user=None,
umask=-1,
encoding=None,
errors=None,
text=None,
pipesize=-1,
process_group=None,
text: bool | None = None,
encoding: str | None = None,
errors: str | None = None,
user: str | int | None = None,
group: str | int | None = None,
extra_groups: Iterable[str | int] | None = None,
umask: int = -1,
pipesize: int = -1,
process_group: int | None = None,
):
"""Call subprocess.Popen without creating a console window."""
current_env = env

if sys.platform == "win32":
creationflags |= subprocess.CREATE_NO_WINDOW
import ctypes
Expand All @@ -52,9 +57,9 @@ def silent_Popen( # noqa: N802
or sys.platform.startswith("openbsd")
):
# pass clean environment to the subprocess
env = os.environ
original_env = env.get("LD_LIBRARY_PATH_ORIG")
env["LD_LIBRARY_PATH"] = original_env if original_env else ""
current_env = os.environ
original_env = current_env.get("LD_LIBRARY_PATH_ORIG")
current_env["LD_LIBRARY_PATH"] = original_env if original_env else ""

return subprocess.Popen(
args=args,
Expand All @@ -67,20 +72,20 @@ def silent_Popen( # noqa: N802
close_fds=close_fds,
shell=shell,
cwd=cwd,
env=env,
env=current_env,
universal_newlines=universal_newlines,
startupinfo=startupinfo,
creationflags=creationflags,
restore_signals=restore_signals,
start_new_session=start_new_session,
pass_fds=pass_fds,
text=text,
encoding=encoding,
errors=errors,
user=user,
group=group,
extra_groups=extra_groups,
user=user,
umask=umask,
encoding=encoding,
errors=errors,
text=text,
pipesize=pipesize,
process_group=process_group,
)
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (C) 2024 Travis Abendshien (CyanVoxel).
# Copyright (C) 2025 Travis Abendshien (CyanVoxel).
# Licensed under the GPL-3.0 License.
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio

Expand All @@ -24,3 +24,11 @@ def strip_punctuation(string: str) -> str:
.replace(" ", "")
.replace(" ", "")
)


def strip_web_protocol(string: str) -> str:
r"""Strips a leading web protocol (ex. \"https://\") as well as \"www.\" from a string."""
prefixes = ["https://", "http://", "www.", "www2."]
for prefix in prefixes:
string = string.removeprefix(prefix)
return string
11 changes: 0 additions & 11 deletions src/tagstudio/core/utils/web.py

This file was deleted.

2 changes: 1 addition & 1 deletion src/tagstudio/qt/cache_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from PIL import Image

from tagstudio.core.constants import THUMB_CACHE_NAME, TS_FOLDER_NAME
from tagstudio.core.global_settings import DEFAULT_CACHED_IMAGE_QUALITY, DEFAULT_THUMB_CACHE_SIZE
from tagstudio.qt.global_settings import DEFAULT_CACHED_IMAGE_QUALITY, DEFAULT_THUMB_CACHE_SIZE

logger = structlog.get_logger(__name__)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
from PySide6.QtGui import QDesktopServices
from PySide6.QtWidgets import QMessageBox

from tagstudio.core.palette import ColorType, UiColor, get_ui_color
from tagstudio.qt.helpers.vendored.ffmpeg import FFMPEG_CMD, FFPROBE_CMD
from tagstudio.qt.models.palette import ColorType, UiColor, get_ui_color
from tagstudio.qt.previews.vendored.ffmpeg import FFMPEG_CMD, FFPROBE_CMD
from tagstudio.qt.translations import Translations

logger = structlog.get_logger(__name__)


class FfmpegChecker(QMessageBox):
class FfmpegMissingMessageBox(QMessageBox):
"""A warning dialog for if FFmpeg is missing."""

HELP_URL = "https://docs.tagstud.io/help/ffmpeg/"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
from PySide6 import QtGui

from tagstudio.core.library.alchemy.library import Library
from tagstudio.core.utils.ignored_registry import IgnoredRegistry
from tagstudio.qt.modals.remove_ignored_modal import RemoveIgnoredModal
from tagstudio.core.library.alchemy.registries.ignored_registry import IgnoredRegistry
from tagstudio.qt.mixed.progress_bar import ProgressWidget
from tagstudio.qt.mixed.remove_ignored_modal import RemoveIgnoredModal
from tagstudio.qt.translations import Translations
from tagstudio.qt.view.widgets.fix_ignored_modal_view import FixIgnoredEntriesModalView
from tagstudio.qt.widgets.progress import ProgressWidget
from tagstudio.qt.views.fix_ignored_modal_view import FixIgnoredEntriesModalView

# Only import for type checking/autocompletion, will not be imported at runtime.
if TYPE_CHECKING:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@


from pathlib import Path
from typing import override

import structlog
from PySide6 import QtGui
from PySide6.QtCore import Signal
from PySide6.QtGui import QShowEvent

from tagstudio.core.constants import IGNORE_NAME, TS_FOLDER_NAME
from tagstudio.core.library.alchemy.library import Library
from tagstudio.core.library.alchemy.models import Tag
from tagstudio.core.library.ignore import Ignore
from tagstudio.qt.helpers import file_opener
from tagstudio.qt.view.widgets.ignore_modal_view import IgnoreModalView
from tagstudio.qt.utils.file_opener import open_file
from tagstudio.qt.views.ignore_modal_view import IgnoreModalView

logger = structlog.get_logger(__name__)

Expand All @@ -36,7 +37,7 @@ def __open_file(self):
if not self.lib.library_dir:
return
ts_ignore_path = Path(self.lib.library_dir / TS_FOLDER_NAME / IGNORE_NAME)
file_opener.open_file(ts_ignore_path, file_manager=True)
open_file(ts_ignore_path, file_manager=True)

def save(self):
if not self.lib.library_dir:
Expand All @@ -45,6 +46,7 @@ def save(self):
lines = [f"{line}\n" for line in lines]
Ignore.write_ignore_file(self.lib.library_dir, lines)

def showEvent(self, event: QtGui.QShowEvent) -> None: # noqa N802
@override
def showEvent(self, event: QShowEvent) -> None: # type: ignore
self.__load_file()
return super().showEvent(event)
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
)
from tagstudio.core.library.alchemy.library import Library
from tagstudio.core.utils.types import unwrap
from tagstudio.qt.helpers import file_opener
from tagstudio.qt.translations import Translations
from tagstudio.qt.view.widgets.library_info_window_view import LibraryInfoWindowView
from tagstudio.qt.utils import file_opener
from tagstudio.qt.views.library_info_window_view import LibraryInfoWindowView

# Only import for type checking/autocompletion, will not be imported at runtime.
if TYPE_CHECKING:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QHBoxLayout, QLabel, QVBoxLayout, QWidget

from tagstudio.qt.widgets.paged_panel.paged_panel_state import PagedPanelState
from tagstudio.qt.controllers.paged_panel_state import PagedPanelState

logger = structlog.get_logger(__name__)

Expand Down
Loading