2023-05-21 20:00:59 +02:00
from math import log10 , prod , sqrt # calculs mathematiques
from matplotlib . figure import Figure # mise en graphique matplotlib dans Tkinter
from matplotlib . backends . backend_tkagg import FigureCanvasTkAgg
import csv # recherche des informations dans une mini base de donnees
from chempy import chemistry # affichage d'une reaction chimique
import tkinter as tk # interface du programme
from tkinter import ttk
def reaction ( cation ) :
"""
Génération de la réaction de complexation
: cation : cation metallique complexé
"""
complexation = chemistry . Reaction ( { ' Y4- ' : 1 , cation : 1 } , { complexes [ cation ] : 1 } ) # reaction
return complexation
def constante_formation ( cation ) :
"""
Calcul de la constante de formation du complexe
: cation : cation metallique complexé
"""
2023-05-21 21:13:38 +02:00
Kf = 10 * * float ( constante [ cation ] )
2023-05-21 20:00:59 +02:00
return Kf
def coeff_distri_ligand ( pH ) :
"""
Calcul du coefficient de distribution alpha ligand
: pH : acidite de la solution titree
"""
Ka_EDTA = [ 10 * * - 2 , 10 * * - 2.7 , 10 * * - 6.16 , 10 * * - 10.26 ] # liste des constantes d'acidite de l'EDTA (de Ka1 a Ka4)
parametre = 1 # denominateur definition aplha ligand
for i in range ( 1 , len ( Ka_EDTA ) ) :
cste_multiplicative = prod ( Ka_EDTA [ - i : ] )
parametre + = 10 * * ( i * - pH ) / cste_multiplicative # termes en [H3O+] divisee par produits de Ka_EDTA
alpha_ligand = 1 / parametre
return alpha_ligand
def cste_formation_pH ( cation , pH ) :
"""
Calcul de la constante d ' equilibre de formation du complexe [MEDTA] modulee par le pH
: Kf : constante de formation du complexe [ MEDTA ]
: pH : acidite de la solution titree
"""
Kf = constante_formation ( cation )
alpha_ligand = coeff_distri_ligand ( pH )
Kf_pH = Kf * alpha_ligand
return Kf_pH
def volume_eq ( conc_init_metal , V_metal , conc_ligand ) :
"""
Calcul du volume equivalent
: conc_init_metal : concentration initial en solution titree
: V_metal : volume initial de solution titree
: conc_ligand : concentration initial en ligand
"""
volume_eq = ( conc_init_metal * V_metal ) / conc_ligand
return volume_eq
def volume_ligand ( conc_init_metal , V_metal , conc_ligand ) :
"""
Calcul de la gamme de volumes d ' EDTA ajoutés
: conc_init_metal : concentration initiale en solution titrée
: V_metal : volume initial de solution titrée
: conc_ligand : concentration initiale en ligand
"""
V_ligand_actuel = 0 # debut titrage
V_ligand_step = 0.1 #intervalle de volume titrant entre chaque mesure
V_equivalence = volume_eq ( conc_init_metal , V_metal , conc_ligand )
V_ligand_stop = 2 * V_equivalence # fin titrage
V_ligand = [ ] # liste des volumes titrant utilises
nbre_V_ligand = round ( V_ligand_stop , 1 ) * 10 # nombre de mesures
for i in range ( int ( nbre_V_ligand ) + 1 ) : # remplissage liste des volumes titrant utilises
V_ligand . append ( round ( V_ligand_actuel , 1 ) ) # on a claqué le round car les valeurs de i n'étaient pas entieres, et la boucle if ne tombait alors jamais sur le cas de l'équivalence
V_ligand_actuel + = V_ligand_step
return V_ligand
def _logM ( V_ligand , conc_init_metal , V_metal , conc_ligand , cation , pH , Kf ) :
"""
Calcul des valeurs de pM = - logM
: conc_init_metal : concentration initiale en solution titrée
: V_metal : volume initial de solution titrée
: conc_ligand : concentration initiale en ligand
: cation : cation metallique complexé
: pH : acidite de la solution titree
: Kf : constante de formation du complexe [ MEDTA ]
"""
pM = [ ]
V_equivalence = volume_eq ( conc_init_metal , V_metal , conc_ligand )
for i in V_ligand : # calcul titrage
if i < V_equivalence : # avant equivalence
M = conc_metal_anteeq ( conc_init_metal , V_metal , conc_ligand , i )
pM . append ( - log10 ( M ) )
elif i == V_equivalence : # a equivalence
M = conc_metal_eq ( conc_init_metal , V_metal , conc_ligand , i , cste_formation_pH ( cation , pH ) )
tk . Label ( interface , text = " Volume équivalent (mL) = %.2f " % i ) . grid ( row = 9 , columnspan = 2 ) # affichage du volume équivalent
tk . Label ( interface , text = " pM = %.2f " % - log10 ( M ) ) . grid ( row = 10 , columnspan = 2 ) # affichage du pM à l'équivalence
pM . append ( - log10 ( M ) )
elif i > V_equivalence : # apres equivalence jusqu'a 2*volume_eq
M = conc_metal_posteq ( conc_init_metal , V_metal , conc_ligand , i , Kf )
pM . append ( - log10 ( M ) )
return pM
def conc_metal_anteeq ( conc_init_metal , V_metal , conc_ligand , V_ligand ) :
"""
Calcul de la concentration en metal à l ' équilibre avant équivalence en considérant la dilution
: conc_init_metal : concentration initiale en solution titrée
: V_metal : volume initial de solution titrée
: conc_ligand : concentration initiale en ligand
: V_ligand : volume de ligand ( titrant ) ajouté
"""
V_tot = V_metal * 10 * * - 3 + V_ligand * 10 * * - 3
M_1 = ( conc_init_metal * V_metal * 10 * * - 3 - conc_ligand * V_ligand * 10 * * - 3 ) / V_tot
return M_1
def conc_metal_eq ( conc_init_metal , V_metal , conc_ligand , V_ligand , Kf_pH ) :
"""
Calcul de la concentration en métal a l ' équilibre à équivalence en considérant la dilution
: conc_init_metal : concentration initial en solution titree
: V_metal : volume initial de solution titree
: conc_ligand : concentration initial en ligand
: V_ligand : volume de ligand ( titrant ) ajoute
: Kf_pH : constante d ' equilibre de formation du complexe [MEDTA] modulee par le pH
"""
V_tot = V_metal * 10 * * - 3 + V_ligand * 10 * * - 3 # volume total de la solution
M_2 = sqrt ( ( conc_init_metal * V_metal * 10 * * - 3 ) / ( Kf_pH * V_tot ) )
return M_2
def conc_metal_posteq ( conc_init_metal , V_metal , conc_ligand , V_ligand , Kf_pH ) :
"""
Calcul de la concentration en metal a l ' equilibre apres equivalence en considerant la dilution
: conc_init_metal : concentration initial en solution titree
: V_metal : volume initial de solution titree
: conc_ligand : concentration initial en ligand
: V_ligand : volume de ligand ( titrant ) ajoute
: Kf_pH : constante d ' equilibre de formation du complexe [MEDTA] modulee par le pH
"""
M_3 = ( conc_init_metal * V_metal * 10 * * - 3 ) / ( Kf_pH * ( conc_ligand * V_ligand * 10 * * - 3 - conc_init_metal * V_metal * 10 * * - 3 ) )
return M_3
def get_values ( ) :
"""
Récupération des données introduites par l ' utilisateur via l ' interface tkinter et création de la courbe de titrage
"""
2023-05-21 21:13:38 +02:00
cation = valeur_actuelle . get ( )
2023-05-21 20:00:59 +02:00
conc_init_metal = float ( d2 . get ( ) )
conc_ligand = float ( d3 . get ( ) )
V_metal = float ( d4 . get ( ) )
pH = float ( d5 . get ( ) )
courbe = graphique ( cation , conc_init_metal , conc_ligand , V_metal , pH ) # courbe de titrage complexométrique
plot = FigureCanvasTkAgg ( courbe , master = interface )
plot . draw ( )
plot . get_tk_widget ( ) . grid ( row = 12 , columnspan = 2 )
tk . Label ( interface , text = reaction ( cation ) ) . grid ( row = 8 , columnspan = 2 ) # affichage réaction
def graphique ( cation , conc_init_metal , conc_ligand , V_metal , pH ) :
"""
Mise en graphique
: cation : cation metallique complexé
: conc_init_metal : concentration initiale en solution titrée
: conc_ligand : concentration initiale en ligand
: V_metal : volume initial de solution titrée
: pH : acidite de la solution titree
"""
fig = Figure ( figsize = ( 4 , 5 ) ) # affichage du graphique
ax = fig . add_subplot ( ) # génération de graphique
V_ligand = volume_ligand ( conc_init_metal , V_metal , conc_ligand )
Kf = constante_formation ( cation )
# caractéristiques du graphique
ax . plot ( V_ligand , _logM ( V_ligand , conc_init_metal , V_metal , conc_ligand , cation , pH , Kf ) , " tab:purple " )
ax . set_xlabel ( " Volume EDTA ajouté en mL " )
ax . set_ylabel ( " pM " )
ax . set_title ( " Courbe de titrage complexométrique " )
return fig
2023-05-21 21:13:38 +02:00
with open ( ' complexes_MEDTA.txt ' , newline = ' ' ) as csvfile :
liste1 = csv . reader ( csvfile )
complexes = dict ( liste1 ) # dictionnaire reliant cation et complexe cation-EDTA
with open ( ' logKf_EDTA.txt ' , newline = ' ' ) as csvfile :
liste2 = csv . reader ( csvfile )
constante = dict ( liste2 ) # dictionnaire reliant cation et Kf cation-EDTA
2023-05-21 20:00:59 +02:00
""" Interface tkinter """
interface = tk . Tk ( ) # fenêtre du programme
interface . title ( ' Complexométrie ' )
2023-05-21 21:13:38 +02:00
# données d1-5
tk . Label ( interface , text = " Cation métallique titré: " ) . grid ( row = 0 )
tk . Label ( interface , text = " conc. cation (M): " ) . grid ( row = 1 )
tk . Label ( interface , text = " conc. ligand (M): " ) . grid ( row = 2 )
tk . Label ( interface , text = " vol. solution metallique (mL): " ) . grid ( row = 3 )
tk . Label ( interface , text = " pH : " ) . grid ( row = 4 )
# scroll to choose : choix de cation métallique à titrer
choix_d1 = constante . keys ( )
valeur_actuelle = tk . StringVar ( interface )
valeur_actuelle . set ( " Xy+ " )
ions_metalliques = tk . OptionMenu ( interface , valeur_actuelle , * choix_d1 )
2023-05-21 20:00:59 +02:00
2023-05-21 21:13:38 +02:00
# entrées
2023-05-21 20:00:59 +02:00
d2 = tk . Entry ( interface )
d3 = tk . Entry ( interface )
d4 = tk . Entry ( interface )
d5 = tk . Entry ( interface )
2023-05-21 21:13:38 +02:00
# positions des entrées
ions_metalliques . grid ( row = 0 , column = 1 )
2023-05-21 20:00:59 +02:00
d2 . grid ( row = 1 , column = 1 )
d3 . grid ( row = 2 , column = 1 )
d4 . grid ( row = 3 , column = 1 )
d5 . grid ( row = 4 , column = 1 )
submit = ttk . Button ( interface , text = " Titrer " , command = get_values ) # button de lancement
submit . grid ( row = 6 , columnspan = 2 ) # position du button de lancement
interface . bind ( " <Return> " , lambda _ : submit . invoke ( ) ) # enter pour titrer
interface . rowconfigure ( 5 , minsize = 30 ) # espaces vides entre en dessou et au dessus des résultats affichés
interface . rowconfigure ( 7 , minsize = 30 )
interface . rowconfigure ( 11 , minsize = 30 )
interface . mainloop ( ) # fin de l'exécution du programme via tkinter