Skip to content

Commit

Permalink
Document DOMNode.watch (#3724)
Browse files Browse the repository at this point in the history
* Document DOMNode.watch

* Create standalone example.

Addresses review comment: #3724 (review).

* Create standalone example.

Addresses review comment: #3724 (review).
  • Loading branch information
rodrigogiraoserrao authored Nov 27, 2023
1 parent 370f5f7 commit 9a9d534
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 0 deletions.
35 changes: 35 additions & 0 deletions docs/examples/guide/reactivity/dynamic_watch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from textual.app import App, ComposeResult
from textual.reactive import reactive
from textual.widget import Widget
from textual.widgets import Button, Label, ProgressBar


class Counter(Widget):
DEFAULT_CSS = "Counter { height: auto; }"
counter = reactive(0) # (1)!

def compose(self) -> ComposeResult:
yield Label()
yield Button("+10")

def on_button_pressed(self) -> None:
self.counter += 10

def watch_counter(self, counter_value: int):
self.query_one(Label).update(str(counter_value))


class WatchApp(App[None]):
def compose(self) -> ComposeResult:
yield Counter()
yield ProgressBar(total=100, show_eta=False)

def on_mount(self):
def update_progress(counter_value: int): # (2)!
self.query_one(ProgressBar).update(progress=counter_value)

self.watch(self.query_one(Counter), "counter", update_progress) # (3)!


if __name__ == "__main__":
WatchApp().run()
25 changes: 25 additions & 0 deletions docs/guide/reactivity.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,31 @@ Textual only calls watch methods if the value of a reactive attribute _changes_.
If the newly assigned value is the same as the previous value, the watch method is not called.
You can override this behaviour by passing `always_update=True` to `reactive`.


### Dynamically watching reactive attributes

You can programmatically add watchers to reactive attributes with the method [`watch`][textual.dom.DOMNode.watch].
This is useful when you want to react to changes to reactive attributes for which you can't edit the watch methods.

The example below shows a widget `Counter` that defines a reactive attribute `counter`.
The app that uses `Counter` uses the method `watch` to keep its progress bar synced with the reactive attribute:

=== "dynamic_watch.py"

```python hl_lines="9 28-29 31"
--8<-- "docs/examples/guide/reactivity/dynamic_watch.py"
```

1. `counter` is a reactive attribute defined inside `Counter`.
2. `update_progress` is a custom callback that will update the progress bar when `counter` changes.
3. We use the method `watch` to set `update_progress` as an additional watcher for the reactive attribute `counter`.

=== "Output"

```{.textual path="docs/examples/guide/reactivity/dynamic_watch.py" press="enter,enter,enter"}
```


## Compute methods

Compute methods are the final superpower offered by the `reactive` descriptor. Textual runs compute methods to calculate the value of a reactive attribute. Compute methods begin with `compute_` followed by the name of the reactive value.
Expand Down

0 comments on commit 9a9d534

Please sign in to comment.