I noticed a common pattern in my panel code:
from time import sleep
import numpy as np
import panel as pn
from matplotlib.figure import Figure
pn.extension(sizing_mode="stretch_width")
button = pn.widgets.Button(name="Click me")
select = pn.widgets.Select(name="Select")
layout = pn.Column()
def expensive_computation(s):
sleep(1)
print("expensive!")
return pn.Column(pn.pane.Str(f"Selected: {s}"))
def expensive_2_step(s):
layout.objects = [expensive_computation(s)]
pn.bind(expensive_2_step, select, watch=True)
select.options = ["a", "b", "c"]
def display(event):
fig = Figure()
ax = fig.add_subplot(111)
ax.plot(np.random.rand(10))
return pn.Column(select, pn.bind(lambda _: layout, select), fig)
pn.serve(
pn.Column(
button,
pn.bind(display, button),
)
)
button,
pn.bind(display, button),
)
)
breakdown: clicking the button will refresh the whole layout
if you replace pn.bind(lambda _: layout, select), fig)
with pn.bind(expensive_computation, select), fig)
it will indeed perform the calculation on every redraw.
Is there a better pattern that still involves only panel components? Could panel do something in the future to implement this boilerplate for us in the background?
pn.cache is supposed to cache data. Simply adding pn.cache to
expensive_2_step
will break the example
Try this pattern – I’m using append
to build up the Column:
from time import sleep
import numpy as np
import panel as pn
pn.extension(sizing_mode="stretch_width")
button = pn.widgets.Button(name="Click me")
select = pn.widgets.Select(name="Select", options=['a', 'b', 'c'])
layout = pn.Column()
def expensive_computation(s):
sleep(1)
print("expensive!")
layout.append(pn.pane.Str(f"Selected: {s}"))
return layout
pn.serve(pn.Column(button, select, pn.bind(expensive_computation, s=select)))
I don’t exactly understand how this solves the issue. The original example replaces the objects of the layout instead of appending (which is wanted). Could you replicate the button’s functionality also in this example? The goal would be to be able to click the button and not re-trigger the expensive computation unnecesarily
to clarify:
what I want is to have the behavior of the original code posted in this thread, with the simplicity of writing:
from time import sleep
import numpy as np
import panel as pn
from matplotlib.figure import Figure
pn.extension(sizing_mode="stretch_width")
button = pn.widgets.Button(name="Click me")
select = pn.widgets.Select(name="Select", options=["a", "b", "c"])
layout = pn.Column()
def expensive_computation(s):
sleep(1)
print("expensive!")
return pn.Column(pn.pane.Str(f"Selected: {s}"))
def display(event):
fig = Figure()
ax = fig.add_subplot(111)
ax.plot(np.random.rand(10))
return pn.Column(select, pn.bind(expensive_computation, select), fig)
pn.serve(
pn.Column(
button,
pn.bind(display, button),
)
)