use_render_queue
use_render_queue
lets you use the render queue in your component. Whenever work is done in a component, it must be performed on the render thread. If you create a new thread to perform some work on the background and then want to update a component, you should queue that work on the render thread. Some actions (like toasts) will raise an error if they are not triggered from the render thread.
Example
This 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.
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)
UI recommendations
- 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.
- 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.
Batch updates
Setter functions from the use_state
hook are always 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”:
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:
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, two toasts appear with each button press: a red one where a != b
(as a
gets updated first), then a neutral one where a == b
(as b
gets updated second). Use the use_render_queue
hook to ensure the updates are always batched together when working with a background thread:
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
Returns a callback function for adding things to the render queue. Should only be used if you're loading data from a background thread and want to update state.
Returns: Callable[[Callable[[], None]], None]
A callback function that takes a function to call on the render thread.