Python

How To Build A ToDo TUI Application With Textual

tui heading 4

Did you know it’s possible to build a rich UI that runs completely in the terminal? Even some experienced Python developers avoid using the terminal in favor of Graphical User Interfaces (GUIs). But, today, many developer tools use a Terminal User Interface (TUI) since they often spend a lot of time working in the terminal. Not only are there programs and tools, but there are also several games that run entirely in the terminal.

So, in this article, we will first develop a good understanding of TUIs and then move on to creating an interesting TUI using PyScripter to manage to-do lists.

What is a TUI?

How To Build A ToDo TUI Application With Textual a close up of a laptop showing a Python script being edited

A TUI, short for Terminal User Interface, uses the terminal’s advanced features as the basis for a kind of GUI, generally occupying the full area of the terminal. It uses escape codes on UNIX platforms and proprietary console commands on the Windows platform to take control of the entire console window and display a user interface. TUIs can handle input via the keyboard, but some modern systems also use mouse input. This is what makes them very special and different from conventional terminals.

Thus, a TUI allows users to interact with a program via the terminal. The TUI applications interact via UI, although many TUI programs also accept commands.

Why is Textual popular for making TUIs?

Textual is a Python framework for creating interactive applications that run in your terminal. It mainly adds interactivity to Rich, a rich text and formatting library, with a Python API inspired by modern web development.

On modern terminal software, Textual apps offer more than 16 million colors, mouse support, and smooth, flicker-free animation. Furthermore, a powerful layout engine and reusable components make it possible to build apps that rival the desktop and web experience.

Currently, the Textual framework runs on Linux, macOS, and Windows and requires Python 3.7 or above.

How to build a ToDo list TUI in Python?

How To Build A ToDo TUI Application With Textual the PyScripter download page shown on a laptop

Let us now move to the main part of this tutorial and build a to-do list in Python. But first, there are some requirements that you need to install.

What are the requirements for this TUI tutorial?

For this tutorial, you will need Python 3.9+ installed on your computer. You might also need to install a feature-packed IDE like PyScripter to write the code.

Then, you can install textual via PyPI.

If you intend to run Textual apps simply, install textual using the following command:

However, if you plan to develop Textual apps, you should install "textual[dev]". This is because the [dev] part installs a few extra dependencies for development.

Now that we are done setting up our environment, let’s start building our ToDo list manager.

How to create a basic ToDo in textual?

We’ll start this tutorial by creating a TUI in Textual that serves our purpose and then add to it using Textual’s amazing CSS-supported features.

How To Build A ToDo TUI Application With Textual part 1

First, we will create a file named Todo.py and import some functions required for our ToDo application:

The first line imports the App class that will act as the base of our application. Next, we import Container from textual.containers. As the name implies, this acts as a widget containing other widgets. 

Finally, we import Header, Footer, Button, and Static. The Header widget shows up at the top of our app and shows the title at the top of the app. The Footer, on the other hand, shows a bar at the bottom of the screen with bound keys. The Button creates a clickable button and Static acts as a base class for simple control. In addition, we also define a list named done that will contain all of the user’s completed tasks.
Let’s first create our Task class:

The Task class is a simple class that inherits from Static. This creates a static widget allowing us to display the tasks we want on our app.

Next, we will create a Todo widget that will cleanly display our Task widgets:

As you can see the ToDo widget class also extends Static. This class has a compose() method that yields child widgets consisting of a Button object and a single Task object. The Button constructor takes a label argument that displays the text on the button. It also takes two optional parameters:

  • id is an identifier that can help identify different buttons and help in applying styles.
  • variant is a string that selects a default style. The “success” variant makes the button green.

These widgets will form a single Task in our application. 
In addition, we also define an on_button_pressed() method that acts as an event handler. Event handlers are methods called by Textual in response to an event, such as a key press, mouse click, etc. Event handlers begin with on_ followed by the name of the event they will handle. Hence, on_button_pressed will handle the Button.Pressed event. Our event handler assigns a CSS class named “Task_Done” (which we will define later) to the ToDo Class whenever our Button is pressed. This will effectively change the color of our task showing that the task is done.

How To Build A ToDo TUI Application With Textual part 2

Finally, we can start building our ToDoApp. We will first begin by creating our ToDoApp class that will inherit from App. We also create a All_Tasks list that will store all our tasks:

Next, we a compose() method that displays our widgets to our app:

Here we only call three widgets. The Header, Footer, and Container. The container will contain all of the ToDo widgets in our TUI. Additionally, we add an id parameter to our Container to help us add widgets.

How to add key bindings in a Python TUI app?

Next, we add bindings to our app. For our application, we will bind four keys:

  • The q key binding will help us quit our TUI.
  • The d key binding will help us toggle between a dark and light mode in our TUI.
  • The r binding will read all the tasks in our todo.txt file and add them to our TUI.
  • Finally, we add a s binding that will save all the tasks that are not done in our TUI.

To create our key bindings, we create a BINDINGS list containing tuples that map (or bind) keys to actions in your app. The first value in the tuple is any one of the keys on the keyboard; the second value is the action/ function to be called; the final value is a short description of the function:

Each key binding is mapped to a function that begins as action_. For example, d will be mapped to the action_toggle_dark() function by adding action_ at the beginning of the toggle_dark that’s present in the tuple. So, now let us define our functions.
We first define the dark mode function, action_toggle_dark(), which inverts the dark attribute:

Next, we define a helper method named add_ToDo():

This function simply takes in a text argument, creates a ToDo object, and adds it to our Tasks Container. This will help us add multiple tasks in our app using a file. Now, we define the action_read() method:

The action_read() method takes in no arguments and is triggered when we press "r" on the keyboard. The function uses self.query("ToDo") to retrieve all ToDo objects in our app. If we have any existing ToDo objects, we simply remove them by calling .remove() method.

Next, we open and read the contents of our todo.txt file. The file contains a single task in one line. We iterate over all the tasks in our file and use the add_ToDo() helper function to add each task to our Container.

Finally, we can define our action_save() method that overwrites our existing todo.txt file:

The function simply reads all the tasks that we have in the given text file and checks them against all the tasks that are selected as done (via the done Button on_pressed method) in our global done list. It then overwrites the todo.txt file with all tasks that are not completed.

What does the final example code for the Python ToDo list TUI app look like?

Here is what our final Todo.py looks like:

How to run and test our Python ToDo list TUI app?

Before running this code, let’s create some tasks. Create a todo.txt file in the same directory as your Todo.py file and add the following tasks in separate lines:

How To Build A ToDo TUI Application With Textual running the TUI Python app

Now let’s execute Todo.py. This will give you the following output in the terminal:

How To Build A ToDo TUI Application With Textual the Python ToDo app screen in action

If we press "r", the tasks contained in todo.txt will appear in the TUI:

How To Build A ToDo TUI Application With Textual lot of buttons

Clicking the “Done” button will cause these tasks to be appended to our done list. For example, pressing the “Done” button corresponding to “Gym” will cause “Gym” to be appended to our done list. 

Pressing “s” will cause the completed tasks to be removed from todo.txt:

How To Build A ToDo TUI Application With Textual a screenful of todo items

Even though the buttons are clickable and you can scroll the container, our TUI is not very interactive. This is because we have not applied any styles to our new widgets. That is where Textual’s new features come in. 

How to enhance this Python TUI using CSS?

CSS files are data files loaded by your app which contain information about styles to apply to your widgets. Textual has CSS support, allowing you to customize your application. As mentioned before, we can make our TUI more visually appealing using CSS. For this, let’s go ahead and create a CSS file, Todo.css.

How To Build A ToDo TUI Application With Textual some example CSS

CSS files contain several declaration blocks. Here’s the first block that Todo.css will contain:

The first line tells Textual that the styles should apply to the ToDo widget, while the lines between the curly brackets contain the styles themselves. layout: horizontal aligns child widgets horizontally from left to right. background: #36454F sets the background color to #36454F or charcoal but there are also other ways to specify colors, such as rgb(54,69,79). height: 7 sets the widget’s height equivalent to seven lines of text. margin: 1 will set a one-cell margin around the ToDo widget to create some space between widgets in the list. Now, min-width: 50 sets the width of the widget to at least fifty cells. Note that there is no maximum so that the width can go on to cover an entire window. Lastly, padding: 2 will set a padding of two cells around its child widgets to avoid cluttering.

Following are the contents of the rest of Todo.css:

The Task block aligns text to the center (horizontally) and middle (vertically) and sets its height to three lines. Using text-opacity, we can even fade the text slightly.

The Button block sets the width of buttons to ten cells equivalent to character widths and sets the dock style to left to align the widget to the left edge. 

We also wanted our ToDo widget to have two states. One would be a default state with a Done Button and the other when the Done Button is clicked; the widget has a green background and bold text. Thus, the last class is a rule where “.” indicates that .Task_Done refers to a CSS class called Task_Done. The new styles will only be applied to widgets with this CSS class. In the background and color options, the $ prefix picks a pre-defined color from the built-in theme. 

And we’re all done with styling our TUI. So, let’s add the path of this CSS path to our ToDoApp class in Todo.py to integrate our CSS file with our TUI app (it is interesting to note that Textual supports multiple CSS paths):

How To Build A ToDo TUI Application With Textual showing where to edit the CSS path

Finally, we can run our app again to test it out:

How To Build A ToDo TUI Application With Textual lots of buttons but repositioned and made more pleasing due to the CSS

You can mark “Cook” and “Study” as done by clicking on their corresponding buttons:

How To Build A ToDo TUI Application With Textual a different look thanks to more CSS

You can then save this updated to-do list by pressing the “s” button. If we press “r” again, we can see the changes:

How To Build A ToDo TUI Application With Textual CSS number 3

We can also confirm these changes by rechecking todo.txt:

How To Build A ToDo TUI Application With Textual the ToDo list app

You may have noticed that we have used PyScripter throughout this project. This is because PyScripter is one of the best tools that you can have to organize your code and understand it better. So if you are starting with Python, PyScripter should be part of your starter kit.

Why should you use PyScripter to create TUIs?

How To Build A ToDo TUI Application With Textual the DelphiFMX and PyScripter download page on a laptop screen

Embarcadero sponsors PyScripter, a popular, free, and open-source Python IDE. This IDE has all the features you’d expect from a modern Python IDE while remaining lightweight and fast. PyScripter is natively compiled for Windows, allowing it to use as little memory as possible while providing maximum performance. It also has full Python debugging capabilities, both locally and remotely. Another noteworthy feature of this IDE is its integration with Python tools like PyLint, TabNanny, Profile, etc. It can also run or debug files directly from memory.

PyScripter includes all the features you’d expect from a professional IDE, such as brace highlighting, code folding, code completion, and syntax checking as you type. It also has features you’d expect to find in a legitimate native app, such as dragging and dropping files into the PyScripter IDE. These features work in tandem to save time and make the development process simple and enjoyable.

In addition, PyScripter includes a Python interpreter with call indications and code completion. This program lets you run scripts without saving them and keeps a record of your command history. This IDE also includes a remote Python debugger for debugging Python code. As a result, variables, the watch window, and the call stack are visible. Additional debugging tools built into PyScripter include conditional breakpoints and thread debugging. Furthermore, PyScripter has debugger indications to help you debug your files without saving them, as well as the ability to destroy them.

Thus, PyScripter is a fantastic IDE seeking to create a Python IDE that can compete with other languages’ traditional Windows-based IDEs. It is lightweight, versatile, and extendable with a lot of features.

Furthermore, since it was built from the ground up for Windows, it is substantially faster and more responsive than cumbersome text editors, making it perfect for making TUIs.

Are you ready to build your own Python TUI?

How To Build A ToDo TUI Application With Textual a laptop screen displaying the download PyScripter page

Congratulations on building a stunning to-do manager TUI! In this tutorial, you have learned several useful things about Python!

Textual is a TUI framework for Python inspired by modern web development. It has a very active repository on GitHub. This awesome framework is built on Rich and is currently a work in progress, but usable by programmers who don’t mind some API instability between updates.

You can conveniently build complex and simple TUIs with Textual in PyScripter. This IDE will make the entire process very seamless and enjoyable for you.

Now that you know PyScripter’s numerous features and how Textual works, go ahead and create your own TUIs in everyone’s favorite IDE!

What are the FAQs on building a Python TUI?

How To Build A ToDo TUI Application With Textual a laptop with the DelphiFMX and DelphiVCl page open on its screen

What are some use cases of TUIs?

TUIs can be used to make programs like stopwatches, calculators, code and other file viewers, basic games, and to-do lists (as shown in this article).

There are several other possibilities. The only limit is your imagination!!

Which other TUI libraries are available in Python?

Pytermgui, picotui, and pyTermTk are other frameworks and libraries that can create interactive and attractive TUIs in Python.

What is the difference between CLI, GUI, and TUI?

GUI means Graphical User interface. You must have heard this word most commonly. It is the GUI that has enabled the common user to use computers. A GUI application is an application that can be run with a mouse, touchpad, or touchscreen.

The CLI is a command line program that accepts text input to perform a certain task. Any application you can use via commands in the terminal falls into this category.

TUI, i.e., Terminal User Interface, falls between GUI and CLI. It can also be called a kind of text-based GUI. Before the arrival of the GUI, everything used to run on the command line; then, the terminal user interface provided a kind of GUI on the command line. TUI programs can be run via a mouse or keyboard.

What is Python4Delphi?

In short, P4D offers a selection of real-world apps that can be modified to your individual needs and used in Python GUI.

Consider integrating Delphi’s GUI and desktop app development power with Python in your applications to give world-class solutions to your clients’ needs. Python4Delphi is the answer to your problems.

Python for Delphi (P4D) is a free Delphi and Lazarus component library that wraps the Python DLL. They make it simple to run Python scripts while allowing for new Python modules and types. The best feature of P4D is that it simplifies the use of Python as a scripting language. It also includes numerous customizable demos and tutorials that are ready to use in the development of real-world apps.

Related posts
CodeIDELearn PythonPythonPython GUITkinter

How To Make More Than 20 ChatGPT Prompts Work With Python GUI Builders And OpenCV Library?

CodeIDEProjectsPythonWindows

Unlock the Power of Python for Deep Learning with Radial Basis Function Networks (RBFNs)

CodeIDELearn PythonPythonPython GUITkinter

How To Make More Than 20 ChatGPT Prompts Work With Python GUI Builders And NumPy Library?

CodeIDEProjectsPythonWindows

Unlock the Power of Python for Deep Learning with Generative Adversarial Networks (GANs) - The Engine behind DALL-E

Leave a Reply

Your email address will not be published. Required fields are marked *