-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs: use_render_queue, use_liveness_scope, use_table_listener docs
- Loading branch information
Showing
8 changed files
with
312 additions
and
7 deletions.
There are no files selected for viewing
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
# use_liveness_scope | ||
|
||
`use_liveness_scope` allows your to interact with the [liveness scope](https://deephaven.io/core/docs/conceptual/liveness-scope-concept/) for a component. Some functions which interact with a component will create live objects that need to be managed by the component to ensure they are kept active. | ||
|
||
The primary use case for this is when creating tables outside the component's own function, and passing them as state for the component's next update. If the table is not kept alive by the component, it will be garbage collected and the component will not be able to update with the new data. | ||
|
||
## Example | ||
|
||
```python | ||
from deephaven import ui, time_table | ||
|
||
|
||
@ui.component | ||
def ui_resetable_table(): | ||
table, set_table = ui.use_state(lambda: time_table("PT1s")) | ||
handle_press = ui.use_liveness_scope(lambda _: set_table(time_table("PT1s")), []) | ||
return [ | ||
ui.action_button( | ||
"Reset", | ||
on_press=handle_press, | ||
), | ||
table, | ||
] | ||
|
||
|
||
resetable_table = ui_resetable_table() | ||
``` | ||
|
||
## UI recommendations | ||
|
||
1. **Avoid using `use_liveness_scope` unless necessary**: This is an advanced feature that should only be used when you need to manage the liveness of objects outside of the component's own function. Prefer instead to derive a live component based on state rather than setting a live component within state. | ||
2. **Use `use_liveness_scope` to manage live objects**: If you need to manage the liveness of objects created outside of the component's own function, use `use_liveness_scope` to ensure they are kept alive. For more information on liveness scopes and why they are needed, see the [liveness scope documentation](https://deephaven.io/core/docs/conceptual/liveness-scope-concept/). | ||
|
||
## Refactoring to avoid liveness scope | ||
|
||
In the above example, we could refactor the component to avoid using `use_liveness_scope` by deriving the table from state instead of setting it directly: | ||
|
||
```python | ||
from deephaven import ui, time_table | ||
|
||
|
||
@ui.component | ||
def ui_resetable_table(): | ||
iteration, set_iteration = ui.use_state(0) | ||
table = ui.use_memo(lambda: time_table("PT1s"), [iteration]) | ||
return [ | ||
ui.action_button( | ||
"Reset", | ||
on_press=lambda: set_iteration(iteration + 1), | ||
), | ||
table, | ||
] | ||
|
||
|
||
resetable_table = ui_resetable_table() | ||
``` | ||
|
||
## API Reference | ||
|
||
```{eval-rst} | ||
.. dhautofunction:: deephaven.ui.use_liveness_scope | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
# use_render_queue | ||
|
||
`use_render_queue` lets you use the render queue in your component. This is useful when you want to queue updates on the render thread from a background thread. | ||
|
||
## Example | ||
|
||
```python | ||
from deephaven import time_table | ||
from deephaven import ui | ||
|
||
_source = time_table("PT5S").update("X = i").tail(5) | ||
|
||
|
||
@ui.component | ||
def toast_table(t): | ||
render_queue = ui.use_render_queue() | ||
|
||
def listener_function(update, is_replay): | ||
data_added = update.added()["X"][0] | ||
render_queue(lambda: ui.toast(f"added {data_added}", timeout=5000)) | ||
|
||
ui.use_table_listener(t, listener_function, []) | ||
return t | ||
|
||
|
||
my_toast_table = toast_table(_source) | ||
``` | ||
|
||
The above example listens to table updates and displays a toast message when the table updates. The `toast` function must be triggered on the render thread, whereas the listener is not fired on the render thread. Therefore, you must use the render queue to trigger the toast. | ||
|
||
## UI recommendations | ||
|
||
1. **Use the render queue to trigger toasts**: When you need to trigger a toast from a background thread, use the render queue to ensure the toast is triggered on the render thread. Otherwise, an exception will be raised. | ||
2. **Use the render queue to batch UI updates from a background thread**: By default, setter functions from the `use_state` hook are already fired on the render thread. However, if you have multiple updates to make to the UI from a background thread, you can use the render queue to batch them together. | ||
|
||
## Batching updates | ||
|
||
Setter functions from the `use_state` hook are fired on the render thread, so if you call a series of updates from a callback on the render thread, they will be batched together. Consider the following, which will increment states `a` and `b` in the callback from pressing on "Update values": | ||
|
||
```python | ||
from deephaven import ui | ||
import time | ||
|
||
|
||
@ui.component | ||
def ui_batch_example(): | ||
a, set_a = ui.use_state(0) | ||
b, set_b = ui.use_state(0) | ||
|
||
ui.toast( | ||
f"Values are {a} and {b}", | ||
variant="negative" if a != b else "neutral", | ||
timeout=5000, | ||
) | ||
|
||
def do_work(): | ||
set_a(lambda new_a: new_a + 1) | ||
# Introduce a bit of delay between updates | ||
time.sleep(0.1) | ||
set_b(lambda new_b: new_b + 1) | ||
|
||
return ui.button("Update values", on_press=do_work) | ||
|
||
|
||
batch_example = ui_batch_example() | ||
``` | ||
|
||
Because `do_work` is called from the render thread (in response to the `on_press` ), `set_a` and `set_b` will queue their updates on the render thread and they will be batched together. This means that the toast will only show once, with the updated values of `a` and `b` and they will always be the same value when the component re-renders. | ||
|
||
If we instead put `do_work` in a background thread, the updates are not guaranteed to be batched together: | ||
|
||
```python | ||
from deephaven import ui | ||
import threading | ||
import time | ||
|
||
|
||
@ui.component | ||
def ui_batch_example(): | ||
a, set_a = ui.use_state(0) | ||
b, set_b = ui.use_state(0) | ||
|
||
ui.toast( | ||
f"Values are {a} and {b}", | ||
variant="negative" if a != b else "neutral", | ||
timeout=5000, | ||
) | ||
|
||
def do_work(): | ||
set_a(lambda new_a: new_a + 1) | ||
# Introduce a bit of delay between updates | ||
time.sleep(0.1) | ||
set_b(lambda new_b: new_b + 1) | ||
|
||
def start_background_thread(): | ||
threading.Thread(target=do_work).start() | ||
|
||
return ui.button("Update values", on_press=start_background_thread) | ||
|
||
|
||
batch_example = ui_batch_example() | ||
``` | ||
|
||
When running the above example, we'll see _two_ toasts with each press of the button: a red one where `a != b` (as `a` gets updated first), then a neutral one where `a == b` (as `b` gets updated second). We can use the `use_render_queue` hook to ensure the updates are always batched together when working with a background thread: | ||
|
||
```python | ||
from deephaven import ui | ||
import threading | ||
import time | ||
|
||
|
||
@ui.component | ||
def ui_batch_example(): | ||
render_queue = ui.use_render_queue() | ||
a, set_a = ui.use_state(0) | ||
b, set_b = ui.use_state(0) | ||
|
||
ui.toast( | ||
f"Values are {a} and {b}", | ||
variant="negative" if a != b else "neutral", | ||
timeout=5000, | ||
) | ||
|
||
def do_work(): | ||
def update_state(): | ||
set_a(lambda new_a: new_a + 1) | ||
# Introduce a bit of delay between updates | ||
time.sleep(0.1) | ||
set_b(lambda new_b: new_b + 1) | ||
|
||
render_queue(update_state) | ||
|
||
def start_background_thread(): | ||
threading.Thread(target=do_work).start() | ||
|
||
return ui.button("Update values", on_press=start_background_thread) | ||
|
||
|
||
batch_example = ui_batch_example() | ||
``` | ||
|
||
Now when we run this example and press the button, we'll see only one toast with the updated values of `a` and `b`, and they will always be the same value when the component re-renders (since the updates are batched together on the render thread). | ||
|
||
## API Reference | ||
|
||
```{eval-rst} | ||
.. dhautofunction:: deephaven.ui.use_render_queue | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
# use_table_listener | ||
|
||
`use_table_listener` lets you listen to a table for updates. This is useful when you want to listen to a table and perform an action when the table updates. | ||
|
||
## Example | ||
|
||
```python | ||
from deephaven import time_table, ui | ||
from deephaven.table import Table | ||
|
||
_source = time_table("PT1s").update("X = i") | ||
|
||
|
||
@ui.component | ||
def ui_table_monitor(t: Table): | ||
def listener_function(update, is_replay): | ||
print(f"Table updated: {update}, is_replay: {is_replay}") | ||
|
||
ui.use_table_listener(t, listener_function, []) | ||
return t | ||
|
||
|
||
table_monitor = ui_table_monitor(_source) | ||
``` | ||
|
||
## UI recommendations | ||
|
||
1. **Use table data hooks instead when possible**: `use_table_listener` is an advanced feature, requiring understanding of how the [table listeners](https://deephaven.io/core/docs/how-to-guides/table-listeners-python/) work, and limitations of running code while the Update Graph is running. Most usages of this are more appropriate to implement with [the table data hooks](./overview.md#data-hooks). | ||
|
||
## Display the last updated row | ||
|
||
Here's an example that listens to table updates and will display the last update as a header above the table. This is a simple example to demonstrate how to use `use_table_listener` to listen to table updates and update state in your component. | ||
|
||
```python | ||
from deephaven import time_table, ui | ||
from deephaven.table import Table | ||
|
||
|
||
@ui.component | ||
def ui_show_last_changed(t: Table): | ||
last_change, set_last_change = ui.use_state("No changes yet.") | ||
|
||
def listener_function(update, is_replay): | ||
set_last_change(f"{update.added()['X'][0]} was added") | ||
|
||
ui.use_table_listener(t, listener_function, []) | ||
return [ui.heading(f"Last change: {last_change}"), t] | ||
|
||
|
||
_source = time_table("PT5s").update("X = i") | ||
show_last_changed = ui_show_last_changed(_source) | ||
``` | ||
|
||
## Display a toast | ||
|
||
Here's an example that listens to table updates and will display a toast message when the table updates. This is a simple example to demonstrate how to use `use_table_listener` to listen to table updates and display a toast message. Note you must use a [render queue](./use_render_queue.md) to trigger the toast, as the listener is not fired on the render thread. | ||
|
||
```python | ||
from deephaven import time_table | ||
from deephaven import ui | ||
|
||
_source = time_table("PT5S").update("X = i").tail(5) | ||
|
||
|
||
@ui.component | ||
def toast_table(t): | ||
render_queue = ui.use_render_queue() | ||
|
||
def listener_function(update, is_replay): | ||
data_added = update.added()["X"][0] | ||
render_queue(lambda: ui.toast(f"added {data_added}", timeout=5000)) | ||
|
||
ui.use_table_listener(t, listener_function, [t]) | ||
return t | ||
|
||
|
||
my_toast_table = toast_table(_source) | ||
``` | ||
|
||
## API Reference | ||
|
||
```{eval-rst} | ||
.. dhautofunction:: deephaven.ui.use_table_listener | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters