1#----------------------------------------------------------------------------- 2# Copyright (c) 2012 - 2021, Anaconda, Inc., and Bokeh Contributors. 3# All rights reserved. 4# 5# The full license is in the file LICENSE.txt, distributed with this software. 6#----------------------------------------------------------------------------- 7''' Bokeh comes with a number of interactive tools. 8 9There are five types of tool interactions: 10 11.. hlist:: 12 :columns: 5 13 14 * Pan/Drag 15 * Click/Tap 16 * Scroll/Pinch 17 * Actions 18 * Inspectors 19 20For the first three comprise the category of gesture tools, and only 21one tool for each gesture can be active at any given time. The active 22tool is indicated on the toolbar by a highlight next to the tool. 23Actions are immediate or modal operations that are only activated when 24their button in the toolbar is pressed. Inspectors are passive tools that 25merely report information or annotate the plot in some way, and may 26always be active regardless of what other tools are currently active. 27 28''' 29 30#----------------------------------------------------------------------------- 31# Boilerplate 32#----------------------------------------------------------------------------- 33import logging # isort:skip 34log = logging.getLogger(__name__) 35 36#----------------------------------------------------------------------------- 37# Imports 38#----------------------------------------------------------------------------- 39 40# Standard library imports 41import difflib 42import typing as tp 43from typing_extensions import Literal 44 45# Bokeh imports 46from ..core.enums import ( 47 Anchor, 48 Dimension, 49 Dimensions, 50 Location, 51 SelectionMode, 52 TooltipAttachment, 53 TooltipFieldFormatter, 54) 55from ..core.has_props import abstract 56from ..core.properties import ( 57 Alpha, 58 Auto, 59 Bool, 60 Color, 61 Date, 62 Datetime, 63 Dict, 64 Either, 65 Enum, 66 Float, 67 Image, 68 Instance, 69 Int, 70 List, 71 NonNullable, 72 Null, 73 Nullable, 74 Override, 75 Percent, 76 Seq, 77 String, 78 Tuple, 79) 80from ..core.validation import error 81from ..core.validation.errors import ( 82 INCOMPATIBLE_BOX_EDIT_RENDERER, 83 INCOMPATIBLE_LINE_EDIT_INTERSECTION_RENDERER, 84 INCOMPATIBLE_LINE_EDIT_RENDERER, 85 INCOMPATIBLE_POINT_DRAW_RENDERER, 86 INCOMPATIBLE_POLY_DRAW_RENDERER, 87 INCOMPATIBLE_POLY_EDIT_RENDERER, 88 INCOMPATIBLE_POLY_EDIT_VERTEX_RENDERER, 89 NO_RANGE_TOOL_RANGES, 90) 91from ..model import Model 92from ..util.deprecation import deprecated 93from ..util.string import nice_join 94from .annotations import BoxAnnotation, PolyAnnotation 95from .callbacks import Callback 96from .glyphs import Line, LineGlyph, MultiLine, Patches, Rect, XYGlyph 97from .layouts import LayoutDOM 98from .ranges import Range1d 99from .renderers import DataRenderer, GlyphRenderer 100 101#----------------------------------------------------------------------------- 102# Globals and constants 103#----------------------------------------------------------------------------- 104 105__all__ = ( 106 'Action', 107 'ActionTool', 108 'BoxEditTool', 109 'BoxSelectTool', 110 'BoxZoomTool', 111 'CrosshairTool', 112 'CustomAction', 113 'CustomJSHover', 114 'Drag', 115 'EditTool', 116 'FreehandDrawTool', 117 'HelpTool', 118 'HoverTool', 119 'Inspection', 120 'InspectTool', 121 'Gesture', 122 'GestureTool', 123 'LassoSelectTool', 124 'LineEditTool', 125 'PanTool', 126 'PointDrawTool', 127 'PolyDrawTool', 128 'PolyEditTool', 129 'PolySelectTool', 130 'ProxyToolbar', 131 'RangeTool', 132 'RedoTool', 133 'ResetTool', 134 'SaveTool', 135 'Scroll', 136 'Tap', 137 'TapTool', 138 'Tool', 139 'Toolbar', 140 'ToolbarBase', 141 'ToolbarBox', 142 'UndoTool', 143 'WheelPanTool', 144 'WheelZoomTool', 145 'ZoomInTool', 146 'ZoomOutTool', 147) 148 149#----------------------------------------------------------------------------- 150# General API 151#----------------------------------------------------------------------------- 152 153@abstract 154class Tool(Model): 155 ''' A base class for all interactive tool types. 156 157 ''' 158 159 description = Nullable(String, help=""" 160 A string describing the purpose of this tool. If not defined, an auto-generated 161 description will be used. This description will be typically presented in the 162 user interface as a tooltip. 163 """) 164 165 _known_aliases: tp.ClassVar[tp.Dict[str, tp.Callable[[], "Tool"]]] = {} 166 167 @classmethod 168 def from_string(cls, name: str) -> "Tool": 169 """ Takes a string and returns a corresponding `Tool` instance. """ 170 constructor = cls._known_aliases.get(name) 171 if constructor is not None: 172 return constructor() 173 else: 174 known_names = cls._known_aliases.keys() 175 matches, text = difflib.get_close_matches(name.lower(), known_names), "similar" 176 if not matches: 177 matches, text = known_names, "possible" 178 raise ValueError(f"unexpected tool name '{name}', {text} tools are {nice_join(matches)}") 179 180 @classmethod 181 def register_alias(cls, name: str, constructor: tp.Callable[[], "Tool"]) -> None: 182 cls._known_aliases[name] = constructor 183 184@abstract 185class ActionTool(Tool): 186 ''' A base class for tools that are buttons in the toolbar. 187 188 ''' 189 pass 190 191# TODO: deprecated, remove at bokeh 3.0 192Action = ActionTool 193 194@abstract 195class GestureTool(Tool): 196 ''' A base class for tools that respond to drag events. 197 198 ''' 199 pass 200 201# TODO: deprecated, remove at bokeh 3.0 202Gesture = GestureTool 203 204@abstract 205class Drag(GestureTool): 206 ''' A base class for tools that respond to drag events. 207 208 ''' 209 pass 210 211@abstract 212class Scroll(GestureTool): 213 ''' A base class for tools that respond to scroll events. 214 215 ''' 216 pass 217 218@abstract 219class Tap(GestureTool): 220 ''' A base class for tools that respond to tap/click events. 221 222 ''' 223 pass 224 225@abstract 226class SelectTool(GestureTool): 227 ''' A base class for tools that perfrom "selections", e.g. ``BoxSelectTool``. 228 229 ''' 230 231 names = List(String, help=""" 232 A list of names to query for. If set, only renderers that have a matching 233 value for their ``name`` attribute will be used. 234 235 .. note: 236 This property is deprecated and will be removed in bokeh 3.0. 237 238 """) 239 240 renderers = Either(Auto, List(Instance(DataRenderer)), default="auto", help=""" 241 An explicit list of renderers to hit test against. If unset, defaults to 242 all renderers on a plot. 243 """) 244 245 mode = Enum(SelectionMode, default="replace", help=""" 246 Defines what should happen when a new selection is made. The default 247 is to replace the existing selection. Other options are to append to 248 the selection, intersect with it or subtract from it. 249 """) 250 251@abstract 252class InspectTool(GestureTool): 253 ''' A base class for tools that perform "inspections", e.g. ``HoverTool``. 254 255 ''' 256 toggleable = Bool(True, help=""" 257 Whether an on/off toggle button should appear in the toolbar for this 258 inspection tool. If ``False``, the viewers of a plot will not be able to 259 toggle the inspector on or off using the toolbar. 260 """) 261 262# TODO: deprecated, remove at bokeh 3.0 263Inspection = InspectTool 264 265@abstract 266class ToolbarBase(Model): 267 ''' A base class for different toolbars. 268 269 ''' 270 271 logo = Nullable(Enum("normal", "grey"), default="normal", help=""" 272 What version of the Bokeh logo to display on the toolbar. If 273 set to None, no logo will be displayed. 274 """) 275 276 autohide = Bool(default=False, help=""" 277 Whether the toolbar will be hidden by default. Default: False. 278 If True, hides toolbar when cursor is not in canvas. 279 """) 280 281 tools = List(Instance(Tool), help=""" 282 A list of tools to add to the plot. 283 """) 284 285class Toolbar(ToolbarBase): 286 ''' Collect tools to display for a single plot. 287 288 ''' 289 290 active_drag: tp.Union[Literal["auto"], Drag, None] = Either(Null, Auto, Instance(Drag), default="auto", help=""" 291 Specify a drag tool to be active when the plot is displayed. 292 """) 293 294 active_inspect: tp.Union[Literal["auto"], InspectTool, tp.Sequence[InspectTool], None] = \ 295 Either(Null, Auto, Instance(InspectTool), Seq(Instance(InspectTool)), default="auto", help=""" 296 Specify an inspection tool or sequence of inspection tools to be active when 297 the plot is displayed. 298 """) 299 300 active_scroll: tp.Union[Literal["auto"], Scroll, None] = Either(Null, Auto, Instance(Scroll), default="auto", help=""" 301 Specify a scroll/pinch tool to be active when the plot is displayed. 302 """) 303 304 active_tap: tp.Union[Literal["auto"], Tap, None] = Either(Null, Auto, Instance(Tap), default="auto", help=""" 305 Specify a tap/click tool to be active when the plot is displayed. 306 """) 307 308 active_multi: tp.Union[Literal["auto"], GestureTool, None] = Nullable(Instance(GestureTool), help=""" 309 Specify an active multi-gesture tool, for instance an edit tool or a range 310 tool. 311 312 Note that activating a multi-gesture tool will deactivate any other gesture 313 tools as appropriate. For example, if a pan tool is set as the active drag, 314 and this property is set to a ``BoxEditTool`` instance, the pan tool will 315 be deactivated (i.e. the multi-gesture tool will take precedence). 316 """) 317 318class ProxyToolbar(ToolbarBase): 319 ''' A toolbar that allow to merge and proxy tools of toolbars in multiple 320 plots. 321 322 ''' 323 324 toolbars = List(Instance(Toolbar), help=""" 325 """) 326 327class ToolbarBox(LayoutDOM): 328 ''' A layoutable toolbar that can accept the tools of multiple plots, and 329 can merge the tools into a single button for convenience. 330 331 ''' 332 333 toolbar = Instance(ToolbarBase, help=""" 334 A toolbar associated with a plot which holds all its tools. 335 """) 336 337 toolbar_location = Enum(Location, default="right") 338 339class PanTool(Drag): 340 ''' *toolbar icon*: |pan_icon| 341 342 The pan tool allows the user to pan a Plot by left-dragging a mouse, or on 343 touch devices by dragging a finger or stylus, across the plot region. 344 345 The pan tool also activates the border regions of a Plot for "single axis" 346 panning. For instance, dragging in the vertical border or axis will effect 347 a pan in the vertical direction only, with horizontal dimension kept fixed. 348 349 .. |pan_icon| image:: /_images/icons/Pan.png 350 :height: 24px 351 352 ''' 353 354 dimensions = Enum(Dimensions, default="both", help=""" 355 Which dimensions the pan tool is constrained to act in. By default 356 the pan tool will pan in any dimension, but can be configured to only 357 pan horizontally across the width of the plot, or vertically across the 358 height of the plot. 359 """) 360 361DEFAULT_RANGE_OVERLAY = lambda: BoxAnnotation( 362 syncable=False, 363 level="overlay", 364 fill_color="lightgrey", 365 fill_alpha=0.5, 366 line_color="black", 367 line_alpha=1.0, 368 line_width=0.5, 369 line_dash=[2,2], 370) 371 372class RangeTool(Drag): 373 ''' *toolbar icon*: |range_icon| 374 375 The range tool allows the user to update range objects for either or both 376 of the x- or y-dimensions by dragging a corresponding shaded annotation to 377 move it or change its boundaries. 378 379 A common use case is to add this tool to a plot with a large fixed range, 380 but to configure the tool range from a different plot. When the user 381 manipulates the overlay, the range of the second plot will be updated 382 automatically. 383 384 .. |range_icon| image:: /_images/icons/Range.png 385 :height: 24px 386 387 ''' 388 389 x_range = Nullable(Instance(Range1d), help=""" 390 A range synchronized to the x-dimension of the overlay. If None, the overlay 391 will span the entire x-dimension. 392 """) 393 394 x_interaction = Bool(default=True, help=""" 395 Whether to respond to horizontal pan motions when an ``x_range`` is present. 396 397 By default, when an ``x_range`` is specified, it is possible to adjust the 398 horizontal position of the range box by panning horizontally inside the 399 box, or along the top or bottom edge of the box. To disable this, and fix 400 the range box in place horizontally, set to False. (The box will still 401 update if the ``x_range`` is updated programmatically.) 402 """) 403 404 y_range = Nullable(Instance(Range1d), help=""" 405 A range synchronized to the y-dimension of the overlay. If None, the overlay 406 will span the entire y-dimension. 407 """) 408 409 y_interaction = Bool(default=True, help=""" 410 Whether to respond to vertical pan motions when a ``y_range`` is present. 411 412 By default, when a ``y_range`` is specified, it is possible to adjust the 413 vertical position of the range box by panning vertically inside the box, or 414 along the top or bottom edge of the box. To disable this, and fix the range 415 box in place vertically, set to False. (The box will still update if the 416 ``y_range`` is updated programmatically.) 417 """) 418 419 overlay = Instance(BoxAnnotation, default=DEFAULT_RANGE_OVERLAY, help=""" 420 A shaded annotation drawn to indicate the configured ranges. 421 """) 422 423 @error(NO_RANGE_TOOL_RANGES) 424 def _check_no_range_tool_ranges(self): 425 if self.x_range is None and self.y_range is None: 426 return "At least one of RangeTool.x_range or RangeTool.y_range must be configured" 427 428class WheelPanTool(Scroll): 429 ''' *toolbar icon*: |wheel_pan_icon| 430 431 The wheel pan tool allows the user to pan the plot along the configured 432 dimension using the scroll wheel. 433 434 .. |wheel_pan_icon| image:: /_images/icons/WheelPan.png 435 :height: 24px 436 437 ''' 438 439 dimension = Enum(Dimension, default="width", help=""" 440 Which dimension the wheel pan tool is constrained to act in. By default the 441 wheel pan tool will pan the plot along the x-axis. 442 """) 443 444class WheelZoomTool(Scroll): 445 ''' *toolbar icon*: |wheel_zoom_icon| 446 447 The wheel zoom tool will zoom the plot in and out, centered on the 448 current mouse location. 449 450 The wheel zoom tool also activates the border regions of a Plot for 451 "single axis" zooming. For instance, zooming in the vertical border or 452 axis will effect a zoom in the vertical direction only, with the 453 horizontal dimension kept fixed. 454 455 .. |wheel_zoom_icon| image:: /_images/icons/WheelZoom.png 456 :height: 24px 457 458 ''' 459 460 dimensions = Enum(Dimensions, default="both", help=""" 461 Which dimensions the wheel zoom tool is constrained to act in. By default 462 the wheel zoom tool will zoom in any dimension, but can be configured to 463 only zoom horizontally across the width of the plot, or vertically across 464 the height of the plot. 465 """) 466 467 maintain_focus = Bool(default=True, help=""" 468 Whether or not zooming tool maintains its focus position. Setting to False 469 results in a more "gliding" behavior, allowing one to zoom out more 470 smoothly, at the cost of losing the focus position. 471 """) 472 473 zoom_on_axis = Bool(default=True, help=""" 474 Whether scrolling on an axis (outside the central plot area) should zoom 475 that dimension. 476 """) 477 478 speed = Float(default=1/600, help=""" 479 Speed at which the wheel zooms. Default is 1/600. Optimal range is between 480 0.001 and 0.09. High values will be clipped. Speed may very between browsers. 481 """) 482 483class CustomAction(ActionTool): 484 ''' Execute a custom action, e.g. ``CustomJS`` callback when a toolbar 485 icon is activated. 486 487 Example: 488 489 .. code-block:: python 490 491 tool = CustomAction(icon="icon.png", 492 callback=CustomJS(code='alert("foo")')) 493 494 plot.add_tools(tool) 495 496 ''' 497 498 def __init__(self, *args, **kwargs): 499 action_tooltip = kwargs.pop("action_tooltip", None) 500 if action_tooltip is not None: 501 deprecated((2, 3, 0), "CustomAction.action_tooltip", "CustomAction.description") 502 kwargs["description"] = action_tooltip 503 super().__init__(*args, **kwargs) 504 505 @property 506 def action_tooltip(self): 507 deprecated((2, 3, 0), "CustomAction.action_tooltip", "CustomAction.description") 508 return self.description 509 @action_tooltip.setter 510 def action_tooltip(self, description): 511 deprecated((2, 3, 0), "CustomAction.action_tooltip", "CustomAction.description") 512 self.description = description 513 514 description = Override(default="Perform a Custom Action") 515 516 callback = Nullable(Instance(Callback), help=""" 517 A Bokeh callback to execute when the custom action icon is activated. 518 """) 519 520 icon = Image(help=""" 521 An icon to display in the toolbar. 522 523 The icon can provided as a string filename for an image, a PIL ``Image`` 524 object, or an RGB(A) NumPy array. 525 """) 526 527class SaveTool(ActionTool): 528 ''' *toolbar icon*: |save_icon| 529 530 The save tool is an action. When activated, the tool opens a download dialog 531 which allows to save an image reproduction of the plot in PNG format. If 532 automatic download is not support by a web browser, the tool falls back to 533 opening the generated image in a new tab or window. User then can manually 534 save it by right clicking on the image and choosing "Save As" (or similar) 535 menu item. 536 537 .. |save_icon| image:: /_images/icons/Save.png 538 :height: 24px 539 540 ''' 541 542class ResetTool(ActionTool): 543 ''' *toolbar icon*: |reset_icon| 544 545 The reset tool is an action. When activated in the toolbar, the tool resets 546 the data bounds of the plot to their values when the plot was initially 547 created. 548 549 .. |reset_icon| image:: /_images/icons/Reset.png 550 :height: 24px 551 552 ''' 553 554 pass 555 556class TapTool(Tap, SelectTool): 557 ''' *toolbar icon*: |tap_icon| 558 559 The tap selection tool allows the user to select at single points by 560 left-clicking a mouse, or tapping with a finger. 561 562 See :ref:`userguide_styling_selected_unselected_glyphs` for information 563 on styling selected and unselected glyphs. 564 565 .. |tap_icon| image:: /_images/icons/Tap.png 566 :height: 24px 567 568 .. note:: 569 Selections can be comprised of multiple regions, even those 570 made by different selection tools. Hold down the <<shift>> key 571 while making a selection to append the new selection to any 572 previous selection that might exist. 573 574 ''' 575 576 behavior = Enum("select", "inspect", default="select", help=""" 577 This tool can be configured to either make selections or inspections 578 on associated data sources. The difference is that selection changes 579 propagate across bokeh and other components (e.g. selection glyph) 580 will be notified. Inspections don't act like this, so it's useful to 581 configure `callback` when setting `behavior='inspect'`. 582 """) 583 584 gesture = Enum("tap", "doubletap", default="tap", help=""" 585 Specifies which kind of gesture will be used to trigger the tool, 586 either a single or double tap. 587 """) 588 589 callback = Nullable(Instance(Callback), help=""" 590 A callback to execute *whenever a glyph is "hit"* by a mouse click 591 or tap. 592 593 This is often useful with the :class:`~bokeh.models.callbacks.OpenURL` 594 model to open URLs based on a user clicking or tapping a specific glyph. 595 596 However, it may also be a :class:`~bokeh.models.callbacks.CustomJS` 597 which can execute arbitrary JavaScript code in response to clicking or 598 tapping glyphs. The callback will be executed for each individual glyph 599 that is it hit by a click or tap, and will receive the ``TapTool`` model 600 as ``cb_obj``. The optional ``cb_data`` will have the data source as 601 its ``.source`` attribute and the selection geometry as its 602 ``.geometries`` attribute. 603 604 The ``.geometries`` attribute has 5 members. 605 ``.type`` is the geometry type, which always a ``.point`` for a tap event. 606 ``.sx`` and ``.sy`` are the screen X and Y coordinates where the tap occurred. 607 ``.x`` and ``.y`` are the converted data coordinates for the item that has 608 been selected. The ``.x`` and ``.y`` values are based on the axis assigned 609 to that glyph. 610 611 .. note:: 612 This callback does *not* execute on every tap, only when a glyph is 613 "hit". If you would like to execute a callback on every mouse tap, 614 please see :ref:`userguide_interaction_jscallbacks_customjs_interactions`. 615 616 """) 617 618class CrosshairTool(InspectTool): 619 ''' *toolbar icon*: |crosshair_icon| 620 621 The crosshair tool is a passive inspector tool. It is generally on at all 622 times, but can be configured in the inspector's menu associated with the 623 *toolbar icon* shown above. 624 625 The crosshair tool draws a crosshair annotation over the plot, centered on 626 the current mouse position. The crosshair tool may be configured to draw 627 across only one dimension by setting the ``dimension`` property to only 628 ``width`` or ``height``. 629 630 .. |crosshair_icon| image:: /_images/icons/Crosshair.png 631 :height: 24px 632 633 ''' 634 635 dimensions = Enum(Dimensions, default="both", help=""" 636 Which dimensions the crosshair tool is to track. By default, both vertical 637 and horizontal lines will be drawn. If only "width" is supplied, only a 638 horizontal line will be drawn. If only "height" is supplied, only a 639 vertical line will be drawn. 640 """) 641 642 line_color = Color(default="black", help=""" 643 A color to use to stroke paths with. 644 """) 645 646 line_alpha = Alpha(help=""" 647 An alpha value to use to stroke paths with. 648 """) 649 650 line_width = Float(default=1, help=""" 651 Stroke width in units of pixels. 652 """) 653 654DEFAULT_BOX_OVERLAY = lambda: BoxAnnotation( 655 syncable=False, 656 level="overlay", 657 top_units="screen", 658 left_units="screen", 659 bottom_units="screen", 660 right_units="screen", 661 fill_color="lightgrey", 662 fill_alpha=0.5, 663 line_color="black", 664 line_alpha=1.0, 665 line_width=2, 666 line_dash=[4, 4], 667) 668 669class BoxZoomTool(Drag): 670 ''' *toolbar icon*: |box_zoom_icon| 671 672 The box zoom tool allows users to define a rectangular egion of a Plot to 673 zoom to by dragging he mouse or a finger over the plot region. The end of 674 the drag event indicates the selection region is ready. 675 676 .. |box_zoom_icon| image:: /_images/icons/BoxZoom.png 677 :height: 24px 678 679 .. note:: 680 ``BoxZoomTool`` is incompatible with ``GMapPlot`` due to the manner in 681 which Google Maps exert explicit control over aspect ratios. Adding 682 this tool to a ``GMapPlot`` will have no effect. 683 684 ''' 685 686 dimensions = Enum(Dimensions, default="both", help=""" 687 Which dimensions the zoom box is to be free in. By default, users may 688 freely draw zoom boxes with any dimensions. If only "width" is supplied, 689 the box will be constrained to span the entire vertical space of the plot, 690 only the horizontal dimension can be controlled. If only "height" is 691 supplied, the box will be constrained to span the entire horizontal space 692 of the plot, and the vertical dimension can be controlled. 693 """) 694 695 overlay = Instance(BoxAnnotation, default=DEFAULT_BOX_OVERLAY, help=""" 696 A shaded annotation drawn to indicate the selection region. 697 """) 698 699 match_aspect = Bool(default=False, help=""" 700 Whether the box zoom region should be restricted to have the same 701 aspect ratio as the plot region. 702 703 .. note:: 704 If the tool is restricted to one dimension, this value has 705 no effect. 706 707 """) 708 709 origin = Enum("corner", "center", default="corner", help=""" 710 Indicates whether the rectangular zoom area should originate from a corner 711 (top-left or bottom-right depending on direction) or the center of the box. 712 """) 713 714class ZoomInTool(ActionTool): 715 ''' *toolbar icon*: |zoom_in_icon| 716 717 The zoom-in tool allows users to click a button to zoom in 718 by a fixed amount. 719 720 .. |zoom_in_icon| image:: /_images/icons/ZoomIn.png 721 :height: 24px 722 723 ''' 724 # TODO ZoomInTool dimensions should probably be constrained to be the same as ZoomOutTool 725 dimensions = Enum(Dimensions, default="both", help=""" 726 Which dimensions the zoom-in tool is constrained to act in. By default the 727 zoom-in zoom tool will zoom in any dimension, but can be configured to only 728 zoom horizontally across the width of the plot, or vertically across the 729 height of the plot. 730 """) 731 732 factor = Percent(default=0.1, help=""" 733 Percentage to zoom for each click of the zoom-in tool. 734 """) 735 736class ZoomOutTool(ActionTool): 737 ''' *toolbar icon*: |zoom_out_icon| 738 739 The zoom-out tool allows users to click a button to zoom out 740 by a fixed amount. 741 742 .. |zoom_out_icon| image:: /_images/icons/ZoomOut.png 743 :height: 24px 744 745 ''' 746 dimensions = Enum(Dimensions, default="both", help=""" 747 Which dimensions the zoom-out tool is constrained to act in. By default the 748 zoom-out tool will zoom in any dimension, but can be configured to only 749 zoom horizontally across the width of the plot, or vertically across the 750 height of the plot. 751 """) 752 753 factor = Percent(default=0.1, help=""" 754 Percentage to zoom for each click of the zoom-in tool. 755 """) 756 757class BoxSelectTool(Drag, SelectTool): 758 ''' *toolbar icon*: |box_select_icon| 759 760 The box selection tool allows users to make selections on a Plot by showing 761 a rectangular region by dragging the mouse or a finger over the plot area. 762 The end of the drag event indicates the selection region is ready. 763 764 See :ref:`userguide_styling_selected_unselected_glyphs` for information 765 on styling selected and unselected glyphs. 766 767 768 .. |box_select_icon| image:: /_images/icons/BoxSelect.png 769 :height: 24px 770 771 ''' 772 773 select_every_mousemove = Bool(False, help=""" 774 Whether a selection computation should happen on every mouse event, or only 775 once, when the selection region is completed. Default: False 776 """) 777 778 dimensions = Enum(Dimensions, default="both", help=""" 779 Which dimensions the box selection is to be free in. By default, users may 780 freely draw selections boxes with any dimensions. If only "width" is set, 781 the box will be constrained to span the entire vertical space of the plot, 782 only the horizontal dimension can be controlled. If only "height" is set, 783 the box will be constrained to span the entire horizontal space of the 784 plot, and the vertical dimension can be controlled. 785 """) 786 787 overlay = Instance(BoxAnnotation, default=DEFAULT_BOX_OVERLAY, help=""" 788 A shaded annotation drawn to indicate the selection region. 789 """) 790 791 origin = Enum("corner", "center", default="corner", help=""" 792 Indicates whether the rectangular selection area should originate from a corner 793 (top-left or bottom-right depending on direction) or the center of the box. 794 """) 795 796DEFAULT_POLY_OVERLAY = lambda: PolyAnnotation( 797 syncable=False, 798 level="overlay", 799 xs_units="screen", 800 ys_units="screen", 801 fill_color="lightgrey", 802 fill_alpha=0.5, 803 line_color="black", 804 line_alpha=1.0, 805 line_width=2, 806 line_dash=[4, 4] 807) 808 809class LassoSelectTool(Drag, SelectTool): 810 ''' *toolbar icon*: |lasso_select_icon| 811 812 The lasso selection tool allows users to make selections on a Plot by 813 indicating a free-drawn "lasso" region by dragging the mouse or a finger 814 over the plot region. The end of the drag event indicates the selection 815 region is ready. 816 817 See :ref:`userguide_styling_selected_unselected_glyphs` for information 818 on styling selected and unselected glyphs. 819 820 .. note:: 821 Selections can be comprised of multiple regions, even those made by 822 different selection tools. Hold down the <<shift>> key while making a 823 selection to append the new selection to any previous selection that 824 might exist. 825 826 .. |lasso_select_icon| image:: /_images/icons/LassoSelect.png 827 :height: 24px 828 829 ''' 830 831 select_every_mousemove = Bool(True, help=""" 832 Whether a selection computation should happen on every mouse event, or only 833 once, when the selection region is completed. 834 """) 835 836 overlay = Instance(PolyAnnotation, default=DEFAULT_POLY_OVERLAY, help=""" 837 A shaded annotation drawn to indicate the selection region. 838 """) 839 840class PolySelectTool(Tap, SelectTool): 841 ''' *toolbar icon*: |poly_select_icon| 842 843 The polygon selection tool allows users to make selections on a 844 Plot by indicating a polygonal region with mouse clicks. single 845 clicks (or taps) add successive points to the definition of the 846 polygon, and a double click (or tap) indicates the selection 847 region is ready. 848 849 See :ref:`userguide_styling_selected_unselected_glyphs` for information 850 on styling selected and unselected glyphs. 851 852 .. note:: 853 Selections can be comprised of multiple regions, even those 854 made by different selection tools. Hold down the <<shift>> key 855 while making a selection to append the new selection to any 856 previous selection that might exist. 857 858 .. |poly_select_icon| image:: /_images/icons/PolygonSelect.png 859 :height: 24px 860 861 ''' 862 863 overlay = Instance(PolyAnnotation, default=DEFAULT_POLY_OVERLAY, help=""" 864 A shaded annotation drawn to indicate the selection region. 865 """) 866 867class CustomJSHover(Model): 868 ''' Define a custom formatter to apply to a hover tool field. 869 870 This model can be configured with JavaScript code to format hover tooltips. 871 The JavaScript code has access to the current value to format, some special 872 variables, and any format configured on the tooltip. The variable ``value`` 873 will contain the untransformed value. The variable ``special_vars`` will 874 provide a dict with the following contents: 875 876 * ``x`` data-space x-coordinate of the mouse 877 * ``y`` data-space y-coordinate of the mouse 878 * ``sx`` screen-space x-coordinate of the mouse 879 * ``sy`` screen-space y-coordinate of the mouse 880 * ``data_x`` data-space x-coordinate of the hovered glyph 881 * ``data_y`` data-space y-coordinate of the hovered glyph 882 * ``indices`` column indices of all currently hovered glyphs 883 * ``name`` value of the ``name`` property of the hovered glyph renderer 884 885 If the hover is over a "multi" glyph such as ``Patches`` or ``MultiLine`` 886 then a ``segment_index`` key will also be present. 887 888 Finally, the value of the format passed in the tooltip specification is 889 available as the ``format`` variable. 890 891 Example: 892 893 As an example, the following code adds a custom formatter to format 894 WebMercator northing coordinates (in meters) as a latitude: 895 896 .. code-block:: python 897 898 lat_custom = CustomJSHover(code=""" 899 var projections = Bokeh.require("core/util/projections"); 900 var x = special_vars.x 901 var y = special_vars.y 902 var coords = projections.wgs84_mercator.invert(x, y) 903 return "" + coords[1] 904 """) 905 906 p.add_tools(HoverTool( 907 tooltips=[( 'lat','@y{custom}' )], 908 formatters={'@y':lat_custom} 909 )) 910 911 .. warning:: 912 The explicit purpose of this Bokeh Model is to embed *raw JavaScript 913 code* for a browser to execute. If any part of the code is derived 914 from untrusted user inputs, then you must take appropriate care to 915 sanitize the user input prior to passing to Bokeh. 916 917 ''' 918 919 args = Dict(String, Instance(Model), help=""" 920 A mapping of names to Bokeh plot objects. These objects are made available 921 to the callback code snippet as the values of named parameters to the 922 callback. 923 """) 924 925 code = String(default="", help=""" 926 A snippet of JavaScript code to transform a single value. The variable 927 ``value`` will contain the untransformed value and can be expected to be 928 present in the function namespace at render time. Additionally, the 929 variable ``special_vars`` will be available, and will provide a dict 930 with the following contents: 931 932 * ``x`` data-space x-coordinate of the mouse 933 * ``y`` data-space y-coordinate of the mouse 934 * ``sx`` screen-space x-coordinate of the mouse 935 * ``sy`` screen-space y-coordinate of the mouse 936 * ``data_x`` data-space x-coordinate of the hovered glyph 937 * ``data_y`` data-space y-coordinate of the hovered glyph 938 * ``indices`` column indices of all currently hovered glyphs 939 940 If the hover is over a "multi" glyph such as ``Patches`` or ``MultiLine`` 941 then a ``segment_index`` key will also be present. 942 943 Finally, the value of the format passed in the tooltip specification is 944 available as the ``format`` variable. 945 946 The snippet will be made into the body of a function and therefore requires 947 a return statement. 948 949 Example: 950 951 .. code-block:: javascript 952 953 code = ''' 954 return value + " total" 955 ''' 956 """) 957 958class HoverTool(InspectTool): 959 ''' *toolbar icon*: |hover_icon| 960 961 The hover tool is a passive inspector tool. It is generally on at all 962 times, but can be configured in the inspector's menu associated with the 963 *toolbar icon* shown above. 964 965 By default, the hover tool displays informational tooltips whenever the 966 cursor is directly over a glyph. The data to show comes from the glyph's 967 data source, and what to display is configurable with the ``tooltips`` 968 property that maps display names to columns in the data source, or to 969 special known variables. 970 971 Here is an example of how to configure and use the hover tool:: 972 973 # Add tooltip (name, field) pairs to the tool. See below for a 974 # description of possible field values. 975 hover.tooltips = [ 976 ("index", "$index"), 977 ("(x,y)", "($x, $y)"), 978 ("radius", "@radius"), 979 ("fill color", "$color[hex, swatch]:fill_color"), 980 ("fill color", "$color[hex]:fill_color"), 981 ("fill color", "$color:fill_color"), 982 ("fill color", "$swatch:fill_color"), 983 ("foo", "@foo"), 984 ("bar", "@bar"), 985 ("baz", "@baz{safe}"), 986 ("total", "@total{$0,0.00}" 987 ] 988 989 You can also supply a ``Callback`` to the ``HoverTool``, to build custom 990 interactions on hover. In this case you may want to turn the tooltips 991 off by setting ``tooltips=None``. 992 993 .. warning:: 994 When supplying a callback or custom template, the explicit intent 995 of this Bokeh Model is to embed *raw HTML and JavaScript code* for 996 a browser to execute. If any part of the code is derived from untrusted 997 user inputs, then you must take appropriate care to sanitize the user 998 input prior to passing to Bokeh. 999 1000 Hover tool does not currently work with the following glyphs: 1001 1002 .. hlist:: 1003 :columns: 3 1004 1005 * annulus 1006 * arc 1007 * bezier 1008 * image_url 1009 * oval 1010 * patch 1011 * quadratic 1012 * ray 1013 * step 1014 * text 1015 1016 .. |hover_icon| image:: /_images/icons/Hover.png 1017 :height: 24px 1018 1019 ''' 1020 1021 names = List(String, help=""" 1022 A list of names to query for. If set, only renderers that have a matching 1023 value for their ``name`` attribute will be used. 1024 1025 .. note: 1026 This property is deprecated and will be removed in bokeh 3.0. 1027 1028 """) 1029 1030 renderers = Either(Auto, List(Instance(DataRenderer)), default="auto", help=""" 1031 An explicit list of renderers to hit test against. If unset, defaults to 1032 all renderers on a plot. 1033 """) 1034 1035 callback = Nullable(Instance(Callback), help=""" 1036 A callback to run in the browser whenever the input's value changes. The 1037 ``cb_data`` parameter that is available to the Callback code will contain two 1038 ``HoverTool`` specific fields: 1039 1040 :index: object containing the indices of the hovered points in the data source 1041 :geometry: object containing the coordinates of the hover cursor 1042 """) 1043 1044 tooltips = Either(Null, String, List(Tuple(String, String)), 1045 default=[ 1046 ("index","$index"), 1047 ("data (x, y)","($x, $y)"), 1048 ("screen (x, y)","($sx, $sy)"), 1049 ], help=""" 1050 The (name, field) pairs describing what the hover tool should 1051 display when there is a hit. 1052 1053 Field names starting with "@" are interpreted as columns on the 1054 data source. For instance, "@temp" would look up values to display 1055 from the "temp" column of the data source. 1056 1057 Field names starting with "$" are special, known fields: 1058 1059 :$index: index of hovered point in the data source 1060 :$name: value of the ``name`` property of the hovered glyph renderer 1061 :$x: x-coordinate under the cursor in data space 1062 :$y: y-coordinate under the cursor in data space 1063 :$sx: x-coordinate under the cursor in screen (canvas) space 1064 :$sy: y-coordinate under the cursor in screen (canvas) space 1065 :$color: color data from data source, with the syntax: 1066 ``$color[options]:field_name``. The available options 1067 are: ``hex`` (to display the color as a hex value), ``swatch`` 1068 (color data from data source displayed as a small color box) 1069 :$swatch: color data from data source displayed as a small color box 1070 1071 Field names that begin with ``@`` are associated with columns in a 1072 ``ColumnDataSource``. For instance the field name ``"@price"`` will 1073 display values from the ``"price"`` column whenever a hover is triggered. 1074 If the hover is for the 17th glyph, then the hover tooltip will 1075 correspondingly display the 17th price value. 1076 1077 Note that if a column name contains spaces, the it must be supplied by 1078 surrounding it in curly braces, e.g. ``@{adjusted close}`` will display 1079 values from a column named ``"adjusted close"``. 1080 1081 Sometimes (especially with stacked charts) it is desirable to allow the 1082 name of the column be specified indirectly. The field name ``@$name`` is 1083 distinguished in that it will look up the ``name`` field on the hovered 1084 glyph renderer, and use that value as the column name. For instance, if 1085 a user hovers with the name ``"US East"``, then ``@$name`` is equivalent to 1086 ``@{US East}``. 1087 1088 By default, values for fields (e.g. ``@foo``) are displayed in a basic 1089 numeric format. However it is possible to control the formatting of values 1090 more precisely. Fields can be modified by appending a format specified to 1091 the end in curly braces. Some examples are below. 1092 1093 .. code-block:: python 1094 1095 "@foo{0,0.000}" # formats 10000.1234 as: 10,000.123 1096 1097 "@foo{(.00)}" # formats -10000.1234 as: (10000.123) 1098 1099 "@foo{($ 0.00 a)}" # formats 1230974 as: $ 1.23 m 1100 1101 Specifying a format ``{safe}`` after a field name will override automatic 1102 escaping of the tooltip data source. Any HTML tags in the data tags will 1103 be rendered as HTML in the resulting HoverTool output. See 1104 :ref:`custom_hover_tooltip` for a more detailed example. 1105 1106 ``None`` is also a valid value for tooltips. This turns off the 1107 rendering of tooltips. This is mostly useful when supplying other 1108 actions on hover via the callback property. 1109 1110 .. note:: 1111 The tooltips attribute can also be configured with a mapping type, 1112 e.g. ``dict`` or ``OrderedDict``. However, if a ``dict`` is used, 1113 the visual presentation order is unspecified. 1114 1115 """).accepts(Dict(String, String), lambda d: list(d.items())) 1116 1117 formatters = Dict(String, Either(Enum(TooltipFieldFormatter), Instance(CustomJSHover)), default=lambda: dict(), help=""" 1118 Specify the formatting scheme for data source columns, e.g. 1119 1120 .. code-block:: python 1121 1122 tool.formatters = {"@date": "datetime"} 1123 1124 will cause format specifications for the "date" column to be interpreted 1125 according to the "datetime" formatting scheme. The following schemes are 1126 available: 1127 1128 :``"numeral"``: 1129 Provides a wide variety of formats for numbers, currency, bytes, times, 1130 and percentages. The full set of formats can be found in the 1131 |NumeralTickFormatter| reference documentation. 1132 1133 :``"datetime"``: 1134 Provides formats for date and time values. The full set of formats is 1135 listed in the |DatetimeTickFormatter| reference documentation. 1136 1137 :``"printf"``: 1138 Provides formats similar to C-style "printf" type specifiers. See the 1139 |PrintfTickFormatter| reference documentation for complete details. 1140 1141 If no formatter is specified for a column name, the default ``"numeral"`` 1142 formatter is assumed. 1143 1144 .. |NumeralTickFormatter| replace:: :class:`~bokeh.models.formatters.NumeralTickFormatter` 1145 .. |DatetimeTickFormatter| replace:: :class:`~bokeh.models.formatters.DatetimeTickFormatter` 1146 .. |PrintfTickFormatter| replace:: :class:`~bokeh.models.formatters.PrintfTickFormatter` 1147 1148 """) 1149 1150 mode = Enum("mouse", "hline", "vline", help=""" 1151 Whether to consider hover pointer as a point (x/y values), or a 1152 span on h or v directions. 1153 """) 1154 1155 muted_policy = Enum("show", "ignore", 1156 default="show", help=""" 1157 Whether to avoid showing tooltips on muted glyphs. 1158 """) 1159 1160 point_policy = Enum("snap_to_data", "follow_mouse", "none", help=""" 1161 Whether the tooltip position should snap to the "center" (or other anchor) 1162 position of the associated glyph, or always follow the current mouse cursor 1163 position. 1164 """) 1165 1166 line_policy = Enum("prev", "next", "nearest", "interp", "none", 1167 default="nearest", help=""" 1168 When showing tooltips for lines, designates whether the tooltip position 1169 should be the "previous" or "next" points on the line, the "nearest" point 1170 to the current mouse position, or "interpolate" along the line to the 1171 current mouse position. 1172 """) 1173 1174 anchor = Enum(Anchor, default="center", help=""" 1175 If point policy is set to `"snap_to_data"`, `anchor` defines the attachment 1176 point of a tooltip. The default is to attach to the center of a glyph. 1177 """) 1178 1179 attachment = Enum(TooltipAttachment, help=""" 1180 Whether the tooltip should be displayed to the left or right of the cursor 1181 position or above or below it, or if it should be automatically placed 1182 in the horizontal or vertical dimension. 1183 """) 1184 1185 show_arrow = Bool(default=True, help=""" 1186 Whether tooltip's arrow should be shown. 1187 """) 1188 1189DEFAULT_HELP_TIP = "Click the question mark to learn more about Bokeh plot tools." 1190DEFAULT_HELP_URL = "https://docs.bokeh.org/en/latest/docs/user_guide/tools.html" 1191 1192class HelpTool(ActionTool): 1193 ''' A button tool to provide a "help" link to users. 1194 1195 The hover text can be customized through the ``help_tooltip`` attribute 1196 and the redirect site overridden as well. 1197 1198 ''' 1199 1200 def __init__(self, *args, **kwargs): 1201 help_tooltip = kwargs.pop("help_tooltip", None) 1202 if help_tooltip is not None: 1203 deprecated((2, 3, 0), "HelpTool.help_tooltip", "HelpTool.description") 1204 kwargs["description"] = help_tooltip 1205 super().__init__(*args, **kwargs) 1206 1207 @property 1208 def help_tooltip(self): 1209 deprecated((2, 3, 0), "HelpTool.help_tooltip", "HelpTool.description") 1210 return self.description 1211 @help_tooltip.setter 1212 def help_tooltip(self, description): 1213 deprecated((2, 3, 0), "HelpTool.help_tooltip", "HelpTool.description") 1214 self.description = description 1215 1216 description = Override(default=DEFAULT_HELP_TIP) 1217 1218 redirect = String(default=DEFAULT_HELP_URL, help=""" 1219 Site to be redirected through upon click. 1220 """) 1221 1222class UndoTool(ActionTool): 1223 ''' *toolbar icon*: |undo_icon| 1224 1225 Undo tool allows to restore previous state of the plot. 1226 1227 .. |undo_icon| image:: /_images/icons/Undo.png 1228 :height: 24px 1229 1230 ''' 1231 1232class RedoTool(ActionTool): 1233 ''' *toolbar icon*: |redo_icon| 1234 1235 Redo tool reverses the last action performed by undo tool. 1236 1237 .. |redo_icon| image:: /_images/icons/Redo.png 1238 :height: 24px 1239 1240 ''' 1241 1242@abstract 1243class EditTool(GestureTool): 1244 ''' A base class for all interactive draw tool types. 1245 1246 ''' 1247 1248 def __init__(self, *args, **kwargs): 1249 custom_tooltip = kwargs.pop("custom_tooltip", None) 1250 if custom_tooltip is not None: 1251 deprecated((2, 3, 0), "EditTool.custom_tooltip", "EditTool.description") 1252 kwargs["description"] = custom_tooltip 1253 super().__init__(*args, **kwargs) 1254 1255 @property 1256 def custom_tooltip(self): 1257 deprecated((2, 3, 0), "EditTool.custom_tooltip", "EditTool.description") 1258 return self.description 1259 @custom_tooltip.setter 1260 def custom_tooltip(self, description): 1261 deprecated((2, 3, 0), "EditTool.custom_tooltip", "EditTool.description") 1262 self.description = description 1263 1264 empty_value = NonNullable(Either(Bool, Int, Float, Date, Datetime, Color, String), help=""" 1265 Defines the value to insert on non-coordinate columns when a new 1266 glyph is inserted into the ``ColumnDataSource`` columns, e.g. when a 1267 circle glyph defines 'x', 'y' and 'color' columns, adding a new 1268 point will add the x and y-coordinates to 'x' and 'y' columns and 1269 the color column will be filled with the defined empty value. 1270 """) 1271 1272 custom_icon = Nullable(Image, help=""" 1273 An icon to display in the toolbar. 1274 1275 The icon can provided as a string filename for an image, a PIL ``Image`` 1276 object, or an RGB(A) NumPy array. 1277 """) 1278 1279 renderers = List(Instance(GlyphRenderer), help=""" 1280 An explicit list of renderers corresponding to scatter glyphs that may 1281 be edited. 1282 """) 1283 1284@abstract 1285class PolyTool(EditTool): 1286 ''' A base class for polygon draw/edit tools. ''' 1287 1288 vertex_renderer = Nullable(Instance(GlyphRenderer), help=""" 1289 The renderer used to render the vertices of a selected line or polygon. 1290 """) 1291 1292 @error(INCOMPATIBLE_POLY_EDIT_VERTEX_RENDERER) 1293 def _check_compatible_vertex_renderer(self): 1294 if self.vertex_renderer is None: 1295 return 1296 glyph = self.vertex_renderer.glyph 1297 if not isinstance(glyph, XYGlyph): 1298 return "glyph type %s found." % type(glyph).__name__ 1299 1300class BoxEditTool(EditTool, Drag, Tap): 1301 ''' *toolbar icon*: |box_edit_icon| 1302 1303 Allows drawing, dragging and deleting ``Rect`` glyphs on one or more 1304 renderers by editing the underlying ``ColumnDataSource`` data. Like other 1305 drawing tools, the renderers that are to be edited must be supplied 1306 explicitly as a list. When drawing a new box the data will always be added 1307 to the ``ColumnDataSource`` on the first supplied renderer. 1308 1309 The tool will modify the columns on the data source corresponding to the 1310 ``x``, ``y``, ``width`` and ``height`` values of the glyph. Any additional 1311 columns in the data source will be padded with ``empty_value``, when adding 1312 a new box. 1313 1314 The supported actions include: 1315 1316 * Add box: Hold shift then click and drag anywhere on the plot or double 1317 tap once to start drawing, move the mouse and double tap again to finish 1318 drawing. 1319 1320 * Move box: Click and drag an existing box, the box will be dropped once 1321 you let go of the mouse button. 1322 1323 * Delete box: Tap a box to select it then press <<backspace>> key while the 1324 mouse is within the plot area. 1325 1326 To **Move** or **Delete** multiple boxes at once: 1327 1328 * Move selection: Select box(es) with <<shift>>+tap (or another selection 1329 tool) then drag anywhere on the plot. Selecting and then dragging on a 1330 specific box will move both. 1331 1332 * Delete selection: Select box(es) with <<shift>>+tap (or another selection 1333 tool) then press <<backspace>> while the mouse is within the plot area. 1334 1335 .. |box_edit_icon| image:: /_images/icons/BoxEdit.png 1336 :height: 24px 1337 ''' 1338 1339 dimensions = Enum(Dimensions, default="both", help=""" 1340 Which dimensions the box drawing is to be free in. By default, users may 1341 freely draw boxes with any dimensions. If only "width" is set, the box will 1342 be constrained to span the entire vertical space of the plot, only the 1343 horizontal dimension can be controlled. If only "height" is set, the box 1344 will be constrained to span the entire horizontal space of the plot, and the 1345 vertical dimension can be controlled. 1346 """) 1347 1348 num_objects = Int(default=0, help=""" 1349 Defines a limit on the number of boxes that can be drawn. By default there 1350 is no limit on the number of objects, but if enabled the oldest drawn box 1351 will be dropped to make space for the new box being added. 1352 """) 1353 1354 @error(INCOMPATIBLE_BOX_EDIT_RENDERER) 1355 def _check_compatible_renderers(self): 1356 incompatible_renderers = [] 1357 for renderer in self.renderers: 1358 if not isinstance(renderer.glyph, Rect): 1359 incompatible_renderers.append(renderer) 1360 if incompatible_renderers: 1361 glyph_types = ', '.join(type(renderer.glyph).__name__ for renderer in incompatible_renderers) 1362 return "%s glyph type(s) found." % glyph_types 1363 1364class PointDrawTool(EditTool, Drag, Tap): 1365 ''' *toolbar icon*: |point_draw_icon| 1366 1367 The PointDrawTool allows adding, dragging and deleting point-like glyphs 1368 (i.e subclasses of``XYGlyph``) on one or more renderers by editing the 1369 underlying ``ColumnDataSource`` data. Like other drawing tools, the 1370 renderers that are to be edited must be supplied explicitly as a list. Any 1371 newly added points will be inserted on the ``ColumnDataSource`` of the 1372 first supplied renderer. 1373 1374 The tool will modify the columns on the data source corresponding to the 1375 ``x`` and ``y`` values of the glyph. Any additional columns in the data 1376 source will be padded with the given ``empty_value`` when adding a new 1377 point. 1378 1379 .. note:: 1380 The data source updates will trigger data change events continuously 1381 throughout the edit operations on the BokehJS side. In Bokeh server 1382 apps, the data source will only be synced once, when the edit operation 1383 finishes. 1384 1385 The supported actions include: 1386 1387 * Add point: Tap anywhere on the plot 1388 1389 * Move point: Tap and drag an existing point, the point will be 1390 dropped once you let go of the mouse button. 1391 1392 * Delete point: Tap a point to select it then press <<backspace>> 1393 key while the mouse is within the plot area. 1394 1395 .. |point_draw_icon| image:: /_images/icons/PointDraw.png 1396 :height: 24px 1397 ''' 1398 1399 add = Bool(default=True, help=""" 1400 Enables adding of new points on tap events. 1401 """) 1402 1403 drag = Bool(default=True, help=""" 1404 Enables dragging of existing points on pan events. 1405 """) 1406 1407 num_objects = Int(default=0, help=""" 1408 Defines a limit on the number of points that can be drawn. By default there 1409 is no limit on the number of objects, but if enabled the oldest drawn point 1410 will be dropped to make space for the new point. 1411 """) 1412 1413 @error(INCOMPATIBLE_POINT_DRAW_RENDERER) 1414 def _check_compatible_renderers(self): 1415 incompatible_renderers = [] 1416 for renderer in self.renderers: 1417 if not isinstance(renderer.glyph, XYGlyph): 1418 incompatible_renderers.append(renderer) 1419 if incompatible_renderers: 1420 glyph_types = ', '.join(type(renderer.glyph).__name__ for renderer in incompatible_renderers) 1421 return "%s glyph type(s) found." % glyph_types 1422 1423class PolyDrawTool(PolyTool, Drag, Tap): 1424 ''' *toolbar icon*: |poly_draw_icon| 1425 1426 The PolyDrawTool allows drawing, selecting and deleting ``Patches`` and 1427 ``MultiLine`` glyphs on one or more renderers by editing the underlying 1428 ``ColumnDataSource`` data. Like other drawing tools, the renderers that 1429 are to be edited must be supplied explicitly. 1430 1431 The tool will modify the columns on the data source corresponding to the 1432 ``xs`` and ``ys`` values of the glyph. Any additional columns in the data 1433 source will be padded with the declared ``empty_value``, when adding a new 1434 point. 1435 1436 If a ``vertex_renderer`` with an point-like glyph is supplied then the 1437 ``PolyDrawTool`` will use it to display the vertices of the multi-lines or 1438 patches on all supplied renderers. This also enables the ability to snap 1439 to existing vertices while drawing. 1440 1441 The supported actions include: 1442 1443 * Add patch or multi-line: Double tap to add the first vertex, then use tap 1444 to add each subsequent vertex, to finalize the draw action double tap to 1445 insert the final vertex or press the <<esc> key. 1446 1447 * Move patch or ulti-line: Tap and drag an existing patch/multi-line, the 1448 point will be dropped once you let go of the mouse button. 1449 1450 * Delete patch or multi-line: Tap a patch/multi-line to select it then 1451 press <<backspace>> key while the mouse is within the plot area. 1452 1453 .. |poly_draw_icon| image:: /_images/icons/PolyDraw.png 1454 :height: 24px 1455 ''' 1456 1457 drag = Bool(default=True, help=""" 1458 Enables dragging of existing patches and multi-lines on pan events. 1459 """) 1460 1461 num_objects = Int(default=0, help=""" 1462 Defines a limit on the number of patches or multi-lines that can be drawn. 1463 By default there is no limit on the number of objects, but if enabled the 1464 oldest drawn patch or multi-line will be dropped to make space for the new 1465 patch or multi-line. 1466 """) 1467 1468 @error(INCOMPATIBLE_POLY_DRAW_RENDERER) 1469 def _check_compatible_renderers(self): 1470 incompatible_renderers = [] 1471 for renderer in self.renderers: 1472 if not isinstance(renderer.glyph, (MultiLine, Patches)): 1473 incompatible_renderers.append(renderer) 1474 if incompatible_renderers: 1475 glyph_types = ', '.join(type(renderer.glyph).__name__ for renderer in incompatible_renderers) 1476 return "%s glyph type(s) found." % glyph_types 1477 1478class FreehandDrawTool(EditTool, Drag, Tap): 1479 ''' *toolbar icon*: |freehand_draw_icon| 1480 1481 Allows freehand drawing of ``Patches`` and ``MultiLine`` glyphs. The glyph 1482 to draw may be defined via the ``renderers`` property. 1483 1484 The tool will modify the columns on the data source corresponding to the 1485 ``xs`` and ``ys`` values of the glyph. Any additional columns in the data 1486 source will be padded with the declared ``empty_value``, when adding a new 1487 point. 1488 1489 The supported actions include: 1490 1491 * Draw vertices: Click and drag to draw a line 1492 1493 * Delete patch/multi-line: Tap a patch/multi-line to select it then press 1494 <<backspace>> key while the mouse is within the plot area. 1495 1496 .. |freehand_draw_icon| image:: /_images/icons/FreehandDraw.png 1497 :height: 24px 1498 ''' 1499 1500 num_objects = Int(default=0, help=""" 1501 Defines a limit on the number of patches or multi-lines that can be drawn. 1502 By default there is no limit on the number of objects, but if enabled the 1503 oldest drawn patch or multi-line will be overwritten when the limit is 1504 reached. 1505 """) 1506 1507 @error(INCOMPATIBLE_POLY_DRAW_RENDERER) 1508 def _check_compatible_renderers(self): 1509 incompatible_renderers = [] 1510 for renderer in self.renderers: 1511 if not isinstance(renderer.glyph, (MultiLine, Patches)): 1512 incompatible_renderers.append(renderer) 1513 if incompatible_renderers: 1514 glyph_types = ', '.join(type(renderer.glyph).__name__ for renderer in incompatible_renderers) 1515 return "%s glyph type(s) found." % glyph_types 1516 1517class PolyEditTool(PolyTool, Drag, Tap): 1518 ''' *toolbar icon*: |poly_edit_icon| 1519 1520 The PolyEditTool allows editing the vertices of one or more ``Patches`` or 1521 ``MultiLine`` glyphs. Glyphs to be edited are defined via the ``renderers`` 1522 property and a renderer for the vertices is set via the ``vertex_renderer`` 1523 property (must render a point-like Glyph (a subclass of ``XYGlyph``). 1524 1525 The tool will modify the columns on the data source corresponding to the 1526 ``xs`` and ``ys`` values of the glyph. Any additional columns in the data 1527 source will be padded with the declared``empty_value``, when adding a new 1528 point. 1529 1530 The supported actions include: 1531 1532 * Show vertices: Double tap an existing patch or multi-line 1533 1534 * Add vertex: Double tap an existing vertex to select it, the tool will 1535 draw the next point, to add it tap in a new location. To finish editing 1536 and add a point double tap otherwise press the <<esc> key to cancel. 1537 1538 * Move vertex: Drag an existing vertex and let go of the mouse button to 1539 release it. 1540 1541 * Delete vertex: After selecting one or more vertices press <<backspace>> 1542 while the mouse cursor is within the plot area. 1543 1544 .. |poly_edit_icon| image:: /_images/icons/PolyEdit.png 1545 :height: 24px 1546 ''' 1547 1548 @error(INCOMPATIBLE_POLY_EDIT_RENDERER) 1549 def _check_compatible_renderers(self): 1550 incompatible_renderers = [] 1551 for renderer in self.renderers: 1552 if not isinstance(renderer.glyph, (MultiLine, Patches)): 1553 incompatible_renderers.append(renderer) 1554 if incompatible_renderers: 1555 glyph_types = ', '.join(type(renderer.glyph).__name__ 1556 for renderer in incompatible_renderers) 1557 return "%s glyph type(s) found." % glyph_types 1558 1559 1560class LineEditTool(EditTool, Drag, Tap): 1561 ''' *toolbar icon*: |line_edit_icon| 1562 1563 The LineEditTool allows editing the intersection points of one or more ``Line`` glyphs. 1564 Glyphs to be edited are defined via the ``renderers`` 1565 property and a renderer for the intersections is set via the ``intersection_renderer`` 1566 property (must render a point-like Glyph (a subclass of ``XYGlyph``). 1567 1568 The tool will modify the columns on the data source corresponding to the 1569 ``x`` and ``y`` values of the glyph. Any additional columns in the data 1570 source will be padded with the declared``empty_value``, when adding a new 1571 point. 1572 1573 The supported actions include: 1574 1575 * Show intersections: Double tap an existing line 1576 1577 * Move point: Drag an existing point and let go of the mouse button to 1578 release it. 1579 1580 .. |line_edit_icon| image:: /_images/icons/LineEdit.png 1581 :height: 24px 1582 ''' 1583 1584 intersection_renderer = Instance(GlyphRenderer, help=""" 1585 The renderer used to render the intersections of a selected line 1586 """) 1587 1588 dimensions = Enum(Dimensions, default="both", help=""" 1589 Which dimensions this edit tool is constrained to act in. By default 1590 the line edit tool allows moving points in any dimension, but can be 1591 configured to only allow horizontal movement across the width of the 1592 plot, or vertical across the height of the plot. 1593 """) 1594 1595 @error(INCOMPATIBLE_LINE_EDIT_INTERSECTION_RENDERER) 1596 def _check_compatible_intersection_renderer(self): 1597 glyph = self.intersection_renderer.glyph 1598 if not isinstance(glyph, LineGlyph): 1599 return "glyph type %s found." % type(glyph).__name__ 1600 1601 @error(INCOMPATIBLE_LINE_EDIT_RENDERER) 1602 def _check_compatible_renderers(self): 1603 incompatible_renderers = [] 1604 for renderer in self.renderers: 1605 if not isinstance(renderer.glyph, (Line,)): 1606 incompatible_renderers.append(renderer) 1607 if incompatible_renderers: 1608 glyph_types = ', '.join(type(renderer.glyph).__name__ 1609 for renderer in incompatible_renderers) 1610 return "%s glyph type(s) found." % glyph_types 1611 1612# 1613#----------------------------------------------------------------------------- 1614# Dev API 1615#----------------------------------------------------------------------------- 1616 1617#----------------------------------------------------------------------------- 1618# Private API 1619#----------------------------------------------------------------------------- 1620 1621#----------------------------------------------------------------------------- 1622# Code 1623#----------------------------------------------------------------------------- 1624 1625Tool.register_alias("pan", lambda: PanTool(dimensions="both")) 1626Tool.register_alias("xpan", lambda: PanTool(dimensions="width")) 1627Tool.register_alias("ypan", lambda: PanTool(dimensions="height")) 1628Tool.register_alias("xwheel_pan", lambda: WheelPanTool(dimension="width")) 1629Tool.register_alias("ywheel_pan", lambda: WheelPanTool(dimension="height")) 1630Tool.register_alias("wheel_zoom", lambda: WheelZoomTool(dimensions="both")) 1631Tool.register_alias("xwheel_zoom", lambda: WheelZoomTool(dimensions="width")) 1632Tool.register_alias("ywheel_zoom", lambda: WheelZoomTool(dimensions="height")) 1633Tool.register_alias("zoom_in", lambda: ZoomInTool(dimensions="both")) 1634Tool.register_alias("xzoom_in", lambda: ZoomInTool(dimensions="width")) 1635Tool.register_alias("yzoom_in", lambda: ZoomInTool(dimensions="height")) 1636Tool.register_alias("zoom_out", lambda: ZoomOutTool(dimensions="both")) 1637Tool.register_alias("xzoom_out", lambda: ZoomOutTool(dimensions="width")) 1638Tool.register_alias("yzoom_out", lambda: ZoomOutTool(dimensions="height")) 1639Tool.register_alias("click", lambda: TapTool(behavior="inspect")) 1640Tool.register_alias("tap", lambda: TapTool()) 1641Tool.register_alias("doubletap", lambda: TapTool(gesture="doubletap")) 1642Tool.register_alias("crosshair", lambda: CrosshairTool()) 1643Tool.register_alias("box_select", lambda: BoxSelectTool()) 1644Tool.register_alias("xbox_select", lambda: BoxSelectTool(dimensions="width")) 1645Tool.register_alias("ybox_select", lambda: BoxSelectTool(dimensions="height")) 1646Tool.register_alias("poly_select", lambda: PolySelectTool()) 1647Tool.register_alias("lasso_select", lambda: LassoSelectTool()) 1648Tool.register_alias("box_zoom", lambda: BoxZoomTool(dimensions="both")) 1649Tool.register_alias("xbox_zoom", lambda: BoxZoomTool(dimensions="width")) 1650Tool.register_alias("ybox_zoom", lambda: BoxZoomTool(dimensions="height")) 1651Tool.register_alias("save", lambda: SaveTool()) 1652Tool.register_alias("undo", lambda: UndoTool()) 1653Tool.register_alias("redo", lambda: RedoTool()) 1654Tool.register_alias("reset", lambda: ResetTool()) 1655Tool.register_alias("help", lambda: HelpTool()) 1656Tool.register_alias("box_edit", lambda: BoxEditTool()) 1657Tool.register_alias("line_edit", lambda: LineEditTool()) 1658Tool.register_alias("point_draw", lambda: PointDrawTool()) 1659Tool.register_alias("poly_draw", lambda: PolyDrawTool()) 1660Tool.register_alias("poly_edit", lambda: PolyEditTool()) 1661Tool.register_alias("freehand_draw", lambda: FreehandDrawTool()) 1662Tool.register_alias("hover", lambda: HoverTool(tooltips=[ 1663 ("index", "$index"), 1664 ("data (x, y)", "($x, $y)"), 1665 ("screen (x, y)", "($sx, $sy)"), 1666])) 1667