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é """ Kf = 10 ** float(constante[cation]) 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 """ cation = valeur_actuelle.get() 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 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 """ Interface tkinter """ interface = tk.Tk() # fenêtre du programme interface.title('Complexométrie') # 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) # entrées d2 = tk.Entry(interface) d3 = tk.Entry(interface) d4 = tk.Entry(interface) d5 = tk.Entry(interface) # positions des entrées ions_metalliques.grid(row=0, column=1) 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("", 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