====== Gestionnaire d'événements ====== Prenons un exemple : je réalise une application chargée de manipuler une base de données. Il s'agit d'une base de données sur le cinéma. Une partie de l'application concerne les acteurs. Je veux pouvoir les afficher, en ajouter, en modifier... Une autre partie concerne les films. Je veux aussi pouvoir les afficher, les modifier... On peut voir ces deux parties comme des sous-applications et on a intérêt à les rendre aussi indépendantes que possibles. Ainsi, on pourra commencer le développement de l'application « acteurs » et plus tard s'occuper de l'application « films ». Plus tard on pourrait ajouter une 3e application « salles ». Si les applications ne sont pas assez indépendantes, ajouter des fonctionnalités sera très difficile. ===== Des contrôleurs séparés ===== On définit donc un module ''acteurs.py'' et un module ''films.py'' qui contiennent chacun toutes les fonctions nécessaires. ''acteurs.py'' contient ainsi les fonctions : * pour afficher les boutons de menu de l'interface concernant les acteurs, * toutes les fonctions permettant d'agir sur les acteurs dans la base de données Pour se fixer les idées, disons que dans ''acteurs.py'' on ait les besoin suivant : * afficher une liste d'acteurs. On définit une fonctions ''show_list_actors(bdd, window)'' qui se trouve dans ''acteurs.py''. Cette fonction a besoin d'un lien vers la base de données et d'un lien vers la fenêtre. * afficher la liste des films liées à un acteur. C'est la fonction ''show_films_with_actor(id_actor, bdd, window)''. Mais cette fonction est dans ''films.py'' puisqu'elle concerne des films. C'est ennuyeux car ''acteurs.py'' a besoin de connaître une fonction de ''films.py''. De même, ''films.py'' aurait besoin de connaître des fonctions de ''acteurs.py''. Ces références croisées seront difficiles à gérer. On voudrait trouver une solution pour que ''acteurs.py'' ne fasse aucune référence à ''films.py'' et réciproquement. ===== Gestionnaire d'événements ===== On crée plutôt un gestionnaire d'événements dont le rôle est de centraliser les événements et les liens entre modules. Le gestionnaire utilise une [[nsi:terminales:structures:file_abstraite|file]] d'attente. # events.py from files import File class Events: def __init__(self): self.__file = File() def push_event(self, trigger:str, data): event = {"name":name, "data":data} self.__file.enfiler(event) def pop_event(self): if self.__file.est_vide(): return None return self.__file.defiler() Maintenant, si ''acteurs.py'' a besoin de lancer la commande ''show_films_with_actor(id_actor, bdd, window)'', il se contente de faire : events.push_event("films:list:actor", id_actor) ''acteurs.py'' a notifié le gestionnaire d'événements. Il n'a pas à se préoccuper de se qui se passera ensuite, ce n'est pas son rôle. Ainsi, ''acteurs.py'' a juste à connaître l'existence de l'objet créé par ''events.py''. De la même façon, ''films.py'' sera dépendant de ''events.py''. Les références sont décroisées. ===== Le routeur ===== On ajoute un objet routeur dont le rôle est de lire les événements et d'exécuter les demandes correspondantes. Le routeur se charge de lire le prochain événement. L'événement est associé à une chaîne de caractères ''trigger'' qui indique ce qu'il faut faire. Le routeur aura ensuite une liste de routes, c'est à dire une liste de d'actions possibles. Le ''trigger'' devra correspondre à une action possible. # routeur.py class Routeur: def __init__(self, events): ''' events: gestionnaire d'événements de type Events ''' self.__events = events self.__routes = [] def add_route(self, trigger, action): self.__routes.append({"trigger":trigger, "action":action}) def exec(self): ''' exécute le prochain événement ''' event = self.__events.pop_event() if event == None: return trigger = event['trigger'] data = event['data'] for route in self.__routes: if route["trigger"] == trigger: # route trouvée fct = route["action"] fct(data) return ===== Création des routes ===== Par exemple dans ''acteurs.py'' on définirait : # acteurs.py def show_list_acteurs(bdd, window): # le code de la fonction... def show_acteur(id_acteur, events, bdd, window): # le code... # il y aurait a un endroit un bouton pour afficher les films de cet acteur events.push_event("films:list:actor", id_actor) def initialisation_routes(events, bdd, window, routeur): # ci-dessous exemple de la route pour afficher un acteur routeur.add_route("show:actor", lambda data:show_acteur(data, events, bdd, window) # toutes les routes utiles Et dans le programme principal, # main.py # import du module from tkinter import * from routeur import Routeur from events import Events from bdd import BDD import acteurs # Construction de la fenêtre principale «root» window = Tk() window.title('Simple exemple') # titre de la fenêtre # création des objets bdd = BDD() events = Events() routeur = Routeur(events) acteurs.initialisation_routes(events, bdd, window, routeur) # ajout d'une fonction chargée de lire les événements def refresh(): routeur.exec() after(100, refresh) # rappelera refresh dans 100ms after(100, refresh) # fin du contenu root.mainloop() La boucle est lancée. Toutes les 100ms, ''refresh'' s'exécutera. Il s'agira d'exécuter le prochain événement dans la file.