Panel - JSCallback function

Hi @otluk

The workaround or hacky code for the gauge has several details which need consideration. The simple solution to stream data from a pandas dataframe would be to watch other solutions, for example, the one shown here

Said that, we can look how to apply the same workaround for sending data for the line chart. We can consider panel is something like a full-stack framework, which connects the backend(python) with the frontend (the browser). How panel does that is something like magic, sending data back and forth (serializing to json the data you want to communicate). In the previous example, you only want to send the type of data a number, then we use an slider, which has a attribute slider.value, of type number. In this case, if you want to send data which consists of timestamps and values, another widget must be used which can support that type of data. The pn.widgets.literal_input can do that work (LiteralInput — Panel v1.3.5). It is noteworthy to mention the difference between the slider previously used and the literal_input, the former only accepts numbers, while the latter can accept numbers, lists or dicts.

You can find below the code. The conversion of the data in the javascript callback took me a while, and it is something panel has done in its code, so this is something like reinventing the wheel. Due to that, it is better to use the first example given in the link.

echart_timeseries


import time
import panel as pn, pandas as pd 
import random, numpy as np

from datetime import datetime

pn.config.sizing_mode = 'stretch_width'
random.seed()


def retrieve():
    n = 5
    # letters =  [chr(i) for i in np.random.randint(ord('a'), ord('z') + 1, n)]
    letters = pd.date_range(start="2018-09-09",end="2020-02-02", periods = 5).to_pydatetime().tolist() 
    values = np.random.randint(0, 100, n)  
    return (letters, values)


literal_input = pn.widgets.LiteralInput(name='Literal Input (dict)', 
        value={'key': [1, 2, 3]}, type=dict, visible=False)

# Stream function
def stream():
    letters, values = retrieve()
    literal_dict = {str(l):v for l, v in zip(letters, values)}
    print('in python', literal_dict)
    # how to access the js object?
    literal_input.value = literal_dict # this step triggers internally the js_callback attached to the slider 


# Set up callback with streaming data
cb = pn.state.add_periodic_callback(stream, 2000)

# 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(theme="dark", height=400)
gauge_pane.object = {
        "xAxis": {
            "type": 'time',
            "data": []
        },
        "yAxis": {
            "type": 'value'
        },
        "series": [{
            "data": [],
            "type": 'line',
            "showSymbol": False,
            "hoverAnimation": False,
        },
        ],
        "responsive": True
    }

row = pn.Column(gauge_pane,literal_input).servable()

literal_input.jscallback(args={'gauge': gauge_pane,}, value="""
    console.log(cb_obj.value)
    let literal_dict = JSON.parse( cb_obj.value.replaceAll("'",'\"') )
    // console.log(literal_dict)
    
    let keys = Object.keys(literal_dict);
    let values = Object.entries(literal_dict);

    console.log(typeof(literal_dict),literal_dict, 'dummy slider:', keys, values ,
            'gauge value',gauge.data.xAxis.data, 
           gauge.data.series[0].data );

    gauge.data.xAxis.data = keys;
    gauge.data.series[0].data = values;

    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")
1 Like