# 仪表画面编辑器

> 编辑背后的那张工作台：面板与添加组件、组件定位列表、选择与画布手势、每一种类型共有的属性分区、仪表画面区、在图元连接点之间画出来的连线，以及这一切背后的自动保存。

在 [View](home) 页面舞台的命令栏上按**编辑**，就在打开的那幅画面上打开编辑器。它需要角色的**编辑仪表画面**权限（没有这项权限时这个动词根本不画出来）、一幅你自己的画面（单元面板和单元画面是在 Process 工作区里排布的），还需要一台接受写入的工作站：工程锁定期间或者运行时停止期间，这个动词变灰并说明为什么。编辑期间这幅画面照常运行：各个组件在画布上读取实时值，于是你是对着现场正在读到的东西来配置的。

这里没有保存这一步。每一处改动在做出来的那一刻就保存下来：字段在你离开它时，开关在你拨动它时，一次移动在你放手时。工作站拒绝的写入会在状态栏的操作记录条里报出来（“仪表画面的修改没有保存：…”），画面则从已经保存下来的内容重新加载。**结束编辑**离开这个模式，而且从不被拒绝，即使一把锁在编辑中途到来也一样；在栏里挑中一块单元面板或者一个文件夹、退出登录、角色变更、某个单元开始一次运行，或者运行时停止，也都会结束它。编辑器打开期间，命令栏把展开和全屏收起来，按 F11 也什么都不做。

## 这张工作台 {#the-workbench}

编辑器把三块界面并排摆开：可视化栏、带网格线的画布，以及右边的一条工具栏。工具栏打开时宽 380 px，可以拖宽或者拖窄（绝不小于 320 px），也可以收成一条只有四个按钮的窄条；按一下那条窄条，所选的那个区就浮在画布上，直到你在别处按一下，而图钉把固定的工具栏放回来。四个区是**添加**、**组件**、**属性**和**仪表画面**。进入编辑器时打开的是组件；在画布上选中任何东西会打开属性；仪表画面只在你选中它时才打开。

## 添加 {#add}

**添加**是那份目录：一个按名称、说明和类别的搜索框，一个**类别**筛选（全部类别，然后是显示、控制、报警、媒体、布局），以及归在那些抬头底下的各种类型（[仪表画面组件](components)里每种类型一页）。点一下某种类型，它就落在第一个放得下它默认大小的空白格上，一个空白格也不剩时则落在左上角并被限制在网格内；把它拖到画布上则由你选定具体的格子。新组件取的是高于其他一切的图层，并且在属性里打开。

**图元**是唯一一种在放置之前就先选定的类型：它在面板里那一行打开图元库，而不是直接放下什么。这个库和[图元](assets-symbols)页面所用的是同一个图库，只是排成两列、每页十二张卡片：一个按名称、库、类别或者关键词的搜索框，一个**素材库**筛选（各带自己的数量），一个**类别**筛选，还有一个**来源**筛选（全部、系统、我的）。一张卡片写出这个图元的名称、它的库和类别、它是系统的还是我的，以及它声明了什么（“静态”“1 个输入”“3 个输入”“· 2 个连接点”）。点一下卡片，就按它作者建议的大小把那个图元放到一个空白格里，或者把它拖到某个格子上；**‹ 全部组件**回到那份目录。你自己的库读不出来时，图库会说明这一点，并且只列出系统的那些库。

在图元旁边，媒体类别还提供**连线锚点**：“不添加图元，直接放置一个有名称、有类型的端点。”它在网格中央附近添加一个独立的、没有类型的连线锚点，等着被接线；见下面的[连线](#connections)。

## 组件 {#components}

**组件**列出这幅画面上每一个已保存的项目，从前到后，抬头里带着数量。一行显示这一类型的图标、组件的标识、它的类型和它的位置（“网格 3,2 · 4×2 · 图层 5”）。搜索匹配类型、名称或者标识。画面上一个组件也没有时，不论搜索里写着什么，这里都写着“这个画面还没有组件。请从‘添加’里放置。”；搜索把画面上的组件全都排除时，写着“没有符合这次搜索的组件。”按下一行就选中这个组件，在画布上把它显出来，并打开属性。这个版本没有渲染器的组件仍旧有它的行和它的格子，于是一幅画面绝不会一声不响地缺一块；它的属性里只提供**删除组件**。

标识是你在属性里给这个组件起的名字，而在它还没有名字的时候，则是那些已经足以认出它的东西：它的角上标题、它配置好的文本或者标注、它绑定的地址或者单元、它所画的图元，或者它的类型。

## 画布 {#the-canvas}

画布就是那幅运行中的画面，加上网格线。点一下选中一个组件并打开它的属性；**Ctrl + 点击**把它加进选择或者再拿出来（最后碰到的那个是主组件，按类型划分的那些分区和那个改变大小的手柄属于它）；点在空白网格上则清除选择。拖动一个组件来移动它，拖动它角上的手柄来改变它的大小；两者都吸附到格子上，都被限制在网格内，并在你放手时提交。允许重叠。Esc 放弃选择，Delete 删除它，但只有在画布持有键盘焦点时才这样，于是在属性字段里的一次击键绝不会传到它们那里。

在一个组件上右击打开它的菜单：**编辑属性**、**创建副本**、**移到最前**、**移到最后**，在图元上还有**旋转 90°**、**水平翻转**和**垂直翻转**，以及**删除**。在多选的情况下，这些动词写出自己的数量（“复制（3）”“删除（3）”）并对整批生效。在空白网格上右击则是**添加组件**，它打开添加区。这里没有剪贴板，也没有粘贴：复制就是那份拷贝。

删除立即生效，并在状态栏里报出来，旁边带着**撤销**：只有一级，就是最后那一次删除，只要那条记录还在屏幕上就有效。撤销把那些组件连同它们原有的连接点一起放回去，并说出有什么回不来了（“有 1 条连线留成了游离端：它的走线在删除之后发生了变化。”）。一条它所属画面已经不再打开的撤销记录会答复“删除没有撤销：它所属的画面已经不再打开。”

在编辑画布上，各个组件画出实时值，但不接受按下：这些控件只在查看模式下作答。

## 属性 {#properties}

**属性**编辑当前的选择。它的抬头写出手上的是什么：这一类型的图标和名称，底下是这个组件的标识。什么也没选中时它写着“请在画布上选择一个组件、一条连线、一个连线锚点或一个交汇点。按住 Ctrl 可以多选，编辑它们共有的属性。”

不管是哪一种类型，这些分区都按同一个顺序排列，于是你换一个组件时，同一件事仍在它原来的位置上：

1. **标识**：组件的**名称**，它在组件列表里、在那份搜索里和在这个抬头里为它命名，在画面上什么也不画。字段空着时，占位文字显示的就是那份列表会怎么称呼它。
2. 这一类型自己的那些分区，顺序是**内容**、**数据**、**格式**，然后是只有这一类型才问的那些分区（字体、表盘、指示、迷你图、进度条、状态、报警、选项、行程、步长、数字、动作、颜色与图标、颜色与大小、发生记录、显示的列、图表、判定、柱子、扇形、适配、连线、朝向），再然后是**历史**、**刻度**、**保真与性能**和**工具栏**。每一个都写在这种组件自己的页面上；在各种类型之间重复出现的那些控件则写在下面。
3. **外观**，每一种类型都是同一个分区。
4. **位置与图层**。
5. **高级**，默认折叠，而且只画在带有不常用附加项的类型上：指标卡的**窗口（s）**和**采样间隔（ms）**，图标的**字形代码（高级）**。
6. **动作**：**创建组件副本**和**删除组件**。

选中多个组件时，面板写着“已选中 N，正在编辑它们共有的属性。按类型划分的字段需要只选一个组件。”（同一种类型的一批会连类型一起写出来，“3 个数值组件”），并提供外观、那两条图层命令，以及**复制 N 个组件** / **删除 N 个组件**。一次批量写入对整幅画面来说是一次保存。

### 地址字段与实时读数 {#address-fields-and-the-live-reading}

每一个指名 Logic 点位的字段都是一个可以自由键入的组合框，建议取自实时点位清单，最多 50 行。列表按这个字段所消费的值类别收窄（仪表盘拿到的是数值，指示灯把布尔点位排在前面，写入类控件只列出可写的东西，按钮的运行命令列出的是单元命令点位），每一行都用提示带着自己的类型。列表没有给出的地址照样接受，包括一个还不存在的地址。在基于 Process 模型定义的画面上，模型的抽象通道排在每一份列表的最前面；选中一条，绑定的就是这条通道，至于是哪个单元，留到某个单元打开这幅画面时再回答。

字段一旦指名了某个点位，它下面就有一行说明这个点位此刻读到什么，由**此刻**这个词领头：值按工作站书写数字的方式写出来，而在把文字读出来说明不了什么的地方，写的是那样东西本身：颜色到来时是它十六进制值旁边的一块色块，图标是那个字形，图片是一张缩略图。不作答的点位会用柔和的注意色说出这一点，而不是留着一个陈旧的数字；字段空着时则根本不画这一行。这一行跟着编辑器自己的节拍走，不另外加自己的定时器。

### 外观 {#appearance}

一个分区装着每个组件都有的东西，对一个组件和对整份选择都一样（“对选中的全部 N 个组件生效。”）。成员之间取值不一致的字段写着**不一致**，而你输入的值会被它们全体采用。

| 字段 | 作用 | 取值 | 默认 |
|---|---|---|---|
| 显示边框 | 画出组件自己的轮廓。 | 开 / 关 | 关 |
| 边框粗细（px） | 这道轮廓有多重。1 px 是一张卡片的边；更重的一笔把一组东西和周围分开。 | 1 到 4，超出按最近的边界取 | 1 |
| 显示背景 | 用那对颜色填满这个矩形，把它变成一张卡片。关掉时，网格从后面透出来。 | 开 / 关 | 关 |
| 背景色 | 填充色是一对，浅色和深色，各是一块色块加一个十六进制输入框：由当前运行的主题决定屏幕上出现哪一半，所以改色就是两半都改。只写了一半的十六进制不算一次编辑，会弹回去。 | 各写 `#RRGGBB` | 浅色 `#FFFFFF`，深色 `#2B2B2B` |
| 标题（角上） | 组件自己的角上标签，位于这一类型所绘制的内容之上。留空就没有这一行说明。在你离开字段时提交。 | 文本，200 个字符 | 空 |
| 标题字号（px） | 留空时每张卡片按自己的单元格决定标题大小；写下一个字号，屏幕上就是那个字号，于是整幅画面的标题保持一致。方框放不下的标题会被裁短，完整标题在提示里。 | 11 到 96，或留空 | 空（自动） |
| 标题颜色 | 标题的一对墨色。不去动它，标题就跟着主题自己那种安静的色调。 | 各写 `#RRGGBB` | 浅色 `#6B6B6B`，深色 `#A0A0A0` |
| 内容缩放 | 内容占它这个方框的多少：填 1 表示按原样铺满单元格；图形按几何方式响应，数值显示按排版方式响应，而一个数字仍旧绝不会画成截断的样子。 | 0.25 到 3，步长 0.05 | 1 |

### 位置与图层 {#position-and-layer}

**X**、**Y**、**宽** 和 **高** 是这个组件占据的格子；会把它推到网格之外的值会被夹住，字段弹回去。**图层（Z）**是它在这一摞里的位置。**移到最前**把选择抬到其他一切之上，**移到最后**把它压到底下，并保持这一批之间的相对次序。移动和改变大小仍然是逐个组件的。

### 显示字号 {#display-size}

每一种会写出数字的类型（数值、仪表盘、指标卡、进度、点位表），在它的格式分区里都带着同样这一对：

| 字段 | 作用 | 取值 | 默认 |
|---|---|---|---|
| 位数（在读文本的数值组件上则是字符数） | 这个字段在分隔符之前能容下多少位。超出的读数会被标记，而不是写短，完整的自己在提示里；超出字符数的文字则改成写短。 | 一个计数；留空就保留这一类型推导出来的默认值，写在占位文字里 | 按类型推导 |
| 字号 | **单元格容得下的最大尺寸**让单元格决定，上限是那些位数所预留的宽度；**我指定的大小**在任何单元格里都照字面来，正是它让两幅画面上的同一个读数保持一致。 | 两种模式 | 单元格容得下的最大尺寸 |
| 磅 | 指定的那个字号，只在指定模式下提供。放不下的东西是被标记，而不是被缩小。 | 磅 | |

### 读数形式 {#reads-as}

同样这些类型带着**读数形式**：**数字**、**百分比**（同一个数字后面加上那个符号）或者**时长**；只有数值组件还提供**文本**，因为它是唯一一种能把一个点位的文字画出来的类型。时长会多出**数值按什么计**：**秒**、**毫秒**或者**分钟**，因为经过的值到达仪表画面时只是一个普通的数。字段下面有一句话，说明你键入的小数位什么时候被按区间的边界取了。

### 刻度 {#scale}

数值、仪表盘、指标卡、进度、柱状图和饼图都带着一个由区段组成的**刻度**分区：“一个区段从上一个区段结束的地方，一直覆盖到它自己的上限。颜色表示含义：具体用什么色由主题决定。”每个区段是一张卡片，抬头是它的说法（或者“第 N 个区段”）和它在哪里结束（“上限 80”“还没有上限”），按它的含义上色，带着上限、含义（**中性**、**信息**、**正常**、**注意**、**严重**），在有一个状态可以命名的类型上还带着一个配说法的字段（“为这个状态命名”，组件会把它写在读数旁边），以及**上移** / **下移**，它们在相邻的两个之间对调含义，而把那些上限留在原地。**添加区段**在末尾追加一个；抬头上的 ✕ 删掉它。在那两种按类别比较的图表上，区段是一块给柱子和扇形上色的调色板，所以不问说法。

仪表盘、指标卡和进度在区段下面还多出一个**参考**：“一个设定值或标称值，标在刻度上它所在的位置。只有刻度覆盖到它时才画出来。”**标记**可以是**无**、**一个固定值**（然后是一个**数值**字段）或者**一个点位**（然后是一个带着实时读数的**地址**字段）。

### 保真与性能 {#fidelity-and-performance}

折线图带着那个决定有多少数据到得了画布的分区：一个**预设**（最高保真、均衡、最高性能；自定义是一种读数，不是一个可选项，只在各字段与任何一档已命名的组合都不符时显示，永远选不中），**全部画出的上限**（100 到 100,000 个点），**超过之后保留**（保留峰值、保留形状、取平均），**逐点画标记的上限**（0 到 5,000；填 0 就从不画），**把落在时间窗口之外的部分也画出来**，**绘制时使用动画**，以及**在角上标明这张图处于哪一档**。超出区间的计数按边界取，面板会说明它取的是哪个数。字段下面有一行估算，说明工作站保留多少个点、有多少个到得了画布，并在画出的点数超过 20,000、或者超过这张图可以保留的内存时发出警告。这些值和它们的含义写在[折线图](component-line-chart)。

### 工具栏 {#toolbox}

每一种图表类型都带着一个**工具栏**分区：它的图形做得到的每一个按钮各一个勾选框，各自画着按钮自己的字形，顺序就是工具栏画它们的顺序。“一个都不勾选时，根本不画工具栏。”折线图提供框选一块区域放大；还原缩放；手动设定坐标范围；所显示窗口的最小值、平均值和最大值；画一条参考线；保存 PNG；复制 PNG；显示或隐藏系列；打开或关闭数据提示；全屏。柱状图或者饼图只提供保存 PNG；复制 PNG；全屏。折线图的**导航**设为“固定窗口”时，它的列表里不再有“框选一块区域放大”和“还原缩放”，工具栏也不画这两个按钮，因为什么都不动的窗口没有缩放可以让它们回答；勾选过的状态会保留下来，窗口重新可以移动时就回来。每个按钮做什么，写在[图表](view-charts)。

### 那两种按类别比较的图表 {#the-categorical-charts}

柱状图和饼图在各自的画法之上共用同一套分区：**内容**里放着画在绘图区里面的**图表标题**（不是角上标题）；**数据**里每个**点位**一张卡片（一个地址、一个退回到地址的**名称**、一个退回到调色板轮次的**颜色**，以及那个实时读数），加上**添加点位**和**排序**（**按声明的顺序**、**从小到大**、**从大到小**）；**格式**里放着**单位**、**小数位**和读数形式。“读不到的点位会被排除在这次比较之外，而不是画成零。”再往下的**柱子**或者**扇形**则各是各的：见[柱状图](component-bar-chart)和[饼图](component-pie-chart)。

## 仪表画面区 {#the-dashboard-area}

**仪表画面**里放着这幅画面对自己声明的东西：名称、说明、图标、网格、启动标记、全屏时的状态栏、可见的角色和删除。它就是[仪表画面设置](view-settings)。在一幅基于 Process 模型定义的画面上，这个区会说明：“这张画面属于一个 Process 模型。一份定义服务于它的每一个单元：每个单元打开它时，都套在自己绑定的通道上。”

## 连线 {#connections}

一个声明了连接点的[图元](component-symbol)，在编辑器里把它们作为标记显示在它的图形上：空着的连接点、被占用的连接点（带一个实心圆点，按它那条连线自己的颜色上色），以及连线未定义时戴着警告的那一个。连线是带类型的网络，画在它们自己的一层上，在设备之下、卡片之上，按[连线类型](assets-connection-types)所声明的颜色和粗细绘制；八种类型是液体、气体、空气 / 风管、电气、信号、网络、物料和机械轴。一个图元的连接点声明自己的类型（或者一份类型配置，由图元的连线分区让你来选），而一个网络的类型，只有在每一个端点都一致的时候才解析得出来。在那里改选另一个类型，连线会跟着一起变：仍是该连接点原来类型的连线锚点改为新类型，随这次选择一起保存（再选回原来的类型，它也随之回来）；被设成其他类型的连线锚点保留自己的类型。同一条连线上若还有另一个图元停在原来的类型，这条连线就保持未定义，直到两者一致。

| 手势 | 作用 |
|---|---|
| 把一个空着的连接点拖到另一个空着的连接点上 | 在两者之间建一条连线。类型对得上就成为一条已定义的连线（“已连接液体连接点。”）；类型冲突或者没有类型的两端照样连上，成为一个**未定义**的网络，用中性、细、虚线的样式画出来，操作记录条会说明这一点（“以未定义方式连接：液体 × 气体。”）。 |
| 把一个空着的连接点拖到空白网格上 | 在那里建一个**连线锚点**，并把这个连接点接到它上面（“已创建一个连线锚点。”）。 |
| 把一个空着的连接点拖到一条已有的线上 | 用一个**交汇点**把这条线断开，并分支到这个连接点（“已创建一条连线分支。”）。这条线必须是已定义的，而且它的类型必须和这个连接点的一致；否则这次手势被拒绝，并说明理由。 |
| 把一个空着的连接点拖到一个交汇点、一个游离端或者一个连线锚点上 | 把它接到那里。已经带着线的游离端或者锚点会就地变成这个连接点；独立的锚点则得到伸向这个连接点的那条线。 |
| 把选中那条线中间的手柄拖到一个空着的连接点上 | 同样是分支，只是从线这边发起。 |
| 拖动一条线 | 把它弯折：走线变成**手动**，保留你留下的那些弯（“已保存手动连线走线。”）。 |
| 拖动一个连线锚点、交汇点或者游离端 | 移动它；接在上面的每一条走线跟着走，接在它上面的手动走线会被标为需要调整。连接点拖不动：它们的位置跟着图元。 |
| 把一个游离端或者一个连线锚点拖到一个空着的连接点上 | 把它重新接到那里（“已重新接上游离的连线端。”）。 |

键盘也做得到同样的事：一个连接点、一条线、它中间的手柄和一个节点都可以取得焦点；在空着的连接点上按回车或者空格把它标为源头，在目标上按回车或者空格完成这次手势，按 Esc 清除源头。Delete 删除选中的线或者连线锚点；在交汇点或者游离端上，它会说明改选哪一条线。

按一下一条线或者一个节点，就选中它并打开属性：

- **连线**（一条线）：抬头里是类型的名称或者“未定义”；两端类型冲突或者不完整时有一条注意色说明（“在它们一致之前无法分支。”），端点移动之后保留下来的手动走线需要调整时还有另一条；**走线**（自动或者手动）、**起点**和**终点**（各带自己的类型）；那句提示“拖动线条可以保留一个手动弯折。选中它中间的手柄可以开始一条分支。”；手动走线上的**重建自动走线**，它清掉那些弯折；以及**删除这一段**，它只删掉那一条线，绝不删掉设备，也不删掉不相干的分支。删掉一个网络的最后一条线，会把它的两端一起带走（“已删除这条连线及其两端。”）；只剩两边的交汇点并成一条线，只剩一边的变成一个游离端，被切成两半的网络变成两个网络。
- **连线锚点**：**名称**（200 个字符）、**连线类型**（未选择，或者八种之一）、**角色**（双向、输入、输出）、**方向**（北、东北、东、东南、南、西南、西、西北），以及**删除这个锚点**，或者在有线终结在它上面时是**删除锚点，保留游离端**。
- **交汇点**：“拖动交汇点的手柄，可以一并调整接在上面的所有走线。选中某条分支的线段即可删除它；只剩两边时，交汇点会自动化简。”
- **游离端**：它保留在哪里（“网格 12.5, 4”），以及怎么把它重新接上：把它拖到一个空着的连接点上，把一个连接点拖到这里，或者删掉接在上面的那条线。

自动走线是正交的，而且不考虑任何障碍物；手动走线一直保留它的弯折，直到你重建它。移动、改变大小、旋转、翻转或者缩放一个图元，绝不会把它断开：那些走线会重新贴到连接点现在所在的位置上，而端点动过的手动走线会被标记为需要调整。删掉一个图元，会把它的连接点变成留在最后位置上的游离端。换掉一个图元的图形（**更换图元…**）会保留新图形仍然声明得出连接点的每一条连线，把其余的放成游离端，操作记录条会写出有多少条。连线的删除（一条线、一个锚点、一次重建走线）用同一个**撤销**来报出，它把整个网络恢复成原样；如果中间对同一条连线又做过一次编辑，那次编辑会被替换掉，操作记录条会说明这一点。

工作站拒绝的连线编辑，会就地显示在属性的最上面，也显示在操作记录条里（“连线的修改没有保存：…”）；一次网络写入还在途中时，那些连线按钮不可用。在查看模式下，同样这些线和端点标记照画，只是没有手柄，而未定义的网络保持它中性的虚线样式，不带任何警告：你也许是故意让它保持那样的。

## 编辑器不做什么 {#what-the-editor-does-not-do}

除了最后那一次删除，它没有撤销，也没有编辑历史：其余每一处改动都是做出来就保存。它没有跨画面的剪贴板，没有方向键微调，没有对齐或者分布工具，没有自由像素摆放，也没有逐次编辑的确认。它不编辑单元面板（那是在它那个模型的[操作面板](process-panels)行上排布的），也不为一个模型创建画面（那是模型的[仪表画面](process-dashboards)行，它打开的正是这同一个编辑器）。它绝不会把网格调成会把某个组件推出去的大小，也绝不向现场写入任何值：编辑画布上的那些控件是静止的。
