Matplotlib小球碰撞动画报错:'NoneType'无'_get_view'属性的解决
解决Matplotlib小球碰撞动画中的AttributeError错误
错误原因
你创建的plt.Circle图形对象未添加到坐标轴中,启用blit=True时,Matplotlib无法获取这些元素的视图属性,从而触发'NoneType' object has no attribute '_get_view'错误。此外,未设置坐标轴范围,可能导致小球跑出可视区域。
修正步骤
- 将创建的每个
Circle对象通过ax.add_patch()添加到坐标轴 - 为坐标轴设置固定范围,匹配场景边界值
- 确保
animate函数返回已添加到轴上的图形元素列表
修正后的完整代码
import numpy as np from random import randint import math from matplotlib import pyplot as plt from matplotlib.animation import FuncAnimation np_rnd = np.random.default_rng() class Ball: def __init__(self, x_, y_, r_, m_, v_speed_, v_accel_): self.m = m_ self.x = x_ self.y = y_ self.x0 = x_ self.y0 = y_ self.r = r_ self.v_speed = v_speed_ self.v_accel = v_accel_ self.t = 0 def get_cmap(n, name='hsv'): return plt.cm.get_cmap(name, n) FPS = 77 X_MIN = -1 X_MAX = 100 Y_MIN = -1 Y_MAX = 100 RADIUS_MIN = 1 RADIUS_MAX = 3 MASS_MAX = 6 SPEED_MAX = 0.5 ACCEL_MAX = 0 N = 5 T = 1 ball_list = [] xy_balls = np.c_[np_rnd.integers(X_MIN+RADIUS_MAX, X_MAX-RADIUS_MAX,(N)), np_rnd.integers(Y_MIN+RADIUS_MAX, Y_MAX-RADIUS_MAX,(N))] r_balls = np_rnd.integers(RADIUS_MIN, RADIUS_MAX, N) color_balls = np.arange(0, N) fig, ax = plt.subplots() # 设置坐标轴范围,固定显示区域 ax.set_xlim(X_MIN, X_MAX) ax.set_ylim(Y_MIN, Y_MAX) ax.plot([X_MIN, X_MAX], [0, 0], color='black', linewidth=0.5) ax.annotate("x", xy=(X_MAX - 2, 0)) ax.plot([0, 0], [Y_MIN, Y_MAX], color='black', linewidth=0.5) ax.annotate("y", xy=(0, Y_MAX - 2)) ax.annotate("0", xy=(2, 2)) cmap = get_cmap(N) for i in range(N): ball_list.append(Ball(xy_balls[i][0], xy_balls[i][1], r_balls[i], randint(1, MASS_MAX), np_rnd.uniform(0, SPEED_MAX, size=2), np_rnd.uniform(0, ACCEL_MAX, size=2) ) ) # 创建Circle对象并添加到坐标轴 balls_plt = [] for center, radius, color in zip(xy_balls, r_balls, color_balls): circle = plt.Circle(center, radius=radius, color=cmap(color)) ax.add_patch(circle) balls_plt.append(circle) def checkBorder(ball): if (ball.x > X_MAX) or (ball.x < X_MIN): ball.x = (X_MAX - 1) if ball.x > X_MAX else X_MIN + 1 ball.x0 = ball.x ball.y0 = ball.y ball.v_speed[0] = -(ball.v_speed[0] + ball.t * ball.v_accel[0]) ball.v_speed[1] = ball.v_speed[1] + ball.t * ball.v_accel[1] ball.v_accel[0] = -ball.v_accel[0] ball.t = 0 if (ball.y > Y_MAX) or (ball.y < Y_MIN): ball.y = (Y_MAX - 1) if ball.y > Y_MAX else Y_MIN + 1 ball.x0 = ball.x ball.y0 = ball.y ball.v_speed[0] = ball.v_speed[0] + ball.t * ball.v_accel[0] ball.v_speed[1] = -(ball.v_speed[1] + ball.t * ball.v_accel[1]) ball.v_accel[1] = -ball.v_accel[1] ball.t = 0 def checkBite(ball1, ball2): D = math.sqrt(((ball1.x - ball2.x) ** 2) + ((ball1.y - ball2.y) ** 2)) if D < (ball1.r + ball2.r): ball1.v_speed[0] = ((ball1.m - ball2.m) * ball1.v_speed[0] + 2 * ball2.m * ball2.v_speed[0]) / ( ball1.m + ball2.m) ball1.v_speed[1] = ((ball1.m - ball2.m) * ball1.v_speed[1] + 2 * ball2.m * ball2.v_speed[1]) / ( ball1.m + ball2.m) ball2.v_speed[0] = ((ball2.m - ball1.m) * ball2.v_speed[0] + 2 * ball1.m * ball1.v_speed[0]) / ( ball1.m + ball2.m) ball2.v_speed[1] = ((ball2.m - ball1.m) * ball2.v_speed[1] + 2 * ball1.m * ball1.v_speed[1]) / ( ball1.m + ball2.m) ball1.x0 = ball1.x + (ball1.v_speed[0] * ball1.r) ball1.y0 = ball1.y + (ball1.v_speed[1] * ball1.r) ball2.x0 = ball2.x + (ball2.v_speed[0] * ball2.r) ball2.y0 = ball2.y + (ball2.v_speed[1] * ball2.r) ball1.t = T ball2.t = T def setCoord(ball): ball.x = ball.x0 + ball.v_speed[0] * ball.t + (ball.v_accel[0] * (ball.t ** 2)) / 2 ball.y = ball.y0 + ball.v_speed[1] * ball.t + (ball.v_accel[1] * (ball.t ** 2)) / 2 ball.t += T def animate(i, balls): for j in range(len(balls)): # 取消注释启用小球碰撞检测 # for k in range(j+1, len(balls)): # checkBite(balls[j], balls[k]) checkBorder(balls[j]) setCoord(balls[j]) balls_plt[j].center = (balls[j].x, balls[j].y) return balls_plt anim = FuncAnimation(fig=fig, func=animate, fargs=(ball_list,), frames=np.linspace(0, FPS * 2, num=1000, dtype=np.intc, endpoint=False), interval=1000 / FPS, blit=True, repeat=True) plt.show()
关键修正点说明
- 添加图形到坐标轴:循环创建Circle对象时,用
ax.add_patch(circle)将每个圆添加到坐标轴,确保Matplotlib能追踪到这些元素 - 固定坐标轴范围:通过
ax.set_xlim和ax.set_ylim锁定显示区域,避免小球移动导致坐标轴自动缩放 - 返回正确的更新元素:
animate函数直接返回已添加到轴上的balls_plt列表,确保blit机制能正确更新这些元素
内容的提问来源于stack exchange,提问作者Nord
相关产品推荐
相关产品推荐

