Python 3 - Tkinter

Bonsoir,

J'avais déjà demandé l'autorisation de poser des questions sur Python, celle-ci m'avait été accordée mais dans le bar uniquement (ce qui est bien normal). Je me permets donc de revenir vers vous :

Après avoir étudié les bases de PYTHON 3 (avant de m'attaquer à microPython) j'essaye de créer une application qui regroupe l'utilisation des classes et d'une interface graphique. J'ai choisi celle fournie avec Python, c'est à dire Tkinter.
Mon problème est le suivant :

  • Lorsque je créé à l'intérieur d'une classe un groupe de label (81) et que je les affiche avec des boucles imbriquées en utilisant la méthode grid, je souhaite récupérer la position dans la "grid" (row + column) avec un clic de souris sur le widget (ici Label) ;

  • J'essaye d'utiliser la méthode widget.grid_info() qui fonctionne pour un label unique que j'ai ajouté (label2) mais qui ne fonctionne pas avec le groupe des 81 "label". Le problème vient très certainement du fait que les 81 "label" ne sont pas précisément identifiés.

Ma question est donc : est-il possible de procéder ainsi pour un groupe de widgets de même nom ?

Sinon j'essayerai d'aborder mon problème différemment :wink:

Voici le code :

import tkinter as tk
from tkinter import messagebox

class forum(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.grille = self.creation_grille()
        self.creer_widgets()
        

    def creation_grille(self):
        grille = [[".",".",".",".",".",".",".",".","."],
                  [".",".",".",".",".",".",".",".","."],
                  [".",".",".",".",".",".",".",".","."],
                  [".",".",".",".",".",".",".",".","."],
                  [".",".",".",".",".",".",".",".","."],
                  [".",".",".",".",".",".",".",".","."],
                  [".",".",".",".",".",".",".",".","."],
                  [".",".",".",".",".",".",".",".","."],
                  [".",".",".",".",".",".",".",".","."]]
        
        return grille
        
    def creer_widgets(self):
        # Création de 81 labels avec des boucles imbriquées
        for row in range(9):
            for col in range(9):
        
                self.label = tk.Label(self,text=self.grille[row][col], width=7, bg = "yellow", highlightthickness=1, highlightbackground='#000000',font=("arial", 24), fg = 'blue')
            
                
                pad_y = (0, 0)
                pad_x = (0, 0)

                if (row+1) % 3 == 0 and (row+1) < 9: # skip for last row
                    pad_y = (0, 10)
                
                if (col+1) % 3 == 0 and (col+1) < 9: # skip for last column
                   pad_x = (0, 10)

                self.label.grid(row=row, column=col, ipadx=5, ipady=5, padx=pad_x, pady=pad_y)
                
        self.label.bind("<Button-1>",self.mouse)

        #création d'un label unique
        self.label2 = tk.Label(self,text="LABEL ", font=("arial", 24), highlightthickness=1, highlightbackground='#000000')
        self.label2.grid(row=10, column=1, ipadx=5, ipady=5, padx=0, pady=10)
        self.label2.bind("<Button-1>",self.mouse2)
        
    def mouse(self,event):
        grid_info = self.label.grid_info()
        messagebox.showinfo("info",str(grid_info["row"]) + " " + str(grid_info["column"]))

    def mouse2(self,event):
        grid_info = self.label2.grid_info()
        messagebox.showinfo("info",str(grid_info["row"]) + " " + str(grid_info["column"]))
        
app = forum()
app.title("forum arduino philippe86220")
app.mainloop()# Boucle d'attente des événements

Visuellement ça donne ceci :

et ça ne fonctionne que pour le dernier label en bas à gauche.

Merci par avance.

PS : MAC OS 10.15.7 Catalina
Python 3.11.4
tkinter 8.6

Bonjour,
En fait je pense qu'il suffit de donner un nom différent pour chaque Label à l'aide d'une boucle et de récupérer le nom du label qui a le focus. Ainsi on peut récupérer la position du widget dans la grid.
Maintenant il ne reste plus qu'à trouver comment ...

Bonne journée.

Juste pour information, il y a aussi pyQt qui fournis une librairie d'interface à Qt comme Tkinter pour tcl/tk.
Je crois aussi avoir vue pyGTK, mais je ne sais pas si celle-ci est portable sur toutes les plateformes.

ça fait une éternité que je n'ai pas joué avec tkinter - quand je veux une interface utilisateur sur mon Mac j'utilise xCode mais essayez un truc comme cela

import tkinter as tk


def clickEtiquette(ligne, col):
    print(f"Etiquette choisie en Ligne {ligne} et Col {col}")

def clickBouton():
    print("Le bouton a été cliqué")
  
affichage = tk.Tk()
affichage.title("Grille d'étiquettes")

for l in range(9):
    for c in range(9):
        label = tk.Label(affichage, text=f"case ({l}, {c})", borderwidth=1, relief="solid", width=15, height=3)
        label.grid(row=l, column=c)
        label.bind("<Button-1>", lambda event, ligne=l, col=c: clickEtiquette(ligne,col))

bouton = tk.Button(affichage, text="Cliquez-moi !", command=clickBouton)
bouton.grid(row=9, column=0, columnspan=9, sticky="w", padx=10, pady=10)  


affichage.mainloop()

➜ le bind est dans les boucles for

Je suis d'accord avec @terwal , PyQt est bien plus commode d'utilisation que tkinter

Merci @J-M-L

Dans ma classe, ça donne :

import tkinter as tk
from tkinter import messagebox

class forum(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.grille = self.creation_grille()
        self.creer_widgets()
        

    def creation_grille(self):
        grille = [[".",".",".",".",".",".",".",".","."],
                  [".",".",".",".",".",".",".",".","."],
                  [".",".",".",".",".",".",".",".","."],
                  [".",".",".",".",".",".",".",".","."],
                  [".",".",".",".",".",".",".",".","."],
                  [".",".",".",".",".",".",".",".","."],
                  [".",".",".",".",".",".",".",".","."],
                  [".",".",".",".",".",".",".",".","."],
                  [".",".",".",".",".",".",".",".","."]]
        
        return grille
        
    def creer_widgets(self):
        # Création de 81 labels avec des boucles imbriquées
        
        
        for row in range(9):
            for col in range(9):
                
                self.label = tk.Label(self,text=self.grille[row][col], width=7, bg = "yellow", highlightthickness=1, highlightbackground='#000000',font=("arial", 24), fg = 'blue')
                
                
                pad_y = (0, 0)
                pad_x = (0, 0)

                if (row+1) % 3 == 0 and (row+1) < 9: # skip for last row
                    pad_y = (0, 10)
                
                if (col+1) % 3 == 0 and (col+1) < 9: # skip for last column
                   pad_x = (0, 10)

                self.label.grid(row=row, column=col, ipadx=5, ipady=5, padx=pad_x, pady=pad_y)
                self.label.bind("<Button-1>",lambda event,ligne = row, colone = col:self.mouse(ligne,colone))
        

        #création d'un label unique
        self.label2 = tk.Label(self,text="LABEL ", font=("arial", 24), highlightthickness=1, highlightbackground='#000000')
        self.label2.grid(row=10, column=1, ipadx=5, ipady=5, padx=0, pady=10)
        self.label2.bind("<Button-1>",self.mouse2)
        
    def mouse(self,ligne, col):
        
        messagebox.showinfo("info",f"{ligne} {col}")

    def mouse2(self,event):
        grid_info = self.label2.grid_info()
        messagebox.showinfo("info",str(grid_info["row"]) + " " + str(grid_info["column"]))
        
app = forum()
app.title("forum arduino philippe86220")
app.mainloop()# Boucle d'attente des événements

En fait il fallait créer une lambda qui transmette les deux paramètres row et col à la fonction qui gère le click de la souris.

Merci beaucoup.

Je ne compte pas m'attarder sur Python 3 donc j'ai pris l'interface graphique par défaut et ça me suffira.
Encore un livre à lire et je me lance dans microPython.

Merci.
Bonne journée

et mettre cela au coeur de la loop bien sûr

Oui @J-M-L,
Je n’ai trouvé aucune information sur cette manière de procéder.
J’ai peut-être mal cherché :wink:

C’est une technique qui vaut de l’or pour moi, elle est simple et efficace. Je vais pouvoir améliorer mon jeu de sudoku :wink:. Je vais pouvoir l’utiliser pour d’autres applications également.

Merci infiniment.
Bonne journée.

avec une fonction lambda vous instanciez la fonction à la demande, ça crée autant de fonctions que l'on veut donc en un seul coup

Ici 81 fonctions :wink:
Merci

oui

vous pouvez un peu nettoyer le code

import tkinter as tk
from tkinter import messagebox

class ForumApp(tk.Tk):
    def __init__(self):
        super().__init__()
        self.grille = [['.' for _ in range(9)] for _ in range(9)]
        self.creer_widgets()

    def creer_widgets(self):
        for row in range(9):
            for col in range(9):
                self.creer_label(row, col)

        self.label2 = self.creer_label(10, 1, text="LABEL")
        self.label2.bind("<Button-1>", lambda event: self.afficher_position(self.label2))

    def creer_label(self, row, col, text='', bg="yellow", font=("arial", 24), fg='blue'):
        label = tk.Label(
            self, text=text, width=7, bg=bg,
            highlightthickness=1, highlightbackground='#000000',
            font=font, fg=fg
        )
        
        pad_y = (0, 0) if (row + 1) % 3 != 0 or row == 8 else (0, 10)
        pad_x = (0, 0) if (col + 1) % 3 != 0 or col == 8 else (0, 10)
        
        label.grid(row=row, column=col, ipadx=5, ipady=5, padx=pad_x, pady=pad_y)
        label.bind("<Button-1>", lambda event, row=row, col=col: self.afficher_position(label))
        
        return label

    def afficher_position(self, label):
        grid_info = label.grid_info()
        messagebox.showinfo("Info", f"{grid_info['row']} {grid_info['column']}")

app = ForumApp()
app.title("Forum Arduino - philippe86220")
app.mainloop()

Effectivement ça simplifie le code et ça le rend plus lisible. Deux instructions m'interpellent :

self.grille = [['.' for _ in range(9)] for _ in range(9)]

pad_y = (0, 0) if (row + 1) % 3 != 0 or row == 8 else (0, 10)

  • La dernière ne me pose pas de problème de compréhension mais je ne savais pas que la formulation d'un "if else" pouvait se faire ainsi ;
  • la première je la comprends dans son ensemble mais le point particulier de l'underscore m'interpelle ? (c'est une façon d'initialiser une liste de 9 lignes et 9 colonnes avec le "." dans chaque élément)

Non c'est juste un moyen de ne pas utiliser une variable pour rien. Ça peut se faire dans d'autres cas par exemple une fonction renvoie deux arguments mais tu n'es intéressé que par le second
_, x = fonction (u)

c'est pour ça que je l'ai utilisée :slight_smile: — ça change du C++ !

ça se lit bien de gauche à droite en anglais comme une explication

Merci @lesept et @J-M-L,
Je n’ai rien vu de tout ça dans mon livre ou sur internet.
C’est vraiment une chance de connaître le forum Arduino …
Bonne soirée à vous deux :wink:

Tout simplement parce que le underscore n’a rien de particulier. C’est un nom de variable comme un autre mais qu’on utilise par convention pour dire qu’on ne s’intéresse pas à cette variable

Pour le if, cf What’s New in Python 2.5 — Python 3.12.0 documentation

Merci @J-M-L
Bonne journée.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.