Étiquette : Python

Projets

Décor Urbain

Plongeons dans un univers qui évoque sans le savoir pourquoi une nostalgie avec cette scène inspirée de ces soirs où après être passer une soirée avec ses amis il est temps de retourner à sa voiture dans la nuit et l’humidité. Au premier plan, une voiture au design minimaliste évoque les lignes classiques d’une BMW M3 e30, garée devant un bâtiment aux néons vibrants. Sous un ciel étoilé, l’ensemble évoque une esthétique des années 80, où le contraste entre l’ombre et les couleurs vives nous transporte dans une ambiance intemporelle et nostalgique.

Inspirations

Pour créer nôtre image, celle-ci nous a beaucoup servit de repère, notamment dans les perspectives.

source : @initial_clip (instagram)

Cette voiture est celle que l’on a représentée, une BMW M3 e30. Mise en vente dans le milieu des années 80

Résultat Final

Pour générer cette image le script est découper en plusieurs parties :

  • Mise en place du script
  • L’arrière plan
  • Le bâtiment
  • La voiture
  • L’enregistrement de l’image

Mise en place du script

import turtle
import random
try:
    from PIL import Image
    pillow_installed = True
except:
    print("Oops! - ModuleNotFoundError: No module named 'PIL' - RTFM :")
    print("https://nsi.xyz/py2png")
    pillow_installed = False
titre = "Décor Urbain"
turtle.title(titre)
screen = turtle.Screen()
screen.setup(width=1920, height=1080)
turtle.speed(150000)
turtle.ht()
turtle.colormode(255)

On commence le code en important tout nos modules nécessaires : Turtle, Random et Pillow.

import turtle
import random
try:
    from PIL import Image
    pillow_installed = True
except:
    print("Oops! - ModuleNotFoundError: No module named 'PIL' - RTFM :")
    print("https://nsi.xyz/py2png")
    pillow_installed = False
titre = "Décor Urbain"
turtle.title(titre)
screen = turtle.Screen()
screen.setup(width=1920, height=1080)
turtle.speed(150000)
turtle.ht()
turtle.colormode(255)

On continue avec la mise en place du titre de la fenêtre, des dimensions de l’écran, la vitesse du curseur, la disparition du curseur et le système de couleur mit en rgb.

L’arrière plan

On rentre dans le vif du sujet avec l’arrière plan de l’image. Qui comporte le ciel étoilé et la route avec ses trottoirs.

# Fonction pour dessiner le fond avec un dégradé
def fond():
    rciel, gciel, bciel = 176, 196, 205  # Couleur initiale du ciel
    hauteur = -351
    turtle.penup()
    turtle.goto(-960, hauteur)
    turtle.pendown()

    # Boucle pour créer un dégradé de couleur vers le haut
    while hauteur < 540:
        turtle.pencolor(int(rciel), int(gciel), int(bciel))
        turtle.forward(1920)
        hauteur += 1
        turtle.goto(-960, hauteur)
        bciel = max(100, bciel - 205 / 540)  # Ajuste la composante bleue
        rciel = max(0, rciel - 176 / 540)    # Ajuste la composante rouge
        gciel = max(0, gciel - 196 / 540)    # Ajuste la composante verte

# Fonction pour dessiner des étoiles aléatoirement
def etoiles():
    for i in range(150):
        turtle.penup()
        x = random.randint(-960, 960)
        y = random.randint(-351, 540)
        turtle.goto(x, y)
        turtle.pendown()
        turtle.dot(random.randint(1, 8), "white")
        
# Dessiner la route en perspective
def route():
    turtle.pensize(7)
    
    turtle.begin_fill()
    turtle.color("black","Slate Grey")
    turtle.penup()
    turtle.goto(-428,-81)
    turtle.pendown()
    turtle.goto(-928,-81)
    turtle.goto(-928,-411)
    turtle.goto(-428,-81)
    turtle.end_fill()
    
    turtle.begin_fill()
    turtle.color("black")
    turtle.penup()
    turtle.goto(-928,-411)
    turtle.pendown()
    turtle.goto(-428,-81)
    turtle.goto(1200,-300)
    turtle.goto(315,-900)
    turtle.goto(-928,-411)
    turtle.end_fill()

    turtle.color("white")
    turtle.penup()
    turtle.goto(-775,-411)
    turtle.pendown()
    turtle.goto(-275,-81)
    
    turtle.color("Slate Grey")
    turtle.begin_fill()
    turtle.penup()
    turtle.goto(315,-410)
    turtle.pendown()
    turtle.goto(-500,-330)
    turtle.goto(0,0)
    turtle.goto(1000,-350)
    turtle.goto(315,-410)
    turtle.end_fill()
    
# Appelle les fonctions pour dessiner le fond, les étoiles et la route
fond(),etoiles(),route()

Le ciel :

# Fonction pour dessiner le fond avec un dégradé
def fond():
    rciel, gciel, bciel = 176, 196, 205  # Couleur initiale du ciel
    hauteur = -351
    turtle.penup()
    turtle.goto(-960, hauteur)
    turtle.pendown()

    # Boucle pour créer un dégradé de couleur vers le haut
    while hauteur < 540:
        turtle.pencolor(int(rciel), int(gciel), int(bciel))
        turtle.forward(1920)
        hauteur += 1
        turtle.goto(-960, hauteur)
        bciel = max(100, bciel - 205 / 540)  # Ajuste la composante bleue
        rciel = max(0, rciel - 176 / 540)    # Ajuste la composante rouge
        gciel = max(0, gciel - 196 / 540)    # Ajuste la composante verte

.Le ciel de l’image est en grand dégradé qui vire d’un gris-bleu à un bleu nuit.
Pour ce faire on défini la couleur initiales du ciel, avec sa quantité de rouge, vert et bleu. On détermine ensuite la hauteur où démarre le dégradé avec la variable « hauteur ». On déplace le curseur en -960 et à la hauteur minimale ici -351.
On démarre une boucle qui prend fin lorsque hauteur atteint 540, soit la hauteur maximale souhaitée. La couleur y est défini sur les valeurs actuelles de rouge, de vert et de bleu, on fait avancer le curseur de 1920, la largeur de la fenêtre. On ajoute un à la hauteur et déplace le curseur de nouveaux en -960 quant aux abscisses et à la nouvelle hauteur. La couleur est ensuite modifier en soustrayant la quantité initiale de la couleur divisé par le nombre de traits faits. Ce qui va logiquement amené la valeur à 0 au bouts de toute les itérations. Mais voulant du bleu et non du noir en couleur finale en instaure une limite avec la fonction max() qui est définie sur 100.

Les étoiles :

# Fonction pour dessiner des étoiles aléatoirement
def etoiles():
    for i in range(150):
        turtle.penup()
        x = random.randint(-960, 960)
        y = random.randint(-351, 540)
        turtle.goto(x, y)
        turtle.pendown()
        turtle.dot(random.randint(1, 8), "white")

Pour faire les étoiles on défini le nombre d’étoiles que l’on veut mettre ici c’est défini en argument de range(), que est défini sur 150 itérations. On défini une variable x qui comporte une valeur entière aléatoire entre -960 et 960, de même pour la variable y cette fois entre -351 et 540. On déplace le curseur à ces valeurs qui servent de coordonnées. Et on y fait un point d’un diamètre aléatoire entre 1 et 8 et de couleur blanche : range()

La génération aléatoire permet d’avoir parfois la formations de constellation non calculées. Cela donne je trouve une belle authenticité à l’image.

La route :

# Dessiner la route en perspective
def route():
    turtle.pensize(7)
    
    #Tottoir gauche
    turtle.begin_fill()
    turtle.color("black","Slate Grey")
    turtle.penup()
    turtle.goto(-428,-81)
    turtle.pendown()
    turtle.goto(-928,-81)
    turtle.goto(-928,-411)
    turtle.goto(-428,-81)
    turtle.end_fill()
    
    #Route
    turtle.begin_fill()
    turtle.color("black")
    turtle.penup()
    turtle.goto(-928,-411)
    turtle.pendown()
    turtle.goto(-428,-81)
    turtle.goto(1200,-300)
    turtle.goto(315,-900)
    turtle.goto(-928,-411)
    turtle.end_fill()

    turtle.color("white")
    turtle.penup()
    turtle.goto(-775,-411)
    turtle.pendown()
    turtle.goto(-275,-81)
    
    #Troittoir droit
    turtle.color("Slate Grey")
    turtle.begin_fill()
    turtle.penup()
    turtle.goto(315,-410)
    turtle.pendown()
    turtle.goto(-500,-330)
    turtle.goto(0,0)
    turtle.goto(1000,-350)
    turtle.goto(315,-410)
    turtle.end_fill()

Le code là assez répétitif, on trace un polygone que l’on rempli de couleur à des fonctions turtle.begin_fill() qui déterminer le début du remplissage et turtle.end_fill() qui détermine la fin du remplissage. On fait ceci pour trois polygones, un qui va faire le trottoir gauche, un qui va faire la route, et l’autre qui va s’installer devant la route et va servir de trottoir droit. Après le traçage du polygone pour la route on trace une ligne simple qui sert de ligne blanche pour ajouter du réalisme à la route.

Les polygones et traits paraissent pas propres, et avec des coordonnées douteuses mais seront par la suites recouverts de façons à ce que cela soit joli.

Appel des fonctions :

# Appelle les fonctions pour dessiner le fond, les étoiles et la route
fond(),etoiles(),route()

On appelle finalement les trois fonctions.

Le bâtiment

#fonction pour convertir les points de mon dessin en des points sur python
def point(x,y): 
    l = (1920 * x) / 14,5
    h = (1080 * y) / 8
    return l,h

pen = turtle.Turtle() 
pen.penup() 
pen.pensize(3)
pen.speed(15000)
pen.ht()
# Liste des coordonnées pour les points du bâtiment 

def batiment():
    coords = [ (315, 445),(-398, 324),(-398, -284),(315, -351), (398, -351), (973, -324), (973, 324),  (315, 445), ] 
    pen.fillcolor("light gray")
    pen.penup()
    pen.goto(coords[0])          
    pen.pendown()
    pen.begin_fill()
    for x, y in coords[1:]:
        pen.goto(x, y)
    pen.end_fill()
# Dessiner la ligne centrale 
    pen.penup() 
    pen.goto(315, 445) # Retour au sommet
    pen.pendown() 
    pen.goto(315, -351) # Descend jusqu'au bas du bâtiment
    return

def pare_soleil():
    coords = [(-398, 27),  (315, 54), (315, -108),(-438, -81), (-398, 27), (-110, 37.5),(-157, -90.5),(108, -101.2),(151,47.25),(315,54),(274,-108),]
    pen.penup()
    pen.goto(coords[0]) # Aller au premier point de la liste
    pen.pendown() 
    pen.fillcolor("#FFD700")# colorier le pare soleil côté gauche
    pen.penup()
    pen.goto(coords[0])          
    pen.pendown()
    pen.begin_fill()
    pen.goto(coords[5])
    pen.goto(coords[6])
    pen.goto(coords[3])
    pen.end_fill()

    pen.fillcolor("#FAFAD2")# colorier le pare soleil côté droit
    pen.penup()
    pen.goto(coords[5])          
    pen.pendown()
    pen.begin_fill()
    pen.goto(coords[1])
    pen.goto(coords[2])
    pen.goto(coords[6])
    pen.end_fill()

    pen.fillcolor("#FFFFF0")# colorier le pare soleil sur l'extrémité de droite pour donné un effet de 3D
    pen.penup()
    pen.goto(coords[1])          
    pen.pendown()
    pen.begin_fill()
    pen.goto(coords[1])
    pen.goto(coords[2])
    pen.goto(coords[10])
    pen.goto(coords[1])
    pen.end_fill()

    pen.color("black")          # Couleur du contour
    pen.pensize(5)              # Épaisseur du contour
    pen.penup()# Début du remplissage de la figure
    pen.goto(coords[0])          # Aller au premier point de la liste des coordonnées
    pen.pendown()
# Dessiner la figure en reliant les points
    for x, y in coords[1:]:
        pen.goto(x, y)
    pen.penup()
    return
             
def contour_fenetre():
    coords = [ (-370, 162),(185, 216),(209, 202),(209,53),(185,52),(185, 201.5),(185, 216),(185, 201.5),(-350, 148),(-350, 31),(-370, 30),(-370, 162),(-350, 148),(-370, 162),(-112,187),(-112,38),(-82.5,39),(-82.5,189),(-62.5,175),(-62.5,41),]
    pen.pensize(3)
    pen.penup()
    pen.goto(coords[0]) # Aller au premier point de la liste des coordonnées
    pen.pendown() 
    for x, y in coords[1:]: # Boucle pour aller de point en point 
        pen.goto(x, y)
        pen.fillcolor("#FFFFF0")

    pen.fillcolor("#808080")# colorier en gris le contour des fentres
    pen.penup()
    pen.goto(coords[0])          
    pen.pendown()
    pen.begin_fill()
    pen.goto(coords[1])
    pen.goto(185,202)
    pen.goto(-82.5,175)
    pen.goto(coords[16])
    pen.goto(coords[15])
    pen.goto(-112,173)
    pen.goto(coords[8])
    pen.goto(coords[9])
    pen.goto(coords[10])
    pen.goto(coords[0])
    pen.end_fill()
    return

def fenetre_gauche():
    coords = [ (-370, 162),(185, 216),(209, 202),(209,53),(185,52),(185, 201.5),(185, 216),(185, 201.5),(-350, 148),(-350, 31),(-370, 30),(-370, 162),(-350, 148),(-370, 162),(-112,187),(-112,38),(-82.5,39),(-82.5,189),(-62.5,175),(-62.5,41),]
    pen.pensize(3)
    pen.fillcolor("#FF69B4")
    pen.penup()
    pen.goto(coords[8])         
    pen.pendown()
    pen.begin_fill()
    pen.goto(-112,173)
    pen.goto(coords[15])
    pen.goto(coords[9])
    pen.goto(coords[8])
    pen.end_fill()
    return

def fenetre_droite():
    coords = [ (-370, 162),(185, 216),(209, 202),(209,53),(185,52),(185, 201.5),(185, 216),(185, 201.5),(-350, 148),(-350, 31),(-370, 30),(-370, 162),(-350, 148),(-370, 162),(-112,187),(-112,38),(-82.5,39),(-82.5,189),(-62.5,175),(-62.5,41),]
    pen.pensize(3)
    pen.fillcolor("#FF69B4")# fenetre de droite
    pen.penup()
    pen.goto(coords[18])          
    pen.pendown()
    pen.begin_fill()
    pen.goto(185,202)
    pen.goto(coords[4])
    pen.goto(coords[19])
    pen.goto(coords[18])
    pen.end_fill()
    return
    
batiment(), pare_soleil(), contour_fenetre(), fenetre_gauche(), fenetre_droite()

Fonction usuelle et mise en place du code :

def point(x,y): 
    l = (1920 * x) / 14,5
    h = (1080 * y) / 8
    return l,h

Pour commencer, nous avons créé une fonction appelée point. Cette fonction consiste à convertir l’emplacement des points de notre image de référence ( en cm) en point sur python tout en gardant les proportions et symétries. Cette fonction est très simple, il suffit de lui donner 2 valeurs soit x et y puis elle va procéder à un produit en croix qui nous rendra l et h l’emplacement du point sur python.

pen.turtle est utilisé pour créer un nouvel objet turtle nommé pen.

On utilisera la “tortue” (ici pen) , on pourra ensuite l’utiliser pour dessiner.

pen.penup() Cette commande indique à la tortue de lever son stylo, ce qui signifie qu’elle va se déplacer sans tracer de ligne.

pen.pensize(3) Cette ligne définit la taille du stylo de la tortue, c’est-à-dire l’épaisseur des lignes qu’elle va tracé.

pen.speed(15000) Cette commande fixe la vitesse de déplacement de la tortue. Nous avons choisi 15000 pour être sûr que la tortue va à sa vitesse maximale en fonction de l’ordinateur utilisé.

 pen.ht() cela signifie “hide turtle”, c’est-à-dire cacher la tortue.

pen = turtle.Turtle() 
pen.penup() 
pen.pensize(3)
pen.speed(15000)
pen.ht()

Définition des fonctions :

def batiment():
    coords = [ (315, 445),(-398, 324),(-398, -284),(315, -351), (398, -351), (973, -324), (973, 324),  (315, 445), ] 
    pen.fillcolor("light gray")
    pen.penup()
    pen.goto(coords[0])          
    pen.pendown()
    pen.begin_fill()
    for x, y in coords[1:]:
        pen.goto(x, y)
    pen.end_fill()
# Dessiner la ligne centrale 
    pen.penup() 
    pen.goto(315, 445) # Retour au sommet
    pen.pendown() 
    pen.goto(315, -351) # Descend jusqu'au bas du bâtiment
    return

def batiment()

Tout d’abord, nous avons créé une fonction nommée batiment. Elle regroupe le code utilisé pour faire la base du bâtiment.

Puis , nous avons créé une liste de tuples nommé coords, chacun représentant les coordonnées (x, y) de points autour desquels la « tortue » va se déplacer pour dessiner les contours du bâtiment.

pen.fillcolor(“light gray”)

Cette commande fixe la couleur de remplissage pour le dessin de la tortue en gris clair. La tortue remplira l’intérieur du bâtiment avec cette couleur.

pen.goto(coords[0]) La tortue va se déplacer au premier point de la liste de tuples.

pen.begin_fill() démarre le processus de remplissage. Une fois que la tortue trace tous les côtés du bâtiment, elle remplira la forme fermée avec la couleur light gray définie plus haut.

pen.end_fill() termine le processus de remplissage et remplit la forme fermée (le bâtiment) en gris clair.

for x, y in coords[1:]:

 pen.goto(x, y)

Cette boucle for parcourt les coordonnées dans coords à partir du deuxième point (indiqué par coords[1:]).

À chaque itération, la tortue se déplace vers le point (x, y), dessinant une ligne entre chaque point consécutif.

pen.penup() 

pen.goto(315, 445)

pen.pendown() 

pen.goto(315, -351)

Cette partie dessine une ligne centrale verticale à l’intérieur du bâtiment.

pen.penup() lève le stylo pour que la tortue se déplace sans tracer jusqu’au sommet du bâtiment.

pen.goto(315, 445) la place en haut du bâtiment.

pen.pendown() abaisse le stylo, permettant de dessiner une ligne jusqu’à pen.goto(315, -351), qui est le bas du bâtiment.

def pare_soleil():
    coords = [(-398, 27),  (315, 54), (315, -108),(-438, -81), (-398, 27), (-110, 37.5),(-157, -90.5),(108, -101.2),(151,47.25),(315,54),(274,-108),]
    pen.penup()
    pen.goto(coords[0]) # Aller au premier point de la liste
    pen.pendown() 
    pen.fillcolor("#FFD700")# colorier le pare soleil côté gauche
    pen.penup()
    pen.goto(coords[0])          
    pen.pendown()
    pen.begin_fill()
    pen.goto(coords[5])
    pen.goto(coords[6])
    pen.goto(coords[3])
    pen.end_fill()

    pen.fillcolor("#FAFAD2")# colorier le pare soleil côté droit
    pen.penup()
    pen.goto(coords[5])          
    pen.pendown()
    pen.begin_fill()
    pen.goto(coords[1])
    pen.goto(coords[2])
    pen.goto(coords[6])
    pen.end_fill()

    pen.fillcolor("#FFFFF0")# colorier le pare soleil sur l'extrémité de droite pour donné un effet de 3D
    pen.penup()
    pen.goto(coords[1])          
    pen.pendown()
    pen.begin_fill()
    pen.goto(coords[1])
    pen.goto(coords[2])
    pen.goto(coords[10])
    pen.goto(coords[1])
    pen.end_fill()

    pen.color("black")          # Couleur du contour
    pen.pensize(5)              # Épaisseur du contour
    pen.penup()# Début du remplissage de la figure
    pen.goto(coords[0])          # Aller au premier point de la liste des coordonnées
    pen.pendown()
# Dessiner la figure en reliant les points
    for x, y in coords[1:]:
        pen.goto(x, y)
    pen.penup()
    return

pare soleil

Ensuite, nous avons créé une fonction nommée pare_soleil, qui regroupe le code utilisé pour dessiner un pare-soleil avec des effets de remplissage et de contour.

Puis, nous avons créé une liste de tuples nommée coords. Chacun de ces tuples représente les coordonnées (x, y) de points autour desquels la tortue va se déplacer pour dessiner le contour du pare-soleil.

La commande pen.penup() lève le stylo pour éviter de tracer une ligne jusqu’à la position initiale. Ensuite, pen.goto(coords[0]) déplace la tortue au premier point de la liste coords, et pen.pendown() abaisse le stylo pour pouvoir commencer à dessiner.

Pare soleil extrémité de gauche

La commande pen.fillcolor(« #FFD700 ») fixe la couleur de remplissage en jaune doré pour dessiner le côté gauche du pare-soleil.

La séquence suivante utilise pen.penup() pour lever le stylo, puis pen.goto(coords[0]) pour déplacer la tortue au premier point de la liste des coordonnées, et pen.pendown() pour abaisser le stylo à cet endroit.

pen.begin_fill() démarre le processus de remplissage. 

Ensuite, la tortue se déplace successivement aux coordonnées coords[5], coords[6], et coords[3], ce qui crée un contour fermé. pen.end_fill() termine le processus et remplit cette zone en jaune doré.

Pare soleil extrémité de droite

Ici, pen.fillcolor(« #FAFAD2 ») change la couleur de remplissage pour un jaune clair (presque crème) qui sera utilisé pour le côté droit du pare-soleil.

pen.penup() lève le stylo pour déplacer la tortue sans tracer. pen.goto(coords[5]) place la tortue au point coords[5], et pen.pendown() abaisse le stylo pour commencer à tracer le contour.

pen.begin_fill() démarre le remplissage, puis pen.goto(coords[1]), pen.goto(coords[2]), et pen.goto(coords[6]) déplace la tortue aux différents points du côté droit du pare-soleil pour former le contour. Enfin, pen.end_fill() remplit cette zone avec la couleur jaune clair.

Troisième remplissage(effet 3d)

Ensuite, pen.fillcolor(« #FFFFF0 ») définit la couleur de remplissage en blanc cassé pour créer un effet 3D sur le bord droit du pare-soleil.

La séquence suivante lève le stylo avec pen.penup(), puis pen.goto(coords[1]) positionne la tortue au point de départ du bord droit. pen.pendown() abaisse le stylo pour commencer à dessiner.

pen.begin_fill() démarre le remplissage, et la tortue se déplace aux coordonnées coords[2], coords[10], et retourne à coords[1] pour former la bordure en blanc cassé, créant ainsi un effet de profondeur. pen.end_fill() termine et remplit cette zone.

Contour

Pour le contour du pare-soleil, pen.color(« black ») définit la couleur du contour en noir, et pen.pensize(5) fixe l’épaisseur de ce contour à 5 pixels.

pen.penup() lève le stylo pour éviter de tracer avant d’arriver au point de départ. pen.goto(coords[0]) place la tortue au premier point de la liste, et pen.pendown() abaisse le stylo pour commencer le tracé.

La boucle for parcourt les coordonnées à partir du deuxième point (indiqué par coords[1:]). À chaque itération, la tortue se déplace aux coordonnées (x, y), dessinant un segment de ligne entre chaque point et formant ainsi le contour complet du pare-soleil.

Enfin, pen.penup() lève le stylo pour éviter de tracer accidentellement en dehors de la figure.

def contour_fenetre():
    coords = [ (-370, 162),(185, 216),(209, 202),(209,53),(185,52),(185, 201.5),(185, 216),(185, 201.5),(-350, 148),(-350, 31),(-370, 30),(-370, 162),(-350, 148),(-370, 162),(-112,187),(-112,38),(-82.5,39),(-82.5,189),(-62.5,175),(-62.5,41),]
    pen.pensize(3)
    pen.penup()
    pen.goto(coords[0]) # Aller au premier point de la liste des coordonnées
    pen.pendown() 
    for x, y in coords[1:]: # Boucle pour aller de point en point 
        pen.goto(x, y)
        pen.fillcolor("#FFFFF0")

    pen.fillcolor("#808080")# colorier en gris le contour des fentres
    pen.penup()
    pen.goto(coords[0])          
    pen.pendown()
    pen.begin_fill()
    pen.goto(coords[1])
    pen.goto(185,202)
    pen.goto(-82.5,175)
    pen.goto(coords[16])
    pen.goto(coords[15])
    pen.goto(-112,173)
    pen.goto(coords[8])
    pen.goto(coords[9])
    pen.goto(coords[10])
    pen.goto(coords[0])
    pen.end_fill()
    return

def contour fenetre

Nous avons créé une fonction appelée contour_fenetre, qui contient le code nécessaire pour dessiner le contour d’une fenêtre avec des effets de remplissage.

Nous définissons ensuite une liste de coordonnées coords pour représenter les points (x, y) que la tortue va relier afin de former le contour de la fenêtre.

La commande pen.pensize(3) définit l’épaisseur du trait à 3 pixels. Ensuite, pen.penup() lève le stylo pour éviter de tracer pendant le déplacement initial, et pen.goto(coords[0]) positionne la tortue au premier point de la liste de coordonnées. pen.pendown() abaisse le stylo pour commencer à dessiner.

La boucle for parcourt chaque point dans coords à partir du deuxième (indiqué par coords[1:]). À chaque itération, la tortue se déplace aux coordonnées (x, y) et trace une ligne entre chaque point, créant ainsi le contour extérieur de la fenêtre.

La commande pen.fillcolor(« #808080 ») change la couleur de remplissage en gris pour le contour de la fenêtre. Ensuite, pen.penup() lève le stylo pour éviter de tracer avant d’arriver au point de départ. pen.goto(coords[0]) positionne la tortue au premier point des coordonnées, et pen.pendown() abaisse le stylo pour commencer le remplissage de la fenêtre.

La commande pen.begin_fill() démarre le processus de remplissage. Tous les segments suivants seront remplis de gris jusqu’à ce qu’on appelle pen.end_fill()

Dans cette séquence, la tortue se déplace de point en point pour dessiner les différents segments de la fenêtre en utilisant des coordonnées spécifiques dans coords ainsi que des coordonnées explicites pour certaines parties de la fenêtre.

  • pen.goto(coords[1]), pen.goto(185, 202), et pen.goto(-82.5, 175) tracent le premier côté du contour intérieur de la fenêtre.
  • pen.goto(coords[16]), pen.goto(coords[15]), et pen.goto(-112, 173) dessinent le côté gauche de la fenêtre.
  • pen.goto(coords[8]), pen.goto(coords[9]), et pen.goto(coords[10]) tracent la partie inférieure.
  • pen.goto(coords[0]) ramène la tortue au point de départ, fermant ainsi la forme.
def fenetre_gauche():
    coords = [ (-370, 162),(185, 216),(209, 202),(209,53),(185,52),(185, 201.5),(185, 216),(185, 201.5),(-350, 148),(-350, 31),(-370, 30),(-370, 162),(-350, 148),(-370, 162),(-112,187),(-112,38),(-82.5,39),(-82.5,189),(-62.5,175),(-62.5,41),]
    pen.pensize(3)
    pen.fillcolor("#FF69B4")
    pen.penup()
    pen.goto(coords[8])         
    pen.pendown()
    pen.begin_fill()
    pen.goto(-112,173)
    pen.goto(coords[15])
    pen.goto(coords[9])
    pen.goto(coords[8])
    pen.end_fill()
    return

def fenetre Gauche

La fonction fenetre_gauche dessine une fenêtre colorée en rose (code hexadécimal #FF69B4) sur le côté gauche de la structure.

Nous commençons par définir la liste coords, qui contient les points (x, y) pour tracer les contours de la fenêtre. Ces coordonnées permettent de structurer la forme que la tortue va dessiner.

Ensuite, pen.pensize(3) définit l’épaisseur du trait à 3 pixels, et pen.fillcolor(« #FF69B4 ») fixe la couleur de remplissage en rose.

La tortue se rend au point de départ coords[8] sans tracer, puis abaisse le stylo pour commencer le remplissage.

La tortue se déplace vers plusieurs points pour créer une forme fermée

Cette séquence de déplacements trace les côtés de la fenêtre. pen.end_fill() termine le processus de remplissage en rose, créant ainsi une fenêtre complète.

def fenetre_droite():
    coords = [ (-370, 162),(185, 216),(209, 202),(209,53),(185,52),(185, 201.5),(185, 216),(185, 201.5),(-350, 148),(-350, 31),(-370, 30),(-370, 162),(-350, 148),(-370, 162),(-112,187),(-112,38),(-82.5,39),(-82.5,189),(-62.5,175),(-62.5,41),]
    pen.pensize(3)
    pen.fillcolor("#FF69B4")# fenetre de droite
    pen.penup()
    pen.goto(coords[18])          
    pen.pendown()
    pen.begin_fill()
    pen.goto(185,202)
    pen.goto(coords[4])
    pen.goto(coords[19])
    pen.goto(coords[18])
    pen.end_fill()
    return

def fenetre droite

Pour la fenêtre de droite, la fonction est exactement la même.Les seules différences sont les pen.goto(coords[.]) car les fenêtres ne sont pas superposées mais côte à côte.

Appel des fonctions :

batiment(), pare_soleil(), contour_fenetre(), fenetre_gauche(), fenetre_droite()

On appel là toutes les fonctions pour dessiner le bâtiment.

La voiture

Pour terminer le visuel de l’image on dessine la voiture.

turtle.pensize(6)

def ligne(xa,ya,xb,yb):
    turtle.penup()
    turtle.goto(xa,ya)
    turtle.pendown()
    turtle.goto(xb,yb)

# faire une ellipse
def roue(x,y,rad):
  	turtle.color("black")
    turtle.begin_fill()
    turtle.penup()
    turtle.goto(x,y)
    turtle.pendown()
    # inclinaison
    turtle.seth(45)
     
    # rad --> rayon de l'arc
    for i in range(2):
        # deux arcs de cercles

        turtle.circle(rad,90)		#côté grand
        turtle.circle(rad//2.5,90)	#côté petit, le diviseur est responsable de la perspective
    turtle.end_fill()

def phares(x,y):
    positions = [(x, y, "orange"), (x + 40, y + 5, "light cyan"), (x + 150, y + 20, "light cyan"), (x + 190, y + 25, "orange")]
    
    for pos_x, pos_y, color in positions:
        turtle.penup()
        turtle.goto(pos_x, pos_y)
        turtle.pendown()
        turtle.dot(30, color)

def pc(x,y):
    turtle.color("white smoke")
    for i in range (7):
        turtle.pensize(2)
        ligne(x+40,y+abs((y/25))-(4*i)+5,x+150,y+abs((y/20))-(4*i)+15)
    phares(x,y)


def voiture_v2():
    #CÔTÉ
    turtle.color("black","grey")
    coord = [((-330,-200),(-310,-210)),((-310,-210),(-310,-223)),((-310,-223),(-265,-220)),((-265,-220),(-270,-233)),((-270,-233),(-35,-240)),((-35,-240),(100,-280)),((100,-280), (100,-388)),((100, -388),(-293,-365)),((-293,-365),(-330,-350)),((-330,-350),(-330,-200))]
    turtle.begin_fill()
    for start, end in coord:
        turtle.penup()
        turtle.goto(start)
        turtle.pendown()
        turtle.goto(end)
    turtle.end_fill()
    ligne(-35,-240,-35,-380)
    #AILERON
    turtle.color("black")
    coord = [((-330,-200),(-250,-195)),((-250,-195),(-255,-206)),((-255,-206),(-310,-210)),((-310,-210),(-330,-200))]
    turtle.begin_fill()
    for start, end in coord:
        turtle.penup()
        turtle.goto(start)
        turtle.pendown()
        turtle.goto(end)
    turtle.end_fill()
    #HAUT
    turtle.color("black","royal blue")
    coord = [((-270,-233),(-220,-140)),((-220,-140),(-50,-150)),((-50,-150),(180,-130)),((180,-130),(250,-210)),((250,-210),(-35,-240)),((-35,-240),(-270,-233))]
    turtle.begin_fill()
    for start, end in coord:
        turtle.penup()
        turtle.goto(start)
        turtle.pendown()
        turtle.goto(end)
    turtle.end_fill()
    turtle.color("black")
    coord = [((-220,-140),(-30,-127)),((-30,-127),(180,-130)),((180,-130),(-50,-150)),((-50,-150),(-220,-140))]
    turtle.begin_fill()
    for start, end in coord:
        turtle.penup()
        turtle.goto(start)
        turtle.pendown()
        turtle.goto(end)
    turtle.end_fill()
    ligne(-50,-150,-35,-240)
    ligne(-210,-370,-190,-140)
    #AVANT
    roue(280,-380,57)		#roue avant droite
    turtle.color("black","dim gray")
    coord = [((-35,-240),(-50,-210)),((-50,-210),(-83,-207)),((-83,-207),(-83,-223)),((-83,-223),(-59,-227)),((-59,-227),(-35,-240)),((-35,-240),(100,-280)),((100,-280),(100,-388)),((100,-388), (350,-355)),((350,-355),(350,-250)),((350,-250),(250,-210)),((250,-210),(-35,-240))]
    turtle.begin_fill()
    for start, end in coord:
        turtle.penup()
        turtle.goto(start)
        turtle.pendown()
        turtle.goto(end)
    turtle.end_fill()
    ligne(100,-280,350,-250)
        
voiture_v2()
roue(-250,-383,55)		#roue arrière
roue(50,-410,65)		#roue avant gauche
pc(130,-300)

Déclaration d’une fonction usuelle :

On définit d’abord de quelle épaisseur seront les traits pour construire la voiture avec turtle.pensize(), ici défini sur 6.
On déclare ensuite une fonction ligne() avec en argument les coordonnées de deux points. Cette fonction va permettre le traçage de lignes en évitant d’utiliser la fonction turtle.forward(), trop imprécise, ou les quatre lignes habituelles en utilisant des turtle.goto(). Faisant gagner quelques lignes.

turtle.pensize(6)

def ligne(xa,ya,xb,yb):
    turtle.penup()
    turtle.goto(xa,ya)
    turtle.pendown()
    turtle.goto(xb,yb)

Traçage d’une roue :

# faire une ellipse
def roue(x,y,rad):
  	turtle.color("black")
    turtle.begin_fill()
    turtle.penup()
    turtle.goto(x,y)
    turtle.pendown()
    # inclinaison
    turtle.seth(45)
     
    # rad --> rayon de l'arc
    for i in range(2):
        # deux arcs de cercles

        turtle.circle(rad,90)		#côté grand
        turtle.circle(rad//2.5,90)	#côté petit, le diviseur est responsable de la perspective
    turtle.end_fill()

Pour la génération de la voiture on va devoir générer 3 roues. Pour ce faire on déclare une fonction roue() avec en arguments les coordonnées et le rayon de la roue.

On souhaite la roue pleine on utilise donc les balises turtle.begin_fill()() et turtle.end_fill() qui vont déterminer le remplissage des roues. On lève le stylo pour ne plus dessiner et on va aux coordonnées demandés. La fonction turtle.seth() permet d’incliner le curseur.

La route est enfaite une ellipse, une ellipse, c’est simplement deux arc de cercles de mêmes rayons reliés par deux autres arc de cercles de rayon différents aux premiers mais communs entre eux. Pour réaliser cela on utilise la fonction turtle.circle() avec en arguments le rayon demander et 90 degrés qui correspond au traçage d’un quart de cercle. On va utiliser une nouvelle fois cette fonction mais cette fois-ci avec en arguments le rayon diviser par 2.5, et toujours 90 degrés. Ceci va permettre le traçage à la suite du premier arc de cercle un deuxième plus petit.

On met ceci dans une boucle à deux itération à l’aide de for i in range(2), et on obtient une ellipse

Pare-chocs avant :

def phares(x,y):
    positions = [(x, y, "orange"), (x + 40, y + 5, "light cyan"), (x + 150, y + 20, "light cyan"), (x + 190, y + 25, "orange")]
    
    for pos_x, pos_y, color in positions:
        turtle.penup()
        turtle.goto(pos_x, pos_y)
        turtle.pendown()
        turtle.dot(30, color)

def pc(x,y):
    turtle.color("white smoke")
    for i in range (7):
        turtle.pensize(2)
        ligne(x+40,y+abs((y/25))-(4*i)+5,x+150,y+abs((y/20))-(4*i)+15)
    phares(x,y)
def phares(x,y):
    positions = [(x, y, "orange"),
                 (x + 40, y + 5, "light cyan"),
                 (x + 150, y + 20, "light cyan"),
                 (x + 190, y + 25, "orange")]
    
    for pos_x, pos_y, color in positions:
        turtle.penup()
        turtle.goto(pos_x, pos_y)
        turtle.pendown()
        turtle.dot(30, color)

Pour tracer les phares je déclare la fonction phares() avec comme arguments les coordonnées des phares.

On déclare en premier lieu les positions et couleur qu’auront les phares à l’aide d’une boucle for avec trois indices correspondants aux coordonnées en x et en y, et la couleur on parcours la liste « positions ». On lève le stylo, se déplace aux positions x et y correspondants à là où se trouvent les variables pos_x et pos_y qui parcourent la liste des coordonnées, on abaisse le stylo, utilise la fonction turtle.dot() avec en arguments une épaisseur de 30 et la couleur correspondante à là où se trouve la variable color qui parcoure la liste. La boucle for permet de mettre fin aux itérations lorsqu’on atteint la fin de la boucle.

def pc(x,y):
    turtle.color("white smoke")
    for i in range (7):
        turtle.pensize(2)
        ligne(x+40,y+abs((y/25))-(4*i)+5,x+150,y+abs((y/20))-(4*i)+15)
    phares(x,y)

Le pare-chocs est composé des phares qu’on a défini juste avant et de 7 lignes qui relient les phares. On déclare donc une fonction pc() avec en argument les coordonnées où le dessin va se faire.

On défini premièrement la couleur sur « white smoke » et l’épaisseur du trait à 2.

On commence une boucle for avec comme indice i avec une range() de 7.

Pour tracer ces traits en gardant une perspective on appelle la fonction ligne(), déclarée en début de code, avec comme arguments les coordonnées des points inscrits en argument de pc(). Quant aux abscisses de points on se contente de garder les valeurs en argument de la fonction en ajouter un léger décalage. Par contre pour les coordonnées en ordonné on ajoute à l’ordonnée de départ une fraction de celle-ci, et de manière à ce que l’ordonné d’arrivé est plus haut pour créer une perspective. On ajoute ensuite 4*i, permettant d’espacer chaque traits à chaque itérations.

Suite à cette boucle on appelle la fonction phares() avec comme arguments les arguments de pc().

def voiture_v2():
    #CÔTÉ
    turtle.color("black","grey")
    coord = [((-330,-200),(-310,-210)),((-310,-210),(-310,-223)),((-310,-223),(-265,-220)),((-265,-220),(-270,-233)),((-270,-233),(-35,-240)),((-35,-240),(100,-280)),((100,-280), (100,-388)),((100, -388),(-293,-365)),((-293,-365),(-330,-350)),((-330,-350),(-330,-200))]
    turtle.begin_fill()
    for start, end in coord:
        turtle.penup()
        turtle.goto(start)
        turtle.pendown()
        turtle.goto(end)
    turtle.end_fill()
    ligne(-35,-240,-35,-380)
    #AILERON
    turtle.color("black")
    coord = [((-330,-200),(-250,-195)),((-250,-195),(-255,-206)),((-255,-206),(-310,-210)),((-310,-210),(-330,-200))]
    turtle.begin_fill()
    for start, end in coord:
        turtle.penup()
        turtle.goto(start)
        turtle.pendown()
        turtle.goto(end)
    turtle.end_fill()
    #HAUT
    turtle.color("black","royal blue")
    coord = [((-270,-233),(-220,-140)),((-220,-140),(-50,-150)),((-50,-150),(180,-130)),((180,-130),(250,-210)),((250,-210),(-35,-240)),((-35,-240),(-270,-233))]
    turtle.begin_fill()
    for start, end in coord:
        turtle.penup()
        turtle.goto(start)
        turtle.pendown()
        turtle.goto(end)
    turtle.end_fill()
    turtle.color("black")
    coord = [((-220,-140),(-30,-127)),((-30,-127),(180,-130)),((180,-130),(-50,-150)),((-50,-150),(-220,-140))]
    turtle.begin_fill()
    for start, end in coord:
        turtle.penup()
        turtle.goto(start)
        turtle.pendown()
        turtle.goto(end)
    turtle.end_fill()
    ligne(-50,-150,-35,-240)
    ligne(-210,-370,-190,-140)
    #AVANT
    roue(280,-380,57)		#roue avant droite
    turtle.color("black","dim gray")
    coord = [((-35,-240),(-50,-210)),((-50,-210),(-83,-207)),((-83,-207),(-83,-223)),((-83,-223),(-59,-227)),((-59,-227),(-35,-240)),((-35,-240),(100,-280)),((100,-280),(100,-388)),((100,-388), (350,-355)),((350,-355),(350,-250)),((350,-250),(250,-210)),((250,-210),(-35,-240))]
    turtle.begin_fill()
    for start, end in coord:
        turtle.penup()
        turtle.goto(start)
        turtle.pendown()
        turtle.goto(end)
    turtle.end_fill()
    ligne(100,-280,350,-250)

Cette voiture est construite de plusieurs polygones le code en est de ce fait assez répétitif suivant toujours la même méthodologie :

  • On défini la couleur de contour et de remplissage du polygone avec la fonction turtle.color()
  • On déclare une liste de coordonnées des sommets de nos polygones
  • On débute le remplissage de nos polygones avec la fonction turtle.begin_fill()
  • On créer une boucle for() qui avec comme indices deux variables, start et end, qui parcourent la liste de coordonnées pour déterminer les deux points à relier
  • On relit les deux points à l’aide de turtle.goto() auxquels on met en argument les valeurs de start et end
  • On conclue avec turtle.end_fill(), pour finir le remplissage.

On note quelques moments où on ajoute des lignes supplémentaires :

  • Lorsque dans #CÔTÉ on ajoute une ligne pour définir la partie droite de la portière
  • Dans #HAUT on fait deux lignes, une qui relie le toit à ligne faite juste avant pour définir le par brise. Et une autre ligne définissant la partie gauche de la portière en reliant le toit au bas de la voiture.
  • Dans #AVANT une ligne définissant la partie haute du pare-chocs
  • Dans #AVANT on appelle la fonction roue() pour faire la roue gauche qui sera en partie cachée par la voiture

Appel des fonctions :

On appel toutes les fonctions ensembles

voiture_v2()
roue(-250,-383,55)		#roue arrière
roue(50,-410,65)		#roue avant gauche
pc(130,-300)

L’enregistrement de l’image :

# Enregistrement de l'image
image = turtle.getcanvas()
nom_du_fichier_sans_extension=titre+"_"+hex(random.randint(2**30+2**25,2**30+2**25+2**24-1))[2:]
image.postscript(file=nom_du_fichier_sans_extension+".ps", colormode='color')
try:
    psimage = Image.open(nom_du_fichier_sans_extension+".ps")
    psimage.load(scale=2)
    psimage_resized = psimage.resize((1920, 1080))
    psimage.save(nom_du_fichier_sans_extension+".png")
    print(nom_du_fichier_sans_extension+".png", psimage.size, "sauvegardé dans le dossier")    
except:
    if not pillow_installed:
        print("Oops! - ModuleNotFoundError: No module named 'PIL' - RTFM :")
        print("https://nsi.xyz/py2png")
    else:
        print("Oops! - 'ghostscript' not installed- RTFM :")
        print("https://nsi.xyz/py2png")
turtle.exitonclick()

turtle.done()

On récupère l’image, on lui assigne un nom aléatoire afin de ne pas écraser les autres et on l’enregistre automatiquement dans le fichier où se trouve le programme, au format png.

Image finale

Lors de l’enregistrement en png, on voit des traits qui apparaissent suite aux déplacements en goto() même si le stylo est en position penup().

Télécharger le code

Art

De l’art sur la NumWorks

Les calculatrices graphiques programmables sont bien plus que de simples outils pour les calculs. Elles ouvrent un monde de possibilités aux élèves, en permettant d’explorer les mathématiques d’une manière interactive et visuelle. Mais leur potentiel va au-delà des formules et des graphiques : elles peuvent être utilisées pour créer de l’art numérique, des animations, et même des jeux !

En programmant ces machines, les étudiants développent leur créativité tout en renforçant leurs compétences en logique et en résolution de problèmes. Ces calculatrices deviennent ainsi des alliées indispensables pour apprendre de manière ludique et innovante, et pour repousser les limites de l’imagination.

Depuis 2019, j’invite mes élèves à explorer leur potentiel créatif en réalisant des dessins uniques, soit avec l’application Grapheur, soit avec l’application Python de la calculatrice. Chaque année, leurs œuvres sont soumises à l’éditeur de la calculatrice, NumWorks, qui sélectionne la plus belle réalisation. L’auteur de cette création se voit offrir une coque au design original pour sa calculatrice. Si vous croisez un élève au lycée Louis Pasteur arborant une telle coque, il est fort probable qu’il soit lauréat de l’un de ces concours.

Certaines de ces réalisations sont également partagées sur les réseaux sociaux, notamment sur Instagram et X (anciennement Twitter). Une quinzaine de ces images ont même été sélectionnées et publiées, avec l’accord de leurs auteurs, dans un livre intitulé Découvrir la calculatrice graphique NumWorks édité en Français et en Anglais.

Il est vrai que se lancer dans la programmation sur une calculatrice graphique peut sembler déroutant au premier abord. Le défi de traduire des idées créatives en code peut déstabiliser, surtout pour ceux qui n’ont jamais exploré cet univers. Cependant, une fois les premières hésitations surmontées, les élèves découvrent rapidement le plaisir de voir leurs concepts prendre vie à l’écran. Ce processus, bien qu’exigeant, devient une source de satisfaction personnelle, les encourageant à se dépasser. En fin de compte, ils s’amusent tout en repoussant leurs propres limites, transformant un défi intimidant en une expérience enrichissante.

Tout à commencé en 2019 …

En 2019, après réflexion et concertation de l’équipe des enseignants de mathématiques, nous avons pris la décision collective de passer aux calculatrices NumWorks dans toutes nos classes. Pour familiariser les élèves avec cet outil, leur premier travail a été de réaliser un projet simple, destiné à leur faire découvrir les différentes fonctionnalités de la calculatrice. Sans attente particulière, j’ai été agréablement surpris par l’enthousiasme et la créativité dont ils ont fait preuve. Certaines productions, bien au-delà de ce que j’avais imaginé, ont révélé un potentiel insoupçonné chez les élèves, ouvrant la voie à des projets encore plus ambitieux par la suite.

Le titre du sujet était : Les mathématiques sont belles. Voici les 30 réalisations des élèves.

Certaines de ces images sont issues de scripts bien connus, adaptés et optimisés pour la NumWorks. Elles ont été réalisées à distance, en ligne, pendant le confinement de mars 2020. J’ai alors demandé à mes élèves de Terminale de constituer un jury pour évaluer ces créations, en insistant sur l’importance d’une bienveillance maximale dans leurs délibérations. Bien que les codes Python aient parfois été maladroits, ces erreurs faisaient partie intégrante du processus d’apprentissage. Se tromper, tâtonner, recommencer et s’améliorer sont autant d’étapes essentielles dans le développement de leurs compétences.

Quelques thread colorés publiés sur X (Twitter)

J’ai rejoint X (anciennement Twitter) durant le confinement, à l’origine pour passer le temps. Depuis, j’ai régulièrement partagé des threads mettant en avant les créations de mes élèves. En voici quelques exemples. (Un tableau à la fin de cet article propose davantage de liens.)

https://twitter.com/nsi_xyz/status/1380526907477884930
https://twitter.com/nsi_xyz/status/1578772799136890880
https://twitter.com/nsi_xyz/status/1593645740219416577
https://twitter.com/nsi_xyz/status/1770189028366709193
https://twitter.com/nsi_xyz/status/1714607831733707112
https://twitter.com/nsi_xyz/status/1802272199455187027

De l’art sur la NumWorks depuis 2020

Le tableau ci-dessous récapitule tous les fils de discussion publiés sur X (anciennement Twitter). Malheureusement, depuis le rachat de Twitter par Elon Musk, il n’est plus possible de consulter ces fils sans posséder un compte sur la plateforme. Cette restriction illustre bien les défis liés à l’utilisation d’outils fermés, qui peuvent rapidement se transformer en véritables obstacles à l’accès à l’information. 🥴

A compter novembre 2024, compte tenu de la fermeture progressive de X, de l’abus des publicités et de la toxicité du réseau, les fils seront publiés sur Blue Sky.
Les fils sur X existent toujours, en cliquant sur le T vous pouvez le lire sans compte sur X (Twitter)

Création
(1er éd.)
Titre du travail à rendre
Classe cible
Thread X (Twitter)
Blue Sky
2019
(2020)
Les mathématiques sont belles.
Seconde, mathématiques
2020 2021 2022 2023 2024 2025
2021
(2021)
Le Python et la Tortue.
1ère, spécialité NSI
2021 2022 2023 20242025
2021
(2021)
Le python et les fractales
Tale, spécialité NSI
2021 2022 2023 20242025
2022
(2022)
Les mathématiques sont belles ! ed. spéciale cercle
Tale, option Mathématiques Expertes
2022 2023 2024
2022
(2023)
Les mathématiques sont belles ! ed. spéciale polynôme
Tale, option Mathématiques Expertes
2023, 2024 20252025
2022
(2024)
Un feu d’artifice en python
Tale, option Mathématiques Expertes
2024 20252025
2023
(2023)
Le Python et la Tortue.
Seconde, SNT
2023 20242025
2024
(2024)
Pixel Art en python
Tale, option Mathématiques Expertes
20242025

Projets

NsCinéma – La base de données

Trouver le film idéal à regarder ? C’est ce que vous proposera cette base de donnée de films utilisant tkinter.
Cette collection comportant des films de tout genre regroupe de nombreux films en les notant, très pratique pour dénicher les meilleurs films.

Le thème

Nous avons choisi le cinéma comme thème pour notre base de données car nous avons tous les deux cette passion en commun. En effet, en plus du fait que nous aimons le cinéma, l’idée de choisir des films pour constituer notre base de données semble correspondre aux consignes et nous a assez vite donné des idées de fonctionnalités, notamment avec les filtres de recherche que nous avons mis en place.

Le code

Tout d’abord nous avons importé toutes les bibliothèques nécessaires au bon fonctionnement de la base de donnée.

from tkinter import *
import tkinter as tk
import sqlite3
from tkinter import messagebox

Ici Tkinter va permettre d’avoir un rendu graphique de la base de données, le tutoriel présent sur le site nsi.xyz nous a fortement aidé (tutoriel).

Enfin Sqlite3 permet de nous connecter a la base de donné (DBbrowser)

Page d’accueil

Pour la page d’accueil, nous avons donc décidé de rester simple. En effet, sur cette page, on y trouve le nom de notre projet, « NsCinéma ». Ensuite, il y a deux boutons : tout d’abord, un bouton qui permet de consulter la base de données sans la modifier, avec un système de filtres et de défilement. Ensuite, un deuxième bouton permet de modifier la base de données, ce qui inclut la création d’une table Film, la suppression de la table, l’ajout de films, ainsi que la possibilité de modifier un film en cas d’erreur lors de l’ajout.

Ainsi, afin que les boutons puissent fonctionner correctement avec l’ouverture d’une nouvelle page, il était nécessaire d’implémenter une fonction permettant de créer la page « Modifier la base de donnée »

Fonction create permettant d’acceder a la page modifier la base de donnée :

def create():
    glodef create():
    global nouvelle_fenetre
    nouvelle_fenetre = tk.Toplevel(screen)
    nouvelle_fenetre.geometry("1080x720")
    nouvelle_fenetre.config(bg="#0e101c")
    
    connection = sqlite3.connect('base_films.db')
    curseur = connection.cursor()
    
    def retour():
        nouvelle_fenetre.destroy()

    #titre
    titre = Label(nouvelle_fenetre, text="Mise a jour de la base de donnée", font=("Consolas",40) ,bg="#0e101c", fg="#b3b3b3")
    titre.pack(pady=50)
    
    #creer la table 
    bouton_creer_table = Button(nouvelle_fenetre, text="Créer Table Film", font=("Consolas",13), bg="#1d1e2e", fg="white", command=create_table, width=60, height=3)
    bouton_creer_table.place(x=250, y=300)

    #Bouton supprimer 
    bouton_supprimer_table = Button(nouvelle_fenetre, text="Supprimer Table Film", font=("Consolas",13), bg="#1d1e2e", fg="white", command=supprimer_table, width=60, height=3)
    bouton_supprimer_table.place(x=250, y=400)

    #bouton ajouter
    bouton_ajouter_film = Button(nouvelle_fenetre, text="Ajouter un film", font=("Consolas",13), bg="#1d1e2e", fg="white", command=page_ajouter_film, width=60, height=3)
    bouton_ajouter_film.place(x=250, y=500)
    
    #bouton modifier
    bouton_ajouter_film = Button(nouvelle_fenetre, text="Modifier la table", font=("Consolas",13), bg="#1d1e2e", fg="white", command=update, width=60, height=3)
    bouton_ajouter_film.place(x=250, y=600)
    
    bouton_retour = Button(nouvelle_fenetre, text="Retour", font=("Consolas",13), bg="#1d1e2e", fg="white", command=retour, width=20, height=2)
    bouton_retour.place(x=850, y=650)
    
    
    
    connection.commit()
    curseur.close()
    connection.close()

Dans cette fonction, nous pouvons observer la présence de la fonction ‘retour’ permettant de revenir en arrière en supprimant la page actuelle.

Bouton :

bouton1 = Button(screen, text="Consulter la base de donnée ", font=("Consolas",13), bg="#272936", fg="white", command= create, width=60, height=3)
bouton1.place(x=250, y=300)

bouton2 = Button(screen, text="Mettre a jour la base de donnée ", font=("Consolas",13), bg="#272936", fg="white", command= create, width=60, height=3)
bouton2.place(x=250, y =400)

Consultation de la base de donnée

Passons maintenons au code permettant de consulter cette base donnée. On créée donc une nouvelle fenêtre constituée de deux parties : une pour appliquer des filtres sur les films et une pour voir les films.

Comme on peut le constater dans le screen ci-dessous.

Vous constatez donc une boîte où l’on peut consulter les films de la base, et au-dessus de celle-ci, un bloc comportant un menu déroulant pour les filtres.

On pourra noter l’utilisation d’un bouton retour pour revenir à la page d’accueil et d’un bouton actualise qui nous servira plus tard pour ajouter/supprimer des films.

Passons plus en détail le code.

nouvelle_fenetre = tk.Tk()
    nouvelle_fenetre.title("NsCinema")
    nouvelle_fenetre.geometry("1080x720")
    nouvelle_fenetre.config(bg="#0e101c")

main_frame = Frame(nouvelle_fenetre, bg="#0e101c")
main_frame.place(x=80, y=300, width=920, height=420)

canvas = Canvas(main_frame, bg="#0e101c")
canvas.pack(side=LEFT, fill=BOTH, expand=YES)

scrollbar = Scrollbar(main_frame, command=canvas.yview)
scrollbar.pack(side=RIGHT, fill=Y)

canvas.configure(yscrollcommand=scrollbar.set)

boitefilm = Frame(canvas, bg="#1d1e2e")
canvas.create_window((0, 0), window=boitefilm, anchor='nw')
    
bouton_retour = Button(nouvelle_fenetre, text="Retour", font=("Consolas",13), bg="#1d1e2e", fg="white", command=retour, width=20, height=2)
bouton_retour.place(x=80, y=120)
    
bouton_actualise = Button(nouvelle_fenetre, text="Actualise", font=("Consolas",13), bg="#1d1e2e", fg="white", command=actualise, width=20, height=2)
bouton_actualise.place(x=800, y=120)
    

boitefilm correspond à la fenêtre (c’est une frame) où l’on, comme on l’à déjà dit, consulte les films. Elle est dans un canevas (ici canvas) qui lui va regrouper tous les éléments graphiques dont boitefilm et notamment la scrollbar qui va permettre de défiler l’axe des ordonnées de canvas : command=canvas.yview.

def etoile(n):
        c = n
        note = ""
        while c != 0:
            note += "★"
            c -= 1
            
        while (5-n) != 0:
            note += "☆"
            n += 1
        return note

    def titre():
        t_nom = Label(boitefilm, text="Nom", font=("Consolas", 14), bg="#1d1e2e", fg="black")
        t_genre = Label(boitefilm, text="Genre", font=("Consolas", 14), bg="#1d1e2e", fg="black")
        t_realisateur = Label(boitefilm, text="Réalisateur", font=("Consolas", 14), bg="#1d1e2e", fg="black")
        t_annee_pub = Label(boitefilm, text="Année", font=("Consolas", 14), bg="#1d1e2e", fg="black")
        t_note = Label(boitefilm, text="Note", font=("Consolas", 13), bg="#1d1e2e", fg="black")

        t_nom.grid(row=0, column=0, sticky=W, pady=21, padx=20)
        t_genre.grid(row=0, column=1, sticky=W, pady=21, padx=5)
        t_realisateur.grid(row=0, column=2, sticky=W, pady=21, padx=5)
        t_annee_pub.grid(row=0, column=3, sticky=W, pady=21, padx=10)
        t_note.grid(row=0, column=4, sticky=W, pady=21, padx=12)
    
            
    def note(n):
        connection = sqlite3.connect('base_films.db')
        curseur = connection.cursor()

        curseur.execute("SELECT * FROM Film WHERE note=?", (n,))
        films = curseur.fetchall()

        connection.commit()
        curseur.close()
        connection.close()

        return films

Ici nous avons trois fonctions qui ont des tâches spécifiques :

  • etoile(n) : c’est une fonction qui va retourner, en fonction du paramètre n, une chaîne de caractère avec n étoiles « ★ » et (5-n) étoiles « ☆ ». Ce qui nous donnes au final un note sur 5 représentée avec des étoiles.
  • titre() : cette fonction va tout simplement permettre l’affichage des titres (avec l’objet Label de tkinter qui permet d’afficher du texte) à la première ligne de la grille avec le paramètre row=0.
  • note() : C’est une fonction qui se connecte à une notre de données contenant les informations sur les films. Elle récupère tous les films ayant une note égale à n et les renvoie. Ici, films = curseur.fetchall() permet de récupérer dans la variable films la requête SQL.

Passons maitenant à la fonction la plus importante de cette partie : ajoute_film()

    
    def ajoute_film(nom, genre, realisateur, annee_pub, note):
        global row_compteur
        connection = sqlite3.connect('base_films.db')
        curseur = connection.cursor()

        truc = (nom, genre, realisateur, annee_pub, note)
        curseur.execute('''CREATE TABLE IF NOT EXISTS Film (
                        id_film INTEGER PRIMARY KEY AUTOINCREMENT,
                        nom TEXT,
                        genre TEXT,
                        realisateur TEXT,
                        annee_pub INTEGER,
                        note REAL
                    )''')
        

        curseur.execute("INSERT INTO Film (nom, genre, realisateur, annee_pub, note) VALUES (?,?,?,?,?)", truc)
        
        

        connection.commit()
        curseur.close()
        connection.close()

        b_nom = Label(boitefilm, text="%s" %(nom), font=("Consolas", 14), bg="#1d1e2e", fg="white")
        b_genre = Label(boitefilm, text="%s" %(genre), font=("Consolas", 14), bg="#1d1e2e", fg="white")
        b_realisateur = Label(boitefilm, text="%s" %(realisateur), font=("Consolas", 14), bg="#1d1e2e", fg="white")
        b_annee_pub = Label(boitefilm, text="%s" %(annee_pub), font=("Consolas", 14), bg="#1d1e2e", fg="white")
        b_note = Label(boitefilm, text="%s" %(etoile(note)), font=("Consolas", 13), bg="#1d1e2e", fg="white")

        b_nom.grid(row=row_compteur, column=0, sticky=W, pady=21, padx=30)
        b_genre.grid(row=row_compteur, column=1, sticky=W, pady=21, padx=15)
        b_realisateur.grid(row=row_compteur, column=2, sticky=W, pady=21, padx=10)
        b_annee_pub.grid(row=row_compteur, column=3, sticky=W, pady=21, padx=10)
        b_note.grid(row=row_compteur, column=4, sticky=W, pady=21, padx=20)
		
        row_compteur += 1


Cette fonction, ajoute_film, ajoute les informations d’un film (nom, genre, réalisateur, année de publication, note) à une base de données SQLite. Elle crée également des objets Label pour afficher ces informations dans une grille, en utilisant la variable row_compteur (c’est une globale) pour déterminer la ligne à laquelle il faut placer ces dernières.

En somme, tel un plat de lasagnes, on ajoute les films ligne après ligne.

Nous utilisons donc cette fonction pour construire notre base de données à l’aide du bout de code suivant et d’une bonne veille boucle for i in range() :

data = [
        ("American Sniper", "Biopic", "Clint Eastwood", 2015, 4),
        ("The Fabel Man", "Biopic", "Steven Spielberg", 2022, 3),
        ("Truman Show", "Science-Fiction", "Peter Weir", 1998, 4),
        ("Prisoners", "Drame", "Denis Villeneuve", 2013, 4),
        ("Very Bad Trip 1", "Comédie", "Todd Phillips", 2009, 3),
        ("Very Bad Trip 2", "Comédie", "Todd Phillips", 2011, 3),
        ("Very Bad Trip 3", "Comédie", "Todd Phillips", 2013, 2),
        ("Ocean Eleven", "Action", "Steven Soderbergh", 2001, 3),
        ("Ocean Twelve", "Action", "Steven Soderbergh", 2004, 2),
        ("Ocean Thirteen", "Action", "Steven Soderbergh", 2007, 3),
        ("La La Land", "Drame", "Damien Chazelle", 2016, 4),
        ("Fury", "Action", "David Ayer", 2014, 3),
        ("Voyage de Chihiro", "Animation", "Hayao Miyazaki", 2001, 5),
        ("Le Château Ambulant", "Animation", "Hayao Miyazaki", 2004, 4),
        ("Goodfellas", "Drame", "Martin Scorsese", 1990, 3),
        ("A Man Called Otto", "Drame", "Marc Forster", 2022, 3),
        ("Elvis", "Biopic", "John Carpenter", 1979, 3),
        ("127 Hours", "Biopic", "Danny Boyle", 2010, 3),
        ("Ad Astra", "Science-Fiction", "James Gray", 2019, 3),
        ("Life: Origine Inconnue", "Science-Fiction", "Daniel Espinosa", 2017, 3),
        ("The Hobbit 1", "Action", "Peter Jackson", 2012, 4),
        ("The Hobbit 2", "Action", "Peter Jackson", 2013, 4),
        ("The Hobbit 3", "Action", "Peter Jackson", 2014, 4),
        ("The Lord of the Rings 1", "Action", "Peter Jackson", 2001, 4),
        ("The Lord of the Rings 2", "Action", "Peter Jackson", 2002, 4),
        ("The Lord of the Rings 3", "Action", "Peter Jackson", 2003, 5),
        ("The Imitation Game", "Biopic", "Morten Tyldum", 2014, 3),
        ("Seul au monde", "Drame", "Robert Zemeckis", 2000, 4),
        ("Nightcrawler", "Thriller", "Dan Gilroy", 2014, 4),
        ("Marriage Story", "Drame", "Noah Baumbach", 2019, 3),
        ("First Man", "Biopic", "Damien Chazelle", 2018, 3),
        ("L'Intervention", "Action", "Fred Grivois", 2019, 4),
        ("Stand by Me", "Drame", "Rob Reiner", 1986, 4),
        ("Middle Men", "Drame", "George Gallo", 2009, 3),
        ("Happiness Therapy", "Comédie", "David O. Russell", 2012, 4),
        ("The Interview", "Comédie", "Seth Rogen", 2014, 1),
        ("Joy", "Drame", "David O. Russell", 2015, 3),
        ("The Silence of the Lambs", "Thriller", "Jonathan Demme", 1991, 5),
        ("Dune : Partie 2", "Science-Fiction", "Denis Villeneuve", 2024, 4),
        ("Man on Fire", "Action", "Tony Scott", 2004, 3),
        ("Dead Poets Society", "Drame", "Peter Weir", 1989, 5),
        ("Batman & Robin", "Action", "Joel Schumacher", 1997, 2),
        ("Catwoman", "Action", "Pitof", 2004, 1),
        ("Battlefield Earth", "Science-Fiction", "Roger Christian", 2000, 1),
        ("Gigli", "Comédie", "Martin Brest", 2003, 1),
        ("The Love Guru", "Comédie", "Marco Schnabel", 2008, 2),
        ("Movie 43", "Comédie", "Various", 2013, 1),
        ("Birdemic: Shock and Terror", "Thriller", "James Nguyen", 2010, 1),
        ("The Happening", "Thriller", "Night Shyamalan", 2008, 2),
        ("The Room", "Drame", "Tommy Wiseau", 2003, 1),
        ("Troll 2", "Comédie", "Claudio Fragasso", 1990, 2),
        ("Manos: The Hands of Fate", "Thriller", "Harold P. Warren", 1966, 1)]
        
        for m in range(len(data)):
                ajoute_film(data[m][0], data[m][1], data[m][2], data[m][3], data[m][4])

Passons à l’explication du menu déroulant comportant les filtres.

On constate qu’il y a deux filtres : un par genre et un par note.

Cela est possible grâce au bout de code suivant :

boitefiltre = Frame(nouvelle_fenetre, bg="#1d1e2e")
boitefiltre.place(x=80, y=200, width=920, height=63)

m_genre= Menubutton(boitefiltre, text="Genre", relief="raised", borderwidth=2, width=75)
m_genre.grid(row=1, column=0, sticky="s")

m_note = Menubutton(boitefiltre, text="Note", relief="raised", borderwidth=2, width=75)
m_note.grid(row=1, column=1, sticky="s")

boitefiltre.grid_rowconfigure(0, weight=1)

m_filtre = Label(boitefiltre, text="Appliquer des filtres de recherche", font=("Consolas",20), bg="#1d1e2e", fg="white")
m_filtre.grid(row=0, column=0, sticky="s", columnspan=5)

menuDeroulant1 = Menu(m_genre)
menuDeroulant1.add_command(label='Action', command=lambda: filtrer_par_genre('Action'))
menuDeroulant1.add_command(label="Science-Fiction", command=lambda: filtrer_par_genre('Science fiction'))
menuDeroulant1.add_command(label="Biopic", command=lambda: filtrer_par_genre('Biopic'))
menuDeroulant1.add_command(label="Drame", command=lambda: filtrer_par_genre('Drame'))
menuDeroulant1.add_command(label="Thriller", command=lambda: filtrer_par_genre('Thriller'))
menuDeroulant1.add_command(label="Comédie", command=lambda: filtrer_par_genre('Comédie'))
m_genre.configure(menu=menuDeroulant1)

menuDeroulant2 = Menu(m_note)
menuDeroulant2.add_command(label='★☆☆☆☆', command=lambda: filtrer_par_note(1))
menuDeroulant2.add_command(label="★★☆☆☆", command=lambda: filtrer_par_note(2))
menuDeroulant2.add_command(label="★★★☆☆", command=lambda: filtrer_par_note(3))
menuDeroulant2.add_command(label="★★★★☆", command=lambda: filtrer_par_note(4))
menuDeroulant2.add_command(label="★★★★★", command=lambda: filtrer_par_note(5))

Ce bout de code créée une boîte de filtres comprenant deux menus déroulants (m_genre et m_note). Ces menus permettent de sélectionner des options de filtrage par genre de film et par note. La boîte de filtres contient également un label (m_filtre) indiquant à quoi elle sert, pour faciliter la compréhension de l’utilisateur.

Les menus déroulants sont associés à des commandes qui déclenchent des fonctions (filtrer_par_genre et filtrer_par_note) et en fonction des sélections de l’utilisateur, cela applique les filtres de recherche pour afficher les films voulues.

Modification de la base de donnée

Sur cette page nous pouvons retrouver les 5 boutons principaux :

  • Créer la table film
  • Supprimer la table film
  • Ajouter un film a la table
  • modifier la table film
  • supprimer un film

Fonction Création Table

def create_table():
    connection = sqlite3.connect('base_films.db')
    curseur = connection.cursor()
    
    curseur.execute('''CREATE TABLE IF NOT EXISTS Film (
                        id_film INTEGER PRIMARY KEY AUTOINCREMENT,
                        nom TEXT,
                        genre TEXT,
                        realisateur TEXT,
                        annee_pub INTEGER,
                        note REAL
                    )''')
     messagebox.showinfo("Creation Table", "La table a été crée.")
    
    connection.commit()
    curseur.close()
    connection.close()

Cette fonction create_table permet de créer une table dans une base de données SQLite nommée 'base_films.db‘. Elle établit une connexion avec la base de donnée 'base_films.db‘ , permettant d’accéder à la base de donnée et d’y effectuer des operations, ajouter, supprimer, mettre à jour… Ainsi la requête SQL permet de creer la table Film avec les attributs suivants : id_film, nom, genre, realisateur, annee_pub, note)

De plus un message est affiché lorsque la table est crée.

Fonction Supprimer Table

def supprimer_table():
    connection = sqlite3.connect('base_films.db')
    curseur = connection.cursor()
    
    curseur.execute("DROP TABLE IF EXISTS Film")
    messagebox.showinfo("Suppression effectuee", "La table a été supprimée.")
   
    connection.commit()
    curseur.close()
    connection.close()

Cette fonction supprimer_table permet de supprimer la table crée précédemment.

Fonction ajouter un film

Lorsque l’on souhaite ajouter un film, une nouvelle page s’ouvre.

La fonction permettant d’ouvrir cette page :

def page_ajouter_film():
    global variable, variable1, variable2, variable3, variable4  
    nouvelle_fenetre = tk.Toplevel(screen)
    nouvelle_fenetre.geometry("1080x720")
    nouvelle_fenetre.config(bg="#0e101c")
    def retour():
        nouvelle_fenetre.destroy()

    titre = Label(nouvelle_fenetre, text="Ajouter un film", font=("Consolas",40) ,bg="#0e101c", fg="#b3b3b3")
    titre.pack(pady=50)


    variable = tk.StringVar()
    variable.set("Nom du film")
    entree = Entry(nouvelle_fenetre, textvariable=variable, font=("Helvetica",15), bg="#1d1e2e", fg="#b3b3b3", width=30)
    entree.pack(padx=10, pady=10)


    variable1 = tk.StringVar()
    variable1.set("Genre du Film")
    entree1 = Entry(nouvelle_fenetre, textvariable=variable1, font=("Helvetica",15), bg="#1d1e2e", fg="#b3b3b3", width=30)
    entree1.pack(padx=10, pady=10)

    
    variable2 = tk.StringVar()
    variable2.set("Realisateur")
    entree2 = Entry(nouvelle_fenetre, textvariable=variable2, font=("Helvetica",15), bg="#1d1e2e", fg="#b3b3b3", width=30)
    entree2.pack(padx=10, pady=10)

 
    variable3 = tk.StringVar()
    variable3.set("Annee de publication")
    entree3 = Entry(nouvelle_fenetre, textvariable=variable3, font=("Helvetica",15), bg="#1d1e2e", fg="#b3b3b3", width=30)
    entree3.pack(padx=10, pady=10)

     
    variable4 = tk.StringVar()
    variable4.set("Note")
    entree4 = Entry(nouvelle_fenetre, textvariable=variable4, font=("Helvetica",15), bg="#1d1e2e", fg="#b3b3b3", width=30)
    entree4.pack(padx=10, pady=10)

   
    bouton_ajouter_film = Button(nouvelle_fenetre, text="Ajouter le film", font=("Consolas",13), bg="#1d1e2e", fg="white", command=ajouter_film, width=20, height=2)
    bouton_ajouter_film.pack(pady=20)
    
    bouton_retour = Button(nouvelle_fenetre, text="Retour", font=("Consolas",13), bg="#1d1e2e", fg="white", command=retour, width=20, height=2)
    bouton_retour.place(x=850, y=650)

Ainsi cette fonction permet d’avoir un rendu graphique explicite pour l’utilisateur. En effet cette page est composé de 5 encadrés remplie d’un champ permettant de faire savoir a l’utilisateur ou rentrer le titre du film… Le bouton ajouter film permet d’ajouter le film a la base de donnée

Afin que cette page puisse fonctionner correctement il est imperatif d’avoir une fonction executant le code SQL qui va avec.

Code permettant l’ajout du film :

def ajouter_film():
    
    global variable, variable1, variable2, variable3, variable4, data  
    nom_film = variable.get()
    genre = variable1.get()
    realisateur = variable2.get()
    annee_pub = variable3.get()
    note = variable4.get()

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

    curseur.execute('''INSERT INTO Film (nom, genre, realisateur, annee_pub, note)
                        VALUES (?, ?, ?, ?, ?)''', (nom_film, genre, realisateur, annee_pub, note))

    connection.commit()
    curseur.close()
    connection.close() 

    messagebox.showinfo("Film ajoute", "Le film a ete ajoute avec succes.")

Cette fonction ajouter un film dans la base de données SQLite et informe l’utilisateur que le film a été ajouté . En effet la ligne 13 exécute une commande SQL qui insere le film dans la base de données. Les “?” sont des paramètres qui sont remplacés par les valeurs des variables spécifiées.

Fonction modifier la table

Lorsque l’on souhaite modifier la table, une nouvelle page s’ouvre.

Fonction permettant d’ouvrir la page de modification:

def update():
    global variable, variable1, variable2, variable3, variable4 
    nouvelle_fenetre = tk.Toplevel(screen)
    nouvelle_fenetre.geometry("1080x720")
    nouvelle_fenetre.config(bg="#0e101c")
    nouvelle_fenetre.title("Modifier un film")

    def retour():
        nouvelle_fenetre.destroy()

    titre = Label(nouvelle_fenetre, text="Modifier un film", font=("Consolas",40) ,bg="#0e101c", fg="#b3b3b3")
    titre.pack(pady=50)

    variable = tk.StringVar()
    variable.set("Nom du film")
    entree = Entry(nouvelle_fenetre, textvariable=variable, font=("Helvetica",15), bg="#1d1e2e", fg="#b3b3b3", width=30)
    entree.pack(padx=10, pady=10)

    variable1 = tk.StringVar()
    variable1.set("Genre du Film")
    entree1 = Entry(nouvelle_fenetre, textvariable=variable1, font=("Helvetica",15), bg="#1d1e2e", fg="#b3b3b3", width=30)
    entree1.pack(padx=10, pady=10)

    variable2 = tk.StringVar()
    variable2.set("Realisateur")
    entree2 = Entry(nouvelle_fenetre, textvariable=variable2, font=("Helvetica",15), bg="#1d1e2e", fg="#b3b3b3", width=30)
    entree2.pack(padx=10, pady=10)

    variable3 = tk.StringVar()
    variable3.set("Annee de publication")
    entree3 = Entry(nouvelle_fenetre, textvariable=variable3, font=("Helvetica",15), bg="#1d1e2e", fg="#b3b3b3", width=30)
    entree3.pack(padx=10, pady=10)

    variable4 = tk.StringVar()
    variable4.set("Note")
    entree4 = Entry(nouvelle_fenetre, textvariable=variable4, font=("Helvetica",15), bg="#1d1e2e", fg="#b3b3b3", width=30)
    entree4.pack(padx=10, pady=10)

    bouton_update_film = Button(nouvelle_fenetre, text="Mettre a jour le film", font=("Consolas",13), bg="#1d1e2e", fg="white", command=update_film, width=20, height=2)
    bouton_update_film.pack(pady=20)

    bouton_retour = Button(nouvelle_fenetre, text="Retour", font=("Consolas",13), bg="#1d1e2e", fg="white", command=retour, width=20, height=2)
    bouton_retour.place(x=850, y=650)

Ce code est semblable a celui de la creation de la page ajouter_film puisque on y retrouve les champs ou l’on doit inserer les informations necessaire.

Comme la page precedente afin que celle ci puisse fonctionner correctement il est imperatif d’avoir une fonction executant le code SQL qui va avec.

def update_film():
    nom_film = variable.get()
    genre = variable1.get()
    realisateur = variable2.get()
    annee_pub = variable3.get()
    note = variable4.get()

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

    curseur.execute('''UPDATE Film SET genre=?, realisateur=?, annee_pub=?, note=? WHERE nom=?''', (genre, realisateur, annee_pub, note, nom_film))

    connection.commit()
    curseur.close()
    connection.close()

    messagebox.showinfo("Film modifié", "Le film a été modifié.")

Cette fonction met à jour les informations d’un film spécifique dans une base de données SQLite en utilisant le nom du film comme clé, et informe l’utilisateur que le film a été modifié.

Fonction Supprimer un film

Fonction permettant d’afficher cette page :

def page_supprimer_film():
    global variable_title
    nouvelle_fenetre = tk.Toplevel(screen)
    nouvelle_fenetre.geometry("1080x720")
    nouvelle_fenetre.config(bg="#0e101c")

    def retour():
        nouvelle_fenetre.destroy()

    titre = Label(nouvelle_fenetre, text="Supprimer un film", font=("Consolas", 40), bg="#0e101c", fg="#b3b3b3")
    titre.pack(pady=50)

    variable_title = tk.StringVar()
    variable_title.set("Titre du film à supprimer")
    entree_title = Entry(nouvelle_fenetre, textvariable=variable_title, font=("Helvetica", 15), bg="#1d1e2e", fg="#b3b3b3", width=30)
    entree_title.pack(padx=10, pady=10)

    bouton_supprimer_film = Button(nouvelle_fenetre, text="Supprimer le film", font=("Consolas", 13), bg="#1d1e2e", fg="white", command=supprimer_titre, width=20, height=2)
    bouton_supprimer_film.pack(pady=20)

    bouton_retour = Button(nouvelle_fenetre, text="Retour", font=("Consolas",13), bg="#1d1e2e", fg="white", command=retour, width=20, height=2)
    bouton_retour.place(x=850, y=650)

Fonction permettant de faire marcher la page :

def supprimer_titre():
    title = variable_title.get()

    connection = sqlite3.connect('base_films.db')
    cursor = connection.cursor()

    cursor.execute("SELECT id_film FROM Film WHERE nom=?", (title,))
    result = cursor.fetchone()

    if result:
        film_id = result[0]

        cursor.execute("DELETE FROM Film WHERE id_film=?", (film_id,))
        messagebox.showinfo("Film supprimé", "Le film a été supprimé avec succès.")
    else:
        messagebox.showwarning("Film introuvable", "Aucun film trouvé avec ce titre.")

    connection.commit()
    cursor.close()
    connection.close()

Cette fonction supprime un film spécifique de la base de données SQLite en utilisant le titre du film comme clé, et informe l’utilisateur que le film a été supprimé ou qu’aucun film n’a été trouvé avec le titre donné.

Bouton Retour

En effet, a la fin du projet nous avons constater que il manquait un bouton afin de revenir en arrière. Sans bouton l’utilisateur est obligé de fermer la fenêtre par la croix.

Ainsi la fonction suivante permet de fermer la page actuelle afin de revenir sur la page précédente.

def retour():
        nouvelle_fenetre.destroy()

Télécharger le projet

Vous trouverez ici le .zip du projet que vous puissiez le télécharger. PS : si vous relancer le script, il vaut mieux recréer la base de donnée à l’aide de DB Browser.

En espérant que vous trouverez un bon film.

Projets

Un Explorateur de fractales en Python

Plongez dans des mondes visuels éblouissants créés à partir de simples équations mathématiques, les fractales. Découvrez des fractales emblématiques telles que Julia et Mandelbrot, personnalisez votre expérience avec des palettes de couleurs. Embarquez pour un voyage captivant au cœur de l’art mathématique !

Les prémices du projet

Tout débute durant mon année de seconde. En math, j’ai utilisé le script Mandelbrot initialement présent dans la calculatrice Numworks. Je me suis amusé à analyser le script et grâce à ça et à mon père, je suis tombé dans les fractales. 

Ensuite, j’ai commencé la NSI en première et j’ai réalisé mon premier tutoriel sur ledit script. Et en fin d’année, j’ai modifié ce script afin de faire de plus belles couleurs.

Cette année, j’ai eu envie d’aller bien plus loin, l’idée vient du fait que pour  réaliser de belles images de fractale, j’utilise des explorateurs de fractales trouvables facilement sur internet pour trouver des coordonnées intéressantes. Mais l’expression le dit bien, on n’est jamais mieux servi que par soi-même, et j’ai donc choisi pour mon dernier projet de lycée de faire mon propre explorateur de fractale.

Le début du projet

Je suis parti du script Julia que j’avais fait durant mon projet de fin d’année de première, vous pourrez retrouver l’explication du script ici (lien vers le tuto Julia palette). Ainsi, nous partons de ce script : 

from kandinsky import*
def julia(N_iteration):
    palette = []
    xmax = 2
    xmin = -2
    ymax = 1.3875
    ymin = -1.387
    r = 255
    g = 255
    b = 255
    for j in range(0,128):
        b = 255 -2 * j
        palette.append([r,g,b])
    for j in range(128,256):
        r = 255 -2 * (j-128)
        g = 255 -2 * (j-128)
        palette.append([r,g,b])
    for x in range(320):
        for y in range(222):
            i = 0
            z = complex(xmin+(xmax-xmin)*x/320+(ymax-(ymax-ymin)*y/222)*1J)
            c = complex(0.36,0.36)
            while i < N_iteration and abs(z) < 2:
                i = i + 1
                z = z*z+c
            couleur = palette[int(255*i/N_iteration)]
            col = color(couleur[0],couleur[1],couleur[2])
            set_pixel(x,y,col)

La logique

I – optimisation

Pour faire mon explorateur, je veux générer une nouvelle fractale à chaque zoom, dézoome, déplacement, sur la droite, la gauche, le haut, le bas en modifiant les valeurs des coins de l’image. Mais si je suis cette logique, le script Julia pose déjà un problème. En effet, le script met des dizaines de secondes à fabriquer une seule image, vous conviendrez qu’un zoom en deux minutes, c’est un peu lent non ? Étrangement, un tutoriel a été écrit durant les vacances de Noël 2023 et bizarrement ce script présente exactement ce dont j’ai besoin ici.

Ainsi, j’ai un script qui me permet de générer des images en .png de fractale rapidement. Maintenant, j’ai remplacé la bibliothèque Kandinsky par Pillow et j’ai entièrement modifié les calculs de la fractale pour utiliser NumPy. Nous avons donc ce script :

from PIL import Image
import time
import tkinter as tk
from tkinter import PhotoImage
import keyboard
import numpy as np

global xmax, xmin, ymax, ymin, réso_x, réso_y
#valeurs de base
xmax = 2
xmin = -2
ymax = 1.3875
ymin = -1.387
réso_x = 30720 
réso_y = 21312

def julia_PIL(N_iteration = 100):
    palette = []
    r = 255
    g = 255
    b = 255
    for j in range(0, 128):
        b = 255 - 2 * j
        palette.append((r, g, b))
    for j in range(128, 256):
        r = 255 - 2 * (j - 128)
        g = 255 - 2 * (j - 128)
        palette.append((r, g, b))

    palette = np.array(palette, dtype=np.uint8)

    # Créer une grille de coordonnées complexes
    x = np.linspace(xmin, xmax, réso_x)
    y = np.linspace(ymin, ymax, réso_y)
    X, Y = np.meshgrid(x, y)
    Z = X + 1J * Y

    # Initialiser une matrice d'indices pour la palette
    indices_matrix = np.zeros((réso_y, réso_x), dtype=int)

    for i in range(N_iteration):
        print(i)
        # Mettre à jour les pixels
        mask = np.logical_and(i < N_iteration, np.abs(Z) < 2)
        indices_matrix[mask] = np.round(255 * i / N_iteration).astype(int)

        # Mettre à jour Z pour les pixels actifs
        Z[mask] = Z[mask] ** 2 + complex(0.36, 0.36)

    # Créer une image à partir de la matrice d'indices
    img_array = palette[indices_matrix]

    # Créer une image PIL à partir de l'array
    img = Image.fromarray(img_array, 'RGB')

    # Sauvegarder l'image
    img.save('fractales_images/test.png')

II – Affichage de l’image 

Pour afficher l’image, j’utilise la fonction Pillow mixé à Tkinter. Pillow génère une image en .png et l’enregistre dans un dossier et à chaque nouvelle génération la nouvelle image remplace l’ancienne. 

Tkinter lui génère une fenêtre de la même résolution que l’image générée par Pillow. Ensuite, il va chercher dans le dossier cette image faite par Pillow et l’affiche dans la fenêtre précédemment créée.

def fractal_builder(N_iteration = 100,nom_img='Explorer_image/Image.png'):
  """
  script de génération de la fractale
  """
    # Créer une image à partir de la matrice des indices
    img_array = palette[indices_matrix]

    # Créer une image à partir de l'array
    img = Image.fromarray(img_array, 'RGB')

    # Sauvegarder l'image
    img.save(nom_img)    

def afficher_image():
    img = PhotoImage(file=chemin_image)
    # stocke l'image dans la variable img
    canvas.create_image(0, 0, anchor=tk.NW, image=img)
    #affiche l'image

III – déplacement et zoom 

Afin de me déplacer et de zoomer, je vais utiliser la bibliothèque keyboard. Elle me permet de détecter quand j’appuie sur les touches de mon clavier. Je crée donc une fonction “explorer” qui s’occupe des déplacements et de leur logique. Je vais ainsi modifier les valeurs des coordonnées des coins de l’image que je prends de la fractale puis la générer avec ses nouvelles valeurs dans la fenêtre de Tkinter.

def explorer(action):
    global xmax, xmin, ymax, ymin,img,nbr_img    
    x = xmax - xmin
    y = ymax - ymin
    
    if keyboard.is_pressed('up'):# zoom avant
        xmax = xmax - (x/20)
        xmin = xmin - (x/20)*-1
        ymax = ymax - (y/20)
        ymin = ymin - (y/20)*-1
        fractal_builder()
        afficher_image()      
    elif keyboard.is_pressed('down'):# zoom arrière
        xmax = xmax + (x/20)
        xmin = xmin + (x/20)*-1
        ymax = ymax + (y/20)
        ymin = ymin + (y/20)*-1
        fractal_builder()
        afficher_image()
    elif keyboard.is_pressed('z'): # déplacement haut
        ymax = ymax - (y/40)
        ymin = ymin - (y/40)
        fractal_builder()
        afficher_image()
    elif keyboard.is_pressed('s'): # déplacement bas
        ymax = ymax + (y/40)
        ymin = ymin + (y/40)
        fractal_builder()
        afficher_image()
    elif keyboard.is_pressed('q'): # déplacement gauche
        xmax = xmax - (x/40)
        xmin = xmin - (x/40)
        fractal_builder()
        afficher_image()
    elif keyboard.is_pressed('d'): # déplacement droite
        xmax = xmax + (x/40)
        xmin = xmin + (x/40)
        fractal_builder()
        afficher_image()

IV – Menu, accueil

Au début du projet, ma volonté était qu’à la fin, j’ai réussi à faire un explorateur de fractaleS. Pouvoir explorer des fractales comme Julia et Mandelbrot que je maîtrise déjà et peut-être même d’autres.

Mais pour faire ça il faut que l’utilisateur puisse choisir dans un menu quelle fractale veut-il explorer et faire un menu, quand j’ai commencé le projet été pour moi le plus dur à réaliser.

Durant les vacances de février 2024 il nous a été demandé de faire un gestionnaire de base de données, d’ailleurs voici ce que j’ai réalisé (lien vers le projet). Mais quel rapport avec des fractales ? Eh bien le fait que ce projet utilise Tkinter pour réaliser un menu, exactement ce que cherche à faire et surtout, c’est simple ! (même si honnêtement, on ne dirait pas).

Parallèlement à ce projet de base de données, j’ai donc piqué des bouts de mon code pour le mettre dans mon explorateur et en seulement quelques minutes, j’ai un menu ! Il ne sert à rien et il est moche (et il le restera, je suis pas designer).

Faire une fenêtre avec Tkinter est plutôt simple à comprendre, on crée des variables qui vont être chaque élément de la page, texte, menu déroulant, champs pour écrire, etc. Pour les construire, Tkinter nous offre des fonctions comme Label() pour les textes, Entry() pour les champs d’écritures entre autres. Il ne nous reste qu’à trouver sur internet les paramètres à mettre à l’intérieur (comme text = “le texte que l’on veut afficher” pour un texte).

titre_acceuil = tk.Label(cadre_acceuil, text="Bienvenue sur cet explorateur de fractales", font=("Consolas", 20), bg="#C2C2C2", fg="black")
soustitre_acceuil = tk.Label(cadre_acceuil, text="Choisissez les paramètres de votre fractale", font=("Consolas", 15), bg="#C2C2C2", fg="black")
titre_acceuil.pack(pady=10)
soustitre_acceuil.place(x=130,y=50)

#création de la liste déroulante de choix des palettes
desc_liste_deroul_palette = tk.Label(cadre_acceuil, text="Choisissez la palette de couleur que vous voulez utiliser", font=("Consolas", 12), bg="#C2C2C2", fg="black")
desc_liste_deroul_palette.place(x=15,y=100)
liste_choix = ["Blanc - Jaune - Noir","Blanc - Noir"] # élément de la liste déroulante
variable_palette = tk.StringVar() # élément initial
variable_palette.set("Blanc - Noir")
liste_deroulante_palette = ttk.Combobox(cadre_acceuil, textvariable=variable_palette, values=liste_choix)
liste_deroulante_palette.place(x=15,y=125)
select = liste_deroulante_palette.get() # sélection de l'élément choisi dans la liste

#création de la liste déroulante de choix de la fractale
desc_liste_deroul_fractale = tk.Label(cadre_acceuil, text="Choisissez la fractale que vous voulez voir", font=("Consolas", 12), bg="#C2C2C2", fg="black")
desc_liste_deroul_fractale.place(x=15,y=145)
liste_choix = ["Julia","Mandelbrot"] # élément de la liste déroulante
variable_fractal = tk.StringVar() # élément intiaux
variable_fractal.set("Julia")
liste_deroulante_fractal = ttk.Combobox(cadre_acceuil, textvariable=variable_fractal, values=liste_choix)
liste_deroulante_fractal.place(x=15,y=170)

# création du champ de texte pour choisir la valeur de c
desc1_chmp_val_julia = tk.Label(cadre_acceuil, text="Si vous avez choisi de générer une fractale de Julia :", font=("Consolas", 12), bg="#C2C2C2", fg="black")
desc2_chmp_val_julia = tk.Label(cadre_acceuil, text="Choisissez la valeur de la constante c (où laisser par défaut), sachant c est un complexe", font=("Consolas", 10), bg="#C2C2C2", fg="black")
desc1_chmp_val_julia.place(x=15,y=195)
desc2_chmp_val_julia.place(x=25,y=220)
variable_r = tk.StringVar()
variable_r.set("0.36")
chmp_str_c_r_julia = tk.Entry(cadre_acceuil, textvariable=variable_r, font=("Helvetica",12), bg="#ffffff", fg="black", width=5)
chmp_str_c_r_julia.place(x=40,y=245)
variable_i = tk.StringVar()
variable_i.set("0.36")
chmp_str_c_i_julia = tk.Entry(cadre_acceuil, textvariable=variable_i, font=("Helvetica",12), bg="#ffffff", fg="black", width=5)
chmp_str_c_i_julia.place(x=100,y=245)

#création de la liste déroulante de choix de la résolution
desc_liste_deroul_resolution = tk.Label(cadre_acceuil, text="Choisissez la résolution de l'explorateurr", font=("Consolas", 12), bg="#C2C2C2", fg="black")
desc_liste_deroul_resolution.place(x=15,y=270)
liste_choix = ["320x222","480x333","720x555"]
variable_resolution = tk.StringVar()
variable_resolution.set("320x222")
liste_deroulante_resolution = ttk.Combobox(cadre_acceuil, textvariable=variable_resolution, values=liste_choix)
liste_deroulante_resolution.place(x=15,y=295)

V – Menu, faire devenir utile l’accueil

Maintenant qu’on a un superbe menu, rendons le utile. Pour cela, je crée des fonctions pour sélectionner la palette, la résolution, la fractale etc que j’affecte à une grande fonction start que j’affecte elle au bouton qui lance l’explorateur. Les fonctions de sélection récupèrent ce qui est écrit dans les champs de textes, menu déroulant pour pouvoir donner c’est paramètre à la fonction mère, start().

start (), elle initialise toutes les variables utiles à l’explorateur, supprime la fenêtre du menu, crée la fenêtre de l’explorateur, initialise l’explorateur avec la première image et lance les dernières fonctions nécessaires comme explorer().

def selection_reso(): # fonction de sélection de la résolution utilisée
    global réso_x,réso_y
    if liste_deroulante_resolution.get() == "320x222":
        réso_x = 320
        réso_y = 222
    if liste_deroulante_resolution.get() == "480x333":
        réso_x = 480
        réso_y = 333
    if liste_deroulante_resolution.get() == "720x555":
        réso_x = 720
        réso_y = 555

def selection_palette(): # fonction de sélection de la palette utilisée
    global palette
    palette = []
    select = liste_deroulante_palette.get()
    if select == "Blanc - Jaune - Noir":
        r, g, b = 255, 255, 255
        for j in range(0, 128):
            b = 255 - 2 * j
            palette.append((r, g, b))
        for j in range(128, 256):
            r = 255 - 2 * (j - 128)
            g = 255 - 2 * (j - 128)
            palette.append((r, g, b))
    else :
        palette = [[i,i,i] for i in range(255)]

def start(): # fonction de lancement de l'exploration
    global img,chemin_image,canvas,choix_fract,compl_r,compl_i, réso_x, réso_y
    selection_palette()
    # sélèction de la fractale
    if liste_deroulante_fractal.get() == "Julia":
        choix_fract = 0
    if liste_deroulante_fractal.get() == "Mandelbrot":
        choix_fract = 1
    selection_reso()
    compl_r,compl_i = chmp_str_c_r_julia.get(),chmp_str_c_i_julia.get() # sélection des valeurs de c
    
    fenetre.destroy() #supprime la fenêtre de l'accueil
    cadre_acceuil_explo = tk.Tk() # crée la fenètre de l'explorateur
    cadre_acceuil_explo.title("Affichage d'une Image")
    
    chemin_image = "Explorer_image/Image.png"
    # Charge l'image
    img = PhotoImage(file=chemin_image)
    # Crée un widget Canvas pour afficher l'image
    canvas = tk.Canvas(cadre_acceuil_explo, width=réso_x, height=réso_y)
    canvas.pack()
    # initialisation de la 1ère fractale
    fractal_builder()
    afficher_image()
    cadre_acceuil_explo.mainloop()

# création et placement du bouton de lancement de l'explorateur
bouton_start_explo = tk.Button(cadre_acceuil, text="Commencer l'exploration", font=("Consolas",15), bg="white", fg="black", command = start)
bouton_start_explo.place(x=230,y=495)

VI – Menu, prévisualisation

Actuellement, j’ai donc un menu qui me permet d’explorer 2 fractales différentes avec x palette différente et une infinité de fractales de Julia. Mais pendant que je joue avec mon script, je me rends compte que c’est lourd de devoir mettre les paramètres, lancer l’exploration, se rendre compte que ce n’est pas ce qu’on veut, donc on relance le script et ainsi de suite… Une idée me vient alors en tête : “pouvoir prévisualiser la fractale qu’on génère dans le menu, ce serait bien non ?” et voilà un nouvel objectif et pas des moindres, il me demande de mettre plusieurs variables en global pour y accéder, d’utiliser les fonctions de sélection, etc.

Pour réaliser cette fonction, on fait la même chose que la fonction start(). Mais cette fois, on ne supprime ni ne crée aucune fenêtre, on vient juste placer un canvas, l’image de la fractale que nous voulons prévisualiser au bon endroit avec les bons paramètres.

def previsu():
    global palette, choix_fract,compl_r,compl_i, réso_x, réso_y
    #sélèction des diffèrents paramètres
    réso_x, réso_y = 240,167
    selection_palette()
    if liste_deroulante_fractal.get() == "Julia":
        choix_fract = 0
    if liste_deroulante_fractal.get() == "Mandelbrot":
        choix_fract = 1
    compl_r,compl_i = chmp_str_c_r_julia.get(),chmp_str_c_i_julia.get()
    
    fractal_builder() # génération d'une fractale avec ces paramètres
    prévi = PhotoImage(file="Explorer_image/Image.png") # récupération de l'image
    # création du cardre et placement de l'image dans le cadre
    label_image = tk.Label(cadre_acceuil, image=prévi)
    label_image.place(x=245,y=310)
    label_image.mainloop()

VII – Menu, tutoriel

La dernière barrière entre mon explorateur et l’utilisateur est ainsi ma logique. En effet, les touches que j’ai choisies pour mon explorateur ne sont sûrement pas les meilleures, donc je rajoute une nouvelle page à mon menu pour y écrire un petit guide des touches. Je profite de ça pour ajouter deux nouvelles fonctions. La première fonction récupère les coordonnées de la fractale qu’on voit pour les nerds. La seconde, plus complexe, prend des “screenshot” de ce qu’on voit dans l’explorateur. En réalité, elle génère une fractale avec les mêmes paramètres que celle que l’on voit dans l’explorateur, mais change son nom au moment de l’enregistrer.

#création du cadre de tutoriel dans la fenêtre de l'accueil
cadre_acceuil = tk.Frame(fenetre, bg="#C2C2C2",heigh = 555,width=720)
cadre_acceuil.pack_propagate(False) 
cadre_tuto = tk.Frame(fenetre, bg="#C2C2C2",heigh = 555,width=720)
cadre_tuto.pack_propagate(False) 
# création et placement du texte du tuto
titre_tuto = tk.Label(cadre_tuto, text="Guide des Touches", font=("Consolas", 20), bg="#C2C2C2", fg="black")
soustitre_tuto = tk.Label(cadre_tuto, text="Vous vous sentez un peu  perdu ?", font=("Consolas", 15), bg="#C2C2C2", fg="black")
titre_tuto.pack()
soustitre_tuto.pack(pady=10)
texte_tuto = tk.Label(cadre_tuto, text="z - aller vers le haut \ns - aller vers le bas \nq - aller à droite \nd - aller à gauche \n\nflèche du haut - zoom avant \nflèche du bas - zoom arrière \n\nm - renvoie les coordonnées de \n    où vous êtes dans la fractale \n\nc - sauvegarde un PNG de ce que \n    vous voyez dans le dossier Explorer_image \n", font=("Consolas", 13), bg="#C2C2C2", fg="black")
texte_tuto.pack()
fin = tk.Label(cadre_tuto, text="Amusez-vous !", font=("Consolas", 15), bg="#C2C2C2", fg="black")
fin.pack(pady=20)

#nouvelles fonctions ajoutées à l'explorateur
def explorer(action):
    global xmax, xmin, ymax, ymin,img,nbr_img
    x = xmax - xmin
    y = ymax - ymin
    """
    différentes fonctions de déplacements
    """
    elif keyboard.is_pressed('m'): # récupération de nos coordonnées (pour les nerds)
        print("xmax = ",xmax)
        print("xmin = ",xmin)
        print("ymax = ",ymax)
        print("ymin = ",ymin)
    elif keyboard.is_pressed('c'): # prend un screenshot de la fractale
        print("Image enregistrée !")
        nbr_img +=1
        fractal_builder(100,'Explorer_image/Image_saved'+str(nbr_img)+'.png')
        afficher_image()
        print(nbr_img)

Conclusion

En conclusion, ce projet ma permis d’utiliser la plupart de mes compétences. Il m’a suivi durant la moitié de mon année de terminale.

Galerie

Quelques exemples de fractale que vous pourrez à votre tour explorer :

Script complet

Voici le script final en .7z, il vous suffit de l’extraire et vous pourrez directement commencer à explorer !

vous pouvez également voir les futures mise à jour sur GitHub ici.

Projets

SPA – les bases de données

En 2022, on estime que 41 186 animaux ont été adoptés à la SPA et 44 199 animaux ont été recueillis par celle-ci. Afin de pouvoir répertorier les animaux présents à la SPA, une table de données est l’outil indispensable.

I – Présentation du projet

Pour commencer, la réalisation de la base de données se porte sur la simulation d’une SPA. Celle-ci se compose d’une page d’Acceuil comportant le menu en haut de la page.

Puis d’une page Ajouter où l’on rentre les informations sur un nouvel animal.

Par la suite, d’une page Modifier permettant de modifier les informations sur un animal déjà enregistré sur la base de données en rentrant son id et en choisissant la donnée à modifier. De plus, lorsque vous choisissez de supprimer un animal, il vous suffit de rentrer son id et de cliquer sur Supprimer l’animal ayant cet id.

Et enfin, la page Parcourir où l’on choisit par quel type de donnée nous souhaitons faire la recherche (id, espèce, race, nom, date et lieu) et où l’on écrit la valeur recherchée ou si c’est une date, si elle est égale, plus grande, plus petites, etc.

Pour chaque page, il est possible de supprimer toute la table, c’est-à-dire supprimer tout le tableau crée. Lorsque vous ajoutez et modifiez, le résultat de Parcourir se trouve sur la fenêtre blanche en dessous de Rechercher.

II – Mise en place du projet

Dans un premier temps, il a été primordial d’importer les librairies présentes pour la création de la base de données.

from tkinter import *
from tkinter import ttk
from tkinter.messagebox import showinfo
import sqlite3

La première ligne de code permet d’utiliser le nom des classes et fonctions définies sur tkinter. La seconde permet de créer des widgets. La troisième, showinfo permet de fournir des instructions à l’utilisateur. Enfin, d’importer le module SQLite3 permet d’interagir avec la base de données en .db .

Après avoir importé les modules dont on a besoin, il faut établir une connexion avec la base de données « base_SPA.db » et créer un curseur pour exécuter les commandes SQL .

connection = sqlite3.connect('base_SPA.db')
curseur = connection.cursor()
Ensuite, il faut créer les différentes fenêtres telles que accueil, ajouter, modifier, parcourir qui sont présents tout le long et dès le début à la page Accueil avec le bouton pour vider la table, c'est-à-dire effacer les données de la table.
from tkinter import *
from tkinter import ttk
from tkinter.messagebox import showinfo
import sqlite3

#génération du lien de la base de donnée
connection = sqlite3.connect('base_SPA.db')
curseur = connection.cursor()

#génération de la base de donnée si elle n'existe pas
curseur.execute("CREATE TABLE IF NOT EXISTS ANNIMAL(id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, nom TEXT, espèce TEXT, race TEXT, age INT, trouvé_où TEXT)")

connection.commit()
curseur.close()
connection.close()

# création de toutes les fenêtres
def afficher_nouvelle_fenetre(message):
    fenetre.withdraw()  # Masque la fenêtre actuelle
    nouvelle_fenetre = Toplevel(fenetre)
    nouvelle_fenetre.title("Nouvelle Fenêtre")
    nouvelle_fenetre.geometry("720x555") 
    Label(nouvelle_fenetre, text=message).pack(padx=20, pady=20)

def affiche_acceuil():
    cadre_pile.append(cadre_acceuil)  # création de la page d'accueil
    afficher_cadre(cadre_acceuil)

def affiche_ajoute():
    cadre_pile.append(cadre_ajoute)  # création de la page ajoute
    afficher_cadre(cadre_ajoute)

def affiche_modifier():
    cadre_pile.append(cadre_modifier)  # création de la page modifier
    afficher_cadre(cadre_modifier)
    
def affiche_parcourir():
    cadre_pile.append(cadre_parcourir)  # création de la page parcourir
    afficher_cadre(cadre_parcourir)


def afficher_cadre(cadre):
    for c in [cadre_acceuil, cadre_ajoute, cadre_modifier, cadre_parcourir]:
        c.pack_forget()
    cadre.pack()

#génération de la fenêtre
fenetre = Tk()

fenetre.title("SPA - les bases de données")
fenetre.geometry("720x555")
fenetre.config(bg="#C2C2C2")
fenetre.iconbitmap("logo.ico")

#fonction pour vider la table
def vider_table():
    connection = sqlite3.connect('base_SPA.db')
    curseur = connection.cursor()
    curseur.execute("DROP TABLE ANNIMAL")
    curseur.execute("CREATE TABLE ANNIMAL(id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, nom TEXT, espèce TEXT, race TEXT, age INT, trouvé_où TEXT)")
    connection.commit()
    curseur.close()
    connection.close()
    
bouton_vider_table = Button(fenetre, text="vider la table", font=("Consolas",15), bg="white", fg="black",command = vider_table)
bouton_vider_table.place(x=530,y=480)

# Cadres pour différentes pages
cadre_acceuil = Frame(fenetre, bg="#C2C2C2")
cadre_ajoute = Frame(fenetre, bg="#C2C2C2")
cadre_modifier = Frame(fenetre, bg="#C2C2C2")
cadre_parcourir = Frame(fenetre, bg="#C2C2C2")

cadre_acceuil.pack()

# Création du menu
menubar = Menu(fenetre)

# Ajout des commandes directement au menu principal
menubar.add_command(label="Acceuil", command=affiche_acceuil)
menubar.add_command(label="Ajouter", command=affiche_ajoute)
menubar.add_command(label="Modifier", command=affiche_modifier)
menubar.add_command(label="Parcourir", command=affiche_parcourir)

# Configuration de la fenêtre avec la barre de menus
fenetre.config(menu=menubar)

# Initialisation de la pile des cadres
cadre_pile = [cadre_acceuil]

fenetre.mainloop()

Les fonctions permettent de gérer la navigation entre les différentes pages en fonction des actions de l’utilisateur. De plus, elles permettent de masquer les cadres précédemment affichés et affichent le cadre correspondant à la page demandée.

Après avoir créé nos différentes pages, il faut les construire, rajouter du texte, du champ de texte, etc. Tout d’abord, commence la page d’accueil, très simplement, on crée des blocs :

# création des "blocs" 
titre_acceuil = Label(cadre_acceuil, text="Bienvenue sur la base de données de la SPA", font=("Consolas", 20), bg="#C2C2C2", fg="black")
soustitre_acceuil = Label(cadre_acceuil, text="DONNONS-LEUR AUTANT QU'ILS NOUS APPORTENT !", font=("Consolas", 13), bg="#C2C2C2", fg="black")

# affichage de ces blocs
titre_acceuil.pack(pady=20)
soustitre_acceuil.pack()

Après cela, il ne nous reste plus qu’à créer les autres pages.

# création des different blocs + affichage des blocs
titre_ajoute = Label(cadre_ajoute, text="AJOUTER", font=("Consolas", 20), bg="#C2C2C2", fg="black")
titre_ajoute.pack(pady=20)
soustitre_ajoute = Label(cadre_ajoute, text="Veuillez entrer les données de l'animal", font=("Consolas", 13), bg="#C2C2C2", fg="black")
soustitre_ajoute.pack()
desc_chmp_nom_str_ajoute = Label(cadre_ajoute, text="entrez son nom", font=("Consolas", 13), bg="#C2C2C2", fg="black")
desc_chmp_nom_str_ajoute.pack()
chmp_nom_str_ajoute = Entry(cadre_ajoute, font=("Helvetica",15), bg="#ffffff", fg="black", width=30)
chmp_nom_str_ajoute.pack()
desc_chmp_espece_str_ajoute = Label(cadre_ajoute, text="entrez son espèce", font=("Consolas", 13), bg="#C2C2C2", fg="black")
desc_chmp_espece_str_ajoute.pack()
chmp_espece_str_ajoute = Entry(cadre_ajoute, font=("Helvetica",15), bg="#ffffff", fg="black", width=30)
chmp_espece_str_ajoute.pack()
desc_chmp_race_str_ajoute = Label(cadre_ajoute, text="entrez sa race", font=("Consolas", 13), bg="#C2C2C2", fg="black")
desc_chmp_race_str_ajoute.pack()
chmp_race_str_ajoute = Entry(cadre_ajoute, font=("Helvetica",15), bg="#ffffff", fg="black", width=30)
chmp_race_str_ajoute.pack()
desc_chmp_age_str_ajoute = Label(cadre_ajoute, text="entrez son âge", font=("Consolas", 13), bg="#C2C2C2", fg="black")
desc_chmp_age_str_ajoute.pack()
chmp_age_str_ajoute = Entry(cadre_ajoute, font=("Helvetica",15), bg="#ffffff", fg="black", width=30)
chmp_age_str_ajoute.pack()
desc_chmp_localisation_str_ajoute = Label(cadre_ajoute, text="entrez le lieu où il a été trouvé", font=("Consolas", 13), bg="#C2C2C2", fg="black")
desc_chmp_localisation_str_ajoute.pack()
chmp_localisation_str_ajoute = Entry(cadre_ajoute, font=("Helvetica",15), bg="#ffffff", fg="black", width=30)
chmp_localisation_str_ajoute.pack()

#fonction pour ajouter les donnée choisies dans les champs de texte de la page ajouter dans la base de donnéee
def ajouter_donnee():
    connection = sqlite3.connect('base_SPA.db')
    curseur = connection.cursor()
    data = (chmp_nom_str_ajoute.get(),chmp_espece_str_ajoute.get(),chmp_race_str_ajoute.get(),chmp_age_str_ajoute.get(),chmp_localisation_str_ajoute.get())
    curseur.execute("INSERT INTO ANNIMAL (nom,espèce,race,age,trouvé_où) VALUES (?,?,?,?,?)",data)
    connection.commit()
    curseur.close()
    connection.close()

# création et placement du bouton utiliser pour valider l'ajout de donnée
bouton_ajouter = Button(cadre_ajoute, text="Ajouter l'annimal", font=("Consolas",15), bg="white", fg="black",command = ajouter_donnee)
bouton_ajouter.pack(pady=20)

Dans ce code, nous créons la fenêtre Ajouter avec les informations à remplir. Lorsque l’on clique sur le bouton Ajouter l’animal, celui ci sera ajouté automatiquement à la table de données.

# création des different blocs + affichage des blocs
titre_modifier = Label(cadre_modifier, text="MODIFIER", font=("Consolas", 20), bg="#C2C2C2", fg="black")
titre_modifier.pack(pady=20)
desc_chmp_modifier = Label(cadre_modifier, text="entrez l'id de l'annimal", font=("Consolas", 13), bg="#C2C2C2", fg="black")
desc_chmp_modifier.pack()
chmp_str_modifier = Entry(cadre_modifier, font=("Helvetica",15), bg="#ffffff", fg="black", width=5)
chmp_str_modifier.pack()

# création de la liste déroulante
desc_liste_deroul_modifier = Label(cadre_modifier, text="Choissisez quel donnée voulez vous modifier", font=("Consolas", 13), bg="#C2C2C2", fg="black")
desc_liste_deroul_modifier.pack(pady=10)
liste_choix = ["Nom","Race","Espèce","Age","Localisation"]
liste_deroulante = ttk.Combobox(cadre_modifier, values=liste_choix)
liste_deroulante.pack()

desc_chmp2_modifier = Label(cadre_modifier, text="entrez la modification", font=("Consolas", 13), bg="#C2C2C2", fg="black")
desc_chmp2_modifier.pack(pady=10)
chmp2_str_modifier = Entry(cadre_modifier, font=("Helvetica",15), bg="#ffffff", fg="black", width=30)
chmp2_str_modifier.pack()

# fonction pour modifier une donnée dans la base de donnée à partir de la liste déroulante et le champ de texte
def modifier_donnee():
    select = liste_deroulante.get()
    datas = (chmp2_str_modifier.get(),chmp_str_modifier.get())
    connection = sqlite3.connect('base_SPA.db')
    curseur = connection.cursor()
    if select == "Nom":
        curseur.execute("UPDATE ANNIMAL SET nom = ? WHERE id = ?",datas)
    if select == "Race":
        curseur.execute("UPDATE ANNIMAL SET race = ? WHERE id = ?",datas)
    if select == "Espèce":
        curseur.execute("UPDATE ANNIMAL SET espèce = ? WHERE id = ?",datas)
    if select == "Age":
        curseur.execute("UPDATE ANNIMAL SET age = ? WHERE id = ?",datas)
    if select == "Localisation":
        curseur.execute("UPDATE ANNIMAL SET trouvé_où = ? WHERE id = ?",datas)
    connection.commit()
    curseur.close()
    connection.close()

# fonction pour supprimer toutes les données lié à un identifiant dans la base de donnée   
def supprimer_donnee():
    sup_select = chmp_str_modifier.get()
    connection = sqlite3.connect('base_SPA.db')
    curseur = connection.cursor()
    curseur.execute("DELETE FROM ANNIMAL WHERE id = ?",sup_select)
    connection.commit()
    curseur.close()
    connection.close()

# création et placement des boutons pour valider les modifications / suppressions
bouton_sup = Button(cadre_modifier, text="Supprimer l'annimal ayant cet id", font=("Consolas",15), bg="white", fg="black", command = supprimer_donnee)
bouton_sup.pack(pady=30)
bouton_modifier = Button(cadre_modifier, text="Modifier", font=("Consolas",15), bg="white", fg="black", command = modifier_donnee)
bouton_modifier.pack()

La fenêtre modifier quant à elle a besoin de l‘id de l’animal. En effet, pour modifier une information sur un animal, la personne doit rentrer l’id présente sur la table de donnée correspondant à l’animal, après cela, elle choisit l‘information à modifier dans la liste déroulante puis rentre la correction à faire. Ensuite, cette fenêtre abrite aussi la possibilité de supprimer l’animal comme cela a été dit précédemment grâce à la fonction supprimer_donnee() par l’id de l’animal.

# création des different blocs + affichage des blocs
titre_parcourir = Label(cadre_parcourir, text="PARCOURIR", font=("Consolas", 20), bg="#C2C2C2", fg="black")
titre_parcourir.pack(pady=10)
desc_liste_deroulante_parcourir = Label(cadre_parcourir, text="Choississez par qu'elle type de donnee souhaitez vous rechercher", font=("Consolas", 13), bg="#C2C2C2", fg="black")
desc_liste_deroulante_parcourir.pack(pady=10)

# création de la liste déroulante du type de donnée
liste_deroulante_parcourir = ttk.Combobox(cadre_parcourir, values=["id","Nom","Espèce", "Age","Race","Localisation"])
liste_deroulante_parcourir.set("id")
liste_deroulante_parcourir.pack(pady=10)

# création du cadre blanc où apparaissent les recherches effectuées
frame_info_recherche = Frame(cadre_parcourir, bg="#C2C2C2",heigh = 50,width=650)
frame_info_recherche.pack_propagate(False) 
frame_info_recherche.pack()

desc_chmp_str_parcourir = Label(frame_info_recherche, text="écrivez cette information de recherche", font=("Consolas", 10), bg="#C2C2C2", fg="black")
desc_chmp_str_parcourir.place(x=0,y=0)

chmp_str_parcourir = Entry(frame_info_recherche, font=("Helvetica",10), bg="#ffffff", fg="black", width=15)
chmp_str_parcourir.place(x=0,y=23)

desc_chmp_str_parcourir = Label(frame_info_recherche, text="Si c'est une valeur elle doit être :", font=("Consolas", 10), bg="#C2C2C2", fg="black")
desc_chmp_str_parcourir.place(x=390,y=0)

# création de la liste déroulante pour préciser la recherche par valeurs numériques
liste_deroulante_info_recherche = ttk.Combobox(frame_info_recherche, values=["=","<=",">=","<",">"],width=15)
liste_deroulante_info_recherche.set("=")
liste_deroulante_info_recherche.place(x=390,y=23)

variable = StringVar()
variable.set("1")
chmp_str_info_recherche_parcourir = Entry(frame_info_recherche,textvariable=variable, font=("Helvetica",10), bg="#ffffff", fg="black", width=15)
chmp_str_info_recherche_parcourir.place(x=510,y=23)

# création de la variable qui stocke tout le résultat d'une recherche
global recherche
recherche = ""

# fonction pour construire notre variable
def parcourir_donnee():
    global recherche
    recherche = ""
    connection = sqlite3.connect('base_SPA.db')
    curseur = connection.cursor()
    data = [chmp_str_parcourir.get()]
    data2 = [chmp_str_info_recherche_parcourir.get()]
    if liste_deroulante_parcourir.get() == "Nom":
        recherche = curseur.execute("SELECT nom,espèce,race,age,trouvé_où FROM ANNIMAL WHERE nom = ?",data).fetchall()
    if liste_deroulante_parcourir.get() == "Race":
        recherche = curseur.execute("SELECT nom,espèce,race,age,trouvé_où FROM ANNIMAL WHERE race = ?",data).fetchall()
    if liste_deroulante_parcourir.get() == "Espèce":
        recherche = curseur.execute("SELECT nom,espèce,race,age,trouvé_où FROM ANNIMAL WHERE espèce = ?",data).fetchall()
    if liste_deroulante_parcourir.get() == "Localisation":
        recherche = curseur.execute("SELECT nom,espèce,race,age,trouvé_où FROM ANNIMAL WHERE trouvé_où = ?",data).fetchall()
    if liste_deroulante_parcourir.get() == "Age":
        if liste_deroulante_info_recherche.get() == "=":
            recherche = curseur.execute("SELECT nom,espèce,race,age,trouvé_où FROM ANNIMAL WHERE age = ?",data2).fetchall()
        elif liste_deroulante_info_recherche.get() == "<":
            recherche = curseur.execute("SELECT nom,espèce,race,age,trouvé_où FROM ANNIMAL WHERE age < ?",data2).fetchall()
        elif liste_deroulante_info_recherche.get() == ">":
            recherche = curseur.execute("SELECT nom,espèce,race,age,trouvé_où FROM ANNIMAL WHERE age > ?",data2).fetchall()
        elif liste_deroulante_info_recherche.get() == "<=":
            recherche = curseur.execute("SELECT nom,espèce,race,age,trouvé_où FROM ANNIMAL WHERE age <= ?",data2).fetchall()
        elif liste_deroulante_info_recherche.get() == ">=":
            recherche = curseur.execute("SELECT nom,espèce,race,age,trouvé_où FROM ANNIMAL WHERE age >= ?",data2).fetchall()
    if liste_deroulante_parcourir.get() == "id":
        if liste_deroulante_info_recherche.get() == "=":
            recherche = curseur.execute("SELECT nom,espèce,race,age,trouvé_où FROM ANNIMAL WHERE id = ?",data2).fetchall()
        elif liste_deroulante_info_recherche.get() == "<":
            recherche = curseur.execute("SELECT nom,espèce,race,age,trouvé_où FROM ANNIMAL WHERE id < ?",data2).fetchall()
        elif liste_deroulante_info_recherche.get() == ">":
            recherche = curseur.execute("SELECT nom,espèce,race,age,trouvé_où FROM ANNIMAL WHERE id > ?",data2).fetchall()
        elif liste_deroulante_info_recherche.get() == "<=":
            recherche = curseur.execute("SELECT nom,espèce,race,age,trouvé_où FROM ANNIMAL WHERE id <= ?",data2).fetchall()
        elif liste_deroulante_info_recherche.get() == ">=":
            recherche = curseur.execute("SELECT nom,espèce,race,age,trouvé_où FROM ANNIMAL WHERE id >= ?",data2).fetchall()
    connection.commit()
    curseur.close()
    connection.close()

# fonction pour afficher la recherche sous une forme lisible
def texte_parcourir():
    texte_widget_frame_parcourir.delete("1.0","end")
    parcourir_donnee()
    for car in str(recherche):
        if car == "[" or car == "]" or car == "(" or car == "'"  :
            texte_widget_frame_parcourir.insert(INSERT,"")
        elif car == ",":
            texte_widget_frame_parcourir.insert(INSERT,"")
        elif car == ")":
            texte_widget_frame_parcourir.insert(INSERT,"\n")
        else :
            texte_widget_frame_parcourir.insert(INSERT,car)
  
# création et placement du bouton pour valider une recherche
bouton_parcourir = Button(cadre_parcourir, text="Rechercher", font=("Consolas",15), bg="white", fg="black",command = texte_parcourir)
bouton_parcourir.pack(pady=10)


frame_parcourir = Frame(cadre_parcourir, bg="#ffffff",heigh = 210,width=650)
frame_parcourir.pack_propagate(False) 
frame_parcourir.pack()

# création et placement de la barre de scroll lié à la recherche effectuée
scrollbar = Scrollbar(frame_parcourir, orient=VERTICAL)
scrollbar.pack(side=RIGHT, fill=Y)

texte_widget_frame_parcourir = Text(frame_parcourir, yscrollcommand=scrollbar.set)
texte_widget_frame_parcourir.pack(fill=BOTH, expand=True)

scrollbar = Scrollbar(texte_widget_frame_parcourir)

scrollbar.config(command=texte_widget_frame_parcourir.yview)

texte_recherche_parcourir = Text(frame_parcourir)

Dans ce code, nous créons la fenêtre Parcourir avec toutes ses informations.

Puis, finalement, on peut terminer le script :

# Affichage de la page d'accueil au lancement du script
cadre_acceuil.pack()

# Création du menu
menubar = Menu(fenetre)

# Ajout des commandes directement au menu principal
menubar.add_command(label="Acceuil", command=affiche_acceuil)
menubar.add_command(label="Ajouter", command=affiche_ajoute)
menubar.add_command(label="Modifier", command=affiche_modifier)
menubar.add_command(label="Parcourir", command=affiche_parcourir)

# Configuration de la fenêtre avec la barre de menus
fenetre.config(menu=menubar)

# Initialisation de la pile des cadres
cadre_pile = [cadre_acceuil]

fenetre.mainloop()

Cette fin de script permet d’afficher l’interface et ses informations. Sans cela, Le script ne serait pas utilisable

III – Conclusion

Ainsi, ce projet nous a permis de mobiliser nos bases acquises en python et SQL pour monter un projet traitant sur les bases de données. Ce type de projet n’est pas des plus simples compte tenu de la technique à avoir et des difficultés rencontrées telles que le positionnement des blocs et les interactions avec la base de données. Pour continuer, il est important dans ce type de projets de comprendre son code ligne à ligne afin de ne pas se tromper sur un nom, une valeur ou autres.

Projets

Application de gestion de playlist (SGBDR) avec Python et…

Dans le monde numérique d’aujourd’hui, la musique est omniprésente, et la possibilité de gérer efficacement ses playlists est essentielle pour tout amateur de musique. Dans cet article, nous allons explorer comment créer une application simple de gestion de playlist en utilisant Python et Tkinter, deux outils puissants et accessibles pour le développement d’interfaces graphiques.

Introduction à Tkinter et SQLite

Tkinter est une bibliothèque standard de Python largement utilisée pour créer des interfaces graphiques. Elle offre une variété de widgets (éléments d’interface utilisateur) tels que des boutons, des étiquettes et des cadres, permettant de construire des applications interactives.

SQLite est une bibliothèque légère de gestion de base de données relationnelle souvent intégrée dans les applications pour stocker des données de manière structurée. Dans notre cas, nous l’utiliserons pour stocker les informations sur notre playlist musicale.

Création de la structure de données

Nous allons commencer par définir la structure de notre base de données SQLite, qui stockera les détails de chaque morceau de musique dans notre playlist. Nous aurons des champs tels que le titre de la chanson, l’année de sortie, la durée, le genre, l’artiste et le type d’album.

# Création de la table dans la base de données SQLite
def create_table():
    conn = sqlite3.connect("playlist.db")
    cursor = conn.cursor()
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS playlist (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            titre TEXT,
            annee INTEGER,
            duree TEXT,
            genre TEXT,
            artiste TEXT,
            album_type TEXT
        )
    ''')
    conn.commit()
    conn.close()

Dans cette fonction create_table(), nous établissons une connexion à notre base de données SQLite et exécutons une requête SQL pour créer une table nommée « playlist » avec les champs spécifiés. Cette fonction vérifie d’abord si la table existe déjà avant de la créer, grâce à la clause IF NOT EXISTS.

Lecture des données de la playlist

Une fois que la structure de la table est définie, nous pouvons écrire une fonction pour lire les données de la table et les afficher. Voici comment cela peut être fait :

# Lecture des données de la table et affichage
def read_data():
    conn = sqlite3.connect("playlist.db")
    cursor = conn.cursor()
    cursor.execute("SELECT * FROM playlist")
    rows = cursor.fetchall()
    conn.close()

    display_text = ""
    for row in rows:
        display_text += f"ID: {row[0]}, Titre: {row[1]}, Année: {row[2]}, Durée: {row[3]}, Genre: {row[4]}, Artiste: {row[5]}, Type d'album: {row[6]}\n"

    text_display.config(state=tk.NORMAL)
    text_display.delete('1.0', tk.END)
    text_display.insert(tk.END, display_text)
    text_display.config(state=tk.DISABLED)

Dans la fonction read_data(), nous établissons à nouveau une connexion à la base de données, exécutons une requête SQL pour sélectionner toutes les entrées de la table « playlist » et les récupérons à l’aide de fetchall(). Ensuite, nous bouclons à travers chaque ligne et les affichons.

Interface Graphique avec Tkinter

Maintenant que nous avons mis en place la gestion des données, nous pouvons créer une interface graphique conviviale pour notre application de gestion de playlist.

# Création de la fenêtre principale
window = tk.Tk()
window.title("Gestion de Playlist")
window.geometry("780x720")
window.minsize(680, 360)
window.configure(bg="#000")

Nous avons créé une fenêtre principale en utilisant tk.Tk() et lui avons donné un titre avec window.title(). Nous avons également défini la géométrie de la fenêtre et spécifié une couleur de fond.

Création des boutons d’action

Ensuite, nous avons ajouté des boutons pour effectuer différentes actions telles que la création de la table, la lecture des données et la manipulation des entrées de la playlist.

# Création des boutons d'action
frame_buttons = tk.Frame(window, bg="#000")
frame_buttons.pack(side=tk.TOP, pady=20)

btn_create = tk.Button(frame_buttons, text="CREATE", font=("Courrier", 14), bg="#01D758", fg="#000", command=create_table)
btn_create.pack(side=tk.LEFT, padx=10)

btn_read = tk.Button(frame_buttons, text="READ", font=("Courrier", 14), bg="#01D758", fg="#000", command=read_data)
btn_read.pack(side=tk.LEFT, padx=10)

Nous avons utilisé la classe Button de Tkinter pour créer des boutons, spécifiant leur texte, leur police, leur couleur de fond et de texte, ainsi que les actions qu’ils effectueront lorsqu’ils seront cliqués.

Conclusion

Dans cet article, nous avons exploré la création d’une application simple de gestion de playlist en utilisant Python et Tkinter. Nous avons vu comment créer une structure de base de données SQLite pour stocker les informations sur les morceaux de musique, ainsi que comment concevoir une interface graphique conviviale pour interagir avec ces données. Avec cette base, les amateurs de musique peuvent commencer à construire leur propre application de gestion de playlist personnalisée.

interface graphique Projets

Interface graphique, en python


Les interfaces graphiques en Python permettent de créer des applications avec une interface utilisateur graphique, offrant ainsi une expérience plus visuelle et interactive. Avec des bibliothèques tel que par exemple Tkinter, Python simplifie le développement d’interfaces utilisateur, que ce soit pour des applications de bureau, des outils de visualisation ou des applications mobiles.

Introduction :

Les interfaces graphiques en Python représentent un moyen puissant de créer des applications interactives, offrant une expérience utilisateur intuitive et visuellement attrayante. Ce projet a pour objectif principal de concevoir une interface graphique permettant l’affichage, la modification et la suppression d’éléments au sein d’une base de données à l’aide de la programmation en Python. Les outils clés employés dans ce projet incluent Tkinter pour la conception de l’interface utilisateur et pandas pour la manipulation des données de la base.

Le compte rendu suivra une structure organisée, mettant en valeur les aspects cruciaux du projet, de la conception de l’interface graphique à l’intégration de la base de données. Les grandes sections de ce compte rendu incluront la conception de l’interface graphique, l’intégration de la base de données, les fonctionnalités principales, la gestion des erreurs et les opportunités d’amélioration, concluant avec un récapitulatif des points saillants du projet. Ces sections aideront à fournir une vision globale et détaillée du processus de développement de l’interface graphique Python dédiée à la gestion de bases de données.

Fonctionnalités Principales :

L’utilisation de la bibliothèque pandas s’avère essentielle pour la manipulation agile des données au sein de notre interface. Cette section explore les techniques employées pour extraire, modifier et supprimer des données, assurant ainsi une cohérence entre les informations présentées à l’utilisateur et celles stockées dans la base de données. Tout comme Tkinter, qui est une bibliothèque d’interface graphique en Python qui permet de créer des applications avec une interface utilisateur visuelle, facilitant ainsi le développement d’applications interactives.

Affichage :

Le processus d’affichage se déroule en plusieurs parties. Pour débuter, nous avons opté pour la création d’une nouvelle fenêtre à laquelle nous avons attribué des dimensions de 800×1000 (une décision basée sur nos préférences personnelles).

def affichage():
    # Créer une nouvelle fenêtre pour afficher le Pokédex
    fenetreupdate = Toplevel()
    fenetreupdate.title("Pokédex")
    fenetreupdate.geometry("800x1000")

Ensuite, pour connecter le script à la base de données (sous format Excel), en considérant que « bdd » représente notre base de données et que l’argument engine='openpyxl' est utilisé pour orienter pandas vers le module openpyxl. Cela permet à pandas de lire et d’écrire des fichiers Excel dans le format spécifique .xlsx.

excel_data = pd.read_excel("bdd.xlsx", engine='openpyxl')

Enfin, pour exploiter chaque ligne et chaque colonne, l’utilisation d’une double boucle for s’est avérée la plus pertinente pour nous. Nous avons également souhaité personnaliser la première colonne (le nom des Pokémon) en appliquant une police en gras pour les mettre en valeur. De plus, nous avons ajouté un espacement entre les données du tableur afin d’améliorer la présentation générale.

for index, row in excel_data.iterrows():
    for col_index, value in enumerate(row):
        label = Label(fenetreupdate, text=str(value))
        if col_index == 0:
            label.config(font=("Arial", 14, "bold"))
        label.grid(row=index+1, column=col_index, padx=50, pady=10, columnspan=2)

Nous avons ensuite fait face a un problème de place car notre base de donnée était trop longue. Nous avons rajouté une scrollbar . Pour cela, on a du d’abord définir un canva qui entoure toute la fenêtre. Puis, configurer ou se trouve la barre par rapport a la fenêtre.

canvas = Canvas(fread)
    canvas.pack(side=LEFT, fill=BOTH, expand=True)
    scrollbar = Scrollbar(fread, orient=VERTICAL, command=canvas.yview)
    scrollbar.pack(side=RIGHT, fill=Y)
    canvas.configure(yscrollcommand=scrollbar.set)

Puis, on a du créer une fonction qui lierai l’évènement du mouvement de la souris avec la scrollbar:

    def bardef(event):
        canvas.configure(scrollregion=canvas.bbox('all'))
    frame.bind('<Configure>', bardef)
    canvas.bind_all("<MouseWheel>", lambda event: canvas.yview_scroll(int(-1 * (event.delta / 120)), "units"))

L’arborescence des fenêtres

Pour avoir un rendu de pokedex crédible, nous avons du trouver une bonne mise en forme des différentes fonctions. On est donc parti d’une première fenêtre qui afficherait deux boutons: Un pour afficher la base de donnée et un pour modifier le pokédex. La fenêtre « Modifier le pokédex » n’utilise pas de sql car elle ne modifie pas de base de donnée, elle répertorie les actions qu’on peut exécuter sur le pokedex ( ajouter, modifier et supprimer) sous forme de trois autres boutons.

	bouton3 = Button(fmodif, text="Ajouter un Pokemon", font=("Consolas", 15), bg="white", fg="black", command=create, width=50, height=3)
    bouton4 = Button(fmodif, text="Modifier un Pokemon", font=("Consolas", 15), bg="white", fg="black", command=update, width=50, height=3)
    bouton5 = Button(fmodif, text="Supprimer un Pokemon", font=("Consolas", 15), bg="white", fg="black", command=delete, width=50, height=3)

Voici les boutons que nous avons utilisé tout le long du code. Il fallait a chaque fois les appeler avec avec la fonction pack(). Un des erreurs que nous avons rencontré est de mettre tout les boutons a la fin du code alors qu’il fallait les placer dans leurs fonctions respectives.

La fonction create

Pour la fonction create, nous avons pensé à faire plusieurs champ de saisie a la suite afin que l’utilisateur puisse rentrer toutes les données du nouveau Pokémon. Voici a quoi ressemble un des champs de saisie:

#champ de saisie nom fr
    nom_pokemon_label = Label(fcreate, text="Entrez le nom du nouveau Pokémon", font=("Arial", 10), bg="red")
    nom_pokemon_label.pack(pady=10)
    nom_pokemon_entry = Entry(fcreate, bg="white")
    nom_pokemon_entry.pack(pady=10)

La variable ‘nom_pokemon_label’ sert a configurer la zone de texte qui permet de savoir a quoi sert le champ de saisie. La fonction Label sert a créer et modifier le style d’une zone de texte et ici, le texte au dessus du champ de saisie. La variable ‘nom_pokemon_entry’ sert a créer le champ de saisie et modifier son apparence. Les deux variables sont appelées avec la fonction pack() qui fait apparaitre les éléments graphiques.

Après avoir fait tout les champs de saisie de chacune des données, on a créé un bouton sauvegarde afin d’enregistrer les nouvelles données dans le fichier.

Dans la partie sql, on a donc d’abord créé une fonction enregistrer() qui prend en paramètre tout les noms de données afin d’enregistrer chaque valeur. Ensuite, nous avons créé la variable ‘nv_ligne’. Avec la commande DataFrame de panda, elle crée une nouvelle ligne en prenant en compte les colonnes du fichier excel. Avec a commande concat, elle enregistre les données en fonction du type de donnée dans chacune des colonnes de la nouvelle ligne. Le ignore_index sert a ne pas prendre en compte les id d’origines.

#création de la ligne du nv pokemon
    nv_ligne = pd.DataFrame([[idtf, nom_pokemon, nom_anglais, type_pokemon, type_secondaire, pv, attaque, defense]], columns=excel_data.columns)
#enregistrer les données dans la nouvelle ligne
    excel_data = pd.concat([excel_data, nv_ligne], ignore_index=True)

Comme a la fin de chaque fenêtre, nous utilisons destroy() pour ne pas accumuler les fenêtres. Donc, après avoir appuyé sur le bouton sauvegarde, la fenêtre se ferme mais un print sur la console d’exécution nous montre que le pokemon a été enregistré.

La fonction update

Nous avons utilisé a peu près le même fonctionnement pour les autres fonctions: une fonction qui crée la fenêtre avec la bibliothèque de tkinter et une autre qui modifie et enregistre les données de la base de données avec les bibliothèques de panda et sqlite3.

Donc, nous avons commencé par une fonction update qui génère la fenètre et son apparence. Nous avons choisis, pour modifier les données de créer d’abord un champ de saisie pour rentrer le nom Français du pokemon a modifier, puis une Combobox ( comme un champ de saisie mais ou on sélectionne la donnée dans une liste) afin de sélectionner la donnée a modifier, puis, un autre champ de saisie pour donc modifier le contenu de la donnée. Nous avons aussi ajouté un bouton sauvegarder qui utilise la fonction update_data qui prend en paramètre chaque variable utilisée par les champ de saisie et la combobox.

#champ de saisie pokemon a modifier
    userlbl = Label(fupdate, text="Entrez le nom du pokémon que vous voulez modifier", font=("Arial", 12), bg="red")
    userlbl.pack(pady=10)
    nom_pokemon = Entry(fupdate, bg="white")
    nom_pokemon.pack(pady=10)
#combobox type de donnée a modif
    liste_donnees = ["Indice", "Nom français", "Nom anglais", "Type", "Type secondaire", "PV", "Attaque", "Défense"]
    comboboxlbl = Label(fupdate, text="Choisissez le type de donnée à modifier", font=("Arial", 12), bg="red")
    comboboxlbl.pack(pady=10)
    listeCombo = ttk.Combobox(fupdate, values=liste_donnees)
    listeCombo.pack(pady=10)
 #cds nv donnée
    userlbl = Label(fupdate, text="Entrez la nouvelle donnée", font=("Arial", 12), bg="red")
    userlbl.pack(pady=10)
    nv_donnée_entry = Entry(fupdate, bg="white")
    nv_donnée_entry.pack(pady=10)

Les champs de saisies sont les même que ceux de la fonction ajouter. Pour la Combobox, c’est le meme principe sauf qu’il faut ajouter une liste avant ou on a rentré les différents types de données et qu’on appelle dans la variable listeCombo avec ‘values’.

Pour la sauvegarde, on a utilisé une fonction update_data() qui prend en paramètre le nom du pokémon a modifier, le type de donnée a modifier et la nouvelle valeur. Pour mettre a jour les données, on a du écrire un enchainement de elif car il faut que l’utilisateur puisse modifier chaque donnée et que donc le code est différent pour chaque donnée.

#maj des données
    if type_donnee == "Indice":
        excel_data.loc[excel_data['Nom français'] == nom_pokemon, 'Indice'] = nv_val
    elif type_donnee == "Nom français":
        excel_data.loc[excel_data['Nom français'] == nom_pokemon, 'Nom français'] = nv_val
    elif type_donnee == "Nom anglais":
        excel_data.loc[excel_data['Nom français'] == nom_pokemon, 'Nom anglais'] = nv_val
    elif type_donnee == "Type":
        excel_data.loc[excel_data['Nom français'] == nom_pokemon, 'Type'] = nv_val
    elif type_donnee == "Type secondaire":
        excel_data.loc[excel_data['Nom français'] == nom_pokemon, 'Type secondaire'] = nv_val
    elif type_donnee == "PV":
        excel_data.loc[excel_data['Nom français'] == nom_pokemon, 'PV'] = nv_val
    elif type_donnee == "Attaque":
        excel_data.loc[excel_data['Nom français'] == nom_pokemon, 'Attaque'] = nv_val
    elif type_donnee == "Défense":
        excel_data.loc[excel_data['Nom français'] == nom_pokemon, 'Défense'] = nv_val
       

A chaque fois on commence avec une vérification pour savoir quel est le type de donnée, puis, avec la fonction loc(), on cherche dans le fichier d’abord le pokémon avec son nom Français, puis on trouve le type de donnée a modifier et on le remplace par la nouvelle valeur. Les données sont ensuite enregistrées dans la base de donnée avec ‘excel_data.to_excel’ comme dans les autres fonctions, la fenêtre et détruite un print affiche que les données ont bien été modifiées.

La fonction delete

Pour la suppression d’un pokemon nous avons utilisé trois fonctions. Dans la première, ont créé la fenêtre et on y ajoute un champ de saisi. En effet, on a seulement besoin du nom du Pokemon puisqu’il s’agit de supprimer toutes les données de ce Pokémon a partir de son nom. Les deux autres fonctions sont liées puisque la première, pokemon_supp() sert a trouver le nom du pokemon qui correspond et utiliser la deuxième fonction suppression() qui met a jour la base de donnée avec ‘to_excel’.

def pokemon_supp(supp_entry):
    #enregistre le nom rentré
    nom_pokemon = supp_entry.get()
    #supprime de la base
    suppression(nom_pokemon)

def suppression(nom_pokemon):
    #connecte le xlsx
    excel_data = pd.read_excel("base de donnée.xlsx")
     # remplace par le nom de la collone du nom
    excel_data = excel_data[excel_data['Nom français'] != nom_pokemon]
    # recrée le exel sans le pokemon
    excel_data.to_excel("base de donnée.xlsx", index=False)

La méthode .get() sert a utiliser une variable avec entry, donc ici la variable du champ de saisie. Dans la fonction suppression(), on connecte d’abord les données du fichier grâce au ‘read_excel’ de panda, puis on sélectionne toutes les lignes du fichier sauf celle du pokémon sélectionné et on recrée la base de donnée avec.

Conclusion

En résumé, ce projet d’interface graphique en Python pour la gestion d’une base de données a été une expérience enrichissante. Nous avons exploré la création d’une fenêtre comportant des boutons, chacun dédié à une fonctionnalité spécifique. Nous avons également mis en place une zone de champs de texte pour manipuler les données à l’intérieur. Bien que cela ait été complexe, cette expérience nous a permis d’approfondir nos connaissances en programmation Python.

Merci d’avoir lu notre article, voici la base de donnée et le script a télécharger.

Tutoriels

Optimiser son code python

Vous cherchez à rendre l’affichage d’une image plus rapide ? où simplement, vous trouvez votre code un peu long à se faire ? Ici, nous nous plongerons dans l’art de perfectionner nos codes pour en maximiser l’efficacité. Vous trouverez ici des moyens de mieux utiliser certaines bibliothèques ou des alternatives à celle-ci.

Teaser du tutoriel

Kandinsky

Vous avez peut-être déjà remarqué que Kandinsky, bibliothèque utile pour dessiner en python, est très lent sur ordinateur. Mais l’avez-vous bien paramétré ? Et êtes-vous satisfait de sa rapidité ? Regardons ensemble comment y remédier.

Comment bien utiliser Kandinsky

Tout d’abord, regardons comment vous avez et pourrez paramétrer l’émulateur de Kandinsky. Pour vérifier ça, ouvrez votre IDE, lancez votre script.

vous partez donc de cela :

Ensuite suivez, ces indications :

tout d’abord allez dans le menu « Options » :

D’ici, vous pouvez :

  • avec l’onglet « OS », choisir l’os utilisé.
  • avec l’onglet « Model », choisir une version de la calculatrice Numworks, ici émulé.
  • avec l’onglet « Zoom », choisir la taille de la fenêtre.

Ici pour accélérer notre script, nous utiliserons l’OS « PC ».

Et voilà, déjà, votre script va bien plus vite sur votre ordinateur. Il est important de savoir que ce n’affecte pas la vitesse du script sur la calculatrice, mais seulement ici sur votre ordinateur.

Pillow, Kandinsky en plus rapide (et plus encore)

Vous trouvez encore votre script lent à l’exécution ? Alors voici la bibliothèque PIL. Avant de commencer, il est important de rappeler que Pillow n’est pas disponible sur Numworks là où l’est Kandinsky.

Comparatif de vitesse entre Pillow et Kandinsky

Pour analyser la vitesse de mes deux options, je vais utiliser le script disponible sur le tuto ici qui génère un « Julia set », et je le modifie pour qu’il utilise Pillow plutôt que Kandinsky.

Afin de chronométrer le temps de génération, j’ai rajouté la bibliothèque Time :

#si le script utilise Pillow
from PIL import Image
#si le script utilise Kandinsky
#import Kandinsky
import time

def julia_PIL(N_iteration):
    start_time = time.time()  # Enregistrez le temps de début

    # création de la palette de couleurs
    palette = []
    xmax = 2
    xmin = -2
    ymax = 1.3875
    ymin = -1.387
    r = 255
    g = 255
    b = 255
    for j in range(0, 128):
        b = 255 - 2 * j
        palette.append((r, g, b))
    for j in range(128, 256):
        r = 255 - 2 * (j - 128)
        g = 255 - 2 * (j - 128)
        palette.append((r, g, b))

    # Création de l'image avec Pillow (rien a mettre si on utilise Kandinsky)
    img = Image.new('RGB', (320, 222))

    #generation de la fractale
    for x in range(320):
        for y in range(222):
            i = 0
            z = complex(xmin + (xmax - xmin) * x / 320 + (ymax - (ymax - ymin) * y / 222) * 1J)
            c = complex(0.36, 0.36)
            while i < N_iteration and abs(z) < 2:
                i = i + 1
                z = z * z + c
            couleur = palette[int(255 * i / N_iteration)]

            # Avec Pillow : Définir la couleur du pixel dans l'image aux coordonnées (x,y)
            img.putpixel((x, y), couleur)
            # Avec Kandinsky : même but que Pillow
            #col = color(couleur[0],couleur[1],couleur[2])
            #set_pixel(x,y,col)

    # Affichage de l'image (rien a mettre si on utilise Kandinsky)
    img.show()

    end_time = time.time()  # Enregistrez le temps de fin
    elapsed_time = end_time - start_time
    print("Temps d'exécution : ",elapsed_time," secondes")

Ici, le script utilisant Kandinsky prend 3,25 secondes, celui avec Pillow prend 4,40 secondes quand je génère une fractale avec 1000 itérations. Mais il est plus lent ?!! en fait non, car Pillow, avec son .show() (ligne 40), doit ouvrir une nouvelle fenêtre photo, ce qui prend longtemps. Si à la place, on enregistre l’image et qu’on ne l’affiche pas, le script prend 0,39 seconde à s’exécuter. On rajoute donc ceci à notre script au même endroit que .show().

img.save('Julia.png')

L’image générée est enregistrée dans le même répertoire que le script, sous le nom « Julia.png » (le nom est modifiable à souhait).

Utilisations

De mon côté, j’ai utilisé le script modifié grâce à Pillow. Ainsi, je peux générer des images a très grand format et j’ai donc réalisé ceci, un « Julia set » en 8k en 211,25 secondes (ce qui est impossible avec Kandinsky, car la bibliothèque est limitée en 320p par 222p) :

En outre, je peux l’utiliser par exemple pour faire un explorateur de fractale sur des images plus petites que de la 8k quand même.

Si vous avez besoin de plus d’information, voici la documentation complète de Pillow, ici.

NumPy

NumPy est une bibliothèque python très utile dans le calcul de tableaux ou de matrices pour accélérer ces calculs ou simplifier la lecture du code grâce à ses fonctions directement implémentée comme la multiplication matricielle. C’est une bibliothèque très utilisée par exemple pour :

  • Calcul Scientifique : NumPy est largement utilisé dans le domaine scientifique pour la modélisation mathématique, les simulations numériques et l’analyse de données.
  • Apprentissage Automatique : De nombreuses bibliothèques d’apprentissage automatique, comme scikit-learn, utilisent NumPy pour la manipulation des données et les opérations numériques.
  • Traitement des Images et du Son : NumPy est souvent utilisé dans le traitement des images et des signaux sonores en raison de ses capacités à travailler avec des tableaux multidimensionnels.
  • Analyse de Données : Les analystes de données et les scientifiques des données utilisent NumPy pour effectuer des opérations numériques efficaces sur de grands ensembles de données.

Pourquoi utiliser les tableaux de NumPy plutôt que de simples listes ?

Les listes normales en Python sont des structures de données de base qui peuvent contenir des éléments de types différents. Elles offrent une grande souplesse, mais peuvent être moins performantes pour les opérations numériques sur de grands ensembles de données. D’un autre côté, les tableaux de NumPy (ou array) sont des structures de données spécialisées pour les calculs numériques qui offrent des performances optimisées.

En outre, voici quelques exemples montrant les différences entre les deux :

  • Type d’Éléments :
    • Liste : Peut contenir des éléments de différents types (entiers, chaînes de caractères, etc.).
    • Array : Contient des éléments d’un type de données homogène. Les Array sont typés, ce qui signifie que tous les éléments doivent être du même type.
  • Performance :
    • Liste : Moins efficace pour les opérations numériques sur de grands ensembles de données en raison de sa flexibilité.
    • Array : Conçu pour les calculs numériques, offrant des opérations vectorielles efficaces et des performances optimisées.
  • Fonctions et Opérations :
    • Liste : Offre des fonctionnalités de base pour la manipulation de données, mais peut nécessiter des boucles explicites pour certaines opérations numériques.
    • Array : Fournit une vaste gamme de fonctions optimisées pour les opérations numériques, y compris des opérations vectorielles, des fonctions mathématiques et des fonctions de statistiques.
  • Taille Dynamique :
    • Liste : La taille d’une liste peut changer dynamiquement en ajoutant ou en supprimant des éléments.
    • Array : La taille d’un Array est fixée à la création. Pour ajouter ou supprimer des éléments, un nouveau tableau doit être créé.
  • Syntaxe :
    • Liste : Définie avec des crochets, par exemple, ma_liste = [1, 2, 3]
    • Array : Créé avec la fonction numpy.array(), par exemple, mon_tableau = numpy.array([1, 2, 3])

Démonstration

Imaginons que nous devons faire des calculs sur des éléments d’une liste d’integer :

liste = [i for i in range(10000000)]
resultat = []
for nb in liste :
    resultat.append(nb ** 2)

Ici, on élève chaque élément de notre liste au carré, ce script prend 3,42 seconde. Avec NumPy, on peut faire ceci :

import numpy as np

arr = np.array([i for i in range(10000000)])
# Applique une fonction pour élever chaque élément de la liste au carré
result = np.square(arr)

Ici, le script prend 1,15 seconde, NumPy est utile pour gérer des tableaux de valeurs très grandes et appliquer des règles de calculs sur ces tableaux. Les array sont un nouveau type d’objet qui correspondent aux listes et matrices.

Fonctions natives intéressantes

NumPy offre un grand nombre de fonctions qui pourront simplifier la lecture de votre code et l’accélérer :

  • Produits de tous les éléments :
import numpy as np

liste = [1, 2, 3, 4, 5]

produit = np.prod(liste)
# renvoie donc avec un print ou un return 120
  • moyenne et écart-type :
import numpy as np

liste = [1, 2, 3, 4, 5]

moyenne = np.mean(liste)
# renvoie donc avec un print ou un return 3.0
ecart_type = np.std(liste)
# renvoie donc avec un print ou un return 1.4142135623730951
  • Chercher des éléments suivants une condition et liste des éléments unique de la liste
import numpy as np

#va renvoyer tous les éléments du tableau supérieur à 2 dans une nouvelle liste
liste_sat = [1, 2, 3, 4, 5]
elements_satisfaisants = np.extract(np.array(liste_sat) > 2, liste_sat)
# renvoie donc avec un print ou un return [3 4 5]

#va renvoyer tous les éléments uniques du tableau et leur nombre d'apparitions dans une nouvelle liste
liste_uni = [1, 2, 2, 3, 4, 4, 5]
elements_uniques, comptages = np.unique(liste_uni, return_counts=True)
# renvoie donc avec un print ou un return [1 2 3 4 5] [1 2 1 2 1]

Si vous avez besoin de plus d’information, voici la documentation complète de NumPy, ici.

Tutoriels

Comment installer et utiliser MicroPython sur un ESP32

Dans le premier projet de l’année, nous avons eu la possibilité d’utiliser un ESP32. Cependant, il fallait que cet ESP32 utilise MicroPython pour que le projet soit valable. Nous verrons donc comment installer et utiliser MicroPython sur un ESP32.

Installation de MicroPython

Avant toute chose, il faut télécharger plusieurs fichiers :

  • Le logiciel qui va permettre de flasher ESP32. Le terme flasher correspond à mettre à jour, modifier le micrologiciel d’un appareil
  • Et le micrologiciel qui va être flashé dans l’ESP32
  • Un logiciel pour décompresser les fichiers

Pour cela il vous faut accéder à ce lien qui renvoie vers le site Github et c’est là où vous trouverez le micrologiciel.

Une fois sur cette page, il vous faut cliquer sur le bouton Code puis sur Download ZIP pour télécharger le micrologiciel en format compressé.

Il faut également télécharger le logiciel qui va permettre de flasher l’ESP32. Il vous faut donc accéder à ce lien pour le télécharger.

Pour avoir la bonne version du logiciel qui est compatible avec notre ESP32, il faut trier à gauche par ESP32-S3 :

La page va s’actualiser et vous n’aurez qu’à cliquer sur le bouton Download à côté de Flash Download Tools.

Vous avez donc téléchargé les deux fichiers nécessaires. Cependant il vous faut les décompresser. Pour cela il vous faut un logiciel pour décompresser comme 7zip.

Vous vous retrouverez donc avec deux dossiers comme ceci :

À partir de maintenant il faut que votre ESP32 soit connecté en USB à votre ordinateur.

Nous allons maintenant installer le nouveau micrologiciel dans votre ESP32, pour cela, dans le dossier flash_download_tool_3.9.5 exécutez le fichier flash_download_tool_3.9.5.exe.

Un invite de commande et une petite fenêtre s’ouvre. Nous allons nous intéresser à la petite fenêtre :

Il faut paramétrer la fenêtre selon votre ESP32, dans mon cas il s’agit d’un ESP32-S3 je sélectionne donc ESP32-S3 dans le ChipType et dans LoadMode il faut mettre USB comme l’ESP32est connecté en USB sur l’ordinateur.

Appuyez sur OK.

Dans cette fenêtre il faut cocher la première case et dans la même ligne cliquer sur les 3 points afin de sélectionner le nouveau micrologiciel. Le micrologiciel se trouve dans l’autre dossier st7789s3_mpy-main et dans le sous dossier firmware et il s’agit du fichier firmware.bin . Et à droite du @ il faut mettre un 0. Vous pouvez savoir si vous avez fait une erreur si une des cases est en rouge si par exemple le chemin vers le fichier est mauvais ou si le numéro n’est pas valide. Vous devriez avoir plus ou moins ceci :

Par la suite en bas à droite dans la catégorie COM il faut choisir le port correspondant à celui de votre ESP32. Si vous ne le connaissez pas il vous faut ouvrir le gestionnaire de périphériques, pour ce faire, appuyez sur le bouton Windows + r et écrivez devmgmt.msc pour ouvrir la fenêtre. Et dans la catégorie Port, il faut trouver le Périphérique série USB

Dans mon cas il s’agit du COM5 donc je renseigne le COM5 dans l’application et dans la catégorie BAUD il faut mettre à 9216000.

Vous devriez avoir la même chose sauf peut-être pour le COM et dans ce cas là avant d’appuyer sur START il faut mettre l’ESP32 en mode téléchargement, pour ça il faut appuyer en même temps sur le bouton de reset sur le côté et celui de boot, qui est le plus proche du bouton de reset, en commençant par appuyer sur le bouton reset. Après cela vous pouvez appuyer sur START et le téléchargement commencera.

Utilisation de MicroPython

Après avoir installé votre version de MicroPython vous avez probablement envie de l’utiliser. Et pour cela il vous faut un IDE comme Thonny mais il ne suffit pas de juste le démarrer, il faut configurer votre ESP32 dessus.

Pour configurer votre interpréteur afin d’utiliser l’ESP32 et non l’ordinateur il faut cliquer en bas à droite sur Python 3 local, qui correspond à votre python installé sur votre ordinateur. Cliquez ensuite sur Configurer l’interpréteur.

Dans le type d’interpréteur, il faut choisir MicroPython (ESP32) comme il s’agit d’un ESP32sous MicroPython. un nouveau paramètre apparait, il s’agit du port de l’ESP32 et il s’agit du même que sur le logiciel d’installation du micrologiciel sauf si vous avez changer de port l’ESP32. Vous devriez donc vous retrouver avec ceci :

Cliquez sur OK, la fenêtre va se fermer et retourner sur l’interface normal avec quelques changements sur ce qui est écrit dans la console et aussi en bas à droite dans l’interpréteur.

Vous pouvez taper help() pour avoir quelques informations mais vous y trouverez principalement de la documentation générique qui ne correspond pas forcément à l’ESP32, pour avoir quelque chose de plus détaillé, vous pouvez retourner sur le Github de la version du firmware, c’est en anglais mais vous aurez la documentation qui correspond le mieux à l’ESP32.

Conseils

Je vous conseille d’afficher les fichiers de l’ESP32, pour ça en haut dans affichage il faut cliquer sur Fichiers.

Cette fenêtre permet d’accéder aux fichiers présents sur l’ESP32, de les supprimer, de les télécharger ou de téléverser des fichiers de votre ordinateur vers l’ESP32.

Vous remarquerez deux fichiers importants : boot.py et main.py. Ces fichiers sont importants car ce sont des fichiers qui vont être exécutés automatiquement par l’ESP32 dès qu’il sera alimenté, dans ce cas, boot.py sera exécuté puis main.py dès que l’exécution de boot.py est terminée. Dans l’image ci-dessus les fichiers correspondent aux fichiers de ma Station Météo sur ESP32, le fichier boot est vide mais tout le code est contenu dans main.py, sachant que les autres fichiers sont soit des modules supplémentaires ou des images qui sont utilisés dans le code principal.

Également dans le dossier contenant le firmware soit st7789s3_mpy-main il y a un dossier exemples qui contient quelques exemples qui peuvent être utiles pour comprendre le fonctionnement des différentes fonctions et méthodes. Cela passe de l’affichage de l’heure, à de l’écriture, ou un jeu. Il y a aussi deux fichiers importants qui sont tft_buttons.py et tft_config.py qui sont les deux modules permettant de s’occuper de l’écran et des boutons et qui doivent donc être en tout temps dans l’ESP32.

Conclusion

Pour conclure, j’espère que ce tutoriel vous sera utile afin de comprendre le fonctionnement de MicroPython sur ESP32 et vous donnera envie d’en savoir plus sur ces derniers.

Tutoriels

Comment installer Pip sur Mac ?

Les jeunes, dont moi, préfèrent les Mac d’Apple pour leur esthétique et l’écosystème intégré, en particulier la stabilité de macOS, idéale pour la programmation. Cependant, certains novices éprouvent des difficultés. Fort de mon expérience, je propose un tutoriel sur les bases de Python, mettant l’accent sur l’installation de Pip sur Mac. Découvrons ensemble comment installer Pip sur ce système ?

Etape 0 : Une claire compréhension de ce que l’on tente d’installer

« Pip » est l’acronyme du « Pip Installs Packages » (Pip Installe des Paquets). C’est une commande en ligne qui simplifie le processus d’installation, de mise à jour et de désinstallation de packages Python depuis le Python Package Index (PyPI). PyPI est un dépôt qui héberge un grand nombre de packages Python développés par la communauté.

Par exemple, pour installer une bibliothèque Python à l’aide de pip, vous pouvez exécuter la commande suivante dans le terminal ou l’invite de commande :

pip install nom_du_paquet

Etape 1 : Téléchargement de PIP.

Pour commencer nous allons lancer notre moteur de recherche safari vue que nous sommes sur Mac.

Puis nous partirons sur le moteur de recherche suivant google en marquant sur notre barre de Recherche google.com.

Nous arrivons donc sur google (vous en faite du chemin dit donc !!!).

Une fois sur notre cher moteur de recherche, nous saisirons « télécharger pip Python » dans la barre de recherche pour accéder à un site sécurisé permettant donc comme convenu l’installation de pip.

Une fois que nous appuyons sur « Entrée », nous atteignons l’endroit souhaité. À partir de ce moment, il est crucial d’être minutieux et de suivre scrupuleusement mes instructions. Donc, une fois arrivés, effectuez un double-clic sur le premier lien ou celui que j’aurai encadré et pointe.

Ce lien nous guide vers la page suivante, simplifiant ainsi notre navigation. En double-cliquant sur « Téléchargement des fichiers », on sera re-directe vers la page suivante.

Double clique sur les premiers liens « Distribution des sources » qui va lancer le téléchargement du programme.

Suite au téléchargement du programme, effectuez un double-clic sur le fichier afin de confirmer son installation.

Mes félicitations ! Vous avez accompli avec brio l’installation de Pip sur le disque dur, marquant ainsi une étape cruciale dans l’amélioration de votre configuration informatique.

Pip a bien été installé sur le disque dur.

Etape 2: Installation de Pip.

Initiez le lancement de Thonny, puis, dans le menu « Outils », procédez à la sélection de l’option « Ouvrir la console du système… » avant d’effectuer un double-clic pour accéder à cette fonctionnalité cruciale.

Nous voilà désormais en présence du Terminal, prêt à être utilisé avec enthousiasme !

Copie le code fourni ci-dessous avec précaution dans la console de ton Mac. Ensuite, presse la touche « Entrée » pour déclencher le processus d’installation de Pip, améliorant ainsi les capacités de ton système de manière significative.

Le code en question:

python3 -m pip install --upgrade pip

Apres avoir copié soigneusement le code mentionné ci-dessous dans la console de ton Mac. Puis, une fois cette étape accomplie, confirme en appuyant sur la touche « Entrée », déclenchant ainsi l’installation de Pip, et observe avec satisfaction l’apparition d’un message de confirmation pour attester du succès de cette opération.

Bonus

Bravo ! Vous avez brillamment suivi les étapes d’installation de pip sur votre Mac, démontrant ainsi votre détermination et votre habileté à maîtriser ces processus techniques. Soyez fiers de cette réalisation, car elle témoigne de votre capacité à surmonter des défis informatiques. Votre réussite dans cette tâche essentielle ouvre la voie à de nouvelles opportunités passionnantes dans le monde de la programmation. Continuez sur cette lancée, et rappelez-vous que chaque victoire, aussi petite soit-elle, contribue à votre progression. Vous avez toutes les raisons d’être fiers de vos compétences nouvellement acquises !