Bonjour

Ce post n'est pas une question mais le fruit de mes réflexions, que j'offre à ceux que cela peut intéresser (donc aux débutants). Je me suis largement inspiré des informations précieuses données par wiztricks un peu plus bas sur ce forum. Tout est contenu dans les commentaires deux petits scripts suivants. Le premier parle des notions maître/esclave et parent/enfant
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
from tkinter import *
app = Tk()
app.geometry('160x130+10+10')
 
frame = Frame(app, name = 'frame', bg='pink')
 
bouton1 = Button(frame, name='bouton1', bg='yellow')
 
bouton2 = Button(app, name='bouton2', text='Bouton2')
 
message = Label(frame, text='maître/esclave \nou parent/enfant ?')
 
frame.pack(padx=20)
bouton1.pack()
bouton2.pack(in_=bouton1, padx=10, pady=10)
message.pack(padx=10, pady=10)
 
print('app =', app)
print('app.children =', app.children)
print('bouton2.master =', bouton2.master)
 
print('\nbouton1 =', bouton1)
print('bouton1.master =', bouton1.master)
print('bouton2 =', bouton2)
 
print('\nid(message) =', id(message))
print('messsage =', message)
 
"""
La notion parent/enfant (parent/child) de tkinter n'a rien avoir avec celle
concernant l'héritage dans le langage python.
 
Les termes parent/enfant et maître/esclave (master/slave) de tkinter sont
généralement utilisés de manière synonyme dans la littérature que l'on trouve
sur internet. Pourtant, dans la documentation tkinter, il semble qu'il y ait
une distinction assez claire :
 - parent/enfant signifie 'widget conteneur'/'widget contenu' au sens visuel.
 - maître/esclave a le même sens mais concerne le geometry manager.
 
Le 'widget contenu' est le même dans les deux cas. L'esclave est donc toujours
l'enfant. Par contre, le maître est parfois un conteneur lui-même contenu dans
le parent.
 
Dans l'instruction < bouton2 = Button(app, ...) >, l'argument 'app' désigne le
widget 'parent' dans lequel sera contenu, au sens visuel du terme, le widget
'enfant' bouton2. On peut vérifier cela en tapant dans la console :
 - print(app)            : renvoie -> '.' qui désigne la racine
 - print(app.children)   : renvoie -> frame ainsi que bouton2
 - print(bouton2.master) : renvoie -> app
 
Le nom 'master' est un choix maladroit pour désigner l'attribut. Il aurait été
plus judicieux de l'appeler 'bouton2.parent'. Cette maladresse est probablement
la cause du mélange des termes que l'on constate dans la littérature internet.
 
Par défaut, le geometry manager utilise le parent indiqué dans l'instruction
< bouton2 = Button(app, ...) > comme étant le widget 'master' pour le packing,
c'est à dire app dans cet exemple. Mais ici ce n'est pas le cas à cause de
l'instruction bouton2.pack(in_=bouton1, ...) qui désigne bouton1 comme le
master. On peut vérifier cela en tapant dans la console :
 - bouton2.pack_info()   : renvoie -> bouton1 (comme master)
 - bouton1.pack_slaves() : renvoie -> bouton2 (comme esclave)
 
Le master désigné par l'option 'in_' ne peut être que le parent (valeur par
défaut) ou un enfant du parent. La conséquence de cette restriction est que le
parent reste toujours le conteneur global, et que les méthodes parent.destroy() et
master.destroy() provoquent toutes deux la destruction du widget ainsi packé.
 
REMARQUE :
La relation parent/enfant est utilisée par tkinter pour construire les noms des
widgets et organiser leur hiérarchie graphique par des 'paths', un peu à la
manière des répertoires/sous-répertoires. On peut voir cela en tapant dans la
console :
 - print(bouton1)        : renvoie -> '.frame.bouton1'
 - print(bouton1.master) : renvoie -> '.frame'
 - print(bouton2)        : renvoie -> '.bouton2'
 
Si aucun nom n'est donné au widget au moment de son instanciation par l'option
'name', tkinter fabrique le nom à l'aide de l' id. On peut voir cela en tapant
dans la console :
 - id(message)  : renvoie -> xxxxxxxx
 - str(message) : renvoie -> '.frame.xxxxxxxx'
"""
Le second vous pose une petite colle (que j'ai mis très longtemps à résoudre) :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
from tkinter import *
 
class MonBouton(Frame):
    def __init__(self, boss, nom, text):
        Frame.__init__(self, boss=None, name='cadre')
        self.bou = Button(self, name=nom, text=text)
        self.bou.pack()
 
class Application (Frame):
    def __init__(self):        
        Frame.__init__(self, name='frame')
        self.pack()
 
        self.bouton1 = Button(self, name='bt1', text='Bouton 1')
        self.bouton1.pack()
 
        self.bouton2 = MonBouton(self, nom='bt2', text='Bouton 2')
        self.bouton2.pack()
 
app = Application()
 
 
 
print('app =', app)
print('app.master =', app.master)
print('\napp.bouton1 =', app.bouton1)
print('app.bouton2 =', app.bouton2)
print('\napp.bouton2.children =', app.bouton2.children)
print('app.bouton2.bou =', app.bouton2.bou)
print('\napp.master.pack_slaves =', app.master.pack_slaves())
print('\napp.pack_slaves =', app.pack_slaves())
 
#app.mainloop()
 
"""
Dans ce programme, app est une frame, enfant de la fenêtre racine nommée <.>
 
Les widgets app.bouton1 et app.bouton2 sont des attributs de app car ils ont
été tous deux instanciés dans la définition de classe de app. Le premier est un
objet de la classe Button() et le second un objet de la classe MonBouton().
 
Les informations dans la console montrent que :
 - Le nom app.bouton1 est <.frame.bt1>, c'est donc un enfant de app
 - Le nom de app.bouton2 est <.cadre> (voir l(explications plus bas), c'est
   donc un enfant de la fenêtre racine.
 
**** Comment faire pour que les boutons soient tous deux enfants de app ? *****
*******        (il suffit de modifier la valeur d'un argument)         ********
 
Réponse :
 
app.bouton1 est enfant de app car, dans son instanciation en ligne 14,
l'argument self représente app.
 
app.bouton2, malgré son apparence visuelle, est une frame car la classe
MonBouton est dérivée d'une frame nommée 'cadre'. Celle-ci est particulière en
ce sens qu'elle contient un widget de la classe Button, nommé 'bou'.
 
Si on veut que le widget app.bouton2 soit enfant de frame (comme app.bouton1),
il faut supprimer la valeur None en ligne 5, de manière à transmettre au
constructeur de la classe parente la référence de frame. Faute de quoi l'objet
app.bouton2 s'instancie par défaut dans la fenêtre racine.
"""
Bonne réussite