DEV Community

amiogithub
amiogithub

Posted on

Transforming My To-Do App: From Console to GUI with PySimpleGUI

After building a console-based to-do app, I wanted to elevate it by creating a graphical user interface (GUI). Using PySimpleGUI, I developed a more user-friendly version of the app that not only looks better but also makes managing tasks more intuitive. Here’s a detailed breakdown of the code and the thought process behind it.

Setting Up the Environment

import functions
import time
import PySimpleGUI as me
import os
Enter fullscreen mode Exit fullscreen mode

Why This?
The first step is importing the necessary modules:

functions handles reading and writing tasks to a file.
time allows for displaying the current time on the GUI.
PySimpleGUI, imported as me, is the library that provides all the tools to build the GUI.
os is used to check if the todos.txt file exists and create it if it doesn’t.
*Ensuring the Existence of the To-Do File
*


if not os.path.exists("todos.txt"):
    with open("todos.txt", "w") as file:
        pass

Enter fullscreen mode Exit fullscreen mode

Why This?
This block checks whether the todos.txt file exists. If it doesn’t, it creates an empty file. This ensures that the app doesn’t crash due to a missing file, which is crucial for smooth user experience.

Designing the GUI Layout

me.theme("DarkPurple4")
clock = me.Text("Type in a to-do")
label = me.Text('', key='clock')
input_box = me.InputText(tooltip="Enter todo", key='todo')
add_button = me.Button(size=2, image_source="004 add.png", mouseover_colors="LightBlue2", tooltip="Add Todo", key="Add")
list_box = me.Listbox(values=functions.get_todos(), key='todos', enable_events=True, size=[45, 10])
edit_button = me.Button("Edit")
complete_button = me.Button("Complete")
exit_button = me.Button("Exit")

Enter fullscreen mode Exit fullscreen mode

**Why This?
Here, I designed the layout of the app:

Theme: I chose a theme called "DarkPurple4" for aesthetics.
Clock: The clock label is initially set with placeholder text, and the actual time is updated later.
Input Box: Users can type their to-do items here.
Buttons: The app features buttons for adding, editing, completing, and exiting tasks. These are designed with tooltips and visual feedback to enhance user interaction.
Listbox: This displays the current to-dos, dynamically updated as users interact with the app.
Arranging the Layout in a Window**

window = me.Window("My To-Do App", layout=[[clock],
                                           [label],
                                           [input_box, add_button],
                                           [list_box, edit_button, complete_button],
                                           [exit_button]],
                   font=('Helvetica', 13))

Enter fullscreen mode Exit fullscreen mode

Why This?
The layout is arranged in a window with a specific font. The window’s title is "My To-Do App," and it organizes the elements in a grid-like structure, making the interface intuitive and easy to navigate.

Main Event Loop: Handling User Interactions

while True:
    event, values = window.read(timeout=200)
    window["clock"].update(value=time.strftime("%b %d, %Y %H:%M:%S"))
Enter fullscreen mode Exit fullscreen mode

Why This?
This loop keeps the app running, constantly listening for user input. The timeout=200 parameter updates the clock every 200 milliseconds, giving a real-time feel to the app.

Handling the Add Button

match event:
    case "Add":
        todos = functions.get_todos()
        new_todo = values['todo'] + "\n"
        todos.append(new_todo)
        functions.write_todos(todos)
        window['todos'].update(values=todos)
Enter fullscreen mode Exit fullscreen mode
match event:
    case "Add":
        todos = functions.get_todos()
        new_todo = values['todo'] + "\n"
        todos.append(new_todo)
        functions.write_todos(todos)
        window['todos'].update(values=todos)
Enter fullscreen mode Exit fullscreen mode

Why This?
When the "Add" button is clicked, this block:

Retrieves the current to-dos.
Appends the new task entered by the user.
Writes the updated list back to the file.
Updates the listbox in the GUI to reflect the new task.
Handling the Edit Button


    case "Edit":
        try:
            todo_to_edit = values['todos'][0]
            new_todo = values['todo']
            todos = functions.get_todos()
            index = todos.index(todo_to_edit)
            todos[index] = new_todo
            functions.write_todos(todos)
            window['todos'].update(values=todos)
        except IndexError:
            me.popup("Select an item first", font=('Helvetica', 15))
Enter fullscreen mode Exit fullscreen mode

Why This?
Editing a task is a bit more complex:

The app checks if a task is selected.
It retrieves the selected task and replaces it with the new input.
If no task is selected, an error message pops up, guiding the user to select an item.
Handling the Complete Button

    case "Complete":
        try:
            todo_to_complete = values['todos'][0]
            todos = functions.get_todos()
            todos.remove(todo_to_complete)
            functions.write_todos(todos)
            window['todos'].update(values=todos)
            window['todo'].update(value='')
        except IndexError:
            me.popup("Select an item first", font=('Helvetica', 15))
Enter fullscreen mode Exit fullscreen mode

Why This?
When a task is marked as complete:

The app removes it from the list and updates the file.
The listbox is updated to reflect the change.
If no task is selected, an error message helps the user correct the mistake.
Exiting the App

    case "Exit":
        break
Enter fullscreen mode Exit fullscreen mode

Why This?
This block simply breaks the loop, closing the app when the "Exit" button is clicked.

Handling Selection from the List

    case 'todos':
        window['todo'].update(value=values['todos'][0])
Enter fullscreen mode Exit fullscreen mode

Why This?
When a user clicks on a task in the list, the input box is automatically populated with that task, making it easier to edit or complete.

Final Cleanup

    case me.WINDOW_CLOSED:
        break

window.close()
Enter fullscreen mode Exit fullscreen mode

Why This?
If the window is closed using the close button, the loop breaks, and the window is properly closed to release resources.

Connecting with Git and GitHub
Version control is essential in any project, so I connected this project with Git and GitHub. Here’s a quick overview:

Initialize a Git Repository:

git init
This sets up a new Git repository in your project folder.
Enter fullscreen mode Exit fullscreen mode

Commit Changes:

git add .
git commit -m "Initial commit of to-do app GUI"
The git add . command stages all files, and git commit saves your changes with a descriptive message.

Enter fullscreen mode Exit fullscreen mode

Push to GitHub:


git remote add origin <repository-URL>
git push -u origin main
This connects your local repository to a GitHub repository and pushes the changes to GitHub.
Enter fullscreen mode Exit fullscreen mode

Final Thoughts
Turning my console-based to-do app into a GUI application was an exciting challenge. This project taught me a lot about user interface design, event-driven programming, and the importance of version control. I hope this breakdown helps you understand not just how the app works, but also why certain decisions were made. If you’re looking to build your own version, start small, iterate, and don’t hesitate to explore tools like PySimpleGUI and GitHub to take your projects to the next level.

Top comments (0)