Python Forum
[Tkinter] Keypad Not Aligned
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[Tkinter] Keypad Not Aligned
#1
I'm having a problem with my GUI window not displaying the keypad on my window correctly. I'm sure it has to do with resolution but don't know how to adjust the code to make it work with my resolution of 800x480. I've posted a screenshot of the window with the keypad and the portion of the code that produces the window. Any help would be greatly appreciated.

[Image: MM2FmMnb]

        while True:
            r,w,x = select([dev], [], [])
            for event in dev.read():
                if event.type==1 and event.value==1:
                        if event.code==28:
                            dbConnection = MySQLdb.connect(host=dbHost, user=dbUser, passwd=dbPass, db=dbName)
                            cur = dbConnection.cursor(MySQLdb.cursors.DictCursor)
                            cur.execute("SELECT * FROM access_list WHERE rfid_code = '%s'" % (rfid_presented))
                            
                            if cur.rowcount != 1:
                                self.welcomeLabel.config(text="ACCESS DENIED")
                                
                                # Log access attempt
                                cur.execute("INSERT INTO access_log SET rfid_presented = '%s', rfid_presented_datetime = NOW(), rfid_granted = 0" % (rfid_presented))
                                dbConnection.commit()
                                
                                time.sleep(3)
                                self.welcomeLabel.grid_forget()
                                self.show_idle()
                            else:
                                user_info = cur.fetchone()
                                userPin = user_info['pin']
                                self.welcomeLabel.grid_forget()
                                self.validUser = ttk.Label(self.tk, text="Welcome\n %s!" % (user_info['name']), font='size, 15', justify='center', anchor='center')
                                self.validUser.grid(columnspan=3, sticky=tk.W+tk.E)
                                
                                self.image = tk.PhotoImage(file=user_info['image'] + ".gif")
                                self.photoLabel = ttk.Label(self.tk, image=self.image)
                                self.photoLabel.grid(columnspan=3)
                                
                                self.enterPINlabel = ttk.Label(self.tk, text="Please enter your PIN:", font='size, 18', justify='center', anchor='center')
                                self.enterPINlabel.grid(columnspan=3, sticky=tk.W+tk.E)
                                pin = ''
                                
                                keypad = [
                                                                    '1', '2', '3',
                                    '4', '5', '6',
                                    '7', '8', '9',
                                    '*', '0', '#',
                                ]
                                
                                # create and position all buttons with a for-loop
                                # r, c used for row, column grid values
                                r = 4
                                c = 0
                                n = 0
                                # list(range()) needed for Python3
                                self.btn = list(range(len(keypad)))
                                for label in keypad:
                                    # partial takes care of function and argument
                                    #cmd = partial(click, label)
                                    # create the button
                                    self.btn[n] = tk.Button(self.tk, text=label, font='size, 18', width=4, height=1, command=lambda digitPressed=label:self.codeInput(digitPressed, userPin, user_info['sms_number']))
                                    # position the button
                                    self.btn[n].grid(row=r, column=c, ipadx=10, ipady=10)
                                    # increment button index
                                    n += 1
                                    # update row/column position
                                    c += 1
                                    if c > 2:
                                        c = 0
                                        r += 1

                                
                                # Log access attempt
                                cur.execute("INSERT INTO access_log SET rfid_presented = '%s', rfid_presented_datetime = NOW(), rfid_granted = 1" % (rfid_presented))
                                dbConnection.commit()
                                accessLogId = cur.lastrowid
                                
                                self.PINentrytimeout = threading.Timer(10, self.returnToIdle_fromPINentry)
                                self.PINentrytimeout.start()
                                
                                self.PINenteredtimeout = threading.Timer(5, self.returnToIdle_fromPINentered)
                            
                            rfid_presented = ""
                            dbConnection.close()
                        else:
                            rfid_presented += keys[ event.code ]
Reply
#2
It looks like the problem occurs above the key button code where you create an image label, a PIN label, and a long text label. These increase the size of the column (comment the 3 lines that grid these widgets to see if this is true). Anyway, put the buttons in a separate frame so you don't have outside widgets that control the size of the buttons.
Reply
#3
We tried to remove the lines and the Num Pad shifted up to the top, but there was still spacing in between the 147 Column and the 258 Column.
Reply
#4
Then you are gridding something else that expands these columns.
Quote:put the buttons in a separate frame so you don't have outside widgets that control the size of the buttons

Don't know what the following means. There are only 3 columns on the keypad.
Quote:but there was still spacing in between the 147 Column and the 258 Column.

This code works as expected, which says that the problem is in some other part of your code. And note that this code does not use a class because it is just a small snippet.
    import tkinter as tk
    from functools import partial 

    master=tk.Tk()

    def callback(btn_num):
        print("btn_num=%s [%s] pressed" % (btn_num, keypad[btn_num]))

    keypad = ['1', '2', '3', '4', '5', '6',
              '7', '8', '9', '*', '0', '#']

    # create and position all buttons with a for-loop
    # r, c used for row, column grid values
    r = 4
    c = 0
    n = 0

    tk.Label(master, text="".join(["This is a really, really, really, ",
                                   "long label which will make this ",
                                   "single column very large"]),
            bg="lightblue").grid(row=0, column=0)

    ## a separate frame for the keys, so the long Label
    ## will not affect these column sizes
    fr=tk.Frame(master, bg="yellow")
    fr.grid(row=10, sticky="w")  ## left side

    btn_list=[]
    for label in keypad:
        btn_list.append(tk.Button(fr, text=label, font='size, 18',
                        width=4, height=1, command=partial(callback, n)))
        btn_list[-1].grid(row=r, column=c, ipadx=10, ipady=10)
        # increment button index
        n += 1
        # update row/column position
        c += 1
        if c > 2:
            c = 0
            r += 1

    master.mainloop() 
Reply
#5
Perfect. That helped us fix the spacing issue. We are having an issue now where the frame that we put in keypad in will not forget when the next window launches. We have tried fr.grid_forget() on line 70 of my code and it displays error name 'fr' is not defined. Although we defined fr in the button sections as fr = tk.Frame(self.tk, bg="yellow")
Reply
#6
Quote:it displays error name 'fr' is not defined
What are the conditions under which this could happen? Have you tried to answer the question yourself? Obviously we do not have enough code to answer on this. You have a class containing many things, and like the original problem, this line of code is correct and runs, so the problem is something else. A hint https://python-textbok.readthedocs.io/en...Scope.html but don't get me wrong, there is no way to tell what is going on from the code you posted.
Reply
#7
Sorry, I thought all the code was posted above, yes we have researched the issues and figured out what needs to be inserted. However we cant seem to figure out where without producing an error. The key pad should disappear after 12 second have passed or incorrect/correct pin entry.

#!/usr/bin/env python3
import sys
import MySQLdb
from threading import Thread
import threading
import time
import RPi.GPIO as GPIO
import json
from random import randint
from evdev import InputDevice
from select import select
#from twilio.rest import Client

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(13,GPIO.OUT)

try:
    # python 2
    import Tkinter as tk
    import ttk
except ImportError:
    # python 3
    import tkinter as tk
    from tkinter import ttk
    
class Fullscreen_Window:
    
    global dbHost
    global dbName
    global dbUser
    global dbPass
    
    dbHost = 'localhost'
    dbName = 'door_lock'
    dbUser = 'admin'
    dbPass = 'Starwars1460'
    
    def __init__(self):
        self.tk = tk.Tk()
        self.tk.title("Three-Factor Authentication Security Door Lock")
        self.frame = tk.Frame(self.tk)
        self.frame.grid()
        self.tk.columnconfigure(0, weight=1)
        
        self.tk.attributes('-zoomed', True)
        self.tk.attributes('-fullscreen', True)
        self.state = True
        self.tk.bind("<F11>", self.toggle_fullscreen)
        self.tk.bind("<Escape>", self.end_fullscreen)
        self.tk.config(cursor="none")
        
        self.show_idle()
        
        t = Thread(target=self.listen_rfid)
        t.daemon = True
        t.start()
        
    def show_idle(self):
        self.welcomeLabel = ttk.Label(self.tk, text="Please Present\nYour Token")
        self.welcomeLabel.config(font='size, 20', justify='center', anchor='center')
        self.welcomeLabel.grid(sticky=tk.W+tk.E, pady=210)
    
    def pin_entry_forget(self):
        self.validUser.grid_forget()
        self.photoLabel.grid_forget()
        self.enterPINlabel.grid_forget()
        count = 0
        while (count < 12):
            fr.grid_forget()
            count += 1
        
    def returnToIdle_fromPINentry(self):
        self.pin_entry_forget()
        self.show_idle()
        
    def returnToIdle_fromPINentered(self):
        self.PINresultLabel.grid_forget()
        self.show_idle()
        
    def returnToIdle_fromAccessGranted(self):
        GPIO.output(13,GPIO.LOW)
        self.SMSresultLabel.grid_forget()
        self.show_idle()
        
    def returnToIdle_fromSMSentry(self):
        self.PINresultLabel.grid_forget()
        self.smsDigitsLabel.grid_forget()
        count = 0
        while (count < 12):
            self.btn[count].grid_forget()
            count += 1
        self.show_idle()
        
    def returnToIdle_fromSMSentered(self):
        self.SMSresultLabel.grid_forget()
        self.show_idle()
    
    def toggle_fullscreen(self, event=None):
        self.state = not self.state  # Just toggling the boolean
        self.tk.attributes("-fullscreen", self.state)
        return "break"

    def end_fullscreen(self, event=None):
        self.state = False
        self.tk.attributes("-fullscreen", False)
        return "break"
        
    def listen_rfid(self):
        global pin
        global accessLogId
        
        keys = "X^1234567890XXXXqwertzuiopXXXXasdfghjklXXXXXyxcvbnmXXXXXXXXXXXXXXXXXXXXXXX"
        dev = InputDevice('/dev/input/event3')
        rfid_presented = ""

        while True:
            r,w,x = select([dev], [], [])
            for event in dev.read():
                if event.type==1 and event.value==1:
                        if event.code==28:
                            dbConnection = MySQLdb.connect(host=dbHost, user=dbUser, passwd=dbPass, db=dbName)
                            cur = dbConnection.cursor(MySQLdb.cursors.DictCursor)
                            cur.execute("SELECT * FROM access_list WHERE rfid_code = '%s'" % (rfid_presented))
                            
                            if cur.rowcount != 1:
                                self.welcomeLabel.config(text="ACCESS DENIED")
                                
                                # Log access attempt
                                cur.execute("INSERT INTO access_log SET rfid_presented = '%s', rfid_presented_datetime = NOW(), rfid_granted = 0" % (rfid_presented))
                                dbConnection.commit()
                                
                                time.sleep(3)
                                self.welcomeLabel.grid_forget()
                                self.show_idle()
                            else:
                                user_info = cur.fetchone()
                                userPin = user_info['pin']
                                self.welcomeLabel.grid_forget()
                                self.validUser = ttk.Label(self.tk, text="Welcome\n %s!" % (user_info['name']), font='size, 15', justify='center', anchor='center')
                                self.validUser.grid(columnspan=3, sticky=tk.W+tk.E)
                                
                                self.image = tk.PhotoImage(file=user_info['image'] + ".gif")
                                self.photoLabel = ttk.Label(self.tk, image=self.image)
                                self.photoLabel.grid(columnspan=3)
                                
                                self.enterPINlabel = ttk.Label(self.tk, text="Please enter your PIN:", font='size, 18', justify='center', anchor='center')
                                self.enterPINlabel.grid(columnspan=3, sticky=tk.W+tk.E)
                                pin = ''
                                
                                keypad = [
                                    '1', '2', '3',
                                    '4', '5', '6',
                                    '7', '8', '9',
                                    '*', '0', '#',
                                ]
                                
                                # create and position all buttons with a for-loop
                                # r, c used for row, column grid values
                                r = 4
                                c = 0
                                n = 0
                                
                                fr = tk.Frame(self.tk, bg="yellow")
                                fr.grid(row=10, column = 0)
                                # list(range()) needed for Python3
                                self.btn = list(range(len(keypad)))
                                for label in keypad:
                                    # partial takes care of function and argument
                                    #cmd = partial(click, label)
                                    # create the button
                                    self.btn.append(tk.Button(fr, text=label, font='size, 18', width=4, height=1, command=lambda digitPressed=label:self.codeInput(digitPressed, userPin, user_info['sms_number'])))
                                    # position the button
                                    self.btn[-1].grid(row=r, column=c, ipadx=10, ipady=10)
                                    # increment button index
                                    n += 1
                                    # update row/column position
                                    c += 1
                                    if c > 2:
                                        c = 0
                                        r += 1

                                
                                # Log access attempt
                                cur.execute("INSERT INTO access_log SET rfid_presented = '%s', rfid_presented_datetime = NOW(), rfid_granted = 1" % (rfid_presented))
                                dbConnection.commit()
                                accessLogId = cur.lastrowid
                                
                                self.PINentrytimeout = threading.Timer(10, self.returnToIdle_fromPINentry)
                                self.PINentrytimeout.start()
                                
                                self.PINenteredtimeout = threading.Timer(5, self.returnToIdle_fromPINentered)
                            
                            rfid_presented = ""
                            dbConnection.close()
                        else:
                            rfid_presented += keys[ event.code ]

    def codeInput(self, value, userPin, mobileNumber):
        global accessLogId
        global pin
        global smsCodeEntered
        pin += value
        pinLength = len(pin)
        
        self.enterPINlabel.config(text="Digits Entered: %d" % pinLength)
        
        if pinLength == 4:
            self.PINentrytimeout.cancel()
            self.pin_entry_forget()
            
            if pin == userPin:
                pin_granted = 1
            else:
                pin_granted = 0
            
            # Log access attempt
            dbConnection = MySQLdb.connect(host=dbHost, user=dbUser, passwd=dbPass, db=dbName)
            cur = dbConnection.cursor()
            cur.execute("UPDATE access_log SET pin_entered = '%s', pin_entered_datetime = NOW(), pin_granted = %s, mobile_number = '%s' WHERE access_id = %s" % (pin, pin_granted, mobileNumber, accessLogId))
            dbConnection.commit()
            
            if pin == userPin:
                self.PINresultLabel = ttk.Label(self.tk, text="Thank You, Now\nPlease Enter Code\nfrom SMS\n")
                self.PINresultLabel.config(font='size, 20', justify='center', anchor='center')
                self.PINresultLabel.grid(columnspan=3, sticky=tk.W+tk.E, pady=20)
                
                self.smsDigitsLabel = ttk.Label(self.tk, text="Digits Entered: 0", font='size, 18', justify='center', anchor='center')
                self.smsDigitsLabel.grid(columnspan=3, sticky=tk.W+tk.E)
                
                smsCode = self.sendSMScode(mobileNumber)
                smsCodeEntered = ''
                
                keypad = [
                    '1', '2', '3',
                    '4', '5', '6',
                    '7', '8', '9',
                    '', '0', '',
                ]
                                
                # create and position all buttons with a for-loop
                # r, c used for row, column grid values
                r = 4
                c = 0
                n = 0
                # list(range()) needed for Python3
                self.btn = list(range(len(keypad)))
                for label in keypad:
                    # partial takes care of function and argument
                    #cmd = partial(click, label)
                    # create the button
                    self.btn[n] = tk.Button(self.tk, text=label, font='size, 18', width=4, height=1, command=lambda digitPressed=label:self.smsCodeEnteredInput(digitPressed, smsCode))
                    # position the button
                    self.btn[n].grid(row=r, column=c, ipadx=10, ipady=10)
                    # increment button index
                    n += 1
                    # update row/column position
                    c += 1
                    if c > 2:
                        c = 0
                        r += 1
                
                self.SMSentrytimeout = threading.Timer(60, self.returnToIdle_fromSMSentry)
                self.SMSentrytimeout.start()
                
            else:
                self.PINresultLabel = ttk.Label(self.tk, text="Incorrect PIN\nEntered!")
                self.PINresultLabel.config(font='size, 20', justify='center', anchor='center')
                self.PINresultLabel.grid(sticky=tk.W+tk.E, pady=210)
                self.PINenteredtimeout.start()
                
    def smsCodeEnteredInput(self, value, smsCode):
        global smsCodeEntered
        global accessLogId
        smsCodeEntered += value
        smsCodeEnteredLength = len(smsCodeEntered)
        
        self.smsDigitsLabel.config(text="Digits Entered: %d" % smsCodeEnteredLength)
        
        if smsCodeEnteredLength == 6:
            self.SMSentrytimeout.cancel()
            self.pin_entry_forget()
            
            if smsCodeEntered == smsCode:
                smscode_granted = 1
            else:
                smscode_granted = 0
            
            # Log access attempt
            dbConnection = MySQLdb.connect(host=dbHost, user=dbUser, passwd=dbPass, db=dbName)
            cur = dbConnection.cursor()
            cur.execute("UPDATE access_log SET smscode_entered = '%s', smscode_entered_datetime = NOW(), smscode_granted = %s WHERE access_id = %s" % (smsCodeEntered, smscode_granted, accessLogId))
            dbConnection.commit()
            
            if smsCodeEntered == smsCode:
                self.SMSresultLabel = ttk.Label(self.tk, text="Thank You,\nAccess Granted")
                self.SMSresultLabel.config(font='size, 20', justify='center', anchor='center')
                self.SMSresultLabel.grid(columnspan=3, sticky=tk.W+tk.E, pady=210)
                
                self.PINresultLabel.grid_forget()
                self.smsDigitsLabel.grid_forget()
                GPIO.output(13,GPIO.HIGH)
                
                self.doorOpenTimeout = threading.Timer(10, self.returnToIdle_fromAccessGranted)
                self.doorOpenTimeout.start()
            else:
                self.PINresultLabel.grid_forget()
                self.smsDigitsLabel.grid_forget()
                
                self.SMSresultLabel = ttk.Label(self.tk, text="Incorrect SMS\nCode Entered!")
                self.SMSresultLabel.config(font='size, 20', justify='center', anchor='center')
                self.SMSresultLabel.grid(sticky=tk.W+tk.E, pady=210)
                
                self.SMSenteredtimeout = threading.Timer(10, self.returnToIdle_fromSMSentered)
                self.SMSenteredtimeout.start()
                
    def sendSMScode(self, mobileNumber):
    
        # Retreive our Twilio access credentials and "from" number
        dbConnection = MySQLdb.connect(host=dbHost, user=dbUser, passwd=dbPass, db=dbName)
        cur = dbConnection.cursor(MySQLdb.cursors.DictCursor)
        cur.execute("SELECT account_sid, auth_token, twilio_sms_number FROM twilio_api_credentials WHERE id = 1")
        credentials = cur.fetchone()
        account_sid = credentials['account_sid']
        auth_token = credentials['auth_token']
        twilio_sms_number = credentials['twilio_sms_number']
        dbConnection.close()
                
        smsCode = str(randint(100000, 999999))
        messageText = "Your access code is %s. Please enter this on the touchscreen to continue." % smsCode

        client = Client(account_sid, auth_token)
        message = client.messages.create(
            to=mobileNumber, 
            from_=twilio_sms_number,
            body=messageText)
        
        return smsCode

if __name__ == '__main__':
    w = Fullscreen_Window()
    w.tk.mainloop()
Reply
#8
Quote:The key pad should disappear after 12 second have passed
Incorrect PIN would do something similar. I have modified the code I posted earlier to destroy the frame after 12 seconds have passed.
import tkinter as tk
from functools import partial

class KeypadTest():
    def __init__(self, master):
        self.master=master
        self.keypad = ['1', '2', '3', '4', '5', '6',
                       '7', '8', '9', '*', '0', '#']
        self.create_buttons()

        self.time_display=tk.IntVar()
        tk.Label(self.master, textvariable=self.time_display,
                 bg="lightsalmon", font=('Verdana', 15)
                 ).grid(row=5, column=0, sticky="nsew")
        self.time_display.set(12)
        self.twelve_seconds(),\

    def callback(self, btn_num):
        print("btn_num=%s [%s] pressed" % (btn_num, self.keypad[btn_num]))

    def create_buttons(self):
        # create and position all buttons with a for-loop
        # r, c used for row, column grid values
        r = 4
        c = 0
        n = 0

        tk.Label(master, text="".join(["This is a really, really, really, ",
                                       "long label which will make this ",
                                       "single column very large"]),
                bg="lightblue").grid(row=0, column=0)

        ## a separate frame for the keys, so the long Label
        ## will not affect these column sizes
        self.fr=tk.Frame(master, bg="yellow")
        self.fr.grid(row=10, sticky="w")  ## left side

        btn_list=[]
        for label in self.keypad:
            btn_list.append(tk.Button(self.fr, text=label, font='size, 18',
                    width=4, height=1, command=partial(self.callback, n)))
            btn_list[-1].grid(row=r, column=c, ipadx=10, ipady=10)
            # increment button index
            n += 1
            # update row/column position
            c += 1
            if c > 2:
                c = 0
                r += 1

    def twelve_seconds(self):
        time_left=self.time_display.get()
        if time_left > 0:
            time_left -= 1
            self.time_display.set(time_left)
            self.master.after(1000, self.twelve_seconds)

        else:
            self.fr.destroy()

master=tk.Tk()
KT=KeypadTest(master)
master.mainloop() 
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  ON-SCREEN KEYPAD module ATARI_LIVE 1 2,149 Sep-30-2020, 12:19 PM
Last Post: Larz60+
  [Tkinter] Tkinter Entry widget and KeyPad intrgration chiragarya1992 0 7,684 Aug-11-2018, 01:09 AM
Last Post: chiragarya1992
  [Tkinter] enter data with keypad gray 4 7,294 Feb-11-2017, 10:20 PM
Last Post: Larz60+

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020