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

Problème TKinter

5 réponses
Avatar
Pierre Maurette
Bonjour,

Je tourne en rond sur un souci avec une petite interface TKinter. Je me
suis décidé à réduire mon appli pour soumettre ici. Je précise que le
Button "inputlines" est en réalité un objet de taille devant évoluer,
au moins au cours du développement.
Le problème est la gestion de l'option menu "Afficher log..." / "Cacher
log..." dès qu'on a resizé en vertical. Je suis prêt à bloquer le
resizing (au moins en vertical) mais toute tentative - j'ai essayé pas
mal de truc - empêche l'apparition / disparition de logBox de
fonctionner. En vous remerciant pour toute suggestion, voici le code:

# coding: iso-8859-1

from Tkinter import *
from ScrolledText import *

class App(Frame):
def __init__(self, master):
Frame.__init__(self)

menu = Menu(root)
root.config(menu=menu)

self.viewmenu = Menu(menu, tearoff=0)
menu.add_cascade(label="Affichage", menu=self.viewmenu)
self.viewmenu.add_command()

self.topFrame = Frame(master)
self.inputlines = Button(self.topFrame, width=80, height=10)
self.inputlines.pack(side=TOP, anchor=NW, fill=X)

self.logBox = ScrolledText(self.topFrame, width=0)
self.logvisible = True
self.viewmenu.entryconfig(0, command=self.togglelog)
self.togglelog()

self.topFrame.pack(side=TOP, anchor=NW, expand=NO, fill=BOTH,
padx=5, pady=2)

middleFrame = Frame(master)
buttonWidth=8

self.goButton = Button(middleFrame, text="Go !", command=None,
width=buttonWidth)
self.goButton.pack(side=LEFT, anchor=W, expand=NO)

self.quitButton = Button(middleFrame, text="Quitter",
command=self.quit, width=buttonWidth)
self.quitButton.pack(side=RIGHT, anchor=E, expand=NO)

middleFrame.pack(side=TOP, anchor=SE, expand=NO, fill=X,
padx=5, pady=2)

bottomFrame = Frame(master)
self.statusBar = StatusBar(bottomFrame, 80)
self.statusBar.set('%s', 'blahblah')
self.statusBar.pack(side=BOTTOM, fill=X)
bottomFrame.pack(side=BOTTOM, expand=NO, fill=X)

def togglelog(self):
self.logvisible = not self.logvisible
if self.logvisible:
self.logBox.pack(side=LEFT, expand=YES, fill=BOTH)
self.viewmenu.entryconfig(0, label='Cacher log...')
else:
self.logBox.pack_forget()
self.viewmenu.entryconfig(0, label='Afficher log...')

class StatusBar(Frame):
def __init__(self, master, largeur=0):
Frame.__init__(self, master)
self.label = Label(self, width=largeur, bd=1, relief=SUNKEN,
anchor=W)
self.label.pack(fill=X)

def set(self, format, *args):
self.label.config(text=format % args)
self.label.update_idletasks()

root = Tk()

app = App(root)
root.mainloop()
try:
root.destroy()
except:
pass

--
Pierre Maurette

5 réponses

Avatar
Jacques Pronchery
Bonjour,

Je tourne en rond sur un souci avec une petite interface TKinter. Je me
suis décidé à réduire mon appli pour soumettre ici. Je précise que le
Button "inputlines" est en réalité un objet de taille devant évoluer, au
moins au cours du développement.
Le problème est la gestion de l'option menu "Afficher log..." / "Cacher
log..." dès qu'on a resizé en vertical. Je suis prêt à bloquer le
resizing (au moins en vertical) mais toute tentative - j'ai essayé pas
mal de truc - empêche l'apparition / disparition de logBox de
fonctionner. En vous remerciant pour toute suggestion, voici le code:

# coding: iso-8859-1

from Tkinter import *
from ScrolledText import *

class App(Frame):
def __init__(self, master):
Frame.__init__(self)

menu = Menu(root)
root.config(menu=menu)

self.viewmenu = Menu(menu, tearoff=0)
menu.add_cascade(label="Affichage", menu=self.viewmenu)
self.viewmenu.add_command()

self.topFrame = Frame(master)
self.inputlines = Button(self.topFrame, width€, height)
self.inputlines.pack(side=TOP, anchor=NW, fill=X)

self.logBox = ScrolledText(self.topFrame, width=0)
self.logvisible = True
self.viewmenu.entryconfig(0, command=self.togglelog)
self.togglelog()

self.topFrame.pack(side=TOP, anchor=NW, expand=NO, fill=BOTH,
padx=5, pady=2)

middleFrame = Frame(master)
buttonWidth=8

self.goButton = Button(middleFrame, text="Go !", command=None,
width=buttonWidth)
self.goButton.pack(side=LEFT, anchor=W, expand=NO)

self.quitButton = Button(middleFrame, text="Quitter",
command=self.quit, width=buttonWidth)
self.quitButton.pack(side=RIGHT, anchor=E, expand=NO)

middleFrame.pack(side=TOP, anchor=SE, expand=NO, fill=X, padx=5,
pady=2)

bottomFrame = Frame(master)
self.statusBar = StatusBar(bottomFrame, 80)
self.statusBar.set('%s', 'blahblah')
self.statusBar.pack(side=BOTTOM, fill=X)
bottomFrame.pack(side=BOTTOM, expand=NO, fill=X)

def togglelog(self):
self.logvisible = not self.logvisible
if self.logvisible:
self.logBox.pack(side=LEFT, expand=YES, fill=BOTH)
self.viewmenu.entryconfig(0, label='Cacher log...')
else:
self.logBox.pack_forget()
self.viewmenu.entryconfig(0, label='Afficher log...')

class StatusBar(Frame):
def __init__(self, master, largeur=0):
Frame.__init__(self, master)
self.label = Label(self, width=largeur, bd=1, relief=SUNKEN,
anchor=W)
self.label.pack(fill=X)

def set(self, format, *args):
self.label.config(text=format % args)
self.label.update_idletasks()

root = Tk()

app = App(root)
root.mainloop()
try:
root.destroy()
except:
pass



Bonjour,

J'ai testé ton programme, je ne vois pas de problème j'ai bien
l'apparition et la disparition de logbox.
Je suis sous Linux Mandriva 2007.
Tu ne nous dit pas sous quel os.

Jacques.

Avatar
Eric Brunel
On Mon, 02 Apr 2007 14:35:51 +0200, Pierre Maurette
wrote:

Bonjour,

Je tourne en rond sur un souci avec une petite interface TKinter. Je me
suis décidé à réduire mon appli pour soumettre ici. Je précise que le
Button "inputlines" est en réalité un objet de taille devant évoluer, au
moins au cours du développement.
Le problème est la gestion de l'option menu "Afficher log..." / "Cacher
log..." dès qu'on a resizé en vertical. Je suis prêt à bloquer le
resizing (au moins en vertical) mais toute tentative - j'ai essayé pas
mal de truc - empêche l'apparition / disparition de logBox de
fonctionner.
[snip code]


La seule solution que je vois est de gérer ça "à la main": à l'entrée dans
togglelog, récupérer la géométrie de la fenêtre par sa méthode geometry,
en extraire sa hauteur, et lui ajouter ou retirer la taille voulue pour le
panneau de log, suivant qu'on l'affiche ou qu'on le cache. Il suffit alors
de redimensionner la fenêtre via la même méthode geometry et ça devrait le
faire. Soit dit en passant, ce serait plus pratique de donner au log une
taille fixe de façon à ne pas devoir se creuser la tête à chaque fois pour
savoir qu'est-ce qu'on doit ajouter/enlever.

Quelques remarques, on va dire "stylistiques":
- Malgré ce qui est dit dans beaucoup de tutoriaux, faire une classe App
qui spécialise Frame est une couennerie. Tu as d'ailleurs dû t'en rendre
compte, vu que tu n'arrêtes pas de faire des accès à la variable globale
root. Dans ton cas, le mieux est de spécialiser la classe Tk et d'utiliser
self au lieu de root partout.
- Ton layout est trop compliqué pour pack; utilise grid. Ca a l'air plus
compliqué au début, mais au final, ce sera beaucoup plus pratique, surtout
pour ce que tu veux faire. En particulier, il est *beaucoup* plus facile
de s'assurer que la barre de boutons est toujours visible en utilisant
grid qu'en utilisant pack. (Juste pour info, j'ai essayé de modifier ton
code pour inclure la solution dont je te parle ci-dessus; j'ai abandonné:
le pack ne faisait jamais ce que je voulais...).

HTH
--
python -c "print ''.join([chr(154 - ord(c)) for c in
'U(17zX(%,5.zmz5(17l8(%,5.Z*(93-965$l7+-'])"

Avatar
Pierre Maurette
[...]
Bonjour,


Bonjour, et merci d'avoir pris un peu de temps pour lancer le truc....

J'ai testé ton programme, je ne vois pas de problème j'ai bien
l'apparition et la disparition de logbox.


Oui, mais si l'utilisateur redimensionne en Y, ne serait-ce que d'un
point, et même en revenant exactement à la hauteur initiale, ça ne
redimensionne plus lors de l'apparition ou disparition de logBox.

Je suis sous Linux Mandriva 2007.
Tu ne nous dit pas sous quel os.


Je teste en parallèle sous Windows XP et Ubuntu 6.10, c'est d'ailleurs
une obligation par rapport à mon projet. Je viens de tester, le
comportement est identique.

J'ai corrigé mon layout, j'ai déplacé et modifié le pack des trois
sous-frames en fin de méthode __init__ :

self.topFrame.pack(side=TOP, anchor=NW, expand=YES, fill=BOTH,
padx=5, pady=2)
bottomFrame.pack(side=BOTTOM, expand=NO, fill=X)
middleFrame.pack(side=BOTTOM, anchor=SE, expand=NO, fill=X,
padx=5, pady=2)

Ce qui m'embêtait et qui m'embête toujours un peu c'est de ne pas
comprendre certains comportement. Mais en pratique, je dois mieux
déterminer le comportement que je souhaite. Et sans doute ajouter un
Frame logFrame pour plus de souplesse.

Pour le comportement, soit une fenêtre de log déétachée, soit une Frame
initialisée à une ligne de haut et une minsize pour l'empêcher de
disparaître. Dans ce cas je m'affranchis du "toggle", pas pratique pour
l'utilisateur.

Jacques.


--
Pierre Maurette

Avatar
Pierre Maurette

[...]

La seule solution que je vois est de gérer ça "à la main": à l'entrée dans
togglelog, récupérer la géométrie de la fenêtre par sa méthode geometry, en
extraire sa hauteur, et lui ajouter ou retirer la taille voulue pour le
panneau de log, suivant qu'on l'affiche ou qu'on le cache. Il suffit alors de
redimensionner la fenêtre via la même méthode geometry et ça devrait le
faire. Soit dit en passant, ce serait plus pratique de donner au log une
taille fixe de façon à ne pas devoir se creuser la tête à chaque fois pour
savoir qu'est-ce qu'on doit ajouter/enlever.


(Voir aussi ma réponse à Jacques, en particulier sur le fait que je
n'avais pas clairement défini l'ergonomie exacte souhaitée)
J'ai testé des trucs avec la méthode geometry, mais ça ne fonctionnait
pas comme attendu, certainement à cause d'autres erreurs.

Quelques remarques, on va dire "stylistiques":
- Malgré ce qui est dit dans beaucoup de tutoriaux, faire une classe App qui
spécialise Frame est une couennerie. Tu as d'ailleurs dû t'en rendre compte,
vu que tu n'arrêtes pas de faire des accès à la variable globale root. Dans
ton cas, le mieux est de spécialiser la classe Tk et d'utiliser self au lieu
de root partout.


OK, merci, je vois ce que vous voulez dire, je vais sans doute
modifier.

- Ton layout est trop compliqué pour pack; utilise grid. Ca a l'air plus
compliqué au début, mais au final, ce sera beaucoup plus pratique, surtout
pour ce que tu veux faire. En particulier, il est *beaucoup* plus facile de
s'assurer que la barre de boutons est toujours visible en utilisant grid
qu'en utilisant pack.


Je n'ai pour l'instant rien vu de grid. Je vais quand même essayer de
terminer ce truc avec pack. A vrai dire, l'apparition - disparition de
la fenêtre log est apparue plus tard, dans mon idée j'utilise TKinter
pour des trucs de base, cliquodromer un outil typiquement, avec
wxPython pour les trucs plus compliqués. Mais je suis sorti de "mon
idée".

(Juste pour info, j'ai essayé de modifier ton code pour
inclure la solution dont je te parle ci-dessus; j'ai abandonné: le pack ne
faisait jamais ce que je voulais...).


Ben voilà ... ;-)

--
Pierre Maurette

Avatar
Pierre Maurette
Bon, voilà, pas mal de recherches pour pas grand'chose. En effet mon
idée n'était pas ergonomiquement valable, ça m'aura juste permis de
progresser un peu.

Une question d'abord: avec un widget texte, un ScrolledText en
l'occurrence, il y a une height minimum, ici 4 lignes il me semble,
même si on configure height à 0, 1, etc. Y a-t-il un moyen de descendre
en dessous de cette valeur ?

Pour le reste:

- J'ai pigé un truc. Si on ne positionne pas geometry par programme,
les dimensions restent libres. Donc le toggle fonctionne, le fenêtre
s'agrandit *et diminue* correctement. Mais toute action de resize de
l'utilisateur positionne les éléments de geometry, et indépendamment
pour le x et le y. Il suffit de les libérer par un self.geometry('') au
bon endroit pour que le comportement soit bien plus sain. J'aurais aimé
ne "libérer" que le x, mais je n'ai pas trouvé, et également conserver
la dernière hauteur de logBox, mais je bute sur un problème pixels
(points) vs lignes (caract-=ères). Je laisse tomber.

- Un autre truc: je n'arrivais pas à lire les dimensions (geometry ou
winfo_????) en fin d'__init__ pour positionner un minsize. Il suffisait
de faire un .update() juste avant, même si visuellement il n'était pas
utile.

Merci encore à tous....

Voilà le code corrigé:

# coding: iso-8859-1

from Tkinter import *
from ScrolledText import *

class RepFTPApp(Tk):
def __init__(self):
Tk.__init__(self)
menu = Menu(self)
self.config(menu=menu, bg='red')
self.viewmenuitems = {True:'Cacher log...', False:'Afficher
log...'}
self.viewmenu = Menu(menu, tearoff=0)
menu.add_cascade(label="Affichage", menu=self.viewmenu)
self.viewmenu.add_command()

self.topFrame = Frame(self, bg='blue')
self.inputlines = Button(self.topFrame, width€, height)
self.inputlines.pack(side=TOP, anchor=NW, fill=X)

self.logFrame = Frame(self, bg='cyan')
self.logBox = ScrolledText(self.logFrame, width=0)
self.logBox.pack(side=LEFT, expand=YES, fill=BOTH)
self.logvisible = True
self.viewmenu.entryconfig(0, command=self.togglelog)

self.middleFrame = Frame(self, bg='yellow')
buttonWidth=8

self.goButton = Button(self.middleFrame, text="Go !",
command=None, width=buttonWidth)
self.goButton.pack(side=LEFT, anchor=W, expand=NO)

self.quitButton = Button(self.middleFrame, text="Quitter",
command=self.quit, width=buttonWidth)
self.quitButton.pack(side=RIGHT, anchor=E, expand=NO)

self.bottomFrame = Frame(self, bg='blue')
self.statusBar = StatusBar(self.bottomFrame, 80)
self.statusBar.set('%s', 'blahblah')
self.statusBar.pack(side=BOTTOM, fill=X)

self.topFrame.pack(side=TOP, anchor=NW, expand=NO, fill=X,
padx=5, pady=2)
self.bottomFrame.pack(side=BOTTOM, expand=NO, fill=X)
self.middleFrame.pack(side=BOTTOM, anchor=SE, expand=NO,
fill=X, padx=5, pady=2)

self.togglelog()

self.update()
self.minsize(self.winfo_width() - 100, self.winfo_height())

def togglelog(self):
def __repack(log):
self.geometry('')
self.logFrame.pack_forget()
if log:
self.logFrame.pack(side=TOP, anchor=NW, expand=YES,
fill=BOTH, padx=5, pady=2)

self.logvisible = not self.logvisible
__repack(self.logvisible)
self.viewmenu.entryconfig(0,
label=self.viewmenuitems[self.logvisible])

class StatusBar(Frame):
def __init__(self, master, largeur=0):
Frame.__init__(self, master)
self.label = Label(self, width=largeur, bd=1, relief=SUNKEN,
anchor=W)
self.label.pack(fill=X)

def set(self, format, *args):
self.label.config(text=format % args)
self.label.update_idletasks()

app = RepFTPApp()
app.mainloop()
try:
app.destroy()
except:
pass

--
Pierre Maurette