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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 07:24:05