Itération 0.1 : Squelette de l’Interface Graphique

Comme on a déjà pas mal parlé de l’interface graphique et qu’on commence à avoir une bonne idée de ce qu’on veut, je pense que cette itération est le moment de créer le squelette sur lequel les autres packages pourront se reposer pour afficher des vues.

Phase d’inception :

L’Interface c’est principalement un moniteur d’informations et une télécommande qui permet d’envoyer des actions à faire, des informations ou encore des évènements.

Et tout comme l’application entière cette Vue Globale est divisée en plusieurs parties. Chacune d’entre elle offrant une panoplie d’informations et d’outils permettant de contrôler et agir sur l’application.

Par exemple on aura la vue des charts qui va afficher les données d’un marché, et on aura la vue des actions permettant d’afficher des sous vues différentes dont par exemple la sélection du marché à afficher, ou la vue qui permettra de configurer un indicateur à afficher, etc..

Reprenons le schéma que j’avais fait dans un précédent article, avec quelques modifications :

  1. APPLICATION_TITLE : On a la vue globale, la Fenêtre, avec son titre et des boutons de contrôle (réduire, plein écran et fermer).
  2. PRIMARY_MENU : Le menu global de l’application
  3. CHARTS_WIDGET_VIEW : La vue des Charts, celle où seront affiché les graphiques des données de marchés et d’indicateurs.
  4. ACTIONS_WIDGET_VIEW : La vue des Actions, celle où seront affiché les sous vue d’actions possible (formulaire, informations, menu d’action, etc..)
  5. STATUS_BAR : La barre de status qui permettra d’afficher le statut de l’application et de ce qu’il s’y passe en permanence.

Le Primary Menu :

Devrait pouvoir fournir des accès à des outils globaux ou des actions globales (par exemple un CTRL+Z, les copier/coller, etc.), des vues de configurations globales à l’application par exemple la configuration des clé d’authentification aux différentes plateformes.

Le Top Menu Chart :

Conteneur de bouton d’action de menu lié à la Chart Widget, il devra pouvoir fournir des actions, des routes vers des vues, des événements lié au Chart Widget.

Par exemple, nous voulons ajouter un indicateur à notre Chart, il devrait pouvoir y avoir un bouton pour ça. Ce bouton ouvrirait une vue de configuration dans l’Actions Widget

Chart Widget Views :

Conteneur de vues des Charts. Que ce soit les données des marchés (Candles et indicateurs directement sur les candles), les données d’indicateurs dans des sous Widgets, etc.

Actions Widget Views

Conteneur de vues des Actions. Recevra les vues des actions à entreprendre sur l’application. Par exemple, on voudrait configurer un indicateur pour un marché, le formulaire se retrouvera dedans.
On voudrait configurer les accès utilisateur à une plateforme, on voudrait avoir la vue sur le formulaire de prise de position, on voudrait avoir la vue de configuration d’un pattern, ou une configuration d’intelligence artificielle, toutes ces vues se retrouveront là.

Actions Menu Widget Views :

Conteneur de bouton d’actions de menu lié à la vue Actions Menu, pour avoir accès rapidement à des vues de configuration, par exemple la prise de position sur un marché. Ou quand on veut pouvoir voir un autre marché sur une autre plateforme, etc. 

Phase d’Élaboration :

Première chose qu’on peut constater c’est que le squelette est principalement composé de conteneurs de widget. Il faut donc pouvoir permettre aux packages d’ajouter des widget à chaque conteneur. Que ce soit pour les montrer tout de suite ou pour les montrer plus tard.

Ensuite il faudra que chaque partie puisse communiquer et agir de concert avec les autres vues et le reste de l’application.

Pour cela, j’ai décidé d’utiliser le pattern Médiateur (ou Mediator en anglais). C’est un des design pattern standard qu’on retrouve un peu partout. Grosso merdo il permet à des participants de se transmettre des messages et d’écouter des canaux de communication. 

Les participants ici seront de différents type, container, widget, et package. Chacun ayant un rôle précis à jouer.

Mediator :

Le médiateur peut enregistrer des participants dans une liste interne. Chaque participants s’adresse au médiateur pour lui demander d’envoyer un message à un ou plusieurs autres participants.
Le médiateur se charge donc de parcourir la liste et de trouver tous les participants sélectionné par l’auteur du message et leur transmettre celui-ci. 

C’est un algorithme assez simple qu’on peut illustrer dans un use case comme ceci :

Après cette vue simpliste on doit pouvoir y apporter quelques améliorations. Par exemple il pourrait être intéressant pour un participant d’écouter un canal d’événement. 

Pour ce faire il suffit de rajouter un tableau dans lequel chaque participant pourra s’inscrire sur des nom de canaux d’événements.
Cette inscription sur l’event introduit directement la méthode à exécuter lorsque l’événement se produit.
On pourrait même aller encore plus loin et définir des action qui pourrait se passer avant et/ou après l’événement défini, mais ça je verrais plus tard si l’application l’exige.

Le schéma de use case se transformerait en ceci :

Il suffit de rajouter une méthode d’enregistrement à un événement et de rajouter un lien d’inclusion dans la méthode send pour définir qu’envoyer un message inclus d’envoyer l’événement aussi et d’exécuter la méthode fournie par le Subscriber Three.

Package :

Le Package est un participant qui doit pouvoir envoyer et recevoir des messages venant du médiateur afin de pouvoir communiquer avec les autres participants.
Par exemple, il doit pouvoir envoyer des messages de mise à jours de données sur un canal, cela permet à toutes les vues de se mettre à jour on les inscrit sur ce canal.

Dans un autre sens, le Package peut s’inscrire sur un ou plusieurs canaux d’événements lui permettant de savoir si un bouton à été cliqué, si un formulaire à été envoyé, etc..

Enfin, et c’est le point important de cette partie, c’est le package qui inscrit les vues et les widget dont il a besoin dans chaque container. Chaque action sera donc directement inscrite et liée au package. Cela veut dire aussi que chaque canaux d’événement sera créé au moment de la création d’une action et lié à cette action.

Container :

Un Container est un objet qu’on construira pour qu’il puisse contenir des Vues (Vues qu’on appelera aussi Widget). 

L’idée du Container c’est d’être une classe qu’on puisse modifier en fonction de nos besoins. Par exemple, si on veut construire un menu d’action, on étendra un container et on y ajoutera des méthode spécifiques aux menus. 

Même chose pour un container censé contenir différentes vues (par exemple la vue Charts ou la vue Action).

Widgets :

Un/une widget, c’est une vue de un ou plusieurs éléments. Par exemple, un bouton est un widget, tout comme un formulaire complet avec ses bouton, ses input, etc. est un widget aussi.

Les Widget devront donc être contenu dans les Container. 

Phase de Construction :

Cette itération devient quand même bien fournie, comme on entre dans la phase de construction, je pense qu’il est intéressant d’ouvrir une nouvelle série d’itérations dans notre itération actuelle la 0.1

On va donc se lancer dans la construction du médiator et des différentes classes abstraites pour préparer le terrain aux futurs itérations 0.1.x .

Premièrement on va créer notre nouveau Package UI avec son fichier __init__.py, dedans on y mettra les bases du package d’Interface Utilisateur.

UI\__init__.py

from Core import *

class UIPackage(AbstractPackage):
    def __init__(self, root_app):
        AbstractPackage.__init__(self, root_app, 'UI')

        self.mediator = Mediator()

    def add_ui_subscriber(self, subscriber_class, name):
        self.mediator.register(subscriber_class, name)

On importe la classe abstraite des Package et on étant notre class UIPackage avec, comme on l’a fait avec le package State dans le dernier article.

J’inclus déjà le médiateur et une méthode qui permet d’ajouter un subscriber au médiateur car toute l’idée de ce médiateur est centrée sur la gestion de l’interface graphique et de la communication qu’elle peut avoir avec les autres Package.

Maintenant qu’on a notre classe de Package, on va écrire le Mediator, toujours dans le même fichier d’ailleurs.

UI\__init__.py

from Core import *
SEND_TO_ALL = "All"


class Mediator:
    def __init__(self):
        self.subscribers = {}

    def register(self, ui_subscriber_class, name):
        subscriber = ui_subscriber_class(self, name)
        self.subscribers[subscriber.name] = subscriber
        return self.subscribers[subscriber.name]

    def send(self, message, sender, receiver=SEND_TO_ALL):
        if receiver == SEND_TO_ALL:
            for sub in self.subscribers:
                self.subscribers[sub].receive(message, sender)
        elif isinstance(receiver, list):
            for sub in sender:
                self.subscribers[sub].receive(message, sender)
        elif isinstance(receiver, str):
            self.subscribers[sender].receive(message, sender)

# ...

Donc pour faire simple, pour enregistrer un participant on envoie simplement une classe de participant, le médiateur se chargera d’en faire une instance lui même (qu’il renverra aussi tôt).

Ensuite, pour envoyer un message, le médiateur doit recevoir un message à envoyer, une instance de participants qui envoie le message et un ou plusieurs nom de participant. Si aucun récepteur n’est défini, on envoi le message à tout le monde.

Maintenant qu’on a notre Médiateur on peut construire notre classe abstraite à étendre pour construire d’autre classe de participants.

UI\__init__.py

class AbstractUISubscriber:
    def __init__(self, name, mediator):
        self.mediator = mediator
        self.name = name

    def send(self, message, receiver=SEND_TO_ALL):
        self.mediator.send(message, self, receiver)

    def receive(self, sender, message):
        try:
            method = getattr(self, f'{message.method_name}Action')
            method(sender, **message.params)
        except AttributeError as error:
            print(error)

La structure est simple, la classe doit recevoir le nom du participant et le médiateur en paramètres.
Elle se servira du Médiateur pour envoyer des messages et le médiateur se servira d’elle pour recevoir des messages.

Maintenant qu’on est prêt à accueillir les participants dans un médiateur, on va pouvoir passer à la suite et ouvrir une nouvelle série d’itérations pour construire les différents type de participant, package, container et widget dans un prochain article !

J’espère en tout cas que cet article aura été enrichissant pour vous. Merci encore pour l’attention que vous portez à mon projet 😉
Comme à chaque fois, n’hésitez pas à vous inscrire et poster des commentaires !

Ecrire un commentaire