On_change event on figure - detecting when width changes

Hi. I am trying to capture an effective event where the width of a figure() changes because the user resized it. The on_change method for figure() does not seem to generate a callback for attr ‘width’. For example:

def foo(attr, old, new):
    print('printing plot width:')
    print(new)
plot.on_change('width', foo)

Also, on_event does not seem to be a good option as the only thing I can capture here with figure() that seems to come close to what I want is MouseMove, but I can’t be sure that the user will actually trigger a mouse move when resizing the browser window (the mouse may never enter the plot/figure). To be sure, here, I would in a callback determine if the width changed.

Maybe there is some better way to do this by detecting a change event from the browser somehow, so maybe somebody can suggest something there. But also I figured there would be someway that Bokeh could sort of natively indicate when the width of figure() had changed.

Any help/advice would be greatly appreciated.

Hi @dataEars are you running your code as a Bokeh server application? Real python callbacks, i.e. with on_change, require running a Bokeh server, because the Bokeh server is the Python process that executes the on_change callback code.

If you are generating standalone (HTML) output with output_file and show, etc. then things are limited to JavaScript callbacks (with js_on_change) that can run in the viewer’s browser.

Otherwise, if you are already running a Bokeh server app and it is not working, then we need a complete Minimal Reproducible Example that can actually be run for investigation.

Hi Bryan,

Thanks for your reply. Yes, I am running the app as a server app. At first I was running the .py file directly within the server command and missed the warning. But I changed my command to reference the directory and am having the same issues. Command:

user1$ bokeh serve --show /Users/user1/Desktop/foo/

Here is a minimal reproducible example:

import pandas as pd
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure, curdoc
from bokeh.events import MouseMove

items = pd.DataFrame(
    {'x_col': [1, 2, 3, 4],
     'y_col': [1, 2, 3, 4]})

source = ColumnDataSource(data=items)

plot = figure(x_axis_label='x_col',
              y_axis_label='y_col',
              sizing_mode='stretch_both')

# This returns empty list.
print('debug: test: printing the subscribed events list for the plot:')
print(plot.subscribed_events)

# This code executes on MouseMove, but if the window has been resized, the new width will
# will not have changed.
def debug():
    print()
    print('debug: test: the MouseMove event has just been triggered...The plot width is:')
    print(str(plot.width))
plot.on_event(MouseMove, debug)

# This code never runs after changing the size of the browser window, even though the
# visible width of the plot clearly changes.
def debug2(attr, old, new):
    print('printing plot width:')
    print(new)
plot.on_change('width', debug2)

plot.scatter(x='x_col', y='y_col', source=source, size=5)

# Add the Bokeh figure to the Bokeh curdoc object
curdoc().add_root(plot)

Oh, I see. Unfortunately, the plot width/height are one of a very few initialiation-only / “set once” properties. For very tedious reasons they can’t update after that. The docs are probably not as clear on this as they could be cc @Timo

Now that read-only properties exist, it’s possible that new read-only properties could be added to report on these values in a “live” way. It’s also possible that the upcoming Bokeh 3.0 work might improve the situation in some other way. cc @mateusz

For now I think your closest proxy is to look for changes to plot.inner_width etc. These are read-only properties that report the current size if the “inner” plot region (i.e. the area between the axes).

Thanks a ton, Bryan. .inner_width, etc… will suit my needs. Tested this approach and callbacks are of course working.