进度条是向用户反馈长时间操作进度的关键交互组件，它能让用户清晰了解任务的执行状态，提升用户体验。本指南将系统讲解如何在 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 {
        <<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](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<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](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<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
```

### 完整示例

```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 脚本应用。