Repository URL to install this package:
|
Version:
2.4.3 ▾
|
#-----------------------------------------------------------------------------
# Copyright (c) 2012 - 2022, Anaconda, Inc., and Bokeh Contributors.
# All rights reserved.
#
# The full license is in the file LICENSE.txt, distributed with this software.
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
# Boilerplate
#-----------------------------------------------------------------------------
from __future__ import annotations
import logging # isort:skip
log = logging.getLogger(__name__)
#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------
# Standard library imports
import itertools
import re
import warnings
from dataclasses import dataclass
from typing import (
Any,
Callable,
Dict,
Iterator,
List,
Sequence,
Tuple,
Union,
cast,
)
# External imports
from typing_extensions import Literal
# Bokeh imports
from ..models import (
HoverTool,
Plot,
Tool,
Toolbar,
)
from ..models.tools import (
Drag,
GestureTool,
InspectTool,
Scroll,
Tap,
)
#-----------------------------------------------------------------------------
# Globals and constants
#-----------------------------------------------------------------------------
__all__ = (
'process_active_tools',
'process_tools_arg',
)
#-----------------------------------------------------------------------------
# General API
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
# Dev API
#-----------------------------------------------------------------------------
# TODO: str should be literal union of e.g. pan | xpan | ypan
Auto = Literal["auto"]
ActiveDrag = Union[Drag, Auto, str, None]
ActiveInspect = Union[List[InspectTool], InspectTool, Auto, str, None]
ActiveScroll = Union[Scroll, Auto, str, None]
ActiveTap = Union[Tap, Auto, str, None]
ActiveMulti = Union[GestureTool, Auto, str, None]
def process_active_tools(toolbar: Toolbar, tool_map: Dict[str, Tool],
active_drag: ActiveDrag, active_inspect: ActiveInspect, active_scroll: ActiveScroll,
active_tap: ActiveTap, active_multi: ActiveMulti) -> None:
""" Adds tools to the plot object
Args:
toolbar (Toolbar): instance of a Toolbar object
tools_map (dict[str]): tool_map from _process_tools_arg
active_drag (str, None, "auto" or Tool): the tool to set active for drag
active_inspect (str, None, "auto", Tool or Tool[]): the tool to set active for inspect
active_scroll (str, None, "auto" or Tool): the tool to set active for scroll
active_tap (str, None, "auto" or Tool): the tool to set active for tap
active_multi (str, None, "auto" or Tool): the tool to set active for tap
Returns:
None
Note:
This function sets properties on Toolbar
"""
if active_drag in ["auto", None] or isinstance(active_drag, Tool):
toolbar.active_drag = cast(Any, active_drag)
elif active_drag in tool_map:
toolbar.active_drag = cast(Any, tool_map[active_drag])
else:
raise ValueError(f"Got unknown {active_drag!r} for 'active_drag', which was not a string supplied in 'tools' argument")
if active_inspect in ["auto", None] or isinstance(active_inspect, Tool) or \
(isinstance(active_inspect, list) and all(isinstance(t, Tool) for t in active_inspect)):
toolbar.active_inspect = cast(Any, active_inspect)
elif isinstance(active_inspect, str) and active_inspect in tool_map:
toolbar.active_inspect = cast(Any, tool_map[active_inspect])
else:
raise ValueError(f"Got unknown {active_inspect!r} for 'active_inspect', which was not a string supplied in 'tools' argument")
if active_scroll in ["auto", None] or isinstance(active_scroll, Tool):
toolbar.active_scroll = cast(Any, active_scroll)
elif active_scroll in tool_map:
toolbar.active_scroll = cast(Any, tool_map[active_scroll])
else:
raise ValueError(f"Got unknown {active_scroll!r} for 'active_scroll', which was not a string supplied in 'tools' argument")
if active_tap in ["auto", None] or isinstance(active_tap, Tool):
toolbar.active_tap = cast(Any, active_tap)
elif active_tap in tool_map:
toolbar.active_tap = cast(Any, tool_map[active_tap])
else:
raise ValueError(f"Got unknown {active_tap!r} for 'active_tap', which was not a string supplied in 'tools' argument")
if active_multi in ["auto", None] or isinstance(active_multi, Tool):
toolbar.active_multi = cast(Any, active_multi)
elif active_multi in tool_map:
toolbar.active_multi = cast(Any, tool_map[active_multi])
else:
raise ValueError(f"Got unknown {active_multi!r} for 'active_multi', which was not a string supplied in 'tools' argument")
def process_tools_arg(plot: Plot, tools: str | Sequence[Tool | str],
tooltips: str | Tuple[str, str] | None = None) -> Tuple[List[Tool], Dict[str, Tool]]:
""" Adds tools to the plot object
Args:
plot (Plot): instance of a plot object
tools (seq[Tool or str]|str): list of tool types or string listing the
tool names. Those are converted using the to actual Tool instances.
tooltips (string or seq[tuple[str, str]], optional):
tooltips to use to configure a HoverTool
Returns:
list of Tools objects added to plot, map of supplied string names to tools
"""
tool_objs, tool_map = _resolve_tools(tools)
repeated_tools = [ str(obj) for obj in _collect_repeated_tools(tool_objs) ]
if repeated_tools:
warnings.warn(f"{','.join(repeated_tools)} are being repeated")
if tooltips is not None:
for tool_obj in tool_objs:
if isinstance(tool_obj, HoverTool):
tool_obj.tooltips = tooltips # type: ignore
break
else:
tool_objs.append(HoverTool(tooltips=tooltips))
return tool_objs, tool_map
#-----------------------------------------------------------------------------
# Private API
#-----------------------------------------------------------------------------
def _resolve_tools(tools: str | Sequence[Tool | str]) -> Tuple[List[Tool], Dict[str, Tool]]:
tool_objs: List[Tool] = []
tool_map: Dict[str, Tool] = {}
if not isinstance(tools, str):
temp_tool_str = ""
for tool in tools:
if isinstance(tool, Tool):
tool_objs.append(tool)
elif isinstance(tool, str):
temp_tool_str += tool + ','
else:
raise ValueError("tool should be a string or an instance of Tool class")
tools = temp_tool_str
for tool in re.split(r"\s*,\s*", tools.strip()):
# re.split will return empty strings; ignore them.
if tool == "":
continue
tool_obj = Tool.from_string(tool)
tool_objs.append(tool_obj)
tool_map[tool] = tool_obj
return tool_objs, tool_map
def _collect_repeated_tools(tool_objs: List[Tool]) -> Iterator[Tool]:
@dataclass
class Item:
obj: Tool
properties: Dict[str, Any]
key: Callable[[Tool], str] = lambda obj: obj.__class__.__name__
for _, group in itertools.groupby(sorted(tool_objs, key=key), key=key):
rest = [ Item(obj, obj.properties_with_values()) for obj in group ]
while len(rest) > 1:
head, *rest = rest
for item in rest:
if item.properties == head.properties:
yield item.obj
#-----------------------------------------------------------------------------
# Code
#-----------------------------------------------------------------------------