| name | uzoncalc-writer |
| description | 使用 UzonCalc 编写 Python 工程计算书,直写可审查的公式推导、验算和结论,并生成自动排版的 HTML 文档。用于创建或修改以 Python 表达工程计算过程的计算书。 |
UzonCalc 工程计算书
UzonCalc 是基于 Python 的工程计算书生成工具。在 @uzon_calc() 装饰的 async def 入口中逐步编写计算逻辑时,UzonCalc 会捕获赋值、公式和文本,并渲染为带目录、数学公式、表格和图表的 HTML 计算书。
最小模板示例
from uzoncalc import *
@uzon_calc()
async def sheet():
doc_title("计算书标题")
H1("示例章节")
"这是一段说明文字。"
a = 10 * unit.meter
b = 5 * unit.meter
c = a + b
if __name__ == "__main__":
view(sheet)
核心规则
| 规则 | 说明 |
|---|
入口必须为异步 async def | @uzon_calc() 仅支持异步函数。 |
| 字符串字面量即段落 | 入口内裸字符串 "文本" / """多行""" 会作为段落输出。 |
| 赋值自动渲染 | x = expr 会渲染为 x = expr = 值;默认同时展示原式、代入式和结果。 |
| 裸函数调用不生成公式 | f(x) 只执行副作用;x = f(x) 会展示调用表达式,但不会自动展开 f 的函数体。 |
@uzon_calc_func 会记录函数体 | 它适合明确需要复用且展示的计算片段,不应用来拆分本应连续展示的工程推导。 |
| 变量名用 camelCase | _ / ^ 会被解析为下标/上标;工程符号、中文和复杂脚标使用 alias()。 |
| 希腊字母自动转换 | alpha 转为 α,Beta 转为 Β。 |
编写要求
- 以手写计算书的顺序组织:说明文字、输入参数、中间量、控制条件、验算和结论。
- 将所有需要审查的工程公式、分支判断和验算结果直接写在
@uzon_calc() 入口中;不得封装为一系列计算函数。
- 每个公式前说明物理或规范含义;控制条件和分支后立即写出采用的路径与校核结论。
- 仅将数据解析、外部 I/O、绘图准备、格式化和数值迭代等非工程推导封装为普通辅助函数;辅助函数不能成为工程公式的唯一表达。
- 所有逻辑保存在单个 Python 文件中,方便分发;内部变量使用简短 camelCase,报告中的符号优先通过
alias() 表达。
推导优先
工程计算书的可读性优先于代码复用。不要把截面面积、内力、应力、承载力或验算判断收进 calculate_xxx() 等函数,再只在入口显示一次函数调用;读者必须能在文档中连续看到每一步的公式和代入关系。
def calculate_stress(force, area):
return force / area
stress = calculate_stress(force, area)
H2("截面应力验算")
"截面面积:"
area = width * height
"平均正应力:"
stress = force / area
"应力限值校核:"
isStressSafe = stress <= allowableStress
f"验算结论:{'满足' if isStressSafe else '不满足'}应力限值。"
- 保持
enable_formula_expression() 的默认开启状态。不要用 disable_formula_expression()、hide() 或只显示结果的 f-string 替代需要展示的公式。
- f-string 适合参数说明、结论和行内引用;核心推导仍使用独立赋值语句。
- 数值迭代可隐藏实现,但入口必须写出控制方程、已知条件、收敛或分支依据,以及迭代结果参与的最终验算。
- 技术辅助函数优先定义在计算入口之外;不要使用嵌套函数或
@uzon_calc_func 将一条连续推导切成多个展示片段。
hide() 到 show() 之间的语句不会插桩。只用它隐藏不应进入计算书的技术细节,不能隐藏工程计算步骤。
编写规范
文档结构
用于定义文档的结构和布局。
doc_title("页眉标题")
page_size("A4")
toc("目录")
font_family("Arial")
head("meta", {"name": "author", "content": "UzonCalc"})
style("body", {"line-height": "1.8"})
H1("一级标题")
H2("二级标题")
H3("三级标题")
Br()
Info("提示信息")
Code("代码", "python")
P("段落")
HTML 元素
可以使用以下预定义的函数,简化 HTML 内容的生成。其中,以小写字母开头的函数会返回 HTML 结果,而以大写字母开头的函数会直接渲染到文档中。
P(
div(
[
h2("截面参数"),
p("以下参数用于承载力验算。"),
span("重要", classes="text-red-600 font-bold"),
],
classes="border p-2",
)
)
H1("设计依据")
P("本节列出主要设计参数。")
Div(
[
h3("材料"),
p("混凝土强度等级为 C50。"),
],
classes="bg-gray-50 p-3",
)
常用 HTML 元素如下:
| 函数 | 说明 |
|---|
h(tag, children, ...) | 通用 HTML 构造器,children 支持字符串或字符串列表 |
h1h6 / H1H6 | 标题元素,小写返回字符串,大写直接渲染 |
title / Title | 文档大标题,默认居中、加粗、大字号 |
subtitle / Subtitle | 文档副标题,默认居中、加粗 |
p / P | 段落 |
div / Div | 块级容器 |
span / Span | 行内容器 |
row / Row | 行容器,默认渲染为 div,可通过 tag 指定标签 |
br / Br | 换行,自闭合元素 |
img / Img | 图片,支持 alt、width、height |
input / Input | 输入元素,当前主要用于生成带 value 的 HTML 输入标签 |
code / Code | 代码块,language 会生成 language-python 等高亮类名 |
info / Info | 蓝色提示框 |
laTex / LaTex | 原始 LaTeX 内容标签 |
plot | 将 Matplotlib savefig 对象转为内嵌 PNG,返回 HTML 字符串 |
Plot | 直接插入带图号的图片;支持 savefig 对象和 PNG 二进制内容 |
属性与样式使用 classes 和 props():
P("控制性参数", props=props(id="control-params"))
Div("验算通过", classes="text-green-700 font-bold")
P(
"带自定义属性的段落",
props=props(
id="note-1",
classes="text-sm",
styles={"color": "#444", "margin-top": "8px"},
data_value="section-note",
aria_label="说明段落",
),
)
Div(
"最终使用 text-blue-700",
classes="text-blue-700",
props=props(classes="text-red-700"),
)
复杂 HTML 直接以小写函数组合后传给大写渲染函数,避免在计算书中记录无业务意义的 HTML 构造赋值:
P(
div(
[
h3("几何参数"),
p(f"梁高:{beamHeight}"),
p(f"梁宽:{beamWidth}"),
info("单位统一采用 SI 制。"),
],
classes="my-3 p-3 border",
)
)
插入图片、代码、LaTeX 与 Matplotlib 图:
Img("assets/section.png", alt="截面示意图", width=480)
Code(
"""
stress = force / area
""",
language="python",
)
LaTex(r"\sigma = \frac{N}{A}")
import matplotlib.pyplot as plt
hide()
fig, ax = plt.subplots()
ax.plot([0, 1, 2], [0, 1, 4])
show()
Plot(fig, width=520)
注意:
- HTML 内容不会自动转义
children,用户输入或外部文本应先自行清洗后再拼入 HTML。
children 列表会按顺序直接拼接,适合嵌套由 p()、div()、span() 等返回的 HTML 片段。
img() 返回居中图片 HTML;Img() 直接插入带图号的图片,并将 alt 作为图注。
Table()、Img()、Plot() 和 EChart() 都会插入内容并返回可用于后续正文引用的占位符。
Plot() 支持 Matplotlib savefig 对象和 PNG 二进制数据;普通文件路径图片使用 Img()。
变量与公式
变量名参考 linux 风格缩写,变量名不宜过长。普通 Python 变量优先使用 camelCase;需要工程符号、中文、上下标时,用 alias() 设置展示名。
force = 100 * unit.newton
area = 2 * unit.meter**2
stress = force / area
alias("rhoWater", "rho_水")
rhoWater = 1000 * unit.kilogram / unit.meter**3
alias("sigmaMax", "sigma_{max}^2")
sigmaMax = 30 * unit.MPa
alias("aPrimeP0", "a^'_p0")
aPrimeP0 = 12 * unit.meter
alias("rhoWater", None)
f"应力为 {stress}。"
enable_fstring_equation()
f"应力为 {stress}。"
disable_fstring_equation()
f"面积 = {(A := 3 * unit.meter**2)}"
alias("变量名", "别名") 只替换展示名称;替换后的 _ 和 ^ 会继续由上下标后处理器渲染。未分组脚标读取一个连续 token,复杂脚标使用 {} 分组,例如 M_{max}、x^{n+1}、x_i^2。若需要保留原始 _、^ 或希腊字母名称,在字符前加反斜杠转义,例如 r"\alpha"、r"x\_raw"。常规工程推导保持 enable_formula_expression() 开启;仅在确实只需代入式和结果的展示场景才关闭它。
单位
from uzoncalc import unit
l = 5 * unit.meter
f = 100 * unit.newton
p = f / (l * l)
v = l.to(unit.centimeter)
控制渲染
hide()
show()
inline()
x = 1; y = 2
end_inline()
enable_substitution()
disable_substitution()
enable_formula_expression()
disable_formula_expression()
decimal(2)
figure_prefix("图")
table_prefix("表")
表格
Table(
headers=[
[
th("构件", rowspan=2),
th("材料", rowspan=2),
th("弹性模量 (MPa)", colspan=2),
],
["Ec", "Es"],
],
rows=[
["盖梁", "C60", 36000, 34500],
["墩柱", "C50", 34500, 32500],
],
title="材料参数",
)
Table(
headers=["项目", "数值", "备注"],
rows=[
Tr(
[
Td("梁高", classes="font-bold"),
1.8 * unit.meter,
Td("控制参数", classes="text-red-600"),
],
classes="bg-yellow-50",
),
Tr(["混凝土等级", "C50", None]),
],
)
Table(
headers=["名称", "值"],
rows=[Td("宽度", classes="font-bold"), 2.5 * unit.meter],
)
图表
图表数据准备不是工程推导,使用 hide() 隐藏;图表本身直接插入计算书。
静态图首先使用 svg 方式,交互式图表使用 echarts 方式。
配色
进行图表配色时,优先选择以下配色方案:
- primary: #7367f0;
- secondary: #42b883;
- dark-page: #e0e0e0;
- positive: #42b883;
- negative: #ff7a7a;
- info: #65a0bb;
echarts 图表
通过 echarts 生成交互式图表, 在使用中,若对 echarts 中的参数不确定,读取 https://echarts.apache.org/zh/option.html 读取文档
from uzoncalc.extension.echarts import EChart, Javascript
EChart({
"xAxis": {"type": "category", "data": ["Mon", "Tue", "Wed"]},
"yAxis": {"type": "value"},
"series": [{"type": "bar", "data": [120, 200, 150]}],
})
EChart({...}, use_gl=True)
svg
对于一般图示,建议使用 svg.py, 示例如下:
import svg
hide()
canvas = svg.SVG(
width=60,
height=60,
elements=[
svg.Circle(
cx=30, cy=30, r=20,
stroke="red",
fill="white",
stroke_width=5,
),
],
)
show()
P(canvas)
Matplotlib 图表
也可以使用 Matplotlib 生成静态图表
import matplotlib.pyplot as plt
hide()
fig, ax = plt.subplots()
ax.plot([1, 2, 3], [4, 5, 6])
show()
Plot(fig)
UI 输入(交互模式)
用 UI() 定义输入窗口,用 Field() 定义字段。计算脚本不需要写前端代码;前端会把字段定义交给 LowCodeForm 渲染,并在用户确认后把当前窗口的输入值返回给 Python。UI(..., caption="说明") 可为当前窗口添加底部说明。
from uzoncalc import UI, Field, FieldType
inputs = await UI(
"结构参数",
[
Field("width", "宽度", FieldType.number, value=10),
Field("height", "高度", FieldType.number, value=5),
Field("mat", "材料", FieldType.selectOne, options=["C30", "C40", "C50"]),
],
)
width = inputs.width * unit.meter
FieldType 可选值:text、number、selectOne、selectMany、boolean、textarea。当前 Python Field 只暴露以下控制项:
| 参数 | 说明 |
|---|
name | 返回值字段名,必须是适合属性访问的标识符,例如 inputs.width |
label | 前端显示标签 |
type | 输入类型,默认 FieldType.text |
placeholder | 占位提示文本 |
value | 默认值;静默执行时直接作为输入值 |
options | selectOne / selectMany 的选项,当前优先使用字符串列表 |
visible | JS 函数字符串,控制字段是否显示 |
onChanged | JS 函数字符串,字段值变化时执行,可联动修改其它字段值 |
字段类型选择规则:
- 数值输入用
FieldType.number,前端会按数字处理;进入工程计算时仍要显式乘单位。
- 单选用
FieldType.selectOne,多选用 FieldType.selectMany,通过 options=["C30", "C40"] 给选项。
- 开关/是否类输入用
FieldType.boolean。
- 长文本输入用
FieldType.textarea。
字段联动通过 visible 和 onChanged 完成,二者必须写成完整的 JS 函数字符串:
inputs = await UI(
"截面参数",
[
Field("useAdvanced", "启用高级参数", FieldType.boolean, value=False),
Field(
"extraDepth",
"附加高度",
FieldType.number,
value=0,
visible="(values) => values.useAdvanced === true",
),
Field("width", "宽度", FieldType.number, value=2.0),
Field("height", "高度", FieldType.number, value=1.5),
Field(
"area",
"面积",
FieldType.number,
value=3.0,
onChanged="(value, oldValue, values) => { values.area = values.width * values.height }",
),
],
)
visible 的参数 values 是当前输入窗口的表单值对象,key 来自每个 Field.name,例如 values.useAdvanced、values.width。visible 应只读取 values 并返回真假值,不要写副作用。
onChanged 的参数依次为 value、oldValue、values、fields:
value 是当前字段的新值。
oldValue 是当前字段变化前的旧值。
values 是当前输入窗口的表单值对象,可通过 values.xxx = ... 联动修改其它字段值。
fields 是当前窗口的字段定义列表,通常不需要使用。
注意:
- 不要把
visible / onChanged 写成表达式片段,例如不要写 "values.enabled === true";应写成 "(values) => values.enabled === true"。
visible 编译失败时前端会默认显示该字段;onChanged 编译失败时前端会移除该回调。
- 多个
await UI(...) 会按执行顺序生成多个输入窗口,每个窗口返回自己的字段值。
- 前端
LowCodeForm 还支持更多属性,但 Python Field 当前未暴露 required、validate、parser、optionLabel、optionValue、emitValue、tooltip、disable、classes 等参数,编写计算书时不要使用这些未暴露接口。
Excel 集成
from uzoncalc.extension.excel import get_excel_table
P(get_excel_table(
excel_path="examples/calc.xlsx",
values={
"Sheet1!A2": 6,
"Sheet1!B2": 10,
},
range="Sheet1!A1:C3",
))
传入 values 时,Excel 集成会写入这些单元格并保存工作簿。需要保留源文件时,先对工作簿副本执行该调用。
保存文档
ctx = run_sync(sheet)
ctx.save("output/result.html")
生成的 HTML 可用浏览器打印为 PDF,或用 pandoc 转为 Word。
运行方式
if __name__ == "__main__":
ctx = run_sync(sheet)
ctx.save("output.html")
ctx1 = run_sync(sheet1)
ctx2 = run_sync(sheet2)
ctx1.save("out1.html")
ctx2.save("out2.html")
注意要点
- 入口中的赋值会被渲染;裸函数调用只保留副作用,赋值形式的调用不会自动展开函数体
@uzon_calc_func 会记录辅助函数内部步骤,因此不要用它拆分本应连续展示的工程推导
hide() / show() 按词法顺序控制插桩,只隐藏技术细节
- 变量名、别名和普通文本中的
_ / ^ 会渲染为下标/上标,命名时应使用 camelCase
- 复杂上下标使用
{} 分组,组合脚标可写成 x_i^2、M_{max}^{n+1}
- 数组下标
arr[0, 1] 自动渲染为下标形式
@uzon_calc() 函数支持相互嵌套调用,内层函数的内容合并到当前上下文
- 单位计算依赖 pint,量纲不匹配时会抛出错误