Python Textual Widget not refreshing

135 views Asked by At

I am new to the Textual library and I was trying to make widgets to update whenever data changes. This is a simple example of what I've tried:

class TestBody(Static):
    data = reactive("")

    def compose(self) -> ComposeResult:
        yield Input()
        yield Label(f"Data: {self.data}")

    def on_input_submitted(self, message):
        self.data = message.value
        print("New data:", self.data)

class MyApp(App):
    def compose(self) -> ComposeResult:
        yield Header()
        yield TestBody()
        yield Footer()

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

In this example, I can type anything in the input component, and when submitted, it should be displayed by the label. But what actually happens is nothing; the label always displays "Data: ". 

By using the textual console tool, I can see that the submit message is definitely being sent and picked up by my handler. I can even see the data changing in the log, but the display does not update.

![Log example

I understand from the docs that I shouldn't need to use the refresh() method since I am using a reactive attribute. In any case, I tried to add self.refresh() inside on_input_submitted and it still did not work.

I have the feeling that I am missing something about this library, but I couldn't find any similar examples.

Thanks in advance.

1

There are 1 answers

0
CimimUxMaio On BEST ANSWER

As someone explained to me in the Textualize discord server:

A reactive will cause a refresh of a render of a widget it's defined as part of; but in your code you're trying to get the Label to update. In the code posted over on that site you'd need to be using a watch method that then calls the update of the Label.

So, this would be the updated code:

class TestBody(Static):
    data = reactive("")

    def compose(self) -> ComposeResult:
        yield Input()
        yield Label()

    def watch_data(self) -> None:
        self.query_one(Label).update(f"Data: {self.data}")

    def on_input_submitted(self, message):
        self.data = message.value

class MyApp(App):
    def compose(self) -> ComposeResult:
        yield Header()
        yield TestBody()
        yield Footer()

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