How to display an annotation and exploding a wedge when hovering over pie chart?

Viewed 575

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.

1 Answers

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

Related