To-Do List Application
This is a simple To-Do List application built using Python’s Tkinter library, PIL (Pillow) for image processing, and Matplotlib for visualizing task completion using a pie chart. The application allows users to manage tasks, mark them as completed, and visualize task statistics.
Features
– Add Tasks: Enter a task in the input field and click “Add Task” to add it to the list.
– Mark as Done : Mark tasks as completed. Completed tasks are displayed in gray with a strikethrough.
– Delete Tasks : Remove individual tasks or delete all tasks at once.
– Save Tasks : Save the current list of tasks to a file.
– Load Tasks : Load tasks from a previously saved file.
– Task Statistics : Display the total number of tasks, remaining tasks, and completed tasks.
– Pie Chart : Visualize the proportion of completed vs. remaining tasks using a pie chart.
import tkinter as tk
from tkinter import messagebox
from PIL import Image, ImageTk
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
newWindow = tk.Tk()
newWindow.title("To-Do List")
newWindow.geometry("800x600+270+200")
newWindow.configure(bg="#1a1a2e")
# Global font styles
font_large = ("Helvetica", 22, "bold")
font_medium = ("Helvetica", 18)
font_small = ("Helvetica", 12)
def resize_image(image_path, size, background_color=(13, 21, 37)):
with Image.open(image_path) as img:
bg = Image.new('RGBA', img.size, background_color + (255,))
bg.paste(img, (0, 0), img)
img = bg.resize(size, Image.LANCZOS)
return ImageTk.PhotoImage(img)
done_icon = resize_image("ToDoList\\correct.png", (20, 20), background_color=(13, 21, 37))
delete_icon = resize_image("ToDoList\\bin.png", (20, 20), background_color=(13, 21, 37))
def mark_as_done(task_label, done_button):
task_label.config(fg="gray", font=(font_medium[0], font_medium[1], "overstrike"))
done_button.config(image=done_icon, state=tk.DISABLED, disabledforeground="green")
update_task_count()
def delete_task(task_frame):
task_frame.destroy()
update_serial_numbers()
update_task_count()
def add_task(task=None):
if not task:
task = task_entry.get()
if task:
task_frame = tk.Frame(task_container, bg="#162447", pady=5)
task_number = len(task_container.winfo_children()) + 1
task_label = tk.Label(task_frame, text=f"{task_number}. {task}", bg="#162447", fg="#e0e0e0", font=font_medium)
task_label.pack(side=tk.LEFT, padx=10)
done_button = tk.Button(task_frame, image=done_icon, command=lambda: mark_as_done(task_label, done_button), bg="#00b894", fg="#ffffff", font=font_small)
done_button.pack(side=tk.RIGHT, padx=5)
delete_button = tk.Button(task_frame, image=delete_icon, command=lambda: delete_task(task_frame), bg="#d63031", fg="#ffffff", font=font_small)
delete_button.pack(side=tk.RIGHT, padx=5)
task_frame.pack(fill=tk.X, pady=5)
task_entry.delete(0, tk.END)
update_serial_numbers()
update_task_count()
canvas.update_idletasks()
canvas.configure(scrollregion=canvas.bbox("all"))
def update_serial_numbers():
for index, task_frame in enumerate(task_container.winfo_children()):
task_label = task_frame.winfo_children()[0]
task_text = task_label.cget("text")
if ". " in task_text:
task_text = task_text.split(". ", 1)[1]
task_label.config(text=f"{index + 1}. {task_text}")
def delete_all_tasks():
for widget in task_container.winfo_children():
widget.destroy()
update_task_count()
def update_task_count():
total_tasks = len(task_container.winfo_children())
done_tasks = sum(1 for task_frame in task_container.winfo_children() if "overstrike" in task_frame.winfo_children()[0].cget("font"))
remaining_tasks = total_tasks - done_tasks
total_tasks_label.config(text=f"Total Tasks: {total_tasks}")
remaining_tasks_label.config(text=f"Remaining Tasks: {remaining_tasks}")
done_tasks_label.config(text=f"Completed Tasks: {done_tasks}")
update_pie_chart(total_tasks, remaining_tasks, done_tasks)
def update_pie_chart(total_tasks, remaining_tasks, done_tasks):
for widget in pie_chart_frame.winfo_children():
widget.destroy()
if total_tasks == 0:
return
labels = 'Completed Tasks', 'Remaining Tasks'
sizes = [done_tasks, remaining_tasks]
colors = ['#66bb6a', '#ef5350']
explode = (0.05, 0)
fig, ax = plt.subplots(figsize=(5, 3), dpi=100)
fig.patch.set_facecolor('#1a1a2e')
ax.set_facecolor('#1a1a2e')
wedges, texts, autotexts = ax.pie(sizes, explode=explode, colors=colors, autopct='%1.1f%%',
startangle=140, textprops=dict(color="w"))
for text in texts:
text.set_color('white')
text.set_fontsize(12)
for autotext in autotexts:
autotext.set_color('white')
autotext.set_fontsize(10)
ax.axis('equal')
ax.legend(wedges, labels, title="Tasks", loc="center left", bbox_to_anchor=(1, 0, 0.5, 1), fontsize=10, title_fontsize='13', facecolor='#1a1a2e', edgecolor='white')
canvas_chart = FigureCanvasTkAgg(fig, master=pie_chart_frame)
canvas_chart.draw()
canvas_chart.get_tk_widget().pack()
plt.close(fig)
def save_tasks():
with open("tasks.txt", "w") as file:
for task_frame in task_container.winfo_children():
task_label = task_frame.winfo_children()[0]
task_text = task_label.cget("text").split(". ", 1)[1]
task_done = "1" if "overstrike" in task_label.cget("font") else "0"
file.write(f"{task_text}|{task_done}\n")
messagebox.showinfo("Save Tasks", "Tasks have been saved successfully.")
def load_tasks():
try:
with open("tasks.txt", "r") as file:
for line in file:
task_text, task_done = line.strip().split("|")
add_task(task_text)
if task_done == "1":
task_frame = task_container.winfo_children()[-1]
task_label = task_frame.winfo_children()[0]
done_button = task_frame.winfo_children()[1]
mark_as_done(task_label, done_button)
update_task_count()
except FileNotFoundError:
pass
left_frame = tk.Frame(newWindow, bg="#1a1a2e")
left_frame.pack(side=tk.LEFT, fill=tk.BOTH, padx=10, pady=10, expand=True)
right_frame = tk.Frame(newWindow, bg="#1a1a2e")
right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, padx=10, pady=10, expand=True)
task_entry = tk.Entry(left_frame, width=35, bg="#162447", fg="#ffffff", font=font_large)
task_entry.pack(pady=10)
add_task_button = tk.Button(left_frame, text="Add Task", command=add_task, bg="#00a8cc", fg="#ffffff", font=font_small)
add_task_button.pack(pady=5)
delete_all_button = tk.Button(left_frame, text="Delete All Tasks", command=delete_all_tasks, bg="#d63031", fg="#ffffff", font=font_small)
delete_all_button.pack(pady=5)
save_button = tk.Button(left_frame, text="Save Tasks", command=save_tasks, bg="#4caf50", fg="#ffffff", font=font_small)
save_button.pack(pady=5)
load_button = tk.Button(left_frame, text="Load Tasks", command=load_tasks, bg="#ffc107", fg="#ffffff", font=font_small)
load_button.pack(pady=5)
total_tasks_label = tk.Label(left_frame, text="Total Tasks: 0", bg="#1a1a2e", fg="#00a8cc", font=font_medium)
total_tasks_label.pack(pady=2)
remaining_tasks_label = tk.Label(left_frame, text="Remaining Tasks: 0", bg="#1a1a2e", fg="#00cec9", font=font_medium)
remaining_tasks_label.pack(pady=2)
done_tasks_label = tk.Label(left_frame, text="Completed Tasks: 0", bg="#1a1a2e", fg="#00b894", font=font_medium)
done_tasks_label.pack(pady=2)
pie_chart_frame = tk.Frame(left_frame, bg="#1a1a2e")
pie_chart_frame.pack(pady=20, fill=tk.BOTH, expand=True)
canvas = tk.Canvas(right_frame, bg="#1a1a2e")
canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar = tk.Scrollbar(right_frame, orient="vertical", command=canvas.yview)
scrollbar.pack(side=tk.RIGHT, fill="y")
canvas.configure(yscrollcommand=scrollbar.set)
canvas.bind('<Configure>', lambda e: canvas.configure(scrollregion=canvas.bbox("all")))
task_container = tk.Frame(canvas, bg="#1a1a2e")
canvas.create_window((0, 0), window=task_container, anchor="nw")
newWindow.mainloop()
Output :-
