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)
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
Correct me if I'm mistaken, but it sounds like you just want to make sure that the string of characters the user inputs for the code don't contain any duplicates?
I don't personally know about the validation commands, but I think this could work to achieve your goal:
def IsCodeValid(encryption_code):
c = [0]*26
pos = 0
for i in range(len(encryption_code)):
pos = ord(encryption_code[i])-ord('A') #find placement relative to A in unicode
c[pos] = c[pos] + 1 #increment counter for that letter
j = 0
ValidCode = True
while j<26 and ValidCode:
if c[j]>1: #check to see if any letter occurred more than once
ValidCode = False
else:
j += 1
return ValidCode
Mind you, this also expects that all letters are entered capitalized. But you can fix that by normalizing the data before accepting it. Alternatively, you could complicate the logic to check both upper & lower case.
Edit: This is assuming that you don't want the code to run if the encryption_code is invalid, you could use this flag to request a new encryption_code from the user before running the rest of the program.
Related
So I'm creating a password manager, and my code is somewhat close to this,
I was wondering if there's a way to check for a character inside a string while it's still being written (i.e. before the user clicks Enter), so that I can change the color of the line that says "An uppercase character" to green when the user inputs an uppercase character, and goes back to original character if the user deletes the character.
from termcolor import colored
from getpass import getpass
import string
def check_for_owasp_standards(password):
uppercase_letters = string.ascii_uppercase
lowercase_letters = string.ascii_lowercase
digits = string.digits
special_characters = "!##$%^&*()-_"
length_is_over_8 = False
contains_uppercase_letter = False
contains_lowercase_letter = False
contains_digit = False
contains_special_character = False
Checklist = []
if len(password) >= 8 :
length_is_over_8 = True
Checklist.append(length_is_over_8)
for character in password:
if character in uppercase_letters:
contains_uppercase_letter = True
break
Checklist.append(contains_uppercase_letter)
for character in password:
if character in lowercase_letters:
contains_lowercase_letter = True
break
Checklist.append(contains_lowercase_letter)
for character in password:
if character in digits:
contains_digit = True
break
Checklist.append(contains_digit)
for character in password:
if character in special_characters:
contains_special_character = True
break
Checklist.append(contains_special_character)
if False in Checklist:
return False
else:
return True
print(colored("According to OWASP password standards 2021 your master password must contain the following :", "magenta"))
print(colored("*An uppercase character", "magenta"))
print(colored("*A lowercase character", "magenta"))
print(colored("*A digit", "magenta"))
print(colored("*Length has to be 8 or more characters\n", "magenta"))
master_password = getpass(colored("Enter your master password : ", "blue"))
if not check_for_owasp_standards(master_password):
print(colored("Password does not meet the standards, let's try again.\n", "yellow"))
else:
print(colored("Master password saved successfully!", "green"))
I created a solution to your problem.
This is a video showing how it works on my console >> YouTube.
You can get the whole code, organised here >> Github.
And here is explanation:
You will need to turn off console output, then capture each keypress, and at the end turn console output back on.
For this you will need from pynput.keyboard import Key, Listener, which works basically like this:
def on_release(key):
print(key)
if key in [Key.enter, Key.esc]:
return False
with Listener(on_release=on_release) as listener:
listener.join()
which will basically print every pressed key, and stop working when pressing Enter or Esc.
Tp stop it from being visible you will disable it by os.system("stty -echo") and enable back with os.system("stty echo")
Here is how I changed your code:
This method is similar to your check_for_owasp_standards(password) but will return a list of booleans for each standard that need to be checked:
def checkStandards(password):
uppercase_letters = string.ascii_uppercase
lowercase_letters = string.ascii_lowercase
digits = string.digits
special_characters = "!##$%^&*()-_"
length_is_over_8 = False
contains_uppercase_letter = False
contains_lowercase_letter = False
contains_digit = False
contains_special_character = False
checklist = []
if len(password) >= 8 :
length_is_over_8 = True
checklist.append(length_is_over_8)
for character in password:
if character in uppercase_letters:
contains_uppercase_letter = True
break
checklist.append(contains_uppercase_letter)
for character in password:
if character in lowercase_letters:
contains_lowercase_letter = True
break
checklist.append(contains_lowercase_letter)
for character in password:
if character in digits:
contains_digit = True
break
checklist.append(contains_digit)
for character in password:
if character in special_characters:
contains_special_character = True
break
checklist.append(contains_special_character)
return checklist
Next function will show this information as coloured. Green for accepted, red for not. The easiest way to show changes, overring previous lines written to console is to clear all console.
def updateStandards(password):
# clear console
os.system('clear')
# check
standards = checkStandards(password)
print(accept("Length >= 8", standards[0]))
print(accept("Contains uppercase letter", standards[1]))
print(accept("Contains lowercase letter", standards[2]))
print(accept("Contains digit", standards[3]))
print(accept("Contains special character", standards[4]))
using
def accept(text, accepted=False):
if accepted:
return f'\033[32;1m{text}\033[0m' # green
else:
return f'\033[31;1m{text}\033[0m' # red
The main function will look like this. It will first reset global variable storing user input to empty string. Then show all rules for password. Then ask user for password and turn off showing in console. At the end, it will turn it back.
def getPassword():
try:
# reset stored input to empty string
global user_input
user_input = ''
# show rules for password
updateStandards('')
# ask for password
print('Write your password:')
# turn off showing input
os.system("stty -echo")
# collect keys until ENTER/ESC
with Listener(on_release=on_release) as listener:
listener.join()
input()
except:
print(traceback.print_exc())
finally:
# turn on showing input
os.system("stty echo")
The last part is to write listener for key press:
def on_release(key):
global user_input
# store key
k = str(key).replace('\'', '')
if len(k) == 1:
user_input += k
# delete last char
if key == Key.backspace:
user_input = user_input[:-1]
updateStandards(user_input)
print('Write your password:', ''.join('*' for _ in range(len(user_input))))
# Stop listener
if key in [Key.enter, Key.esc]:
finished(user_input)
return False
When user end writing password, you can do anything you want with it:
def finished(password):
# do what you need to do, e.g. store password somewhere
standards = checkStandards(password)
if all(standards):
print('Your password is:', password)
else:
print('Your password should meet all the rules')
I'm trying to make my own spin on Hangman. A basic part of Hangman is that the user guesses a letter and if it is there, it appears in the entry box else it gets added to a list of incorrect guesses.
What I want to do now is assign a letter to each read-only entry box based on the random word generated. However, I also don't want the user to know what letter is assigned to it (obviously).
def press(num):
string1 = " "
string1+=str(num)
appears.set(string1)
appears = StringVar()
entrybox = Entry(hangman,state= 'readonly',textvariable = appears)
entrybox.place(x = 0, y = 0)
q = Button(hangman, text = 'Q', width = 4, command = lambda : press('Q'))
q.place(x = 200, y = 440)
This is what i've managed to do so far
You can use a dictionary to store a mapping from letters to StringVars. Then, look up the guessed letter and replace the text from a placeholder to the letter.
word = "tiger" # just an example
let2svar = {}
for let in word:
svar = StringVar()
svar.set("_")
entrybox = Entry(hangman, state='readonly', textvariable=svar)
entrybox.place(x=0, y=0)
if let not in let2svar:
let2svar[let] = []
let2svar[let].append(svar)
# Now when user guesses letter 'guess', check if it is in the word.
if guess in let2svar:
for svar in let2svar[guess]:
svar.set(guess)
You can also check out https://docs.python.org/3/library/collections.html#collections.defaultdict for a cleaner way to handle the dictionary.
So I set out to make a simple game of hangman and everything worked fine, the whole code worked but it lacked the ability to allow the user to replay when the game is over. Thus I set out to put all the code I have written in various functions. So that I can call the functions when they are required (I thought it was the most logical way to allow replay-ability). Various problems followed but one stood out.
The main culprit (I think) is that I could not successfully get a value to update globally. I've read similar questions on the site but could not successfully adapt it to my case. I have a sample code to show what exactly I mean:
def GameMode():
choice = input('Play alone or play with friends? A F : ')
choice = choice.upper()
if choice == 'A':
wordslotmachine = ['stand','emerald','splash']
word = random.choice(wordslotmachine)
word = word.upper()
Rules()
elif choice == 'F':
word = input('Enter your word for your friends to guess: ')
word = word.upper()
Rules()
else:
choice = input('Please enter A or F: ')
choice = choice.upper()
I would need the program to remember what the value of "word" is and use this word in another method (this method is ran by another method showed below "Rules()"):
def MainGame():
guesses = ''
turns = 10
underscore = 0
seconds = 1
checker = 0
cheaterchance = 5
while turns > 0: #check if the turns are more than zero
for char in word: # for every character in secret_word
if char in guesses: # see if the character is in the players guess
print(char+' ', end='')
else:
print('_ ', end='')# if not found, print a dash
underscore += 1
if underscore == 0:
print(': You got it!')
Wait()
NewGame()
break
#A block of if's to check for cheating
if guess not in word:
print('Your guesses so far: '+guesses)
turns -= 1
if turns == 0:
break
else:
print('')
print('Try again. You have',turns,'more guesses')
print('Delayed chance to answer by',seconds,'seconds')
counter = 1
print(0,'.. ', end='')
while counter < seconds:
time.sleep(1)
print(counter,'.. ', end='')
counter += 1
if counter == seconds:
time.sleep(1)
print(counter,'.. done!', end='')
print('')
print('')
seconds += 1
underscore = 0
else:
print('Your guesses so far: '+guesses)
underscore = 0
#The else portion of the code to check for cheating
I have tried defining "word" outside of the function. Doing this doesn't fix the problem, GameMode() will not successfully update the value of "word". And whatever the value of "word" defined outside of the function will be called and used by MainGame(). However doing this shows another problem.
That being, the code that previously worked (it successfully read the input and correctly updated the game status) now does not work. Even if the correct letter is entered by the user, the program reads the input as incorrect.
These are the two problems I have faced so far and have yet to find a way to overcome them.
Note: I have successfully created a way to make the game replay-able by putting the entire original code (without the functions) inside a while loop. However I would still very much like to know how I can get the code to work using functions.
Edit: This is the function for Rules():
def Rules():
#Bunch of prints to explain the rules
MainGame()
print('Start guessing...')
Wait() is just a delay function with a countdown.
Global vs. Local variables.
You can reference and use a global variable from within a function, but you cannot change it.
It's bad practice, but you CAN declare a variable within your function to be global and then changes to it inside your function will apply to the variable of the same name globally.
HOWEVER, what I suggest is to return the word at the end of your function.
def whatever_function(thisword):
do some stuff
return word
new_word = whatever_function(thisword)
Functions can, and usually should, return values. Make GameMode() return the word to the caller;
def GameMode():
choice = input('Play alone or play with friends? A F : ')
choice = choice.upper()
if choice == 'A':
wordslotmachine = ['stand','emerald','splash']
word = random.choice(wordslotmachine)
word = word.upper()
Rules() #ignore this
elif choice == 'F':
word = input('Enter your word for your friends to guess: ')
word = word.upper()
Rules() #ignore this
else:
choice = input('Please enter A or F: ')
choice = choice.upper()
return word
From the main call GameMode and save the word;
def MainGame():
guesses = ''
turns = 10
underscore = 0
seconds = 1
checker = 0
cheaterchance = 5
word = GameMode() # add this here
You almost certainly want to use a class with instance variables
Contrived example:
class Hangman:
def __init__(self):
print("Starting hangman")
def mode(self):
# ...
self.word = 'whatever'
def play(self):
print("Look i have access to word", self.word)
if __name__ == '__main__':
hm = Hangman()
hm.mode()
hm.play() # may be what you want to put in a while loop
To access a global variable from inside a function, you have to tell python that it is global:
my_global_var = 1
def some_func():
global my_global_var
You have to use global keyword in every method that the global variable is being used or python will think you are defining/ using a local variable
Having said that, you should avoid globals as a coding practise.
global word #probably define this after imports.
def GameMode():
global word #add this
choice = input('Play alone or play with friends? A F : ')
choice = choice.upper()
if choice == 'A':
wordslotmachine = ['stand','emerald','splash']
word = random.choice(wordslotmachine)
word = word.upper()
Rules() #ignore this
elif choice == 'F':
word = input('Enter your word for your friends to guess: ')
word = word.upper()
Rules() #ignore this
else:
choice = input('Please enter A or F: ')
choice = choice.upper()
def MainGame():
guesses = ''
turns = 10
underscore = 0
seconds = 1
checker = 0
cheaterchance = 5
global word # add this here
use the code
global word
above the def or let the def return the value of word so it is stored in a variable outside the def
eingabe = Text(window, width=200, height=4)
eingabe.pack()
def c2n():
alphabet = "abcdefghijklmnopqrstuvwxyz"
k = 3
c = ''
code = ''
try:
code = str(eingabe.get('1.0',END))
code = code.lower()
code = code.replace("\n","")
except:
label1.configure(text="Error!")
finally:
if code.isalpha():
for z in code:
if z == ' ':
c += z
elif z in alphabet:
c += alphabet[(alphabet.index(z) + k) % (len(alphabet))]
label1.configure(text=(str(c)))
else:
label1.configure(text="only letters please")
I recently started with Python and one of my first tasks was to create a Caesar cipher encrypter/decrypter GUI with Tkiner. This is only a snippet from my code.
So I wanted to use Text not Entry to give my users more space to fill in stuff, but I realized that now my users can't hit the next rot in the text field without creating a \n. The problem with that is that I check with code .isalpha() if the text only contain letters, and thats not working any longer.
Now I am looking for a new way to check for letters only or find a new way to handle the \n.
Any positive criticism and feedback for the rest of the garbage code would be nice also, but as I said I recently started with Python and english is not my native language.
Just test for isspace:
def crypt(text, k=3):
alphabet = "abcdefghijklmnopqrstuvwxyz"
result = []
for z in text.lower():
if z.isspace():
result.append(z)
elif z in alphabet:
result.append(alphabet[(alphabet.index(z) + k) % (len(alphabet))])
else:
raise AssertionError()
return ''.join(result)
def c2n(text):
try:
label1.configure(text=crypt(eingabe.get('1.0',END)))
except AssertionError:
label1.configure(text="only letters please")
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 7 years ago.
Improve this question
Hello so i was doing a school project and I wrote this out I got it all working so my next task was to make it to read the message from an file so i changed the definition where it asked for the message which is UserMessage() so i run the code it works it prints out whats in the text file but when i do the last bit where it gets the final answer it shows this error
Traceback (most recent call last):
File "C:\Users\christian\Desktop\Python\2 keywor with cypher code.py", line 138, in <module>
Restart()
File "C:\Users\christian\Desktop\Python\2 keywor with cypher code.py", line 127, in Restart
Text = TranslateMessage2(Key2, Message2, Option)
File "C:\Users\christian\Desktop\Python\2 keywor with cypher code.py", line 107, in TranslateMessage2
Translated.append(symbol) # The symbol was not in LETTERS, so add it to translated as is.
NameError: name 'Translated' is not defined
import pyperclip
Valid_Letters = 'ZABCDEFGHIJKLMNOPQRSTUVWXY' # This is where it stores the Letters witch are being used in the program
def Linespacer():
print('================================================================')
def GetOption(): # This is the first queston in the program witch is asking if they want to encrypt decrypt
while True:
print('Do you wish to encrypt or decrypt a message?')
Option = input().lower()
if Option in 'encrypt e decrypt d'.split():
return Option # This is where if the user does not enter the right ifomation it goes back tot he top and ask the question
else:
print('Enter either "encrypt" or "e" or "decrypt" or "d".') # If the user doers not enter the right thing for example if they enter an 'l' it prints that message
def UserMessage():
mes = str(input('would you like to encrypt or decrypt from a file or type your own message?(file/f or message/m): '))
if mes == 'f' or mes == 'file':
name = str(input("What is the document you want to read called?: "))
with open(name, "rt") as in_file:
text = in_file.read()
print(text)
return text
if mes == 'm' or mes == 'message':
text = str(input('Enter your message: '))
def UserKeyword(): # This def is what ask the user for the Message and gathers the infomation
print('Enter your First keyword:') # Prints out the message asking for the keyword
return input()
def UserKeyword2(): # This def is what ask the user for the Message and gathers the infomation
print('Enter your Second keyword:') # Prints out the message asking for the keyword
return input()
def TranslateMessage(Key, Message, Option): # This is the main def and where it does all of the maths when you call the def it reqires 3 Variables
Translated = [] # stores the encrypted/decrypted message string
keyIndex = 0 # This is the defult keyIndex when the program is started
Key = Key.upper() # This is allowing the user to have Upper case letters or lowercase letters
for symbol in Message: # loop through each character in message
num = Valid_Letters.find(symbol.upper()) #
if num != -1: # -1 means symbol.upper() was not found in LETTERS
if Option == 'encrypt' or Option == 'e':
num += Valid_Letters.find(Key[keyIndex]) #This makes it so if they are encrypting it adds
elif Option == 'decrypt' or Option == 'd':
num -= Valid_Letters.find(Key[keyIndex]) # This makes it so if they are decrypting it subtract
num %= len(Valid_Letters)
if symbol.isupper():
Translated.append(Valid_Letters[num])
elif symbol.islower():
Translated.append(Valid_Letters[num].lower())
keyIndex += 1 # move to the next letter in the key
if keyIndex == len(Key):
keyIndex = 0
else:
Translated.append(symbol) # The symbol was not in LETTERS, so add it to translated as is.
return ''.join(Translated) # It joins all of the functions together so the user can have all of the text together
def TranslateMessage2(Key2, Message2, Option): # This is the main def and where it does all of the maths when you call the def it reqires 3 Variables
Translated2 = [] # stores the encrypted/decrypted message string
keyIndex = 0 # This is the defult keyIndex when the program is started
Key2 = Key2.upper() # This is allowing the user to have Upper case letters or lowercase letters
for symbol in Message2: # loop through each character in message
num = Valid_Letters.find(symbol.upper()) #
if num != -1: # -1 means symbol.upper() was not found in LETTERS
if Option == 'encrypt' or Option == 'e':
num += Valid_Letters.find(Key2[keyIndex]) #This makes it so if they are encrypting it adds
elif Option == 'decrypt' or Option == 'd':
num -= Valid_Letters.find(Key2[keyIndex]) # This makes it so if they are decrypting it subtract
num %= len(Valid_Letters)
if symbol.isupper():
Translated2.append(Valid_Letters[num])
elif symbol.islower():
Translated2.append(Valid_Letters[num].lower())
keyIndex += 1 # move to the next letter in the key
if keyIndex == len(Key2):
keyIndex = 0
else:
Translated.append(symbol) # The symbol was not in LETTERS, so add it to translated as is.
return ''.join(Translated2) # It joins all of the functions together so the user can have all of the text together
def PlayAgainMessage():
again = str(input("Would you like to restart Y or N: "));
Linespacer()
if again == "Y" or again == "y": # This is an if statment it is saying that if the user types "Y" it runs the code to restart the program
Restart(); # If the user types "N" it Exits the program
elif again == "N" or again == "n":
exit()
def Restart(): # This is the def which allows the user to restart the prgram once they have done it
Option = GetOption()
Message = UserMessage()
Key = UserKeyword()
Key2 = UserKeyword2()
Message2 = TranslateMessage(Key, Message, Option)
Text = TranslateMessage2(Key2, Message2, Option)
Linespacer()
print('Your translated text is: %s' % Text) # Prints the message and get the text which has been encrypt or decrypt
pyperclip.copy(Text)
Linespacer()
name = str(input("What would you like to name the File: "))
name1 = str;
file = open(name+".txt", "w")
file.write(Text)
file.close()
PlayAgainMessage()
Restart()
You left a Translated in your TranslateMessage2 when you probably meant to change it to Translated2.
In TranslateMessage2:
Translated.append(symbol)
Should be:
Translated2.append(symbol)
You defined Translated in TranslateMessage but not in TranslateMessage2
As a result Translated does not exist within the global namespace, nor the namespace of TranslateMessage2.
Assuming you intentionally wanted to append to Translated and not Translated2 you either need to make Translated global variable or assign it to a variable through your TranslateMessage function.
To make Translated global you simply need to initialise it outside of the function.