Skip to content
Advertisement

Panel – JSCallback function – Linking stream data to a JSObject in Echarts within a Panel dashboard

The goal is to stream data into panel and show the current temperature in a Gauge. Panel EChart Gauge

Therefore I am using the callback function for the panel to retrieve a new temperature value and like to update the Gauge as well with the JSCallback function.

Panel JSCallback Function

I also looked into the following questions on stackoverflow:

Here is the current code:

import time
import panel as pn
import random

random.seed()

def read_temp():
    return random.randint(15, 30)

global temperature
temperature = read_temp()

# Stream function
def stream():
    temperature = read_temp()
    print(temperature)
    #how to access the js object?
    pn.state.jscallback(args={'gauge': gauge_pane}, value="""
gauge.data.series[0].data[0].value = """+str(temperature))

gauge = {
    'tooltip': {
        'formatter': '{a} <br/>{b} : {c}°C'
    },
    'series': [
        {
            'name': 'Gauge',
            'type': 'gauge',
            'detail': {'formatter': '{value}°C'},
            'data': [{'value': [temperature], 'name': 'Temperature'}]
        }
    ]
};

#Panel
pn.extension('echarts',sizing_mode="stretch_width",template="fast")
ACCENT = "orange"
pn.state.template.param.update(site="Test", title="Introduction to data apps with Panel", 
                               sidebar_width=200, accent_base_color=ACCENT, 
                               header_background=ACCENT, font="Montserrat")
gauge_pane = pn.pane.ECharts(gauge,width=400, height=400).servable()

pn.pane.JPG("logo.jpg", sizing_mode="scale_width", embed=False).servable(area="sidebar")
pn.panel("# Settings").servable(area="sidebar")

# Set up callback with streaming data
pn.state.add_periodic_callback(stream, 500)

May be someone from the panel developers / community has some hints or some example. I mainly like to understand how I can access the JS Objects to be updated in the panel. So maybe JSLink is the right way to do it? If yes how would that work with my little example? – Thank you! 🙂

Advertisement

Answer

There is no easy way to call a jscallback from python, I think, but I am not sure. Bokeh callbacks with CustomJS: is there a way to trigger a callback with CustomJS from inside another callback with custom JS?

I think your problem is related there is no pn.state.jscallback (or maybe I am not aware of that).

I added a dummy slider, which is not visible, to use the callback attached to this slider. When you change the slider value, the callback is triggered internally by panel. This is good because the js_callback always has a cb_obj object in the javascript code, which you can use to pass the temperature generated with the random.randint function. I think you do not need to define the temperature global in that case either.

A gif of the solution working is here https://discourse.holoviz.org/uploads/default/original/2X/f/f65a8da7b3224b1a6a86b57e4ddd20dc0aa11bb1.gif

import time
import panel as pn
import random

random.seed()

def read_temp():
    return random.randint(15, 30)

global temperature
temperature = read_temp()

slider = pn.widgets.FloatSlider(visible=False)

# Stream function
def stream():
    temperature = read_temp()
    print('in python', temperature)
    #how to access the js object?
    slider.value = random.randint(0, 30) # this step triggers internally the js_callback attached to the slider 

gauge = {
    'tooltip': {
        'formatter': '{a} <br/>{b} : {c}°C'
    },
    'series': [
        {
            'name': 'Gauge',
            'type': 'gauge',
            'detail': {'formatter': '{value}°C'},
            'data': [{'value': [temperature], 'name': 'Temperature'}]
        }
    ]
};

# Set up callback with streaming data
pn.state.add_periodic_callback(stream, 500)

#Panel
pn.extension('echarts',sizing_mode="stretch_width",template="fast")
ACCENT = "orange"
pn.state.template.param.update(site="Test", title="Introduction to data apps with Panel", 
                               sidebar_width=200, accent_base_color=ACCENT, 
                               header_background=ACCENT, font="Montserrat")

gauge_pane = pn.pane.ECharts(gauge,width=400, height=400)

row = pn.Row(gauge_pane,slider).servable()

slider.jscallback(args={'gauge': gauge_pane}, value="""
    console.log( 'dummy slider:', cb_obj.value, 
            'gauge value',gauge.data.series[0].data[0].value);
    gauge.data.series[0].data[0].value = cb_obj.value;
    gauge.properties.data.change.emit()"""
    )



# pn.pane.JPG("logo.jpg", sizing_mode="scale_width", embed=False).servable(area="sidebar")
pn.panel("# Settings").servable(area="sidebar")
User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement