Dans une application graphique, nous pouvons utiliser un mécanisme intéressant pour permettre à l'usager de supprimer un enregistrement précis : l'attribut iid des lignes affichées dans un TreeView.
Cet attribut doit absolument avoir une valeur unique pour chacune des lignes affichées, sans quoi le programme plantera. Il est donc pertinent d'assigner l'identifiant de l'enregistrement à l'attribut iid.
Le fait d'assigner l'identifiant de l'enregistrement à l'attribut iid facilitera le lien entre une ligne affichée et un enregistrement.
Ex :
requete = 'SELECT id, nom FROM etablissements ORDER BY nom'
...
tableau.insert('', 'end', iid=enreg[0], values=(enreg[1],) ...)
La façon la plus intuitive pour choisir une ligne dans une liste est de cliquer sur cette ligne. Le TreeView permet justement de cliquer sur une ligne et ainsi retrouver les données de la ligne choisie.
Pour y arriver, il faut d'abord associer une fonction à l'événement « clic sur une ligne qui porte une étiquette donnée ».
Ceci sera fait à l'aide de la fonction tag_bind() qui attend les paramètres suivants :
Ex :
tableau.tag_bind('selection', '<Button-1>', tableau_click)
Lors de l'ajout d'une ligne dans le tableau, on prendra soin de spécifier cette étiquette à l'aide de l'attribut tags.
tableau.insert('', 'end', iid=enreg[0], values=(enreg[1],), tags=('selection',))
Dans le gestionnaire d'événement, il sera possible de retrouver les données de la ligne choisie. Pour l'instant, nous n'avons besoin que de l'identifiant.
Puisque cet identifiant correspond à la valeur du iid, vous pouvez utiliser la fonction identify_row() pour le retrouver.
Cette fonction reçoit en paramètre la position de la ligne cliquée par rapport au coin supérieur gauche du tableau. Cette information est obtenue à l'aide de event.y.
id = tableau.identify_row(event.y)
La suppression proprement dite sera faite à partir de l'identifiant. N'oubliez pas de placer ces instructions à l'intérieur d'un try...except.
Ex :
requete = 'DELETE FROM etablissements WHERE id = ?
curseur.execute(requete, (str(id),))
connexion.commit()
Si l'opération échoue, il faudra afficher un message clair à l'usager.
Dans le cas où l'opération a réussi, une façon simple d'en informer l'usager consiste à retirer la ligne du TreeView. On pourra également afficher un message de confirmation à l'écran.
Ex :
tableau.delete(id)
Il n'y a rien comme un exemple complet pour bien comprendre comment s'imbriquent ces nouvelles notions dans ce que vous connaissez déjà.
#!/usr/bin/env python
from tkinter import *
from tkinter.ttk import *
import sqlite3
import sys
import traceback
import atexit
def exit_handler():
"""Déconnecte la base de données si la connexion avait réussi."""
global connexion
print('On sort du programme!')
if connexion:
print('Fermeture de la connexion')
connexion.close()
else:
print('La connexion n\'a pas été faite, aucune fermeture nécessaire')
def touche_entree(event):
"""Même chose qu'un clic sur le bouton terminer."""
fenetre.destroy()
def tableau_click(event):
"""Clic sur un item du tableau."""
id = tableau.identify_row(event.y)
try:
# supprimer l'enregistrement de la base de données
requete = 'DELETE FROM etablissements WHERE id = ?'
curseur.execute(requete, (str(id),))
connexion.commit()
if curseur.rowcount > 0:
# retirer la ligne du tableau
tableau.delete(id)
message.configure(text='L\'établissement a été supprimé avec succès !')
else:
message.configure(text='L\'établissement à supprimer n\'existe pas.', foreground='orange')
except sqlite3.OperationalError as e:
message.configure(text='Un problème a empêché la suppression de l\'établissement.', foreground='red')
traceback.print_exc(file=sys.stdout)
except Exception as e:
message.configure(text='Une erreur inattendue est survenue.', foreground='red')
traceback.print_exc(file=sys.stdout)
########## programme principal ##########
connexion = None
atexit.register(exit_handler)
# fenêtre principale
fenetre = Tk()
fenetre.title('Système scolaire')
fenetre.config(padx=20, pady=20)
fenetre.bind("<Return>", touche_entree)
# libellé
libelle = Label(fenetre, text = 'Cliquez sur l\'établissement à supprimer :')
libelle.pack(padx = 10, pady = 10)
#tableau
tableau = Treeview(fenetre, columns=('nom'))
tableau.heading('nom', text='Nom de l\'établissement')
tableau['show'] = 'headings' # sans ceci, il y avait une colonne vide à gauche qui a pour rôle d'afficher le paramètre "text" qui peut être spécifié lors du insert
tableau.tag_bind('selection', '<Button-1>', tableau_click)
tableau.pack(padx = 10, pady = (0, 10))
# message
message = Label(fenetre, text = '')
message.pack(padx = 10, pady = (0, 10))
# bouton pour terminer le programme
bouton_terminer = Button(fenetre, text = 'Terminer', command = fenetre.destroy)
bouton_terminer.pack(padx = 10, pady = (0, 10))
# lecture et affichage des données
try:
connexion = sqlite3.connect('file:systemescolaire.db?mode=rw', uri=True)
curseur = connexion.cursor()
requete = 'SELECT id, nom FROM etablissements ORDER BY nom'
curseur.execute(requete)
resultat = curseur.fetchall()
if len(resultat):
for enreg in resultat:
# chaque ligne n'a pas de parent, est ajoutée à la fin de la liste, utilise le champ id comme identifiant et on fournit les valeurs pour chacune des colonnes du tableau
tableau.insert('', 'end', iid=enreg[0], values=(enreg[1],), tags=('selection',))
else:
libelle.configure(text = 'Il n\'y a présentement aucun établissement.')
tableau.pack_forget()
except sqlite3.OperationalError as e:
if connexion is None:
libelle.configure(text='La connexion à la base de données a échoué.', foreground='red')
else:
libelle.configure(text='Il y a une erreur dans la requête SQL.', foreground='red')
traceback.print_exc(file=sys.stdout)
except Exception as e:
libelle.configure(text='Une erreur inattendue est survenue.', foreground='red')
traceback.print_exc(file=sys.stdout)
# la fenêtre s'affiche puis attend les interactions de l'usager
fenetre.mainloop()
▼Publicité