'Event Pick to update BarChart Attributes

New to the forum!

I’m trying to create an interactive barchart for a homework problem – I am wondering where I am going wrong with out using some one else's solution (like this awesome code here!)

I click on the chart to generate a reference line with a new y value and to change the color of the bar. For simplicity, I’m debugging using just two colors and comparing to the mean (when y >mean, y<mean). Aside from the two codes below, I've tried to clear the chart and re-draw it within the onclick function and to write a separate function, although not sure how to call it... Any guidance would be much appreciated - I'm not sure how the pieces fit together, so its hard to break it down for troubleshooting.

df=pd.DataFrame({'mean':[40000,50000,20000,60000,3000],'CI':[4000,4000,3000,1000,200]},index=['A','B','C','D','E'])
df=df.T

fig, ax = plt.subplots()
bars = ax.bar([1,2,3,4,5], df.loc['mean'])
    
#Set horizontal line
hline = ax.axhline(y=20000, c='red', linestyle='--')
ax.set_xticks([1,2,3,4,5])
ax.set_xticklabels(df.columns)

def onclick(event):
    hline.set_ydata([event.ydata, event.ydata])
    df2.loc['y']=event.ydata
    for val in df2.loc['y']:
        if df2.loc['y'] < df2.loc['mean']:
            col.append('red')
        else:
            col.append('white')
fig.canvas.mpl_connect('button_press_event', onclick)
    

Also tried

def onclick(event):
    #provide y data, based on where clicking.  Note to self:  'xdata' would give slanted line
    hline.set_ydata([event.ydata, event.ydata])
    df2.loc['y']=event.ydata
    for bar in bars:
        if event.ydata < df2.loc['mean']:
            bar.set_color('red')
        else:
            bar.set_color('white')
    return result


Solution 1:[1]

The main problem is that you never redraw the canvas, so every change you communicate to matplotlib will not appear in the figure generated by the backend. You also have to update the properties of the rectangles representing the bars - you tried this with bar.set_color() in one of the versions which changes both facecolor and edgecolor, intended or otherwise.

import pandas as pd
import matplotlib.pyplot as plt

df=pd.DataFrame({'mean':[40000,50000,20000,60000,3000],'CI':[4000,4000,3000,1000,200]},index=['A','B','C','D','E'])
df2=df.T

fig, ax = plt.subplots()
bars = ax.bar(range(df2.loc['mean'].size), df2.loc['mean'])
    
#Set horizontal line
hline = ax.axhline(y=20000, c='red', linestyle='--')
ax.set_xticks(range(df2.columns.size), df2.columns)

def onclick(event):
    #update hline position
    hline.set_ydata([event.ydata])
    #change all rectangles that represent the bars
    for bar in bars:
        #retrieve bar height and compare
        if bar.get_height() > event.ydata:
            #to set the color
            bar.set_color("red")
        else:
            bar.set_color("white")
    #redraw the figure to make changes visible
    fig.canvas.draw_idle()
            
fig.canvas.mpl_connect('button_press_event', onclick)
plt.show()

output: enter image description here

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1