Hello, everyone. Thanks for the great tool you’ve all created!
I’m building a rough schedule calendar based on a dcc.Graph. Calendar items are represented by shapes, rectangles in this case. Each shape has a corresponding marker, that will eventually be in “text” mode and will display a description of the calendar item. However, when I drag or resize a shape, its marker disappears.
Is this a bug or am doing something wrong?
Thanks in advance.
My imports:
import plotly.graph_objects as go
#import dash
import jupyter_dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Output, Input
import numpy as np
import json
Create figure
fig = go.Figure()
xrange = [-0.5, 7.5]
yrange = [6,18]
xwidth = abs(xrange[0] - xrange[1])
yheight = abs(yrange[0] - yrange[1])
# Set axes properties
fig.update_xaxes(range=[xrange[0], xrange[1]], showgrid=False)
fig.update_yaxes(range=[yrange[1], yrange[0]])
fig.update_layout(
#autosize=False,
showlegend = False
)
# Add shapes
fig.add_shape(
go.layout.Shape(
name = "blueShape",
type="rect",
xref = "x",
yref = "y",
x0=0,
y0=7,
x1=1,
y1=8 - 0.1,
line=dict(
width=0,
),
fillcolor="skyBlue",
opacity = 0.5,
layer = "below"
))
fig.add_shape(
# filled Rectangle
go.layout.Shape(
name = "pinkShape",
type="rect",
x0=0,
y0=8,
x1=1,
y1=9.5 - 0.1,
line=dict(
width=0,
),
fillcolor="Pink",
opacity = 0.5,
layer = "below"
))
fig.add_shape(
# filled Rectangle
go.layout.Shape(
name = "greenShape",
type="rect",
x0=4,
y0=15,
x1=5,
y1=15.5 - 0.1,
line=dict(
width=0,
),
fillcolor="green",
opacity = 0.5,
layer = "below"
))
fig.add_shape(
# filled Rectangle
go.layout.Shape(
name = "yellowShape",
type="rect",
x0=2,
y0=13,
x1=3,
y1=14 - 0.1,
line=dict(
width=0,
),
fillcolor="yellow",
opacity = 0.5,
layer = "below"
))
for shape in fig.layout.shapes:
fig.add_trace(
go.Scatter(
x = [float(shape["x0"] + shape["x1"]) / 2],
y = [float(shape["y0"] + shape["y1"]) / 2],
visible = True,
text = shape["name"],
mode = "markers"))
fig.update_shapes(dict(xref='x', yref='y'))
#fig.show(config={'editable':True})
Layout and app
#app = dash.Dash()
app = jupyter_dash.JupyterDash()
app.layout = html.Div([
dcc.Graph(id = 'chart',
figure = fig,
#config = {'editable': True, "edits":{"annotationPosition": False}}),
config = {'editable': True}),
html.Pre(id = "feedback"),
html.Div(id = "store",
#style={'display': 'none'}
)
])
Callbacks
#snap the shapes to appropriate position
#output to "store" div
#!! there is a dcc.Store object
@app.callback(Output("store", "children"),
[Input("chart", "relayoutData"),
Input("chart", "figure")])
def figToStore(feedback_data, fig):
if feedback_data != {"autosize": True}:
shape = list(feedback_data.keys())[0][7]
new_x0 = int(float(feedback_data["shapes[" + shape + "].x0"]) + 0.5)
new_x1 = int(float(feedback_data["shapes[" + shape + "].x1"]) + 0.5)
new_y0 = round(float(feedback_data["shapes[" + shape + "].y0"]) * 4) / 4
new_y1 = round(float(feedback_data["shapes[" + shape + "].y1"] + 0.1) * 4) / 4 - 0.1# -0.1 to help differintiate between a block below
if new_x0 == new_x1:
new_x1 = new_x0 + 1
if new_y0 == new_y1:
new_y1 = new_y0 + 0.24
fig["layout"]["shapes"][int(shape)]["x0"] = new_x0
fig["layout"]["shapes"][int(shape)]["x1"] = new_x1
fig["layout"]["shapes"][int(shape)]["y0"] = new_y0
fig["layout"]["shapes"][int(shape)]["y1"] = new_y1
#snap shape's marker<<<---------------------------------------------------<<<
fig["data"][int(shape)]["x"] = (new_x0 + new_x1) / 2
fig["data"][int(shape)]["y"] = (new_y0 + new_y1) / 2
fig["data"][int(shape)]["visible"] = "true"
return json.dumps(fig)
#placeholder, Input must not equal Output
@app.callback(Output("chart", "figure"),
[Input("store", "children")])
def figToChart(fig):
return json.loads(fig)
@app.callback(Output("feedback", "children"),
[Input("chart", "relayoutData")])
def updatePre(feedback_data):
return json.dumps(feedback_data)
Run server
if __name__ == '__main__':
app.run_server()