Apply/assign something to all data traces of patched_figure

I’m trying to assign e.g. an opacity value to all markers of all data traces in patched_figure.
since I cannot use len(patched_figure['data']), since one cannot read from patched_figure. How would I achieve this?
current, non-working approach

for i, _ in enumerate(patched_figure['data']):
   patched_figure['data'][i]['marker']['opacity'] = 0.5

I could do something like

i=0
while True:
   try:
      patched_figure['data'][i]['marker']['opacity'] = 0.5
   except IndexError:
      break
   i += 1

but it feels uncomfortably hacky

Hi @luggie I think you can’t do that with partial updates. I understand the Patch() as an instruction to change a explicitly defined part of the component.

To test this, I tried to return a list of patch objects to a figure. An extract of the error I got :

  {
    "__dash_patch_update": "__dash_patch_update",
    "operations": [
      {
        "operation": "Merge",
        "location": [
          "data",
          0
        ],
        "params": {
          "value": {
            "marker": {
              "color": "darkblue",
              "size": 20
            }
          }
        }
      }
    ]
  }

If you know the number of traces in the figure, you could do this:

@app.callback(
    Output(..., 'figure'), 
    Input(...),                            
    prevent_initial_call=True                             
)
def new(_):
    # Create a Patch object
    patched_figure = Patch()

    # assuming to have 7 traces in the figure
    for i in range(7):
        patched_figure["data"][i].update({"marker": {'opacity': 0.5}})
    return patched_figure

EDIT: you don’t even have to know the number of traces. If you want to change all of the traces, you only have to assure, that the range is grater than the number of traces in your figure. Just keep in mind, that the process gets slower with increasing the range. An example:

@app.callback(
    Output(..., 'figure'), 
    Input(...),                            
    prevent_initial_call=True                             
)
def new(_):
    # Create a Patch object
    patched_figure = Patch()

    # works even if there are only 7 traces in the figure
    for i in range(100):
        patched_figure["data"][i].update({"marker": {'opacity': 0.5}})
    return patched_figure
2 Likes

Yes that’s true. One can iterate higher then what is present without running into IndexError or PlotlyKeyError.
However this is not true for a go.Figure object. This is what broke my app.

i dont know how the patch() works but, should it not be something like,

@app.callback(
    Output('current_fig', 'figure'), 
    Input(...),                            
    prevent_initial_call=Input                             
)
def new(_):
    # Create a Patch object
    current_fig = Patch()
  
    for i in range(100):
        current_fig['data'][i]['marker']['opacity'] = 0.5
    return current_fig

Yes, you are right. My code updates the marker dictionary whereas yours assigns a new value to the opacity key.

you are enumerating the number of figure datas, but you only have 1 figure data hence the error.
you need to enum the list inside figure[‘data’][‘enum this list’]

easier to create a list that stores the number of traces you have and just use that list range

1 Like

maybe

    for i in range(len(patched_figure['data'])):
         patched_figure['data'][i]['marker']['opacity'] = 0.5

Hey @srcveiga, take a look at this (the limitations):

oh, as in you would need to store the list in a dcc store? Yes you may need to but i think using the current fig state is enough.

@app.callback(
    Output('patched_figure', 'figure'),
    Input('...'),
    State('patched_figure', 'figure'),
    prevent_initial_call= True
)

def new(_, patched_figure):
    # Create a Patch object
    patched_figure = Patch()

    for i in range(len(patched_figure['data'])):
         patched_figure['data'][i]['marker']['opacity'] = 0.5
    return patched_figure

If the problem is returning the value it could be that the trace does not have an actual marker?

For a go.scatter it should be something like, ‘line’. For a go.candle it should be something like, ‘ascending’ ‘line’ etc.
I think.

We never got to see the callback or his figure, but he was using len when he needed to use the range just like you did in your example.