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

Streamlit中图例尺寸适配求助:自定义滚动功能部署后失效

问题:Matplotlib滚动图例在Streamlit部署后失效

本地使用Matplotlib绘制饼图时,针对大量取值的图例实现了鼠标滚轮滚动功能,本地运行正常,但部署到Streamlit后,图例始终完整显示在页面上,无法截断启用滚动,调整图表和图例尺寸后问题仍未解决,图例已延伸至页面下方。

相关代码
fig1 = plt.figure(figsize=(7, 3))
axx = fig.add_axes([0.1, 0.6, 0.3, 0.3])
p1 = volcanos["Country"].value_counts()

fig1, axx = plt.subplots()
axx.pie(p1, labels=p1.keys(), autopct='%1.1f%%',
                    shadow=True, startangle=90)
legend = axx.legend(loc="upper left", bbox_to_anchor=(1.02, 0, 0.07, 1))

# pixels to scroll per mousewheel event
d = {"down" : 30, "up" : -30}
def func(evt):
    if legend.contains(evt):
        bbox = legend.get_bbox_to_anchor()
        bbox = Bbox.from_bounds(bbox.x0, bbox.y0+d[evt.button], bbox.width, bbox.height)
        tr = legend.axes.transAxes.inverted()
        legend.set_bbox_to_anchor(bbox.transformed(tr))
        fig1.canvas.draw_idle()

fig1.canvas.mpl_connect("scroll_event", func)

st.pyplot(fig1)
原因分析

Streamlit的st.pyplot()并非直接渲染Matplotlib的交互式canvas,而是将其转换为静态图像或简化渲染层,导致你绑定的scroll_event这类Matplotlib原生交互事件无法触发,动态调整图例bbox的逻辑也无法生效。

解决方案

方案1:改用Plotly实现带滚动图例的饼图

Plotly的图例原生支持滚动,且在Streamlit中兼容性良好,无需手动绑定事件:

import plotly.express as px
import streamlit as st

# 处理数据格式
p1 = volcanos["Country"].value_counts().reset_index()
p1.columns = ["Country", "Count"]

# 绘制饼图并配置滚动图例
fig = px.pie(p1, values='Count', names='Country', title='火山分布按国家统计')
fig.update_layout(
    legend=dict(
        yanchor="top",
        y=0.99,
        xanchor="left",
        x=1.02,
        bgcolor="white",
        bordercolor="black",
        borderwidth=1,
        height=400  # 限制图例高度,超出部分自动出现滚动条
    )
)
st.plotly_chart(fig, use_container_width=True)

方案2:用mpld3将Matplotlib交互图嵌入Streamlit

通过mpld3将Matplotlib图转换为带交互的HTML,再用Streamlit的组件嵌入,保留原有的滚轮滚动逻辑:

import mpld3
import streamlit as st
import matplotlib.pyplot as plt
from matplotlib.transforms import Bbox

# 数据处理
p1 = volcanos["Country"].value_counts()

# 绘制饼图并绑定滚动事件
fig1, axx = plt.subplots(figsize=(7, 3))
axx.pie(p1, labels=p1.keys(), autopct='%1.1f%%', shadow=True, startangle=90)
legend = axx.legend(loc="upper left", bbox_to_anchor=(1.02, 0, 0.07, 1))

d = {"down" : 30, "up" : -30}
def func(evt):
    if legend.contains(evt):
        bbox = legend.get_bbox_to_anchor()
        bbox = Bbox.from_bounds(bbox.x0, bbox.y0+d[evt.button], bbox.width, bbox.height)
        tr = legend.axes.transAxes.inverted()
        legend.set_bbox_to_anchor(bbox.transformed(tr))
        fig1.canvas.draw_idle()

fig1.canvas.mpl_connect("scroll_event", func)

# 转换为交互式HTML并嵌入
html_str = mpld3.fig_to_html(fig1)
st.components.v1.html(html_str, height=600, scrolling=True)

注意:需先安装mpld3(pip install mpld3),部分环境可能存在兼容性差异。

方案3:用Streamlit滑块模拟图例滚动

通过滑块控制图例的垂直偏移,静态更新图表来模拟滚动效果:

import streamlit as st
import matplotlib.pyplot as plt
from matplotlib.transforms import Bbox

p1 = volcanos["Country"].value_counts()

# 添加滑块控制图例偏移
y_offset = st.slider("调整图例位置", min_value=-len(p1)*10, max_value=0, value=0, step=10)

# 绘制饼图并应用偏移
fig1, axx = plt.subplots(figsize=(7, 3))
axx.pie(p1, labels=p1.keys(), autopct='%1.1f%%', shadow=True, startangle=90)
legend = axx.legend(loc="upper left", bbox_to_anchor=(1.02, y_offset/100, 0.07, 1))

st.pyplot(fig1)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 18:05:18