implemented user options and made translation capable of changing language

This commit is contained in:
Faraphel 2022-06-11 22:38:53 +02:00
parent 7f85d7eeae
commit 31a28c3cf1
8 changed files with 130 additions and 10 deletions

3
.gitignore vendored
View file

@ -1 +1,2 @@
/.idea/ /.idea/
/option.json

View file

@ -1,8 +1,15 @@
from source.gui import install
import sys import sys
from source.gui import install
from source.option import Option
from source.translation import load_language
# this allows every variable to be accessible from other files, useful for the plugins # this allows every variable to be accessible from other files, useful for the plugins
self = sys.modules[__name__] self = sys.modules[__name__]
self.window = install.Window() options = Option.from_file("./option.json")
translater = load_language(options["language"])
self.window = install.Window(options)
self.window.run() self.window.run()

View file

@ -1,3 +1,5 @@
import os
import sys
from threading import Thread from threading import Thread
from typing import Callable from typing import Callable
@ -28,3 +30,10 @@ def threaded(func: Callable) -> Callable:
Thread(target=func, args=args, kwargs=kwargs, daemon=True).start() Thread(target=func, args=args, kwargs=kwargs, daemon=True).start()
return wrapper return wrapper
def restart_program():
"""
Restart the program
"""
os.execl(sys.executable, sys.executable, *sys.argv)

View file

@ -1,7 +1,6 @@
import traceback import traceback
from tkinter import messagebox from tkinter import messagebox
from typing import Callable from typing import Callable
from source.translation import translate as _ from source.translation import translate as _
@ -12,6 +11,6 @@ def better_gui_error(func: Callable) -> Callable:
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
try: return func(*args, **kwargs) try: return func(*args, **kwargs)
except: messagebox.showerror(_("ERROR"), traceback.format_exc()) except: messagebox.showerror(_("Error"), traceback.format_exc())
return wrapper return wrapper

View file

@ -12,6 +12,7 @@ from typing import Generator
from source.gui import better_gui_error from source.gui import better_gui_error
from source.mkw.Game import Game from source.mkw.Game import Game
from source.mkw.ModConfig import ModConfig from source.mkw.ModConfig import ModConfig
from source.option import Option
from source.translation import translate as _ from source.translation import translate as _
from source import event from source import event
from source import * from source import *
@ -35,9 +36,11 @@ class InstallerState(enum.Enum):
# Main window for the installer # Main window for the installer
class Window(tkinter.Tk): class Window(tkinter.Tk):
def __init__(self): def __init__(self, options: Option):
super().__init__() super().__init__()
self.options: Option = options
self.title(_("INSTALLER_TITLE")) self.title(_("INSTALLER_TITLE"))
self.resizable(False, False) self.resizable(False, False)
self.iconbitmap("./assets/icon.ico") self.iconbitmap("./assets/icon.ico")
@ -141,8 +144,15 @@ class Menu(tkinter.Menu):
master.add_cascade(label=_("LANGUAGE_SELECTION"), menu=self) master.add_cascade(label=_("LANGUAGE_SELECTION"), menu=self)
def callback(file: Path):
def func(): self.master.master.options["language"] = file.stem
return func
for file in Path("./assets/language/").iterdir(): for file in Path("./assets/language/").iterdir():
self.add_command(label=json.loads(file.read_text(encoding="utf8"))["name"]) self.add_command(
label=json.loads(file.read_text(encoding="utf8"))["name"],
command=callback(file)
)
# Track configuration menu # Track configuration menu
class TrackConfiguration(tkinter.Menu): class TrackConfiguration(tkinter.Menu):

View file

@ -37,6 +37,8 @@ class Track:
# others not mandatory attributes # others not mandatory attributes
for key, value in kwargs.items(): for key, value in kwargs.items():
# if the attribute start with __, this is a magic attribute, and it should not be modified
if key.startswith("__"): continue
setattr(self, key, value) setattr(self, key, value)
@classmethod @classmethod

77
source/option.py Normal file
View file

@ -0,0 +1,77 @@
import json
from pathlib import Path
from source import restart_program
class Option:
__slots__ = ("_path", "_options")
reboot_on_change: list[any] = ["language"]
default_options: dict[str, any] = {
"language": "en"
}
def __init__(self, language=None):
self._path: Path | None = None
self._options: dict[str, any] = self.default_options.copy()
if language is not None: self._options["language"] = language
def __getitem__(self, key: str) -> any:
"""
get an options value from its key
:param key: the option name
:return: the value of the option
"""
return self._options[key]
def __setitem__(self, key: str, value: any) -> None:
"""
change the value of an options for a key, if the options have been loaded from a file, save it inside
if the option is in the reboot_on_change list, reboot the program
:param key: the name of the option to edit
:param value: the value of the option
:return:
"""
self._options[key] = value
if self._path: self.save()
if key in self.reboot_on_change: restart_program()
def save(self, option_file: Path | str = None) -> None:
"""
save the options to the file
:return: None
"""
if option_file is None: option_file = self._path
if isinstance(option_file, str): option_file = Path(option_file)
with option_file.open("w") as file:
json.dump(self._options, file, indent=4, ensure_ascii=False)
@classmethod
def from_dict(cls, option_dict: dict) -> "Option":
"""
Create a Option from a dict if the parameters are in the default_options
:param option_dict: dict containing the configuration
:return: Option
"""
kwargs = {}
for key in cls.default_options.keys():
if "key" in option_dict: kwargs[key] = option_dict[key]
return cls(**kwargs)
@classmethod
def from_file(cls, option_file: str | Path) -> "Option":
"""
Loads the option from a file. If the option file does not exist, only load default configuration
:param option_file: the option file
:return: Option
"""
if isinstance(option_file, str): option_file = Path(option_file)
if not option_file.exists(): obj = cls()
else: obj = cls.from_dict(json.loads(option_file.read_text(encoding="utf8")))
obj._path = option_file
return obj

View file

@ -1,8 +1,19 @@
import json import json
import sys
from pathlib import Path from pathlib import Path
language_data = json.loads(Path("./assets/language/en.json").read_text(encoding="utf8")) self = sys.modules[__name__]
self._language_data = {}
def load_language(language: str):
"""
Load a language file.
:param language: language code to load
:return:
"""
self._language_data = json.loads(Path(f"./assets/language/{language}.json").read_text(encoding="utf8"))
def translate(*text) -> str: def translate(*text) -> str:
@ -11,4 +22,8 @@ def translate(*text) -> str:
:param text: list of text to translate :param text: list of text to translate
:return: translated text :return: translated text
""" """
return "".join([language_data["translation"].get(word, word) for word in text]) return "".join([self._language_data["translation"].get(word, word) for word in text])