Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract element-related functions into AnnotatorPlot #17

Merged
merged 1 commit into from
Aug 28, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
198 changes: 101 additions & 97 deletions holonote/annotate/annotator.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,9 @@ def __init__(self, init=True, **params):
if "annotation_table" not in params:
params["annotation_table"] = AnnotationTable()

super().__init__(**params)
connector_kws = {'fields':params.pop('fields')} if 'fields' in params else {}
connector = params.pop('connector') if 'connector' in params else self.connector_class(**connector_kws)
super().__init__(connector=connector, **params)
self._region = {}
self._last_region = None

Expand Down Expand Up @@ -315,45 +317,19 @@ def commit(self, return_commits=False):
return commits


class Annotator(AnnotatorInterface):
"""
An annotator displays the contents of an AnnotationTable and
provides the means to manipulate view the corresponding contents,
add new annotations and update existing annotations.
"""

class AnnotatorPlot(AnnotatorInterface):
rect_min = param.Number(default=-1000, doc="Temporary parameter until vectorized element fully supported")

rect_max = param.Number(default=1050, doc="Temporary parameter until vectorized element fully supported")

_count = param.Integer(default=None, precedence=-1)

indicator = Indicator

def __init__(self, spec, **params):
"""
The spec argument must be an element or a dictionary of kdim dtypes
"""
params['_count'] = 0
self.element = None
if not isinstance(spec, dict):
self.element = spec.clone() # Clone so we can add a tap tool and trust our copy.

connector_kws = {'fields':params.pop('fields')} if 'fields' in params else {}
connector = params.pop('connector') if 'connector' in params else self.connector_class(**connector_kws)
super().__init__(connector = connector,
**dict(kdim_dtypes=(spec if self.element is None
else self._infer_kdim_dtypes(spec)), **params))

self._selection_info = {}

self._annotation_count_stream = hv.streams.Params(parameterized=self,
parameters=['_count'], transient=True)
self._selected_values = []
self._selected_options = []

def __init__(self, **kwargs):
super().__init__(**kwargs)
self._selection_enabled = True
self._editable_enabled = True
self._selected_values = []
self._selected_options = []

transient=False
self._edit_streams = [
Expand Down Expand Up @@ -384,41 +360,6 @@ def _infer_kdim_dtypes(cls, element):
kdim_dtypes[str(kdim)] = type(element.dimension_values(kdim)[0])
return kdim_dtypes

def refresh(self, clear=False):
hv.streams.Stream.trigger([self._annotation_count_stream])
if clear:
self.clear_indicated_region()

def set_annotation_table(self, annotation_table):
self.select_by_index()
self._set_region(None)
super().set_annotation_table(annotation_table)
self.refresh(clear=True)

@property
def selection_enabled(self):
return self._selection_enabled

@selection_enabled.setter
def selection_enabled(self, enabled):
self._selection_enabled = enabled
if not enabled:
self.select_by_index()

@property
def editable_enabled(self):
return self._editable_enabled

@editable_enabled.setter
def editable_enabled(self, enabled):
self._editable_enabled = enabled
if not enabled:
self.clear_indicated_region()

def enable_editable(self, enabled=True):
print('enable_editable method deprecated - set editable_enabled property directly')
self.editable_enabled = enabled

def clear_indicated_region(self):
"Clear any region currently indicated on the plot by the editor"
self._edit_streams[0].event(bounds=None)
Expand All @@ -443,33 +384,26 @@ def selection_element(self):
else:
return self.element

@property
def selection_enabled(self):
return self._selection_enabled

def selected_dim_expr(self, selected_value, non_selected_value):
self._selected_values.append(selected_value)
self._selected_options.append({i:selected_value for i in self.selected_indices})
index_name = ('id' if (self.annotation_table._field_df.index.name is None)
else self.annotation_table._field_df.index.name)
return hv.dim(index_name).categorize(
self._selected_options[-1], default=non_selected_value)

def clear_edits(self):
super().clear_edits()
self.clear_indicated_region()
@selection_enabled.setter
def selection_enabled(self, enabled):
self._selection_enabled = enabled
if not enabled:
self.select_by_index()

@property
def dim_expr(self):
return self._selection_info["dim_expr"]
def editable_enabled(self):
return self._editable_enabled

@editable_enabled.setter
def editable_enabled(self, enabled):
self._editable_enabled = enabled
if not enabled:
self.clear_indicated_region()

def show_region(self):
if self.region != {}:
if len(self.region['value']) == 2:
bounds = (self.region['value'][0], 0,
self.region['value'][1], 1)
else:
bounds = (self.region['value'][0], self.region['value'][2],
self.region['value'][1], self.region['value'][3])
self._edit_streams[0].event(bounds = bounds)

def _filter_stream_values(self, bounds, x, y, geometry):
if not self._editable_enabled:
Expand All @@ -485,6 +419,7 @@ def _filter_stream_values(self, bounds, x, y, geometry):

return bounds, x, y, geometry


def _make_selection_dmap(self):
def inner(bounds, x, y, geometry):
bounds, x, y, geometry = self._filter_stream_values(bounds, x, y, geometry)
Expand Down Expand Up @@ -655,6 +590,83 @@ def static_indicators(self):
raise NotImplementedError # FIXME: Both in overlay


def set_range(self, startx, endx, starty=None, endy=None):
super().set_range(startx, endx, starty, endy)
self.show_region()

def set_point(self, posx, posy=None):
super().set_point(posx, posy)
self.show_region()


class Annotator(AnnotatorPlot):
"""
An annotator displays the contents of an AnnotationTable and
provides the means to manipulate view the corresponding contents,
add new annotations and update existing annotations.
"""


_count = param.Integer(default=0, precedence=-1)


def __init__(self, spec, **params):
"""
The spec argument must be an element or a dictionary of kdim dtypes
"""
self.element = None
if not isinstance(spec, dict):
self.element = spec.clone() # Clone so we can add a tap tool and trust our copy.

super().__init__(
kdim_dtypes=spec if self.element is None else self._infer_kdim_dtypes(spec),
**params,
)
self._selection_info = {}

self._annotation_count_stream = hv.streams.Params(
parameterized=self,
parameters=['_count'],
transient=True,
)

def refresh(self, clear=False) -> None:
hv.streams.Stream.trigger([self._annotation_count_stream])
if clear:
self.clear_indicated_region()

def set_annotation_table(self, annotation_table):
self.select_by_index()
self._set_region(None)
super().set_annotation_table(annotation_table)
self.refresh(clear=True)

def selected_dim_expr(self, selected_value, non_selected_value):
self._selected_values.append(selected_value)
self._selected_options.append({i:selected_value for i in self.selected_indices})
index_name = ('id' if (self.annotation_table._field_df.index.name is None)
else self.annotation_table._field_df.index.name)
return hv.dim(index_name).categorize(
self._selected_options[-1], default=non_selected_value)

def clear_edits(self):
super().clear_edits()
self.clear_indicated_region()

@property
def dim_expr(self):
return self._selection_info["dim_expr"]

def show_region(self):
if self.region != {}:
if len(self.region['value']) == 2:
bounds = (self.region['value'][0], 0,
self.region['value'][1], 1)
else:
bounds = (self.region['value'][0], self.region['value'][2],
self.region['value'][1], self.region['value'][3])
self._edit_streams[0].event(bounds = bounds)

# Methods overriding the baseclass to add extra interactivity

def select_by_index(self, *inds):
Expand Down Expand Up @@ -721,11 +733,3 @@ def has_snapshot(self):
def revert_to_snapshot(self):
super().revert_to_snapshot()
self.refresh()

def set_range(self, startx, endx, starty=None, endy=None):
super().set_range(startx, endx, starty, endy)
self.show_region()

def set_point(self, posx, posy=None):
super().set_point(posx, posy)
self.show_region()