Callbacks not updating browser window in panel

Hi
The task is to update a browser window, localhost, with revised electricity prices via a table and chart.

I verify that the callback calls the function that creates the table and chart but these are not re rendered in the browser window. I, and a friend, must have tried at least 10 different versions including class based but clearly I am missing the point. The table and chart dont have any reactive parameters but just draw on the most recent data.
The called chart is returned as an MPL pane and the table is a styled data frame extract
Very grateful if someone could suggest what I might do to fix it. The original approach is:

def update_data():
    global cht,my_table,counter,displaycount
    data = open_pickle_file(file_path)
    prices = data.pivot(columns='REGIONID',values='RRP')
    prices.index.name = None
    cht = pcht(prices)
    my_table = display_table(prices)
    counter +=1
    displaycount = pn.pane.Markdown("counter is "+str(counter) )
    print("updating "+str(counter))
print ("starting")

pn.state.add_periodic_callback(update_data, period=2000)
#prices.plot()
update_data()
pn.Column(displaycount,my_table,cht,max_width=450,max_height=700).servable()

I should add that putting cht and my_table in update_data is unecessary.
The code that I’d like to fix is:

counter = 0
def update_data():
    global prices
    global cht,my_table,counter,displaycount
    data = open_pickle_file(file_path)
    prices = data.pivot(columns='REGIONID',values='RRP')
    prices.index.name = None
    print("updating "+str(counter))
print ("starting")
update_data()
pn.state.add_periodic_callback(update_data, period=20000)
cht = pcht(prices)
my_table = display_table(prices)
counter +=1
displaycount = pn.pane.Markdown("counter is "+str(counter) )
pn.Column(displaycount,my_table,cht,max_width=450,max_height=700).servable()

I looked at .stream but I am already updating the data for the table and the chart by opening the pickle file.

sigh. Going for a walk

I’m wondering if I can use the approach in

Hi

After another few hours the styled data frame panenow updates correctly. However exactly the same syntax simply doesn’t work in Matplotlib chart even though I can see the make chart function is being called.

I really would be grateful if anyone could suggest why the chart doesn’t respond in the same way the data frame does. The revised code is:

counter = pn.rx(0)
#matplotlib.use('agg')
def get_data():
    global counter
    
    #print("fetching data "+str(counter.rx.value))
    data = open_pickle_file(file_path)
    return data.pivot(columns='REGIONID',values='RRP')

prices = pn.rx(get_data())

def update_prices():
    global chart_view
    counter.rx.value = counter + 1
    
    prices.rx.value = get_data()
    #print ("updated prices "+str(counter))
   
chart_view = pn.pane.Matplotlib(prices.rx.pipe(pcht),tight=True,format="svg", sizing_mode="scale_both")
prices_view = pn.pane.DataFrame(prices.rx.pipe(display_table))

pn.state.add_periodic_callback(update_prices, period=10000)

md2 = pn.indicators.Number(name =" this is the counter ", value = counter,default_color = 'white')
pn.Column(md2,prices_view,chart_view,max_width=450,max_height=700).servable()

pcht returns a matplotlib Fig.

Thanks to GPT4 and a lot of persistence i eventually worked out a very simple way to get it done. I post it here in case anyone as amateur as me ever wants to update a matplotlib chart with new data on a schedule. The only gripe I have is that matplotx(dracula) isn’t accepted as figure style although i use it everywhere else.

# Function to update the plot
def update_plot(event=None):
    prices = get_data()
    fig = pcht(prices)
    mpl_pane.object = fig
    table_pane.object = display_table(prices)

# Create an initial plot
prices = get_data()
fig = pcht(prices)
table = display_table(prices)
mpl_pane = pn.pane.Matplotlib(fig, sizing_mode='stretch_width')
table_pane = pn.pane.DataFrame(table)

# Set up periodic callback to update the plot every 5 minutes
pn.state.add_periodic_callback(update_plot, 270000)  # 27000ms = 4.5 minutes

# Layout for the Panel
layout = pn.Column(mpl_pane,table_pane,max_width=450,max_height=600)

# Serve the Panel
layout.servable()

great product panel but I’ve chewed up a lot of time and despite best efforts the documentation remains difficult for an amateur like me.

1 Like

Hi! Thanks for sharing the result. I’d like to understand this better. Have you had the chance to check out the tutorials, or were you just searching periodic callback? i.e. how did you approach the documentation? Any suggestions to improve the docs?

Also, this might be a helpful resource:

I read the documents as thoroughly as I could and conducted various experiments over the 3 days it took to get the few lines of code running properly.

I’ve been using a Panel application for a year now. The app at www.itkservices2.com displays various charts for Australian electricity fuels and prices in a series of tabs with some reactive widgets. Although I could do it better today I’m happy enough with it an use it regularly.

So this was the first time trying to use callbacks.

The difficulty is that the documentation presents various different call back strategies such as streamz or pn.rx or in the worst case(for me) a holoviews hv.map

The callback examples in the “how to” section all use .stream however the “periodically run callbacks” page uses “param.trigger”. Other examples use other methods.

The code that works for me uses

chart_stream = stream.map(prices)

and without gpt4 I would never have found that line of code

or the stream.emit method.

So I guess a page that deals with stream = Stream and how it links with call_backs would help an old guy like me

I am not complaining. The lyre bird in the bush is calling its tuneful song, the weather is good and my app is on the way to working. Getting it working was, so far, by far the hardest thing I’ve worked with in Panel

And for the sake of completeness or as an example the live streaming update (loads a touch slow) is at
https://itkservices3.com embedded via an iframe and linked using a cloudflare tunnel to the server. The “nemdash” menu links to the main panel app via another iframe.

I am happy with result and with the tools available to me.

Hi @oxbow ! Interesting case. Glad you got it working and thank you for sharing! You say this:

chart_stream = stream.map(prices)

But in what code do you use that line?

I could not find it in the mpl+df +add_periodic_callback code you posted above.

Could you post or link to the code that uses the stream.map code line ?

Does itkservices3 use the solution you gave above, so using add_periodic_callback ?

Also, would it be possible for you to share a working sample of a pickle file you used with the code, so people can run the code sample you provided?

ahh… I was in error showing that line, it was copied from a file in haste. I should edit it out. Thanks for pointing that out and my apologies.

In the end the solution was very simple, but I maintain it wasn’t obvious from the docs. Either that or I, my mate who writes his own compiler, and Claude Opus are very silly.

In answer to the question about the how docs could be improved so that users can learn the callback code I’d have found it easier if there had been a mention somewhere in the “how to” or other commentary of the .object attribute. I’d have got to the required code by myself if I’d realised that rather than assinging the result to a pane directly it had to be assigned to a pane.object.

Again. I expect to use Panel in further applications, it’s a great tool. Next up electricity price forecasting and sensitivities about moving generators in or out of the bid stack.

2 Likes

Thanks for your detailed feedback! Totally agree that docs could be improved in a number of ways.

That’s what makes this kind of case is so instructive.

If you can’t edit your post, could post the corection in a reply? I’m still not sure from your reply if you ended up using .stream or not :slight_smile:

Is the last code you posted the version that worked succesfully?

Hopefully it will become easier to find these solutions as AI’s evolve.

PS I looked up lyrebird :slight_smile: Cool bird!

the last code I posted worked successfully and is the model I will use next time I want to run periodic call backs.

I did not use .stream in the end. “All” that was needed was for the updated .objects to be rerendered on localhost.

For more context I have a separate process running that downloads the relevant data (in this case 5 minute electricity prices) from an online directory (a new file is added and beautiful soup identifies the last .zip file), parses the csv file, adds the prices to the “prices” data frame and saves it as .pkl.

That code runs fine with a traditional while true: sleep loop because there is no user interaction. Probably should be a cronjob.

The panel code is required to update webobjects and is a great resource. Back to my scheduled job.

1 Like

And yes I used GPT 4 and claude opus occasionally asking one to critique the other :slight_smile: In the end though its knowing the right question to ask. My wife once said you really want to have some idea of the answer before you ask a question but I would say speaking as someone that worked at a high level in investment banking research for many years that the skill is in knowing the right question to ask and also understanding why the answer is what it is.

1 Like

Agree! So AI ping pong got it over the line? Nice.

I have been looking for a (web) client that makes that easy across 2+ AI’s.

Those 2 perspectives on AI sound pretty congruent btw :slight_smile:

I am still wondering if .streams and pn.rx would work as well, and if there is a clear best of those 3. Would be nice to have all 3 variations of the same code as an example for others.

Same regarding assigning to a pane.object. Great that it works, but wondering if there is an even more optimal pattern that does not need object.

Will update if I find out more.

Simon Willison has LLM as a cli tool and I like reading what he says.

Thanks! Will check it out.

Meanwhile, there’s a whole discussion going on, regarding your example above, in the dev channel on the Panel Discord server!

Are you on Discord? Very much recommended for the discussions on Panel code and solutions. And the Panel developers participate.

The Discord Panel / Holoviz server is here: Discord

Let me know if you need an invite :slight_smile:

Please provide an invite. I will lurk as I doubt there is much I can add in the developer forum. I struggle enough with application code.

Sure! Btw it is a forum intended for Holoviz / Panel users! But devs participate, and there is a separate dev channel in the forum.

Let me know if the forum invite below works :slight_smile:

The invite worked.

1 Like