How to Debug Your Textual Application

Textual is a great Python package for creating a lightweight, powerful, text-based user interface. That means you can create a GUI in your terminal with Python without learning curses! But what happens when you encounter some problems that require debugging your application? A TUI takes over your terminal, which means you cannot see anything from Python’s print() statement.

Wait? What about your IDE? Can that help? Actually no. When you run a TUI, you need a fully functional terminal to interact with it. PyCharm doesn’t work well with Textual. WingIDE doesn’t even have a terminal emulator. Visual Studio Code also doesn’t work out of the box, although you may be able to make it work with a custom json or yaml file. But what do you do if you can’t figure that out?

That is the crux of the problem and what you will learn about in this tutorial: How to debug Textual applications!

Getting Started

To get the most out of this tutorial, make sure you have installed Textual’s development tools by using the following command:

python -m pip install textual-dev --upgrade

Once you have the latest version of textual-dev installed, you may continue!

Debugging with Developer Mode

When you want to debug a Textual application, you need to open two terminal windows. On Microsoft Windows, you can open two Powershell or two Command Prompts. In the first terminal, run this command:

textual console

The Textual console will listen for any Textual application running in developer mode. But first, you need some kind of application to test with. Open up your favorite Python IDE and create a new file called hello_textual.py. Then enter the following code into it:

from textual.app import App, ComposeResult
from textual.widgets import Button


class WelcomeButton(App):

    def compose(self) -> ComposeResult:
        yield Button("Exit")

    def on_button_pressed(self) -> None:
        self.mount(Button("Other"))


if __name__ == "__main__":
    app = WelcomeButton()
    app.run()

To run a Textual application, use the other terminal you opened earlier. The one that isn’t running Textual Console in it. Then run this command:

textual run --dev hello_textual.py

You will see the following in your terminal:

Simple Textual app

If you switch over to the other terminal, you will see a lot of output that looks something like this:

Textual Console output

Now, if you want to test that you are reaching a part of your code in Textual, you can add a print() function now to your on_button_pressed() method. You can also use self.log.info() which you can read about in the Textual documentation.

Let’s update your code to include some logging:

from textual.app import App, ComposeResult
from textual.widgets import Button


class WelcomeButton(App):

    def compose(self) -> ComposeResult:
        yield Button("Exit")
        print("The compose() method was called!")

    def on_button_pressed(self) -> None:
        self.log.info("You pressed a button")
        self.mount(Button("Other"))


if __name__ == "__main__":
    app = WelcomeButton()
    app.run()

Now, when you run this code, you can check your Textual Console for output. The print() statement should be in the Console without you doing anything other than running the code. You must click the button to get the log statement in the Console.

Here is what the log output will look like in the Console:

Logging to the Textual Console

And here is an example of what you get when you print() to the Console:

Printing output to Textual Console

There’s not much difference here, eh? Either way, you get the information you need and if you need to print out Python objects, this can be a handy debugging tool.

If you find the output in the Console to be too verbose, you can use -x or --exclude to exclude log groups. Here’s an example:

textual console -x SYSTEM -x EVENT -x DEBUG -x INFO

In this version of the Textual Console, you are suppressing SYSTEM, EVENT, DEBUG, and INFO messages.

Launch your code from earlier and you will see that the output in your Console is greatly reduced:

Textual Console with output suppressed

Now, let’s learn how to use notification as a debugging tool.

Debugging with Notification

If you like using print() statements then you will love that Textual’s App() class provides a notify() method. You can call it anywhere in your application using self.app.notify() , along with a message. If you are in your App class, you can reduce the call to simply self.notify().

Let’s take the example from earlier and update it to use the notify method instead:

from textual.app import App, ComposeResult
from textual.widgets import Button


class WelcomeButton(App):

    def compose(self) -> ComposeResult:
        yield Button("Exit")

    def on_button_pressed(self) -> None:
        self.mount(Button("Other"))
        self.notify("You pressed the button!")


if __name__ == "__main__":
    app = WelcomeButton()
    app.run()

The notify() method takes the following parameters:

  • message – The message you want to display in the notification
  • title – An optional title to add to the message
  • severity – The message’s severity, which translates to a different color for the notification. You may use “information”, “error” or “warning”
  • timeout – The timeout in seconds for how long to show the message

Try editing the notification to use more of these features. For example, you could update the code above to use this instead:

self.notify("You pressed the button!", title="Info Message", severity="error")

Textual’s App class also provides a bell() method you can call to play the system bell. You could add this to really get the user’s attention, assuming they have the system bell enabled on their computer.

Wrapping Up

Debugging your TUI application successfully is a skill. You need to know how to find errors, and Textual’s dev mode makes this easier. While it would be great if a Python IDE had a fully functional terminal built into it, that is a very niche need. So it’s great that Textual included the tooling you need to figure out your code.

Give these tips a try, and you’ll soon be able to debug your Textual applications easily!