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

使用PyInstaller打包Python程序为exe后遇figurecanvasagg交互性报错求助

解决打包后Matplotlib非交互式后端问题

问题原因

你遇到的警告是因为Matplotlib在打包环境中默认使用了非交互式的Agg后端,而你的程序基于Tkinter构建,后端不匹配导致无法正常显示图像。安装PyQt6无效是因为程序未使用Qt框架,对应的QtAgg后端和Tkinter环境不兼容。

解决方案

1. 指定Matplotlib后端为TkAgg

在导入matplotlib相关模块前,强制设置后端为TkAgg,确保和Tkinter环境匹配:

import matplotlib
# 指定适配Tkinter的后端
matplotlib.use('TkAgg')
from matplotlib import pyplot as pl
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

2. 修改图像显示逻辑(适配Tkinter框架)

替换原代码中的pl.show(),将Matplotlib图像嵌入到Tkinter窗口中,避免独立弹窗的兼容性问题。修改ox_photo函数末尾的绘图代码:

# 原代码:
# pl.imshow(cv2.cvtColor(final_image, cv2.COLOR_BGR2RGB))
# pl.show()

# 替换为:
# 创建Matplotlib画布
fig = pl.figure(figsize=(10, 8))
pl.imshow(cv2.cvtColor(final_image, cv2.COLOR_BGR2RGB))
pl.axis('off')  # 隐藏坐标轴

# 在Tkinter弹窗中显示图像
img_window = tk.Toplevel(root)
img_window.title("识别结果")
canvas = FigureCanvasTkAgg(fig, master=img_window)
canvas.draw()
canvas.get_tk_widget().pack()

3. 打包时确保依赖被正确包含

使用PyInstaller打包时,添加参数确保Matplotlib的Tk后端依赖被完整打包:

pyinstaller --onefile --windowed --hidden-import=matplotlib.backends.backend_tkagg --hidden-import=tkinter your_script_name.py
  • --onefile:生成单个可执行文件
  • --windowed:隐藏控制台窗口(不需要控制台输出时使用)
  • --hidden-import:手动指定PyInstaller未自动检测到的依赖项

修改后的完整代码示例

import cv2
import numpy as np
import imutils
import easyocr
import matplotlib
matplotlib.use('TkAgg')
from matplotlib import pyplot as pl
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import tkinter as tk
from tkinter import filedialog

def ox_photo(filepath : str):
    img = cv2.imread(filepath)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    img_filter = cv2.bilateralFilter(gray, 11, 15, 15)
    edges = cv2.Canny(img_filter, 30, 200)

    cont = cv2.findContours(edges.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    cont = imutils.grab_contours(cont)
    cont = sorted(cont, key=cv2.contourArea, reverse=True)

    pos = None
    for c in cont:
        approx = cv2.approxPolyDP(c, 10, True)

        if len(approx) == 4:
            pos = approx
            break

    mask = np.zeros(gray.shape, np.uint8)
    new_img = cv2.drawContours(mask, [pos], 0, 255, -1)
    bitwise_img = cv2.bitwise_and(img, img, mask=mask)

    (z, v) = np.where(mask == 255)
    (z1, v1) = (np.min(z), np.min(v))
    (z2, v2) = (np.max(z), np.max(v))
    crop = gray[z1:z2, v1:v2]

    text = easyocr.Reader(['en'])
    text = text.readtext(crop)

    res = text[0][-2]
    final_image = cv2.putText(img, res, (z1, v2 - 100), cv2.FONT_HERSHEY_PLAIN, 5, (255, 255, 255), 5)
    final_image = cv2.rectangle(img, (v1, z1), (v2, z2), (0, 255, 0), 5)

    # 嵌入到Tkinter窗口显示结果
    fig = pl.figure(figsize=(10, 8))
    pl.imshow(cv2.cvtColor(final_image, cv2.COLOR_BGR2RGB))
    pl.axis('off')
    
    img_window = tk.Toplevel(root)
    img_window.title("识别结果")
    canvas = FigureCanvasTkAgg(fig, master=img_window)
    canvas.draw()
    canvas.get_tk_widget().pack()

def open_image():
    filepath = filedialog.askopenfilename(title="Выберите картинку", filetypes=[("Image Files", ".jpg .jpeg .png .bmp")])
    if filepath:
        ox_photo(filepath)

root = tk.Tk()
root.title("Выбор картинки")

label = tk.Label(root, text="Выберите картинку:")
label.pack()

button = tk.Button(root, text="Обзор", command=open_image)
button.pack()

root.mainloop()

额外注意事项

  • 如果不需要嵌入Tkinter窗口,仅修改后端设置并保留pl.show(),打包时添加对应hidden-import参数也可解决问题,但嵌入方式的兼容性更好。
  • 打包前确保所有依赖包(matplotlib、tkinter、cv2等)为最新稳定版本。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 05:07:22