I am trying to make a question game in python using tkinter. I am struggling to define a function that can check the answer that the player clicked on and add to a score that is then printed out below the answers.
The outcome of the code whenever I click is that the score is 0 and the question and answers don't change. Also, if I click repeatedly, it prints 0 as a label below each other.
I only included all of my code so that if someone wanted to test the code out for themselves, it wouldn't throw up any errors.
import tkinter
import random
from random import shuffle
score = 0
def check_answer(answer):
if answer == answers[s][0] or answer == answers[s][1]:
global score
score += 1
lbl = tkinter.Label(window, text=score)
lbl.pack()
#This sets up a window and names it "The Hunt" but doesn't generate the window
window = tkinter.Tk()
window.title("The Hunt!")
#This sets the background colour
window.configure(background="#1C3F95")
#This generates labels and buttons that are the same or similar colour to the background
welcome = tkinter.Label(window, text="Welcome to the Hunt!", bg="#1C3F95")
begin = tkinter.Button(window, text="Click here to begin", bg="#1C7C95")
#This is my code for the question generation. As I made that for a pygame window, I obviously had to change it slightly
questions = ["What species of bird is also a nickname for New Zealand?", "Which Twins can you play as in Assassin's Creed Syndicate?",
"Which year was 'Killing In The Name' Christmas Number one?"]
answers = [["kiwi", "Kiwi", "Falcon", "Sparrow", "Crow"], ["frye", "Frye", "Bank", "Green", "Bundy"], ["2009", "2009",
"1999", "1993",
"2004"]]
#I had to do it in two separate lists as it's easier to work with later on
# Also I made the correct answers non-case sensitive to make it easier to test.
r = len(questions)
score = 0
s = random.randrange(0, r, 1)
#This generates a random number within the range of how many questions there are
# and then prints out that question
#This generates a label that displays the randomly generated question
question = tkinter.Label(window, text=questions[s])
list = answers[s]
output = []
for i in range(1, 5):
output.append(answers[s][i])
shuffle(output)
# this takes the answers that correspond with the randomly generated question and shuffles the answers
# I did this as otherwise, the answer would always be the first answer to appear and the player could exploit this
#This code is what displays the labels and buttons on the window. It lets the computer decide where the best place for
#each component is
welcome.pack()
begin.pack()
question.pack()
for i in output:
answer = tkinter.Button(window, text=i, command=lambda answer = i: check_answer(i))
answer.pack()
#I had decided to pack the answers like this as it was easier than typing out each element of the list and also
#more efficent
window.mainloop()
#this is the code that actually generates the window
Starting at the top, let's change your check_answer definition to not create a new label every time:
def check_answer(answer):
if answer == answers[s][0] or answer == answers[s][1]:
global score
score += 1
lbl["text"] = score
Next, we need one small change in your for loop: we want to send answer, not i:
for i in output:
answer = tkinter.Button(window, text=i, command=lambda answer = i: check_answer(answer))
answer.pack()
lbl = tkinter.Label(window, text=score)
lbl.pack()
Lastly, we'll add that label that we removed earlier down to the bottom where you had it initially. You can change the location of this by packing it sooner in the code for aesthetics. Your code still doesn't cycle to a new question once one is answered (correctly or otherwise), but at least now you can see when the user answers correctly.
Hope this helps.
First of all, you did a small error in the lambda callback:
command=lambda answer=i: check_answer(answer)
It should be.
Then for the many labels, create one label and just change the text:
def check_answer(answer):
print(answer)
if answer == answers[s][0] or answer == answers[s][1]:
global score
score += 1
lbl.configure(text=str(score))
(much code i did not copy)
for i in output:
answer = tkinter.Button(window, text=i, command=lambda answer=i: check_answer(answer))
answer.pack()
lbl = tkinter.Label(window, text=score)
lbl.pack()
Related
I'm trying to learn Tkinter, and learning how to set a variable that can be worked on later on in the code from the choice selected from an optionmenu. Only been coding tkinter or python for about 2 days properly now and unsure why this doesn't work.
my code:
root = Tk()
root.title("test")
p1aspVARIABLE = str()
def tellmethevariable():
if p1aspVARIABLE == "AA":
print(p1aspVARIABLE)
else:
print("Not 'AA'")
def tellmeP1Asp(choice):
choice = p1aspCHOICE.get()
print(choice)
p1aspVARIABLE = choice
print(p1aspVARIABLE)
alleles = ["AA", "Ab", "bb"]
p1aspCHOICE = StringVar()
p1aspCHOICE.set(alleles[0])
alleledropdown = OptionMenu(root, p1aspCHOICE, *alleles, command=tellmeP1Asp)
button1 = Button(root, command=tellmethevariable)
alleledropdown.grid(row=0, column=0)
button1.grid(row=0, column=1)
root.mainloop()
I don't understand why the code is able to print out the p1aspCHOICE when ran from the "tellmeP1Asp", but not when done through "tellmethevariable"?
I'm not sure how to get the p1aspVARIABLE to properly change to what was chosen in the OptionMenu list?
I'm not knowledgeable enough of what I'm doing wrong to properly google this, have been trying for a few hours now but to no avail.
What I tried:
I've set this up in countless ways over the last few hours, mostly last night before giving up. This is actually one of the only versions of this code that ran.
What I was expecting:
When the button1 Button is clicked, for it to either print the choice (if it was "AA", so it would print "AA"), or, if it wasn't "AA", it would print "Not 'AA'"
i am trying to make it so that a user inputs a number, and that number of buttons are created Using TKinter, I have tried doing it by using the following, Where the Buttons are successfully created, however i am struggling with calling them in order to place them / display them on the grid (Added randint to simulate user input (User Input not limited to 9 and may be as high as 40))
from tkinter import *
from random import randint
inputValue = randint(3,9)
print(inputValue)
root = Tk()
while inputValue > 0: # for every number in inputted value
inputValue = int(inputValue) - 1 # take one
globals()['Sailor%s' % inputValue] = Button(root, text="Lap :" + str(inputValue), command=lambda: retrieve_input()) # Create the button function in the format 'Sailors{Inputnumber}'
('Sailors%s' % inputValue).grid(row=inputValue, column=1, columnspan=2) # Place the button (Doesn't work)
root.mainloop() # Does work (required)
Howerver the following does not work (It is meant to place the button),
('Sailors%s' % inputValue).grid(row=inputValue, column=1, columnspan=2) # Place the button (Doesn't work)
Can you think of a method i can use in order to create and place Amount of buttons?
Thanks in advance
You should never create dynamic variable names like you are attempting to do. It adds a lot of complexity, reduces clarity, and provides no real benefit.
Instead, use a dictionary or list to keep track of the buttons. In your case, however, since you're never using the buttons anywhere but in the loop you can just use a local variable.
Example using a local variable, in case you never need to access the button in code after you create it:
for count in range(inputValue):
button = Button(...)
button.grid(...)
Here's how you do it if you need to access the buttons later in your code:
buttons = []
for count in range(inputValue):
button = Button(...)
button.grid(...)
buttons.append(button)
With the above you can iterate over all of the buttons in buttons:
for button in buttons:
button.configure(state='disabled')
If you need to configure a single button, use its index:
button[0].configure(...)
You can calling grid on a string at the moment, which throws your error.
Your need to replace ('Sailors%s' % inputValue) with globals()['Sailor%s' % inputValue], with orders your buttons on individual rows, labeled 0-8.
So, your current code is:
from tkinter import *
from random import randint
inputValue = randint(3,9)
print(inputValue)
root = Tk()
while inputValue > 0: # for every number in inputted value
inputValue = int(inputValue) - 1 # take one
globals()['Sailor%s' % inputValue] = Button(root, text="Lap :" + str(inputValue), command=lambda: retrieve_input()) # Create the button function in the format 'Sailors{Inputnumber}'
globals()['Sailor%s' % inputValue].grid(row=inputValue, column=1, columnspan=2)
root.mainloop() # Does work (required)
When retrieve_input is defined, the code will work fine.
Just to point out, instead of inputValue = int(inputValue) - 1 you can use inputValue -= 1.
I am new in Python programming and I am learning how to create user interfaces. I would like to create a very basic interface which has the following flow: using a while loop, the interface shows all the questions included into a list of questions. Each time a question is presented, two button (Yes-No) appears under the question. Only when one of them is clicked, the interface will show the next questions.
I attach here the code I tried.
import tkinter as tk
questions=['Question 1','Question 2','Question 3','Question 4', 'Question 5']
root = tk.Tk()
root.minsize(300, 300)
answers=['Yes','No']
b_list = []
def ask():
count=0
while count<len(questions):
lab=tk.Label(root,text=questions[count])
lab.pack()
count+=1
for i in range(2):
b = tk.Button(root, text = answers[i],command=ask)
b.grid(row = 0, column = i)
b_list.append(b)
root.mainloop()
Such code doesn't work at all. I guess also that I made a mistake into the while loop, asking to display all the questions, instead of one at once. Any idea to make such code working?
Thanks for your time!!
There are two main reasons enabling your code not to run:
In one script you can't write GUI with two or more geometry managers. This is not only for tkinter but also for other GUI third party library like PyQt.
The label should display different messages or questions in each round. So it means it's necessary for you to modify the content with StringVar. It behaved as a variable of string. You can learn more here
I don't have a clear idea of why you wanna store button and the place where you save the result made by users.
Here is my code:
import tkinter as tk
questions = ['Question 1', 'Question 2',
'Question 3', 'Question 4', 'Question 5']
answers = ['Yes', 'No']
root = tk.Tk()
root.minsize(300, 300)
def ask():
if questions:
lab_text.set(questions.pop(0))
for index, answer in enumerate(answers):
lab_text = tk.StringVar()
lab = tk.Label(root, textvariable=lab_text)
lab.grid(row=0, column=0)
b = tk.Button(root, text=answer, command=ask)
b.grid(row=1, column=index)
#initialize label
ask()
root.mainloop()
This can also be done in an object orientated manner which may hold up better for future proofing.
Please see my commented version of the script below for an explanation and example:
from tkinter import *
import random
class App:
def __init__(self, root):
self.root = root
self.array = ["Question1", "Question2", "Question3", "Question4", "Question5"] #list containing questions
self.answer = [] #empty list for storing answers
self.question = Label(self.root, text=self.array[len(self.answer)]) #creates a text label using the element in the 0th position of the question list
self.yes = Button(self.root, text="Yes", command=self.yescmd) #creates button which calls yescmd
self.no = Button(self.root, text="No", command=self.nocmd) #creates button which calles nocmd
self.question.pack()
self.yes.pack()
self.no.pack()
def yescmd(self):
self.answer.append("yes") #adds yes to the answer list
self.command() #calls command
def nocmd(self):
self.answer.append("no") #adds no to the answer list
self.command() #calls command
def command(self):
if len(self.answer) == len(self.array): #checks if number of answers equals number of questions
self.root.destroy() #destroys window
print(self.answer) #prints answers
else:
self.question.configure(text=self.array[len(self.answer)]) #updates the text value of the question label to be the next question
root = Tk()
App(root)
root.mainloop()
This is essentially only different in the sense that we're just configuring the label to show the next element in the questions list rather than destroying it or popping the list.
I am working on a group project where we must create a simple program and we have chosen to make a multiple choice game using Tkinter. We have constructed most of the game already, but are having a problem when keeping a count of the correct answers. We are using Radiobuttons to list the answers for each question, however if the user clicks the button more than once it keeps incrementing the count as many times as they click it. Here is the code that we have. Please excuse the messiness, as we have not quite gone through to clean it up, as well as it is a bit of a beginner project and we are not the most experienced group of programmers.
(I am purposefully not including the complete code because the file paths for the images we have are directly linked to the home computer so they would not be able to be used anyways)
root = Tk()
counter = 0
d = ''
var = StringVar()
def next():
global i,img,groups,listanswer, questions, randint, key,d, counter
s = randint(1,4)
key = random.choice(list(questions.keys()))
img = ImageTk.PhotoImage(Image.open(key))
panel = Label(root, image = img)
panel.grid(column=0, row=0)
b = []
c = listanswer.index(str(questions.get(key)))
d = listanswer[c]
b.append(d)
listanswer.remove(d)
def selection():
global counter, d, sel
sel = str(var.get())
if sel == d:
counter +=1
i=1
while i<5:
a=random.choice(listanswer)
b.append(a)
if s!=i:
Radiobutton(root, text=a, padx=20,variable=var,
value=a,command=selection).grid(column=0, row=i)
listanswer.remove(a)
i+=1
R1 = Radiobutton(root, text=d, padx=20,variable=var, value=d,command =
selection).grid(column=0, row=s)
listanswer=listanswer+b
questions.pop(key)
counterlabel.configure(text='%g' %counter)
counterlabel=Label(root,width=8)
counterlabel.grid(column=1, row=5)
counterval=Label(root, width=10, text='Correct:')
counterval.grid(column=0,row=5)
next=Button(root,text='next',command=next)
next.grid(column=2, row=2)
var = IntVar()
label = Label(root)
label.grid()
root.mainloop()
If I understood the correctly every time you click on the radiobutton the code will check if that answer is correct. If it is, you increase the counter.
Instead, I recommend checking all answers when any of the radiobuttons is clicked, and set the counter accordingly (i.e. the counter resets every time you click).
Hopefully this helps!
I am creating a multiple choice quiz in Tkinter and am using radiobuttons and checkbuttons. For this question I am using radiobuttons. How do I get the value from the radio buttons in order to compare them in the if statement 'Calculate Score'? The computer returns with: 'calculate_score_1() takes exactly 1 argument (0 given)'
Also, how do I pass a variable between classes? I have ten classes for ten questions but want all of them to be able to access the variable 'Score' when adding 1 to the score when the user gets the answer correct.
class Question_1_Window(tk.Toplevel):
'''A simple instruction window'''
def __init__(self, parent):
tk.Toplevel.__init__(self, parent)
self.text = tk.Label(self, width=75, height=4, text = "1) Do you have the time to do at least twenty minutes of prefect duty each week?")
self.text.pack(side="top", fill="both", expand=True)
question_1_Var = IntVar() #creating a variable to be assigned to the radiobutton
Yes_1 = Radiobutton(self, text = "Yes", variable = question_1_Var, value=1, height=5, width = 20)
Yes_1.pack() #creating 'yes' option
#Here we are assigning values to each option which will be used in the validation
No_1 = Radiobutton(self, text = "No", variable = question_1_Var, value=2, height=5, width = 20)
No_1.pack() #creating 'no' option
def calculate_score_1(self):
Enter_1.config(state="disabled")
#self.question_1.config(state="disabled")
if (question_1_Var.get() == 1) and not (question_1_Var.get() == 2):
print("calculate score has worked") #test lines
#score = score + 1
else:
print("not worked") #testlines
Enter_1 = Button(self, text= "Enter", width=10, command = calculate_score_1)
Enter_1.pack()
calculate_score_1 is not a method of the instance, but is defined inside the __init__ method. Thus, that method should not have the self parameter. Remove that parameter, then it should work. If you need it (you seem not to) you can still use the self parameter of the outer __init__ method.
If you want to access the score from another class (or in fact from another method of the same class) you have to make it a member of the instance, by defining it as self.score = .... You can then access it like this: your_question_1_window_instance.score.
Finally, if you have "ten classes for ten questions" you should try to find some common ground for all those questions and create either a common super class or even one class that can be parametrized to fit all the questions. You just need the title, the type (select one/select many) and a list of answers, and which ones are correct. Everything else -- creating the check boxes, checking the answer, etc. -- should always be the same.