Search code examples
pythontkintergettkinter-entry

How to allow each character to be entered once?


I have an encryption code, which consists of 26 letters, and an option which allows the user to change it if they wish. After trying out my program several times, I came across an error, a logical one. The user can change the code, but also, they can enter the same character 26 times or at least 1 character more than once which could ruin my entire program. Is there any way to only allow the user to type each letter exactly once? Here's what I have so far:

import tkinter 

letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 

encryption_code = 'LFWOAYUISVKMNXPBDCRJTQEGHZ'

letters += letters.lower()
encryption_code += encryption_code.lower() 

window = tkinter.Tk()

encrypt_entry = tkinter.Entry(window)
encrypt_entry.pack()


def code_change(event):
    global encrypt_entry
    global encryption_code

    encryptget = encrypt_entry.get()

    if len(encryptget) == 26:
        print("You have changed the encryption code")
        encryption_code = encryptget
        encryption_code += encryption_code.lower()
        enc = dict(zip(letters, encryption_code)) 
        dec = dict(zip(encryption_code, letters))

    elif len(encryptget) < 26 or len(encryptget) > 26:
        print("Please enter 26 characters")
        encrypt_entry.delete(0, tkinter.END) 

window.bind('<Return>', code_change)

EDIT I have tried the following but now if I try typing the alphabet or encryption_code the elif statement does nothing.

import tkinter 

letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 

encryption_code = 'LFWOAYUISVKMNXPBDCRJTQEGHZ'

letters += letters.lower()
encryption_code += encryption_code.lower() 

window = tkinter.Tk()

encrypt_entry = tkinter.Entry(window)
encrypt_entry.pack()


def code_change(event):
    global encrypt_entry
    global encryption_code

    encryptget = encrypt_entry.get()

    if len(set(encryptget)) == 26 and encryptget != encryption_code and encryptget != letters:
        print("You have changed the encryption code")
        encryption_code = encryptget
        encryption_code += encryption_code.lower()
        enc = dict(zip(letters, encryption_code)) 
        dec = dict(zip(encryption_code, letters))

    elif len(set(encryptget)) != 26 and encryptget == encryption_code and encryptget == letters:
        print("Please enter each character exactly once")
        encrypt_entry.delete(0, tkinter.END)        

window.bind('<Return>', code_change)

Solution

  • Tkinter has a feature specifically for this sort of validation. You're able to have it call a function for every insertion, and and this function can either accept or reject that insertion based on whatever criteria you want.

    In your case the criteria is "no duplicate characters". An easy way to determine that is to convert the string to a set (by definition, a set has no duplicates), and then compare the length of the set to the length of the string.

    To call this function each time the user presses a key, set the validate and validatecommand options of the entry widget. There's an extra step where you have to register the command, which tells tkinter which of several special arguments you want your command to receive.

    The solution looks something like this:

    # Define a command to be run whenever the user edits the entry
    # N.B. d will be "1" for an insert, "0" for delete, and "-1"
    # for anything else. P will be the value of the entry if this
    # edit is allowed.
    #
    # Note: this function must always return either True or False
    def encryption_validation(d, P):
        if d == "1":   # ie: an insert
            unique_chars = set(P)
            if len(P) > len(unique_chars):
                return False
        return True
    
    # register the command with tkinter
    vcmd = (window.register(encryption_validation), '%d', '%P')
    
    # configure the entry widget to use this command
    encrypt_entry.configure(validate="key", validatecommand=vcmd)
    

    With the above, it will be impossible for the user to enter any character twice. Note that this solution also prevents the user from pasting a string with duplicate characters.

    For a more exhaustive explanation of entry validation, see this answer on stackoverflow: https://stackoverflow.com/a/4140988/7432