布局自定义
在 Potato 中用 HTML 模板和 CSS 构建自定义标注布局,涵盖并排对比、多列 scheme 和项目级样式。
v2.1.0 新增
Potato 提供两种自定义标注界面布局的方式:
- 自动生成布局:Potato 生成一个 HTML 布局文件,你可以编辑它
- 自定义布局文件:创建你自己的 HTML 模板,完全掌控样式
快速开始
使用自动生成的布局
- 运行一次服务器,Potato 会写出
layouts/task_layout.html - 编辑生成的文件来自定义样式
- 你的修改在服务器重启后依然保留(除非你修改了配置中的
annotation_schemes)
编辑 annotation_schemes 会重新生成该文件,因为表单标记必须与它收集的 scheme 相匹配。如果某个布局你想保留,请在改动 scheme 之前把它另存为自己的文件名。
使用自定义布局文件
- 创建你的布局文件(例如
layouts/custom_task_layout.html) - 在配置中引用它:
task_layout: layouts/custom_task_layout.html该路径相对于配置文件所在目录解析,因此与配置放在一起的布局文件会随项目一同迁移。
项目级基础 CSS
布局文件只负责标注页面的样式。若要为项目提供的每一个页面设置样式,包括知情同意页和说明页,请用 base_css 指向一个样式表:
base_css: styles/custom.cssPotato 在服务器启动时读取该文件,并把它作为 <style id="potato-project-base-css"> 块注入每个页面的 <head> 中,位置在所有内置样式之后,因此你的规则优先生效。修改后需要重启服务器;文件缺失只会记录一条警告,不会让服务器停止。
布局文件结构
自定义布局文件必须包含:
<style>
/* Your custom CSS */
</style>
<div class="annotation_schema">
<!-- Your annotation forms -->
<form id="schema_name" class="annotation-form radio" data-annotation-id="0">
<fieldset schema="schema_name">
<legend>Question text</legend>
<!-- Input elements -->
</fieldset>
</form>
</div>必需的表单属性
Potato 通过以下四个属性把标记与你的配置对应起来,其中任何一个不匹配,标注就到不了输出文件:
| 属性 | 描述 |
|---|---|
id | 必须与配置中 annotation_schemes 的 name 一致 |
class | 包含 annotation-form 和类型(如 radio、multiselect) |
data-annotation-id | 顺序索引(0, 1, 2...) |
schema | fieldset 和 input 上的属性,与 scheme 名称一致 |
必需的输入属性
<input class="schema_name annotation-input"
type="radio"
name="schema_name"
value="label_value"
schema="schema_name"
label_name="label_value"
onclick="onlyOne(this);registerAnnotation(this);">示例布局
Potato 附带三个完整的示例布局。下面的命令要在克隆下来的 Potato 仓库根目录下运行,因为这些配置及其数据文件都是该 checkout 的一部分。若要用你自己的配置运行自己的布局,请改用 potato start <your-config>.yaml -p 8000。
1. 内容审核仪表板
位置:examples/custom-layouts/content-moderation/
包含带内容元数据的警告横幅头部、违规类别的双列网格、按颜色编码的严重程度级别,以及一条审核工作流。
python potato/flask_server.py start examples/custom-layouts/content-moderation/config.yaml -p 80002. 客服对话质量评估
位置:examples/custom-layouts/dialogue-qa/
包含带元数据徽章的案例头部、分组的评估区块、圆形 Likert 量表评分、质量问题清单,以及按颜色编码的解决状态指示。
python potato/flask_server.py start examples/custom-layouts/dialogue-qa/config.yaml -p 80003. 医学影像审阅
位置:examples/custom-layouts/medical-review/
包含医疗风格的 UI 样式、位置与严重程度的双列布局、分组的检查结果区块、结构化报告,以及带说明的建议卡片。
python potato/flask_server.py start examples/custom-layouts/medical-review/config.yaml -p 8000CSS 技巧
网格布局
创建多列布局:
.annotation-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px;
}
.full-width {
grid-column: 1 / -1;
}
@media (max-width: 768px) {
.annotation-grid {
grid-template-columns: 1fr;
}
}按颜色编码的选项
用表示严重程度的颜色来设置单选按钮的样式:
.severity-option input[type="radio"] {
position: absolute;
opacity: 0;
}
.severity-label {
display: block;
padding: 10px;
border-radius: 6px;
border: 2px solid transparent;
cursor: pointer;
transition: all 0.2s;
}
/* Green for "None" */
.severity-none .severity-label {
background: #dcfce7;
color: #166534;
}
.severity-none input:checked + .severity-label {
background: #22c55e;
color: white;
}
/* Red for "Severe" */
.severity-severe .severity-label {
background: #fee2e2;
color: #991b1b;
}
.severity-severe input:checked + .severity-label {
background: #ef4444;
color: white;
}隐藏原生 input 而对它的 label 设置样式,可以让整个彩色区块都能点击,当选项集是网格而不是竖排列表时这一点尤其重要。
区块样式
创建视觉分组:
.annotation-section {
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 8px;
padding: 16px;
margin-bottom: 16px;
}
.section-title {
font-size: 13px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 2px solid #3b82f6;
}圆形 Likert 评分
圆点一眼就能读作一个量表,而且比一摞单选行占用更少的垂直空间,这能让多问题表单保持在一屏之内:
.likert-circle {
width: 36px;
height: 36px;
border-radius: 50%;
border: 2px solid #e2e8f0;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.likert-option input:checked + .likert-circle {
background: #8b5cf6;
color: white;
border-color: #7c3aed;
}与实例显示结合
自定义布局可以和 instance_display 配置一起使用。实例内容(图像、文本、对话)在标注表单上方单独渲染,因此布局文件控制的是问题,instance_display 控制的是被标注的材料。
instance_display:
fields:
- key: image_url
type: image
display_options:
zoomable: true
task_layout: layouts/custom_task_layout.html
annotation_schemes:
- annotation_type: radio
name: category
description: "Which category does this image belong to?"
labels: [A, B, C]最佳实践
- 让 scheme 名称对上:表单
id必须与annotation_schemes中的name完全一致 - 顺序的标注 ID:
data-annotation-id使用 0, 1, 2... - 带上必需的处理函数:单选用
onclick="onlyOne(this);registerAnnotation(this);",复选框用onclick="registerAnnotation(this);" - 测试响应式:用媒体查询支持移动端
- 保持可访问性:使用规范的 label,并保留键盘导航
故障排除
标注没有保存
检查:
- 表单
id是否与标注 scheme 的name一致 - input 是否带有
schema和label_name属性 - 点击处理函数(
registerAnnotation)是否存在
样式没有生效
- 确认 CSS 优先级足够高,能覆盖默认样式
- 检查
<style>块是否位于布局文件内部 - 用浏览器开发者工具查看实际生效的样式
布局没有加载
- 确认
task_layout中的路径是相对于配置文件的 - 检查 HTML 语法错误
- 查看服务器日志中的错误信息
延伸阅读
有关实现细节,请参阅源代码文档。