I am trying to use legend picking (Link to legend picking dokumentation) on matplotlib's fill_between (Link to fill_between dokumentation).
Its works just fine when i use matplotlib.pyplot.plot but when i use it on matplotlib.pyplot.fill_between nothing happens, but i receive no errors to fix. I suspect i has something to do with the fill command not being lines and i use the get_lines() function, but i am having a hard time to actually understand what is happening.
The extra comma on output "the working line" using plt.plot is due to plot returns a tuple and fill_between return a value.
df = pd.DataFrame(...)
x_dist = df['distance']
df.pop('distance')
labels = []
lines = []
fig, ax = plt.subplots()
cols = df.columns
for i, col in enumerate(cols):
bot = df[cols[i]] # layer bot as bot
top = df[cols[i - 1]] # previous layer bot as top
# plot layer if the layer has different values from the layers above
if col[:4] != '0000': # discard terrain (first entry)
if not bot.equals(top):
pl = ax.fill_between(x_dist, top, bot, label=col) # non-working line
# pl, = ax.plot(x_dist, bot, label=col) # working line
lines.append(pl)
labels.append(pl.get_label())
# set grid on plot
ax.grid('both')
# set legend on plot and place it outside plot
box = ax.get_position()
ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])
leg = ax.legend(fancybox=True, shadow=True, loc='center left', bbox_to_anchor=(1, 0.5))
lined = {} # Will map legend lines to original lines.
for legline, origline in zip(leg.get_lines(), lines):
legline.set_picker(True) # Enable picking on the legend line.
lined[legline] = origline
def on_pick(event):
legline = event.artist
origline = lined[legline]
visible = not origline.get_visible()
origline.set_visible(visible)
legline.set_alpha(1.0 if visible else 0.2)
fig.canvas.draw()
fig.canvas.mpl_connect('pick_event', on_pick)
plt.show()
CodePudding user response:
Found a solution.
Instead of the leg.get_lines() is used leg.findobj where i am searching for the patches.Rectangles using the "match" parameter in the findobj function. remember to import the library "from matplotlib import patches".
For some reason when clicking the legend and disabling the plot and changing the opacity of the legend rectangle, the color of the rectangle changes to default blue (don't know why?!). To fix this i had to get the color from the fill_between plot and set the color of the legend rectangle within the on_pick function.
The solution to my code above:
df = pd.DataFrame(...)
x_dist = df['distance']
df.pop('distance')
labels = []
lines = []
fig, ax = plt.subplots()
cols = df.columns
for i, col in enumerate(cols):
bot = df[cols[i]] # layer bot as bot
top = df[cols[i - 1]] # previous layer bot as top
# plot layer if the layer has different values from the layers above
if col[:4] != '0000': # discard terrain (first entry)
if not bot.equals(top):
pl = ax.fill_between(x_dist, top, bot, label=col) # non-working line
# pl, = ax.plot(x_dist, bot, label=col) # working line
lines.append(pl)
labels.append(pl.get_label())
# set grid on plot
ax.grid('both')
# set legend on plot and place it outside plot
box = ax.get_position()
ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])
leg = ax.legend(fancybox=True, shadow=True, loc='center left', bbox_to_anchor=(1, 0.5))
lined = {} # Will map legend lines to original lines.
for legline, origline in zip(leg.findobj(patches.Rectangle), lines):
legline.set_picker(True) # Enable picking on the legend line.
lined[legline] = origline
def on_pick(event):
legline = event.artist
origline = lined[legline]
color_fill = origline.get_facecolor()
visible = not origline.get_visible()
origline.set_visible(visible)
legline.set_alpha(1.0 if visible else 0.2)
legline.set_color(color_fill)
fig.canvas.draw()
fig.canvas.mpl_connect('pick_event', on_pick)
plt.show()
A more generic answer:
from matplotlib import pyplot as plt, patches
import numpy as np
x = np.linspace(1, 10, 10)
y1 = np.linspace(1, 5, 10)
y2 = np.linspace(10, 20, 10)
fig, ax = plt.subplots()
pl = ax.fill_between(x, y1, y2, label='fill_plot')
leg = ax.legend()
fill_plots = [pl]
leg_to_fill = {}
for leg_obj, fill_plot in zip(leg.findobj(patches.Rectangle), fill_plots):
leg_obj.set_picker(True)
leg_to_fill[leg_obj] = fill_plot
def on_pick(event):
leg_obj = event.artist
fill_plot = leg_to_fill[leg_obj]
fill_plot_color = fill_plot.get_facecolor()
visible = not fill_plot.get_visible()
fill_plot.set_visible(visible)
leg_obj.set_alpha(1.0 if visible else 0.2)
leg_obj.set_color(fill_plot_color)
fig.canvas.draw()
fig.canvas.mpl_connect('pick_event', on_pick)
plt.show()