pythontuitextual

Python Textual Widget not refreshing


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.


Solution

  • 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()