Building a basic GUI application step-by-step in Python with Tkinter

 

In this blog you will learn to build a basic GUI application in Python step by step.

The aim is:

  • Mastering most common GUI techniques (widgets layout, GUI constraints, event binding, etc.)
  • Understanding each and every single method and parameter used here.
  • Serve as a basis for building your own GUI applications.

You will learn basic tips:

  • building a GUI application class,
  • creating widgets,
  • laying them in containers,
  • attaching and handling events,
  • manipulating widgets values,
  • etc.

Our constraints for this blog:

  • Do the thing with Tkinter (the standard GUI toolkit provided with Python).
  • Program it the right way
  • Use plain english
  • Explicit is better than implicit  😉

Tkinter wraps Tcl/tk so that it can be used it in Python.

If properly used – your GUI will work on all systems (Windows, Linux, MacOS X…).


OBJECTIVE :

Our objective is to create a very simple application like this:

app

A widget is a GUI element (a button, a checkbox, a label, a tab, a pane…).
Our application has 3 widgets:

  • A text field (“Enter text here”)
  • A button (“Click me !”)
  • A blue label (“Hello !”)

The behaviour we want:

  • When ENTER is pressed in the text field, or the button is clicked, the blue label will display the text which was entered.
  • We want to constraint window resize so that window can only be resized horizontally.
  • If the window is resized, the text field and blue label will expand horizontally, like this:

app_resized

We will do this simply by Python- Tkinter.


STEP 1 : Import the GUI toolkit module

Let’s import the required module.

import Tkinter

Tkinter is part of the standard Python distribution, so we expect it to be present. We just import it.


STEP 2 : Create a class

It’s best to put our application in a class.


import tkinter

class simpleapp_tk(tkinter.tk):

In tkinter, we inherit from tkinter.tk, which is the base class for standard windows.


STEP 3 : The constructor

import Tkinter

class simpleapp_tk(Tkinter.Tk):
    def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)

simpleapp_tk derives from Tkinter.Tk, so we have to call the Tkinter.Tk constructor (Tkinter.Tk.__init__()).

A GUI is a hierarchy of objects: A button may be contained in a pane which is contained in a tab which is contained in a window, etc.
So each GUI element has a parent (its container, usually).
That’s why both constructor have a parent parameter.
Keeping track of parents is usefull when we have to show/hide a group of widgets, repaint them on screen or simply destroy them when application exits.


STEP 4 : Keep track of our parent

import Tkinter

class simpleapp_tk(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
        self.parent = parent

It is a good habit, when building any type of GUI component, to keep a reference to our parent.


STEP 5 : Initialize our GUI

import Tkinter

class simpleapp_tk(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
        self.initialize()

 

    def initialize(self):
pass  

It’s usually best to have the portion of code which creates all the GUI elements (button, text fields…) separate from the logic of the program.
That’s why we create the initialize() method. We will create all our widgets (buttons, text field, etc.) in this method.

For the moment, the Tkinter version contains nothing (hence the pass instruction which does nothing.)


STEP 6 : Creation of main


import Tkinter

class simpleapp_tk(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()

def initialize(self):
        pass

if __name__ == “__main__”:
app = simpleapp_tk(None)
app.title(‘my application’)

Now we have a class, let’s use it !
We create a main which is executed when the program is run from the command-line.

In Tkinter, we instanciate our class (app=simpleapp_tk()). We give it no parent (None), because it’s the first GUI element we build.
We also give a title to our window (app.title()).


STEP 7 : Loop it !


import Tkinter

class simpleapp_tk(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()

def initialize(self):
        pass

if __name__ == “__main__”:
app = simpleapp_tk(None)
app.title(‘my application’)
    app.mainloop()

Now, we tell program to loop with .mainloop()

What’s that ?

It means that each program will now loop indefinitely, waiting for events (user clicking a button, pressing keys, operating system asking our application to quit, etc.).
The Tkinter and wxPython main loops will receive these events and handle them accordingly.
This is call event-driven programming (Because the program will do nothing but wait for events, and only react when it receives an event.)


STEP 8 : Layout manager


import Tkinter

class simpleapp_tk(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()

def initialize(self):
self.grid()

if __name__ == “__main__”:
app = simpleapp_tk(None)
app.title(‘my application’)
app.mainloop()

There are several way to put widgets in a window (or another container): Add them side by site horizontally, vertically, in grid, etc.

So there are different classes (called layout managers) capable of placing the widgets in containers in different ways. Some are more flexible than others.
Each container (window, pane, tab, dialog…) can have its own layout manager.

I recommend the grid layout manager. It’s a simple grid where you put your widgets, just like you would place things in a spreadsheet (Excel, OpenOffice Calc…).
For example: Put a button at column 2, row 1.  Pur a checkbox at column 5, row 3.  etc.
If you have to learn one, learn this one.

In Tkinter, calling self.grid() will simply create the grid layout manager, and tell our window to use it.

Now let’s add widgets.


STEP 9 : Adding the text entry


import Tkinter

class simpleapp_tk(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()

def initialize(self):
        self.grid()

self.entry = Tkinter.Entry(self)
self.entry.grid(column=0,row=0,sticky=’EW’)

if __name__ == “__main__”:
app = simpleapp_tk(None)
app.title(‘my application’)
app.mainloop()

To add a widget, you always:

  • first create the widget
  • and add it to a layout manager

Frist, we create the widget:

In Tkinter, we create the Entry widget (self.entry=Tkinter.Entry())

Now, time to add them to the layout manager.

In Tkinter, we call the .grid() method on the widget. We indicate where to put it in the grid (column=0, row=0).
When a cell grows larger than the widget is contains, you can ask the widget to stick to some edges of the cell. That’s the sticky=’EW’.
(E=east (left), W=West (right), N=North (top), S=South (bottom))
We specified ‘EW’, which means the widget will try to stick to both left and right edges of its cell.
(That’s one of our goals: Have the text entry expand when the window is resized.)


STEP 10 : Adding the button 


import Tkinter

class simpleapp_tk(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()

def initialize(self):
        self.grid()

        self.entry = Tkinter.Entry(self)
        self.entry.grid(column=0,row=0,sticky=’EW’)

        button = Tkinter.Button(self,text=u”Click me !”)
button.grid(column=1,row=0)

if __name__ == “__main__”:
app = simpleapp_tk(None)
app.title(‘my application’)
app.mainloop()

Its quite easy in this case: Create the button, and add it.

Note that in this case, we do not keep a referece to the button (because we will not read or alter its value later).


STEP 11 : Adding the label

import Tkinter

class simpleapp_tk(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()

def initialize(self):
        self.grid()

        self.entry = Tkinter.Entry(self)
        self.entry.grid(column=0,row=0,sticky=’EW’)

button = Tkinter.Button(self,text=u”Click me !”)
        button.grid(column=1,row=0)

        label = Tkinter.Label(self,
anchor=”w”,fg=”white”,bg=”blue”)
label.grid(column=0,row=1,columnspan=2,sticky=’EW’)

if __name__ == “__main__”:
app = simpleapp_tk(None)
app.title(‘my application’)
app.mainloop()

We also create and add the label.

  • This is a Label object in Tkinter.

For the color: We use a white text on a blue background.

  • In Tkinter, it’s fg=”white”,bg=”blue”.

For the text alignment:

  • In Tkinteranchor=”w” means that the text should be left aligned in the label.

For the label position in grid:

  • In Tkinter, this is again the .grid() method, but this time we also span it across two


    cells (so that it appears below the text field and the button.): This is the columnspan=2 parameter.

For the label expansion:

  • Again, we use sticky=”EW” for Tkinter

STEP 12 : Enable resizing


import Tkinter

class simpleapp_tk(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()

def initialize(self):
        self.grid()

        self.entry = Tkinter.Entry(self)
        self.entry.grid(column=0,row=0,sticky=’EW’)

button = Tkinter.Button(self,text=u”Click me !”)
        button.grid(column=1,row=0)

label = Tkinter.Label(self,
                              anchor=”w”,fg=”white”,bg=”blue”)
        label.grid(column=0,row=1,columnspan=2,sticky=’EW’)

        self.grid_columnconfigure(0,weight=1)

if __name__ == “__main__”:
app = simpleapp_tk(None)
app.title(‘my application’)
app.mainloop()

Now we tell the layout manager to resize its columns and rows when the window is resized.
Well, not the rows. Only the first column (0).

Thats grid_columnconfigure() for Tkinter..

Now try resizing the window.
See ?
Text fields and blue label now properly resize to adapt to window size.

But that’s not pretty when resizing the window vertically:


So let’s add a contraint so that the user can only resize the window horizontally.


STEP 13 : Adding constraint


import Tkinter

class simpleapp_tk(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()

def initialize(self):
        self.grid()

        self.entry = Tkinter.Entry(self)
        self.entry.grid(column=0,row=0,sticky=’EW’)

button = Tkinter.Button(self,text=u”Click me !”)
        button.grid(column=1,row=0)

label = Tkinter.Label(self,
                              anchor=”w”,fg=”white”,bg=”blue”)
        label.grid(column=0,row=1,columnspan=2,sticky=’EW’)

self.grid_columnconfigure(0,weight=1)
        self.resizable(True,False)

if __name__ == “__main__”:
app = simpleapp_tk(None)
app.title(‘my application’)
app.mainloop()

In Tkinter, we use .resizable(True,False) to prevent the vertical resizing of the window


STEP 14 : Adding event handlers


import Tkinter

class simpleapp_tk(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()

def initialize(self):
        self.grid()

        self.entry = Tkinter.Entry(self)
        self.entry.grid(column=0,row=0,sticky=’EW’)
        self.entry.bind(“”, self.OnPressEnter)

button = Tkinter.Button(self,text=u”Click me !”,
command=self.OnButtonClick
)
        button.grid(column=1,row=0)

label = Tkinter.Label(self,
                              anchor=”w”,fg=”white”,bg=”blue”)
        label.grid(column=0,row=1,columnspan=2,sticky=’EW’)

self.grid_columnconfigure(0,weight=1)
self.resizable(True,False)

    def OnButtonClick(self):
print “You clicked the button !”

def OnPressEnter(self,event):
print “You pressed enter !”

if __name__ == “__main__”:
app = simpleapp_tk(None)
app.title(‘my application’)
app.mainloop()

Event handlers are methods which will be called when something happens in the GUI.
We bind the event handlers to specific widgets on specific events only.
So let’s do something when the button is clicked or ENTER pressed in the text field.

  • We create a OnButtonClick() method which will be called then the button is clicked.
  • We create a OnPressEnter() method which will be called then ENTER is pressed in the text field.

Then we bind these methods to the widgets.

For the button:

  • In Tkinter, simply add command=self.OnButtonClick

For the text field:

  • In Tkinter, we use a .bind() method.
    “” is the key we want to catch.
    self.OnPressEnter is the method we want to be fired when this event is catched.

Therefore:

  • Clicking on the button will trigger the OnButtonClick() method.
  • Pressing ENTER in the text field will trigger the OnPressEnter() method.

STEP 15 : Changing the label

import Tkinter

class simpleapp_tk(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()

def initialize(self):
        self.grid()

        self.entry = Tkinter.Entry(self)
        self.entry.grid(column=0,row=0,sticky=’EW’)
self.entry.bind(“”, self.OnPressEnter)

button = Tkinter.Button(self,text=u”Click me !”,
command=self.OnButtonClick)
        button.grid(column=1,row=0)

        self.labelVariable = Tkinter.StringVar()
        label = Tkinter.Label(self,textvariable=self.labelVariable,
                              anchor=”w”,fg=”white”,bg=”blue”)
        label.grid(column=0,row=1,columnspan=2,sticky=’EW’)

self.grid_columnconfigure(0,weight=1)
self.resizable(True,False)

def OnButtonClick(self):
        self.labelVariable.set(“You clicked the button !”)

def OnPressEnter(self,event):
self.labelVariable.set(“You pressed enter !”)

if __name__ == “__main__”:
app = simpleapp_tk(None)
app.title(‘my application’)
app.mainloop()

Now let’s change the label.

  • Under Tkinter, this is a bit more tricky. You have to:
    • create a special Tkinter variable (self.labelVariable = Tkinter.StringVar())
    • then bind it to the widget (textvariable=self.labelVariable)
    • then use set() or get() to set or read its value (self.labelVariable.set(“You clicked the button !”))

In Tkinter, each time you want to read or set values in a widgets (text field, label, checkbox, radio button, etc.), you have to create a Tkinter variable and bind it to the widget. There are several Tkinter variable types (StringVar, IntVar, DoubleVar, BooleanVar).


STEP 16 : Display the value


import Tkinter

class simpleapp_tk(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()

def initialize(self):
        self.grid()

self.entryVariable = Tkinter.StringVar()
        self.entry = Tkinter.Entry(self,textvariable=self.entryVariable)
        self.entry.grid(column=0,row=0,sticky=’EW’)
self.entry.bind(“”, self.OnPressEnter)
        self.entryVariable.set(u”Enter text here.”)

button = Tkinter.Button(self,text=u”Click me !”,
command=self.OnButtonClick)
        button.grid(column=1,row=0)

self.labelVariable = Tkinter.StringVar()
        label = Tkinter.Label(self,textvariable=self.labelVariable,
                              anchor=”w”,fg=”white”,bg=”blue”)
        label.grid(column=0,row=1,columnspan=2,sticky=’EW’)
        self.labelVariable.set(u”Hello !”)

self.grid_columnconfigure(0,weight=1)
self.resizable(True,False)

def OnButtonClick(self):
self.labelVariable.set( self.entryVariable.get()+” (You clicked the button)” )

def OnPressEnter(self,event):
        self.labelVariable.set( self.entryVariable.get()+” (You pressed ENTER)” )

if __name__ == “__main__”:
app = simpleapp_tk(None)
app.title(‘my application’)
app.mainloop()

Now let’s read the value of the text field and display it in the blue label.

In Tkinter, we have to create a Tkinter variable again, so that we can do self.entryVariable.get().

As we have a text variable to access the Tkinter text field content, we can also set the default value (“Enter text here.” and “Hello !”)


STEP 17 : Small refinement: auto-select the text field


import Tkinter

class simpleapp_tk(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()

def initialize(self):
        self.grid()

        self.entryVariable = Tkinter.StringVar()
        self.entry = Tkinter.Entry(self,textvariable=self.entryVariable)
        self.entry.grid(column=0,row=0,sticky=’EW’)
self.entry.bind(“”, self.OnPressEnter)
self.entryVariable.set(u”Enter text here.”)

button = Tkinter.Button(self,text=u”Click me !”,
command=self.OnButtonClick)
        button.grid(column=1,row=0)

self.labelVariable = Tkinter.StringVar()
        label = Tkinter.Label(self,textvariable=self.labelVariable,
                              anchor=”w”,fg=”white”,bg=”blue”)
        label.grid(column=0,row=1,columnspan=2,sticky=’EW’)
self.labelVariable.set(u”Hello !”)

self.grid_columnconfigure(0,weight=1)
self.resizable(True,False)
        self.entry.focus_set()
self.entry.selection_range(0, Tkinter.END)

def OnButtonClick(self):
self.labelVariable.set( self.entryVariable.get()+” (You clicked the button)” )
        self.entry.focus_set()
self.entry.selection_range(0, Tkinter.END)

def OnPressEnter(self,event):
        self.labelVariable.set( self.entryVariable.get()+” (You pressed ENTER)” )
        self.entry.focus_set()
self.entry.selection_range(0, Tkinter.END)

if __name__ == “__main__”:
app = simpleapp_tk(None)
app.title(‘my application’)
app.mainloop()

Here is a small refinement: The text field will be automatically selected is the user presses ENTER or clicks the button, so that he/she can immediately type a new text again (replacing the existing one.).

We first set the focus in this element (focus_set() or SetFocus()), then select all the text (selection_range() or SetSelection()).


STEP 18 : Tkinter resize hiccup

import Tkinter

class simpleapp_tk(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()

def initialize(self):
        self.grid()

        self.entryVariable = Tkinter.StringVar()
        self.entry = Tkinter.Entry(self,textvariable=self.entryVariable)
        self.entry.grid(column=0,row=0,sticky=’EW’)
self.entry.bind(“”, self.OnPressEnter)
self.entryVariable.set(u”Enter text here.”)

button = Tkinter.Button(self,text=u”Click me !”,
command=self.OnButtonClick)
        button.grid(column=1,row=0)

self.labelVariable = Tkinter.StringVar()
        label = Tkinter.Label(self,textvariable=self.labelVariable,
                              anchor=”w”,fg=”white”,bg=”blue”)
        label.grid(column=0,row=1,columnspan=2,sticky=’EW’)
self.labelVariable.set(u”Hello !”)

self.grid_columnconfigure(0,weight=1)
self.resizable(True,False)
        self.update()
self.geometry(self.geometry())
        self.entry.focus_set()
        self.entry.selection_range(0, Tkinter.END)

def OnButtonClick(self):
self.labelVariable.set( self.entryVariable.get()+” (You clicked the button)” )
self.entry.focus_set()
        self.entry.selection_range(0, Tkinter.END)

def OnPressEnter(self,event):
        self.labelVariable.set( self.entryVariable.get()+” (You pressed ENTER)” )
self.entry.focus_set()
        self.entry.selection_range(0, Tkinter.END)

if __name__ == “__main__”:
app = simpleapp_tk(None)
app.title(‘my application’)
app.mainloop()

Tkinter has a small resize hiccup: It will try to accomodate window size all the time to widgets. This is nice, but not always a desired behaviour.
For example, type a loooooooong text in the text field, press ENTER: The window grows.
Type a short text again, press ENTER: the window shrinks.

You usually do not want your application window grow and shrink automatically all the time. The users will not like that.

That’s why we fix the size of the window by setting the window size to its own size (self.geometry(self.geometry())).
This way, Tkinter will stop trying to accomodate window size all the time.
(And we perform an update() to make sure Tkinter has finished rendering all widgets and evaluating their size.)

We’re done !

On running the above code, we will get the output like this:

app

And after clicking on the maximize button, the appeared window will be extended this this:

app_resized

Blog written by;

Rohit Sharma


 

Leave a comment