qthreadwithreturn


Nameqthreadwithreturn JSON
Version 1.3.12 PyPI version JSON
download
home_pageNone
SummaryPySide6 高级线程工具库 - 带返回值的线程类和线程池执行器
upload_time2025-10-09 08:40:32
maintainerNone
docs_urlNone
authorNone
requires_python>=3.10
licenseNone
keywords qt pyside6 threading concurrent futures gui pyqt async thread-pool worker-thread
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            <div align="center">

# QThreadWithReturn

![QThreadWithReturn](https://socialify.git.ci/271374667/QThreadWithReturn/image?description=1&language=1&name=1&pattern=Plus&theme=Auto)

[![Python](https://img.shields.io/badge/Python-3.10+-blue.svg)](https://python.org)
[![PySide6](https://img.shields.io/badge/PySide6-6.4+-green.svg)](https://www.qt.io/qt-for-python)
[![License](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Tests](https://img.shields.io/badge/Tests-200%20passed-brightgreen.svg)](tests/)

基于 PySide6 的多线程高度封装库,简化 GUI 应用中的多线程编程。

简单易用,支持返回值和回调机制,避免复杂的信号槽设置、完善的内存回收、超时控制和任务取消功能、线程池支持、以及完整的类型提示。

</div>

## 简介

**该库针对需要后台耗时运行,且只有在完成后才需要更新 UI 的场景**
进行了高度封装,例如进行网络爬虫获取数据,或者进行大规模数据处理,通过回调函数的方式代替了信号与槽机制,能够在保持高封装性的同时,减少大量信号的使用,同时内部自动处理了线程的启动、结束和资源回收等问题,使得多线程编程变得更加简单和直观。

*如果是需要在线程运行过程中频繁更新 UI 的场景,或者需要线程间频繁通信的场景,建议使用传统的 `QThread` 和信号槽机制*

## 快速开始

**首先您的逻辑和界面代码应该是分离的**,不能写在同一个类里面,最好分为多个 `.py` 文件。其次是写在同一个 `.py`
文件但是不同的类里面。如果逻辑和界面的操作写在一起反而不如原本的 `QThread` 方式,会导致您的项目更加混乱。

下面是一个简单的银行取款的例子,假设取款是一个耗时操作,他是一系列复杂的逻辑操作,于是他被写在另一个类 `Bank` 里面,而界面代码写在
`MyWindow` 里面。
界面当中有一个按钮,当点击之后会调用 `Bank` 里面的 `draw` 方法进行取款操作,取款成功之后会更新界面。
其中 `draw` 方法的返回值会被 `QThreadWithReturn` 自动捕获,然后传入 `finished` 函数,而如果发生异常则会传入 `failure` 函数。

其中的 `finished` 和 `failure` 函数都是线程运行完毕之后运行的回调函数,**都是在主线程中运行的,所以可以直接操作界面控件**
,以及访问各种类的属性。

```python
"""
QThreadWithReturn的基础例子
"""

import time

from PySide6.QtCore import Qt
from PySide6.QtWidgets import QApplication, QWidget, QPushButton, QLabel

from qthreadwithreturn import QThreadWithReturn


class Bank:
    """模拟银行取款操作

    这里是耗时操作的例子,在实际的项目中逻辑应该放在其他的模块中,不应该和界面代码混在一起
    这样做只是为了演示 QThreadWithReturn 的使用
    """

    def draw(self, amount: float) -> str:
        """模拟取款操作"""
        time.sleep(2)  # 模拟耗时操作
        return f"成功取款 {amount} 元"


class MyWindow(QWidget):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("QThreadWithReturn 示例")
        self.setGeometry(100, 100, 300, 200)

        self.bank = Bank()

        self.button = QPushButton("取款 100 元", self)
        self.button.setGeometry(50, 50, 200, 40)

        self.label = QLabel("等待取款...", self)
        self.label.setAlignment(Qt.AlignmentFlag.AlignHCenter)
        self.label.setGeometry(50, 100, 200, 40)

        self.button.clicked.connect(self.start_draw)

    def start_draw(self):
        """开始取款操作"""
        self.button.setEnabled(False)
        self.label.setText("取款中...")

        # 使用 QThreadWithReturn 进行取款操作
        # 通过finished和failure两个闭包函数节约了两个信号,运行完毕之后返回值会自动传入finished函数
        # 如果发生异常则会传入failure函数
        def finished(result: str):
            # 成功后自动调用(传入参数为self.bank.draw的返回值)
            self.label.setText(result)
            self.button.setEnabled(True)

        def failure(result: Exception):
            # 失败后自动调用(传入参数为self.bank.draw抛出的异常)
            self.label.setText(f"取款失败: {result}")
            self.button.setEnabled(True)

        thread = QThreadWithReturn(self.bank.draw, 100)  # 调用取款方法,传入参数100
        thread.add_done_callback(finished)
        thread.add_failure_callback(failure)
        thread.start()


if __name__ == '__main__':
    app = QApplication([])
    window = MyWindow()
    window.show()
    app.exec()

```

从上面其实就能看到 QThreadWithReturn
的优势,对于原本需要使用信号来传递的返回值和异常,现在都可以通过回调函数来处理,而且逻辑都可以通过闭包的形式写在相应的方法里面,
而不会污染其他的命名空间,即使出现了问题也能很快定位到问题所在。您也无需担心内存泄漏的问题,因为 QThreadWithReturn
会自动处理线程的结束和资源回收。

假设您的项目中有很多类似的耗时操作需要在后台运行,并且只有在完成后才需要更新 UI,那么使用 QThreadWithReturn
会让您的代码变得更加简洁、安全、易读。

### 总结

1. 逻辑代码和界面代码分离
2. 创建一个 QThreadWithReturn 对象,传入需要运行的函数和参数
3. 使用 `add_done_callback` 方法添加成功回调函数
4. 使用 `add_failure_callback` 方法添加失败回调函数(可选,如果不需要处理异常可以不添加)
5. 调用 `start` 方法启动线程

建议使用闭包函数的方式来定义回调函数,这样可以避免命名冲突,并且可以直接访问类的属性和方法。

## ✨ 特性

### 🎯 QThreadWithReturn

- `concurrent.futures.Future` 的 API,无需二次学习,快速上手
- 内置超时控制和任务取消(包括强制停止)
- 自动管理线程生命周期,防止内存泄漏
- 支持任意可调用对象(函数、方法、lambda 等)
- 完整的类型提示
- 与 Qt 事件循环无缝集成

### 🏊‍♂️ QThreadPoolExecutor

- `concurrent.futures.ThreadPoolExecutor` 的 API,无需二次学习,快速上手
- 线程池管理和任务调度
- 支持线程初始化器和命名
- 支持 `as_completed` 方法按完成顺序处理任务
- 任务取消和强制停止支持
- 完整的类型提示
- 上下文管理器支持

## 🚀 安装

```bash
# 使用 uv
uv add qthreadwithreturn

# 使用 pip  
pip install qthreadwithreturn
```

## 📖 API 参考

### QThreadWithReturn

| 方法                               | 描述          |
|----------------------------------|-------------|
| `start(timeout_ms=-1)`           | 启动线程,可选超时设置 |
| `result(timeout=None)`           | 获取执行结果,阻塞等待 |
| `exception(timeout=None)`        | 获取异常信息      |
| `cancel(force_stop=False)`       | 取消线程执行      |
| `running()`                      | 检查是否正在运行    |
| `done()`                         | 检查是否已完成     |
| `cancelled()`                    | 检查是否已取消     |
| `add_done_callback(callback)`    | 添加成功完成回调    |
| `add_failure_callback(callback)` | 添加失败回调      |

### QThreadPoolExecutor

| 方法                                                            | 描述                |
|---------------------------------------------------------------|-------------------|
| `submit(fn, *args, **kwargs)`                                 | 提交任务到线程池          |
| `shutdown(wait=True, cancel_futures=False, force_stop=False)` | 关闭线程池             |
| `as_completed(futures, timeout=None)`                         | 按完成顺序迭代 Future 对象 |

### 🛠️ 开发环境设置

本项目使用 uv 进行配置,您可以前往 https://docs.astral.sh/uv/ 了解更多关于 uv 的相关内容。

```bash
# 克隆仓库
git clone https://github.com/271374667/QThreadWithReturn.git
cd QThreadWithReturn

# 使用 uv 安装依赖
uv sync

# 运行测试
uv run pytest

# 运行演示
uv run python -m demo.thread_demo_gui
```

## 📄 许可证

本项目使用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情。

## 📞 支持

- **问题报告**: [GitHub Issues](https://github.com/271374667/QThreadWithReturn/issues)
- **讨论**: [GitHub Discussions](https://github.com/271374667/QThreadWithReturn/discussions)
- **邮件**: 271374667@qq.com

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "qthreadwithreturn",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.10",
    "maintainer_email": "PythonImporter <271374667@qq.com>",
    "keywords": "qt, pyside6, threading, concurrent, futures, gui, pyqt, async, thread-pool, worker-thread",
    "author": null,
    "author_email": "PythonImporter <271374667@qq.com>",
    "download_url": "https://files.pythonhosted.org/packages/59/22/07974dbaf1f8d952fcd586e9709ce84eb24be7578eb41ee2273956eba5b2/qthreadwithreturn-1.3.12.tar.gz",
    "platform": null,
    "description": "<div align=\"center\">\r\n\r\n# QThreadWithReturn\r\n\r\n![QThreadWithReturn](https://socialify.git.ci/271374667/QThreadWithReturn/image?description=1&language=1&name=1&pattern=Plus&theme=Auto)\r\n\r\n[![Python](https://img.shields.io/badge/Python-3.10+-blue.svg)](https://python.org)\r\n[![PySide6](https://img.shields.io/badge/PySide6-6.4+-green.svg)](https://www.qt.io/qt-for-python)\r\n[![License](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\r\n[![Tests](https://img.shields.io/badge/Tests-200%20passed-brightgreen.svg)](tests/)\r\n\r\n\u57fa\u4e8e PySide6 \u7684\u591a\u7ebf\u7a0b\u9ad8\u5ea6\u5c01\u88c5\u5e93\uff0c\u7b80\u5316 GUI \u5e94\u7528\u4e2d\u7684\u591a\u7ebf\u7a0b\u7f16\u7a0b\u3002\r\n\r\n\u7b80\u5355\u6613\u7528\uff0c\u652f\u6301\u8fd4\u56de\u503c\u548c\u56de\u8c03\u673a\u5236\uff0c\u907f\u514d\u590d\u6742\u7684\u4fe1\u53f7\u69fd\u8bbe\u7f6e\u3001\u5b8c\u5584\u7684\u5185\u5b58\u56de\u6536\u3001\u8d85\u65f6\u63a7\u5236\u548c\u4efb\u52a1\u53d6\u6d88\u529f\u80fd\u3001\u7ebf\u7a0b\u6c60\u652f\u6301\u3001\u4ee5\u53ca\u5b8c\u6574\u7684\u7c7b\u578b\u63d0\u793a\u3002\r\n\r\n</div>\r\n\r\n## \u7b80\u4ecb\r\n\r\n**\u8be5\u5e93\u9488\u5bf9\u9700\u8981\u540e\u53f0\u8017\u65f6\u8fd0\u884c\uff0c\u4e14\u53ea\u6709\u5728\u5b8c\u6210\u540e\u624d\u9700\u8981\u66f4\u65b0 UI \u7684\u573a\u666f**\r\n\u8fdb\u884c\u4e86\u9ad8\u5ea6\u5c01\u88c5\uff0c\u4f8b\u5982\u8fdb\u884c\u7f51\u7edc\u722c\u866b\u83b7\u53d6\u6570\u636e\uff0c\u6216\u8005\u8fdb\u884c\u5927\u89c4\u6a21\u6570\u636e\u5904\u7406\uff0c\u901a\u8fc7\u56de\u8c03\u51fd\u6570\u7684\u65b9\u5f0f\u4ee3\u66ff\u4e86\u4fe1\u53f7\u4e0e\u69fd\u673a\u5236\uff0c\u80fd\u591f\u5728\u4fdd\u6301\u9ad8\u5c01\u88c5\u6027\u7684\u540c\u65f6\uff0c\u51cf\u5c11\u5927\u91cf\u4fe1\u53f7\u7684\u4f7f\u7528\uff0c\u540c\u65f6\u5185\u90e8\u81ea\u52a8\u5904\u7406\u4e86\u7ebf\u7a0b\u7684\u542f\u52a8\u3001\u7ed3\u675f\u548c\u8d44\u6e90\u56de\u6536\u7b49\u95ee\u9898\uff0c\u4f7f\u5f97\u591a\u7ebf\u7a0b\u7f16\u7a0b\u53d8\u5f97\u66f4\u52a0\u7b80\u5355\u548c\u76f4\u89c2\u3002\r\n\r\n*\u5982\u679c\u662f\u9700\u8981\u5728\u7ebf\u7a0b\u8fd0\u884c\u8fc7\u7a0b\u4e2d\u9891\u7e41\u66f4\u65b0 UI \u7684\u573a\u666f\uff0c\u6216\u8005\u9700\u8981\u7ebf\u7a0b\u95f4\u9891\u7e41\u901a\u4fe1\u7684\u573a\u666f\uff0c\u5efa\u8bae\u4f7f\u7528\u4f20\u7edf\u7684 `QThread` \u548c\u4fe1\u53f7\u69fd\u673a\u5236*\r\n\r\n## \u5feb\u901f\u5f00\u59cb\r\n\r\n**\u9996\u5148\u60a8\u7684\u903b\u8f91\u548c\u754c\u9762\u4ee3\u7801\u5e94\u8be5\u662f\u5206\u79bb\u7684**\uff0c\u4e0d\u80fd\u5199\u5728\u540c\u4e00\u4e2a\u7c7b\u91cc\u9762\uff0c\u6700\u597d\u5206\u4e3a\u591a\u4e2a `.py` \u6587\u4ef6\u3002\u5176\u6b21\u662f\u5199\u5728\u540c\u4e00\u4e2a `.py`\r\n\u6587\u4ef6\u4f46\u662f\u4e0d\u540c\u7684\u7c7b\u91cc\u9762\u3002\u5982\u679c\u903b\u8f91\u548c\u754c\u9762\u7684\u64cd\u4f5c\u5199\u5728\u4e00\u8d77\u53cd\u800c\u4e0d\u5982\u539f\u672c\u7684 `QThread` \u65b9\u5f0f\uff0c\u4f1a\u5bfc\u81f4\u60a8\u7684\u9879\u76ee\u66f4\u52a0\u6df7\u4e71\u3002\r\n\r\n\u4e0b\u9762\u662f\u4e00\u4e2a\u7b80\u5355\u7684\u94f6\u884c\u53d6\u6b3e\u7684\u4f8b\u5b50\uff0c\u5047\u8bbe\u53d6\u6b3e\u662f\u4e00\u4e2a\u8017\u65f6\u64cd\u4f5c\uff0c\u4ed6\u662f\u4e00\u7cfb\u5217\u590d\u6742\u7684\u903b\u8f91\u64cd\u4f5c\uff0c\u4e8e\u662f\u4ed6\u88ab\u5199\u5728\u53e6\u4e00\u4e2a\u7c7b `Bank` \u91cc\u9762\uff0c\u800c\u754c\u9762\u4ee3\u7801\u5199\u5728\r\n`MyWindow` \u91cc\u9762\u3002\r\n\u754c\u9762\u5f53\u4e2d\u6709\u4e00\u4e2a\u6309\u94ae\uff0c\u5f53\u70b9\u51fb\u4e4b\u540e\u4f1a\u8c03\u7528 `Bank` \u91cc\u9762\u7684 `draw` \u65b9\u6cd5\u8fdb\u884c\u53d6\u6b3e\u64cd\u4f5c\uff0c\u53d6\u6b3e\u6210\u529f\u4e4b\u540e\u4f1a\u66f4\u65b0\u754c\u9762\u3002\r\n\u5176\u4e2d `draw` \u65b9\u6cd5\u7684\u8fd4\u56de\u503c\u4f1a\u88ab `QThreadWithReturn` \u81ea\u52a8\u6355\u83b7\uff0c\u7136\u540e\u4f20\u5165 `finished` \u51fd\u6570\uff0c\u800c\u5982\u679c\u53d1\u751f\u5f02\u5e38\u5219\u4f1a\u4f20\u5165 `failure` \u51fd\u6570\u3002\r\n\r\n\u5176\u4e2d\u7684 `finished` \u548c `failure` \u51fd\u6570\u90fd\u662f\u7ebf\u7a0b\u8fd0\u884c\u5b8c\u6bd5\u4e4b\u540e\u8fd0\u884c\u7684\u56de\u8c03\u51fd\u6570\uff0c**\u90fd\u662f\u5728\u4e3b\u7ebf\u7a0b\u4e2d\u8fd0\u884c\u7684\uff0c\u6240\u4ee5\u53ef\u4ee5\u76f4\u63a5\u64cd\u4f5c\u754c\u9762\u63a7\u4ef6**\r\n\uff0c\u4ee5\u53ca\u8bbf\u95ee\u5404\u79cd\u7c7b\u7684\u5c5e\u6027\u3002\r\n\r\n```python\r\n\"\"\"\r\nQThreadWithReturn\u7684\u57fa\u7840\u4f8b\u5b50\r\n\"\"\"\r\n\r\nimport time\r\n\r\nfrom PySide6.QtCore import Qt\r\nfrom PySide6.QtWidgets import QApplication, QWidget, QPushButton, QLabel\r\n\r\nfrom qthreadwithreturn import QThreadWithReturn\r\n\r\n\r\nclass Bank:\r\n    \"\"\"\u6a21\u62df\u94f6\u884c\u53d6\u6b3e\u64cd\u4f5c\r\n\r\n    \u8fd9\u91cc\u662f\u8017\u65f6\u64cd\u4f5c\u7684\u4f8b\u5b50\uff0c\u5728\u5b9e\u9645\u7684\u9879\u76ee\u4e2d\u903b\u8f91\u5e94\u8be5\u653e\u5728\u5176\u4ed6\u7684\u6a21\u5757\u4e2d\uff0c\u4e0d\u5e94\u8be5\u548c\u754c\u9762\u4ee3\u7801\u6df7\u5728\u4e00\u8d77\r\n    \u8fd9\u6837\u505a\u53ea\u662f\u4e3a\u4e86\u6f14\u793a QThreadWithReturn \u7684\u4f7f\u7528\r\n    \"\"\"\r\n\r\n    def draw(self, amount: float) -> str:\r\n        \"\"\"\u6a21\u62df\u53d6\u6b3e\u64cd\u4f5c\"\"\"\r\n        time.sleep(2)  # \u6a21\u62df\u8017\u65f6\u64cd\u4f5c\r\n        return f\"\u6210\u529f\u53d6\u6b3e {amount} \u5143\"\r\n\r\n\r\nclass MyWindow(QWidget):\r\n    def __init__(self):\r\n        super().__init__()\r\n\r\n        self.setWindowTitle(\"QThreadWithReturn \u793a\u4f8b\")\r\n        self.setGeometry(100, 100, 300, 200)\r\n\r\n        self.bank = Bank()\r\n\r\n        self.button = QPushButton(\"\u53d6\u6b3e 100 \u5143\", self)\r\n        self.button.setGeometry(50, 50, 200, 40)\r\n\r\n        self.label = QLabel(\"\u7b49\u5f85\u53d6\u6b3e...\", self)\r\n        self.label.setAlignment(Qt.AlignmentFlag.AlignHCenter)\r\n        self.label.setGeometry(50, 100, 200, 40)\r\n\r\n        self.button.clicked.connect(self.start_draw)\r\n\r\n    def start_draw(self):\r\n        \"\"\"\u5f00\u59cb\u53d6\u6b3e\u64cd\u4f5c\"\"\"\r\n        self.button.setEnabled(False)\r\n        self.label.setText(\"\u53d6\u6b3e\u4e2d...\")\r\n\r\n        # \u4f7f\u7528 QThreadWithReturn \u8fdb\u884c\u53d6\u6b3e\u64cd\u4f5c\r\n        # \u901a\u8fc7finished\u548cfailure\u4e24\u4e2a\u95ed\u5305\u51fd\u6570\u8282\u7ea6\u4e86\u4e24\u4e2a\u4fe1\u53f7\uff0c\u8fd0\u884c\u5b8c\u6bd5\u4e4b\u540e\u8fd4\u56de\u503c\u4f1a\u81ea\u52a8\u4f20\u5165finished\u51fd\u6570\r\n        # \u5982\u679c\u53d1\u751f\u5f02\u5e38\u5219\u4f1a\u4f20\u5165failure\u51fd\u6570\r\n        def finished(result: str):\r\n            # \u6210\u529f\u540e\u81ea\u52a8\u8c03\u7528(\u4f20\u5165\u53c2\u6570\u4e3aself.bank.draw\u7684\u8fd4\u56de\u503c)\r\n            self.label.setText(result)\r\n            self.button.setEnabled(True)\r\n\r\n        def failure(result: Exception):\r\n            # \u5931\u8d25\u540e\u81ea\u52a8\u8c03\u7528(\u4f20\u5165\u53c2\u6570\u4e3aself.bank.draw\u629b\u51fa\u7684\u5f02\u5e38)\r\n            self.label.setText(f\"\u53d6\u6b3e\u5931\u8d25: {result}\")\r\n            self.button.setEnabled(True)\r\n\r\n        thread = QThreadWithReturn(self.bank.draw, 100)  # \u8c03\u7528\u53d6\u6b3e\u65b9\u6cd5,\u4f20\u5165\u53c2\u6570100\r\n        thread.add_done_callback(finished)\r\n        thread.add_failure_callback(failure)\r\n        thread.start()\r\n\r\n\r\nif __name__ == '__main__':\r\n    app = QApplication([])\r\n    window = MyWindow()\r\n    window.show()\r\n    app.exec()\r\n\r\n```\r\n\r\n\u4ece\u4e0a\u9762\u5176\u5b9e\u5c31\u80fd\u770b\u5230 QThreadWithReturn\r\n\u7684\u4f18\u52bf\uff0c\u5bf9\u4e8e\u539f\u672c\u9700\u8981\u4f7f\u7528\u4fe1\u53f7\u6765\u4f20\u9012\u7684\u8fd4\u56de\u503c\u548c\u5f02\u5e38\uff0c\u73b0\u5728\u90fd\u53ef\u4ee5\u901a\u8fc7\u56de\u8c03\u51fd\u6570\u6765\u5904\u7406\uff0c\u800c\u4e14\u903b\u8f91\u90fd\u53ef\u4ee5\u901a\u8fc7\u95ed\u5305\u7684\u5f62\u5f0f\u5199\u5728\u76f8\u5e94\u7684\u65b9\u6cd5\u91cc\u9762\uff0c\r\n\u800c\u4e0d\u4f1a\u6c61\u67d3\u5176\u4ed6\u7684\u547d\u540d\u7a7a\u95f4\uff0c\u5373\u4f7f\u51fa\u73b0\u4e86\u95ee\u9898\u4e5f\u80fd\u5f88\u5feb\u5b9a\u4f4d\u5230\u95ee\u9898\u6240\u5728\u3002\u60a8\u4e5f\u65e0\u9700\u62c5\u5fc3\u5185\u5b58\u6cc4\u6f0f\u7684\u95ee\u9898\uff0c\u56e0\u4e3a QThreadWithReturn\r\n\u4f1a\u81ea\u52a8\u5904\u7406\u7ebf\u7a0b\u7684\u7ed3\u675f\u548c\u8d44\u6e90\u56de\u6536\u3002\r\n\r\n\u5047\u8bbe\u60a8\u7684\u9879\u76ee\u4e2d\u6709\u5f88\u591a\u7c7b\u4f3c\u7684\u8017\u65f6\u64cd\u4f5c\u9700\u8981\u5728\u540e\u53f0\u8fd0\u884c\uff0c\u5e76\u4e14\u53ea\u6709\u5728\u5b8c\u6210\u540e\u624d\u9700\u8981\u66f4\u65b0 UI\uff0c\u90a3\u4e48\u4f7f\u7528 QThreadWithReturn\r\n\u4f1a\u8ba9\u60a8\u7684\u4ee3\u7801\u53d8\u5f97\u66f4\u52a0\u7b80\u6d01\u3001\u5b89\u5168\u3001\u6613\u8bfb\u3002\r\n\r\n### \u603b\u7ed3\r\n\r\n1. \u903b\u8f91\u4ee3\u7801\u548c\u754c\u9762\u4ee3\u7801\u5206\u79bb\r\n2. \u521b\u5efa\u4e00\u4e2a QThreadWithReturn \u5bf9\u8c61\uff0c\u4f20\u5165\u9700\u8981\u8fd0\u884c\u7684\u51fd\u6570\u548c\u53c2\u6570\r\n3. \u4f7f\u7528 `add_done_callback` \u65b9\u6cd5\u6dfb\u52a0\u6210\u529f\u56de\u8c03\u51fd\u6570\r\n4. \u4f7f\u7528 `add_failure_callback` \u65b9\u6cd5\u6dfb\u52a0\u5931\u8d25\u56de\u8c03\u51fd\u6570(\u53ef\u9009,\u5982\u679c\u4e0d\u9700\u8981\u5904\u7406\u5f02\u5e38\u53ef\u4ee5\u4e0d\u6dfb\u52a0)\r\n5. \u8c03\u7528 `start` \u65b9\u6cd5\u542f\u52a8\u7ebf\u7a0b\r\n\r\n\u5efa\u8bae\u4f7f\u7528\u95ed\u5305\u51fd\u6570\u7684\u65b9\u5f0f\u6765\u5b9a\u4e49\u56de\u8c03\u51fd\u6570\uff0c\u8fd9\u6837\u53ef\u4ee5\u907f\u514d\u547d\u540d\u51b2\u7a81\uff0c\u5e76\u4e14\u53ef\u4ee5\u76f4\u63a5\u8bbf\u95ee\u7c7b\u7684\u5c5e\u6027\u548c\u65b9\u6cd5\u3002\r\n\r\n## \u2728 \u7279\u6027\r\n\r\n### \ud83c\udfaf QThreadWithReturn\r\n\r\n- `concurrent.futures.Future` \u7684 API\uff0c\u65e0\u9700\u4e8c\u6b21\u5b66\u4e60\uff0c\u5feb\u901f\u4e0a\u624b\r\n- \u5185\u7f6e\u8d85\u65f6\u63a7\u5236\u548c\u4efb\u52a1\u53d6\u6d88(\u5305\u62ec\u5f3a\u5236\u505c\u6b62)\r\n- \u81ea\u52a8\u7ba1\u7406\u7ebf\u7a0b\u751f\u547d\u5468\u671f\uff0c\u9632\u6b62\u5185\u5b58\u6cc4\u6f0f\r\n- \u652f\u6301\u4efb\u610f\u53ef\u8c03\u7528\u5bf9\u8c61\uff08\u51fd\u6570\u3001\u65b9\u6cd5\u3001lambda \u7b49\uff09\r\n- \u5b8c\u6574\u7684\u7c7b\u578b\u63d0\u793a\r\n- \u4e0e Qt \u4e8b\u4ef6\u5faa\u73af\u65e0\u7f1d\u96c6\u6210\r\n\r\n### \ud83c\udfca\u200d\u2642\ufe0f QThreadPoolExecutor\r\n\r\n- `concurrent.futures.ThreadPoolExecutor` \u7684 API\uff0c\u65e0\u9700\u4e8c\u6b21\u5b66\u4e60\uff0c\u5feb\u901f\u4e0a\u624b\r\n- \u7ebf\u7a0b\u6c60\u7ba1\u7406\u548c\u4efb\u52a1\u8c03\u5ea6\r\n- \u652f\u6301\u7ebf\u7a0b\u521d\u59cb\u5316\u5668\u548c\u547d\u540d\r\n- \u652f\u6301 `as_completed` \u65b9\u6cd5\u6309\u5b8c\u6210\u987a\u5e8f\u5904\u7406\u4efb\u52a1\r\n- \u4efb\u52a1\u53d6\u6d88\u548c\u5f3a\u5236\u505c\u6b62\u652f\u6301\r\n- \u5b8c\u6574\u7684\u7c7b\u578b\u63d0\u793a\r\n- \u4e0a\u4e0b\u6587\u7ba1\u7406\u5668\u652f\u6301\r\n\r\n## \ud83d\ude80 \u5b89\u88c5\r\n\r\n```bash\r\n# \u4f7f\u7528 uv\r\nuv add qthreadwithreturn\r\n\r\n# \u4f7f\u7528 pip  \r\npip install qthreadwithreturn\r\n```\r\n\r\n## \ud83d\udcd6 API \u53c2\u8003\r\n\r\n### QThreadWithReturn\r\n\r\n| \u65b9\u6cd5                               | \u63cf\u8ff0          |\r\n|----------------------------------|-------------|\r\n| `start(timeout_ms=-1)`           | \u542f\u52a8\u7ebf\u7a0b\uff0c\u53ef\u9009\u8d85\u65f6\u8bbe\u7f6e |\r\n| `result(timeout=None)`           | \u83b7\u53d6\u6267\u884c\u7ed3\u679c\uff0c\u963b\u585e\u7b49\u5f85 |\r\n| `exception(timeout=None)`        | \u83b7\u53d6\u5f02\u5e38\u4fe1\u606f      |\r\n| `cancel(force_stop=False)`       | \u53d6\u6d88\u7ebf\u7a0b\u6267\u884c      |\r\n| `running()`                      | \u68c0\u67e5\u662f\u5426\u6b63\u5728\u8fd0\u884c    |\r\n| `done()`                         | \u68c0\u67e5\u662f\u5426\u5df2\u5b8c\u6210     |\r\n| `cancelled()`                    | \u68c0\u67e5\u662f\u5426\u5df2\u53d6\u6d88     |\r\n| `add_done_callback(callback)`    | \u6dfb\u52a0\u6210\u529f\u5b8c\u6210\u56de\u8c03    |\r\n| `add_failure_callback(callback)` | \u6dfb\u52a0\u5931\u8d25\u56de\u8c03      |\r\n\r\n### QThreadPoolExecutor\r\n\r\n| \u65b9\u6cd5                                                            | \u63cf\u8ff0                |\r\n|---------------------------------------------------------------|-------------------|\r\n| `submit(fn, *args, **kwargs)`                                 | \u63d0\u4ea4\u4efb\u52a1\u5230\u7ebf\u7a0b\u6c60          |\r\n| `shutdown(wait=True, cancel_futures=False, force_stop=False)` | \u5173\u95ed\u7ebf\u7a0b\u6c60             |\r\n| `as_completed(futures, timeout=None)`                         | \u6309\u5b8c\u6210\u987a\u5e8f\u8fed\u4ee3 Future \u5bf9\u8c61 |\r\n\r\n### \ud83d\udee0\ufe0f \u5f00\u53d1\u73af\u5883\u8bbe\u7f6e\r\n\r\n\u672c\u9879\u76ee\u4f7f\u7528 uv \u8fdb\u884c\u914d\u7f6e\uff0c\u60a8\u53ef\u4ee5\u524d\u5f80 https://docs.astral.sh/uv/ \u4e86\u89e3\u66f4\u591a\u5173\u4e8e uv \u7684\u76f8\u5173\u5185\u5bb9\u3002\r\n\r\n```bash\r\n# \u514b\u9686\u4ed3\u5e93\r\ngit clone https://github.com/271374667/QThreadWithReturn.git\r\ncd QThreadWithReturn\r\n\r\n# \u4f7f\u7528 uv \u5b89\u88c5\u4f9d\u8d56\r\nuv sync\r\n\r\n# \u8fd0\u884c\u6d4b\u8bd5\r\nuv run pytest\r\n\r\n# \u8fd0\u884c\u6f14\u793a\r\nuv run python -m demo.thread_demo_gui\r\n```\r\n\r\n## \ud83d\udcc4 \u8bb8\u53ef\u8bc1\r\n\r\n\u672c\u9879\u76ee\u4f7f\u7528 MIT \u8bb8\u53ef\u8bc1 - \u67e5\u770b [LICENSE](LICENSE) \u6587\u4ef6\u4e86\u89e3\u8be6\u60c5\u3002\r\n\r\n## \ud83d\udcde \u652f\u6301\r\n\r\n- **\u95ee\u9898\u62a5\u544a**: [GitHub Issues](https://github.com/271374667/QThreadWithReturn/issues)\r\n- **\u8ba8\u8bba**: [GitHub Discussions](https://github.com/271374667/QThreadWithReturn/discussions)\r\n- **\u90ae\u4ef6**: 271374667@qq.com\r\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "PySide6 \u9ad8\u7ea7\u7ebf\u7a0b\u5de5\u5177\u5e93 - \u5e26\u8fd4\u56de\u503c\u7684\u7ebf\u7a0b\u7c7b\u548c\u7ebf\u7a0b\u6c60\u6267\u884c\u5668",
    "version": "1.3.12",
    "project_urls": {
        "Homepage": "https://github.com/271374667/QThreadWithReturn",
        "Issues": "https://github.com/271374667/QThreadWithReturn/issues",
        "Repository": "https://github.com/271374667/QThreadWithReturn.git"
    },
    "split_keywords": [
        "qt",
        " pyside6",
        " threading",
        " concurrent",
        " futures",
        " gui",
        " pyqt",
        " async",
        " thread-pool",
        " worker-thread"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "e0e15fbd5d042c61468f0361054fd4031cd95ec12606046413724628dd098143",
                "md5": "a07bebd9cd5ca406a2001565050064ae",
                "sha256": "db4297099d33bcf56101769341c069151af814b9381530dcec03cde58f6054fa"
            },
            "downloads": -1,
            "filename": "qthreadwithreturn-1.3.12-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "a07bebd9cd5ca406a2001565050064ae",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.10",
            "size": 20088,
            "upload_time": "2025-10-09T08:40:31",
            "upload_time_iso_8601": "2025-10-09T08:40:31.386885Z",
            "url": "https://files.pythonhosted.org/packages/e0/e1/5fbd5d042c61468f0361054fd4031cd95ec12606046413724628dd098143/qthreadwithreturn-1.3.12-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "592207974dbaf1f8d952fcd586e9709ce84eb24be7578eb41ee2273956eba5b2",
                "md5": "f1e08cbe542ba305361d014e177dee48",
                "sha256": "3bfc3583606f5445ec00953e42b93cb368a6aaa94947ba545ebbcba840c0e67b"
            },
            "downloads": -1,
            "filename": "qthreadwithreturn-1.3.12.tar.gz",
            "has_sig": false,
            "md5_digest": "f1e08cbe542ba305361d014e177dee48",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.10",
            "size": 56157,
            "upload_time": "2025-10-09T08:40:32",
            "upload_time_iso_8601": "2025-10-09T08:40:32.876859Z",
            "url": "https://files.pythonhosted.org/packages/59/22/07974dbaf1f8d952fcd586e9709ce84eb24be7578eb41ee2273956eba5b2/qthreadwithreturn-1.3.12.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-10-09 08:40:32",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "271374667",
    "github_project": "QThreadWithReturn",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "qthreadwithreturn"
}
        
Elapsed time: 2.42787s