0

I pulled the below code from SO and modified it to get closer to what I need. Basically, I want the user to move a slider to change the position of the axhline, which then will be used to do come calculations. My issue is that the line which should remove the previous axhline does not remove it and it breaks my function and new lines won't be create on slider changes. What is wrong with my remove() call? Is the reference to the axhline getting lost when the function terminates? I tried to make it global, but that didn't help.

    # Import libraries
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button
%matplotlib notebook

# Create a subplot
fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.35)
r = 0.6
g = 0.2
b = 0.5

# Create and plot a bar chart
year = [2002, 2004, 2006, 2008, 2010]
production = [25, 15, 35, 30, 10]
plt.bar(year, production, color=(r, g, b))
targ_line = plt.axhline(y=20, xmin=-.1, clip_on=False, zorder=1, color='#e82713')

# Create 3 axes for 3 sliders red,green and blue
target = plt.axes([0.25, 0.2, 0.65, 0.03])


# Create a slider from 0.0 to 35.0 in axes target
# with 20.0 as initial value.
red = Slider(target, 'Target', 0.0, 35.0, 20)


# Create fuction to be called when slider value is changed
# This is the part I am having trouble with.  I want to remove the previous line
# so that there will only be one axhline present

def update(val):  
    #targ_line.remove()   # uncommenting this line doesn't remove the line and prevents the remainder of the code in this function from running.
    Y = red.val
    targ_line = ax.axhline(y=Y, color = 'black')
    ax.bar(year, production, color=(r, g, b),
        edgecolor="black")

# Call update function when slider value is changed
red.on_changed(update)


# Create axes for reset button and create button
resetax = plt.axes([0.8, 0.025, 0.1, 0.04])
button = Button(resetax, 'Reset', color='gold',
                hovercolor='skyblue')

# Create a function resetSlider to set slider to
# initial values when Reset button is clicked

def resetSlider(event):
        red.reset()


# Call resetSlider function when clicked on reset button
button.on_clicked(resetSlider)

# Display graph
plt.show()
higrm
  • 23
  • 4
  • Thanks @czeni you pointed me in the right direction. This code will remove the axhline and allow me to create a new one (czeni's way of modifying the existing one is a better solution, I admit.) `def update(val): global Y2, targ_line Y2 = red.val targ_line.remove() targ_line = ax.axhline(y=Y2, xmin=-.01, clip_on=False, zorder=1, color='k')` – higrm Jul 22 '21 at 10:30
  • I needed to make the reference to targ_line global. – higrm Jul 22 '21 at 10:36

1 Answers1

0

You can try updating the axhline instead of recreating it. For example:

def update(val):  
    #Y = red.val
    #targ_line = ax.axhline(y=Y, color = 'black')
    targ_line.set_ydata(y=red.val)
    targ_line.set_color('black')
    ax.bar(year, production, color=(r, g, b), edgecolor="black")
czeni
  • 427
  • 2
  • 11
  • Thanks czeni, that works, but it doesn't answer my question about how can I actually remove the original and then draw a new one? I can be remove it `targ_line.remove() ` , but how do I then to create a new one? That would greatly enhance my understanding of how Python works. Thanks! – higrm Jul 22 '21 at 10:21
  • @higrm As you wrote before, if you really want to remove the `targ_line` you need to access the targ_line in the global namespace. You can do that with adding `global targ_line` as the first line of the `update()` function. Although, this is not the recommended way. Moreover, the [Matplotlib slider demo](https://matplotlib.org/stable/gallery/widgets/slider_demo.html) also follows an updateing approach in the event handler functions. – czeni Jul 23 '21 at 09:35