如何固定plotly.express.timeline甘特图的条形宽度/厚度
Plotly甘特图固定条形竖直厚度的实现方案
Plotly本身没有直接提供固定条形像素厚度的配置项,但可以通过动态计算图表总高度的方式实现需求,以下是两种可落地的实现方案:
方案1:Python端预计算图表高度
你之前担心的高度计算非线性问题,本质是没有固定布局边距、图例占位等参数,将这些参数固定后,高度和y轴分类数为完全线性关系,精度足够使用。
实现代码如下:
import datetime as dt import plotly.express as px data = [ {'Task': '1-2', 'Start': '2021-09-26', 'Finish': '2021-09-27', 'Resource': 'R1'}, {'Task': '2-1', 'Start': '2021-09-23', 'Finish': '2021-09-24', 'Resource': 'R2'}, {'Task': '2-2', 'Start': '2021-09-26', 'Finish': '2021-09-27', 'Resource': 'R2'}, {'Task': '3-1', 'Start': '2021-09-30', 'Finish': '2021-10-01', 'Resource': 'R3'}, {'Task': '3-2', 'Start': '2021-11-26', 'Finish': '2021-11-27', 'Resource': 'R3'}, ] # 固定配置参数,可根据需求调整 BAR_HEIGHT_PX = 30 # 期望的条形固定厚度,单位像素 Y_AXIS_GAP_RATIO = 0.2 # y轴相邻分类的间距占比 TOP_MARGIN = 60 # 顶部边距 BOTTOM_MARGIN = 60 # 底部边距 LEGEND_HEIGHT = 40 # 图例占位高度,图例放在左右侧时可设为0 # 统计y轴去重分类数量 y_category_count = len({d['Resource'] for d in data}) # 计算总图表高度 total_height = int(y_category_count * BAR_HEIGHT_PX / (1 - Y_AXIS_GAP_RATIO) + TOP_MARGIN + BOTTOM_MARGIN + LEGEND_HEIGHT) fig = px.timeline( data, x_start="Start", x_end="Finish", y="Resource", ) # 更新布局配置 fig.update_layout( height=total_height, yaxis=dict( categorygap=Y_AXIS_GAP_RATIO ), margin=dict(t=TOP_MARGIN, b=BOTTOM_MARGIN) ) # 序列化返回即可 return plotly.io.to_json(fig)
方案2:前端动态调整适配
如果不想在Python端处理逻辑,也可以在前端渲染前动态计算高度,修改布局参数后再渲染,实现代码如下:
<script src="https://cdn.plot.ly/plotly-2.0.0.min.js"></script> <script type="text/javascript"> // 固定配置参数,可根据需求调整 const BAR_HEIGHT_PX = 30; const Y_AXIS_GAP_RATIO = 0.2; const TOP_MARGIN = 60; const BOTTOM_MARGIN = 60; const LEGEND_HEIGHT = 40; let graph = {{ gantt_json|safe }}; // 统计y轴去重分类数量 const yCategoryCount = new Set(graph.data[0].y).size; // 计算总图表高度 const totalHeight = Math.ceil(yCategoryCount * BAR_HEIGHT_PX / (1 - Y_AXIS_GAP_RATIO) + TOP_MARGIN + BOTTOM_MARGIN + LEGEND_HEIGHT); // 写入布局配置 graph.layout.height = totalHeight; graph.layout.yaxis = graph.layout.yaxis || {}; graph.layout.yaxis.categorygap = Y_AXIS_GAP_RATIO; graph.layout.margin = graph.layout.margin || {}; graph.layout.margin.t = TOP_MARGIN; graph.layout.margin.b = BOTTOM_MARGIN; Plotly.newPlot('gantt_graph', graph, {}); </script>
注意事项
- 如果存在同一y轴分类下多条形并行的场景,只需将分类数乘以单分类下最大并行条形数,即可适配计算逻辑
- 两种方案都可以保证页面缩放时,条形厚度保持固定,不会随窗口尺寸变化
内容的提问来源于stack exchange,提问作者Jérôme
相关产品推荐
相关产品推荐

