Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

ploty chart not updating on event change #1147

Answered by fsmosca
pharouhk asked this question in Question
Discussion options

Hi,

I have created a plotly chart on my web page with some filters that filter the data depending on what a user selects.
Basic calculations are filtering successfully however the plotly graph remains static. Below is a snippet of my code:

def create_fig(data):
 fig = px.pie(
 chart_data,
 values="Count",
 names="Status",
 hole=0.8,
 color="Status",
 color_discrete_map={"Success": "#D7BB0B", "Failed": "#F2F2F2"},
 height=70,
 )
 fig.update_layout(plot_bgcolor="#10253F", paper_bgcolor="#10253F", showlegend=False)
 fig.update_traces(textinfo="none", hoverinfo="none", hovertemplate=None)
 fig.update_layout(margin_l=0)
 fig.update_layout(margin_r=0)
 fig.update_layout(margin_b=0)
 fig.update_layout(margin_t=0)
 fig.write_html("fig.html", include_plotlyjs="cdn")
 fig_html = open("fig.html", "r", encoding="utf-8").read()
 return html.div(utils.html_to_vdom(fig_html))
@component
def dashboard():
 return interactions_layout()
@component
def interactions_layout():
 data_filter, set_data_filter = hooks.use_state("A")
 plotly_data, set_plotly_data = hooks.use_state(read_data(data_filter))
 def handle_event(event):
 set_data_filter(event["target"]["value"])
 set_plotly_data(read_data(data_filter))
 layout = html.div(
 html.select(
 {"value": data_filter, "on_change": handle_event},
 html.option({"value": "A"}, "A"),
 html.option({"value": "C"}, "C"),
 html.option({"value": "S"}, "S"),
 ),
 html.div({"style": {}}, create_fig(plotly_data)),
 )
 return layout
You must be logged in to vote

This is the full reproducible sample code. See some comments in create_fig().

It is better if the plotly html will work as the users can interact on the rendered plot. Currently only the image worked.

I test it on my windows 10 PC.

from io import StringIO
import base64
from reactpy import component, html, utils, hooks
from reactpy.backend.fastapi import configure, Options
from fastapi import FastAPI
import plotly
import plotly.express as px
import pandas as pd
PLOTLY_JS = {
 'src': 'https://cdn.plot.ly/plotly-latest.min.js',
 'charset': 'utf-8'
}
DATA = [
 {"Status": "Success", "Count": 200, "Quality": "A"},
 {"Status": "Failed", "Count": 50, "Quality": "S"},
 ...

Replies: 6 comments 5 replies

Comment options

Could you format your code properly?

You must be logged in to vote
2 replies
Comment options

Edited original post to add code formatting.

Comment options

Thanks @Archmonger for the edit.

Comment options

This is what I found. That sample code is incomplete, there is no read_data.

I test it with a different code. The issue is in create_fig() specifically the line return html.div(utils.html_to_vdom(fig_html)).

The parameter data changes, the output fig.html also changes. But the displayed plot in the browser does not change.

Could there be an issue in html.div(utils.html_to_vdom(fig_html))?

def create_fig(data):
 fig = px.pie(
 data,
 values="Count",
 names="Status",
 hole=0.8,
 color="Status",
 color_discrete_map={"Success": "#D7BB0B", "Failed": "#F2F2F2"},
 height=70,
 )
 ...
 fig.write_html("fig.html", include_plotlyjs="cdn")
 fig_html = open("fig.html", "r", encoding="utf-8").read()
 return html.div(utils.html_to_vdom(fig_html)) # <==========
You must be logged in to vote
1 reply
Comment options

Based on my findings, I truly believe the issue is with utils.html_to_vdom(). When I inspect on the browser, the correct javascript code is appended within a separate script tag but for some reason it doesn't display on the page. My assumption is the new code is behind the initial script tag and needs to be overwritten instead of being appended.

Comment options

I also tried rendering the image, by saving the fig to 'png'. It also does not work. But if I randomize the image filename, it will render. So this is my current workaround.

Sample read_data()

import pandas as pd
app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static")
def read_data(filter: str) -> list[dict]:
 data = [
 {"Status": "Success", "Count": 200, "Quality": "A"},
 {"Status": "Failed", "Count": 50, "Quality": "S"},
 {"Status": "Success", "Count": 50, "Quality": "C"},
 {"Status": "Failed", "Count": 17, "Quality": "A"},
 {"Status": "Success", "Count": 95, "Quality": "C"},
 {"Status": "Failed", "Count": 85, "Quality": "A"},
 {"Status": "Success", "Count": 4, "Quality": "S"}
 ]
 df = pd.DataFrame(data)
 df = df.loc[df['Quality'] == filter]
 return df.to_dict('records')
def create_fig(chart_data: list[dict]):
 ...
 imgfn = f'{str(uuid.uuid4())[:12]}_fig.png'
 fig.write_image(f"./static/{imgfn}")
 return html.img({'src': f'/static/{imgfn}', 'style': {'width': '50%'}})
You must be logged in to vote
0 replies
Comment options

It may not be necessary to save the image to disk. This also works.

import base64
img_bytes = fig.to_image(format="png")
encoded = base64.b64encode(img_bytes).decode("utf-8")
return html.img({'src': f'data:image/png;base64, {encoded}'})
You must be logged in to vote
2 replies
Comment options

Thanks @fsmosca I would try this but how I wish I got this suggestions much earlier. I was stuck on this for days and almost lost my mind so had to re-write my entire code using a different framework.

Comment options

There is still one thing that I cannot solve. There is delay on the image shown and the option I selected.

Here I selected "A" But the image shown is from my previous selection.

image

I have been reading state and delayed reaction part of the documentation.

This problem is important as we like visual, users alter the input, we alter the visual.

Comment options

Got it, we actually need a button to plot. Users will select "A" or "B" or "S". After the selection, we let users to press the button to plot data based on their filter.

image

The interaction component

@component
def interactions_layout():
 data_filter, set_data_filter = hooks.use_state("C")
 plotly_data, set_plotly_data = hooks.use_state(read_data(data_filter))
 def plot_data(event):
 res = read_data(data_filter)
 # print(f'current data after filter: {res}')
 set_plotly_data(res) 
 # html.div(create_fig(plotly_data)), 
 def handle_event(event):
 set_data_filter(event["target"]["value"]) 
 return html.div(
 Select(handle_event, data_filter), 
 
 html.div(
 html.button(
 {
 "style": {"margin-top": "10px"},
 "on_click": plot_data
 }, "Plot data"
 ), 
 html.div(create_fig(plotly_data)),
 )
 )

Separate component for select

@component
def Select(handle_event, data_filter):
 return html.div(
 html.select(
 {
 'style': {
 'width': '100px'
 },
 "value": data_filter,
 "on_change": handle_event
 },
 html.option({"value": "A"}, "A"),
 html.option({"value": "C"}, "C"),
 html.option({"value": "S"}, "S")
 )
 )
You must be logged in to vote
0 replies
Comment options

This is the full reproducible sample code. See some comments in create_fig().

It is better if the plotly html will work as the users can interact on the rendered plot. Currently only the image worked.

I test it on my windows 10 PC.

from io import StringIO
import base64
from reactpy import component, html, utils, hooks
from reactpy.backend.fastapi import configure, Options
from fastapi import FastAPI
import plotly
import plotly.express as px
import pandas as pd
PLOTLY_JS = {
 'src': 'https://cdn.plot.ly/plotly-latest.min.js',
 'charset': 'utf-8'
}
DATA = [
 {"Status": "Success", "Count": 200, "Quality": "A"},
 {"Status": "Failed", "Count": 50, "Quality": "S"},
 {"Status": "Success", "Count": 50, "Quality": "C"},
 {"Status": "Failed", "Count": 17, "Quality": "A"},
 {"Status": "Success", "Count": 95, "Quality": "C"},
 {"Status": "Failed", "Count": 85, "Quality": "A"},
 {"Status": "Success", "Count": 4, "Quality": "S"}
]
def read_data(filter: str) -> list[dict]: 
 df = pd.DataFrame(DATA)
 df = df.loc[df['Quality'] == filter]
 return df.to_dict('records')
def get_fig(chart_data: list[dict]) -> plotly.graph_objs._figure.Figure:
 df = pd.DataFrame(chart_data)
 fig = px.pie(
 df,
 values="Count",
 names="Status",
 hole=0.8,
 color="Status",
 color_discrete_map={"Success": "#D7BB0B", "Failed": "#F2F2F2"},
 height=300,
 width=400,
 title=f"Plot for filter {chart_data[0]['Quality']}"
 )
 return fig
 
def create_fig(chart_data: list[dict]):
 fig = get_fig(chart_data)
 # (1) fig->html_buffer->vdom does not work when data is updated.
 # buffer = StringIO()
 # fig.write_html(buffer, include_plotlyjs='cdn')
 # fig_html = buffer.getvalue()
 # return html.div(utils.html_to_vdom(fig_html))
 # (2) fig->html_file->vdom does not work when data is updated.
 # htmlfn = 'fig.html'
 # fig.write_html(htmlfn, include_plotlyjs="cdn")
 # fig_html = open(htmlfn, "r", encoding='utf-8').read()
 # return html.div(utils.html_to_vdom(fig_html))
 # (3) fig->bytes->img does work even if data is updated.
 # Disadvantage: Users cannot interact with the image.
 img_bytes = fig.to_image(format="png")
 encoded = base64.b64encode(img_bytes).decode("utf-8")
 return html.img({'src': f'data:image/png;base64, {encoded}'})
@component
def Select(handle_event, data_filter):
 return html.div(
 html.select(
 {
 'style': {
 'width': '100px'
 },
 "value": data_filter,
 "on_change": handle_event
 },
 html.option({"value": "A"}, "A"),
 html.option({"value": "C"}, "C"),
 html.option({"value": "S"}, "S")
 )
 )
@component
def PlotButton(plot_data):
 return html.div(
 html.button(
 {
 "style": {"margin-top": "10px", "width": "100px"},
 "on_click": plot_data
 },
 "Plot data"
 )
 )
@component
def interactions_layout():
 data_filter, set_data_filter = hooks.use_state("C")
 plotly_data, set_plotly_data = hooks.use_state(read_data(data_filter))
 def plot_data(event):
 set_plotly_data(read_data(data_filter)) 
 def handle_event(event):
 set_data_filter(event["target"]["value"]) 
 return html.div(
 Select(handle_event, data_filter),
 PlotButton(plot_data),
 html.div(create_fig(plotly_data))
 )
@component
def Dashboard():
 return interactions_layout()
app = FastAPI()
configure(
 app,
 Dashboard,
 Options(
 head=html.head(
 html.script(PLOTLY_JS)
 )
 )
)
You must be logged in to vote
0 replies
Answer selected by Archmonger
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet

AltStyle によって変換されたページ (->オリジナル) /