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.
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")