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

Ultralytics+Tkinter+PyInstaller打包EXE后加载YOLO模型异常

问题描述

基于Tkinter和Ultralytics YOLO开发的程序经PyInstaller打包为EXE后,加载YOLO模型时出现以下异常:

  • 主窗口冻结
  • 控制台短暂弹出
  • 生成一个相同的新窗口,关闭新窗口后程序可正常运行,无其他报错

调试环境(VS2022)中无此问题,仅打包后出现,所有模型文件与EXE处于同一目录。

复现代码:

from io import StringIO
memory_buffer = StringIO()
sys.stdout = memory_buffer
import tkinter as tk
from ultralytics import YOLO
import cv2 as cv
global testimg
testimg = cv.imread("testimg.jpg")

class MyGUI:
    def __init__(self):
        self.root = tk.Tk() # root here is window name now
        self.root.geometry("600x400")
        self.root.columnconfigure(0, weight = 1)
        self.root.rowconfigure(0, weight = 1)
        self.root.rowconfigure(1, weight = 1)
        self.root.rowconfigure(2, weight = 1)
        self.buttonmodel1 = tk.Button(self.root, text = "Model 1", bg = "#c9c9c9", bd= 5, width = 200, command = self.button1pressed)
        self.buttonmodel1.grid(row =0, column=0, sticky= "WENS", padx = 3)
        self.buttonmodel2 = tk.Button(self.root, text = "Model 2", bg = "#c9c9c9", bd= 5, width = 200)
        self.buttonmodel2.grid(row =1, column=0, sticky= "WENS", padx = 3)
        self.buttonmodel3 = tk.Button(self.root, text = "Model 3", bg = "#c9c9c9", bd= 5, width = 200)
        self.buttonmodel3.grid(row =2, column=0, sticky= "WENS", padx = 3)
        self.root.mainloop() 
        
    def button1pressed(self):
        global testimg
        model1 = YOLO("model1.pt")
        model2 = YOLO("model2.pt")
        model3 = YOLO("model3.pt")
        resultsmodel1 = model1.predict(testimg)
        print("model1")
        resultsmodel2 = model2.predict(testimg)
        print("model2")
        resultsmodel3 = model3.predict(testimg)
        print("model3")
MyGUI()

打包命令:

pyinstaller --collect-data ultralytics Model_Test.py --onefile --windowed
解决建议

1. 用线程隔离模型操作,避免阻塞Tkinter主循环

Tkinter是单线程框架,耗时的模型加载、推理会阻塞主循环导致窗口冻结,甚至触发异常窗口。将模型操作放到子线程中执行:

修改后代码:

from io import StringIO
import sys
import os
import threading
import tkinter as tk
from ultralytics import YOLO
import cv2 as cv

# 禁用YOLO冗余日志,避免触发控制台
os.environ["YOLO_VERBOSE"] = "false"
# 重定向stdout和stderr,彻底屏蔽输出
memory_buffer = StringIO()
sys.stdout = memory_buffer
sys.stderr = memory_buffer

class MyGUI:
    def __init__(self):
        self.root = tk.Tk()
        self.root.geometry("600x400")
        self.root.columnconfigure(0, weight=1)
        for i in range(3):
            self.root.rowconfigure(i, weight=1)
        
        # 改用类内变量存储图像,避免全局变量问题
        self.testimg = cv.imread("testimg.jpg")
        
        self.buttonmodel1 = tk.Button(
            self.root, text="Model 1", bg="#c9c9c9", bd=5, width=200, 
            command=self.run_model_task
        )
        self.buttonmodel1.grid(row=0, column=0, sticky="WENS", padx=3)
        self.buttonmodel2 = tk.Button(self.root, text="Model 2", bg="#c9c9c9", bd=5, width=200)
        self.buttonmodel2.grid(row=1, column=0, sticky="WENS", padx=3)
        self.buttonmodel3 = tk.Button(self.root, text="Model 3", bg="#c9c9c9", bd=5, width=200)
        self.buttonmodel3.grid(row=2, column=0, sticky="WENS", padx=3)
        self.root.mainloop() 
        
    def run_model_task(self):
        # 启动子线程执行模型操作,不阻塞主循环
        threading.Thread(target=self.process_models, daemon=True).start()
        
    def process_models(self):
        # 获取当前EXE绝对路径,避免打包后路径错误
        current_dir = os.path.dirname(os.path.abspath(__file__))
        # 加载模型时禁用verbose,减少内部输出
        model1 = YOLO(os.path.join(current_dir, "model1.pt"), verbose=False)
        model2 = YOLO(os.path.join(current_dir, "model2.pt"), verbose=False)
        model3 = YOLO(os.path.join(current_dir, "model3.pt"), verbose=False)
        # 推理时禁用窗口显示,避免Ultralytics创建额外窗口
        resultsmodel1 = model1.predict(self.testimg, show=False)
        resultsmodel2 = model2.predict(self.testimg, show=False)
        resultsmodel3 = model3.predict(self.testimg, show=False)

MyGUI()

2. 优化PyInstaller打包参数

使用更严谨的打包命令,确保Ultralytics依赖资源完整打包,彻底禁用控制台:

pyinstaller --collect-data ultralytics --collect-binaries ultralytics --noconsole --onefile Model_Test.py
  • --collect-binaries ultralytics:确保Ultralytics依赖的二进制文件(如推理引擎)被正确打包
  • --noconsole:彻底屏蔽控制台窗口,替代--windowed(部分场景下--windowed仍会触发临时控制台)

3. 排查额外窗口触发原因

如果问题仍存在,可检查Ultralytics的初始化逻辑,避免其内部意外创建Tkinter实例:

  • 确保所有YOLO操作都在子线程中执行
  • 禁用YOLO的任何可视化相关参数(如show=False、verbose=False)
问题根源分析
  1. Tkinter单线程阻塞:耗时的模型操作阻塞主循环,导致窗口无响应,同时Ultralytics内部的日志或初始化操作可能意外触发新的Tkinter窗口。
  2. PyInstaller资源缺失:Ultralytics依赖的部分二进制文件未被正确打包,运行时触发异常流程。
  3. 未屏蔽完整输出:仅重定向stdout未处理stderr,Ultralytics的错误日志触发临时控制台窗口。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 14:32:33