Kahibaro
Discord Login Register

To-do list

Planning a Simple To-Do List Program

A to-do list app is a classic beginner project: it’s small, useful, and lets you practice user input, lists, conditions, loops, and files (if you want to save tasks).

In this chapter you’ll:

You can type and run all examples in a single .py file.


Designing the Program (Before Coding)

A basic to-do list program usually needs to:

  1. Store tasks (somewhere in memory, usually a list)
  2. Show tasks to the user
  3. Add new tasks
  4. Mark tasks as done (optional but very useful)
  5. Delete tasks (optional)
  6. Keep running until the user chooses to exit

A simple text menu is enough:

You don’t need all of these at once. Start small and add features step by step.


Step 1: Representing Tasks

We need a way to store each task. For a very first version, each task can just be a string in a list:

Later, we can add a “done/not done” status.

Basic structure:

tasks = []
# add a task
tasks.append("Buy milk")
# show all tasks
for task in tasks:
    print(task)

This gives you a simple in-memory list. When the program ends, the list is lost (we’ll fix that later with files).


Step 2: A Simple Menu Loop

To make a usable to-do list, we’ll use a loop that:

  1. Prints a menu
  2. Asks the user what they want to do
  3. Runs the chosen option
  4. Repeats until the user chooses to quit
tasks = []
while True:
    print("\nTo-Do List")
    print("1. Show tasks")
    print("2. Add task")
    print("3. Quit")
    choice = input("Choose an option (1-3): ")
    if choice == "1":
        print("Showing tasks...")
    elif choice == "2":
        print("Adding a task...")
    elif choice == "3":
        print("Goodbye!")
        break
    else:
        print("Invalid choice, please try again.")

This is the “skeleton” of the program. Now we fill in the parts for showing and adding tasks.


Step 3: Showing and Adding Tasks

Showing all tasks with numbers

It’s helpful to number tasks so the user can refer to them later:

def show_tasks(tasks):
    if not tasks:  # list is empty
        print("No tasks yet!")
        return
    print("\nYour tasks:")
    for index, task in enumerate(tasks, start=1):
        print(f"{index}. {task}")

Adding a new task

A simple function for adding a task:

def add_task(tasks):
    new_task = input("Enter a new task: ").strip()
    if new_task == "":
        print("Task cannot be empty.")
        return
    tasks.append(new_task)
    print("Task added.")

Now connect these functions to the menu loop:

tasks = []
def show_tasks(tasks):
    if not tasks:
        print("No tasks yet!")
        return
    print("\nYour tasks:")
    for index, task in enumerate(tasks, start=1):
        print(f"{index}. {task}")
def add_task(tasks):
    new_task = input("Enter a new task: ").strip()
    if new_task == "":
        print("Task cannot be empty.")
        return
    tasks.append(new_task)
    print("Task added.")
while True:
    print("\nTo-Do List")
    print("1. Show tasks")
    print("2. Add task")
    print("3. Quit")
    choice = input("Choose an option (1-3): ")
    if choice == "1":
        show_tasks(tasks)
    elif choice == "2":
        add_task(tasks)
    elif choice == "3":
        print("Goodbye!")
        break
    else:
        print("Invalid choice, please try again.")

This is already a working to-do list (without saving or marking done).


Step 4: Marking Tasks as Done

We want to track whether a task is complete. Two simple options:

  1. Store tasks as strings, and decorate them
    Example: "Buy milk [DONE]"
  2. Store tasks as dictionaries
    Example: {"text": "Buy milk", "done": False}

The second option is cleaner. Let’s switch to that.

Changing task structure

Instead of:

tasks.append(new_task)  # a string

Use:

tasks.append({"text": new_task, "done": False})

Update show_tasks to display [ ] or [x]:

def show_tasks(tasks):
    if not tasks:
        print("No tasks yet!")
        return
    print("\nYour tasks:")
    for index, task in enumerate(tasks, start=1):
        status = "[x]" if task["done"] else "[ ]"
        print(f"{index}. {status} {task['text']}")

Now add a function to mark one task as done:

def mark_task_done(tasks):
    if not tasks:
        print("No tasks to mark as done.")
        return
    show_tasks(tasks)
    choice = input("Enter the number of the task to mark as done: ")
    if not choice.isdigit():
        print("Please enter a valid number.")
        return
    index = int(choice) - 1  # convert to list index
    if index < 0 or index >= len(tasks):
        print("Task number out of range.")
        return
    tasks[index]["done"] = True
    print(f"Marked task {index + 1} as done.")

Add this option to the menu:

while True:
    print("\nTo-Do List")
    print("1. Show tasks")
    print("2. Add task")
    print("3. Mark task as done")
    print("4. Quit")
    choice = input("Choose an option (1-4): ")
    if choice == "1":
        show_tasks(tasks)
    elif choice == "2":
        add_task(tasks)
    elif choice == "3":
        mark_task_done(tasks)
    elif choice == "4":
        print("Goodbye!")
        break
    else:
        print("Invalid choice, please try again.")

Now tasks stay “done” as long as the program is running.


Step 5: Deleting Tasks

Deleting lets you remove tasks completely.

We can reuse the same style of input checking from marking done.

def delete_task(tasks):
    if not tasks:
        print("No tasks to delete.")
        return
    show_tasks(tasks)
    choice = input("Enter the number of the task to delete: ")
    if not choice.isdigit():
        print("Please enter a valid number.")
        return
    index = int(choice) - 1
    if index < 0 or index >= len(tasks):
        print("Task number out of range.")
        return
    removed = tasks.pop(index)
    print(f"Deleted task: {removed['text']}")

Update the menu again:

while True:
    print("\nTo-Do List")
    print("1. Show tasks")
    print("2. Add task")
    print("3. Mark task as done")
    print("4. Delete task")
    print("5. Quit")
    choice = input("Choose an option (1-5): ")
    if choice == "1":
        show_tasks(tasks)
    elif choice == "2":
        add_task(tasks)
    elif choice == "3":
        mark_task_done(tasks)
    elif choice == "4":
        delete_task(tasks)
    elif choice == "5":
        print("Goodbye!")
        break
    else:
        print("Invalid choice, please try again.")

At this point, you have a fully interactive to-do list while the program is running.


Step 6: Saving and Loading Tasks (Optional)

Right now, tasks disappear when you close the program. To keep them, we can:

We’ll use a plain text file with one task per line.

Simple text format

We need to save both the text and the done status. One simple format:

Where:

Saving tasks to a file

def save_tasks(tasks, filename="tasks.txt"):
    with open(filename, "w", encoding="utf-8") as file:
        for task in tasks:
            done_flag = "1" if task["done"] else "0"
            line = done_flag + ";" + task["text"] + "\n"
            file.write(line)
    print("Tasks saved.")

Loading tasks from a file

def load_tasks(filename="tasks.txt"):
    tasks = []
    try:
        with open(filename, "r", encoding="utf-8") as file:
            for line in file:
                line = line.rstrip("\n")
                if ";" not in line:
                    continue  # skip invalid lines
                done_flag, text = line.split(";", 1)
                task = {
                    "text": text,
                    "done": (done_flag == "1")
                }
                tasks.append(task)
    except FileNotFoundError:
        # No saved tasks yet
        tasks = []
    return tasks

Now use these in your main program:

def show_tasks(tasks):
    if not tasks:
        print("No tasks yet!")
        return
    print("\nYour tasks:")
    for index, task in enumerate(tasks, start=1):
        status = "[x]" if task["done"] else "[ ]"
        print(f"{index}. {status} {task['text']}")
def add_task(tasks):
    new_task = input("Enter a new task: ").strip()
    if new_task == "":
        print("Task cannot be empty.")
        return
    tasks.append({"text": new_task, "done": False})
    print("Task added.")
def mark_task_done(tasks):
    if not tasks:
        print("No tasks to mark as done.")
        return
    show_tasks(tasks)
    choice = input("Enter the number of the task to mark as done: ")
    if not choice.isdigit():
        print("Please enter a valid number.")
        return
    index = int(choice) - 1
    if index < 0 or index >= len(tasks):
        print("Task number out of range.")
        return
    tasks[index]["done"] = True
    print(f"Marked task {index + 1} as done.")
def delete_task(tasks):
    if not tasks:
        print("No tasks to delete.")
        return
    show_tasks(tasks)
    choice = input("Enter the number of the task to delete: ")
    if not choice.isdigit():
        print("Please enter a valid number.")
        return
    index = int(choice) - 1
    if index < 0 or index >= len(tasks):
        print("Task number out of range.")
        return
    removed = tasks.pop(index)
    print(f"Deleted task: {removed['text']}")
def save_tasks(tasks, filename="tasks.txt"):
    with open(filename, "w", encoding="utf-8") as file:
        for task in tasks:
            done_flag = "1" if task["done"] else "0"
            line = done_flag + ";" + task["text"] + "\n"
            file.write(line)
    print("Tasks saved.")
def load_tasks(filename="tasks.txt"):
    tasks = []
    try:
        with open(filename, "r", encoding="utf-8") as file:
            for line in file:
                line = line.rstrip("\n")
                if ";" not in line:
                    continue
                done_flag, text = line.split(";", 1)
                task = {
                    "text": text,
                    "done": (done_flag == "1")
                }
                tasks.append(task)
    except FileNotFoundError:
        tasks = []
    return tasks
# Main program
tasks = load_tasks()
while True:
    print("\nTo-Do List")
    print("1. Show tasks")
    print("2. Add task")
    print("3. Mark task as done")
    print("4. Delete task")
    print("5. Save and quit")
    choice = input("Choose an option (1-5): ")
    if choice == "1":
        show_tasks(tasks)
    elif choice == "2":
        add_task(tasks)
    elif choice == "3":
        mark_task_done(tasks)
    elif choice == "4":
        delete_task(tasks)
    elif choice == "5":
        save_tasks(tasks)
        print("Goodbye!")
        break
    else:
        print("Invalid choice, please try again.")

Now your to-do list remembers tasks between runs.


Step 7: Possible Extensions

Once the basic version works, you can extend it in many ways. Some ideas:

Each new feature is a chance to practice functions, input handling, and data structures.


Summary

By building this to-do list, you practiced:

This project combines many basic Python skills into a single, useful program you can keep improving as you learn more.

Views: 12

Comments

Please login to add a comment.

Don't have an account? Register now!