Skip to content

布局自定义

在 Potato 中用 HTML 模板和 CSS 构建自定义标注布局,涵盖并排对比、多列 scheme 和项目级样式。

v2.1.0 新增

Potato 提供两种自定义标注界面布局的方式:

  1. 自动生成布局:Potato 生成一个 HTML 布局文件,你可以编辑它
  2. 自定义布局文件:创建你自己的 HTML 模板,完全掌控样式

快速开始

使用自动生成的布局

  1. 运行一次服务器,Potato 会写出 layouts/task_layout.html
  2. 编辑生成的文件来自定义样式
  3. 你的修改在服务器重启后依然保留(除非你修改了配置中的 annotation_schemes)

编辑 annotation_schemes 会重新生成该文件,因为表单标记必须与它收集的 scheme 相匹配。如果某个布局你想保留,请在改动 scheme 之前把它另存为自己的文件名。

使用自定义布局文件

  1. 创建你的布局文件(例如 layouts/custom_task_layout.html)
  2. 在配置中引用它:
yaml
task_layout: layouts/custom_task_layout.html

该路径相对于配置文件所在目录解析,因此与配置放在一起的布局文件会随项目一同迁移。

项目级基础 CSS

布局文件只负责标注页面的样式。若要为项目提供的每一个页面设置样式,包括知情同意页和说明页,请用 base_css 指向一个样式表:

yaml
base_css: styles/custom.css

Potato 在服务器启动时读取该文件,并把它作为 <style id="potato-project-base-css"> 块注入每个页面的 <head> 中,位置在所有内置样式之后,因此你的规则优先生效。修改后需要重启服务器;文件缺失只会记录一条警告,不会让服务器停止。

布局文件结构

自定义布局文件必须包含:

html
<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...)
schemafieldset 和 input 上的属性,与 scheme 名称一致

必需的输入属性

html
<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/

包含带内容元数据的警告横幅头部、违规类别的双列网格、按颜色编码的严重程度级别,以及一条审核工作流。

bash
python potato/flask_server.py start examples/custom-layouts/content-moderation/config.yaml -p 8000

2. 客服对话质量评估

位置:examples/custom-layouts/dialogue-qa/

包含带元数据徽章的案例头部、分组的评估区块、圆形 Likert 量表评分、质量问题清单,以及按颜色编码的解决状态指示。

bash
python potato/flask_server.py start examples/custom-layouts/dialogue-qa/config.yaml -p 8000

3. 医学影像审阅

位置:examples/custom-layouts/medical-review/

包含医疗风格的 UI 样式、位置与严重程度的双列布局、分组的检查结果区块、结构化报告,以及带说明的建议卡片。

bash
python potato/flask_server.py start examples/custom-layouts/medical-review/config.yaml -p 8000

CSS 技巧

网格布局

创建多列布局:

css
.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;
    }
}

按颜色编码的选项

用表示严重程度的颜色来设置单选按钮的样式:

css
.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 设置样式,可以让整个彩色区块都能点击,当选项集是网格而不是竖排列表时这一点尤其重要。

区块样式

创建视觉分组:

css
.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 评分

圆点一眼就能读作一个量表,而且比一摞单选行占用更少的垂直空间,这能让多问题表单保持在一屏之内:

css
.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 控制的是被标注的材料。

yaml
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]

最佳实践

  1. 让 scheme 名称对上:表单 id 必须与 annotation_schemes 中的 name 完全一致
  2. 顺序的标注 ID:data-annotation-id 使用 0, 1, 2...
  3. 带上必需的处理函数:单选用 onclick="onlyOne(this);registerAnnotation(this);",复选框用 onclick="registerAnnotation(this);"
  4. 测试响应式:用媒体查询支持移动端
  5. 保持可访问性:使用规范的 label,并保留键盘导航

故障排除

标注没有保存

检查:

  • 表单 id 是否与标注 scheme 的 name 一致
  • input 是否带有 schema 和 label_name 属性
  • 点击处理函数(registerAnnotation)是否存在

样式没有生效

  • 确认 CSS 优先级足够高,能覆盖默认样式
  • 检查 <style> 块是否位于布局文件内部
  • 用浏览器开发者工具查看实际生效的样式

布局没有加载

  • 确认 task_layout 中的路径是相对于配置文件的
  • 检查 HTML 语法错误
  • 查看服务器日志中的错误信息

延伸阅读

有关实现细节,请参阅源代码文档。