Change a specific trace data with button

Hi there,

Let me know how I can change a specific trace using buttons? look at the following code:

import plotly.graph_objs as go
import random as rnd

num_steps = 3

fig=go.Figure()
fig.add_scatter(y=[0, 0, 0],  line={'color': 'red'},name='firsttrace')
fig.add_scatter(y=[3, 3, 3],  line={'color': 'blue'},name='Secondttrace')
fig.add_scatter(y=[2, 2, 2],  line={'color': 'purple'},name='Thirdtrace')
fig.add_scatter(y=[1, 1, 1],  line={'color': 'gray'},name='Forthtrace')

buttons = []
for i in range(num_steps):
    
    button = dict(method='restyle',  args=[{'y': [[rnd.randint(1,3) for i in range(3)],[rnd.randint(1,3) for i in range(3)]],
                                            'line':[{'color': 'black'},{'color': 'green'}]}],  label=i)
    
    # Add button to buttons list
    buttons.append(button)

fig.layout.updatemenus = [{'buttons': buttons}]
fig.show()

in the above sample, I want to change data and color of traces with name ‘Secondttrace’ and ‘Forthtrace’ and let the other traces remain as they are. Maybe there is a way using selector, but I don’t know.

Thanks, in advanced.

Hi, does this help?

Go to this example:

Magic underscore notation can be used in the selector to match nested properties. Here is an example of updating the color of all traces that were formally colored “MediumPurple”`

Dear @AIMPED
Thanks for your contribution but it was not helpful unfortunately. I need to change one of the figure traces by clicking on a button.

While this does not answer your question, at least I figured out (for myself) how to change each trace independently.

import plotly.graph_objs as go

fig=go.Figure()
fig.add_scatter(y=[1, 1, 1],  line={'color': 'red'},name='firsttrace')
fig.add_scatter(y=[2, 2, 2],  line={'color': 'blue'},name='Secondttrace')
fig.add_scatter(y=[3, 3, 3],  line={'color': 'purple'},name='Thirdtrace')
fig.add_scatter(y=[4, 4, 4],  line={'color': 'gray'},name='Forthtrace')


button = [dict(
    method='update',  
    args=[
        {
            'mode':[
                'markers+lines', 
                'markers+lines', 
                'markers+lines', 
                'markers+lines'
            ],
            'marker':[
                {'color': ['pink', 'red', 'green'], 'size':[6,15,23], 'line':{'color':'black'}},
                {'color': ['orange', 'blue', 'black'], 'size':[23,11,8], 'line':{'color':'black'}},
                {'color': ['red', 'white', 'gray'], 'size':[1,15,30], 'line':{'color':'black'}},
                {'color': ['green', 'purple', 'magenta'], 'size':[23,11,8], 'line':{'color':'black'}},


            ],
            'line':[
                {'color':'crimson'},
                {'color':'blue', 'width':8},
                {'color':'green'},
                {'color':'white'},
            ]
        },
    ],
    label='button'
)]
    

fig.layout.updatemenus = [{'buttons': button}]
fig.show()
2 Likes

Happy to hear that.

I hope someone also solve mine.

Hey @Bijan, I actually thought, you could solve yours with my code :thinking:

I found some time to prototype something. Obviously it’s not an universal approach, but maybe a starting point.
Exemplarily the line color of two traces are changed.

Basically I follow four steps:

  • find the trace to be changed (by name)
  • inspect current traces in figure
  • update traces
  • create button
def create(figure, name_list, change_dict_list):
    info = len([*figure.select_traces()])
    indices = [idx for idx, trace in enumerate(fig.select_traces()) if trace.name in name_list]
    
    # analyze change_dict_list
    new_colors = []
    for d in change_dict_list:
        new_colors.append(d.get('line').get('color'))
    
    # analyze traces
    existing_colors = []
    for trace in figure.select_traces():
        existing_colors.append(trace.line.color)
    
    # substitute colors
    for idx, new_color in zip(indices, new_colors):
        existing_colors[idx] = new_color
        
    # prepare output
    return {'line': [{'color': color} for color in existing_colors]}

usage:

import plotly.graph_objs as go

# base figure
fig=go.Figure()
fig.add_scatter(y=[1, 1, 1],  line={'color': 'blue'},name='Firsttrace')
fig.add_scatter(y=[2, 2, 2],  line={'color': 'blue'},name='Secondtrace')
fig.add_scatter(y=[3, 3, 3],  line={'color': 'blue'},name='Thirdtrace')
fig.add_scatter(y=[4, 4, 4],  line={'color': 'blue'},name='Forthtrace')

button = [dict(
    method='update',  
    args=[
        create(fig,  ['Secondtrace', 'Forthtrace'], [{'line':{'color':'yellow'}}, {'line':{'color':'red'}}])
    ],
    label='button'
)]
    
fig.layout.updatemenus = [{'buttons': button}]
fig.show()

btn

1 Like

Dear @AIMPED, Really thanks for the time you put for providing above code. What I need exactly was fig.select_traces() and it was completely helpful.

1 Like

During the first lockdown in 2020 a lot of questions on different button actions have been addressed. Then I posted
these notebooks to explain how the buttons are connected to trace or layout restyling:
b'Restyling a Plotly figure, via a dropdow | empet | Plotly' (see also the links posted in this notebook)
b'Restyle subplots simultaneouslyhttps | empet | Plotly'
b'Updatemenus: set secondary yaxis invisib | empet | Plotly'
b'Distplot with updatemenus | empet | Plotly'

3 Likes

Dear @empet

Like always, great data. Thanks.

Just a question. Imagine I have two fig objects like fig1 and fig2 that all of their data and layout are different. Is it possible to add a drop button that by clicking on first item fig1 be shown and by clicking on the second item the fig2 be shown?

@Bijan Could you give your code for the two traces and explain whether they are plotted in two subplots or in the same plot window, but displayed alternatively?

in same plot window alternatively

@Bijan If you download, read, and run the examples in the notebooks I pointed out, above, you’ll find out how to displays two traces alternatively.

Sorry dear @empet I think I explained the problem bad. My problem arose when I have two different figures that eachone has various and different traces and different layouts.

I’m thinking that maybe there is a way that by click button, each figure be shown on same window alternatively without using args and defining traces inside args. If there be a way that directly refer to each figure by clicking the button.

@Bijan
I don’t think to be able to find a workaround to display alternatively two different figures, with distinct traces and layout, because the button definition is inserted, via updatemenus in fig.layout.

2 Likes

Really thanks dear @empet. You always guide me generously and show me the correct way.

I think this issue maybe is an important issue that Plotly should have this feature in while analyzing data I think we need sometimes to switch from a figure to another figure (With completely various traces and layout and type and …) for many purposes. maybe Plotly managers like dear @adamschroeder if knows this provide this feature for Plotly in future.

Again thanks.

hi @Bijan
There are a few priority features we are planning to work on for 2023, but it doesn’t hurt to add this feature request if you’d like.
You can create a feature request in our repo. Our engineers will take see if it’s even possible.

1 Like

Hi @adamschroeder
Thanks for guidance and just now I create a request.

1 Like