New Custom Tool for drawing rays

Hi all,
i am trying to implement a custom tool for drawing rays on tap event. I have implemented it via js_on_event and works fine but i need it as a tool since i want to give the ability of drawing rays, segments or lines and hence multiple custom tools are needed.

This code gets the plot displayed in the browser but when tapping nothing happens. I am struggling to understand what i am missing.

Would you mind to help me out?

Many thanks andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.models import ColumnDataSource, Tool

from bokeh.models.tools import Tap

from bokeh.plotting import figure

output_file(‘tool.html’)

JS_CODE = “”"

import * as p from “core/properties”

import {TapTool, TapToolView} from “models/tools/gestures/tap_tool”

export class TapBBToolView extends TapToolView

this is executed on subsequent mouse/touch moves

_pan: (e) ->

frame = @plot_model.frame

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

source.data.x.push(x)

source.data.y.push(y)

source.change.emit()

export class TapBBTool extends TapTool

default_view: TapToolView

type: “TapTool”

tool_name: “H Line”

icon: “bk-tool-icon-arrow-right”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class TapBBTool(Tool):

implementation = JS_CODE

source = Instance(ColumnDataSource)

source_HorizontalRay = ColumnDataSource(data=dict(x=, y=, angle=))

plot = figure(x_range=(0,10), y_range=(0,10), tools=[TapBBTool(source=source_HorizontalRay)])

plot.title.text =“Tap to draw on the plot”

plot.ray(x=‘x’, y=‘y’, angle = 0, length=0, line_dash=“solid”, line_width=2, source=source_HorizontalRay)

show(plot)

Hi Andrea,

You almost got it. Here’s the working version. The things that needed to be fixed: wrong function name (must be “_tap” instead of “_pan”), wrong “default_view” (must be TapBBToolView), extra data field for the data source (you’d get an error about inconsistent column lengths).

I also realized that TapTool is not needed because all it can provide you if just a single field “event_type”, so I used a more general GestureTool instead.

import bokeh.util.compiler

bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from bokeh.io import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure

output_file('tool.html')
curstate().file['resources'].minified = False

JS_CODE = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"

export class TapBBToolView extends GestureToolView

  # this is executed on subsequent mouse/touch moves
  _tap: (e) ->
    frame = @plot_model.frame

    {sx, sy} = e.bokeh
    if not frame.bbox.contains(sx, sy)
      return null

    x = frame.xscales['default'].invert(sx)
    y = frame.yscales['default'].invert(sy)
    source = @model.source
    source.data.x.push(x)
    source.data.y.push(y)
    source.change.emit()

export class TapBBTool extends GestureTool
  default_view: TapBBToolView
  type: "TapTool"
  event_type: "tap"
  tool_name: "H Line"
  icon: "bk-tool-icon-arrow-right"
  default_order: 12

  @define { source: [ p.Instance ] }
"""

class TapBBTool(Tool):
    __implementation__ = JS_CODE
    source = Instance(ColumnDataSource)

source_HorizontalRay = ColumnDataSource(data=dict(x=[], y=[]))

plot = figure(x_range=(0, 10), y_range=(0, 10),
              tools=[TapBBTool(source=source_HorizontalRay)])
plot.title.text = "Tap to draw on the plot"
plot.ray(x='x', y='y', angle=0, length=0, line_dash="solid", line_width=2,
         source=source_HorizontalRay)

show(plot)

Regards,

Eugene

···

On Thursday, November 30, 2017 at 6:58:10 PM UTC+7, Andrea Tagliabue wrote:

Hi all,
i am trying to implement a custom tool for drawing rays on tap event. I have implemented it via js_on_event and works fine but i need it as a tool since i want to give the ability of drawing rays, segments or lines and hence multiple custom tools are needed.

This code gets the plot displayed in the browser but when tapping nothing happens. I am struggling to understand what i am missing.

Would you mind to help me out?

Many thanks andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.models import ColumnDataSource, Tool

from bokeh.models.tools import Tap

from bokeh.plotting import figure

output_file(‘tool.html’)

JS_CODE = “”"

import * as p from “core/properties”

import {TapTool, TapToolView} from “models/tools/gestures/tap_tool”

export class TapBBToolView extends TapToolView

this is executed on subsequent mouse/touch moves

_pan: (e) ->

frame = @plot_model.frame

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

source.data.x.push(x)

source.data.y.push(y)

source.change.emit()

export class TapBBTool extends TapTool

default_view: TapToolView

type: “TapTool”

tool_name: “H Line”

icon: “bk-tool-icon-arrow-right”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class TapBBTool(Tool):

implementation = JS_CODE

source = Instance(ColumnDataSource)

source_HorizontalRay = ColumnDataSource(data=dict(x=, y=, angle=))

plot = figure(x_range=(0,10), y_range=(0,10), tools=[TapBBTool(source=source_HorizontalRay)])

plot.title.text =“Tap to draw on the plot”

plot.ray(x=‘x’, y=‘y’, angle = 0, length=0, line_dash=“solid”, line_width=2, source=source_HorizontalRay)

show(plot)

Wow really thanks for the quick answer. In order to avoid me bothering you for other issues, is there any doc regarding these custom tools? They looks to me pretty complex in terms of multiple options.
Many thanks again
Andrea

···

On 30 November 2017 at 13:18, Eugene Pakhomov [email protected] wrote:

Hi Andrea,

You almost got it. Here’s the working version. The things that needed to be fixed: wrong function name (must be “_tap” instead of “_pan”), wrong “default_view” (must be TapBBToolView), extra data field for the data source (you’d get an error about inconsistent column lengths).

I also realized that TapTool is not needed because all it can provide you if just a single field “event_type”, so I used a more general GestureTool instead.

import bokeh.util.compiler

bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure

output_file('tool.html')
curstate().file['resources'].minified = False

JS_CODE = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"

export class TapBBToolView extends GestureToolView

  # this is executed on subsequent mouse/touch moves
  _tap: (e) ->
    frame = @plot_model.frame

    {sx, sy} = e.bokeh
    if not frame.bbox.contains(sx, sy)
      return null

    x = frame.xscales['default'].invert(sx)
    y = frame.yscales['default'].invert(sy)
    source = @model.source
    source.data.x.push(x)
    source.data.y.push(y)
    source.change.emit()


export class TapBBTool extends GestureTool
  default_view: TapBBToolView
  type: "TapTool"
  event_type: "tap"
  tool_name: "H Line"
  icon: "bk-tool-icon-arrow-right"
  default_order: 12

  @define { source: [ p.Instance ] }
"""


class TapBBTool(Tool):
    __implementation__ = JS_CODE
    source = Instance(ColumnDataSource)


source_HorizontalRay = ColumnDataSource(data=dict(x=[], y=[]))

plot = figure(x_range=(0, 10), y_range=(0, 10),
              tools=[TapBBTool(source=source_HorizontalRay)])
plot.title.text = "Tap to draw on the plot"
plot.ray(x='x', y='y', angle=0, length=0, line_dash="solid", line_width=2,
         source=source_HorizontalRay)

show(plot)

Regards,

Eugene

On Thursday, November 30, 2017 at 6:58:10 PM UTC+7, Andrea Tagliabue wrote:

Hi all,
i am trying to implement a custom tool for drawing rays on tap event. I have implemented it via js_on_event and works fine but i need it as a tool since i want to give the ability of drawing rays, segments or lines and hence multiple custom tools are needed.

This code gets the plot displayed in the browser but when tapping nothing happens. I am struggling to understand what i am missing.

Would you mind to help me out?

Many thanks andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.models import ColumnDataSource, Tool

from bokeh.models.tools import Tap

from bokeh.plotting import figure

output_file(‘tool.html’)

JS_CODE = “”"

import * as p from “core/properties”

import {TapTool, TapToolView} from “models/tools/gestures/tap_tool”

export class TapBBToolView extends TapToolView

this is executed on subsequent mouse/touch moves

_pan: (e) ->

frame = @plot_model.frame

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

source.data.x.push(x)

source.data.y.push(y)

source.change.emit()

export class TapBBTool extends TapTool

default_view: TapToolView

type: “TapTool”

tool_name: “H Line”

icon: “bk-tool-icon-arrow-right”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class TapBBTool(Tool):

implementation = JS_CODE

source = Instance(ColumnDataSource)

source_HorizontalRay = ColumnDataSource(data=dict(x=, y=, angle=))

plot = figure(x_range=(0,10), y_range=(0,10), tools=[TapBBTool(source=source_HorizontalRay)])

plot.title.text =“Tap to draw on the plot”

plot.ray(x=‘x’, y=‘y’, angle = 0, length=0, line_dash=“solid”, line_width=2, source=source_HorizontalRay)

show(plot)

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/ac17d835-5909-4ac2-a552-9b94d3dba436%40continuum.io.

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

Unfortunately, I don’t think so. At least, I couldn’t find any.
Usually, I just study the source code of the existing tools.

  • Eugene
···

On Thursday, November 30, 2017 at 7:24:57 PM UTC+7, Andrea Tagliabue wrote:

Wow really thanks for the quick answer. In order to avoid me bothering you for other issues, is there any doc regarding these custom tools? They looks to me pretty complex in terms of multiple options.
Many thanks again
Andrea

On 30 November 2017 at 13:18, Eugene Pakhomov [email protected] wrote:

Hi Andrea,

You almost got it. Here’s the working version. The things that needed to be fixed: wrong function name (must be “_tap” instead of “_pan”), wrong “default_view” (must be TapBBToolView), extra data field for the data source (you’d get an error about inconsistent column lengths).

I also realized that TapTool is not needed because all it can provide you if just a single field “event_type”, so I used a more general GestureTool instead.

import bokeh.util.compiler

bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure

output_file('tool.html')
curstate().file['resources'].minified = False

JS_CODE = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"

export class TapBBToolView extends GestureToolView

  # this is executed on subsequent mouse/touch moves
  _tap: (e) ->
    frame = @plot_model.frame

    {sx, sy} = e.bokeh
    if not frame.bbox.contains(sx, sy)
      return null

    x = frame.xscales['default'].invert(sx)
    y = frame.yscales['default'].invert(sy)
    source = @model.source
    source.data.x.push(x)
    source.data.y.push(y)
    source.change.emit()


export class TapBBTool extends GestureTool
  default_view: TapBBToolView
  type: "TapTool"
  event_type: "tap"
  tool_name: "H Line"
  icon: "bk-tool-icon-arrow-right"
  default_order: 12

  @define { source: [ p.Instance ] }
"""


class TapBBTool(Tool):
    __implementation__ = JS_CODE
    source = Instance(ColumnDataSource)


source_HorizontalRay = ColumnDataSource(data=dict(x=[], y=[]))

plot = figure(x_range=(0, 10), y_range=(0, 10),
              tools=[TapBBTool(source=source_HorizontalRay)])
plot.title.text = "Tap to draw on the plot"
plot.ray(x='x', y='y', angle=0, length=0, line_dash="solid", line_width=2,
         source=source_HorizontalRay)

show(plot)

Regards,

Eugene

On Thursday, November 30, 2017 at 6:58:10 PM UTC+7, Andrea Tagliabue wrote:

Hi all,
i am trying to implement a custom tool for drawing rays on tap event. I have implemented it via js_on_event and works fine but i need it as a tool since i want to give the ability of drawing rays, segments or lines and hence multiple custom tools are needed.

This code gets the plot displayed in the browser but when tapping nothing happens. I am struggling to understand what i am missing.

Would you mind to help me out?

Many thanks andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.models import ColumnDataSource, Tool

from bokeh.models.tools import Tap

from bokeh.plotting import figure

output_file(‘tool.html’)

JS_CODE = “”"

import * as p from “core/properties”

import {TapTool, TapToolView} from “models/tools/gestures/tap_tool”

export class TapBBToolView extends TapToolView

this is executed on subsequent mouse/touch moves

_pan: (e) ->

frame = @plot_model.frame

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

source.data.x.push(x)

source.data.y.push(y)

source.change.emit()

export class TapBBTool extends TapTool

default_view: TapToolView

type: “TapTool”

tool_name: “H Line”

icon: “bk-tool-icon-arrow-right”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class TapBBTool(Tool):

implementation = JS_CODE

source = Instance(ColumnDataSource)

source_HorizontalRay = ColumnDataSource(data=dict(x=, y=, angle=))

plot = figure(x_range=(0,10), y_range=(0,10), tools=[TapBBTool(source=source_HorizontalRay)])

plot.title.text =“Tap to draw on the plot”

plot.ray(x=‘x’, y=‘y’, angle = 0, length=0, line_dash=“solid”, line_width=2, source=source_HorizontalRay)

show(plot)

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/ac17d835-5909-4ac2-a552-9b94d3dba436%40continuum.io.

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

yeah me too but most of the time i can’t figure out what i am doing wrong :slight_smile:

···

On 30 November 2017 at 13:42, Eugene Pakhomov [email protected] wrote:

Unfortunately, I don’t think so. At least, I couldn’t find any.
Usually, I just study the source code of the existing tools.

  • Eugene

On Thursday, November 30, 2017 at 7:24:57 PM UTC+7, Andrea Tagliabue wrote:

Wow really thanks for the quick answer. In order to avoid me bothering you for other issues, is there any doc regarding these custom tools? They looks to me pretty complex in terms of multiple options.
Many thanks again
Andrea

On 30 November 2017 at 13:18, Eugene Pakhomov [email protected] wrote:

Hi Andrea,

You almost got it. Here’s the working version. The things that needed to be fixed: wrong function name (must be “_tap” instead of “_pan”), wrong “default_view” (must be TapBBToolView), extra data field for the data source (you’d get an error about inconsistent column lengths).

I also realized that TapTool is not needed because all it can provide you if just a single field “event_type”, so I used a more general GestureTool instead.

import bokeh.util.compiler

bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure

output_file('tool.html')
curstate().file['resources'].minified = False

JS_CODE = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"

export class TapBBToolView extends GestureToolView

  # this is executed on subsequent mouse/touch moves
  _tap: (e) ->
    frame = @plot_model.frame

    {sx, sy} = e.bokeh
    if not frame.bbox.contains(sx, sy)
      return null

    x = frame.xscales['default'].invert(sx)
    y = frame.yscales['default'].invert(sy)
    source = @model.source
    source.data.x.push(x)
    source.data.y.push(y)
    source.change.emit()


export class TapBBTool extends GestureTool
  default_view: TapBBToolView
  type: "TapTool"
  event_type: "tap"
  tool_name: "H Line"
  icon: "bk-tool-icon-arrow-right"
  default_order: 12

  @define { source: [ p.Instance ] }
"""


class TapBBTool(Tool):
    __implementation__ = JS_CODE
    source = Instance(ColumnDataSource)


source_HorizontalRay = ColumnDataSource(data=dict(x=[], y=[]))

plot = figure(x_range=(0, 10), y_range=(0, 10),
              tools=[TapBBTool(source=source_HorizontalRay)])
plot.title.text = "Tap to draw on the plot"
plot.ray(x='x', y='y', angle=0, length=0, line_dash="solid", line_width=2,
         source=source_HorizontalRay)

show(plot)

Regards,

Eugene

On Thursday, November 30, 2017 at 6:58:10 PM UTC+7, Andrea Tagliabue wrote:

Hi all,
i am trying to implement a custom tool for drawing rays on tap event. I have implemented it via js_on_event and works fine but i need it as a tool since i want to give the ability of drawing rays, segments or lines and hence multiple custom tools are needed.

This code gets the plot displayed in the browser but when tapping nothing happens. I am struggling to understand what i am missing.

Would you mind to help me out?

Many thanks andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.models import ColumnDataSource, Tool

from bokeh.models.tools import Tap

from bokeh.plotting import figure

output_file(‘tool.html’)

JS_CODE = “”"

import * as p from “core/properties”

import {TapTool, TapToolView} from “models/tools/gestures/tap_tool”

export class TapBBToolView extends TapToolView

this is executed on subsequent mouse/touch moves

_pan: (e) ->

frame = @plot_model.frame

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

source.data.x.push(x)

source.data.y.push(y)

source.change.emit()

export class TapBBTool extends TapTool

default_view: TapToolView

type: “TapTool”

tool_name: “H Line”

icon: “bk-tool-icon-arrow-right”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class TapBBTool(Tool):

implementation = JS_CODE

source = Instance(ColumnDataSource)

source_HorizontalRay = ColumnDataSource(data=dict(x=, y=, angle=))

plot = figure(x_range=(0,10), y_range=(0,10), tools=[TapBBTool(source=source_HorizontalRay)])

plot.title.text =“Tap to draw on the plot”

plot.ray(x=‘x’, y=‘y’, angle = 0, length=0, line_dash=“solid”, line_width=2, source=source_HorizontalRay)

show(plot)

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/ac17d835-5909-4ac2-a552-9b94d3dba436%40continuum.io.

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

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/931b65c5-9b87-407a-9df2-7706e9ea3213%40continuum.io.

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

Hi Eugene,
i am really sorry to keep bothering at you but with no doc and very few examples i can’t see big improvements from my work. My last point of my work is to been able to draw oblique segments( ray would be the perfect solution) too.
The code i posted below is clearly wrong but i guess the idea is more and less that. I don’t understand how i can tell JS to store the initial click coordinates and while i am moving the mouse i would like to see the segment or ray drawn.
Can you suggest any idea on this?

Many thanks again
Andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.io.state import curstate

from bokeh.models import ColumnDataSource, Tool

from bokeh.plotting import figure

output_file(‘tool.html’)

curstate().file[‘resources’].minified = False

JS_CODE_DrawAnnotation = “”"

import * as p from “core/properties”

import {GestureTool, GestureToolView} from “models/tools/gestures/gesture_tool”

export class DrawToolView extends GestureToolView

this is executed on subsequent mouse/touch moves

_tap: (e) ->

frame = @plot_model.frame

@model.source.data = {x0: , y0: , x1: , y1:}

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

if source.data.y0.length == 0

source.data.x0.push(x)

source.data.y0.push(y)

else

source.data.x1.push(cb_obj.x)

source.data.y1.push(cb_obj.y)

source.change.emit()

this is executed then the pan/drag ends

_pan_end: (e) -> return null

export class DrawObliqueLine extends GestureTool

default_view: DrawToolView

type: “DrawObliqueLine”

tool_name: “Line”

icon: “bk-tool-icon-lasso-select”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class DrawObliqueLine(Tool):

implementation = JS_CODE_DrawAnnotation

source = Instance(ColumnDataSource)

source_ObliqueLine = ColumnDataSource(data=dict(x0=, y0=, x1=, y1=))

plot = figure(x_range=(0, 10), y_range=(0, 10) )

plot.add_tools(DrawObliqueLine(source=source_ObliqueLine))

plot.title.text = “Tap to draw on the plot”

plot.segment(x0=‘x0’, y0=‘y0’, x1=‘x1’, y1=‘y1’, line_dash=“solid”, line_width=2, source=source_ObliqueLine)

show(plot)

···

On 30 November 2017 at 14:15, Andrea Tagliabue [email protected] wrote:

yeah me too but most of the time i can’t figure out what i am doing wrong :slight_smile:

On 30 November 2017 at 13:42, Eugene Pakhomov [email protected] wrote:

Unfortunately, I don’t think so. At least, I couldn’t find any.
Usually, I just study the source code of the existing tools.

  • Eugene

On Thursday, November 30, 2017 at 7:24:57 PM UTC+7, Andrea Tagliabue wrote:

Wow really thanks for the quick answer. In order to avoid me bothering you for other issues, is there any doc regarding these custom tools? They looks to me pretty complex in terms of multiple options.
Many thanks again
Andrea

On 30 November 2017 at 13:18, Eugene Pakhomov [email protected] wrote:

Hi Andrea,

You almost got it. Here’s the working version. The things that needed to be fixed: wrong function name (must be “_tap” instead of “_pan”), wrong “default_view” (must be TapBBToolView), extra data field for the data source (you’d get an error about inconsistent column lengths).

I also realized that TapTool is not needed because all it can provide you if just a single field “event_type”, so I used a more general GestureTool instead.

import bokeh.util.compiler

bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure

output_file('tool.html')
curstate().file['resources'].minified = False

JS_CODE = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"

export class TapBBToolView extends GestureToolView

  # this is executed on subsequent mouse/touch moves
  _tap: (e) ->
    frame = @plot_model.frame

    {sx, sy} = e.bokeh
    if not frame.bbox.contains(sx, sy)
      return null

    x = frame.xscales['default'].invert(sx)
    y = frame.yscales['default'].invert(sy)
    source = @model.source
    source.data.x.push(x)
    source.data.y.push(y)
    source.change.emit()


export class TapBBTool extends GestureTool
  default_view: TapBBToolView
  type: "TapTool"
  event_type: "tap"
  tool_name: "H Line"
  icon: "bk-tool-icon-arrow-right"
  default_order: 12

  @define { source: [ p.Instance ] }
"""


class TapBBTool(Tool):
    __implementation__ = JS_CODE
    source = Instance(ColumnDataSource)


source_HorizontalRay = ColumnDataSource(data=dict(x=[], y=[]))

plot = figure(x_range=(0, 10), y_range=(0, 10),
              tools=[TapBBTool(source=source_HorizontalRay)])
plot.title.text = "Tap to draw on the plot"
plot.ray(x='x', y='y', angle=0, length=0, line_dash="solid", line_width=2,
         source=source_HorizontalRay)

show(plot)

Regards,

Eugene

On Thursday, November 30, 2017 at 6:58:10 PM UTC+7, Andrea Tagliabue wrote:

Hi all,
i am trying to implement a custom tool for drawing rays on tap event. I have implemented it via js_on_event and works fine but i need it as a tool since i want to give the ability of drawing rays, segments or lines and hence multiple custom tools are needed.

This code gets the plot displayed in the browser but when tapping nothing happens. I am struggling to understand what i am missing.

Would you mind to help me out?

Many thanks andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.models import ColumnDataSource, Tool

from bokeh.models.tools import Tap

from bokeh.plotting import figure

output_file(‘tool.html’)

JS_CODE = “”"

import * as p from “core/properties”

import {TapTool, TapToolView} from “models/tools/gestures/tap_tool”

export class TapBBToolView extends TapToolView

this is executed on subsequent mouse/touch moves

_pan: (e) ->

frame = @plot_model.frame

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

source.data.x.push(x)

source.data.y.push(y)

source.change.emit()

export class TapBBTool extends TapTool

default_view: TapToolView

type: “TapTool”

tool_name: “H Line”

icon: “bk-tool-icon-arrow-right”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class TapBBTool(Tool):

implementation = JS_CODE

source = Instance(ColumnDataSource)

source_HorizontalRay = ColumnDataSource(data=dict(x=, y=, angle=))

plot = figure(x_range=(0,10), y_range=(0,10), tools=[TapBBTool(source=source_HorizontalRay)])

plot.title.text =“Tap to draw on the plot”

plot.ray(x=‘x’, y=‘y’, angle = 0, length=0, line_dash=“solid”, line_width=2, source=source_HorizontalRay)

show(plot)

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/ac17d835-5909-4ac2-a552-9b94d3dba436%40continuum.io.

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

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/931b65c5-9b87-407a-9df2-7706e9ea3213%40continuum.io.

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

Hi,

There's not much/enough about this topic. Unfortunately the core team is very small and can only accomplish so much at a time, so human limits dictate that decisions must be made about what to prioritize. Custom tools are definitely a very advanced usage, so it's definitely the case that many more common things have had to be prioritized ahead of it in terms of comprehensive documentation. This is expecially true, given that extending any BokehJS model means knowing about it at a somewhat low level, the implicit assumption is that anyone making a custom tool will need to be able to dig into the BokehJS code in any case

You might want to consider looking at this example:

  https://github.com/bokeh/bokeh/blob/master/examples/howto/js_events.py

Which uses the relatively new events system to do allow for similar things like custom tools, without actually having to create a full custom extension. Instead, you can just provide CustomJS callbacks for the events you care about, all of which are documented here:

  https://bokeh.pydata.org/en/latest/docs/reference/events.html

Finally, you might want to chime in your thoughts on this GH issue discussing potential draw tools:

  https://github.com/bokeh/bokeh/issues/6370

Thanks,

Bryan

···

On Nov 30, 2017, at 06:24, Andrea Tagliabue <[email protected]> wrote:

Wow really thanks for the quick answer. In order to avoid me bothering you for other issues, is there any doc regarding these custom tools? They looks to me pretty complex in terms of multiple options.
Many thanks again
Andrea

On 30 November 2017 at 13:18, Eugene Pakhomov <[email protected]> wrote:
Hi Andrea,

You almost got it. Here's the working version. The things that needed to be fixed: wrong function name (must be "_tap" instead of "_pan"), wrong "default_view" (must be TapBBToolView), extra data field for the data source (you'd get an error about inconsistent column lengths).
I also realized that TapTool is not needed because all it can provide you if just a single field "event_type", so I used a more general GestureTool instead.

import bokeh.util.compiler

bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from bokeh.io import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure

output_file('tool.html')
curstate().file['resources'].minified = False

JS_CODE = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"

export class TapBBToolView extends GestureToolView

  # this is executed on subsequent mouse/touch moves
  _tap: (e) ->
    frame = @plot_model.frame

    {sx, sy} = e.bokeh
    if not frame.bbox.contains(sx, sy)
      return null

    x = frame.xscales['default'].invert(sx)
    y = frame.yscales['default'].invert(sy)
    source = @model.source
    source.data.x.push(x)
    source.data.y.push(y)
    source.change.emit()

export class TapBBTool extends GestureTool
  default_view: TapBBToolView
  type: "TapTool"
  event_type: "tap"
  tool_name: "H Line"
  icon: "bk-tool-icon-arrow-right"
  default_order: 12

  @define { source: [ p.Instance ] }
"""

class TapBBTool(Tool):
    __implementation__ = JS_CODE
    source = Instance(ColumnDataSource)

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

plot = figure(x_range=(0, 10), y_range=(0, 10),
              tools=[TapBBTool(source=source_HorizontalRay)])
plot.title.text = "Tap to draw on the plot"
plot.ray(x='x', y='y', angle=0, length=0, line_dash="solid", line_width=2,
         source=source_HorizontalRay)

show(plot)

Regards,
Eugene

On Thursday, November 30, 2017 at 6:58:10 PM UTC+7, Andrea Tagliabue wrote:
Hi all,
i am trying to implement a custom tool for drawing rays on tap event. I have implemented it via js_on_event and works fine but i need it as a tool since i want to give the ability of drawing rays, segments or lines and hence multiple custom tools are needed.
This code gets the plot displayed in the browser but when tapping nothing happens. I am struggling to understand what i am missing.
Would you mind to help me out?

Many thanks andrea

import bokeh.util.compiler
bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from bokeh.io import output_file, show
from bokeh.models import ColumnDataSource, Tool
from bokeh.models.tools import Tap
from bokeh.plotting import figure

output_file('tool.html')

JS_CODE = """
import * as p from "core/properties"
import {TapTool, TapToolView} from "models/tools/gestures/tap_tool"

export class TapBBToolView extends TapToolView

  # this is executed on subsequent mouse/touch moves
  _pan: (e) ->
    frame = @plot_model.frame

    {sx, sy} = e.bokeh
    if not frame.bbox.contains(sx, sy)
      return null

    x = frame.xscales['default'].invert(sx)
    y = frame.yscales['default'].invert(sy)
    source = @model.source
    source.data.x.push(x)
    source.data.y.push(y)
    source.change.emit()

export class TapBBTool extends TapTool
  default_view: TapToolView
  type: "TapTool"

  tool_name: "H Line"
  icon: "bk-tool-icon-arrow-right"
  event_type: "tap"
  default_order: 12

  @define { source: [ p.Instance ] }
"""

class TapBBTool(Tool):
    __implementation__ = JS_CODE
    source = Instance(ColumnDataSource)

source_HorizontalRay = ColumnDataSource(data=dict(x=, y=, angle=))

plot = figure(x_range=(0,10), y_range=(0,10), tools=[TapBBTool(source=source_HorizontalRay)])
plot.title.text ="Tap to draw on the plot"
plot.ray(x='x', y='y', angle = 0, length=0, line_dash="solid", line_width=2, source=source_HorizontalRay)

show(plot)

--
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/ac17d835-5909-4ac2-a552-9b94d3dba436%40continuum.io.

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

--
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/CAKUye6BUwhPJOh-asYXsxK4YSkdogZCE_qFU4JNf8t3hFQfMHA%40mail.gmail.com.
For more options, visit https://groups.google.com/a/continuum.io/d/optout.

Here you go:

import bokeh.util.compiler

bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from bokeh.io import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure

output_file('tool.html')
curstate().file['resources'].minified = False

JS_CODE_DrawAnnotation = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"
export class DrawToolView extends GestureToolView
  _get_scaled_frame_coords: (e) ->
    frame = @plot_model.frame
    {sx, sy} = e.bokeh
    if frame.bbox.contains(sx, sy)
      x = frame.xscales['default'].invert(sx)
      y = frame.yscales['default'].invert(sy)
      return [x, y]

  _pan_start: (e) ->
    coords = @_get_scaled_frame_coords(e)
    if coords
      [x, y] = coords
      @model.source.stream({x0: [x], y0: [y], x1: [x], y1: [y]})

  _pan: (e) ->
    coords = @_get_scaled_frame_coords(e)
    if coords
      [x, y] = coords
      last_row = @model.source.data.x0.length - 1
      @model.source.patch({x1: [[last_row, x]], y1: [[last_row, y]]})

export class DrawObliqueLine extends GestureTool
  default_view: DrawToolView
  type: "DrawObliqueLine"

  tool_name: "Line"
  icon: "bk-tool-icon-lasso-select"
  event_type: "pan"
  default_order: 12

  @define { source: [ p.Instance ] }
"""

class DrawObliqueLine(Tool):
    __implementation__ = JS_CODE_DrawAnnotation
    source = Instance(ColumnDataSource)

source_ObliqueLine = ColumnDataSource(data=dict(x0=[], y0=[], x1=[], y1=[]))

plot = figure(x_range=(0, 10), y_range=(0, 10))
plot.add_tools(DrawObliqueLine(source=source_ObliqueLine))
plot.title.text = "Pan to draw on the plot"

plot.segment(x0='x0', y0='y0', x1='x1', y1='y1', line_dash="solid",
             line_width=2, source=source_ObliqueLine)

show(plot)

Regards,

Eugene

···

On Thursday, November 30, 2017 at 10:07:00 PM UTC+7, Andrea Tagliabue wrote:

Hi Eugene,
i am really sorry to keep bothering at you but with no doc and very few examples i can’t see big improvements from my work. My last point of my work is to been able to draw oblique segments( ray would be the perfect solution) too.
The code i posted below is clearly wrong but i guess the idea is more and less that. I don’t understand how i can tell JS to store the initial click coordinates and while i am moving the mouse i would like to see the segment or ray drawn.
Can you suggest any idea on this?

Many thanks again
Andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.io.state import curstate

from bokeh.models import ColumnDataSource, Tool

from bokeh.plotting import figure

output_file(‘tool.html’)

curstate().file[‘resources’].minified = False

JS_CODE_DrawAnnotation = “”"

import * as p from “core/properties”

import {GestureTool, GestureToolView} from “models/tools/gestures/gesture_tool”

export class DrawToolView extends GestureToolView

this is executed on subsequent mouse/touch moves

_tap: (e) ->

frame = @plot_model.frame

@model.source.data = {x0: , y0: , x1: , y1:}

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

if source.data.y0.length == 0

source.data.x0.push(x)

source.data.y0.push(y)

else

source.data.x1.push(cb_obj.x)

source.data.y1.push(cb_obj.y)

source.change.emit()

this is executed then the pan/drag ends

_pan_end: (e) -> return null

export class DrawObliqueLine extends GestureTool

default_view: DrawToolView

type: “DrawObliqueLine”

tool_name: “Line”

icon: “bk-tool-icon-lasso-select”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class DrawObliqueLine(Tool):

implementation = JS_CODE_DrawAnnotation

source = Instance(ColumnDataSource)

source_ObliqueLine = ColumnDataSource(data=dict(x0=, y0=, x1=, y1=))

plot = figure(x_range=(0, 10), y_range=(0, 10) )

plot.add_tools(DrawObliqueLine(source=source_ObliqueLine))

plot.title.text = “Tap to draw on the plot”

plot.segment(x0=‘x0’, y0=‘y0’, x1=‘x1’, y1=‘y1’, line_dash=“solid”, line_width=2, source=source_ObliqueLine)

show(plot)

On 30 November 2017 at 14:15, Andrea Tagliabue [email protected] wrote:

yeah me too but most of the time i can’t figure out what i am doing wrong :slight_smile:

On 30 November 2017 at 13:42, Eugene Pakhomov [email protected] wrote:

Unfortunately, I don’t think so. At least, I couldn’t find any.
Usually, I just study the source code of the existing tools.

  • Eugene

On Thursday, November 30, 2017 at 7:24:57 PM UTC+7, Andrea Tagliabue wrote:

Wow really thanks for the quick answer. In order to avoid me bothering you for other issues, is there any doc regarding these custom tools? They looks to me pretty complex in terms of multiple options.
Many thanks again
Andrea

On 30 November 2017 at 13:18, Eugene Pakhomov [email protected] wrote:

Hi Andrea,

You almost got it. Here’s the working version. The things that needed to be fixed: wrong function name (must be “_tap” instead of “_pan”), wrong “default_view” (must be TapBBToolView), extra data field for the data source (you’d get an error about inconsistent column lengths).

I also realized that TapTool is not needed because all it can provide you if just a single field “event_type”, so I used a more general GestureTool instead.

import bokeh.util.compiler

bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure

output_file('tool.html')
curstate().file['resources'].minified = False

JS_CODE = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"

export class TapBBToolView extends GestureToolView

  # this is executed on subsequent mouse/touch moves
  _tap: (e) ->
    frame = @plot_model.frame

    {sx, sy} = e.bokeh
    if not frame.bbox.contains(sx, sy)
      return null

    x = frame.xscales['default'].invert(sx)
    y = frame.yscales['default'].invert(sy)
    source = @model.source
    source.data.x.push(x)
    source.data.y.push(y)
    source.change.emit()


export class TapBBTool extends GestureTool
  default_view: TapBBToolView
  type: "TapTool"
  event_type: "tap"
  tool_name: "H Line"
  icon: "bk-tool-icon-arrow-right"
  default_order: 12

  @define { source: [ p.Instance ] }
"""


class TapBBTool(Tool):
    __implementation__ = JS_CODE
    source = Instance(ColumnDataSource)


source_HorizontalRay = ColumnDataSource(data=dict(x=[], y=[]))

plot = figure(x_range=(0, 10), y_range=(0, 10),
              tools=[TapBBTool(source=source_HorizontalRay)])
plot.title.text = "Tap to draw on the plot"
plot.ray(x='x', y='y', angle=0, length=0, line_dash="solid", line_width=2,
         source=source_HorizontalRay)

show(plot)

Regards,

Eugene

On Thursday, November 30, 2017 at 6:58:10 PM UTC+7, Andrea Tagliabue wrote:

Hi all,
i am trying to implement a custom tool for drawing rays on tap event. I have implemented it via js_on_event and works fine but i need it as a tool since i want to give the ability of drawing rays, segments or lines and hence multiple custom tools are needed.

This code gets the plot displayed in the browser but when tapping nothing happens. I am struggling to understand what i am missing.

Would you mind to help me out?

Many thanks andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.models import ColumnDataSource, Tool

from bokeh.models.tools import Tap

from bokeh.plotting import figure

output_file(‘tool.html’)

JS_CODE = “”"

import * as p from “core/properties”

import {TapTool, TapToolView} from “models/tools/gestures/tap_tool”

export class TapBBToolView extends TapToolView

this is executed on subsequent mouse/touch moves

_pan: (e) ->

frame = @plot_model.frame

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

source.data.x.push(x)

source.data.y.push(y)

source.change.emit()

export class TapBBTool extends TapTool

default_view: TapToolView

type: “TapTool”

tool_name: “H Line”

icon: “bk-tool-icon-arrow-right”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class TapBBTool(Tool):

implementation = JS_CODE

source = Instance(ColumnDataSource)

source_HorizontalRay = ColumnDataSource(data=dict(x=, y=, angle=))

plot = figure(x_range=(0,10), y_range=(0,10), tools=[TapBBTool(source=source_HorizontalRay)])

plot.title.text =“Tap to draw on the plot”

plot.ray(x=‘x’, y=‘y’, angle = 0, length=0, line_dash=“solid”, line_width=2, source=source_HorizontalRay)

show(plot)

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/ac17d835-5909-4ac2-a552-9b94d3dba436%40continuum.io.

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

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/931b65c5-9b87-407a-9df2-7706e9ea3213%40continuum.io.

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

Hi Eugene,
Terrific work!! Thanks so much.
Bryan: i have been used matplotlib a lot for my apps/code but when i discovered bokeh i just thought it was much better in terms of speed and easier accessibility. Having the plot in the browser and been able to have full control of what you see is just a great experience. And you can share it so easily.

However I do have to say i really struggled to get my mind around the doc and examples. But with your and Eugene’s help i have been able to convert my old and slow matplotlib app in something much better. As soon as i will be done with my code and all my custom functionalities i will send you all my code so you can upload these very unique examples and all the community and future bokeh developer will have much less tough days.

Also. this example from Web Busino https://gist.github.com/busino/d560a58b315e7bde2321e3960ea88616/raw/6d1cc896e2e86b45b659814c4eacccf5ad353efd/bokeh_frog.html should be in the doc. That’s a such a great example of very dynamic and custom usage of the mouse control for js_on_event.
I will definitively write some personal view on https://github.com/bokeh/bokeh/issues/6370

Many thanks again

Andrea

···

On 30 November 2017 at 16:49, Eugene Pakhomov [email protected] wrote:

Here you go:

import bokeh.util.compiler

bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure

output_file('tool.html')
curstate().file['resources'].minified = False

JS_CODE_DrawAnnotation = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"
export class DrawToolView extends GestureToolView
  _get_scaled_frame_coords: (e) ->
    frame = @plot_model.frame
    {sx, sy} = e.bokeh
    if frame.bbox.contains(sx, sy)
      x = frame.xscales['default'].invert(sx)
      y = frame.yscales['default'].invert(sy)
      return [x, y]

  _pan_start: (e) ->
    coords = @_get_scaled_frame_coords(e)
    if coords
      [x, y] = coords
      @model.source.stream({x0: [x], y0: [y], x1: [x], y1: [y]})

  _pan: (e) ->
    coords = @_get_scaled_frame_coords(e)
    if coords
      [x, y] = coords
      last_row = @model.source.data.x0.length - 1
      @model.source.patch({x1: [[last_row, x]], y1: [[last_row, y]]})

export class DrawObliqueLine extends GestureTool
  default_view: DrawToolView
  type: "DrawObliqueLine"

  tool_name: "Line"
  icon: "bk-tool-icon-lasso-select"
  event_type: "pan"
  default_order: 12

  @define { source: [ p.Instance ] }
"""


class DrawObliqueLine(Tool):
    __implementation__ = JS_CODE_DrawAnnotation
    source = Instance(ColumnDataSource)


source_ObliqueLine = ColumnDataSource(data=dict(x0=[], y0=[], x1=[], y1=[]))

plot = figure(x_range=(0, 10), y_range=(0, 10))
plot.add_tools(DrawObliqueLine(source=source_ObliqueLine))
plot.title.text = "Pan to draw on the plot"

plot.segment(x0='x0', y0='y0', x1='x1', y1='y1', line_dash="solid",
             line_width=2, source=source_ObliqueLine)

show(plot)

Regards,

Eugene

On Thursday, November 30, 2017 at 10:07:00 PM UTC+7, Andrea Tagliabue wrote:

Hi Eugene,
i am really sorry to keep bothering at you but with no doc and very few examples i can’t see big improvements from my work. My last point of my work is to been able to draw oblique segments( ray would be the perfect solution) too.
The code i posted below is clearly wrong but i guess the idea is more and less that. I don’t understand how i can tell JS to store the initial click coordinates and while i am moving the mouse i would like to see the segment or ray drawn.
Can you suggest any idea on this?

Many thanks again
Andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.io.state import curstate

from bokeh.models import ColumnDataSource, Tool

from bokeh.plotting import figure

output_file(‘tool.html’)

curstate().file[‘resources’].minified = False

JS_CODE_DrawAnnotation = “”"

import * as p from “core/properties”

import {GestureTool, GestureToolView} from “models/tools/gestures/gesture_tool”

export class DrawToolView extends GestureToolView

this is executed on subsequent mouse/touch moves

_tap: (e) ->

frame = @plot_model.frame

@model.source.data = {x0: , y0: , x1: , y1:}

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

if source.data.y0.length == 0

source.data.x0.push(x)

source.data.y0.push(y)

else

source.data.x1.push(cb_obj.x)

source.data.y1.push(cb_obj.y)

source.change.emit()

this is executed then the pan/drag ends

_pan_end: (e) -> return null

export class DrawObliqueLine extends GestureTool

default_view: DrawToolView

type: “DrawObliqueLine”

tool_name: “Line”

icon: “bk-tool-icon-lasso-select”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class DrawObliqueLine(Tool):

implementation = JS_CODE_DrawAnnotation

source = Instance(ColumnDataSource)

source_ObliqueLine = ColumnDataSource(data=dict(x0=, y0=, x1=, y1=))

plot = figure(x_range=(0, 10), y_range=(0, 10) )

plot.add_tools(DrawObliqueLine(source=source_ObliqueLine))

plot.title.text = “Tap to draw on the plot”

plot.segment(x0=‘x0’, y0=‘y0’, x1=‘x1’, y1=‘y1’, line_dash=“solid”, line_width=2, source=source_ObliqueLine)

show(plot)

On 30 November 2017 at 14:15, Andrea Tagliabue [email protected] wrote:

yeah me too but most of the time i can’t figure out what i am doing wrong :slight_smile:

On 30 November 2017 at 13:42, Eugene Pakhomov [email protected] wrote:

Unfortunately, I don’t think so. At least, I couldn’t find any.
Usually, I just study the source code of the existing tools.

  • Eugene

On Thursday, November 30, 2017 at 7:24:57 PM UTC+7, Andrea Tagliabue wrote:

Wow really thanks for the quick answer. In order to avoid me bothering you for other issues, is there any doc regarding these custom tools? They looks to me pretty complex in terms of multiple options.
Many thanks again
Andrea

On 30 November 2017 at 13:18, Eugene Pakhomov [email protected] wrote:

Hi Andrea,

You almost got it. Here’s the working version. The things that needed to be fixed: wrong function name (must be “_tap” instead of “_pan”), wrong “default_view” (must be TapBBToolView), extra data field for the data source (you’d get an error about inconsistent column lengths).

I also realized that TapTool is not needed because all it can provide you if just a single field “event_type”, so I used a more general GestureTool instead.

import bokeh.util.compiler

bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure

output_file('tool.html')
curstate().file['resources'].minified = False

JS_CODE = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"

export class TapBBToolView extends GestureToolView

  # this is executed on subsequent mouse/touch moves
  _tap: (e) ->
    frame = @plot_model.frame

    {sx, sy} = e.bokeh
    if not frame.bbox.contains(sx, sy)
      return null

    x = frame.xscales['default'].invert(sx)
    y = frame.yscales['default'].invert(sy)
    source = @model.source
    source.data.x.push(x)
    source.data.y.push(y)
    source.change.emit()


export class TapBBTool extends GestureTool
  default_view: TapBBToolView
  type: "TapTool"
  event_type: "tap"
  tool_name: "H Line"
  icon: "bk-tool-icon-arrow-right"
  default_order: 12

  @define { source: [ p.Instance ] }
"""


class TapBBTool(Tool):
    __implementation__ = JS_CODE
    source = Instance(ColumnDataSource)


source_HorizontalRay = ColumnDataSource(data=dict(x=[], y=[]))

plot = figure(x_range=(0, 10), y_range=(0, 10),
              tools=[TapBBTool(source=source_HorizontalRay)])
plot.title.text = "Tap to draw on the plot"
plot.ray(x='x', y='y', angle=0, length=0, line_dash="solid", line_width=2,
         source=source_HorizontalRay)

show(plot)

Regards,

Eugene

On Thursday, November 30, 2017 at 6:58:10 PM UTC+7, Andrea Tagliabue wrote:

Hi all,
i am trying to implement a custom tool for drawing rays on tap event. I have implemented it via js_on_event and works fine but i need it as a tool since i want to give the ability of drawing rays, segments or lines and hence multiple custom tools are needed.

This code gets the plot displayed in the browser but when tapping nothing happens. I am struggling to understand what i am missing.

Would you mind to help me out?

Many thanks andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.models import ColumnDataSource, Tool

from bokeh.models.tools import Tap

from bokeh.plotting import figure

output_file(‘tool.html’)

JS_CODE = “”"

import * as p from “core/properties”

import {TapTool, TapToolView} from “models/tools/gestures/tap_tool”

export class TapBBToolView extends TapToolView

this is executed on subsequent mouse/touch moves

_pan: (e) ->

frame = @plot_model.frame

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

source.data.x.push(x)

source.data.y.push(y)

source.change.emit()

export class TapBBTool extends TapTool

default_view: TapToolView

type: “TapTool”

tool_name: “H Line”

icon: “bk-tool-icon-arrow-right”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class TapBBTool(Tool):

implementation = JS_CODE

source = Instance(ColumnDataSource)

source_HorizontalRay = ColumnDataSource(data=dict(x=, y=, angle=))

plot = figure(x_range=(0,10), y_range=(0,10), tools=[TapBBTool(source=source_HorizontalRay)])

plot.title.text =“Tap to draw on the plot”

plot.ray(x=‘x’, y=‘y’, angle = 0, length=0, line_dash=“solid”, line_width=2, source=source_HorizontalRay)

show(plot)

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/ac17d835-5909-4ac2-a552-9b94d3dba436%40continuum.io.

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

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/931b65c5-9b87-407a-9df2-7706e9ea3213%40continuum.io.

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

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/e90b70b6-a4a8-47df-befa-e0eb46e33a8f%40continuum.io.

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

Hi guys,
sorry but it’s still me. Nothing too serious but i wanted to have your opinion on something. I slightly modified the code in “new custom tool” https://bokeh.pydata.org/en/latest/docs/user_guide/extensions_gallery/tool.html where even if i release the left click button and i restart to draw something the code will not clear what has been drawn before.

I am using a simple.plot.line annotation. I was thinking: is there a way to prevent JS to restart drawing from the last point? I mean if i move away from the very last drawn point and i restart left clicking somewhere else JS will link the very last point with the current new one. Ideally i would like a way for drawing distinct annotations.

is it possible to achieve the same outcome with plot.line?

Many thanks again

Andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.io.state import curstate

from bokeh.models import ColumnDataSource, Tool

from bokeh.plotting import figure

output_file(‘tool.html’)

curstate().file[‘resources’].minified = False

JS_CODE_DrawAnnotation = “”"

import * as p from “core/properties”

import {GestureTool, GestureToolView} from “models/tools/gestures/gesture_tool”

export class DrawToolView extends GestureToolView

this is executed on subsequent mouse/touch moves

_pan: (e) ->

frame = @plot_model.frame

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

source.data.x.push(x)

source.data.y.push(y)

source.change.emit()

this is executed then the pan/drag ends

_pan_end: (e) -> return null

export class DrawAnnotation extends GestureTool

default_view: DrawToolView

type: “DrawAnnotation”

tool_name: “Drag Annotation”

icon: “bk-tool-icon-lasso-select”

event_type: “pan”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class DrawAnnotation(Tool):

implementation = JS_CODE_DrawAnnotation

source = Instance(ColumnDataSource)

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

plot = figure(x_range=(0, 10), y_range=(0, 10) )

plot.add_tools(DrawAnnotation(source=source_DrawAnnotation))

plot.line(x=‘x’, y=‘y’, source=source_DrawAnnotation)

show(plot)

···

On 30 November 2017 at 17:10, Andrea Tagliabue [email protected] wrote:

Hi Eugene,
Terrific work!! Thanks so much.
Bryan: i have been used matplotlib a lot for my apps/code but when i discovered bokeh i just thought it was much better in terms of speed and easier accessibility. Having the plot in the browser and been able to have full control of what you see is just a great experience. And you can share it so easily.

However I do have to say i really struggled to get my mind around the doc and examples. But with your and Eugene’s help i have been able to convert my old and slow matplotlib app in something much better. As soon as i will be done with my code and all my custom functionalities i will send you all my code so you can upload these very unique examples and all the community and future bokeh developer will have much less tough days.

Also. this example from Web Busino https://gist.github.com/busino/d560a58b315e7bde2321e3960ea88616/raw/6d1cc896e2e86b45b659814c4eacccf5ad353efd/bokeh_frog.html should be in the doc. That’s a such a great example of very dynamic and custom usage of the mouse control for js_on_event.
I will definitively write some personal view on https://github.com/bokeh/bokeh/issues/6370

Many thanks again

Andrea

On 30 November 2017 at 16:49, Eugene Pakhomov [email protected] wrote:

Here you go:

import bokeh.util.compiler

bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure

output_file('tool.html')
curstate().file['resources'].minified = False

JS_CODE_DrawAnnotation = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"
export class DrawToolView extends GestureToolView
  _get_scaled_frame_coords: (e) ->
    frame = @plot_model.frame
    {sx, sy} = e.bokeh
    if frame.bbox.contains(sx, sy)
      x = frame.xscales['default'].invert(sx)
      y = frame.yscales['default'].invert(sy)
      return [x, y]

  _pan_start: (e) ->
    coords = @_get_scaled_frame_coords(e)
    if coords
      [x, y] = coords
      @model.source.stream({x0: [x], y0: [y], x1: [x], y1: [y]})

  _pan: (e) ->
    coords = @_get_scaled_frame_coords(e)
    if coords
      [x, y] = coords
      last_row = @model.source.data.x0.length - 1
      @model.source.patch({x1: [[last_row, x]], y1: [[last_row, y]]})

export class DrawObliqueLine extends GestureTool
  default_view: DrawToolView
  type: "DrawObliqueLine"

  tool_name: "Line"
  icon: "bk-tool-icon-lasso-select"
  event_type: "pan"
  default_order: 12

  @define { source: [ p.Instance ] }
"""


class DrawObliqueLine(Tool):
    __implementation__ = JS_CODE_DrawAnnotation
    source = Instance(ColumnDataSource)


source_ObliqueLine = ColumnDataSource(data=dict(x0=[], y0=[], x1=[], y1=[]))

plot = figure(x_range=(0, 10), y_range=(0, 10))
plot.add_tools(DrawObliqueLine(source=source_ObliqueLine))
plot.title.text = "Pan to draw on the plot"

plot.segment(x0='x0', y0='y0', x1='x1', y1='y1', line_dash="solid",
             line_width=2, source=source_ObliqueLine)

show(plot)

Regards,

Eugene

On Thursday, November 30, 2017 at 10:07:00 PM UTC+7, Andrea Tagliabue wrote:

Hi Eugene,
i am really sorry to keep bothering at you but with no doc and very few examples i can’t see big improvements from my work. My last point of my work is to been able to draw oblique segments( ray would be the perfect solution) too.
The code i posted below is clearly wrong but i guess the idea is more and less that. I don’t understand how i can tell JS to store the initial click coordinates and while i am moving the mouse i would like to see the segment or ray drawn.
Can you suggest any idea on this?

Many thanks again
Andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.io.state import curstate

from bokeh.models import ColumnDataSource, Tool

from bokeh.plotting import figure

output_file(‘tool.html’)

curstate().file[‘resources’].minified = False

JS_CODE_DrawAnnotation = “”"

import * as p from “core/properties”

import {GestureTool, GestureToolView} from “models/tools/gestures/gesture_tool”

export class DrawToolView extends GestureToolView

this is executed on subsequent mouse/touch moves

_tap: (e) ->

frame = @plot_model.frame

@model.source.data = {x0: , y0: , x1: , y1:}

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

if source.data.y0.length == 0

source.data.x0.push(x)

source.data.y0.push(y)

else

source.data.x1.push(cb_obj.x)

source.data.y1.push(cb_obj.y)

source.change.emit()

this is executed then the pan/drag ends

_pan_end: (e) -> return null

export class DrawObliqueLine extends GestureTool

default_view: DrawToolView

type: “DrawObliqueLine”

tool_name: “Line”

icon: “bk-tool-icon-lasso-select”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class DrawObliqueLine(Tool):

implementation = JS_CODE_DrawAnnotation

source = Instance(ColumnDataSource)

source_ObliqueLine = ColumnDataSource(data=dict(x0=, y0=, x1=, y1=))

plot = figure(x_range=(0, 10), y_range=(0, 10) )

plot.add_tools(DrawObliqueLine(source=source_ObliqueLine))

plot.title.text = “Tap to draw on the plot”

plot.segment(x0=‘x0’, y0=‘y0’, x1=‘x1’, y1=‘y1’, line_dash=“solid”, line_width=2, source=source_ObliqueLine)

show(plot)

On 30 November 2017 at 14:15, Andrea Tagliabue [email protected] wrote:

yeah me too but most of the time i can’t figure out what i am doing wrong :slight_smile:

On 30 November 2017 at 13:42, Eugene Pakhomov [email protected] wrote:

Unfortunately, I don’t think so. At least, I couldn’t find any.
Usually, I just study the source code of the existing tools.

  • Eugene

On Thursday, November 30, 2017 at 7:24:57 PM UTC+7, Andrea Tagliabue wrote:

Wow really thanks for the quick answer. In order to avoid me bothering you for other issues, is there any doc regarding these custom tools? They looks to me pretty complex in terms of multiple options.
Many thanks again
Andrea

On 30 November 2017 at 13:18, Eugene Pakhomov [email protected] wrote:

Hi Andrea,

You almost got it. Here’s the working version. The things that needed to be fixed: wrong function name (must be “_tap” instead of “_pan”), wrong “default_view” (must be TapBBToolView), extra data field for the data source (you’d get an error about inconsistent column lengths).

I also realized that TapTool is not needed because all it can provide you if just a single field “event_type”, so I used a more general GestureTool instead.

import bokeh.util.compiler

bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure

output_file('tool.html')
curstate().file['resources'].minified = False

JS_CODE = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"

export class TapBBToolView extends GestureToolView

  # this is executed on subsequent mouse/touch moves
  _tap: (e) ->
    frame = @plot_model.frame

    {sx, sy} = e.bokeh
    if not frame.bbox.contains(sx, sy)
      return null

    x = frame.xscales['default'].invert(sx)
    y = frame.yscales['default'].invert(sy)
    source = @model.source
    source.data.x.push(x)
    source.data.y.push(y)
    source.change.emit()


export class TapBBTool extends GestureTool
  default_view: TapBBToolView
  type: "TapTool"
  event_type: "tap"
  tool_name: "H Line"
  icon: "bk-tool-icon-arrow-right"
  default_order: 12

  @define { source: [ p.Instance ] }
"""


class TapBBTool(Tool):
    __implementation__ = JS_CODE
    source = Instance(ColumnDataSource)


source_HorizontalRay = ColumnDataSource(data=dict(x=[], y=[]))

plot = figure(x_range=(0, 10), y_range=(0, 10),
              tools=[TapBBTool(source=source_HorizontalRay)])
plot.title.text = "Tap to draw on the plot"
plot.ray(x='x', y='y', angle=0, length=0, line_dash="solid", line_width=2,
         source=source_HorizontalRay)

show(plot)

Regards,

Eugene

On Thursday, November 30, 2017 at 6:58:10 PM UTC+7, Andrea Tagliabue wrote:

Hi all,
i am trying to implement a custom tool for drawing rays on tap event. I have implemented it via js_on_event and works fine but i need it as a tool since i want to give the ability of drawing rays, segments or lines and hence multiple custom tools are needed.

This code gets the plot displayed in the browser but when tapping nothing happens. I am struggling to understand what i am missing.

Would you mind to help me out?

Many thanks andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.models import ColumnDataSource, Tool

from bokeh.models.tools import Tap

from bokeh.plotting import figure

output_file(‘tool.html’)

JS_CODE = “”"

import * as p from “core/properties”

import {TapTool, TapToolView} from “models/tools/gestures/tap_tool”

export class TapBBToolView extends TapToolView

this is executed on subsequent mouse/touch moves

_pan: (e) ->

frame = @plot_model.frame

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

source.data.x.push(x)

source.data.y.push(y)

source.change.emit()

export class TapBBTool extends TapTool

default_view: TapToolView

type: “TapTool”

tool_name: “H Line”

icon: “bk-tool-icon-arrow-right”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class TapBBTool(Tool):

implementation = JS_CODE

source = Instance(ColumnDataSource)

source_HorizontalRay = ColumnDataSource(data=dict(x=, y=, angle=))

plot = figure(x_range=(0,10), y_range=(0,10), tools=[TapBBTool(source=source_HorizontalRay)])

plot.title.text =“Tap to draw on the plot”

plot.ray(x=‘x’, y=‘y’, angle = 0, length=0, line_dash=“solid”, line_width=2, source=source_HorizontalRay)

show(plot)

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/ac17d835-5909-4ac2-a552-9b94d3dba436%40continuum.io.

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

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/931b65c5-9b87-407a-9df2-7706e9ea3213%40continuum.io.

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

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/e90b70b6-a4a8-47df-befa-e0eb46e33a8f%40continuum.io.

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

Hey Andrea,

I’m not sure what you’re asking about. The example that I’ve sent you earlier works exactly as you describe - it draws separate lines on each pan, it doesn’t link the previous stop with the next start.

Regards,

Eugene

···

On Friday, December 1, 2017 at 9:37:43 PM UTC+7, Andrea Tagliabue wrote:

Hi guys,
sorry but it’s still me. Nothing too serious but i wanted to have your opinion on something. I slightly modified the code in “new custom tool” https://bokeh.pydata.org/en/latest/docs/user_guide/extensions_gallery/tool.html where even if i release the left click button and i restart to draw something the code will not clear what has been drawn before.

I am using a simple.plot.line annotation. I was thinking: is there a way to prevent JS to restart drawing from the last point? I mean if i move away from the very last drawn point and i restart left clicking somewhere else JS will link the very last point with the current new one. Ideally i would like a way for drawing distinct annotations.

is it possible to achieve the same outcome with plot.line?

Many thanks again

Andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.io.state import curstate

from bokeh.models import ColumnDataSource, Tool

from bokeh.plotting import figure

output_file(‘tool.html’)

curstate().file[‘resources’].minified = False

JS_CODE_DrawAnnotation = “”"

import * as p from “core/properties”

import {GestureTool, GestureToolView} from “models/tools/gestures/gesture_tool”

export class DrawToolView extends GestureToolView

this is executed on subsequent mouse/touch moves

_pan: (e) ->

frame = @plot_model.frame

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

source.data.x.push(x)

source.data.y.push(y)

source.change.emit()

this is executed then the pan/drag ends

_pan_end: (e) -> return null

export class DrawAnnotation extends GestureTool

default_view: DrawToolView

type: “DrawAnnotation”

tool_name: “Drag Annotation”

icon: “bk-tool-icon-lasso-select”

event_type: “pan”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class DrawAnnotation(Tool):

implementation = JS_CODE_DrawAnnotation

source = Instance(ColumnDataSource)

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

plot = figure(x_range=(0, 10), y_range=(0, 10) )

plot.add_tools(DrawAnnotation(source=source_DrawAnnotation))

plot.line(x=‘x’, y=‘y’, source=source_DrawAnnotation)

show(plot)

On 30 November 2017 at 17:10, Andrea Tagliabue [email protected] wrote:

Hi Eugene,
Terrific work!! Thanks so much.
Bryan: i have been used matplotlib a lot for my apps/code but when i discovered bokeh i just thought it was much better in terms of speed and easier accessibility. Having the plot in the browser and been able to have full control of what you see is just a great experience. And you can share it so easily.

However I do have to say i really struggled to get my mind around the doc and examples. But with your and Eugene’s help i have been able to convert my old and slow matplotlib app in something much better. As soon as i will be done with my code and all my custom functionalities i will send you all my code so you can upload these very unique examples and all the community and future bokeh developer will have much less tough days.

Also. this example from Web Busino https://gist.github.com/busino/d560a58b315e7bde2321e3960ea88616/raw/6d1cc896e2e86b45b659814c4eacccf5ad353efd/bokeh_frog.html should be in the doc. That’s a such a great example of very dynamic and custom usage of the mouse control for js_on_event.
I will definitively write some personal view on https://github.com/bokeh/bokeh/issues/6370

Many thanks again

Andrea

On 30 November 2017 at 16:49, Eugene Pakhomov [email protected] wrote:

Here you go:

import bokeh.util.compiler

bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure

output_file('tool.html')
curstate().file['resources'].minified = False

JS_CODE_DrawAnnotation = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"
export class DrawToolView extends GestureToolView
  _get_scaled_frame_coords: (e) ->
    frame = @plot_model.frame
    {sx, sy} = e.bokeh
    if frame.bbox.contains(sx, sy)
      x = frame.xscales['default'].invert(sx)
      y = frame.yscales['default'].invert(sy)
      return [x, y]

  _pan_start: (e) ->
    coords = @_get_scaled_frame_coords(e)
    if coords
      [x, y] = coords
      @model.source.stream({x0: [x], y0: [y], x1: [x], y1: [y]})

  _pan: (e) ->
    coords = @_get_scaled_frame_coords(e)
    if coords
      [x, y] = coords
      last_row = @model.source.data.x0.length - 1
      @model.source.patch({x1: [[last_row, x]], y1: [[last_row, y]]})

export class DrawObliqueLine extends GestureTool
  default_view: DrawToolView
  type: "DrawObliqueLine"

  tool_name: "Line"
  icon: "bk-tool-icon-lasso-select"
  event_type: "pan"
  default_order: 12

  @define { source: [ p.Instance ] }
"""


class DrawObliqueLine(Tool):
    __implementation__ = JS_CODE_DrawAnnotation
    source = Instance(ColumnDataSource)


source_ObliqueLine = ColumnDataSource(data=dict(x0=[], y0=[], x1=[], y1=[]))

plot = figure(x_range=(0, 10), y_range=(0, 10))
plot.add_tools(DrawObliqueLine(source=source_ObliqueLine))
plot.title.text = "Pan to draw on the plot"

plot.segment(x0='x0', y0='y0', x1='x1', y1='y1', line_dash="solid",
             line_width=2, source=source_ObliqueLine)

show(plot)

Regards,

Eugene

On Thursday, November 30, 2017 at 10:07:00 PM UTC+7, Andrea Tagliabue wrote:

Hi Eugene,
i am really sorry to keep bothering at you but with no doc and very few examples i can’t see big improvements from my work. My last point of my work is to been able to draw oblique segments( ray would be the perfect solution) too.
The code i posted below is clearly wrong but i guess the idea is more and less that. I don’t understand how i can tell JS to store the initial click coordinates and while i am moving the mouse i would like to see the segment or ray drawn.
Can you suggest any idea on this?

Many thanks again
Andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.io.state import curstate

from bokeh.models import ColumnDataSource, Tool

from bokeh.plotting import figure

output_file(‘tool.html’)

curstate().file[‘resources’].minified = False

JS_CODE_DrawAnnotation = “”"

import * as p from “core/properties”

import {GestureTool, GestureToolView} from “models/tools/gestures/gesture_tool”

export class DrawToolView extends GestureToolView

this is executed on subsequent mouse/touch moves

_tap: (e) ->

frame = @plot_model.frame

@model.source.data = {x0: , y0: , x1: , y1:}

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

if source.data.y0.length == 0

source.data.x0.push(x)

source.data.y0.push(y)

else

source.data.x1.push(cb_obj.x)

source.data.y1.push(cb_obj.y)

source.change.emit()

this is executed then the pan/drag ends

_pan_end: (e) -> return null

export class DrawObliqueLine extends GestureTool

default_view: DrawToolView

type: “DrawObliqueLine”

tool_name: “Line”

icon: “bk-tool-icon-lasso-select”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class DrawObliqueLine(Tool):

implementation = JS_CODE_DrawAnnotation

source = Instance(ColumnDataSource)

source_ObliqueLine = ColumnDataSource(data=dict(x0=, y0=, x1=, y1=))

plot = figure(x_range=(0, 10), y_range=(0, 10) )

plot.add_tools(DrawObliqueLine(source=source_ObliqueLine))

plot.title.text = “Tap to draw on the plot”

plot.segment(x0=‘x0’, y0=‘y0’, x1=‘x1’, y1=‘y1’, line_dash=“solid”, line_width=2, source=source_ObliqueLine)

show(plot)

On 30 November 2017 at 14:15, Andrea Tagliabue [email protected] wrote:

yeah me too but most of the time i can’t figure out what i am doing wrong :slight_smile:

On 30 November 2017 at 13:42, Eugene Pakhomov [email protected] wrote:

Unfortunately, I don’t think so. At least, I couldn’t find any.
Usually, I just study the source code of the existing tools.

  • Eugene

On Thursday, November 30, 2017 at 7:24:57 PM UTC+7, Andrea Tagliabue wrote:

Wow really thanks for the quick answer. In order to avoid me bothering you for other issues, is there any doc regarding these custom tools? They looks to me pretty complex in terms of multiple options.
Many thanks again
Andrea

On 30 November 2017 at 13:18, Eugene Pakhomov [email protected] wrote:

Hi Andrea,

You almost got it. Here’s the working version. The things that needed to be fixed: wrong function name (must be “_tap” instead of “_pan”), wrong “default_view” (must be TapBBToolView), extra data field for the data source (you’d get an error about inconsistent column lengths).

I also realized that TapTool is not needed because all it can provide you if just a single field “event_type”, so I used a more general GestureTool instead.

import bokeh.util.compiler

bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure

output_file('tool.html')
curstate().file['resources'].minified = False

JS_CODE = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"

export class TapBBToolView extends GestureToolView

  # this is executed on subsequent mouse/touch moves
  _tap: (e) ->
    frame = @plot_model.frame

    {sx, sy} = e.bokeh
    if not frame.bbox.contains(sx, sy)
      return null

    x = frame.xscales['default'].invert(sx)
    y = frame.yscales['default'].invert(sy)
    source = @model.source
    source.data.x.push(x)
    source.data.y.push(y)
    source.change.emit()


export class TapBBTool extends GestureTool
  default_view: TapBBToolView
  type: "TapTool"
  event_type: "tap"
  tool_name: "H Line"
  icon: "bk-tool-icon-arrow-right"
  default_order: 12

  @define { source: [ p.Instance ] }
"""


class TapBBTool(Tool):
    __implementation__ = JS_CODE
    source = Instance(ColumnDataSource)


source_HorizontalRay = ColumnDataSource(data=dict(x=[], y=[]))

plot = figure(x_range=(0, 10), y_range=(0, 10),
              tools=[TapBBTool(source=source_HorizontalRay)])
plot.title.text = "Tap to draw on the plot"
plot.ray(x='x', y='y', angle=0, length=0, line_dash="solid", line_width=2,
         source=source_HorizontalRay)

show(plot)

Regards,

Eugene

On Thursday, November 30, 2017 at 6:58:10 PM UTC+7, Andrea Tagliabue wrote:

Hi all,
i am trying to implement a custom tool for drawing rays on tap event. I have implemented it via js_on_event and works fine but i need it as a tool since i want to give the ability of drawing rays, segments or lines and hence multiple custom tools are needed.

This code gets the plot displayed in the browser but when tapping nothing happens. I am struggling to understand what i am missing.

Would you mind to help me out?

Many thanks andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.models import ColumnDataSource, Tool

from bokeh.models.tools import Tap

from bokeh.plotting import figure

output_file(‘tool.html’)

JS_CODE = “”"

import * as p from “core/properties”

import {TapTool, TapToolView} from “models/tools/gestures/tap_tool”

export class TapBBToolView extends TapToolView

this is executed on subsequent mouse/touch moves

_pan: (e) ->

frame = @plot_model.frame

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

source.data.x.push(x)

source.data.y.push(y)

source.change.emit()

export class TapBBTool extends TapTool

default_view: TapToolView

type: “TapTool”

tool_name: “H Line”

icon: “bk-tool-icon-arrow-right”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class TapBBTool(Tool):

implementation = JS_CODE

source = Instance(ColumnDataSource)

source_HorizontalRay = ColumnDataSource(data=dict(x=, y=, angle=))

plot = figure(x_range=(0,10), y_range=(0,10), tools=[TapBBTool(source=source_HorizontalRay)])

plot.title.text =“Tap to draw on the plot”

plot.ray(x=‘x’, y=‘y’, angle = 0, length=0, line_dash=“solid”, line_width=2, source=source_HorizontalRay)

show(plot)

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/ac17d835-5909-4ac2-a552-9b94d3dba436%40continuum.io.

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

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/931b65c5-9b87-407a-9df2-7706e9ea3213%40continuum.io.

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

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/e90b70b6-a4a8-47df-befa-e0eb46e33a8f%40continuum.io.

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

Hi Eugene,
no sorry for the confusion. Your examples for lines is absolutely correct! My question is on this tool https://bokeh.pydata.org/en/latest/docs/user_guide/extensions_gallery/tool.html

I modified the code so instead to clear the notation on the next left click it will continue to draw. However i would like to have two separate annotations. Instead, my code keep drawing from the very last point of the annotation. If you run the code i copied in the email you will see what i mean. Drawing a word is impossible: you will have all the letters linked together.

Thanks
Andrea

···

On 1 December 2017 at 16:02, Eugene Pakhomov [email protected] wrote:

Hey Andrea,

I’m not sure what you’re asking about. The example that I’ve sent you earlier works exactly as you describe - it draws separate lines on each pan, it doesn’t link the previous stop with the next start.

Regards,

Eugene

On Friday, December 1, 2017 at 9:37:43 PM UTC+7, Andrea Tagliabue wrote:

Hi guys,
sorry but it’s still me. Nothing too serious but i wanted to have your opinion on something. I slightly modified the code in “new custom tool” https://bokeh.pydata.org/en/latest/docs/user_guide/extensions_gallery/tool.html where even if i release the left click button and i restart to draw something the code will not clear what has been drawn before.

I am using a simple.plot.line annotation. I was thinking: is there a way to prevent JS to restart drawing from the last point? I mean if i move away from the very last drawn point and i restart left clicking somewhere else JS will link the very last point with the current new one. Ideally i would like a way for drawing distinct annotations.

is it possible to achieve the same outcome with plot.line?

Many thanks again

Andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.io.state import curstate

from bokeh.models import ColumnDataSource, Tool

from bokeh.plotting import figure

output_file(‘tool.html’)

curstate().file[‘resources’].minified = False

JS_CODE_DrawAnnotation = “”"

import * as p from “core/properties”

import {GestureTool, GestureToolView} from “models/tools/gestures/gesture_tool”

export class DrawToolView extends GestureToolView

this is executed on subsequent mouse/touch moves

_pan: (e) ->

frame = @plot_model.frame

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

source.data.x.push(x)

source.data.y.push(y)

source.change.emit()

this is executed then the pan/drag ends

_pan_end: (e) -> return null

export class DrawAnnotation extends GestureTool

default_view: DrawToolView

type: “DrawAnnotation”

tool_name: “Drag Annotation”

icon: “bk-tool-icon-lasso-select”

event_type: “pan”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class DrawAnnotation(Tool):

implementation = JS_CODE_DrawAnnotation

source = Instance(ColumnDataSource)

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

plot = figure(x_range=(0, 10), y_range=(0, 10) )

plot.add_tools(DrawAnnotation(source=source_DrawAnnotation))

plot.line(x=‘x’, y=‘y’, source=source_DrawAnnotation)

show(plot)

On 30 November 2017 at 17:10, Andrea Tagliabue [email protected] wrote:

Hi Eugene,
Terrific work!! Thanks so much.
Bryan: i have been used matplotlib a lot for my apps/code but when i discovered bokeh i just thought it was much better in terms of speed and easier accessibility. Having the plot in the browser and been able to have full control of what you see is just a great experience. And you can share it so easily.

However I do have to say i really struggled to get my mind around the doc and examples. But with your and Eugene’s help i have been able to convert my old and slow matplotlib app in something much better. As soon as i will be done with my code and all my custom functionalities i will send you all my code so you can upload these very unique examples and all the community and future bokeh developer will have much less tough days.

Also. this example from Web Busino https://gist.github.com/busino/d560a58b315e7bde2321e3960ea88616/raw/6d1cc896e2e86b45b659814c4eacccf5ad353efd/bokeh_frog.html should be in the doc. That’s a such a great example of very dynamic and custom usage of the mouse control for js_on_event.
I will definitively write some personal view on https://github.com/bokeh/bokeh/issues/6370

Many thanks again

Andrea

On 30 November 2017 at 16:49, Eugene Pakhomov [email protected] wrote:

Here you go:

import bokeh.util.compiler

bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure

output_file('tool.html')
curstate().file['resources'].minified = False

JS_CODE_DrawAnnotation = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"
export class DrawToolView extends GestureToolView
  _get_scaled_frame_coords: (e) ->
    frame = @plot_model.frame
    {sx, sy} = e.bokeh
    if frame.bbox.contains(sx, sy)
      x = frame.xscales['default'].invert(sx)
      y = frame.yscales['default'].invert(sy)
      return [x, y]

  _pan_start: (e) ->
    coords = @_get_scaled_frame_coords(e)
    if coords
      [x, y] = coords
      @model.source.stream({x0: [x], y0: [y], x1: [x], y1: [y]})

  _pan: (e) ->
    coords = @_get_scaled_frame_coords(e)
    if coords
      [x, y] = coords
      last_row = @model.source.data.x0.length - 1
      @model.source.patch({x1: [[last_row, x]], y1: [[last_row, y]]})

export class DrawObliqueLine extends GestureTool
  default_view: DrawToolView
  type: "DrawObliqueLine"

  tool_name: "Line"
  icon: "bk-tool-icon-lasso-select"
  event_type: "pan"
  default_order: 12

  @define { source: [ p.Instance ] }
"""


class DrawObliqueLine(Tool):
    __implementation__ = JS_CODE_DrawAnnotation
    source = Instance(ColumnDataSource)


source_ObliqueLine = ColumnDataSource(data=dict(x0=[], y0=[], x1=[], y1=[]))

plot = figure(x_range=(0, 10), y_range=(0, 10))
plot.add_tools(DrawObliqueLine(source=source_ObliqueLine))
plot.title.text = "Pan to draw on the plot"

plot.segment(x0='x0', y0='y0', x1='x1', y1='y1', line_dash="solid",
             line_width=2, source=source_ObliqueLine)

show(plot)

Regards,

Eugene

On Thursday, November 30, 2017 at 10:07:00 PM UTC+7, Andrea Tagliabue wrote:

Hi Eugene,
i am really sorry to keep bothering at you but with no doc and very few examples i can’t see big improvements from my work. My last point of my work is to been able to draw oblique segments( ray would be the perfect solution) too.
The code i posted below is clearly wrong but i guess the idea is more and less that. I don’t understand how i can tell JS to store the initial click coordinates and while i am moving the mouse i would like to see the segment or ray drawn.
Can you suggest any idea on this?

Many thanks again
Andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.io.state import curstate

from bokeh.models import ColumnDataSource, Tool

from bokeh.plotting import figure

output_file(‘tool.html’)

curstate().file[‘resources’].minified = False

JS_CODE_DrawAnnotation = “”"

import * as p from “core/properties”

import {GestureTool, GestureToolView} from “models/tools/gestures/gesture_tool”

export class DrawToolView extends GestureToolView

this is executed on subsequent mouse/touch moves

_tap: (e) ->

frame = @plot_model.frame

@model.source.data = {x0: , y0: , x1: , y1:}

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

if source.data.y0.length == 0

source.data.x0.push(x)

source.data.y0.push(y)

else

source.data.x1.push(cb_obj.x)

source.data.y1.push(cb_obj.y)

source.change.emit()

this is executed then the pan/drag ends

_pan_end: (e) -> return null

export class DrawObliqueLine extends GestureTool

default_view: DrawToolView

type: “DrawObliqueLine”

tool_name: “Line”

icon: “bk-tool-icon-lasso-select”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class DrawObliqueLine(Tool):

implementation = JS_CODE_DrawAnnotation

source = Instance(ColumnDataSource)

source_ObliqueLine = ColumnDataSource(data=dict(x0=, y0=, x1=, y1=))

plot = figure(x_range=(0, 10), y_range=(0, 10) )

plot.add_tools(DrawObliqueLine(source=source_ObliqueLine))

plot.title.text = “Tap to draw on the plot”

plot.segment(x0=‘x0’, y0=‘y0’, x1=‘x1’, y1=‘y1’, line_dash=“solid”, line_width=2, source=source_ObliqueLine)

show(plot)

On 30 November 2017 at 14:15, Andrea Tagliabue [email protected] wrote:

yeah me too but most of the time i can’t figure out what i am doing wrong :slight_smile:

On 30 November 2017 at 13:42, Eugene Pakhomov [email protected] wrote:

Unfortunately, I don’t think so. At least, I couldn’t find any.
Usually, I just study the source code of the existing tools.

  • Eugene

On Thursday, November 30, 2017 at 7:24:57 PM UTC+7, Andrea Tagliabue wrote:

Wow really thanks for the quick answer. In order to avoid me bothering you for other issues, is there any doc regarding these custom tools? They looks to me pretty complex in terms of multiple options.
Many thanks again
Andrea

On 30 November 2017 at 13:18, Eugene Pakhomov [email protected] wrote:

Hi Andrea,

You almost got it. Here’s the working version. The things that needed to be fixed: wrong function name (must be “_tap” instead of “_pan”), wrong “default_view” (must be TapBBToolView), extra data field for the data source (you’d get an error about inconsistent column lengths).

I also realized that TapTool is not needed because all it can provide you if just a single field “event_type”, so I used a more general GestureTool instead.

import bokeh.util.compiler

bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure

output_file('tool.html')
curstate().file['resources'].minified = False

JS_CODE = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"

export class TapBBToolView extends GestureToolView

  # this is executed on subsequent mouse/touch moves
  _tap: (e) ->
    frame = @plot_model.frame

    {sx, sy} = e.bokeh
    if not frame.bbox.contains(sx, sy)
      return null

    x = frame.xscales['default'].invert(sx)
    y = frame.yscales['default'].invert(sy)
    source = @model.source
    source.data.x.push(x)
    source.data.y.push(y)
    source.change.emit()


export class TapBBTool extends GestureTool
  default_view: TapBBToolView
  type: "TapTool"
  event_type: "tap"
  tool_name: "H Line"
  icon: "bk-tool-icon-arrow-right"
  default_order: 12

  @define { source: [ p.Instance ] }
"""


class TapBBTool(Tool):
    __implementation__ = JS_CODE
    source = Instance(ColumnDataSource)


source_HorizontalRay = ColumnDataSource(data=dict(x=[], y=[]))

plot = figure(x_range=(0, 10), y_range=(0, 10),
              tools=[TapBBTool(source=source_HorizontalRay)])
plot.title.text = "Tap to draw on the plot"
plot.ray(x='x', y='y', angle=0, length=0, line_dash="solid", line_width=2,
         source=source_HorizontalRay)

show(plot)

Regards,

Eugene

On Thursday, November 30, 2017 at 6:58:10 PM UTC+7, Andrea Tagliabue wrote:

Hi all,
i am trying to implement a custom tool for drawing rays on tap event. I have implemented it via js_on_event and works fine but i need it as a tool since i want to give the ability of drawing rays, segments or lines and hence multiple custom tools are needed.

This code gets the plot displayed in the browser but when tapping nothing happens. I am struggling to understand what i am missing.

Would you mind to help me out?

Many thanks andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.models import ColumnDataSource, Tool

from bokeh.models.tools import Tap

from bokeh.plotting import figure

output_file(‘tool.html’)

JS_CODE = “”"

import * as p from “core/properties”

import {TapTool, TapToolView} from “models/tools/gestures/tap_tool”

export class TapBBToolView extends TapToolView

this is executed on subsequent mouse/touch moves

_pan: (e) ->

frame = @plot_model.frame

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

source.data.x.push(x)

source.data.y.push(y)

source.change.emit()

export class TapBBTool extends TapTool

default_view: TapToolView

type: “TapTool”

tool_name: “H Line”

icon: “bk-tool-icon-arrow-right”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class TapBBTool(Tool):

implementation = JS_CODE

source = Instance(ColumnDataSource)

source_HorizontalRay = ColumnDataSource(data=dict(x=, y=, angle=))

plot = figure(x_range=(0,10), y_range=(0,10), tools=[TapBBTool(source=source_HorizontalRay)])

plot.title.text =“Tap to draw on the plot”

plot.ray(x=‘x’, y=‘y’, angle = 0, length=0, line_dash=“solid”, line_width=2, source=source_HorizontalRay)

show(plot)

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/ac17d835-5909-4ac2-a552-9b94d3dba436%40continuum.io.

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

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/931b65c5-9b87-407a-9df2-7706e9ea3213%40continuum.io.

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

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/e90b70b6-a4a8-47df-befa-e0eb46e33a8f%40continuum.io.

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

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/5f8011a4-9894-4e7f-8e3a-664614bf61ac%40continuum.io.

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

Ah, right, sorry.

I think you’ll have to use MultiLine instead of Line: https://bokeh.pydata.org/en/latest/docs/reference/models/glyphs/multi_line.html

But most of the code will be pretty much the same as in my example - you add a new row to the data source on each _pan_start and change the last row on each _pan (either by “patch” or by a direct change with a subsequent “change.emit()”).

Regards,

Eugene

···

On Friday, December 1, 2017 at 10:06:47 PM UTC+7, Andrea Tagliabue wrote:

Hi Eugene,
no sorry for the confusion. Your examples for lines is absolutely correct! My question is on this tool https://bokeh.pydata.org/en/latest/docs/user_guide/extensions_gallery/tool.html

I modified the code so instead to clear the notation on the next left click it will continue to draw. However i would like to have two separate annotations. Instead, my code keep drawing from the very last point of the annotation. If you run the code i copied in the email you will see what i mean. Drawing a word is impossible: you will have all the letters linked together.

Thanks
Andrea

On 1 December 2017 at 16:02, Eugene Pakhomov [email protected] wrote:

Hey Andrea,

I’m not sure what you’re asking about. The example that I’ve sent you earlier works exactly as you describe - it draws separate lines on each pan, it doesn’t link the previous stop with the next start.

Regards,

Eugene

On Friday, December 1, 2017 at 9:37:43 PM UTC+7, Andrea Tagliabue wrote:

Hi guys,
sorry but it’s still me. Nothing too serious but i wanted to have your opinion on something. I slightly modified the code in “new custom tool” https://bokeh.pydata.org/en/latest/docs/user_guide/extensions_gallery/tool.html where even if i release the left click button and i restart to draw something the code will not clear what has been drawn before.

I am using a simple.plot.line annotation. I was thinking: is there a way to prevent JS to restart drawing from the last point? I mean if i move away from the very last drawn point and i restart left clicking somewhere else JS will link the very last point with the current new one. Ideally i would like a way for drawing distinct annotations.

is it possible to achieve the same outcome with plot.line?

Many thanks again

Andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.io.state import curstate

from bokeh.models import ColumnDataSource, Tool

from bokeh.plotting import figure

output_file(‘tool.html’)

curstate().file[‘resources’].minified = False

JS_CODE_DrawAnnotation = “”"

import * as p from “core/properties”

import {GestureTool, GestureToolView} from “models/tools/gestures/gesture_tool”

export class DrawToolView extends GestureToolView

this is executed on subsequent mouse/touch moves

_pan: (e) ->

frame = @plot_model.frame

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

source.data.x.push(x)

source.data.y.push(y)

source.change.emit()

this is executed then the pan/drag ends

_pan_end: (e) -> return null

export class DrawAnnotation extends GestureTool

default_view: DrawToolView

type: “DrawAnnotation”

tool_name: “Drag Annotation”

icon: “bk-tool-icon-lasso-select”

event_type: “pan”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class DrawAnnotation(Tool):

implementation = JS_CODE_DrawAnnotation

source = Instance(ColumnDataSource)

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

plot = figure(x_range=(0, 10), y_range=(0, 10) )

plot.add_tools(DrawAnnotation(source=source_DrawAnnotation))

plot.line(x=‘x’, y=‘y’, source=source_DrawAnnotation)

show(plot)

On 30 November 2017 at 17:10, Andrea Tagliabue [email protected] wrote:

Hi Eugene,
Terrific work!! Thanks so much.
Bryan: i have been used matplotlib a lot for my apps/code but when i discovered bokeh i just thought it was much better in terms of speed and easier accessibility. Having the plot in the browser and been able to have full control of what you see is just a great experience. And you can share it so easily.

However I do have to say i really struggled to get my mind around the doc and examples. But with your and Eugene’s help i have been able to convert my old and slow matplotlib app in something much better. As soon as i will be done with my code and all my custom functionalities i will send you all my code so you can upload these very unique examples and all the community and future bokeh developer will have much less tough days.

Also. this example from Web Busino https://gist.github.com/busino/d560a58b315e7bde2321e3960ea88616/raw/6d1cc896e2e86b45b659814c4eacccf5ad353efd/bokeh_frog.html should be in the doc. That’s a such a great example of very dynamic and custom usage of the mouse control for js_on_event.
I will definitively write some personal view on https://github.com/bokeh/bokeh/issues/6370

Many thanks again

Andrea

On 30 November 2017 at 16:49, Eugene Pakhomov [email protected] wrote:

Here you go:

import bokeh.util.compiler

bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure

output_file('tool.html')
curstate().file['resources'].minified = False

JS_CODE_DrawAnnotation = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"
export class DrawToolView extends GestureToolView
  _get_scaled_frame_coords: (e) ->
    frame = @plot_model.frame
    {sx, sy} = e.bokeh
    if frame.bbox.contains(sx, sy)
      x = frame.xscales['default'].invert(sx)
      y = frame.yscales['default'].invert(sy)
      return [x, y]

  _pan_start: (e) ->
    coords = @_get_scaled_frame_coords(e)
    if coords
      [x, y] = coords
      @model.source.stream({x0: [x], y0: [y], x1: [x], y1: [y]})

  _pan: (e) ->
    coords = @_get_scaled_frame_coords(e)
    if coords
      [x, y] = coords
      last_row = @model.source.data.x0.length - 1
      @model.source.patch({x1: [[last_row, x]], y1: [[last_row, y]]})

export class DrawObliqueLine extends GestureTool
  default_view: DrawToolView
  type: "DrawObliqueLine"

  tool_name: "Line"
  icon: "bk-tool-icon-lasso-select"
  event_type: "pan"
  default_order: 12

  @define { source: [ p.Instance ] }
"""


class DrawObliqueLine(Tool):
    __implementation__ = JS_CODE_DrawAnnotation
    source = Instance(ColumnDataSource)


source_ObliqueLine = ColumnDataSource(data=dict(x0=[], y0=[], x1=[], y1=[]))

plot = figure(x_range=(0, 10), y_range=(0, 10))
plot.add_tools(DrawObliqueLine(source=source_ObliqueLine))
plot.title.text = "Pan to draw on the plot"

plot.segment(x0='x0', y0='y0', x1='x1', y1='y1', line_dash="solid",
             line_width=2, source=source_ObliqueLine)

show(plot)

Regards,

Eugene

On Thursday, November 30, 2017 at 10:07:00 PM UTC+7, Andrea Tagliabue wrote:

Hi Eugene,
i am really sorry to keep bothering at you but with no doc and very few examples i can’t see big improvements from my work. My last point of my work is to been able to draw oblique segments( ray would be the perfect solution) too.
The code i posted below is clearly wrong but i guess the idea is more and less that. I don’t understand how i can tell JS to store the initial click coordinates and while i am moving the mouse i would like to see the segment or ray drawn.
Can you suggest any idea on this?

Many thanks again
Andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.io.state import curstate

from bokeh.models import ColumnDataSource, Tool

from bokeh.plotting import figure

output_file(‘tool.html’)

curstate().file[‘resources’].minified = False

JS_CODE_DrawAnnotation = “”"

import * as p from “core/properties”

import {GestureTool, GestureToolView} from “models/tools/gestures/gesture_tool”

export class DrawToolView extends GestureToolView

this is executed on subsequent mouse/touch moves

_tap: (e) ->

frame = @plot_model.frame

@model.source.data = {x0: , y0: , x1: , y1:}

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

if source.data.y0.length == 0

source.data.x0.push(x)

source.data.y0.push(y)

else

source.data.x1.push(cb_obj.x)

source.data.y1.push(cb_obj.y)

source.change.emit()

this is executed then the pan/drag ends

_pan_end: (e) -> return null

export class DrawObliqueLine extends GestureTool

default_view: DrawToolView

type: “DrawObliqueLine”

tool_name: “Line”

icon: “bk-tool-icon-lasso-select”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class DrawObliqueLine(Tool):

implementation = JS_CODE_DrawAnnotation

source = Instance(ColumnDataSource)

source_ObliqueLine = ColumnDataSource(data=dict(x0=, y0=, x1=, y1=))

plot = figure(x_range=(0, 10), y_range=(0, 10) )

plot.add_tools(DrawObliqueLine(source=source_ObliqueLine))

plot.title.text = “Tap to draw on the plot”

plot.segment(x0=‘x0’, y0=‘y0’, x1=‘x1’, y1=‘y1’, line_dash=“solid”, line_width=2, source=source_ObliqueLine)

show(plot)

On 30 November 2017 at 14:15, Andrea Tagliabue [email protected] wrote:

yeah me too but most of the time i can’t figure out what i am doing wrong :slight_smile:

On 30 November 2017 at 13:42, Eugene Pakhomov [email protected] wrote:

Unfortunately, I don’t think so. At least, I couldn’t find any.
Usually, I just study the source code of the existing tools.

  • Eugene

On Thursday, November 30, 2017 at 7:24:57 PM UTC+7, Andrea Tagliabue wrote:

Wow really thanks for the quick answer. In order to avoid me bothering you for other issues, is there any doc regarding these custom tools? They looks to me pretty complex in terms of multiple options.
Many thanks again
Andrea

On 30 November 2017 at 13:18, Eugene Pakhomov [email protected] wrote:

Hi Andrea,

You almost got it. Here’s the working version. The things that needed to be fixed: wrong function name (must be “_tap” instead of “_pan”), wrong “default_view” (must be TapBBToolView), extra data field for the data source (you’d get an error about inconsistent column lengths).

I also realized that TapTool is not needed because all it can provide you if just a single field “event_type”, so I used a more general GestureTool instead.

import bokeh.util.compiler

bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure

output_file('tool.html')
curstate().file['resources'].minified = False

JS_CODE = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"

export class TapBBToolView extends GestureToolView

  # this is executed on subsequent mouse/touch moves
  _tap: (e) ->
    frame = @plot_model.frame

    {sx, sy} = e.bokeh
    if not frame.bbox.contains(sx, sy)
      return null

    x = frame.xscales['default'].invert(sx)
    y = frame.yscales['default'].invert(sy)
    source = @model.source
    source.data.x.push(x)
    source.data.y.push(y)
    source.change.emit()


export class TapBBTool extends GestureTool
  default_view: TapBBToolView
  type: "TapTool"
  event_type: "tap"
  tool_name: "H Line"
  icon: "bk-tool-icon-arrow-right"
  default_order: 12

  @define { source: [ p.Instance ] }
"""


class TapBBTool(Tool):
    __implementation__ = JS_CODE
    source = Instance(ColumnDataSource)


source_HorizontalRay = ColumnDataSource(data=dict(x=[], y=[]))

plot = figure(x_range=(0, 10), y_range=(0, 10),
              tools=[TapBBTool(source=source_HorizontalRay)])
plot.title.text = "Tap to draw on the plot"
plot.ray(x='x', y='y', angle=0, length=0, line_dash="solid", line_width=2,
         source=source_HorizontalRay)

show(plot)

Regards,

Eugene

On Thursday, November 30, 2017 at 6:58:10 PM UTC+7, Andrea Tagliabue wrote:

Hi all,
i am trying to implement a custom tool for drawing rays on tap event. I have implemented it via js_on_event and works fine but i need it as a tool since i want to give the ability of drawing rays, segments or lines and hence multiple custom tools are needed.

This code gets the plot displayed in the browser but when tapping nothing happens. I am struggling to understand what i am missing.

Would you mind to help me out?

Many thanks andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.models import ColumnDataSource, Tool

from bokeh.models.tools import Tap

from bokeh.plotting import figure

output_file(‘tool.html’)

JS_CODE = “”"

import * as p from “core/properties”

import {TapTool, TapToolView} from “models/tools/gestures/tap_tool”

export class TapBBToolView extends TapToolView

this is executed on subsequent mouse/touch moves

_pan: (e) ->

frame = @plot_model.frame

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

source.data.x.push(x)

source.data.y.push(y)

source.change.emit()

export class TapBBTool extends TapTool

default_view: TapToolView

type: “TapTool”

tool_name: “H Line”

icon: “bk-tool-icon-arrow-right”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class TapBBTool(Tool):

implementation = JS_CODE

source = Instance(ColumnDataSource)

source_HorizontalRay = ColumnDataSource(data=dict(x=, y=, angle=))

plot = figure(x_range=(0,10), y_range=(0,10), tools=[TapBBTool(source=source_HorizontalRay)])

plot.title.text =“Tap to draw on the plot”

plot.ray(x=‘x’, y=‘y’, angle = 0, length=0, line_dash=“solid”, line_width=2, source=source_HorizontalRay)

show(plot)

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/ac17d835-5909-4ac2-a552-9b94d3dba436%40continuum.io.

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

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/931b65c5-9b87-407a-9df2-7706e9ea3213%40continuum.io.

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

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/e90b70b6-a4a8-47df-befa-e0eb46e33a8f%40continuum.io.

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

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/5f8011a4-9894-4e7f-8e3a-664614bf61ac%40continuum.io.

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

Hi Eugene,
i am not sure about what you are referring to. I am not not using your code. I am using the code i found on https://bokeh.pydata.org/en/latest/docs/user_guide/extensions_gallery/tool.html

i literally just deleted this:

 # this is executed when the pan/drag event starts
  _pan_start: (e) ->
    @model.source.data = {x: [], y: []}
···

so instead to delete all the previous annotations it keeps everything. So if i use multi line as you suggested should be something like this but i am not sure i to modify the JS code.

import bokeh.util.compiler
bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure
output_file('tool.html')
curstate().file['resources'].minified = False
JS_CODE_DrawAnnotation = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"
export class DrawToolView extends GestureToolView
# this is executed on subsequent mouse/touch moves
_pan: (e) ->
frame = @plot_model.frame
{sx, sy} = e.bokeh
if not frame.bbox.contains(sx, sy)
return null
x = frame.xscales['default'].invert(sx)
y = frame.yscales['default'].invert(sy)
source = @model.source
                                source.data.xs.push(x)
source.data.ys.push(y)
                                source.change.emit()
# this is executed then the pan/drag ends
_pan_end: (e) -> return null
export class DrawAnnotation extends GestureTool
default_view: DrawToolView
type: "DrawAnnotation"
tool_name: "Drag Annotation"
icon: "bk-tool-icon-lasso-select"
event_type: "pan"
default_order: 12
@define { source: [ p.Instance ] }
"""
class DrawAnnotation(Tool):
__implementation__ = JS_CODE_DrawAnnotation
source = Instance(ColumnDataSource)
source_DrawAnnotation = ColumnDataSource(data=dict(xs=[],ys=[]))
plot = figure(x_range=(0, 10), y_range=(0, 10) )
plot.add_tools(DrawAnnotation(source=source_DrawAnnotation))
plot.multi_line(xs='xs', ys='ys', source=source_DrawAnnotation)
show(plot)

Thanks,

Andrea

On 1 December 2017 at 16:13, Eugene Pakhomov [email protected] wrote:

Ah, right, sorry.

I think you’ll have to use MultiLine instead of Line: https://bokeh.pydata.org/en/latest/docs/reference/models/glyphs/multi_line.html

But most of the code will be pretty much the same as in my example - you add a new row to the data source on each _pan_start and change the last row on each _pan (either by “patch” or by a direct change with a subsequent “change.emit()”).

Regards,

Eugene

On Friday, December 1, 2017 at 10:06:47 PM UTC+7, Andrea Tagliabue wrote:

Hi Eugene,
no sorry for the confusion. Your examples for lines is absolutely correct! My question is on this tool https://bokeh.pydata.org/en/latest/docs/user_guide/extensions_gallery/tool.html

I modified the code so instead to clear the notation on the next left click it will continue to draw. However i would like to have two separate annotations. Instead, my code keep drawing from the very last point of the annotation. If you run the code i copied in the email you will see what i mean. Drawing a word is impossible: you will have all the letters linked together.

Thanks
Andrea

On 1 December 2017 at 16:02, Eugene Pakhomov [email protected] wrote:

Hey Andrea,

I’m not sure what you’re asking about. The example that I’ve sent you earlier works exactly as you describe - it draws separate lines on each pan, it doesn’t link the previous stop with the next start.

Regards,

Eugene

On Friday, December 1, 2017 at 9:37:43 PM UTC+7, Andrea Tagliabue wrote:

Hi guys,
sorry but it’s still me. Nothing too serious but i wanted to have your opinion on something. I slightly modified the code in “new custom tool” https://bokeh.pydata.org/en/latest/docs/user_guide/extensions_gallery/tool.html where even if i release the left click button and i restart to draw something the code will not clear what has been drawn before.

I am using a simple.plot.line annotation. I was thinking: is there a way to prevent JS to restart drawing from the last point? I mean if i move away from the very last drawn point and i restart left clicking somewhere else JS will link the very last point with the current new one. Ideally i would like a way for drawing distinct annotations.

is it possible to achieve the same outcome with plot.line?

Many thanks again

Andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.io.state import curstate

from bokeh.models import ColumnDataSource, Tool

from bokeh.plotting import figure

output_file(‘tool.html’)

curstate().file[‘resources’].minified = False

JS_CODE_DrawAnnotation = “”"

import * as p from “core/properties”

import {GestureTool, GestureToolView} from “models/tools/gestures/gesture_tool”

export class DrawToolView extends GestureToolView

this is executed on subsequent mouse/touch moves

_pan: (e) ->

frame = @plot_model.frame

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

source.data.x.push(x)

source.data.y.push(y)

source.change.emit()

this is executed then the pan/drag ends

_pan_end: (e) -> return null

export class DrawAnnotation extends GestureTool

default_view: DrawToolView

type: “DrawAnnotation”

tool_name: “Drag Annotation”

icon: “bk-tool-icon-lasso-select”

event_type: “pan”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class DrawAnnotation(Tool):

implementation = JS_CODE_DrawAnnotation

source = Instance(ColumnDataSource)

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

plot = figure(x_range=(0, 10), y_range=(0, 10) )

plot.add_tools(DrawAnnotation(source=source_DrawAnnotation))

plot.line(x=‘x’, y=‘y’, source=source_DrawAnnotation)

show(plot)

On 30 November 2017 at 17:10, Andrea Tagliabue [email protected] wrote:

Hi Eugene,
Terrific work!! Thanks so much.
Bryan: i have been used matplotlib a lot for my apps/code but when i discovered bokeh i just thought it was much better in terms of speed and easier accessibility. Having the plot in the browser and been able to have full control of what you see is just a great experience. And you can share it so easily.

However I do have to say i really struggled to get my mind around the doc and examples. But with your and Eugene’s help i have been able to convert my old and slow matplotlib app in something much better. As soon as i will be done with my code and all my custom functionalities i will send you all my code so you can upload these very unique examples and all the community and future bokeh developer will have much less tough days.

Also. this example from Web Busino https://gist.github.com/busino/d560a58b315e7bde2321e3960ea88616/raw/6d1cc896e2e86b45b659814c4eacccf5ad353efd/bokeh_frog.html should be in the doc. That’s a such a great example of very dynamic and custom usage of the mouse control for js_on_event.
I will definitively write some personal view on https://github.com/bokeh/bokeh/issues/6370

Many thanks again

Andrea

On 30 November 2017 at 16:49, Eugene Pakhomov [email protected] wrote:

Here you go:

import bokeh.util.compiler

bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure

output_file('tool.html')
curstate().file['resources'].minified = False

JS_CODE_DrawAnnotation = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"
export class DrawToolView extends GestureToolView
  _get_scaled_frame_coords: (e) ->
    frame = @plot_model.frame
    {sx, sy} = e.bokeh
    if frame.bbox.contains(sx, sy)
      x = frame.xscales['default'].invert(sx)
      y = frame.yscales['default'].invert(sy)
      return [x, y]

  _pan_start: (e) ->
    coords = @_get_scaled_frame_coords(e)
    if coords
      [x, y] = coords
      @model.source.stream({x0: [x], y0: [y], x1: [x], y1: [y]})

  _pan: (e) ->
    coords = @_get_scaled_frame_coords(e)
    if coords
      [x, y] = coords
      last_row = @model.source.data.x0.length - 1
      @model.source.patch({x1: [[last_row, x]], y1: [[last_row, y]]})

export class DrawObliqueLine extends GestureTool
  default_view: DrawToolView
  type: "DrawObliqueLine"

  tool_name: "Line"
  icon: "bk-tool-icon-lasso-select"
  event_type: "pan"
  default_order: 12

  @define { source: [ p.Instance ] }
"""


class DrawObliqueLine(Tool):
    __implementation__ = JS_CODE_DrawAnnotation
    source = Instance(ColumnDataSource)


source_ObliqueLine = ColumnDataSource(data=dict(x0=[], y0=[], x1=[], y1=[]))

plot = figure(x_range=(0, 10), y_range=(0, 10))
plot.add_tools(DrawObliqueLine(source=source_ObliqueLine))
plot.title.text = "Pan to draw on the plot"

plot.segment(x0='x0', y0='y0', x1='x1', y1='y1', line_dash="solid",
             line_width=2, source=source_ObliqueLine)

show(plot)

Regards,

Eugene

On Thursday, November 30, 2017 at 10:07:00 PM UTC+7, Andrea Tagliabue wrote:

Hi Eugene,
i am really sorry to keep bothering at you but with no doc and very few examples i can’t see big improvements from my work. My last point of my work is to been able to draw oblique segments( ray would be the perfect solution) too.
The code i posted below is clearly wrong but i guess the idea is more and less that. I don’t understand how i can tell JS to store the initial click coordinates and while i am moving the mouse i would like to see the segment or ray drawn.
Can you suggest any idea on this?

Many thanks again
Andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.io.state import curstate

from bokeh.models import ColumnDataSource, Tool

from bokeh.plotting import figure

output_file(‘tool.html’)

curstate().file[‘resources’].minified = False

JS_CODE_DrawAnnotation = “”"

import * as p from “core/properties”

import {GestureTool, GestureToolView} from “models/tools/gestures/gesture_tool”

export class DrawToolView extends GestureToolView

this is executed on subsequent mouse/touch moves

_tap: (e) ->

frame = @plot_model.frame

@model.source.data = {x0: , y0: , x1: , y1:}

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

if source.data.y0.length == 0

source.data.x0.push(x)

source.data.y0.push(y)

else

source.data.x1.push(cb_obj.x)

source.data.y1.push(cb_obj.y)

source.change.emit()

this is executed then the pan/drag ends

_pan_end: (e) -> return null

export class DrawObliqueLine extends GestureTool

default_view: DrawToolView

type: “DrawObliqueLine”

tool_name: “Line”

icon: “bk-tool-icon-lasso-select”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class DrawObliqueLine(Tool):

implementation = JS_CODE_DrawAnnotation

source = Instance(ColumnDataSource)

source_ObliqueLine = ColumnDataSource(data=dict(x0=, y0=, x1=, y1=))

plot = figure(x_range=(0, 10), y_range=(0, 10) )

plot.add_tools(DrawObliqueLine(source=source_ObliqueLine))

plot.title.text = “Tap to draw on the plot”

plot.segment(x0=‘x0’, y0=‘y0’, x1=‘x1’, y1=‘y1’, line_dash=“solid”, line_width=2, source=source_ObliqueLine)

show(plot)

On 30 November 2017 at 14:15, Andrea Tagliabue [email protected] wrote:

yeah me too but most of the time i can’t figure out what i am doing wrong :slight_smile:

On 30 November 2017 at 13:42, Eugene Pakhomov [email protected] wrote:

Unfortunately, I don’t think so. At least, I couldn’t find any.
Usually, I just study the source code of the existing tools.

  • Eugene

On Thursday, November 30, 2017 at 7:24:57 PM UTC+7, Andrea Tagliabue wrote:

Wow really thanks for the quick answer. In order to avoid me bothering you for other issues, is there any doc regarding these custom tools? They looks to me pretty complex in terms of multiple options.
Many thanks again
Andrea

On 30 November 2017 at 13:18, Eugene Pakhomov [email protected] wrote:

Hi Andrea,

You almost got it. Here’s the working version. The things that needed to be fixed: wrong function name (must be “_tap” instead of “_pan”), wrong “default_view” (must be TapBBToolView), extra data field for the data source (you’d get an error about inconsistent column lengths).

I also realized that TapTool is not needed because all it can provide you if just a single field “event_type”, so I used a more general GestureTool instead.

import bokeh.util.compiler

bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure

output_file('tool.html')
curstate().file['resources'].minified = False

JS_CODE = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"

export class TapBBToolView extends GestureToolView

  # this is executed on subsequent mouse/touch moves
  _tap: (e) ->
    frame = @plot_model.frame

    {sx, sy} = e.bokeh
    if not frame.bbox.contains(sx, sy)
      return null

    x = frame.xscales['default'].invert(sx)
    y = frame.yscales['default'].invert(sy)
    source = @model.source
    source.data.x.push(x)
    source.data.y.push(y)
    source.change.emit()


export class TapBBTool extends GestureTool
  default_view: TapBBToolView
  type: "TapTool"
  event_type: "tap"
  tool_name: "H Line"
  icon: "bk-tool-icon-arrow-right"
  default_order: 12

  @define { source: [ p.Instance ] }
"""


class TapBBTool(Tool):
    __implementation__ = JS_CODE
    source = Instance(ColumnDataSource)


source_HorizontalRay = ColumnDataSource(data=dict(x=[], y=[]))

plot = figure(x_range=(0, 10), y_range=(0, 10),
              tools=[TapBBTool(source=source_HorizontalRay)])
plot.title.text = "Tap to draw on the plot"
plot.ray(x='x', y='y', angle=0, length=0, line_dash="solid", line_width=2,
         source=source_HorizontalRay)

show(plot)

Regards,

Eugene

On Thursday, November 30, 2017 at 6:58:10 PM UTC+7, Andrea Tagliabue wrote:

Hi all,
i am trying to implement a custom tool for drawing rays on tap event. I have implemented it via js_on_event and works fine but i need it as a tool since i want to give the ability of drawing rays, segments or lines and hence multiple custom tools are needed.

This code gets the plot displayed in the browser but when tapping nothing happens. I am struggling to understand what i am missing.

Would you mind to help me out?

Many thanks andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.models import ColumnDataSource, Tool

from bokeh.models.tools import Tap

from bokeh.plotting import figure

output_file(‘tool.html’)

JS_CODE = “”"

import * as p from “core/properties”

import {TapTool, TapToolView} from “models/tools/gestures/tap_tool”

export class TapBBToolView extends TapToolView

this is executed on subsequent mouse/touch moves

_pan: (e) ->

frame = @plot_model.frame

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

source.data.x.push(x)

source.data.y.push(y)

source.change.emit()

export class TapBBTool extends TapTool

default_view: TapToolView

type: “TapTool”

tool_name: “H Line”

icon: “bk-tool-icon-arrow-right”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class TapBBTool(Tool):

implementation = JS_CODE

source = Instance(ColumnDataSource)

source_HorizontalRay = ColumnDataSource(data=dict(x=, y=, angle=))

plot = figure(x_range=(0,10), y_range=(0,10), tools=[TapBBTool(source=source_HorizontalRay)])

plot.title.text =“Tap to draw on the plot”

plot.ray(x=‘x’, y=‘y’, angle = 0, length=0, line_dash=“solid”, line_width=2, source=source_HorizontalRay)

show(plot)

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/ac17d835-5909-4ac2-a552-9b94d3dba436%40continuum.io.

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

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/931b65c5-9b87-407a-9df2-7706e9ea3213%40continuum.io.

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

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/e90b70b6-a4a8-47df-befa-e0eb46e33a8f%40continuum.io.

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

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/5f8011a4-9894-4e7f-8e3a-664614bf61ac%40continuum.io.

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

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/c9f8b041-6ac7-4651-b754-4bad3e4ff145%40continuum.io.

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

No, what I mean is, the functionality that you need is more similar to the code that I have provided earlier than to the example that you’ve used.
Single annotation for the example from the extensions gallery is more similar to the single line annotation, multiple non-continuous annotations are more similar to a multi-line annotation.

  • Eugene
···

On Friday, December 1, 2017 at 10:33:14 PM UTC+7, Andrea Tagliabue wrote:

Hi Eugene,
i am not sure about what you are referring to. I am not not using your code. I am using the code i found on https://bokeh.pydata.org/en/latest/docs/user_guide/extensions_gallery/tool.html

i literally just deleted this:

 # this is executed when the pan/drag event starts
  _pan_start: (e) ->
    @model.source.data = {x: [], y: []}

so instead to delete all the previous annotations it keeps everything. So if i use multi line as you suggested should be something like this but i am not sure i to modify the JS code.

import bokeh.util.compiler
bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure
output_file('tool.html')
curstate().file['resources'].

minified = False
JS_CODE_DrawAnnotation = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/                            gesture_tool"
export class DrawToolView extends GestureToolView
# this is executed on subsequent mouse/touch moves
_pan: (e) ->
frame = @plot_model.frame
{sx, sy} = e.bokeh
if not frame.bbox.contains(sx, sy)
return null
x = frame.xscales['default'].                                invert(sx)
y = frame.yscales['default'].                                invert(sy)
source = @model.source
                                source.data.xs.push(x)
source.data.ys.push(y)
                                source.change.emit()
# this is executed then the pan/drag ends
_pan_end: (e) -> return null
export class DrawAnnotation extends GestureTool
default_view: DrawToolView
type: "DrawAnnotation"
tool_name: "Drag Annotation"
icon: "bk-tool-icon-lasso-select"
event_type: "pan"
default_order: 12
@define { source: [ p.Instance ] }
"""
class DrawAnnotation(Tool):
__implementation__ = JS_CODE_DrawAnnotation
source = Instance(ColumnDataSource)
source_DrawAnnotation = ColumnDataSource(data=dict(xs=
[],ys=[]))
plot = figure(x_range=(0, 10), y_range=(0, 10) )
plot.add_tools(DrawAnnotation(
source=source_DrawAnnotation))
plot.multi_line(xs='xs', ys='ys', source=source_DrawAnnotation)
show(plot)

Thanks,

Andrea

On 1 December 2017 at 16:13, Eugene Pakhomov [email protected] wrote:

Ah, right, sorry.

I think you’ll have to use MultiLine instead of Line: https://bokeh.pydata.org/en/latest/docs/reference/models/glyphs/multi_line.html

But most of the code will be pretty much the same as in my example - you add a new row to the data source on each _pan_start and change the last row on each _pan (either by “patch” or by a direct change with a subsequent “change.emit()”).

Regards,

Eugene

On Friday, December 1, 2017 at 10:06:47 PM UTC+7, Andrea Tagliabue wrote:

Hi Eugene,
no sorry for the confusion. Your examples for lines is absolutely correct! My question is on this tool https://bokeh.pydata.org/en/latest/docs/user_guide/extensions_gallery/tool.html

I modified the code so instead to clear the notation on the next left click it will continue to draw. However i would like to have two separate annotations. Instead, my code keep drawing from the very last point of the annotation. If you run the code i copied in the email you will see what i mean. Drawing a word is impossible: you will have all the letters linked together.

Thanks
Andrea

On 1 December 2017 at 16:02, Eugene Pakhomov [email protected] wrote:

Hey Andrea,

I’m not sure what you’re asking about. The example that I’ve sent you earlier works exactly as you describe - it draws separate lines on each pan, it doesn’t link the previous stop with the next start.

Regards,

Eugene

On Friday, December 1, 2017 at 9:37:43 PM UTC+7, Andrea Tagliabue wrote:

Hi guys,
sorry but it’s still me. Nothing too serious but i wanted to have your opinion on something. I slightly modified the code in “new custom tool” https://bokeh.pydata.org/en/latest/docs/user_guide/extensions_gallery/tool.html where even if i release the left click button and i restart to draw something the code will not clear what has been drawn before.

I am using a simple.plot.line annotation. I was thinking: is there a way to prevent JS to restart drawing from the last point? I mean if i move away from the very last drawn point and i restart left clicking somewhere else JS will link the very last point with the current new one. Ideally i would like a way for drawing distinct annotations.

is it possible to achieve the same outcome with plot.line?

Many thanks again

Andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.io.state import curstate

from bokeh.models import ColumnDataSource, Tool

from bokeh.plotting import figure

output_file(‘tool.html’)

curstate().file[‘resources’].minified = False

JS_CODE_DrawAnnotation = “”"

import * as p from “core/properties”

import {GestureTool, GestureToolView} from “models/tools/gestures/gesture_tool”

export class DrawToolView extends GestureToolView

this is executed on subsequent mouse/touch moves

_pan: (e) ->

frame = @plot_model.frame

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

source.data.x.push(x)

source.data.y.push(y)

source.change.emit()

this is executed then the pan/drag ends

_pan_end: (e) -> return null

export class DrawAnnotation extends GestureTool

default_view: DrawToolView

type: “DrawAnnotation”

tool_name: “Drag Annotation”

icon: “bk-tool-icon-lasso-select”

event_type: “pan”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class DrawAnnotation(Tool):

implementation = JS_CODE_DrawAnnotation

source = Instance(ColumnDataSource)

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

plot = figure(x_range=(0, 10), y_range=(0, 10) )

plot.add_tools(DrawAnnotation(source=source_DrawAnnotation))

plot.line(x=‘x’, y=‘y’, source=source_DrawAnnotation)

show(plot)

On 30 November 2017 at 17:10, Andrea Tagliabue [email protected] wrote:

Hi Eugene,
Terrific work!! Thanks so much.
Bryan: i have been used matplotlib a lot for my apps/code but when i discovered bokeh i just thought it was much better in terms of speed and easier accessibility. Having the plot in the browser and been able to have full control of what you see is just a great experience. And you can share it so easily.

However I do have to say i really struggled to get my mind around the doc and examples. But with your and Eugene’s help i have been able to convert my old and slow matplotlib app in something much better. As soon as i will be done with my code and all my custom functionalities i will send you all my code so you can upload these very unique examples and all the community and future bokeh developer will have much less tough days.

Also. this example from Web Busino https://gist.github.com/busino/d560a58b315e7bde2321e3960ea88616/raw/6d1cc896e2e86b45b659814c4eacccf5ad353efd/bokeh_frog.html should be in the doc. That’s a such a great example of very dynamic and custom usage of the mouse control for js_on_event.
I will definitively write some personal view on https://github.com/bokeh/bokeh/issues/6370

Many thanks again

Andrea

On 30 November 2017 at 16:49, Eugene Pakhomov [email protected] wrote:

Here you go:

import bokeh.util.compiler

bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure

output_file('tool.html')
curstate().file['resources'].minified = False

JS_CODE_DrawAnnotation = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"
export class DrawToolView extends GestureToolView
  _get_scaled_frame_coords: (e) ->
    frame = @plot_model.frame
    {sx, sy} = e.bokeh
    if frame.bbox.contains(sx, sy)
      x = frame.xscales['default'].invert(sx)
      y = frame.yscales['default'].invert(sy)
      return [x, y]

  _pan_start: (e) ->
    coords = @_get_scaled_frame_coords(e)
    if coords
      [x, y] = coords
      @model.source.stream({x0: [x], y0: [y], x1: [x], y1: [y]})

  _pan: (e) ->
    coords = @_get_scaled_frame_coords(e)
    if coords
      [x, y] = coords
      last_row = @model.source.data.x0.length - 1
      @model.source.patch({x1: [[last_row, x]], y1: [[last_row, y]]})

export class DrawObliqueLine extends GestureTool
  default_view: DrawToolView
  type: "DrawObliqueLine"

  tool_name: "Line"
  icon: "bk-tool-icon-lasso-select"
  event_type: "pan"
  default_order: 12

  @define { source: [ p.Instance ] }
"""


class DrawObliqueLine(Tool):
    __implementation__ = JS_CODE_DrawAnnotation
    source = Instance(ColumnDataSource)


source_ObliqueLine = ColumnDataSource(data=dict(x0=[], y0=[], x1=[], y1=[]))

plot = figure(x_range=(0, 10), y_range=(0, 10))
plot.add_tools(DrawObliqueLine(source=source_ObliqueLine))
plot.title.text = "Pan to draw on the plot"

plot.segment(x0='x0', y0='y0', x1='x1', y1='y1', line_dash="solid",
             line_width=2, source=source_ObliqueLine)

show(plot)

Regards,

Eugene

On Thursday, November 30, 2017 at 10:07:00 PM UTC+7, Andrea Tagliabue wrote:

Hi Eugene,
i am really sorry to keep bothering at you but with no doc and very few examples i can’t see big improvements from my work. My last point of my work is to been able to draw oblique segments( ray would be the perfect solution) too.
The code i posted below is clearly wrong but i guess the idea is more and less that. I don’t understand how i can tell JS to store the initial click coordinates and while i am moving the mouse i would like to see the segment or ray drawn.
Can you suggest any idea on this?

Many thanks again
Andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.io.state import curstate

from bokeh.models import ColumnDataSource, Tool

from bokeh.plotting import figure

output_file(‘tool.html’)

curstate().file[‘resources’].minified = False

JS_CODE_DrawAnnotation = “”"

import * as p from “core/properties”

import {GestureTool, GestureToolView} from “models/tools/gestures/gesture_tool”

export class DrawToolView extends GestureToolView

this is executed on subsequent mouse/touch moves

_tap: (e) ->

frame = @plot_model.frame

@model.source.data = {x0: , y0: , x1: , y1:}

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

if source.data.y0.length == 0

source.data.x0.push(x)

source.data.y0.push(y)

else

source.data.x1.push(cb_obj.x)

source.data.y1.push(cb_obj.y)

source.change.emit()

this is executed then the pan/drag ends

_pan_end: (e) -> return null

export class DrawObliqueLine extends GestureTool

default_view: DrawToolView

type: “DrawObliqueLine”

tool_name: “Line”

icon: “bk-tool-icon-lasso-select”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class DrawObliqueLine(Tool):

implementation = JS_CODE_DrawAnnotation

source = Instance(ColumnDataSource)

source_ObliqueLine = ColumnDataSource(data=dict(x0=, y0=, x1=, y1=))

plot = figure(x_range=(0, 10), y_range=(0, 10) )

plot.add_tools(DrawObliqueLine(source=source_ObliqueLine))

plot.title.text = “Tap to draw on the plot”

plot.segment(x0=‘x0’, y0=‘y0’, x1=‘x1’, y1=‘y1’, line_dash=“solid”, line_width=2, source=source_ObliqueLine)

show(plot)

On 30 November 2017 at 14:15, Andrea Tagliabue [email protected] wrote:

yeah me too but most of the time i can’t figure out what i am doing wrong :slight_smile:

On 30 November 2017 at 13:42, Eugene Pakhomov [email protected] wrote:

Unfortunately, I don’t think so. At least, I couldn’t find any.
Usually, I just study the source code of the existing tools.

  • Eugene

On Thursday, November 30, 2017 at 7:24:57 PM UTC+7, Andrea Tagliabue wrote:

Wow really thanks for the quick answer. In order to avoid me bothering you for other issues, is there any doc regarding these custom tools? They looks to me pretty complex in terms of multiple options.
Many thanks again
Andrea

On 30 November 2017 at 13:18, Eugene Pakhomov [email protected] wrote:

Hi Andrea,

You almost got it. Here’s the working version. The things that needed to be fixed: wrong function name (must be “_tap” instead of “_pan”), wrong “default_view” (must be TapBBToolView), extra data field for the data source (you’d get an error about inconsistent column lengths).

I also realized that TapTool is not needed because all it can provide you if just a single field “event_type”, so I used a more general GestureTool instead.

import bokeh.util.compiler

bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure

output_file('tool.html')
curstate().file['resources'].minified = False

JS_CODE = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"

export class TapBBToolView extends GestureToolView

  # this is executed on subsequent mouse/touch moves
  _tap: (e) ->
    frame = @plot_model.frame

    {sx, sy} = e.bokeh
    if not frame.bbox.contains(sx, sy)
      return null

    x = frame.xscales['default'].invert(sx)
    y = frame.yscales['default'].invert(sy)
    source = @model.source
    source.data.x.push(x)
    source.data.y.push(y)
    source.change.emit()


export class TapBBTool extends GestureTool
  default_view: TapBBToolView
  type: "TapTool"
  event_type: "tap"
  tool_name: "H Line"
  icon: "bk-tool-icon-arrow-right"
  default_order: 12

  @define { source: [ p.Instance ] }
"""


class TapBBTool(Tool):
    __implementation__ = JS_CODE
    source = Instance(ColumnDataSource)


source_HorizontalRay = ColumnDataSource(data=dict(x=[], y=[]))

plot = figure(x_range=(0, 10), y_range=(0, 10),
              tools=[TapBBTool(source=source_HorizontalRay)])
plot.title.text = "Tap to draw on the plot"
plot.ray(x='x', y='y', angle=0, length=0, line_dash="solid", line_width=2,
         source=source_HorizontalRay)

show(plot)

Regards,

Eugene

On Thursday, November 30, 2017 at 6:58:10 PM UTC+7, Andrea Tagliabue wrote:

Hi all,
i am trying to implement a custom tool for drawing rays on tap event. I have implemented it via js_on_event and works fine but i need it as a tool since i want to give the ability of drawing rays, segments or lines and hence multiple custom tools are needed.

This code gets the plot displayed in the browser but when tapping nothing happens. I am struggling to understand what i am missing.

Would you mind to help me out?

Many thanks andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.models import ColumnDataSource, Tool

from bokeh.models.tools import Tap

from bokeh.plotting import figure

output_file(‘tool.html’)

JS_CODE = “”"

import * as p from “core/properties”

import {TapTool, TapToolView} from “models/tools/gestures/tap_tool”

export class TapBBToolView extends TapToolView

this is executed on subsequent mouse/touch moves

_pan: (e) ->

frame = @plot_model.frame

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

source.data.x.push(x)

source.data.y.push(y)

source.change.emit()

export class TapBBTool extends TapTool

default_view: TapToolView

type: “TapTool”

tool_name: “H Line”

icon: “bk-tool-icon-arrow-right”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class TapBBTool(Tool):

implementation = JS_CODE

source = Instance(ColumnDataSource)

source_HorizontalRay = ColumnDataSource(data=dict(x=, y=, angle=))

plot = figure(x_range=(0,10), y_range=(0,10), tools=[TapBBTool(source=source_HorizontalRay)])

plot.title.text =“Tap to draw on the plot”

plot.ray(x=‘x’, y=‘y’, angle = 0, length=0, line_dash=“solid”, line_width=2, source=source_HorizontalRay)

show(plot)

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/ac17d835-5909-4ac2-a552-9b94d3dba436%40continuum.io.

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

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/931b65c5-9b87-407a-9df2-7706e9ea3213%40continuum.io.

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

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/e90b70b6-a4a8-47df-befa-e0eb46e33a8f%40continuum.io.

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

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/5f8011a4-9894-4e7f-8e3a-664614bf61ac%40continuum.io.

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

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/c9f8b041-6ac7-4651-b754-4bad3e4ff145%40continuum.io.

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

Hi Eugene,
Ok got it! sorry for the confusion! I will try to implement it!
Thanks,
Andrea

···

On 1 December 2017 at 16:37, Eugene Pakhomov [email protected] wrote:

No, what I mean is, the functionality that you need is more similar to the code that I have provided earlier than to the example that you’ve used.
Single annotation for the example from the extensions gallery is more similar to the single line annotation, multiple non-continuous annotations are more similar to a multi-line annotation.

  • Eugene

On Friday, December 1, 2017 at 10:33:14 PM UTC+7, Andrea Tagliabue wrote:

Hi Eugene,
i am not sure about what you are referring to. I am not not using your code. I am using the code i found on https://bokeh.pydata.org/en/latest/docs/user_guide/extensions_gallery/tool.html

i literally just deleted this:

 # this is executed when the pan/drag event starts
  _pan_start: (e) ->
    @model.source.data = {x: [], y: []}

so instead to delete all the previous annotations it keeps everything. So if i use multi line as you suggested should be something like this but i am not sure i to modify the JS code.

import bokeh.util.compiler
bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure
output_file('tool.html')
curstate().file['resources'].m

inified = False
JS_CODE_DrawAnnotation = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture                            _tool"
export class DrawToolView extends GestureToolView
# this is executed on subsequent mouse/touch moves
_pan: (e) ->
frame = @plot_model.frame
{sx, sy} = e.bokeh
if not frame.bbox.contains(sx, sy)
return null
x = frame.xscales['default'].inver                                t(sx)
y = frame.yscales['default'].inver                                t(sy)
source = @model.source
                                source.data.xs.push(x)
source.data.ys.push(y)
                                source.change.emit()
# this is executed then the pan/drag ends
_pan_end: (e) -> return null
export class DrawAnnotation extends GestureTool
default_view: DrawToolView
type: "DrawAnnotation"
tool_name: "Drag Annotation"
icon: "bk-tool-icon-lasso-select"
event_type: "pan"
default_order: 12
@define { source: [ p.Instance ] }
"""
class DrawAnnotation(Tool):
__implementation__ = JS_CODE_DrawAnnotation
source = Instance(ColumnDataSource)
source_DrawAnnotation = ColumnDataSource(data=dict(xs=
[],ys=[]))
plot = figure(x_range=(0, 10), y_range=(0, 10) )
plot.add_tools(DrawAnnotation(
source=source_DrawAnnotation))
plot.multi_line(xs='xs', ys='ys', source=source_DrawAnnotation)
show(plot)

Thanks,

Andrea

On 1 December 2017 at 16:13, Eugene Pakhomov [email protected] wrote:

Ah, right, sorry.

I think you’ll have to use MultiLine instead of Line: https://bokeh.pydata.org/en/latest/docs/reference/models/glyphs/multi_line.html

But most of the code will be pretty much the same as in my example - you add a new row to the data source on each _pan_start and change the last row on each _pan (either by “patch” or by a direct change with a subsequent “change.emit()”).

Regards,

Eugene

On Friday, December 1, 2017 at 10:06:47 PM UTC+7, Andrea Tagliabue wrote:

Hi Eugene,
no sorry for the confusion. Your examples for lines is absolutely correct! My question is on this tool https://bokeh.pydata.org/en/latest/docs/user_guide/extensions_gallery/tool.html

I modified the code so instead to clear the notation on the next left click it will continue to draw. However i would like to have two separate annotations. Instead, my code keep drawing from the very last point of the annotation. If you run the code i copied in the email you will see what i mean. Drawing a word is impossible: you will have all the letters linked together.

Thanks
Andrea

On 1 December 2017 at 16:02, Eugene Pakhomov [email protected] wrote:

Hey Andrea,

I’m not sure what you’re asking about. The example that I’ve sent you earlier works exactly as you describe - it draws separate lines on each pan, it doesn’t link the previous stop with the next start.

Regards,

Eugene

On Friday, December 1, 2017 at 9:37:43 PM UTC+7, Andrea Tagliabue wrote:

Hi guys,
sorry but it’s still me. Nothing too serious but i wanted to have your opinion on something. I slightly modified the code in “new custom tool” https://bokeh.pydata.org/en/latest/docs/user_guide/extensions_gallery/tool.html where even if i release the left click button and i restart to draw something the code will not clear what has been drawn before.

I am using a simple.plot.line annotation. I was thinking: is there a way to prevent JS to restart drawing from the last point? I mean if i move away from the very last drawn point and i restart left clicking somewhere else JS will link the very last point with the current new one. Ideally i would like a way for drawing distinct annotations.

is it possible to achieve the same outcome with plot.line?

Many thanks again

Andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.io.state import curstate

from bokeh.models import ColumnDataSource, Tool

from bokeh.plotting import figure

output_file(‘tool.html’)

curstate().file[‘resources’].minified = False

JS_CODE_DrawAnnotation = “”"

import * as p from “core/properties”

import {GestureTool, GestureToolView} from “models/tools/gestures/gesture_tool”

export class DrawToolView extends GestureToolView

this is executed on subsequent mouse/touch moves

_pan: (e) ->

frame = @plot_model.frame

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

source.data.x.push(x)

source.data.y.push(y)

source.change.emit()

this is executed then the pan/drag ends

_pan_end: (e) -> return null

export class DrawAnnotation extends GestureTool

default_view: DrawToolView

type: “DrawAnnotation”

tool_name: “Drag Annotation”

icon: “bk-tool-icon-lasso-select”

event_type: “pan”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class DrawAnnotation(Tool):

implementation = JS_CODE_DrawAnnotation

source = Instance(ColumnDataSource)

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

plot = figure(x_range=(0, 10), y_range=(0, 10) )

plot.add_tools(DrawAnnotation(source=source_DrawAnnotation))

plot.line(x=‘x’, y=‘y’, source=source_DrawAnnotation)

show(plot)

On 30 November 2017 at 17:10, Andrea Tagliabue [email protected] wrote:

Hi Eugene,
Terrific work!! Thanks so much.
Bryan: i have been used matplotlib a lot for my apps/code but when i discovered bokeh i just thought it was much better in terms of speed and easier accessibility. Having the plot in the browser and been able to have full control of what you see is just a great experience. And you can share it so easily.

However I do have to say i really struggled to get my mind around the doc and examples. But with your and Eugene’s help i have been able to convert my old and slow matplotlib app in something much better. As soon as i will be done with my code and all my custom functionalities i will send you all my code so you can upload these very unique examples and all the community and future bokeh developer will have much less tough days.

Also. this example from Web Busino https://gist.github.com/busino/d560a58b315e7bde2321e3960ea88616/raw/6d1cc896e2e86b45b659814c4eacccf5ad353efd/bokeh_frog.html should be in the doc. That’s a such a great example of very dynamic and custom usage of the mouse control for js_on_event.
I will definitively write some personal view on https://github.com/bokeh/bokeh/issues/6370

Many thanks again

Andrea

On 30 November 2017 at 16:49, Eugene Pakhomov [email protected] wrote:

Here you go:

import bokeh.util.compiler

bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure

output_file('tool.html')
curstate().file['resources'].minified = False

JS_CODE_DrawAnnotation = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"
export class DrawToolView extends GestureToolView
  _get_scaled_frame_coords: (e) ->
    frame = @plot_model.frame
    {sx, sy} = e.bokeh
    if frame.bbox.contains(sx, sy)
      x = frame.xscales['default'].invert(sx)
      y = frame.yscales['default'].invert(sy)
      return [x, y]

  _pan_start: (e) ->
    coords = @_get_scaled_frame_coords(e)
    if coords
      [x, y] = coords
      @model.source.stream({x0: [x], y0: [y], x1: [x], y1: [y]})

  _pan: (e) ->
    coords = @_get_scaled_frame_coords(e)
    if coords
      [x, y] = coords
      last_row = @model.source.data.x0.length - 1
      @model.source.patch({x1: [[last_row, x]], y1: [[last_row, y]]})

export class DrawObliqueLine extends GestureTool
  default_view: DrawToolView
  type: "DrawObliqueLine"

  tool_name: "Line"
  icon: "bk-tool-icon-lasso-select"
  event_type: "pan"
  default_order: 12

  @define { source: [ p.Instance ] }
"""


class DrawObliqueLine(Tool):
    __implementation__ = JS_CODE_DrawAnnotation
    source = Instance(ColumnDataSource)


source_ObliqueLine = ColumnDataSource(data=dict(x0=[], y0=[], x1=[], y1=[]))

plot = figure(x_range=(0, 10), y_range=(0, 10))
plot.add_tools(DrawObliqueLine(source=source_ObliqueLine))
plot.title.text = "Pan to draw on the plot"

plot.segment(x0='x0', y0='y0', x1='x1', y1='y1', line_dash="solid",
             line_width=2, source=source_ObliqueLine)

show(plot)

Regards,

Eugene

On Thursday, November 30, 2017 at 10:07:00 PM UTC+7, Andrea Tagliabue wrote:

Hi Eugene,
i am really sorry to keep bothering at you but with no doc and very few examples i can’t see big improvements from my work. My last point of my work is to been able to draw oblique segments( ray would be the perfect solution) too.
The code i posted below is clearly wrong but i guess the idea is more and less that. I don’t understand how i can tell JS to store the initial click coordinates and while i am moving the mouse i would like to see the segment or ray drawn.
Can you suggest any idea on this?

Many thanks again
Andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.io.state import curstate

from bokeh.models import ColumnDataSource, Tool

from bokeh.plotting import figure

output_file(‘tool.html’)

curstate().file[‘resources’].minified = False

JS_CODE_DrawAnnotation = “”"

import * as p from “core/properties”

import {GestureTool, GestureToolView} from “models/tools/gestures/gesture_tool”

export class DrawToolView extends GestureToolView

this is executed on subsequent mouse/touch moves

_tap: (e) ->

frame = @plot_model.frame

@model.source.data = {x0: , y0: , x1: , y1:}

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

if source.data.y0.length == 0

source.data.x0.push(x)

source.data.y0.push(y)

else

source.data.x1.push(cb_obj.x)

source.data.y1.push(cb_obj.y)

source.change.emit()

this is executed then the pan/drag ends

_pan_end: (e) -> return null

export class DrawObliqueLine extends GestureTool

default_view: DrawToolView

type: “DrawObliqueLine”

tool_name: “Line”

icon: “bk-tool-icon-lasso-select”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class DrawObliqueLine(Tool):

implementation = JS_CODE_DrawAnnotation

source = Instance(ColumnDataSource)

source_ObliqueLine = ColumnDataSource(data=dict(x0=, y0=, x1=, y1=))

plot = figure(x_range=(0, 10), y_range=(0, 10) )

plot.add_tools(DrawObliqueLine(source=source_ObliqueLine))

plot.title.text = “Tap to draw on the plot”

plot.segment(x0=‘x0’, y0=‘y0’, x1=‘x1’, y1=‘y1’, line_dash=“solid”, line_width=2, source=source_ObliqueLine)

show(plot)

On 30 November 2017 at 14:15, Andrea Tagliabue [email protected] wrote:

yeah me too but most of the time i can’t figure out what i am doing wrong :slight_smile:

On 30 November 2017 at 13:42, Eugene Pakhomov [email protected] wrote:

Unfortunately, I don’t think so. At least, I couldn’t find any.
Usually, I just study the source code of the existing tools.

  • Eugene

On Thursday, November 30, 2017 at 7:24:57 PM UTC+7, Andrea Tagliabue wrote:

Wow really thanks for the quick answer. In order to avoid me bothering you for other issues, is there any doc regarding these custom tools? They looks to me pretty complex in terms of multiple options.
Many thanks again
Andrea

On 30 November 2017 at 13:18, Eugene Pakhomov [email protected] wrote:

Hi Andrea,

You almost got it. Here’s the working version. The things that needed to be fixed: wrong function name (must be “_tap” instead of “_pan”), wrong “default_view” (must be TapBBToolView), extra data field for the data source (you’d get an error about inconsistent column lengths).

I also realized that TapTool is not needed because all it can provide you if just a single field “event_type”, so I used a more general GestureTool instead.

import bokeh.util.compiler

bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure

output_file('tool.html')
curstate().file['resources'].minified = False

JS_CODE = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"

export class TapBBToolView extends GestureToolView

  # this is executed on subsequent mouse/touch moves
  _tap: (e) ->
    frame = @plot_model.frame

    {sx, sy} = e.bokeh
    if not frame.bbox.contains(sx, sy)
      return null

    x = frame.xscales['default'].invert(sx)
    y = frame.yscales['default'].invert(sy)
    source = @model.source
    source.data.x.push(x)
    source.data.y.push(y)
    source.change.emit()


export class TapBBTool extends GestureTool
  default_view: TapBBToolView
  type: "TapTool"
  event_type: "tap"
  tool_name: "H Line"
  icon: "bk-tool-icon-arrow-right"
  default_order: 12

  @define { source: [ p.Instance ] }
"""


class TapBBTool(Tool):
    __implementation__ = JS_CODE
    source = Instance(ColumnDataSource)


source_HorizontalRay = ColumnDataSource(data=dict(x=[], y=[]))

plot = figure(x_range=(0, 10), y_range=(0, 10),
              tools=[TapBBTool(source=source_HorizontalRay)])
plot.title.text = "Tap to draw on the plot"
plot.ray(x='x', y='y', angle=0, length=0, line_dash="solid", line_width=2,
         source=source_HorizontalRay)

show(plot)

Regards,

Eugene

On Thursday, November 30, 2017 at 6:58:10 PM UTC+7, Andrea Tagliabue wrote:

Hi all,
i am trying to implement a custom tool for drawing rays on tap event. I have implemented it via js_on_event and works fine but i need it as a tool since i want to give the ability of drawing rays, segments or lines and hence multiple custom tools are needed.

This code gets the plot displayed in the browser but when tapping nothing happens. I am struggling to understand what i am missing.

Would you mind to help me out?

Many thanks andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.models import ColumnDataSource, Tool

from bokeh.models.tools import Tap

from bokeh.plotting import figure

output_file(‘tool.html’)

JS_CODE = “”"

import * as p from “core/properties”

import {TapTool, TapToolView} from “models/tools/gestures/tap_tool”

export class TapBBToolView extends TapToolView

this is executed on subsequent mouse/touch moves

_pan: (e) ->

frame = @plot_model.frame

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

source.data.x.push(x)

source.data.y.push(y)

source.change.emit()

export class TapBBTool extends TapTool

default_view: TapToolView

type: “TapTool”

tool_name: “H Line”

icon: “bk-tool-icon-arrow-right”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class TapBBTool(Tool):

implementation = JS_CODE

source = Instance(ColumnDataSource)

source_HorizontalRay = ColumnDataSource(data=dict(x=, y=, angle=))

plot = figure(x_range=(0,10), y_range=(0,10), tools=[TapBBTool(source=source_HorizontalRay)])

plot.title.text =“Tap to draw on the plot”

plot.ray(x=‘x’, y=‘y’, angle = 0, length=0, line_dash=“solid”, line_width=2, source=source_HorizontalRay)

show(plot)

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/ac17d835-5909-4ac2-a552-9b94d3dba436%40continuum.io.

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

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/931b65c5-9b87-407a-9df2-7706e9ea3213%40continuum.io.

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

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/e90b70b6-a4a8-47df-befa-e0eb46e33a8f%40continuum.io.

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

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/5f8011a4-9894-4e7f-8e3a-664614bf61ac%40continuum.io.

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

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/c9f8b041-6ac7-4651-b754-4bad3e4ff145%40continuum.io.

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

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/4c9ce506-92ff-4c5a-91d6-4056457ecb65%40continuum.io.

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

Hi Eugene,

Thanks for all the useful information in this thread, learning quite a bit about the custom tool callbacks, very helpful. I am however running into an issue executing the examples you’re posting, whenever I run them, whether inline in the notebook, or in standalone files, I see the JS console error:

“”"

Uncaught TypeError: Cannot read property ‘contains’ of undefined

at DrawToolView.exports.DrawToolView.DrawToolView._get_scaled_frame_coords (tool.html:131)

at DrawToolView.exports.DrawToolView.DrawToolView._pan (tool.html:152)

at DrawToolView. (bokeh-0.12.10.js:sourcemap:3605)

at Signal.emit (bokeh-0.12.10.js:sourcemap:3327)

at UIEvents.exports.UIEvents.UIEvents.trigger (bokeh-0.12.10.js:sourcemap:3825)

at UIEvents.exports.UIEvents.UIEvents._trigger (bokeh-0.12.10.js:sourcemap:3817)

at UIEvents.exports.UIEvents.UIEvents._pan (bokeh-0.12.10.js:sourcemap:3893)

at Array. (bokeh-0.12.10.js:sourcemap:3512)

at Manager.emit (bokeh-0.12.10.js:sourcemap:38862)

at emit (bokeh-0.12.10.js:sourcemap:37972)

“”"

I’m assuming the ‘contains’ is a reference to the line:

if not frame.bbox.contains(sx, sy)

 return null

I have achieved this while literally copying and pasting your examples above, running bokeh 0.12.11 in python 3.6 under windows. Pretty green to JS, so apologies if I’ve missed something.

Kind regards,
George

···

On Friday, 1 December 2017 15:37:48 UTC, Eugene Pakhomov wrote:

No, what I mean is, the functionality that you need is more similar to the code that I have provided earlier than to the example that you’ve used.
Single annotation for the example from the extensions gallery is more similar to the single line annotation, multiple non-continuous annotations are more similar to a multi-line annotation.

  • Eugene

On Friday, December 1, 2017 at 10:33:14 PM UTC+7, Andrea Tagliabue wrote:

Hi Eugene,
i am not sure about what you are referring to. I am not not using your code. I am using the code i found on https://bokeh.pydata.org/en/latest/docs/user_guide/extensions_gallery/tool.html

i literally just deleted this:

 # this is executed when the pan/drag event starts
  _pan_start: (e) ->
    @model.source.data = {x: [], y: []}

so instead to delete all the previous annotations it keeps everything. So if i use multi line as you suggested should be something like this but i am not sure i to modify the JS code.

import bokeh.util.compiler
bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure
output_file('tool.html')
curstate().file['resources'].

minified = False
JS_CODE_DrawAnnotation = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/                            gesture_tool"
export class DrawToolView extends GestureToolView
# this is executed on subsequent mouse/touch moves
_pan: (e) ->
frame = @plot_model.frame
{sx, sy} = e.bokeh
if not frame.bbox.contains(sx, sy)
return null
x = frame.xscales['default'].                                invert(sx)
y = frame.yscales['default'].                                invert(sy)
source = @model.source
                                source.data.xs.push(x)
source.data.ys.push(y)
                                source.change.emit()
# this is executed then the pan/drag ends
_pan_end: (e) -> return null
export class DrawAnnotation extends GestureTool
default_view: DrawToolView
type: "DrawAnnotation"
tool_name: "Drag Annotation"
icon: "bk-tool-icon-lasso-select"
event_type: "pan"
default_order: 12
@define { source: [ p.Instance ] }
"""
class DrawAnnotation(Tool):
__implementation__ = JS_CODE_DrawAnnotation
source = Instance(ColumnDataSource)
source_DrawAnnotation = ColumnDataSource(data=dict(xs=
[],ys=[]))
plot = figure(x_range=(0, 10), y_range=(0, 10) )
plot.add_tools(DrawAnnotation(
source=source_DrawAnnotation))
plot.multi_line(xs='xs', ys='ys', source=source_DrawAnnotation)
show(plot)

Thanks,

Andrea

On 1 December 2017 at 16:13, Eugene Pakhomov [email protected] wrote:

Ah, right, sorry.

I think you’ll have to use MultiLine instead of Line: https://bokeh.pydata.org/en/latest/docs/reference/models/glyphs/multi_line.html

But most of the code will be pretty much the same as in my example - you add a new row to the data source on each _pan_start and change the last row on each _pan (either by “patch” or by a direct change with a subsequent “change.emit()”).

Regards,

Eugene

On Friday, December 1, 2017 at 10:06:47 PM UTC+7, Andrea Tagliabue wrote:

Hi Eugene,
no sorry for the confusion. Your examples for lines is absolutely correct! My question is on this tool https://bokeh.pydata.org/en/latest/docs/user_guide/extensions_gallery/tool.html

I modified the code so instead to clear the notation on the next left click it will continue to draw. However i would like to have two separate annotations. Instead, my code keep drawing from the very last point of the annotation. If you run the code i copied in the email you will see what i mean. Drawing a word is impossible: you will have all the letters linked together.

Thanks
Andrea

On 1 December 2017 at 16:02, Eugene Pakhomov [email protected] wrote:

Hey Andrea,

I’m not sure what you’re asking about. The example that I’ve sent you earlier works exactly as you describe - it draws separate lines on each pan, it doesn’t link the previous stop with the next start.

Regards,

Eugene

On Friday, December 1, 2017 at 9:37:43 PM UTC+7, Andrea Tagliabue wrote:

Hi guys,
sorry but it’s still me. Nothing too serious but i wanted to have your opinion on something. I slightly modified the code in “new custom tool” https://bokeh.pydata.org/en/latest/docs/user_guide/extensions_gallery/tool.html where even if i release the left click button and i restart to draw something the code will not clear what has been drawn before.

I am using a simple.plot.line annotation. I was thinking: is there a way to prevent JS to restart drawing from the last point? I mean if i move away from the very last drawn point and i restart left clicking somewhere else JS will link the very last point with the current new one. Ideally i would like a way for drawing distinct annotations.

is it possible to achieve the same outcome with plot.line?

Many thanks again

Andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.io.state import curstate

from bokeh.models import ColumnDataSource, Tool

from bokeh.plotting import figure

output_file(‘tool.html’)

curstate().file[‘resources’].minified = False

JS_CODE_DrawAnnotation = “”"

import * as p from “core/properties”

import {GestureTool, GestureToolView} from “models/tools/gestures/gesture_tool”

export class DrawToolView extends GestureToolView

this is executed on subsequent mouse/touch moves

_pan: (e) ->

frame = @plot_model.frame

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

source.data.x.push(x)

source.data.y.push(y)

source.change.emit()

this is executed then the pan/drag ends

_pan_end: (e) -> return null

export class DrawAnnotation extends GestureTool

default_view: DrawToolView

type: “DrawAnnotation”

tool_name: “Drag Annotation”

icon: “bk-tool-icon-lasso-select”

event_type: “pan”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class DrawAnnotation(Tool):

implementation = JS_CODE_DrawAnnotation

source = Instance(ColumnDataSource)

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

plot = figure(x_range=(0, 10), y_range=(0, 10) )

plot.add_tools(DrawAnnotation(source=source_DrawAnnotation))

plot.line(x=‘x’, y=‘y’, source=source_DrawAnnotation)

show(plot)

On 30 November 2017 at 17:10, Andrea Tagliabue [email protected] wrote:

Hi Eugene,
Terrific work!! Thanks so much.
Bryan: i have been used matplotlib a lot for my apps/code but when i discovered bokeh i just thought it was much better in terms of speed and easier accessibility. Having the plot in the browser and been able to have full control of what you see is just a great experience. And you can share it so easily.

However I do have to say i really struggled to get my mind around the doc and examples. But with your and Eugene’s help i have been able to convert my old and slow matplotlib app in something much better. As soon as i will be done with my code and all my custom functionalities i will send you all my code so you can upload these very unique examples and all the community and future bokeh developer will have much less tough days.

Also. this example from Web Busino https://gist.github.com/busino/d560a58b315e7bde2321e3960ea88616/raw/6d1cc896e2e86b45b659814c4eacccf5ad353efd/bokeh_frog.html should be in the doc. That’s a such a great example of very dynamic and custom usage of the mouse control for js_on_event.
I will definitively write some personal view on https://github.com/bokeh/bokeh/issues/6370

Many thanks again

Andrea

On 30 November 2017 at 16:49, Eugene Pakhomov [email protected] wrote:

Here you go:

import bokeh.util.compiler

bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure

output_file('tool.html')
curstate().file['resources'].minified = False

JS_CODE_DrawAnnotation = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"
export class DrawToolView extends GestureToolView
  _get_scaled_frame_coords: (e) ->
    frame = @plot_model.frame
    {sx, sy} = e.bokeh
    if frame.bbox.contains(sx, sy)
      x = frame.xscales['default'].invert(sx)
      y = frame.yscales['default'].invert(sy)
      return [x, y]

  _pan_start: (e) ->
    coords = @_get_scaled_frame_coords(e)
    if coords
      [x, y] = coords
      @model.source.stream({x0: [x], y0: [y], x1: [x], y1: [y]})

  _pan: (e) ->
    coords = @_get_scaled_frame_coords(e)
    if coords
      [x, y] = coords
      last_row = @model.source.data.x0.length - 1
      @model.source.patch({x1: [[last_row, x]], y1: [[last_row, y]]})

export class DrawObliqueLine extends GestureTool
  default_view: DrawToolView
  type: "DrawObliqueLine"

  tool_name: "Line"
  icon: "bk-tool-icon-lasso-select"
  event_type: "pan"
  default_order: 12

  @define { source: [ p.Instance ] }
"""


class DrawObliqueLine(Tool):
    __implementation__ = JS_CODE_DrawAnnotation
    source = Instance(ColumnDataSource)


source_ObliqueLine = ColumnDataSource(data=dict(x0=[], y0=[], x1=[], y1=[]))

plot = figure(x_range=(0, 10), y_range=(0, 10))
plot.add_tools(DrawObliqueLine(source=source_ObliqueLine))
plot.title.text = "Pan to draw on the plot"

plot.segment(x0='x0', y0='y0', x1='x1', y1='y1', line_dash="solid",
             line_width=2, source=source_ObliqueLine)

show(plot)

Regards,

Eugene

On Thursday, November 30, 2017 at 10:07:00 PM UTC+7, Andrea Tagliabue wrote:

Hi Eugene,
i am really sorry to keep bothering at you but with no doc and very few examples i can’t see big improvements from my work. My last point of my work is to been able to draw oblique segments( ray would be the perfect solution) too.
The code i posted below is clearly wrong but i guess the idea is more and less that. I don’t understand how i can tell JS to store the initial click coordinates and while i am moving the mouse i would like to see the segment or ray drawn.
Can you suggest any idea on this?

Many thanks again
Andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.io.state import curstate

from bokeh.models import ColumnDataSource, Tool

from bokeh.plotting import figure

output_file(‘tool.html’)

curstate().file[‘resources’].minified = False

JS_CODE_DrawAnnotation = “”"

import * as p from “core/properties”

import {GestureTool, GestureToolView} from “models/tools/gestures/gesture_tool”

export class DrawToolView extends GestureToolView

this is executed on subsequent mouse/touch moves

_tap: (e) ->

frame = @plot_model.frame

@model.source.data = {x0: , y0: , x1: , y1:}

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

if source.data.y0.length == 0

source.data.x0.push(x)

source.data.y0.push(y)

else

source.data.x1.push(cb_obj.x)

source.data.y1.push(cb_obj.y)

source.change.emit()

this is executed then the pan/drag ends

_pan_end: (e) -> return null

export class DrawObliqueLine extends GestureTool

default_view: DrawToolView

type: “DrawObliqueLine”

tool_name: “Line”

icon: “bk-tool-icon-lasso-select”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class DrawObliqueLine(Tool):

implementation = JS_CODE_DrawAnnotation

source = Instance(ColumnDataSource)

source_ObliqueLine = ColumnDataSource(data=dict(x0=, y0=, x1=, y1=))

plot = figure(x_range=(0, 10), y_range=(0, 10) )

plot.add_tools(DrawObliqueLine(source=source_ObliqueLine))

plot.title.text = “Tap to draw on the plot”

plot.segment(x0=‘x0’, y0=‘y0’, x1=‘x1’, y1=‘y1’, line_dash=“solid”, line_width=2, source=source_ObliqueLine)

show(plot)

On 30 November 2017 at 14:15, Andrea Tagliabue [email protected] wrote:

yeah me too but most of the time i can’t figure out what i am doing wrong :slight_smile:

On 30 November 2017 at 13:42, Eugene Pakhomov [email protected] wrote:

Unfortunately, I don’t think so. At least, I couldn’t find any.
Usually, I just study the source code of the existing tools.

  • Eugene

On Thursday, November 30, 2017 at 7:24:57 PM UTC+7, Andrea Tagliabue wrote:

Wow really thanks for the quick answer. In order to avoid me bothering you for other issues, is there any doc regarding these custom tools? They looks to me pretty complex in terms of multiple options.
Many thanks again
Andrea

On 30 November 2017 at 13:18, Eugene Pakhomov [email protected] wrote:

Hi Andrea,

You almost got it. Here’s the working version. The things that needed to be fixed: wrong function name (must be “_tap” instead of “_pan”), wrong “default_view” (must be TapBBToolView), extra data field for the data source (you’d get an error about inconsistent column lengths).

I also realized that TapTool is not needed because all it can provide you if just a single field “event_type”, so I used a more general GestureTool instead.

import bokeh.util.compiler

bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure

output_file('tool.html')
curstate().file['resources'].minified = False

JS_CODE = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"

export class TapBBToolView extends GestureToolView

  # this is executed on subsequent mouse/touch moves
  _tap: (e) ->
    frame = @plot_model.frame

    {sx, sy} = e.bokeh
    if not frame.bbox.contains(sx, sy)
      return null

    x = frame.xscales['default'].invert(sx)
    y = frame.yscales['default'].invert(sy)
    source = @model.source
    source.data.x.push(x)
    source.data.y.push(y)
    source.change.emit()


export class TapBBTool extends GestureTool
  default_view: TapBBToolView
  type: "TapTool"
  event_type: "tap"
  tool_name: "H Line"
  icon: "bk-tool-icon-arrow-right"
  default_order: 12

  @define { source: [ p.Instance ] }
"""


class TapBBTool(Tool):
    __implementation__ = JS_CODE
    source = Instance(ColumnDataSource)


source_HorizontalRay = ColumnDataSource(data=dict(x=[], y=[]))

plot = figure(x_range=(0, 10), y_range=(0, 10),
              tools=[TapBBTool(source=source_HorizontalRay)])
plot.title.text = "Tap to draw on the plot"
plot.ray(x='x', y='y', angle=0, length=0, line_dash="solid", line_width=2,
         source=source_HorizontalRay)

show(plot)

Regards,

Eugene

On Thursday, November 30, 2017 at 6:58:10 PM UTC+7, Andrea Tagliabue wrote:

Hi all,
i am trying to implement a custom tool for drawing rays on tap event. I have implemented it via js_on_event and works fine but i need it as a tool since i want to give the ability of drawing rays, segments or lines and hence multiple custom tools are needed.

This code gets the plot displayed in the browser but when tapping nothing happens. I am struggling to understand what i am missing.

Would you mind to help me out?

Many thanks andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.models import ColumnDataSource, Tool

from bokeh.models.tools import Tap

from bokeh.plotting import figure

output_file(‘tool.html’)

JS_CODE = “”"

import * as p from “core/properties”

import {TapTool, TapToolView} from “models/tools/gestures/tap_tool”

export class TapBBToolView extends TapToolView

this is executed on subsequent mouse/touch moves

_pan: (e) ->

frame = @plot_model.frame

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

source.data.x.push(x)

source.data.y.push(y)

source.change.emit()

export class TapBBTool extends TapTool

default_view: TapToolView

type: “TapTool”

tool_name: “H Line”

icon: “bk-tool-icon-arrow-right”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class TapBBTool(Tool):

implementation = JS_CODE

source = Instance(ColumnDataSource)

source_HorizontalRay = ColumnDataSource(data=dict(x=, y=, angle=))

plot = figure(x_range=(0,10), y_range=(0,10), tools=[TapBBTool(source=source_HorizontalRay)])

plot.title.text =“Tap to draw on the plot”

plot.ray(x=‘x’, y=‘y’, angle = 0, length=0, line_dash=“solid”, line_width=2, source=source_HorizontalRay)

show(plot)

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/ac17d835-5909-4ac2-a552-9b94d3dba436%40continuum.io.

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

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/931b65c5-9b87-407a-9df2-7706e9ea3213%40continuum.io.

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

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/e90b70b6-a4a8-47df-befa-e0eb46e33a8f%40continuum.io.

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

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/5f8011a4-9894-4e7f-8e3a-664614bf61ac%40continuum.io.

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

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/c9f8b041-6ac7-4651-b754-4bad3e4ff145%40continuum.io.

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

Hi George,

Are you really sure that you’re using Bokeh 0.12.11? Try adding import bokeh; print(bokeh.__version__) at the very top of one of the examples and see what it outputs when you run it.

Apart from the possible issue with the version, I don’t really have any idea on why it might be happening.

Regards,

Eugene

···

On Friday, December 1, 2017 at 10:57:22 PM UTC+7, George Crowther wrote:

Hi Eugene,

Thanks for all the useful information in this thread, learning quite a bit about the custom tool callbacks, very helpful. I am however running into an issue executing the examples you’re posting, whenever I run them, whether inline in the notebook, or in standalone files, I see the JS console error:

“”"

Uncaught TypeError: Cannot read property ‘contains’ of undefined

at DrawToolView.exports.DrawToolView.DrawToolView._get_scaled_frame_coords (tool.html:131)

at DrawToolView.exports.DrawToolView.DrawToolView._pan (tool.html:152)

at DrawToolView. (bokeh-0.12.10.js:sourcemap:3605)

at Signal.emit (bokeh-0.12.10.js:sourcemap:3327)

at UIEvents.exports.UIEvents.UIEvents.trigger (bokeh-0.12.10.js:sourcemap:3825)

at UIEvents.exports.UIEvents.UIEvents._trigger (bokeh-0.12.10.js:sourcemap:3817)

at UIEvents.exports.UIEvents.UIEvents._pan (bokeh-0.12.10.js:sourcemap:3893)

at Array. (bokeh-0.12.10.js:sourcemap:3512)

at Manager.emit (bokeh-0.12.10.js:sourcemap:38862)

at emit (bokeh-0.12.10.js:sourcemap:37972)

“”"

I’m assuming the ‘contains’ is a reference to the line:

if not frame.bbox.contains(sx, sy)

 return null

I have achieved this while literally copying and pasting your examples above, running bokeh 0.12.11 in python 3.6 under windows. Pretty green to JS, so apologies if I’ve missed something.

Kind regards,
George

On Friday, 1 December 2017 15:37:48 UTC, Eugene Pakhomov wrote:

No, what I mean is, the functionality that you need is more similar to the code that I have provided earlier than to the example that you’ve used.
Single annotation for the example from the extensions gallery is more similar to the single line annotation, multiple non-continuous annotations are more similar to a multi-line annotation.

  • Eugene

On Friday, December 1, 2017 at 10:33:14 PM UTC+7, Andrea Tagliabue wrote:

Hi Eugene,
i am not sure about what you are referring to. I am not not using your code. I am using the code i found on https://bokeh.pydata.org/en/latest/docs/user_guide/extensions_gallery/tool.html

i literally just deleted this:

 # this is executed when the pan/drag event starts
  _pan_start: (e) ->
    @model.source.data = {x: [], y: []}

so instead to delete all the previous annotations it keeps everything. So if i use multi line as you suggested should be something like this but i am not sure i to modify the JS code.

import bokeh.util.compiler
bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure
output_file('tool.html')
curstate().file['resources'].

minified = False
JS_CODE_DrawAnnotation = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/                            gesture_tool"
export class DrawToolView extends GestureToolView
# this is executed on subsequent mouse/touch moves
_pan: (e) ->
frame = @plot_model.frame
{sx, sy} = e.bokeh
if not frame.bbox.contains(sx, sy)
return null
x = frame.xscales['default'].                                invert(sx)
y = frame.yscales['default'].                                invert(sy)
source = @model.source
                                source.data.xs.push(x)
source.data.ys.push(y)
                                source.change.emit()
# this is executed then the pan/drag ends
_pan_end: (e) -> return null
export class DrawAnnotation extends GestureTool
default_view: DrawToolView
type: "DrawAnnotation"
tool_name: "Drag Annotation"
icon: "bk-tool-icon-lasso-select"
event_type: "pan"
default_order: 12
@define { source: [ p.Instance ] }
"""
class DrawAnnotation(Tool):
__implementation__ = JS_CODE_DrawAnnotation
source = Instance(ColumnDataSource)
source_DrawAnnotation = ColumnDataSource(data=dict(xs=
[],ys=[]))
plot = figure(x_range=(0, 10), y_range=(0, 10) )
plot.add_tools(DrawAnnotation(
source=source_DrawAnnotation))
plot.multi_line(xs='xs', ys='ys', source=source_DrawAnnotation)
show(plot)

Thanks,

Andrea

On 1 December 2017 at 16:13, Eugene Pakhomov [email protected] wrote:

Ah, right, sorry.

I think you’ll have to use MultiLine instead of Line: https://bokeh.pydata.org/en/latest/docs/reference/models/glyphs/multi_line.html

But most of the code will be pretty much the same as in my example - you add a new row to the data source on each _pan_start and change the last row on each _pan (either by “patch” or by a direct change with a subsequent “change.emit()”).

Regards,

Eugene

On Friday, December 1, 2017 at 10:06:47 PM UTC+7, Andrea Tagliabue wrote:

Hi Eugene,
no sorry for the confusion. Your examples for lines is absolutely correct! My question is on this tool https://bokeh.pydata.org/en/latest/docs/user_guide/extensions_gallery/tool.html

I modified the code so instead to clear the notation on the next left click it will continue to draw. However i would like to have two separate annotations. Instead, my code keep drawing from the very last point of the annotation. If you run the code i copied in the email you will see what i mean. Drawing a word is impossible: you will have all the letters linked together.

Thanks
Andrea

On 1 December 2017 at 16:02, Eugene Pakhomov [email protected] wrote:

Hey Andrea,

I’m not sure what you’re asking about. The example that I’ve sent you earlier works exactly as you describe - it draws separate lines on each pan, it doesn’t link the previous stop with the next start.

Regards,

Eugene

On Friday, December 1, 2017 at 9:37:43 PM UTC+7, Andrea Tagliabue wrote:

Hi guys,
sorry but it’s still me. Nothing too serious but i wanted to have your opinion on something. I slightly modified the code in “new custom tool” https://bokeh.pydata.org/en/latest/docs/user_guide/extensions_gallery/tool.html where even if i release the left click button and i restart to draw something the code will not clear what has been drawn before.

I am using a simple.plot.line annotation. I was thinking: is there a way to prevent JS to restart drawing from the last point? I mean if i move away from the very last drawn point and i restart left clicking somewhere else JS will link the very last point with the current new one. Ideally i would like a way for drawing distinct annotations.

is it possible to achieve the same outcome with plot.line?

Many thanks again

Andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.io.state import curstate

from bokeh.models import ColumnDataSource, Tool

from bokeh.plotting import figure

output_file(‘tool.html’)

curstate().file[‘resources’].minified = False

JS_CODE_DrawAnnotation = “”"

import * as p from “core/properties”

import {GestureTool, GestureToolView} from “models/tools/gestures/gesture_tool”

export class DrawToolView extends GestureToolView

this is executed on subsequent mouse/touch moves

_pan: (e) ->

frame = @plot_model.frame

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

source.data.x.push(x)

source.data.y.push(y)

source.change.emit()

this is executed then the pan/drag ends

_pan_end: (e) -> return null

export class DrawAnnotation extends GestureTool

default_view: DrawToolView

type: “DrawAnnotation”

tool_name: “Drag Annotation”

icon: “bk-tool-icon-lasso-select”

event_type: “pan”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class DrawAnnotation(Tool):

implementation = JS_CODE_DrawAnnotation

source = Instance(ColumnDataSource)

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

plot = figure(x_range=(0, 10), y_range=(0, 10) )

plot.add_tools(DrawAnnotation(source=source_DrawAnnotation))

plot.line(x=‘x’, y=‘y’, source=source_DrawAnnotation)

show(plot)

On 30 November 2017 at 17:10, Andrea Tagliabue [email protected] wrote:

Hi Eugene,
Terrific work!! Thanks so much.
Bryan: i have been used matplotlib a lot for my apps/code but when i discovered bokeh i just thought it was much better in terms of speed and easier accessibility. Having the plot in the browser and been able to have full control of what you see is just a great experience. And you can share it so easily.

However I do have to say i really struggled to get my mind around the doc and examples. But with your and Eugene’s help i have been able to convert my old and slow matplotlib app in something much better. As soon as i will be done with my code and all my custom functionalities i will send you all my code so you can upload these very unique examples and all the community and future bokeh developer will have much less tough days.

Also. this example from Web Busino https://gist.github.com/busino/d560a58b315e7bde2321e3960ea88616/raw/6d1cc896e2e86b45b659814c4eacccf5ad353efd/bokeh_frog.html should be in the doc. That’s a such a great example of very dynamic and custom usage of the mouse control for js_on_event.
I will definitively write some personal view on https://github.com/bokeh/bokeh/issues/6370

Many thanks again

Andrea

On 30 November 2017 at 16:49, Eugene Pakhomov [email protected] wrote:

Here you go:

import bokeh.util.compiler

bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure

output_file('tool.html')
curstate().file['resources'].minified = False

JS_CODE_DrawAnnotation = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"
export class DrawToolView extends GestureToolView
  _get_scaled_frame_coords: (e) ->
    frame = @plot_model.frame
    {sx, sy} = e.bokeh
    if frame.bbox.contains(sx, sy)
      x = frame.xscales['default'].invert(sx)
      y = frame.yscales['default'].invert(sy)
      return [x, y]

  _pan_start: (e) ->
    coords = @_get_scaled_frame_coords(e)
    if coords
      [x, y] = coords
      @model.source.stream({x0: [x], y0: [y], x1: [x], y1: [y]})

  _pan: (e) ->
    coords = @_get_scaled_frame_coords(e)
    if coords
      [x, y] = coords
      last_row = @model.source.data.x0.length - 1
      @model.source.patch({x1: [[last_row, x]], y1: [[last_row, y]]})

export class DrawObliqueLine extends GestureTool
  default_view: DrawToolView
  type: "DrawObliqueLine"

  tool_name: "Line"
  icon: "bk-tool-icon-lasso-select"
  event_type: "pan"
  default_order: 12

  @define { source: [ p.Instance ] }
"""


class DrawObliqueLine(Tool):
    __implementation__ = JS_CODE_DrawAnnotation
    source = Instance(ColumnDataSource)


source_ObliqueLine = ColumnDataSource(data=dict(x0=[], y0=[], x1=[], y1=[]))

plot = figure(x_range=(0, 10), y_range=(0, 10))
plot.add_tools(DrawObliqueLine(source=source_ObliqueLine))
plot.title.text = "Pan to draw on the plot"

plot.segment(x0='x0', y0='y0', x1='x1', y1='y1', line_dash="solid",
             line_width=2, source=source_ObliqueLine)

show(plot)

Regards,

Eugene

On Thursday, November 30, 2017 at 10:07:00 PM UTC+7, Andrea Tagliabue wrote:

Hi Eugene,
i am really sorry to keep bothering at you but with no doc and very few examples i can’t see big improvements from my work. My last point of my work is to been able to draw oblique segments( ray would be the perfect solution) too.
The code i posted below is clearly wrong but i guess the idea is more and less that. I don’t understand how i can tell JS to store the initial click coordinates and while i am moving the mouse i would like to see the segment or ray drawn.
Can you suggest any idea on this?

Many thanks again
Andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.io.state import curstate

from bokeh.models import ColumnDataSource, Tool

from bokeh.plotting import figure

output_file(‘tool.html’)

curstate().file[‘resources’].minified = False

JS_CODE_DrawAnnotation = “”"

import * as p from “core/properties”

import {GestureTool, GestureToolView} from “models/tools/gestures/gesture_tool”

export class DrawToolView extends GestureToolView

this is executed on subsequent mouse/touch moves

_tap: (e) ->

frame = @plot_model.frame

@model.source.data = {x0: , y0: , x1: , y1:}

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

if source.data.y0.length == 0

source.data.x0.push(x)

source.data.y0.push(y)

else

source.data.x1.push(cb_obj.x)

source.data.y1.push(cb_obj.y)

source.change.emit()

this is executed then the pan/drag ends

_pan_end: (e) -> return null

export class DrawObliqueLine extends GestureTool

default_view: DrawToolView

type: “DrawObliqueLine”

tool_name: “Line”

icon: “bk-tool-icon-lasso-select”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class DrawObliqueLine(Tool):

implementation = JS_CODE_DrawAnnotation

source = Instance(ColumnDataSource)

source_ObliqueLine = ColumnDataSource(data=dict(x0=, y0=, x1=, y1=))

plot = figure(x_range=(0, 10), y_range=(0, 10) )

plot.add_tools(DrawObliqueLine(source=source_ObliqueLine))

plot.title.text = “Tap to draw on the plot”

plot.segment(x0=‘x0’, y0=‘y0’, x1=‘x1’, y1=‘y1’, line_dash=“solid”, line_width=2, source=source_ObliqueLine)

show(plot)

On 30 November 2017 at 14:15, Andrea Tagliabue [email protected] wrote:

yeah me too but most of the time i can’t figure out what i am doing wrong :slight_smile:

On 30 November 2017 at 13:42, Eugene Pakhomov [email protected] wrote:

Unfortunately, I don’t think so. At least, I couldn’t find any.
Usually, I just study the source code of the existing tools.

  • Eugene

On Thursday, November 30, 2017 at 7:24:57 PM UTC+7, Andrea Tagliabue wrote:

Wow really thanks for the quick answer. In order to avoid me bothering you for other issues, is there any doc regarding these custom tools? They looks to me pretty complex in terms of multiple options.
Many thanks again
Andrea

On 30 November 2017 at 13:18, Eugene Pakhomov [email protected] wrote:

Hi Andrea,

You almost got it. Here’s the working version. The things that needed to be fixed: wrong function name (must be “_tap” instead of “_pan”), wrong “default_view” (must be TapBBToolView), extra data field for the data source (you’d get an error about inconsistent column lengths).

I also realized that TapTool is not needed because all it can provide you if just a single field “event_type”, so I used a more general GestureTool instead.

import bokeh.util.compiler

bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure

output_file('tool.html')
curstate().file['resources'].minified = False

JS_CODE = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"

export class TapBBToolView extends GestureToolView

  # this is executed on subsequent mouse/touch moves
  _tap: (e) ->
    frame = @plot_model.frame

    {sx, sy} = e.bokeh
    if not frame.bbox.contains(sx, sy)
      return null

    x = frame.xscales['default'].invert(sx)
    y = frame.yscales['default'].invert(sy)
    source = @model.source
    source.data.x.push(x)
    source.data.y.push(y)
    source.change.emit()


export class TapBBTool extends GestureTool
  default_view: TapBBToolView
  type: "TapTool"
  event_type: "tap"
  tool_name: "H Line"
  icon: "bk-tool-icon-arrow-right"
  default_order: 12

  @define { source: [ p.Instance ] }
"""


class TapBBTool(Tool):
    __implementation__ = JS_CODE
    source = Instance(ColumnDataSource)


source_HorizontalRay = ColumnDataSource(data=dict(x=[], y=[]))

plot = figure(x_range=(0, 10), y_range=(0, 10),
              tools=[TapBBTool(source=source_HorizontalRay)])
plot.title.text = "Tap to draw on the plot"
plot.ray(x='x', y='y', angle=0, length=0, line_dash="solid", line_width=2,
         source=source_HorizontalRay)

show(plot)

Regards,

Eugene

On Thursday, November 30, 2017 at 6:58:10 PM UTC+7, Andrea Tagliabue wrote:

Hi all,
i am trying to implement a custom tool for drawing rays on tap event. I have implemented it via js_on_event and works fine but i need it as a tool since i want to give the ability of drawing rays, segments or lines and hence multiple custom tools are needed.

This code gets the plot displayed in the browser but when tapping nothing happens. I am struggling to understand what i am missing.

Would you mind to help me out?

Many thanks andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.models import ColumnDataSource, Tool

from bokeh.models.tools import Tap

from bokeh.plotting import figure

output_file(‘tool.html’)

JS_CODE = “”"

import * as p from “core/properties”

import {TapTool, TapToolView} from “models/tools/gestures/tap_tool”

export class TapBBToolView extends TapToolView

this is executed on subsequent mouse/touch moves

_pan: (e) ->

frame = @plot_model.frame

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

source.data.x.push(x)

source.data.y.push(y)

source.change.emit()

export class TapBBTool extends TapTool

default_view: TapToolView

type: “TapTool”

tool_name: “H Line”

icon: “bk-tool-icon-arrow-right”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class TapBBTool(Tool):

implementation = JS_CODE

source = Instance(ColumnDataSource)

source_HorizontalRay = ColumnDataSource(data=dict(x=, y=, angle=))

plot = figure(x_range=(0,10), y_range=(0,10), tools=[TapBBTool(source=source_HorizontalRay)])

plot.title.text =“Tap to draw on the plot”

plot.ray(x=‘x’, y=‘y’, angle = 0, length=0, line_dash=“solid”, line_width=2, source=source_HorizontalRay)

show(plot)

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/ac17d835-5909-4ac2-a552-9b94d3dba436%40continuum.io.

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

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/931b65c5-9b87-407a-9df2-7706e9ea3213%40continuum.io.

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

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/e90b70b6-a4a8-47df-befa-e0eb46e33a8f%40continuum.io.

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

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/5f8011a4-9894-4e7f-8e3a-664614bf61ac%40continuum.io.

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

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/c9f8b041-6ac7-4651-b754-4bad3e4ff145%40continuum.io.

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

Sorry Eugene, you’re absolutely right, updated and it’s working now. Sentiments about the examples on this thread still stand, very helpful.

George

···

On 1 Dec 2017 4:26 pm, “Eugene Pakhomov” [email protected] wrote:

Hi George,

Are you really sure that you’re using Bokeh 0.12.11? Try adding import bokeh; print(bokeh.__version__) at the very top of one of the examples and see what it outputs when you run it.

Apart from the possible issue with the version, I don’t really have any idea on why it might be happening.

Regards,

Eugene

On Friday, December 1, 2017 at 10:57:22 PM UTC+7, George Crowther wrote:

Hi Eugene,

Thanks for all the useful information in this thread, learning quite a bit about the custom tool callbacks, very helpful. I am however running into an issue executing the examples you’re posting, whenever I run them, whether inline in the notebook, or in standalone files, I see the JS console error:

“”"

Uncaught TypeError: Cannot read property ‘contains’ of undefined

at DrawToolView.exports.DrawToolView.DrawToolView._get_scaled_frame_coords (tool.html:131)

at DrawToolView.exports.DrawToolView.DrawToolView._pan (tool.html:152)

at DrawToolView. (bokeh-0.12.10.js:sourcemap:3605)

at Signal.emit (bokeh-0.12.10.js:sourcemap:3327)

at UIEvents.exports.UIEvents.UIEvents.trigger (bokeh-0.12.10.js:sourcemap:3825)

at UIEvents.exports.UIEvents.UIEvents._trigger (bokeh-0.12.10.js:sourcemap:3817)

at UIEvents.exports.UIEvents.UIEvents._pan (bokeh-0.12.10.js:sourcemap:3893)

at Array. (bokeh-0.12.10.js:sourcemap:3512)

at Manager.emit (bokeh-0.12.10.js:sourcemap:38862)

at emit (bokeh-0.12.10.js:sourcemap:37972)

“”"

I’m assuming the ‘contains’ is a reference to the line:

if not frame.bbox.contains(sx, sy)

 return null

I have achieved this while literally copying and pasting your examples above, running bokeh 0.12.11 in python 3.6 under windows. Pretty green to JS, so apologies if I’ve missed something.

Kind regards,
George

On Friday, 1 December 2017 15:37:48 UTC, Eugene Pakhomov wrote:

No, what I mean is, the functionality that you need is more similar to the code that I have provided earlier than to the example that you’ve used.
Single annotation for the example from the extensions gallery is more similar to the single line annotation, multiple non-continuous annotations are more similar to a multi-line annotation.

  • Eugene

On Friday, December 1, 2017 at 10:33:14 PM UTC+7, Andrea Tagliabue wrote:

Hi Eugene,
i am not sure about what you are referring to. I am not not using your code. I am using the code i found on https://bokeh.pydata.org/en/latest/docs/user_guide/extensions_gallery/tool.html

i literally just deleted this:

 # this is executed when the pan/drag event starts
  _pan_start: (e) ->
    @model.source.data = {x: [], y: []}

so instead to delete all the previous annotations it keeps everything. So if i use multi line as you suggested should be something like this but i am not sure i to modify the JS code.

import bokeh.util.compiler
bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure
output_file('tool.html')
curstate().file['resources'].m

inified = False
JS_CODE_DrawAnnotation = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture                            _tool"
export class DrawToolView extends GestureToolView
# this is executed on subsequent mouse/touch moves
_pan: (e) ->
frame = @plot_model.frame
{sx, sy} = e.bokeh
if not frame.bbox.contains(sx, sy)
return null
x = frame.xscales['default'].inver                                t(sx)
y = frame.yscales['default'].inver                                t(sy)
source = @model.source
                                source.data.xs.push(x)
source.data.ys.push(y)
                                source.change.emit()
# this is executed then the pan/drag ends
_pan_end: (e) -> return null
export class DrawAnnotation extends GestureTool
default_view: DrawToolView
type: "DrawAnnotation"
tool_name: "Drag Annotation"
icon: "bk-tool-icon-lasso-select"
event_type: "pan"
default_order: 12
@define { source: [ p.Instance ] }
"""
class DrawAnnotation(Tool):
__implementation__ = JS_CODE_DrawAnnotation
source = Instance(ColumnDataSource)
source_DrawAnnotation = ColumnDataSource(data=dict(xs=
[],ys=[]))
plot = figure(x_range=(0, 10), y_range=(0, 10) )
plot.add_tools(DrawAnnotation(
source=source_DrawAnnotation))
plot.multi_line(xs='xs', ys='ys', source=source_DrawAnnotation)
show(plot)

Thanks,

Andrea

On 1 December 2017 at 16:13, Eugene Pakhomov [email protected] wrote:

Ah, right, sorry.

I think you’ll have to use MultiLine instead of Line: https://bokeh.pydata.org/en/latest/docs/reference/models/glyphs/multi_line.html

But most of the code will be pretty much the same as in my example - you add a new row to the data source on each _pan_start and change the last row on each _pan (either by “patch” or by a direct change with a subsequent “change.emit()”).

Regards,

Eugene

On Friday, December 1, 2017 at 10:06:47 PM UTC+7, Andrea Tagliabue wrote:

Hi Eugene,
no sorry for the confusion. Your examples for lines is absolutely correct! My question is on this tool https://bokeh.pydata.org/en/latest/docs/user_guide/extensions_gallery/tool.html

I modified the code so instead to clear the notation on the next left click it will continue to draw. However i would like to have two separate annotations. Instead, my code keep drawing from the very last point of the annotation. If you run the code i copied in the email you will see what i mean. Drawing a word is impossible: you will have all the letters linked together.

Thanks
Andrea

On 1 December 2017 at 16:02, Eugene Pakhomov [email protected] wrote:

Hey Andrea,

I’m not sure what you’re asking about. The example that I’ve sent you earlier works exactly as you describe - it draws separate lines on each pan, it doesn’t link the previous stop with the next start.

Regards,

Eugene

On Friday, December 1, 2017 at 9:37:43 PM UTC+7, Andrea Tagliabue wrote:

Hi guys,
sorry but it’s still me. Nothing too serious but i wanted to have your opinion on something. I slightly modified the code in “new custom tool” https://bokeh.pydata.org/en/latest/docs/user_guide/extensions_gallery/tool.html where even if i release the left click button and i restart to draw something the code will not clear what has been drawn before.

I am using a simple.plot.line annotation. I was thinking: is there a way to prevent JS to restart drawing from the last point? I mean if i move away from the very last drawn point and i restart left clicking somewhere else JS will link the very last point with the current new one. Ideally i would like a way for drawing distinct annotations.

is it possible to achieve the same outcome with plot.line?

Many thanks again

Andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.io.state import curstate

from bokeh.models import ColumnDataSource, Tool

from bokeh.plotting import figure

output_file(‘tool.html’)

curstate().file[‘resources’].minified = False

JS_CODE_DrawAnnotation = “”"

import * as p from “core/properties”

import {GestureTool, GestureToolView} from “models/tools/gestures/gesture_tool”

export class DrawToolView extends GestureToolView

this is executed on subsequent mouse/touch moves

_pan: (e) ->

frame = @plot_model.frame

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

source.data.x.push(x)

source.data.y.push(y)

source.change.emit()

this is executed then the pan/drag ends

_pan_end: (e) -> return null

export class DrawAnnotation extends GestureTool

default_view: DrawToolView

type: “DrawAnnotation”

tool_name: “Drag Annotation”

icon: “bk-tool-icon-lasso-select”

event_type: “pan”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class DrawAnnotation(Tool):

implementation = JS_CODE_DrawAnnotation

source = Instance(ColumnDataSource)

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

plot = figure(x_range=(0, 10), y_range=(0, 10) )

plot.add_tools(DrawAnnotation(source=source_DrawAnnotation))

plot.line(x=‘x’, y=‘y’, source=source_DrawAnnotation)

show(plot)

On 30 November 2017 at 17:10, Andrea Tagliabue [email protected] wrote:

Hi Eugene,
Terrific work!! Thanks so much.
Bryan: i have been used matplotlib a lot for my apps/code but when i discovered bokeh i just thought it was much better in terms of speed and easier accessibility. Having the plot in the browser and been able to have full control of what you see is just a great experience. And you can share it so easily.

However I do have to say i really struggled to get my mind around the doc and examples. But with your and Eugene’s help i have been able to convert my old and slow matplotlib app in something much better. As soon as i will be done with my code and all my custom functionalities i will send you all my code so you can upload these very unique examples and all the community and future bokeh developer will have much less tough days.

Also. this example from Web Busino https://gist.github.com/busino/d560a58b315e7bde2321e3960ea88616/raw/6d1cc896e2e86b45b659814c4eacccf5ad353efd/bokeh_frog.html should be in the doc. That’s a such a great example of very dynamic and custom usage of the mouse control for js_on_event.
I will definitively write some personal view on https://github.com/bokeh/bokeh/issues/6370

Many thanks again

Andrea

On 30 November 2017 at 16:49, Eugene Pakhomov [email protected] wrote:

Here you go:

import bokeh.util.compiler

bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure

output_file('tool.html')
curstate().file['resources'].minified = False

JS_CODE_DrawAnnotation = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"
export class DrawToolView extends GestureToolView
  _get_scaled_frame_coords: (e) ->
    frame = @plot_model.frame
    {sx, sy} = e.bokeh
    if frame.bbox.contains(sx, sy)
      x = frame.xscales['default'].invert(sx)
      y = frame.yscales['default'].invert(sy)
      return [x, y]

  _pan_start: (e) ->
    coords = @_get_scaled_frame_coords(e)
    if coords
      [x, y] = coords
      @model.source.stream({x0: [x], y0: [y], x1: [x], y1: [y]})

  _pan: (e) ->
    coords = @_get_scaled_frame_coords(e)
    if coords
      [x, y] = coords
      last_row = @model.source.data.x0.length - 1
      @model.source.patch({x1: [[last_row, x]], y1: [[last_row, y]]})

export class DrawObliqueLine extends GestureTool
  default_view: DrawToolView
  type: "DrawObliqueLine"

  tool_name: "Line"
  icon: "bk-tool-icon-lasso-select"
  event_type: "pan"
  default_order: 12

  @define { source: [ p.Instance ] }
"""


class DrawObliqueLine(Tool):
    __implementation__ = JS_CODE_DrawAnnotation
    source = Instance(ColumnDataSource)


source_ObliqueLine = ColumnDataSource(data=dict(x0=[], y0=[], x1=[], y1=[]))

plot = figure(x_range=(0, 10), y_range=(0, 10))
plot.add_tools(DrawObliqueLine(source=source_ObliqueLine))
plot.title.text = "Pan to draw on the plot"

plot.segment(x0='x0', y0='y0', x1='x1', y1='y1', line_dash="solid",
             line_width=2, source=source_ObliqueLine)

show(plot)

Regards,

Eugene

On Thursday, November 30, 2017 at 10:07:00 PM UTC+7, Andrea Tagliabue wrote:

Hi Eugene,
i am really sorry to keep bothering at you but with no doc and very few examples i can’t see big improvements from my work. My last point of my work is to been able to draw oblique segments( ray would be the perfect solution) too.
The code i posted below is clearly wrong but i guess the idea is more and less that. I don’t understand how i can tell JS to store the initial click coordinates and while i am moving the mouse i would like to see the segment or ray drawn.
Can you suggest any idea on this?

Many thanks again
Andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.io.state import curstate

from bokeh.models import ColumnDataSource, Tool

from bokeh.plotting import figure

output_file(‘tool.html’)

curstate().file[‘resources’].minified = False

JS_CODE_DrawAnnotation = “”"

import * as p from “core/properties”

import {GestureTool, GestureToolView} from “models/tools/gestures/gesture_tool”

export class DrawToolView extends GestureToolView

this is executed on subsequent mouse/touch moves

_tap: (e) ->

frame = @plot_model.frame

@model.source.data = {x0: , y0: , x1: , y1:}

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

if source.data.y0.length == 0

source.data.x0.push(x)

source.data.y0.push(y)

else

source.data.x1.push(cb_obj.x)

source.data.y1.push(cb_obj.y)

source.change.emit()

this is executed then the pan/drag ends

_pan_end: (e) -> return null

export class DrawObliqueLine extends GestureTool

default_view: DrawToolView

type: “DrawObliqueLine”

tool_name: “Line”

icon: “bk-tool-icon-lasso-select”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class DrawObliqueLine(Tool):

implementation = JS_CODE_DrawAnnotation

source = Instance(ColumnDataSource)

source_ObliqueLine = ColumnDataSource(data=dict(x0=, y0=, x1=, y1=))

plot = figure(x_range=(0, 10), y_range=(0, 10) )

plot.add_tools(DrawObliqueLine(source=source_ObliqueLine))

plot.title.text = “Tap to draw on the plot”

plot.segment(x0=‘x0’, y0=‘y0’, x1=‘x1’, y1=‘y1’, line_dash=“solid”, line_width=2, source=source_ObliqueLine)

show(plot)

On 30 November 2017 at 14:15, Andrea Tagliabue [email protected] wrote:

yeah me too but most of the time i can’t figure out what i am doing wrong :slight_smile:

On 30 November 2017 at 13:42, Eugene Pakhomov [email protected] wrote:

Unfortunately, I don’t think so. At least, I couldn’t find any.
Usually, I just study the source code of the existing tools.

  • Eugene

On Thursday, November 30, 2017 at 7:24:57 PM UTC+7, Andrea Tagliabue wrote:

Wow really thanks for the quick answer. In order to avoid me bothering you for other issues, is there any doc regarding these custom tools? They looks to me pretty complex in terms of multiple options.
Many thanks again
Andrea

On 30 November 2017 at 13:18, Eugene Pakhomov [email protected] wrote:

Hi Andrea,

You almost got it. Here’s the working version. The things that needed to be fixed: wrong function name (must be “_tap” instead of “_pan”), wrong “default_view” (must be TapBBToolView), extra data field for the data source (you’d get an error about inconsistent column lengths).

I also realized that TapTool is not needed because all it can provide you if just a single field “event_type”, so I used a more general GestureTool instead.

import bokeh.util.compiler

bokeh.util.compiler._npmjs = 'npm.cmd'
from bokeh.core.properties import Instance
from [bokeh.io](http://bokeh.io) import output_file, show
from bokeh.io.state import curstate
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure

output_file('tool.html')
curstate().file['resources'].minified = False

JS_CODE = """
import * as p from "core/properties"
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"

export class TapBBToolView extends GestureToolView

  # this is executed on subsequent mouse/touch moves
  _tap: (e) ->
    frame = @plot_model.frame

    {sx, sy} = e.bokeh
    if not frame.bbox.contains(sx, sy)
      return null

    x = frame.xscales['default'].invert(sx)
    y = frame.yscales['default'].invert(sy)
    source = @model.source
    source.data.x.push(x)
    source.data.y.push(y)
    source.change.emit()


export class TapBBTool extends GestureTool
  default_view: TapBBToolView
  type: "TapTool"
  event_type: "tap"
  tool_name: "H Line"
  icon: "bk-tool-icon-arrow-right"
  default_order: 12

  @define { source: [ p.Instance ] }
"""


class TapBBTool(Tool):
    __implementation__ = JS_CODE
    source = Instance(ColumnDataSource)


source_HorizontalRay = ColumnDataSource(data=dict(x=[], y=[]))

plot = figure(x_range=(0, 10), y_range=(0, 10),
              tools=[TapBBTool(source=source_HorizontalRay)])
plot.title.text = "Tap to draw on the plot"
plot.ray(x='x', y='y', angle=0, length=0, line_dash="solid", line_width=2,
         source=source_HorizontalRay)

show(plot)

Regards,

Eugene

On Thursday, November 30, 2017 at 6:58:10 PM UTC+7, Andrea Tagliabue wrote:

Hi all,
i am trying to implement a custom tool for drawing rays on tap event. I have implemented it via js_on_event and works fine but i need it as a tool since i want to give the ability of drawing rays, segments or lines and hence multiple custom tools are needed.

This code gets the plot displayed in the browser but when tapping nothing happens. I am struggling to understand what i am missing.

Would you mind to help me out?

Many thanks andrea

import bokeh.util.compiler

bokeh.util.compiler._npmjs = ‘npm.cmd’

from bokeh.core.properties import Instance

from bokeh.io import output_file, show

from bokeh.models import ColumnDataSource, Tool

from bokeh.models.tools import Tap

from bokeh.plotting import figure

output_file(‘tool.html’)

JS_CODE = “”"

import * as p from “core/properties”

import {TapTool, TapToolView} from “models/tools/gestures/tap_tool”

export class TapBBToolView extends TapToolView

this is executed on subsequent mouse/touch moves

_pan: (e) ->

frame = @plot_model.frame

{sx, sy} = e.bokeh

if not frame.bbox.contains(sx, sy)

return null

x = frame.xscales[‘default’].invert(sx)

y = frame.yscales[‘default’].invert(sy)

source = @model.source

source.data.x.push(x)

source.data.y.push(y)

source.change.emit()

export class TapBBTool extends TapTool

default_view: TapToolView

type: “TapTool”

tool_name: “H Line”

icon: “bk-tool-icon-arrow-right”

event_type: “tap”

default_order: 12

@define { source: [ p.Instance ] }

“”"

class TapBBTool(Tool):

implementation = JS_CODE

source = Instance(ColumnDataSource)

source_HorizontalRay = ColumnDataSource(data=dict(x=, y=, angle=))

plot = figure(x_range=(0,10), y_range=(0,10), tools=[TapBBTool(source=source_HorizontalRay)])

plot.title.text =“Tap to draw on the plot”

plot.ray(x=‘x’, y=‘y’, angle = 0, length=0, line_dash=“solid”, line_width=2, source=source_HorizontalRay)

show(plot)

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/ac17d835-5909-4ac2-a552-9b94d3dba436%40continuum.io.

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

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/931b65c5-9b87-407a-9df2-7706e9ea3213%40continuum.io.

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

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/e90b70b6-a4a8-47df-befa-e0eb46e33a8f%40continuum.io.

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

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/5f8011a4-9894-4e7f-8e3a-664614bf61ac%40continuum.io.

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

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/c9f8b041-6ac7-4651-b754-4bad3e4ff145%40continuum.io.

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

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/89e44076-83c8-4a81-a073-1fb7cf52e50b%40continuum.io.

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