PyQt5 使用 Pyinstaller+multiprocessing 打包多进程应用时,引发的一些问题

解决 Pyinstaller 打包 PyQt5+multiprocessing 多进程应用时,引发的一些问题,包括反复启动主进程,以及:AttributeError: 'NoneType' object has no attribute 'write'

本文提供一些解决方案,您可能需要根据自己的实际情况,逐个尝试,直到自己的multiprocessing多进程应用正常运行

一、解决反复启动 GUI 主进程的问题

这个问题的产生原因,只知道是与Windows系统有关,因为Python官方在multiprocessing库中指明,这个方法只影响windows系统,且主要针对生成可执行文件,也就是Windows 下的 exe程序
官方原文:multiprocessing.freeze_support()

问题复现

1. 准备示例程序

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
@ File        : test_multiprocessing.py
@ Author      : yqbao
@ Version     : V1.0.0
@ Description : 
"""
import sys
from multiprocessing import Process
import time
from PyQt5.QtWidgets import QApplication, QPushButton, QVBoxLayout, QWidget


def background_task():
    while True:
        print("后台进程正在运行...")
        time.sleep(3)  # 模拟一些后台工作


class MainWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle('PyQt5 与 Multiprocessing')
        layout = QVBoxLayout()

        self.start_button = QPushButton('启动后台进程')
        self.start_button.clicked.connect(self.start_process)

        layout.addWidget(self.start_button)
        self.setLayout(layout)

    def start_process(self):
        self.process = Process(target=background_task)
        self.process.start()
        print("后台进程已启动。")

    def closeEvent(self, event):
        self.process.terminate()
        self.process.join()
        event.accept()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

运行后,可以得到下面这样的窗口
image

2. 使用下面的两种方式打包

# 方式1,无 -w
pyinstaller -D -y .\test_process.py

# 方式2,有 -w
pyinstaller -D -w -y .\test_process.py

无论那种方式,启动后,若点击“启动后台进程”均无法成功,且主进程会反复启动
image

解决办法

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
@ File        : test_multiprocessing.py
@ Author      : yqbao
@ Version     : V1.0.0
@ Description : 
"""

from multiprocessing import freeze_support

... # 忽略,无变化

if __name__ == '__main__':
    freeze_support()  # 使用冻结

    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

无论那种打包方式,启动后,均正常运行
image

二、解决AttributeError: 'NoneType' object has no attribute 'write'

这个问题,您不一定有,因为这和你的应用程序有关,上文我们已经通过添加freeze_support()来解决主程序 GUI 反复重启的问题,但是某些时候,当你用pyinstaller -D -w -y .\test_process.py这种无控制台窗口模式(也就是黑窗口)时,就会出现这个问题:
如下图:
image

推测产生的原因

可能是使用-w无控制台窗口模式时,标准输出被重定向了,导致相关函数无法得到正确初始化,于是反应出来就是NoneType

解决办法

方法1:尝试使用--noconsole 代替 --windowed(-w)

此参数是完全禁用控制台窗口,而不是重定向

方法2:手动重定向输出

```python
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
@ Author      : yqbao
@ Version     : V1.0.0
@ Description :
"""

... # 忽略,您自己的应用程序


if __name__ == "__main__":
    sys.stdout = open('output.txt', 'w')  # 仅在打包时添加手动重定向
    sys.stderr = open('error.txt', 'w')  # 仅在打包时添加手动重定向
    freeze_support()

    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    exit_code = app.exec_()
    sys.stdout.close()  # 仅在打包时添加关闭文件流
    sys.stderr.close() # 仅在打包时添加关闭文件流
    sys.exit(exit_code)

本文章的原文地址
GitHub主页

posted @ 2024-10-16 12:16  星尘的博客  阅读(6)  评论(0编辑  收藏  举报