☰Read-only archive⌕
Looking for help with Bokeh?

This is a read-only archive. Ask questions and join the conversation on GitHub.

Visit Community Support on GitHub →
Archived discussions · Captured October 28, 2026

Plot range is reset when ColumnDataSource is updated and drawn using Ray

Community Support
nasedil.genio.1
nasedil.genio.1

I have an issue when trying to link several plots and at the same time to draw cross at mouse position (also on all the plots). The linking is done using js callback, because I need to link different types of ranges (like datetime with numerical, or two numericals with offset). The hover cross is done by drawing rays and updating ColumnDataSource in js callback.

What happens here is that when I try to pan/zoom the first plot before moving the second, it’s stuck, but once the second plot is moved it starts to work as it should. The linking only works fine, as well as only crosses, but not both at the same time.

I’ve made a small example to reproduce the issue:

from datetime import datetime, timedelta
import bokeh
from bokeh.layouts import column
from bokeh.plotting import figure, show
from bokeh.io import output_file
from bokeh.model import Model
from bokeh.models import HoverTool, Range, CustomJS, ColumnDataSource
output_file(‘axis-link-bug.html’)

first = figure(x_axis_type=‘datetime’)
second = figure(x_axis_type=‘datetime’)

first.line([datetime.now(), datetime.now()+timedelta(seconds=100000)], [0, 100])
second.line([datetime.now(), datetime.now()+timedelta(seconds=100000)], [0, 100])

first_source = ColumnDataSource(data=dict(x=[0], y=[0], angle=[90]))
second_source = ColumnDataSource(data=dict(x=[0], y=[0], angle=[90]))

first.ray(x=‘x’, y=‘y’, length=0, angle=‘angle’, angle_units=“deg”,
color=‘grey’, line_width=1, source=first_source)

second.ray(x=‘x’, y=‘y’, length=0, angle=‘angle’, angle_units=“deg”,
color=‘grey’, line_width=1, source=second_source)

hover_js = ‘’’
var geometry = cb_data[‘geometry’];
first_source.data[‘x’][0] = geometry.x;
second_source.data[‘x’][0] = geometry.x;
first_source.change.emit();
second_source.change.emit();
‘’’

hover = HoverTool(tooltips=None,
callback=CustomJS(code=hover_js, args={‘first_source’: first_source,
‘second_source’: second_source}))
first.add_tools(hover)
second.add_tools(hover)

class RangeLinker(Model):
ranges = List(Instance(Range))
implementation = “”"
import * as p from “core/properties”
import {Model} from “model”

export class RangeLinker extends Model

type: ‘RangeLinker’

@define {
ranges: [ p.Array, ]
}

handler: (rangeIndex) =>
return (() => @sync(rangeIndex))

initialize: (attrs, options) =>
super(attrs, options)

for r, i in @ranges
  @connect(r.change, @handler(i))

@sync(0)

sync: (rangeIndex) =>
range = @ranges[rangeIndex]
for r, i in @ranges
if (i != rangeIndex)
r.attributes[‘start’] = range.start
r.attributes[‘end’] = range.end
if (r.plots? and r.plots.length > 0)
r.plots[0].change.emit()
“”"

time_link = RangeLinker(ranges=[first.x_range, second.x_range])
numeric_link = RangeLinker(ranges=[first.y_range, second.y_range])

first.x_range.callback = CustomJS(args=dict(time_link=time_link))
first.y_range.callback = CustomJS(args=dict(numeric_link=numeric_link))

show(column(first, second))

``

⌁   Link to post
Bryan
Bryan

Hi,

By default the defat DataRange objects auto-scale over all available glyph renderers. Since you are using Raw more like an annotation, this is probably not idea. You can configure the ranges .renderers property to only have the lines, and not the rays, e.g. similar to:

  r = p.line(...)
        p.circle(...)
  plot.x_range.renderers = [r] # only autorange the line

Thanks,

Bryan

···

On Jun 7, 2018, at 01:17, nasedil.genio.1@gmail.com wrote:

I have an issue when trying to link several plots and at the same time to draw cross at mouse position (also on all the plots). The linking is done using js callback, because I need to link different types of ranges (like datetime with numerical, or two numericals with offset). The hover cross is done by drawing rays and updating ColumnDataSource in js callback.

What happens here is that when I try to pan/zoom the first plot before moving the second, it's stuck, but once the second plot is moved it starts to work as it should. The linking only works fine, as well as only crosses, but not both at the same time.

I've made a small example to reproduce the issue:
from datetime import datetime, timedelta
import bokeh
from bokeh.layouts import column
from bokeh.plotting import figure, show
from bokeh.io import output_file
from bokeh.model import Model
from bokeh.models import HoverTool, Range, CustomJS, ColumnDataSource
output_file('axis-link-bug.html')

first = figure(x_axis_type='datetime')
second = figure(x_axis_type='datetime')

first.line([datetime.now(), datetime.now()+timedelta(seconds=100000)], [0, 100])
second.line([datetime.now(), datetime.now()+timedelta(seconds=100000)], [0, 100])

first_source = ColumnDataSource(data=dict(x=[0], y=[0], angle=[90]))
second_source = ColumnDataSource(data=dict(x=[0], y=[0], angle=[90]))

first.ray(x='x', y='y', length=0, angle='angle', angle_units="deg",
          color='grey', line_width=1, source=first_source)

second.ray(x='x', y='y', length=0, angle='angle', angle_units="deg",
           color='grey', line_width=1, source=second_source)

hover_js = '''
var geometry = cb_data['geometry'];
first_source.data['x'][0] = geometry.x;
second_source.data['x'][0] = geometry.x;
first_source.change.emit();
second_source.change.emit();
'''

hover = HoverTool(tooltips=None,
                  callback=CustomJS(code=hover_js, args={'first_source': first_source,
                                                         'second_source': second_source}))
first.add_tools(hover)
second.add_tools(hover)

class RangeLinker(Model):
    ranges = List(Instance(Range))
    __implementation__ = """
import * as p from "core/properties"
import {Model} from "model"

export class RangeLinker extends Model

  type: 'RangeLinker'

  @define {
    ranges: [ p.Array, ]
  }

  handler: (rangeIndex) =>
    return (() => @sync(rangeIndex))

  initialize: (attrs, options) =>
    super(attrs, options)

    for r, i in @ranges
      @connect(r.change, @handler(i))

    @sync(0)

  sync: (rangeIndex) =>
    range = @ranges[rangeIndex]
    for r, i in @ranges
      if (i != rangeIndex)
        r.attributes['start'] = range.start
        r.attributes['end'] = range.end
        if (r.plots? and r.plots.length > 0)
          r.plots[0].change.emit()
"""

time_link = RangeLinker(ranges=[first.x_range, second.x_range])
numeric_link = RangeLinker(ranges=[first.y_range, second.y_range])

first.x_range.callback = CustomJS(args=dict(time_link=time_link))
first.y_range.callback = CustomJS(args=dict(numeric_link=numeric_link))

show(column(first, second))

--
You received this message because you are subscribed to the Google Groups "Bokeh Discussion - Public" group.
To unsubscribe from this group and stop receiving emails from it, send an email to bokeh+unsubscribe@continuum.io.
To post to this group, send email to bokeh@continuum.io.
To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/b70bc09f-4ce6-4eac-8589-0f40c97ae9f2%40continuum.io\.
For more options, visit https://groups.google.com/a/continuum.io/d/optout\.

⌁   Link to post
nasedil.genio.1
nasedil.genio.1

Hi,

I’ve tried both:

first.x_range.renderers =
second.x_range.renderers =

``

and

first.x_range.renderers = [first_line]
second.x_range.renderers = [second_line]

``

but none of these helped.

···

пятница, 15 июня 2018 г., 18:52:40 UTC+3 пользователь Bryan Van de ven написал:

Hi,

By default the defat DataRange objects auto-scale over all available glyph renderers. Since you are using Raw more like an annotation, this is probably not idea. You can configure the ranges .renderers property to only have the lines, and not the rays, e.g. similar to:

    r = p.line(...)

    p.circle(...)

    plot.x_range.renderers = [r] # only autorange the line

Thanks,

Bryan

On Jun 7, 2018, at 01:17, nasedil...@gmail.com wrote:

I have an issue when trying to link several plots and at the same time to draw cross at mouse position (also on all the plots). The linking is done using js callback, because I need to link different types of ranges (like datetime with numerical, or two numericals with offset). The hover cross is done by drawing rays and updating ColumnDataSource in js callback.

What happens here is that when I try to pan/zoom the first plot before moving the second, it’s stuck, but once the second plot is moved it starts to work as it should. The linking only works fine, as well as only crosses, but not both at the same time.

I’ve made a small example to reproduce the issue:

from datetime import datetime, timedelta

import bokeh

from bokeh.layouts import column

from bokeh.plotting import figure, show

from bokeh.io import output_file

from bokeh.model import Model

from bokeh.models import HoverTool, Range, CustomJS, ColumnDataSource

output_file(‘axis-link-bug.html’)

first = figure(x_axis_type=‘datetime’)

second = figure(x_axis_type=‘datetime’)

first.line([datetime.now(), datetime.now()+timedelta(seconds=100000)], [0, 100])

second.line([datetime.now(), datetime.now()+timedelta(seconds=100000)], [0, 100])

first_source = ColumnDataSource(data=dict(x=[0], y=[0], angle=[90]))

second_source = ColumnDataSource(data=dict(x=[0], y=[0], angle=[90]))

first.ray(x=‘x’, y=‘y’, length=0, angle=‘angle’, angle_units=“deg”,

      color='grey', line_width=1, source=first_source)

second.ray(x=‘x’, y=‘y’, length=0, angle=‘angle’, angle_units=“deg”,

       color='grey', line_width=1, source=second_source)

hover_js = ‘’’

var geometry = cb_data[‘geometry’];

first_source.data[‘x’][0] = geometry.x;

second_source.data[‘x’][0] = geometry.x;

first_source.change.emit();

second_source.change.emit();

‘’’

hover = HoverTool(tooltips=None,

              callback=CustomJS(code=hover_js, args={'first_source': first_source,
                                                     'second_source': second_source}))

first.add_tools(hover)

second.add_tools(hover)

class RangeLinker(Model):

ranges = List(Instance(Range))
__implementation__ = """

import * as p from “core/properties”

import {Model} from “model”

export class RangeLinker extends Model

type: ‘RangeLinker’

@define {

ranges:           [ p.Array, [] ]

}

handler: (rangeIndex) =>

return (() => @sync(rangeIndex))

initialize: (attrs, options) =>

super(attrs, options)
for r, i in @ranges
  @connect(r.change, @handler(i))
@sync(0)

sync: (rangeIndex) =>

range = @ranges[rangeIndex]
for r, i in @ranges
  if (i != rangeIndex)
    r.attributes['start'] = range.start
    r.attributes['end'] = range.end
    if (r.plots? and r.plots.length > 0)
      r.plots[0].change.emit()

“”"

time_link = RangeLinker(ranges=[first.x_range, second.x_range])

numeric_link = RangeLinker(ranges=[first.y_range, second.y_range])

first.x_range.callback = CustomJS(args=dict(time_link=time_link))

first.y_range.callback = CustomJS(args=dict(numeric_link=numeric_link))

show(column(first, second))

–
You received this message because you are subscribed to the Google Groups “Bokeh Discussion - Public” group.

To unsubscribe from this group and stop receiving emails from it, send an email to bokeh+un...@continuum.io.

To post to this group, send email to bo...@continuum.io.

To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/b70bc09f-4ce6-4eac-8589-0f40c97ae9f2%40continuum.io.

For more options, visit https://groups.google.com/a/continuum.io/d/optout.

⌁   Link to post

End of archived discussion · Replies are disabled