# Plotly update figure on selection

**URL:** <https://community.plotly.com/t/plotly-update-figure-on-selection/86339>\
**Category:** Dash Python\
**Tags:** question\
**Created:** [August 6, 2024, 12:48pm UTC](https://community.plotly.com/t/plotly-update-figure-on-selection/86339 "2024-08-06T12:48:45Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![H4CK3R](https://avatars.discourse-cdn.com/v4/letter/h/5f9b8f/32.png) [@H4CK3R](https://community.plotly.com/u/H4CK3R)\
**Post date:** [August 6, 2024, 12:48pm UTC](https://community.plotly.com/t/plotly-update-figure-on-selection/86339/1 "2024-08-06T12:48:45Z")

</div>

Hello,

I am trying to update figure on selection but it doesn’t turn back when when clicked “reset” or double-click

```python

app = Dash(
    suppress_callback_exceptions=True
)

def getGraph(x,y,selectedData):
    selectedPoints,unselectedPoints = getPoints(x,y,selectedData)
    fig = go.Figure()
    fig.add_trace(
        go.Scatter(
            x = selectedPoints[:,0],
            y = selectedPoints[:,1],
            mode="markers",
            marker=dict(color="crimson"),
            name="selectedScatter"
        )
    )
    fig.add_trace(
        go.Scatter(
            x = unselectedPoints[:,0],
            y = unselectedPoints[:,1],
            mode="markers",
            name="unselectedScatter",
            marker=dict(color="cyan")
        )
    )
    return fig

num_size = 1000
x = np.random.random_integers(low=0,high=100,size=num_size)
y = np.random.random_integers(low=0,high=100,size=num_size)
originalFig = go.Figure()
originalFig.add_trace(
    go.Scatter(
        x=x,
        y=y,
        mode="markers",
        marker=dict(color="cyan")
    )
)

app.layout = html.Div(
    [
        html.Div(
            dcc.Graph(
                id="the-graph",
                style={
                    "width":"90vw"
                }
            ),
            style= {
                "display":"flex",
                "align-items":"center",
                "justify-content":"center"
            }
        ),
    ]
)

@callback(
    Output("the-graph","figure"),
    Input("the-graph","selectedData")
)
def updateFigure(selectedData):
    if selectedData:
        return getGraph(x,y,selectedData)
    else:
        return originalFig
    

app.run_server()

```

I observed that when selected using “box-select” or “lassoSelect” then selectedData dict is generated populating it from null to dict but after the figure is updated the selectedData turns back to empty list and not null, it becomes

> selectedData = {“points”:}

I would like it to revert back to originalFig when double-click event, but I haven’t found anything related to that.

is there a way to rectify this?

---

<div class="post-metadata">

**Author:** ![davidharris](https://avatars.discourse-cdn.com/v4/letter/d/df705f/32.png) [@davidharris](https://community.plotly.com/u/davidharris)\
**Post date:** [August 7, 2024, 7:42am UTC](https://community.plotly.com/t/plotly-update-figure-on-selection/86339/2 "2024-08-07T07:42:35Z")

</div>

I don’t have a full solution to this, only a couple of thoughts that might hopefully be useful.

An easy way of reverting back to the original figure could be to just add a ‘reset’ button to the layout and use a callback handling its click data - would that work for what you want?

If not, you have a few lines in your code that effectively prevent the figure ever returning to its original setting.

```python
    if selectedData["points"] != []:
        selected_points = selectedData["points"]
    else:
        selected_points = previousSelectedPoints

```

I can see why you have these - your callback replaces the figure, and that resets the selection to empty and you need to prevent the callback retriggering and immediately resetting the figure to the original. But I can’t see how to change it to get the behaviour you need if you’re replacing the figure.

Maybe an alternative would be to add a new trace to the figure (using Patch) showing the selected points, leaving the original trace unchanged, and have your callback manipulate only this second trace? I don’t really know if that would work - it depends on just what events get triggered when the second trace is modified - but it’s what I’d try if no-one else comes up with a better suggestion.

As a separate thing, using a _global_ to store previous state will go wrong if there is more than one user - see the link below. The usual solution is to use a dcc.Store() to store state, not a global.

> **[Part 4. Sharing Data Between Callbacks | Dash for Python Documentation | Plotly](https://dash.plotly.com/sharing-data-between-callbacks#why-global-variables-will-break-your-app)**
>
> Global variables will break your Dash apps. However, there are other ways to share data between callbacks. This chapter is useful for callbacks that run expensive data processing tasks or process large data.

---

<div class="post-metadata">

**Author:** ![H4CK3R](https://avatars.discourse-cdn.com/v4/letter/h/5f9b8f/32.png) [@H4CK3R](https://community.plotly.com/u/H4CK3R)\
**Post date:** [August 7, 2024, 8:24am UTC](https://community.plotly.com/t/plotly-update-figure-on-selection/86339/3 "2024-08-07T08:24:17Z")

</div>

I really appreciate the response and I found a temporary workaround just an hour ago, though I will keep this open until someone from the team confirm if theres an easy way to do It.

I am assuming you understood my problem, Isn’t it a simple thing like shouldn’t updating a figure on selection a simple thing in concept but when I found the things that change on clicks on graph are so many

this is the work-around that works perfectly specifically for me:

```python

app = Dash(
    suppress_callback_exceptions=True
)

def getGraph(x,y,selectedData):
    selectedPoints,unselectedPoints = getPoints(x,y,selectedData) #get the seleted points and unselected points in least time complexity
    fig = go.Figure()
    fig.add_trace(
        go.Scatter(
            x = selectedPoints[:,0],
            y = selectedPoints[:,1],
            mode="markers",
            marker=dict(color="crimson"),
            name="selectedScatter"
        )
    )
    fig.add_trace(
        go.Scatter(
            x = unselectedPoints[:,0],
            y = unselectedPoints[:,1],
            mode="markers",
            name="unselectedScatter",
            marker=dict(color="cyan")
        )
    )
    return fig

num_size = 100
x = np.random.random_integers(low=0,high=100,size=num_size)
y = np.random.random_integers(low=0,high=100,size=num_size)
originalFig = go.Figure()
originalFig.add_trace(
    go.Scatter(
        x=x,
        y=y,
        mode="markers",
        marker=dict(color="cyan"),
        name="totalScatter",
        showlegend=True
    )
)

app.layout = html.Div(
    [
        html.Div(
            dcc.Graph(
                id="the-graph",
                style={
                    "width":"90vw"
                }
            ),
            style= {
                "display":"flex",
                "align-items":"center",
                "justify-content":"center"
            }
        ),
    ]
)

@callback(
    Output("the-graph","figure"),
    [
        Input("the-graph","selectedData"),
        Input("the-graph","relayoutData")
    ],
    prevent_initial_call=True
)
def updateFigure(selectedData,relayoutData,*args,**kwargs):
    if (selectedData is not None) and ((relayoutData.get("xaxis.showspikes",True) == True) or (relayoutData.get("yaxis.showspikes",True) == True)): # condition to trigger when there is some selected data but also the "reset" button is not clicked ( when reset is clicked it actually becomes False)
        fig = getGraph(x,y,selectedData)
        if (relayoutData.get("xaxis.range[0]",None) is not None): # this is when you zoom out also extracting the ranges i.e., the zoom limiits this is added because when you zoom it reverted mine to original image. 
            fig.update_xaxes(range=[relayoutData.get("xaxis.range[0]"),relayoutData.get("xaxis.range[1]")])
            fig.update_yaxes(range=[relayoutData.get("yaxis.range[0]"),relayoutData.get("yaxis.range[1]")])
        return fig        
    else: # rest any case ( by pressing reset button or refresh the page) )just display the normal plot
        originalFig.update_layout(**relayoutData)
        return originalFig

app.run_server()

```

I would actually like to have a simpler solution that this if at all its possible, The only things that throws wrench in my code is the selection mechanism where when you selected data using “select” or “lassoSelect” the box just highlights it so it stores points in selectedData but as soon as fig is updated the points becom null again (selectedData = {“points”:})

---

<div class="post-metadata">

**Author:** ![AIMPED](https://sea2.discourse-cdn.com/flex024/user_avatar/community.plotly.com/aimped/32/21758_2.png) [@AIMPED](https://community.plotly.com/u/AIMPED)\
**Post date:** [August 10, 2024, 10:50pm UTC](https://community.plotly.com/t/plotly-update-figure-on-selection/86339/4 "2024-08-10T22:50:00Z")

</div>

I’ve come across this too. The problem is, that the selected data is not reset automatically. I usually just add the selected as output when using it as input to trigger the callback. As return value for the selected data I use an empty dict, None, or empty list. I can’t remember exactly right now.
