Affichage des articles dont le libellé est python menu using tkinter. Afficher tous les articles
Affichage des articles dont le libellé est python menu using tkinter. Afficher tous les articles

Python Tkinter Hamburger Menu

How To Create a Hamburger Menu Dashboard In Python Tkinter



In this Python Tutorial we will see How to Create a dashboard with a smooth sliding hamburger menu using Python  and Tkinter library.

What We Are Gonna Use In This Project:

- Python Programming Language.
- Tkinter (GUI).
- VS Editor.






Project Source Code:




import math
import tkinter as tk
from tkinter import ttk
import time

class HamburgerMenu:
def __init__(self, master):
"""Initialize the Dashboard application
Args:
master: The tkinter root window
"""
self.master = master
self.master.title("Dashboard")
# Configure the main window size and background color
self.master.geometry("1000x700")
self.master.configure(bg='#1a1a1a') # Dark background for modern look
# Initialize menu state variables
self.menu_expanded = False # Track if side menu is expanded
self.menu_width = 0 # Current width of the menu (for animation)
self.target_width = 280 # Target width when menu is fully expanded
self.animation_speed = 15 # Controls how fast the menu slides
self.animation_running = False # Flag to prevent multiple animations
self.active_menu = "Home" # Track which menu item is currently active
# Build the UI components
self.setup_frames()
self.create_title()
self.create_hamburger_button()
self.create_menu_items()
self.create_content()

def setup_frames(self):
"""Create and configure the main frame structure of the application"""
# Create the header bar at the top
self.header_frame = tk.Frame(self.master, bg='#121212', height=70)
self.header_frame.pack(fill='x', side='top')
self.header_frame.pack_propagate(False) # Prevent the frame from shrinking
# Add a thin separator line below the header
self.separator_frame = tk.Frame(self.master, height=2, bg='#2a2a2a')
self.separator_frame.pack(fill='x')
# Main container that will hold both menu and content
self.container = tk.Frame(self.master, bg='#121212')
self.container.pack(fill='both', expand=True)
# This is the sliding menu panel container (starts with zero width)
self.menu_container = tk.Frame(self.container, bg='#121212', width=0)
self.menu_container.pack(side='left', fill='y')
# Important to maintain controlled width
self.menu_container.pack_propagate(False)
# Inner menu frame with padding for a polished look
self.menu_frame = tk.Frame(self.menu_container, bg='#1e1e1e', bd=0)
self.menu_frame.pack(fill='both', expand=True, padx=(10, 5), pady=10)
# Main content area where dashboards will be displayed
self.content_frame = tk.Frame(self.container, bg='#121212')
self.content_frame.pack(side='left', fill='both', expand=True,
padx=20, pady=10)

def create_title(self):
"""Create the title/logo area in the header"""
# Frame to hold the title elements
title_frame = tk.Frame(self.header_frame, bg='#121212')
title_frame.pack(side='left', padx=(70, 0))
# Create a small colored vertical bar as a design accent
accent = tk.Frame(title_frame, width=4, height=24, bg='#4a9eff')
accent.pack(side='left', padx=(0, 10))
# The actual title text
title_label = tk.Label(
title_frame,
text="DASHBOARD",
font=("Helvetica", 16, "bold"),
bg='#121212',
fg='#ffffff'
)
title_label.pack(side='left')
# Add current time display to header
self.time_label = tk.Label(
self.header_frame,
text="",
font=("Helvetica", 12),
bg='#121212',
fg='#888888'
)
self.time_label.pack(side='right', padx=20)
self.update_time() # Start the time update loop

def update_time(self):
"""Update the time display in the header (called every second)"""
current_time = time.strftime("%H:%M:%S")
self.time_label.config(text=current_time)
self.master.after(1000, self.update_time) # Schedule the next update


def create_hamburger_button(self):
"""Create the hamburger menu button with hover effects"""
# Use a canvas to create a circular button
self.button_canvas = tk.Canvas(
self.header_frame,
width=40,
height=40,
bg='#121212',
highlightthickness=0 # Remove the default canvas border
)
self.button_canvas.place(x=15, y=15)
# Draw the circular background
self.button_bg = self.button_canvas.create_oval(0, 0, 40, 40,
fill='#1e1e1e', outline='')
# Add the hamburger icon (☰) text
self.button_text = self.button_canvas.create_text(
20, 20, # Center of the circle
text="☰",
font=("Helvetica", 18),
fill='#ffffff'
)
# Bind mouse events for interactivity
self.button_canvas.bind('<Button-1>', self.toggle_menu) # Click event
self.button_canvas.bind('<Enter>', self.hamburger_hover_in) # Mouse enter
self.button_canvas.bind('<Leave>', self.hamburger_hover_out) # Mouse leave
self.button_canvas.configure(cursor="hand2") # Change cursor to hand on hover

def hamburger_hover_in(self, event):
"""Handle mouse enter event on hamburger button"""
# Darken background
self.button_canvas.itemconfig(self.button_bg, fill='#252525')
# Change icon color
self.button_canvas.itemconfig(self.button_text, fill='#4a9eff')

def hamburger_hover_out(self, event):
"""Handle mouse leave event on hamburger button"""
# Restore background
self.button_canvas.itemconfig(self.button_bg, fill='#1e1e1e')
# Restore icon color
self.button_canvas.itemconfig(self.button_text, fill='#ffffff')

def create_menu_items(self):
"""Create the sliding menu with all menu items"""
# Define menu items with their icons, labels and accent colors
menu_items = [
("⌂", "Home", "#4a9eff"),
("◉", "Profile", "#4ecdc4"),
("⚙", "Settings", "#9b59b6"),
("△", "Analytics", "#3498db"),
("✉", "Messages", "#e74c3c"),
("?", "Support", "#f1c40f"),
]
# Create profile section at the top of menu
self.create_profile_section()
# Create menu header with "MENU" text
menu_header = tk.Frame(self.menu_frame, bg='#1e1e1e', height=30)
menu_header.pack(fill='x', pady=(20, 15))
menu_header.pack_propagate(False)
tk.Label(
menu_header,
text="MENU",
font=("Helvetica", 10, "bold"),
bg='#1e1e1e',
fg='#555555'
).pack(side='left', padx=15, pady=10)
# Container frame for all menu items with some padding
menu_items_container = tk.Frame(self.menu_frame, bg='#1e1e1e')
menu_items_container.pack(fill='x', padx=5)

# Store references to all menu buttons for later access
self.menu_buttons = []
# Create each menu item
for icon, text, color in menu_items:
# Create a frame for each menu item
item_frame = tk.Frame(menu_items_container, bg='#1e1e1e', height=50)
item_frame.pack(fill='x', pady=3)
item_frame.pack_propagate(False)
# Use canvas for better visual control
item_canvas = tk.Canvas(
item_frame,
bg='#1e1e1e',
highlightthickness=0,
height=50
)
item_canvas.pack(fill='x')
# Store references for the canvas and its rectangle for hover effects
item_frame.canvas = item_canvas
item_frame.bg_rect = item_canvas.create_rectangle(
0, 0, 280, 50,
fill='#1e1e1e',
outline='',
width=0
)
# Create left accent bar with the item's color
accent_rect = item_canvas.create_rectangle(
8, 10, 12, 40,
fill=color,
outline='',
width=0
)
# Hide accent initially (only shown on hover/active)
item_canvas.itemconfig(accent_rect, state='hidden')
item_frame.accent_rect = accent_rect
# Add icon with subtle background in a circular shape
icon_x, icon_y = 30, 25
# Draw circular background for icon
icon_bg = item_canvas.create_oval(
icon_x-15, icon_y-15,
icon_x+15, icon_y+15,
fill='#252525',
outline=''
)
# Draw the icon text
icon_text = item_canvas.create_text(
icon_x, icon_y,
text=icon,
font=("Helvetica", 14),
fill='#ffffff'
)
# Add menu text
text_label = item_canvas.create_text(
80, 25,
text=text,
font=("Helvetica", 12),
fill='#ffffff',
anchor='w'
)
# Check if this is the active menu and style it accordingly
if text == self.active_menu:
# Highlight the active menu
item_canvas.itemconfig(item_frame.bg_rect, fill='#252525')
item_canvas.itemconfig(accent_rect, state='normal')
# Store the active state
item_frame.active = True
else:
item_frame.active = False
# Store color references for hover effects
item_frame.original_bg = '#1e1e1e' # Default background
item_frame.hover_bg = '#252525' # Background on hover
item_frame.active_bg = '#2a2a2a' # Background when active
item_frame.accent_color = color # Accent color for this item
item_frame.text = text # Menu item text for reference
# Bind hover and click events
item_canvas.bind('<Enter>', lambda e,
f=item_frame: self.menu_item_hover_in(f))
item_canvas.bind('<Leave>', lambda e,
f=item_frame: self.menu_item_hover_out(f))
item_canvas.bind('<Button-1>', lambda e,
f=item_frame: self.menu_item_click(f))
# Change cursor on hover
item_canvas.configure(cursor="hand2")
# Add to list of menu buttons for later reference
self.menu_buttons.append(item_frame)

# Add a divider below menu items
divider = tk.Frame(self.menu_frame, height=1, bg='#2a2a2a')
divider.pack(fill='x', pady=15, padx=15)
# Add logout button at the bottom
self.create_logout_button()


def create_profile_section(self):
"""Create the user profile section at the top of the menu"""
# Profile section container
profile_frame = tk.Frame(self.menu_frame, bg='#1e1e1e', height=90)
profile_frame.pack(fill='x', pady=(15, 0))
profile_frame.pack_propagate(False)
# Create a canvas for the profile circle
profile_canvas = tk.Canvas(
profile_frame,
width=70,
height=70,
bg='#1e1e1e',
highlightthickness=0
)
profile_canvas.place(relx=0.5, y=10, anchor='n') # Center horizontally
# Draw circular profile background
profile_bg = profile_canvas.create_oval(5, 5, 65, 65,
fill='#252525', outline='')
# Draw profile placeholder with user initials
profile_text = profile_canvas.create_text(
35, 35, # Center of the circle
text="JD", # User initials (John Doe)
font=("Helvetica", 22, "bold"),
fill='#4a9eff'
)
# Add user name below profile image
tk.Label(
profile_frame,
text="John Doe",
font=("Helvetica", 12, "bold"),
bg='#1e1e1e',
fg='#ffffff'
).place(relx=0.5, y=90, anchor='s') # Center horizontally

def create_logout_button(self):
"""Create the logout button at the bottom of the menu"""
# Create a frame for logout button
logout_frame = tk.Frame(self.menu_frame, bg='#1e1e1e', height=50)
logout_frame.pack(fill='x', side='bottom', padx=15, pady=10)
# Create a canvas for the logout button to add rounded corners effect
logout_canvas = tk.Canvas(
logout_frame,
bg='#1e1e1e',
highlightthickness=0,
height=45
)
logout_canvas.pack(fill='x')
# Draw rectangle background
logout_bg = logout_canvas.create_rectangle(
0, 0, 280, 45,
fill='#252525',
outline='',
width=0
)
# Add icon and text
logout_icon = logout_canvas.create_text(
25, 23,
text="→", # Right arrow icon
font=("Helvetica", 14),
fill='#e74c3c',
anchor='w'
)
logout_text = logout_canvas.create_text(
55, 23,
text="Logout",
font=("Helvetica", 12, "bold"),
fill='#ffffff',
anchor='w'
)
# Add hover effects
def on_enter(e):
"""Mouse enter effect for logout button"""
logout_canvas.itemconfig(logout_bg, fill='#2c2c2c')
logout_canvas.itemconfig(logout_text, fill='#e74c3c')
def on_leave(e):
"""Mouse leave effect for logout button"""
logout_canvas.itemconfig(logout_bg, fill='#252525')
logout_canvas.itemconfig(logout_text, fill='#ffffff')
# Bind events
logout_canvas.bind('<Enter>', on_enter)
logout_canvas.bind('<Leave>', on_leave)
logout_canvas.bind('<Button-1>', lambda e: print("Logout clicked"))
logout_canvas.configure(cursor="hand2")
def menu_item_hover_in(self, frame):
"""Handle mouse enter event on menu items
Args:
frame: The menu item frame being hovered
"""
# Only change appearance if this isn't the active menu item
if not frame.active:
frame.canvas.itemconfig(frame.bg_rect, fill=frame.hover_bg)
frame.canvas.itemconfig(frame.accent_rect, state='normal')
def menu_item_hover_out(self, frame):
"""Handle mouse leave event on menu items
Args:
frame: The menu item frame being left
"""
# Only change appearance if this isn't the active menu item
if not frame.active:
frame.canvas.itemconfig(frame.bg_rect, fill=frame.original_bg)
frame.canvas.itemconfig(frame.accent_rect, state='hidden')

def menu_item_click(self, frame):
"""Handle click event on menu items
Args:
frame: The menu item frame being clicked
"""
# Deactivate all menu items first
for btn in self.menu_buttons:
btn.active = False
btn.canvas.itemconfig(btn.bg_rect, fill=btn.original_bg)
btn.canvas.itemconfig(btn.accent_rect, state='hidden')
# Activate the clicked item
frame.active = True
frame.canvas.itemconfig(frame.bg_rect, fill=frame.active_bg)
frame.canvas.itemconfig(frame.accent_rect, state='normal')
# Update the active menu
self.active_menu = frame.text
print(f"Clicked on {frame.text}")




def create_content(self):
"""Create the main content area with welcome panel and info cards"""
# Create welcome panel at the top
self.welcome_frame = tk.Frame(self.content_frame, bg='#181818', height=200)
self.welcome_frame.pack(fill='x', pady=(0, 20))
self.welcome_frame.pack_propagate(False)
# Add decorative accent line
accent_line = tk.Frame(self.welcome_frame, bg='#4a9eff', width=80, height=3)
accent_line.place(x=40, y=40)
# Welcome text
tk.Label(
self.welcome_frame,
text="Welcome Back!",
font=("Helvetica", 28, "bold"),
bg='#181818',
fg='#ffffff'
).place(x=40, y=55)
tk.Label(
self.welcome_frame,
text="Explore your dashboard and manage your content",
font=("Helvetica", 12),
bg='#181818',
fg='#888888'
).place(x=40, y=100)
# Create a status indicator with green dot
status_frame = tk.Frame(self.welcome_frame, bg='#181818')
status_frame.place(x=40, y=140)
status_dot = tk.Canvas(status_frame, width=12, height=12,
bg='#181818', highlightthickness=0)
status_dot.pack(side='left')
status_dot.create_oval(0, 0, 10, 10, fill='#2ecc71', outline='') # Green dot
tk.Label(
status_frame,
text="All systems operational",
font=("Helvetica", 11),
bg='#181818',
fg='#2ecc71'
).pack(side='left', padx=5)
# Create three info cards section
self.card_container = tk.Frame(self.content_frame, bg='#121212')
self.card_container.pack(fill='x')
# Define data for the three cards
cards_data = [
("Statistics", "#2980b9", "128", "Active Users"),
("Performance", "#27ae60", "94%", "System Uptime"),
("Activity", "#8e44ad", "37", "New Events")
]
# Store card widgets for animation
self.content_cards = []
# Create each info card
for title, color, number, subtitle in cards_data:
card = tk.Frame(self.card_container, bg='#181818', width=200, height=150)
card.pack(side='left', padx=10, fill='both', expand=True)
card.pack_propagate(False)
# Add colored top border as accent
top_border = tk.Frame(card, height=3, bg=color)
top_border.pack(fill='x', side='top')
# Add card content - title, number, and subtitle
tk.Label(
card,
text=title,
font=("Helvetica", 14, "bold"),
bg='#181818',
fg='#ffffff'
).pack(anchor='w', padx=15, pady=(20, 5))
tk.Label(
card,
text=number,
font=("Helvetica", 28, "bold"),
bg='#181818',
fg=color
).pack(anchor='w', padx=15, pady=(5, 0))
tk.Label(
card,
text=subtitle,
font=("Helvetica", 12),
bg='#181818',
fg='#888888'
).pack(anchor='w', padx=15, pady=(0, 15))
# Add card to list for animation
self.content_cards.append(card)




def toggle_menu(self, event=None):
"""Toggle the side menu open/closed when hamburger button is clicked"""
# Prevent multiple animations from running at once
if self.animation_running:
return
# Toggle the menu state
if not self.menu_expanded:
self.expand_menu()
else:
self.collapse_menu()
# Change hamburger button to X or back
if self.menu_expanded:
self.button_canvas.itemconfig(self.button_text, text="✕") # X icon
else:
self.button_canvas.itemconfig(self.button_text, text="☰") # Hamburger icon


def expand_menu(self):
"""Start animation to expand the side menu"""
self.menu_expanded = True
self.animation_running = True
self.animate_menu()


def collapse_menu(self):
"""Start animation to collapse the side menu"""
self.menu_expanded = False
self.animation_running = True
self.animate_menu()



def animate_menu(self):
"""Handle the smooth animation of the side menu sliding in/out"""
# Target width is either full width or zero
# depending on if expanding/collapsing
target = self.target_width if self.menu_expanded else 0
def update():
"""Update function called repeatedly during animation"""
current_width = self.menu_container.winfo_width()
# Calculate the step size for smooth easing effect
if self.menu_expanded:
# When expanding, move faster when further from target
distance = target - current_width
step = max(1, int(distance * 0.2)) # At least 1 pixel
new_width = min(current_width + step, target)
else:
# When collapsing, move faster when further from zero
distance = current_width
step = max(1, int(distance * 0.2)) # At least 1 pixel
new_width = max(current_width - step, target)
# Update the menu width
self.menu_container.configure(width=new_width)
# Continue animation if not reached target
if new_width != target:
self.master.after(8, update) # Schedule next update
else:
self.animation_running = False # Animation complete

# Start the animation
update()




def main():
"""Main entry point of the application"""
root = tk.Tk()
app = HamburgerMenu(root)
root.mainloop()

if __name__ == "__main__":
main()



The Final Result:












Python Tkinter Collapsible Sidebar

How to Create a Collapsible Sidebar in Python Tkinter

How to Create a Collapsible Sidebar in Python Tkinter


In this Python Tutorial we will see How to Create a wooden chess board using Python's built-in Tkinter library.

What We Are Gonna Use In This Project:

- Python Programming Language.
- Tkinter (GUI).
- VS Editor.






Project Source Code:




import tkinter as tk
import time
import math


class ModernSidebar(tk.Frame):
"""
A modern, animated sidebar widget for tkinter applications.
"""
def __init__(self, parent):
# Initialize the parent Frame class
super().__init__(parent)
# ===== SIDEBAR DIMENSIONS =====
# These control how wide the sidebar is when expanded/collapsed
self.EXPANDED_WIDTH = 280 # Width when sidebar is open
self.COLLAPSED_WIDTH = 70 # Width when sidebar is closed
# ===== ANIMATION SETTINGS =====
# Controls how smooth the expand/collapse animation looks
self.ANIMATION_DURATION = 300 # Animation time in milliseconds
# ===== COLOR SCHEME =====
self.PRIMARY_COLOR = "#1f2937" # Main dark background
self.SECONDARY_COLOR = "#111827" # Slightly darker shade
self.ACCENT_COLOR = "#10b981" # Green accent color
self.HOVER_COLOR = "#374151" # Color when hovering over buttons
self.TEXT_COLOR = "#f3f4f6" # Light text color
self.CONTENT_BG = "#f9fafb" # Light background for main content
# ===== STATE VARIABLES =====
# These keep track of the sidebar's current state
self.is_collapsed = False # Is the sidebar currently collapsed?
self.current_width = self.EXPANDED_WIDTH # Current width of sidebar
self.animating = False # Is an animation currently running?
self.active_button = None # Which menu button is currently selected
# Set up the user interface
self.setup_ui()
def setup_ui(self):
"""
Creates all the visual elements of the sidebar.
This is like building the skeleton of our interface.
"""
# Configure the main frame (the container for everything)
self.configure(bg=self.PRIMARY_COLOR)
# ===== CREATE THE SIDEBAR CONTAINER =====
# This frame holds all the sidebar content (logo, menu, buttons)
self.sidebar = tk.Frame(
self,
bg=self.PRIMARY_COLOR,
width=self.EXPANDED_WIDTH,
highlightthickness=0 # Remove ugly border
)
self.sidebar.pack(side="left", fill="y")
self.sidebar.pack_propagate(False) # Don't let children resize this frame
# ===== CREATE THE LOGO SECTION =====
self.create_logo_section()
# ===== CREATE A SEPARATOR LINE =====
# This adds a subtle line between logo and menu
separator = tk.Frame(self.sidebar, height=1, bg="#374151")
separator.pack(fill="x", padx=15, pady=(0, 15))
# ===== CREATE THE MENU SECTION =====
self.create_menu_section()
# ===== CREATE THE COLLAPSE BUTTON =====
self.create_collapse_button()
# ===== CREATE THE MAIN CONTENT AREA =====
self.create_content_area()

def create_logo_section(self):
"""
Creates the logo area at the top of the sidebar.
Shows full text when expanded, initials when collapsed.
"""
# Container for logo elements
self.logo_frame = tk.Frame(self.sidebar, bg=self.PRIMARY_COLOR)
self.logo_frame.pack(fill="x", padx=15, pady=(25, 20))
# Full logo text (shown when expanded)
self.logo_text = tk.Label(
self.logo_frame,
text="Modern Sidebar",
font=("Arial", 20, "bold"),
fg=self.ACCENT_COLOR,
bg=self.PRIMARY_COLOR
)
self.logo_text.pack(anchor="w") # Align to left (west)
# Short logo (shown when collapsed)
self.logo_icon = tk.Label(
self.logo_frame,
text="MS",
font=("Arial", 22, "bold"),
fg=self.ACCENT_COLOR,
bg=self.PRIMARY_COLOR
)
# Note: logo_icon is created but not packed yet - it will show when collapsed

def create_menu_section(self):
"""
Creates the menu buttons section of the sidebar.
Each button has an icon and text label.
"""
# Container for all menu buttons
self.menu_frame = tk.Frame(self.sidebar, bg=self.PRIMARY_COLOR)
self.menu_frame.pack(fill="both", expand=True, padx=10, pady=5)
# ===== DEFINE MENU ITEMS =====
# Each item has: (display_text, icon_function, tooltip_text)
self.menu_items = [
("Dashboard", self.create_dashboard_icon, "View your dashboard"),
("Menu", self.create_menu_icon, "Manage menu items"),
("Orders", self.create_orders_icon, "Track orders"),
("Customers", self.create_customers_icon, "Customer management"),
("Analytics", self.create_analytics_icon, "View analytics"),
("Inventory", self.create_inventory_icon, "Manage inventory"),
("Marketing", self.create_marketing_icon, "Marketing campaigns"),
("Settings", self.create_settings_icon, "System settings")
]
# ===== CREATE MENU BUTTONS =====
# Store all buttons in a list so we can manage them later
self.menu_buttons = []
# Loop through each menu item and create a button for it
for i, (text, icon_func, tooltip) in enumerate(self.menu_items):
button = self.create_menu_button(text, icon_func, tooltip, i)
self.menu_buttons.append(button)
# Make the first button active by default
self.set_active_button(self.menu_buttons[0], 0)

def create_menu_button(self, text, icon_func, tooltip, index):
"""
Creates a single menu button with icon and text.
Args:
text: The text to display on the button
icon_func: Function that draws the icon
tooltip: Text to show when hovering (when collapsed)
index: Position of this button in the menu
"""
# ===== CREATE BUTTON CONTAINER =====
button_frame = tk.Frame(self.menu_frame, bg=self.PRIMARY_COLOR, height=52)
button_frame.pack(fill="x", pady=5)
button_frame.pack_propagate(False)
# ===== CREATE ACTIVE INDICATOR =====
# This colored bar shows which button is currently selected
indicator = tk.Frame(button_frame, width=4, bg=self.PRIMARY_COLOR)
indicator.pack(side="left", fill="y")
# ===== CREATE BUTTON CONTENT CONTAINER =====
content = tk.Frame(button_frame, bg=self.PRIMARY_COLOR)
content.pack(side="left", fill="both", expand=True, padx=8, pady=8)
# ===== CREATE ICON =====
# Canvas allows us to draw custom icons
icon_canvas = tk.Canvas(
content,
width=24,
height=28,
bg=self.PRIMARY_COLOR,
highlightthickness=0, # Remove border
bd=0 # Remove border
)
icon_canvas.pack(side="left", padx=(5, 10))
# Draw the icon using the provided function
icon_func(icon_canvas, self.TEXT_COLOR)
# ===== CREATE TEXT LABEL =====
text_label = tk.Label(
content,
text=text,
font=("Arial", 11),
fg=self.TEXT_COLOR,
bg=self.PRIMARY_COLOR,
anchor="w" # Align text to left
)
text_label.pack(side="left", fill="x", expand=True)
# ===== STORE REFERENCES =====
# Store important parts in the frame so we can access them later
button_frame.indicator = indicator
button_frame.icon_canvas = icon_canvas
button_frame.text_label = text_label
button_frame.content = content
button_frame.index = index
button_frame.icon_func = icon_func
# ===== BIND CLICK EVENTS =====
# Make all parts of the button clickable
for widget in [button_frame, content, icon_canvas, text_label]:
widget.bind("<Button-1>", lambda e, f=button_frame,
i=index: self.on_button_click(f, i))
widget.bind("<Enter>", lambda e, f=button_frame: self.on_button_hover(f))
widget.bind("<Leave>", lambda e, f=button_frame: self.on_button_leave(f))
pass
# Add tooltip for collapsed state
self.create_tooltip(button_frame, tooltip)
return button_frame
def create_tooltip(self, widget, text):
"""
Creates a tooltip that appears when hovering over buttons in collapsed mode.
"""
def show_tooltip(event):
# Only show tooltip when sidebar is collapsed
if self.is_collapsed:
# Calculate position for tooltip
x = widget.winfo_rootx() + 75
y = widget.winfo_rooty() + 20
# Create tooltip window
self.tooltip = tk.Toplevel(widget)
self.tooltip.wm_overrideredirect(True) # Remove window decorations
self.tooltip.wm_geometry(f"+{x}+{y}")
# Create tooltip content
tooltip_frame = tk.Frame(self.tooltip, bg=self.SECONDARY_COLOR)
tooltip_frame.pack()
label = tk.Label(
tooltip_frame,
text=text,
bg=self.SECONDARY_COLOR,
fg=self.TEXT_COLOR,
font=("Arial", 10)
)
label.pack(padx=10, pady=6)
def hide_tooltip(event):
# Remove tooltip if it exists
if hasattr(self, 'tooltip'):
self.tooltip.destroy()
# Bind hover events
widget.bind('<Enter>', show_tooltip)
widget.bind('<Leave>', hide_tooltip)




def create_collapse_button(self):
"""
Creates the button that collapses/expands the sidebar.
"""
# Container for the collapse button
bottom_frame = tk.Frame(self.sidebar, bg=self.PRIMARY_COLOR)
bottom_frame.pack(side="bottom", fill="x", pady=15)
# The actual collapse button
self.collapse_button = tk.Button(
bottom_frame,
text="◄", # Left arrow when expanded
font=("Arial", 12),
fg=self.TEXT_COLOR,
bg=self.SECONDARY_COLOR,
activebackground=self.ACCENT_COLOR, # Color when clicked
activeforeground="#ffffff",
bd=0, # No border
width=6,
height=2,
cursor="hand2", # Hand cursor on hover
relief="flat", # Flat appearance
command=self.toggle_sidebar # Function to call when clicked
)
self.collapse_button.pack(fill="x")
def toggle_sidebar(self):
"""
Expands or collapses the sidebar with smooth animation.
This is the main function that creates the sliding effect.
"""
# Don't start new animation if one is already running
if self.animating:
return
# Mark that animation is starting
self.animating = True
# Determine target width and calculate animation parameters
target_width = self.COLLAPSED_WIDTH \
if not self.is_collapsed \
else self.EXPANDED_WIDTH
start_width = self.current_width
width_difference = target_width - start_width
start_time = time.time()
# Switch logo display immediately
if not self.is_collapsed:
# Going to collapsed state - show icon only
self.logo_text.pack_forget()
self.logo_icon.pack(anchor="center")
else:
# Going to expanded state - show full text
self.logo_icon.pack_forget()
self.logo_text.pack(anchor="w")
def animate_step():
"""
This function runs repeatedly to create the animation effect.
It's called every 16 milliseconds.
"""
# Calculate how much time has passed
elapsed_time = time.time() - start_time
# Check if animation is complete
if elapsed_time >= self.ANIMATION_DURATION / 1000:
# Animation finished - set final state
self.update_sidebar_width(target_width)
self.is_collapsed = not self.is_collapsed
self.collapse_button.configure(text="►" if self.is_collapsed else "◄")
self.update_button_layout()
self.animating = False
return
# Calculate animation progress (0 to 1)
progress = elapsed_time / (self.ANIMATION_DURATION / 1000)
# Apply easing for smoother animation
eased_progress = self.ease_out_cubic(progress)
# Calculate new width and update sidebar
new_width = start_width + (width_difference * eased_progress)
self.update_sidebar_width(new_width)
# Schedule next animation step
self.after(16, animate_step)
# Start the animation
animate_step()

def ease_out_cubic(self, x):
"""
Easing function that makes animation start fast and slow down.
This creates a more natural, smooth animation effect.
"""
return 1 - math.pow(1 - x, 3)
def update_sidebar_width(self, width):
"""
Updates the actual width of the sidebar.
"""
self.current_width = width
self.sidebar.configure(width=int(width))
def update_button_layout(self):
"""
Updates the layout of menu buttons when sidebar is collapsed/expanded.
In collapsed mode, only icons are shown. In expanded mode, both icons and text.
"""
for button in self.menu_buttons:
if self.is_collapsed:
# Hide text and center the icon
button.text_label.pack_forget()
button.icon_canvas.pack_forget()
button.icon_canvas.pack(side="top", padx=0, pady=(10, 0),
anchor="center")
else:
# Show both icon and text
button.icon_canvas.pack_forget()
button.icon_canvas.pack(side="left", padx=(5, 10))
button.text_label.pack(side="left", fill="x", expand=True)

# Restore active button state
if self.active_button:
self.set_active_button(self.active_button, self.active_button.index)


def create_content_area(self):
"""
Creates the main content area next to the sidebar.
This is where the actual application content would go.
"""
# Main content container
self.content_frame = tk.Frame(self, bg=self.CONTENT_BG)
self.content_frame.pack(side="left", fill="both", expand=True)
# Sample header for the content area
header = tk.Frame(self.content_frame, bg=self.CONTENT_BG, height=70)
header.pack(fill="x", padx=20, pady=10)
header.pack_propagate(False)
# Welcome message
welcome_label = tk.Label(
header,
text="Welcome Back - Application Dashboard",
font=("Arial", 24, "bold"),
fg="#111827",
bg=self.CONTENT_BG
)
welcome_label.pack(anchor="w", pady=10)
# ===== ICON DRAWING FUNCTIONS =====
# These functions draw simple icons using canvas drawing methods
def create_dashboard_icon(self, canvas, color):
"""Draws a simple house/dashboard icon"""
# House base
canvas.create_rectangle(7, 14, 17, 24, outline=color, width=2)
# House roof
canvas.create_polygon(3, 14, 12, 5, 21, 14, fill="", outline=color, width=2)
def create_menu_icon(self, canvas, color):
"""Draws three horizontal lines (hamburger menu)"""
for y in [8, 14, 20]:
canvas.create_line(5, y, 19, y, fill=color, width=2)
def create_orders_icon(self, canvas, color):
"""Draws a clipboard with checkmarks"""
# Clipboard
canvas.create_rectangle(6, 2, 18, 22, outline=color, width=2)
# Clip at top
canvas.create_rectangle(10, 1, 14, 4, outline=color, width=2)
# Checkmarks
canvas.create_line(8, 8, 10, 10, fill=color, width=2)
canvas.create_line(10, 10, 14, 6, fill=color, width=2)
canvas.create_line(8, 14, 10, 16, fill=color, width=2)
canvas.create_line(10, 16, 14, 12, fill=color, width=2)
def create_customers_icon(self, canvas, color):
"""Draws a simple person icon"""
# Head (circle)
canvas.create_oval(8, 4, 16, 12, outline=color, width=2)
# Body (arc)
canvas.create_arc(6, 15, 18, 25, start=0, extent=180,
outline=color, width=2, style="arc")
# Connect head to body
canvas.create_line(12, 12, 12, 15, fill=color, width=2)
def create_analytics_icon(self, canvas, color):
"""Draws a simple chart/graph"""
# Axes
canvas.create_line(4, 20, 20, 20, fill=color, width=2) # X-axis
canvas.create_line(4, 4, 4, 20, fill=color, width=2) # Y-axis
# Chart line
points = [4, 16, 7, 12, 10, 16, 13, 8, 16, 11, 20, 4]
canvas.create_line(*points, fill=color, width=2, smooth=True)
def create_inventory_icon(self, canvas, color):
# Shelf frame
canvas.create_line(4, 4, 4, 20, fill=color, width=2) # Left support
canvas.create_line(20, 4, 20, 20, fill=color, width=2) # Right support
# Shelves
canvas.create_line(4, 8, 20, 8, fill=color, width=2)
canvas.create_line(4, 14, 20, 14, fill=color, width=2)
canvas.create_line(4, 20, 20, 20, fill=color, width=2)
# Items on shelves
canvas.create_rectangle(6, 5, 9, 8, fill=color, outline="")
canvas.create_rectangle(15, 5, 18, 8, fill=color, outline="")
canvas.create_rectangle(7, 11, 10, 14, fill=color, outline="")
canvas.create_rectangle(14, 11, 17, 14, fill=color, outline="")
def create_marketing_icon(self, canvas, color):
# Marketing icon with megaphone
# Megaphone body
canvas.create_polygon(4, 14, 4, 10, 12, 6, 12, 18, 4, 14,
fill="", outline=color, width=2)
# Handle
canvas.create_rectangle(2, 10, 4, 14, outline=color, width=2, fill="")
# Sound waves
canvas.create_arc(12, 8, 18, 16, start=270, extent=180, style="arc",
outline=color, width=1)
canvas.create_arc(14, 6, 20, 18, start=270, extent=180, style="arc",
outline=color, width=1)
canvas.create_arc(16, 4, 22, 20, start=270, extent=180, style="arc",
outline=color, width=1)
def create_settings_icon(self, canvas, color):
"""Draws a gear/settings icon"""
center_x, center_y = 12, 12
# Gear teeth
for angle in range(0, 360, 45):
rad = math.radians(angle)
inner_x = center_x + 6 * math.cos(rad)
inner_y = center_y + 6 * math.sin(rad)
outer_x = center_x + 9 * math.cos(rad)
outer_y = center_y + 9 * math.sin(rad)
canvas.create_line(inner_x, inner_y, outer_x, outer_y, fill=color, width=2)
# Center circles
canvas.create_oval(8, 8, 16, 16, outline=color, width=2)
canvas.create_oval(10, 10, 14, 14, fill=color)



def set_active_button(self, button_frame, index):
"""
Makes a button the active/selected button.
Updates colors and the indicator bar.
"""
# Reset previous active button
if self.active_button:
self.active_button.configure(bg=self.PRIMARY_COLOR)
self.active_button.content.configure(bg=self.PRIMARY_COLOR)
self.active_button.text_label.configure(bg=self.PRIMARY_COLOR,
fg=self.TEXT_COLOR)
self.active_button.indicator.configure(bg=self.PRIMARY_COLOR, width=0)
# Redraw previous active icon with normal color
self.active_button.icon_canvas.delete("all")
self.active_button.icon_func(self.active_button.icon_canvas,
self.TEXT_COLOR)
# Set new active button
button_frame.configure(bg=self.PRIMARY_COLOR)
button_frame.content.configure(bg=self.PRIMARY_COLOR)
button_frame.text_label.configure(bg=self.PRIMARY_COLOR, fg=self.ACCENT_COLOR)
button_frame.indicator.configure(bg=self.ACCENT_COLOR, width=4)
# Redraw icon with accent color
button_frame.icon_canvas.configure(bg=self.PRIMARY_COLOR)
button_frame.icon_canvas.delete("all")
button_frame.icon_func(button_frame.icon_canvas, self.ACCENT_COLOR)
# Update active button reference
self.active_button = button_frame


# ===== EVENT HANDLING FUNCTIONS =====
# These functions respond to user interactions
def on_button_click(self, button_frame, index):
"""
Called when a menu button is clicked.
Changes the active button and updates the display.
"""
self.set_active_button(button_frame, index)

def on_button_hover(self, button_frame):
"""
Called when mouse hovers over a button.
Changes the button's appearance to show it's interactive.
"""
# Don't change active button appearance
if button_frame == self.active_button:
return
# Change colors to show hover state
button_frame.configure(bg=self.HOVER_COLOR)
button_frame.content.configure(bg=self.HOVER_COLOR)
button_frame.icon_canvas.configure(bg=self.HOVER_COLOR)
button_frame.text_label.configure(bg=self.HOVER_COLOR, fg="#ffffff")
# Redraw icon with brighter color
button_frame.icon_canvas.delete("all")
button_frame.icon_func(button_frame.icon_canvas, "#ffffff")

def on_button_leave(self, button_frame):
"""
Called when mouse leaves a button.
Resets the button's appearance to normal.
"""
# Don't change active button appearance
if button_frame == self.active_button:
return
# Reset to normal colors
button_frame.configure(bg=self.PRIMARY_COLOR)
button_frame.content.configure(bg=self.PRIMARY_COLOR)
button_frame.icon_canvas.configure(bg=self.PRIMARY_COLOR)
button_frame.text_label.configure(bg=self.PRIMARY_COLOR, fg=self.TEXT_COLOR)
# Redraw icon with normal color
button_frame.icon_canvas.delete("all")
button_frame.icon_func(button_frame.icon_canvas, self.TEXT_COLOR)



class ModernApp(tk.Tk):
"""
Main application window that contains the sidebar.
This is the root window of our application.
"""
def __init__(self):
# Initialize the main window
super().__init__()
# ===== WINDOW CONFIGURATION =====
self.title("Modern Sidebar Demo")
self.geometry("1000x680")
self.configure(bg="#ffffff")
# ===== CREATE THE SIDEBAR =====
sidebar = ModernSidebar(self)
sidebar.pack(fill="both", expand=True)
# ===== CENTER THE WINDOW ON SCREEN =====
self.center_window()
def center_window(self):
"""
Centers the application window on the screen.
"""
# Update window to get actual size
self.update_idletasks()
# Get window dimensions
width = self.winfo_width()
height = self.winfo_height()
# Calculate center position
x = (self.winfo_screenwidth() // 2) - (width // 2)
y = (self.winfo_screenheight() // 2) - (height // 2)
# Set window position
self.geometry(f'+{x}+{y}')


# ===== MAIN PROGRAM =====
if __name__ == "__main__":
# Create and run the application
app = ModernApp()
app.mainloop() # Start the GUI event loop



The Final Result:

Python Tkinter Collapsible Sidebar 1

Python Tkinter Collapsible Sidebar 2

Python Tkinter Collapsible Sidebar 3