From 443d16b28cf8c6896a7da486f2fd7cada49277b4 Mon Sep 17 00:00:00 2001 From: Faraphel Date: Fri, 19 Aug 2022 20:16:48 +0200 Subject: [PATCH] MKWColor is now a dataclass to improve readability --- Pack/MKWFaraphel/macros.json | 8 +- Pack/MKWFaraphel/mod_config.json | 102 +++++++++++----------- source/gui/preview/track_formatting.py | 6 +- source/gui/preview/track_selecting.py | 6 +- source/gui/preview/track_sorting.py | 4 +- source/mkw/MKWColor.py | 113 +++++++++++++------------ source/mkw/ModConfig.py | 8 +- source/safe_eval/safe_eval.py | 15 +--- 8 files changed, 129 insertions(+), 133 deletions(-) diff --git a/Pack/MKWFaraphel/macros.json b/Pack/MKWFaraphel/macros.json index 502deac..3ec09e6 100644 --- a/Pack/MKWFaraphel/macros.json +++ b/Pack/MKWFaraphel/macros.json @@ -1,14 +1,14 @@ { "GAME_REGION": "str(extracted_game.original_game.wit_path.region)", - "TRACK_TEXT_SCORE": "bmg_color_text(['yellow', 'orange', 'dark red', 'azure'][getattr(track, 'warning', 0)], f'\\\\x{65296+track.score:x} ') if hasattr(track, 'score') else ''", + "TRACK_TEXT_SCORE": "get_color(name=['yellow', 'orange', 'dark red', 'azure'][getattr(track, 'warning', 0)]).color_text(f'\\\\x{65296+track.score:x} ') if hasattr(track, 'score') else ''", "TRACK_TEXT_PREFIX": "f'{prefix} ' if (prefix := get_tag_template('prefix', '')) else ''", "TRACK_TEXT_SUFFIX": "f' ({suffix})' if (suffix := get_tag_template('suffix', '')) else ''", "TRACK_TEXT_NAME": "getattr(track, 'name', '')", "TRACK_TEXT_AUTHORS": "'\\\\n'.join(author) if isinstance(author := getattr(track, 'author', '/'), list) else author", - "TRACK_TEXT_WARNING_IF_DISABLED": "bmg_color_text('red', '/') if getattr(track, 'warning', 0) != 0 else ''", - "TRACK_TEXT_HIGHLIGHT_START": "bmg_color_raw('azure') if eval(highlight_if if (highlight_if := mod_config.specific_settings['highlight_if'].value) is not None else 'False', env={'track': track, 'mod_config': mod_config}) is True else ''", - "TRACK_TEXT_HIGHLIGHT_END": "bmg_color_raw('off')", + "TRACK_TEXT_WARNING_IF_DISABLED": "get_color(name='red').color_text('/') if getattr(track, 'warning', 0) != 0 else ''", + "TRACK_TEXT_HIGHLIGHT_START": "get_color(name='azure').raw if eval(highlight_if if (highlight_if := mod_config.specific_settings['highlight_if'].value) is not None else 'False', env={'track': track, 'mod_config': mod_config}) is True else ''", + "TRACK_TEXT_HIGHLIGHT_END": "get_color(name='off').raw", "SETTINGS_MODE": "mod_config.specific_settings['mode'].value", "SETTINGS_BALANCING": "mod_config.specific_settings['balancing'].value", diff --git a/Pack/MKWFaraphel/mod_config.json b/Pack/MKWFaraphel/mod_config.json index 461348e..1a3c779 100644 --- a/Pack/MKWFaraphel/mod_config.json +++ b/Pack/MKWFaraphel/mod_config.json @@ -55,59 +55,59 @@ "tags_templates": { "prefix": { - "MSRDS": "{{ bmg_color_text('green', tag) }}", - "CTR": "{{ bmg_color_text('orange', tag) }}", - "CTTR": "{{ bmg_color_text('dark orange', tag) }}", - "CNR": "{{ bmg_color_text('orange', tag) }}", - "DKR": "{{ bmg_color_text('dark red', tag) }}", - "LCP": "{{ bmg_color_text('green', tag) }}", - "LEGO-R": "{{ bmg_color_text('light red', tag) }}", - "MP9": "{{ bmg_color_text('neon yellow', tag) }}", - "MSUSA": "{{ bmg_color_text('green', tag) }}", - "FZMV": "{{ bmg_color_text('yellow', tag) }}", - "KAR": "{{ bmg_color_text('green', tag) }}", - "KO": "{{ bmg_color_text('dark orange', tag) }}", - "FZ": "{{ bmg_color_text('yellow', tag) }}", - "RV": "{{ bmg_color_text('white', tag) }}", - "SADX": "{{ bmg_color_text('dark blue', tag) }}", - "SCR": "{{ bmg_color_text('yellow', tag) }}", - "SH": "{{ bmg_color_text('pink', tag) }}", - "SM64": "{{ bmg_color_text('pink', tag) }}", - "SMB1": "{{ bmg_color_text('light red', tag) }}", - "SMB2": "{{ bmg_color_text('red', tag) }}", - "SSBB": "{{ bmg_color_text('vivid red', tag) }}", - "SMS": "{{ bmg_color_text('dark red', tag) }}", - "SMO": "{{ bmg_color_text('apple red', tag) }}", - "VVVVVV": "{{ bmg_color_text('azure', tag) }}", - "WF": "{{ bmg_color_text('green', tag) }}", - "WP": "{{ bmg_color_text('neon yellow 2', tag) }}", - "Zelda OoT": "{{ bmg_color_text('green', tag) }}", - "Zelda TP": "{{ bmg_color_text('green', tag) }}", - "Zelda WW": "{{ bmg_color_text('green', tag) }}", - "PMWR": "{{ bmg_color_text('neon yellow 2', tag) }}", - "SHR": "{{ bmg_color_text('green', tag) }}", - "SK64": "{{ bmg_color_text('green', tag) }}", - "SMG": "{{ bmg_color_text('light red', tag) }}", - "Spyro 1": "{{ bmg_color_text('azure', tag) }}", - "Switch": "{{ bmg_color_text('vivid red', tag) }}", - "Wii": "{{ bmg_color_text('azure', tag) }}", - "3DS": "{{ bmg_color_text('light orange', tag) }}", - "DS": "{{ bmg_color_text('white', tag) }}", - "GCN": "{{ bmg_color_text('azure', tag) }}", - "GBA": "{{ bmg_color_text('dark blue', tag) }}", - "N64": "{{ bmg_color_text('pink', tag) }}", - "SNES": "{{ bmg_color_text('green', tag) }}", - "RMX": "{{ bmg_color_text('orange', tag) }}", - "MKT": "{{ bmg_color_text('dark orange', tag) }}", - "GP": "{{ bmg_color_text('dark red', tag) }}", - "UT": "{{ bmg_color_text('red', tag) }}", - "GK2": "{{ bmg_color_text('green', tag) }}", - "GK3": "{{ bmg_color_text('green', tag) }}", - "GK7": "{{ bmg_color_text('green', tag) }}", - "FGKR": "{{ bmg_color_text('dark orange', tag) }}" + "MSRDS": "{{ get_color(name='green').color_text(tag) }}", + "CTR": "{{ get_color(name='orange').color_text(tag) }}", + "CTTR": "{{ get_color(name='dark orange').color_text(tag) }}", + "CNR": "{{ get_color(name='orange').color_text(tag) }}", + "DKR": "{{ get_color(name='dark red').color_text(tag) }}", + "LCP": "{{ get_color(name='green').color_text(tag) }}", + "LEGO-R": "{{ get_color(name='light red').color_text(tag) }}", + "MP9": "{{ get_color(name='neon yellow').color_text(tag) }}", + "MSUSA": "{{ get_color(name='green').color_text(tag) }}", + "FZMV": "{{ get_color(name='yellow').color_text(tag) }}", + "KAR": "{{ get_color(name='green').color_text(tag) }}", + "KO": "{{ get_color(name='dark orange').color_text(tag) }}", + "FZ": "{{ get_color(name='yellow').color_text(tag) }}", + "RV": "{{ get_color(name='white').color_text(tag) }}", + "SADX": "{{ get_color(name='dark blue').color_text(tag) }}", + "SCR": "{{ get_color(name='yellow').color_text(tag) }}", + "SH": "{{ get_color(name='pink').color_text(tag) }}", + "SM64": "{{ get_color(name='pink').color_text(tag) }}", + "SMB1": "{{ get_color(name='light red').color_text(tag) }}", + "SMB2": "{{ get_color(name='red').color_text(tag) }}", + "SSBB": "{{ get_color(name='vivid red').color_text(tag) }}", + "SMS": "{{ get_color(name='dark red').color_text(tag) }}", + "SMO": "{{ get_color(name='apple red').color_text(tag) }}", + "VVVVVV": "{{ get_color(name='azure').color_text(tag) }}", + "WF": "{{ get_color(name='green').color_text(tag) }}", + "WP": "{{ get_color(name='neon yellow 2').color_text(tag) }}", + "Zelda OoT": "{{ get_color(name='green').color_text(tag) }}", + "Zelda TP": "{{ get_color(name='green').color_text(tag) }}", + "Zelda WW": "{{ get_color(name='green').color_text(tag) }}", + "PMWR": "{{ get_color(name='neon yellow 2').color_text(tag) }}", + "SHR": "{{ get_color(name='green').color_text(tag) }}", + "SK64": "{{ get_color(name='green').color_text(tag) }}", + "SMG": "{{ get_color(name='light red').color_text(tag) }}", + "Spyro 1": "{{ get_color(name='azure').color_text(tag) }}", + "Switch": "{{ get_color(name='vivid red').color_text(tag) }}", + "Wii": "{{ get_color(name='azure').color_text(tag) }}", + "3DS": "{{ get_color(name='light orange').color_text(tag) }}", + "DS": "{{ get_color(name='white').color_text(tag) }}", + "GCN": "{{ get_color(name='azure').color_text(tag) }}", + "GBA": "{{ get_color(name='dark blue').color_text(tag) }}", + "N64": "{{ get_color(name='pink').color_text(tag) }}", + "SNES": "{{ get_color(name='green').color_text(tag) }}", + "RMX": "{{ get_color(name='orange').color_text(tag) }}", + "MKT": "{{ get_color(name='dark orange').color_text(tag) }}", + "GP": "{{ get_color(name='dark red').color_text(tag) }}", + "UT": "{{ get_color(name='red').color_text(tag) }}", + "GK2": "{{ get_color(name='green').color_text(tag) }}", + "GK3": "{{ get_color(name='green').color_text(tag) }}", + "GK7": "{{ get_color(name='green').color_text(tag) }}", + "FGKR": "{{ get_color(name='dark orange').color_text(tag) }}" }, "suffix": { - "Boost": "{{ bmg_color_text('orange', tag) }}" + "Boost": "{{ get_color(name='orange').color_text(tag) }}" } }, "tags_cups": [ diff --git a/source/gui/preview/track_formatting.py b/source/gui/preview/track_formatting.py index 28df018..b72878a 100644 --- a/source/gui/preview/track_formatting.py +++ b/source/gui/preview/track_formatting.py @@ -3,7 +3,7 @@ from tkinter import ttk from typing import TYPE_CHECKING import re -from source.mkw.MKWColor import MKWColor +from source.mkw import MKWColor from source.gui.preview import AbstractPreviewWindow if TYPE_CHECKING: @@ -28,7 +28,7 @@ class Window(AbstractPreviewWindow): self.entry_template_input.bind("", self.preview) self.text_track_format = tkinter.Text( - self, background="black", foreground=MKWColor("off").color_code, state=tkinter.DISABLED + self, background="black", foreground=MKWColor.get(bmg="off").color_code, state=tkinter.DISABLED ) self.text_track_format.grid(row=2, column=1, sticky="NEWS") @@ -37,7 +37,7 @@ class Window(AbstractPreviewWindow): self.text_track_format.configure(yscrollcommand=self.scrollbar_track_preview.set) - for color in MKWColor.get_all_colors(): + for color in MKWColor.all_colors: self.text_track_format.tag_configure(color.bmg, foreground=color.color_code) self.text_track_format.tag_configure("error", background="red", foreground="white") diff --git a/source/gui/preview/track_selecting.py b/source/gui/preview/track_selecting.py index 6298003..c2bcd37 100644 --- a/source/gui/preview/track_selecting.py +++ b/source/gui/preview/track_selecting.py @@ -2,7 +2,7 @@ import tkinter from tkinter import ttk from typing import TYPE_CHECKING -from source.mkw.MKWColor import MKWColor +from source.mkw import MKWColor from source.gui.preview import AbstractPreviewWindow from source.gui import better_gui_error @@ -27,7 +27,9 @@ class Window(AbstractPreviewWindow): self.entry_template_input.grid(row=1, column=1, columnspan=2, sticky="NEWS") self.entry_template_input.bind("", self.preview) - self.text_track_format = tkinter.Text(self, width=40, bg="black", fg=MKWColor("off").color_code, state=tkinter.DISABLED) + self.text_track_format = tkinter.Text( + self, width=40, bg="black", fg=MKWColor.get(bmg="off").color_code, state=tkinter.DISABLED + ) self.text_track_format.grid(row=2, column=1, sticky="NEWS") self.text_track_select = tkinter.Text(self, width=20, bg="black", state=tkinter.DISABLED) diff --git a/source/gui/preview/track_sorting.py b/source/gui/preview/track_sorting.py index 17215e9..29c2c68 100644 --- a/source/gui/preview/track_sorting.py +++ b/source/gui/preview/track_sorting.py @@ -2,7 +2,7 @@ import tkinter from tkinter import ttk from typing import TYPE_CHECKING -from source.mkw.MKWColor import MKWColor +from source.mkw import MKWColor from source.gui.preview import AbstractPreviewWindow from source.gui import better_gui_error @@ -27,7 +27,7 @@ class Window(AbstractPreviewWindow): self.entry_template_input.bind("", self.preview) self.text_track_format = tkinter.Text( - self, background="black", foreground=MKWColor("off").color_code, state=tkinter.DISABLED + self, background="black", foreground=MKWColor.get(bmg="off").color_code, state=tkinter.DISABLED ) self.text_track_format.grid(row=2, column=1, sticky="NEWS") diff --git a/source/mkw/MKWColor.py b/source/mkw/MKWColor.py index c96f38b..e7b8b48 100644 --- a/source/mkw/MKWColor.py +++ b/source/mkw/MKWColor.py @@ -1,3 +1,5 @@ +from dataclasses import dataclass + from source.translation import translate as _ @@ -6,71 +8,74 @@ class ColorNotFound(Exception): super().__init__(_("CANNOT_FIND_COLOR", ' "', color_data, '"')) +@dataclass(init=True, slots=True) class MKWColor: """ Represent a color that can be used inside MKW files """ - - _all_colors: list[dict] = [ - {"bmg": "yor7", "hex": 0xF5090B, "name": "apple red"}, - {"bmg": "yor6", "hex": 0xE82C09, "name": "dark red"}, - {"bmg": "yor5", "hex": 0xE65118, "name": "dark orange"}, # flame - {"bmg": "yor4", "hex": 0xFF760E, "name": "orange"}, # pumpkin - {"bmg": "yor3", "hex": 0xFFA61F, "name": "light orange"}, # bright yellow - {"bmg": "yor2", "hex": 0xFEBC1F, "name": "yellow"}, # ripe mango - {"bmg": "yor1", "hex": 0xFFE71F, "name": "light yellow"}, - {"bmg": "yor0", "hex": 0xFFFF22, "name": "neon yellow"}, - {"bmg": "blue2", "hex": 0x1170EC, "name": "dark blue"}, - {"bmg": "blue1", "hex": 0x75B5F6, "name": "azure"}, - {"bmg": "green", "hex": 0x0EB00A, "name": "green"}, - {"bmg": "yellow", "hex": 0xFFFD1E, "name": "neon yellow 2"}, - {"bmg": "red4", "hex": 0xEE0C10, "name": "vivid red"}, - {"bmg": "red3", "hex": 0xFF0308, "name": "red"}, - {"bmg": "red2", "hex": 0xF14A4E, "name": "light red"}, - {"bmg": "red1", "hex": 0xE46C74, "name": "pink"}, - {"bmg": "white", "hex": 0xFFFFFF, "name": "white"}, - {"bmg": "clear", "hex": 0x000000, "name": "clear"}, - {"bmg": "off", "hex": 0xDDDDDD, "name": "off"}, - ] - - __slots__ = ("bmg", "hex", "name") - - def __init__(self, color_data: any, color_key: str = "name"): - colors = list(filter(lambda color: color[color_key].upper() == color_data.upper(), self._all_colors)) - if len(colors) == 0: raise ColorNotFound(color_data) - - for key, value in colors[0].items(): - setattr(self, key, value) - - @classmethod - def get_all_colors(cls): - for color in cls._all_colors: - yield cls(color["name"]) - + + bmg: str + hexadecimal: hex + name: str + @property def color_code(self) -> str: """ - Return the color code that can be used in tkinter + Return a color code that can be used in tkinter :return: the color code """ + return f"#{self.hexadecimal:06X}" - return f"#{self.hex:06X}" + @property + def raw(self) -> str: + """ + return the special control character to start coloring a text + :return: return the color control character + """ + return r"\c{" + self.bmg + "}" + + def color_text(self, text: str) -> str: + """ + color a text, then reset the color + :param text: text to color + :return: return the formatted text with the color + """ + return f'{self.raw}{text}{get(bmg="off").raw}' -def bmg_color_raw(color_name: str) -> str: +all_colors: list[MKWColor] = [ + MKWColor(bmg="white", hexadecimal=0xFFFFFF, name="white"), + MKWColor(bmg="clear", hexadecimal=0x000000, name="clear"), + MKWColor(bmg="off", hexadecimal=0xDDDDDD, name="off"), + + MKWColor(bmg="yor7", hexadecimal=0xF5090B, name="apple red"), + MKWColor(bmg="yor6", hexadecimal=0xE82C09, name="dark red"), + MKWColor(bmg="yor5", hexadecimal=0xE65118, name="dark orange"), # flame + MKWColor(bmg="yor4", hexadecimal=0xFF760E, name="orange"), # pumpkin + MKWColor(bmg="yor3", hexadecimal=0xFFA61F, name="light orange"), # bright yellow + MKWColor(bmg="yor2", hexadecimal=0xFEBC1F, name="yellow"), # ripe mango + MKWColor(bmg="yor1", hexadecimal=0xFFE71F, name="light yellow"), + MKWColor(bmg="yor0", hexadecimal=0xFFFF22, name="neon yellow"), + MKWColor(bmg="blue2", hexadecimal=0x1170EC, name="dark blue"), + MKWColor(bmg="blue1", hexadecimal=0x75B5F6, name="azure"), + MKWColor(bmg="green", hexadecimal=0x0EB00A, name="green"), + MKWColor(bmg="yellow", hexadecimal=0xFFFD1E, name="neon yellow 2"), + MKWColor(bmg="red4", hexadecimal=0xEE0C10, name="vivid red"), + MKWColor(bmg="red3", hexadecimal=0xFF0308, name="red"), + MKWColor(bmg="red2", hexadecimal=0xF14A4E, name="light red"), + MKWColor(bmg="red1", hexadecimal=0xE46C74, name="pink"), +] + + +def get(**color_datas) -> MKWColor: """ - Useful shortcut to place a color - :param color_name: name of the color - :return: return the color character + Get a original track object from keys and its value + :param color_datas: dictionary of track key and their value + :return: the corresponding original track """ - return r"\c{" + MKWColor(color_name).bmg + "}" - - -def bmg_color_text(color_name: str, text: str) -> str: - """ - Useful shortcut to color a text - :param color_name: name of the color - :param text: text to color - :return: return the formatted text with the color - """ - return f'{bmg_color_raw(color_name)}{text}{bmg_color_raw("off")}' + try: + return next(filter( + lambda color: all(getattr(color, key) == value for key, value in color_datas.items()), + all_colors + )) + except StopIteration: raise ColorNotFound(color_datas) diff --git a/source/mkw/ModConfig.py b/source/mkw/ModConfig.py index 0f3e9cf..c6e3e85 100644 --- a/source/mkw/ModConfig.py +++ b/source/mkw/ModConfig.py @@ -8,10 +8,9 @@ from PIL import Image from source import threaded from source.mkw import Tag, Slot from source.mkw.Cup import Cup -from source.mkw.MKWColor import bmg_color_text, bmg_color_raw +from source.mkw import MKWColor from source.mkw.ModSettings import AbstractModSettings from source.mkw.Track import CustomTrack, DefaultTrack, Arena -from source.mkw import OriginalTrack from source.progress import Progress from source.safe_eval import safe_eval, multiple_safe_eval from source.wt.szs import SZSPath @@ -170,9 +169,8 @@ class ModConfig: :return: the modconfig environment """ return { - "mod_config": self, - "bmg_color_raw": bmg_color_raw, - "bmg_color_text": bmg_color_text + "mod_config": self, + "get_color": MKWColor.get } | ( base_env if base_env is not None else {} ) diff --git a/source/safe_eval/safe_eval.py b/source/safe_eval/safe_eval.py index ecb3f9c..effef6d 100644 --- a/source/safe_eval/safe_eval.py +++ b/source/safe_eval/safe_eval.py @@ -86,18 +86,6 @@ def safe_eval(template: "TemplateSafeEval", env: "Env" = None, macros: dict[str, elif node.id in args: raise SafeEvalException(_("CANNOT_SET_ARGUMENT", ' : "', node.id, '"')) - # when calling any function - case ast.Call: - # ban the function and method from the environment - for callnode in ast.walk(node.func): - if isinstance(callnode, ast.Attribute): - for attrnode in ast.walk(callnode.value): - if isinstance(attrnode, ast.Name): - if attrnode.id in globals_ | locals_ or attrnode.id in args: - raise SafeEvalException( - _("CALLING_FUNCTION_NOT_ALLOWED", ' : "', callnode.attr, '"') - ) - # when assigning a value with ":=" case ast.NamedExpr: # embed the value into a deepcopy, to avoid interaction with class attribute @@ -139,3 +127,6 @@ def safe_eval(template: "TemplateSafeEval", env: "Env" = None, macros: dict[str, lambda_template = eval(compile(expression, "", "eval"), globals_, locals_) self.safe_eval_cache[template_key] = lambda_template # cache the callable for potential latter call return lambda_template + + +# TODO: disable some method and function call. for example, mod_config.path.unlink() is dangerous !