Infoforall

Python-Physique 04 : Placement des éléments

Nous allons ici découvrir comment faire apparaitres de petits éléments interactifs qu'on nomme widgets, contraction de window et gadget. Cela pourra être un bouton, une barre de sélection...

Voici le visuel que nous allons parvenir à réaliser :

avancement avec slider

Les deux curseurs du bas sont des zones où on pourra interagir avec l'utilisateur et modifier l'allure des courbes en temps réel.

Remarque : si vous travaillez via un site vous permettant d'utiliser mapplotlib, il faudrait rajouter la ligne permettant de sauvegarder les courbes dans un fichier-image :

plt.savefig("essai.png")

1 - Présentation d'un widget : le Slider

Commençons par voir comment créer l'une de ces barres.

Commençons par reprendre le code final de l'activité précédente en replaçant les deux inputs par des valeurs fixes, par exemple 5 moles et 16 moles.

#!/usr/bin/env python

# -*- coding: utf-8 -*-


import matplotlib.pyplot as plt


# Déclaration des coefficients stochiométriques

cs_C4H10 = 2

cs_O2 = 13

cs_CO2 = 8

cs_H2O = 10


# Initialisation des quantités de matières initiales (en mol)

no_C4H10 = 5

no_O2 = 16

no_CO2 = 0

no_H2O = 0


# Calcul de l'avancement maximum

x_max_C4H10 = no_C4H10 / cs_C4H10

x_max_O2 = no_O2 / cs_O2

x_max = min(x_max_O2,x_max_C4H10)


# Création des listes

x = [ 0, x_max ]

n_C4H10 = [ (no_C4H10-cs_C4H10*valeur) for valeur in x]

n_O2 = [ (no_O2 - cs_O2*valeur) for valeur in x]

n_CO2 = [ (no_CO2 + cs_CO2*valeur) for valeur in x]

n_H2O = [ (no_H2O + cs_H2O*valeur) for valeur in x]


# Création des courbes

plt.plot(x, n_C4H10, label="C4H10", color='orange')

plt.plot(x, n_O2, label="O2", color='red')

plt.plot(x, n_CO2, label="CO2", color='black')

plt.plot(x, n_H2O, label="H2O", color='blue')


# Réglages axes et grille

liste_des_n_max = [ max(n_C4H10), max(n_O2), max(n_CO2), max(n_H2O) ]

n_max = max(liste_des_n_max)

plt.xlabel("Avancement (mol)")

plt.ylabel("Quantité de matière (mol)")

plt.title("Evolution de la réaction de combustion")

plt.legend(loc='upper center')

plt.axis([0,x_max,0,n_max])

plt.grid()


# Création des Widgets


# Création de l'image

nom_du_fichier = "courbes_avancement_"+str(no_C4H10)+str(no_O2)

plt.savefig(nom_du_fichier+".png")


# Affichage de l'interface

plt.show()

Dans les activités précédentes, nous avons vu que le nom des variables devaient normalement plutôt commencer par une minuscule. Nous avons néanmoins contourné cette norme puisque certaines notations de sciences physiques utilisent des majuscules. Voyons maintenant ce qu'indique une majuscule en informatique. Voici comment comment nous allons créer cette barre réglable à la souris :

w_reactif_1 = Slider(apparence_reactif_1, 'no(C4H10)', 0.1, 50.0, valinit=5, valstep=0.1, color='orange')

Nous voyons que :

Rajoutons maintenant apparence_reactif_1 avant de créer le Slider :

01° Rajouter les lignes suivantes sous le commentaire Creation des Widgets.

apparence_reactif_1 = plt.axes([0.25, 0.0, 0.65, 0.03], facecolor='grey')

w_reactif_1 = Slider(apparence_reactif_1, 'no(C4H10)', 0.1, 50.0, valinit=5, valstep=0.1, color='orange')

Vous devriez avoir une erreur de ce type :

NameError: name 'Slider' is not defined

Pour créer nos wodgets, nous allons devoir importer leurs codes avec la ligne suivante qu'il faudra placer avec les autres importations.

import matplotlib.pyplot as plt

from matplotlib.widgets import Slider

Voici le code à tester en définitive :

#!/usr/bin/env python

# -*- coding: utf-8 -*-


import matplotlib.pyplot as plt

from matplotlib.widgets import Slider


# Déclaration des coefficients stochiométriques

cs_C4H10 = 2

cs_O2 = 13

cs_CO2 = 8

cs_H2O = 10


# Initialisation des quantités de matières initiales (en mol)

no_C4H10 = 5

no_O2 = 16

no_CO2 = 0

no_H2O = 0


# Calcul de l'avancement maximum

x_max_C4H10 = no_C4H10 / cs_C4H10

x_max_O2 = no_O2 / cs_O2

x_max = min(x_max_O2,x_max_C4H10)


# Création des listes

x = [ 0, x_max ]

n_C4H10 = [ (no_C4H10-cs_C4H10*valeur) for valeur in x]

n_O2 = [ (no_O2 - cs_O2*valeur) for valeur in x]

n_CO2 = [ (no_CO2 + cs_CO2*valeur) for valeur in x]

n_H2O = [ (no_H2O + cs_H2O*valeur) for valeur in x]


# Création des courbes

plt.plot(x, n_C4H10, label="C4H10", color='orange')

plt.plot(x, n_O2, label="O2", color='red')

plt.plot(x, n_CO2, label="CO2", color='black')

plt.plot(x, n_H2O, label="H2O", color='blue')


# Réglages axes et grille

liste_des_n_max = [ max(n_C4H10), max(n_O2), max(n_CO2), max(n_H2O) ]

n_max = max(liste_des_n_max)

plt.xlabel("Avancement (mol)")

plt.ylabel("Quantité de matière (mol)")

plt.title("Evolution de la réaction de combustion")

plt.legend(loc='upper center')

plt.axis([0,x_max,0,n_max])

plt.grid()


# Création des Widgets

apparence_reactif_1 = plt.axes([0.25, 0.0, 0.65, 0.03], facecolor='grey')

w_reactif_1 = Slider(apparence_reactif_1, 'no(C4H10)', 0.1, 50.0, valinit=5, valstep=0.1, color='orange')


# Création de l'image

nom_du_fichier = "courbes_avancement_"+str(no_C4H10)+str(no_O2)

plt.savefig(nom_du_fichier+".png")


# Affichage de l'interface

plt.show()

02° Lancer ce code dans IDLE :

premier slider

Point positif : plus d'erreur. Point négatif : il est placé un peu n'importe où.

Si vous déplacer la valeur maximale de la zone orange, vous pourrez constater que la valeur à droite est modifiée, que le curseur devient plus grand ou plus petit et qu'une ligne rouge indique la position de la valeur initiale. Par contre, cela ne modifie en rien le graphique pour l'instant.

Pour améliorer le visuel, il va falloir aller chercher dans la documentation de matplotlib: Lien vers la documentation sur les axes

Pour rappel, notre code était :

apparence_reactif_1 = plt.axes([0.25, 0.0, 0.65, 0.03], facecolor='grey')

En allant lire la documentation, on peut voir que la liste permet de placer le rectangle symbolisant le Slider : [left, bottom, width, height]

03° Sachant que les dimensions sont fournies en pourcentage de la place disponible, vérifier si notre Slider est bien :

placement du slider

04° Modifier le code pour obtenir ceci :

exercice placement du slider

...CORRECTION...

Puisqu'on doit fournir une liste contenant [left, bottom, width, height], le code a fournir est :

apparence_reactif_1 = plt.axes([0.25, 0.25, 0.5, 0.5], facecolor='grey')

Comme vous le voyez, on peut placer le Slider où on veut mais on ne peut pas agrandir la feuille avec cette méthode de placement. Nous allons donc devoir voir un peu plus en détail comme matplotlib place les différents éléments.

2 - Gestion d'une fenêtre avec un seul graphique

En réalité, nous avons laissé matplotlib placer tout ceci par défaut. Et cela ne donne donc pas grand chose.

Vous allez voir ici comment placer correctement et rigoureusement les éléments de votre fenêtre graphique.

Commençons par le cas le plus basique : on veut une zone graphique et placer les différents widgets autour.

Il existe une méthode qui permet de "pousser" le graphique et de limiter la place qu'il prend :

avancement avec slider joli

On peut pousser un peu plus et obtenir ceci :

avancement avec petit graphique

La seule différence avec le code de la partie précédente tient en une ligne ici :

# Création des courbes

plt.subplots_adjust(left=0.75, bottom=0.50)

plt.plot(x, n_C4H10, label="C4H10", color='orange')

plt.plot(x, n_O2, label="O2", color='red')

plt.plot(x, n_CO2, label="CO2", color='black')

plt.plot(x, n_H2O, label="H2O", color='blue')

05° Identifier la ligne rajoutée. Comment se nommme la fonction utilisée ? A quoi correspondent 0.75 et 0.50 ?

...CORRECTION...

La fonction se nomme subplots_adjust.

Elle permet de décaler le côté gauche du graphe sélectionné de 75% de la largeur.

Elle permet de décaler le côté bas du graphe sélectionné de 50% de la hauteur.

avancement 75 50

06° Modifier le code pour obtenir ceci par exemple :

avancement 25 25

...CORRECTION...

#!/usr/bin/env python

# -*- coding: utf-8 -*-


import matplotlib.pyplot as plt

from matplotlib.widgets import Slider


# Déclaration des coefficients stochiométriques

cs_C4H10 = 2

cs_O2 = 13

cs_CO2 = 8

cs_H2O = 10


# Initialisation des quantités de matières initiales (en mol)

no_C4H10 = 5

no_O2 = 16

no_CO2 = 0

no_H2O = 0


# Calcul de l'avancement maximum

x_max_C4H10 = no_C4H10 / cs_C4H10

x_max_O2 = no_O2 / cs_O2

x_max = min(x_max_O2,x_max_C4H10)


# Création des listes

x = [ 0, x_max ]

n_C4H10 = [ (no_C4H10-cs_C4H10*valeur) for valeur in x]

n_O2 = [ (no_O2 - cs_O2*valeur) for valeur in x]

n_CO2 = [ (no_CO2 + cs_CO2*valeur) for valeur in x]

n_H2O = [ (no_H2O + cs_H2O*valeur) for valeur in x]


# Création des courbes

plt.subplots_adjust(left=0.25, bottom=0.25)

plt.plot(x, n_C4H10, label="C4H10", color='orange')

plt.plot(x, n_O2, label="O2", color='red')

plt.plot(x, n_CO2, label="CO2", color='black')

plt.plot(x, n_H2O, label="H2O", color='blue')


# Réglages axes et grille

liste_des_n_max = [ max(n_C4H10), max(n_O2), max(n_CO2), max(n_H2O) ]

n_max = max(liste_des_n_max)

plt.xlabel("Avancement (mol)")

plt.ylabel("Quantité de matière (mol)")

plt.title("Evolution de la réaction de combustion")

plt.legend(loc='upper center')

plt.axis([0,x_max,0,n_max])

plt.grid()


# Création des Widgets

apparence_reactif_1 = plt.axes([0.25, 0.05, 0.65, 0.03], facecolor='grey')

w_reactif_1 = Slider(apparence_reactif_1, 'no(C4H10)', 0.1, 50.0, valinit=5, valstep=0.1, color='orange')


# Création de l'image

nom_du_fichier = "courbes_avancement_"+str(no_C4H10)+str(no_O2)

plt.savefig(nom_du_fichier+".png")


# Affichage de l'interface

plt.show()

07° Rajouter un deuxième Slider pour règler la quantité de matière initiale de dioxygène :

2 Sliders

...CORRECTION...

#!/usr/bin/env python

# -*- coding: utf-8 -*-


import matplotlib.pyplot as plt

from matplotlib.widgets import Slider


# Déclaration des coefficients stochiométriques

cs_C4H10 = 2

cs_O2 = 13

cs_CO2 = 8

cs_H2O = 10


# Initialisation des quantités de matières initiales (en mol)

no_C4H10 = 5

no_O2 = 16

no_CO2 = 0

no_H2O = 0


# Calcul de l'avancement maximum

x_max_C4H10 = no_C4H10 / cs_C4H10

x_max_O2 = no_O2 / cs_O2

x_max = min(x_max_O2,x_max_C4H10)


# Création des listes

x = [ 0, x_max ]

n_C4H10 = [ (no_C4H10-cs_C4H10*valeur) for valeur in x]

n_O2 = [ (no_O2 - cs_O2*valeur) for valeur in x]

n_CO2 = [ (no_CO2 + cs_CO2*valeur) for valeur in x]

n_H2O = [ (no_H2O + cs_H2O*valeur) for valeur in x]


# Création des courbes

plt.subplots_adjust(left=0.25, bottom=0.25)

plt.plot(x, n_C4H10, label="C4H10", color='orange')

plt.plot(x, n_O2, label="O2", color='red')

plt.plot(x, n_CO2, label="CO2", color='black')

plt.plot(x, n_H2O, label="H2O", color='blue')


# Réglages axes et grille

liste_des_n_max = [ max(n_C4H10), max(n_O2), max(n_CO2), max(n_H2O) ]

n_max = max(liste_des_n_max)

plt.xlabel("Avancement (mol)")

plt.ylabel("Quantité de matière (mol)")

plt.title("Evolution de la réaction de combustion")

plt.legend(loc='upper center')

plt.axis([0,x_max,0,n_max])

plt.grid()


# Création des Widgets

apparence_reactif_1 = plt.axes([0.25, 0.10, 0.65, 0.03], facecolor='grey')

w_reactif_1 = Slider(apparence_reactif_1, 'no(C4H10)', 0.1, 50.0, valinit=5, valstep=0.1, color='orange')

apparence_reactif_2 = plt.axes([0.25, 0.05, 0.65, 0.03], facecolor='grey')

w_reactif_2 = Slider(apparence_reactif_2, 'no(O2)', 0.1, 50.0, valinit=16, valstep=0.1, color='red')


# Création de l'image

nom_du_fichier = "courbes_avancement_"+str(no_C4H10)+str(no_O2)

plt.savefig(nom_du_fichier+".png")


# Affichage de l'interface

plt.show()

3 - Code couleur

Cette partie n'a rien à voir avec Python mais avec le code couleur de ce site.

A partir de maintenant, nous allons utiliser la couleur ORANGE pour représenter les variables qui vont référence à un widget ou à les zones graphiques.

#!/usr/bin/env python

# -*- coding: utf-8 -*-


import matplotlib.pyplot as plt

from matplotlib.widgets import Slider


# Déclaration des coefficients stochiométriques

cs_C4H10 = 2

cs_O2 = 13

cs_CO2 = 8

cs_H2O = 10


# Initialisation des quantités de matières initiales (en mol)

no_C4H10 = 5

no_O2 = 16

no_CO2 = 0

no_H2O = 0


# Calcul de l'avancement maximum

x_max_C4H10 = no_C4H10 / cs_C4H10

x_max_O2 = no_O2 / cs_O2

x_max = min(x_max_O2,x_max_C4H10)


# Création des listes

x = [ 0, x_max ]

n_C4H10 = [ (no_C4H10-cs_C4H10*valeur) for valeur in x]

n_O2 = [ (no_O2 - cs_O2*valeur) for valeur in x]

n_CO2 = [ (no_CO2 + cs_CO2*valeur) for valeur in x]

n_H2O = [ (no_H2O + cs_H2O*valeur) for valeur in x]


# Création des courbes

plt.subplots_adjust(left=0.25, bottom=0.25)

plt.plot(x, n_C4H10, label="C4H10", color='orange')

plt.plot(x, n_O2, label="O2", color='red')

plt.plot(x, n_CO2, label="CO2", color='black')

plt.plot(x, n_H2O, label="H2O", color='blue')


# Réglages axes et grille

liste_des_n_max = [ max(n_C4H10), max(n_O2), max(n_CO2), max(n_H2O) ]

n_max = max(liste_des_n_max)

plt.xlabel("Avancement (mol)")

plt.ylabel("Quantité de matière (mol)")

plt.title("Evolution de la réaction de combustion")

plt.legend(loc='upper center')

plt.axis([0,x_max,0,n_max])

plt.grid()


# Création des Widgets

apparence_reactif_1 = plt.axes([0.25, 0.10, 0.65, 0.03], facecolor='grey')

w_reactif_1 = Slider(apparence_reactif_1, 'no(C4H10)', 0.1, 50.0, valinit=5, valstep=0.1, color='orange')

apparence_reactif_2 = plt.axes([0.25, 0.05, 0.65, 0.03], facecolor='grey')

w_reactif_2 = Slider(apparence_reactif_2, 'no(O2)', 0.1, 50.0, valinit=16, valstep=0.1, color='red')


# Création de l'image

nom_du_fichier = "courbes_avancement_"+str(no_C4H10)+str(no_O2)

plt.savefig(nom_du_fichier+".png")


# Affichage de l'interface

plt.show()

4 - Autres widgets

Pour obtenir plus d'informations, il va falloir aller chercher dans la documentation de matplotlib: Lien vers la documentation sur les widgets

Et si vous voulez des exemples de code : Lien vers les exemples sur les widgets

Vous pourrez y trouver par exemple des boutons, des zones à cocher (checkbox) ou même des zones où les utilsateurs pourront taper du texte.

Dans la prochaine activité, nous verrons comment utiliser nos widgets pour agir sur les courbes. Cela va nécessiter de parler des fonctions.