You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

打包后的Kivy应用多线程时不断弹出新窗口问题求助

解决PyInstaller打包Kivy应用后多进程触发弹出新窗口的问题

你猜的完全正确——这个问题的核心就是PyInstaller打包后,多进程场景下子进程会重新执行主脚本的全部代码,而不是像直接运行Python脚本那样,子进程的__name__不会是__main__。这就导致每次触发多进程操作时,子进程都会重新初始化Kivy应用,弹出新窗口。

下面是具体的解决步骤,分代码修改和打包配置调整两部分:

1. 重构主脚本,隔离主进程启动逻辑

首先修改你的主脚本,确保只有主进程才会初始化Kivy应用,同时处理PyInstaller的多进程兼容问题:

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder
import multiprocessing

# 必须放在脚本开头,处理PyInstaller打包后的多进程支持
multiprocessing.freeze_support()

class UI(Widget):
    pass

class UIApp(App):
    def build(self):
        # 延迟导入Window,避免子进程加载时触发初始化
        from kivy.core.window import Window
        Window.bind(on_key_down=self.key_action)
        return UI()
    
    def key_action(self, *args):
        self.process()
    
    def on_start(self, **kwargs):
        pass
    
    def on_stop(self):
        raise SystemExit(0)
    
    def process(self):
        # 延迟导入OtherFile,避免子进程提前加载并执行其中的代码
        import OtherFile
        OtherFile.test()

if __name__ == "__main__":
    # 所有启动Kivy的代码严格放在这个块内,子进程不会执行
    Builder.load_file('UI.kv')
    UIApp().run()

2. 检查并优化子进程代码

如果OtherFile.test()里用了multiprocessing创建子进程,确保子进程的执行逻辑不会涉及Kivy窗口初始化。比如如果OtherFile.py里有类似这样的代码:

import multiprocessing

def _worker_task():
    # 这里只放你的业务逻辑,不要导入或初始化Kivy相关内容
    print("子进程执行任务")

def test():
    p = multiprocessing.Process(target=_worker_task)
    p.start()
    p.join()

确保_worker_task里没有任何Kivy相关的操作,否则即使主脚本处理了,子进程还是会触发Kivy窗口创建。

3. 调整Spec文件适配单文件打包

你的原始spec文件和打包命令存在冲突(命令行的-singlefile会覆盖spec里的配置),修改spec文件来支持单文件打包:

# -*- mode: python -*-
import sys
from os import path
site_packages = next(p for p in sys.path if 'site-packages' in p)
from kivy.deps import sdl2, glew
block_cipher = None

# 注意路径用原始字符串避免转义问题
proj_path = r'C:\Users\GFuel\PycharmProjects\proj'

a = Analysis(
    ['Main.py'],
    pathex=[proj_path],
    binaries=[],
    datas=[],
    hiddenimports=['kivy.core.window'],  # 添加必要的隐藏导入
    hookspath=[],
    runtime_hooks=[],
    excludes=[],
    win_no_prefer_redirects=False,
    win_private_assemblies=False,
    cipher=block_cipher,
    noarchive=False
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)

# 单文件打包不需要COLLECT,直接把所有资源打包进EXE
exe = EXE(
    pyz,
    a.scripts,
    a.binaries,
    a.zipfiles,
    a.datas,
    *[Tree(p) for p in (sdl2.dep_bins + glew.dep_bins)],
    name='AppName',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    console=False,  # 窗口模式设为False,控制台模式设为True
    disable_windowed_traceback=False
)

然后用命令重新打包:

PyInstaller AppName.spec

关键原理说明

  • multiprocessing.freeze_support():告诉PyInstaller打包后的可执行文件,在启动子进程时跳过主脚本的启动逻辑,只执行子进程的目标任务。
  • 延迟导入:避免子进程在启动时加载Kivy的窗口模块或你的业务模块,防止意外初始化Kivy应用。
  • 严格的if __name__ == '__main__':隔离:确保只有主进程才会执行Kivy应用的启动代码,子进程不会进入这个分支。

内容的提问来源于stack exchange,提问作者Dodeius

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 04:17:06