[Python] Python animation timing not working correctly

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

  1. Stack

    Stack Membro Participativo

    So i recently strated using Python from scratch. I am trying to build a pictogram-made bar graph, where within each bar, each row appears one after another; and then each bar appears one after another. I wrote some code which i m 100%sure is quite amateur, but the animation does not work correctly I think - instead of having constant time intervals between rows and bars, the interval gets longer as the animation progresses

    Oh and in addition, the year labels do not appear.. Here is my code:

    import matplotlib

    matplotlib.use('TkAgg')
    import matplotlib.pyplot as plt
    import matplotlib.animation as animation
    from matplotlib.offsetbox import OffsetImage, AnnotationBbox
    import numpy as np

    # ========== CONFIGURATION ==========
    ICON_PATH = 'dot.png' # Replace with your PNG
    ZOOM = 0.4 # Increased zoom for visibility
    BAR_SPACING = 0.15 # Wider spacing between bars
    ICON_SPACING_X = 0.02 # More horizontal space between icons
    ICON_SPACING_Y = 0.02 # More vertical space between rows
    LABEL_Y_OFFSET = 0 # Labels placed lower

    BARS = [
    {"total": 5 * 10, "label": "2000"},
    {"total": 5 * 25, "label": "2005"},
    {"total": 5 * 36 + 4, "label": "2010"},
    {"total": 5 * 48 + 2, "label": "2015"},
    {"total": 5 * 58 + 2, "label": "2020"},
    {"total": 5 * 60, "label": "2024"},
    ]

    COLUMNS_PER_BAR = 5
    ROW_INTERVAL_MS = 50 # Affects all bars now
    BAR_DELAY_MS = 200 # Delay between bars (now respects ROW_INTERVAL_MS)
    # ===================================

    # Load icon (ensure it's a high-resolution PNG/SVG)
    icon = plt.imread(ICON_PATH)

    # Precompute bar data
    max_rows = 0
    for idx, bar in enumerate(BARS):
    full_rows, remainder = divmod(bar["total"], COLUMNS_PER_BAR)
    bar["full_rows"] = full_rows
    bar["remainder"] = remainder
    bar["total_rows"] = full_rows + (1 if remainder else 0)
    bar["x_pos"] = 0.1+idx * BAR_SPACING
    max_rows = max(max_rows, bar["total_rows"])

    # Set up figure with expanded limits
    fig, ax = plt.subplots(figsize=((len(BARS)) * BAR_SPACING+3, 12)) # Larger figure size
    ax.set_xlim(-0.5, (len(BARS)) * BAR_SPACING)
    ax.set_ylim(LABEL_Y_OFFSET - 2, max_rows * ICON_SPACING_Y + 2)
    ax.axis('off')


    def add_icon(x, y, ax):
    img = OffsetImage(icon, zoom=ZOOM) # Increased zoom
    ab = AnnotationBbox(img, (x, y), frameon=False)
    ax.add_artist(ab)
    return ab


    def animate(frame_data):
    ax.clear()
    ax.axis('off')
    current_bar_idx, current_row = frame_data

    for bar_idx, bar in enumerate(BARS[:current_bar_idx + 1]):
    # Draw up to current_row for the current bar
    if bar_idx < current_bar_idx:
    rows_to_draw = bar["total_rows"]
    else:
    rows_to_draw = current_row + 1

    for row in range(rows_to_draw):
    cols = COLUMNS_PER_BAR if row < bar["full_rows"] else bar["remainder"]
    for col in range(cols):
    x = bar["x_pos"] + col * ICON_SPACING_X
    y = 0.1+row * ICON_SPACING_Y
    add_icon(x, y, ax)

    # Add label
    ax.text(
    bar["x_pos"] + (COLUMNS_PER_BAR * ICON_SPACING_X) / 2,
    LABEL_Y_OFFSET,
    bar["label"],
    ha='center',
    va='top',
    fontsize=14,
    weight='bold'
    )

    return ax.artists


    # Generate frames with correct timing for all bars
    frames = []
    for bar_idx, bar in enumerate(BARS):
    # Add frames for each row in this bar
    for row in range(bar["total_rows"]):
    frames.append((bar_idx, row))
    # Add delay between bars using empty frames
    if bar_idx != len(BARS) - 1:
    delay_frames = int(BAR_DELAY_MS / ROW_INTERVAL_MS)
    for _ in range(delay_frames):
    frames.append((bar_idx, bar["total_rows"] - 1)) # Hold last frame

    # Create animation
    ani = animation.FuncAnimation(
    fig,
    animate,
    frames=frames,
    interval=ROW_INTERVAL_MS,
    blit=True
    )

    plt.show()
    # ani.save('animation.gif', writer='pillow', fps=1000/ROW_INTERVAL_MS)

    Continue reading...

Compartilhe esta Página