Reputation: 1224
How do I make interactive buttons in plotly, that will change the chart and send their interaction to streamlit?
I have tried using the built in buttons in plotly https://plotly.com/python/custom-buttons/#relayout-button
This works for changing the chart, but the behavior cannot be sent out as an interaction of the click events here https://plotly.com/python/click-events/
There is currently only one library in streamlit to get the interactions of plotly charts and as far as I have seen, under the hood it uses the plotly events https://github.com/null-jones/streamlit-plotly-events
So the only solution that I can come up with is to create a second chart as a subplot and style it to look like a button. This is a major hack and lots of red flags are popping up, but I cannot figure out another way to do it.
This is what I have so far:
from plotly.subplots import make_subplots
import plotly.graph_objects as go
fig = make_subplots(rows=1, cols=2, column_widths=[0.1, 0.7])
fig.add_trace(
go.Bar(x=[1, 1], y=['foo', 'bar'], orientation='h', width=.4, hoverinfo='skip'),
row=1, col=1)
fig.add_trace(
go.Bar(x=[1, 1], y=['foo', 'bar'], orientation='h', width=.4, hoverinfo='skip'),
row=1, col=1)
fig.add_trace(
go.Bar(
x=[1, 2],
y=['foo', 'bar'],
orientation='h',
name='revenue',
width=.4
),
row=1, col=2)
fig.add_trace(
go.Bar(
x=[2, 4],
y=['foo', 'bar'],
orientation='h',
name='potential',
width=.4
),
row=1, col=2)
fig.update_yaxes(matches='y')
fig.update_layout(barmode='stack',
title_text="Multiple Subplots with Shared Y-Axes")
fig.show()
Am I missing something?
Upvotes: 1
Views: 2633
Reputation: 1
I was facing a similar problem: I needed to display a specific video according to the click action on a scatter plot. My first attempt was similar to your code above, but then I found this link, https://discuss.streamlit.io/t/how-to-show-audio-player-when-point-on-interactive-plot-is-clicked/20664, in the Streamlit forum, where I got the null-jones addon for Plotly, https://github.com/null-jones/streamlit-plotly-events. This specific addon returns a list of dicts with the following format:
{
x: int (x value of point),
y: int (y value of point),
curveNumber: (index of curve),
pointNumber: (index of selected point),
pointIndex: (index of selected point)
}
If you could use the coordinates or index information to set up your update logic, then this probably can help you out. For instance, my approach was as follows:
I've installed the addon using pip: pip install streamlit-plotly-events
;
I've generated the scatter plot figure using the hover_name
property set with the video paths:
import plotly.express as px
fig = px.scatter(df, x=0, y=1, hover_name=df.paths)
I've created a dict for mapping the hover_text and the scatter plot's coordinates:
x_data = fig.data[0]['x']
y_data = fig.data[0]['y']
hoverdata = fig.data[0]['hovertext']
for x_val, y_val, text in zip(x_data, y_data, hoverdata):
if (x_val, y_val) not in st.session_state.data_dict.keys():
st.session_state.data_dict[(x_val, y_val)] = text
Using the addon, I got the coordinates of each clicked point:
from streamlit_plotly_events import plotly_events
# this line displays the plot already
selected_points = plotly_events(fig, click_event=True)
current_x = selected_points[0]['x']
current_y = selected_points[0]['y']
video_path = st.session_state.data_dict[(current_x, current_y)]
With the video_path, the last step was to display the video in the app using the st.video()
method:
video_file = open(video_path, 'rb')
video_bytes = video_file.read()
st.video(video_bytes)
Upvotes: 0