Can Textual honor Rich Styling?

Hello!

I have a little app that I had ported over to using Rich for all its styling, and generates its output as two Rich Renderables, a Panel and a Table. I am using Rich styling to add reactive color and style to elements within the Panel and Table as the data changes.

When I ported this over to Textual, I get my Renderables updating correctly, but all the styling is not rendering and is showing up as the configuration text like “[red1]”. I have gone over the docs several times and can’t find a way to make render correctly. Is there a way to have Textual honor the Rich styling?

Thank you!

A small self-contained example of what you mean might be helpful. I don’t have that much experience with rich styling but it does generally seem to work properly in widgets for me.

Interesting. I would love to see an example.

I can provide the full working Rich code as well, but the buildHeader class is exactly the same code so to save space I will just post the Textual code:

import random
from textual.app import App, ComposeResult
from textual.reactive import reactive
from textual.widgets import Footer, Static
from rich.panel import Panel
from rich.table import Table

class buildHeader:
    """Display header with clock."""

    # Create a myHeader panel
    def myHeader(column1) -> Panel:
        # Create grid
        grid = Table.grid(expand=True)
        grid.add_column(justify="center", ratio=1)
        grid.add_row(
            # Create content for row with Rich styling
            f"[italic]Number:[/italic] [red1]{column1}[/red1]" if column1 <= 100 else (f"[italic]Number:[/italic] [green3]{column1}[/green3]" if column1 >= 200 else f"[italic]Number:[/italic] [gold1]{column1}[/gold1]"),
        )

        # Return panel with grid
        return Panel(grid)

class ExampleHeader(Static):
    """Set the Header with Time"""

    def get_number(self):
        """Get a random number between 1-300"""
        return random.randrange(300)

    def get_header_data(self):
        """Get the header data"""
        reactive(buildHeader.myHeader(self.get_number()))

    def update_header_data(self):
        """Update the header data"""
        self.update(buildHeader.myHeader(self.get_number()))

    def on_mount(self) -> None:
        """Startup Action"""
        # Set the interval for updates
        self.update_header = self.set_interval(1 / 2 , self.update_header_data)

        # Get the data the first time
        self.get_header_data()

class ExampleApp(App):
    """Example App for using Rich styling in Textual"""

    BINDINGS = [
            ("q", "quit", "Quit"),
        ]

    def compose(self) -> ComposeResult:
        # Call Header Widget
        yield ExampleHeader()

        # Show Footer
        yield Footer()

def main():
    app = ExampleApp()
    app.run()

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        print(f'Exited')

Styling working in Rich on left and Not working in Textual on right:

Thanks for replying!

OK I see what you mean. I thought you were passing marup text directly to a Static, which definitely does work. I’m not really sure why the rich styling doesn’t carry through. You can make it work like this though

class buildHeader:
    """Display header with clock."""

    # Create a myHeader panel
    @staticmethod
    def myHeader(column1) -> Panel:
        # Create grid
        grid = Table.grid(expand=True)
        grid.add_column(justify="center", ratio=1)
        grid.add_row(
            # Create content for row with Rich styling
            Text.from_markup(f"[italic]Number:[/italic] [red1]{column1}[/red1]" if column1 <= 100 else (f"[italic]Number:[/italic] [green3]{column1}[/green3]" if column1 >= 200 else f"[italic]Number:[/italic] [gold1]{column1}[/gold1]")),
        )

        # Return panel with grid
        return Panel(grid)

I basically just wrapped the panel contents with Text.from_markup

2 Likes

Thank you, this was exactly what I needed.