Textual 高度样式指南:深入理解height属性与<scalar>单位体系
【免费下载链接】textualThe lean application framework for Python. Build sophisticated user interfaces with a simple Python API. Run your apps in the terminal and a web browser.项目地址: https://gitcode.com/gh_mirrors/te/textual
height是 Textual 中控制组件(Widget)垂直尺寸的核心样式属性,它建立在<scalar>单位体系之上,支持固定单元格、百分比、比例(fr)、相对视口(vw/vh)等多种取值方式。本文以 docs/styles/height.md 为主干,结合 src/textual/css/scalar.py 等源码,完整讲解height的语法、每种单位的计算规则、CSS 与 Python 双路设置方式,以及它和box-sizing、min-height、max-height的协作关系,让你能精确掌控 Textual 应用的纵向布局。
height属性是什么
height样式用于设置一个 Widget 的高度。它是 Textual 布局系统中与width对称的纵向尺寸属性,两者都接收一个<scalar>类型的值。
在 src/textual/css/styles.py 中,height被声明为:
height = ScalarProperty(percent_unit=Unit.HEIGHT) """Set the height of the widget."""其中percent_unit=Unit.HEIGHT的含义是:当使用%百分比单位时,高度百分比默认相对于容器的高度计算(这一点与width的percent_unit=Unit.WIDTH正好对应)。
语法
height: <scalar>;与box-sizing的关系
height默认设置的是**内容区(content area)的高度;只有当box-sizing被设置为border-box(Textual 的默认值)时,height设置的才是边框区(border area)**的高度。也就是说:
box-sizing: border-box(默认):height包含边框与内边距,加了padding或border后组件整体尺寸不变,但内容空间被压缩;box-sizing: content-box:height只作用于内容区,padding和border会在其基础上继续撑大组件。
<scalar>单位体系全解
height的取值本质是一个<scalar>长度值。完整的单位参考表(源自 docs/css_types/scalar.md)如下:
| 单位符号 | 单位名称 | 示例 | 说明 |
|---|---|---|---|
| (无) | 单元格 Cell | 10 | 单元格(行/列)数量,唯一绝对单位 |
fr | 分数 Fraction | 1fr | 组件应占据的空间比例 |
% | 百分比 Percent | 75% | 相对于容器组件的长度 |
w | 宽度 Width | 25w | 相对于容器组件宽度的百分比 |
h | 高度 Height | 75h | 相对于容器组件高度的百分比 |
vw | 视口宽度 Viewport width | 25vw | 相对于视口宽度的百分比 |
vh | 视口高度 Viewport height | 75vh | 相对于视口高度的百分比 |
| — | 自动 Auto | auto | 尝试计算恰好容纳内容、无需滚动的最优尺寸 |
对应到源码,这套单位被定义为 src/textual/css/scalar.py 中的Unit枚举,以及符号映射表UNIT_SYMBOL(scalar.py)。
各单位的计算规则
- Cell(无符号):高度方向上一格即一行。例如
height: 10表示组件高度为 10 行。支持整数与浮点数,但浮点数最终会被截断为整数。 - Fraction(
fr):比例分配剩余空间。例如两个并排组件分别设height: 1fr与height: 3fr,后者的高度就是前者的 3 倍。 - Percent(
%):相对于容器可用空间的比例。height: 50%表示组件高度为容器高度的 50%。 w单位:相对于容器宽度的比例。height: 25w表示组件高度 = 容器宽度 × 25%。若容器宽 100 格,则该组件高 25 格。h单位:相对于容器高度的比例。height: 75h表示组件高度 = 容器高度 × 75%。vw单位:相对于视口宽度的比例,无视中间容器。视口宽度 = 终端宽度减去左右 dock 组件的宽度。height: 6.25vw即视口宽度的 6.25%。vh单位:相对于视口高度的比例,无视中间容器。视口高度 = 终端高度减去上下 dock 组件的高度。height: 75vh即视口高度的 75%。auto:自动计算恰好容纳内容的最优高度。例如一个内容只有一行的Label,height: auto时其高度就是 1 行。
快速上手:基础示例
原文档附带的入门示例位于 docs/examples/styles/height.py:
from textual.app import App from textual.widget import Widget class HeightApp(App): CSS_PATH = "height.tcss" def compose(self): yield Widget() if __name__ == "__main__": app = HeightApp() app.run()配套样式表 docs/examples/styles/height.tcss:
Screen > Widget { background: green; height: 50%; color: white; }运行后,屏幕上会出现一个绿色背景、高度正好占屏幕 50% 的组件。由于Screen(屏幕)是它的直接容器,height: 50%最终解析为「屏幕高度的一半」。
实战对照:所有高度格式一次看全
原文档的进阶示例用 9 个占位组件把全部单位放在同一个界面中对比,演示脚本是 docs/examples/styles/height_comparison.py:
from textual.app import App from textual.containers import VerticalScroll from textual.widgets import Label, Placeholder, Static class Ruler(Static): def compose(self): ruler_text = "·\n·\n·\n·\n•\n" * 100 yield Label(ruler_text) class HeightComparisonApp(App): CSS_PATH = "height_comparison.tcss" def compose(self): yield VerticalScroll( Placeholder(id="cells"), Placeholder(id="percent"), Placeholder(id="w"), Placeholder(id="h"), Placeholder(id="vw"), Placeholder(id="vh"), Placeholder(id="auto"), Placeholder(id="fr1"), Placeholder(id="fr2"), ) yield Ruler() if __name__ == "__main__": app = HeightComparisonApp() app.run()每个Placeholder的id标明其所用的单位,右侧还有一个 dock 的垂直标尺方便目测高度。完整的样式表 docs/examples/styles/height_comparison.tcss 如下:
#cells { height: 2; } #percent { height: 12.5%; } #w { height: 5w; } #h { height: 12.5h; } #vw { height: 6.25vw; } #vh { height: 12.5vh; } #auto { height: auto; } #fr1 { height: 1fr; } #fr2 { height: 2fr; } Screen { layers: ruler; overflow: hidden; } Ruler { layer: ruler; dock: right; width: 1; background: $accent; }假设终端为 80×24,文档中对每个高度的注释(对应 height_comparison.tcss 中的(1)!~(9)!)逐条解释了计算过程:
height: 2—— 固定 2 行高。height: 12.5%—— 容器(VerticalScroll)高 24 行,12.5% × 24 = 3 行。height: 5w—— 直接容器宽 80,5% × 80 = 4 行高(注意这里用的是容器的宽度)。height: 12.5h—— 直接容器高 24,12.5% × 24 = 3 行高(用的是容器的高度)。height: 6.25vw—— 视口宽 80,6.25% × 80 = 5 行高。height: 12.5vh—— 视口高 24,12.5% × 24 = 3 行高。height: auto—— 内容仅一行,因此高度解析为 1。height: 1fr—— 占据 1 份比例空间,高度是2fr组件的一半。height: 2fr—— 占据 2 份比例空间,高度是1fr组件的两倍。
这组例子清楚展示了w/h与vw/vh的区别:前两者相对直接容器的宽/高计算,后两者相对视口的宽/高计算,与中间容器无关。
CSS 与 Python 双路设置
CSS 方式
/* 显式单元格高度 */ height: 10; /* 百分比高度 */ height: 50%; /* 自动高度 */ height: autoPython 方式
self.styles.height = 10 # 显式单元格高度,可以是 int self.styles.height = "50%" self.styles.height = "auto"从源码看,ScalarProperty.__set__(src/textual/css/_style_properties.py)对 Python 侧的赋值做了三种归一化处理:
- 传入
int/float:直接封装为Unit.CELLS单元格单位; - 传入字符串:调用
Scalar.parse()解析,解析失败会抛出带帮助信息的StyleValueError; - 传入
Scalar对象:原样采用。
无论哪种途径,只要规则真正发生变化,都会触发refresh(layout=True)触发布局重算,保证界面立即响应。
源码级原理:Scalar 的解析与解析
height之所以能支持如此多样的单位,核心在于 src/textual/css/scalar.py 中的Scalar类型(一个由「数值 + 单位 + 百分比基准单位」组成的三元组),其解析正则如下(scalar.py):
_MATCH_SCALAR = re.compile(r"^(-?\d+\.?\d*)(fr|%|w|h|vw|vh)?$").matchScalar.parse()会先特判auto,其余情况按正则拆分数值与单位符号,再映射为Unit枚举成员;随后resolve()根据单位在RESOLVE_MAP(scalar.py)中选择对应的解析函数:
_resolve_cells:直接返回数值本身;_resolve_fraction:返回fraction_unit × 数值;_resolve_width:数值 × 容器宽度 / 100;_resolve_height:数值 × 容器高度 / 100;_resolve_view_width/_resolve_view_height:分别以视口宽度、视口高度为基准换算。
一个值得注意的细节:Scalar内部还保存了percent_unit(百分比基准单位)。height在声明时传入了Unit.HEIGHT,因此在resolve()中遇到%单位时会先用percent_unit替换后再查RESOLVE_MAP,最终按容器高度计算——这正是「height: 12.5%相对容器高度」这一行为在源码中的落点。
与 min-height、max-height、width 的配合
height不是孤立属性,它通常与以下属性协同工作:
min-height与max-height:限制组件高度的下限与上限。在 styles.py 中,它们同样以ScalarProperty(percent_unit=Unit.HEIGHT)声明,但通过allow_auto=False禁用了auto取值——也就是说min-height/max-height不能写auto。width:设置组件宽度,与height共同构成组件的二维尺寸。
常见用法:给一个百分比高度的组件加min-height兜底,避免终端窗口缩小时组件被压得过矮而不可用。
小结
height是 Textual 布局体系中最常用的纵向尺寸属性,其核心在于<scalar>单位的灵活组合:
- 精确控制用单元格(
height: 10); - 响应式布局用百分比(
height: 50%)或相对视口的vh; - 弹性分配空间用
fr; - 自适应内容用
auto。
理解每种单位相对「直接容器」还是「视口」计算(对应源码中size与viewport两个入参),再配合box-sizing、min-height/max-height,即可在终端与浏览器两种运行环境下都获得稳定、可预期的纵向布局效果。更多相关样式可参考 docs/styles/index.md 的完整样式目录。
【免费下载链接】textualThe lean application framework for Python. Build sophisticated user interfaces with a simple Python API. Run your apps in the terminal and a web browser.项目地址: https://gitcode.com/gh_mirrors/te/textual
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考