Files
sunvpy-docs/docs/content/38-jin-du-tiao-shi-yong-zhi-nan.md
2026-04-10 13:47:53 +08:00

24 KiB
Raw Blame History

进度条是向用户反馈长时间操作进度的关键交互组件,它能让用户清晰了解任务的执行状态,提升用户体验。本指南将系统讲解如何在 SunvStation 脚本中高效使用进度条功能,涵盖基础用法、高级技巧和最佳实践。

在学习本页内容后,建议按照以下路径继续深入:

进度条系统架构概览

SunvStation 的进度条功能采用分层架构设计,通过 ProgressMixin 混入类与 SSProcessManager 集成,结合底层 C++ 封装的 Progress 类和进度条控制函数,形成完整的三层架构体系。

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 {
        <<C++封装类>>
        +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, PySSWidget.py

核心组件说明

组件 类型 职责描述
ProgressMixin Mixin 类 提供进度条的封装方法,管理 enable_progress 开关
Progress C++ 封装类 智能步进控制器,将循环总数映射到 0-100 的进度范围
enable_progress bool 属性 控制进度条是否显示的全局开关
progress Progress 实例 当前进度条控制器实例
startProgress() 全局函数 创建并显示进度条对话框
stepProgress() 全局函数 更新进度条的当前位置和提示信息
closeProgress() 全局函数 关闭进度条对话框

Sources: progress_mixin.py, PySSWidget.py

进度条使用基本流程

进度条操作遵循"启动 → 更新 → 关闭"的标准生命周期管理流程。理解这个流程对于正确使用进度条至关重要,下图展示了完整的操作步骤:

flowchart TD
    A[开始使用进度条] --> B[调用 startProgress<br/>启动进度条]
    B --> C{enable_progress<br/>是否启用?}
    C -->|是| D[创建 Progress 实例<br/>设置进度范围 0-100]
    C -->|否| E[跳过进度显示]
    D --> F[执行循环任务]
    F --> G{progress.step&#40&#41<br/>是否需要更新?}
    G -->|是| H[调用 stepProgress<br/>更新进度条显示]
    G -->|否| F
    H --> F
    F --> I{任务是否完成?}
    I -->|否| F
    I -->|是| J[调用 closeProgress<br/>关闭进度条]
    J --> K[进度条使用完成]
    E --> K
    
    style D fill:#e1f5ff
    style H fill:#fff4e1
    style J fill:#ffe1e1

理解这个流程后,我们将逐步详细解析每个环节的具体实现方法。

Sources: progress_mixin.py

步骤一:启动进度条

使用 startProgress() 方法启动进度条,需要指定标题和总任务数。标题将显示在进度条对话框顶部,总任务数用于计算进度的步进间隔。

基本用法

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
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, PySSWidget.py

步骤二:更新进度条

在循环或长时间任务中,使用 stepProgress() 方法更新进度条显示。这个方法会自动判断是否真正需要更新 UI避免频繁刷新导致的性能问题。

基本用法

# 遍历选择集并更新进度
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 类的核心功能是智能步进控制。它会根据总任务数自动计算步进间隔,确保进度条更新不会过于频繁:

# 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, PySSWidget.py

更新频率对比

以下对比展示了智能步进机制与传统每次循环都更新的区别:

场景 总任务数 传统更新次数 智能步进更新次数 性能提升
小型任务 50 50 50 无提升
中型任务 1000 1000 100 10倍提升
大型任务 100000 100000 100 1000倍提升

Sources: PySSWidget.py

步骤三:关闭进度条

任务完成后,必须调用 closeProgress() 方法关闭进度条对话框。这个方法会清理进度条资源,并确保 UI 正确更新。

基本用法

# 任务完成后关闭进度条
SSProcess.closeProgress()

方法行为

closeProgress() 会执行以下操作:

  1. 调用全局 closeProgress() 函数关闭对话框
  2. self.progress 设置为 None,释放资源
  3. 通过异常处理确保即使关闭失败也不会影响程序运行
def closeProgress(self):
    """关闭进度条。"""
    if self.progress is not None:
        try:
            closeProgress()
        except Exception:
            pass  # 忽略关闭异常
        self.progress = None

Sources: progress_mixin.py, PySSWidget.py

关闭时机的重要性

确保在以下所有情况下都调用 closeProgress()

场景 说明 处理方式
正常完成 循环自然结束 在循环结束后调用
异常中断 遇到错误提前退出 使用 try-finally 确保调用
用户取消 响应用户中断请求 在取消处理中调用
条件跳过 不满足某些条件时退出 在所有退出分支中调用

最佳实践:使用 finally 确保关闭

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

完整示例:批量导出数据

下面是一个完整的示例,展示在批量导出选择集对象时如何使用进度条。

代码实现

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

禁用进度条

在某些场景下(如自动化脚本、后台任务),可能需要临时或永久禁用进度条显示。使用 disable_progress() 方法可以实现这一需求。

基本用法

# 禁用进度条
SSProcess.disable_progress()

# 后续的所有进度条操作都不会显示
SSProcess.startProgress("处理数据", 100)  # 不会显示进度条
SSProcess.stepProgress("处理中...")     # 不会更新进度条
SSProcess.closeProgress()               # 不会执行任何操作

方法行为

disable_progress() 会执行以下操作:

  1. enable_progress 设置为 False
  2. 如果当前有正在显示的进度条,立即关闭它
  3. 后续所有进度条方法调用都会被跳过
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

使用场景对比

场景 是否禁用进度条 原因
交互式脚本 用户需要看到进度反馈
批处理自动化任务 无用户交互,节省资源
后台定时任务 避免弹出窗口干扰
单元测试 测试环境不需要UI反馈
调试阶段 需要观察程序执行状态

条件禁用示例

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

进度条与日志配合使用

将进度条与日志系统结合使用,可以在提供视觉反馈的同时保留详细的执行记录,特别适合复杂的长时间任务。

配合模式

flowchart LR
    A[任务开始] --> B[记录日志 INFO]
    B --> C[启动进度条]
    C --> D{循环执行}
    D --> E[执行子任务]
    E --> F{子任务成功?}
    F -->|是| G[记录日志 DEBUG<br/>详细信息]
    F -->|否| H[记录日志 ERROR<br/>错误信息]
    G --> I[更新进度条]
    H --> I
    I --> J{任务完成?}
    J -->|否| D
    J -->|是| K[关闭进度条]
    K --> L[记录日志 INFO<br/>任务完成]
    
    style C fill:#e1f5ff
    style I fill:#fff4e1
    style K fill:#ffe1e1

完整示例

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, log_mixin.py

常见问题与解决方案

问题一:进度条不显示

现象:调用 startProgress() 后进度条没有显示。

可能原因及解决方案

原因 检查方法 解决方案
已禁用进度条 检查 SSProcess.enable_progress 调用 SSProcess.enable_progress = True 重新启用
总任务数为0 检查传入的 total 参数 确保传入有效的正整数
主线程阻塞 检查是否有耗时操作在主线程 将耗时操作放到子线程中执行
# 检查进度条状态
print(f"进度条启用状态: {SSProcess.enable_progress}")

# 重新启用进度条
SSProcess.enable_progress = True

Sources: progress_mixin.py

问题二:进度条卡住不动

现象:进度条显示后长时间没有更新。

可能原因及解决方案

# 错误示例:步进过大导致进度条长时间不更新
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

问题三:进度条关闭后程序无响应

现象:调用 closeProgress() 后程序似乎卡住。

解决方案

# 原因分析进度条关闭操作在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

最佳实践总结

1. 进度条使用检查清单

在使用进度条前,请确认以下要点:

检查项 说明
✓ 确认总任务数 使用准确的数字初始化进度条避免0或负数
✓ 在try前启动 确保进度条启动在try块之前
✓ 在finally中关闭 使用try-finally确保进度条一定会被关闭
✓ 提供有意义的提示 stepProgress 的message应反映当前操作状态
✓ 考虑禁用场景 自动化任务中考虑使用 disable_progress()

2. 性能优化建议

优化点 说明 示例
减少UI更新 利用 Progress.step() 的自动步进机制 不需要手动计算更新频率
异步关闭 大任务完成后考虑异步关闭进度条 使用子线程关闭避免阻塞
条件显示 根据任务时长决定是否显示进度条 短任务(<1秒跳过进度条

3. 代码模板

# 标准进度条使用模板
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

下一步学习

掌握了进度条的基本使用后,您可以继续学习以下相关主题:

通过这些内容的学习,您将能够构建出用户体验良好、健壮性强的 SunvStation 脚本应用。