PyQt6+多进程打包exe报错:freeze_support ValueError 原因及解决
用PyQt6+multiprocessing开发的应用在编辑器中运行正常,但通过PyInstaller 6.10.0打包成exe后,在Windows11环境(Python 3.12.5、PyQt6 6.7.1)下运行出现以下报错:
Traceback (most recent call last): File "application.py", line 11, in <module> File "PyInstaller\hooks\rthooks\pyi_rth_multiprocessing.py", line 43, in _freeze_support ValueError: not enough values to unpack (expected 2, got 1) [PYI-31452:ERROR] Failed to execute script 'application' due to unhandled exception!
经排查,报错原因是gui.py中的代码sys.argv += ['-platform', 'windows:darkmode=1'],移除该行后程序可正常运行。以下是问题的原因分析及可行解决方案:
代码结构
application.py
import sys import multiprocessing import gui from PyQt6.QtWidgets import QApplication, QMainWindow if __name__ == '__main__': multiprocessing.freeze_support() app = QApplication(sys.argv) window = QMainWindow() gui.Gui().setupUi(window) window.show() sys.exit(app.exec())
gui.py
import sys import concurrent.futures import backend sys.argv += ['-platform', 'windows:darkmode=1'] class Gui: def __init__(self): self.process = None pass # more code def setupUi(self, mainwindow): initialization_multiprocessmanager_thread = concurrent.futures.ThreadPoolExecutor(max_workers=1) initialization_multiprocessmanager_thread.submit(self.initialization_multiprocessmanager, ) mainwindow.setWindowTitle("title") mainwindow.resize(1200, 680) pass # more code def initialization_multiprocessmanager(self): try: self.process = backend.Backend() print('backend done') except Exception as e: print('backend', e)
backend.py
import multiprocessing class Backend: def __init__(self): self.dict1 = multiprocessing.Manager().dict() self.dict2 = multiprocessing.Manager().dict() self.dict3 = multiprocessing.Manager().dict() def worker(self): pass # more code
报错原因分析
PyInstaller的multiprocessing运行时钩子(pyi_rth_multiprocessing.py)在执行multiprocessing.freeze_support()时,会解析命令行参数判断当前是否为子进程启动。由于application.py先导入了gui模块,gui.py中修改sys.argv的代码会在freeze_support()执行前生效。
当打包后的程序启动子进程时,钩子会尝试按键值对(如--key=value)的格式解析参数,但-platform和windows:darkmode=1是两个独立的列表元素,不符合钩子预期的解析逻辑,从而触发"not enough values to unpack"的错误。
可行解决方案
方案1:延迟修改sys.argv到合适时机
将修改sys.argv的代码从gui.py移到application.py中,放在multiprocessing.freeze_support()之后、QApplication(sys.argv)之前。这样既保证Qt能读取到平台参数,又不会干扰multiprocessing的钩子逻辑:
修改后的application.py:
import sys import multiprocessing import gui from PyQt6.QtWidgets import QApplication, QMainWindow if __name__ == '__main__': multiprocessing.freeze_support() # 在此处添加平台参数 sys.argv += ['-platform', 'windows:darkmode=1'] app = QApplication(sys.argv) window = QMainWindow() gui.Gui().setupUi(window) window.show() sys.exit(app.exec())
同时删除gui.py中的sys.argv += ['-platform', 'windows:darkmode=1']行。
方案2:通过Qt API直接设置深色模式
不修改命令行参数,改用Qt内置API设置深色模式,彻底避免触碰sys.argv:
在application.py中创建QApplication前添加以下代码(适配Qt6.7+):
from PyQt6.QtWidgets import QStyleFactory from PyQt6.QtGui import QPalette from PyQt6.QtCore import Qt if __name__ == '__main__': multiprocessing.freeze_support() app = QApplication(sys.argv) # 设置Fusion风格并应用深色调色板 app.setStyle(QStyleFactory.create("Fusion")) dark_palette = QPalette() # 自定义深色调色板(或直接使用系统主题) dark_palette.setColor(QPalette.ColorRole.Window, QColor(53, 53, 53)) dark_palette.setColor(QPalette.ColorRole.WindowText, Qt.GlobalColor.white) dark_palette.setColor(QPalette.ColorRole.Base, QColor(25, 25, 25)) dark_palette.setColor(QPalette.ColorRole.AlternateBase, QColor(53, 53, 53)) dark_palette.setColor(QPalette.ColorRole.ToolTipBase, Qt.GlobalColor.white) dark_palette.setColor(QPalette.ColorRole.ToolTipText, Qt.GlobalColor.white) dark_palette.setColor(QPalette.ColorRole.Text, Qt.GlobalColor.white) dark_palette.setColor(QPalette.ColorRole.Button, QColor(53, 53, 53)) dark_palette.setColor(QPalette.ColorRole.ButtonText, Qt.GlobalColor.white) dark_palette.setColor(QPalette.ColorRole.BrightText, Qt.GlobalColor.red) dark_palette.setColor(QPalette.ColorRole.Link, QColor(42, 130, 218)) dark_palette.setColor(QPalette.ColorRole.Highlight, QColor(42, 130, 218)) dark_palette.setColor(QPalette.ColorRole.HighlightedText, Qt.GlobalColor.black) app.setPalette(dark_palette) window = QMainWindow() gui.Gui().setupUi(window) window.show() sys.exit(app.exec())
方案3:修改PyInstaller钩子逻辑(不推荐)
找到PyInstaller安装目录下的hooks/rthooks/pyi_rth_multiprocessing.py,修改参数解析代码,使其兼容非键值对的参数。但这种方法会影响所有PyInstaller打包的程序,且PyInstaller更新后会覆盖修改,仅适合有经验的用户临时应急。
内容的提问来源于stack exchange,提问作者Alyssar

