Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inheriting from Frame or not in a Tkinter application

Tags:

python

tkinter

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()   
like image 334
foosion Avatar asked Sep 04 '11 15:09

foosion


People also ask

Do I need a frame tkinter?

The Frame widget is very important for the process of grouping and organizing other widgets in a somehow friendly way. It works like a container, which is responsible for arranging the position of other widgets. It uses rectangular areas in the screen to organize the layout and to provide padding of these widgets.

What does a tkinter application class inherit from to create the GUI window?

Defining a Tkinter object-oriented window First, define an App class that inherits from the tk.Tk class. Inside the __init__() method, call the __init__() method of the tk.Tk class. Second, create a new instance of the App class and call the mainloop() method to display the root window.

What is the difference between tkinter window and frame?

Tk creates the root window. Every tkinter application must have a root window. When you instantiate it you also create a tcl interpreter that is used by tkinter. Frame is just a widget, designed to be a container for other widgets.


3 Answers

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.

like image 119
Bryan Oakley Avatar answered Sep 30 '22 16:09

Bryan Oakley


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.

like image 38
unutbu Avatar answered Sep 30 '22 16:09

unutbu


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() 
like image 29
user3061910 Avatar answered Sep 30 '22 15:09

user3061910