Skip to content

Commit

Permalink
feat: begin adding settings menu
Browse files Browse the repository at this point in the history
  • Loading branch information
python357-1 committed Dec 15, 2024
1 parent c33d020 commit 11d0553
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 1 deletion.
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ SQLAlchemy==2.0.34
structlog==24.4.0
typing_extensions>=3.10.0.0,<=4.11.0
ujson>=5.8.0,<=5.9.0
vtf2img==0.1.0
vtf2img==0.1.0
toml==0.10.2
1 change: 1 addition & 0 deletions tagstudio/src/core/settings/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__all__ = ["tssettings"]
42 changes: 42 additions & 0 deletions tagstudio/src/core/settings/tssettings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from pathlib import Path

import toml
from pydantic import BaseModel, Field

Check failure on line 4 in tagstudio/src/core/settings/tssettings.py

View workflow job for this annotation

GitHub Actions / Run MyPy

[mypy] reported by reviewdog 🐶 Cannot find implementation or library stub for module named "pydantic" [import-not-found] Raw Output: /home/runner/work/TagStudio/TagStudio/tagstudio/src/core/settings/tssettings.py:4:1: error: Cannot find implementation or library stub for module named "pydantic" [import-not-found]


# NOTE: pydantic also has a BaseSettings class (from pydantic-settings) that allows any settings
# properties to be overwritten with environment variables. as tagstudio is not currently using
# environment variables, i did not base it on that, but that may be useful in the future.
class TSSettings(BaseModel):
dark_mode: bool = Field(default=False)
language: str = Field(default="en-US")

@staticmethod
def read_settings(path: Path | str, **kwargs) -> "TSSettings":
# library = kwargs.get("library")
settings_data: dict[str, any] = dict()

Check failure on line 17 in tagstudio/src/core/settings/tssettings.py

View workflow job for this annotation

GitHub Actions / Run MyPy

[mypy] reported by reviewdog 🐶 Function "builtins.any" is not valid as a type [valid-type] Raw Output: /home/runner/work/TagStudio/TagStudio/tagstudio/src/core/settings/tssettings.py:17:34: error: Function "builtins.any" is not valid as a type [valid-type]
if path.exists():

Check failure on line 18 in tagstudio/src/core/settings/tssettings.py

View workflow job for this annotation

GitHub Actions / Run MyPy

[mypy] reported by reviewdog 🐶 Item "str" of "Path | str" has no attribute "exists" [union-attr] Raw Output: /home/runner/work/TagStudio/TagStudio/tagstudio/src/core/settings/tssettings.py:18:12: error: Item "str" of "Path | str" has no attribute "exists" [union-attr]
with open(path, "rb").read() as filecontents:

Check failure on line 19 in tagstudio/src/core/settings/tssettings.py

View workflow job for this annotation

GitHub Actions / Run MyPy

[mypy] reported by reviewdog 🐶 "bytes" has no attribute "__enter__"; maybe "__iter__"? [attr-defined] Raw Output: /home/runner/work/TagStudio/TagStudio/tagstudio/src/core/settings/tssettings.py:19:18: error: "bytes" has no attribute "__enter__"; maybe "__iter__"? [attr-defined]

Check failure on line 19 in tagstudio/src/core/settings/tssettings.py

View workflow job for this annotation

GitHub Actions / Run MyPy

[mypy] reported by reviewdog 🐶 "bytes" has no attribute "__exit__" [attr-defined] Raw Output: /home/runner/work/TagStudio/TagStudio/tagstudio/src/core/settings/tssettings.py:19:18: error: "bytes" has no attribute "__exit__" [attr-defined]
if len(filecontents.strip()) != 0:
settings_data = toml.loads(filecontents.decode("utf-8"))

# if library: #TODO: add library-specific settings
# lib_settings_path = Path(library.folder / "settings.toml")
# lib_settings_data: dict[str, any]
# if lib_settings_path.exists:
# with open(lib_settings_path, "rb") as filedata:
# lib_settings_data = tomllib.load(filedata)
# lib_settings = TSSettings(**lib_settings_data)

return TSSettings(**settings_data)

def to_dict(self) -> dict[str, any]:

Check failure on line 33 in tagstudio/src/core/settings/tssettings.py

View workflow job for this annotation

GitHub Actions / Run MyPy

[mypy] reported by reviewdog 🐶 Function "builtins.any" is not valid as a type [valid-type] Raw Output: /home/runner/work/TagStudio/TagStudio/tagstudio/src/core/settings/tssettings.py:33:36: error: Function "builtins.any" is not valid as a type [valid-type]
d = dict[str, any]()

Check failure on line 34 in tagstudio/src/core/settings/tssettings.py

View workflow job for this annotation

GitHub Actions / Run MyPy

[mypy] reported by reviewdog 🐶 Function "builtins.any" is not valid as a type [valid-type] Raw Output: /home/runner/work/TagStudio/TagStudio/tagstudio/src/core/settings/tssettings.py:34:23: error: Function "builtins.any" is not valid as a type [valid-type]
for prop_name, prop_value in self:
d[prop_name] = prop_value

return d

def save(self, path: Path | str) -> None:
with open(path, "w") as f:
toml.dump(self.to_dict(), f)
63 changes: 63 additions & 0 deletions tagstudio/src/qt/modals/settings_modal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import copy

from PySide6.QtWidgets import (
QCheckBox,
QComboBox,
QHBoxLayout,
QLabel,
QVBoxLayout,
)
from src.core.settings import TSSettings

Check failure on line 10 in tagstudio/src/qt/modals/settings_modal.py

View workflow job for this annotation

GitHub Actions / Run MyPy

[mypy] reported by reviewdog 🐶 Module "src.core.settings" has no attribute "TSSettings" [attr-defined] Raw Output: /home/runner/work/TagStudio/TagStudio/tagstudio/src/qt/modals/settings_modal.py:10:1: error: Module "src.core.settings" has no attribute "TSSettings" [attr-defined]
from src.qt.widgets.panel import PanelWidget


class SettingsModal(PanelWidget):
def __init__(self, settings: TSSettings):
super().__init__()
self.tempSettings = copy.deepcopy(settings)

self.main = QVBoxLayout(self)

# ---
self.darkMode_Label = QLabel()
self.darkMode_Value = QCheckBox()
self.darkMode_Row = QHBoxLayout()
self.darkMode_Row.addWidget(self.darkMode_Label)
self.darkMode_Row.addWidget(self.darkMode_Value)

self.darkMode_Label.setText("Dark Mode")
self.darkMode_Value.setChecked(self.tempSettings.dark_mode)

self.darkMode_Value.stateChanged.connect(
lambda state: self.set_property("dark_mode", bool(state))
)

# ---
self.language_Label = QLabel()
self.language_Value = QComboBox()
self.language_Row = QHBoxLayout()
self.language_Row.addWidget(self.language_Label)
self.language_Row.addWidget(self.language_Value)

self.language_Label.setText("Language")
language_list = [ # TODO: put this somewhere else
"en-US",
"en-GB",
"es-MX",
# etc...
]
self.language_Value.addItems(language_list)
self.language_Value.setCurrentIndex(language_list.index(self.tempSettings.language))
self.language_Value.currentTextChanged.connect(
lambda text: self.set_property("language", text)
)

# ---
self.main.addLayout(self.darkMode_Row)
self.main.addLayout(self.language_Row)

def set_property(self, prop_name: str, value: any) -> None:

Check failure on line 59 in tagstudio/src/qt/modals/settings_modal.py

View workflow job for this annotation

GitHub Actions / Run MyPy

[mypy] reported by reviewdog 🐶 Function "builtins.any" is not valid as a type [valid-type] Raw Output: /home/runner/work/TagStudio/TagStudio/tagstudio/src/qt/modals/settings_modal.py:59:51: error: Function "builtins.any" is not valid as a type [valid-type]
setattr(self.tempSettings, prop_name, value)

def get_content(self) -> TSSettings:
return self.tempSettings
30 changes: 30 additions & 0 deletions tagstudio/src/qt/ts_qt.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
from src.core.library.alchemy.fields import _FieldID
from src.core.library.alchemy.library import Entry, LibraryStatus
from src.core.media_types import MediaCategories
from src.core.settings import TSSettings
from src.core.ts_core import TagStudioCore
from src.core.utils.refresh_dir import RefreshDirTracker
from src.core.utils.web import strip_web_protocol
Expand All @@ -90,6 +91,7 @@
from src.qt.modals.fix_dupes import FixDupeFilesModal
from src.qt.modals.fix_unlinked import FixUnlinkedEntriesModal
from src.qt.modals.folders_to_tags import FoldersToTagsModal
from src.qt.modals.settings_modal import SettingsModal
from src.qt.modals.tag_database import TagDatabasePanel
from src.qt.resource_manager import ResourceManager
from src.qt.widgets.item_thumb import BadgeType, ItemThumb
Expand Down Expand Up @@ -243,6 +245,13 @@ def start(self) -> None:
self.main_window.dragMoveEvent = self.drag_move_event # type: ignore[method-assign]
self.main_window.dropEvent = self.drop_event # type: ignore[method-assign]

self.settings_path = (
Path.home() / ".config/TagStudio" / "settings.toml"
) # TODO: put this somewhere else
self.newSettings = TSSettings.read_settings(
self.settings_path
) # TODO: make this cross-platform

splash_pixmap = QPixmap(":/images/splash.png")
splash_pixmap.setDevicePixelRatio(self.main_window.devicePixelRatio())
self.splash = QSplashScreen(splash_pixmap, Qt.WindowType.WindowStaysOnTopHint)
Expand Down Expand Up @@ -324,6 +333,12 @@ def start(self) -> None:
file_menu.addAction(close_library_action)

# Edit Menu ============================================================
settings_menu_action = QAction("&Settings", menu_bar)
settings_menu_action.triggered.connect(lambda: self.open_settings_menu())
edit_menu.addAction(settings_menu_action)

edit_menu.addSeparator()

new_tag_action = QAction("New &Tag", menu_bar)
new_tag_action.triggered.connect(lambda: self.add_tag_action_callback())
new_tag_action.setShortcut(
Expand Down Expand Up @@ -637,6 +652,21 @@ def add_tag_action_callback(self):
)
self.modal.show()

def open_settings_menu(self):
self.modal = PanelModal(
SettingsModal(self.newSettings),
"Settings",
"Settings",
has_save=True,
save_callback=(lambda x: self.update_settings(x)),
)

self.modal.show()

def update_settings(self, settings: TSSettings):
self.newSettings = settings
self.newSettings.save(self.settings_path)

def select_all_action_callback(self):
self.selected = list(range(0, len(self.frame_content)))

Expand Down
2 changes: 2 additions & 0 deletions tagstudio/tests/example_settings.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dark_mode = true
language = "es-MX"
Binary file not shown.
11 changes: 11 additions & 0 deletions tagstudio/tests/test_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import pathlib

from src.core.settings.tssettings import TSSettings

CWD = pathlib.Path(__file__)


def test_read_settings():
settings = TSSettings.read_settings(CWD.parent / "example_settings.toml")
assert settings.dark_mode
assert settings.language == "es-MX"

0 comments on commit 11d0553

Please sign in to comment.