TransWikia.com

Give a delay on Entry textchanged event in python and tkinter

Stack Overflow Asked by rickifernando on January 29, 2021

I have a problem in my code ,

I was using python and tkinter to develop my programs,

but i have a problem that make me stuck for a while ,

i was build an Employee Attandance application,

An employee have an ID ,
so in my application Employee input his/her id on textboxt in my form

like this
enter image description here

the problem is ,each employee has len(id) between 9 and 10

so i think i create an event triggered start after len(id) input is 9

ids = StringVar(value="")
ids.trace("w", lambda name, index, mode, ids=ids: textchanged(ids))
inputid = tkinter.Entry(window,font="Helvetica 20 bold",textvariable=ids)
inputid.pack()
inputid.place(relx=0.5,rely=0.187,width=300,height=30,anchor='sw',)
inputid.size()

def textchanged(ids):
id = ids.get()
if len(id) >= 9:
    time.sleep(3)
    print(id)
    compareimage(id)

the problem is ,the textbox stop to get input after 8 char id input in textbox
I can’t input until 9 length id in my textbox

but in my console it can still print untul 9 lenght id

enter image description here

how can i solve this,

can anyone please help me?

i add this code to my question

def compareimage(id):
    try:
        path = folder + id
        ClassNames = []
        mylist = os.listdir(path)
        for cls in mylist:
            curImg = cv2.imread(f'{path}/{cls}')
            image.append(curImg)
            ClassNames.append(os.path.splitext(cls)[0])
        encodeListKnown = finencode(image)
        print(encodeListKnown)
        print(ClassNames)
        print(mylist)
        time.sleep(3)
        while (True):
            ret, frames = cap.read()
            frames =cv2.flip(frames,1)
            gray = cv2.cvtColor(frames, cv2.COLOR_BGR2RGB)
            imgS = cv2.resize(frames, (0, 0), None, 0.25, 0.25)
            imgS = cv2.resize(frames, (0, 0), None, 0.25, 0.25)
            imgS = cv2.cvtColor(imgS, cv2.COLOR_BGR2RGB)
            facecurentframe = face_recognition.face_locations(imgS)
            encodecurframe = face_recognition.face_encodings(imgS, facecurentframe)
            encodelistface = []
            for faceloc in zip(encodecurframe, facecurentframe):
                encodeface = faceloc[0]
                encodelistface.append(encodeface)
            matches = face_recognition.compare_faces(encodeListKnown, encodelistface[0])
            facedistance = face_recognition.face_distance(encodeListKnown, encodelistface[0])
            print(encodelistface)
            print(facedistance)
            print(matches)
            a = str(matches)
            print(bool(matches))
            if (bool(matches) == True):
                print("True")
                now = datetime.now()
                stringnow = now.strftime('%y/%m/%d %H:%M:%S.%f')
                f = open(foldertxt, "a")
                logabsen = id + " , "+ stringnow  + "n"
                f.write(logabsen )
                playsound(folderaudio)
                encodelistface.clear()
                encodeListKnown.clear()
                image.clear()
                ClassNames.clear()
                inputid.delete(0,END)
                break
            else:
                print("False")
                encodelistface.clear()
                encodeListKnown.clear()
                image.clear()
                ClassNames.clear()
                inputid.delete(0,END)
                break
    except :
        print("There is An Exception" , sys.exc_info()[0])

2 Answers

You can use after() to schedule a timeout task whenever inputid is updated. You need to pick a timeout period that is long enough between each update of inputid by the barcode scanner. If inputid is updated again before the scheduled timeout, you need to cancel the previous timeout task and rescheduled anther one.

If the timeout task is triggered, you can perform the face recognition in the timeout callback, but suggest to use thread to do it as it may be a time consuming task which will block tkinter main loop.

Below is an example:

import tkinter as tk
import random
import threading
import time

def compareimage(id):
    face.config(text='Checking ...', image=tk.PhotoImage())
    face.update_idletasks()
    # simulate the face recognition task
    time.sleep(5)
    # show the result image
    face.image = tk.PhotoImage(file='result.png')
    face.config(text=id, image=face.image)
    ids.set('')

def text_completed(ids):
    print('timed out')
    id = ids.get()
    if len(id) > 8:
        print('id:', id)
        threading.Thread(target=compareimage, args=(id,), daemon=True).start()

def text_changed(ids):
    global timeout_id
    if timeout_id:
        # cancel previous scheduled task
        root.after_cancel(timeout_id)
    if len(ids.get()) > 0:
        # schedule a timeout task, adjust the delay to suit your real situation
        timeout_id = root.after(1000, text_completed, ids)

# simulate barcode reader input
def scan_id(n=10):
    if n > 0:
        inputid.insert('end', random.choice('1234567890'))
        inputid.after(50, scan_id, n-1)

timeout_id = None

root = tk.Tk()

tk.Label(root, text='Employee Attandance', font='Helvetica 32 bold').pack()

ids = tk.StringVar(value='')
ids.trace('w', lambda *args: text_changed(ids))

inputid = tk.Entry(root, font='Helvetica 20 bold', textvariable=ids, width=12)
inputid.pack()

face = tk.Label(root, text='', image=tk.PhotoImage(), compound='top', width=600, height=400)
face.pack()

tk.Button(root, text='Scan ID', command=lambda: scan_id(random.randrange(9,11))).pack()

root.mainloop()

Note that the button Scan ID is to simulate the barcode reader.

Answered by acw1668 on January 29, 2021

I refactored your script and removed all the unnecessary fluff. I also removed your wait and the compareimage call that you did not provide. It works fine. My guess is that either the wait or the call to compareimage were stealing focus from and/or blocking your Entry. If you take my version of your script and add in the wait and call, one at a time, you should be able to figure out what is blocking your Entry.

Note: There is no reason to call pack before place, and I have no clue what size() is supposed to do. ids is already global so, all the hoops you jumped through to plug it into a lambda were unnecessary. If something is >= 9 it is also simply > 8

import tkinter as tk

root = tk.Tk()
root.geometry('800x600+300+300')

ids = tk.StringVar(value="")

inputid = tk.Entry(root, font="Helvetica 20 bold", textvariable=ids)
inputid.place(relx=0.5, rely=0.187, width=300, height=30, anchor='sw')

def textchanged(*args):
    id = ids.get()
    if len(id) > 8:
        print(id)
        
ids.trace("w", textchanged)
        
root.mainloop()

Answered by Michael Guidry on January 29, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP