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

如何在Streamlit中精准计算自定义st.markdown行数以统一高度?

实现高度一致的Streamlit自定义Markdown组件

我正在构建两个自定义st.markdown组件,需要给它们定义高度、宽度和背景色,核心要求是两者高度必须一致,且等于内容行数最多的那个组件的高度(比如一个内容占10行,另一个占7行,两个都要设为10行的高度)。

现有Markdown组件示例代码

目前的组件实现模板如下:

st.markdown(
    f"""
    <div style="background-color:#e7efe0;margin: 10px; padding:10px;border-radius:10px; height:{max_lines_count}em; width: 35em; margin:0 auto">
        <p style="color:#333333;text-align:left;font-size:12px;">Text here</p>
    </div>
    """,
    unsafe_allow_html=True
)

(注:原代码中闭合标签写错了,已修正为</p>)

现有行数计算算法的问题

我之前尝试通过估算单行可容纳的字符数来计算行数,比如设定单行最多22个字符,超了就换行。函数代码如下:

def count_lines(sentence, max_num=22):
    words = sentence.split()
    lines = 1
    current_line_length = 0
    for word in words:
        word_length = len(word)
        if current_line_length == 0:
            current_line_length += word_length
        elif current_line_length + 1 + word_length <= max_num:
            current_line_length += 1 + word_length
        else:
            lines += 1
            current_line_length = word_length
    return lines

但这个方法误差很大——不同字符的宽度不一样,比如逗号“,”和字母“m”的宽度差很多,按字符数算出来的行数和实际渲染的行数对不上。所以需要找到能精准计算字符串像素宽度的方法,当字符串像素宽度超过Markdown组件的宽度时判定换行,从而算出准确的行数。

精准计算行数的两种方案

方案1:用Python的PIL库计算文本像素宽度

通过PIL模拟前端的字体渲染,计算文本的实际像素宽度,再结合组件的像素宽度来统计行数。

from PIL import ImageFont

def calculate_text_width(text, font_path=None, font_size=12):
    # 优先使用指定字体,否则用系统默认字体
    if font_path:
        font = ImageFont.truetype(font_path, font_size)
    else:
        font = ImageFont.load_default()
    # 返回文本的像素宽度
    width, _ = font.getsize(text)
    return width

def count_actual_lines(sentence, container_width_px=420, font_size=12, font_path=None):
    words = sentence.split()
    if not words:
        return 1
    
    lines = 1
    current_line_width = 0
    font = ImageFont.truetype(font_path, font_size) if font_path else ImageFont.load_default()
    # 计算单个空格的像素宽度
    space_width, _ = font.getsize(" ")

    for word in words:
        word_width, _ = font.getsize(word)
        
        if current_line_width == 0:
            # 处理第一行的第一个单词
            if word_width > container_width_px:
                # 单个单词超长,按容器宽度拆分多行
                lines += word_width // container_width_px
                current_line_width = word_width % container_width_px
            else:
                current_line_width = word_width
        else:
            # 加上空格后的总宽度
            total_width = current_line_width + space_width + word_width
            if total_width <= container_width_px:
                current_line_width = total_width
            else:
                lines += 1
                if word_width > container_width_px:
                    lines += word_width // container_width_px
                    current_line_width = word_width % container_width_px
                else:
                    current_line_width = word_width
    return lines

使用说明:

  • container_width_px对应组件的实际像素宽度,示例中35em(字号12px)= 12*35=420px
  • 要尽量匹配前端组件使用的字体,比如前端用Arial,就传入对应的字体文件路径,否则计算结果会有偏差
  • 已经处理了单个单词超过容器宽度的极端情况

方案2:前端JS动态调整(最精准)

既然最终是在浏览器中渲染,直接用JS动态计算组件高度是最准确的,不需要在Python端提前算行数。

import streamlit as st
from streamlit.components.v1 import html

def render_synced_markdown(text1, text2):
    # 渲染两个带ID的容器
    html_content = f"""
    <div id="box1" style="background-color:#e7efe0;margin:10px; padding:10px;border-radius:10px; width:35em; margin:0 auto">
        <p style="color:#333333;text-align:left;font-size:12px;">{text1}</p>
    </div>
    <div id="box2" style="background-color:#e7efe0;margin:10px; padding:10px;border-radius:10px; width:35em; margin:0 auto">
        <p style="color:#333333;text-align:left;font-size:12px;">{text2}</p>
    </div>
    <script>
        // 获取两个容器的实际渲染高度
        const h1 = document.getElementById('box1').offsetHeight;
        const h2 = document.getElementById('box2').offsetHeight;
        const maxH = Math.max(h1, h2);
        // 同步设置两个容器的高度为最大值
        document.getElementById('box1').style.height = maxH + 'px';
        document.getElementById('box2').style.height = maxH + 'px';
    </script>
    """
    # 预留padding和margin的高度,避免内容被截断
    html(html_content, height=maxH + 40)

这个方案完全依赖浏览器的实际渲染结果,不存在字体、字符宽度的误差,实现起来也更简单高效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 09:44:55