I’m experimenting with the capabilities of parametrized classes and rendered outputs.
In this case, my pane does not dynamically update when a parameter is changed, although the @depends decorator seems to be appropriately created.
The following code is meant to create 3 elements: a submit button that sends the value from prompt_input
to prompt_input_dict
and provides a corresponding UUID, a markdown pane displaying the prompt_input
value, and a markdown pane of the latest inserted prompt_input
into prompt_input_dict
.
class ExpEnv(param.Parameterized):
prompt_input = param.String()
prompt_input_dict = param.Dict(default=OrderedDict())
prompt_input_submit = param.Action(lambda self: self.param.trigger('prompt_input_submit'))
@param.depends('prompt_input_submit', watch=True)
def _input_dict_update(self):
self.prompt_input_dict.update({uuid4(): self.prompt_input})
@param.depends('prompt_input_dict')
def view_prompts(self):
md_list = [pn.pane.Markdown(f"K: V")]
for k,v in self.prompt_input_dict.items():
print('entered view_prompts loop')
md_list.append(pn.pane.Markdown(f"{k} :{v}"))
self.md_list = md_list
# return pn.layout.Column(*md_list)
return md_list[-1]
ee = ExpEnv()
When I programatically set ee.prompt_input = 'test1'
, this is the outcome for
pn.Row(ee.param.prompt_input_submit, pn.pane.Markdown(ee.param.prompt_input), ee.view_prompts)
:
![image](https://discourse.holoviz.org/uploads/default/original/2X/a/a37e52d3b192255eab03865a8c018eaf1419f856.png)
test1
successfully dynamically populates the markdown in the middle when ee.prompt_input
is set.
However, the third element is not updated after hitting the ‘Prompt input submit’ button, as the view_prompts
method is not called although prompt_input_dict
changes and now includes OrderedDict([(UUID('199cd339-4704-45a8-ab73-619587493e75'), 'test1')])
It is only when I manually rerun pn.Row(ee.param.prompt_input_submit, pn.pane.Markdown(ee.param.prompt_input), ee.view_prompts)
that the uuid and input are displayed(and ‘entered view_prompts loop’ is printed):
![image](https://discourse.holoviz.org/uploads/default/original/2X/8/896cb9029b4fba29d3eebf50dc5cc4d545e6bdf0.png)
I’m not sure what the issue here is or where my misunderstanding is coming from.
Thank you! Hope everyone is enjoying a wonderful New Year.
Figured it out!
I dug through the Param docs a bit more and found that the modification of mutable classes does not trigger the method call. https://param.holoviz.org/user_guide/Dependencies_and_Watchers.html#param-trigger
As a workaround, I’m attaching the trigger to the prompt_input_submit parameter.
prompt_input_submit = param.Action(lambda self: self.param.trigger('prompt_input_submit', 'prompt_input_dict'))
However, I’m not certain if this is the optimal way to proceed.
New code for the class:
class ExpEnv(param.Parameterized):
prompt_input = param.String()
prompt_input_dict = param.Dict(default=OrderedDict())
prompt_input_submit = param.Action(lambda self: self.param.trigger('prompt_input_submit', 'prompt_input_dict'))
@param.depends('prompt_input_submit', watch=True)
def _input_dict_update(self):
self.prompt_input_dict.update({uuid4(): self.prompt_input})
@param.depends('prompt_input_dict')
def view_prompts(self):
md_list = [pn.pane.Markdown(f"K: V")]
for k,v in self.prompt_input_dict.items():
print('entered view_prompts loop')
md_list.append(pn.pane.Markdown(f"{k} :{v}"))
self.md_list = md_list
return pn.layout.Column(*md_list)
# return md_list[-1]
Hi @dleybel, here’s an alternative implementation using param.Event
instead of param.Action
to declare the event/button.
Indeed Param has no way to know when a mutable container has been updated in place. Instead I’m manually triggering callbacks depending on prompt_input_dict
by calling self.param.trigger('prompt_input_dict')
right after updating it.
from uuid import uuid4
import param
import panel as pn
pn.extension()
class ExpEnv(param.Parameterized):
prompt_input = param.String()
prompt_input_dict = param.Dict(default=dict())
prompt_input_submit = param.Event()
@param.depends('prompt_input_submit', watch=True)
def _input_dict_update(self):
self.prompt_input_dict.update({uuid4(): self.prompt_input})
self.param.trigger('prompt_input_dict')
@param.depends('prompt_input_dict')
def view_prompts(self):
md_list = [pn.pane.Markdown(f"K: V")]
for k,v in self.prompt_input_dict.items():
print('entered view_prompts loop')
md_list.append(pn.pane.Markdown(f"{k} :{v}"))
self.md_list = md_list
return pn.layout.Column(*md_list)
# return md_list[-1]
ee = ExpEnv()
pn.Row(ee.param, ee.view_prompts)
![image](https://discourse.holoviz.org/uploads/default/original/2X/4/44b873b1bee35b8eb82bd70678e139783ebf31a0.png)
1 Like
Thanks, @maximlt, I’ve modified my code to use that pattern instead. That’s definitely a cleaner way of going about triggering the update and makes the code easier to read and follow.
It seems that Event is the proper parameter to use, given that all it does is trigger itself.
Although, I’m a little confused as to why param.Action doesn’t trigger itself when combined with some arbitrary function like prompt_input_submit = param.Action(lambda x: True)
.
1 Like