To put it simply, a GUI button seems to be linked to the wrong function.
i have started to create a Vigenere cipher. A button that is meant to redirect to an encrypt function instead redirects to the decrypt function. W/ my current code it doesn't matter because the two codes are nearly the same but they will eventually have different uses and so need to be distinguished from each other.
i am unsure as to where the problem is so the whole code is attached.
from appJar import gui
app = gui("Vigenere Chipher")
app.addLabel("title", "Vigenere Chipher")
app.setBg("cyan")
app.setSize(600,400)
def press(Encrypt):
app.removeAllWidgets()
app.addLabel("title", "Encryption")
app.addEntry("Phrase")
app.setEntryDefault("Phrase", "Enter Phrase Here")
app.addEntry("Key")
app.setEntryDefault("Key", "Enter Key Here")
app.addLabel("RESULT", "This is your encrypted message: ")
def press(Submit):
phrase = app.getEntry("Phrase")
key = app.getEntry("Key")
def press(Back):
app.removeAllWidgets()
app.addLabel("title", "Vigenere Chipher")
app.setBg("cyan")
app.setSize(600,400)
app.addButtons(["Submit", "Back"], press)
def press(Decrypt):
app.removeAllWidgets()
app.addLabel("title", "Decryption")
app.addEntry("Phrase")
app.setEntryDefault("Phrase", "Enter Phrase Here")
app.addEntry("Key")
app.setEntryDefault("Key", "Enter Key Here")
phrase = app.getEntry("Phrase")
key = app.getEntry("Key")
app.addLabel("RESULT", "This is your decrypted message: ")
def press(Back):
app.removeAllWidgets()
app.addLabel("title", "Vigenere Chipher")
app.setBg("cyan")
app.setSize(600,420)
app.addButtons(["Encrypt", "Decrypt", "Close"], press)
app.addButtons(["Submit", "Back"], press)
app.addButtons(["Encrypt", "Decrypt", "Close"], press)
app.go()
Thankyou for your help
You've defined multiple functions with the same name press. Only the last definition will be used (the decrypting one), as it will replace the earlier definitions - this is not what you're intending.
You've then linked all buttons to this function.
So, the first thing you need to change, is to give each function its own name:
def encrypt()
...
def decrypt()
...
Then, you need to change how you've linked the buttons to the function.
You have two options:
1) Have a generic press function, that uses an IF statement on the button name, to determine the action:
def press(btn):
if btn == "Encrypt":
encrypt()
elif btn == "Decrypt":
decrypt()
elif btn == "Close":
app.stop()
app.addButtons(["Encrypt", "Decrypt", "Close"], press)
2) Directly link each button to the correct function.
app.addButtons(["Encrypt", "Decrypt", "Close"], [encrypt, decrypt, app.stop])
Later on, you'll come across the same issues with your Submit & Back buttons, and will need to use the same tactics...
Related
So here I have a program which first displays a information message and then you click next and it tells you to input your name before opening up the main window.
INFO ->(next) ENTER NAME ->(next)
When I enter my name in the entry box I want it to be checked that it does not contain 1.numbers and 2.is not blank. under the validate="key" option it means that once I start typing it validates. But rather I want it to only check the name once i press the NEXT button... If not it will open errorbox()
class errorbox():
def __init__(self):
windowError = Tk()
windowError.title("Error")
windowError.geometry('300x400')
error_message = Label(windowError, font=("Bold", 10), justify="left", text="Please enter a valid name")
def clicked1():
description.configure(text="Please enter your name")
nameBox = Entry(windowSplash, width=20, textvariable=name)
nameBox.place(rely=0.5, x=130, anchor=W)
reg = windowSplash.register(validate)
nameBox.config(validate="none",validatecommand=clicked2)
button2 = Button(text="Next", bg="white", width=5, command=lambda:[clicked2(),validate()])
button2.place(rely=0.5, x=300, anchor=E)
button1.destroy()
def validate(input):
if input.isdigit():
print("Invalid name was entered" + input)
errorbox()
return False
elif input is "":
print("No name entered")
errorbox()
return False
else:
return True
def clicked2():
print(name.get(), "logged in...")
windowSplash.destroy()
windowTool = Tk()
windowTool.title("Radial Measurements Calculator Tool")
windowTool.geometry('300x400')
name = StringVar()
windowSplash.mainloop()
Welcome to Stack Overflow Community.
I might have interpreted your question, but please make sure that next time you provide a minimal, reproducible example when you ask.
Here are a couple of things that I have observed.
The validate function takes input as a parameter, so make sure you pass that in the lambda function by lambda input = name.get(): [clicked2(),validate(input)].
By checking input.isdigit() does not guarantee that there might not be numbers after/between characters, so I suggest you to iterate through the string and check for isdigit()/type() or use re module. Also, an efficient way to check for empty string could be if not name.get():.
If you aim to open the new window only after the validation, I suggest you to call clicked2 from the validate function under a condition and not form the next button, because in this case your return form validate isn't used for anything.
Ultimately I want to make a small program with a text box where you have to type in the first 50 or so digits of Pi. What I want is for nothing to happen if the user types the correct characters, but I want something to flash red if they input the wrong character. For example, if the user types "3.1", nothing happens but the text showing up in the text box, but if they then type the wrong number, like "3.15", I want something to flash red.
from tkinter import *
def input(event):
inp = (ent.get('1.0', END))
if inp == '3':
print(inp)
else:
print(('--') + (inp))
root = Tk()
root.title('pi, okay')
root.geometry('425x50')
ent = Text(root, width = 50, height = 1)
ent.bind('<KeyRelease>', input)
ent.pack()
mainloop()
What I think SHOULD happen with this is for the console to print "3" IF the user inputs a "3", and for the console to print "--(whatever else the user would have typed)" if it is not a 3. But what actually happens is that the program will print "--(input)" no matter what.
You can use something like this if you need only one line of Input:
var = StringVar()
ent = Entry(root, width=50, textvariable=var)
def check_value(var, ent, *args):
pi = "3.1415"
if not pi.startswith(var.get()):
print("wrong input")
ent.config(fg="red")
else:
ent.config(fg="black")
var.trace('w', lambda *args: check_value(var, ent, *args))
ent.pack()
Here, var.trace() will call function check_value everytime when user types anything in Entry widget. You can add you logic there to verify the input value and change UI(or print logs) based on verification result.
In my program, the entry widget no longer validates after the delete command has been used on it - the idea is that if it meets a certain requirement, the text in the box is automatically deleted but continues to validate the input.
from tkinter import *
TEXT_TO_MATCH = 'APPLE'
def validate(userinput):
if userinput == TEXT_TO_MATCH:
print(True)
input_box.delete(0, END)
else:
print(False)
return True
window = Tk()
window.title('Delete after validation')
reg = window.register(validate)
input_box = Entry(window, validatecommand=(reg, '%P'), validate='all')
input_box.pack()
window.mainloop()
The entry widget automatically resets the validate option to "none" when you edit the entry widget from within the validation function.
You can re-enable the validation by using after_idle to reset the validate option after control has been returned to mainloop
def validate(userinput):
if userinput == TEXT_TO_MATCH:
input_box.delete(0, END)
input_box.after_idle(lambda: input_box.configure(validate="all"))
return True
I have a script that continually takes in text and outputs text (its a text based game)
I would like to run it through a tkinter GUI as opposed to the console
Python : Converting CLI to GUI
This question perfectly answers how to convert "print" into a GUI insert.
The problem is that my game obviously runs through a ton of loops, and that screws up the "app.mainloop()" because it either never runs (and then the GUI never shows up) or you run it first, and it doesn't let anything else run.
I suppose I could try and and stagger these loops somehow, but that seems very hackish. I could also try to modify my entire codebase to run inside the app.mainloop(), but what I really think I need is multiple threads. Problem is, I have no idea how to make that work.
There are a few other questions, but they either don't work or don't make much sense:
Tkinter with multiple threads
Run process with realtime output to a Tkinter GUI
Thanks.
Edit: extremely simplified code:
def moveNorth():
print('You have moved north')
def interpreter(command):
if command == 'go north':
moveNorth()
else:
print('invalid command')
def listener():
playerchoice = sys.stdin.readline().strip()
return playerchoice
if __name__ == '__main__':
print('Welcome')
while playing:
interpreter(listener())
I think you might be making it more complicated than it needs to be.
For Tkinter at least it is very simple change console interactions into a GUI interaction instead.
The simplest example I can give is to use an Entry field for user input and a Text widget for the output.
Here is a simple example of a console based game being moved to a GUI using Tkinter.
Console number guessing game:
import random
print("simple game")
print("-----------")
random_num = random.randint(1, 5)
print(random_num)
x = True
while x == True:
#Input for user guesses.
guess = input("Guess a number between 1 and 5: ")
if guess == str(random_num):
#Print answer to console.
print("You win!")
x = False
else:
print("Try again!")
Here is the Tkinter GUI example of the same game:
import tkinter as tk
import random
root = tk.Tk()
entry_label = tk.Label(root, text = "Guess a number between 1 and 5: ")
entry_label.grid(row = 0, column = 0)
#Entry field for user guesses.
user_entry = tk.Entry(root)
user_entry.grid(row = 0, column = 1)
text_box = tk.Text(root, width = 25, height = 2)
text_box.grid(row = 1, column = 0, columnspan = 2)
text_box.insert("end-1c", "simple guessing game!")
random_num = random.randint(1, 5)
def guess_number(event = None):
#Get the string of the user_entry widget
guess = user_entry.get()
if guess == str(random_num):
text_box.delete(1.0, "end-1c") # Clears the text box of data
text_box.insert("end-1c", "You win!") # adds text to text box
else:
text_box.delete(1.0, "end-1c")
text_box.insert("end-1c", "Try again!")
user_entry.delete(0, "end")
# binds the enter widget to the guess_number function
# while the focus/cursor is on the user_entry widget
user_entry.bind("<Return>", guess_number)
root.mainloop()
As you can see there is a bit more code for the GUI but most of that is the GUI design.
The main part that you need to change is the use of entry vs input for your answers and the use of insert vs print for your response. The rest is really just design stuff.
If you want to keep the questions on a continuous nature you can update the label with a new question or you could even use tkinters askstring function for each new question. There are many options.
the main thing is getting the value of the user answer, using that answer to test with the question, then printing the results to the text box.
I'm trying to make a log in program, but I can't get it to work.
When I'm running the code and type in the right Username and Password, it says wrong input.
import tkinter
window = tkinter.Tk()
window.title('LoginPage')
gebruikersnaam = 'Donald'
wachtwoord = 'Trump'
lblUsername = tkinter.Label(window,text='Username:')
lblUsername.pack(fill=tkinter.X)
entUsername = tkinter.Entry(window)
entUsername.pack(fill=tkinter.X)
lblPassword = tkinter.Label(window,text='Password:')
lblPassword.pack(fill=tkinter.X)
entPassword = tkinter.Entry(window)
entPassword.pack(fill=tkinter.X)
lblResult = tkinter.Label(window)
lblResult.pack(fil=tkinter.X)
def checkLogin(username,password):
if str(username) == gebruikersnaam and str(password) == wachtwoord:
lblResult.configure(text='Login was succesfull')
else:
lblResult.configure(text='Username or Password is wrong')
btn = tkinter.Button(window,text='Login',command= lambda x=entPassword.get(), y=entUsername.get():checkLogin(y,x))
btn.pack(fill=tkinter.X)
window.mainloop()
Right now, your code calls get() methods as soon as it runs, that's why you are getting empty values and "wrong input".
You need to make get() methods run after button click to give user some time to actually enter some values.
So you should pass Entrys as parameters and call get() methods inside of lambda.
btn = tkinter.Button(..., command=lambda x=entUsername, y=entPassword: checkLogin(x.get(), y.get()))
BUT, I think, instead of lambda, you should use a wrapper function, that calls checkLogin. Which makes your code easier to read and follow.
def wrapperCheck():
checkLogin(entUsername.get(), entPassword.get())
btn = tkinter.Button(window, text='Login', command=wrapperCheck)
btn.pack(fill=tkinter.X)
EDIT: Yet, even better approach is (thanks to comment from Bryan Oakley), remove parameters from your method and fetch those values inside of it.
def checkLogin():
username = entUsername.get()
password = entPassword.get()
#since get returns string objects you dont need str calls
if username == gebruikersnaam and password == wachtwoord:
lblResult.configure(text='Login was succesfull')
else:
lblResult.configure(text='Username or Password is wrong')
#also you can remove these temp values and directly compare like below
# entUsername.get() == gebruikersnaam and entPassword.get() == wachtwoord
btn = tkinter.Button(window,text='Login',command= checkLogin)