Live updates with tabulator widget

Hi i’m trying to find a solution for streaming/updating data to tables but I can’t get it to work. Here are some things I have attempted.

#1:

    import holoviews as hv
    import panel as pn
    import pandas as pd

    renderer = hv.renderer('bokeh')
    pn.extension()

    class Dashboard():
        def __init__(self):
            self.stream_table = pn.widgets.DataFrame()

        def update_data(self):
            df =  pd.util.testing.makeDataFrame()
            self.stream_table.value = df
        
        def start(self):
            pn.state.add_periodic_callback(self.update_data, 50)
            pn.serve(self.stream_table)


    d = Dashboard()
    d.start()

#2: Based on: this topic.

    import holoviews as hv
    import panel as pn
    import pandas as pd
    from tornado.ioloop import IOLoop
    import param

    renderer = hv.renderer('bokeh')
    pn.extension()


    class Dashboard(param.Parameterized):

        def __init__(self, **params):
            super(Dashboard, self).__init__(**params)
            self.df = pd.util.testing.makeDataFrame()
            self.widget = pn.widgets.Tabulator(value=self.df)
            pn.state.add_periodic_callback(self.update_data, 50)

        def update_data(self):
            df =  pd.util.testing.makeDataFrame()
            self.widget.value = df

        def panel(self):
            return self.widget


    def create_app():
        app = Dashboard()
        return app.panel()


    loop = IOLoop().current()
    server = pn.serve(create_app(), show=False, loop=loop, start=False, port=5006)
    server.io_loop.start()

#3: With patch/stream:

    import holoviews as hv
    import numpy as np
    import panel as pn
    import pandas as pd
    from tornado.ioloop import IOLoop
    import param

    renderer = hv.renderer('bokeh')
    pn.extension()


    class Dashboard(param.Parameterized):

        def __init__(self, **params):
            super(Dashboard, self).__init__(**params)
            self.df = self.get_data()
            self.widget = pn.widgets.Tabulator(value=self.df)
            pn.state.add_periodic_callback(self.update_data, 50)


        def get_data(self):
            return pd.util.testing.makeDataFrame().reset_index()[['A', 'B', 'C', 'D']]

        def update_data(self):
            df = self.get_data()
            self.widget.patch(df)

        def panel(self):
            return self.widget


    def create_app():
        app = Dashboard()
        return app.panel()


    loop = IOLoop().current()
    server = pn.serve(create_app(), show=False, loop=loop, start=False, port=5006)
    server.io_loop.start()

In all cases, i get the following error:

    File "/usr/local/anaconda3/envs/replayer38/lib/python3.8/site-packages/bokeh/server/session.py", line 218, in _document_patched
        raise RuntimeError("_pending_writes should be non-None when we have a document lock, and we should have the lock when the document changes")
    RuntimeError: _pending_writes should be non-None when we have a document lock, and we should have the lock when the document change

Any help on this would be appreciated! Thanks in advance

Panel: 0.11.0a8
Holoviews: 1.14.1
Pandas: 1.2.0
Tornado: 6.1

The issue was that i was starting the callback before the server was loaded.

This works:

    import panel as pn
    import pandas as pd
    pn.extension()

    def generate_df():
        df.value = pd.util.testing.makeDataFrame()

    def start(event):
        periodic_cb.start()
        
    button = pn.widgets.Button(name='Start')
    button.on_click(start)
            
    df = pn.widgets.Tabulator(value=pd.util.testing.makeDataFrame())
    periodic_cb = pn.state.add_periodic_callback(generate_df, period=50, start=False)

    col = pn.Column(df, button)
    pn.serve(col)

Is there a way to start without needing a button ?

I also noted that if you replace the Tabulator by a dataframe, the app progressively slows down:

This works at first and then slows down/halts:

import panel as pn
import pandas as pd
pn.extension()

def generate_df():
    df.value = pd.util.testing.makeDataFrame()

def start(event):
    periodic_cb.start()
    
button = pn.widgets.Button(name='Start')
button.on_click(start)
        
df = pn.widgets.DataFrame(value=pd.util.testing.makeDataFrame())
periodic_cb = pn.state.add_periodic_callback(df, period=50, start=False)

col = pn.Column(df, button)
pn.serve(col)

I read something the other day that might be of interest, essentially document ready if I understand runs straight after initialisation so that may be able to kick you into action without a button.

What I don’t know is if panel has its own thing or if you can tie this in

1 Like

Hi @Dvisacker

You can use pn.state.onload.

https://panel.holoviz.org/gallery/simple/defer_data_load.html

3 Likes

Thanks @Marc and @carl for the answers, I got it work with pn.state.onload

1 Like