I've been trying to make a toggle button to turn on and off the gridlines on my graph every time it's clicked. I understand how the toggle works as I was able to do it for some lines being animated onto my graph. I'm unsure if it's a problem with how MatplotLib sets axis and zorder etc. Or if it's to do with the fact i'm animating the graph and it updates the whole figure every frame. I didn't think that would be a problem though considering that everything is redrawn and not just the changing plots. I've tried many different ways of doing this widget and none of it seemed to work. I've changed the visibility of the lines by using alpha, the colour since my background is black, switching ax.grid() to ax.grid(False). I'm not sure anymore, this is what I've got currently: ax_grid = fig.add_axes([0.85, 0.2, 0.1, 0.04]) gbutton = Button(ax_grid, 'Grid', color = '0.3', hovercolor='0.7') # Define the grid visibility state grid_visible = False # Initialize the grid visibility state def grid_lines(event): global grid_visible grid_visible = not grid_visible # Toggle the state plt.sca(ax) # Ensure we are modifying the main plot's axes ax.set_axisbelow(False) # trying to set it above fig.canvas.draw() if grid_visible: ax.grid(color='white') # Show grid with properties else: ax.grid(color='black') # Simply turn off the grid without extra arguments fig.canvas.draw_idle() # Redraw the canvas Another version: ax_grid = fig.add_axes([0.85, 0.2, 0.1, 0.04]) gbutton = Button(ax_grid, 'Grid', color = '0.3', hovercolor='0.7') # Define the grid visibility state grid_visible = False # Initialize the grid visibility state def grid_lines(event): global grid_visible grid_visible = not grid_visible # Toggle state # Access grid lines directly and toggle their visibility for line in ax.get_xgridlines() + ax.get_ygridlines(): line.set_visible(grid_visible) fig.canvas.draw_idle() # Redraw the canvas My whole code incase it's needed: import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation from matplotlib.widgets import Slider, Button import scipy from math import sqrt plt.rcParams["figure.autolayout"] = True print("Default text color is: ", plt.rcParams['text.color']) plt.rcParams.update({'text.color': "white"}) # changing default text colour to white dt = 0.1 numsteps = 10000 pi = scipy.constants.pi G = 4.30091e-3 # AU^3 * M_sun^-1 * yr^-2 wA = 3.0 wB = 3.0 thetaA = 0 thetaB = thetaA + pi # to put the other store on the opposite end of starA # initialise variables r = 5 mA = 10 # mass in solar mass mB = 10 M = mA + mB x_valA, y_valA = [], [] x_valB, y_valB = [], [] # Create the animation, fig represents the object/canvas and ax means it is the area being plotted on fig, ax = plt.subplots(figsize=(8, 8), dpi=100) ax.set_xlim(-10, 10) ax.set_ylim(-10, 10) ax.set_aspect('equal') ax.set_facecolor("black") fig.patch.set_facecolor("k") # Create the stars and COM plot starA, = ax.plot([], [], 'o', color='blue', markersize=10, label='Star A',zorder=9.5) starB, = ax.plot([], [], 'o', color='red', markersize=10, label='Star B',zorder=9.5) COM = ax.plot([0], [0], '+', color='white', markersize=5, label='COM',zorder=10) orbitA, = ax.plot([], [], '-', color='cyan', alpha=0.5, label='Orbit A', zorder=5) orbitB, = ax.plot([], [], '-', color='pink', alpha=0.5, label='Orbit B', zorder=5) ax.grid(color='white', linestyle='--', linewidth=0.5, zorder=1) leg = ax.legend(facecolor='k', labelcolor='w', fancybox=True, framealpha=0.6, loc='upper right', bbox_to_anchor=(1,1)) leg.set_zorder(20) def orbit(r, mA, mB, M): global x_valA, y_valA, x_valB, y_valB, thetaA, thetaB, G, dt # Reset variables x_valA, y_valA = [], [] x_valB, y_valB = [], [] M = mA + mB rA = r * (mB / M) rB = r * (mA / M) # initial positions positionA = np.array([rA * np.cos(thetaA), rA * np.sin(thetaA)]) # Star A initial position positionB = np.array([rB * np.cos(thetaB), rB * np.sin(thetaB)]) # Star B initial position # SIMULATION LOOP for _ in range(numsteps): # Store positions for both stars x_valA.append(positionA[0]) y_valA.append(positionA[1]) x_valB.append(positionB[0]) y_valB.append(positionB[1]) # Update speed and angles for next positions wA = sqrt(G * M / rA * rA) wB = sqrt(G * M / rB * rB) thetaA += wA * dt # update angle for starA thetaB += wB * dt # update angle for starB # Calculate new positions based on updated angles positionA = np.array([rA * np.cos(thetaA), rA * np.sin(thetaA)]) positionB = np.array([rB * np.cos(thetaB), rB * np.sin(thetaB)]) # After simulation loop, update the orbit lines with the recorded paths orbitA.set_data(x_valA, y_valA) orbitB.set_data(x_valB, y_valB) # initialising the data def init(): starA.set_data([], []) starB.set_data([], []) orbitA.set_data([], []) # Clear the initial orbit paths orbitB.set_data([], []) return starA, starB, orbitA, orbitB def update(frame): starA.set_data([x_valA[frame]], [y_valA[frame]]) # Pass as lists starB.set_data([x_valB[frame]], [y_valB[frame]]) if orbit_lines_visible: orbitA.set_data(x_valA[:frame+1], y_valA[:frame+1]) orbitB.set_data(x_valB[:frame+1], y_valB[:frame+1]) return starA, starB, orbitA, orbitB ani = FuncAnimation(fig, update, frames=numsteps, init_func=init, blit=False, interval=50) plt.title("Binary Star System", fontsize=20, fontweight='bold') def up(val): global r, mA, mB, M r = seperation_slider.val mA = mA_slider.val mB = mB_slider.val M = mA + mB orbit(r, mA, mB, M) # updated values into function starA.set_markersize(mA_slider.val) starB.set_markersize(mB_slider.val) ani.event_source.stop() # Stop the current animation ani.event_source.start() # Restart the animation with updated orbit fig.canvas.draw_idle() seperation_slider = Slider(ax=plt.axes([0.125, 0.02, 0.10, 0.04]), label='Seperation', valmin=1, valmax=15, valinit=r, valstep=1.11, facecolor='w') mA_slider = Slider(ax=plt.axes([0.45, 0.02, 0.15, 0.04]), label="Mass A", valmin=0.1, valmax=100, valinit=mA, valstep=1.11, facecolor='b') mB_slider = Slider(ax=plt.axes([0.80, 0.02, 0.15, 0.04]), label="Mass B", valmin=0.1, valmax=100, valinit=mB, valstep=1.11, facecolor='r') seperation_slider.label.set_size(12) mA_slider.label.set_size(12) mB_slider.label.set_size(12) mA_slider.vline.set_color('cyan') mB_slider.vline.set_color('violet') seperation_slider.vline.set_color('black') seperation_slider.on_changed(up) mA_slider.on_changed(up) mB_slider.on_changed(up) orbit(r, mA, mB, M) ax_reset = fig.add_axes([0.85, 0.08, 0.1, 0.04]) rbutton = Button(ax_reset, 'Reset', color='0.3', hovercolor='0.7') def reset(event): seperation_slider.reset() mA_slider.reset() mB_slider.reset() rbutton.on_clicked(reset) # Toggle for orbit lines visibility orbit_lines_visible = False def lines(event): global orbit_lines_visible if orbit_lines_visible: orbitA.set_alpha(0) # Hide orbit A orbitB.set_alpha(0) # Hide orbit B else: orbitA.set_alpha(0.5) # Show orbit A orbitB.set_alpha(0.5) # Show orbit B orbit_lines_visible = not orbit_lines_visible fig.canvas.draw_idle() ax_lines = fig.add_axes([0.85, 0.14, 0.1, 0.04]) button = Button(ax_lines, 'Orbit Lines', color='0.3', hovercolor='0.7') button.on_clicked(lines) ax_grid = fig.add_axes([0.85, 0.2, 0.1, 0.04]) gbutton = Button(ax_grid, 'Grid', color = '0.3', hovercolor='0.7') # Define the grid visibility state grid_visible = False # Initialize the grid visibility state def grid_lines(event): global grid_visible grid_visible = not grid_visible if grid_visible: ax.grid(True, color='white') else: ax.grid(False) # Force immediate redraw fig.canvas.draw() plt.show() Continue reading...