The Textual Python framework, developed by the creators of the Rich library, is a cutting-edge tool designed to create modern, interactive, and dynamic terminal-based applications. By leveraging Python's simplicity and the Rich library's powerful text styling capabilities, Textual allows developers to craft visually appealing command-line interfaces (CLIs) and terminal dashboards that often rival graphical user interfaces (GUIs) in functionality and appearance.
Why Use Textual?
Textual brings several advantages to developers looking to enhance their terminal applications:
Rich Text and Styling: Built on Rich, it offers unparalleled support for text styling, colors, and formatting in terminal output.
Event-Driven Architecture: Enables dynamic interactions, animations, and updates, providing a highly responsive user experience.
Cross-Platform Compatibility: Designed to work seamlessly on Linux, macOS, and Windows terminals.
Ease of Use: With an intuitive API and Pythonic design, developers can quickly build complex applications.
Open Source: Being open source ensures that the framework is free to use, adapt, and improve upon.
Core Concepts of Textual
Understanding the fundamental components of Textual is crucial to building applications efficiently.
1. Widgets
Widgets are the building blocks of a Textual application. They represent interactive components like buttons, labels, and text inputs.
2. Layouts
Textual provides layout management to organize widgets effectively. You can use grids, vertical, or horizontal layouts to structure your interface.
3. Events
Events drive the behavior of a Textual application. User interactions like clicks, key presses, and mouse movements trigger events that widgets can respond to.
4. Styling with CSS
Textual integrates a CSS-like syntax to style widgets, enabling developers to define colors, padding, margins, and more.
5. Async Functionality
Textual applications heavily rely on asynchronous programming. This allows them to handle real-time updates, animations, and user interactions seamlessly.
Setting Up Textual
To start using Textual, follow these steps:
1. Install the Framework
Install Textual via pip:
pip install textual
Ensure that Python 3.7 or higher is installed on your system.
2. Create a Basic Application
A minimal Textual application looks like this:
from textual.app import App
from textual.widgets import Static
class MyApp(App):
def compose(self):
yield Static("Hello, Textual World!")
if __name__ == "__main__":
MyApp().run()
Save this code in a file (e.g., my_app.py) and run it using:
python my_app.py
3. Explore Widgets
Textual provides various widgets such as buttons, text inputs, checkboxes, and sliders. Here’s an example showcasing multiple widgets:
from textual.app import App
from textual.widgets import Button, Checkbox, Static
class WidgetDemoApp(App):
def compose(self):
yield Static("Interactive Widgets", classes="header")
yield Button("Click Me")
yield Checkbox("Agree to terms and conditions")
if __name__ == "__main__":
WidgetDemoApp().run()
Advanced Features of Textual
1. Event Handling
Event handling is essential for interactive applications. You can override methods like on_event or use decorators to bind specific events to functions:
from textual.app import App
from textual.widgets import Button
class EventApp(App):
def compose(self):
yield Button("Click Me", id="btn")
async def on_button_pressed(self, event):
if event.button.id == "btn":
self.log("Button was clicked!")
if __name__ == "__main__":
EventApp().run()
2. CSS Styling
To style your application, create a styles.css file:
header {
color: green;
font-weight: bold;
padding: 1;
}
button {
background: blue;
color: white;
border: 1 solid white;
padding: 1;
}
Link this CSS file in your application:
class StyledApp(App):
CSS_PATH = "styles.css"
def compose(self):
yield Static("Styled Header", classes="header")
yield Button("Styled Button")
if __name__ == "__main__":
StyledApp().run()
3. Layout Management
Textual’s layout system helps organize widgets effectively. For example, a grid layout can be created as follows:
from textual.app import App
from textual.containers import Grid
from textual.widgets import Button
class GridApp(App):
def compose(self):
with Grid():
yield Button("Button 1")
yield Button("Button 2")
yield Button("Button 3")
if __name__ == "__main__":
GridApp().run()
4. Async Operations
Textual’s support for asynchronous programming allows you to fetch data or perform background tasks without blocking the UI:
import asyncio
from textual.app import App
from textual.widgets import Static
class AsyncApp(App):
def compose(self):
yield Static("Fetching data...")
async def on_mount(self):
await asyncio.sleep(2) # Simulate a delay
self.query_one(Static).update("Data fetched successfully!")
if __name__ == "__main__":
AsyncApp().run()
Building a Complete Application
Here’s an example of a simple to-do list application built with Textual:
from textual.app import App
from textual.widgets import Input, Button, Static
class TodoApp(App):
def compose(self):
yield Input(placeholder="Add a new task", id="task_input")
yield Button("Add Task", id="add_button")
yield Static("Tasks:", id="task_list")
async def on_button_pressed(self, event):
if event.button.id == "add_button":
task_input = self.query_one("#task_input")
task_list = self.query_one("#task_list")
new_task = task_input.value.strip()
if new_task:
task_list.update(task_list.renderable + f"\n- {new_task}")
task_input.value = ""
if __name__ == "__main__":
TodoApp().run()
Conclusion
The Textual Python framework offers a modern way to build terminal-based applications that are both functional and visually appealing. Its rich set of features, intuitive API, and seamless integration with Rich make it an ideal choice for developers looking to create next-generation CLIs. Whether you’re crafting a simple dashboard or a complex interactive tool, Textual’s capabilities will empower you to bring your ideas to life.