Label not updating - python

I'm trying to create a GUI using tkinter. This is my code:
from tkinter import *
from random import randint
B3Questions = ["How is a cactus adapted to a desert environment?", "What factors could cause a species to become extinct?"]
B3Answers = ["It has leaves reduced to spines to cut water loss, a thick outer layer to cut down water loss and a deep-wide spreading root system to obtain as much water as possible", "Increased competition, new predators and new diseases"]
B3Possibles = [x for x in range (len(B3Questions))]
def loadGUI():
root = Tk() #Blank Window
questNum = generateAndCheck()
questionToPrint = StringVar()
answer = StringVar()
def showQuestion():
questionToPrint.set(B3Questions[questNum])
def showAnswer():
answer.set(B3Answers[questNum])
def reloadGUI():
global questNum
questNum = generateAndCheck()
return questNum
question = Label(root, textvariable = questionToPrint)
question.pack()
answerLabel = Label(root, textvariable = answer, wraplength = 400)
answerLabel.pack()
bottomFrame = Frame(root)
bottomFrame.pack()
revealAnswer = Button(bottomFrame, text="Reveal Answer", command=showAnswer)
revealAnswer.pack(side=LEFT)
nextQuestion = Button(bottomFrame, text="Next Question", command=reloadGUI)
nextQuestion.pack(side=LEFT)
showQuestion()
root.mainloop()
def generateAndCheck():
questNum = randint(0, 1)
print(questNum)
if questNum not in B3Possibles:
generateAndCheck()
else:
B3Possibles.remove(questNum)
return questNum
Basically, when pressing "Next Question", the question label does not update. Pressing "Next Question" again will throw the code into a nice loop of errors.
I honestly cannot see where I'm going wrong, but that's likely due to my lack of experience

RobertR answered your first question. The reason why you receive an error when pressing the Next Question button again is because your list, B3Possibilities has two numbers, 0 and 1. So when you run the function twice, you'll remove both one and zero from this list. Then you have an empty list. When you call reloadGUI a third time you'll never hit your else statement because the randint generated will NEVER be in B3Possibilites. Your if clause is called and you dive into an un-ending recursive call.
A solution to this might be to have a check in your generageAndCheck function:
if(len(B3Possibiles) == 0):
#run some code. Maybe restart the program?

First off, the short answer is that you're not actually updating the content of the StringVar questionToPrint. I would fix this by changing the reloadGUI() function to this:
def reloadGUI():
global questNum
questNum = generateAndCheck()
showQuestion()
answer.set("") # Clear the answer for the new question
Also, as Dzhao pointed out, the reason you're getting an error after you run out of questions is because you need to put some sort of protection within your generateAndCheck() function to prevent against infinite recursion.
Additionally, I would recommend you change the way you determine what question to ask because the way you have it right now is needlessly complicated. Look in to the random module a little more, particularly the random.choice() function. You'll notice it raises an IndexError when the list is empty, so you can catch that error and that will help solve the problem that Dzhao had pointed out.

Related

execute multiple variable functions(var_1,var_2,var_3)

I got another little question...
I want to make multiple variables which I create with 'setattr'
That works quite fine. It creates these variables:
self.sectionButton_1 = Button(text=x)
self.sectionButton_2 = Button(text=x)
self.sectionButton_3 = Button(text=x)
Now I want them to get displayed on the window with tkinter so that this should happen:
self.sectionButton_1.grid(row=i, column=0)
self.sectionButton_2.grid(row=i, column=0)
and so on..
But how do I have to edit the loop that the sectionButtons gonna be created with .grid from tkinter in a loop without writing the above ten times.
# Display Section selection
def checkSection(self):
# Read all sections from config
self.sections = config.sections()
self.sectionsCount = str(len(self.sections))
self.i = 0
self.text = Label(text="Choose Section:" + self.sectionsCount)
self.text.grid(row=1, column=0)
for x in self.sections:
i = +1
setattr(self, 'sectionButton_' + str(i), Button(text=x))
I'm not that good at explaining but hopefully its enough to understand my problem ^^
If not, just comment, I will try to answer it
If you have a group of related variables of the same type and you're doing the same operations to each one then that's a natural place to switch to using a list instead of individual variables.
Your code would become more like:
self.sectionButtons = []
for i, x in enumerate(self.sections):
button = Button(text=x)
button.grid(row=i+1, column=0)
self.sectionButtons.append(button)
This also has the advantage of no longer needing to construct the variable names as strings and use setattr, which is often a sign there's a better way.

To keep widgets in their place when a new widget is introduced in tkinter

So, I am using Tkinter to make a simple Rock-Paper-Scissors game, and when the score is updated to 1 or when the first point is scored the other widgets are moving by some distance. I tried adding and removing padx values but this doesn't change anything. I am still learning so if I can improve the code, please do suggest me.
Thanks.
computer_score=0
score=0
label_score=None
def Rock():
global computer_score,score
option_list=["Rock","Paper","Scissors"]
rand=rint(0,len(option_list)-1)
label_check=Label(root,text=("Computer chose "+option_list[rand]),font=("Helvetica",11,"bold"),bg="white")
label_check.grid(row=4,column=1,pady=5,padx=20,columnspan=2)
if option_list[rand] == button1.cget("text"):
label_res=Label(root,text="It's a Draw, Try again",font=("Helvetica",11,"bold"),bg="white")
label_res.grid(row=5,column=1,pady=5,padx=20,columnspan=2)
elif option_list[rand] == option_list[1] :
computer_score=computer_score+1
label_score=Label(root,text=computer_score,font=("Helvetica",11,"bold"),bg="white")
label_score.grid(row=0,column=0,padx=4)
label_res=Label(root,text="Computer Won, Try again",font=("Helvetica",11,"bold"),bg="white")
label_res.grid(row=5,column=1,pady=5,padx=20,columnspan=2)
else:
score=score+1
label_score=Label(root,text=score,font=("Helvetica",13,"bold"),bg="white")
label_score.grid(row=0,column=3)
label_res=Label(root,text="You won!!, Play again?",font=("Helvetica",11,"bold"),bg="white")
label_res.grid(row=5,column=1,pady=5,padx=20,columnspan=2)
A similar code is for paper and scissors too and when the score is 1, the other widgets are moving away. Their code is also kept below.
button1=Button(root,text="Rock",command=Rock,height =2, width = 8,font=(8))
button1.grid(row=1,column=1,pady=5,padx=20,columnspan=2)
button2=Button(root,text="Paper",command=Paper,height =2, width = 8,font=(8))
button2.grid(row=2,column=1,pady=5,padx=20,columnspan=2)
button3=Button(root,text="Scissors",command=Scissors,height =2, width = 8,font=(8))
button3.grid(row=3,column=1,pady=5,padx=20,columnspan=2)
label1=Label(root,text="Rock Paper Scissors",font=("Comic Sans MS",22,"italic"),bg="white")
label1.grid(row=0,column=1,sticky=W+E,padx=100,pady=(0,5),columnspan=2)
Hope you all understood it, sorry for any mistakes, this is my first time asking and I am still learning how to write clean code.
Thank you.
Edit: Actually, I tried to use a method where the result is decided by just comparing the text on button pressed, so to avoid the hassle of big code but I couldn't get any help, so if anyone can think of such, help with it too.
Basic Idea:
if button_text== randint_ list item:
it is draw
as such, I wanted to do instead of above mentioned whole code for Rock. But I donno how to get the text of the button.
Here's my approach to this, basically have all the labels pre-paced in the window an then use the .config() method on them to change the texts (you can continue with your approach as well). I have predefined the widths of the label, this will prevent them from resizing dynamically as the text in them changes. Here's the modified code:
from tkinter import *
import random
computer_score=0
score=0
label_score=None
def Rock():
global computer_score,score
option_list=["Rock","Paper","Scissors"]
rand=random.randint(0,len(option_list)-1)
label_check.config(text="Computer chose "+option_list[rand])
if option_list[rand] == button1.cget("text"):
label_res.config(text="It's a Draw, Try again")
elif option_list[rand] == option_list[1] :
computer_score=computer_score+1
computer_score_label.config(text=computer_score)
label_res.config(text="Computer Won, Try again")
else:
score=score+1
player_score_label.config(text=score)
label_res.config(text="You won!!, Play again?")
root=Tk()
button1=Button(root,text="Rock",command=Rock,height =2, width = 8,font=(8))
button1.grid(row=1,column=1,pady=5,padx=20,columnspan=2)
#commented since code was unavailble for this part
'''score_frame=Frame(root)
score_frame.grid(row=0,columnspan=5)
score_frame.pack_propagate(False)
button2=Button(root,text="Paper",command=Paper,height =2, width = 8,font=(8))
button2.grid(row=2,column=1,pady=5,padx=20,columnspan=2)
button3=Button(root,text="Scissors",command=Scissors,height =2, width = 8,font=(8))
button3.grid(row=3,column=1,pady=5,padx=20,columnspan=2)'''
computer_score_label=Label(root,font=("Helvetica",11,"bold"),bg="white",width=3)
computer_score_label.grid(row=0,column=0)
player_score_label=Label(root,font=("Helvetica",11,"bold"),bg="white",width=3)
player_score_label.grid(row=0,column=3)
label_res=Label(root,font=("Helvetica",11,"bold"),bg="white")
label_res.grid(row=5,column=1,pady=5,padx=20,columnspan=2)
label_check=Label(root,font=("Helvetica",11,"bold"),bg="white")
label_check.grid(row=4,column=1,pady=5,padx=20,columnspan=2)
label1=Label(root,text="Rock Paper Scissors",font=("Comic Sans MS",22,"italic"),bg="white")
label1.grid(row=0,column=1,sticky=W+E,padx=100,pady=(0,5),columnspan=2)
root.mainloop()

In Python3.8, Cannot get a variable to be checked in an if statement

I am learning python. I am not new to programming, so this is frustrating and a little embarrassing too: I I want to check if the random number is 20 - and if it is, change the background color. If I change comparison to != it works. But I cannot seem to check for a value. I tried making the result a string/int as random generates a float. Any ideas..?
The code follows:
import random
import tkinter as tk
temp_result = 0
pickedColor = ""
def d20roll():
temp_result = random.randint(1, 20) # generates a float
lbl_result["text"] = str(temp_result) # makes the float a string for display
print(temp_result) #shows me the result
if temp_result == 20:# if the rnd is 20, make the color red.
pickedColor = "red"
else:
pickedColor = "black"
window = tk.Tk()
window.columnconfigure(0, minsize=150)
window.rowconfigure([0, 1], minsize=50)
frame = tk.Frame(
master=window,
relief=tk.RAISED,
borderwidth=2)
frame.grid(row=0, column=0, padx=2,pady=2)
btn_d20roll = tk.Button(master=frame, text="D20 Roll", command=d20roll)
lbl_result = tk.Label(fg="white", bg=pickedColor)
btn_d20roll.grid(row=0, column=0, sticky="nsew")
lbl_result.grid(row=1, column=0)
window.mainloop() '''
You're setting the value of temp_result inside a function, and testing that value outside of that function. Without diving too far in your code, I see 2 problems with that:
At the moment you test the value of temp_result, the function has not been called yet. The value of temp_result is therefore still the initial value.
When you assign to a variable inside of a function, Python considers that variable local to the function and no change will be visible outside of the function. You could change that by using global temp_result in the function, but it's generally not recommended to communicate values from inside a function to outside of it. It's better to return a value instead.
You're going to have to arrange your code so that a button press will result in:
a random number being generated
that random number being compared to some value
and the background color being set to some value based on that
I'm not familiar with Tkinter so I can't really help with that.

Putting questions into a list and randomly selecting them

I've had this problem for a while where my quiz won't work, where certain tkinter windows wouldn't close even though the code was there. However, after speaking to my teacher, he said that it was possible to read my text file to get the questions and answers, then place them in a list so that I could randomly select them to be displayed in the window (That way no question appears twice). He then said that you could have the window refresh after each question is answered, putting a different question up with different answers (Maybe even a "Correct"/"Wrong" inbetween each question). If anyone has an idea how to do ths is would be a massive help. Thanks.
def quiz_easy():
global question
questions = []
correctanswers = []
possibleanswer1s = []
possibleanswer2s = []
possibleanswer3s = []
easy_questions = open("sup.txt", "r")
count = 1
for line in easy_questions:
if count == 1:
questions.append(line.rstrip())
elif count == 2:
correctanswers.append(line.rstrip())
elif count == 3:
possibleanswer1s.append(line.rstrip())
elif count == 4:
possibleanswer2s.append(line.rstrip())
elif count == 5:
possibleanswer3s.append(line.rstrip())
if count == 5:
count = 1
else:
count = count + 1
print(questions)
print (correctanswers)
randno1 = random.randint(0,len(questions)-1)
master = Tk()
text = Text(master)
text.insert(INSERT, questions[randno1])
text.insert(END, "?")
text.pack()
text.tag_add("here","5.0", "1.4")
text.tag_add("start", "1.8", "1.13")
Think OOP intead of sequencial
Your Quiz should be a class.
Avoiding MVC approach, stay simple the class directly handle the UI, the data and the process.
The constructor read the question file and update your internal data representation (That may be reworked too), and start the Root Tk instance
Create a method to display the question (a Label) and the area dedicated to the answer (an Entry)
Add a button "OK" or bind the key on the Entry widget to start a method dedicated to the control of the answer and displaying of the correct answer
Create this method
Add a method for the Tkinter mainloop
In the main of the script instanciate the quizz, call the method "display a question" and then call the method for mainloop()
We are waiting for your code ;-) to assist
tips: 1: Prefer:
import Tkinter as tk # instead of from Tkinter import * to keep your code explicit.
2: Forget the global directive in your code .. non sense here
3: the file reading is rought, the format of your file is not optimised.
to not going too quickly in XML paradygm ..
think about prefix ruler
QUE translate pig in french
COR cochon
ALT proc
ALT truie
ALT verrat
ALT porcelet
ALT cochonet
ALT gorret
ALT nourrin
ALT nourrain
ALT cochette
for each line you just append a possible answer in a list of possible answers
Ready to run your brain ... go. ^^

My Variable will not change out side the definition

I wrote my first py code. This code to create a lotto number generator.The problem is that my variable nums will not change. Please help me.
I understand this is not as good as it can be so please tell me how to improve, any comments will be appreciated.I wanted the random numbers to come up in the ladle so i could press the submit button and 5 new numbers to come up.The problem is my veritable "nums" wont change.Thank you for helping.
import random
from tkinter import *
#TK()
window = Tk()
window.title("Lottery Nummber Generator")
#Def Click
def click():
global nums
global numsgen
numsgen = random.sample(range(1, 49), 5)
nums = " ".join(str(x) for x in numsgen)
print(nums)
numsgen = random.sample(range(1, 49), 5)
nums = " ".join(str(x) for x in numsgen)
#Fake just to make it look nice
Label(window,text="").grid(row=1, column=0,sticky=W)
Label(window,text="").grid(row=2, column=1,sticky=W)
Label(window,text="").grid(row=2, column=3,sticky=W)
#Submit button
Button(window, text="Submit", width=5,command=click).grid(row=3, column=1, sticky=W)
#Label
group = LabelFrame(window, text="Lottery Numbers:", padx=5, pady=5,fg="dark orange")
group.pack(padx=10, pady=10,)
group.grid(row=2, column=1,sticky=W)
w = Label(group, text=nums)
w.pack()
mainloop()
To get a label to update, you must do one of two things:
Associate a StringVar with the label. When you update the variable (via its set method), any labels associated with it will automatically change. This is a great solution if more than one widget needs to display the same value.
Directly configure the label. This requires that you save a reference to the widget(s) so that you can later reconfigure them. For example, you could put w.configure(text=nums) in your click function. This solution is a bit simpler than using a StringVar, simply because it requires one less object.

Categories

Resources