Hi, everyone!
I’m working on part of a Dash app that I want to use to add points to one trace in a scatterpoint graph by clicking on the graph canvas.
So far, my work has progressed as far as:
- recognizing I need clickData to trigger on the point I’m clicking
- creating a second trace in a grid with 0 opacity to trigger clickData
- writing a callback to print that clickData to the app (partially for troubleshooting purposes)
- recognizing I need a way to update the graph, preferably just the data rather than running the whole figure generation again
- finding the dcc.Graph.extendData property, both here and in the docs
I’m currently having problems getting the point from clickData into the graph’s trace; I had to deal with some datatype problems at first, but now, after having printed to terminal to see if the “new” x and y data in the callback is getting the clickData appended, I can’t figure out how to get that data into the graph traces and plotted as output. All code is as follows:
import plotly.graph_objects as go
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input,Output,State
import plotly.graph_objs as go
import numpy as np
np.random.seed(1)
extentLeft = 0.
extentRight = 100.
extentBottom = 0.
extentTop = 60.
stepSize = 0.1
xGridX = np.arange(extentLeft, extentRight, stepSize)
yGridY = np.arange(extentBottom, extentTop, stepSize)
xzx, yzy = np.meshgrid(xGridX, yGridY, sparse=False)
xzx = np.concatenate(xzx, axis=0 )
yzy = np.concatenate(yzy, axis=0 )
GridTrace = go.Scatter(x=xzx, y=yzy, opacity=0)
app = dash.Dash()
x = np.random.uniform(extentLeft, extentRight, 100)
y = np.random.uniform(extentBottom, extentTop, 100)
scatter = go.Figure(go.Scatter(x=x, y=y, mode='markers'))
color = '#a3a7e4'
scatter.update_traces(overwrite=True, marker={"color":color, "size": 10})
scatter.update_layout(hovermode='closest')
scatter.add_trace(GridTrace)
app.layout = html.Div(children=[
html.H1(id='clickOutput'),
dcc.Graph(id='exampleGraph',figure=scatter)
])
@app.callback(
Output('clickOutput','children'),
Output('exampleGraph','extendData'),
[Input('exampleGraph','clickData')],
[State('exampleGraph','figure')]
)
def callback_point(clickData, existing):
clickPoint = (np.round_([clickData["points"][0]["x"], clickData["points"][0]["y"]], decimals = 2))
x_new = [existing['data'][0]['x'] + [float(str(clickPoint[0]))]]
y_new = [existing['data'][0]['y'] + [float(str(clickPoint[1]))]]
return "{} is the retrieved clickData".format(clickPoint), [dict(x=[[x_new[0]]], y=[[y_new[0]]]), [0]]
if __name__ == '__main__':
app.run_server(debug=True)
It seems to me like I have too many references to the graph across the callback’s Input, Output and State, but I can’t figure out what else would go there instead. All canonical examples of expandData seem to be triggered by dcc.Interval, but I don’t see why that should be the problem here; it’s just a different triggering event. Any ideas?
Thanks!