How to trigger callbacks on mouse click for tap tool?

I will eventually be developing an app using bokeh server which automatically updates a plot based on the current mouse pointer position upon clicking. To test the custom callback functionality I attempted to modify the tap tool callback example as follows:

from bokeh.models import ColumnDataSource, OpenURL, TapTool, CustomJS
from bokeh.plotting import figure, output_file, show

output_file(“openurl.html”)

source = ColumnDataSource(data=dict(
x=[1, 2, 3, 4, 5],
y=[2, 5, 8, 2, 7],
color=[“navy”, “orange”, “olive”, “firebrick”, “gold”]
))

callback = CustomJS(args=dict(source=source), code="""
// get data source from Callback args
var data = source.data;
var geometry = cb_data[‘geometries’];

    /// calculate x and y
    var x = geometry[0].x
    var y = geometry[0].y

    /// update data source
    data['x'].push(x);
    data['y'].push(y);
    data['color'].push('gold')
   
    // trigger update of data source
    source.trigger('change');
""")

taptool = TapTool(callback=callback)
p = figure(plot_width=400, plot_height=400,
tools=[taptool], title=“Click the Dots”)
p.circle(‘x’, ‘y’, color=‘color’, size=20, source=source)
show§

``

The intent was to draw a dot upon left clicking any part of the plot. However I found out that the callback only seems to be triggered when clicking on the actual dots. Is there a way to trigger it using the tap tool anywhere in the plot, or can it only be triggered by clicking on a glyph? If so, are there any alternative methods to achieve what I am looking for in bokeh?

Thanks,

Alex

Hi,

  You are correct. Tap only responds to tapping on a glyph. My

suggestion for a workaround would be to make an invisible
(alpha=0) or white rectangle that’s the same size as your plot
that you then draw on top of.

  We have had another request I believe for just being able to add

actions to a random mouse click, but it needs some serious thought
and I don’t think it’ll be a feature any time soon.

Hope that helps

Bird

···

On 10/7/16 5:29 PM, Alex Goodman wrote:

    I will eventually be developing an app using bokeh

server which automatically updates a plot based on the current
mouse pointer position upon clicking. To test the custom
callback functionality I attempted to modify the tap tool
callback example as follows:

from bokeh.models import ColumnDataSource, OpenURL, TapTool, CustomJS

          from bokeh.              plotting

import figure,
output_file, show

            output_file("openurl.html")





            source = ColumnDataSource(data=dict(

                x=[1, 2, 3, 4, 5],

                y=[2, 5, 8, 2, 7],

                color=["navy", "orange", "olive", "firebrick", "gold"]

                ))





            callback = CustomJS(args=dict(source=source), code="""

                    // get data source from Callback args

                    var data = source.data;

                    var geometry = cb_data['geometries'];







                    /// calculate x and y

                    var x = geometry[0].x

                    var y = geometry[0].y





                    /// update data source

                    data['x'].push(x);

                    data['y'].push(y);

                    data['color'].push('gold')

                   

                    // trigger update of data source

                    source.trigger('change');

                """)





            taptool = TapTool(callback=callback)

            p = figure(plot_width=400,
            plot_height=400,

                       tools=[taptool], title=                "Click

the Dots")

            p.circle('x', 'y', color='color', size=20, source=source)

            show(p)

``

      The intent was to draw a dot upon left clicking any part of

the plot. However I found out that the callback only seems to
be triggered when clicking on the actual dots. Is there a way
to trigger it using the tap tool anywhere in the plot, or can
it only be triggered by clicking on a glyph? If so, are there
any alternative methods to achieve what I am looking for in
bokeh?

Thanks,

Alex

  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 [email protected].

  To post to this group, send email to [email protected]

  To view this discussion on the web visit [https://groups.google.com/a/continuum.io/d/msgid/bokeh/45a6d369-2b3c-46cb-bfc4-baee8b9ca04a%40continuum.io](https://groups.google.com/a/continuum.io/d/msgid/bokeh/45a6d369-2b3c-46cb-bfc4-baee8b9ca04a%40continuum.io?utm_medium=email&utm_source=footer).

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


Sarah Bird
Developer, Bokeh

    [
      ![Continuum Analytics](http://docs.continuum.io/_static/img/ContinuumWordmark.png)
    ](http://continuum.io)

Clunky workaround, but does it also seem unnecessarily slow? Behaves weird when colliding with selection by adding glyph on top of previously created glyph.

from bokeh.models import CustomJS, ColumnDataSource, Range1d, Circle, TapTool, Rect
from bokeh.plotting import figure, show

source = ColumnDataSource({‘x’:, ‘y’:})

callback = CustomJS(args=dict(source=source), code="""
// get data source from Callback
var data = source.data;

    /// update data source with new circle
    data['x'].push(cb_data['geometries'][0].x);
    data['y'].push(cb_data['geometries'][0].y);

    // trigger update of data source
    source.trigger('change');
""")

tap = TapTool(callback=callback)

p = figure(plot_width=400,
plot_height=400,
tools=[tap],
title=“Tap to add circles”,
x_range=Range1d(start=0.0, end=1.0),
y_range=Range1d(start=0.0, end=1.0))

circle = Circle(
x=‘x’,
y=‘y’,
size=15,
fill_alpha=0.5,
fill_color=‘blue’)

rect = Rect(
x=0.5,
y=0.5,
width=400,
height=400,
line_alpha=0,
fill_alpha=0)

p.add_glyph(source, circle)
p.add_glyph(rect)
show§

``

The TapTool only calls the callback on new selections. If you want to do something on a generic tap event anywhere, regardless of whether there was any hit-testing event, you should use the new bokeh.events and js_on_change method:

    from bokeh.models import ColumnDataSource, CustomJS
    from bokeh.plotting import figure, output_file, show
    from bokeh.events import Tap

    source = ColumnDataSource(data=dict(
        x=[1, 2, 3, 4, 5],
        y=[2, 5, 8, 2, 7],
        color=["navy", "orange", "olive", "firebrick", "gold"]
        ))

    callback = CustomJS(args=dict(source=source), code="""
        // get data source from Callback args
        var data = source.data;

        /// update data source
        data['x'].push(cb_obj.x);
        data['y'].push(cb_obj.y);
        data['color'].push('gold')

        // trigger update of data source
        source.trigger('change');
    """)

    p = figure(plot_width=400, plot_height=400, tools="", title="Click the Dots")
    p.circle('x', 'y', color='color', size=20, source=source)

    p.js_on_event(Tap, callback)

    output_file("openurl.html")

    show(p)

Thanks,

Bryan

···

On Oct 7, 2016, at 19:29, Alex Goodman <[email protected]> wrote:

from bokeh.models import ColumnDataSource, OpenURL, TapTool, CustomJS
from bokeh.plotting import figure, output_file, show

output_file("openurl.html")

source = ColumnDataSource(data=dict(
    x=[1, 2, 3, 4, 5],
    y=[2, 5, 8, 2, 7],
    color=["navy", "orange", "olive", "firebrick", "gold"]
    ))

callback = CustomJS(args=dict(source=source), code="""
        // get data source from Callback args
        var data = source.data;
        var geometry = cb_data['geometries'];

        /// calculate x and y
        var x = geometry[0].x
        var y = geometry[0].y

        /// update data source
        data['x'].push(x);
        data['y'].push(y);
        data['color'].push('gold')
        
        // trigger update of data source
        source.trigger('change');
    """)

taptool = TapTool(callback=callback)
p = figure(plot_width=400, plot_height=400,
           tools=[taptool], title="Click the Dots")
p.circle('x', 'y', color='color', size=20, source=source)
show(p)