Highlight point in scatterplot when hovering over image

130 views Asked by At

I'm trying to link a grid of images to a scatterplot, so that the right scatterplot point is highlighted when hovering over the corresponding image.

Problem is that when registering DOM events iteratively, they seem to get overwritten, and as a result only the last point of the scatterplot gets highlighted. How can I register independent events for each image.

See reproducible examples below.

enter image description here

import io
import random

import ipywidgets as ipw
from ipyevents import Event
import numpy as np
import PIL
from PIL import Image as PILImage
from bqplot import LinearScale, Figure, Axis, Scatter

# Random ipywidget image generator
def random_image():
    arr = np.random.randint(255, size=(200, 200, 3), dtype=np.uint8)
    img = PILImage.fromarray(arr)
    with io.BytesIO() as fileobj:
        img.save(fileobj, 'PNG')
        img_b = fileobj.getvalue()
    img_w = ipw.Image(value=img_b)
    return img_w

# Create data
data = [{'x': idx, 'y': random.randint(1,10), 'image': random_image()}
         for idx in range(1,6)]

# Create scatter plot
xs = LinearScale()
ys = LinearScale()
xa = Axis(scale=xs)                                                
ya = Axis(scale=ys, orientation='vertical')                        
points = Scatter(x=[d['x'] for d in data],                                    
                 y=[d['y'] for d in data],                                   
                 scales={'x': xs, 'y': ys})
highlighted_point = Scatter(x=[-1000], y=[-1000], # Dummy point out of view
                            scales={'x': xs, 'y': ys},
                            preserve_domain={'x': True, 'y': True},
                            colors=['red'])
fig = Figure(marks=[points, highlighted_point], axes=[xa,ya])

# Add DOM events to images
img_list = []
for d in data:
    img = d['image']
    event = Event(source=img, watched_events=['mouseenter'])
    def handle_event(event):
        highlighted_point.x = [d['x']]
        highlighted_point.y = [d['y']]
    event.on_dom_event(handle_event)
    img_list.append(img)
images = ipw.HBox(img_list)
ipw.VBox([fig, images])
1

There are 1 answers

0
Loïc Dutrieux On

I found a way to do it using functools.partial

import functools

# Event handler
def handle_event(idx, event):
    highlighted_point.x = [data[idx]['x']]
    highlighted_point.y = [data[idx]['y']]

# Add DOM events to images
img_list = []
for idx, d in enumerate(data):
    img = d['image']
    event = Event(source=img, watched_events=['mouseenter'])
    event.on_dom_event(functools.partial(handle_event, idx))
    img_list.append(img)
images = ipw.HBox(img_list)
ipw.VBox([fig, images])