Save user input from tkinter to a variable and check its content - python

I have written a script simulating a card game in Python where the user decides how many cards and how many piles of cards they want to play with. This input is controlled by following code where boundary_1 and boundary_2 give upper and lower limit in an integer interval and message is the user input:
def input_check(boundary_1, message, boundary_2):
run = True
while run:
try:
user_input =int(input(message))
if boundary_1 <= user_input <= boundary_2:
run = False
return user_input
else:
print ("Incorrect Value, try again!")
run = True
except ValueError:
print ("Incorrect Value, try again!")
I now want to try and make a GUI out of this card game using tkinter and I would therefore like to know if there's any way to save the user's input to a variable that could be sent into the input_check() function above? I've read through some tutorials on tkinter and found the following code:
def printtext():
global e
string = e.get()
text.insert(INSERT, string)
from tkinter import *
root = Tk()
root.title('Name')
text = Text(root)
e = Entry(root)
e.pack()
e.focus_set()
b = Button(root,text='okay',command=printtext)
text.pack()
b.pack(side='bottom')
root.mainloop()
The following code simply prints the user's input in the Textbox, what I need is the user's input being checked by my input_check() and then have an error message printed in the Textbox or the input saved to a variable for further use if it was approved. Is there any nice way to do this?
Many thanks in advance!

The simplest solution is to make string global:
def printtext():
global e
global string
string = e.get()
text.insert(INSERT, string)
When you do that, other parts of your code can now access the value in string.
This isn't the best solution, because excessive use of global variables makes a program hard to understand. The best solution is to take an object-oriented approach where you have an "application" object, and one of the attributes of that object would be something like "self.current_string".
For an example of how I recommend you structure your program, see https://stackoverflow.com/a/17470842/7432

Related

How to pass input from one entry widget into multiple sequential functions OR how to pass button input into a function

So I essentially have 2 codes at the moment, a text-based game and a GUI, that function separately that I'm trying to combine. The game basically has a series of functions it can call based on user input - eg:
def game():
print("Would you like to play? [Y/N]")
start = inpupt().lower().strip()
if start == 'y':
adventure()
elif start == 'n':
goodbye()
with adventure() calling two or more options leading to their respective functions, etc. etc.
The GUI is tkinter based, has a label, a scrolled text box, an entry widget, and an "enter" button. I've created my own "print" functions that allow the code to input text into the scrolled text box, and then disable the box again, so that the user doesn't directly edit it. I've also been able to have the button/enter key pull text from the entry widget and print it in the text box, and then clear the entry widget.
What I don't know how to do is get the entry widget/button to integrate with the different game functions. It seems like you can set a function for the button - which is how I was able to pull the entry text, but I don't know how to then pass that pulled information on to another function.
I tried something like this which worked for just entering text into the scrollbox with this attached to the button as the command:
def clicked(self, variable, event=None):
self.variable = self.userin.get()
self.print1(self.variable)
self.clear_text()
But then I don't know how to call that as a part of my game function. This was all I could think of (and I know it's wrong and bad, ok?):
def game():
print1("Would you like to go on an adventure? [Y/N]")
clicked(self, start) #trying to use "start" as the variable
if self.start == "y":
self.print1("Let's go!")
elif self.start == "n":
self.print1("Okay, bye!")
else:
self.print1("What?")
But then game() is trying to call the clicked() function instead of waiting for the button, so it's not really pulling the input from the user. I also kept getting the error that "start" was not defined, probably for that reason. (Also please ignore any errors in where "self" is supposed to go... this was my first stab at using classes, and I know I'm probably doing more wrong than right, I'd figure it out if you had a solution in this vein)
So is there a way to get the button to pass the user input into the function, without the function being exclusive to the button? (Like I know I could get it to make one choice, but then how would I get the button to function for the next choice?)
My "Plan B" was to just create buttons for user input - either yes/no or a/b/c/d - so that the function of the buttons would be the same : return "B" (or something like that), but then how would I get the function to pull that input? Or wait for that input?
I have googled this six ways to Sunday, and have not found anything that works for this situation. I thought I'd found something with the CallBack function - but then I realized they were using it for multiple buttons, instead of the same button for multiple functions.
Here is a quick example using some of what you provided that should demonstrate how you can link your conditions for the button. Regardless of how you actually structure your code, the concept should demonstrate one way you could do it. I would look into some tkinter tutorials as these concepts are well documented. I hope this helps.
import tkinter as tk
def clicked():
entry_value = entry.get()
entry.delete("0", tk.END)
print(entry_value)
# set up whatever conditions you want the button to be linked with
if str(entry_value) == "y":
print("Alright, get ready!")
# now you call your function to start the game here
start_game()
elif str(entry_value) == "n":
print("Ok, maybe another time")
label.config(text="Ok, maybe another time, closing the game now")
root.after(2500, lambda: root.destroy())
else:
label.config(text="Invalid entry, please enter a lower case 'y' or 'n'")
label.after(3000, reset_label)
def start_game():
# just an example this creates a new window to simulate some bhavior of the game
top = tk.Toplevel()
top.geometry("500x500")
top.title("New Game")
top.config(bg="blue")
# add things to the toplevel that shows the game? not sure what you want to do
def reset_label():
label.config(text="Would you like to play? [y/n]")
root = tk.Tk()
root.geometry("300x200")
label = tk.Label(root, text="Would you like to play? [y/n]")
label.pack()
entry = tk.Entry(root)
entry.pack()
button = tk.Button(root, text="Go", command=clicked)
button.pack()
root.mainloop()
Thank you for the suggestions! This is what I worked out (once I figured out the right way to change the command - no extra parenthesis!):
def choice1(event=None):
whereto = userin.get().lower().strip()
if whereto == "woods":
print1("The woods are spooky")
button["command"] = woods
elif whereto == "tavern":
print1("The tavern is crowded")
button["command"] = tavern
else:
print1("What?")
print1("Woods or Tavern?")
def clicked(event=None):
start = userin.get().lower().strip()
print1(userin.get())
clear_text()
changelabel("Let's go!")
if start == "y":
print1("Lets go!")
print1("Woods or Tavern?")
button["command"] = choice1
elif start == "n":
print1("Ok bye!")
else:
print1("What?")
print1("Do you want to play a game?")
I'd like to make the return key work the same as the button click, so I suspect I'm going to have to add the root.bind('<Return>', func) everywhere too

How to solve error "ValueError: could not convert string to float: ' ' " [duplicate]

It's the whole day I'm trying to solve this ridiculous problem, but without success; in this forum there is a lot of material, but usually they are enormous amounts of code and I cannot understand what's going on. On the internet they generally suggest casting but it doesn't work. To make things easy I wrote a sample code to illustrate the issues I'm having in a bigger code
import tkinter as tk
from tkinter import *
#from tkinter import ttk
my_window = tk.Tk()
my_label = tk.Label(master=my_window,text="N")
my_label.pack()
my_entry = tk.Entry(master=my_window)
my_entry.pack()
N = my_entry.get()
print(float(N))
my_window.mainloop()
Very nice and simple, but I get the following message
ValueError: could not convert string to float: ''
I tried many possibilities, but anything worked. Any little suggestion is very much appreciated. Thanks
Edit:
I want to understand how to assign float(N) to a global variable, say a, so I can use it later on in the code, and I just took the code of #Cool Cloud and modified it a little
import tkinter as tk
from tkinter import *
my_window = tk.Tk()
a=1.
def cast():
N = my_entry.get()
try: # Try to execute this
print(float(N))
a=float(N)
except ValueError: # If ValueError(meaning it is not a float) is triggered then print...
print('Not a number!')
my_label = tk.Label(master=my_window,text="N")
my_label.pack()
my_entry = tk.Entry(master=my_window)
my_entry.pack()
# N = my_entry.get() N will be empty as my_entry is empty when code is executed
Button(my_window,text='Click me to convert to
float',command=cast).pack() # A button to trigger event
print(a)
my_window.mainloop()
The output of this is in the following image
As you can see it directly prints 1.0 without waiting for the assignment a=float(N), so my doubt is how can I actually do this assignment, to use it later in my code. Thanks
P.S.: I understand that print(a) inside the definition of cast() would give correctly 123.0 in this case, but my problem is more general: I'm trying to understand how to entry an N value, making it float and "propagate" it to the rest of the program. My doubt is given by the fact that print(a) almost at the very last line of the program, and still doesn't wait for cast() to come in.
GUI programming is event driven, which means you will have to code based on events triggered. Every python code runs from top to bottom, and in this case all the code from top to bottom, outside functions, is executed. That means as soon as:
my_entry = tk.Entry(master=my_window)
my_entry.pack()
...is executed, the next line to be executed is N = my_entry.get(), and at the time of execution there is NOTHING inside the entry widget and hence N becomes empty string, and then you are trying to convert that empty string to a float, which will obviously give an error.
What you should be doing is, make a button and when you click it(event is triggered) and connect it to a function that will get the input and convert to float. So that when you click the button you have entered something onto the entry and now it is no longer empty.
import tkinter as tk
from tkinter import *
my_window = tk.Tk()
def cast():
N = my_entry.get()
try: # Try to execute this
print(float(N))
except ValueError: # If ValueError(meaning it is not a float) is triggered then print...
print('Not a number!')
my_label = tk.Label(master=my_window,text="N")
my_label.pack()
my_entry = tk.Entry(master=my_window)
my_entry.pack()
# N = my_entry.get() N will be empty as my_entry is empty when code is executed
Button(my_window,text='Click me to convert to float',command=cast).pack() # A button to trigger event
my_window.mainloop()
Another way, and an unpopular way, is to use DoubleVar() which will get() the text for you, in this case, your entry will accept any value but when you get() the value from the DoubleVar() it will raise a TclError if it is not a float.
def cast():
# Unlike before, error will be generated at get() and you dont need float()
# because if there is no error then it is guaranteed to be a float anyway
try:
N = var.get()
print(N)
except TclError: # If TclError is triggered then print...
print('Not a number!')
var = DoubleVar() # tkinter control variable
my_entry = tk.Entry(master=my_window,textvariable=var)
my_entry.pack()
Note: You can do my_entry.get() and it wont give any error even if the input is not a float, error comes only in the case of var.get().
From:https://www.geeksforgeeks.org/python-tkinter-entry-widget/
"get() : Returns the entry’s current text as a string."
I think that you can't just convert string to float.

Python entry input verification

I want to create a simple game in Python 3.8, and i need to verify an entry`s input in order to create it. Something like this:
if input.text == "":
print("Error")
but i don`t know how to do this in Python. I used to do that a lot in C# but here it s not that easy apparently.
Considering you are talking about an Entry and also have the Tag tkinter in your question I assume you want to get some user input from a tkinter Entry widget.
To get a value from a Entry widget you can use the get() method. This returns a string.
You can use a simple button command or a bind() to call a function that then checks the value of the entry field.
You can also throw in a strip() just in case the user uses a space or two without imputing anything else. This way a string of spaces still returns back as an error.
Here is a simple example:
import tkinter as tk
def check_entry():
value = entry.get().strip()
if value == '':
print('Error')
else:
print('Value is not an empty string. Now do something.')
root = tk.Tk()
entry = tk.Entry(root)
entry.pack()
tk.Button(root, text='Check Entry', command=check_entry).pack()
root.mainloop()
To get an input, you can use the input function. This will automatically return a string. Here is sample usage of this:
user_input = input("Please put your input here: ") # Get user input
if user_input == "": # Compare input to empty string
print("Error")
You can see the Python docs for more information about input.
Try with:
# get the input using the input built-in function
user_input = input("insert your input here: ")
# check the input
if user_input == "":
# raise an exception and stop the program
raise ValueError("invalid input! Empty string")

Tkinter cbox doesn't change var value

So, essentially what is going on is I made a password manager that had a password generation part to it, I moved it to a windowed Tkinter program for ease of use. I got everything down except for the check box, so at first when the function was called it would give me the error that alphabet had empty length so I set alphabet equal to the list with special characters. After that I tried them with while loops, same result. (this whole code is a function inside the program that only gets ran when a button is pressed) I know I could probably fix this issue with the init but I was hoping if anyone knew an easier way without rewriting too much. Here is the edit to make the code simplified. I used it with a while loop, and got the same result as the if statement. I get the error that a is not defined in this situation.
from tkinter import *
import random
def cbox_var():
while cbox_1 == True:
a = 10
while cbox_1 == False:
a = 20
print(a)
main = Tk()
cbox_1 = Checkbutton(main, text="yes or no")
cbox_1.pack()
testbutton = Button(main,text="Test", command=cbox_var)
testbutton.pack()
main.mainloop()
To get the value of a checkbutton you must assign one of the special tkinter variables to it. You can then get the value by calling the get method on the variable.
Example:
import tkinter as tk
def cbox_var():
checked = cbox_variable.get()
print("Checked?", checked)
main = tk.Tk()
cbox_variable = tk.BooleanVar()
cbox_1 = tk.Checkbutton(main, variable=cbox_variable, text="yes or no")
cbox_1.pack()
testbutton = tk.Button(main,text="Test", command=cbox_var)
testbutton.pack()
main.mainloop()

What is the significance of the number 4 when one feeds a callback to the show argument of a Tkinter Entry Box?

Question
Why is my random ascii character selector function outputting fours, and what is the significance of the number four in this context? Why am I not recieving an error message?
Remember, the question is not about how to solve the issue, it is about why that particular number was output.
Background and Code
I am trying to creating a basic email client. I thought that it would be cool for my password box to show random characters instead of the obvious *. So, I created a function which chose a random ascii letter.
import random
import string
def random_char():
char_select = random.randrange(52)
char_choice = string.ascii_letters[char_select]
return char_choice
When I run this in an interactive terminal, it spits out a random letter. But, when I run it through my widget
self.Password = Entry (self, show = lambda: random_char())
I am met with a bunch of fours.
Extra Credit
If you have the time, please visit my related question, How to have a Tkinter Entry box repeat a function each time a character is inputted?
The show parameter accepts a value not a callback. Tkinter is taking your callback object and trying to convert it to a string and that is what you get when you type in the Entry box.
Instead you can re-configure your Entry after you type by using binding:
def key(event):
entry.configure(show = random_char())
entry = tk.Entry (self)
entry.pack()
entry.bind("<Key>", key)
EDIT
Bryan Oakley is correct in that this will change all the characters to the same single random character as you type. Showing different random characters as you type is not the way you are supposed to use the Entry widget. You can try something like:
def key(event):
global real_password
global garbage
current_len = len(v.get())
if event.char and event.char in string.ascii_letters:
real_password += event.char
garbage += random_char()
garbage = garbage[:current_len]
v.set(garbage)
v = tk.StringVar()
real_password = ""
garbage = ""
entry = tk.Entry (self, textvariable = v)
entry.pack()
entry.bind("<KeyRelease>", key)
Of course there are lots of limitations, the last character typed is changed when the key is released not when is pressed, so you have to type fast :) , there is not control over the cursor movement keys etc. But anyway it was fun trying.

Categories

Resources