makerhacks cross-posted this post in Programming & Dev 3 months ago


Create Amazing Python User Interfaces in Your Terminal with Textual

in #python3 months ago

image.png

Creating a UI for your Python scripts can be a pain, especially if you just need something one step up from a CLI interface. It seems waste to have to configure a whole web app or GUI sometimes.

Enter Textual, a Python suite that allows you to quickly (like, a couple of minutes tops) spin up beautiful text-based UIs, even running over the web!

What is it?

Textual is a modern Python framework for building TUI (Text User Interface) apps using asyncio and rich for styling.

Here by way of example is my To-Do app:

image.png

Even the debug traces are made pretty!:

image.png

Getting Started

Make sure you have Python 3.7 or newer.

pip3 install textual

Optionally, install rich to experiment with more styling:

pip3 install rich

Create a file called hello_textual.py:

from textual.app import App
from textual.widgets import Static

class HelloTextual(App):
    def compose(self):
        yield Static("Hello, Textual!")

if __name__ == "__main__":
    HelloTextual().run()

How it works:

  • App is the base class for your application.
  • compose() is a generator where you define what widgets to display.
  • Static is a simple widget that shows fixed text.

Adding Style

Textual uses CSS-like styling. Create a file hello_textual.tcss:

Screen {
    background: black;
    color: green;
    align: center middle;
}

Then update your app to include this stylesheet:

from textual.app import App
from textual.widgets import Static

class HelloTextual(App):
    CSS_PATH = "hello_textual.tcss"

    def compose(self):
        yield Static("Hello, Textual!")

if __name__ == "__main__":
    HelloTextual().run()

Buttons and Labels

We want our apps to be interactive so the obvious next step is to add a button that when clicked does something.

image.png

Create a file called button_example.py:

from textual.app import App, ComposeResult
from textual.widgets import Label, Button
from textual.containers import Vertical

class HelloWorldApp(App):
    def compose(self) -> ComposeResult:
        with Vertical():
            self.label = Label("Press the button")
            yield self.label
            yield Button("Click Me", id="hello-button")

    def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "hello-button":
            self.label.update("Hello, world!")

if __name__ == "__main__":
    HelloWorldApp().run()

How it works:

  • Label displays text and can be updated dynamically.
  • Button triggers events you can respond to.
  • on_button_pressed is triggered when a button is clicked.
  • Vertical() is a layout container to stack widgets vertically.

If, like me, you are using an IDE that does not like Ctrl-Q to quit out of the application, you can add the following lines right under the class definition like so:

# Class definition, new lines go right under ..
class HelloWorldApp(App):

# Add these lines
    """A simple Textual app to demonstrate the basics."""
    BINDINGS = [("q", "quit", "Quit"),]

Now you will be able to quit out with q

Next Steps

Now that you've created a basic app with styles, you can explore more widgets such as Input and DataTable

You can also handle user input and add interactivity by overriding methods like:

  • on_mount
  • on_key
  • on_button_pressed

For more info, check out the official docs:
https://textual.textualize.io/

This is just the beginning. Textual gives you the ability to build all sorts of apps with tons of widgets, structured layouts, mouse support, and themes.