pythonmatplotlibinteractive

Why is my (working) code (which makes an interactive plot) not working anymore when putting it inside a function?


I made an interactive plot with python using matplotlib and I want to call it by putting it inside a function. Everything works until I put my whole code inside the function make_plot(). Running the code without the make_plot() function gives me exactly what I want. Why is it not working anymore when inside another function (I just lose the interactive functionality)?

import matplotlib.pyplot as plt
from matplotlib.widgets import Button
import numpy as np

def make_plot():
    def button_clicked(event):
        global line_plotted
        if line_plotted:
            ax1.lines[1].remove()
            line_plotted = False
        else:
            ax1.plot(line[0], line[1], color='r', linestyle='--')
            line_plotted = True
        fig.canvas.draw()
            
    
    plt.ion()
    
    x = np.linspace(0, 4*np.pi, 500)
    y = np.sin(x)
    line = [0, 4 * np.pi], [1, 1]
    
    fig = plt.figure(figsize=(15,5))
    ax1 = fig.add_subplot()
    ax1.plot(x,y)
    ax1.plot(line[0], line[1], color='r', linestyle='--')
    line_plotted = True
    
    button_ax = plt.axes([0.8, 0.05, 0.1, 0.075])
    button = Button(button_ax, 'Show red line')
    button.on_clicked(button_clicked)
    
    
    
    plt.show()

make_plot()

I tried putting the button_clicked(event) function outside the make_plot() function but it didn't help.


Solution

  • global line_plotted
    

    -->

    nonlocal line_plotted
    

    After you move all code inside a function, line_plotted doesn't live in global level, nonlocal keyword is used to reference a variable in the nearest scope.

    Edit: full code

    run in pycharm and spyder are all interactive -- click button .

    import matplotlib
    import matplotlib.pyplot as plt
    from matplotlib.widgets import Button
    import numpy as np
    
    matplotlib.use("TkAgg")
    
    
    def make_plot():
        def button_clicked(event):
            nonlocal line_plotted
            if line_plotted:
                ax1.lines[1].remove()
                line_plotted = False
            else:
                ax1.plot(line[0], line[1], color="r", linestyle="--")
                line_plotted = True
            fig.canvas.draw()
    
        plt.ion()
    
        x = np.linspace(0, 4 * np.pi, 500)
        y = np.sin(x)
        line = [0, 4 * np.pi], [1, 1]
    
        fig = plt.figure(figsize=(15, 5))
        ax1 = fig.add_subplot()
        ax1.plot(x, y)
        ax1.plot(line[0], line[1], color="r", linestyle="--")
        line_plotted = True
    
        button_ax = plt.axes([0.8, 0.05, 0.1, 0.075])
        button = Button(button_ax, "Show red line")
        button.on_clicked(button_clicked)
    
        plt.show(block=True)
    
    
    make_plot()