Luk
Luk

Reputation: 1159

How to connect an event to a matplotlib.collections.PatchCollection

Using matplotlib.collections.PatchCollection, I have created a pyplot figure that shows a grid of hexagons: hexagons

Now, I want to make the plot interactive, i.e. add some form of event handling. In particular, I want to be able to hover my cursor over any of the hexagons and, when I do so, I want to fill all of the neighbouring hexagons with a certain color.

But, let's start easy: How can I connect a patch, meaning a hexagon, to an event?

I have stored the coordinates of the center points of each hexagon in a numpy-array. So, what I need is a way to tell me the index of the hexagon that I have clicked on, or my cursor is currently over. There are 100 hexagons alltogether. When I click on hexagon number 43, I simply need to get this index and, then, I think I know how to get my distance to all the neighbours. But how do I get this index?

Does anyone know?

Upvotes: 0

Views: 531

Answers (1)

Diziet Asahi
Diziet Asahi

Reputation: 40737

PatchCollection, like all collections, has a property contains() that can tell you over which member of the collection (if any) the event was fired.

The only "trick" is that you have to make sure you initially have an array of facecolors that is the same size as the number of members in your collection, otherwise things will get messy. Here I use PathCollection.set_facecolors() after creation to make sure of that.

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Circle
from matplotlib.collections import PatchCollection


def hover(event):
    if event.inaxes == ax:
        cont, ind = p.contains(event)
        if cont:
            idx = ind['ind'][0]
            colors = p.get_facecolors()
            colors[idx] = highlight_color
            p.set_facecolors(colors)
        else:
            p.set_facecolors([default_color] * N)
    fig.canvas.draw()


default_color = (0, 0, 1, 1)
highlight_color = (1, 0, 0, 1)

N = 10
r = 1
x, y = np.random.randint(10, 50, size=(2, N))
patches = [Circle((xi, yi), r) for xi, yi in zip(x, y)]
p = PatchCollection(patches)
p.set_facecolors([default_color] * N)

fig, ax = plt.subplots()
ax.add_collection(p)
ax.set_xlim(0, 50)
ax.set_ylim(0, 50)

fig.canvas.mpl_connect("motion_notify_event", hover)

plt.show()

Upvotes: 1

Related Questions