You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

行星轨道动画问题:多窗口、图片不显示及Hover功能失效求助

行星轨道动画开发问题及解决方案

问题描述

  • 输入年份后弹出多个图表窗口,仅需保留一个动画窗口;
  • 行星轨道显示正常,但行星图片无法在对应轨道位置显示(已验证图片路径并通过PIL调整尺寸);
  • 自定义hover交互函数未触发。

修正后的完整代码

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from astropy.time import Time
from astropy.coordinates import solar_system_ephemeris, get_body_barycentric
from astropy.coordinates import CartesianRepresentation
from astropy import units as u
from PIL import Image
import mplcursors

def compute_positions_and_orbits(year):
    positions = {}
    orbits = {}
    
    start_time = Time(f'{year}-01-01', scale='utc')
    end_time = start_time + 365 * u.day  # 模拟一年的数据

    with solar_system_ephemeris.set('builtin'):
        planets = ['mercury', 'venus', 'earth', 'mars', 'jupiter', 'saturn', 'uranus', 'neptune']

        for planet_name in planets:
            times = Time(np.linspace(start_time.jd, end_time.jd, 1000), format='jd')
            planet = get_body_barycentric(planet_name, times)
            coords = planet.represent_as(CartesianRepresentation).xyz.value
            positions[planet_name] = coords
            orbits[planet_name] = coords

    return positions, orbits

def update(frame, current_year, positions, orbits, planet_images, planet_sizes):
    ax = plt.gca()
    ax.clear()
    ax.set_xlabel('X (AU)')
    ax.set_ylabel('Y (AU)')
    ax.set_title(f'Planetary Orbits and Positions in {current_year}')
    ax.set_facecolor('black')

    # 存储行星图片的艺术家对象,用于hover识别
    planet_artists = {}

    for planet_name, orbit in orbits.items():
        x_orbit, y_orbit, _ = orbit
        x_pos, y_pos, _ = positions[planet_name]

        # 绘制轨道
        ax.plot(x_orbit, y_orbit, color='white', alpha=0.3)

        # 绘制行星图片
        img = planet_images[planet_name]
        size = planet_sizes[planet_name]
        extent = (x_pos[frame] - size, x_pos[frame] + size, 
                  y_pos[frame] - size, y_pos[frame] + size)
        artist = ax.imshow(img, extent=extent, alpha=0.8, origin='lower')
        planet_artists[artist] = planet_name

    # 设置坐标轴比例和范围
    ax.set_aspect('equal')
    ax.set_xlim(-50, 50)
    ax.set_ylim(-50, 50)

    return planet_artists.values()

def display_zoomed_image(planet_name, planet_image):
    print(f"Hovering over planet: {planet_name}")
    fig, ax = plt.subplots(figsize=(4,4))
    ax.imshow(planet_image, origin='lower')
    ax.set_title(planet_name)
    ax.axis('off')
    plt.show()

def animate(year):
    positions, orbits = compute_positions_and_orbits(year)
    num_frames = len(next(iter(positions.values()))[0])

    # 提前加载并调整行星图片,避免每次update重复加载
    planet_image_paths = {
        'mercury': r"C:\Users\Dell\Desktop\planet imgs\mercury.jpeg",
        'venus': r"C:\Users\Dell\Desktop\planet imgs\venus.jpeg",
        'earth': r"C:\Users\Dell\Desktop\planet imgs\earth.jpeg",
        'mars': r"C:\Users\Dell\Desktop\planet imgs\mars.jpeg",
        'jupiter': r"C:\Users\Dell\Desktop\planet imgs\jupiter.png",  # 修正拼写错误
        'saturn': r"C:\Users\Dell\Desktop\planet imgs\saturn.png",
        'uranus': r"C:\Users\Dell\Desktop\planet imgs\uranus.jpeg",
        'neptune': r"C:\Users\Dell\Desktop\planet imgs\neptune.jpg"
    }
    # 根据行星实际大小设置显示尺寸(AU单位)
    planet_sizes = {
        'mercury': 0.2,
        'venus': 0.3,
        'earth': 0.3,
        'mars': 0.25,
        'jupiter': 1.0,
        'saturn': 0.8,
        'uranus': 0.5,
        'neptune': 0.5
    }
    planet_images = {}
    for name, path in planet_image_paths.items():
        img = Image.open(path)
        # 调整为正方形,避免拉伸
        img = img.resize((200, 200))
        planet_images[name] = img

    fig = plt.figure(figsize=(10, 10))
    ax = fig.add_subplot(111)

    # 初始化动画
    ani = animation.FuncAnimation(fig, update, frames=num_frames, 
                                  fargs=(year, positions, orbits, planet_images, planet_sizes),
                                  interval=50, repeat=False)

    # 设置hover交互,只初始化一次
    def on_hover(event):
        # 检查hover的对象是否是行星图片
        if hasattr(event.target, '_extent'):
            # 匹配对应的行星名称
            for artist, name in planet_images.items():
                if event.target == artist:
                    display_zoomed_image(name, planet_images[name])
                    break

    mplcursors.cursor(ax, hover=True).connect("add", on_hover)

    plt.show()

def main():
    while True:
        year_input = input("Enter a year between 1900 and 2040: ")
        try:
            year = int(year_input)
            if 1900 <= year <= 2040:
                animate(year)
                break
            else:
                print("Invalid year. Please enter a year between 1900 and 2040.")
        except ValueError:
            print("Invalid input. Please enter a valid year.")

if __name__ == "__main__":
    main()

问题修复细节

1. 多窗口弹出问题

  • 原因:update函数中每次调用plt.show(),与FuncAnimation自身的窗口渲染重复触发;原代码中未使用的第三方模块(transformers、tensorflow、spacy)会造成不必要的资源加载。
  • 修复:删除update函数内的plt.show(),仅保留animate函数末尾的一次调用;移除未使用的冗余模块。

2. 行星图片不显示问题

  • 原因:
    • plt.imshow默认origin='upper',与轨道坐标系(Y轴向上)不匹配;
    • 原代码在update中重复加载图片,性能低下且易引发渲染异常;
    • 木星图片路径拼写错误(jupyter.png应为jupiter.png);
    • 图片尺寸与显示范围不匹配,导致图片超出可视区域。
  • 修复:
    • 提前加载所有行星图片并调整为统一正方形尺寸;
    • imshow添加origin='lower'参数,匹配轨道坐标系;
    • 修正木星路径拼写错误;
    • 为不同行星设置对应显示尺寸(AU单位),确保图片在轨道位置可见。

3. Hover交互未触发问题

  • 原因:
    • 原代码在update中每次重新创建mplcursors.cursor,plt.cla()会清除所有交互事件;
    • on_hover函数未正确识别hover的行星对象,硬编码为Earth。
  • 修复:
    • 将cursor初始化和事件连接移到animate函数中,仅执行一次;
    • 修改on_hover函数,通过event.target识别行星图片对象,匹配对应的行星名称;
    • 调整display_zoomed_image函数,直接使用提前加载的PIL图片对象。

内容的提问来源于stack exchange,提问作者Shades05

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 00:55:55