Why Menus, Toolbars, and Status Bars Matter
Many desktop applications share a familiar structure: a menubar at the top for discoverable commands, a toolbar for quick access to common actions, and a status bar at the bottom for lightweight feedback. In Tkinter, you typically implement these with a Menu attached to the root window, a Frame containing Button widgets for the toolbar, and a Label docked to the bottom for the status bar.
Building a Menubar with Cascades
A Tkinter menubar is a Menu instance configured on the top-level window. Cascades (like File/Edit/Help) are submenus you attach to the menubar. Each submenu can contain commands, separators, check/radio items, and more. Here we focus on commands, separators, and keyboard accelerators.
Step-by-step: Create the menubar and cascades
1) Create the menubar and attach it to the window with root.config(menu=menubar). 2) Create submenus (File/Edit/Help). 3) Add each submenu to the menubar using add_cascade.
import tkinter as tk
from tkinter import filedialog, messagebox
root = tk.Tk()
root.title("Editor Shell")
menubar = tk.Menu(root)
root.config(menu=menubar)
file_menu = tk.Menu(menubar, tearoff=0)
edit_menu = tk.Menu(menubar, tearoff=0)
help_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="File", menu=file_menu)
menubar.add_cascade(label="Edit", menu=edit_menu)
menubar.add_cascade(label="Help", menu=help_menu)Note on tearoff=0: this disables the dashed “tear-off” line that would otherwise allow detaching the menu (a legacy behavior many apps don’t use).
Menu Commands, Separators, and Accelerators
Menu items are usually added with add_command. A separator is added with add_separator to visually group related actions. Accelerators are shown as text (for example, Ctrl+S) via the accelerator option, but they do not automatically bind the keyboard shortcut—you must bind the key sequence yourself.
Continue in our app.
You can listen to the audiobook with the screen off, receive a free certificate for this course, and also have access to 5,000 other free online courses.
Or continue reading below...Download the app
Step-by-step: Define commands and wire them into the File menu
In a real app, these commands would operate on a document (often a Text widget). Here, we keep the actions simple and focus on structure and status updates.
status_var = tk.StringVar(value="Ready")
current_path = None
def set_status(msg):
status_var.set(msg)
def cmd_new(event=None):
global current_path
current_path = None
set_status("New file")
def cmd_open(event=None):
global current_path
path = filedialog.askopenfilename(
title="Open",
filetypes=[("Text files", "*.txt"), ("All files", "*")]
)
if not path:
set_status("Open canceled")
return
current_path = path
set_status(f"Opened: {path}")
def cmd_save(event=None):
global current_path
if current_path is None:
path = filedialog.asksaveasfilename(
title="Save As",
defaultextension=".txt",
filetypes=[("Text files", "*.txt"), ("All files", "*")]
)
if not path:
set_status("Save canceled")
return
current_path = path
set_status(f"Saved: {current_path}")
def cmd_exit(event=None):
root.destroy()Now add these commands to the File menu, including separators and accelerator labels:
file_menu.add_command(label="New", command=cmd_new, accelerator="Ctrl+N")
file_menu.add_command(label="Open...", command=cmd_open, accelerator="Ctrl+O")
file_menu.add_command(label="Save", command=cmd_save, accelerator="Ctrl+S")
file_menu.add_separator()
file_menu.add_command(label="Exit", command=cmd_exit, accelerator="Alt+F4")Step-by-step: Bind keyboard shortcuts (accelerators)
The accelerator option only displays the shortcut text. To make it work, bind the corresponding key sequences to the same command functions. For cross-platform behavior, you can use Control bindings for most shortcuts; Alt+F4 is typically handled by the window manager on Windows, but you can still provide an explicit binding if desired.
root.bind_all("<Control-n>", cmd_new)
root.bind_all("<Control-o>", cmd_open)
root.bind_all("<Control-s>", cmd_save)Tip: Using event=None in the command signatures lets the same function work both as a menu command (no event passed) and as a key binding handler (event passed).
Add basic Edit and Help items
Even if you don’t implement full editing features yet, you can stub menu items and still provide status feedback. Help often contains an About dialog.
def cmd_about():
messagebox.showinfo("About", "Example Tkinter app shell")
set_status("Viewed About")
edit_menu.add_command(label="Undo", command=lambda: set_status("Undo (not implemented)"), accelerator="Ctrl+Z")
edit_menu.add_command(label="Redo", command=lambda: set_status("Redo (not implemented)"), accelerator="Ctrl+Y")
edit_menu.add_separator()
edit_menu.add_command(label="Find...", command=lambda: set_status("Find (not implemented)"), accelerator="Ctrl+F")
help_menu.add_command(label="About", command=cmd_about)Adding a Simple Toolbar with a Frame
A toolbar is commonly a horizontal strip of buttons under the menubar. In Tkinter, a toolbar can be a Frame packed at the top, containing buttons for the most-used actions (New/Open/Save). Toolbars often use icons, but text buttons are fine for learning and prototyping.
Step-by-step: Create the toolbar and connect buttons to the same commands
toolbar = tk.Frame(root, bd=1, relief="raised")
tk.Button(toolbar, text="New", width=8, command=cmd_new).pack(side="left", padx=2, pady=2)
tk.Button(toolbar, text="Open", width=8, command=cmd_open).pack(side="left", padx=2, pady=2)
tk.Button(toolbar, text="Save", width=8, command=cmd_save).pack(side="left", padx=2, pady=2)
toolbar.pack(side="top", fill="x")Design note: Reusing the same command functions for menu items and toolbar buttons keeps behavior consistent and reduces duplication.
Adding a Status Bar Anchored to the Bottom
A status bar is typically a single-line area at the bottom of the window. It’s useful for non-intrusive feedback: “Opened file…”, “Saved…”, “Ready”, and so on. In Tkinter, a Label with a sunken relief looks like a classic status bar.
Step-by-step: Create the status bar
status_bar = tk.Label(root, textvariable=status_var, anchor="w", bd=1, relief="sunken")
status_bar.pack(side="bottom", fill="x")Key options:
textvariablelets you update the status by changingstatus_var.anchor="w"keeps text aligned to the left.relief="sunken"andbd=1provide a traditional status-bar look.
Putting It Together: A Minimal App Shell
The following script combines menubar, toolbar, and status bar into one working window. You can later add your main content area (for example, a central editor widget) between the toolbar and status bar.
import tkinter as tk
from tkinter import filedialog, messagebox
root = tk.Tk()
root.title("Tkinter App Shell")
root.geometry("700x450")
status_var = tk.StringVar(value="Ready")
current_path = None
def set_status(msg):
status_var.set(msg)
def cmd_new(event=None):
global current_path
current_path = None
set_status("New file")
def cmd_open(event=None):
global current_path
path = filedialog.askopenfilename(
title="Open",
filetypes=[("Text files", "*.txt"), ("All files", "*")]
)
if not path:
set_status("Open canceled")
return
current_path = path
set_status(f"Opened: {path}")
def cmd_save(event=None):
global current_path
if current_path is None:
path = filedialog.asksaveasfilename(
title="Save As",
defaultextension=".txt",
filetypes=[("Text files", "*.txt"), ("All files", "*")]
)
if not path:
set_status("Save canceled")
return
current_path = path
set_status(f"Saved: {current_path}")
def cmd_exit(event=None):
root.destroy()
def cmd_about():
messagebox.showinfo("About", "Example Tkinter desktop interface")
set_status("Viewed About")
# Menubar
menubar = tk.Menu(root)
root.config(menu=menubar)
file_menu = tk.Menu(menubar, tearoff=0)
edit_menu = tk.Menu(menubar, tearoff=0)
help_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="File", menu=file_menu)
menubar.add_cascade(label="Edit", menu=edit_menu)
menubar.add_cascade(label="Help", menu=help_menu)
file_menu.add_command(label="New", command=cmd_new, accelerator="Ctrl+N")
file_menu.add_command(label="Open...", command=cmd_open, accelerator="Ctrl+O")
file_menu.add_command(label="Save", command=cmd_save, accelerator="Ctrl+S")
file_menu.add_separator()
file_menu.add_command(label="Exit", command=cmd_exit, accelerator="Alt+F4")
edit_menu.add_command(label="Undo", command=lambda: set_status("Undo (not implemented)"), accelerator="Ctrl+Z")
edit_menu.add_command(label="Redo", command=lambda: set_status("Redo (not implemented)"), accelerator="Ctrl+Y")
edit_menu.add_separator()
edit_menu.add_command(label="Find...", command=lambda: set_status("Find (not implemented)"), accelerator="Ctrl+F")
help_menu.add_command(label="About", command=cmd_about)
# Accelerators (bindings)
root.bind_all("<Control-n>", cmd_new)
root.bind_all("<Control-o>", cmd_open)
root.bind_all("<Control-s>", cmd_save)
# Toolbar
toolbar = tk.Frame(root, bd=1, relief="raised")
tk.Button(toolbar, text="New", width=8, command=cmd_new).pack(side="left", padx=2, pady=2)
tk.Button(toolbar, text="Open", width=8, command=cmd_open).pack(side="left", padx=2, pady=2)
tk.Button(toolbar, text="Save", width=8, command=cmd_save).pack(side="left", padx=2, pady=2)
toolbar.pack(side="top", fill="x")
# Main area placeholder
main = tk.Frame(root)
main.pack(side="top", fill="both", expand=True)
tk.Label(main, text="Main content area (add your widgets here)").pack(pady=30)
# Status bar
status_bar = tk.Label(root, textvariable=status_var, anchor="w", bd=1, relief="sunken")
status_bar.pack(side="bottom", fill="x")
root.mainloop()Exercise: Connect Menu Items to Existing Commands and Update the Status Bar
Goal: Practice wiring menu items to your application’s existing actions (New/Open/Save/Exit) and ensure the status bar reflects what the user did.
Tasks
- Task 1: In your current project, locate your existing functions for
New,Open,Save, andExit. Replace the placeholder command functions in this chapter with calls to your existing ones. - Task 2: Add a
set_status()helper and aStringVar-driven status bar. Update the status bar after each action. Examples: “Created new document”, “Opened: filename”, “Saved”, “Exited”. - Task 3: Add separators to group actions in the File menu (for example, separate Save from Exit).
- Task 4: Add accelerators to the menu items (Ctrl+N/Ctrl+O/Ctrl+S) and bind those shortcuts to the same functions.
- Task 5: Add a toolbar with buttons for New/Open/Save that call the same commands as the menu items.
Check yourself
- Clicking File → Open and clicking the Open toolbar button should perform the same action and produce the same status message.
- Pressing Ctrl+S should trigger Save and update the status bar, even if you never opened the File menu.
- Canceling Open/Save dialogs should not crash the app; it should set a sensible status like “Open canceled” or “Save canceled”.