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

PyShiny Express中Plotly动画3D散点图无法播放求助

问题:Plotly动画3D散点图在PyShiny中无法播放

尝试使用Plotly生成动画3D散点图,并在PyShiny Express创建的仪表板中展示。该代码在Jupyter Notebook中运行时动画完全正常,但在Shiny中点击播放按钮后无动画效果,仅工具提示(tooltip)可正常显示数据。动画仅用于改变散点标记的大小,已排查异步函数问题但仍未解决。

问题原因分析

核心问题是Shiny渲染Plotly部件时,动画帧的trace必须与初始图的trace严格匹配。原代码中初始图通过px.scatter_3d生成,而动画帧直接创建了全新的go.Scatter3d对象,导致Shiny无法将帧中的trace与初始trace关联,进而无法触发视觉上的动画更新(仅tooltip数据更新是因为数据本身存在,但视觉元素未被正确映射)。

解决方案及修改后的代码

修改思路:确保动画帧仅更新需要变化的属性(标记大小、颜色),而非重新创建整个trace,保持帧与初始trace的结构一致性。

import io
from pathlib import Path
from shiny import reactive
from shiny.express import input, ui, render
from shiny.types import FileInfo
import pandas as pd
import plotly.graph_objects as go
import plotly.express as px
from shinywidgets import render_widget

ui.page_opts(
    title="Visualisation",
    fillable=True,
    id="page",
)

with ui.card(full_screen=True):
    @render_widget
    def coordinate_space():
        try:
            with ui.Progress(min=1, max=5) as p:
                data = {
                    'X': [1, 2, 3, 4, 5],
                    'Y': [5, 4, 3, 2, 1],
                    'Z': [2, 3, 4, 5, 6],
                    'FZ_new': [10, 20, 30, 40, 50],
                    'FZ': [5, 10, 15, 20, 25]
                }

                scaled_coordinate_system = pd.DataFrame(data)

                p.set(2, message="Data loaded, preparing plot...")

                # 初始trace:保留结构,后续帧基于此更新属性
                fig = go.Figure()
                fig.add_trace(go.Scatter3d(
                    x=scaled_coordinate_system['X'],
                    y=scaled_coordinate_system['Y'],
                    z=scaled_coordinate_system['Z'],
                    mode='markers',
                    marker=dict(size=scaled_coordinate_system['FZ_new'], color=scaled_coordinate_system['FZ_new']),
                    opacity=0.7,
                    name='Scatter Trace'
                ))

                p.set(3, message="Creating animation frames...")

                # 帧仅更新需要变化的属性,不重新创建trace
                frames = [
                    go.Frame(
                        data=[go.Scatter3d(
                            marker=dict(size=scaled_coordinate_system['FZ_new'] * 10, color="Red"),
                            opacity=0.9
                        )],
                        name='Frame 1'
                    ),
                    go.Frame(
                        data=[go.Scatter3d(
                            marker=dict(size=scaled_coordinate_system['FZ'], color=scaled_coordinate_system['FZ']),
                            opacity=0.7
                        )],
                        name='Frame 2'
                    )
                ]

                fig.frames = frames

                p.set(4, message="Updating layout...")

                fig.update_layout(
                    scene=dict(
                        xaxis_title='X AXIS',
                        yaxis_title='Y AXIS',
                        zaxis_title='Z AXIS'
                    ),
                    updatemenus=[dict(
                        type='buttons',
                        direction="down",
                        showactive=True,
                        buttons=[
                            dict(
                                label='Play',
                                method='animate',
                                args=[None, dict(
                                    frame=dict(duration=500, redraw=True),
                                    fromcurrent=True,
                                    transition=dict(duration=0)
                                )]
                            ),
                            dict(
                                label='Pause',
                                method='animate',
                                args=[[None], dict(
                                    frame=dict(duration=0, redraw=False),
                                    mode='immediate'
                                )]
                            )
                        ]
                    )]
                )

                fig.update_layout(margin=dict(l=0, r=0, b=0, t=0))
                camera = dict(
                    eye=dict(x=2, y=2, z=0.5)
                )

                fig.update_layout(scene_camera=camera)
                p.set(5, message="Rendering plot...")
                return fig

        except Exception as e:
            ui.notification_show(f"{e}", duration=20, type="error")
            return None
关键修改点说明
  • 初始图直接用go.Figure() + add_trace()创建,避免px.scatter_3d生成的隐藏结构导致trace不匹配
  • 动画帧仅更新marker和opacity属性,不重新定义整个Scatter3d对象,确保与初始trace的索引、name等结构一致
  • 保持updatemenus的配置不变,确保播放/暂停逻辑正常触发

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 05:30:08