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