Infoforall

Python-Physique 05 : Fonctions

Nous allons voir un aspect fondamental de la programmation : la possibilité d'insérer des mini-programmes dans les programmes de façon à créer de nouvelles actions. Vous avez déjà utilisé de nombreuses fois les fonctions. Lesquelles ? print est une fonction, input est une fonction, le plot de matplotlib est une fonction, max et min sont des fonctions.

Mais nous allons voir aujourd'hui comment créer vos propres fonctions si l'action que vous voulez effectuer n'est pas gérée par l'une des innombrables fonctions ou méthodes de Python.

Sur ce site, les fonctions sont toujours en rouge. Sauf les fonctions natives de Python comme le print, le input, le max, le min ... Ceci pour éviter de surcharger le code avec une multitude de texte en rouge.

Nous pourrons ainsi réaliser des interfaces automatiquement modifiables :

Ou alors sans changement automatique d'échelles :

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 - Premieres fonctions créées : les procédures

Une fonction est un ensemble d'instructions qui seront effectuées lorsqu'on va appeler la fonction. Aucune ligne de code de la fonction ne sera exécutée tant que la fonction n'aura pas été rendue active par son appel. Un petit exemple pour la forme. Il permet d'afficher les caractéristiques d'un personnage :

#!/usr/bin/env python

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


# - - - - - - - - - - - - - - - - - -

# Déclarations des fonctions

# - - - - - - - - - - - - - - - - - -

def test_fonction() :

    print(" - a Effectuer les réglages sur le système pour obtenir la fréquence précédente.")

    print(" - b Modifier l'affichage pour visualiser un motif dans son intégralité")

    print(" - c Mesurer la période T")

    print(" - d Calculer la fréquence à partir de T.")


# - - - - - - - - - - - - - - - - - -

# Corps du programme

# - - - - - - - - - - - - - - - - - -

print("Mesures de période et fréquence sur un signal sonore")

print("1 - Nous allons travailler avec une fréquence de 500 Hz")

test_fonction()


print("2 - Aurait-on obtenu la bonne fréquence f si nous avions laissé la valeur de la période T est ms ?")

print("3 - Nous allons travailler avec une fréquence de 1000 Hz")

test_fonction()


print("4 - Ranger le matériel et la paillasse.")

input("Appuyez sur ENTREE pour quitter")

CLIQUEZ ICI POUR VOIR L'ORDRE DES INSTRUCTIONS EXECUTEES :

01° Tester le bouton puis exécuter réellement le code. Que constatez-vous de nouveau avec d'aller plus loin ?

...CORRECTION...

On constate que les print au dessus du corps du programme (ceux avec une lettre comme print("- a ...") ne s'affichent pas dès le début.

Les lignes de code sous def test_fonction() : ne sont exécutées que si on tape test_fonction() dans le programme principal !

En plus, on constate qu'on peut exécuter plusieurs fois le code en tapant plusieurs fois test_fonction() et contrairement à une boucle, les exécutions ne se font pas nécessairement à la chaîne les unes des autres.

Et voici l'explication de ce programme ligne par ligne :

Les deux premières lignes sont habituelles : on précise qu'on travaille en Python puis on précise l'encodage des caractères du fichier-texte .py en lui-même.

#!/usr/bin/env python

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

Nous arrivons ensuite à une chose totalement nouvelle : la zone de déclaration des fonctions. On commence ici par la ligne def test_fonction(): où tous les éléments sont importants :

  1. def test_fonction(): : On commence par le mot-clé def qui indique qu'on va donner le nom d'une fonction.
  2. def test_fonction(): : On place un espace entre def et le nom.
  3. def test_fonction(): : On donne le nom de la fonction, test_fonction ici, suivi de parenthèses ().
  4. def test_fonction(): : On finit la déclaration du nom par les deux points : pour signaler que la suite va être composée des actions à effectuer (c'est le même principe qu'avec le if, le while, le for...)
  5. On tabule pour indiquer que l'instruction est incluse dans la fonction.
  6. La fin des instructions est indiquée par le fait qu'on revienne au même niveau que le texte def ... du départ.
  7. def test_fonction() :

        print(" - a Effectuer les réglages sur le système pour obtenir la fréquence précédente.")

        print(" - b Modifier l'affichage pour visualiser un motif dans son intégralité")

        print(" - c Mesurer la période T")

        print(" - d Calculer la fréquence à partir de T.")

    <-- A partir d'ici, l'interpréteur comprend qu'il ne s'agit plus de test_fonction()

Le résultat attendu :

Mesures de période et fréquence sur un signal sonore

1 - Nous allons travailler avec une fréquence de 500 Hz

- a Effectuer les réglages sur le système pour obtenir la fréquence précédente.

- b Modifier l'affichage pour visualiser un motif dans son intégralité

- c Mesurer la période T

- d Calculer la fréquence à partir de T.

2 - Aurait-on obtenu la bonne fréquence f si nous avions laissé la valeur de la période T est ms ?

3 - Nous allons travailler avec une fréquence de 1000 Hz

- a Effectuer les réglages sur le système pour obtenir la fréquence précédente.

- b Modifier l'affichage pour visualiser un motif dans son intégralité

- c Mesurer la période T

- d Calculer la fréquence à partir de T.

4 - Ranger le matériel et la paillasse.

Appuyez sur ENTREE pour quitter

Point important : lorsque vous voulez utiliser la fonction, il ne faut pas juste donner son nom : il faut rajouter les parenthèses après son nom. Il faut donc utiliser test_fonction().

On peut donc utiliser de telles fonctions pour effectuer des tâches répétitives (et exactement identiques) qu'on devrait taper sinon à plusieurs endroits dans le code. On notera que si on devait les faire à la suite directe les unes des autres, les boucles FOR ou WHILE conviennent également.

Et si on veut faire des choses un peu différentes ? On peut aussi ?

Oui, on peut transmettre des arguments (des données) aux fonctions de façon à ce qu'elles utilisent ces contenus venant de l'extérieur. Lors de la déclaration de la fonction, il suffit de placer entre les parenthèses les variables nommées paramètres qui devront récupérer les arguments envoyés.

def test_fonction(question, f, unite) :

    print(" - ", question, "a Régler le système pour obtenir une fréquence de ", f, " ", unite, ".")

    print(" - ", question, "b Modifier l'affichage pour visualiser un motif dans son intégralité")

    print(" - ", question, "c Mesurer la période T")

    print(" - ", question, "d Calculer la fréquence à partir de T.")

On devra maintenant fournir les données lors de l'appel de test_fonction : on doit lui transmettre le numéro de la question, la fréquence voulue et l'unité choisie sur la valeur transmise précédente. Pour faire appel à la fonction dans le programme, il faut taper par exemple :

test_fonction(1, 500, "Hz")

02° Modifier test_fonction comme ci-dessus et en faire l'appel deux fois avec des arguments (valeurs transmises) différents. Par exemple :

test_fonction(1, 500, "Hz")

test_fonction(3, 1.00, "kHz")

...CORRECTION...

#!/usr/bin/env python

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


# - - - - - - - - - - - - - - - - - -

# Déclarations des fonctions

# - - - - - - - - - - - - - - - - - -

def test_fonction(question, f, unite) :

    print(" - ", question, "a Régler le système pour obtenir une fréquence de ", f, " ", unite, ".")

    print(" - ", question, "b Modifier l'affichage pour visualiser un motif dans son intégralité")

    print(" - ", question, "c Mesurer la période T")

    print(" - ", question, "d Calculer la fréquence à partir de T.")


# - - - - - - - - - - - - - - - - - -

# Corps du programme

# - - - - - - - - - - - - - - - - - -

print("Mesures de période et fréquence sur un signal sonore")

print("1 - Nous allons travailler avec une fréquence de 500 Hz")

test_fonction(1, 500, "Hz")


print("2 - Aurait-on obtenu la bonne fréquence f si nous avions laissé la valeur de la période T est ms ?")

print("3 - Nous allons travailler avec une fréquence de 1000 Hz")

test_fonction(3, 1.00, "kHz")


print("4 - Ranger le matériel et la paillasse.")

input("Appuyez sur ENTREE pour quitter")

Alors, ça fonctionne comment ?

C'est relativement simple : lors du premier appel, on envoie les valeurs suivantes dans l'ordre :

def test_fonction(question, f, unite) :

test_fonction(1, 500, "Hz")

  1. L'argument 1 va donc être relié au paramètre de réception question.
  2. L'argument 500 va donc être relié au paramètre de réception f.
  3. L'argument "Hz" va donc être relié au paramètre de réception unite.

Vous avez ci-dessous une animation permettant de voir l'affectation successives des 3 paramètres (les 3 boites bleues) :

CLIQUEZ ICI POUR VOIR L'ORDRE DES INSTRUCTIONS EXECUTEES :

question :

f :

unite :

#!/usr/bin/env python

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


# - - - - - - - - - - - - - - - - - -

# Déclarations des fonctions

# - - - - - - - - - - - - - - - - - -

def test_fonction(question, f, unite) :

    print(" - ", question, "a Régler le système pour obtenir une fréquence de ", f, " ", unite, ".")

    print(" - ", question, "b Modifier l'affichage pour visualiser un motif dans son intégralité")

    print(" - ", question, "c Mesurer la période T")

    print(" - ", question, "d Calculer la fréquence à partir de T.")


# - - - - - - - - - - - - - - - - - -

# Corps du programme

# - - - - - - - - - - - - - - - - - -

print("Mesures de période et fréquence sur un signal sonore")

test_fonction(1, 500, "Hz")

test_fonction(3, 1.00, "kHz")

input("Appuyez sur ENTREE pour quitter")

C'est donc comme si on avait tapé en début de fonction :

question = 1

f = 500

unite = "Hz"

Puis, ceci lors du deuxième appel :

question = 3

f = 1.00

unite = "kHz"

L'intérêt, c'est que cette affectation va se faire automatiquement en fonction des arguments fournis lors de l'apppel de la fonction.

Il existe une façon plus claire de réaliser des affichages mélant string et variables : la méthode format qu'on doit appliquer sur un string à l'aide d'un point :

    print(" - {0}a Régler le système pour obtenir une fréquence de {1} {2}.".format(question, f, unite))

Comme vous le voyez, on tape simplement le string qu'on veut voir s'afficher et on place des accolades contenant des numéros d'index aux endroits où on veut voir s'afficher l'une des variables :

03° Modifier la fonction pour utiliser la méthode .format sur les 4 prints.

...CORRECTION...

#!/usr/bin/env python

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


# - - - - - - - - - - - - - - - - - -

# Déclarations des fonctions

# - - - - - - - - - - - - - - - - - -

def test_fonction(question, f, unite) :

    print(" - {0}a Régler le système pour obtenir une fréquence de {1} {2}.".format(question, f, unite))

    print(" - {0}b Modifier l'affichage pour visualiser un motif dans son intégralité".format(question))

    print(" - {0}c Mesurer la période T".format(question))

    print(" - {0}d Calculer la fréquence à partir de T.".format(question))


# - - - - - - - - - - - - - - - - - -

# Corps du programme

# - - - - - - - - - - - - - - - - - -

print("Mesures de période et fréquence sur un signal sonore")

test_fonction(1, 500, "Hz")

test_fonction(3, 1.00, "kHz")

input("Appuyez sur ENTREE pour quitter")

04° Créer une fonction fois_trois(x) qui possède un seul paramètre et qui effectue print(3*x) avec la donnée(argument) fournie. Dans le corps du programme, en faire l'appel en donnant 10 puis "10" en paramètre.

Vous devriez voir que cette fois, votre fonction accepte plusieurs types de paramètres.

Voilà l'affichage que vous devriez obtenir :

30

101010

...CORRECTION...

#!/usr/bin/env python

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


# - - - - - - - - - - - - - - - - - -

# Déclarations des fonctions

# - - - - - - - - - - - - - - - - - -

def fois_trois(x): # x est un paramètre

    print(3*x)


# - - - - - - - - - - - - - - - - - -

# Corps du programme

# - - - - - - - - - - - - - - - - - -

fois_trois(10) # 10 est un argument

fois_trois("10") # "10" est un argument

input("Appuyer sur ENTREE pour quitter")

05° On veut créer une fonction déclarée ainsi def affine(a,x,b): qui affiche le calcul suivant a*x+b à l'aide d'un print. Créer la fonction. En faire l'appel en utilisant affine(2,10,4). Que vaut a pendant l'appel ? x ? b ? Vous devriez obtenir et afficher 24 puisque 2*10+4 = 24.

...CORRECTION...

#!/usr/bin/env python

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


# - - - - - - - - - - - - - - - - - -

# Déclarations des fonctions

# - - - - - - - - - - - - - - - - - -

def affine(a,x,b): # a,x et b sont les paramètres

    print(a*x+b)


# - - - - - - - - - - - - - - - - - -

# Corps du programme

# - - - - - - - - - - - - - - - - - -

affine(2,10,4) # 2, 10 et 4 sont des arguments

input("Appuyer sur ENTREE pour quitter")

06° Modifier le programme principal (mais pas votre définition de fonction), pour afficher les valeurs de f(x)= 2x+4 pour x variant de 1 à 3.

...CORRECTION...

#!/usr/bin/env python

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


# - - - - - - - - - - - - - - - - - -

# Déclarations des fonctions

# - - - - - - - - - - - - - - - - - -

def affine(a,x,b): # a,x et b sont les paramètres

    print(a*x+b)


# - - - - - - - - - - - - - - - - - -

# Corps du programme

# - - - - - - - - - - - - - - - - - -

affine(2,1,4) # 2, 1 et 4 sont des arguments

affine(2,2,4) # 2, 2 et 4 sont des arguments

affine(2,3,4) # 2, 3 et 4 sont des arguments

07° Idem avec x variant de 1 à 100. Une boucle FOR sera nécessaire cette fois.

...CORRECTION...

#!/usr/bin/env python

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


# - - - - - - - - - - - - - - - - - -

# Déclarations des fonctions

# - - - - - - - - - - - - - - - - - -

def affine(a,x,b): # a,x et b sont les paramètres

    print(a*x+b)


# - - - - - - - - - - - - - - - - - -

# Corps du programme

# - - - - - - - - - - - - - - - - - -

for x in range(1,101) :

    affine(2,x,4) # 2, x et 4 sont des arguments

2 - Fonctions avec retour d'information

Pour renvoyer ce que pointe une variable reponse dans la partie de code qui a fait appel à la fonction, il suffit d'utiliser le code suivant return (reponse) si reponse = a*x+b. Mais on pourrait même faire plus court en utilisant directement return(a*x+b)

#!/usr/bin/env python

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


# - - - - - - - - - - - - - - - - - -

# Déclarations des fonctions #

# - - - - - - - - - - - - - - - - - -

def affine(a,x,b) :

    y = a*x+b

    return(y)


# - - - - - - - - - - - - - - - - - -

# Corps du programme #

# - - - - - - - - - - - - - - - - - -

f1 = affine(2,10,4)

print(f1)

input("Appuyer sur ENTREE pour quitter")

Comme vous le voyez, on stocke le résultat de la fonction (qu'elle renvoie avec le return) dans une variable f1 pour garder le résultat en mémoire.

CLIQUEZ ICI POUR VOIR LE CONTENU DES VARIABLES :

Variables y, a, x et y de la fonction :

y :

a :

x :

b :

Variable f du programme principal :

f1 :

08° Avant d'utiliser le code, tentez de découvrir ce que doit afficher normalement le code print(f1).

...CORRECTION...

On voit qu'on veut stocker dans f1 le résultat de la fonction affine(2,10,4).

Lors de l'appel, on a donc a = 2, x = 10 et b = 4.

La fonction va alors stocker dans y le résultat du calcul 2*10+4, soit 24.

Le return fait référence à y et, de retour dans le programme principal, f1 contiendra 24.

ATTENTION : lorsque la fonction rencontre return, elle calcule le résultat et sort ensuite du codage de la fonction : le reste du code ne sera pas analysé.

On peut également donner le contenu d'une variable comme argument : vous n'êtes pas obligé de donner une valeur en "dur" ce qui est très pratique.

09° Et ce code, que renvoie-t-il ? Faire le calcul à la main puis lancer le programme pour vérifier.

#!/usr/bin/env python

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


# - - - - - - - - - - - - - - - - - -

# Déclarations des fonctions #

# - - - - - - - - - - - - - - - - - -

def affine(a,x,b) :

    y = a*x+b

    return(y)


# - - - - - - - - - - - - - - - - - -

# Corps du programme #

# - - - - - - - - - - - - - - - - - -

f1 = affine(2,3,4)

f2 = affine(2,f1,4)

print(f1)

print(f2)

input("Appuyer sur ENTREE pour quitter")

...CORRECTION...

f1 va contenir le retour de la fonction affine(2,3,4), donc 2*3+4, soit 10.


f2 va contenir le retour de la fonction affine(2,f1,4)

Comme la variable f1 fait référence à l'integer 10, le paramètre x de la fonction affine va recevoir 10.

On calcule donc 2*10+4, soit 24.

Utilité des fonctions avec matplotlib

A partir d'ici, nous sommes donc capables d'afficher n'importe quel résultat issu d'une formule : il suffit de créer une liste dans laquelle on utilise la bonne fonction pour effectuer le calcul. La structure fournie ci-dessous est la structure basique de tout programme de ce type :

Voici ici l'exemple d'une mise au carré :

graphique du carré

import matplotlib.pyplot as plt


# - - - - - - - - - - - - - - - - - -

# Déclarations des fonctions #

# - - - - - - - - - - - - - - - - - -

def f(a) :

    resultat = a**2

    return(resultat)


# - - - - - - - - - - - - - - - - - -

# Corps du programme #

# - - - - - - - - - - - - - - - - - -

x = [ valeur for valeur in range(-100,100) ]

y = [ f(valeur) for valeur in x ]


plt.plot(x, y)

plt.grid()


plt.savefig("essai.png")

plt.show()

On notera que j'ai utilisé ici des variables différentes pour le corps du programme et la fonction. Cela permet d'éviter une certaine confusion. Mais nous verrons plus tard que nous aurions pu écrire tout simplement ceci :

import matplotlib.pyplot as plt


# - - - - - - - - - - - - - - - - - -

# Déclarations des fonctions #

# - - - - - - - - - - - - - - - - - -

def f(x) :

    resultat = x**2

    return(resultat)


# - - - - - - - - - - - - - - - - - -

# Corps du programme #

# - - - - - - - - - - - - - - - - - -

x = [ valeur for valeur in range(-100,100) ]

y = [ f(valeur) for valeur in x ]


plt.plot(x, y)

plt.grid()


plt.savefig("essai.png")

plt.show()

Néanmoins, autant éviter d'écrire écrire un programme de ce type pour l'expliquer nécessite justement d'expliquer des notions supplémentaires. Autant utiliser des programmes utilisant des noms de variables différents.

3 - Fonction-événement

Voyons maintenant comment utiliser cela dans matplotlib.

La gestion des événements n'est pas dans les attendus des élèves. Mais cela vous permettra de leur créer des graphiques interactifs permettant d'expliquer bien des choses ou de créer des exercices interactifs.

Nous en étions arrivés à cette interface :

2 Sliders

#!/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()

J'aimerai maitenant faire les calculs précédents dans des fonctions. Pourquoi ? Tout simplement parce que je vais ensuite gérer la courbe en temps réel dès qu'on touchera aux curseurs. Il faudra donc refaire les calculs. Autant créer des fonctions plutôt que de tout retaper.

10° Créer la fonction avancement_max qu'on pourra utiliser de cette facon :

x_max = avancement_max(no_C4H10, no_O2)

Voici une correction possible. J'ai rajouté des commentaires sur plusieurs lignes servant de documentation dans les fonctions.

#!/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


# - - - - - - - - - - - - - - - - - -

# Déclarations des fonctions

# - - - - - - - - - - - - - - - - - -

def avancement_max(no_C4H10, no_O2) :

    """ Fonction qui renvoie l'avancement maximale de la réaction

    Les paramètres attendus sont

    * la quantité de matière initiale de C4H10,

    * la quantité de matière initiale de O2"""

    x_max_C4H10 = no_C4H10 / cs_C4H10

    x_max_O2 = no_O2 / cs_O2

    x_max = min(x_max_O2,x_max_C4H10)

    return(x_max)


# - - - - - - - - - - - - - - - - - -

# Corps du programme

# - - - - - - - - - - - - - - - - - -


# Calcul de l'avancement maximum

x_max = avancement_max(no_C4H10, no_O2)


# 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()

Nous allons maintenant faire de la programmation événementielle : nous allons permettre au programme d'activer une fonction lorsqu'on touche à l'un des deux curseurs avec les deux lignes suivantes :

w_reactif_1.on_changed(changerCourbes)

w_reactif_2.on_changed(changerCourbes)

Ici, on va donc aller chercher la référence du widget (w_reactif_1 ou w_reactif_2), appliquer la méthode on_changed. Cette méthode va lancer une surveillance du widget. A chaque changement de valeur, l'interpréteur va alors lancer automatiquement la fonction qu'on fournit en argument. Ici, il s'agit de la fonction changerCourbesdans les deux cas.

Cette fonction est un peu particulière : comme il s'agit d'une fonction liée à un événement, nous sommes obligés de reception un ensemble d'informations liées à l'événement (localisation de la souris, touche clavier activée ...). On nomme souvent even la variable qui récupère ces informations :

def changerCourbes(event) :

    """ Fonction-événement qui s'active lorsqu'un des deux Sliders est modifié"""

    no_C4H10 = w_reactif_1.val

    no_O2 = w_reactif_2.val

    print(no_C4H10)

11° Quelle instruction permet visiblement d'aller chercher la valeur actualisée des quantités de matière initiales ?

...CORRECTION...

Il faut simplement donner la référence du widget et aller chercher la valeur avec l'attribut val.

12° Lancer le programme suivant qui permet d'afficher dans la console la valeur du Slider 1 lorsqu'on le modifie.

#!/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


# - - - - - - - - - - - - - - - - - -

# Déclarations des fonctions

# - - - - - - - - - - - - - - - - - -

def avancement_max(no_C4H10, no_O2) :

    """ Fonction qui renvoie l'avancement maximale de la réaction

    Les paramètres attendus sont

    * la quantité de matière initiale de C4H10,

    * la quantité de matière initiale de O2"""

    x_max_C4H10 = no_C4H10 / cs_C4H10

    x_max_O2 = no_O2 / cs_O2

    x_max = min(x_max_O2,x_max_C4H10)

    return(x_max)


def changerCourbes(event) :

    """ Fonction-événement qui s'active lorsqu'un des deux Sliders est modifié"""

    no_C4H10 = w_reactif_1.val

    no_O2 = w_reactif_2.val

    print(no_C4H10)


# - - - - - - - - - - - - - - - - - -

# Corps du programme

# - - - - - - - - - - - - - - - - - -


# Calcul de l'avancement maximum

x_max = avancement_max(no_C4H10, no_O2)


# 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')


# Surveillance de deux événements

w_reactif_1.on_changed(changerCourbes)

w_reactif_2.on_changed(changerCourbes)


# 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 - Modifier les courbes

Comment modifier les courbes une fois créées ? Et bien pour commencer, il faut avoir stocker leurs références (leurs 'adresses-mémoires'). Sans cela, il est difficile de faire grand chose. Nous allons donc devoir modifier les 4 lignes avec lesquelles nous avons créé les 4 graphiques :

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')

Or, on peut lire dans la documentation que la fonction plot renvoie la référence d'un objet qu'on va pouvoir mémoriser. Subtilité à connaitre néanmoins : il faut enregistrer un tuple et nous allons devoir rajouter une virgule de façon à forcer Python à créer un tuple et pas une liste.

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

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

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

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

Ou même de façon plus claire :

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

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

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

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

13° Utiliser le code ci-dessous. Les courbes sont-elles automatiquement modifiées lorsqu'on crée de nouvelles listes qui portent le même nom que les anciennes ? Pensez à regarder la console : on y affiche l'identifiant de la liste n_C4H10 créée dans le programme principal et l'identifiant de la liste n_C4H10 créée dans la fonction.

#!/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


# - - - - - - - - - - - - - - - - - -

# Déclarations des fonctions

# - - - - - - - - - - - - - - - - - -

def avancement_max(no_C4H10, no_O2) :

    """ Fonction qui renvoie l'avancement maximale de la réaction

    Les paramètres attendus sont

    * la quantité de matière initiale de C4H10,

    * la quantité de matière initiale de O2"""

    x_max_C4H10 = no_C4H10 / cs_C4H10

    x_max_O2 = no_O2 / cs_O2

    x_max = min(x_max_O2,x_max_C4H10)

    return(x_max)


def changerCourbes(event) :

    """ Fonction-événement qui s'active lorsqu'un des deux Sliders est modifié"""


    # Récupération des nouvelles quantités de matière initiales

    no_C4H10 = w_reactif_1.val

    no_O2 = w_reactif_2.val


    # Recherche de l'avancement maximum

    x_max = avancement_max(no_C4H10, no_O2)


    # Création des nouvelles 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]

    print(id(n_C4H10))


# - - - - - - - - - - - - - - - - - -

# Corps du programme

# - - - - - - - - - - - - - - - - - -


# Calcul de l'avancement maximum

x_max = avancement_max(no_C4H10, no_O2)


# Création des listes

x = [ 0, x_max ]

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

print(id(n_C4H10))

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)

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

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

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

(courbe4,) = 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')


# Surveillance de deux événements

w_reactif_1.on_changed(changerCourbes)

w_reactif_2.on_changed(changerCourbes)


# 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()

...CORRECTION...

On constate que la variable n_C4H10 pointe à chaque modifiation vers des identifiants différents. Cela veut dire que le nom n_C4H10 désigne à chaque fois une zone mémoire différente.

Par exemple :

1757095414408 dans le programme principal puis les valeurs suivantes lors des appels successifs de la fonction :

1757095417864, 1757094898888 ...


Les courbes ne changent pas car elles pointent toujours vers l'adresse fournie lors de leur création 1757095414408.

14° Compléter la fonction changerCourbes pour qu'elle gére correctement les variations sur les 4 courbes et sur les deux Sliders. Il faudra utiliser la remarque ci-dessous :

Pour modifier la liste représentant les coordonnées X et Y, il suffit alors d'utiliser une méthode sur cette référence :

courbe1.set_xdata(nouvelle_liste_X)

courbe1.set_ydata(nouvelle_liste_Y)

...CORRECTION...

def changerCourbes(event) :

    """ Fonction-événement qui s'active lorsqu'un des deux Sliders est modifié"""


    # Récupération des nouvelles quantités de matière initiales

    no_C4H10 = w_reactif_1.val

    no_O2 = w_reactif_2.val


    # Recherche de l'avancement maximum

    x_max = avancement_max(no_C4H10, no_O2)


    # Création des nouvelles 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]


    # Modification des listes liées aux courbes

    courbe1.set_xdata(x)

    courbe2.set_xdata(x)

    courbe3.set_xdata(x)

    courbe4.set_xdata(x)

    courbe1.set_ydata(n_C4H10)

    courbe2.set_ydata(n_O2)

    courbe3.set_ydata(n_CO2)

    courbe4.set_ydata(n_H2O)

Le code complet correspond à quelque chose de ce type :

#!/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


# - - - - - - - - - - - - - - - - - -

# Déclarations des fonctions

# - - - - - - - - - - - - - - - - - -

def avancement_max(no_C4H10, no_O2) :

    """ Fonction qui renvoie l'avancement maximale de la réaction

    Les paramètres attendus sont

    * la quantité de matière initiale de C4H10,

    * la quantité de matière initiale de O2"""

    x_max_C4H10 = no_C4H10 / cs_C4H10

    x_max_O2 = no_O2 / cs_O2

    x_max = min(x_max_O2,x_max_C4H10)

    return(x_max)


def changerCourbes(event) :

    """ Fonction-événement qui s'active lorsqu'un des deux Sliders est modifié"""


    # Récupération des nouvelles quantités de matière initiales

    no_C4H10 = w_reactif_1.val

    no_O2 = w_reactif_2.val


    # Recherche de l'avancement maximum

    x_max = avancement_max(no_C4H10, no_O2)


    # Création des nouvelles 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]


    # Modification des listes liées aux courbes

    courbe1.set_xdata(x)

    courbe2.set_xdata(x)

    courbe3.set_xdata(x)

    courbe4.set_xdata(x)

    courbe1.set_ydata(n_C4H10)

    courbe2.set_ydata(n_O2)

    courbe3.set_ydata(n_CO2)

    courbe4.set_ydata(n_H2O)


# - - - - - - - - - - - - - - - - - -

# Corps du programme

# - - - - - - - - - - - - - - - - - -


# Calcul de l'avancement maximum

x_max = avancement_max(no_C4H10, no_O2)


# 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)

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

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

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

(courbe4,) = 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')


# Surveillance de deux événements

w_reactif_1.on_changed(changerCourbes)

w_reactif_2.on_changed(changerCourbes)


# 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()

C'est sympa mais les échelles ne sont pas du tout modifiées. Du coup, on doit utiliser l'outil déplacement (l'icône avec les 4 directions) ou le zoom (l'icône loupe) pour parvenir à afficher quelque chose de compréhensible.

5 - Modification de l'échelle

Pour gérer les échelles, nous allons devoir aller encore plus loin dans l'enregistrement des références : nous allons avoir besoin de mémoriser la référence du graphique en plus des références courbe1 ... des courbes sur votre graphique.

Voici comment.

AVANT : nous avions ceci :

# Création des courbes

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

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

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

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

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

En gros, on trace à chaque fois des choses sur la fenêtre actuelle. Nous allons maintenant mémoriser dans fig l'identifiant de la fenêtre-figure en cours et dans ax l'identifiant du graphique que nous venons de créer.

APRES, on obtient donc ceci :

# Création des courbes

fig, ax = plt.subplots() # NOUVEAU : on enregistre les références de la figure et du graphique

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

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

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

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

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

15° Intégrer les modifications dans votre code.

...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


# - - - - - - - - - - - - - - - - - -

# Déclarations des fonctions

# - - - - - - - - - - - - - - - - - -

def avancement_max(no_C4H10, no_O2) :

    """ Fonction qui renvoie l'avancement maximale de la réaction

    Les paramètres attendus sont

    * la quantité de matière initiale de C4H10,

    * la quantité de matière initiale de O2"""

    x_max_C4H10 = no_C4H10 / cs_C4H10

    x_max_O2 = no_O2 / cs_O2

    x_max = min(x_max_O2,x_max_C4H10)

    return(x_max)


def changerCourbes(event) :

    """ Fonction-événement qui s'active lorsqu'un des deux Sliders est modifié"""


    # Récupération des nouvelles quantités de matière initiales

    no_C4H10 = w_reactif_1.val

    no_O2 = w_reactif_2.val


    # Recherche de l'avancement maximum

    x_max = avancement_max(no_C4H10, no_O2)


    # Création des nouvelles 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]


    # Modification des listes liées aux courbes

    courbe1.set_xdata(x)

    courbe2.set_xdata(x)

    courbe3.set_xdata(x)

    courbe4.set_xdata(x)

    courbe1.set_ydata(n_C4H10)

    courbe2.set_ydata(n_O2)

    courbe3.set_ydata(n_CO2)

    courbe4.set_ydata(n_H2O)


# - - - - - - - - - - - - - - - - - -

# Corps du programme

# - - - - - - - - - - - - - - - - - -


# Calcul de l'avancement maximum

x_max = avancement_max(no_C4H10, no_O2)


# 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

fig, ax = plt.subplots() # NOUVEAU : on enregistre les références de la figure et du graphique

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

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

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

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

(courbe4,) = ax.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')


# Surveillance de deux événements

w_reactif_1.on_changed(changerCourbes)

w_reactif_2.on_changed(changerCourbes)


# 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()

Vous ne devriez pas voir de différence par rapport à avant.

Par contre, nous disposons maintenant d'un accés à notre fenêtre, au graphe et aux courbes;

Pour modifier les axes, il suffit maintenant de rajouter ceci sous les modifications des courbes dans la fonction-événement :

def changerCourbes(event) :

    """ Fonction-événement qui s'active lorsqu'un des deux Sliders est modifié"""


    # Récupération des nouvelles quantités de matière initiales

    no_C4H10 = w_reactif_1.val

    no_O2 = w_reactif_2.val


    # Recherche de l'avancement maximum

    x_max = avancement_max(no_C4H10, no_O2)


    # Création des nouvelles 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]


    # Modification des listes liées aux courbes

    courbe1.set_xdata(x)

    courbe2.set_xdata(x)

    courbe3.set_xdata(x)

    courbe4.set_xdata(x)

    courbe1.set_ydata(n_C4H10)

    courbe2.set_ydata(n_O2)

    courbe3.set_ydata(n_CO2)

    courbe4.set_ydata(n_H2O)


    # Modification des échelles

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

    n_max = max(liste_des_n_max)

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

16° Intégrer les dernières modifications dans votre code.

Comme vous le voyez, c'est sympa mais c'est un peu pénible d'avoir un changement permanent d'échelle. Nous pourrions faire bien mieux : nous pourrions utiliser un bouton qui pourrait gérer le changement d'échelles plutôt que de le faire automatiquement.

6 - Widget Button

Nous allons maintenant créer un widget Button qui sera activé par l'utilisation d'une méthode on_clicked.

Voici le code correspondant et quelques questions pour vous permettre de vous l'approprier :

#!/usr/bin/env python

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


import matplotlib.pyplot as plt

from matplotlib.widgets import Slider, Button


# 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


# - - - - - - - - - - - - - - - - - -

# Déclarations des fonctions

# - - - - - - - - - - - - - - - - - -

def avancement_max(no_C4H10, no_O2) :

    """ Fonction qui renvoie l'avancement maximale de la réaction

    Les paramètres attendus sont

    * la quantité de matière initiale de C4H10,

    * la quantité de matière initiale de O2"""

    x_max_C4H10 = no_C4H10 / cs_C4H10

    x_max_O2 = no_O2 / cs_O2

    x_max = min(x_max_O2,x_max_C4H10)

    return(x_max)


def changerCourbes(event) :

    """ Fonction-événement qui s'active lorsqu'un des deux Sliders est modifié"""


    # Récupération des nouvelles quantités de matière initiales

    no_C4H10 = w_reactif_1.val

    no_O2 = w_reactif_2.val


    # Recherche de l'avancement maximum

    x_max = avancement_max(no_C4H10, no_O2)


    # Création des nouvelles 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]


    # Modification des listes liées aux courbes

    courbe1.set_xdata(x)

    courbe2.set_xdata(x)

    courbe3.set_xdata(x)

    courbe4.set_xdata(x)

    courbe1.set_ydata(n_C4H10)

    courbe2.set_ydata(n_O2)

    courbe3.set_ydata(n_CO2)

    courbe4.set_ydata(n_H2O)


    # Modification des échelles

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

    n_max = max(liste_des_n_max)


    return( (x_max,n_max) )


def changerEchelles(event) :

    x_max, n_max = changerCourbes(event)

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


# - - - - - - - - - - - - - - - - - -

# Corps du programme

# - - - - - - - - - - - - - - - - - -


# Calcul de l'avancement maximum

x_max = avancement_max(no_C4H10, no_O2)


# 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

fig, ax = plt.subplots() # NOUVEAU : on enregistre les références de la figure et du graphique

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

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

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

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

(courbe4,) = ax.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')

apparence_bouton = plt.axes([0.01, 0.15, 0.20, 0.06])

w_bouton = Button(apparence_bouton, 'ECHELLES !', color='red')


# Gestionnaire d'événements

w_reactif_1.on_changed(changerCourbes)

w_reactif_2.on_changed(changerCourbes)

w_bouton.on_clicked(changerEchelles)


# 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()

17° Tester le programme pour vérifier qu'il fonctionne. Qu'a-t-on rajouter dans la partie importation ?

...CORRECTION...

On rajoute la méthode constructeur permettant de créer des boutons, Button.

18° Comment se nomme la méthode qui permet de gérer le click sur le bouton ?

...CORRECTION...

Elle se nomme on_clicked et on lui fournit en argument la fonction changerEchelles.

19° Que se passe-t-il lorsqu'on clique sur le bouton ?

...CORRECTION...

On arrive dans la fonction changerEchelles en récupérer les infos sur l'événement.

On active alors la fonction changerCourbes et cela va calculer deux valeurs qu'on renvoie via le return. On les récupére et on change l'échelle à l'aide de l'instruction ax.axis.

7 - Importation de modules de fonctions

Nous avons déjà vu de nombreuses bibliothèques (ou modules). Par les importer, il suffit d'utiliser l'instruction import :

Pour voir toutes les fonctions de la bibliothèque math : DOC PYTHON

Bon, alors autant toujours utiliser, from XXX import * si cela évite d'écrire un truc en plus à chaque fois qu'on utilise une fonction ?

Non. Si vous n'importez qu'un seul module, vous pouvez faire un import total et complet, oui.

Mais si vous faites cela avec deux, trois ou quatre modules, les chances sont grandes qu'ils possèdent des fonctions qui portent le même nom. Et là, bonne chance pour retrouver la bonne !

A titre d'exemple, voici un code qui vous permet d'estimer le temps nécessaire à un ensemble de calcul.

Il intègre le module time et utilise la fonction time qui va permettre d'obtenir le temps écoulé depuis le temps zéro de l'ordinateur (certainement le 1er janvier 1970 à 00h00). Il suffit de stocker le temps initial au commencement d'un calcul et le temps final après calcul et nous aurons la durée du calcul.

#!/usr/bin/env python

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

from time import time


# - - - - - - - - - - - - - - - - - -

# Déclarations des fonctions #

# - - - - - - - - - - - - - - - - - -

def foisDeux(entree) :

    reponse = entree*2

    return(reponse)


# - - - - - - - - - - - - - - - - - -

# Corps du programme #

# - - - - - - - - - - - - - - - - - -


valeur = float(input("Valeur initiale : "))

t0 = time() # On stocke le temps initial avant les différents calculs


for i in range(101) :

    print("Le terme de rang ",i," = ",valeur)

    valeur = foisDeux(valeur)


tf = time() # On stocke le temps final après les différents calculs

duree = tf-t0


print("Temps écoulé lors du calcul : ",duree," s")

input("Appuyer sur ENTREE")

20° Tester le programme ci-dessus. Faire varier le nombre de calculs pour voir l'évolution du temps de calculs avec le nombre de calculs. Vous devriez parvenir à faire cela sans savoir ce que fait exactement la fonction time, c'est l'avantage de tout ceci : on importe et on utilise.