Save and Open ToDo Lists – Python Tkinter GUI Tutorial #156

In this video I’ll show you how to save and open our todo lists for our To-Do list app with Tkinter and Python!

We’ll start out by creating a menu at the top of our app that has dropdowns for Saving a File, Opening a File, and Clearing our current list.

Then we’ll build out the functionality using a filedialog widget to allow us to name our files before we save them. We’ll actually save the files as .dat files.

Python Code: todo.py
(Github Code)

from tkinter import *
from tkinter.font import Font
from tkinter import filedialog
import pickle

root = Tk()
root.title('Codemy.com - ToDo List!')
root.iconbitmap('c:/gui/codemy.ico')
root.geometry("500x500")

# Define our Font
my_font = Font(
	family="Brush Script MT",
	size=30,
	weight="bold")

# Creat frame
my_frame = Frame(root)
my_frame.pack(pady=10)

# Create listbox
my_list = Listbox(my_frame,
	font=my_font,
	width=25,
	height=5,
	bg="SystemButtonFace",
	bd=0,
	fg="#464646",
	highlightthickness=0,
	selectbackground="#a6a6a6",
	activestyle="none")

my_list.pack(side=LEFT, fill=BOTH)

# Create dummy list
#stuff = ["Walk The Dog", "Buy Groceries", "Take A Nap", "Learn Tkinter", "Rule The World"]
# Add dummy list to list box
#for item in stuff:
#	my_list.insert(END, item)

# Create scrollbar
my_scrollbar = Scrollbar(my_frame)
my_scrollbar.pack(side=RIGHT, fill=BOTH)

# Add scrollbar
my_list.config(yscrollcommand=my_scrollbar.set)
my_scrollbar.config(command=my_list.yview)

# create entry box to add items to the list
my_entry = Entry(root, font=("Helvetica", 24), width=26)
my_entry.pack(pady=20)

# Create a button frame
button_frame = Frame(root)
button_frame.pack(pady=20)

# FUNCTIONS
def delete_item():
	my_list.delete(ANCHOR)

def add_item():
	my_list.insert(END, my_entry.get())
	my_entry.delete(0, END)

def cross_off_item():
	# Cross off item
	my_list.itemconfig(
		my_list.curselection(),
		fg="#dedede")
	# Get rid of selection bar
	my_list.selection_clear(0, END)

def uncross_item():
	# Cross off item
	my_list.itemconfig(
		my_list.curselection(),
		fg="#464646")
	# Get rid of selection bar
	my_list.selection_clear(0, END)

def delete_crossed():
	count = 0
	while count < my_list.size():
		if my_list.itemcget(count, "fg") == "#dedede":
			my_list.delete(my_list.index(count))
		
		else: 
			count += 1

def save_list():
	file_name = filedialog.asksaveasfilename(
		initialdir="C:/gui/data",
		title="Save File",
		filetypes=(
			("Dat Files", "*.dat"), 
			("All Files", "*.*"))
		)
	if file_name:
		if file_name.endswith(".dat"):
			pass
		else:
			file_name = f'{file_name}.dat'

		# Delete crossed off items before saving
		count = 0
		while count < my_list.size():
			if my_list.itemcget(count, "fg") == "#dedede":
				my_list.delete(my_list.index(count))
			
			else: 
				count += 1

		# grab all the stuff from the list
		stuff = my_list.get(0, END)

		# Open the file
		output_file = open(file_name, 'wb')

		# Actually add the stuff to the file
		pickle.dump(stuff, output_file)


def open_list():
	file_name = filedialog.askopenfilename(
		initialdir="C:/gui/data",
		title="Open File",
		filetypes=(
			("Dat Files", "*.dat"), 
			("All Files", "*.*"))
		)

	if file_name:
		# Delete currently open list
		my_list.delete(0, END)

		# Open the file
		input_file = open(file_name, 'rb')

		# Load the data from the file
		stuff = pickle.load(input_file)

		# Output stuff to the screen
		for item in stuff:
			my_list.insert(END, item)





def delete_list():
	my_list.delete(0, END)

# Create Menu
my_menu = Menu(root)
root.config(menu=my_menu)

# Add items to the menu
file_menu = Menu(my_menu, tearoff=False)
my_menu.add_cascade(label="File", menu=file_menu)
# Add dropdown items
file_menu.add_command(label="Save List", command=save_list)
file_menu.add_command(label="Open List", command=open_list)
file_menu.add_separator()
file_menu.add_command(label="Clear List", command=delete_list)


# Add some buttons
delete_button = Button(button_frame, text="Delete Item", command=delete_item)
add_button = Button(button_frame, text="Add Item", command=add_item)
cross_off_button = Button(button_frame, text="Cross Off Item", command=cross_off_item)
uncross_button = Button(button_frame, text="Uncross Item", command=uncross_item)
delete_crossed_button = Button(button_frame, text="Delete Crossed", command=delete_crossed)

delete_button.grid(row=0, column=0)
add_button.grid(row=0, column=1, padx=20)
cross_off_button.grid(row=0, column=2)
uncross_button.grid(row=0, column=3, padx=20)
delete_crossed_button.grid(row=0, column=4)

root.mainloop()

John Elder

John is the CEO of Codemy.com where he teaches over 100,000 students how to code! He founded one of the Internet's earliest advertising networks and sold it to a publicly company at the height of the first dot com boom. After that he developed the award-winning Submission-Spider search engine submission software that's been used by over 3 million individuals, businesses, and governments in over 42 countries. He's written several Amazon #1 best selling books on coding, and runs a popular Youtube coding channel.

View all posts

Add comment

John Elder

John is the CEO of Codemy.com where he teaches over 100,000 students how to code! He founded one of the Internet's earliest advertising networks and sold it to a publicly company at the height of...