Таймер обратного отсчета не обновляется в tkinter

#python #tkinter

#python #tkinter

Вопрос:

Я пытаюсь создать платежное приложение, используя python и tkinter. Существует «основная» программа, которая получает от пользователя такие данные, как номер карты, cvv и т.д., и отправляет их в другую программу, которая является платежным шлюзом.

Таким образом, это не настоящий платежный шлюз. Он просто перепроверяет номер карты, cvv и т.д. с базой данных.

Я не включил основную программу в свой код, потому что могу заверить вас, что я проверил, были ли значения успешно переданы из основной программы, и это так.

Приведенная ниже программа немного длинная, извините, но я постараюсь сосредоточиться на проблеме.

Вот «платежный шлюз»:

 import smtplib, ssl
from tkinter import*
from tkinter import messagebox
import mysql.connector as mc
from random import randint
import time
global root

def visa_card_payment(cardnumber, cvv, name, expiry, premium, product):
    global root  

mycon=mc.connect(host="localhost",user="root",passwd="1234",database="visa",charset='utf8')
cursor=mycon.cursor()

todo1="select registered_email from all_data where card_no=%s and cvv='%s' and expiry='%s'"%(cardnumber,cvv,expiry)

cursor.execute(todo1)
data=cursor.fetchall()

if cursor.rowcount==1:
    
    cursor.execute(todo1)
    data=cursor.fetchone()
    print(data[0])
    emailid=data[0]
    
    def countdown(t):

        global root


        def payu(entered_otp):
            global root
            global otp
            global status
            global attempts

            if otp==entered_otp:

                status="true"

                root.destroy()
                root=Tk()
                root.title("Payment Successful")
                root.geometry("200x200")

                succ_label=Label("Payment Successful")
                succ_label.pack()

            elif otp!=entered_otp:
                if attempts!=3:
                    messagebox.showwarning(root,"Incorrect OTP. You have %s tries left."%(3-attempts))
                    attempts= 1

                elif attempts==3:
                    messagebox.showwarning(root,"Authentication Failed")
                    root.destroy()

        global root                    
        global attempts
        global status
        global otp

        attempts=1
        otp=randint(99999,1000000)
        status="false"

        port = 465
        smtp_server = "smtp.gmail.com"
        sender_email = "visaandezpay@gmail.com"
        receiver_email =str(emailid)
        password = "visaandezpay2003"
        message = """Subject:

        To %s,
        Dear Visa Cardholder,
        Your One Time Password (OTP) for payment to Tirth Insurance 4 Life towards payment of '%s' for policy '%s' is: %s
        Your OTP will be active for 5 minutes.
        Please do not share this OTP with anyone else."""%(name,premium,product,otp)

        context = ssl.create_default_context()
        with smtplib.SMTP_SSL(smtp_server, port, context=context) as server:
            server.login(sender_email, password)
            server.sendmail(sender_email, receiver_email, message)
        
        root=Tk()
        root.title("EzPay Payment Gateway")
        root.geometry("200x200")
        otp_label=Label(root,text="Enter OTP:")
        otp_entry=Entry(root,width=30,borderwidth=3)
        otp_entry.insert(0,0)
        countdown_label1=Label(root,text="OTP active for:")
        countdown_label2=Label(root,text="05:00")
        submit_button=Button(root,text="Submit",command=lambda:payu(int(otp_entry.get())))

        otp_label.grid(row=0,column=0)
        otp_entry.grid(row=0,column=1)
        countdown_label1.grid(row=1,column=0)
        countdown_label2.grid(row=1,column=1)
        submit_button.grid(row=2,column=1)
        
        root.mainloop()
        
        while t:
            
            mins, secs = divmod(t, 60)
            timeformat='{:02d}:{:02d}'.format(mins, secs)
            countdown_label2.grid_forget()
            countdown_label2=Label(root,text=timeformat)
            countdown_label2.grid(row=1,column=1)
            time.sleep(1)
            t=-1
        if status=="true":
            pass

        elif status=="false":
            
            otp=1000000
            messagebox.showwarning(root,"Authentication Failed")
            root.destroy()
        
    
    countdown(300)
  

Основная программа импортирует эту программу и запускает функцию visa_card_payment, а также предоставляет требуемые значения.

При запуске программа запускает запрос, проверяет, равно ли количество строк 1, получает идентификатор желаемого адреса электронной почты и вводит обратный отсчет (t) с t = 300

Затем он генерирует случайный otp, отправляет для этого электронное письмо и создает новое окно, в котором пользователю предлагается ввести otp. Вот тут-то и начинается проблема.

У меня есть countdown_label2, который изначально имеет текст 5:00. Затем у меня есть функция while (t) для обратного отсчета в течение 5 минут.

Каждую секунду он должен забывать местоположение предыдущего countdown_label2, создавать новую метку с текстом на одну секунду меньше и помещать ее обратно.

Он будет запускать программу в течение 5 минут, в течение которых, если будет введен и отправлен правильный otp, он покажет, что он успешен, в противном случае он повторится еще для двух попыток, и это тоже в течение обратного отсчета

Электронное письмо получено успешно, но countdown_label2 не обновляется, оно остается в 5:00.

Я думал, что проблема в том, куда я поместил root.mainloop() , но там, где я поместил его в приведенный выше код, работает только, иначе экран otp не отображается.

Вот экран otp:

экран otp

Если я нажму отправить, появится новое окно с указанным root.title() , но в нем ничего не отображается (у меня есть succ_label , но он не отображается).

Если я закрою экран, то появится вот что:

ошибка

Я действительно сожалею, что потратил ваше время на чтение так много для небольшой задачи, но буду признателен за любую помощь.

Спасибо!

PS: все, что находится в функции def visa_card_payment, должно иметь отступ.

Комментарии:

1. Почему никто не отвечает?

2. Я могу только предложить вам сделать более простой пример вашей проблемы. Теперь ваш код включает, например, отправку электронной почты, которая не может быть связана с вашей проблемой, верно?

3. Да, я это сделаю

Ответ №1:

Здесь нарушаются некоторые фундаментальные правила Tkinter.

  1. Никогда не используйте более одного корневого метода tkinter Tk (они не будут хорошо сосуществовать вместе)
  2. Никогда не используйте бесконечные циклы while (они препятствуют обновлению графического интерфейса)

Для решения 1 используйте Toplevel вместо Tk для любых дочерних окон

Решение 2 немного сложнее

Цикл while, содержащий ваш обратный отсчет, не будет выполняться, поскольку root.mainloop() будет продолжать выполняться до закрытия графического интерфейса.

Вам следует рассмотреть возможность размещения вашего таймера обратного отсчета внутри отдельной функции, которая вызывается раз в секунду методом tkinter .after.

При нажатии кнопки вы вызываете что-то вроде этого, чтобы вызвать функцию через одну секунду.

 root.after(1000, myCountdownFunction)
  

тогда ваша функция будет выглядеть примерно так

 def myCountDownFunction():
    mins, secs = divmod(t, 60)
    timeformat='{:02d}:{:02d}'.format(mins, secs)
    countdown_label2.grid_forget()
    countdown_label2=Label(root,text=timeformat)
    countdown_label2.grid(row=1,column=1)
    t=-1
    root.after(1000, MyCountdownFunction) # Calls this function again in 1 second
  

Этот вопрос очень широкий, и я сомневаюсь, что какой-либо ответ сможет полностью решить вашу проблему без полной перезаписи кода.

Комментарии:

1. спасибо, что нашли время ответить на этот вопрос. Теперь он работает отлично 👌