borisdonchev
borisdonchev

Reputation: 1224

Plotly <-> Streamlit interactive buttons

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

Answers (1)

Matheus Stauffer
Matheus Stauffer

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:

  1. I've installed the addon using pip: pip install streamlit-plotly-events;

  2. 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)
    
  3. 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
    
  4. 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)]
    
    
  5. 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

Related Questions