DEV Community

Cover image for How I Built a Simple To-Do App in Python: From Concept to Code
amiogithub
amiogithub

Posted on

How I Built a Simple To-Do App in Python: From Concept to Code

Creating a to-do app in Python was one of my recent projects, and it was an enriching experience that evolved over time. Initially, I started with simpler functions, focusing on getting the app to run smoothly in the console. As days passed, I refined and optimized the code, leading to the final version I'm sharing here. While I started with PyCharm, I encountered performance issues like overheating, so I'd recommend using VS Code instead for a smoother experience.

Why Build a To-Do App?

A to-do app might seem like a basic project, but it's a fantastic way to apply and improve your programming skills. It involves working with file handling, loops, conditional statements, and functions—all essential concepts in Python. Plus, the end product is something practical that you can actually use in your daily life.

Breaking Down the Code: A Step-by-Step Explanation

Let's dive into the code, and I'll walk you through my thought process at each stage.

  1. Setting Up the Environment:
import functions
import time

now = time.strftime("%b %d, %Y %H:%M:%S")
print("It is", now)
print("Routine checkup")
Enter fullscreen mode Exit fullscreen mode

Why This?

I wanted the app to feel personal, so I added a timestamp at the start. It gives a real-time feel and serves as a gentle reminder of when you last checked your tasks. Importing the functions module keeps the main file clean and organized by handling file operations separately.

  1. Adding Tasks:
if user_action.startswith('add'):
    todo = user_action[4:]
    todos = functions.get_todos()
    todos.append(todo + '\n')
    functions.write_todos(todos)
Enter fullscreen mode Exit fullscreen mode

Why This?
Initially, I experimented with different ways to add tasks. The approach I settled on—using string slicing—ensures that the app is user-friendly. It allows users to type "add " without needing to follow a rigid format. By appending a newline character (\n), the tasks are neatly stored in the text file, ensuring proper formatting.

3.Showing Tasks:

elif user_action.startswith('show'):
    todos = functions.get_todos()
    for index, item in enumerate(todos):
        item = item.strip('\n')
        row = f'{index + 1} - {item}'
        print(row)
Enter fullscreen mode Exit fullscreen mode

Why This?
Displaying tasks might seem straightforward, but it's crucial to ensure clarity. I used enumerate() to list tasks with numbers, making it easy to reference them later. Removing newline characters during display ensures the output is clean and readable.

4. Editing Tasks:

elif user_action.startswith('edit'):
    try:
        number = int(user_action[5:]) - 1
        todos = functions.get_todos()
        new_todo = input("Enter new todo: ") + '\n'
        todos[number] = new_todo
        functions.write_todos(todos)
    except ValueError:
        print("Oops! Your command is invalid.")
        continue

Enter fullscreen mode Exit fullscreen mode

Why This?
Editing was one of the trickier features to implement. The main challenge was managing user input and ensuring the correct task was updated. I chose to subtract 1 from the task number to align with Python's zero-based indexing. This choice simplifies coding but might require a brief explanation for users.

5. Completing Tasks:

elif user_action.startswith('complete'):
    try:
        number = int(user_action[9:])
        todos = functions.get_todos()
        todo_to_remove = todos.pop(number - 1).strip('\n')
        functions.write_todos(todos)
        print(f'Todo "{todo_to_remove}" was successfully removed.')
    except IndexError:
        print("The task number is not in your list.")
        continue
Enter fullscreen mode Exit fullscreen mode

Why This?
Completing a task involves removing it from the list, which brings up the challenge of managing list indices. By using .pop(), I was able to remove tasks efficiently, and the app confirms the deletion to avoid any confusion. Handling potential errors, like an IndexError, ensures that the app remains robust even with incorrect input.

6. Exiting the App:

elif user_action.startswith('exit'):
    break
Enter fullscreen mode Exit fullscreen mode

Why This?
The exit function is simple but essential. It gracefully terminates the loop, ending the session while ensuring that no tasks are lost in the process.

7.Functions File: Keeping It Organized

FILEPATH='todos.txt'
def get_todos(file_path=FILEPATH):
    with open(file_path, 'r') as file_local:
        todos_local = file_local.readlines()
        return todos_local

def write_todos(todos_arg, file_path=FILEPATH):
    with open(file_path, 'w') as file_local:
        file_local.writelines(todos_arg)
Enter fullscreen mode Exit fullscreen mode

Why This?
Splitting file operations into a separate functions.py file kept the main script clean and easy to read. It also makes it easier to debug and scale the app. By abstracting these operations, I ensured that the main logic focused on user interaction, while the functions file handled data persistence.

Final Thoughts
Building this to-do app was a fantastic learning experience. I encourage anyone interested in Python to try building something similar. Start simple, test your code, and gradually add features. Remember, it's not just about writing code—it's about understanding why you choose certain approaches and how they contribute to a more efficient and user-friendly program. And if you're using PyCharm and facing performance issues, give VS Code a try—it might just make your coding journey a little smoother.

Image description

Top comments (0)