Skip to content

Commit

Permalink
Merge pull request #213 from astrofrog/always-on-mouse-interaction
Browse files Browse the repository at this point in the history
Start implementing 'always-on' MouseInteraction
  • Loading branch information
astrofrog authored Apr 6, 2021
2 parents ce94a93 + bee7c4d commit 492c93e
Show file tree
Hide file tree
Showing 19 changed files with 75 additions and 110 deletions.
3 changes: 3 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
* Fixed implementation of ``JupyterApplication.viewers`` to now return
list of viewers as opposed to empty list. [#214]

* Add the ability to register callback functions for mouse and keyboard
events with the bqplot viewers. [#213]

0.2.2 (2021-03-18)
==================

Expand Down
2 changes: 0 additions & 2 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ General

.. automodapi:: glue_jupyter.common.state_widgets

.. automodapi:: glue_jupyter.compat

bqplot viewers
--------------

Expand Down
5 changes: 3 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# html_static_path = ['_static']

# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
Expand Down Expand Up @@ -196,9 +196,10 @@
('py:class', 'ipywidgets.widgets.widget.LoggingHasTraits'),
('py:class', 'ipywidgets.widgets.domwidget.DOMWidget'),
('py:class', 'ipywidgets.widgets.widget_core.CoreWidget'),
('py:class', 'ipyvuetify.VuetifyTemplate.VuetifyTemplate'),
('py:class', 'traitlets.traitlets.HasTraits'),
('py:class', 'traitlets.traitlets.HasDescriptors'),
('py:class', 'glue.external.echo.core.HasCallbackProperties'),
('py:class', 'echo.core.HasCallbackProperties'),
('py:class', 'glue.viewers.image.layer_artist.ImageLayerArtist'),
('py:class', 'glue.viewers.image.layer_artist.BaseImageLayerArtist'),
('py:class', 'glue_vispy_viewers.volume.layer_state.VolumeLayerState'),
Expand Down
4 changes: 2 additions & 2 deletions glue_jupyter/bqplot/common/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ def __init__(self, viewer):
self.viewer = viewer

def activate(self):
self.viewer.figure.interaction = self.interact
self.viewer._mouse_interact.next = self.interact

def deactivate(self):
self.viewer.figure.interaction = None
self.viewer._mouse_interact.next = None


@viewer_tool
Expand Down
47 changes: 47 additions & 0 deletions glue_jupyter/bqplot/common/viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
from glue.core.subset import roi_to_subset_state
from glue.core.command import ApplySubsetState

from bqplot_image_gl.interacts import MouseInteraction, keyboard_events, mouse_events

from echo.callback_container import CallbackContainer

from ...view import IPyWidgetView
from ...link import dlink, on_change
from ...utils import float_or_none, debounced, get_ioloop
Expand Down Expand Up @@ -39,6 +43,18 @@ def __init__(self, session, state=None):
self._fig_margin_zero['left'] = 0
self._fig_margin_zero['bottom'] = 0

# Set up a MouseInteraction instance here tied to the figure. In the
# tools we then chain this with any other active interact so that we can
# always listen for certain events. This allows us to then have e.g.
# mouse-over coordinates regardless of whether tools are active or not.
self._event_callbacks = CallbackContainer()
self._mouse_interact = MouseInteraction(x_scale=self.scale_x,
y_scale=self.scale_y,
move_throttle=70,
events=keyboard_events + mouse_events)
self._mouse_interact.on_msg(self._on_mouse_interaction)
self.figure.interaction = self._mouse_interact

super(BqplotBaseView, self).__init__(session, state=state)

# Remove the following two lines once glue v0.16 is required - see
Expand Down Expand Up @@ -81,6 +97,37 @@ def update_axes(*ignore):

self.create_layout()

def add_event_callback(self, callback):
"""
Add a callback function for mouse and keyboard events when the mouse is over the figure.
Functions should take a single argument which is a dictionary containing the event
details. One of the keys of the dictionary is ``event`` which is a string that can
be one of the following:
* 'click'
* 'dblclick'
* 'mouseenter'
* 'mouseleave'
* 'contextmenu'
* 'mousemove'
* 'keydown'
* 'keyup'
The rest of the dictionary depends on the specific event triggered.
"""
self._event_callbacks.append(callback)

def remove_event_callback(self, callback):
"""
Remove a callback function for mouse and keyboard events.
"""
self._event_callbacks.remove(callback)

def _on_mouse_interaction(self, interaction, data, buffers):
for callback in self._event_callbacks:
callback(data)

@debounced(delay_seconds=0.5, method=True)
def update_glue_scales(self, *ignored):
# To prevent glue from calling _adjust_limit_aspect() as each value comes in, we wait for
Expand Down
2 changes: 1 addition & 1 deletion glue_jupyter/bqplot/histogram/layer_artist.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from glue.core.exceptions import IncompatibleAttribute
from glue.viewers.histogram.state import HistogramLayerState
from glue_jupyter.compat import LayerArtist
from glue.viewers.common.layer_artist import LayerArtist
from glue.utils import color2hex

from ...link import link, dlink
Expand Down
2 changes: 1 addition & 1 deletion glue_jupyter/bqplot/profile/layer_artist.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import bqplot

from glue_jupyter.compat import LayerArtist
from glue.viewers.common.layer_artist import LayerArtist

from ...link import dlink

Expand Down
4 changes: 2 additions & 2 deletions glue_jupyter/bqplot/scatter/layer_artist.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
from glue.core.data import Subset
from glue.viewers.scatter.state import ScatterLayerState
from glue.core.exceptions import IncompatibleAttribute
from glue_jupyter.compat import LayerArtist
from glue.viewers.common.layer_artist import LayerArtist

from ...link import dlink, on_change
from ...utils import colormap_to_hexlist, debounced, float_or_none
from glue.external.echo import CallbackProperty
from echo import CallbackProperty
from glue.utils import ensure_numerical, color2hex

__all__ = ['BqplotScatterLayerState', 'BqplotScatterLayerArtist']
Expand Down
5 changes: 4 additions & 1 deletion glue_jupyter/bqplot/tests/test_bqplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ def test_interact(app, dataxyz):
# s.widget_menu_select_x.click()# = True
tool = s.toolbar.tools['bqplot:xrange']
s.toolbar.active_tool = tool
assert s.figure.interaction == tool.interact
# Note that we need to access 'next' because the default interact
# is a MouseInteraction and tools are activated/deactivated via the
# 'next' property.
assert s.figure.interaction.next == tool.interact


def test_scatter2d(app, dataxyz, dataxz):
Expand Down
2 changes: 1 addition & 1 deletion glue_jupyter/common/state3d.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from glue.viewers.common.state import ViewerState
from glue.external.echo import (CallbackProperty, SelectionCallbackProperty)
from echo import (CallbackProperty, SelectionCallbackProperty)
from glue.core.data_combo_helper import ComponentIDComboHelper
from glue.core.state_objects import StateAttributeLimitsHelper
from glue.utils import nonpartial
Expand Down
87 changes: 0 additions & 87 deletions glue_jupyter/compat.py

This file was deleted.

4 changes: 2 additions & 2 deletions glue_jupyter/ipyvolume/scatter/layer_artist.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
import ipyvolume

from glue.core.data import Subset
from glue_jupyter.compat import LayerArtist
from glue.viewers.common.layer_artist import LayerArtist
from glue.core.data_combo_helper import ComponentIDComboHelper
from glue.core.exceptions import IncompatibleAttribute
from glue.viewers.scatter.state import ScatterLayerState
from glue.external.echo import CallbackProperty, SelectionCallbackProperty
from echo import CallbackProperty, SelectionCallbackProperty
from glue.utils import ensure_numerical, color2hex

from ...link import link, on_change
Expand Down
2 changes: 1 addition & 1 deletion glue_jupyter/ipyvolume/volume/layer_artist.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import matplotlib.colors

from glue_vispy_viewers.volume.layer_state import VolumeLayerState
from glue.external.echo import CallbackProperty
from echo import CallbackProperty
from glue.core.data import Subset
from glue.core.exceptions import IncompatibleAttribute
from glue_vispy_viewers.common.layer_artist import VispyLayerArtist
Expand Down
6 changes: 3 additions & 3 deletions glue_jupyter/link.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import ipywidgets as widgets
import glue.external.echo.selection
import echo.selection


def _is_traitlet(obj):
Expand Down Expand Up @@ -92,7 +92,7 @@ def link_component_id_to_select_widget(state, state_attr, widget, widget_attr='v

def update(*ignore):
options = [k for k in getattr(type(state), state_attr).get_choices(state)
if not isinstance(k, glue.external.echo.selection.ChoiceSeparator)]
if not isinstance(k, echo.selection.ChoiceSeparator)]
display_func = getattr(type(state), state_attr).get_display_func(state)
value = getattr(state, state_attr)
widget.options = [(display_func(options[k]), k) for k in range(len(options))]
Expand All @@ -106,7 +106,7 @@ def update(*ignore):

def update_state(change):
options = [k for k in getattr(type(state), state_attr).get_choices(state)
if not isinstance(k, glue.external.echo.selection.ChoiceSeparator)]
if not isinstance(k, echo.selection.ChoiceSeparator)]
if change.new is not None:
setattr(state, state_attr, options[change.new])

Expand Down
2 changes: 1 addition & 1 deletion glue_jupyter/state_traitlets_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from traitlets.utils.bunch import Bunch
from glue.core.state_objects import State
from glue.core import Data, Subset, ComponentID
from glue.external.echo import CallbackList, CallbackDict
from echo import CallbackList, CallbackDict
from matplotlib.colors import Colormap
from matplotlib.cm import get_cmap

Expand Down
2 changes: 1 addition & 1 deletion glue_jupyter/tests/test_state_traitlets_helpers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import traitlets
from glue.core import Data
from glue.core.state_objects import State, CallbackProperty
from glue.external.echo import ListCallbackProperty
from echo import ListCallbackProperty
from glue_jupyter.state_traitlets_helpers import GlueState


Expand Down
2 changes: 1 addition & 1 deletion glue_jupyter/widgets/linked_dropdown.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from ipywidgets import Dropdown
from glue.external.echo.selection import ChoiceSeparator
from echo.selection import ChoiceSeparator
from glue.utils import avoid_circular

__all__ = ['LinkedDropdown']
Expand Down
2 changes: 1 addition & 1 deletion glue_jupyter/widgets/tests/test_linked_dropdown.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from glue.core.state_objects import State
from glue.core.data_combo_helper import ComponentIDComboHelper
from glue.external.echo import SelectionCallbackProperty
from echo import SelectionCallbackProperty

from ..linked_dropdown import LinkedDropdown

Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ install_requires =
ipywidgets>=7.4.0
ipyvue>=1.2.0,<2
ipyvuetify>=1.2.0,<2
bqplot-image-gl>=1.0.0
bqplot-image-gl>=1.3.0
bqplot>=0.12.17
scikit-image

Expand Down

0 comments on commit 492c93e

Please sign in to comment.