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

11 KiB
Raw Permalink Blame History

撤销标记管理是 SunvStation 地理编辑系统中的一项核心功能,它通过在执行可撤销操作前创建标记,支持用户对编辑操作进行撤销和重做。该机制基于命令模式,将每次可撤销操作封装为独立的命令单元,存储在 Undo/Redo 栈中,确保数据编辑的安全性和可追溯性。

撤销标记管理在批量地理对象编辑、属性修改和几何变换等场景中尤为重要,能够有效防止误操作导致的数据丢失,提升编辑流程的容错能力和用户体验。

撤销标记系统架构

撤销标记系统采用双层架构设计:上层通过 SSProcessManager 提供统一的 Python 接口,下层由 ScaleMap 的 C++ 核心实现命令存储和状态管理。系统维护两个命令栈Undo 栈存储已执行的操作历史Redo 栈存储被撤销的操作,两者协同工作实现操作的双向回溯。

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 栈中创建新的标记位置

使用示例

# 批量修改前创建撤销标记
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

执行撤销操作

undo() 方法从 Undo 栈中弹出最近执行的命令并执行逆向操作,恢复数据到操作前的状态。支持指定步数或使用标记进行批量撤销。

方法 参数 返回值 说明
undo(step=1) step: 执行步数,-1 时使用 undo 标签 void 撤销指定步数的操作

使用示例

# 撤销最近一次操作
SSProcess.undo()

# 撤销最近 5 次操作
SSProcess.undo(5)

# 撤销到上一个标记位置
SSProcess.undo(-1)

Sources: PySSMap.py

执行重做操作

redo() 方法从 Redo 栈中弹出被撤销的命令并重新执行,恢复之前撤销的操作。与 undo() 方法形成对称操作。

方法 参数 返回值 说明
redo(step=1) step: 执行步数,-1 时使用 undo 标签 void 重做指定步数的操作

使用示例

# 重做最近一次撤销的操作
SSProcess.redo()

# 重做最近 3 次撤销的操作
SSProcess.redo(3)

Sources: PySSMap.py

栈状态查询与控制

系统提供多个方法用于查询 Undo/Redo 栈的状态,以及重置栈内容,便于用户了解可撤销/重做的操作范围。

方法 返回值 说明
getUndoStackSize() int 获取 Undo 栈中的命令数量
getRedoStackSize() int 获取 Redo 栈中的命令数量
getUndoStack() StringArray 获取 Undo 栈中的命令描述列表
getRedoStack() StringArray 获取 Redo 栈中的命令描述列表
resetUndoRedoStack() void 清空 Undo/Redo 栈,删除所有历史记录

使用示例

# 查询可撤销的操作数量
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

手动压栈操作

pushUndoAddobj() 方法提供手动压栈功能,用于将指定地物对象列表显式地加入 Undo 栈,通常用于自定义编辑场景。

方法 参数 返回值 说明
pushUndoAddobj(geoList) geoList: GeoBaseList 对象列表 void 手动将对象列表压入 Undo 栈

使用示例

# 创建新对象后手动压栈
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

操作流程规范

标准编辑流程

遵循标准的撤销标记管理流程可确保操作的可追溯性和数据安全性。下图展示了完整的编辑操作生命周期:

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. 事务边界:定义明确的编辑事务单元,便于整体撤销

典型应用场景

批量属性修改

批量修改选择集中地物的属性值时,应在循环前创建撤销标记,确保所有修改可作为一个整体撤销。

# 场景:批量修改管线的颜色和线宽
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.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

几何变换操作

执行对象的平移、旋转、缩放等几何变换时,撤销标记管理提供快速恢复机制。

# 场景:批量旋转选择集对象
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

最佳实践建议

标记命名规范

为撤销标记提供清晰的描述性名称,便于用户识别和调试。命名应简洁明了,反映操作的实质内容。

推荐命名格式 示例 说明
"动作+对象类型" "修改管线属性" 动词+名词结构
"批量+具体操作" "批量删除注记" 明确操作类型
"事务名称" "节点编辑事务" 用于复合操作
"功能模块+操作" "数据导入操作" 模块化命名

性能优化建议

  1. 避免频繁创建标记:在循环外创建标记,而非每次迭代都创建
  2. 合理使用标记边界:根据业务逻辑划分事务单元,而非滥用标记
  3. 定期清空栈:在长时间编辑会话中,适时调用 resetUndoRedoStack() 释放内存
  4. 栈容量监控:通过 getUndoStackSize() 监控栈深度,避免过度占用资源

错误处理机制

在创建撤销标记时,应检查地图实例的有效性,并在操作失败时进行适当的错误处理。

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

与其他模块的集成

撤销标记管理与对象缓存机制紧密协同,在批量保存到数据库前提供撤销能力。同时,它与地物属性读写地理对象编辑模块集成,确保所有编辑操作的可追溯性。

在实现自定义编辑功能时,建议在操作前后分别调用 pushUndoMark()updateRequest(),形成完整的编辑闭环,保持与系统标准编辑流程的一致性。