Files
sunvpy-docs/docs/content/25-che-xiao-biao-ji-guan-li.md
2026-04-10 13:47:53 +08:00

319 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
撤销标记管理是 SunvStation 地理编辑系统中的一项核心功能,它通过在执行可撤销操作前创建标记,支持用户对编辑操作进行撤销和重做。该机制基于命令模式,将每次可撤销操作封装为独立的命令单元,存储在 Undo/Redo 栈中,确保数据编辑的安全性和可追溯性。
撤销标记管理在批量地理对象编辑、属性修改和几何变换等场景中尤为重要,能够有效防止误操作导致的数据丢失,提升编辑流程的容错能力和用户体验。
## 撤销标记系统架构
撤销标记系统采用双层架构设计:上层通过 `SSProcessManager` 提供统一的 Python 接口,下层由 `ScaleMap` 的 C++ 核心实现命令存储和状态管理。系统维护两个命令栈Undo 栈存储已执行的操作历史Redo 栈存储被撤销的操作,两者协同工作实现操作的双向回溯。
```mermaid
flowchart TD
A[用户操作] --> B[SSProcessManager<br/>pushUndoMark]
B --> C[ScaleMap<br/>pushUndoMark]
C --> D{创建命令标记}
D --> E[命令序列化]
E --> F[Undo栈<br/>LIFO结构]
F --> G[操作执行]
G --> H{用户触发撤销?}
H -->|是| I[undo方法]
H -->|否| J{用户触发重做?}
I --> K[弹出Undo栈顶命令]
K --> L[执行逆向操作]
L --> M[压入Redo栈]
J -->|是| N[redo方法]
N --> O[弹出Redo栈顶命令]
O --> P[执行正向操作]
P --> Q[压入Undo栈]
```
## 核心 API 方法
### 创建撤销标记
`pushUndoMark()` 方法是撤销标记管理的入口,在执行任何可撤销操作前调用,为后续操作建立撤销点。该方法接受可选的操作描述参数,用于标识命令组的用途。
| 方法 | 参数 | 返回值 | 说明 |
|------|------|--------|------|
| `pushUndoMark(action="CommandGroup")` | `action`: 命令组名称(可选) | void | 在 Undo 栈中创建新的标记位置 |
**使用示例**
```python
# 批量修改前创建撤销标记
SSProcess.pushUndoMark("批量修改管线属性")
for i in range(SSProcess.getSelGeoCount()):
SSProcess.setSelGeoValue(i, "SSObj_Color", "16777215")
SSProcess.setSelGeoValue(i, "SSObj_LineWidth", "2")
# 修改完成后,用户可通过 Ctrl+Z 或调用 undo() 方法撤销
```
Sources: [ssprocess_mixins/project_mixin.py](ssprocess_mixins/project_mixin.py#L50-L61)
### 执行撤销操作
`undo()` 方法从 Undo 栈中弹出最近执行的命令并执行逆向操作,恢复数据到操作前的状态。支持指定步数或使用标记进行批量撤销。
| 方法 | 参数 | 返回值 | 说明 |
|------|------|--------|------|
| `undo(step=1)` | `step`: 执行步数,-1 时使用 undo 标签 | void | 撤销指定步数的操作 |
**使用示例**
```python
# 撤销最近一次操作
SSProcess.undo()
# 撤销最近 5 次操作
SSProcess.undo(5)
# 撤销到上一个标记位置
SSProcess.undo(-1)
```
Sources: [PySSMap.py](PySSMap.py#L1633-L1641)
### 执行重做操作
`redo()` 方法从 Redo 栈中弹出被撤销的命令并重新执行,恢复之前撤销的操作。与 `undo()` 方法形成对称操作。
| 方法 | 参数 | 返回值 | 说明 |
|------|------|--------|------|
| `redo(step=1)` | `step`: 执行步数,-1 时使用 undo 标签 | void | 重做指定步数的操作 |
**使用示例**
```python
# 重做最近一次撤销的操作
SSProcess.redo()
# 重做最近 3 次撤销的操作
SSProcess.redo(3)
```
Sources: [PySSMap.py](PySSMap.py#L1643-L1648)
### 栈状态查询与控制
系统提供多个方法用于查询 Undo/Redo 栈的状态,以及重置栈内容,便于用户了解可撤销/重做的操作范围。
| 方法 | 返回值 | 说明 |
|------|--------|------|
| `getUndoStackSize()` | int | 获取 Undo 栈中的命令数量 |
| `getRedoStackSize()` | int | 获取 Redo 栈中的命令数量 |
| `getUndoStack()` | StringArray | 获取 Undo 栈中的命令描述列表 |
| `getRedoStack()` | StringArray | 获取 Redo 栈中的命令描述列表 |
| `resetUndoRedoStack()` | void | 清空 Undo/Redo 栈,删除所有历史记录 |
**使用示例**
```python
# 查询可撤销的操作数量
undo_count = SSProcess.getUndoStackSize()
print(f"可撤销操作数: {undo_count}")
# 查询可重做的操作数量
redo_count = SSProcess.getRedoStackSize()
print(f"可重做操作数: {redo_count}")
# 获取 Undo 栈中的命令描述
undo_stack = SSProcess.getUndoStack()
for i in range(undo_stack.size()):
print(f" {i+1}. {undo_stack[i]}")
# 清空所有撤销/重做历史(谨慎使用)
# SSProcess.resetUndoRedoStack()
```
Sources: [PySSMap.py](PySSMap.py#L1650-L1675)
### 手动压栈操作
`pushUndoAddobj()` 方法提供手动压栈功能,用于将指定地物对象列表显式地加入 Undo 栈,通常用于自定义编辑场景。
| 方法 | 参数 | 返回值 | 说明 |
|------|------|--------|------|
| `pushUndoAddobj(geoList)` | `geoList`: GeoBaseList 对象列表 | void | 手动将对象列表压入 Undo 栈 |
**使用示例**
```python
# 创建新对象后手动压栈
obj, geo = SSProcess.createNewObjByCode(3103013)
SSProcess.addNewObjPoint(x, y, z, 0, "Point1")
# 手动压栈以便后续撤销
geo_list = GeoBaseList()
geo_list.append(geo)
SSProcess.pushUndoAddobj(geo_list)
```
Sources: [PySSMap.py](PySSMap.py#L1686-L1692)
## 操作流程规范
### 标准编辑流程
遵循标准的撤销标记管理流程可确保操作的可追溯性和数据安全性。下图展示了完整的编辑操作生命周期:
```mermaid
sequenceDiagram
participant U as 用户
participant S as SSProcess
participant M as ScaleMap
participant UStack as Undo栈
participant RStack as Redo栈
U->>S: 1. pushUndoMark("操作描述")
S->>M: 2. pushUndoMark()
M->>UStack: 3. 创建标记并压栈
M->>RStack: 4. 清空 Redo 栈
U->>S: 5. 执行编辑操作
S->>M: 6. 修改数据
opt 用户请求撤销
U->>S: 7. undo()
S->>M: 8. undo()
M->>UStack: 9. 弹出命令
M->>M: 10. 执行逆向操作
M->>RStack: 11. 压入 Redo 栈
end
opt 用户请求重做
U->>S: 12. redo()
S->>M: 13. redo()
M->>RStack: 14. 弹出命令
M->>M: 15. 执行正向操作
M->>UStack: 16. 压入 Undo 栈
end
```
### 标记创建时机
撤销标记应在以下关键节点创建:
1. **批量操作前**:批量修改属性、移动对象、删除对象等操作前
2. **复合操作起始点**:由多个子操作组成的复杂编辑流程开始时
3. **重要编辑前**:不可逆或影响范围较大的编辑操作前
4. **事务边界**:定义明确的编辑事务单元,便于整体撤销
## 典型应用场景
### 批量属性修改
批量修改选择集中地物的属性值时,应在循环前创建撤销标记,确保所有修改可作为一个整体撤销。
```python
# 场景:批量修改管线的颜色和线宽
SSProcess.pushUndoMark("批量修改管线样式")
for i in range(SSProcess.getSelGeoCount()):
SSProcess.setSelGeoValue(i, "SSObj_Color", "16711680") # 红色
SSProcess.setSelGeoValue(i, "SSObj_LineWidth", "3")
# 如需撤销所有修改,调用:
# SSProcess.undo(-1)
```
Sources: [ssprocess_mixins/geo_edit_mixin.py](ssprocess_mixins/geo_edit_mixin.py#L321-L400)
### 对象创建与删除
创建或删除对象时,通过撤销标记管理可快速回退到操作前的状态。
```python
# 场景:创建新注记对象
SSProcess.pushUndoMark("创建注记对象")
obj, geo = SSProcess.createNewObjByClass("说明注记")
note = castToMarkNote(geo)
note.setNote("新建管线节点")
SSProcess.addNewObjPoint(x, y, 0, 0, "节点1")
SSProcess.addNewObjToSaveObjList()
SSProcess.saveBufferObjToDatabase()
# 如需撤销创建操作:
# SSProcess.undo()
```
Sources: [ssprocess_mixins/geo_edit_mixin.py](ssprocess_mixins/geo_edit_mixin.py#L159-L201)
### 几何变换操作
执行对象的平移、旋转、缩放等几何变换时,撤销标记管理提供快速恢复机制。
```python
# 场景:批量旋转选择集对象
from sunvpy.PySSMath import Point3D
SSProcess.pushUndoMark("旋转选择集对象")
# 获取旋转中心(假设第一个对象为参考点)
center_geo = SSProcess.selGeoList[0]
points = center_geo.getPoints()
center_pt = points[0]
# 执行旋转
angle = 1.5708 # 90度弧度制
SSProcess.map.rotate(SSProcess.selGeoList, center_pt, angle, False)
# 请求视图更新
SSProcess.updateRequest()
```
Sources: [PySSMap.py](PySSMap.py#L1558-L1564)
## 最佳实践建议
### 标记命名规范
为撤销标记提供清晰的描述性名称,便于用户识别和调试。命名应简洁明了,反映操作的实质内容。
| 推荐命名格式 | 示例 | 说明 |
|--------------|------|------|
| "动作+对象类型" | "修改管线属性" | 动词+名词结构 |
| "批量+具体操作" | "批量删除注记" | 明确操作类型 |
| "事务名称" | "节点编辑事务" | 用于复合操作 |
| "功能模块+操作" | "数据导入操作" | 模块化命名 |
### 性能优化建议
1. **避免频繁创建标记**:在循环外创建标记,而非每次迭代都创建
2. **合理使用标记边界**:根据业务逻辑划分事务单元,而非滥用标记
3. **定期清空栈**:在长时间编辑会话中,适时调用 `resetUndoRedoStack()` 释放内存
4. **栈容量监控**:通过 `getUndoStackSize()` 监控栈深度,避免过度占用资源
### 错误处理机制
在创建撤销标记时,应检查地图实例的有效性,并在操作失败时进行适当的错误处理。
```python
def safe_edit_operation():
"""安全的编辑操作封装"""
if SSProcess.map is None:
print("错误:无法获取当前地图实例")
return False
try:
SSProcess.pushUndoMark("安全编辑操作")
# 执行编辑操作
# ...
SSProcess.updateRequest()
return True
except Exception as e:
print(f"编辑操作失败:{str(e)}")
# 可选择在此处调用 undo() 回滚部分修改
return False
```
Sources: [ssprocess_mixins/project_mixin.py](ssprocess_mixins/project_mixin.py#L50-L61)
## 与其他模块的集成
撤销标记管理与[对象缓存机制](23-dui-xiang-huan-cun-ji-zhi)紧密协同,在批量保存到数据库前提供撤销能力。同时,它与[地物属性读写](15-huo-qu-di-wu-shu-xing-zhi)和[地理对象编辑](19-chuang-jian-mo-ren-di-wu-dui-xiang)模块集成,确保所有编辑操作的可追溯性。
在实现自定义编辑功能时,建议在操作前后分别调用 `pushUndoMark()``updateRequest()`,形成完整的编辑闭环,保持与系统标准编辑流程的一致性。