buttons for subplots using plotly

29 Views Asked by At

I'm new to Plotly and I'm trying to have buttons for each subplot and not the figure. I'm not able to find the way to do it or find the way for it.

Can anyone please help me how can I do it ?

Here is my code :

def plot_data(data, filename):
    """
    Plotting the data
    param data: data to plot.
    param filename: file name to write the output in.
    return: None.
    """

    subplot_titles_tuple = get_titles(data)
    fig = make_subplots(shared_xaxes=False,rows=len(data), cols=1, subplot_titles=subplot_titles_tuple)
    for i, segment in enumerate(data):
        trace = go.Scatter(x=segment['x'], y=segment['y'], mode='lines', name=f"Plot {i + 1}") # change the name of the plot
        #print('im here',segment['x'] )
        fig.add_trace(trace,row=i+1,col=1)

        # Set subplot titles
        fig.update_layout(title=f"Plots for {filename}")

        # Set x-axis and y-axis titles according to segment data
        x_title = segment.get('x_array_label', 'RCM')  # Default to 'X-axis' if 'x_array_label' is not present
        y_title = segment.get('y_array_label', 'Y-axis')  # Default to 'Y-axis' if 'y_array_label' is not present
        fig.update_xaxes(title_text=x_title, row=i + 1, col=1)
        fig.update_yaxes(title_text=y_title, row=i + 1, col=1)


        ### Buttons for the user to change in the plot
        fig.update_layout(
            updatemenus=[
                dict(
                    buttons=[
                        dict(label="Linear", method="relayout", args=[{"yaxis.type": "linear"}]),
                        dict(label="Log", method="relayout", args=[{"yaxis.type": "log"}]),
                        dict(label="Points", method="restyle", args=[{"mode": "markers"}]),
                        dict(label="Line", method="restyle", args=[{"mode": "lines+markers"}]),
                    ],
                    direction="down",
                    showactive=True,
                    x=1,
                    xanchor="left",
                    y=0.9,
                    yanchor="top"
                )
            ]
        )



    # change xlim and ylim

    xlim_slider = widgets.FloatRangeSlider(
        value=[min(segment['x']), max(segment['x'])],  # Initial limits based on data
        min=min(segment['x']),
        max=max(segment['x']),
        step=0.1,
        description='xlim:',
        continuous_update=False
    )

    ylim_slider = widgets.FloatRangeSlider(
        value=[min(segment['y']), max(segment['y'])],  # Initial limits based on data
        min=min(segment['y']),
        max=max(segment['y']),
        step=0.1,
        description='ylim:',
        continuous_update=False
    )


    # Function to update xlim and ylim
    def update_plot(xlim, ylim):
        fig.update_xaxes(range=xlim)
        fig.update_yaxes(range=ylim)

    # Connect sliders to update function
    widgets.interactive(update_plot, xlim=xlim_slider, ylim=ylim_slider)

    # Show or save the plot
    plot_filename = f"{os.path.splitext(filename)[0]}_plots.html"
    plot_path = os.path.join(os.getcwd(), plot_filename)
    fig.write_html(plot_path)
    print(f"All plots saved in {plot_filename}")

This is what I'm getting : screenshot.

I want those options on each subplots not the whole graph.

1

There are 1 best solutions below

0
EricLavault On

The main issue is that fig.update_layout(updatemenus=[...]) is executed inside the for loop, and thus at each iteration the updatemenus list in the layout config is overridden, not extended (same problem with the layout title, though you can only set one title anyway since the layout config applies to the whole figure, but it seems you already managed that using subplot_titles).

Regarding the buttons updating the yaxis type, you need to specify the axis id (eg. yaxis, yaxis2, etc.) that corresponds to the subplot index.

And for the buttons invoking the restyle method, you need to specify a list of trace indices that corresponds to the trace(s) present on the corresponding subplot (ie. trace index won't necessarily match subplot index).

fig = make_subplots(shared_xaxes=False,rows=len(data), cols=1, subplot_titles=subplot_titles_tuple)
fig.update_layout(title=f"Plots for {filename}")

updatemenus = []
btn_y = [0.9, 0.4]

for i, segment in enumerate(data):
    trace = go.Scatter(x=segment['x'], y=segment['y'], mode='lines', name=f"Plot {i + 1}") # change the name of the plot
    #print('im here',segment['x'] )
    fig.add_trace(trace,row=i+1,col=1)

    # Set x-axis and y-axis titles according to segment data
    x_title = segment.get('x_array_label', 'RCM')  # Default to 'X-axis' if 'x_array_label' is not present
    y_title = segment.get('y_array_label', 'Y-axis')  # Default to 'Y-axis' if 'y_array_label' is not present
    fig.update_xaxes(title_text=x_title, row=i + 1, col=1)
    fig.update_yaxes(title_text=y_title, row=i + 1, col=1)

    yaxis_id = 'yaxis' if i == 0 else f'yaxis{i+1}'

    updatemenus.extend([
        dict(
            buttons=[
                dict(label="Linear", method="relayout", args=[{f"{yaxis_id}.type": "linear"}]),
                dict(label="Log", method="relayout", args=[{f"{yaxis_id}.type": "log"}])
            ],
            direction="down",
            showactive=True,
            active=0,
            x=1,
            xanchor="left",
            y=btn_y[i],
            yanchor="top"
        ),
        dict(
            buttons=[
                dict(label="Points", method="restyle", args=[{"mode": "markers"}, [i]]),
                dict(label="Line", method="restyle", args=[{"mode": "lines+markers"}, [i]]),
            ],
            direction="down",
            showactive=True,
            active=1,
            x=1,
            xanchor="left",
            y=btn_y[i]-0.05,
            yanchor="top"
        )
    ])

fig.update_layout(updatemenus=updatemenus)