函数内使用cv2.VideoCapture()导致tkinter窗口冻结无报错如何解决
问题根因
- Tkinter采用单线程事件驱动模型,所有界面更新、事件响应都依赖
root.mainloop()主循环调度执行。你代码中的无限while循环直接抢占了主线程资源,导致主循环永远无法执行,因此窗口会直接冻结无响应。 - 原有代码还存在隐含问题:没有保留
ImageTk.PhotoImage对象的引用,Python垃圾回收机制会销毁局部变量img,就算窗口不冻结也会出现画面空白的问题。
修复实现方案
以下是符合你需求的可运行代码:点击主窗口按钮唤起Toplevel子窗口显示摄像头实时画面,所有子窗口逻辑全部封装在触发函数内,不会出现窗口冻结问题:
import tkinter as tk import cv2 from PIL import Image, ImageTk root = tk.Tk() root.configure(bg='#3d3d3d') root.geometry('300x200') def open_camera_window(): # 子窗口全部逻辑封装在当前触发函数内 top = tk.Toplevel(root) top.title("人脸识别摄像头") f1 = tk.LabelFrame(top, bg='#3d3d3d') f1.pack(padx=10, pady=10) feed = tk.Label(f1) feed.pack() cap = cv2.VideoCapture(0) # 给Label绑定属性保存图像引用,避免被GC回收 feed.curr_img = None def update_frame(): if cap.isOpened(): ret, img = cap.read() if not ret: return img = cv2.flip(img, 1) img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) feed.curr_img = ImageTk.PhotoImage(Image.fromarray(img_rgb)) feed.config(image=feed.curr_img) # 间隔30ms更新一次,对应约30fps,可自行调整间隔 feed.after(30, update_frame) # 绑定子窗口关闭事件,释放摄像头资源 def on_close_window(): cap.release() cv2.destroyAllWindows() top.destroy() top.protocol("WM_DELETE_WINDOW", on_close_window) update_frame() # 主窗口触发按钮 open_btn = tk.Button(root, text="启动人脸识别", command=open_camera_window) open_btn.place(relx=0.5, rely=0.5, anchor=tk.CENTER) root.mainloop()
关键改动说明
- 移除无限
while循环,改用Tkinter自带的after()方法,将帧更新操作插入到主事件队列中调度执行,不会阻塞主循环 - 给显示画面的Label绑定属性存储
PhotoImage对象,避免图像被垃圾回收导致空白 - 绑定子窗口关闭回调,关闭时自动释放摄像头资源,避免设备被占用无法二次打开
- 所有子窗口、摄像头调度逻辑都封装在按钮触发函数内,符合你的开发要求
内容的提问来源于stack exchange,提问作者NISH3003
相关产品推荐
相关产品推荐

