Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Gérer plusieurs fenetres Toplevel sous Tkinter

23 réponses
Avatar
ylc
Bonjour,
J'ai ouvert une s=E9rie de fen=EAtres Toplevel avec le m=EAme nom, chacune
comportant un bouton de commande pour agir sur la fen=EAtre.
Le probl=E8me est que tous ces boutons pointent vers une seule proc=E9dure
qui n'agit que sur la derni=E8re fen=EAtre ouverte et non pas sur la
fen=EAtre du bouton cliqu=E9.
D'o=F9 ma question : quand on clique sur un bouton d'une fen=EAtre,
comment r=E9cup=E9rer le nom de cette fen=EAtre pour agir dessus ?
voici un petit prog d'exemple : tous les boutons agissent sur la
derni=E8re fen=EAtre !......
merci. YLC

# test multifen=EAtres
from Tkinter import *
def rouge() :
top.config(bg=3D'red')
def vert() :
top.config(bg=3D'green')
def fenetres() :
global top
i=3D0
while i < 5 :
i=3Di+1
top=3DToplevel()
top.title("toplevel"+str(i))
top.geometry("150x20+20+"+str(i*50))
broug =3D Button(top, text =3D 'rouge', command =3D rouge)
bvert =3D Button(top, text =3D 'vert', command =3D vert)
broug.pack(side=3DLEFT)
bvert.pack()
# main
fen =3D Tk(className=3D"Fenetre racine")
fenetres()
fen.mainloop()
# #############################################

10 réponses

1 2 3
Avatar
Eric Brunel
On Mon, 24 Nov 2008 12:21:45 +0100, ylc wrote:

Bonjour,
J'ai ouvert une série de fenêtres Toplevel avec le même nom, chacune
comportant un bouton de commande pour agir sur la fenêtre.
Le problème est que tous ces boutons pointent vers une seule procédure
qui n'agit que sur la dernière fenêtre ouverte et non pas sur la
fenêtre du bouton cliqué.
D'où ma question : quand on clique sur un bouton d'une fenêtre,
comment récupérer le nom de cette fenêtre pour agir dessus ?
voici un petit prog d'exemple : tous les boutons agissent sur la
dernière fenêtre !......
merci. YLC

# test multifenêtres
from Tkinter import *
def rouge() :
top.config(bg='red')
def vert() :
top.config(bg='green')
def fenetres() :
global top
i=0
while i < 5 :
i=i+1
top=Toplevel()
top.title("toplevel"+str(i))
top.geometry("150x20+20+"+str(i*50))
broug = Button(top, text = 'rouge', command = rouge)
bvert = Button(top, text = 'vert', command = vert)
broug.pack(side=LEFT)
bvert.pack()
# main
fen = Tk(className="Fenetre racine")
fenetres()
fen.mainloop()
# #############################################



Il y a plein de solutions à ce problème, mais celle qui semble préférable
dans ton cas est de passer par une sous-classe de Toplevel.

Par exemple:
-------------------------------------------------------
from Tkinter import *

class MaFenetre(Toplevel):
def __init__(self, i):
Toplevel.__init__(self)
self.title("toplevel"+str(i))
self.geometry("150x20+20+"+str(i*50))
broug = Button(self, text = 'rouge', command = self.rouge)
bvert = Button(self, text = 'vert', command = self.vert)
broug.pack(side=LEFT)
bvert.pack()
def rouge(self):
self.config(bg='red')
def vert(self):
self.config(bg='green')

def fenetres():
for i in range(5):
ma_fenetre = MaFenetre(i)

fen = Tk(className="Fenetre racine")
fenetres()
fen.mainloop()
-------------------------------------------------------

Je ne sais pas si tu maîtrises l'orienté-objet, donc je ne vais pas me
lancer dans plein d'explications peut-être inutiles. Si jamais tu ne
comprends pas ce que fait le code ci-dessus, redemande: il y aura bien une
bonne âme pour t'éclairer (peut-être bien moi, d'ailleurs...).

HTH
--
python -c "print ''.join([chr(154 - ord(c)) for c in
'U(17zX(%,5.zmz5(17l8(%,5.Z*(93-965$l7+-'])"
Avatar
ylc
Bonjour,
Merci pour ta réponse, elle fontionne effectivement bien et est
sûrement la plus élégante. Malheureusement, je ne maitrise pas bien
l'orienté objet et dès que cela se complique je m'y perd.
N'y aurait-il pas la possibilité d'identifier le nom de la fenêtre
dans les procédures "rouge" et "vert" pour lui appliquer la commande ?
Avatar
Eric Brunel
On Tue, 25 Nov 2008 09:31:24 +0100, ylc wrote:

Bonjour,
Merci pour ta réponse, elle fontionne effectivement bien et est
sûrement la plus élégante. Malheureusement, je ne maitrise pas bien
l'orienté objet et dès que cela se complique je m'y perd.
N'y aurait-il pas la possibilité d'identifier le nom de la fenêtre
dans les procédures "rouge" et "vert" pour lui appliquer la commande ?



Mais elle est parfaitement identifiée: elle s'appelle self ;-)

Blague à part, il n'y a rien de fondalementalement compliqué dans le bout
de programme que j'ai écrit. L'orienté objet n'a rien de très complexe non
plus, mais je sais qu'on a besoin d'un petit déclic dans la tête pour
commencer à voir les choses clairement. Il faut aussi sans doute accepter
que certaines choses "magiques" se passent sans qu'on ait besoin de rien
dire; c'est d'ailleurs sans doute ce qui est le plus difficile pour
beaucoup de gens...

Cela dit, ne sachant pas vraiment ce que tu connais/maîtrises ou pas, ça
va être difficile de donner des explications pertinentes. Je pourrais me
lancer dans un cours théorique sur l'orienté-objet, mais ça serait long et
peut-être sans intérêt pour toi si tu as déjà des notions de base. Donc ça
m'aiderait de savoir quels langages tu connais déjà, et quels sont les
concepts OO que tu comprends, et ceux avec lesquels tu as du mal (en vrac:
classe, instance, attribut, méthode, spécialisation...).
--
python -c "print ''.join([chr(154 - ord(c)) for c in
'U(17zX(%,5.zmz5(17l8(%,5.Z*(93-965$l7+-'])"
Avatar
ylc
Ne cherche pas à m'expliquer l'Oo, j'ai déjà passé des heures dessu s
et je comprend certains trucs, mais cela m'embrouille.....et je
préfère m'en passer........tant que c'est possible.

Tu ne réponds pas à ma question : peut-on récupérer l'identifiant
d'une fenêtre losqu'on clique un bouton dans celle-ci pour lui
appliquer la commande ?
merci.
Avatar
Eric Brunel
On Tue, 25 Nov 2008 10:52:51 +0100, ylc wrote:

Ne cherche pas à m'expliquer l'Oo, j'ai déjà passé des heures dessus
et je comprend certains trucs, mais cela m'embrouille.....et je
préfère m'en passer........tant que c'est possible.



Tu te prives de beaucoup de choses très puissantes et très utiles...

Tu ne réponds pas à ma question : peut-on récupérer l'identifiant
d'une fenêtre losqu'on clique un bouton dans celle-ci pour lui
appliquer la commande ?
merci.



Mais quel identifiant? Il n'y a pas d'"identifiant" de la fenêtre, juste
une instance de MaFenetre, qui est aussi une instance de la classe
Toplevel puisque MaFenetre est une sous-classe de Toplevel, instance sur
laquelle on applique la méthode config... Donc ce que je disais est tout à
fait correct: à l'intérieur des méthodes rouge et vert, l'"identifiant" de
la fenêtre, c'est self...

Je ne vois pas vraiment ce qui te gêne en fait. D'où mes questions...
Faire:

class MaFenetre(...):
def rouge(self):
...
ma_fenetre = MaFenetre(...)
ma_fenetre.rouge()

est pratiquement équivalent à:

class MaFenetre(...):
...
def MaFenetre_rouge(self):
...
ma_fenetre = MaFenetre(...)
MaFenetre_rouge(ma_fenetre)

A l'intérieur de la méthode rouge, comme à l'intérieur de la fonction
MaFenetre_rouge, le paramètre self vaut ma_fenetre... C'est un basique
passage de paramètre à une fonction dans les deux cas. C'est juste la
syntaxe qui diffère: une méthode est appelée en faisant
ma_fenetre.rouge(), alors que la fonction l'est en faisant
MaFenetre_rouge(ma_fenetre). Mais ça fait presque exactement la même chose
derrière...

Si tu voulais quelque chose qui à l'intérieur de la méthode rouge te
renvoie le nom 'ma_fenetre', il y a effectivement des possibilités de le
faire. Mais d'une part, ça va être abominablement compliqué, et d'autre
part ça ne t'apportera rien, puisque tu as déjà l'objet que tu veux
manipuler sous le nom self...

Mais peut-être n'ai-je pas compris la question...?

HTH quand même...
--
python -c "print ''.join([chr(154 - ord(c)) for c in
'U(17zX(%,5.zmz5(17l8(%,5.Z*(93-965$l7+-'])"
Avatar
ylc
Le problème est plus compliqué que dans l'exemple que j'ai écrit, car
dans chaque fenetre de mon prog il y a un canvas et dans ce canvas il
y a une image. Les boutons servent à modifier l'image (rotation,
taille,..) et à chaque fois il faut regénérer l'affichage dans la
fenêtre.
Il y a un integer (.nnnnnnnnnn) identifiant chaque fenêtre ou
(nnnnnnnn.nnnnnnnn) pour l'identifier avec son parent. C'est cet
identifiant que je pensais récupérer au clic sur un bouton pour
l'affecter à ma variable nomfenetre.
C'est pas possible ça ?
Avatar
Eric Brunel
On Tue, 25 Nov 2008 11:34:16 +0100, ylc wrote:

Le problème est plus compliqué que dans l'exemple que j'ai écrit, car
dans chaque fenetre de mon prog il y a un canvas et dans ce canvas il
y a une image. Les boutons servent à modifier l'image (rotation,
taille,..) et à chaque fois il faut regénérer l'affichage dans la
fenêtre.
Il y a un integer (.nnnnnnnnnn) identifiant chaque fenêtre ou
(nnnnnnnn.nnnnnnnn) pour l'identifier avec son parent. C'est cet
identifiant que je pensais récupérer au clic sur un bouton pour
l'affecter à ma variable nomfenetre.
C'est pas possible ça ?



OK, ça ressemble à l'identifiant au niveau tk, c'est ça? Je ne suis pas
sûr de comprendre pourquoi tu en as besoin, vu que normalement, avec
Tkinter, toutes les manipulations se font via des objets Python. Mais en
supposant que tu as une bonne raison, on peut récupérer l'identifiant tk
de la fenêtre simplement en faisant str(self) dans les méthodes.

HTH
--
python -c "print ''.join([chr(154 - ord(c)) for c in
'U(17zX(%,5.zmz5(17l8(%,5.Z*(93-965$l7+-'])"
Avatar
ylc
Je suis désolé d'insister, mais je crois qu'on ne parle pas de la mêm e
chose. Je ne maitrise pas Python aussi bien que vous et j'ai renoncé
à faire de l'objet. Je suis certain qu'on peut écrire des progs sans
faire de l'Oo puisque j'ai réussi jusqu'à présent à le faire.
Même si cela peut paraitre archaïque ou démodé, voilà ce que je
voudrais faire dans mon exemple :

from Tkinter import *
def rouge() :
IdFen = //get id de la fenêtre activée// <<<<<<< ce que je
cherche à faire
IdFen.config(bg='red') <<<<<<< pour
pouvoir faire ça
def vert() :
IDfen = //get id de la fenêtre activée// <<<<<<< ce que je
cherche à faire
IdFen.config(bg='green') <<<<<<< pour
pouvoir faire ça

def fenetres() :
global top
i=0
while i < 5 :
i=i+1
top=Toplevel()
top.title("toplevel"+str(i))
top.geometry("150x20+20+"+str(i*50))
broug = Button(top, text = 'rouge', command = rouge)
bvert = Button(top, text = 'vert', command = vert)
broug.pack(side=LEFT)
bvert.pack()
# main
fen = Tk(className="Fenetre racine")
fenetres()
fen.mainloop()

.......et sans utiliser de "self"..... :)
Il doir y avoir une commande du genre Cget, mais celle-ci ne
s'applique pas aux options des toplevels.
YLC
Avatar
Pierre Maurette
ylc, le 25/11/2008 a écrit :
Je suis désolé d'insister, mais je crois qu'on ne parle pas de la même
chose. Je ne maitrise pas Python aussi bien que vous et j'ai renoncé
à faire de l'objet. Je suis certain qu'on peut écrire des progs sans
faire de l'Oo puisque j'ai réussi jusqu'à présent à le faire.



Ce que je trouve bien entre autres chose dans Python c'est
effectivement le fait de pouvoir faire ou ne pas faire de l'objet, de
pouvoir utiliser de l'objet dans un script non objet, etc. En bref, de
ne pas être tenu par une "philisophie" trop rigide. Mais sur ce coup-là
je vous trouve un peu têtu. Si vous avez renoncé à "faire de l'objet"
même un tout petit peu, il vous faut également renoncer à Tkinter, qui
est objet. Dès que vous écrivez:
MaFenetrePrincipale = Tk()
vous faites de l'objet.

Même si cela peut paraitre archaïque ou démodé, voilà ce que je
voudrais faire dans mon exemple :

from Tkinter import *
def rouge() :
IdFen = //get id de la fenêtre activée// <<<<<<< ce que je
cherche à faire
IdFen.config(bg='red') <<<<<<< pour
pouvoir faire ça
def vert() :
IDfen = //get id de la fenêtre activée// <<<<<<< ce que je
cherche à faire
IdFen.config(bg='green') <<<<<<< pour
pouvoir faire ça

def fenetres() :
global top
i=0
while i < 5 :
i=i+1
top=Toplevel()
top.title("toplevel"+str(i))
top.geometry("150x20+20+"+str(i*50))
broug = Button(top, text = 'rouge', command = rouge)
bvert = Button(top, text = 'vert', command = vert)
broug.pack(side=LEFT)
bvert.pack()
# main
fen = Tk(className="Fenetre racine")
fenetres()
fen.mainloop()

.......et sans utiliser de "self"..... :)



Si c'est un jeu, vous pouvez faire ça, juste une petite modif de votre
code, mais qui ne me semble pas réellement plus simple à piger qu'une
petite classounette:

#! /usr/bin/env python
# -*- coding: utf-8 -*-

from Tkinter import *

def rouge(n):
vasistas[n].config(bg='red')
def vert(n) :
vasistas[n].config(bg='green')

def fenetres() :
i=0
tops = []
while i < 5 :
top=Toplevel()
top.title("toplevel"+str(i))
top.geometry("150x20+20+"+str(i*50))
broug = Button(top
, text = 'rouge'
, command = eval("lambda : rouge(" + str(i) +
")"))
bvert = Button(top
, text = 'vert'
, command = eval("lambda : vert(" + str(i) + ")"))
broug.pack(side=LEFT)
bvert.pack()
i=i+1
tops.append(top)
return tops



fen = Tk(className="Fenetre racine")
vasistas = fenetres()
fen.mainloop()


Il doir y avoir une commande du genre Cget, mais celle-ci ne
s'applique pas aux options des toplevels.



Je pense que vous vous perdez avec ces histoires d'identificateurs.
Déjà, dans le code ci-dessus, on garde trace (un nom dans un namespace)
des fenêtres créees, sous la forme vasistas[n]. Dans le code de Eric,
on créait les fenêtres comme de petits organismes qu'on laisserait
vivre leur vie. Mais on aurait aussi bien pu en garder l'accès, en les
nommant. Voici un exemple, dans lequel la fenêtre principale peut agir
sur les petites fenêtres, et même les petites fenêtres on accès à leurs
copines (attention, c'est bugué et ça ne résiste pas à la fermeture
d'une petite fenêtre):

#! /usr/bin/env python
# -*- coding: utf-8 -*-

from Tkinter import *

class MaFenetre(Toplevel):
def __init__(self, i):
Toplevel.__init__(self)
self.ident = i
self.title("toplevel"+str(i))
self.geometry("150x20+20+"+str(i*50))
broug = Button(self, text = 'rouge', command = self.rouge)
bvert = Button(self, text = 'vert', command = self.vert)
broug.pack(side=LEFT)
bvert.pack()
def _noircitAutres(self):
#noircit les autres fenêtres
[vasistas[i].noir() for i in range(self.ident) + range(self.ident
+ 1, 5)]
def rouge(self):
self._noircitAutres()
self.config(bg='red')
def vert(self):
self._noircitAutres()
self.config(bg='green')
def noir(self):
self.config(bg='black')

def fenetres():
return [MaFenetre(i) for i in range(5)]

def rouge():
vasistas[int(spin.get())].rouge()

def vert():
vasistas[int(spin.get())].vert()


fen = Tk(className="Fenetre racine")
vasistas = fenetres()
vasistas[2].noir()
spin = Spinbox(fen, from_ = 0, to = 4)
broug = Button(fen, text = 'rouge', command = rouge)
bvert = Button(fen, text = 'vert', command = vert)
spin.pack()
broug.pack(side=LEFT)
bvert.pack(side=RIGHT)
fen.mainloop()

Enfin, tant qu'à faire, une classe également pour la fenêtre
principale. Ça résiste un peu mieux à la fermeture des petites
fenêtres, mais ça demande à être débugué. Je vous conseille vivement de
partir dans cette voie:

#! /usr/bin/env python
# -*- coding: utf-8 -*-


from Tkinter import *

class MainFenetre(Tk):

def __init__(self, nom):
Tk.__init__(self, className=nom)
broug = Button(self, text = 'rouge', command = self.rouge)
bvert = Button(self, text = 'vert', command = self.vert)
self.spin = Spinbox(self, from_ = 0, to = 4)
self.spin.pack()
broug.pack(side=LEFT)
bvert.pack()
self.vasistas = [MaFenetre(i) for i in range(5)]

def rouge(self):
self.vasistas[int(self.spin.get())].rouge()

def vert(self):
self.vasistas[int(self.spin.get())].vert()


class MaFenetre(Toplevel):
vasistas = []

def __init__(self, i):
Toplevel.__init__(self)
self.ident = i
self.title("toplevel"+str(i))
self.geometry("150x20+20+"+str(i*50))
broug = Button(self, text = 'rouge', command = self.rouge)
bvert = Button(self, text = 'vert', command = self.vert)
broug.pack(side=LEFT)
bvert.pack()
MaFenetre.vasistas.append(self)
self.protocol("WM_DELETE_WINDOW", self.onClose)

def onClose(self):
MaFenetre.vasistas.remove(self)
fen.vasistas = MaFenetre.vasistas
fen.spin.config(to=len(MaFenetre.vasistas) - 1)
self.destroy()

def _noircitAutres(self):
#noircit les autres fenêtres
[MaFenetre.vasistas[i].noir() for i in range(self.ident) +
range(self.ident + 1, len(MaFenetre.vasistas))]

def rouge(self):
self._noircitAutres()
self.config(bg='red')

def vert(self):
self._noircitAutres()
self.config(bg='green')

def noir(self):
self.config(bg='black')



fen = MainFenetre("Fenetre racine")
fen.mainloop()

--
Pierre Maurette
Avatar
ylc
Bonjour,
Merci beaucoup à Eric et à Pierre pour vos explications. Je ne sais
quoi trop vous dire pour ne pas vous vexer, mais vous voulez
absolument que j'utilise des classes pour résoudre mon problème. Pour
vous cela semble très évident, mais pour moi cela ne l'est pas. Dès
que le code se complique, cela devient à mes yeux inconpréhensible.
C'est comme si vous vouliez résoudre un problème d'arithmétique en
apportant une solution algébrique.....vous aurez beau dire que c'est
facile et que vous avez la bonne réponse, ce n'est pas la solution
demandée.
Disons que je fais un blocage sur le mot self ; ce n'est pas grave, ça
doit se soigner, et on peut vivre avec :-)
Je vais chercher une autre solution pour ne pas avoir à gérer mes
images dans plusieurs Toplevels (comme ça je contourne la difficulté)
et réduire peut-être mes ambitions sur les manipulations que je
voulais rendre possible.
Je vous remercie quand même très sincèrement.
YLC
1 2 3