Black Lives Matter. Please consider donating to Black Girls Code today.

How to listen events on modifying a shape

Drawing shapes on Cartesian Plot section of docs, state that:

Drawing or modifying a shape triggers a relayout event, which can be captured by a callback inside a Dash application.

But I’m unable to figure out how to do the same in plotly python within jupyter notebook (because I believe dash is also using the callbacks just named differently). The code I used was:

fig = go.FigureWidget(go.Scatter(x=[1,2,3], y=[4,5,6]))
fig.add_shape(
    editable=True,
    x0=0, x1=2, y0=0, y1=3,
    xref='x1', yref='y1'
)
fig.update_layout(dragmode='drawrect')
shp = fig.layout.shapes[0]

fig.show(config={'modeBarButtonsToAdd':[
                                        'drawrect',
                                        'eraseshape'
                                       ]})

In another cell, I’m trying to capture the x-axis range to which rectangle span (i.e. its width):

out = ipywidgets.Output()

@out.capture()
def shape_handler(shp_obj, x0, x1):
    print(x0, x1)

shp.on_change(shape_handler, 'x0', 'x1', append=True)

But nothing is printed. Also I can observe manually that fig.layout.shapes never updates when I modify rectangle. It just contains the default value:

(layout.Shape({
     'editable': True, 'x0': 0, 'x1': 2, 'xref': 'x', 'y0': 0, 'y1': 3, 'yref': 'y'
 }),)

So I think I may be doing it wrong. But I couldn’t find any other attribute that stores information about shapes.

Why I want this:
I’m trying to make some interface like this:

I believe plotly is powerful enough to make this possible - capturing the x-axis range of selection (I can’t use rangeslider because I don’t want zooming it does). And, if it’s not possible in jupyter notebooks - then I have to resort for dash app, but still I’m not sure about whether I need to pass fig or shp in Input parameter of callback decorator - an example will be very helpful!

Thanks for the great work, I hope a reply soon.

Hi jaladh,

I am facing exactly the same issue.
I think the issue is coming from the fig.show() that seems to break the callback.
If you just replace :

fig.show(config={'modeBarButtonsToAdd':[
                                        'drawrect',
                                        'eraseshape'
                                       ]})

by :

from IPython.display import display
display(fig)  

it works. But you are loosing the two buttons “drawrect” and “eraseshape”.

Maybe worth posting a bug report on github ?
Please post here if you can find a solution or work-around :slight_smile:

Hope this helps.

Patrick

By the way I’ve just noticed from :


It says :
It is important to note that FigureWidgetdoes not use the renderers framework discussed above, so you should not use theshow()figure method or theplotly.io.showfunction onFigureWidget objects.

So, you all functions, on_* (such as on_change) which are FigureWidget specific will not work is you use the “show” function.

other useful ref: