# How can I correctly obtain the updated x\_range after dragging in Jupyter?

**URL:** <https://discourse.bokeh.org/t/how-can-i-correctly-obtain-the-updated-x-range-after-dragging-in-jupyter/11107>\
**Category:** Community Support\
**Tags:** jupyter, bokehjs, bokeh\
**Created:** [December 13, 2023, 5:41pm UTC](https://discourse.bokeh.org/t/how-can-i-correctly-obtain-the-updated-x-range-after-dragging-in-jupyter/11107 "2023-12-13T17:41:25Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![haojiahuoX](https://discourse.bokeh.org/user_avatar/discourse.bokeh.org/haojiahuox/32/6459_2.png) [@haojiahuoX](https://discourse.bokeh.org/u/haojiahuoX)\
**Post date:** [December 13, 2023, 5:41pm UTC](https://discourse.bokeh.org/t/how-can-i-correctly-obtain-the-updated-x-range-after-dragging-in-jupyter/11107/1 "2023-12-13T17:41:26Z")

</div>

In Jupyter using Bokeh, I need the x\_range of `p` . However, after dragging, the x\_range does not update and remains at its initial value. Why is this happening? Below is my code and a screenshot. I used a simple example to demonstrate the issue. After dragging, the print in the subsequent cell still shows the initial values.

```auto
import numpy as np

from bokeh.layouts import column
from bokeh.models import ColumnDataSource, RangeTool
from bokeh.plotting import figure, show

x = np.linspace(0, 10, 500)
y = np.sin(x)

source = ColumnDataSource(data=dict(x=x, y=y))

p = figure(height=300, width=800, tools="xpan", toolbar_location=None,
           x_axis_type="datetime", x_axis_location="above",
           background_fill_color="#efefef", x_range=(x[15], x[25]))

p.line('x', 'y', source=source)
p.yaxis.axis_label = 'Price'

select = figure(title="Drag the middle and edges of the selection box to change the range above",
                height=130, width=800, y_range=p.y_range,
                x_axis_type="datetime", y_axis_type=None,
                tools="", toolbar_location=None, background_fill_color="#efefef")

range_tool = RangeTool(x_range=p.x_range)
range_tool.overlay.fill_color = "navy"
range_tool.overlay.fill_alpha = 0.2

select.line('x', 'y', source=source)
select.ygrid.grid_line_color = None
select.add_tools(range_tool)

show(column(p, select))

```

```auto
print("Initial x_range:", range_tool.x_range.start, p.x_range.end)

```

 ![1702447967401](https://discourse-uploads.bokeh.org/original/2X/e/eb234b623497f62778c38d12e6827db8acd586f7.png)

---

<div class="post-metadata">

**Author:** ![Bryan](https://discourse.bokeh.org/user_avatar/discourse.bokeh.org/bryan/32/3625_2.png) [@Bryan](https://discourse.bokeh.org/u/Bryan)\
**Post date:** [December 13, 2023, 5:50pm UTC](https://discourse.bokeh.org/t/how-can-i-correctly-obtain-the-updated-x-range-after-dragging-in-jupyter/11107/2 "2023-12-13T17:50:15Z")

</div>

> [@haojiahuoX](#):
>
> However, after dragging, the x\_range does not update and remains at its initial value. Why is this happening?

In your current code, the BokehJS content is embedded in the output cell, and there is no connection between the BokehJS side of things and the Python kernel.

If you want full bi-directional updates between Javascript and the Python kernel, you would need to embed a [Bokeh server app](https://docs.bokeh.org/en/latest/docs/user_guide/server/server_introduction.html#ug-server-introduction) (making that synchronization happen automatically is the purpose of the Bokeh server). See e.g. [examples/server/api/notebook\_embed.ipynb](https://github.com/bokeh/bokeh/blob/HEAD/examples/server/api/notebook_embed.ipynb)

---

<div class="post-metadata">

**Author:** ![Icoti](https://discourse.bokeh.org/user_avatar/discourse.bokeh.org/icoti/32/5856_2.png) [@Icoti](https://discourse.bokeh.org/u/Icoti)\
**Post date:** [December 13, 2023, 8:55pm UTC](https://discourse.bokeh.org/t/how-can-i-correctly-obtain-the-updated-x-range-after-dragging-in-jupyter/11107/3 "2023-12-13T20:55:36Z")

</div>

@haojiahuoX, I don’t exactly know what you want to do with the `x_range` of `p`, but as [Bryan](https://discourse.bokeh.org/u/Bryan) said, you have to build a server to access it in real time.

You could rewrite your code as follows:

```auto
import numpy as np

from bokeh.io import curdoc
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, RangeTool, Paragraph
from bokeh.plotting import figure

x = np.linspace(0, 10, 500)
y = np.sin(x)

source = ColumnDataSource(data=dict(x=x, y=y))

p = figure(height=300, width=800, tools="xpan", toolbar_location=None,
           x_axis_type="datetime", x_axis_location="above",
           background_fill_color="#efefef", x_range=(x[15], x[25]))

p.line('x', 'y', source=source)
p.yaxis.axis_label = 'Price'

select = figure(title="Drag the middle and edges of the selection box to change the range above",
                height=130, width=800, y_range=p.y_range,
                x_axis_type="datetime", y_axis_type=None,
                tools="", toolbar_location=None, background_fill_color="#efefef")

range_tool = RangeTool(x_range=p.x_range)
range_tool.overlay.fill_color = "navy"
range_tool.overlay.fill_alpha = 0.2

select.line('x', 'y', source=source)
select.ygrid.grid_line_color = None
select.add_tools(range_tool)

start_par = Paragraph(text=f"range_tool.x_range.start: {range_tool.x_range.start}, p.x_range.start: {p.x_range.start}")
end_par = Paragraph(text=f"range_tool.x_range.end: {range_tool.x_range.end}, p.x_range.end: {p.x_range.end}")

def callback_start(attr, old, new):
    start_par.text = f"range_tool.x_range.start: {range_tool.x_range.start}, p.x_range.start: {p.x_range.start}"

def callback_end(attr, old, new):
    end_par.text = f"range_tool.x_range.end: {range_tool.x_range.end}, p.x_range.end: {p.x_range.end}"

range_tool.x_range.on_change("start", callback_start)
range_tool.x_range.on_change("end", callback_end)

layout = column(p, select, start_par, end_par)

curdoc().add_root(layout)

```

and run it in a Python powershell with `bokeh serve --show file_name.py`.

To run it in JupyterLab, modify the `bokeh.io` import to:  
`from bokeh.io import curdoc, show, output_notebook`  
After the imports, add `output_notebook()` in a free line,  
at the end, replace `curdoc().add_root(layout)` by:

```auto
def my_app(doc):
    doc.add_root(layout)
show(my_app)

```

In the callback functions, you then have access to the real time value of start and end of the x-axis of p.

---

<div class="post-metadata">

**Author:** ![Bryan](https://discourse.bokeh.org/user_avatar/discourse.bokeh.org/bryan/32/3625_2.png) [@Bryan](https://discourse.bokeh.org/u/Bryan)\
**Post date:** [December 13, 2023, 10:46pm UTC](https://discourse.bokeh.org/t/how-can-i-correctly-obtain-the-updated-x-range-after-dragging-in-jupyter/11107/4 "2023-12-13T22:46:27Z")

</div>

Just a note: in the notebook you really want to have new Bokeh objects created for every (re-)evaluation of the cell with `show`. Each new invocations results in a new session, and sessions are not designed to share objects. Typically all the “app” code is encapsulated entirely in a function like `my_app`, not just the `add_root` part. This is how things are demonstrated in the example I linked.

Alternatively, a small class can be defined for the app, which may be preferable for code organization. There was an example of that discussed here:

> [@Live plot with interactivity](https://discourse.bokeh.org/t/live-plot-with-interactivity/10890/6):
>
> Yes, here is an updated version from that example: class bkapp: df = sea\_surface\_temperature.copy() def \_\_init\_\_(self): self.\_theme\_json = """ attrs: figure: background\_fill\_color: "#DDDDDD" outline\_line\_color: white toolbar\_location: above height: 500 width: 800 Grid: grid\_line\_dash: [6, 4] grid…

---

<div class="post-metadata">

**Author:** ![haojiahuoX](https://discourse.bokeh.org/user_avatar/discourse.bokeh.org/haojiahuox/32/6459_2.png) [@haojiahuoX](https://discourse.bokeh.org/u/haojiahuoX)\
**Post date:** [December 14, 2023, 4:05am UTC](https://discourse.bokeh.org/t/how-can-i-correctly-obtain-the-updated-x-range-after-dragging-in-jupyter/11107/5 "2023-12-14T04:05:11Z")

</div>

Thank you for your response. In order to avoid server calls, I initially used ipywidgets to interact with the Python kernel, and I needed to obtain the x\_range in the process to invoke a computation module and display another image. However, it seems that the approach of using ipywidgets may not be correct. I will try building a server to address this.

---

<div class="post-metadata">

**Author:** ![haojiahuoX](https://discourse.bokeh.org/user_avatar/discourse.bokeh.org/haojiahuox/32/6459_2.png) [@haojiahuoX](https://discourse.bokeh.org/u/haojiahuoX)\
**Post date:** [December 14, 2023, 4:06am UTC](https://discourse.bokeh.org/t/how-can-i-correctly-obtain-the-updated-x-range-after-dragging-in-jupyter/11107/6 "2023-12-14T04:06:47Z")

</div>

Thank you for your response. This example has been very helpful for me. I will try to encapsulate an app to address my issue.

---

<div class="post-metadata">

**Author:** ![Bryan](https://discourse.bokeh.org/user_avatar/discourse.bokeh.org/bryan/32/3625_2.png) [@Bryan](https://discourse.bokeh.org/u/Bryan)\
**Post date:** [December 14, 2023, 4:13am UTC](https://discourse.bokeh.org/t/how-can-i-correctly-obtain-the-updated-x-range-after-dragging-in-jupyter/11107/7 "2023-12-14T04:13:29Z")

</div>

@haojiahuoX There is some integration with Bokeh and ipywidgets but I am not really familiar with it (or its current state) cc @mateusz @Philipp_Rudiger

---

<div class="post-metadata">

**Author:** ![system](https://discourse-uploads.bokeh.org/original/2X/1/14eb24f9e10e4e9b78e3f10f66c04ffa96446e5a.svg) [@system](https://discourse.bokeh.org/u/system)\
**Post date:** [March 13, 2024, 4:13am UTC](https://discourse.bokeh.org/t/how-can-i-correctly-obtain-the-updated-x-range-after-dragging-in-jupyter/11107/8 "2024-03-13T04:13:33Z")

</div>

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.
