module filtering subclasses have been simplified to made the code easier to read with less directory inside other useless directory and comment have been added

This commit is contained in:
Faraphel 2022-07-31 23:05:54 +02:00
parent 547e9108da
commit 59106d8ed9
30 changed files with 223 additions and 223 deletions

View file

@ -1,7 +1,6 @@
import tkinter
from tkinter import ttk
from source.translation import translate as _
from source.gui.preview import track_formatting
ModConfig: any

View file

@ -23,15 +23,15 @@ class AbstractPreviewWindow(tkinter.Toplevel, ABC):
super().__init__()
...
def get_preview_window_class(name: str) -> Type[AbstractPreviewWindow]:
@classmethod
def get(cls, name: str) -> Type["AbstractPreviewWindow"]:
"""
Return the windows class object from its name
:param name: name of the window class
:return: the window class object
"""
for window_class in filter(lambda cls: cls.name == name, AbstractPreviewWindow.__subclasses__()):
return window_class
for subclass in filter(lambda subclass: subclass.name == name, cls.__subclasses__()):
return subclass
raise InvalidPreviewWindowName(name)

View file

@ -8,7 +8,7 @@ from source import threaded
from source.mkw import Tag
from source.mkw.Cup import Cup
from source.mkw.MKWColor import bmg_color_text
from source.mkw.ModSettings import ModSettings
from source.mkw.ModSettings import AbstractModSettings
from source.mkw.Track import Track
import json
@ -65,8 +65,10 @@ class ModConfig:
self.macros: dict = macros if macros is not None else {}
self.messages: dict = messages if messages is not None else {}
self.global_settings: dict = ModSettings(global_settings)
self.specific_settings: dict = ModSettings(specific_settings if specific_settings is not None else {})
self.global_settings: dict = AbstractModSettings.get(global_settings)
self.specific_settings: dict = AbstractModSettings.get(
specific_settings if specific_settings is not None else {}
)
self.name: str = name
self.nickname: str = nickname if nickname is not None else name

View file

@ -1,9 +1,10 @@
import tkinter
from tkinter import ttk
from source.mkw.ModSettings.TypeSettings import AbstractTypeSettings
from source.mkw.ModSettings import AbstractModSettings
class Choices(AbstractTypeSettings):
class Choices(AbstractModSettings):
"""
This setting type allow you to input a string text.
You can optionally add a "preview" to allow the user to use a window to select the value.

View file

@ -1,11 +1,11 @@
import tkinter
from tkinter import ttk
from source.mkw.ModSettings.TypeSettings import AbstractTypeSettings
from source.gui.preview import get_preview_window_class
from source.mkw.ModSettings import AbstractModSettings
from source.gui.preview import AbstractPreviewWindow
class String(AbstractTypeSettings):
class String(AbstractModSettings):
"""
This setting type allow you to input a string text.
You can optionally add a "preview" to allow the user to use a window to select the value.
@ -30,7 +30,7 @@ class String(AbstractTypeSettings):
if self.preview is not None:
button = ttk.Button(
master, text="...", width=3,
command=lambda: get_preview_window_class(
command=lambda: AbstractPreviewWindow.get(
self.preview
)(master.master.master.master.mod_config, value_variable)
)

View file

@ -1,31 +0,0 @@
import tkinter
from tkinter import ttk
from abc import ABC, abstractmethod
class AbstractTypeSettings(ABC):
type: str # type name of the settings
value: str # value for the settings
enabled: bool # is the settings enabled
@abstractmethod
def __init__(self, value: str = None, preview: str = None, enabled: bool = False):
...
@abstractmethod
def tkinter_show(self, master: ttk.LabelFrame, checkbox) -> None:
"""
Show the option inside a tkinter widget
:master: master widget
:checkbox: checkbox inside the labelframe allowing to enable or disable the setting
"""
master.grid_rowconfigure(1, weight=1)
master.grid_columnconfigure(1, weight=1)
enabled_variable = tkinter.BooleanVar(master, value=self.enabled)
enabled_variable.trace_add("write", lambda *_: setattr(self, "enabled", enabled_variable.get()))
checkbox.configure(variable=enabled_variable)
...
from source.mkw.ModSettings.TypeSettings import String, Choices

View file

@ -1,4 +1,6 @@
from source.mkw.ModSettings.TypeSettings import AbstractTypeSettings
import tkinter
from tkinter import ttk
from abc import ABC, abstractmethod
class InvalidSettingsType(Exception):
@ -6,21 +8,51 @@ class InvalidSettingsType(Exception):
super().__init__(f"Error : Type of mod settings '{settings_type}' not found.")
class ModSettings:
def __new__(cls, settings_dict: dict) -> dict[str, AbstractTypeSettings]:
class AbstractModSettings(ABC):
"""
Base class for every different type of ModSettings
"""
type: str # type name of the settings
value: str # value for the settings
enabled: bool # is the settings enabled
@abstractmethod
def __init__(self, value: str = None, preview: str = None, enabled: bool = False):
...
@abstractmethod
def tkinter_show(self, master: ttk.LabelFrame, checkbox) -> None:
"""
Show the option inside a tkinter widget
:master: master widget
:checkbox: checkbox inside the labelframe allowing to enable or disable the setting
"""
master.grid_rowconfigure(1, weight=1)
master.grid_columnconfigure(1, weight=1)
enabled_variable = tkinter.BooleanVar(master, value=self.enabled)
enabled_variable.trace_add("write", lambda *_: setattr(self, "enabled", enabled_variable.get()))
checkbox.configure(variable=enabled_variable)
...
@classmethod
def get(cls, settings_dict: dict) -> dict[str, "AbstractModSettings"]:
"""
Load all the settings in mod_settings_dict
:param settings_dict: dictionnary containing all the settings defined for the mod
:param settings_dict: dictionary containing all the settings defined for the mod
"""
settings: dict[str, AbstractTypeSettings] = {}
settings: dict[str, AbstractModSettings] = {}
for settings_name, settings_data in settings_dict.items():
for subclass in filter(
lambda subclass: subclass.type == settings_data["type"], AbstractTypeSettings.__subclasses__()
):
for subclass in filter(lambda subclass: subclass.type == settings_data["type"], cls.__subclasses__()):
settings_data.pop("type")
settings[settings_name] = subclass(**settings_data)
break
else: raise InvalidSettingsType(settings_name)
return settings
# these import load the different ModSettings, and so get_mod_settings will be able to fetch them with __subclasses__
from source.mkw.ModSettings import Choices, String

View file

@ -2,7 +2,7 @@ from io import BytesIO
from typing import Generator, IO
from source.mkw.Patch import *
from source.mkw.Patch.PatchOperation import PatchOperation
from source.mkw.Patch.PatchOperation import AbstractPatchOperation
from source.mkw.Patch.PatchObject import PatchObject
from source.wt.szs import SZSPath
@ -55,7 +55,7 @@ class PatchFile(PatchObject):
for operation_name, operation in self.configuration.get("operation", {}).items():
# process every operation and get the new patch_path (if the name is changed)
# and the new content of the patch
patch_name, patch_content = PatchOperation(operation_name)(**operation).patch(
patch_name, patch_content = AbstractPatchOperation.get(operation_name)(**operation).patch(
self.patch, patch_name, patch_content
)

View file

@ -1,14 +1,14 @@
from io import BytesIO
from typing import IO
from source.mkw.Patch.PatchOperation.Operation import *
from source.mkw.Patch.PatchOperation import AbstractPatchOperation
from source.wt import bmg
Patch: any
class BmgDecoder(AbstractOperation):
class BmgDecoder(AbstractPatchOperation):
"""
decode a bmg file to a txt file
"""

View file

@ -1,13 +1,13 @@
from io import BytesIO
from typing import IO
from source.mkw.Patch.PatchOperation.Operation import *
from source.mkw.Patch.PatchOperation import AbstractPatchOperation
from source.wt import bmg
Patch: any
class BmgEncoder(AbstractOperation):
class BmgEncoder(AbstractPatchOperation):
"""
encode a bmg file to a txt file
"""

View file

@ -1,6 +1,7 @@
from source.mkw.Patch.PatchOperation.Operation.BmgTxtEditor.Layer import *
from source.mkw.Patch.PatchOperation.BmgTxtEditor import AbstractLayer
from source.wt import ctc
Patch: any

View file

@ -1,9 +1,10 @@
import re
from source.mkw.Patch.PatchOperation.Operation.BmgTxtEditor.Layer import *
from source.mkw.Patch.PatchOperation.BmgTxtEditor import AbstractLayer
from source.mkw.Track import Track
from source.wt import bmg
Patch: any

View file

@ -1,4 +1,4 @@
from source.mkw.Patch.PatchOperation.Operation.BmgTxtEditor.Layer import *
from source.mkw.Patch.PatchOperation.BmgTxtEditor import AbstractLayer
Patch: any

View file

@ -1,6 +1,7 @@
from source.mkw.Patch.PatchOperation.Operation.BmgTxtEditor.Layer import *
from source.mkw.Patch.PatchOperation.BmgTxtEditor import AbstractLayer
from source.wt import bmg
Patch: any

View file

@ -1,6 +1,6 @@
import re
from source.mkw.Patch.PatchOperation.Operation.BmgTxtEditor.Layer import *
from source.mkw.Patch.PatchOperation.BmgTxtEditor import AbstractLayer
Patch: any

View file

@ -0,0 +1,62 @@
from io import BytesIO
from typing import IO
from abc import ABC, abstractmethod
from source.mkw.Patch import *
from source.mkw.Patch.PatchOperation import AbstractPatchOperation
Layer: any
Patch: any
class AbstractLayer(ABC):
"""
Represent a Layer for a bmgtxt patch
"""
@abstractmethod
def patch_bmg(self, patch: "Patch", decoded_content: str) -> str:
"""
Patch a bmg with the actual layer. Return the new bmg content.
"""
@classmethod
def get(cls, layer: dict) -> "Layer":
"""
return the correct type of layer corresponding to the layer mode
:param layer: the layer to load
"""
for subclass in filter(lambda subclass: subclass.mode == layer["mode"], cls.__subclasses__()):
layer.pop("mode")
return subclass(**layer)
raise InvalidBmgLayerMode(layer["mode"])
class BmgTxtEditor(AbstractPatchOperation):
"""
edit a decoded bmg
"""
type = "bmgtxt-edit"
def __init__(self, layers: list[dict]):
"""
:param layers: layers
"""
self.layers = layers
def patch(self, patch: "Patch", file_name: str, file_content: IO) -> (str, IO):
decoded_content: str = file_content.read().decode("utf-8")
for layer in self.layers:
decoded_content = AbstractLayer.get(layer).patch_bmg(patch, decoded_content)
patch_content: IO = BytesIO(decoded_content.encode("utf-8"))
return file_name, patch_content
# Load the subclasses so that get_layer can filter them.
from source.mkw.Patch.PatchOperation.BmgTxtEditor import (
CTFileLayer, FormatOriginalTrackLayer, IDLayer, PatchLayer, RegexLayer
)

View file

@ -1,14 +1,14 @@
from io import BytesIO
from typing import IO
from source.mkw.Patch.PatchOperation.Operation import *
from source.mkw.Patch.PatchOperation import AbstractPatchOperation
from source.wt import img
Patch: any
class ImageDecoder(AbstractOperation):
class ImageDecoder(AbstractPatchOperation):
"""
decode a game image to a image file
"""

View file

@ -1,6 +1,6 @@
from PIL import ImageDraw, Image
from source.mkw.Patch.PatchOperation.Operation.ImageEditor.Layer import *
from source.mkw.Patch.PatchOperation.ImageEditor import AbstractLayer
Patch: any

View file

@ -1,7 +1,7 @@
from PIL import Image
from source.mkw.Patch import *
from source.mkw.Patch.PatchOperation.Operation.ImageEditor.Layer import *
from source.mkw.Patch.PatchOperation.ImageEditor import AbstractLayer
class ImageLayer(AbstractLayer):
@ -36,3 +36,4 @@ class ImageLayer(AbstractLayer):
)
return image

View file

@ -1,7 +1,7 @@
from PIL import ImageFont, ImageDraw, Image
from source.mkw.Patch import *
from source.mkw.Patch.PatchOperation.Operation.ImageEditor.Layer import *
from source.mkw.Patch.PatchOperation.ImageEditor import AbstractLayer
class TextLayer(AbstractLayer):

View file

@ -1,8 +1,13 @@
from io import BytesIO
from typing import IO
from abc import abstractmethod, ABC
from PIL import Image
from source.mkw.Patch import *
from source.mkw.Patch.PatchOperation import AbstractPatchOperation
Layer: any
Patch: any
@ -58,6 +63,42 @@ class AbstractLayer(ABC):
Patch an image with the actual layer. Return the new image.
"""
@classmethod
def get(cls, layer: dict) -> "Layer":
"""
return the correct type of layer corresponding to the layer mode
:param layer: the layer to load
"""
for subclass in filter(lambda subclass: subclass.type == layer["type"], cls.__subclasses__()):
layer.pop("type")
return subclass(**layer)
raise InvalidImageLayerType(layer["type"])
from source.mkw.Patch.PatchOperation.Operation.ImageEditor.Layer import ColorLayer, ImageLayer, TextLayer
__all__ = ["AbstractLayer", "ColorLayer", "ImageLayer", "TextLayer"]
class ImageGenerator(AbstractPatchOperation):
"""
generate a new image based on a file and apply a generator on it
"""
type = "img-edit"
def __init__(self, layers: list[dict]):
self.layers: list["Layer"] = [AbstractLayer.get(layer) for layer in layers]
def patch(self, patch: "Patch", file_name: str, file_content: IO) -> (str, IO):
image = Image.open(file_content).convert("RGBA")
for layer in self.layers:
image = layer.patch_image(patch, image)
patch_content = BytesIO()
image.save(patch_content, format="PNG")
patch_content.seek(0)
return file_name, patch_content
# Load the class so that __subclasses__ can find them
from source.mkw.Patch.PatchOperation.ImageEditor import (
ColorLayer, ImageLayer, TextLayer
)

View file

@ -1,14 +1,14 @@
from io import BytesIO
from typing import IO
from source.mkw.Patch.PatchOperation.Operation import *
from source.mkw.Patch.PatchOperation import AbstractPatchOperation
from source.wt import img
Patch: any
class ImageEncoder(AbstractOperation):
class ImageEncoder(AbstractPatchOperation):
"""
encode an image to a game image file
"""

View file

@ -1,17 +0,0 @@
from abc import ABC, abstractmethod
Patch: any
class AbstractLayer(ABC):
@abstractmethod
def patch_bmg(self, patch: "Patch", decoded_content: str) -> str:
"""
Patch a bmg with the actual layer. Return the new bmg content.
"""
from source.mkw.Patch.PatchOperation.Operation.BmgTxtEditor.Layer import IDLayer, RegexLayer, CTFileLayer, PatchLayer, \
FormatOriginalTrackLayer
__all__ = ["AbstractLayer", "IDLayer", "RegexLayer", "CTFileLayer", "PatchLayer", "FormatOriginalTrackLayer"]

View file

@ -1,45 +0,0 @@
from io import BytesIO
from typing import IO
from source.mkw.Patch import *
from source.mkw.Patch.PatchOperation.Operation import *
from source.mkw.Patch.PatchOperation.Operation.BmgTxtEditor.Layer import *
class BmgTxtEditor(AbstractOperation):
"""
edit a decoded bmg
"""
type = "bmgtxt-edit"
def __init__(self, layers: list[dict]):
"""
:param layers: layers
"""
self.layers = layers
def patch(self, patch: "Patch", file_name: str, file_content: IO) -> (str, IO):
decoded_content: str = file_content.read().decode("utf-8")
for layer in self.layers:
decoded_content = self.Layer(layer).patch_bmg(patch, decoded_content)
patch_content: IO = BytesIO(decoded_content.encode("utf-8"))
return file_name, patch_content
class Layer:
"""
represent a layer for a bmg-edit
"""
def __new__(cls, layer: dict) -> "Layer":
"""
return the correct type of layer corresponding to the layer mode
:param layer: the layer to load
"""
for subclass in filter(lambda subclass: subclass.mode == layer["mode"],
AbstractLayer.__subclasses__()):
layer.pop("mode")
return subclass(**layer)
raise InvalidBmgLayerMode(layer["mode"])

View file

@ -1,47 +0,0 @@
from io import BytesIO
from typing import IO
from PIL import Image
from source.mkw.Patch import *
from source.mkw.Patch.PatchOperation.Operation import *
from source.mkw.Patch.PatchOperation.Operation.ImageEditor.Layer import *
class ImageGenerator(AbstractOperation):
"""
generate a new image based on a file and apply a generator on it
"""
type = "img-edit"
def __init__(self, layers: list[dict]):
self.layers: list["Layer"] = [self.Layer(layer) for layer in layers]
def patch(self, patch: "Patch", file_name: str, file_content: IO) -> (str, IO):
image = Image.open(file_content).convert("RGBA")
for layer in self.layers:
image = layer.patch_image(patch, image)
patch_content = BytesIO()
image.save(patch_content, format="PNG")
patch_content.seek(0)
return file_name, patch_content
class Layer:
"""
represent a layer for an image generator
"""
def __new__(cls, layer: dict) -> "Layer":
"""
return the correct type of layer corresponding to the layer mode
:param layer: the layer to load
"""
for subclass in filter(lambda subclass: subclass.type == layer["type"],
AbstractLayer.__subclasses__()):
layer.pop("type")
return subclass(**layer)
raise InvalidImageLayerType(layer["type"])

View file

@ -1,22 +0,0 @@
from abc import ABC, abstractmethod
from typing import IO
Patch: any
Layer: any
class AbstractOperation(ABC):
mode: str # name of the operation
@abstractmethod
def patch(self, patch: "Patch", file_name: str, file_content: IO) -> (str, IO):
"""
patch a file and return the new file_path (if changed) and the new content of the file
"""
from source.mkw.Patch.PatchOperation.Operation import ImageDecoder, ImageEncoder, Rename, Special, StrEditor, \
BmgTxtEditor, ImageEditor, BmgEncoder, BmgDecoder
__all__ = ["AbstractOperation", "ImageDecoder", "ImageEncoder", "Rename",
"Special", "StrEditor", "BmgTxtEditor", "ImageEditor", "BmgEncoder", "BmgDecoder"]

View file

@ -1,12 +1,12 @@
from typing import IO
from source.mkw.Patch.PatchOperation.Operation import *
from source.mkw.Patch.PatchOperation import AbstractPatchOperation
Patch: any
class Rename(AbstractOperation):
class Rename(AbstractPatchOperation):
"""
Rename the output file
"""

View file

@ -1,12 +1,12 @@
from typing import IO
from source.mkw.Patch.PatchOperation.Operation import *
from source.mkw.Patch.PatchOperation import AbstractPatchOperation
Patch: any
class Special(AbstractOperation):
class Special(AbstractPatchOperation):
"""
use a file defined as special in the patch to replace the current file content
"""

View file

@ -1,12 +1,12 @@
from io import BytesIO
from typing import IO
from source.mkw.Patch.PatchOperation.Operation import *
from source.mkw.Patch.PatchOperation import AbstractPatchOperation
from source.mkw.Patch import *
from source.wt import wstrt
class StrEditor(AbstractOperation):
class StrEditor(AbstractPatchOperation):
"""
patch the main.dol file
"""

View file

@ -1,18 +1,39 @@
from source.mkw.Patch import *
from source.mkw.Patch.PatchOperation.Operation import *
from abc import ABC, abstractmethod
from typing import IO, Type
Patch: any
Layer: any
class PatchOperation:
class AbstractPatchOperation(ABC):
"""
Represent an operation that can be applied onto a patch to modify it before installing
An AbstractPatchOperation representing any PatchOperation that can be used on one of the game files
"""
def __new__(cls, name) -> "Operation":
mode: str # name of the operation
@abstractmethod
def patch(self, patch: "Patch", file_name: str, file_content: IO) -> (str, IO):
"""
patch a file and return the new file_path (if changed) and the new content of the file
"""
@classmethod
def get(cls, name: str) -> Type["AbstractPatchOperation"]:
"""
Return an operation from its name
:name: name of the operation
:return: an Operation from its name
"""
for subclass in filter(lambda subclass: subclass.type == name, AbstractOperation.__subclasses__()):
for subclass in filter(lambda subclass: subclass.type == name, cls.__subclasses__()):
return subclass
raise InvalidPatchOperation(name)
# load all the subclass of AbstractPatchOperation to that __subclasses__ can filter them
from source.mkw.Patch.PatchOperation import (
ImageDecoder, ImageEncoder, Rename, Special, StrEditor,
BmgTxtEditor, ImageEditor, BmgEncoder, BmgDecoder
)