0

I am still sort of new to tkinter and I am having trouble trying to switch a frame after some time has passed. I have a function called recognizer that will try and identify the user, and if they are recognized, the Successful frame will show up, and if the user is not recognized, it will increment a value i until it has gone over the number of tries they are given. When i is equal to 3, it will break out of the loop, and the Denied frame will show up.

My issue is being able to switch from the Successful/Denied frame to different after some time has passed. I tried using the tkinter after method to try and get the frame to show up, but so far have not gotten any luck. Would appreciate the help.

Simplified version of my code:

def recognize(self, i):
    with open('labels', 'rb') as f:
        dict = pickle.load(f)
        f.close()

    camera = PiCamera()
    camera.resolution = (640, 480)
    camera.framerate = 30
    rawCapture = PiRGBArray(camera, size = (640, 480))

    faceCascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")
    recognizer = cv2.face.LBPHFaceRecognizer_create()
    recognizer.read("trainer.yml")
    #i = 0
    ex = True
    time.sleep(3)
    for frame in camera.capture_continuous(rawCapture, format = "bgr", use_video_port = True):
        frame = frame.array
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        faces = faceCascade.detectMultiScale(
            gray,
            scaleFactor = 1.2,
            minNeighbors = 5,
            minSize= (300,300),
            )

        for(x,y,w,h) in faces:
            cv2.rectangle(frame, (x,y), (x+w, y+h), (0,255,0), 2)
            _id,confidence = recognizer.predict(gray[y:y+h, x:x+w])
            for name, value in dict.items():
                if value == _id:
                    print(name)
                    print(str(confidence))
                    if i == 3:
                        self.controller.show_frame("Denied")
                        print("Too many tries, exiting program")
                        time.sleep(5)
                        cv2.destroyAllWindows()
                        time.sleep(1)
                        ex = False
                        break
                    if confidence <= 40:
                        print("Welcome: " + name + "\nUnlocking door")
                        self.controller.show_frame("Successful")
                        time.sleep(5)
                        print("Locking door")
                        cv2.destroyAllWindows()
                        ex = False
                        break
                    else:
                        print("Unknown,Please trying again!")
                        time.sleep(1)
                        i+=1
            #self.after(15000, self.controller.show_frame, "HomeScreen")
            break
        cv2.imshow('frame', frame)
        key = cv2.waitKey(1)
        rawCapture.truncate(0)
        if ex == False:
            break
        if key == 27:
            break
        #break
    cv2.destroyAllWindows()
    camera.close()

class FaceRecognition(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)
        container.grid_rowconfigure(0, weight = 1)
        container.grid_columnconfigure(0, weight = 1)

        self.frames = {}
        for F in (HomeScreen, Successful, Denied):
            page_name = F.__name__
            frame = F(parent = container, controller = self)
            self.frames[page_name] = frame

            # put all of pages in same location;
            # the one on top of stacking order will be visible
            frame.grid(row = 0, column = 0, sticky = "nsew")
    def show_frame(self, page_name):
        '''Show a frame for the given page name'''
        frame = self.frames[page_name]
        frame.tkraise()


class HomeScreen(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        self.configure(background = 'teal')

        SignIn = tk.Button(self, text = "Profiles", font = "Arial 30", fg = "yellow", bg = "green", command = lambda: self.controller.show_frame("Profiles"))
        SignIn.place(x=200, y = 200)

        Create = tk.Button(self, text = "User Passcode", font = "Arial 30")
        Create.place(x=600, y=200)

        scan = tk.Button(self, text = "Face Scan", font = "Arial 30", fg = "yellow", bg = "green", command = lambda:[self.controller.show_frame("Display"), self.aquire()])
        scan.place(x=400, y=300)
        
    def aquire(self):
        self.after(500, recognize, self, 0)

class Display(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        self.configure(background = 'teal')
        
        label = tk.Label(self, text = "Please Stay Still \nto get Face Scanned", bg = 'teal', fg = 'yellow', font = "Arial 30")
        label.pack()

class Successful(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        self.configure(background = 'green')

        success = tk.Label(self, text = "FACIAL SCAN SUCCESSFUL\nWELCOME", bg = 'green', fg = 'yellow', font = "Arial 30")
        success.place(x=512, y = 200, anchor = "center")

class Denied(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        self.configure(background = 'red')

        denied = tk.Label(self, text = "FACIAL SCAN DENIED\nGOING BACK TO HOME SCREEN", bg = 'red', fg = 'green', font = "Arial 30")
        denied.place(x=512, y = 200, anchor = "center")
Darth_Yoda
  • 37
  • 3
  • 1
    Don't use `time.sleep` when using `tkinter`. Instead use `.after(time_in_ms, function, *args)`. For more info read [this](https://stackoverflow.com/questions/25753632/tkinter-how-to-use-after-method) – TheLizzard Apr 12 '21 at 20:54
  • IS there a way I can use .after to switch frames? – Darth_Yoda Apr 12 '21 at 21:05
  • Yes but you will have to redesign your whole code... Move everything after the big `for` loop in a function when just use `self.controller.after(3000, function)`. You will have to do something similar with the other `time.sleep` calls – TheLizzard Apr 12 '21 at 21:08
  • Please reduce this code down to a [mcve]. If you're trying to learn how to automatically switch after a delay, your example should have two pages and just enough code to do the switch. That will help you (and us) to focus and understand the root of the problem. – Bryan Oakley Apr 12 '21 at 21:38
  • @TheLizzard Thank you that worked. – Darth_Yoda Apr 13 '21 at 20:55

0 Answers0