web-dev-qa-db-fra.com

Hériter de Frame ou non dans une application Tkinter

J'ai vu deux façons fondamentales de mettre en place un programme tkinter. Y a-t-il une raison de préférer l'un à l'autre?

from Tkinter import *

class Application():
    def __init__(self, root, title):
        self.root = root
        self.root.title(title) 

        self.label = Label(self.root, text='Hello')
        self.label.grid(row=0, column=0)  

root = Tk()
app = Application(root, 'Sample App')
root.mainloop()

et

from Tkinter import *

class Application(Frame):
    def __init__(self, title, master=None):
        Frame.__init__(self, master)
        self.grid()
        self.master.title(title) 

        self.label = Label(self, text='Hello')
        self.label.grid(row=0, column=0) 

app = Application('Sample App')
app.mainloop()   
31
foosion

L'option que je préfère * est d'hériter de la classe Tk. Je pense que c'est le choix le plus raisonnable puisque la fenêtre est, en fait, votre application. Hériter de Frame n'a plus de sens pour moi alors hériter de Button ou Canvas ou Label. Puisque vous ne pouvez avoir qu'une seule racine, il est logique que c'est de cela que vous héritez.

Je pense également que cela rend le code plus lisible si vous effectuez l'importation en tant que import Tkinter as tk plutôt que from Tkinter import *. Tous vos appels mentionnent alors explicitement le module tk. Je ne le recommande pas pour tous les modules, mais pour moi, cela a du sens avec Tkinter.

Par exemple:

import Tkinter as tk

class SampleApp(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        self.label = tk.Label(text="Hello, world")
        self.label.pack(padx=10, pady=10)

app = SampleApp()
app.mainloop()

* Remarque: depuis la rédaction de cette réponse, j'ai changé de position. Je préfère maintenant hériter de Frame plutôt que Tk. Il n'y a pas vraiment d'avantage dans un sens ou dans l'autre, c'est plus un choix philosophique qu'autre chose. Quoi qu'il en soit, je pense que si vous héritez de Frame ou Tk, je pense que l'un ou l'autre choix est meilleur que le premier exemple du code qui hérite de rien.

Le seul léger avantage hérité de Frame a sur Tk est dans le cas où vous voulez que votre application prenne en charge plusieurs fenêtres identiques. Dans ce cas, l'héritage de Frame vous permet de créer la première fenêtre en tant qu'enfant de root et des fenêtres supplémentaires en tant qu'enfants d'instances de Toplevel. Cependant, j'ai vu très peu de programmes qui ont besoin de le faire.

Pour plus d'informations sur la façon dont je pense que les programmes Tkinter devraient être structurés, voir ma réponse à la question Structure du programme Python Tkinter .

26
Bryan Oakley

Un cadre est généralement utilisé comme maître de géométrie pour d'autres widgets . Puisqu'une application a généralement de nombreux widgets, vous souhaiterez souvent les contenir tous dans un cadre, ou au moins utiliser le cadre pour ajouter un borderwidth, un rembourrage ou autre élément.

De nombreux exemples d'extraits que vous pourriez trouver sur le Web n'utilisent pas de cadre, car ils veulent simplement montrer une fonctionnalité dans le plus petit nombre de codes.

Donc, utilisez un cadre si vous en avez besoin, sinon, ne le faites pas.

Edit : Je pense que la meilleure façon d'organiser une interface graphique est donnée dans ce Tkinter tutorial :

simpleApp.py:

import Tkinter as tk

class SimpleApp(object):
    def __init__(self, master, **kwargs):
        title=kwargs.pop('title')
        frame=tk.Frame(master, **kwargs)
        frame.pack()
        self.label = tk.Label(frame, text=title)
        self.label.pack(padx=10,pady=10)

if __name__=='__main__':
    root = tk.Tk()
    app = SimpleApp(root,title='Hello, world')
    root.mainloop()

C'est principalement comme votre premier exemple dans lequel SimpleApp hérite de object, pas Frame. Je pense que c'est mieux que de sous-classer Frame car nous ne remplaçons pas les méthodes Frame. Je préfère penser à SimpleApp comme ayant un Frame plutôt que d'être un Frame.

Avoir SimpleApp sous-classe object présente un avantage significatif sur la sous-classe tk.Tk, cependant: il facilite l'intégration de SimpleApp dans une application plus grande:

import simpleApp
import Tkinter as tk

class BigApp(object):
    def __init__(self, master, **kwargs):
        title=kwargs.pop('title')
        frame=tk.Frame(master, **kwargs)
        frame.pack()
        self.simple = simpleApp.SimpleApp(frame,title=title)
        frame.pack(padx=10, pady=10)
        self.simple2 = simpleApp.SimpleApp(frame,title=title)    
        frame.pack()

if __name__=='__main__':
    root = tk.Tk()
    app = BigApp(root,title='Hello, world')
    root.mainloop()

Ainsi, simpleApp.py peut être un script autonome ainsi qu'un module importable. Si vous essayez ceci avec SimpleApp héritant de tk.Tk, vous vous retrouvez avec des fenêtres supplémentaires indésirables.

12
unutbu

Il peut être avantageux de définir votre objet de niveau supérieur pour hériter de Tk au lieu de Frame. L'avantage survient lorsque vous avez un élément dynamique dans votre interface graphique, par exemple un Label dont vous souhaitez définir le contenu avec un textvariable=foo au lieu de text= 'Label text'.

Dans ce cas, il est très utile d'utiliser les objets Tkinter.DoubleVar, Tkinter.IntVar Et Tkinter.StringVar Pour contenir les données, car l'interface graphique se mettra automatiquement à jour chaque fois que ces objets seront définis. Cependant, pour utiliser ces objets, vous devez spécifier leur maître en tant qu'instance racine Tkinter.Tk() en cours d'exécution. C'est plus facile si vous faites explicitement de votre objet principal une sous-classe de Tkinter.Tk, Puis que vous générez des cadres et des widgets, vous pouvez donc transmettre l'instance Tk et configurer correctement vos variables.

Voici un court exemple de programme pour illustrer l'idée.

import Tkinter as tk       

class Tkclass(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        app=Application(self)
        app.master.title("Animal to Meat")
        app.mainloop()

class Application(tk.Frame):    

    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        self.grid(sticky=tk.N+tk.S+tk.E+tk.W)
        self.meatvar = tk.StringVar(master=parent)
        self.meatvar.set("Meat?")
        self.createWidgets()

    def createWidgets(self):
        top=self.winfo_toplevel()                
        top.rowconfigure(0, weight=1)            
        top.columnconfigure(0, weight=1)         
        self.rowconfigure(0, weight=1)           
        self.columnconfigure(0, weight=1) 
        self.columnconfigure(1, weight=1)  
        self.columnconfigure(2, weight=1)  
        self.columnconfigure(3, weight=1)  

        self.cowButton = tk.Button(self, text='Cow', command=self.setBeef)
        self.cowButton.grid(row=0,column=0)
        self.pigButton = tk.Button(self, text='Pig',command=self.setPork)
        self.pigButton.grid(row=0,column=1)
        self.meatLabel = tk.Label(self)
        self.meatLabel.configure(textvariable=self.meatvar)
        self.meatLabel.grid(row=0,column=2)
        self.quit = tk.Button(self, text='Quit',command=self.QuitApp)
        self.quit.grid(row=0, column=3)           

    def setBeef(self):
        self.meatvar.set("Beef")

    def setPork(self):
        self.meatvar.set("Pork")

    def QuitApp(self):
        top=self.winfo_toplevel()
        top.quit()

main = Tkclass() 
1
user3061910