Python Tkinter Frame Grid

When I first started building desktop apps in Python, I struggled to keep my widgets organized. Everything looked cluttered until I mastered the combination of Frames and the Grid manager.

In this tutorial, I will show you how to use the Tkinter Frame with the Grid geometry manager to create professional, structured layouts.

Why Use Frames with the Grid Manager?

In my years of developing Python GUIs, I’ve found that placing every widget directly into the main window is a recipe for disaster.

Frames act as containers. They allow you to group related widgets, making your code easier to maintain and your UI more flexible.

When you nest a Grid inside a Frame, you gain pinpoint control over where every button, label, and entry field sits.

Method 1: Create a Basic Two-Column Layout

One of the most common tasks I handle is creating a split-screen interface. For this example, let’s build a simple US Census Data Entry form.

I prefer this method when I need a clear distinction between a sidebar (navigation) and a main content area (form).

We will create a main container and then split it into two frames: a navigation frame on the left and a data entry frame on the right.

import tkinter as tk
from tkinter import ttk

def save_data():
    print("Data saved to the federal database.")

# Initialize the main window
root = tk.Tk()
root.title("US Census Bureau - Resident Data Entry")
root.geometry("600x400")

# 1. Create the Sidebar Frame
sidebar = tk.Frame(root, width=150, bg="#2c3e50", height=400, relief='sunken', borderwidth=2)
sidebar.grid(row=0, column=0, sticky="ns")

# 2. Create the Main Content Frame
main_frame = tk.Frame(root, bg="#ecf0f1", width=450, height=400)
main_frame.grid(row=0, column=1, sticky="nsew")

# Configure grid weights so the main frame expands
root.grid_columnconfigure(1, weight=1)
root.grid_rowconfigure(0, weight=1)

# Adding Widgets to Sidebar
lbl_menu = tk.Label(sidebar, text="Menu", bg="#2c3e50", fg="white", font=("Arial", 12, "bold"))
lbl_menu.grid(row=0, column=0, padx=10, pady=20)

btn_dashboard = tk.Button(sidebar, text="Dashboard", width=15)
btn_dashboard.grid(row=1, column=0, padx=10, pady=5)

# Adding Widgets to Main Content (The Form)
lbl_title = tk.Label(main_frame, text="Resident Information Form", font=("Arial", 16), bg="#ecf0f1")
lbl_title.grid(row=0, column=0, columnspan=2, pady=20)

tk.Label(main_frame, text="Full Name:", bg="#ecf0f1").grid(row=1, column=0, padx=20, pady=10, sticky="e")
ent_name = tk.Entry(main_frame)
ent_name.grid(row=1, column=1, padx=20, pady=10, sticky="w")

tk.Label(main_frame, text="State of Residence:", bg="#ecf0f1").grid(row=2, column=0, padx=20, pady=10, sticky="e")
state_cb = ttk.Combobox(main_frame, values=["New York", "California", "Texas", "Florida", "Illinois"])
state_cb.grid(row=2, column=1, padx=20, pady=10, sticky="w")

btn_submit = tk.Button(main_frame, text="Submit to Registry", command=save_data, bg="#27ae60", fg="white")
btn_submit.grid(row=3, column=0, columnspan=2, pady=30)

root.mainloop()

You can refer to the screenshot below to see the output.

Tkinter Frame Grid

In the code above, I used sticky=”ns” on the sidebar to make sure it stretches from the top to the bottom.

I also used columnconfigure on the root window. This is a crucial step I often see beginners skip; it ensures that the main frame fills the remaining horizontal space if the window is resized.

Method 2: Use Nested Frames for Complex Dashboards

When I work on larger projects, like a Stock Market Tracker for Wall Street, a single frame isn’t enough. I use nested frames to create “blocks” of information.

Think of this like building with LEGO blocks. Each block is a frame with its own grid system.

The Scenario: A Portfolio Dashboard

We want a top section for the “Market Status” and a bottom section divided into “Watchlist” and “Account Summary.”

import tkinter as tk

def refresh_quotes():
    print("Fetching latest NYSE/NASDAQ prices...")

app = tk.Tk()
app.title("AmeriTrade Insights Dashboard")
app.geometry("800x500")

# Top Frame (Market Header)
header_frame = tk.Frame(app, bg="#1a1a1a", height=80)
header_frame.grid(row=0, column=0, columnspan=2, sticky="ew")

lbl_market = tk.Label(header_frame, text="S&P 500: 5,123.42 (+0.45%) | NASDAQ: 16,274.94 (-0.12%)", 
                      fg="#00ff00", bg="#1a1a1a", font=("Courier", 14))
lbl_market.pack(pady=20) # Note: You can use pack inside a frame even if the frame is in a grid!

# Bottom Left Frame (Watchlist)
watchlist_frame = tk.LabelFrame(app, text="Market Watchlist", padx=10, pady=10)
watchlist_frame.grid(row=1, column=0, padx=10, pady=10, sticky="nsew")

stocks = [("AAPL", "185.92"), ("MSFT", "415.10"), ("TSLA", "175.22"), ("AMZN", "178.15")]

for i, (symbol, price) in enumerate(stocks):
    tk.Label(watchlist_frame, text=symbol, font=("Arial", 10, "bold")).grid(row=i, column=0, sticky="w", pady=2)
    tk.Label(watchlist_frame, text=f"${price}").grid(row=i, column=1, sticky="e", padx=20)

# Bottom Right Frame (Account Summary)
account_frame = tk.LabelFrame(app, text="Federal Tax & Account Info", padx=10, pady=10)
account_frame.grid(row=1, column=1, padx=10, pady=10, sticky="nsew")

tk.Label(account_frame, text="Buying Power:").grid(row=0, column=0, sticky="w")
tk.Label(account_frame, text="$12,450.00", fg="blue").grid(row=0, column=1, sticky="e")

tk.Label(account_frame, text="Estimated Tax Due:").grid(row=1, column=0, sticky="w")
tk.Label(account_frame, text="$2,105.50", fg="red").grid(row=1, column=1, sticky="e")

btn_refresh = tk.Button(account_frame, text="Update Prices", command=refresh_quotes)
btn_refresh.grid(row=2, column=0, columnspan=2, pady=20)

# Adjust weights so frames expand evenly
app.grid_columnconfigure(0, weight=1)
app.grid_columnconfigure(1, weight=1)
app.grid_rowconfigure(1, weight=1)

app.mainloop()

You can refer to the screenshot below to see the output.

Python Tkinter Frame Grid

Using tk.LabelFrame instead of a standard tk.Frame provides a built-in border and title.

In my experience, this makes the UI instantly more intuitive for users, as it visually categorizes the data without extra labels.

Master the Grid Sticky Property

When you place a frame inside a grid, it defaults to the center of the cell. This usually looks awkward.

I always use the sticky attribute to align frames. Here is a quick cheat sheet based on what I use daily:

  • sticky="ew": Stretches the frame horizontally.
  • sticky="ns": Stretches the frame vertically.
  • sticky="nsew": Fills the entire available space in the cell.

If you are designing a responsive app that needs to look good on both a 13-inch MacBook and a 27-inch Dell monitor, nsew combined with grid_columnconfigure is your best friend.

Method 3: Dynamic Grid Alignment in Frames

Sometimes, you don’t know how many items will be in your grid. For example, a US Postal Service (USPS) Package Tracker might have a variable number of status updates.

I use a loop to dynamically place widgets into a frame’s grid. This keeps the code clean.

import tkinter as tk

def add_tracking_step():
    # Example logic for adding dynamic rows
    pass

root = tk.Tk()
root.title("USPS Tracking - Official Tool")

container = tk.Frame(root, padx=20, pady=20)
container.grid(row=0, column=0)

tracking_data = [
    ("Oct 24, 08:00 AM", "Arrived at Facility", "Chicago, IL"),
    ("Oct 23, 02:30 PM", "In Transit", "Indianapolis, IN"),
    ("Oct 22, 11:00 AM", "Departed Facility", "Louisville, KY"),
    ("Oct 21, 09:00 AM", "Picked Up", "Nashville, TN")
]

# Headers
tk.Label(container, text="Timestamp", font=("Arial", 10, "bold")).grid(row=0, column=0, padx=10, sticky="w")
tk.Label(container, text="Status", font=("Arial", 10, "bold")).grid(row=0, column=1, padx=10, sticky="w")
tk.Label(container, text="Location", font=("Arial", 10, "bold")).grid(row=0, column=2, padx=10, sticky="w")

# Dynamic Rows
for index, (time, status, loc) in enumerate(tracking_data):
    # index + 1 because row 0 is the header
    tk.Label(container, text=time).grid(row=index+1, column=0, padx=10, pady=5, sticky="w")
    tk.Label(container, text=status).grid(row=index+1, column=1, padx=10, pady=5, sticky="w")
    tk.Label(container, text=loc).grid(row=index+1, column=2, padx=10, pady=5, sticky="w")

root.mainloop()

You can refer to the screenshot below to see the output.

Tkinter Frame Grid in Python

When using loops, always keep track of your row index.

I often use a variable like current_row if I need to add buttons or summaries below the looped content. This prevents widgets from overlapping in the grid.

Important Considerations with Grid and Frames

Over the years, I’ve noticed a few common pitfalls that can break your layout.

Don’t Mix Geometry Managers

Never use .pack() and .grid() in the same master window or the same frame.

The application will freeze as the two managers fight for control over the widget size. However, it is perfectly fine to use .grid() to position a Frame, and then use .pack() inside that specific Frame.

Manage Spacing

Use padx and pady within the .grid() call to create “breathing room.”

Without padding, your US-based business apps will look cramped and unprofessional. I usually find that padx=10 and pady=5 is the “sweet spot” for most desktop resolutions.

I hope you found this tutorial useful and that it helps you build better layouts with Tkinter.

Using Frames with the Grid manager is the most efficient way to handle complex interfaces in Python. Once you get the hang of weights and sticky properties, you can build almost any layout imaginable.

In this article, I showed you how to create a sidebar layout, a multi-section dashboard, and a dynamic tracking table using USA-specific data examples.

You may also read:

51 Python Programs

51 PYTHON PROGRAMS PDF FREE

Download a FREE PDF (112 Pages) Containing 51 Useful Python Programs.

pyython developer roadmap

Aspiring to be a Python developer?

Download a FREE PDF on how to become a Python developer.

Let’s be friends

Be the first to know about sales and special discounts.