more comment in all modules (except GUI)

This commit is contained in:
Faraphel 2023-03-14 08:44:39 +01:00
parent 88e89209ac
commit f6f2e1ed52
32 changed files with 142 additions and 98 deletions

View file

@ -1,8 +1,5 @@
A faire :
1. Principal : 1. Principal :
- Documenter (Docstring, README, ...) - Documenter (Docstring, ...)
- mode d'emploi (video + pdf) expliquant le fonctionnement - mode d'emploi (video + pdf) expliquant le fonctionnement
2. Bonus : 2. Bonus :

View file

@ -6,13 +6,13 @@ from source.gui.scene import MainMenu
from source.gui.window import GameWindow from source.gui.window import GameWindow
from source.path import path_font from source.path import path_font, path_image
from source.gui.better_pyglet import Label from source.gui.better_pyglet import Label
# Change la police par défaut utilisé pour le Century Gothic # Change la police par défaut utilisé pour le Century Gothic
pyglet.font.add_directory(path_font) pyglet.font.add_directory(path_font)
Label.default_kwargs["font_name"] = "Century Gothic" # NOQA: Label à un "default_kwargs" avec la metaclass Label.default_kwargs["font_name"] = "Century Gothic"
# Créer une nouvelle fenêtre # Créer une nouvelle fenêtre
window = GameWindow( window = GameWindow(
@ -21,7 +21,8 @@ window = GameWindow(
option_path=Path("./option.json") option_path=Path("./option.json")
) )
try: window.set_icon(pyglet.image.load("./assets/image/icon/icon.png")) # Change l'icône de cette fenêtre
try: window.set_icon(pyglet.image.load(path_image / "/icon/icon.png"))
except: pass # NOQA E722 except: pass # NOQA E722
window.set_minimum_size(720, 480) window.set_minimum_size(720, 480)

View file

@ -1,5 +1,7 @@
from cx_Freeze import setup, Executable from cx_Freeze import setup, Executable
from source.path import path_image, path_assets
setup( setup(
name='Bataille Navale', name='Bataille Navale',
description='Bataille Navale', description='Bataille Navale',
@ -8,7 +10,7 @@ setup(
options={ options={
"build_exe": { "build_exe": {
"include_files": ["./assets"], "include_files": [path_assets],
} }
}, },
@ -17,7 +19,7 @@ setup(
executables=[ executables=[
Executable( Executable(
"main.pyw", "main.pyw",
icon="./assets/image/icon/icon.ico", icon=path_image / "/icon/icon.ico",
base="win32gui", base="win32gui",
target_name="Bataille Navale.exe", target_name="Bataille Navale.exe",
shortcut_name="Bataille Navale", shortcut_name="Bataille Navale",

View file

@ -3,35 +3,36 @@ from typing import Callable
class Listener: class Listener:
""" """
The Listener can be subclassed to allow the subclass to add, remove and call event easily. Les classes héritant de Listener permettent d'ajouter, retirer et appeler facilement des événements.
""" """
def __init__(self): def __init__(self):
# dictionnaire des événements et de leurs fonctions associées
self._events_listener: dict[str, set[Callable]] = {} self._events_listener: dict[str, set[Callable]] = {}
def add_listener(self, name: str, callback: Callable): def add_listener(self, name: str, callback: Callable):
""" """
Add a function to an event name Ajoute une fonction à un événement
:param name: the name of the event to react :param name: le nom de l'événement
:param callback: the function to call :param callback: la fonction à appeler
""" """
if name not in self._events_listener: self._events_listener[name] = set() if name not in self._events_listener: self._events_listener[name] = set()
self._events_listener[name].add(callback) self._events_listener[name].add(callback)
def remove_listener(self, name: str, callback: Callable): def remove_listener(self, name: str, callback: Callable):
""" """
Remove a function from an event name Retire une fonction d'un événement
:param name: the event name where to remove the callback :param name: le nom de l'événement
:param callback: the callback function to remove :param callback: la fonction à retirer
""" """
self._events_listener[name].remove(callback) self._events_listener[name].remove(callback)
def trigger_event(self, name: str, *args, **kwargs): def trigger_event(self, name: str, *args, **kwargs):
""" """
Call all the callback attached to an event Appelle les fonctions associées à un événement
:param name: the name of the event to call :param name: le nom de l'événement
:param args: the args of the callbacks :param args: les arguments des fonctions
:param kwargs: the kwargs of the callbacks :param kwargs: les arguments à clé des fonctions
""" """
# .copy() pour que si le listener supprime un de ses événements, la liste de la boucle de change pas de taille # .copy() pour que si le listener supprime un de ses événements, la liste de la boucle de change pas de taille

View file

@ -3,5 +3,7 @@ from typing import Any
class Element(ABC): class Element(ABC):
default_kwargs: dict[str, Any]
def __init_subclass__(cls, **kwargs): def __init_subclass__(cls, **kwargs):
cls.default_kwargs: dict[str, Any] = {} # all subclasses will have their own "default_kwargs" dict cls.default_kwargs = {} # all subclasses will have their own "default_kwargs" dict

View file

@ -16,7 +16,7 @@ if TYPE_CHECKING:
class Client(StoppableThread): class Client(StoppableThread):
""" """
The thread executed on the person who join a room. Ce thread est utilisé pour la personne qui rejoint la salle.
""" """
def __init__(self, window: "Window", def __init__(self, window: "Window",
@ -39,9 +39,10 @@ class Client(StoppableThread):
try: try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as connection: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as connection:
try: try:
# Se connecte à l'opposant
connection.connect((self.ip_address, self.port)) connection.connect((self.ip_address, self.port))
except ConnectionRefusedError: except ConnectionRefusedError:
# Appelle l'événement lorsque la connexion échoue # Appelle la fonction prévue lorsque la connexion échoue
if self.on_connexion_refused is not None: if self.on_connexion_refused is not None:
in_pyglet_context(self.on_connexion_refused) in_pyglet_context(self.on_connexion_refused)
return return
@ -91,13 +92,14 @@ class Client(StoppableThread):
with open(path_old_save, "r", encoding="utf-8") as file: with open(path_old_save, "r", encoding="utf-8") as file:
save_data = json.load(file) save_data = json.load(file)
# paramètres & jeu # paramètres et jeu
settings = PacketSettings.from_connection(connection) settings = PacketSettings.from_connection(connection)
PacketUsername(username=self.username).send_data_connection(connection) PacketUsername(username=self.username).send_data_connection(connection)
enemy_username = PacketUsername.from_connection(connection).username enemy_username = PacketUsername.from_connection(connection).username
if load_old_save: if load_old_save:
# si les joueurs utilise une ancienne sauvegarde, créer la scène du jeu à partir des données
game_scene = in_pyglet_context( game_scene = in_pyglet_context(
self.window.set_scene, self.window.set_scene,
scene.Game.from_json, # depuis le fichier json scene.Game.from_json, # depuis le fichier json
@ -109,6 +111,7 @@ class Client(StoppableThread):
) )
else: else:
# s'il n'y a pas de sauvegarde à utiliser, initialise une nouvelle scène
game_scene = in_pyglet_context( game_scene = in_pyglet_context(
self.window.set_scene, self.window.set_scene,
scene.Game, scene.Game,
@ -124,6 +127,7 @@ class Client(StoppableThread):
my_turn=not settings.host_start my_turn=not settings.host_start
) )
# commence la partie réseau du jeu
game_network( game_network(
thread=self, thread=self,
connection=connection, connection=connection,

View file

@ -18,7 +18,7 @@ if TYPE_CHECKING:
class Host(StoppableThread): class Host(StoppableThread):
""" """
The thread executed on the person who create a room. Le thread utilisé lorsqu'un joueur créer une salle
""" """
def __init__(self, window: "Window", port: int, username: str, settings: "PacketSettings", **kw): def __init__(self, window: "Window", port: int, username: str, settings: "PacketSettings", **kw):
@ -29,6 +29,7 @@ class Host(StoppableThread):
self.settings = settings self.settings = settings
self.port = port self.port = port
# condition utilisée lorsque l'on attend la décision de charger une partie ou non
self.condition_load = Condition() self.condition_load = Condition()
self.accept_load: bool = False self.accept_load: bool = False
@ -54,7 +55,7 @@ class Host(StoppableThread):
path_old_save: Optional[Path] = None path_old_save: Optional[Path] = None
for file in path_save.iterdir(): # cherche une ancienne sauvegarde correspondant à l'ip de l'adversaire for file in path_save.iterdir(): # cherche une ancienne sauvegarde correspondant à l'IP de l'adversaire
if file.stem.startswith(ip_address): if file.stem.startswith(ip_address):
path_old_save = file path_old_save = file
break break
@ -72,7 +73,8 @@ class Host(StoppableThread):
from source.gui.scene import GameLoad from source.gui.scene import GameLoad
in_pyglet_context(self.window.set_scene, GameLoad, path=path_old_save, thread_host=self) in_pyglet_context(self.window.set_scene, GameLoad, path=path_old_save, thread_host=self)
with self.condition_load: self.condition_load.wait() # attend que l'utilisateur choisisse l'option # attend que l'utilisateur choisisse l'option
with self.condition_load: self.condition_load.wait()
PacketLoadOldSave(value=self.accept_load).send_data_connection(connection) PacketLoadOldSave(value=self.accept_load).send_data_connection(connection)
@ -89,6 +91,7 @@ class Host(StoppableThread):
PacketUsername(username=self.username).send_data_connection(connection) PacketUsername(username=self.username).send_data_connection(connection)
if self.accept_load: if self.accept_load:
# si une sauvegarde doit être chargée
game_scene = in_pyglet_context( game_scene = in_pyglet_context(
self.window.set_scene, self.window.set_scene,
scene.Game.from_json, # depuis le fichier json scene.Game.from_json, # depuis le fichier json
@ -100,6 +103,7 @@ class Host(StoppableThread):
) )
else: else:
# s'il n'y a pas de sauvegarde à charger
game_scene = in_pyglet_context( game_scene = in_pyglet_context(
self.window.set_scene, self.window.set_scene,
scene.Game, scene.Game,
@ -115,6 +119,7 @@ class Host(StoppableThread):
my_turn=self.settings.host_start my_turn=self.settings.host_start
) )
# commence la partie réseau du jeu
game_network( game_network(
thread=self, thread=self,
connection=connection, connection=connection,

View file

@ -9,8 +9,15 @@ if TYPE_CHECKING:
def handle_error(window: "Window", exception: Exception): def handle_error(window: "Window", exception: Exception):
"""
Fonction permettant d'afficher le bon message d'erreur du au réseau.
:param window: la fenêtre du jeu
:param exception: l'erreur qui s'est produite
"""
message: str = "Erreur :\n" message: str = "Erreur :\n"
# récupère le message d'erreur selon le type de l'erreur
match type(exception): match type(exception):
case builtins.ConnectionResetError: case builtins.ConnectionResetError:
message += "Perte de connexion avec l'adversaire." message += "Perte de connexion avec l'adversaire."

View file

@ -18,12 +18,13 @@ def game_network(
game_scene: "Game", game_scene: "Game",
): ):
""" """
Run the networking to make the game work and react with the other player Partie réseau permettant au jeu de fonctionner et de réagir avec l'autre joueur
:param game_scene: the scene of the game :param game_scene: la scène du jeu
:param thread: the thread where this function is called. :param thread: le thread dans lequel la fonction est appelé
:param connection: the connection with the other player :param connection: la connexion avec l'autre joueur
""" """
# associe le type de packet avec la fonction correspondante
game_methods: dict[Type["Packet"], Callable] = { game_methods: dict[Type["Packet"], Callable] = {
packet.PacketChat: game_scene.network_on_chat, packet.PacketChat: game_scene.network_on_chat,
packet.PacketBoatPlaced: game_scene.network_on_boat_placed, packet.PacketBoatPlaced: game_scene.network_on_boat_placed,
@ -35,16 +36,19 @@ def game_network(
} }
while True: while True:
# récupère le type de packet reçu
data_type = Packet.type_from_connection(connection) data_type = Packet.type_from_connection(connection)
if data_type is None: if data_type is None:
if thread.stopped: return # vérifie si le thread n'est pas censé s'arrêter # s'il n'y a pas de donnée reçue, vérifie si le thread devrait s'arrêter, sinon ignore
if thread.stopped: return
continue continue
# récupère les données du packet
data = data_type.from_connection(connection) data = data_type.from_connection(connection)
in_pyglet_context( in_pyglet_context(
game_methods[data_type], data # récupère la methode relié ce type de donnée game_methods[data_type], data # récupère la methode relié à ce type de donnée
) # Appelle la méthode. ) # Appelle la méthode.
if thread.stopped: return # vérifie si le thread n'est pas censé s'arrêter if thread.stopped: return # vérifie si le thread n'est pas censé s'arrêter

View file

@ -6,5 +6,5 @@ from source.network.packet.abc import SignalPacket
@dataclass @dataclass
class PacketAskSave(SignalPacket): class PacketAskSave(SignalPacket):
""" """
A packet that is sent when the player wish to save the game. Un packet envoyé quand le joueur souhaite sauvegarder
""" """

View file

@ -6,5 +6,5 @@ from source.network.packet.abc import SignalPacket
@dataclass @dataclass
class PacketBoatPlaced(SignalPacket): class PacketBoatPlaced(SignalPacket):
""" """
A packet that signal that all the boat of the player have been placed Un packet signalant que tous les bateaux du joueur ont été placé
""" """

View file

@ -9,6 +9,9 @@ from source.network.packet.abc import Packet
@dataclass @dataclass
class PacketBoatsData(Packet): class PacketBoatsData(Packet):
"""
Un packet contenant les données de la position de tous les bateaux
"""
boats: np.array = field() boats: np.array = field()

View file

@ -9,7 +9,7 @@ from source.type import Point2D
@dataclass @dataclass
class PacketBombPlaced(SimplePacket): class PacketBombPlaced(SimplePacket):
""" """
A packet that signal that a bomb have been placed on the board Un packet qui signale qu'une bombe à été placé sur la grille
""" """
position: Point2D = field() position: Point2D = field()

View file

@ -9,7 +9,7 @@ from source.type import Point2D
@dataclass @dataclass
class PacketBombState(SimplePacket): class PacketBombState(SimplePacket):
""" """
A packet that signal how a bomb exploded on the board Un packet qui signale qu'une bombe à explosé sur la grille
""" """
position: Point2D = field() position: Point2D = field()

View file

@ -6,7 +6,7 @@ from source.network.packet.abc import VariableLengthPacket
@dataclass @dataclass
class PacketChat(VariableLengthPacket): class PacketChat(VariableLengthPacket):
""" """
A packet that represent a message from the chat Un packet qui représente un message du chat
""" """
message: str = field() message: str = field()

View file

@ -7,7 +7,7 @@ from source.network.packet.abc import SimplePacket
@dataclass @dataclass
class PacketHaveSaveBeenFound(SimplePacket): class PacketHaveSaveBeenFound(SimplePacket):
""" """
A packet that is sent when the player accept or refuse a requested save. Un packet indiquant si le joueur à trouvé un ancien fichier de sauvegarde avec l'opposant
""" """
value: bool = field() # True si requête accepter, sinon False value: bool = field() # True si requête accepter, sinon False

View file

@ -7,7 +7,7 @@ from source.network.packet.abc import SimplePacket
@dataclass @dataclass
class PacketLoadOldSave(SimplePacket): class PacketLoadOldSave(SimplePacket):
""" """
A packet that is sent when the player accept or refuse a requested save. Un packet qui est envoyé lorsque l'hôte accepte ou refuse de charger une ancienne sauvegarde
""" """
value: bool = field() # True si requête accepter, sinon False value: bool = field() # True si requête accepter, sinon False

View file

@ -6,5 +6,5 @@ from source.network.packet.abc import SignalPacket
@dataclass @dataclass
class PacketQuit(SignalPacket): class PacketQuit(SignalPacket):
""" """
A packet that is sent when the player wish to quit a game. Un packet envoyé lorsque le joueur souhaite quitter la partie
""" """

View file

@ -7,7 +7,7 @@ from source.network.packet.abc import SimplePacket
@dataclass @dataclass
class PacketResponseSave(SimplePacket): class PacketResponseSave(SimplePacket):
""" """
A packet that is sent when the player accept or refuse a requested save. Un packet qui est envoyé lorsque le joueur accepte ou refuse une demande de sauvegarde
""" """
value: bool = field() # True si requête accepter, sinon False value: bool = field() # True si requête accepter, sinon False

View file

@ -7,6 +7,10 @@ from source.network.packet.abc import Packet
@dataclass @dataclass
class PacketSettings(Packet): class PacketSettings(Packet):
"""
Un packet contenant tous les paramètres de la partie
"""
grid_width: int = field() grid_width: int = field()
grid_height: int = field() grid_height: int = field()
host_start: bool = field() host_start: bool = field()

View file

@ -5,6 +5,10 @@ from source.network.packet.abc import VariableLengthPacket
@dataclass @dataclass
class PacketUsername(VariableLengthPacket): class PacketUsername(VariableLengthPacket):
"""
Un packet contenant le nom d'utilisateur de l'opposant
"""
username: str = field() username: str = field()
@property @property

View file

@ -9,10 +9,8 @@ T = TypeVar("T", bound="Packet")
class Packet(ABC): class Packet(ABC):
""" """
A packet that can be sent on a socket. Un packet pouvant être envoyé dans un socket.
Multiple subtype of packet can be sent and received in an easier way. Permet aux sous-classes de ce packet d'être envoyé et reçu d'une manière beaucoup plus simple.
The to_bytes and from_connection method need to be defined.
""" """
packet_types: set[T] = set() packet_types: set[T] = set()

View file

@ -10,8 +10,8 @@ T = TypeVar("T", bound="SignalPacket")
class SignalPacket(Packet, ABC): class SignalPacket(Packet, ABC):
""" """
A packet that has for only usage to send a signal thanks to the type of the class. Un packet ne contenant aucune donnée.
It does not hold any other data. Permet de réagir à des événements seulement avec le type de la classe.
""" """
def to_bytes(self) -> bytes: def to_bytes(self) -> bytes:

View file

@ -11,8 +11,8 @@ T = TypeVar("T", bound="SimplePacket")
class SimplePacket(Packet, ABC): class SimplePacket(Packet, ABC):
""" """
A packet with a simple packet format. Un packet avec un format plus simple.
Only the from_bytes and to_bytes method need to be implemented. Se base sur le "packet_format" pour envoyé et reservoir les données.
""" """
packet_format: str = ">" packet_format: str = ">"
@ -21,9 +21,9 @@ class SimplePacket(Packet, ABC):
@abstractmethod @abstractmethod
def from_bytes(cls, data: bytes) -> T: def from_bytes(cls, data: bytes) -> T:
""" """
Convert a bytes object into a packet. Convertie un objet bytes en un SimplePacket
:param data: the data to convert into a packet. Should be "packet_size" long. :param data: les données à charger, doit être "packet_size" de long
:return: a packet corresponding to the bytes. :return: un SimplePacket correspondant à ces données
""" """
pass pass

View file

@ -11,8 +11,7 @@ T = TypeVar("T", bound="VariableLengthPacket")
class VariableLengthPacket(Packet, ABC): class VariableLengthPacket(Packet, ABC):
""" """
A Packet that represent a single value that can be encoded with a variable length. Un packet représentant une seule valeur avec une longueur variable qui peut être encodé, comme une chaîne.
The property "data" and the method "from_bytes" need to be defined.
""" """
packet_format: str = ">I" packet_format: str = ">I"
@ -20,6 +19,11 @@ class VariableLengthPacket(Packet, ABC):
@property @property
@abstractmethod @abstractmethod
def data(self) -> bytes: def data(self) -> bytes:
"""
Donnée à envoyer sur le réseau
:return: la donnée sous la forme d'un objet bytes
"""
pass pass
def to_bytes(self) -> bytes: def to_bytes(self) -> bytes:

View file

@ -9,6 +9,10 @@ if TYPE_CHECKING:
class Option: class Option:
"""
Cette classe permet de modifier, sauvegarder et charger les options.
"""
def __init__(self, window: "GameWindow", def __init__(self, window: "GameWindow",
volume_ambient: float = 0.1, volume_ambient: float = 0.1,
volume_fx: float = 0.1, volume_fx: float = 0.1,
@ -24,7 +28,7 @@ class Option:
self.set_fps_limit(fps_limit) self.set_fps_limit(fps_limit)
self.set_vsync(vsync) self.set_vsync(vsync)
# propriété # propriétés
@staticmethod @staticmethod
def get_volume_ambient() -> float: return media.SoundAmbient.get_volume() def get_volume_ambient() -> float: return media.SoundAmbient.get_volume()

View file

@ -1,10 +1,10 @@
from typing import Union, Callable from typing import Union, Callable
Point2D = tuple[int, int] # a 2D point Point2D = tuple[int, int] # un point 2D
BBox = tuple[int, int, int, int] # a boundary box BBox = tuple[int, int, int, int] # une boîte de collision
ColorRGB = tuple[int, int, int] # a RGB Color ColorRGB = tuple[int, int, int] # une couleur RGB
ColorRGBA = tuple[int, int, int, int] # a RGBA Color ColorRGBA = tuple[int, int, int, int] # une couleur RGBA
DistanceFunc = Callable[["BoxWidget"], int] # a function that return a position / distance DistanceFunc = Callable[["BoxWidget"], int] # une fonction renvoyant une position / distance
Distance = Union[int, DistanceFunc] # a position / distance, represented by a number or a function Distance = Union[int, DistanceFunc] # une position / distance sous la forme d'un nombre ou d'une fonction

View file

@ -3,10 +3,10 @@ from source.type import Point2D, BBox
def in_bbox(point: Point2D, bbox: BBox) -> bool: def in_bbox(point: Point2D, bbox: BBox) -> bool:
""" """
Return true if a point is inside a bounding box Indique si un point est dans un rectangle
:param point: the point to check :param point: le point à vérifier
:param bbox: the bbox where to check the point :param bbox: la bbox à vérifier
:return: True if the point is inside the bbox, False otherwise :return: True si le point est dans la bbox, sinon Faux
""" """
point_x, point_y = point point_x, point_y = point

View file

@ -3,17 +3,15 @@ from typing import Callable, Any
def dict_filter(filter_func: Callable[[Any, Any], bool], dictionary: dict[Any, Any]) -> dict[Any, Any]: def dict_filter(filter_func: Callable[[Any, Any], bool], dictionary: dict[Any, Any]) -> dict[Any, Any]:
""" """
Filter a dict object with the filter function given. Filtre les objets d'un dictionnaire avec la fonction de filtre donnée.
:param filter_func: the function to filter with :param filter_func: La fonction utilisée pour le filtre. Reçois l'argument clé et valeur
:param dictionary: the dictionary to filter :param dictionary: Le dictionnaire à filtrer
:return: the filtered dictionary :return: Le dictionnaire filtrer
Example : Exemple :
filter_func = lambda key, value: key.startswith("valeur") filter_func = lambda key, value: key.startswith("valeur")
dictionary = {"valeur1": 1, "valeur2": 2, "clé1": None} dictionary = {"valeur1": 1, "valeur2": 2, "clé1": None}
-> {"valeur1": 1, "valeur2": 2}
result = {"valeur1": 1, "valeur2": 2}
""" """
return { return {
@ -26,16 +24,15 @@ def dict_filter(filter_func: Callable[[Any, Any], bool], dictionary: dict[Any, A
def dict_filter_prefix(prefix: str, dictionary: dict[str, Any]) -> dict[str, Any]: def dict_filter_prefix(prefix: str, dictionary: dict[str, Any]) -> dict[str, Any]:
""" """
Take only the keys that start with the prefix, and remove this prefix from the keys. Ne garde que les clés qui commencent avec ce préfixe dans le dictionnaire et retire leur préfixe.
:param prefix: the prefix to use :param prefix: le préfixe à utiliser
:param dictionary: the dictionary to filter :param dictionary: le dictionnaire à filtrer
:return: the dictionary with the prefix :return: le dictionnaire avec le préfixe
Example: Exemple :
prefix = "button" prefix = "button"
dictionary = {"button1": 1, "button2": 2, "label1": None} dictionary = {"button1": 1, "button2": 2, "label1": None}
-> {"1": 1, "2": 2}
result = {"1": 1, "2": 2}
""" """
return { return {
@ -48,10 +45,10 @@ def dict_filter_prefix(prefix: str, dictionary: dict[str, Any]) -> dict[str, Any
def dict_add_prefix(prefix: str, dictionary: dict[str, Any]) -> dict[str, Any]: def dict_add_prefix(prefix: str, dictionary: dict[str, Any]) -> dict[str, Any]:
""" """
Add a prefix to every key of the dictionary Ajoute un préfixe à toute les clés d'un dictionnaire
:param prefix: the prefix to add :param prefix: le préfixe à ajouter
:param dictionary: the dictionary to modify :param dictionary: le dictionnaire à modifier
:return: the dictionary with the prefix at the start of the keys :return: le dictionnaire avec le préfixe à chaque clé
""" """
return { return {

View file

@ -5,10 +5,10 @@ from source.type import Point2D
def copy_array_offset(src: np.array, dst: np.array, offset: Point2D) -> None: def copy_array_offset(src: np.array, dst: np.array, offset: Point2D) -> None:
""" """
Copy a numpy array into another one with an offset Copie une matrice dans une autre matrice avec un décalage.
:param src: source array :param src: la matrice source
:param dst: destination array :param dst: la matrice de destination
:param offset: the offset where to copy the array :param offset: le décalage avec lequel copier la matrice
""" """
column, row = offset column, row = offset
width, height = src.shape width, height = src.shape

View file

@ -2,5 +2,10 @@ from datetime import datetime
from pathlib import Path from pathlib import Path
def path_ctime_str(path: Path): def path_ctime_str(path: Path) -> str:
"""
Un raccourci permettant d'obtenir la représentation de la date de création d'un fichier
:param path: le chemin du fichier
:return: la date correspondante sous la forme d'un texte
"""
return datetime.fromtimestamp(path.lstat().st_ctime).strftime('%d/%m/%Y %H:%M:%S') return datetime.fromtimestamp(path.lstat().st_ctime).strftime('%d/%m/%Y %H:%M:%S')

View file

@ -7,8 +7,9 @@ import pyglet
class StoppableThread(Thread): class StoppableThread(Thread):
""" """
A thread that can be stopped. Un thread pouvant être arrêté.
The run method need to check for the "self._stop" variable and return manually if it is true. La méthode "run" doit souvent vérifier la variable self.stopped et faire un return manuellement si cette variable
est vrai.
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -16,17 +17,18 @@ class StoppableThread(Thread):
self.stopped = False self.stopped = False
def stop(self) -> None: def stop(self) -> None:
# indique que le thread devrait s'arrêter dès que possible
self.stopped = True self.stopped = True
def in_pyglet_context(func: Callable, *args, **kwargs) -> Any: def in_pyglet_context(func: Callable, *args, **kwargs) -> Any:
""" """
This function can be call in a thread. It will call the "func" in the pyglet event loop, avoiding Cette fonction doit être appelée dans un thread. Elle appellera une fonction dans la boucle d'événement de pyglet,
some operation that are not allowed outside of the pyglet context, and return its result ce qui permet d'éviter certaines opérations illégales en dehors de ce contexte et renvoie le résultat.
:param func: the function to call in the pyglet context :param func: la fonction à appeler
:param args: the args of the function :param args: les arguments de la fonction
:param kwargs: the kwargs of the function :param kwargs: les arguments à clé de la fonction
:return: the result of the function :return: le résultat de la fonction
""" """
queue = Queue() queue = Queue()