38

I've seen two basic ways of setting up a tkinter program. Is there any reason to prefer one to the other?

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()

and

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()   
nbro
  • 15,395
  • 32
  • 113
  • 196
foosion
  • 7,619
  • 25
  • 65
  • 102

3 Answers3

41

The option I prefer* is to inherit from the class Tk. I think it is the more reasonable choice since the window is, in effect, your application. Inheriting from Frame doesn't make any more sense to me then inheriting from Button or Canvas or Label. Since you can only have a single root, it makes sense that that is what you inherit from.

I also think it makes the code more readable if you do the import as import Tkinter as tk rather than from Tkinter import *. All of your calls then explicitly mention the tk module. I don't recommend this for all modules, but to me it makes sense with Tkinter.

For example:

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()

* Note: since originally writing this answer I have changed my position. I now prefer to inherit from Frame rather than Tk. There's no real advantage one way or the other, it's more of a philosophical choice than anything else. Regardless, I believe that whether you inherit from Frame or Tk, I think either choice is better than the first example in the code that inherits from nothing.

The one slight advantage inheriting from Frame has over Tk is in the case where you want your application to support multiple identical windows. In that case, inheriting from Frame lets you create the first window as a child of root, and additional windows as children of instances of Toplevel. However, I've seen very few programs that ever have a need to do this.

For more information about how I think Tkinter programs should be structured, see my answer to the question Python Tkinter program structure.

Community
  • 1
  • 1
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • What's the advantage of inheriting from Tk rather than just root = tk() as in my first example above? Does inheriting from Tk give you the same "beautification" flexibility as inheriting from Frame? – foosion Sep 04 '11 at 18:58
  • from module import * always make me nervous, but it's so common for Tkinter that I've gotten over it. Is there a real risk? I'm not so much worried about readability as some name conflict. – foosion Sep 04 '11 at 19:01
  • 1
    @foosion: There is no advantage nor disadvantage. You still have exactly the same "beautification" flexibility. You still have the option of embedding a frame, if you want to get a particular border effect. I just don't understand the reasoning of having your main application object be a child of some other object. From a logical perspective, the application object should be the top-most object in a hierarchy. – Bryan Oakley Sep 04 '11 at 19:42
  • @foosion: the risk to "from tkinter import *" is the same as for any other module. Given that tkinter classes have very generic names (Button, Label, etc), it would be easy to accidentally create classes of the same name and cause name clashes. – Bryan Oakley Sep 04 '11 at 19:45
  • Changing import Tkinter to import Tix appears to work without other changes when using Tix widgets (e.g., ScrolledListBox) and inheriting from Tk (or root = Tk()), but not when inheriting from Frame. If so, this would be an advantage to inheriting from Tk – foosion Sep 05 '11 at 16:16
  • If you inherit from Tk, you can resize with self.resizable(True,False). This doesn't work if inheriting from Frame. – foosion Sep 06 '11 at 16:21
  • @foosion: but you can still set the resizable bit on the window, you just use `root` (or whatever you call it) instead of `self`. However, there's almost never a good reason to make a window a fixed size. If a user wants a window to be larger or smaller, who are we (as the app designer) to tell them otherwise? It's much better (and just as easy) to design your app to have good resize behavior. – Bryan Oakley Sep 06 '11 at 19:05
  • you're right - I should have said I found it simpler when inheriting from Tk, because I could just use self. Why is @Bryan disappearing when I type it at the beginning of a comment? – foosion Sep 07 '11 at 00:30
  • Just curious on what planet designing good resize behavior is as easy as writing self.resizable(False, False). Just for the sake of argument ;p – Darren Ringer Jul 28 '15 at 17:20
  • @DarrenRinger: Frankly, if you take a methodical approach to doing your widget layout, proper resize behavior is really easy. Remember, the goal is to write software that meets the needs of the _user_, rather than to meet your needs as a developer. – Bryan Oakley Jul 28 '15 at 17:25
  • @BryanOakley: Well my needs as a developer are to meet the needs of the user so I don't really understand that. As for ease, that's like saying "Going to the winery to buy a nice bottle of vintage wine for your date is just as easy as getting a bottle of Mad Dog 20/20 from the corner store - because after all, it's your date's needs, not your needs". The latter, while inferior, definitely seems easier. – Darren Ringer Jul 28 '15 at 18:43
  • @DarrenRinger: this is all speculation without knowing the needs of the user. Most often, they are best served by allowing them to resize the window to meet their needs. That isn't true 100% of the time, but it's close. You can't always control fonts, lighting, eyesight, sistance to the screen -- all problems that can potentially be worked around by giving the user a bit of control. As for the wine analogy, that's pretty far off base. A better analogy is that you want to force the user to drink from a very specific glass even if they have can't easily use it due to a physical limitation. – Bryan Oakley Jul 28 '15 at 18:48
  • @BryanOakley: That's a good argument and I guess I'm really just hung up on the semantics of the word "easy" - to me that just implies time. You are correct that every situation is different but in a case where the user insists on having the window methods finalized in 30 seconds, and all you have so far is a mockup for prototyping, it is much easier to lock things in place where they already are. If you measure easy by LOC then it's very quantifiable. Realistically, I think LOC is almost meaningless and I agree with all of your points ;) – Darren Ringer Jul 28 '15 at 18:52
  • If you prefer inheriting from `Frame`, you should move that recommendation to the top of your answer. – Stevoisiak Feb 21 '18 at 15:53
14

A Frame is usually used as a geometry master for other widgets. Since an application usually has numerous widgets, you'll often want to contain them all in a Frame, or at least use the Frame to add some borderwidth, padding, or other nicety.

Many example snippets you might find on the web do not use a Frame because they just want to demonstrate some feature in the shortest amount of code.

So, use a Frame if you need it, otherwise, do not.

Edit: I think the best way to organize a GUI is given in this 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()

This is mainly like your first example in that SimpleApp inherits from object, not Frame. I think this is better than subclassing Frame since we are not overriding any Frame methods. I prefer to think of SimpleApp as having a Frame rather than being a Frame.

Having SimpleApp subclass object does have a significant advantage over subclassing tk.Tk, however: it makes it easy to embed SimpleApp in a larger app:

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()

Thus, simpleApp.py can be a stand-alone script as well as an importable module. If you try this with SimpleApp inheriting from tk.Tk, you end up with extra undesired windows.

martineau
  • 119,623
  • 25
  • 170
  • 301
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • I can use grid for complex layouts in both versions. Is the difference that I can do a prettier border with Frame? – foosion Sep 04 '11 at 15:32
  • @foosion: The [Frame docs](http://effbot.org/tkinterbook/frame.htm) show a few configuration options (yes, mostly concerning borders) that Frame has that can not be done with grid. Use the Frame if you need one of those, otherwise, do not. – unutbu Sep 04 '11 at 15:39
  • is the conclusion that we might as well use Frame generally, because there's no real additional cost, it provides a few more options and it's not worth thinking about which way to go for every program? – foosion Sep 04 '11 at 15:49
  • @foosion: I think that is reasonable. – unutbu Sep 04 '11 at 15:58
  • borderwidth=10, relief=SUNKEN is rather ugly, IMO – foosion Sep 04 '11 at 17:32
  • @foosion: Hey, that was my best design of the day :) – unutbu Sep 04 '11 at 18:13
  • There's no harm in inheriting from `Frame`, but neither does it make much sense. All frames must have a parent, so why not make your app inherit from the parent of all widgets (ie: the root window)? – Bryan Oakley Sep 04 '11 at 18:30
  • See my comment below about Tix – foosion Sep 05 '11 at 16:16
  • Also, my comment about resizable. There do appear to be some things that work when inheriting from Tk that don't work when inheriting from Frame – foosion Sep 06 '11 at 16:22
  • @foosion: there's nothing that works or doesn't work -- everything you can do by inheriting from Frame you can do by inheriting from Tk. The only difference is in how you reference the main window (for example, `self` versus `root` or `master` or whatever you choose). – Bryan Oakley Sep 06 '11 at 19:07
  • I've tried the example and changed the inheritance to `tk.Tk` and no extra windows did appear. – m3nda Nov 09 '15 at 01:28
1

There can be an advantage to setting your top level object to inherit from Tk instead of Frame. The advantage arises when you have dynamic element to your GUI, e.g. a Label whose contents you want to set with a textvariable=foo instead of text= 'Label text'.

In this case, it is very helpful to use the Tkinter.DoubleVar, Tkinter.IntVar, and Tkinter.StringVar objects to hold the data, since the GUI will automatically update whenever these objects are set. However, to use these objects, you must specify their master as the root Tkinter.Tk() instance running. This is easier if you explicitly make your main object be a subclass of Tkinter.Tk, then have that generate frames and widgets, so you can pass along the Tk instance and set up your variables properly.

Here is a short example program to illustrate the idea.

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() 
tijko
  • 7,599
  • 11
  • 44
  • 64
  • 1
    Your answer isn't entirely correct. You can specify _any_ widget as the master, you don't need to give it the root window. You can use the tkinter variables just fine when inheriting from Frame. – Bryan Oakley Dec 03 '13 at 15:22