Formation PUB400 : Python, 2018 Opérations CRUD avec tkinter

10.3 Suppression dans une application graphique


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.

Attribut iid vs identifiant de l'enregistrement

Le fait d'assigner l'identifiant de l'enregistrement à l'attribut iid facilitera le lien entre une ligne affichée et un enregistrement.

Ex :

Python

requete = 'SELECT id, nom FROM etablissements ORDER BY nom'

 

...

 

tableau.insert('', 'end', iid=enreg[0], values=(enreg[1],) ...)

Clic sur une ligne du TreeView

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 : 

  • l'étiquette recherchée (fera le lien avec une étiquette fournie lors de l'ajout des lignes dans le tableau)
  • une chaîne qui identifie le bouton de la souris (Button-1 = bouton gauche, Button-2 = bouton centre, Button-3 = bouton droit), entourée de chevrons (< et >)
  • le nom de la fonction associée à l'événement (gestionnaire d'événement)

Ex :

Python

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.

Python

 tableau.insert('', 'end', iid=enreg[0], values=(enreg[1],), tags=('selection',))

Retrouver l'identifiant de la ligne choisie

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.

Python

id = tableau.identify_row(event.y)

Suppression

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 :

Python

requete = 'DELETE FROM etablissements WHERE id = ?

curseur.execute(requete, (str(id),))

connexion.commit()

Informer l'usager de l'état de l'opération

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 :

Python

tableau.delete(id)

Un exemple complet

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

Python

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

Veuillez noter que le contenu de cette fiche vous est partagé à titre gracieux, au meilleur de mes connaissances et sans aucune garantie.
Merci de partager !
Soumettre