In JupyterLab, using Plotly, I have set up a callback to update the y-axis when zooming.
Please refer to the code below for a complete example.
The zoom function is connected to the layout using on_change.
The yaxis
updates correctly when the figure is displayed using IPython's display
or directly with fig
. However, it stops working if the figure.show()
method is used.
What is the issue with this method in this particular scenario? Why does on_change no longer work?
import plotly.graph_objs as go
import pandas as pd
import numpy as np
x = np.linspace(0, 100, 1000)
y = 0.1* x * np.sin(2 * np.pi * 0.1 * x)
# Create the DataFrame
df = pd.DataFrame({'Time': x, 'Value': y})
df = df.set_index('Time')
fig = go.FigureWidget()
fig.add_trace(
go.Scatter(x=df.index, y=df['Value'], mode='lines')
)
fig.update_layout(
xaxis={'rangeslider_visible': True}
)
# callback function to update yaxis min and max upon zoom
def zoom(layout, xrange):
in_view = df.loc[fig.layout.xaxis.range[0]:fig.layout.xaxis.range[1]]
fig.layout.yaxis.range = [in_view.Value.min(), in_view.Value.max()]
fig.layout.on_change(zoom, 'xaxis.range')
#fig # on_change (zoom) works!
display(fig) # on_change (zoom) works!
#fig.show() # on_change (zoom) doesn't work
I am aware that figure.show()
utilizes the renderers framework, but I am unable to understand its functionality in this "on_change" scenario.
Could someone shed some light on this or direct me to the appropriate documentation?
Thank you!
I also ran into this problem where using the show()
method prevented the on_change
handler from working.
The use of fig.show()
actually calls the plotly.io.show()
method, which re-renders the object itself based on the specified renderer. However, the on_change
event handler is a standalone event module, and is not part of the Figure's property. Therefore, it will not be included when plotly.io re-renders it, and the fig.show()
method won't carry on_change()
settings. On the other hand, executing fig/display(fig)
just displays the object in JupyterLab/Notebook, without re-rendering, so there is no issue.
If you are using show()
to pass the config parameter, that is likely where the problem is. The fig.show(config=config)
method actually sets the figure's private attribute _config
, which inherits from the parent class BaseFigure
, and the only API interface to modify it is through the show()
method. Therefore, if you want to make on_change
work and set config at the same time, use fig.__setattr__('_config', config)
to set fig's config, such as scrollZoom = True
.
Btw, it's a good idea to use fig.__getattributer__('_config')
to get the original _config
value first, and then merge it with the new value you want to assign.
...
# set callback
fig.layout.on_change(zoom, 'xaxis.range')
# prepare mouse wheel zoom for example
_config = dict(scrollZoom=True)
# get original _config attribute and merge with yours
_config = fig.__getattribute__('_config') | _config
fig.__setattr__('_config', _config)
# now you have both scrollZoom config and on_change zoom
# callback work together, and don't use fig.show()
fig
It's not the official way, but it's an acceptable practice until plotly solves the show()
issue.