Source code for holoviews.plotting.links
import weakref
from collections import defaultdict
import param
from ..core.util import dimension_sanitizer
[docs]class Link(param.Parameterized):
"""
A Link defines some connection between a source and target object
in their visualization. It is quite similar to a Stream as it
allows defining callbacks in response to some change or event on
the source object, however, unlike a Stream, it does not transfer
data and make it available to user defined subscribers. Instead
a Link directly causes some action to occur on the target, for JS
based backends this usually means that a corresponding JS callback
will effect some change on the target in response to a change on
the source.
A Link must define a source object which is what triggers events,
but must not define a target. It is also possible to define bi-
directional links between the source and target object.
"""
# Mapping from a source id to a Link instance
registry = weakref.WeakKeyDictionary()
# Mapping to define callbacks by backend and Link type.
# e.g. Link._callbacks['bokeh'][Stream] = Callback
_callbacks = defaultdict(dict)
# Whether the link requires a target
_requires_target = False
def __init__(self, source, target=None, **params):
if source is None:
raise ValueError(f'{type(self).__name__} must define a source')
if self._requires_target and target is None:
raise ValueError(f'{type(self).__name__} must define a target.')
# Source is stored as a weakref to allow it to be garbage collected
self._source = None if source is None else weakref.ref(source)
self._target = None if target is None else weakref.ref(target)
super().__init__(**params)
self.link()
[docs] @classmethod
def register_callback(cls, backend, callback):
"""
Register a LinkCallback providing the implementation for
the Link for a particular backend.
"""
cls._callbacks[backend][cls] = callback
@property
def source(self):
return self._source() if self._source else None
@property
def target(self):
return self._target() if self._target else None
[docs] def link(self):
"""
Registers the Link
"""
if self.source in self.registry:
links = self.registry[self.source]
params = {
k: v for k, v in self.param.values().items() if k != 'name'}
for link in links:
link_params = {
k: v for k, v in link.param.values().items() if k != 'name'}
if (type(link) is type(self) and link.source is self.source
and link.target is self.target and params == link_params):
return
self.registry[self.source].append(self)
else:
self.registry[self.source] = [self]
[docs] def unlink(self):
"""
Unregisters the Link
"""
links = self.registry.get(self.source)
if self in links:
links.pop(links.index(self))
[docs]class DataLink(Link):
"""
DataLink defines a link in the data between two objects allowing
them to be selected together. In order for a DataLink to be
established the source and target data must be of the same length.
"""
_requires_target = True
[docs]class SelectionLink(Link):
"""
Links the selection between two glyph renderers.
"""
_requires_target = True
[docs]class VertexTableLink(Link):
"""
Defines a Link between a Path type and a Table that will
display the vertices of selected path.
"""
vertex_columns = param.List(default=[])
_requires_target = True
def __init__(self, source, target, **params):
if 'vertex_columns' not in params:
dimensions = [dimension_sanitizer(d.name) for d in target.dimensions()[:2]]
params['vertex_columns'] = dimensions
super().__init__(source, target, **params)
[docs]class RectanglesTableLink(Link):
"""
Links a Rectangles element to a Table.
"""
_requires_target = True