Home > front end >  how to open camera in a tkinter frame and close only the frame/camera when needed
how to open camera in a tkinter frame and close only the frame/camera when needed

Time:12-03

I have a tkinter multi-frame application that involves multiple page/frames in one page I want to have a camera using open cv and the camera should close when I navigate to some other screen/page and should start automatically when the frames comes on screen or is in focus the frame is comming when I navigate to a page but it is not closing and always running in the background when I use frame.quit() it closes the whole application but I only want to close that camera

The main class which controls navigation

import tkinter as tk
import connectionPage
import startPage
// it has more than 15 pages but for example I have removed others

class tkinterApp(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        container = tk.Frame(self)
        container.pack(side="top", fill="both", expand=True)
        self.frames = {}
        self.geometry(geometry)
        self.attributes("-fullscreen", True)
        StartPage=startPage.StartPage
        ConnectionPage=connectionPage.connect_page
        FrontCameraPage = frontCameraPage.FrontCameraPage
        for F in (StartPage, ConnectionPage,FrontCameraPage):
            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(row=0, column=0, sticky="nsew")

        self.show_frame(startPage.StartPage)
    def show_frame(self, cont):
        frame = self.frames[cont]
        if hasattr(frame, 'refresh'):
            frame.refresh()
        frame.tkraise()

    def get_page(self, page_class):
        return self.frames[page_class]


if __name__ == "__main__":
    app = tkinterApp()
    app.mainloop()

my camera frame/page

class FrontCameraPage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller

        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))

        def end_trip():
            self.cameraFrame.quit()# right now i am using quit but it is closing whole applicaiton i just want to close camera 



        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))

        self.endTrip = tk.Button(self.buttonFrame, text="END TRIP", font=small_Font, bg=dark_blue, command=pass = "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=pass,fg="white")
        self.cancelButton.grid(row=3, column=0, ipadx=10)
        


    def refresh(self):
        time.sleep(1)
        width, height = 200, 200
        cap = cv2.VideoCapture(0)
        cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
        cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
        lmain = tk.Label(self.cameraFrame)
        lmain.pack()

        def show_frame():
            _, frame = cap.read()
            frame = cv2.flip(frame, 1)
            cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
            img = PIL.Image.fromarray(cv2image)
            imgtk = ImageTk.PhotoImage(image=img)
            lmain.imgtk = imgtk
            lmain.configure(image=imgtk)
            lmain.after(10, show_frame)
        show_frame()

the camera should close when I change the frame and go to another frame or click any button.

CodePudding user response:

One of the way is to use virtual events to notify the camera frame when it is being switched in or out.

When the camera frame receives those virtual events, it can then start or stop the capturing based on the type of the event.

Below is the modified FrontCameraPage:

class FrontCameraPage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller

        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))

        self.endTrip = tk.Button(self.buttonFrame, text="END TRIP", font=small_Font, bg=dark_blue, fg = "White")
        self.endTrip.grid(row=2, column=0, ipadx=10,pady = (0,5))
        self.endTrip['command'] = self.stop_capture

        self.cancelButton = tk.Button(self.buttonFrame, text="Cancel", font=small_Font, bg=dark_blue, fg="white")
        self.cancelButton.grid(row=3, column=0, ipadx=10)
        self.cancelButton['command'] = lambda: controller.show_frame(startPage.StartPage)

        # setup callbacks for switching in and out events
        self.bind('<<SwitchIn>>', self.start_capture)
        self.bind('<<SwitchOut>>', self.stop_capture)

        self.capture = None  # task id for the capture loop

        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 for showing the captured image
        self.lmain = tk.Label(self.cameraFrame)
        self.lmain.pack()

    def start_capture(self, event=None):
        if self.capture is None:
            # start the capture loop
            self.show_frame()
            print('capture started')

    def stop_capture(self, event=None):
        if self.capture:
            # stop the capture loop
            self.after_cancel(self.capture)
            self.capture = None
            print('capture stopped')

    def show_frame(self):
        ret, frame = self.cap.read()
        if ret:
            frame = cv2.flip(frame, 1)
            cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
            img = Image.fromarray(cv2image)
            self.imgtk = ImageTk.PhotoImage(image=img)
            self.lmain.configure(image=self.imgtk)
        self.capture = self.after(10, self.show_frame)

And below is the modified tkinterApp:

class tkinterApp(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)

        ...

        self.frame = None # the current frame shown
        self.show_frame(startPage.StartPage)

    def show_frame(self, cont):
        if self.frame:
            # trigger the switching out handler of the frame
            self.frame.event_generate('<<SwitchOut>>')
        self.frame = self.frames[cont]
        self.frame.tkraise()
        # trigger the switching in handler of the frame
        self.frame.event_generate('<<SwitchIn>>')

    ...
  • Related