[Python] Unable to create a toggle for gridlines on MatplotLib Python

Discussão em 'Python' iniciado por Stack, Março 4, 2025.

  1. Stack

    Stack Membro Participativo

    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...

Compartilhe esta Página