Hi there,
I really like the Patch()
method but I came a cross an issue. I wanted to add a shape in a figure which does not have a initial shape in it. Problem here is, that the Patch()
method does not work as the shape list
in the figure layout does not exist. So I can’t use the list specific methods such as extend
or append
.
I tried setting an empty shape list
in the initial figure but this is getting interpreted as none
by the JS so it throws an error.
undefined does not have a method named “concat” or “fantasy-land/concat”
I think this is a general issue with the Patch()
method. Maybe someone has an idea how to overcome this.
Here a MRE:
import json
import random
import dash
from dash import html, dcc, Input, Output, Patch
import plotly.graph_objs as go
import numpy as np
def create_shape(x, y, size=2, color='rgba(39,43,48,255)'):
"""
function creates a shape for a dcc.Graph object
Parameters:
x: x coordinate of center point for the shape
y: y coordinate of center point for the shape
size: size of annotation (diameter)
color: (rgba / rgb / hex) string or any other color string recognized by plotly
Returns:
a list containing a dictionary, keys corresponding to dcc.Graph layout update
"""
shape = [
{
'editable': True,
'xref': 'x',
'yref': 'y',
'layer': 'above',
'opacity': 1,
'line': {
'color': color,
'width': 1,
'dash': 'solid'
},
'fillcolor': color,
'fillrule': 'evenodd',
'type': 'circle',
'x0': x - size / 2,
'y0': y - size / 2,
'x1': x + size / 2,
'y1': y + size / 2
}
]
return shape
def extract_click_coordinates(click_data: dict) -> tuple[int, int]:
"""
function extracts information from a dictionary
Parameters:
click_data: dictionary which is return by the plotly-dash click event on a dcc.Graph
Returns:
x, y: coordinates of the click event
"""
points = click_data.get('points')[0]
x = points.get('x')
y = points.get('y')
return x, y
app = dash.Dash(
__name__,
)
fig = go.Figure(
data=go.Heatmap(
z=np.random.randint(0, 255, size=(30, 30))
),
layout={
'width': 900,
'height': 900,
# 'shapes': create_shape(10, 10, size=2, color='black'),
'shapes': []
}
)
app.layout = html.Div(
[
dcc.Graph(
id='graph',
figure=fig
),
html.Pre(
id='out',
children=json.dumps(
fig.to_dict().get('layout').get('shapes', 'empty'),
indent=2
)
)
],
)
@app.callback(
Output('graph', 'figure'),
Output('out', 'children'),
Input('graph', 'clickData'),
prevent_initial_call=True
)
def show_fig(clickData):
x, y = extract_click_coordinates(clickData)
patched = Patch()
patched['layout']['shapes'].extend(create_shape(x, y, size=2, color='white'))
return patched, f'just checking... {random.random()}'
if __name__ == '__main__':
app.run(debug=True)