如何在tkinter Frame中调用OpenCV摄像头并按需单独关闭
问题根因
- 你将
VideoCapture实例、帧刷新的after任务ID、显示画面的Label都定义为refresh方法的局部变量,方法执行结束后外部无法访问这些资源,无法主动释放 frame.quit()的作用是终止整个Tkinter主事件循环,自然会退出整个应用,和关闭摄像头没有关联- 切换页面时没有触发摄像头资源释放的逻辑,导致OpenCV一直占用摄像头,帧刷新任务也在后台持续运行
修复方案
1. 修改主类tkinterApp的show_frame方法
增加切换页面前停止当前页面摄像头的逻辑:
def show_frame(self, cont): # 先停止当前显示页面的摄像头(如果存在stop_camera方法) for frame in self.frames.values(): if frame.winfo_ismapped() and hasattr(frame, 'stop_camera'): frame.stop_camera() break frame = self.frames[cont] if hasattr(frame, 'refresh'): frame.refresh() frame.tkraise()
2. 修改FrontCameraPage页面代码
将摄像头相关资源定义为实例属性,新增stop_camera方法:
import cv2 import PIL.Image, PIL.ImageTk import time class FrontCameraPage(tk.Frame): def __init__(self, parent, controller): tk.Frame.__init__(self, parent) self.controller = controller # 提前定义摄像头相关实例属性 self.cap = None self.after_id = None self.lmain = None self.label = tk.Label(self, text="FRONT CAMERA", font=MediumFont, bg="white").grid(row = 0,column = 0,columnspan=2, sticky = "nsew") self.cameraFrame = tk.Frame(self, bg=gray) self.cameraFrame.grid(row = 1,column = 0, sticky = "nsew") self.buttonFrame = tk.Frame(self,bg = "white") self.buttonFrame.grid(row = 1,column = 1, sticky = "nsew",padx = (10,0)) self.Info = tk.Label(self.buttonFrame, text="FACE NOT\n DETECTED", font=MediumFont,bg = dark_red,fg ="white") self.Info.grid(row=0, column=0, ipadx=8,pady = (0,5)) self.switchCamera = tk.Button(self.buttonFrame, text="SWITCH CAMERA", font=small_Font, bg=dark_blue, command=lambda: self.switch_camera(),fg ="white") self.switchCamera.grid(row=1, column=0, ipadx=5,pady = (0,5)) # 绑定结束行程按钮到stop_camera方法,可自行扩展后续跳转逻辑 self.endTrip = tk.Button(self.buttonFrame, text="END TRIP", font=small_Font, bg=dark_blue, command=self.end_trip,fg="white") self.endTrip.grid(row=2, column=0, ipadx=10,pady = (0,5)) # 取消按钮示例,绑定返回其他页面逻辑 self.cancelButton = tk.Button(self.buttonFrame, text="Cancel", font=small_Font, bg=dark_blue, command=lambda: controller.show_frame(startPage.StartPage),fg="white") self.cancelButton.grid(row=3, column=0, ipadx=10) def end_trip(self): self.stop_camera() # 此处可添加行程结束后的业务逻辑、页面跳转逻辑等 self.controller.show_frame(startPage.StartPage) def stop_camera(self): # 先停止帧刷新循环 if self.after_id: self.after_cancel(self.after_id) self.after_id = None # 释放摄像头资源 if self.cap and self.cap.isOpened(): self.cap.release() self.cap = None # 清空画面显示 if self.lmain: self.lmain.config(image="") self.lmain = None def refresh(self): time.sleep(1) width, height = 200, 200 # 初始化摄像头存入实例属性 self.cap = cv2.VideoCapture(0) self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, width) self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height) # 显示画面的Label存入实例属性 self.lmain = tk.Label(self.cameraFrame) self.lmain.pack() def show_frame(): if not self.cap or not self.cap.isOpened(): return _, frame = self.cap.read() frame = cv2.flip(frame, 1) cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA) img = PIL.Image.fromarray(cv2image) imgtk = ImageTk.PhotoImage(image=img) self.lmain.imgtk = imgtk self.lmain.configure(image=imgtk) # 保存after任务id到实例属性 self.after_id = self.after(10, show_frame) show_frame()
修改后可实现效果:
- 进入摄像头页面自动启动摄像头
- 切换到其他页面时自动停止摄像头释放资源
- 点击结束行程/取消按钮时停止摄像头,不会退出整个应用
内容的提问来源于stack exchange,提问作者stark
相关产品推荐
相关产品推荐

