Search code examples
plotlyjupyter-labplotly-pythonyaxis

Plotly in Jupyterlab: difference between show method and display (effect on plotly on_change)


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!


Solution

  • I also ran into this problem where using the show() method prevented the on_change handler from working.

    1. 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.

    2. 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.