<div align="center">
# QThreadWithReturn

[](https://python.org)
[](https://www.qt.io/qt-for-python)
[](LICENSE)
[](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\r\n\r\n[](https://python.org)\r\n[](https://www.qt.io/qt-for-python)\r\n[](LICENSE)\r\n[](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"
}