Param Dynamic with lambda not updated in Panel

Hi,

I found out about the param dynamic type and wanted to use it for displaying current settings of a camera. So I setup a lambda and found that the panel indicator didn’t update at all. So I went ahead and tried to make a minimal example where I even trigger new values manually in a coroutine:

from random import random
import panel as pn
import param
import asyncio 

live_data_task = None

class A(param.Parameterized):
    a = param.Number(lambda: random())
a = A()

number = pn.indicators.Number.from_param(a.param.a)

async def live_data():
    while True:
        await asyncio.sleep(0.5)
        print(a.a)


def on_session_created(context):
    print("Session created")
    global live_data_task
    if not live_data_task:
        live_data_task = asyncio.create_task(live_data())

def on_session_destroyed(context):
    print("Session destroyed")
    if live_data_task: live_data_task.cancel()


pn.state.on_session_created(on_session_created)
pn.state.on_session_destroyed(on_session_destroyed)
number.servable()

Hope someone can point out that I’m just doing it wrong! :slight_smile:

Best regards,
Fredrik

I’ll be honest, I don’t really understand your example. but your lambda: random() seems to be in the place where you’re just supposed to initialize a default value. I don’t know if this relates to your camera example, but it seems to solve the problem in your sample code:

from random import random
import panel as pn
import param
import asyncio 

live_data_task = None

class A(param.Parameterized):
    a = param.Number()
a = A()

number = pn.indicators.Number.from_param(a.param.a)

async def live_data():
    while True:
        await asyncio.sleep(0.5)
        a.a = random()
        print(a.a)


def on_session_created(context):
    print("Session created")
    global live_data_task
    if not live_data_task:
        live_data_task = asyncio.create_task(live_data())

def on_session_destroyed(context):
    print("Session destroyed")
    if live_data_task: live_data_task.cancel()


pn.state.on_session_created(on_session_created)
pn.state.on_session_destroyed(on_session_destroyed)
number.servable()

… I think I understand what you’re trying to do, but I think it’s a limitation with Param, not Panel. Panel is based around Param’s “push” functionality, but you’re trying to combine it with the “pull” model. I tried to add a simple param watcher on A.a (without any Panel stuff), and that doesn’t work either.

From the Param website

  1. Parameters as fancy Python attributes: making use of Param’s semantic type checking, inheritance of default values and docstrings, etc., but not using any dynamic or event-handling features of Param. When Parameter values need to change, users change them explicitly, using their own Python code in separate methods and functions invoked from outside of Param.
  2. “Push” model: Using Param’s Dependencies and Watchers so that Param invokes user-written code to change Parameter values based on events that Param detects (typically chaining from changes in some other parameter values). A “push” model is typical for event-driven GUI applications, where a user interacts with a GUI widget to change some Parameter value, prompting Param to execute chained dependencies in response.
  3. “Pull” model: Using Dynamic parameter values (described here) where the value of each dynamic parameter is computed when the parameter is read, potentially computing it from some global state values. A “pull” model is typical for simulations with a global clock, making it easy to use value a1 at time 1, value a2 at times 2-100, value a3 for 100-, etc.

This also works:


from random import random
import panel as pn
import param
import asyncio 

live_data_task = None

class A(param.Parameterized):
    a = param.Number()
    b = param.Event()

    @param.depends('b', watch = True)
    def update_a(self):
        self.a = random()

a = A()

number = pn.indicators.Number.from_param(a.param.a)

async def live_data():
    while True:
        await asyncio.sleep(0.5)
        a.b = True
        # print(a.a)


def on_session_created(context):
    print("Session created")
    global live_data_task
    if not live_data_task:
        live_data_task = asyncio.create_task(live_data())

def on_session_destroyed(context):
    print("Session destroyed")
    if live_data_task: live_data_task.cancel()


pn.state.on_session_created(on_session_created)
pn.state.on_session_destroyed(on_session_destroyed)

number.servable()

Yes you are right about me wanting to use the pull model.
Ok, so it’s not really compatible with panel then, that’s good to know.
Thank you for showing me the Event based solution!

1 Like