Thanks to visit codestin.com
Credit goes to github.com

Skip to content

More interaction #106

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

Merged
merged 3 commits into from
Jan 8, 2023
Merged
Show file tree
Hide file tree
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
85 changes: 71 additions & 14 deletions fastplotlib/graphics/_base.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from typing import *
from warnings import warn

import numpy as np

from .features._base import cleanup_slice

Expand Down Expand Up @@ -92,10 +95,19 @@ def _set_feature(self, feature: str, new_data: Any, indices: Any):
def _reset_feature(self, feature: str):
pass

def link(self, event_type: str, target: Any, feature: str, new_data: Any, callback_function: callable = None):
def link(
self,
event_type: str,
target: Any,
feature: str,
new_data: Any,
callback: callable = None,
bidirectional: bool = False
):
if event_type in self.pygfx_events:
self.world_object.add_event_handler(self.event_handler, event_type)

# make sure event is valid
elif event_type in self.feature_events:
if isinstance(self, GraphicCollection):
feature_instance = getattr(self[:], event_type)
Expand All @@ -105,15 +117,35 @@ def link(self, event_type: str, target: Any, feature: str, new_data: Any, callba
feature_instance.add_event_handler(self.event_handler)

else:
raise ValueError("event not possible")
raise ValueError(f"Invalid event, valid events are: {self.pygfx_events + self.feature_events}")

if event_type in self.registered_callbacks.keys():
self.registered_callbacks[event_type].append(
CallbackData(target=target, feature=feature, new_data=new_data, callback_function=callback_function))
else:
# make sure target feature is valid
if feature is not None:
if feature not in target.feature_events:
raise ValueError(f"Invalid feature for target, valid features are: {target.feature_events}")

if event_type not in self.registered_callbacks.keys():
self.registered_callbacks[event_type] = list()
self.registered_callbacks[event_type].append(
CallbackData(target=target, feature=feature, new_data=new_data, callback_function=callback_function))

callback_data = CallbackData(target=target, feature=feature, new_data=new_data, callback_function=callback)

for existing_callback_data in self.registered_callbacks[event_type]:
if existing_callback_data == callback_data:
warn("linkage already exists for given event, target, and data, skipping")
return

self.registered_callbacks[event_type].append(callback_data)

if bidirectional:
target.link(
event_type=event_type,
target=self,
feature=feature,
new_data=new_data,
callback=callback,
bidirectional=False # else infinite recursion, otherwise target will call
# this instance .link(), and then it will happen again etc.
)

def event_handler(self, event):
if event.type in self.registered_callbacks.keys():
Expand Down Expand Up @@ -145,6 +177,28 @@ class CallbackData:
new_data: Any
callback_function: callable = None

def __eq__(self, other):
if not isinstance(other, CallbackData):
raise TypeError("Can only compare against other <CallbackData> types")

if other.target is not self.target:
return False

if not other.feature == self.feature:
return False

if not other.new_data == self.new_data:
return False

if (self.callback_function is None) and (other.callback_function is None):
return True

if other.callback_function is self.callback_function:
return True

else:
return False


@dataclass
class PreviouslyModifiedData:
Expand All @@ -156,10 +210,6 @@ class PreviouslyModifiedData:
class GraphicCollection(Graphic):
"""Graphic Collection base class"""

pygfx_events = [
"click"
]

def __init__(self, name: str = None):
super(GraphicCollection, self).__init__(name)
self._items: List[Graphic] = list()
Expand Down Expand Up @@ -207,14 +257,21 @@ def __getitem__(self, key):
selection = self._items[key]

# fancy-ish indexing
elif isinstance(key, (tuple, list)):
elif isinstance(key, (tuple, list, np.ndarray)):
if isinstance(key, np.ndarray):
if not key.ndim == 1:
raise TypeError(f"{self.__class__.__name__} indexing supports "
f"1D numpy arrays, int, slice, tuple or list of integers, "
f"your numpy arrays has <{key.ndim}> dimensions.")
selection = list()

for ix in key:
selection.append(self._items[ix])

selection_indices = key
else:
raise TypeError(f"Graphic Collection indexing supports int, slice, tuple or list of integers, "
raise TypeError(f"{self.__class__.__name__} indexing supports "
f"1D numpy arrays, int, slice, tuple or list of integers, "
f"you have passed a <{type(key)}>")

return CollectionIndexer(
Expand Down
9 changes: 7 additions & 2 deletions fastplotlib/graphics/features/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,13 @@ def _call_event_handlers(self, event_data: FeatureEvent):

for func in self._event_handlers:
try:
if len(getfullargspec(func).args) > 0:
func(event_data)
args = getfullargspec(func).args

if len(args) > 0:
if args[0] == "self" and not len(args) > 1:
func()
else:
func(event_data)
else:
func()
except:
Expand Down
3 changes: 2 additions & 1 deletion fastplotlib/graphics/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
class ImageGraphic(Graphic, Interaction):
feature_events = [
"data",
"colors",
"cmap",
]

def __init__(
self,
data: Any,
Expand Down
3 changes: 3 additions & 0 deletions fastplotlib/graphics/line.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ class LineGraphic(Graphic, Interaction):
feature_events = [
"data",
"colors",
"cmap",
"thickness",
"present"
]

def __init__(
Expand Down
16 changes: 15 additions & 1 deletion fastplotlib/graphics/line_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ class LineCollection(GraphicCollection, Interaction):
feature_events = [
"data",
"colors",
"cmap",
"thickness",
"present"
]

def __init__(
Expand Down Expand Up @@ -122,6 +125,13 @@ def _set_feature(self, feature: str, new_data: Any, indices: Any):
if not hasattr(self, "_previous_data"):
self._previous_data = dict()
elif hasattr(self, "_previous_data"):
if feature in self._previous_data.keys():
# for now assume same index won't be changed with diff data
# I can't think of a usecase where we'd have to check the data too
# so unless there is bug we keep it like this
if self._previous_data[feature].indices == indices:
return # nothing to change, and this allows bidirectional linking without infinite recusion

self._reset_feature(feature)

coll_feature = getattr(self[indices], feature)
Expand All @@ -132,14 +142,18 @@ def _set_feature(self, feature: str, new_data: Any, indices: Any):

# later we can think about multi-index events
previous = deepcopy(data[0])
coll_feature._set(new_data)

if feature in self._previous_data.keys():
self._previous_data[feature].data = previous
self._previous_data[feature].indices = indices
else:
self._previous_data[feature] = PreviouslyModifiedData(data=previous, indices=indices)

# finally set the new data
# this MUST occur after setting the previous data attribute to prevent recursion
# since calling `feature._set()` triggers all the feature callbacks
coll_feature._set(new_data)

def _reset_feature(self, feature: str):
if feature not in self._previous_data.keys():
return
Expand Down