Search code examples
pythonpython-3.xstaterichtextual

Textual: UI is not updating after change the value


I am trying to build a simple TUI based app using Python's textual package. I have one left panel where I want to display list of items and on right panel I wan to show details of the selected item from left panel. So I want add items in the left panel using keybinding provided by textual lib but when I add new item to the list it does not updates the UI of the left panel to show newly added item in the list. I am following this [doc][1]

[1]: https://textual.textualize.io/guide/reactivity/#__tabbed_2_1 to add that feature but it is not working as expected. I am not able to figure out what's wrong here or my understanding of the things are incorrect.

Here is my full code:

import uuid
from time import monotonic

from textual.app import App, ComposeResult
from textual.containers import Container
from textual.reactive import reactive
from textual.widget import Widget
from textual.widgets import Button, Header, Footer, Static, ListView, ListItem, Label


class LeftPanel(Widget):

    items = reactive([])

    def compose(self) -> ComposeResult:

        yield Static(
            "All Request",
            expand=True,
            id="left_panel_header"
        )

        yield ListView(
            *self.items,
            initial_index=None,
        )


class RightPanel(Widget):
    """A stopwatch widget."""

    def compose(self) -> ComposeResult:
        yield ListView(
            ListItem(Label("4")),
            ListItem(Label("5")),
            ListItem(Label("6")),
            initial_index=None,
        )


class DebugApp(App):
    """A Textual app to manage stopwatches."""

    CSS_PATH = "main.css"
    BINDINGS = [
        ("d", "toggle_dark", "Toggle dark mode"),
        ("a", "add_item", "Add new item"),
    ]

    def compose(self) -> ComposeResult:
        """Called to add widgets to the app."""
        yield Container(LeftPanel(id="my_list"), id="left_panel")
        yield Container(RightPanel(), id="right_panel")
        yield Footer()

    def action_add_item(self):
        self.query_one("#my_list").items.append(ListItem(Label(str(uuid.uuid4()), classes="request_item")))
        self.dark = not self.dark  # This works

    def action_toggle_dark(self) -> None:
        """An action to toggle dark mode."""
        self.dark = not self.dark


def render_ui():
    app = DebugApp(watch_css=True)
    app.run()

Solution

  • It should work if you call the .append method of the ListView instance. To achieve this you can give it its own id:

            yield = ListView(
                *self.items,
                initial_index=None,
                id = "my_list_LV"
            )
    

    and then use

    # ...
        def action_add_item(self):
            self.query_one("#my_list_LV").append(ListItem(Label(str(uuid.uuid4()), classes="request_item")))
    
    

    (I tested this and it worked for me).