Sleek and professional GUI with Tkinter: A step-by-step guide

Mohit Patel
10 min readFeb 18, 2023

--

A scalable multipage application that uses custom widgets to make the code more readable and reduce the amount of code needed.

What’s the target?

Before we dive into the world of GUI building with Tkinter, let me share with you our ultimate goal — to create the most awesome, user-friendly, and pythonic interface with the least amount of code repetition possible. In other words, we’re going to be making magic with minimal effort!

So, Let’s get started

In this article journey we’ll be covering

  1. Basic Tkinter concepts
  2. Planning and design process
  3. Multipage settings
  4. Custom widgets
  5. Integrating everything

1.Basic Tkinter Concept

import tkinter as tk

# ------------------------------- ROOT WINDOW ----------------------------------


class TkinterApp(tk.Tk):

def __init__(self):
tk.Tk.__init__(self)
self.title("Attendance Tracking App")


root = TkinterApp()
root.mainloop()

The root window in Tkinter can be compared to the foundation of a building. Just like a building needs a solid foundation to stand on, the GUI application needs a root window as its base. From this root window, you can build upon and add different frames, similar to rooms in a building. Each frame acts as a separate unit and can contain different widgets, like furniture in a room. The frames can also be organized and arranged within the root window, similar to how rooms are organized in a building. So, the root window and frames form the structure of your Tkinter application, making it easier to manage and organize.

Think of this code as the blueprint of our magnificent house, and as we progress, we’ll be adding each room with care and precision, but before that, let’s take a moment to envision the final appearance of our dream home.

2. Planning and design

Before diving into the code, I spent some time thinking about the layout of the app, and how the frames would be arranged within the root window. I decided to use Tkinter’s built-in frame widgets and arranged them in a way that would provide a clean and intuitive user experience. This involved careful consideration of the placement and size of each frame, and how they would interact with one another.

After considering the layout of the app, I decided to include a sidebar (1) that would serve as the primary navigation for the app. The sidebar would have two frames: one for branding elements like the university name and logo (1.1), and the other for the submenus (1.2). Adjacent to the sidebar, I included a header (2) that would change depending on the option selected from the submenu. Below the header, I added a main frame (3) that would be used to render the other pages that I would be creating. Each of the pages that I’ll be creating will have some kind of user input that will be accommodated inside the selection bar (3.1) and the figure corresponding to these inputs will be accommodated inside the plot frame (3.2) and both selection bar and plot frame are inside main frame.

Are you ready to make rooms ?

import tkinter as tk

# -------------------------- DEFINING GLOBAL VARIABLES -------------------------
# these are just the colors we'll be using to paint our rooms (frames)
selectionbar_color = '#eff5f6'
sidebar_color = '#F5E1FD'
header_color = '#53366b'
visualisation_frame_color = "#ffffff"

# ------------------------------- ROOT WINDOW ----------------------------------


class TkinterApp(tk.Tk):

def __init__(self):
tk.Tk.__init__(self)
self.title("Attendance Tracking App")

# ------------- BASIC APP LAYOUT -----------------

self.geometry("1100x700")
self.resizable(0, 0)
self.title('Attendance Tracking System')
self.config(background=selectionbar_color)
icon = tk.PhotoImage(file='images\\WU_logo.png')
self.iconphoto(True, icon)

# ---------------- HEADER ------------------------

self.header = tk.Frame(self, bg=header_color)
self.header.place(relx=0.3, rely=0, relwidth=0.7, relheight=0.1)

# ---------------- SIDEBAR -----------------------
# SIDEBAR FRAME
self.sidebar = tk.Frame(self, bg=sidebar_color)
self.sidebar.place(relx=0, rely=0, relwidth=0.3, relheight=1)

# BRANDING FRAME (UNIVERSITY LOGO AND NAME)
self.brand_frame = tk.Frame(self.sidebar, bg=sidebar_color)
self.brand_frame.place(relx=0, rely=0, relwidth=1, relheight=0.15)
self.uni_logo = icon.subsample(9)
logo = tk.Label(self.brand_frame, image=self.uni_logo, bg=sidebar_color)
logo.place(x=5, y=20)

uni_name = tk.Label(self.brand_frame,
text='ABC',
bg=sidebar_color,
font=("", 15, "bold")
)
uni_name.place(x=55, y=27, anchor="w")

uni_name = tk.Label(self.brand_frame,
text='University',
bg=sidebar_color,
font=("", 15, "bold")
)
uni_name.place(x=55, y=60, anchor="w")

# SUBMENU FRAME (FOR PLACING SUBMENUS)
self.submenu_frame = tk.Frame(self.sidebar, bg=sidebar_color)
self.submenu_frame.place(relx=0, rely=0.2, relwidth=1, relheight=0.85)

#----------------------- SUBMENU------------------------------
# TO BE ADDED IN THE SECTION 5 OF ARTICLE
# 5.Integrating Everything together
#-------------------------------------------------------------

# -------------------- MAIN FRAME ---------------------------

main_frame = tk.Frame(self)
main_frame.place(relx=0.3, rely=0.1, relwidth=0.7, relheight=0.9)

# ------------------ MULTI PAGE SETTINGS ----------------------
# TO BE ADDED IN THE SECTION 3 OF ARTICLE
# 3.Multipage settings
#--------------------------------------------------------------


app = TkinterApp()
app.mainloop()

3. Multipage settings

Now, imagine our magnificent house has a secret room, a magical room that can transform into any other room in the house with just a flick of a switch. This is exactly what the show_frame() method does which is defines in the code below. By calling the show_frame() method, we can change the current visible room to any other room, or in this case, frame, that we have created within our main frame(Frame1, Frame2). With this powerful tool in our arsenal, we now have the capability to create a multi-page GUI application that can switch between different frames with ease, making it a seamless user experience.

The classes Frame1 and Frame2 are used to create the different pages in our Tkinter application. These classes can contain different widgets, such as buttons, labels, and text boxes, which will be used to create the desired functionality for each page.

In terms of our room analogy, each class defines what a room should look like and what it should contain, such as its walls, floor, ceiling, furniture, etc., these our basically our widgets.

# ------------------------------- ROOT WINDOW ----------------------------------


class TkinterApp(tk.Tk):

def __init__(self):
tk.Tk.__init__(self)

# ------------------------------------------------------------------
# - ALL THE STUFF FROM PREVIOUS SECTION -
# ------------------------------------------------------------------


# -------------------- MULTI PAGE SETTINGS ------------------------
main_frame = tk.Frame(self)
main_frame.place(relx=0.3, rely=0.1, relwidth=0.7, relheight=0.9)

# ADDING FRAMES TO MAIN FRAME
self.frames = {}

for F in (Frame1, Frame2):
frame = F(main_frame, self)
self.frames[F] = frame
frame.place(relx=0, rely=0, relwidth=1, relheight=1)
self.show_frame(Frame1)

def show_frame(self, cont):
'''
this function enable us to switch between frames
'''
frame = self.frames[cont]
frame.tkraise()


class Frame1(tk.Frame):

def __init__(self, parent, controller):

tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Frame1", font=("Helvetica", 17))
label.pack(fill=tk.BOTH)


class Frame2(tk.Frame):

def __init__(self, parent, controller):

tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Frame2", font=("Helvetica", 17))
label.pack(fill=tk.BOTH)


root = TkinterApp()
root.mainloop()

Why to use classes for different pages?

One of the main reasons for using a class for each page in a tkinter application is organization and maintainability. By separating the code for each page into a separate class, it becomes much easier to manage the different elements and functionality of each page. Additionally, having each class inherit from the tkinter Frame class, allows for easy integration of each page as a frame within the overall application.

What is inheritance?

Inheritance is a mechanism in object-oriented programming where one class can inherit the properties and methods of another class. Think of it as creating a custom frame by using the properties of an already existing frame (tkinter’s Frame class) and being able to add or modify some functionality according to your needs. So, the new class you created is a frame in itself, because it inherited all the properties of the Frame class, and it can be used as a frame in your program.

Before delving into the interconnections of the show_frame() method and buttons to enable the navigation of our GUI application, I would like to introduce the concept of custom widgets. Once the idea of custom widgets has been thoroughly explained, we can then seamlessly link the show_frame() method with buttons to bring the multi-page functionality to life and offer a smooth user experience.

4. Custom Widgets

What is custom widget?

Think of Tkinter widgets as furniture pieces that you can arrange and rearrange in different rooms, or frames, in your house. Custom widgets take this a step further by allowing you to create new and unique pieces that perfectly fit the design and style of your house. You can achieve this by either combining existing Tkinter widgets or by crafting a completely new piece from scratch. With custom widgets, you have the freedom to create a truly customized and tailored home for your GUI application.

when creating custom widgets in tkinter, it is often useful to inherit from the tkinter Frame class, and then pack other tkinter widgets inside of it. This allows you to create a cohesive, self-contained widget that can be easily placed and manipulated as a single unit.

Creating custom widgets using inheritance is similar to creating classes for each page of an app, please refer to inheritance explained above. In both cases, you are creating a new class that inherits properties and methods from an existing class i.e. the tkinter Frame class. The main difference is that with custom widgets, you are creating a reusable component that can be used multiple times within the app, whereas with classes for each page, you are creating a specific instance of a page that is unique to the app. In both cases, you can add additional functionality and attributes to the new class as needed.

What we will create ?

We’re creating a custom widget for a submenu because it will allow us to easily make multiple submenus in our application without having to start from scratch and write new code for each one.

what is submenu ?

A Submenu in our program offers a convenient way to categorize related functions under a common heading. This makes it easier for users to navigate and find the features they need. The submenu is made up of a heading and a series of options that users can interact with. Simply clicking on these options will trigger a specific function linked to that option. The function could be to provide quick access to different frames or pages within the application, such as Frame1 or Frame2, or it can serve any other purpose you require.

Where to place it?

we’ve to add our submenu to the submenu frame which is there inside sidebar frame as shown in the figure below.

With a clear understanding of what a submenu is and its purpose, let’s delve into the code and see how to implement it in our program.

# ------------------------  CUSTOM WIDGETS  ------------------------------------
# SIDEBAR SUBMENU WIDGET

class SidebarSubMenu(tk.Frame):
"""
A submenu which can have multiple options and these can be linked with
functions.
"""
def __init__(self, parent, sub_menu_heading, sub_menu_options):
"""
parent: The frame where submenu is to be placed
sub_menu_heading: Heading for the options provided
sub_menu_operations: Options to be included in sub_menu
"""
tk.Frame.__init__(self, parent)

# setting submenu frame color to sidebar color
self.config(bg=sidebar_color)

# creating and placing submenu heading Label
self.sub_menu_heading_label = tk.Label(self,
text=sub_menu_heading,
bg=sidebar_color,
fg="#333333",
font=("Arial", 10)
)
self.sub_menu_heading_label.place(x=30, y=10, anchor="w")

# creating and placing submenu heading Label
sub_menu_sep = tk.Separator(self, orient='horizontal')
sub_menu_sep.place(x=30, y=30, relwidth=0.8, anchor="w")

# creating and placing buttons for each option
self.options = {}
for n, x in enumerate(sub_menu_options):
self.options[x] = tk.Button(self,
text=x,
bg=sidebar_color,
font=("Arial", 9, "bold"),
bd=0,
cursor='hand2',
activebackground='#ffffff',
)
self.options[x].place(x=30, y=45 * (n + 1), anchor="w")

5.Integrating Everything Together

Now that we have looked at all the individual components of the GUI, let’s put them together. We’ll start by placing submenu1 in submenu_frame, as we discussed earlier. Then, we'll assign functions to the options in submenu1, so that users can trigger specific actions by clicking on them.

import tkinter as tk
from tkinter import ttk

# -------------------------- DEFINING GLOBAL VARIABLES -------------------------

selectionbar_color = '#eff5f6'
sidebar_color = '#F5E1FD'
header_color = '#53366b'
visualisation_frame_color = "#ffffff"

# ------------------------------- ROOT WINDOW ----------------------------------


class TkinterApp(tk.Tk):
"""
The class creates a header and sidebar for the application. Also creates
two submenus in the sidebar, one for attendance overview with options to
track students and modules, view poor attendance and another for
database management, with options to update and add new modules to the
database.
"""
def __init__(self):
tk.Tk.__init__(self)
self.title("Attendance Tracking App")

# ------------- BASIC APP LAYOUT -----------------

self.geometry("1100x700")
self.resizable(0, 0)
self.title('Attendance Tracking System')
self.config(background=selectionbar_color)
icon = tk.PhotoImage(file='images\\LU_logo.png')
self.iconphoto(True, icon)

# ---------------- HEADER ------------------------

self.header = tk.Frame(self, bg=header_color)
self.header.place(relx=0.3, rely=0, relwidth=0.7, relheight=0.1)

# ---------------- SIDEBAR -----------------------
# CREATING FRAME FOR SIDEBAR
self.sidebar = tk.Frame(self, bg=sidebar_color)
self.sidebar.place(relx=0, rely=0, relwidth=0.3, relheight=1)

# UNIVERSITY LOGO AND NAME
self.brand_frame = tk.Frame(self.sidebar, bg=sidebar_color)
self.brand_frame.place(relx=0, rely=0, relwidth=1, relheight=0.15)
self.uni_logo = icon.subsample(9)
logo = tk.Label(self.brand_frame, image=self.uni_logo, bg=sidebar_color)
logo.place(x=5, y=20)

uni_name = tk.Label(self.brand_frame,
text='ABC',
bg=sidebar_color,
font=("", 15, "bold")
)
uni_name.place(x=55, y=27, anchor="w")

uni_name = tk.Label(self.brand_frame,
text='University',
bg=sidebar_color,
font=("", 15, "bold")
)
uni_name.place(x=55, y=60, anchor="w")

# SUBMENUS IN SIDE BAR

# # SUBMENU 1
self.submenu_frame = tk.Frame(self.sidebar, bg=sidebar_color)
self.submenu_frame.place(relx=0, rely=0.2, relwidth=1, relheight=0.85)
submenu1 = SidebarSubMenu(self.submenu_frame,
sub_menu_heading='SUBMENU 1',
sub_menu_options=["Display Frame1",
"Display Frame2",
]
)
submenu1.options["Display Frame1"].config(
command=lambda: self.show_frame(Frame1)
)
submenu1.options["Display Frame2"].config(
command=lambda: self.show_frame(Frame2)
)

submenu1.place(relx=0, rely=0.025, relwidth=1, relheight=0.3)

# -------------------- MULTI PAGE SETTINGS ----------------------------

container = tk.Frame(self)
container.config(highlightbackground="#808080", highlightthickness=0.5)
container.place(relx=0.3, rely=0.1, relwidth=0.7, relheight=0.9)

self.frames = {}

for F in (Frame1,
Frame2,
):

frame = F(container, self)
self.frames[F] = frame
frame.place(relx=0, rely=0, relwidth=1, relheight=1)
self.show_frame(Frame1)

def show_frame(self, cont):
"""
The function 'show_frame' is used to raise a specific frame (page) in
the tkinter application and update the title displayed in the header.

Parameters:
cont (str): The name of the frame/page to be displayed.
title (str): The title to be displayed in the header of the application.

Returns:
None
"""
frame = self.frames[cont]
frame.tkraise()


# ------------------------ MULTIPAGE FRAMES ------------------------------------


class Frame1(tk.Frame):
def __init__(self, parent, controller):

tk.Frame.__init__(self, parent)

label = tk.Label(self, text='Frame 1', font=("Arial", 15))
label.pack()


class Frame2(tk.Frame):
def __init__(self, parent, controller):

tk.Frame.__init__(self, parent)

label = tk.Label(self, text='Frame 2', font=("Arial", 15))
label.pack()


# ----------------------------- CUSTOM WIDGETS ---------------------------------

class SidebarSubMenu(tk.Frame):
"""
A submenu which can have multiple options and these can be linked with
functions.
"""
def __init__(self, parent, sub_menu_heading, sub_menu_options):
"""
parent: The frame where submenu is to be placed
sub_menu_heading: Heading for the options provided
sub_menu_operations: Options to be included in sub_menu
"""
tk.Frame.__init__(self, parent)
self.config(bg=sidebar_color)
self.sub_menu_heading_label = tk.Label(self,
text=sub_menu_heading,
bg=sidebar_color,
fg="#333333",
font=("Arial", 10)
)
self.sub_menu_heading_label.place(x=30, y=10, anchor="w")

sub_menu_sep = ttk.Separator(self, orient='horizontal')
sub_menu_sep.place(x=30, y=30, relwidth=0.8, anchor="w")

self.options = {}
for n, x in enumerate(sub_menu_options):
self.options[x] = tk.Button(self,
text=x,
bg=sidebar_color,
font=("Arial", 9, "bold"),
bd=0,
cursor='hand2',
activebackground='#ffffff',
)
self.options[x].place(x=30, y=45 * (n + 1), anchor="w")


app = TkinterApp()
app.mainloop()

If you want to explore the complete code for the GUI application that we’ve been discussing, I have created a GitHub repository for you. Inside the repository, you’ll find three folders, namely TrialGUI, py_files, and ipynb_files. The TrialGUI folder contains the code that I shared earlier to help you understand the concepts better. The py_files folder has the complete code for the final GUI that I demonstrated in the video at the start. Additionally, there is another folder, ipynb_files, that contains the same code, but it can be opened in Jupyter Notebook. You can find the link to the GitHub repository below.

Theflash444123/Sleek-GUI-with-Tkinter (github.com)

About Me

Hey there, I’m Mohit and I’m currently pursuing a Masters in Data Science. I didn’t have any formal programming background but I was super curious to learn how to make my own computer applications. I discovered Tkinter and realized it was the perfect tool for creating visually-appealing and useful applications.

Working on this project helped me dive into the world of GUI programming and taught me some important concepts about object-oriented programming. I hope my experience can inspire others, especially beginners and non-programmers, to explore the power of Python and Tkinter, and to show them that with a bit of effort and perseverance, they too can create their own GUI applications.

--

--