Plotly.py 自定义按钮实战:用 updatemenu 四种 method 打造可交互图表
Plotly.py 自定义按钮实战用 updatemenu 四种 method 打造可交互图表【免费下载链接】plotly.pyThe interactive graphing library for Python :sparkles:项目地址: https://gitcode.com/gh_mirrors/pl/plotly.py在 Plotly.py 中自定义按钮Custom Buttons可以让用户在图表的浏览器界面中直接切换图表形态、控制数据与布局属性无需重新运行代码。本文以仓库内 doc/python/custom-buttons.md 文档为主线系统讲解updatemenu的四种核心方法——restyle、relayout、update、animate并结合本仓库源码Button 图对象、Layout.updatemenus 属性深入剖析每个参数的含义与底层行为。读完本文你将能够在 Surface/Heatmap 切换、聚类区域高亮、多轨迹显隐联动、动画控制等真实场景中独立编写出可复制、可运行的交互按钮。什么是自定义按钮与 updatemenuPlotly 的按钮控件由布局属性layout.updatemenu承载。在 Python 侧你通过fig.update_layout(updatemenus[...])一次性注入一个或多个菜单项每个菜单项通过buttons列表定义若干按钮点击按钮即触发一次 Plotly.js 的图表修改命令。在本仓库的图对象体系中updatemenu是一棵独立的布局子树见 plotly/graph_objs/layout/updatemenu/init.py包含三个子对象对象作用Button定义单个按钮的label、method、args、args2、execute、visible等属性Font设置按钮文字字体Pad设置按钮容器四周的内边距b/l/r/t单位 pxButton的全部合法属性_valid_props可以从源码 plotly/graph_objs/layout/updatemenu/_button.py 直接看到args、args2、execute、label、method、name、templateitemname、visible。这些属性在构建dict时与go.layout.updatemenu.Button(...)构造器参数一一对应。method 的四种取值method决定了点击按钮后调用哪一类 Plotly.js 函数来修改图表。从源码 Button.method 的定义 可以看到它是一个枚举属性可取[restyle, relayout, animate, update, skip]其中skip用于纯前端事件挂接的高级场景。文档中四种常用方法对应关系如下method修改目标对应 plotly.js 函数典型用途restyledata或数据属性Plotly.restyle切换轨迹类型、colorscale、线型等relayoutlayout属性Plotly.relayout修改 shapes、标题、坐标轴等布局updatedata 和 layoutPlotly.update同时控制轨迹显隐与标题、注释animate动画帧Plotly.animate播放/暂停动画Restyle 按钮修改数据与数据属性当需要修改数据或数据属性时使用restyle方法。它的args第一个元素是属性名或属性字典第二个元素是目标值支持一次修改一个属性也支持一次修改多个属性。更新单个数据属性Surface ↔ Heatmap 切换下面的例子用一对按钮在图表的type属性surface↔heatmap之间切换。初始轨迹是go.Surface点击 Heatmap 后同一份数据会被重绘为热力图import plotly.graph_objects as go import pandas as pd # load dataset df pd.read_csv(https://raw.githubusercontent.com/plotly/datasets/master/volcano.csv) # create figure fig go.Figure() # Add surface trace fig.add_trace(go.Surface(zdf.values.tolist(), colorscaleViridis)) # Update plot sizing fig.update_layout( width800, height900, autosizeFalse, margindict(t0, b0, l0, r0), templateplotly_white, ) # Update 3D scene options fig.update_scenes( aspectratiodict(x1, y1, z0.7), aspectmodemanual ) # Add dropdown fig.update_layout( updatemenus[ dict( type buttons, direction left, buttonslist([ dict( args[type, surface], label3D Surface, methodrestyle ), dict( args[type, heatmap], labelHeatmap, methodrestyle ) ]), pad{r: 10, t: 10}, showactiveTrue, x0.11, xanchorleft, y1.1, yanchortop ), ] ) # Add annotation fig.update_layout( annotations[ dict(textTrace type:, showarrowFalse, x0, y1.08, yrefpaper, alignleft) ] ) fig.show()要点拆解args[type, surface]args的第 0 个元素是属性名字符串type第 1 个元素是目标值surface。Plotly 会把它翻译成Plotly.restyle(fig, {type: surface})。typebuttons明确把该菜单渲染为按钮组而非下拉菜单默认为下拉dropdown。directionleft按钮在水平方向从左向右排布可选right、up、down。pad{r: 10, t: 10}对应 Pad 对象 的r右侧与t顶部内边距单位 px。showactiveTrue当前激活的按钮保持高亮状态。x/y/xanchor/yanchor按钮容器在画布中的定位。y1.1配合yrefpaper的 annotation 可以把按钮放在绘图区上方。update_scenes是go.Figure提供的便捷方法用于设置 3D 场景的aspectratio与aspectmode保证切换为热力图之前 3D 视角固定。更新多个数据属性colorscale、反转与等高线第二个例子展示restyle同时驱动多个数据属性的能力三组按钮分别控制 colorscale、reversescale颜色反转以及等高线contours.showlines线显示。注意args可以直接传属性字典如{contours.showlines: False, type: contour}实现一次点击修改多个数据属性import plotly.graph_objects as go import pandas as pd # load dataset df pd.read_csv(https://raw.githubusercontent.com/plotly/datasets/master/volcano.csv) # Create figure fig go.Figure() # Add surface trace fig.add_trace(go.Heatmap(zdf.values.tolist(), colorscaleViridis)) # Update plot sizing fig.update_layout( width800, height900, autosizeFalse, margindict(t100, b0, l0, r0), ) # Update 3D scene options fig.update_scenes( aspectratiodict(x1, y1, z0.7), aspectmodemanual ) # Add dropdowns # button_layer_1_height 1.08 button_layer_1_height 1.12 button_layer_2_height 1.065 fig.update_layout( updatemenus[ dict( buttonslist([ dict( args[colorscale, Viridis], labelViridis, methodrestyle ), dict( args[colorscale, Cividis], labelCividis, methodrestyle ), dict( args[colorscale, Blues], labelBlues, methodrestyle ), dict( args[colorscale, Greens], labelGreens, methodrestyle ), ]), type buttons, directionright, pad{r: 10, t: 10}, showactiveTrue, x0.1, xanchorleft, ybutton_layer_1_height, yanchortop ), dict( buttonslist([ dict( args[reversescale, False], labelFalse, methodrestyle ), dict( args[reversescale, True], labelTrue, methodrestyle ) ]), type buttons, directionright, pad{r: 10, t: 10}, showactiveTrue, x0.13, xanchorleft, ybutton_layer_2_height, yanchortop ), dict( buttonslist([ dict( args[{contours.showlines: False, type: contour}], labelHide lines, methodrestyle ), dict( args[{contours.showlines: True, type: contour}], labelShow lines, methodrestyle ), ]), type buttons, directionright, pad{r: 10, t: 10}, showactiveTrue, x0.5, xanchorleft, ybutton_layer_2_height, yanchortop ), ] ) fig.update_layout( annotations[ dict(textcolorscale, x0, xrefpaper, y1.1, yrefpaper, alignleft, showarrowFalse), dict(textReversebrColorscale, x0, xrefpaper, y1.06, yrefpaper, showarrowFalse), dict(textLines, x0.47, xrefpaper, y1.045, yrefpaper, showarrowFalse) ]) fig.show()技巧说明通过updatemenus[...]传入多个菜单项即可在同一页面上叠加多组按钮用button_layer_1_height 1.12、button_layer_2_height 1.065这类「层高常量」手动控制各按钮行与 annotation 在 paper 坐标系yrefpaper中的纵向位置避免重叠。reversescale直接以 Python 布尔值True/False作为args的目标值说明args支持任意 JSON 兼容类型。点状属性名dotted property name如contours.showlines可以直接写进args字典Plotly.js 会按路径逐层解析这正是restyle修改嵌套数据属性的标准写法。如需使用cmocean色带如cmocean包提供的 colorscale可用pip install cmocean安装后通过colorscalecmocean.thermal等名称引用。Relayout 按钮修改布局属性当需要修改布局属性layout下的任何内容如shapes、标题、坐标轴范围、图例等时使用relayout方法。下面这个例子用relayout动态注入/清空shapes在散点图上用圆圈圈出不同的数据簇import plotly.graph_objects as go # Generate dataset import numpy as np np.random.seed(1) x0 np.random.normal(2, 0.4, 400) y0 np.random.normal(2, 0.4, 400) x1 np.random.normal(3, 0.6, 600) y1 np.random.normal(6, 0.4, 400) x2 np.random.normal(4, 0.2, 200) y2 np.random.normal(4, 0.4, 200) # Create figure fig go.Figure() # Add traces fig.add_trace( go.Scatter( xx0, yy0, modemarkers, markerdict(colorDarkOrange) ) ) fig.add_trace( go.Scatter( xx1, yy1, modemarkers, markerdict(colorCrimson) ) ) fig.add_trace( go.Scatter( xx2, yy2, modemarkers, markerdict(colorRebeccaPurple) ) ) # Add buttons that add shapes cluster0 [dict(typecircle, xrefx, yrefy, x0min(x0), y0min(y0), x1max(x0), y1max(y0), linedict(colorDarkOrange))] cluster1 [dict(typecircle, xrefx, yrefy, x0min(x1), y0min(y1), x1max(x1), y1max(y1), linedict(colorCrimson))] cluster2 [dict(typecircle, xrefx, yrefy, x0min(x2), y0min(y2), x1max(x2), y1max(y2), linedict(colorRebeccaPurple))] fig.update_layout( updatemenus[ dict( typebuttons, buttons[ dict(labelNone, methodrelayout, args[shapes, []]), dict(labelCluster 0, methodrelayout, args[shapes, cluster0]), dict(labelCluster 1, methodrelayout, args[shapes, cluster1]), dict(labelCluster 2, methodrelayout, args[shapes, cluster2]), dict(labelAll, methodrelayout, args[shapes, cluster0 cluster1 cluster2]) ], ) ] ) # Update remaining layout properties fig.update_layout( title_textHighlight Clusters, showlegendFalse, ) fig.show()要点拆解args[shapes, []]把layout.shapes设为空列表即“清除所有形状”args[shapes, cluster0]则整体替换为新的形状数组。relayout的args语义与Plotly.relayout完全一致第一个元素是布局属性名第二个是目标值。形状列表用dict(typecircle, xrefx, yrefy, ...)构造xref/yref指明形状坐标参考系为数据轴x0/y0/x1/y1为圆的外接矩形边界。注意None按钮通过传入空列表清空形状这与restyle中“属性字典 目标值”的写法形成了对比relayout始终针对布局路径。Update 按钮同时修改数据与布局当需要在一次点击中同时修改数据和布局时使用update方法它是restyle与relayout的组合。args相应变为一个三元结构[数据更新字典, 布局更新字典]。下面的例子用四颗按钮控制四条轨迹的visible数据侧并同步切换标题与注释布局侧import plotly.graph_objects as go import pandas as pd # Load dataset df pd.read_csv( https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv) df.columns [col.replace(AAPL., ) for col in df.columns] # Initialize figure fig go.Figure() # Add Traces fig.add_trace( go.Scatter(xlist(df.index), ylist(df.High), nameHigh, linedict(colorMediumSlateBlue))) fig.add_trace( go.Scatter(xlist(df.index), y[df.High.mean()] * len(df.index), nameHigh Average, visibleFalse, linedict(colorMediumSlateBlue, dashdash))) fig.add_trace( go.Scatter(xlist(df.index), ylist(df.Low), nameLow, linedict(colorDarkOrange))) fig.add_trace( go.Scatter(xlist(df.index), y[df.Low.mean()] * len(df.index), nameLow Average, visibleFalse, linedict(colorDarkOrange, dashdash))) # Add Annotations and Buttons high_annotations [dict(x-0.05, ydf.High.mean(), xanchorright, yanchorbottom, xrefx domain, yrefy, textHigh Avg:br %.2f % df.High.mean(), showarrowFalse), dict(xdf.High.idxmax(), ydf.High.max(), xrefx, yrefy, textHigh Max:br %.2f % df.High.max(), ax0, ay-40)] low_annotations [dict(x-0.05, ydf.Low.mean(), xanchorright, yanchortop, xrefx domain, yrefy, textLow Avg:br %.2f % df.Low.mean(), showarrowFalse), dict(xdf.Low.idxmin(), ydf.Low.min(), xrefx, yrefy, textLow Min:br %.2f % df.Low.min(), ax0, ay40)] fig.update_layout( updatemenus[ dict( typebuttons, directionright, active0, x0.57, y1.2, buttonslist([ dict(labelNone, methodupdate, args[{visible: [True, False, True, False]}, {title: Yahoo, annotations: []}]), dict(labelHigh, methodupdate, args[{visible: [True, True, False, False]}, {title: Yahoo High, annotations: high_annotations}]), dict(labelLow, methodupdate, args[{visible: [False, False, True, True]}, {title: Yahoo Low, annotations: low_annotations}]), dict(labelBoth, methodupdate, args[{visible: [True, True, True, True]}, {title: Yahoo, annotations: high_annotations low_annotations}]), ]), ) ]) # Set title fig.update_layout( title_textYahoo, xaxis_domain[0.05, 1.0] ) fig.show()要点拆解args的第一部分是数据更新字典{visible: [True, False, True, False]}列表长度与轨迹数一一对应控制四条 Scatter 的显隐第二部分是布局更新字典{title: ..., annotations: ...}。这正是Plotly.update的签名(data, layout)。active0默认激活第 0 颗按钮None保证初始状态与标题、注释一致。初始构造轨迹时把“均值线”设置为visibleFalse再在按钮中开启是「先备好轨迹、由按钮决定何时显示」的常见模式。xaxis_domain[0.05, 1.0]压缩绘图区为左侧高频注释腾出空间。Animate 按钮驱动动画animate方法用于启动或暂停动画其行为与 Plotly 的frames机制配合使用。仓库中与之配套的完整动画示例见 doc/python/animations.md其中 Plotly Express 的animation_frame/animation_group参数可以直接生成带播放按钮的动画图例如import plotly.express as px df px.data.gapminder() px.scatter(df, xgdpPercap, ylifeExp, animation_frameyear, animation_groupcountry, sizepop, colorcontinent, hover_namecountry, log_xTrue, size_max55, range_x[100,100000], range_y[25,90])如果你需要完全自定义的动画控制可以在go.Figure上通过frames[go.Frame(...)]定义帧再用methodanimate的按钮指定args[None, {frame: {duration: 500, redraw: False}, fromcurrent: True, transition: {duration: 300}}]来播放或传入args2实现播放/暂停的切换参见下文args2说明。深入源码Button 属性的完整语义从 Button 对象源码 可以确认每个按钮字段的精确语义这些字段均可在dict或构造器中直接使用属性类型含义argslist最多 3 个元素点击时传递给method指定 Plotly 方法的参数值args2list最多 3 个元素第二组参数当按钮处于激活态时再次点击会改用这组参数执行常用于制作“开关型”按钮executebool为False时跳过 API 命令执行但仍绑定状态便于挂接plotly_buttonclicked等事件后手动执行labelstr / number按钮上显示的文字method枚举restyle/relayout/animate/update/skipskip不做任何 API 调用仅保留菜单交互namestr模板template中命名条目用templateitemnamestr引用模板中命名条目并加以覆盖visiblebool该按钮是否可见容器层的Pad提供b/l/r/t四个内边距属性单位 px对应文档示例中的pad{r: 10, t: 10}Font则控制按钮文字的family/size/color。三者均挂载在layout.updatemenu子树下因此无论用dict字面量还是go.layout.updatemenu.Button构造器最终都会被序列化为标准的 Plotly.js JSON 结构校验规则定义于 plotly/validators/_validators.json。在 Python 侧的组装入口方面fig.update_layout(updatemenus...)定义于 plotly/basedatatypes.py它会以递归方式合并传入的字典并触发属性校验layout.updatemenus与layout.annotations的图对象属性则分别定义于 plotly/graph_objs/_layout.py 与 plotly/graph_objs/_layout.py。理解了这条「Python 字典 → 图对象 → Plotly.js JSON」的链路就能明白按钮示例中所有写法的底层去向。实战建议与常见问题选对 method只动数据选restyle只动布局选relayout两者都要动选update。选错会导致按钮“无效”或只更新了预期的一半。args的形态restyle的args是[属性名, 值]或[{属性字典}]relayout是[布局属性名, 值]update是[数据字典, 布局字典]。三种形态不可混用。按钮定位x/y默认相对绘图区配xanchor/yanchor锚点使用需要放在绘图区上方时配合yrefpaper的 annotation 与合适的y值如 1.1即可多组按钮记得为每组分配不同的y避免重叠。默认激活态用active指定默认高亮的按钮下标并保证初始布局与它一致避免出现状态漂移。测试验证仓库测试目录中已有针对relayout消息的用例tests/test_core/test_figure_messages/test_plotly_relayout.py与属性赋值用例tests/test_core/test_graph_objs/test_property_assignment.py可作为编写按钮逻辑后回归验证的参考。参考本教程核心来源doc/python/custom-buttons.md动画配套文档doc/python/animations.md按钮图对象实现plotly/graph_objs/layout/updatemenu/_button.py、plotly/graph_objs/layout/updatemenu/_pad.py布局组装入口plotly/graph_objs/_layout.py、plotly/basedatatypes.py更多updatemenu布局属性细节可查阅仓库内自动生成的layout.updatemenu图对象源码及其校验定义 plotly/validators/_validators.json。【免费下载链接】plotly.pyThe interactive graphing library for Python :sparkles:项目地址: https://gitcode.com/gh_mirrors/pl/plotly.py创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考