进度条是向用户反馈长时间操作进度的关键交互组件,它能让用户清晰了解任务的执行状态,提升用户体验。本指南将系统讲解如何在 SunvStation 脚本中高效使用进度条功能,涵盖基础用法、高级技巧和最佳实践。 在学习本页内容后,建议按照以下路径继续深入: - [配置日志记录器](37-pei-zhi-ri-zhi-ji-lu-qi) — 了解日志记录的配置方法,与进度条配合使用 - [错误处理与异常管理](39-cuo-wu-chu-li-yu-yi-chang-guan-li) — 掌握进度条操作中的错误处理 - [批量保存到数据库](24-pi-liang-bao-cun-dao-shu-ju-ku) — 查看进度条在实际批量操作中的应用 ## 进度条系统架构概览 SunvStation 的进度条功能采用**分层架构设计**,通过 `ProgressMixin` 混入类与 SSProcessManager 集成,结合底层 C++ 封装的 `Progress` 类和进度条控制函数,形成完整的三层架构体系。 ```mermaid classDiagram class ProgressAware { <<抽象基类>> +disable_progress()* +startProgress(title, total)* +stepProgress(message)* +closeProgress()* } class ProgressMixin { +enable_progress: bool +progress: Progress +disable_progress() +startProgress(title, total) +stepProgress(message) +closeProgress() } class SSProcessManager { +enable_progress: bool +progress: Progress +startProgress(title, total) +stepProgress(message) +closeProgress() } class Progress { <> +setSize(nSize) +step() boolean +getPos() int +getStep() int } class GlobalFunctions { <<函数模块>> +startProgress(title, barCount) +closeProgress() +setProgressRange(min, max) +stepProgress(pos, title) } ProgressAware <|-- ProgressMixin ProgressMixin <|-- SSProcessManager ProgressMixin ..> Progress : 使用 ProgressMixin ..> GlobalFunctions : 调用 ``` 这种架构设计的核心优势在于**关注点分离**:`ProgressMixin` 负责进度条的业务逻辑封装,底层函数处理进度条的创建和更新,而 `Progress` 类则提供了防抖动的智能步进机制,避免频繁更新导致的性能问题。 Sources: [progress_mixin.py](ssprocess_mixins/progress_mixin.py#L15-L80), [PySSWidget.py](PySSWidget.py#L934-L977) ### 核心组件说明 | 组件 | 类型 | 职责描述 | |------|------|----------| | `ProgressMixin` | Mixin 类 | 提供进度条的封装方法,管理 `enable_progress` 开关 | | `Progress` | C++ 封装类 | 智能步进控制器,将循环总数映射到 0-100 的进度范围 | | `enable_progress` | bool 属性 | 控制进度条是否显示的全局开关 | | `progress` | Progress 实例 | 当前进度条控制器实例 | | `startProgress()` | 全局函数 | 创建并显示进度条对话框 | | `stepProgress()` | 全局函数 | 更新进度条的当前位置和提示信息 | | `closeProgress()` | 全局函数 | 关闭进度条对话框 | Sources: [progress_mixin.py](ssprocess_mixins/progress_mixin.py#L22-L80), [PySSWidget.py](PySSWidget.py#L500-L560) ## 进度条使用基本流程 进度条操作遵循"启动 → 更新 → 关闭"的标准生命周期管理流程。理解这个流程对于正确使用进度条至关重要,下图展示了完整的操作步骤: ```mermaid flowchart TD A[开始使用进度条] --> B[调用 startProgress
启动进度条] B --> C{enable_progress
是否启用?} C -->|是| D[创建 Progress 实例
设置进度范围 0-100] C -->|否| E[跳过进度显示] D --> F[执行循环任务] F --> G{progress.step()
是否需要更新?} G -->|是| H[调用 stepProgress
更新进度条显示] G -->|否| F H --> F F --> I{任务是否完成?} I -->|否| F I -->|是| J[调用 closeProgress
关闭进度条] J --> K[进度条使用完成] E --> K style D fill:#e1f5ff style H fill:#fff4e1 style J fill:#ffe1e1 ``` 理解这个流程后,我们将逐步详细解析每个环节的具体实现方法。 Sources: [progress_mixin.py](ssprocess_mixins/progress_mixin.py#L32-L80) ## 步骤一:启动进度条 使用 `startProgress()` 方法启动进度条,需要指定标题和总任务数。标题将显示在进度条对话框顶部,总任务数用于计算进度的步进间隔。 ### 基本用法 ```python from sunvpy import SSProcess # 启动进度条,显示标题和总任务数 SSProcess.startProgress("批量导出数据", 1000) ``` ### 方法参数说明 | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | `title` | str | 是 | 进度条对话框标题,描述当前正在执行的任务 | | `total` | int | 是 | 总任务数,用于计算进度步进间隔 | ### 实现原理 `startProgress()` 内部会执行以下操作: 1. 检查 `enable_progress` 开关是否启用 2. 创建 `Progress(total)` 实例,自动计算步进间隔 3. 调用全局 `startProgress(title, 1)` 创建进度条对话框 4. 调用 `setProgressRange(0, 100)` 设置进度范围固定为 0-100 ```mermaid sequenceDiagram participant Script as 脚本代码 participant Mixin as ProgressMixin participant Progress as Progress类 participant UI as 进度条UI Script->>Mixin: startProgress("导出数据", 1000) activate Mixin Mixin->>Mixin: 检查 enable_progress alt enable_progress == True Mixin->>Progress: new Progress(1000) Progress-->>Mixin: 实例创建成功 Mixin->>UI: startProgress("导出数据", 1) UI-->>Mixin: 对话框已显示 Mixin->>UI: setProgressRange(0, 100) Mixin->>Mixin: 保存实例到 self.progress end Mixin-->>Script: 方法返回 deactivate Mixin ``` 这种设计的优势在于:**将循环总数自动映射到 0-100 的标准进度范围**,避免了手动计算百分比的繁琐,同时通过 `Progress` 类的智能步进机制,减少不必要的 UI 更新次数。 Sources: [progress_mixin.py](ssprocess_mixins/progress_mixin.py#L32-L46), [PySSWidget.py](PySSWidget.py#L495-L500) ## 步骤二:更新进度条 在循环或长时间任务中,使用 `stepProgress()` 方法更新进度条显示。这个方法会自动判断是否真正需要更新 UI,避免频繁刷新导致的性能问题。 ### 基本用法 ```python # 遍历选择集并更新进度 total_count = SSProcess.getSelGeoCount() SSProcess.startProgress("处理选择集对象", total_count) for i in range(total_count): # 执行业务逻辑 SSProcess.setSelGeoValue(i, "SSObj_Name", f"对象_{i}") # 更新进度条,显示当前处理的索引 SSProcess.stepProgress(f"正在处理第 {i+1}/{total_count} 个对象") # 关闭进度条 SSProcess.closeProgress() ``` ### 方法参数说明 | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | `message` | str | 是 | 当前步骤的描述信息,将显示在进度条下方 | ### 智能步进机制 `Progress` 类的核心功能是**智能步进控制**。它会根据总任务数自动计算步进间隔,确保进度条更新不会过于频繁: ```python # Progress 内部的步进逻辑(简化示例) class Progress: def __init__(self, total): self.total = total self.current = 0 # 自动计算步进间隔,确保在0-100范围内更新不超过100次 self.step_size = max(1, total // 100) def step(self): self.current += 1 # 只有当累计步进达到阈值时才返回True,触发UI更新 if self.current >= self.step_size: self.current = 0 return True return False ``` 这种机制的效果是:无论循环执行多少次(100次还是100000次),进度条最多只更新100次,大大提高了性能。 Sources: [progress_mixin.py](ssprocess_mixins/progress_mixin.py#L48-L60), [PySSWidget.py](PySSWidget.py#L946-L972) ### 更新频率对比 以下对比展示了智能步进机制与传统每次循环都更新的区别: | 场景 | 总任务数 | 传统更新次数 | 智能步进更新次数 | 性能提升 | |------|----------|-------------|----------------|----------| | 小型任务 | 50 | 50 | 50 | 无提升 | | 中型任务 | 1000 | 1000 | 100 | 10倍提升 | | 大型任务 | 100000 | 100000 | 100 | 1000倍提升 | Sources: [PySSWidget.py](PySSWidget.py#L950-L972) ## 步骤三:关闭进度条 任务完成后,必须调用 `closeProgress()` 方法关闭进度条对话框。这个方法会清理进度条资源,并确保 UI 正确更新。 ### 基本用法 ```python # 任务完成后关闭进度条 SSProcess.closeProgress() ``` ### 方法行为 `closeProgress()` 会执行以下操作: 1. 调用全局 `closeProgress()` 函数关闭对话框 2. 将 `self.progress` 设置为 `None`,释放资源 3. 通过异常处理确保即使关闭失败也不会影响程序运行 ```python def closeProgress(self): """关闭进度条。""" if self.progress is not None: try: closeProgress() except Exception: pass # 忽略关闭异常 self.progress = None ``` Sources: [progress_mixin.py](ssprocess_mixins/progress_mixin.py#L62-L80), [PySSWidget.py](PySSWidget.py#L505-L510) ### 关闭时机的重要性 确保在以下所有情况下都调用 `closeProgress()`: | 场景 | 说明 | 处理方式 | |------|------|----------| | 正常完成 | 循环自然结束 | 在循环结束后调用 | | 异常中断 | 遇到错误提前退出 | 使用 try-finally 确保调用 | | 用户取消 | 响应用户中断请求 | 在取消处理中调用 | | 条件跳过 | 不满足某些条件时退出 | 在所有退出分支中调用 | ### 最佳实践:使用 finally 确保关闭 ```python SSProcess.startProgress("处理数据", total_count) try: for i in range(total_count): # 执行业务逻辑 process_item(i) # 更新进度 SSProcess.stepProgress(f"处理 {i+1}/{total_count}") # 检查是否需要中断 if should_cancel(): raise KeyboardInterrupt("用户取消操作") except Exception as e: SSProcess.logger.error(f"处理失败: {e}") raise finally: # 确保进度条一定会被关闭 SSProcess.closeProgress() ``` Sources: [progress_mixin.py](ssprocess_mixins/progress_mixin.py#L62-L80) ## 完整示例:批量导出数据 下面是一个完整的示例,展示在批量导出选择集对象时如何使用进度条。 ### 代码实现 ```python from sunvpy import SSProcess def export_selection_to_csv(file_path): """将选择集中的对象导出到CSV文件""" # 获取选择集数量 geo_count = SSProcess.getSelGeoCount() if geo_count == 0: print("选择集为空,无需导出") return # 启动进度条 SSProcess.startProgress("导出选择集到CSV", geo_count) try: # 准备CSV文件 with open(file_path, 'w', encoding='utf-8') as f: # 写入CSV表头 f.write("ID,代码,名称,类型,颜色\n") # 遍历选择集并导出 for i in range(geo_count): # 获取对象属性 obj_id = SSProcess.getSelGeoValue(i, "SSObj_ID") obj_code = SSProcess.getSelGeoValue(i, "SSObj_Code") obj_name = SSProcess.getSelGeoValue(i, "SSObj_Name") obj_type = SSProcess.getSelGeoValue(i, "SSObj_Type") obj_color = SSProcess.getSelGeoValue(i, "SSObj_Color") # 写入CSV行 f.write(f"{obj_id},{obj_code},{obj_name},{obj_type},{obj_color}\n") # 每处理10个对象更新一次进度 SSProcess.stepProgress(f"正在导出第 {i+1}/{geo_count} 个对象") print(f"成功导出 {geo_count} 个对象到 {file_path}") except Exception as e: print(f"导出失败: {e}") SSProcess.logger.error(f"导出失败: {e}") finally: # 确保关闭进度条 SSProcess.closeProgress() # 调用示例 export_selection_to_csv("export.csv") ``` ### 代码解析 | 代码段 | 说明 | |--------|------| | `geo_count = SSProcess.getSelGeoCount()` | 先获取总数量,用于进度条初始化 | | `SSProcess.startProgress(...)` | 在 try 块之前启动进度条 | | `SSProcess.stepProgress(...)` | 在循环内部更新进度,显示当前处理位置 | | `finally: SSProcess.closeProgress()` | 使用 finally 确保进度条一定被关闭 | Sources: [progress_mixin.py](ssprocess_mixins/progress_mixin.py#L32-L80) ## 禁用进度条 在某些场景下(如自动化脚本、后台任务),可能需要临时或永久禁用进度条显示。使用 `disable_progress()` 方法可以实现这一需求。 ### 基本用法 ```python # 禁用进度条 SSProcess.disable_progress() # 后续的所有进度条操作都不会显示 SSProcess.startProgress("处理数据", 100) # 不会显示进度条 SSProcess.stepProgress("处理中...") # 不会更新进度条 SSProcess.closeProgress() # 不会执行任何操作 ``` ### 方法行为 `disable_progress()` 会执行以下操作: 1. 将 `enable_progress` 设置为 `False` 2. 如果当前有正在显示的进度条,立即关闭它 3. 后续所有进度条方法调用都会被跳过 ```python def disable_progress(self): """关闭进度条显示并立即关闭正在显示的进度条。""" self.enable_progress = False if self.progress is not None: try: closeProgress() except Exception: pass self.progress = None ``` Sources: [progress_mixin.py](ssprocess_mixins/progress_mixin.py#L24-L30) ### 使用场景对比 | 场景 | 是否禁用进度条 | 原因 | |------|----------------|------| | 交互式脚本 | 否 | 用户需要看到进度反馈 | | 批处理自动化任务 | 是 | 无用户交互,节省资源 | | 后台定时任务 | 是 | 避免弹出窗口干扰 | | 单元测试 | 是 | 测试环境不需要UI反馈 | | 调试阶段 | 否 | 需要观察程序执行状态 | ### 条件禁用示例 ```python import sys def process_data(): """根据运行环境决定是否显示进度条""" # 如果是从命令行运行且带有 --quiet 参数,则禁用进度条 if '--quiet' in sys.argv: SSProcess.disable_progress() print("静默模式:进度条已禁用") # 正常使用进度条(会根据 enable_progress 自动判断) SSProcess.startProgress("处理数据", 1000) for i in range(1000): process_item(i) SSProcess.stepProgress(f"处理 {i+1}") SSProcess.closeProgress() # 调用示例 # python script.py --quiet # 禁用进度条 # python script.py # 显示进度条 ``` Sources: [progress_mixin.py](ssprocess_mixins/progress_mixin.py#L24-L30) ## 进度条与日志配合使用 将进度条与日志系统结合使用,可以在提供视觉反馈的同时保留详细的执行记录,特别适合复杂的长时间任务。 ### 配合模式 ```mermaid flowchart LR A[任务开始] --> B[记录日志 INFO] B --> C[启动进度条] C --> D{循环执行} D --> E[执行子任务] E --> F{子任务成功?} F -->|是| G[记录日志 DEBUG
详细信息] F -->|否| H[记录日志 ERROR
错误信息] G --> I[更新进度条] H --> I I --> J{任务完成?} J -->|否| D J -->|是| K[关闭进度条] K --> L[记录日志 INFO
任务完成] style C fill:#e1f5ff style I fill:#fff4e1 style K fill:#ffe1e1 ``` ### 完整示例 ```python import logging from sunvpy import SSProcess # 配置日志记录器 logger = logging.getLogger('batch_process') logger.setLevel(logging.INFO) file_handler = logging.FileHandler('batch_process.log', encoding='utf-8') formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') file_handler.setFormatter(formatter) logger.addHandler(file_handler) # 将日志注入到 SSProcess SSProcess.set_logger(logger) def batch_process_objects(): """批量处理对象,配合进度条和日志""" logger.info("开始批量处理任务") # 获取选择集数量 total_count = SSProcess.getSelGeoCount() logger.info(f"选择集对象数量: {total_count}") if total_count == 0: logger.warning("选择集为空,跳过处理") return # 启动进度条 SSProcess.startProgress("批量处理对象", total_count) success_count = 0 error_count = 0 try: for i in range(total_count): try: # 执行业务逻辑 process_single_object(i) # 记录详细日志 logger.debug(f"成功处理对象 {i+1}/{total_count}") success_count += 1 except Exception as e: # 记录错误日志 logger.error(f"处理对象 {i+1} 失败: {str(e)}") error_count += 1 # 更新进度条 SSProcess.stepProgress(f"处理进度: {i+1}/{total_count}") finally: # 关闭进度条 SSProcess.closeProgress() # 记录汇总日志 logger.info(f"处理完成: 成功 {success_count} 个, 失败 {error_count} 个") def process_single_object(index): """处理单个对象的业务逻辑""" # 这里实现具体的处理逻辑 obj_name = SSProcess.getSelGeoValue(index, "SSObj_Name") SSProcess.setSelGeoValue(index, "SSObj_Name", f"{obj_name}_已处理") # 调用示例 batch_process_objects() # 输出示例: # 2024-01-15 10:30:00 - INFO - 开始批量处理任务 # 2024-01-15 10:30:00 - INFO - 选择集对象数量: 100 # 2024-01-15 10:30:05 - INFO - 处理完成: 成功 98 个, 失败 2 个 # 2024-01-15 10:30:05 - ERROR - 处理对象 15 失败: 无法获取属性值 ``` ### 日志级别与进度条的配合策略 | 操作阶段 | 进度条操作 | 日志级别 | 说明 | |----------|-----------|---------|------| | 任务开始 | startProgress | INFO | 记录任务启动,包含总数量 | | 每个循环 | stepProgress | DEBUG | 记录详细信息(可关闭) | | 子任务成功 | - | DEBUG | 记录成功的详细步骤 | | 子任务失败 | - | ERROR | 记录失败的原因和堆栈 | | 任务结束 | closeProgress | INFO | 记录任务完成,包含统计 | 这种配合方式的优势在于:**进度条提供实时视觉反馈,日志提供可追溯的详细记录**,两者互为补充,满足不同场景的需求。 Sources: [progress_mixin.py](ssprocess_mixins/progress_mixin.py#L32-L80), [log_mixin.py](ssprocess_mixins/log_mixin.py#L26-L42) ## 常见问题与解决方案 ### 问题一:进度条不显示 **现象**:调用 `startProgress()` 后进度条没有显示。 **可能原因及解决方案**: | 原因 | 检查方法 | 解决方案 | |------|---------|----------| | 已禁用进度条 | 检查 `SSProcess.enable_progress` | 调用 `SSProcess.enable_progress = True` 重新启用 | | 总任务数为0 | 检查传入的 `total` 参数 | 确保传入有效的正整数 | | 主线程阻塞 | 检查是否有耗时操作在主线程 | 将耗时操作放到子线程中执行 | ```python # 检查进度条状态 print(f"进度条启用状态: {SSProcess.enable_progress}") # 重新启用进度条 SSProcess.enable_progress = True ``` Sources: [progress_mixin.py](ssprocess_mixins/progress_mixin.py#L24-L30) ### 问题二:进度条卡住不动 **现象**:进度条显示后长时间没有更新。 **可能原因及解决方案**: ```python # 错误示例:步进过大导致进度条长时间不更新 SSProcess.startProgress("处理", 100000) # 任务数过大 for i in range(100000): process_item(i) # 每1000次才会更新一次进度,看起来像卡住 SSProcess.stepProgress(f"处理 {i}") # 正确示例:提供更有意义的进度提示 SSProcess.startProgress("处理", 100000) for i in range(100000): process_item(i) # 进度条内部会智能控制更新频率,但提示信息会实时显示 SSProcess.stepProgress(f"处理 {i+1}/{100000}") ``` Sources: [PySSWidget.py](PySSWidget.py#L950-L972) ### 问题三:进度条关闭后程序无响应 **现象**:调用 `closeProgress()` 后程序似乎卡住。 **解决方案**: ```python # 原因分析:进度条关闭操作在UI线程执行,可能被其他操作阻塞 # 解决方案1:确保在主线程中关闭 def close_progress_safely(): if SSProcess.progress is not None: try: closeProgress() except Exception as e: print(f"关闭进度条时出错: {e}") finally: SSProcess.progress = None close_progress_safely() # 解决方案2:使用超时机制 import threading import time def close_with_timeout(timeout=5): def _close(): try: closeProgress() except: pass thread = threading.Thread(target=_close) thread.start() thread.join(timeout=timeout) if thread.is_alive(): print("警告:进度条关闭超时") SSProcess.progress = None close_with_timeout() ``` Sources: [progress_mixin.py](ssprocess_mixins/progress_mixin.py#L62-L80) ## 最佳实践总结 ### 1. 进度条使用检查清单 在使用进度条前,请确认以下要点: | 检查项 | 说明 | |--------|------| | ✓ 确认总任务数 | 使用准确的数字初始化进度条,避免0或负数 | | ✓ 在try前启动 | 确保进度条启动在try块之前 | | ✓ 在finally中关闭 | 使用try-finally确保进度条一定会被关闭 | | ✓ 提供有意义的提示 | `stepProgress` 的message应反映当前操作状态 | | ✓ 考虑禁用场景 | 自动化任务中考虑使用 `disable_progress()` | ### 2. 性能优化建议 | 优化点 | 说明 | 示例 | |--------|------|------| | 减少UI更新 | 利用 `Progress.step()` 的自动步进机制 | 不需要手动计算更新频率 | | 异步关闭 | 大任务完成后考虑异步关闭进度条 | 使用子线程关闭避免阻塞 | | 条件显示 | 根据任务时长决定是否显示进度条 | 短任务(<1秒)跳过进度条 | ### 3. 代码模板 ```python # 标准进度条使用模板 from sunvpy import SSProcess def task_with_progress(): """带进度条的标准任务模板""" # 1. 获取总任务数 total = get_task_count() if total == 0: return # 2. 启动进度条 SSProcess.startProgress("任务描述", total) try: # 3. 执行循环任务 for i in range(total): # 执行业务逻辑 do_work(i) # 4. 更新进度 SSProcess.stepProgress(f"处理 {i+1}/{total}") except Exception as e: # 5. 错误处理 SSProcess.logger.error(f"任务失败: {e}") raise finally: # 6. 确保关闭 SSProcess.closeProgress() ``` Sources: [progress_mixin.py](ssprocess_mixins/progress_mixin.py#L15-L80) ## 下一步学习 掌握了进度条的基本使用后,您可以继续学习以下相关主题: - [配置日志记录器](37-pei-zhi-ri-zhi-ji-lu-qi) — 了解如何将日志与进度条配合,构建完整的反馈机制 - [错误处理与异常管理](39-cuo-wu-chu-li-yu-yi-chang-guan-li) — 学习在进度条操作中正确处理异常 - [批量保存到数据库](24-pi-liang-bao-cun-dao-shu-ju-ku) — 查看进度条在实际批量操作中的应用示例 通过这些内容的学习,您将能够构建出用户体验良好、健壮性强的 SunvStation 脚本应用。