Search code examples
matplotlibcollectionssliderfill

Matplotlib fill_between after slide update does not redraw


I'm drawing a simple line (y = mx + b) and I'm rotating it about the origin (only in the first quadrant) and I'm filling the between the line and the x-axis. I change the slope of the line via a slider. I've researched this and it appears that in order to keep the region filled as the line rotates is to use "Collections", which implemented but now I enter an infinite loop. Specifically, as I change the slider, the filled region is correct but only for one change of the slider. After this it enters into an infinite loop. Any help would be appreciated. The primary portion of my code is below:

import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation
from matplotlib.widgets import Slider  # import the Slider widget

fig = plt.figure()

axcolor = 'lightgoldenrodyellow'
main_ax = plt.axes([0, .5, 10, 15])
slider_ax = plt.axes([0.2, 0.08, .7, .03], facecolor=axcolor)   

#Some constants
torad = np.pi/180
WAngle = 20.
xmax = 10

#Slider min max
s_min = 00
s_max = 60
s_init = WAngle

# set the current plot to main_ax
plt.sca(main_ax)

#Define and plot the triangle
plt.axes() #Select the main axis
plt.title('test')
plt.xlim(0,10)
plt.ylim(0,10)

#Now define the line, only need the second point, first point 0,0
Beta = WAngle
Bx = 10
By = Bx*np.tan(Beta * torad)

# Draw line and add to plot
xdat = [0, 10]
ydat = [0, By]
line = plt.Line2D(xdat, ydat,color='k')  #(x1,x2),(y1,y2)   
axis = plt.gca()
axis.add_line(line) 
fillbet = plt.fill_between(xdat, ydat, color="#539ecd",label="wedge")
sline = Slider(slider_ax, 'line', s_min, s_max, valinit=s_init) 

## def update(val): 
def update(val):
    myangle = sline.val
    By = Bx*np.tan(myangle * torad)
    xdat = [0, Bx]
    ydat = [0, By]
    line.set_xdata((0, Bx))
    line.set_ydata((0, By))
    for collection in axis.collections:
        if str(collection.get_label()) == "wedge":
            collection.remove()
            del collection
        fillbet = plt.fill_between(xdat, ydat, color='#539ecd')
    plt.gcf().canvas.draw_idle()

sline.on_changed(update)

plt.axis('scaled')
plt.show()

Solution

  • Your code is mostly working, but you had 2 unfortunate bugs.

    • You forgot to re-label the PolyCollection when you recreate it in the update() function, so that it can be deleted the second time around.
    • You put the instruction to recreate the PolyCollection inside the for-loop

    The following code seem to do what you want:

    import matplotlib.pyplot as plt
    import numpy as np
    import matplotlib.animation
    from matplotlib.widgets import Slider  # import the Slider widget
    
    fig = plt.figure()
    
    axcolor = 'lightgoldenrodyellow'
    main_ax = plt.axes([0, .5, 10, 15])
    slider_ax = plt.axes([0.2, 0.08, .7, .03], facecolor=axcolor)   
    
    #Some constants
    torad = np.pi/180
    WAngle = 20.
    xmax = 10
    
    #Slider min max
    s_min = 0
    s_max = 60
    s_init = WAngle
    
    # set the current plot to main_ax
    plt.sca(main_ax)
    
    #Define and plot the triangle
    plt.axes() #Select the main axis
    plt.title('test')
    plt.xlim(0,10)
    plt.ylim(0,10)
    
    #Now define the line, only need the second point, first point 0,0
    Beta = WAngle
    Bx = 10
    By = Bx*np.tan(Beta * torad)
    
    # Draw line and add to plot
    xdat = [0, 10]
    ydat = [0, By]
    line = plt.Line2D(xdat, ydat,color='k')  #(x1,x2),(y1,y2)   
    axis = plt.gca()
    axis.add_line(line) 
    fillbet = plt.fill_between(xdat, ydat, color="#539ecd",label="wedge")
    sline = Slider(slider_ax, 'line', s_min, s_max, valinit=s_init) 
    
    ## def update(val): 
    def update(val):
        myangle = sline.val
        By = Bx*np.tan(myangle * torad)
        xdat = [0, Bx]
        ydat = [0, By]
        line.set_xdata((0, Bx))
        line.set_ydata((0, By))
        for collection in axis.collections:
            if str(collection.get_label()) == "wedge":
                collection.remove()
        fillbet = plt.fill_between(xdat, ydat, color='#539ecd', label="wedge")
        fig.canvas.draw_idle()
    
    sline.on_changed(update)
    
    plt.axis('scaled')
    plt.show()