Étiquette : Python

Projets

Le projet Colors en python sur NumWorks

Colors est un utilitaire développé en python pour la NumWorks. Il permet d’obtenir facilement les formats de couleur RGB et hexadécimal d’une couleur via les valeurs Red, Green, et Blue, qui sont à configurer.

Développement

Étant en train de réfléchir au fonctionnment d’un futur projet (paint.py 🤔🤫). J’avais besoin de fournir un utilitaire facultatif en complément de ce projet pour ne pas l’alourdir. Voici comment est né Colors.

Je commence donc le développement tranquillement, et en un petit week-end, le projet Colors est terminé.

Colors v1

De multiples problèmes m’ont fait perdre beaucoup de temps. Je voulais, à l’origine, afficher une large palette de couleurs, puis grâce à un curseur et des flèches, déplacer le curseur sur un pixel de la palette et nous renvoyer les informations sur la couleur selectionné, c’était palette.py. Problème : j’avais besoin au minimum de 255 pixels en hauteur, et 255 pixels en largeur. Mais l’écran de la NumWorks fait 222 pixels en hauteur. Il m’étais donc impossible d’effectuer la palette précise que je pensais avec une telle taille d’écran.

Parallèlement, en me balandant sur le net afin de comprendre le fonctionnement du format RGB et hexadécimal par rapport aux couleurs, je fus tombé sur un système de sélection de couleur grâce à des barres coulissantes, et j’ai adoré !

Je me suis dit que ce serait parfait pour Palette (qui deviendra Colors). J’ai donc commencé à développer un système reproduisant le fonctionnement d’une barre coulissante. Et comme je suis sympa, je vous mets à disposition la fonction documentée si vous voulez en intégrer vous même dans vos programmes ! 😎

Ayant fini, je demande ce qu’en pense cent20. Grave erreur…

Bon bah mon projet attendra, l’écriture du cahier des charges de Colors v2 est en cours.

La nouvelle fonctionnalité majeure (il n’y en a de nombreuses autres pas forcément visibles) de cette seconde version est l’ajout des teintes :

Colors v2

La v2 plus ou moins terminée, une v3 est en réflexion…

Colors, qui devait être à la base un petit projet abouti en 2 jours, deviendra un gros projet de 2 semaines (pas à temps plein🙄).

Cette troisième et dernière version améliore grandement le rafraichissement des données (en limitant le clignotement), l’interface graphique est également légèrement revu. Cette version supporte également le pavé numérique. Et oui, on fait pas les choses à moitié ! 😏

Colors !

À toi de colorer !

Cet utilitaire est disponible sur https://nsi.xyz/colors

Projets

Factors Game en python sur NumWorks

Factors Game est à l’origine un jeu indépendant jouable en ligne, donc directement depuis un site web. Ceci dit, les Numworks ne peuvent pas encore se connecter à internet… Mais pas de panique, nous nous sommes occupés de l’adaptation du jeu sur calculatrice ! On est sacrément sympa nous…
Arriveras tu à atteindre le niveau 404 ?

Développement

02/04
🧐 Bon, maintenant que t’as fini ton nyan_cat, résolu deux de mes énigmes, corrigé les bugs sur laboHelp, aidé au développement de pixChoice. Ça te dit de faire un jeu sur la calculatrice ?
🤓 Pourquoi pas !

Voici le jeu qui était demandé : Factors Game – The 🐐 of Fun Math Games (mnito.github.io)

🧐 On va afficher les nombres dans des tuiles de 42 pixels par 42 pixels

(2 jours plus tard)

🤓 J’ai fini le système de mouvement, et le système de génération aléatoire de la grille. Il y a juste le front qui est à revoir…


🧐 Bon, pas très UX les déplacements du haut vers le bas comme sur le jeu original avec la résolution de l’écran des calculatrices. On va les faire de gauche à droite du coup, tu dois réadapter le système de mouvement.
🤓 … D’accord

Parallèlement le codage des nombres a été intégré, il est d’Eric Schrafstetter, il est explicité sur cette vidéo.

Ce codage, celui du jeu 2048, dont nous avons amélioré le rendu a été réutilisé pour écrire F A C T O R S en haut à gauche de l’écran !

Petit récap’, mouvements OK, grille OK, maintenant il faut faire le système de calcul entre le curseur et les cases de la grille (vous savez, les divisons, les additions, tout ça tout ça. Ça se fait pas par magie malheureusement).

Aussitôt commencé, aussitôt terminé ! Moi qui redoutait cette phase du développement, ça avait été plus facile que prévu. Plus qu’à faire le système de détection de fin du niveau. Fastoche ! Si le curseur est égal à 1, ou que le curseur est positionné dans la dernière colonne, niveau terminé ! Et on relance la génération de la grille sans oublier d’incrémenter le niveau et calculer la moyenne.

Voilà. En quelques jours, le jeu est théoriquement jouable.

🤓 Plus que le menu, et le système de sauvegarde. Mais le jeu est jouable !
🧐 Ok, pour le menu, on va faire ça, ça, mais il faut ça s’il y a ça, ça ça doit donc s’activer et du coup réactiver ça si ça a été désactivé seulement si ça avait été activé et puis…
🤓 Et si on faisait un cahier des charges ? 😅
🧐 Je m’en occupe.

🤓 Le menu est terminé, il faut sauvegarder le score de chaque niveau ?
🧐 Oui, on va utiliser un dictionnaire.
🤓 Larousse ou Le Petit Robert ?

Menu

🧐Le dictionnaire sert à rien, on aurait pu faire ça avec un tableau, mais laissons le dictionnaire ! 😂

🤓 Bon, je taffe sur une transition entre les niveaux, car les coups de sleep(3), ça va 2 secondes… (3 !)

🧐 Parfait !

Et voilà, en une bonne semaine, le jeu est terminé, il manque plus que le système de sauvegarde, enfin c’est ce que je pensais…

🧐 Bon, ton code est illisible on ne comprend rien. Tu me redéveloppes toute la fonction principale en respectant ces contraintes :

  • Le moins de if imbriqués possibles
  • Chaque if doit donner lieu à 2,3 traitements maximum
  • 61 lignes pour une fonction c’est trop
  • Tout abus de booléen est à éviter

🤓 Mais ça nécessite de redévelopper quasiment toutes les fonctions. Donc tout le jeu…
🧐 Oui mais sinon on comprend rien.
🤓 Oui mais le jeu marche.
🧐 Oui mais on comprend rien.
🤓 J’ai compris…

Vous vous comprenez hein rassurez moi. À moins que j’ai un problème.

def factors(): # Ceci est l'ancien code incompréhensible !
    global cursor, pos, level_current, retry
    gui()
    menu()
    level_in_progress, in_menu, re_menu, last_key, end, go, input_await = False, False, False, None, False, False, False
    while not keydown(5):
        if not level_in_progress:
            if not in_menu:
                menu(0)
                start_level(1)
                cursor, pos, end, input_await = level_current, [51 + 44 * 1, 13 + 44 * 1], False, False
                aff(level_current, pos[0] + 44 // 2 + center(level_current)[0],
                    pos[1] + 44 // 2 + center(level_current)[1], black_color)
                level_in_progress = True
            if not re_menu and in_menu:
                menu(1)
                start_level(0)
                re_menu = True
        if keydown(1) and pos[1] >= 51 and last_key != 1:
            if not in_menu:
                move(0, -1)
            if in_menu and level_current != level_max:
                level_current += 1
                re_menu = False
            last_key = 1
        if keydown(2) and pos[1] <= 51 + 44 * 2 and last_key != 2:
            if not in_menu:
                move(0, 1)
            if in_menu and level_current != 1:
                level_current -= 1
                re_menu = False
            last_key = 2
        if keydown(3) and last_key != 3:
            if not in_menu:
                move(1, 0)
            if in_menu:
                level_in_progress, in_menu, re_menu = False, False, False
            if input_await:
                go = True
                fill_rect(pos[0] + 42, pos[1] + 42, 0 - 220, 0 - 176, (255, 255, 255))
            last_key = 3
        if keydown(0) and last_key != 0:
            if pos[0] != 51 + 44 or input_await:
                level_in_progress = False
                retry = True
                if input_await:
                    fill_rect(pos[0] + 42, pos[1] + 42, 0 - 176, 0 - 176, (255, 255, 255))
            if not in_menu and pos[0] == 51 + 44:
                level_in_progress, in_menu = False, True
            last_key = 0
        if not (keydown(0) or keydown(1) or keydown(2) or keydown(3)):
            last_key = None
        if not (pos[0] != 51 + 44 * 5 and (cursor != 1 or pos[0] == 51 + 44)):
            end = True
        if end:
            if not input_await:
                level_transition(cursor)
                input_await = True
            if go:
                next_level()
                level_in_progress, retry, input_await, go = False, False, False, False

Finalement, presque tout le jeu a donc été redéveloppé pour améliorer la lisibilité des fonctions python.
Vous pouvez donc désormais comprendre le code ! Sauf le système de sauvegarde, ça vous vous débrouillez ! 🤫

🧐 Oui enfin le système de sauvegarde est codé pour être incompréhensible, c’est une fonctionnalité pour éviter qu’un joueur lambda ne génère trop facilement une clé de sauvegarde.

🤓 À noter, le code a été compressé pour passer de 10.2 ko à 8.42 ko, et désormais il ne respecte plus PEP 8 – Style Guide for Python Code (EnFr)

Fonctionnement

La plupart des différents événements sont lancés grâce à la liste pos[x, y]. Cette liste correspond aux coordonnées x, et y du curseur :

La position par défaut du curseur est donc [0, 1] ! Oui, l’axe vertical est inversé. 🙄

Ainsi, si pos vaut [-1, y], on lance le menu, si pos[4, 0], on termine le niveau, etc. L’affichage sur l’écran est géré par la fonction d_p(), qui va convertir pos en coordonnées réelles (celles de l’écran), car évidemment, les coordonnées de pos sont virtuelles ! Le (0 ; 0) de l’écran lui se situe tout en haut à gauche !

Fonctionnalités délaissées

Beaucoup de fonctionnalités ont été retirés pour réduire au plus la taille du fichier ainsi que la mémoire utilisée ou pour d’autres raisons.
Un histogramme dynamique avec le score des 20 derniers niveaux a été par exemple testé mais abandonné, le système de chargement d’une sauvegarde sur la couche graphique est lui aussi retiré, remplacé par une fonctionnalité équivalente mais exploitant le console python.

À toi de jouer !

Ce jeu est disponible sur https://nsi.xyz/factors

Nous te mettons au défi d’atteindre le niveau 404. Mais y arriveras tu ? 😱

Projets

Binnerto ©, un système de gestion de bases de…

Il y a quelque mois de cela, nous vous avions laissé bien pantois. En effet, sur le site Binnerto©, un site très qualitatif de produits geek, il était jusque-là impossible de commander le moindre petit produit, et nous en sommes grandement désolés.

Mais aujourd’hui est un jour nouveau, nous venons de sortir du sable, enfin !

Vous allez désormais (dans un temps prochain (soumis à condition et à l’acceptation du crédit par votre banque)) pouvoir acheter (dans la limite des stocks disponibles et des vendeurs disponibles pour réaliser les commandes) tous les produits vendus par notre site dès maintenant grâce à un SGBDR !

Quelques petits rappels utiles et informations nécessaires à la compréhension

Vous vous demandez bien qu’est-ce qu’un SGBDR et à quoi cela peut bien servir pour commander votre produit préféré (et fort bien utile). Et bien pour une fois cette acronyme est en français (ou DBRMS dans la langue de Shakespeare), il signifie littéralement Système de Gestion de Bases de Données Relationnelles. Euuuuh quèsaco ? En fait c’est un logiciel qui permet de mettre en relation différents fichiers contenant de nombreuses données. Voyons à quoi cela ressemble dans notre cas, cela apportera plus de clarté.

Imaginons que vous vous appeliez Antonin Kroyable et que vous ayez commandé une Souris révolutionnaire pour votre tout nouvel appareil à la poire le 14 février 2022. Et bien le SGBDR ; nommons le affectueusement S&Co pour la suite pour plus de simplicité et de proximité, permet de lier trois fichiers pour les unifier. Dans un fichier vous trouverez vos informations personnelles, ce sera le fichier « client », dans un second, vous aurez toutes les informations sur cette souris, c’est-à-dire dans le fichier « produit ». Et pour la commande que vous avez faites ? Et bien elle se trouve dans le fichier « commande » évidemment. Tout le travail du S&Co et donc de lier ces trois fichiers pour avoir les informations les plus utiles pour chaque besoin, c’est-à-dire pour vous clients, et pour nous gestionnaires du site et responsables des stocks et livraisons produits, sans avoir des fichiers de 3 Km de long avec toutes les informations, dont certaines inutiles à certains mais utiles à d’autres.

Rentrons maintenant dans le cœur de la bête pour mieux l’appréhender.

Il faut savoir que ce S&Co, étant user-friendly, a une interface utilisateur (ou UI en anglais) graphique, permettant une meilleure expérience utilisateur (ou UX encore une fois en anglais), en limitant de perdre ce dernier, qui n’a pas forcément les connaissances nécessaires pour affronter la peur d’un terminal sombre et peu ragoûtant. Il est au moins rassuré, mais cela nous complexifie bien la tâche, comme nous allons le voir.

Dans cette quête de la rose-amère de la perfection du design, ce S&Co ne fera que pâle figure car étant codé extrêmement en Python avec comme interface graphique Tkinter, les possibilités de design sont amoindries comparé à d’autres langages mieux prévus pour les interfaces graphiques. Nonobstant, vous avez quand-même un produit a minima fini.

Il y aura donc, vous l’aurez compris deux parties majeures pour appréhender ce S&Co, la partie graphique, en Tkinter, qui n’est que la rustine de ce qui fait tourner la machine, ce qui est à l’intérieur, c’est-à-dire les fonctions mères pour mettre en relation SQL (le langage qui permet de parler aux tables de données ; le charmeur de données), et Python.

Le vif du sujet SQL-Python

Sans plus de divagations, voyons d’où tout est parti : SQLite. En effet, il est impossible de discuter directement avec les données en Python, ou alors cela serait très complexe.

Pour plus de facilité, on utilise à la place un autre langage, le SQL (qui est d’une facilitée incroyable en comparaison). Mais pour lier un programme Python avec du SQL il faut une bibliothèque, et c’est la que SQLite vient nous sauver ! En effet on peut rentrer des commandes SQL dans une fonction Python et avoir tous les bénéfices du SQL en ayant les retours en Python pour être réutilisés, c’est presque magique !

Réalisation des trois bases de données dans un fichier CSV

Pour commencer avec des données à chaque démarrage sans partir de rien, nous avons créé 3 fichiers CSV (où les éléments sont séparés par des virgules) que l’on récupère ensuite pour l’importer (on verra cela plus tard). Donc, comme on l’a dit plus tôt, 3 tables seront liées, il y aura donc 3 fichiers CSV qui contiendront quelques éléments pour chaque table (environ une dizaine).

Tout d’abord, il y a la table PRODUITS, dans le fichiers produits.csv. Dans cette table on présente comme suit les principales caractéristiques du produits :

id_produitnom_produitprixstock
6Binnerto en acier0.001

Toutes les données sont présentées comme cela sauf, que d’une colonne à l’autre, c’est un virgule qui les sépare. L’id_produit est un identifiant unique permettant à coup sur de retrouver le produit sans risque de se tromper (par exemple si 2 produits ont un nom identique).

Il en est de même pour la table CLIENTS sur le fichier clients.csv :

id_clientnomprenomadresse_mailadressedate_naissancedate_inscriptionvip
8KroyableAntoninantonin.kroyable@lol.mdr13 chemin des Rippes, Salle-à-Sac10-03-200711-11-2011True

De la même manière, id_client est l’identifiant unique qui correspond au client.

Passons maintenant à la dernière table, la plus importante, la table COMMANDES, dans le fichier commandes.py :

id_commandeid_client_commandeid_produitdate_commandedate_livraisonenvoyequantiteprix_total
308614-02-202219-02-2022True30.00

Encore une fois, id_commande est l’identifiant unique de la commande mais, il y a une petite subtilité encore. En effet, on va lier les deux autres tables à celle-ci avec 2 attributs : id_client_commande et id_produit, qui correspondent respectivement à la ligne de chaque table. Ainsi, avec une moindre écriture, on arrive à avoir toutes les informations pertinentes sur une seule ligne !

CRUD, la clé de voûte du projet

CRUD, de l’anglais Create, Read, Update, Delete, a été notre expression clé durant ce projet, notre guide suprême, car pour un S&Co, cela les 4 points les plus importants. Il faut pouvoir Créer des tables et des éléments dans ces tables, les Lire, les Mettre-à-Jour, mais aussi les supprimer. Voyons comment cela a pu être réalisé en Python/SQL.

1. Create

Deux choses sont à créer dans ce projet, les tables, que ce soit PRODUITS, CLIENTS ou encore COMMANDES, mais aussi les éléments qui composent ces tables, les lignes si vous vous rappelez de nos tableaux de tout-à-l’heure.

Pour créer une table, rien de plus simple comme on le voit ici avec la création de la table PRODUITS :

def create_table_produits():
    curseur.execute("CREATE TABLE IF NOT EXISTS PRODUITS(id_produit INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, nom_produit TEXT, prix NUMERIC, stock INTEGER)")
    connection.commit() #On envoie la modif dans la base de données

Le SQL est ce qui est dans la méthode .execute()

Cette fonction permet donc de créer la table PRODUITS, en vérifiant bien sûr qu’elle n’existe pas pour éviter les erreurs. Si vous vous rappelez de l’id_produit de tout à l’heure, qui permet de retrouver avec certitude un produit, et bien en SQL, c’est une PRIMARY KEY, soit une clé primaire ; une sorte de numéro d’identité du produit (qui est donc unique).

Mais on peut aussi avoir besoin de créer un enregistrement, comme suit :

def add_produits(produits_ajout):
    curseur.executemany("INSERT INTO PRODUITS(nom_produit, prix, stock) VALUES (?,?,?)", produits_ajout)
    make()

En vérifiant bien sûr que le tuple en entrée soit composé de 3 éléments.

2. Read

Pour lire, ça se corse, on peut vouloir lire tout ou partie, il a donc fallu faire des choix en réfléchissant à chaque fois au besoin le plus utile de lecture.

Ainsi on peut tout lire comme ici :

def select_all_produits():
    curseur.execute("SELECT * FROM PRODUITS")
    return [("id_produit", "nom_produit", "prix", "stock")] + curseur.fetchall()

On retourne en plus les noms des attributs afin de pouvoir les afficher comme il faut dans le tableau en Tkinter

Mais aussi comme cela :

def select_all_commandes():
    curseur.execute("SELECT nom, prenom, nom_produit, date_commande, date_livraison, envoye, quantite FROM COMMANDES AS co INNER JOIN PRODUITS AS p ON co.id_produit = p.id_produit LEFT JOIN CLIENTS AS cl ON co.id_client_commande = cl.id_client")
    return [("nom", "prenom", "nom_produit", "date_commande", "date_livraison", "envoye", "quantite")] + curseur.fetchall()

Ici, on fait une double jointure pour afficher à la place de l’id_client et de l’id_produit, le nom du client et du produit pour plus de clarté.

En jouant avec le SQL, il y a aussi des fonctions plus spécifiques comme celle qui suit permettant d’afficher toutes les commandes d’un même client :

def toutes_commandes_clients(id_client):
    curseur.execute("SELECT nom, prenom, nom_produit, date_commande, date_livraison, envoye, quantite FROM COMMANDES AS co INNER JOIN PRODUITS AS p ON co.id_produit = p.id_produit LEFT JOIN CLIENTS AS cl ON co.id_client_commande = cl.id_client WHERE id_client_commande=?",(id_client,))
    return [("nom", "prenom", "nom_produit", "date_commande", "date_livraison", "envoye", "quantite")] + curseur.fetchall()

3. Update

Pour mettre à jour, nous nous sommes demandé ce que nous aurions le plus besoin de mettre à jour dans chaque enregistrement de chaque table, et pour cela nous avons pour chaque table créé une fonction update qui change uniquement l’élément qui a le plus besoin d’être changé :

def update_commandes(id_commande, envoye: bool):
    curseur.execute("UPDATE COMMANDES SET envoye=? WHERE id_commande=?", (envoye, id_commande))
    make()

On change ici l’état de la commande avec un Booléen (Vrai/Faux), car c’est la commande que l’on est le plus amenés à réaliser.

4. Delete

Pour la suppression, afin d’être sûr de ne pas faire d’erreurs, cette dernière se base uniquement sur l’id unique de chaque enregistrement dans une table.

Ainsi :

def delete_clients(id_client):
    curseur.execute("SELECT * FROM COMMANDES WHERE id_client_commande=?",(id_client,))
    temp = curseur.fetchall()
    if str(temp) == "[]":
        curseur.execute("DELETE FROM CLIENTS WHERE id_client=?", (id_client,))
    make()

Ici, on supprime un produit en fonction de son id_produit, tout en vérifiant qu’il n’est pas utilisé dans la table commande, sinon cela pourrait poser problème.

Un affichage graphique : Tkinter

CComme dit plutôt, il faut bien interagir avec toutes ces bases de données si complexes (qu’on appellera BDD), et ça avec une interface graphique en Tkinter. Après avoir réfléchi à plusieurs croquis de ce que pourrait être l’UI (dont on vous ne donnera pas d’exemple pour ne pas nous faire voler nos maquettes), on a réussi à trouver l’UI la plus simple pour nos futurs employés, qui devront se servir de cette interface pour gérer les commandes, pendant que nous serons à Ibiza en train d’empocher un maximum d’argent.

Tous les produits de chez Binnerto en un clic

N’oublions pas le maître-mot de tout ce que l’on peut produire : LA FACILITÉ ! Il est donc inutile lorsqu’on veut sélectionner un produit d’écrire chaque lettre de ce dernier (sans oublier les potentielles erreurs de frappes ou maladresses). Nous avons donc opter pour une liste des produits qu’il suffit de sélectionner. Avec Tkinter, l’outil se nomme « Combobox » et c’est bien pratique.

from tkinter import *
from tkinter import ttk

def action(recup):
    #on appelle la fonction qui lie Python et SQL pour interagir avec la TABLE

listeproduits = ["Parpaing externe", "Clé USHellB", "Clavier Pasteur STL", "Voiture à Hydrogène", "CPU Nucléaire Leif", "Bouton d'allumage automatique", "Binnerto en acier", "Souris révolutionnaire"]
choix_prod = Label(racine, text = "Enlever Stock :")
choix_prod.place(x=300, y=130)
aff_prod = ttk.Combobox(racine, state = "readonly", values = listeproduits)
aff_prod.bind("<<ComboboxSelected>>", action)
aff_prod.place(x=265, y=170)

Comme une bonne nouvelle n’arrive jamais seule, certes l’outil « Combobox » est parfait pour ce que nous voulons faire, mais il n’est pas inclus lors de l’importation de Tkinter *. Il faut donc le rajouter à part, au péril d’un peu plus de mémoire. 

C’est bien beau de pouvoir sélectionner un produit dans une liste, mais il faut bien le récupérer pour pouvoir l’exploiter un peu. Heureusement, la méthode « .get() » permet de récupérer le choix fait par l’utilisateur, et à l’aide de la fonction ci-dessus qui affiche ce choix, on est sûr d’avoir bien récupéré le produit. Maintenant que l’on peut sélectionner un produit, il ne reste plus qu’à décider s’il faut le retirer du stock en cas de commande, ou l’ajouter en cas de fabrication (même si ce dernier cas n’arrive jamais 🤫).

Un accès aux commandes plus pratique

Nous suivons avec soin chaque commande pour la meilleure satisfaction de nos clients. C’est pour cela qu’il faut savoir à l’aide du numéro de commande : les produits qu’elle contient, le nom et prénom du client, le jour de l’envoi, si le colis a été reçu, etc…

Il faut donc qu’à l’aide de « id_commande », on trouve toutes ces informations, et cela, de façon la plus lisible possible… et pourquoi pas un tableau ?! Mais il faut d’abord récupérer le numéro d’une commande saisit dans un input :

def num_commande(event):
	n_fen = Toplevel(racine)
    t = Tableau(n_fen, select_client_specifique(int(entree.get()))) #on ouvre une nouvelle fenêtre avec le tableau représentant les données

def valide(test):
    return test.isdigit()

com_liv = Label(racine, text = "Afficher la commande d'un client (rentrez son ID)")
com_liv.place(x=900, y=130)
choix_com = racine.register(valide)
value = StringVar(racine)
entree = Entry(racine, textvariable = value, width = 30, validate = 'key', validatecommand = (choix_com,'%S'))
entree.place(x=865, y=170)
entree.bind("<Return>", num_commande)

Comme pour les produits, on récupère le numéro de la commande à l’aide de la méthode « .get() », mais cette fois-ci, uniquement lorsque nous validerons en appuyant sur la touche « Entrée » du clavier. Mais imaginons que lorsque notre employé utilise cette interface, il s’endorme sur le clavier ! Malheur ! Il faut redoubler de sécurité. C’est pour cela que dans cet input, on peut seulement y renseigner des chiffres, et cela, grâce à la fonction « valide » qui vérifie à chaque saisie si celui la est un chiffre.

Le Tkinter a pu grandement avancer grâce au tutoriel d’Aël D. sur Tkinter

Vous trouverez ci-dessous le projet, qui est en constante évolution :

Projets

Système de gestion de base de données relationnelle sur…

Venez découvrir tout au long de ce compte rendu comment nous avons créé un programme en python permettant à l’utilisateur d’interagir avec la base de donnée que nous avons créé.

Dans un premier, on a importé toutes les bibliothèques nécessaires à la création du programme.

from tkinter import *
from tkinter import ttk
import tkinter as tk
import sqlite3
import pandas
import os

La bibliothèque « tkinter » va servir à créer l’interface graphique ( vous pouvez en apprendre plus grâce à ce tutoriel, créé par Aël, élève en Spé NSI en classe de 1ère ).

La bibliothèque sqlite3 permet d’interagir avec une base de donnée.

La bibliothèque pandas permet de récupérer des données dans un ficher csv.

Et enfin la bibliothèque os permet de supprimer des fichiers dans le cadre de ce programme.

I. Création de la page d’accueil

Nous avons d’abord réfléchi à l’interface graphique que nous souhaitions réaliser.

Il nous fallait d’abord une page d’accueil, avec quatre boutons permettant d’effectuer les quatre fonctions nécessaires à l’utilisation d’une base de données : créer la base, lire la base, modifier la base, et enfin supprimer la base.

Le code ci-dessous permet de créer la page d’accueil, contenant un titre et 4 boutons.

window = Tk() # page d'accueil avec les 4 boutons

window.title("Gérer une base de donnée sur les jeux vidéos")
window.geometry("1280x1024")
window.maxsize(1280,1024)
window.config(bg="#7EBDC2")
window.iconbitmap("manette.ico")

titre = Label(window, text="4 boutons pour intéragir avec la base de données", font=("Consolas",20), bg="#d1fdff", fg="black")
titre.pack(pady=45)

boite = Frame(window, bg="#7EBDC2")
boite.pack(pady=200)

boutonC = Button(boite, text="CREATE", font=("Consolas",15), bg="white", fg="black", command=create, width=10, height=3)
boutonC.grid(row = 1, column = 0, padx = 15, pady=15)

boutonR = Button(boite, text="READ", font=("Consolas",15), bg="white", fg="black", command=read, width=10, height=3)
boutonR.grid(row = 1, column = 1, padx = 15, pady=15)

boutonU = Button(boite, text="UPDATE", font=("Consolas",15), bg="white", fg="black", command=update, width=10, height=3)
boutonU.grid(row = 3, column = 0, padx = 15, pady=15)

boutonD = Button(boite, text="DROP\n DATABASE", font=("Consolas",15), bg="white", fg="black", command=delete, width=10, height=3)
boutonD.grid(row = 3, column = 1, padx = 15, pady=15)

window.mainloop()

Visuel graphique :

II. Comment utiliser nos fonctions

Lier une interface graphique à des fonctions nécessite l’emploi de boutons.

Il nous fallait donc créer 4 fonctions permettant d’interagir avec la base de données.

A. La fonction create()

Nos fichiers à exploiter sont au format .csv. Avec la bibliothèque pandas, nous avons stockés les fichiers csv dans des variables pour pouvoir les exploiter.

ed = pandas.read_csv("relation_editeur.csv")
jv = pandas.read_csv("base_principale.csv")

Puis nous avons dû créer un fichier au format .db (Data Base).

connection = sqlite3.connect('base.db')
curseur = connection.cursor()

Nous avons créé les deux relations en indiquant les attributs et leur type.

curseur.execute("CREATE TABLE IF NOT EXISTS ED(Id_Editeur INTEGER, Editeur TEXT, PRIMARY KEY(Id_Editeur AUTOINCREMENT))")
curseur.execute("CREATE TABLE IF NOT EXISTS JV(Id_Jeu INTEGER, Jeu TEXT, Annee INTEGER, Prix FLOAT, Plateforme TEXT, Id_Editeur INTEGER, PRIMARY KEY(Id_Jeu AUTOINCREMENT), FOREIGN KEY(Id_Editeur) REFERENCES ED(Id_Editeur))")

Et enfin, nous avons créé deux boucles « for » pour ajouter les enregistrements des fichiers .csv dans la base de données automatiquement.

for i in range(len(ed)):
    data = [ed.loc[i, "Editeur"]]
    curseur.execute("INSERT INTO ED(Editeur) VALUES (?)", data)
    
for i in range(len(jv)):
    data = []
    data2 = list(jv.loc[i, ["Jeu", "Annee", "Prix", "Plateforme", "Id_Editeur" ]])
    data2[1] = int(data2[1])
    data2[4] = int(data2[4])
    data.append(tuple(data2))
    curseur.executemany("INSERT INTO JV(Jeu, Annee, Prix, Plateforme, Id_Editeur) VALUES (?, ?, ?, ?, ?)", data)

Pour finir, nous avons mis chacun de ces éléments dans une seule fonction : create(), appelée par le premier bouton de l’accueil. Nous avons décidé d’afficher une phrase pour indiquer à l’utilisateur que la base de données a bien été créé. Un simple Label nous a suffit (allez voir le tutoriel d’Aël pour plus de détails).

B. La fonction drop_database()

En SQL, la commande « DROP DATABASE » permet de supprimer la base de données. En python, il est impossible d’utiliser cette commande. On a donc importé le module os qui permet dans ce cas de supprimer un fichier.

Ainsi, la structure de la fonction drop_database() serait :

os.remove("base.db")

Cependant, si le fichier n’existe pas, tenter de le supprimer affichera, dans l’IDE python, un message d’erreur. Pour éviter ce problème, nous avons créé une variable globale « test » qui prend la valeur 0 si la base n’existe pas, et 1 si la base existe. Nous avons donc testé avec un « if » la valeur de « test », et le fichier ne sera supprimé que si « test » est égal à 1 et un message de confirmation s’affichera, sinon un message apparaît sur la fenêtre tkinter pour indiquer que la base n’existe pas, ce qui nous donne la fonction suivante.

def drop_database(): # supprime le fichier de la base de donnée s'il existe
    global test
    global message
    global texte
    
    if test == 1:
        os.remove("base.db")
        test -= 1
        supmes()
        message = "La base de donnée a bien été suprimée"
        texte = Label(window, text=message, font=("Helvetica",15), bg="#d1fdff", fg="black")
        texte.pack()
    elif test == 0:
        supmes()
        message = "La base de donnée n'existe actuellement pas"
        texte = Label(window, text=message, font=("Helvetica",15), bg="#d1fdff", fg="black")
        texte.pack()

C. La fonction read()

Pour la santé mentale de tout lecteur, nous ne montrerons pas ici l’intégralité de notre fonction (sinon, le fichier entier est téléchargeable en bas de page).

En SQL, pour sélectionner des éléments d’une base de données, on utilise la commande « SELECT Attribut_1, Attribut_2, … FROM DB ». En python, avec la bibliothèque sqlite3, la structure est exactement la même. Or il faut récupérer ce que contient le curseur et le stocker dans une variable, pour ensuite l’afficher. Par exemple, pour afficher tous les attributs d’une relation dans la console de l’IDE Python, on utilise :

curseur.execute("SELECT * FROM RELATION")
lecture = curseur.fetchall()#récupère les données obtenues par la ligne 1 en 'str'
print(lecture)

Lorsque l’on clique sur le bouton « read » de la page d’accueil, une nouvelle fenêtre tkinter apparaît. Sur cette fenêtre, on peut choisir de lire la relation principale (relation Jeux Vidéo), la relation secondaire (relation Editeurs) ou de retourner au menu, soit à l’accueil.

Le bouton « Retour Menu » supprime simplement la fenêtre, avec la commande tkinter :

window_r.destroy()

La lecture de la relation Editeurs est assez simple. En effet, la relation Editeurs ne contient que deux attributs : la clé primaire « Id_Editeur » et le nom de l’éditeur, « Editeur ». On a donc choisi de montrer par défaut toute la relation dans la même fenêtre que celle contenant les boutons de lecture, et donc supprimer les boutons. Nous avons donc dû globaliser nos boutons, afin de les supprimer ainsi :

global bouton
bouton.pack_forget()

Enfin, il a fallu sélectionner tous les éléments et les afficher dans un Label.

curseur.execute("SELECT * FROM ED")#fonction simplifiée
affichage = curseur.fetchall()
affiche = Label(text=affichage)#plus de détails sur le tutoriel
affiche.pack()

Pour finir, il nous a fallu afficher le contenu de la relation principale selon les demandes de l’utilisateur.

Pour cela, nous avons créé, grâce à des recherches supplémentaires sur internet, des checkbutton (des cases à cocher), qui peuvent être sélectionnés ou non pour choisir si l’on affiche l’attribut.

De plus, nous avons permis à l’utilisateur de choisir de spécifier sa demande avec la commande SQL « WHERE Attribut = Valeur ». Cela nous permet donc de faire une sélection très précise d’éléments à afficher.

D. La fonction update()

La dernière fonction à créer !

Modifier la base de données implique 3 actions : ajouter un enregistrement, supprimer un enregistrement, et modifier une valeur d’un des attributs de l’enregistrement.

Nous avons décidé qu’il était impossible, grâce à l’interface, d’ajouter directement un éditeur dans la relation « Editeur ». Ainsi lors de l’ajout ou de la modification d’un enregistrement de la relation Jeux Vidéo, si l’éditeur renseigné n’existe pas dans la relation correspondante, il y est créé. Cet ajout se fait simplement :

#Selon les spécificités de la bibliothèque sqlite3, avec "Editeur" renseigné par l'utilisateur
curseur.execute("SELECT Id_Editeur FROM ED WHERE Editeur = ?",(Editeur,))
temp = curseur.fetchall()
if temp == [] : #si l'éditeur n'existe pas
  curseur.execute("INSERT INTO JV (Editeur) VALUES (?)",(Editeur,))
  curseur.execute("SELECT Id_Editeur FROM ED WHERE Editeur = ?",(Editeur,))
  id_editeur = curseur.fetchall()#après ajout dans ED, on récupère l'Identifiant pour l'ajout dans JV

Pour la fonction ajouter(), nous avons créé 5 entrées correspondant aux valeurs du jeu à ajouter à la relation, et un bouton pour confirmer l’ajout.

Tant que le bouton de confirmation n’est pas cliqué, l’enregistrement n’existe pas dans la base de données.

Lorsque l’utilisateur confirme son ajout, une fonction ajouter_bis() est lancée, exécutant simplement :

valeur_1 = entree_1.get()
valeur_2 = entree_2.get()
valeur_3 = entree_3.get()
valeur_4 = entree_4.get()
#Id_Editeur récupéré par comme précédemment
data = (valeur_1, valeur_2, valeur_3, valeur_4, Id_Editeur)
curseur.execute("INSERT INTO JV (Jeu, Annee, Prix, Plateforme, Id_Editeur) VALUES (?,?,?,?,?)",data)

Ensuite, nous avons créé la fonction modifier(). Nous avons créé deux boutons permettant de modifier soit la relation Jeux Vidéo, soit la relation Editeurs.

Pour modifier la relation Editeur, on peut choisir l’éditeur à modifier soit par son Id_Editeur, soit par son nom actuel grâce à un bouton déroulant. Nous avons donc créé ce bouton déroulant et deux entrées correspondant d’abord au nouveau nom de l’éditeur, et ensuite à l’editeur à modifier. La commande SQL qui s’exécute est la suivante :

if bouton_attribut.get() == "Editeur"	
  curseur.execute("UPDATE ED SET Editeur = ? WHERE Editeur = ?",(entree_1.get(),entree_2.get()))
else :
  curseur.execute("UPDATE ED SET Editeur = ? WHERE Id_Editeur = ?",(entree_1.get(),entree_2.get())  

Si l’éditeur indiqué par son Id_Editeur ou son nom existe, alors l’enregistrement est modifié et un message de confirmation est affiché. Sinon il est indiqué que l’éditeur n’existe pas.

La fonction pour modifier un enregistrement de la relation principale est, dans les grandes lignes la même, mais l’utilisateur doit choisir l’attribut à modifier, avec un bouton déroulant. Ainsi :

curseur.execute("UPDATE ED SET Attribut_1 = ? WHERE Attribut_2 = ?",(entree_1.get(),entree_2.get())

« Attribut_1 » et « Attribut_2 » sont des valeurs fixes, testés par les boutons déroulants (demandant de nombreux tests « if » et « elif »)

Pour finir, il fallait pouvoir supprimer des éléments de la relation principale (la relation secondaire Editeurs ne peut que être lue ou modifiée). Deux attributs permettent d’identifier un jeu : son identifiant unique, et son nom (qui peut ne pas être unique). Pourtant nous avons choisi qu’il serait possible de supprimer un jeu grâce à son nom pour un utilisateur qui ne connaîtrait pas l’identifiant du jeu. Si le jeu indiqué par son Id_Jeu ou son nom existe, alors l’enregistrement est modifié et un message de confirmation est affiché. Sinon il est indiqué que le jeu n’existe pas.

Par la suite, quelque soit le choix de l’utilisateur, le fonction est le même : une entrée apparaît, dans laquelle il est possible de renseigner soit le jeu, soit l’id du jeu. Si le jeu indiqué par son Id_Jeu ou son nom existe, alors l’enregistrement est supprimé et un message de confirmation est affiché. Sinon il est indiqué que le jeu n’existe pas.

Nous avons donc créé les quatre fonctions pour interagir avec une base de données, soit la création, la lecture, la modification et la suppression de la base.

III. Conclusion

Ce compte-rendu touche maintenant à sa fin. N’hésitez pas à aller voir le tutoriel de Aël pour en apprendre plus sur la bibliothèque tkinter. Le fichier zip contenant tous les éléments nécessaires au bon fonctionnement du programme est disponible ci-dessous.

Projets

Agence de voyage et base de données

Une base de données sur des destinations paradisiaques tropicales. Consulter et réserver un voyage en exploitant une base de données dans python. L’interface utilisateur est gérée par tkinter

1 – Organiser les idées

Tout bon projet commence par un plan, afin de pouvoir poser et organiser l’idée de ce que l’on veut créer. Nous avons donc commencé par dessiner la base de notre projet :

2 – Création de nos tables

Une fois l’idée posée, il est temps de passer au programme !

Avant toute chose nous avons créé nos 4 tables et leurs attributs pour y ajouter après nos fichiers .csv créés à l’avance :

#création des tables
curseur.execute("CREATE TABLE IF NOT EXISTS DESTINATION (Id_Destination INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, Nom TEXT, Continent TEXT, Temp_Moy INT, Note INT)")
curseur.execute("CREATE TABLE IF NOT EXISTS CLIENT(Id_Client INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, Prenom TEXT, Nom TEXT, Lieu_depart TEXT)")
curseur.execute("CREATE TABLE IF NOT EXISTS VOL(Id_Vol INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, Id_Destination INT, Lieu_Depart TEXT, Date_Loc_Depart DATE, Heure_Loc_Depart TIME, Temps_Vol TIME, Prix INT, FOREIGN KEY (Id_Destination) REFERENCES DESTINATION(Id_Destination), FOREIGN KEY (Lieu_Depart) REFERENCES CLIENT(Id_Client))")
curseur.execute("CREATE TABLE IF NOT EXISTS RESERVATION(Id_Reservation INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, Id_Client INT, Id_Destination INT, Id_Vol INT, Num_Siege INT,FOREIGN KEY (Id_Client) REFERENCES CLIENT(Id_Client), FOREIGN KEY (Id_Destination) REFERENCES DESTINATION(Id_Destination), FOREIGN KEY (Id_Vol) REFERENCES VOL(Id_Vol))")
connection.commit()

Une fois les tables créées ont les a rempli avec les enregistrements contenus dans les fichiers .csv grâce au module panda:

#ajout des enregistrements .csv
pandas.read_csv("Client.csv").to_sql("CLIENT", connection, if_exists='replace', index=False)
pandas.read_csv("Destination.csv").to_sql("DESTINATION", connection, if_exists='replace', index=False)
pandas.read_csv("Vol.csv").to_sql("VOL", connection, if_exists='replace', index=False)
pandas.read_csv("Reservation.csv").to_sql("RESERVATION", connection, if_exists='replace', index=False)
connection.commit()

3 – La fenêtre d’accueil

A partir de là a commencé la création de la page d’accueil (dans les règles de l’art !)

Ici on définit l’aspect de notre fenêtre d’accueil, qui servira de modèle par la suite aux autres fenêtres.

#création de la fenêtre principale
window = Tk()
window.title("Agence de voyage : Partir un Jour sans Retour")
window.geometry("1080x720")
window.minsize(1080,775)
window.iconbitmap("island.ico")
window.config(background = "#B5D7F1")

Dans cette fenêtre on ajoute également 3 boutons, “destinations”, “réserver” et “supprimer son compte client” où les grandes actions s’effectuent.

4 – Fenêtre des destinations

Notre bouton destination mène donc à une fenêtre où les clients pourront découvrir les destinations proposées.

Les destinations sont d’abord triées par continent et rangées dans les boîtes correspondantes.

Ouvertes, ces boîtes affichent les destinations possibles du continent, rangées en tableau grâce à Treeview.

    tableau_europe = ttk.Treeview(boite_europe, columns=('Nom', 'Températures Moyennes', 'Note'), show = 'headings')
    tableau_europe.heading('Nom', text='Nom')
    tableau_europe.heading('Températures Moyennes', text='Températures Moyennes')
    tableau_europe.heading('Note', text='Note')
    
    for row in curseur.execute("SELECT Nom, Temp_Moy, Note FROM DESTINATION WHERE Continent = 'Europe' ORDER BY Nom"):
        tableau_europe.insert('', tk.END, values = row)
   
    tableau_europe.grid(row=0, column=0, sticky='nsew')

Nous avons ensuite utilisé Style() afin de personnaliser nos tableaux avec tous les attributs servant à les modifier comme sur l’image ci-dessous :

    #Style tableau europe
    
    tableau_europe = ttk.Style()
    #Prendre un theme
    tableau_europe.theme_use("clam")
    #configurer les couleurs du treeview
    tableau_europe.configure("Treeview",
        background = "#FEF3EA",
        foreground = "#206093",
        rowheight = 30,
        fieldbackground = "#FEF3EA"             
        )
    #Changer la couleur sélectionné par la souris
    tableau_europe.map('Treeview',
        background=[('selected', '#FFEE93')])

Ainsi qu’un bouton retour afin de fermer la boîte ouverte et de nouveau afficher les autres.

5 – Fenêtre réservation

Pour réserver un vol, il faut pouvoir choisir sa destination et s’inscrire dans la table Client.

Le plus simple a été de relier notre menu déroulant à la base de données. 

choix_destination = curseur.execute("SELECT Nom FROM DESTINATION")
    option_destination = OptionMenu(reservation, variable_destination, *choix_destination)
    option_destination.config(width=35, font=('Consolas', 12))

C’est au niveau du bouton de validation où cela s’est légèrement corsé.

Pas de soucis pour récupérer les textes entrés par l’utilisateur, ni son identifiant, qui constitueront son enregistrement dans la table Client.

        temp_client = (variable_prenom.get(), variable_nom.get())
        curseur.execute("INSERT INTO CLIENT(Prenom, Nom) VALUES (?,?)", temp_client)
        
        curseur.execute("SELECT MAX(Id_Client) FROM CLIENT")
        tempclient = curseur.fetchone()

C’est la récupération de l’identifiant de la destination qui aura posé le souci majeur puisque la donnée récupérée du menu déroulant se présentait comme un tuple sans en être un, enfin rien d’insurmontable un rien de bricolage suffit à régler le souci !

        temp_tempdestination = variable_destination.get()
        temp_bricodestination = (temp_tempdestination[2:-3],)
        curseur.execute("SELECT Id_Destination FROM DESTINATION WHERE Nom = ?", temp_bricodestination)
        temp_destination = curseur.fetchone()

On récupère également l’identifiant du vol

curseur.execute("SELECT Id_Vol FROM VOL WHERE Id_Destination = ?", temp_destination)
        temp_vol = curseur.fetchone()

Et une fois tous les attributs nécessaires à un enregistrement dans la relation réservation récupérés, on peut les y ajouter :

temp_reservation = (tempclient[0], temp_destination[0], temp_vol[0], randint(1, 600))
        curseur.execute("INSERT INTO RESERVATION(Id_Client, Id_Destination, Id_Vol, Num_Siege) VALUES (?,?,?,?)", temp_reservation)
        connection.commit()

6 – Fenêtre suppression de client

 Enfin si un client souhaite supprimer son compte nous avons la fenêtre “supprimer”.

Elle est composée d’une entrée utilisateur, où le client pourra renseigner son identifiant et d’un bouton “Valider” qui exécutera la suppression.

    def valider_suppression():
        temp_supprimer = variable_idclient.get()
        curseur.execute("DELETE FROM CLIENT WHERE Id_Client = ?", temp_supprimer)

Interface Tkinter réalisée avec l’aide du tutoriel d’Aël D.

Voici notre code compressé au format 7zip :

Boucle Tutoriels

Différentes utilisations de la boucle bornée for en Python…

Essayons de dompter le tigre… ou plutôt le serpent en l’occurrence. D’ailleurs en parlant d’occurrence, plongeons dans le monde pantagruélique de la programmation et voyons aujourd’hui la boucle for.

A – Mais qu’est ce qu’une boucle, et a fortiori une boucle for ?

Et non ce n’est pas une formule magique, mais presque !

Mais d’abord l’essentiel serait de savoir ce qu’est une boucle en Python puis ensuite de l’adapter à la boucle for (c’est for en chocolat).

Alors comment dire, quand votre petit frère vous énerve tout particulièrement et que vous ne voulez pas rester trop longtemps dans cette géhenne ni faire trop d’escarmouche, alors vous avez une option, non pas la fuite, mais faire tourner en boucle ce personnage qui hante éblouit vos vies.

En lui promettant quelque chose puis en retirant cette promesse, tout en le répétant à de multiples reprises, vous parvenez par la magie du langage, d’objectiver votre conscience, pour qu’ensuite elle l’atteigne au cœur même de sa subjectivité.

Le mal étant fait, profitez de votre œuvre ! Bravo, vous avec compris le concept de boucle et ici c’est une boucle infinie. Super Mince ! Bon, il manque quelque chose pour que la boucle fonctionne vraiment et soit exécutable, c’est à dire pour que ce chérubin fasse ce que vous voulez (sinon il va rester planté là).

Par exemple quand on a peur de vous entendre dire encore une fois que les personnes âgées n’utilisant pas leurs appareils mobiles à bon escient devraient être emprisonnées à vie sur l’île de la Pêche au Sirop, on va dire : « Avant de nous dire cette frivolité tu es priés d’exercer une rotation dans le sens trigonométrique, de ta langue en faisant 7 tours ».

Ainsi vous connaissez déjà les boucles, et ici en plus c’est une boucle for, trop for ! (elle était facile j’avoue)

Donc, vous voyez le cerveau est surprenant de plasticité, vous savez dorénavant quelque chose que vous ne pensiez pas savoir, alors que vous n’avez rien appris de nouveau.

Mais alors que veut dire le mot for, que nos congénères anglicans apprécient tant ?

Selon toute vraisemblance, la traduction la plus logique serait pour, ou bien sur.

Rentrons maintenant que nous appréhendons ces points importants dans le cœur du sujet, et je peux vous dire qu’on est pas sorti du sable.

Alors, il faut savoir qu’en Python (et nous ne parlerons que du Python même si certains éléments seront présents chez d’autres espèces, sélection naturelle oblige), la boule for est une boucle conditionnelle, c’est-à-dire que comme son nom l’indique, elle est soumise à une condition. C’est pourtant clair non ?

Il faut qu’une condition sine qua none se présente pour que la boucle arrête de boucler et que votre frère vous écoute donc on fera attention à cela pour que tout se passe bien.

Pour qu’elle s’arrête il faut aussi un début. Il peut être indiqué explicitement, ou implicitement avec le sens du code.

A savoir que for ne peut pas marcher tout seul comme un grand, il doit obligatoirement être associé à un parent majeur comme une variable ou certaines fonctions.

B – Les différents usages de la boucle for

1. L’association boucle for et fonction range()

Commençons par le plus classique des points, l’appairage avec la fameuse fonction range() !

Donc vous avez un code tel qu’il suit

for i in range(debut, fin, pas):
    # je fais des choses tralalalala

On retrouve donc la structure clé ; l’instruction for, et ensuite un bazar sans nom avec la fonction range().

Décryptons tout cela en partant de la droite.

Alors range() c’est tout bête, c’est juste une fonction qui va aller de la valeur entière donnée de départ à la valeur d’arrivée en répétant cette tâche un nombre certain de fois. Et les pas seront l’écart entre deux valeurs données.

Et en fait i sera une variable temporaire de la boucle automatiquement créée par Python pour récupérer la valeur de la fonction range car sinon elle se perd.

Ainsi si l’on résume, on va parcourir toutes les valeurs entre 2 entiers avec un pas donné !

Ainsi, résumons :

for i in range(42, 50, 2):
	print(i)

Donnera :

42
44
46
48

Car oui il manquait un détail, on ne prend pas la valeur finale, mais la valeur strictement inférieure comme dernière valeur.

Par conséquent, cette boucle peut s’écrire de différentes manières en fonction du niveau de raffinement voulu :

Mode auto :

for i in range(fin):
	# laziza

Ce mode a par défaut un pas de 1 et un début à 0.

Et ici :

for i in range(debut, fin):
	# laziza

Pareil que tout à l’heure sauf qu’on donne explicitement le début.

Et donc tout cela, c’est bien magique, mais voyons un cas concret pour mieux comprendre l’utilisation.

Imaginons que j’ai une liste de matières préférées, classée par ordre de préférence décroissant ; c’est-à-dire que la matière préférée sera en premier, etc…

J’ai donc une liste comme suit :

matieres_preferees = ["NSI", "Maths Expertes", "Maths", "Physique-Chimie", "Philo", "Ma LV2", "Enseignement Scientifique", "SES"] 

Je vais donc dire au grand jour l’ordre de mes matières :

for id_matiere in range(len(matieres_preferees)):
    print("La matiere", matieres_preferees[id_matiere], "est au rang", id_matiere + 1, "dans mon ordre de préférence")
    #ou bien :  print("La matiere {} est au rang {} dans mon ordre de préférence",.format(matieres_preferees[id_matiere], id_matiere + 1) 

En fait ici la fonction len, très utile, donne la longueur de la liste, on parcourt donc la liste de son id 0 (son premier élément), jusqu’à l’id juste inférieur à la longueur de la liste, soit l’id du dernier élément de la liste.

Pour l’affichage, on rajoute un 1 à l’id de la matière, pour donner le rang, car sinon le premier élément aurait le rang 0, peu parlant en langage franchouillard.

Ce code va donc rendre :

La matiere NSI est au rang 1 dans mon ordre de préférence
La matiere Maths Expertes est au rang 2 dans mon ordre de préférence
La matiere Maths est au rang 3 dans mon ordre de préférence
La matiere Physique-Chimie est au rang 4 dans mon ordre de préférence
La matiere Philo est au rang 5 dans mon ordre de préférence
La matiere Ma LV2 est au rang 6 dans mon ordre de préférence
La matiere Enseignement Scientifique est au rang 7 dans mon ordre de préférence
La matiere SES est au rang 8 dans mon ordre de préférence

Et voila vous savez parcourir des nombres à l’aide de for et de range !

2. Le parcours d’éléments autonome (sans valeur chiffrée)

Maintenant, reprenons l’exemple précédant, et affinons le un peu pour le rendre plus élégant.

On va s’avouer quand même que l’écriture de tout à l’heure pour dire l’ordre de nos matières préférées était un peu lourd, redondant, et inutile. Il faut en effet passer par la valeur chiffrée de l’id de l’élément sur la liste pour ensuite arriver à l’élément en lui même. Or, on a vu en philosophie que le fait de traduire sa pensée subjective vers quelque chose d’objectif, comme le langage ou ici la valeur chiffrée, trahissait notre pensée. Donc ce n’est pas bon car cela ne reflète pas exactement ce qu’on veut faire, il faut donc essayer de rester dans notre idée, sans devoir passer par un élément externe pour y arriver.

Or, c’est magique, Python a parfaitement compris cela et nous propose une solution quasiment optimale pour cela ; on parcourt la liste mais sans dire d’autres éléments en utilisant la structure suivante :

for elem in groupe_d_elem:
  	#tralalilala

L’écriture est très simple, et même enfantine, parce que littéralement on dit « Pour chaque élément dans le groupe d’éléments »

A savoir que la variable plus haut nommée elem sera automatiquement créée et mise à jour par Python pour chaque valeur ; rien à faire 😉 !

Donc revenons à notre exemple, cela va donc faire comme suit :

for matiere in matieres_preferees:
  	print("La matiere est :", matiere)

Ce qui rend donc après exécution :

La matiere est : NSI
La matiere est : Maths Expertes
La matiere est : Maths
La matiere est : Physique-Chimie
La matiere est : Philo
La matiere est : Ma LV2
La matiere est : Enseignement Scientifique
La matiere est : SES

On conviendra que c’est bien pratique a écrire et ce de façon simple mais, problème, on a perdu la numérotation des matières pour savoir l’ordre.

C’est là qu’arrive une fonction encore une fois bien pratique de Python ; enumerate.

3. Enumerate, notre sauveur

On reprend donc exactement ce qu’on a écrit tout à l’heure avec le parcours simple sans le range en rajoutant quelques petits détails :

for rang_matiere, matiere in enumerate(matieres_preferees, start=1):
  	print("La matiere est :", matiere, "au rang de preference :", rang_matiere)

Et là, magie, on retrouve notre rang tant manqué !!

La matiere est : NSI au rang de preference : 1
La matiere est : Maths Expertes au rang de preference : 2
La matiere est : Maths au rang de preference : 3
La matiere est : Physique-Chimie au rang de preference : 4
La matiere est : Philo au rang de preference : 5
La matiere est : Ma LV2 au rang de preference : 6
La matiere est : Enseignement Scientifique au rang de preference : 7
La matiere est : SES au rang de preference : 8

En fait ici Python va créer 2 variables ; la variable matière vu comme tout à l’heure et une variable rang_matière, qu’il incrémente à chaque fois de 1 au fur et à mesure du passage à la matière suivante. On lui indique même un bonne valeur de départ pour être certain qu’il parte du bon nombre comme vu tout à l’heure.

Donc si on résume, pour faire ce que l’on a fait c’est-à-dire afficher le rang et la matière deux options s’offrent à nous ; la méthode « Pattes d’Ours » grossière, brutale et peu élégante avec for et la fonction range, ou bien celle avec enumerate, ou la valeur donnée et l’objet sont tous deux présents dans des variables et donc plus facilement utilisables (pour des vérification par exemple avec des tests conditionnels).

En parlant de tests conditionnels d’ailleurs, on peut sortir de la boucle for au bout d’un certain moment si on veut en utilisant break.

Prenons ici notre même exemple et imaginons qu’on veut juste afficher le top 3 des matières, cela fait donc :

for rang_matiere, matiere in enumerate(matieres_preferees, start=1):
    if rang_matiere > 3:
        break
    else:
        print("La matière", matiere, "est au rang de preference :", rang_matiere)

Cela rend donc après exécution :

La matière NSI est au rang de preference : 1
La matière Maths Expertes est au rang de preference : 2
La matière Maths est au rang de preference : 3

Magique non ?

Bien sur, l’inverse est aussi possible avec continue, c’est-à-dire que même si quelque chose est vérifié, on ne sort pas de la boucle, on continue encore.

Passons maintenant à un autre côté magique de Python, une petite astuce sur les chaînes de caractères ; en effet pour lui, ces dernières sont comme des listes, on peut donc les parcourir de la même manière.

Ainsi la phrase :

phrase = "D'apres une etude de l'INSEE la totalite des gens ayant fait NSI en Premiere et en Terminale vivraient heureux et plus longtemps que les autres"

Donnerait ceci en faisant un petit booster pour n’afficher que les mots :

for id_lettre, lettre in enumerate(phrase):
    if lettre == " ":
        pass
    else:
        print(lettre, end="")
        if id_lettre == len(phrase) - 1 or phrase[id_lettre + 1] == " ":
          print("\n")

Cela donnerait donc :

D'apres

une

etude

de

l'INSEE

la

totalite

des

gens

ayant

fait

NSI

en

Premiere

et

en

Terminale

vivraient

heureux

et

plus

longtemps

que

les

autres

On remarquera ici encore quelques petites astuces sur les boucles for. Il y a tout d’abord le pass, qui permet de sauter une répétition si jamais il se passe quelque chose (ici s’il y a un espace), mais aussi les vérifications d’id de la phrase pour savoir si c’est la fin d’un mot ou non.

4. Un petit plus, l’usage de yield associé à la boucle for

yield en Python est un petit peu particulier. Pour le définir, on pourrait le qualifier comme un return, mais qui s’incrémenterait de 1 tout seul à chaque appel. Comme pour return, il ne peut donc être utilisé que dans une fonction.

Voyons un exemple pour mieux l’appréhender :

def nombre_ex():
    for i in range(40,43):
        print("Nouvelle valeur !")
        yield i

On associe donc à yield, la valeur de i comprise entre 40 et 42.

Si on appelle juste la fonction telle qu’elle dans un terminal Python, on obtient un retour étrange :

>>> nombre_ex()
<generator object nombre_ex at 0x100e2a490>

Etrange, la fonction n’est pas perçue comme une fonction, mais comme un générateur, et elle n’est pas exécutée ! C’est tout simplement parce que yield à la place de return joue la place d’un générateur, vu qu’il s’incrémente d’une valeur.

On peut donc utiliser la boucle for sur ce générateur :

temp_iter = nombre_ex()
for elem in temp_iter:
  print(elem)

Ce qui nous fait donc :

Nouvelle valeur !
40
Nouvelle valeur !
41
Nouvelle valeur !
42

C – L’instanciation d’un itération

1. L’itération d’un objet

Pour les adeptes de la POO (Programmation Orientée Objet), n’oublions pas qu’il existe une méthode, pour pouvoir rajouter à votre classe la possibilité d’être itérative selon votre volonté !

Il s’agit de la méthode __iter__ ; comme indiqué dans son nom, elle permet à votre classe de devenir … itérative !

Mais comment concrètement cela marche ?

Voyons déjà sa structure de base et son explication à l’aide d’un exemple neutre.

class maClasse:
    def __init__(self):
        #J'initie ma classe
    #[...]
    def __iter__(self): #autres arguments si voulu
        #Je retourne mon objet itératif
    def __next__(self):
        if condition is verifie:
      		#Je passe à l'élément suivant dans mon itération en suivant certaines conditions
        else:
            raise StopIteration #on stoppe l'itération si la condition n'est pas vérifiée
        
temp = maClasse("""Tartampion""")
for elem in temp:
    #Réaliser une action

Comme vous le voyez, comme pour les méthodes __init__, ou bien __str__ (qui permet d’utiliser la fonction print()), les méthode __iter__ et __next__ sont implicites. Sachez que l’on reconnait ces méthodes dites implicites mais qui sont bien utiles par la présence de doubles underscores (_) avant et après le terme de la méthode.

Ici __iter__ et __next__ sont au cœur même du concept d’itération pour Python. Et c’est ici qu’il faut comprendre quelque chose d’important dans le fonctionnement de for (que je n’exprime que maintenant supposant que si vous êtes ici, c’est que vous maîtrisez mieux tous les concepts de programmation de base).

En effet, pour fonctionner, la boucle for appelle à chaque fois la fonction iter(), sur l’objet sur lequel on est. En ajoutant une méthode __iter__ à notre objet, on le rend donc « itératif » et on fait donc fonctionner la fonction iter(), et ainsi la boucle for. Mais pour l’instant cette boucle va toujours afficher le même élément, car on n’a pas expliqué comment aller à l’élément suivant ; c’est ce qui se passe avec la méthode __next__, on explique à Python comment dans une boucle il pourra aller à l’élément suivant !

Donc voilà, on a pu créer un comportement itératif pour notre classe !

Mais attention, pour pouvoir le faire, il faut bien comprendre et assimiler le fonctionnement de cette dernière, en connaissant bien les conditions de passage d’un élément à l’autre ; en prenant en quelque sorte du recul sur notre objet, pour essayer de se le représenter virtuellement, au risque d’avoir quelques surprises !

2. Des objets itératifs dans des packages

Bien évidemment, vous vous en doutez, s’il est possible de réaliser l’itération dans ses propres classes, de grands packages de classes en sont dotés, comme par exemple le package os.

Ce package présente, comme son nom l’indique, tout ce qui est en lien avec l’OS (ou système d’exploitation en français). Or dans un ordinateur, il y a quelque chose qu’on peut avoir besoin dans un programme Python, qui est présent en nombre et souvent très mal rangé. Et oui, il s’agit des documents présents dans des « directories » (ou dossiers), que la boucle for permet donc aisément de parcourir, comme suit avec ce petit exemple très simple :

import os

for filename in os.listdir("un_dossier/mon_directory/"):
    print(filename)
    if filename.endswith(".py"):
        print("Ohhh un fichier Python !!")

Comme vous le voyez, on a itéré sur les fichiers de notre directory à l’aide de cette classe (qui fonctionne moyennement sur Linux, attention), et cela est faisable sur toute classe sur laquelle le développeur en a laissé la possibilité.

Conclusion

Et voilà, vous êtes enfin arrivés au terme de ce tuto plutôt didactif, votre calvaire et enfin terminé !

Dans tout les cas j’espère que celui-ci vous aura été aussi utile à vous qu’à moi, me permettant de découvrir des talents cachés de la boucle for que je ne vais cesser d’utiliser.

La programmation et l’algorithmique prennent aujourd’hui une place toujours plus prépondérante dans nos vies, et cela ne va cesser de s’accroître. Maîtriser ces concepts de base autour de la programmation peut vous permettre de mieux appréhender ce nouveau domaine en pleine croissance, et ainsi de vous rendre compte qu’il est beaucoup plus éloigné des geeks solitaires à lunettes que vous pensiez, mais plutôt le fruit de logique, d’expériences, d’idées, et de savoir ; se rapprochant en fait d’une logique algorithmique ; mathématique.

Maintenant la boucle for n’aura (presque) plus de secrets pour vous en Python !

Tutoriels

Utiliser Tkinter pour donner des interfaces à vos scripts…

Dans ce tutoriel, vous allez apprendre pas à pas les bases de tkinter en Python : créer une fenêtre, y ajouter du texte, des boutons, et autres, mais aussi l’organiser à l’aide de boites et de grilles.

Qu’est ce que Tkinter ?

Tkinter (ou ToolKit Interface) est une library Python permettant de donner une interface graphique à vos scripts Python et ainsi les rendre plus agréables à utiliser, qu’ils s’agisse de simulateurs, de programmes de calcul ou de mini jeux textuels par exemple.

Il est important de noter que Tkinter n’est pas disponible sur la Numworks ou autre calculatrice utilisant Python, mais est par contre utilisable sous Windows, Linux macOS et même sur Android en utilisant Pydroid

Installer Tkinter

Tkinter est inclus avec Python 3 sous Windows, Linux ou macOS, vous n’aurez donc normalement pas à vous soucier de devoir l’installer en plus.

Au début de votre script, vous aurez simplement à taper les lignes suivantes afin d’importer la bibliothèque tkinter :

from tkinter import *
import tkinter as tk

Maintenant que l’on a spécifié que l’on allait utiliser cette bibliothèque, on peut commencer à mettre en place notre fenêtre.

Création de la fenêtre

C’est dans cette fenêtre que tous les éléments vont apparaître.

Pour commencer, nous allons créer une fenêtre. Pour cela une ligne suffit:

fenetre = Tk()

Lorsque l’on exécute le script rien ne se passe: il faut spécifier au script que l’on veut faire apparaître la fenêtre. Pour cela, entrer la ligne suivante à la fin de votre script.

fenetre.mainloop()

Il est important que cette ligne reste la dernière de votre programme, car tout ce qui sera écrit après cette ligne n’apparaîtra pas dans la fenêtre

On obtient alors normalement le résultat suivant :

Maintenant que l’on a créé cette fenêtre, on peut la personnaliser: lui donner un nom, des dimensions, une couleur de fond etc…

fenetre.title("Ma permière fenêtre Tkinter!")
fenetre.geometry("1080x720")
fenetre.config(bg="#d1fdff")

Les dimensions seront écrites sous la forme "largeurxhauteur"

La couleur peut être spécifiée de deux manières différentes:

  • Avec le nom de la couleur en anglais (ex. bg="grey")
  • En utilisant sa notation en héxadécimal, comme dans l’exemple donné ci-dessus. Il est possible de trouver des couleurs en héxadécimal à l’aide de cet outil

On obtient alors ceci :

Il est également possible de personnaliser l’icône de la fenêtre.

Pour cela, il vous faudra une image au format .ico . On peut convertir des images au format .png en .ico à l’aide d’un convertisseur en ligne

Une fois que vous avez votre icône, placez la dans le même dossier que votre script, et entrez la ligne suivante (en remplaçant "logo.ico" par "lenomdevotreicone.ico")

fenetre.iconbitmap("logo.ico")

Il est aussi possible de définir la taille minimale de la fenêtre:

fenetre.minsize(500,500)

On a donc appris à personnaliser notre fenêtre tkinter… mais elle reste tout de même bien vide. Nous allons donc voir comment ajouter des éléments à cette fenêtre.

Ajouter du texte

Pour ajouter du texte sur notre interface, on va utiliser une instance nommée Label

titre = Label(fenetre, text="Voici un exemple de Label en police consolas et en taille 20", font=("Consolas",20), bg="#d1fdff", fg="black")
soustitre = Label(fenetre, text="Voici un exemple de Label en police Helvetica et en taille 15", font=("Helvetica",15), bg="#d1fdff", fg="black")

On remarque que l’on peut personnaliser différents aspects du Label:

  • La boîte dans laquelle il se trouve (cette notion sera abordée un peu plus loin)
  • Le texte qu’il renferme ( text )
  • Sa police et taille d’écriture ( font )
  • Son arrière plan ( bg pour background )
  • Sa couleur de police ( fg pour foreground )

Si l’on exécute le script, on s’aperçoit que le texte ne s’affiche pas: là aussi, il faut spécifier qu’il faut l’afficher à l’aide de l’instruction .pack(). Il sera nécessaire d’appeler tout nouvel élément que l’on créé de cette manière.

titre.pack()
soustitre.pack()

Les différents éléments que vous allez incorporer à votre fenêtre apparaîtront dans l’ordre dans lequel vous allez les appeler à l’aide de .pack() , ici, titre sera donc au dessus de soustitre.

Il est possible de donner une marge à notre texte en utilisant pady et padx. Par exemple, l’instruction suivante donne une marge de 45 des deux côtés en hauteur

titre.pack(pady=45)

On peut aussi spécifier au texte de prendre toute la place possible en utilisant expand comme ceci:

titre.pack(expand=YES)

Ajouter des boutons

Maintenant que l’on sait faire apparaître du texte sur notre fenêtre, on va s’intéresser aux boutons. Leur mise en place est très similaire à celle des Labels. Cependant, on peut leur ajouter d’autres arguments

  • command qui spécifiera la fonction à éxécuter en cas de clic sur le bouton
  • width et height qui permettent de modifier respectivement sa largeur et sa longueur. Si ils ne sont pas spécifiés, la taille du bouton sera celle nécessaire pour que son texte s’affiche
nb_clicks=0
def compteur():
    global nb_clicks
    nb_clicks += 1
    print(nb_clicks)

bouton1 = Button(fenetre, text="Je suis un bouton", font=("Consolas",15), bg="white", fg="black", command=compteur, width=10, height=3)

Ici, on a donc créé un Bouton d’une largeur de 10 et d’un hauteur de 3 qui incrémente un compteur à chaque clic et qui donne la valeur du compteur dans la console

Mais ce que l’on chercherait à faire, ce serait de faire apparaître le texte directement dans la fenêtre.

On créé alors un nouveau Label qui va afficher la valeur de nb_clicks.

nb_clicks=0

textecompteur = Label(fenetre, text="nombre de clicks : %s" %(nb_clicks), font=("Helvetica",15), bg="#d1fdff", fg="black")
#ici, dans la chaîne de caractères, le "%s" sera remplacé par la valeur de la variable placée dans le tuple situé après le % soit nb_clicks

def compteur():
    global nb_clicks
    nb_clicks += 1
    print(nb_clicks)

bouton1 = Button(fenetre, text="Je suis un bouton", font=("Consolas",15), bg="white", fg="black", command=compteur, width=50, height=3)

On se rend alors compte que même si la valeur est incrémentée, le label ne s’actualise pas dans la fenêtre. On va alors ajouter une ligne dans la fonction compteur qui va mettre à jour ce Label à chaque clic du bouton à l’aide de l’instruction .config()

def compteur():
    global nb_clicks
    nb_clicks += 1
    textecompteur.config(text="nombre de clicks : %s" %(nb_clicks))

Voici ce que ça donne:

Cependant, si la fonction à exécuter nécessite des arguments, les choses vont se passer un peu différemment.

Dans cet exemple, on va créer deux boutons, qui vont exécuter la même fonction en cas de clic, mais avec des arguments différents.

Il est nécessaire pour cela d’importer la fonction partial de la library functools.

from functools import partial

On créé d’abord notre fonction. Ici, elle prend en argument une couleur et applique cette couleur au Label titre en cas d’appel de la fonction:

def changer_couleur_titre(couleur):
    titre.config(fg=couleur)

Ensuite, on créé les boutons avec pour commande partial( nomdelafonction, argument )

bouton2 = Button(fenetre, text="titre rouge", font=("Consolas",15), bg="white", fg="black", command=partial(changer_couleur_titre,"red"))
bouton3 = Button(fenetre, text="titre bleu", font=("Consolas",15), bg="white", fg="black", command=partial(changer_couleur_titre,"blue"))

Lorsque l’on va cliquer sur bouton2 le titre va devenir rouge, et sur le bouton3 il deviendra bleu

Ajouter un champ pour entrer du texte

Pour certains programmes, et en particulier les simulateurs, il est nécessaire d’entrer des données manuellement, et c’est là que les Entry entrent en jeu.

Là aussi, le processus est assez similaire aux Labels, la seule différence est que text est remplacé par textvariable qui sera donc la variable que l’on va modifier lorsque l’on écrira dans ce champ.

A l’instar des boutons, il est aussi possible de modifier leur taille à l’aide de width et height

Ici, la création de la variable est un peu différente car il doit s’agir d’une variable tkinter. On procède alors de la manière suivante:

#création de la variable
variable = tk.StringVar()
variable.set("Vous pouvez me modifier")

#création du champ
entree = Entry(fenetre, textvariable=variable, font=("Helvetica",15), bg="#d1fdff", fg="black", width=30)

Il faudra utiliser la même méthode à chaque fois que l’on voudra créer des variables pour des chaînes de caractères que l’on voudra afficher dans une interface tkinter

Il est possible aussi possible d’appeler cette variable (en utilisant .get() car il s’agit d’une variable tkinter) pour modifier du texte par exemple:

def changer_titre():
    titre.config(text=variable.get())

bouton4 = Button(fenetre, text="Cliquez pour actualiser", font=("Helvetica",15), bg="#d1fdff", fg="black", command = changer_titre)

Ici, à chaque clic du bouton, le texte du titre sera actualisé pour devenir celui du champ:

Maintenant que l’on sait incorporer différents éléments à notre fenêtre, on va s’intéresser à comment les organiser

Utiliser les boites

Les boites, ou Frames, sont très utiles lorsque l’on utilise tkinter puisqu’elles permettent de regrouper des éléments entre eux et ainsi créer différents menus entre lesquels on peut naviguer.

Il est nécessaire de les définir avant les éléments qui vont en faire partie, c’est pourquoi il est conseillé de les définir juste en dessous de la configuration de la fenêtre.

On créé une boite de la manière suivante, en spécifiant la boite dans laquelle elle est placée, et la couleur de son arrière plan

boitelabels = Frame(fenetre, bg="#d1fdff")

Ici, on a spécifié que la boite apparaîtrait dans la fenêtre principale, mais il est aussi possible de faire apparaître des boites dans des boites.

Comme tout autre élément, si l’on veut qu’une boite apparaisse, il ne faudra pas oublier de l’appeler avec un .pack(). Si un élément est appelé ainsi mais pas sa boîte, il n’apparaîtra pas, et si jamais il n’est pas appelé et que sa boite si, il n’apparaîtra pas non plus

Pour qu’un élément apparaisse dans une boîte, il suffit de remplacer le fenetre que l’on utilisait jusqu’à présent par le nom de la boite:

titre = Label(boitelabels, text="Voici un exemple de Label en police consolas et en taille 20", font=("Consolas",20), bg="#d1fdff", fg="black")
soustitre = Label(boitelabels, text="Voici un exemple de Label en police Helvetica et en taille 15", font=("Helvetica",15), bg="#d1fdff", fg="black")

Nous allons maintenant essayer de créer un menu nous permettant de naviguer entre tout ce que l’on a créé jusqu’à présent.

On créé alors différentes boites pour nos différents éléments. Dans cet exemple

  • boitelabels contiendra les éléments servi à la démonstration de ce que sont les Labels
  • boitecompteur contiendra le compteur créé un peu plus tôt
  • boitecouleurs contiendra les boutons permettant de modifier la couleur du titre
  • boiteentree contiendra les éléments ayant servi à la démonstration de l’utilité des Entries
  • boitemenu contiendra les boutons permettant d’afficher les différentes boites. Elle va être la seule à apparaître lorsque l’on lancera le script et permettra d’afficher les autres
boitecompteur = Frame(fenetre, bg="#d1fdff")
boitecouleurs = Frame(fenetre, bg="#d1fdff")
boiteentree = Frame(fenetre, bg="#d1fdff")
boitemenu = Frame(fenetre, bg="#d1fdff")

En on n’oublie pas de modifier la boite de chaque élément pour qu’il n’apparaisse plus dans la fenêtre principale mais dans la boite dans laquelle on veut qu’il apparaisse

#exemple: ici on place le label textecompteur dans la boitecompteur:
textecompteur = Label(boitecompteur, text="nombre de clicks : %s" %(nb_clicks), font=("Helvetica",15), bg="#d1fdff", fg="black")

On va aussi créer des boutons dans boitemenu qui, lorqu’ils seront cliqués appelleront une autre boite avec un .pack() et feront disparaître le menu à l’aide de .pack_forget(). On va aussi mettre en place des boutons de retour au menu dans chacune des autres boites qui vont, eux, faire disparaître leur boite et apparaître le menu en cas de clic;

def afficher_labels():
    boitelabels.pack(expand=YES)
    boitemenu.pack_forget()

def afficher_compteur():
    boitecompteur.pack(expand=YES)
    boitemenu.pack_forget()
    
def afficher_couleurs():
    boitecouleurs.pack(expand=YES)
    boitemenu.pack_forget()
    
def afficher_entree():
    boiteentree.pack(expand=YES)
    boitemenu.pack_forget()
    
def retour_menu():
    boitemenu.pack(expand=YES)
    boitelabels.pack_forget()
    boitecompteur.pack_forget()
    boitecouleurs.pack_forget()
    boiteentree.pack_forget()
    
b_labels = Button(boitemenu, text="Afficher les Labels", font=("Consolas",15), bg="white", fg="black", command=afficher_labels, width=75)
b_compteur = Button(boitemenu, text="Afficher le compteur", font=("Consolas",15), bg="white", fg="black", command=afficher_compteur, width=75)    
b_couleurs = Button(boitemenu, text="Afficher les boutons pour changer la couleur du titre", font=("Consolas",15), bg="white", fg="black", command=afficher_couleurs, width=75)    
b_entree = Button(boitemenu, text="Afficher le champ pour changer le titre", font=("Consolas",15), bg="white", fg="black", command=afficher_entree, width=75)
b_retour1 = Button(boitelabels, text="retourner au menu", font=("Consolas",15), bg="white", fg="black", command=retour_menu, width=35)
b_retour2 = Button(boitecompteur, text="retourner au menu", font=("Consolas",15), bg="white", fg="black", command=retour_menu, width=35)
b_retour3 = Button(boitecouleurs, text="retourner au menu", font=("Consolas",15), bg="white", fg="black", command=retour_menu, width=35)
b_retour4 = Button(boiteentree, text="retourner au menu", font=("Consolas",15), bg="white", fg="black", command=retour_menu, width=35)
Le Menu

Tout ça est bien beau, mais pour l’instant on ne sait qu’afficher différents éléments les uns au dessous des autres, mais pas les uns à côté des autres. C’est là que les grilles vont intervenir

Utiliser les grilles

Pour faire une grille, on va remplacer les .pack() que l’on utilisait par des .grid(). Il faut savoir que si l’on utilise une grille pour un élément, tous les éléments présents dans la même boite devront eux aussi utiliser cette même grille.

Comme on voudrait seulement placer deux des boutons côte à côte, on va les placer dans une sous boite de la boite du menu:

#création de la sous boite
sousboitemenu = Frame(boitemenu, bg="#d1fdff")

Et on modifie la boite des deux boutons concernés, ainsi que leurs dimensions pour qu’ils soient alignés avec les autres. (On ajoute aussi un \n dans le texte du second bouton pour qu’il s’affiche sur deux lignes)

b_compteur = Button(sousboitemenu, text="Afficher le compteur", font=("Consolas",15), bg="white", fg="black", command=afficher_compteur, width=36, height=2)    
b_couleurs = Button(sousboitemenu, text="Afficher les boutons \npour changer la couleur du titre", font=("Consolas",15), bg="white", fg="black", command=afficher_couleurs, width=36, height=2) 

Il est temps maintenant de placer les boutons en grille à l’aide de .grid().

Cette méthode accepte plusieurs arguments:

  • row qui définit la ligne dans laquelle l’élément va être placé
  • column qui définit sa colonne
  • sticky qui définit de quel côté de la fenêtre l’élément doit se rapprocher ( W, E, S ou N pour les quatre points cardinaux)
  • padx et pady (optionnels) qui définissent les marges en x et en y
b_compteur.grid(row=1, column=0, sticky=W, pady=5, padx=5)
b_couleurs.grid(row=1, column=1, sticky=W, pady=5, padx=5)

On obtient alors le résultat suivant:

Conclusion

Voilà, vous possédez maintenant les bases nécessaires pour pouvoir créer vos propres interfaces graphiques pour vos scripts Python !

Si jamais certains éléments ne sont pas assez clairs, vous pouvez toujours télécharger le script créé pour ce tutoriel afin de le tester ou de lire son code.

NumApps

Le jeu 2048, sur ta NumWorks !

2048 est un jeu vidéo de type puzzle conçu en mars 2014 par le développeur indépendant italien Gabriele Cirulli et publié en ligne sous licence libre. La version présenté ici est le portage du jeu, réalisé par Eric Schrafstetter et légèrement modifiée par nos soins pour un rendu sublimé sur la NumWorks.

Portage du jeu, en python, sur NumWorks

Eric Schrafstetter est le premier a voir porté ce jeu sur la calculatrice NumWorks, ce jeu nécessite une version Epsilon 13 ou supérieur installé sur la calculatrice. En effet, la version 13 de l’OS de cette calculatrice à permis le développement de jeux car il était désormais possible de récupérer les touches du clavier saisies.

Publié le premier avril 2020, la première version de ce programme a été suivie par une version améliorée proposée le 30 juillet 2021. Nous avons découvert ce jeu fin décembre 2021, et avons décidé de l’améliorer afin de proposer une expérience joueur améliorée.

La version 2 de Eric Schrafstetter

La version modifié par nos soins

Modifications réalisées par nos soins

  1. La grille a été centrée et la bordure épaisse réduite, on conserve l’effet « ombre » sans écraser la grille.
  2. Le score a été déplacé, il est moins visible pendant la partie, l’interface présente une symétrie visuelle et la modification du score n’attire pas l’œil.
  3. Les micros-lags ont été supprimés. Dans la version d’origine, la grille était entièrement effacée puis reconstruite ce qui générait un affichage haché avec un effet de rafraichissement pas agréable. Le jeu est du coup plus fluide, plus agréable à utiliser.
  4. Une détection de la fin de partie a été implémentée, si la grille est remplie et que 4 touches sont pressées sans que rien ne se passe, on signale au joueur que la partie est terminée.

Une vidéo de présentation du codage

Si le codage de ce jeu vous intéresse, la vidéo de son auteur est incontournable. 😍

Commandes

Le jeu se joue simplement avec les touches fléchées de la calculatrice.

Télécharger

Des liens vers les version 1 et 2 du jeu, celle de Eric Schrafstetter sont au début de cet article. Nous vous proposons de tester la version modifiée par nos soins :

Projets

Jeu du pendu codé en Python

Vous êtes seul et ne savez pas quoi faire? Nous vous proposons un jeux du pendu: affrontez l’ordinateur!

Notre projet consiste à créer un programme en python sur un jeux traditionnel d’école: le jeux du pendu. 

Introduction

L’ordinateur va choisir un mot parmi une liste prédéfinie et inconnue de vous, joueur. Vous devez alors essayer de retrouver le mot en proposant différentes lettres. Vous avez le droit a 8 erreurs, et pas une de plus. Une fois toutes les tentatives épuisées, le bonhomme est pendu: vous avez perdu! Mais si vous trouvez le mot avant d’avoir épuisé toutes vos chances, alors c’est gagné!!

Lorsque vous avez trouvé la bonne lettre, un message s’affiche pour vous informer que votre proposition est juste. Le mot qui était vide se complète alors par la bonne lettre. Ainsi il sera plus facile de deviner quel est le mot.

Le code en entier

from random import randint,choice

def initialisation(): 
    liste_mots=["chocolat", "tentacule", "dictionnaire", "magnifique", "saucisson", "xylophone", "serveur", "houx", "clavier"]
    choix = choice(liste_mots) 
    myst = ""
    for i in choix:
        myst += "_"
    return (choix,myst)

def verification(lettre,mot): 
    if lettre in mot:
        return True
    else:
        return False

def mot_complet(mot):
    if "_" not in mot:
        return True
    else:
        return False

def complete_mot(lettre,mot,myst):
    for i in range(len(mot)):
        if mot[i] == lettre
            temp = list(myst) 
            temp[i] = lettre 
            myst = "".join(temp)
    return myst 

def dessin_pendu(n):
    dessin = ""
    if n == 1:
        dessin = "\n" * 7 + " =============="
    elif n == 2:
        dessin = "\n" + "  ||/\n" + "  ||\n" + "  ||\n" + "  ||\n" + "  ||\n" + " /||\n" + " =============="
    elif n == 3:
        dessin = "==============\n" + "  ||/\n" + "  ||\n" + "  ||\n" + "  ||\n" + "  ||\n" + " /||\n" + " =============="
    elif n == 4:
        dessin = "============Δ=\n" + "  ||/        |\n" + "  ||\n" + "  ||\n" + "  ||\n" + "  ||\n" + " /||\n" + " =============="
    elif n == 5:
        dessin = "============Δ=\n" + "  ||/        |\n" + "  ||         O\n" + "  ||\n" + "  ||\n" + "  ||\n" + " /||\n" + " =============="
    elif n == 6:
        dessin = "============Δ=\n" + "  ||/        |\n" + "  ||         O\n" + "  ||        /|\ \n" + "  ||\n" + "  ||\n" + " /||\n" + " =============="
    elif n == 7:
        dessin = "============Δ=\n" + "  ||/        |\n" + "  ||         O\n" + "  ||        /|\ \n" + "  ||         |\n" + "  ||\n" + " /||\n" + " =============="
    elif n == 8:
        dessin = "============Δ=\n" + "  ||/        |\n" + "  ||         O\n" + "  ||        /|\ \n" + "  ||         |\n" + "  ||        / \ \n" + " /||\n" + " =============="
    return dessin
  
def jeu():
    tentatives = 8
    mot_choisi,mot_cache = initialisation()
    while (tentatives > 0) and (not mot_complet(mot_cache)):
        print("\nNombre de tentatives restantes :\t", tentatives, "\n")
        print(" ".join(mot_cache),"\n\n")
        lettre_choisie = input("Choisir une lettre (en minuscule) :\t")
        if verification(lettre_choisie,mot_choisi):
            mot_cache = complete_mot(lettre_choisie, mot_choisi, mot_cache)
            print("\nLa lettre se trouve bien dans le mot\n")
        else:
            tentatives -= 1
            print("\nLa lettre ne se trouve pas dans le mot...\n\n", dessin_pendu(8 - tentatives),"\n\n")
    if tentatives == 0:
        print(" ".join(mot_cache))
        print("\nLe mot était :\n")
        print(" ".join(mot_choisi))
        print("\nVous avez malheureusement échoué ... \n")
    if "_" not in mot_cache:
        print(" ".join(mot_cache),"\n\n")
        print("\nBravo, vous avez gagné !\n")
    input("\nVeuillez taper sur la touche Entrer pour recommencer\n\n")
    menu()    
        
def menu():
    print("\n-----------BIENVENUE SUR LE JEU DU PENDU-----------\n")
    print("-------------------BONNE CHANCE--------------------\n\n")
    print("C'EST PARTI! \n")
    jeu()

menu()

Nous avons choisi la bibliothèque random pour avoir accès à choice qui nous permet donc d’avoir un programme capable de faire un choix de manière complètement aléatoire.

Les appels aux différentes fonctions

Le programme ci dessus se résume en une ligne;

menu()

Celle ci sert à faire appel à la fonction menu, qui elle même va faire appel à d’autres fonctions. Ainsi le programme peut s’exécuter. Par exemple, on remarque que la fonction menu fait appel à la fonction jeu, qui elle même, fait appel à d’autres fonctions (à voir dans le code ci-dessous)

def menu():
    #cette fonction présente le jeu
    jeu() #renvoie à la fonction "jeu"
    
def jeu():
  #cette fonction est le squelette du programme. elle permet de relier les nombreuses fonctions de manière a pouvoir jouer
    mot_choisi,mot_cache = initialisation() #renvoie à la fonction initialisation
    while (tentatives > 0) and (not mot_complet(mot_cache)): #tant que le nb de tentatives est > 0 et que --> renvoie à la fonction mot complet  --> alors...
        
        """ 
        etc...
        """

Les grandes lignes

Pour pouvoir jouer au jeu du pendu, nous avons besoin d’avoir une liste de mots prédéfinie dans laquelle, un mot au hasard, va être choisi. Le mot, qui aura été sélectionné, apparaitra sous forme de tirets (le nombre de tirets correspondant au nombre de lettres dans le mot).

def initialisation():
    liste_mots=["chocolat", "tentacule", "dictionnaire", "magnifique", "saucisson", "xylophone", "serveur", "houx", "clavier"]
    choix = choice(liste_mots) 
    myst = "" 
    for i in choix:
        myst += "_"
    return (choix,myst)

Une fois le mot apparu sous la forme de tirets, il faut que lorsqu’une lettre est découverte par le joueur, elle prenne la place du tiret auquel elle correspond (complete_mot). Pour cela, le mot va être transformé en une liste et pourra donc être parcouru par le programme. Mais tout d’abord il faut vérifier que la lettre se trouve bien dans le mot (verification).

def verification(lettre,mot): 
    if lettre in mot:
        return True
    else:
        return False

def complete_mot(lettre,mot,myst):  
    for i in range(len(mot)):
        if mot[i] == lettre
            temp = list(myst) # Transforme le mot en une liste temp
            temp[i] = lettre # Remplace le tiret underscore par la lettre
            myst = "".join(temp) # Re-combine les éléments de temp pour en faire à nouveau un mot
    return myst # Renvoi le mot caché, complété avec la (ou les) lettre(s)

Dans ce jeu, nous avons fait le choix de représenter un bonhomme pendu, comme dans le jeu classique d’école. Pour cela, nous avons programmé une fonction permettant d’afficher une partie du dessin du pendu, au fur et à mesure que le nombre de chances diminue, . Au début il n’y a donc pas de dessin; dessin = "" . Plus le pendu apparait, plus les chances de trouver le bon mot diminuent.

def dessin_pendu(n):
    dessin = ""
    if n == 1:
        dessin = "\n" * 7 + " =============="
    elif n == 2:
        dessin = "\n" + "  ||/\n" + "  ||\n" + "  ||\n" + "  ||\n" + "  ||\n" + " /||\n" + " =============="
    elif n == 3:
        dessin = "==============\n" + "  ||/\n" + "  ||\n" + "  ||\n" + "  ||\n" + "  ||\n" + " /||\n" + " =============="
    elif n == 4:
        dessin = "============Δ=\n" + "  ||/        |\n" + "  ||\n" + "  ||\n" + "  ||\n" + "  ||\n" + " /||\n" + " =============="
    elif n == 5:
        dessin = "============Δ=\n" + "  ||/        |\n" + "  ||         O\n" + "  ||\n" + "  ||\n" + "  ||\n" + " /||\n" + " =============="
    elif n == 6:
        dessin = "============Δ=\n" + "  ||/        |\n" + "  ||         O\n" + "  ||        /|\ \n" + "  ||\n" + "  ||\n" + " /||\n" + " =============="
    elif n == 7:
        dessin = "============Δ=\n" + "  ||/        |\n" + "  ||         O\n" + "  ||        /|\ \n" + "  ||         |\n" + "  ||\n" + " /||\n" + " =============="
    elif n == 8:
        dessin = "============Δ=\n" + "  ||/        |\n" + "  ||         O\n" + "  ||        /|\ \n" + "  ||         |\n" + "  ||        / \ \n" + " /||\n" + " =============="
    return dessin
dessin final (tentative = 0, le joueur a perdu)

Pour terminer, nous avons donc crée une fonction qui, comme dit plus haut, structure notre programme. Elle lie toutes les autres fonctions. Le nombre de tentatives y est rentré, et le programme se lance.

Tant que le nombre de tentatives est supérieur à 0 et que le mot n’est pas complet, le script s’exécute. Si la lettre choisie par le joueur est dans le mot, alors le (ou les dans certains cas) tiret correspondant à la lettre sera remplacé par celle-ci. Sinon, le nombre de tentatives diminue de 1 et le dessin du pendu correspondant au nombre de tentatives apparait.

Pour gagné, il suffit donc de vérifier s’il reste ou non des tirets dans le mot (mot_complet). Lorsqu’il n’y en a plus et que tentative>0, alors c’est gagné. Et lorsque le nombre de tentative = 0, alors le mot apparait en entier, c’est perdu.

def jeu():
    tentatives = 8
    mot_choisi,mot_cache = initialisation()
    while (tentatives > 0) and (not mot_complet(mot_cache)):
        print("\nNombre de tentatives restantes :\t", tentatives, "\n")
        print(" ".join(mot_cache),"\n\n") # Pour un affichage montrant bien le nombre de lettres à trouver en séparant chaque tiret underscore
        lettre_choisie = input("Choisir une lettre (en minuscule) :\t")
        if verification(lettre_choisie,mot_choisi):
            mot_cache = complete_mot(lettre_choisie, mot_choisi, mot_cache)
            print("\nLa lettre se trouve bien dans le mot\n")
        else:
            tentatives -= 1
            print("\nLa lettre ne se trouve pas dans le mot...\n\n", dessin_pendu(8 - tentatives),"\n\n")
    if tentatives == 0:
        print(" ".join(mot_cache))
        print("\nLe mot était :\n")
        print(" ".join(mot_choisi))
        print("\nVous avez malheureusement échoué ... \n")
    if "_" not in mot_cache:
        print(" ".join(mot_cache),"\n\n")
        print("\nBravo, vous avez gagné !\n")
    input("\nVeuillez taper sur la touche Entrer pour recommencer\n\n")
    menu()    
    
    
def mot_complet(mot): 
    if "_" not in mot:
        return True
    else:
        return False    

Une fois la partie finie, il ne reste plus qu’à cliquer sur la touche Entrer et le programme nous renvoie au menu de base. Une nouvelle partie peut commencer.

A vous de jouer

Si contre, vous trouverez le lien pour télécharger le programme et faire des parties dignes de ce nom. Quelques explications ont été rajoutées dans ce code pour, si vous le souhaitez, réussir à comprendre plus en détail comment il fonctionne.

Maintenant, à vous de jouer!

Projets

Simulateur de transmission du covid-19

Vérifiez que vous respectez bien le protocole sanitaire avec ce simulateur. Il vous permettra de vous donner une idée sur votre civisme, votre exposition au coronavirus en espace clos ainsi qu’à visualiser les variables qui entrent en jeu dans ce processus de transmission.

Introduction

Ce projet a été réalisé par Iliess L., Luca B. et Romain B., élèves en première NSI en 2021/2022 au lycée Louis Pasteur.

Depuis près de deux ans, nous sommes en pleine pandémie. Cette pandémie a engendré un très grand nombre de morts. Pour éviter que d’autres gens ne meurent à cause du Covid-19 et sensibiliser, nous avons décidé de créer un simulateur de propagation de Covid-19 assez fiable. Malgré que cette histoire de virus relève de la biologie, sa transmission, elle, relève de la physique : la propagation d’aérosols (mini-gouttelettes en suspension dans l’air éjectées par une personne) ainsi une équation a pu être élaborée.

Ce programme permet de calculer le pourcentage de chance que quelqu’un se fasse contaminer par le Covid-19 dans un espace clos et plus précisément dans une salle de classe (d’environ 125 m³). Le résultat dépend du comportement de l’utilisateur ainsi chaque personne testant ce simulateur se sent un minimum concerné.

Nous demandons donc à l’utilisateur différentes informations sur la conduite d’un groupe de personnes dans un espace fermé notamment s’ils ouvrent beaucoup les fenêtres, si le port du masque est bien respecté, le temps passé dans la salle, le nombre de personne dans la salle et le débit d’air inspiré/expiré.

Le code

Le programme est composé de 5 fonctions et de 3 lignes de code. Les fonctions sont toutes dans le même genre, seuls les « prints », les variables et les valeurs changent.

Analysons une fonction :

La première partie de la fonction concerne les différents cas possibles.

def port_du_masque(): #savoir comment le masque est porté en classe, c'est une proportion
    print("1 - Dans votre classe, tout le monde porte bien le masque sur le nez et tout le temps.")
    print("2 - Il y en a quelques uns qui le mettent sous le nez.")
    print("3 - Tout le monde le porte tout le temps sous le nez.")
    print("4 - Personne ne le porte !")
    print()

Ensuite, nous utilisons une instruction conditionnelle pour permettre à l’utilisateur de choisir parmi les choix proposés et ainsi y associer une valeur correspondante au choix.

    choix = int(input("Veuillez selectionner votre choix:\t"))
    if choix == 1:
        F = 0.1
    elif choix == 2:
        F =  0.2
    elif choix == 3:
        F = 0.80
    elif choix == 4:
        F = 1

Et enfin une petite sécurité qui permet de vous renvoyez au début de la fonction si la réponse de l’utilisateur ne convient pas aux propositions.

    else:
        print()
        print("Votre choix n'est pas valide, veuillez taper 1, 2 ou 3 s'il vous plait.")
        print()
        port_du_masque()
    return F

4 des 5 fonctions sont exécutées dans une fonction globale. Cette fonction fait le plus gros du travail. Elle exécute toutes les autres fonctions, fait le calcul final de probabilité et compare le résultat de l’utilisateur aux normes de l’Etat.

Voici le calcul utilisé pour calculer la probabilité d’infection d’une personne.

print (" Dans votre classe, il y a",(nb_personne*t*Q**2*f**2*quantum)/(L*volume), "% de chance que quelqu'un soit infecté.")#calcul de la probabilité

Problèmes rencontrés

Le premier problème qu’on a pu rencontrer est le suivant : rendre une variable locale globale. La variable calculée dans la fonction était perdue si on ne la sauvegardait pas dans une nouvelle variable globale.

On a trouvé deux moyens de contrer ce problème :

Le premier est d’utiliser la fonction native « global ».

def temps_expo():#savoir le temps passé en classe en heure
    global t
    print("Combien de temps en heures passez-vous en classe en moyenne ? ")
    print()
    print("1 - entre 0 et 3 heures.")
    print("2 - entre 3 et 6 heures.")
    print("3 - entre 6 et 9 heures.")
    print()
    choix = int(input("Veuillez selectionner votre choix:\t"))
    if choix == 1:
        T = 2
    elif choix == 2:
        T = 5
    elif choix == 3:
        T = 8
    else:#permet de renvoyer au début de la fonction si le choix ne convient pas
        print()
        print("Votre choix n'est pas valide, veuillez taper 1, 2 ou 3 s'il vous plait.")
        print()
        temps_expo()
     t = T

Et le deuxième est d’exécuter la fonction directement dans la fonction globale et en même temps, assigner la valeur que la fonction renvoie à une nouvelle variable.

def proba_totale(): #probabilité d'infection
    print("------------------------------------------------------")
    print("|                       COVID                        |")
    print("------------------------------------------------------")
    t = temps_expo()

Le deuxième problème était : le « return » de la fonction qui ne marchait pas.

Pour résoudre ce problème, nous avons tout simplement décidé de remplacer le « return » par un « print ».

	print (" Dans votre classe, il y a",(nb_personne*t*Q**2*f**2*quantum)/(L*volume), "% de chance que quelqu'un soit infecté.")#calcul de la probabilité
    # return (nb_personne*t*Q**2*f**2*quantum)/(L*volume)

Conclusion

Ce simulateur permet de calculer la probabilité d’infection d’une personne dans un espace fermé par conséquent il peut permettre de faire réagir certaines personnes qui ne respectent pas les règles mises en place par le gouvernement : ce programme a aussi un objectif de sensibilisation.

En bas de la page, vous pourrez télécharger le programme.

Crédits

Toutes les informations et la formule utilisées proviennent de la chaine youtube Scienceetonnante tenu par David Louapre, un physicien et la vidéo d’où nous avons tiré toutes nos informations est inspirée des recherches de physiciens du MIT basé aux Etats-Unis : Bazant M. Z., & Bush J. W..