Compare tkinter Entry to actual addition answer - python

I'm trying to compare the input of an Entry box to the actual answer. This is one of my first python projects and I'm still very confused and to be honest I don't even know how to start asking the question.
The user will click either the addition or subtraction button. A string will appear asking "What does 4 + 5 equal?" The numbers are generated randomly.
I then insert an Entry box using the tkinter library. I don't know how to get() the input and compare it to the sum or difference of the actual numbers.
I was trying to follow this video but I've failed using other methods as well. FYI, I was focusing on the addition method mostly so if you test, that with addition first.
from tkinter import Entry
import tkinter as tk
import random as rand
root = tk.Tk()
root.geometry("450x450+500+300")
root.title("Let's play Math!")
welcomeLabel = tk.Label(text="LET'S PLAY MATH!").pack()
startLabel = tk.Label(text='Select a math operation to start').pack()
def addition():
a = rand.randrange(1,10,1)
b = rand.randrange(1,10,1)
global Answer
Answer = a + b
tk.Label(text="What does " + str(a) + " + " + str(b) + " equal? ").place(x=0, y=125)
global myAnswer
myAnswer = Entry().place(x=300, y= 125)
def checkAnswer():
entry = myAnswer.get()
while int(entry) != Answer:
if int(entry) != Answer:
tk.Label(text="Let's try again.").pack()
elif int(entry) == Answer:
tk.Label(text="Hooray!").pack()
addBtn = tk.Button(text="Addition", command=addition).place(x=100, y = 60)
subBtn = tk.Button(text="Subtraction", command=subtraction).place(x=200, y=60)
checkBtn = tk.Button(text="Check Answer", command=checkAnswer).place(x=300, y = 150)
tk.mainloop()

All you need to do to fix your issue is separate the creation of your Entry() object from the placing of it:
def addition():
a = rand.randrange(1,10,1)
b = rand.randrange(1,10,1)
global Answer
Answer = int(a + b)
tk.Label(text="What does " + str(a) + " + " + str(b) + " equal? ").place(x=0, y=125)
global myAnswer
myAnswer = Entry()
myAnswer.place(x=300, y= 125)
Entry() returns the entry object, which has a get() method. However, when you chain .place(), you return its result instead, which is None. Therefore you never actually store the Entry object in your variable.
Also, it is a good idea to ensure that Answer is an int as well.

to get the value of the answer do answer = myanswer.get() or any other variable name. To compare it to the correct answer do
if int(answer) == correctAnswer:
#the code
is that what you were asking?

Consider this and below is an example that responds to whether or not the number entered to answer is "Correct!" or "False!" when the user clicks on answer_btn:
import tkinter as tk
import random as rand
def is_correct():
global answer, check
if answer.get() == str(a + b):
check['text'] = "Correct!"
else:
check['text'] = "False!"
def restart():
global question, check
random_nums()
question['text'] = "{}+{}".format(a, b)
check['text'] = "Please answer the question!"
def random_nums():
global a, b
a = rand.randrange(1, 10, 1)
b = rand.randrange(1, 10, 1)
root = tk.Tk()
#create widgets
question = tk.Label(root)
answer = tk.Entry(root, width=3, justify='center')
check = tk.Label(root)
tk.Button(root, text="Check", command=is_correct).pack()
tk.Button(root, text="New question", command=restart).pack()
#layout widgets
question.pack()
answer.pack()
check.pack()
restart()
root.mainloop()

Related

How to fix the game function?

im beginner on python and as a practice i want to solve this ( https://www.practicepython.org/exercise/2014/07/05/18-cows-and-bulls.html ) by tkinter
when i run this code and press start button it hangs and no error is shown in pycharm, even when i debug
obviously the bug is in game code
can you help me with this?
from tkinter import *
import random
def entry_box():
global user_guess
start_button.destroy()
user_guess=Entry(win,font='Bradly 12', width=25)
user_guess.insert(END,'0000')
user_guess.place(x=150,y=150)
game()
def game():
global user_guess , result
digit=[]
num =[]
counter = 0
for i in range(4):
digit.append(random.randint(0,9))
while num != digit:
num = list(user_guess.get())
cow = 0
bull = 0
for i in range(4):
if int(num[i]) in digit:
if int(num[i])==digit[i]:
cow += 1
else:
bull += 1
counter += 1
result.set('cow = %d \n bull = %d'%(cow,bull))
result.set('WELL DONE! YOU FOUND IT AFTER %d GUESS ' %counter )
win = Tk()
win.title('Let\'s play')
frame = Frame(win, height=300, width=500, bg='lightblue')
frame.pack()
result = StringVar()
result.set('cow=0\nbull=0')
start_button = Button(win , text='start', font='Broadway 15',command = entry_box)
start_button.place(x=200, y=175)
results = Label(win, textvariable=result, font='Broadway 20',bg='lightblue', fg='darkblue')
results.place(x=200 , y=0)
win.mainloop()
Your code hangs because of multiple reasons:
as #furas mentioned, num != digit will always be False, because you compare list of chars with list of digits;
Even if you fix this, your code will just never exit the loop: you will compare the first list [0,0,0,0] with the random one (num) and unless they are equal, you will get False. Then the code takes the same two lists, and compares them infinitely, with always the same outcome. You can see this easily if you add a print statement in the while loop.
This is one possible solution:
import tkinter as tk
import random
class MainGame(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.main_frame = tk.Frame(self, height=300, width=500, bg='lightblue')
self.main_frame.pack()
self.result = tk.StringVar()
self.result.set("Cow=0\nBull=0")
self.start_button = tk.Button(self.main_frame, text='start', font='Broadway 15', command=self.entry_box)
self.start_button.place(x=200, y=175)
self.results = tk.Label(self.main_frame, textvariable=self.result, font='Broadway 20', bg='lightblue',
fg='darkblue')
self.results.place(x=200, y=0)
def entry_box(self):
self.start_button.destroy()
self.user_guess = tk.Entry(self.main_frame, font='Bradly 12', width=25)
self.user_guess.insert(tk.END, '0000')
self.user_guess.place(x=150, y=150)
self.start_game()
def start_game(self):
user_input = self.user_guess.get()
num = [int(cypher) for cypher in user_input]
counter = 0
game_playing = True
while game_playing is True:
digit = [random.randint(0, 9) for i in range(4)]
cow, bull = 0, 0
for i in range(4):
if num[i] in digit:
if num[i] == digit[i]:
cow += 1
else:
bull += 1
counter += 1
print("Turn {}: cows = {}\t bulls={}".format(counter, cow, bull))
if cow == 4:
print("digit = {}\t num = {}".format(digit, num))
game_playing = False
if __name__ == "__main__":
game = MainGame()
game.mainloop()
First thing: do not use global imports (from tkinter import *). Instead use import tkinter as tk.
Then let's look at your entry_box: the function as you defined it takes no argument, but then you expect it to delete the start_button: the only reason this works is because the function is defined in the same script, but as soon as you move it to another script for example, it will fail. You did the same with result, which is defined in the main body, but then used in game, and for the win variable used to set the user_guess Entry widget.
The solution for this is to 1) reformat your code to account for these changes, which can work for something this small 2) use global variables, but this is usually considered a bad practice, because as your code grows it becomes a pain to track them down (plus many other reasons) 3) use classes, which is what I did here.
So here is what I did: I create a class, and in the __init__ I put the things I want to have available when the script starts, or that I generally want to use throughout the game: the main frame where I will place the widgets, the results or the start button.
Then the entry_box function: You see that I pass just self: since the start button was defined as self.start_button in the __init__, there is no reason to pass it as an argument, because it is already stored in self as an attribute. Same with the main_frame where I put the Entry widget.
Lastly the game function: here, I make sure that the comparison between num and digit is between int numbers. Then if the comparison fails, I draw another random list of 4 elements for digit and repeat. The results are printed in the terminal.
This is by no means perfect, and you can easily improve it in a few ways:
1) the comparison between the lists can be easily made faster and shorter with numpy;
2) I did not update the results widget. You should be able to do it with what I told you here.
3) I assume you want the user to input the user_guess. You just have to modify a bit this code
Hope it helps!

How to print Python Output in Entry widget?

I've searched through the whole Internet for how to do this, and nothing came to me. There were some similar topics, when programmers asked of how to parse 'int' numbers to the Entry output. But it is much simpler because you just use getters, then insert() - and voila.
I am trying to do the following thing. I print the text written in one line. And for each word I want to count how many times it appeared in the same text.
E.g., I print in my first Entry "one two one two three" - I get "0 0 1 1 0" in the second Entry widget.
Any non-space sequence of characters is considered a word.
from tkinter import *
class DM_3_1:
def __init__(self):
root = Tk()
root.geometry('250x150')
root.title("DiscreteMaths_3_1")
usertext = StringVar()
Label_1 = Label(root, text="Input")
Label_2 = Label(root, text="Output")
inputField = Entry(root, textvariable = usertext)
outputField = Entry(root)
inputField.bind('<Return>', lambda _: printLine())
def printLine():
counter = {}
for word in inputField.get():
counter[word] = counter.get(word, 0) + 1
Ans = print(counter[word] - 1, end=' ')
outputField.insert(0, str(Ans))
Label_1.grid(row = 0)
Label_2.grid(row = 1)
inputField.grid(row = 0, column = 1)
outputField.grid(row = 1, column = 1)
root.mainloop()
DM_3_1()
What I get in the output now: Here is the screenshot
As far as you can see, the application works, but there's 'NoneNoneNone...'(depends on the number of characters, including whitespaces) instead of '0 0 1 1 0'. How do I solve my problem? Where's a logical mistake? I guess, it's about the function, but I don't actually see the mistake.
You have set Ans to be equal to print rather than the value it was supposed to be. Also your for loop was getting every character rather than every word.
Corrected code:
from tkinter import *
class DM_3_1:
def __init__(self):
root = Tk()
root.geometry('250x150')
root.title("DiscreteMaths_3_1")
usertext = StringVar()
Label_1 = Label(root, text="Input")
Label_2 = Label(root, text="Output")
inputField = Entry(root, textvariable = usertext)
outputField = Entry(root)
inputField.bind('<Return>', lambda _: printLine())
def printLine():
counter = {}
words=inputField.get().split()
for word in words:
counter[word] = counter.get(word, 0) + 1
Ans = counter[word] - 1
print(Ans, end=" ")
outputField.insert(END, str(Ans))
Label_1.grid(row = 0)
Label_2.grid(row = 1)
inputField.grid(row = 0, column = 1)
outputField.grid(row = 1, column = 1)
root.mainloop()
DM_3_1()
edit:
As Mike-SMT pointer out its easier to use .split
code edited to use .split
So my solutions to this kind of counter is to track each word and keep a list of all the words. Then keep a list of all the unique words. The count each time a unique word appears in the complete list.
I restructured your code a bit to conform a bit better with standards.
I rewrote your printLine method to keep track of all the words in a string and create a dictionary that contains a list of all the unique words and how many times they show up in the string.
When writing a class you will need to learn to use self. to convert standard variables into class attributes. Class attributes can be accessed from anywhere in the class including methods within the class. Using regular variables will likely cause problems as they are not available to methods after __init__ has completed.
take a look at the below code.
import tkinter as tk
class DM_3_1:
def __init__(self, parent):
self.root = parent
self.root.geometry('250x150')
self.root.title("DiscreteMaths_3_1")
Label_1 = tk.Label(self.root, text="Input")
Label_2 = tk.Label(self.root, text="Output")
Label_1.grid(row=0)
Label_2.grid(row=1)
self.inputField = tk.Entry(self.root)
self.outputField = tk.Entry(self.root)
self.inputField.grid(row=0, column=1)
self.outputField.grid(row=1, column=1)
self.inputField.bind('<Return>', self.printLine)
def printLine(self, Event):
word_list = []
counter = 0
unique_words_in_string = []
total_times_word_appears = {}
for word in self.inputField.get().split():
word_list.append(word)
if word not in unique_words_in_string:
unique_words_in_string.append(word)
for word in unique_words_in_string:
counter = 0
for other_word in word_list:
if word == other_word:
counter += 1
total_times_word_appears[word]=counter
self.outputField.delete(0, "end")
self.outputField.insert("end", total_times_word_appears)
if __name__ == "__main__":
root = tk.Tk()
DM_3_1(root)
root.mainloop()

str AttributeError: 'str' object has no attribute 'set' (updating label)

This is a simple math game. The function of interest is checkAnswer(). I am trying to update label1 so that the label updates with the new str instead of continuously printing multiple labels.
Error:
AttributeError: 'str' object has no attribute 'set'
from tkinter import *
from random import randint
num1 = 0
num2 = 0
userAnswer = 0
answer = 0
score = 0
labeltext = ""
#PROGRAM FUNCTIONS
def question():
global num1, num2
global answer
num1 = randint(1,10)
num2 = randint(1,10)
question = Label(text = "What is " + str(num1)+ " + " + str(num2) + "?").pack()
answer = num1+num2
print(answer) #testing purposes
def userAnswer():
global userAnswer
userAnswer = IntVar()
entry = Entry(root, textvariable = userAnswer).pack()
submit = Button(root, text = "submit", command = checkAnswer).pack()
def checkAnswer():
global labeltext
print(userAnswer.get())
if userAnswer == answer:
labeltext.set("good job")
score += 1
elif userAnswer != answer:
labeltext.set("oh no")
labeltext = StringVar()
label1 = Label(root, textvariable = labeltext).pack()
#INTERFACE CODE
root = Tk()
question()
userAnswer()
root.mainloop()
You are getting that AttributeError because you initially bind a string "" to the global name labeltext, and a Python string doesn't have a .set method. (That wouldn't make sense because Python strings are immutable). You eventually bind a Tkinter StringVar to labeltext, and StringVars do have a .set method, but your code does that after it's already tried to call .set on the plain Python string.
A similar problem will occur with the IntVar you named userAnswer. That has an additional problem: its name clashes with one of your functions. You can't do that!
Here's a repaired version of your code, with a few other minor changes. There's no need to use the global directive on those StringVars or the IntVar since you are simply calling methods of those objects. You only need global if you need to perform an assignment on a global object, merely accessing the existing value of a global or calling one of its methods doesn't need the global directive.
from tkinter import *
from random import randint
#PROGRAM FUNCTIONS
def question():
global true_answer
num1 = randint(1,10)
num2 = randint(1,10)
Label(text="What is " + str(num1)+ " + " + str(num2) + "?").pack()
true_answer = num1 + num2
print(true_answer) #testing purposes
def answer():
Entry(root, textvariable=userAnswer).pack()
Button(root, text="submit", command=checkAnswer).pack()
def checkAnswer():
global score
print(userAnswer.get()) #testing purposes
if userAnswer.get() == true_answer:
labeltext.set("good job")
score += 1
else:
labeltext.set("oh no")
label1 = Label(root, textvariable=labeltext).pack()
#INTERFACE CODE
root = Tk()
true_answer = 0
score = 0
userAnswer = IntVar()
labeltext = StringVar()
question()
answer()
root.mainloop()
However, that code still has several problems. It can only ask a single question. And each time you hit the "submit" button it adds a new Label widget, which I don't think you really want.
It's not a good idea to use global variables. They break modularity, which makes the code harder to understand, and harder to modify and re-use.
Here's an enhanced version of your program which puts everything into a class, so we can use instance attributes instead of globals.
This version asks multiple questions. It doesn't have a "submit" button, instead the question is automatically submitted when the user hits the Enter / Return key, either on the main keyboard or the numeric keypad.
import tkinter as tk
from random import randint
class Quiz(object):
def __init__(self):
root = tk.Tk()
# The question
self.question_var = tk.StringVar()
tk.Label(root, textvariable=self.question_var).pack()
# The answer
self.user_answer_var = tk.StringVar()
entry = tk.Entry(root, textvariable=self.user_answer_var)
entry.pack()
# Check the answer when the user hits the Enter key,
# either on the main keyboard or the numeric KeyPad
entry.bind("<Return>", self.check_answer)
entry.bind("<KP_Enter>", self.check_answer)
self.true_answer = None
# The response
self.response_var = tk.StringVar()
self.score = 0
tk.Label(root, textvariable=self.response_var).pack()
# Ask the first question
self.ask_question()
root.mainloop()
def ask_question(self):
num1 = randint(1, 10)
num2 = randint(1, 10)
self.question_var.set("What is {} + {}?".format(num1, num2))
self.true_answer = num1 + num2
#print(self.true_answer) #testing purposes
def check_answer(self, event):
user_answer = self.user_answer_var.get()
#print(user_answer) #testing purposes
if int(user_answer) == self.true_answer:
text = "Good job"
self.score += 1
else:
text = "Oh no"
self.response_var.set('{} Score={}'.format(text, self.score))
# Clear the old answer and ask the next question
self.user_answer_var.set('')
self.ask_question()
Quiz()
Please note the import tkinter as tk statement. It's much better to use this form than from tkinter import * since that "star" import dumps 130 names into your namespace, which is messy, and can lead to name collisions, especially if you do star imports with other modules. The import tkinter as tk form requires you to do a little more typing, but it also makes the code much easier to read, since it's obvious which names are coming from Tkinter.
I've also changed the names of the variables and the class methods (functions) so they conform to the Python PEP-0008 style guide.
There are various further enhancements that could be made. In particular, this code doesn't gracefully handle user input that isn't a valid integer.

TkInter Entry Box Being Disabled

I'm working on a GUI Python program using Tkinter.
I have a function that is called when a button is pressed (and when the program is loaded). The program is currently unfinished and only checks data validation at this current point. As the default entry is current invalid, it throws an error.
However, after this point, the entry box is disabled and will not let me enter any data. I cannot figure out why this is happening and I was wondering if someone could tell me the reason so I can work on a solution.
Thanks
import sys
import random
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
root = Tk()
root.title("COSC110 - Guessing Game")
hint = StringVar()
guesses = []
guess_input = ''
def loadWordList(filename): #Load the words from a file into a list given a filename.
file = open(filename, 'r')
line = file.read().lower()
wordlist = line.split()
return wordlist
word = random.choice(loadWordList('words.txt'))
def getHint(word, guesses): #Get hint function, calculates and returns the current hint.
hint = ' '
for letter in word:
if letter not in guesses:
hint += '_ '
else:
hint += letter
return hint
def guessButton(guess, word, guesses):
guess = str(guess_input)
guess = guess.lower()
if not guess.isalpha():
is_valid = False
elif len(guess) !=1:
is_valid = False
else:
is_valid = True
while is_valid == False:
messagebox.showinfo("Error:","Invalid input. Please enter a letter from a-z.")
break
hint.set(getHint(word, guesses))
return hint
label_instruct = Label(root, text="Please enter your guess: ")
label_instruct.grid(row=1,column=1,padx=5,pady=10)
guess_input = Entry(root,textvariable=guess_input)
guess_input.grid(row=1, column=2)
guess_button = Button(root, text="Guess", width=15, command=guessButton(guess_input,word,guesses))
guess_button.grid(row=1, column=3,padx=15)
current_hint = Label(root, textvariable=hint)
current_hint.grid(column=2,row=2)
label_hint = Label(root, text="Current hint:")
label_hint.grid(column=1,row=2)
label_remaining = Label(root, text="Remaining guesses: ")
label_remaining.grid(column=1,row=3)
root.mainloop() # the window is now displayed
Any tips are appreciated.
There are two apparent problems.
Firstly, you shouldn't use
guess_button = Button(root, text="Guess", width=15, command=guessButton(guess_input,word,guesses))
because you can't call a function with arguments on the command config.
My suggestion would be to take a look here and use one of the proposed methods, I particularly like the one using functools and partial:
from functools import partial
#(...)
button = Tk.Button(master=frame, text='press', command=partial(action, arg))
with action being the function you want to call and arg the parameters you want to call separated by a comma.
Secondly, you are using
guess = str(guess_input)
which doesn't return the Entry typed text, use instead
guess = guess_input.get()
PS: Albeit not directly related to your question, you should use
if var is False:
instead of
if var == False:

Trying to make a label update in tkinter from a random variable

I'm trying to make a virtual dice within Python and using tkinter for the GUI, basically generating a random number form 1-6 but i cannot get the label to update with the result of the 'roll'. Can anyone help me please? This is the code I have so far:
from tkinter import*
import random
class Application(Frame):
result = 0
def __init__(self, master):
Frame.__init__(self,master)
self.grid()
self.create_widgets()
def create_widgets(self):
self.label1 = Label(self)
self.label1["text"] = "You rolled a " + str(self.result)
self.label1.grid()
self.button1 = Button(self, text = "Roll again?")
self.button1["command"] = self.rd()
self.button1.grid()
def rd(self):
result = random.randint(1, 6)
self.label1.config(text= "You rolled a " + str(self.result))
root = Tk()
root.title("Dice")
root.geometry("100x50")
app = Application(root)
root.mainloop()
self.button1["command"] = self.rd()
On this line, you're assigning the result of self.rd to the command. rd returns None, so you're effectively saying that the button has no command at all. Try:
self.button1["command"] = self.rd
Additionally, on these lines:
result = random.randint(1, 6)
self.label1.config(text= "You rolled a " + str(self.result))
You create a variable result, but then config the label with self.result, which is completely independent of result, so it will always display 0. Try:
result = random.randint(1, 6)
self.label1.config(text= "You rolled a " + str(result))
Or, if you want to keep track of the last derived result for other purposes,
self.result = random.randint(1, 6)
self.label1.config(text= "You rolled a " + str(self.result))
You have made two errors here:
You assign the result of a call to self.rd (which is None) as the button's command, not the method itself; and
You assign the random dice roll to the local name result, not the class attribute self.result.
The correct code would include:
self.button1["command"] = self.rd
# ^ note parentheses '()' removed
and:
def rd(self):
self.result = random.randint(1, 6)
# ^ note 'self.' added
self.label1.config(text= "You rolled a " + str(self.result))

Categories

Resources