3

I want to add an event to a pie chart what, upon hovering, shows an annotation with the value and label of what wedge. After a while of trying, I got a working example for line plot, but for pie charts I can't understand how to obtain the data of each wedge. My code so far:

from matplotlib.figure import Figure

figure = Figure()
drawFigure = figure.subplots()
annotate = drawFigure.annotate("", xy=(0,0),xytext=(-20, 20), textcoords="offset points",
                                         bbox=dict(boxstyle="round", fc="black", ec="b", lw=2),
                                         arrowprops=dict(arrowstyle="->"))
annotate.set_visible(False)
days, amounts = zip(*my_dict.items())
wedges, _ = drawFigure.pie(amounts, labels=None)
drawFigure.axis('equal')
figure.canvas.mpl_connect("motion_notify_event", Hover)

def UpdateAnnotation(ind):
    data = ind.get_patch_transform() # I'm not sure this does anything useful
    # what now? can't seem to get anything working

def Hover(event):
    vis = annotate.get_visible()
    if event.inaxes == drawFigure:
        for i, w in enumerate(wedges):
            if w.contains_point([event.x, event.y]):
                print("hovering")
        figure.canvas.draw_idle()

Ideally, I'd like that when I hover over a wedge, an annotation pops up with the key : value pair corresponding to that wedge. Also, exploding that wedge would be nice.

JohanC
  • 71,591
  • 8
  • 33
  • 66
Andrei
  • 59
  • 5
  • Maybe you can adapt [matplotlib mouseclick event in pie chart](https://stackoverflow.com/questions/12604087/matplotlib-mouseclick-event-in-pie-chart)? – JohanC Oct 07 '20 at 16:44
  • @JohanC If I were to use that method, it would mean displaying the labels. I want a pie chart with no visible labels, but when I hover over a wedge the label and value should appear. Can I somehow hide the labels without using `labels=None` ? – Andrei Oct 07 '20 at 17:34

1 Answers1

2

You can assign a label to each wedge, which can be retrieved via .get_label(). The label text can be set to invisible with textprops={'visible': False}.

To "explode" a wedge, you can move its center, using the mean angle for the direction.

To make the annotation text distinguishable on the black background, a light text color can be chosen.

import numpy as np
import matplotlib.pyplot as plt

def hover_func(event):
    global selected_wedge
    if selected_wedge is not None:
        selected_wedge.set_center((0, 0))
        selected_wedge = None
    if event.inaxes == ax:
        for w in wedges:
            if w.contains_point([event.x, event.y]):
                annotation.set_text(f'{w.get_label()}: {months[w.get_label()]}')
                annotation.xy = (event.xdata, event.ydata)
                annotation.set_visible(True)
                theta = np.radians((w.theta1 + w.theta2) / 2)
                w.set_center((.2 * np.cos(theta), .2 * np.sin(theta)))
                selected_wedge = w
                fig.canvas.draw_idle()
    if selected_wedge is None and annotation.get_visible():
        annotation.set_visible(False)
        fig.canvas.draw_idle()

selected_wedge = None
fig, ax = plt.subplots()
annotation = ax.annotate("", xy=(0, 0), xytext=(-20, 20), textcoords="offset points",
                         color='yellow',
                         bbox=dict(boxstyle="round", fc="black", ec="b", lw=2),
                         arrowprops=dict(arrowstyle="->"))
annotation.set_visible(False)
months = {'January': 31, 'February': 29, 'March': 31, 'April': 30, 'May': 31, 'June': 30,
          'July': 31, 'August': 31, 'September': 30, 'October': 31, 'November': 30, 'December': 31}
wedges, _ = ax.pie(months.values(), labels=months.keys(), textprops={'visible': False},
                   colors=plt.get_cmap('plasma', len(months)).colors)
fig.canvas.mpl_connect("motion_notify_event", hover_func)

plt.show()

example plot

JohanC
  • 71,591
  • 8
  • 33
  • 66
  • Thanks a lot! Weirdly, even though I set up `elif vis:` portion, last hovered wedge always stays exploded and with annotation still visible. I tried adding a print inside it and no matter what I do, I don't get a print statement. When should that trigger? – Andrei Oct 08 '20 at 07:06
  • 1
    The previous version only removed the annotation when you were outside the 'ax' object but inside the figure. The updated version now also hides the annotation when you are outside the wedges. – JohanC Oct 08 '20 at 07:20