How to Create To Do List Project In Python Tkinter
In this Python Tutorial we will see How To Create a To Do List Project in Python using Tkinter.
This application provides a modern interface for managing tasks, allowing users to add, view, delete tasks, and Complete/Completed toggle button .
What We Are Gonna Use In This Project:
- Python Tkinter: GUI framework.- Visual Studio Editor.
Project Source Code:
- Build the user interface
Calculates screen dimensions and positions the application window at the center of the user's display.
def center_window(self):
"""Center the window on the screen."""
# Get screen dimensions
screen_width = self.root.winfo_screenwidth()
screen_height = self.root.winfo_screenheight()
# Calculate position
x = (screen_width - 900) // 2
y = (screen_height - 600) // 2
# Set window position
self.root.geometry(f"900x600+{x}+{y}")
- Custom Window
Creates a custom title bar with application branding and window controls, replacing the default system title bar.
def setup_title_bar(self):
"""Create custom title bar with controls."""
self.title_bar = tk.Frame(self.root, bg=self.special_color, height=40)
self.title_bar.pack(fill=tk.X)
# Application title
title = tk.Label(self.title_bar, text="Tasks Manager",
font=self.title_font,
bg=self.special_color, fg="white")
title.pack(side=tk.LEFT, padx=15)
# Window controls container
controls = tk.Frame(self.title_bar, bg=self.special_color)
controls.pack(side=tk.RIGHT, padx=5)
# Control buttons
buttons = [
("□", lambda: self.toggle_maximize(), "#64748b"),
("x", lambda: self.root.destroy(), "#ef4444")
]
for symbol, command, hover_color, in buttons:
btn = tk.Label(controls, text=symbol, bg=self.special_color,
fg="white", font=self.title_font, padx=10)
btn.pack(side=tk.LEFT)
# Hover effects
btn.bind("<Enter>", lambda e,
color=hover_color: e.widget.configure(bg=color))
btn.bind("<Leave>", lambda e: e.widget.configure(bg=self.special_color))
btn.bind("<Button-1>", lambda e, cmd=command: cmd())
- Window State Management
Toggles between maximized and normal window states when users click the maximize button.
def toggle_maximize(self):
"""Toggle between maximized and normal window state."""
if self.root.state() == 'zoomed':
self.root.state('normal')
else:
self.root.state('zoomed')
- Bottom Control Panel
Creates the bottom action bar containing the primary "Add New Task" button with hover animations.
def setup_action_bar(self):
"""Create the bottom action bar with controls."""
action_bar = tk.Frame(self.root, bg=self.bg_color, height=60)
action_bar.pack(fill=tk.X, padx=20, pady=(0, 20))
# Add task button
add_btn = tk.Button(action_bar, text="+ Add New Task",
font=self.button_font, bg=self.special_color,
fg="white", border=0, padx=20, pady=10,
command=self.add_task, cursor="hand2")
add_btn.pack(side=tk.LEFT)
# Hover effect
add_btn.bind("<Enter>", lambda e: e.widget.configure(bg="#1d4ed8"))
add_btn.bind("<Leave>", lambda e: e.widget.configure(bg=self.special_color))
- Drag Functionality Setup
Configures mouse event bindings to enable window dragging from the title bar.
def setup_window_drag(self):
"""Configure window dragging functionality."""
self.title_bar.bind("<Button-1>", self.on_drag_start)
self.title_bar.bind("<B1-Motion>", self.on_drag_motion)
- Drag Initialization
Captures initial mouse coordinates when the user starts dragging the window.
def on_drag_start(self, event):
"""Store initial coordinates when starting drag."""
self.drag_data = (event.x_root - self.root.winfo_x(),
event.y_root - self.root.winfo_y())
- Drag Position Updates
Continuously updates window position as the user drags the mouse across the screen.
def on_drag_motion(self, event):
"""Update window position during drag."""
x = event.x_root - self.drag_data[0]
y = event.y_root - self.drag_data[1]
self.root.geometry(f"+{x}+{y}")
- Scrollable Content Area
Creates a scrollable canvas system for displaying task cards when content exceeds window height.
def setup_scrollable_canvas(self):
"""Create a scrollable canvas for task cards."""
# Create canvas with scrollbar
self.canvas =tk.Canvas(self.content_frame, bg=self.bg_color,
highlightthickness=0)
scrollbar = ttk.Scrollbar(self.content_frame, orient="vertical",
command=self.canvas.yview)
# Configure canvas scroll region
self.canvas.configure(yscrollcommand=scrollbar.set)
# Create frame for task cards
self.dashboard_panel = tk.Frame(self.canvas, bg=self.bg_color)
# Pack elements
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
# Create window in canvas for dashboard
self.canvas_window = self.canvas.create_window((0,0),
window=self.dashboard_panel, anchor="nw")
# Configure canvas scrolling
self.canvas.bind("<Configure>", self.on_canvas_configure)
self.dashboard_panel.bind("<Configure>", self.on_frame_configure)
self.canvas.bind_all("<MouseWheel>", self.on_mousewheel)
- Canvas Resize Handler
Updates the canvas scroll region and content width when the canvas is resized.
def on_canvas_configure(self, event):
"""Update canvas scroll region when canvas is configured."""
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
width = event.width
self.canvas.itemconfig(self.canvas_window, width=width)
- Frame Resize Handler
Recalculates scroll boundaries when the inner frame content changes size.
def on_frame_configure(self, event):
"""Update canvas scroll region when frame is configured."""
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
- Mouse Wheel Scrolling
Enables mouse wheel scrolling through the task list.
def on_mousewheel(self, event):
"""Handle mouse wheel scrolling."""
self.canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")
- Task Creation
Handles new task creation with validation, modal dialog presentation, and task limit enforcement (12-task maximum limit).
def add_task(self):
"""Add a new task with modern dialog."""
if len(self.tasks) >= 12:
self.show_error("Maximum Tasks Reached",
"Please Complete Tasks Before Adding New Ones")
return
dialog = ModernInputDialog(self.root, "Add New Task", "Enter task details")
self.root.wait_window(dialog)
if dialog.result:
task = {
'text': dialog.result,
'date': datetime.now().strftime("%b %d, %Y"),
'status':'pending'
}
self.tasks.append(task)
self.update_task_panel()
- UI Refresh
Rebuilds the entire task display panel, organizing tasks into horizontal tiers with proper spacing.
def update_task_panel(self):
"""Refresh the task display with styling."""
for widget in self.dashboard_panel.winfo_children():
widget.destroy()
for i, task in enumerate(self.tasks):
if i % self.tasks_per_tier == 0:
tier_frame = tk.Frame(self.dashboard_panel, bg = self.bg_color)
tier_frame.pack(fill = tk.X, pady = (10, 0))
self.create_task_card(tier_frame, task)
- Individual Task UI
Generates individual task cards with completion status, action buttons, and custom styling.
def create_task_card(self, parent, task):
"""Create a task card with completion status."""
card = tk.Frame(parent, bg=self.card_bg, padx = 15, pady = 15,
highlightthickness = 1,
highlightbackground = "#e2eff0")
card.pack(side=tk.LEFT, padx = 10, pady = 10,
fill=tk.BOTH, expand=True)
header = tk.Frame(card, bg = self.card_bg)
header.pack(fill=tk.X, pady=(0, 10))
# Use completed font if task is marked as completed
task_font = self.completed_font if task.get('completed', False)
else self.text_font
task_label = tk.Label(header, text = task['text'],
font = task_font, bg = self.card_bg,
fg = self.text_color, wraplength = 200,
justify = tk.LEFT)
task_label.pack(side = tk.LEFT, anchor = "w")
tk.Label(card, text = task['date'], font = ("Helvatica", 9),
bg = self.card_bg, fg = "#64748b").pack(anchor="w")
btn_frame = tk.Frame(card, bg = self.card_bg)
btn_frame.pack(fill = tk.X, pady = (10, 0))
# Change complete button text based on completion status
complete_text = "✓ Completed" if task.get('completed', False)
else "✓ Complete"
complete_btn = tk.Button(btn_frame, text = complete_text,
font = self.button_font,
bg = "#22c55e" if not task.get('completed', False)
else "#64748b",
fg="white", border = 0, padx = 10, pady = 5,
command = lambda: self.toggle_complete_task(
task, task_label, complete_btn))
complete_btn.pack(side = tk.LEFT, padx = (0, 5))
delete_btn = tk.Button(btn_frame, text = "Delete",
font = self.button_font, bg = "#ef4444",
fg="white", border= 0, padx = 10, pady = 5,
command = lambda: self.delete_task(task))
delete_btn.pack(side = tk.LEFT)
for btn, hover_color in [(complete_btn, "#16a34a"), (delete_btn, "#dc2626")]:
btn.bind("<Enter>", lambda e,
color = hover_color: e.widget.configure(bg=color))
btn.bind("<Leave>", lambda e, btn = btn: self.reset_button_color(e, btn))
- Task Status Management
Handles task completion toggling with success notifications.
def toggle_complete_task(self, task, label, button):
"""Toggle task completion status."""
task['completed'] = not task.get('completed', False)
if task['completed']:
label.configure(font = self.completed_font)
button.configure(text = "✓ Completed", bg="#64748b")
self.show_success("Task Completed", "Grate job completing your task !")
else:
label.configure(font = self.text_font)
button.configure(text = "✓ Complete", bg="#22c55e")
- Hover State Management
Manages button color states after hover events end.
def reset_button_color(self, event, button):
"""Reset button color after hover."""
if "Complete" in button['text']:
if "Completed" in button['text']:
button.configure(bg = "#64748b")
else:
button.configure(bg = "#22c55e")
else:
button.configure(bg = "#ef4444")
- Task Removal
Removes tasks from the data structure and triggers UI refresh.
def delete_task(self, task):
"""Remove a task and update display."""
self.tasks.remove(task)
self.update_task_panel()
- User Notifications
Display system notifications for errors and successful actions using standard message boxes.
def show_error(self, title, message):
"""Display error message"""
messagebox.showerror(title, message)
def show_success(self, title, message):
"""Display error message"""
messagebox.showinfo(title, message)
- ModernInputDialog Class - Custom Modal Dialog
Display system notifications for errors and successful actions using standard message boxes.
class ModernInputDialog(tk.Toplevel):
"""Custom modern dialog for task input."""
def __init__(self, parent, title, prompt):
super().__init__(parent)
self.result = None
# Configure dialog window
self.title(title)
self.geometry("400x250")
self.configure(bg="#ffffff")
self.resizable(False, False)
self.overrideredirect(True)
self.attributes('-alpha', 0.0)
self.center_on_parent(parent)
self.container = tk.Frame(self, bg="#ffffff",
highlightthickness=1,
highlightbackground="#e2e8f0",
padx=20, pady=20)
self.container.pack(fill=tk.BOTH, expand=True)
title_bar = tk.Frame(self.container, bg="#ffffff")
title_bar.pack(fill=tk.X, pady=(0, 15))
tk.Label(title_bar, text = title, font=("Helvatica", 14, "bold"),
bg="#ffffff",
fg="#1e293b").pack(side=tk.LEFT)
close_btn = tk.Label(title_bar, text = "×", font=("Helvetica", 14),
bg="#ffffff", fg="#64748b", cursor="hand2")
close_btn.pack(side=tk.RIGHT)
close_btn.bind("<Button-1>", lambda e: self.cancel())
tk.Label(self.container, text = prompt, font=("Helvetica", 11),
bg="#ffffff", fg="#1e293b").pack(fill=tk.X)
entry_frame = tk.Frame(self.container, bg="#f1f5f9",
highlightthickness=1,
highlightbackground="#e2e8f0")
entry_frame.pack(fill=tk.X, pady=(10, 20))
self.entry = tk.Entry(entry_frame, font=("Helvetica", 12),
bd=0, bg="#f1f5f9", fg="#1e2936",
insertbackground="#2563eb")
self.entry.pack(fill=tk.X, padx=10, pady=8)
self.entry.focus_set()
btn_frame = tk.Frame(self.container, bg="#ffffff")
btn_frame.pack(fill=tk.X, pady=(0, 10))
# Create modern styled buttons
buttons = [
("Cancel", "#f1f2f9", "#64748b", self.cancel),
("Add Task", "#2563eb", "#ffffff", self.submit)
]
for text, bg, fg, command in buttons:
btn = tk.Button(btn_frame, text=text, font=("Helvetica", 12),
bg=bg, fg=fg, bd=0, padx=20, pady=8, cursor="hand2",
command=command)
btn.pack(side=tk.RIGHT, padx=5)
# Add hover effects
if text == "cancel":
btn.bind("<Enter>", lambda e: e.widget.configure(bg="#e2e8f0"))
btn.bind("<Leave>", lambda e: e.widget.configure(bg="#f1f5f9"))
else:
btn.bind("<Enter>", lambda e: e.widget.configure(bg="#1d4ed8"))
btn.bind("<Leave>", lambda e: e.widget.configure(bg="#2563eb"))
# Add key bindings
self.bind("<Return>", lambda e: self.submit())
self.bind("<Escape>", lambda e: self.cancel())
# Make dialog draggable
title_bar.bind("<Button-1>", self.start_drag)
title_bar.bind("<B1-Motion>", self.drag)
# Fade in animation
self.fade_in()
def fade_in(self):
"""Animate the dialog fading in."""
alpha = self.attributes("-alpha")
if alpha < 1.0:
alpha += 0.1
self.attributes("-alpha", alpha)
self.after(20, self.fade_in)
def fade_out(self):
"""Animate the dialog fading out."""
alpha = self.attributes("-alpha")
if alpha > 0:
alpha -= 0.1
self.attributes("-alpha", alpha)
self.after(20, self.fade_out)
else:
self.destroy()
def submit(self):
"""Handle dialog submission with fade out animation."""
self.result = self.entry.get()
self.fade_out()
def cancel(self):
"""Handle dialog cancellation with fade out animation."""
self.fade_out()
def center_on_parent(self, parent):
"""Center the dialog window on its parent."""
# Get parent window position and size
parent_x = parent.winfo_x()
parent_y = parent.winfo_y()
parent_width = parent.winfo_width()
parent_height = parent.winfo_height()
# Calculate center position
x = parent_x + (parent_width - 400) // 2
y = parent_y + (parent_height - 250) // 2
# Set dialog position
self.geometry(f"400x250+{x}+{y}")
def start_drag(self, event):
"""Store initial coordinates for window dragging."""
self.drag_data = (event.x_root - self.winfo_x(), event.y_root - self.winfo_y())
def drag(self, event):
"""Update window position during drag."""
x = event.x_root - self.drag_data[0]
y = event.y_root - self.drag_data[1]
self.geometry(f"+{x}+{y}")
The Final Result:
Download Projects Source Code







