marimo 布局指南:使用 mo.hstack 与 mo.vstack 构建弹性 Flex 布局
marimo 布局指南使用 mo.hstack 与 mo.vstack 构建弹性 Flex 布局【免费下载链接】marimoA reactive notebook for Python — run reproducible experiments, query with SQL, execute as a script, deploy as an app, and version with git. Stored as pure Python. All in a modern, AI-native editor.项目地址: https://gitcode.com/GitHub_Trending/ma/marimomarimo 是面向 Python 的响应式 notebook其内置布局原语mo.hstack水平堆叠与mo.vstack垂直堆叠基于 CSS Flexbox 实现用于将任意 UI 元素、HTML 片段与输出组合成可对齐、可换行、可调间距的弹性布局。本文以 docs/api/layouts/stacks.md 为骨架结合 flex.py 源码与测试用例完整讲解两个 API 的参数语义、内部实现原理与实战组合技巧读完即可在 notebook 中构建控件面板、统计卡片网格与仪表盘式布局。一、核心 API 概览hstack 与 vstack 是什么mo.hstack与mo.vstack是 marimo 提供的两个无状态布局函数位于 marimo/_plugins/stateless/flex.py。它们不携带交互状态只负责把一组items渲染成行row或列column方向的 Flex 容器返回 Html 对象因此可以直接作为 cell 的输出也可以嵌套在其他布局中。两个函数的签名如下取自源码 flex.pymo.vstack( items, # Sequence[object]要堆叠的元素列表 *, align: start|end|center|stretch|None None, # 交叉轴对齐 justify: start|center|end|space-between|space-around start, # 主轴对齐 gap: float 0.5, # 元素间距单位 rem heights: equal|Sequence[float]|None None, # 等高或相对高度 ) - Html mo.hstack( items, *, justify: start|center|end|space-between|space-around space-between, align: start|end|center|stretch|None None, wrap: bool False, # 是否允许换行 gap: float 0.5, widths: equal|Sequence[float]|None None, # 等宽或相对宽度 ) - Html两个 API 的差异集中在两点默认对齐不同hstack默认justifyspace-between元素在行内两端分散排列vstack默认justifystart元素从顶部开始排列。能力侧重不同hstack独有wrap换行参数vstack始终不换行两者分别用widths水平堆叠时控制每列相对宽度与heights垂直堆叠时控制每行相对高度承担尺寸比例职责。从源码_flex实现flex.py可见它们最终共享同一套底层渲染逻辑区别仅是传入的direction、默认justify与wrap不同# 内部映射justify 参数 - CSS justify-content justify_content_map { start: flex-start, center: center, end: flex-end, space-between: space-between, space-around: space-around, None: space-between, } # 内部映射align 参数 - CSS align-items align_items_map { start: flex-start, center: center, end: flex-end, stretch: stretch, None: normal, }生成的容器 CSS 为display: flex; flex: 1; flex-direction: row|column; justify-content: ...; align-items: ...; flex-wrap: ...; gap: ...rem。测试 tests/_plugins/stateless/test_flex.py 对生成的 HTML 字符串做了精确断言可直接对照验证每个参数的渲染结果。二、参数详解与交互式调参示例原文档 stacks.md 提供了一个用mo.ui.dropdown、mo.ui.number、mo.ui.checkbox驱动布局参数的交互示例先用mo.Html生成一组橙色方块再用mo.hstack/mo.vstack实时预览justify、align、gap、wrap的效果。该示例正是理解本节参数语义的最佳实验台。2.1 items任意可渲染对象items接受任意序列list 等内部通过as_html(item)见 flex.py统一转换为 Html 输出——这意味着你传入的元素可以是mo.ui.*控件、mo.md文本、mo.Html片段、mo.stat统计卡片、matplotlib/plotly 图表甚至普通字符串。原文档示例用mo.Html构造方块def create_box(num1): box_size 30 num * 10 return mo.Html( fdiv stylemin-width: {box_size}px; min-height: {box_size}px; fbackground-color: orange; text-align: center; line-height: {box_size}px{str(num)}/div ) boxes [create_box(i) for i in range(1, 5)] # 4 个依次增大的橙色方块2.2 justify主轴对齐控制排列方向上的对齐方式取值与语义取值CSS 效果说明startflex-start沿主轴起点堆叠centercenter沿主轴居中endflex-end沿主轴终点堆叠space-betweenspace-between两端对齐元素间等距hstack默认space-aroundspace-around每个元素两侧等距环绕在hstack中它是水平方向在vstack中它是垂直方向。注意justify只有在容器有富余空间时才体现差异——例如hstack默认值space-between在行宽恰好被内容占满时与start效果相同。2.3 align交叉轴对齐控制垂直于排列方向上的对齐取值start、end、center、stretch以及默认值None渲染为 CSSalign-items: normal效果接近 stretch。在hstack中它是垂直对齐行内元素顶部、底部、居中对齐在vstack中它是水平对齐列内元素左、右、居中对齐。stretch让元素拉伸填满交叉轴空间是原文档外层vstack(..., alignstretch)让横向分隔线铺满整行的原因。2.4 gap间距元素间距单位是 rem1rem默认等于 16px见源码注释 flex.py接受浮点数默认0.5。原文档示例用mo.ui.number(start0, step0.25, stop2, value0.25)生成 02 之间步长 0.25 的 gap 取值可以直观看到间距从 0 到 2rem32px的渐变效果。该控件参数与 input.py 中mo.ui.number的start/stop/step/value签名一一对应。2.5 wrap仅 hstack是否换行hstack独有的布尔参数默认False。为True时当一行内容超出容器宽度元素会折行继续排列对应 CSSflex-wrap: wrap为False时强制单行不换行nowrap。原文档示例用mo.ui.checkbox(labelwrap)实时切换该行为。vstack没有此参数内部固定wrapFalse见 flex.py。2.6 widthshstack与 heightsvstack相对尺寸分配widths仅hstackequal表示所有元素等宽也可传与items等长的相对宽度列表如[1, 2]表示第二个元素宽度是第一个的两倍源码 docstring 示例 flex.pyNone为默认——按内容自适应。heights仅vstack语义完全对称equal表示等高列表如[1, 2]表示第二个元素高度是第一个的两倍。底层实现上equal会被展开为[1 for _ in range(len(items))]而数字列表会被逐项写入子元素的flex样式见 flex.py 与_item_styleflex.py。测试 test_flex.py 断言了widths[1, 2]与widthsequal生成的子元素styleflex: 1/flex: 2结构。小技巧在hstack中传widths[0, 1]可以让第一个元素按内容自适应flex: 0第二个元素填满剩余空间——这是源码 docstring 中实现一个元素撑满、另一个贴合内容的标准手法。三、完整交互示例实时调参预览布局原文档 stacks.md 的核心示例下方已整理为可直接运行的 notebook 代码把上面所有参数串成了一张控制面板 预览区import marimo as mo app marimo.App() app.cell def __(): def create_box(num1): box_size 30 num * 10 return mo.Html( fdiv stylemin-width: {box_size}px; min-height: {box_size}px; fbackground-color: orange; text-align: center; line-height: {box_size}px{str(num)}/div ) boxes [create_box(i) for i in range(1, 5)] return (boxes,) app.cell def __(boxes): justify mo.ui.dropdown( [start, center, end, space-between, space-around], valuespace-between, labeljustify, ) align mo.ui.dropdown( [start, center, end, stretch], valuecenter, labelalign ) gap mo.ui.number(start0, step0.25, stop2, value0.25, labelgap) wrap mo.ui.checkbox(labelwrap) return (align, gap, justify, wrap) app.cell def __(align, boxes, gap, justify, wrap): horizontal mo.hstack( boxes, alignalign.value, justifyjustify.value, gapgap.value, wrapwrap.value, ) vertical mo.vstack( boxes, alignalign.value, gapgap.value, ) mo.vstack( [ mo.hstack([justify, align, gap], justifycenter), # 控件面板水平居中 horizontal, # 水平堆叠预览 mo.md(-----------------------------), # 分隔线 vertical, # 垂直堆叠预览 ], alignstretch, # 让分隔线拉伸填满宽度 gap1, ) return示例结构的解读数据层create_box用mo.Html生成 4 个尺寸递增的橙色方块作为布局的最小单元控制层justify/align用mo.ui.dropdown枚举可选值gap用mo.ui.number在 02rem 区间以 0.25 步长调参wrap用mo.ui.checkbox开关——四个控件的.value被mo.hstack/mo.vstack直接消费实现拖动控件即实时重排的响应式演示布局层上方mo.hstack预览行内排列含换行开关下方mo.vstack预览纵向排列两者共同被外层mo.vstack装进同一张面板并用alignstretch让mo.md分隔线横贯整行、gap1拉开区块间距。这正是 marimo 响应式特性的直观体现布局参数来自 UI 控件状态控件值一变化依赖它的布局 cell 会自动重算重渲染。仓库中的可运行版本见 examples/outputs/stacks.py其中还额外演示了mo.hstack([t, n], justifystart)、widthsequal的mo.stat卡片行等场景。四、进阶组合用嵌套构建网格与仪表盘4.1 行列嵌套构建网格两个函数的 docstring 都明确指出Combine withhstackto build a grid of items与vstack组合构建网格。经典网格写法是vstack包多行hstack# 源码 docstring 示例flex.py 中 hstack/vstack 的 grid 示例 mo.vstack( [ mo.hstack([mo.md(...), mo.ui.text_area()]), # 第一行 mo.hstack([mo.ui.checkbox(), mo.ui.text(), mo.ui.date()]), # 第二行 ] )也可反过来用hstack包多个vstack形成纵向卡片列。examples/outputs/stacks.py中还有一例文本列 数字列的经典排版左列用mo.vstack([mo.md(text)] * 5)堆叠段落右列用mo.vstack(..., alignend, justifyspace-between)让数字顶端对齐最后用mo.hstack([q, s], widths[5, 1])按 5:1 的宽度比并排——一屏之内用到了vstack的对齐、hstack的宽度比例两种能力。4.2 嵌套时的实现细节flex 包装与实时渲染嵌套堆叠并非简单地把内层 HTML 塞进外层源码_FlexContainerHtmlflex.py做了两件关键事情保留嵌套 flex 语义当某个子元素本身是hstack/vstack产物_FlexContainerHtml实例且外层指定了widths/heights时内层会被包上display: flex; min-width: 0; min-height: 0的包装 div从而让内层自己的flex: 1与justify依然生效普通内容则用 block 包装以填满空间。对应测试 test_nested_stacks_preserve_flex_wrapper 精确断言了这两种包装的差异。子元素活引用_FlexContainerHtml保存的是子 Html 的实时引用每次访问.text都会重新构建 HTML 字符串_build_text。这意味着像mo.status.spinner这类会动态更新自身内容的可变更 Html 元素放在堆叠里也会随状态刷新而不是在构造时被冻结。回归测试 test_mutable_html_children_update_live 正是针对该行为对应 marimo issue #8618。五、快速上手清单行列基本用法mo.hstack([控件, 文本, 图表], justifystart)排一行mo.vstack([...], gap1)排一列元素自动经过as_html转换无需手动包装。等宽/等高mo.hstack(items, widthsequal)或mo.vstack(items, heightsequal)。按比例分配mo.hstack(items, widths[1, 2])、mo.vstack(items, heights[1, 2])widths[0, 1]可让某元素自适应、其余填满。间距与对齐gap以 rem 为单位微调间距justify/align分别控制主轴与交叉轴方向跨行换用wrapTrue仅 hstack。组合仪表盘vstack包hstack或反之逐层嵌套配合widths/heights比例即可拼出统计卡片行、控件面板、图文并排等常见排版嵌套比例布局由_FlexContainerHtml自动保证内层 flex 语义。交互调参把justify/align/gap/wrap绑定到mo.ui.dropdown、mo.ui.number、mo.ui.checkbox的.value上即可得到原文档同款实时预览面板。以上全部行为均可在 flex.py 源码、test_flex.py 测试及 examples/outputs/stacks.py 示例中找到可运行、可验证的依据。【免费下载链接】marimoA reactive notebook for Python — run reproducible experiments, query with SQL, execute as a script, deploy as an app, and version with git. Stored as pure Python. All in a modern, AI-native editor.项目地址: https://gitcode.com/GitHub_Trending/ma/marimo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考