Open jrycw opened 3 months ago
Thx. I've fixed question 1 in #6682
Regarding question 2 I'm not aware that you can update two reactive expressions at once, i.e. update
result.rx.value = ""
is_running.rx.value = True
simultaniously. Is it possible @philippjfr?
You can see the UI flashing when the button is clicked 2nd and 3rd time
https://github.com/holoviz/panel/assets/42288570/f03d0038-ffb6-4ca4-a53e-76d76fb48d05
import random
from time import sleep
import panel as pn
pn.extension()
OPTIONS = ["Wind Turbine", "Solar Panel", "Battery Storage"]
# State
result = pn.rx("")
is_running = pn.rx(False)
# Classifier function
def classify(image):
sleep(2)
return random.choice(OPTIONS)
# Reactive expressions
def _show_submit_message(result, is_running):
return not result and not is_running
show_submit_message = pn.rx(_show_submit_message)(result, is_running)
def run_classification(_):
result.rx.value = ""
is_running.rx.value = True
prediction = classify(None)
result.rx.value = f"It's a {prediction}"
is_running.rx.value = False
# Components
click_submit = pn.pane.Markdown("Click Submit", visible=show_submit_message)
run = pn.widgets.Button(
name="Submit", button_type="primary", on_click=run_classification
)
progress_message = pn.Row(
pn.indicators.LoadingSpinner(
value=True, width=25, height=25, align="center", margin=(5, 0, 5, 10)
),
pn.panel("Running classifier ...", margin=0),
visible=is_running,
)
# Layout
pn.Column(run, click_submit, result, progress_message).servable()
@MarcSkovMadsen Thanks for the demonstration. I'm considering whether it would be possible to encapsulate these two status changes within a form to send them simultaneously.
With the help of .rx.when, I've rewritten the exercise as follows, and I've included a marker # ***
to indicate the changed lines.
import random
from time import sleep
import panel as pn
pn.extension()
OPTIONS = ["Wind Turbine", "Solar Panel", "Battery Storage"]
# State
result = pn.rx("")
is_running = pn.rx(False)
# Classifier function
def classify(image):
sleep(2)
return random.choice(OPTIONS)
# Reactive expressions
def _show_submit_message(result, is_running):
return not result and not is_running
show_submit_message = pn.rx(_show_submit_message)(result, is_running)
gate = pn.rx(False) # ***
gated_expr = show_submit_message.rx.when(gate) # ***
def run_classification(_):
result.rx.value = ""
is_running.rx.value = True
gate.rx.value = True # ***
gate.rx.value = False # ***
prediction = classify(None)
result.rx.value = f"It's a {prediction}"
is_running.rx.value = False
gate.rx.value = True # ***
gate.rx.value = False # ***
# Components
click_submit = pn.pane.Markdown("Click Submit", visible=gated_expr # ***)
run = pn.widgets.Button(
name="Submit", button_type="primary", on_click=run_classification
)
progress_message = pn.Row(
pn.indicators.LoadingSpinner(
value=True, width=25, height=25, align="center", margin=(5, 0, 5, 10)
),
pn.panel("Running classifier ...", margin=0),
visible=is_running,
)
# Layout
pn.Column(run, click_submit, result, progress_message).servable()
it appears that we can utilize gate.rx.value = True
to update the status changes simultaneously. However, immediately after each gate.rx.value = True
, it seems necessary to set gate.rx.value = False
to enable simultaneous updates in the next round.
Thanks. I hope @philippjfr will also chime in so that we get the best approach documented.
I would write it like this personally:
import random
import time
import panel as pn
pn.extension()
OPTIONS = ["Wind Turbine", "Solar Panel", "Battery Storage"]
# Classifier function
def classify(image):
time.sleep(2)
return random.choice(OPTIONS)
def format_prediction(_):
yield pn.Row(
pn.indicators.LoadingSpinner(
value=True, width=25, height=25, align="center", margin=(5, 0, 5, 10)
),
pn.panel("Running classifier ...", margin=0)
)
prediction = classify(None)
yield f"It's a {prediction}"
# Components
run = pn.widgets.Button(name="Submit", button_type="primary")
result = pn.bind(format_prediction, run).rx.when(run, initial='Click submit')
# Layout
pn.Row(
run,
pn.pane.ParamRef(result)
)
If we were actually classifying some image then it should be:
pn.bind(format_prediction, image_input).rx.when(run, initial='Click submit')
While exploring this example, I've made two interesting observations:
The variable
has_result
is defined but not used. It appears we could directly rely on the boolean value ofresult
instead of employingresult.rx.pipe(bool)
. Therefore,has_result
can be removed.The
_show_submit_message
function depends on the boolean values ofresult
andis_running
. If we change these twopn.rx
values sequentially (the first two lines ofrun_classification
), there is a very brief moment where the "Click Submit" message will be shown on the screen, right afterresult.rx.value = ""
but beforeis_running.rx.value = True
is executed. I'm wondering if there's a technique to update these twopn.rx
values simultaneously or to defer or throttle these changes just before the lineprediction = classify(None)
.