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

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))

Related

The global isn't working and didn't recognizing variable "text"?

The global isn't working properly and it says that
it does not know what text is, even though it is declared global.
def nguess():
answer = random.randint ( 1, 50 )
def check():
global attempts
attempts = 10
global text
attempts -= 1
guess = int(e.get())
if answer == guess:
text.set("yay you gat it right")
btnc.pack_forget()
elif attempts == 0:
text.set("you are out of attempts")
btnc.pack_forget ()
elif guess > answer:
text.set("incorrect! you have "+ str(attempts) + "attempts remaining. Go higher")
elif guess < answer:
text.set("incorrect! you have "+ str(attempts) + "attempts remaining. Go lower")
return
nw = tk.Toplevel(app)
nw.title("guess the number")
nw.geometry("500x150")
lable = Label(nw, text="guess the number between 1 - 50")
lable.pack()
e = Entry(nw, width = 40, borderwidth = 10)
e.pack()
btnc = Button(nw,text = "Check", command = check)
btnc.pack()
btnq = Button ( nw, text="Quit", command=nw.destroy )
btnq.pack()
text = StringVar()
text.set("you have ten attempts remaining ")
guess_attempts = Label (nw,textvariable = text)
guess_attempts.pack()
Well, what's going on is you're trying to get a variable before initialize it i.e. in check function you're calling a global text variable so what it means is you're bringing whatever text variable stores in global namespace, but the problem is text variable isn't exist in global namespace yet because you've created after calling the check function. Below I show an example:
def test():
global variable
print(variable)
test()
variable = 'Hello'
This will raise an error because of what I just explained, so what you have to do is something like this(based on the example):
def test():
global variable
print(variable)
variable = 'Hello'
test()
In short, initialize the text variable before calling the check function which uses global text

Compare tkinter Entry to actual addition answer

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()

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.

Python program won't execute

I'm in the middle of making a small game involving hacking into people's computers, and stealing files and money in order to complete missions. Here is the code as of now:
#SICCr4k2: Broke
#
#
#
#Remember whenever you are printing a random ip address to add the "." in between each part of the ip (each random number)
## LAST LEFT ON HERE: MAKE BUTTONS FOR NODES
## MAKE FILES FOR NULL'S NODE
## SET THE CORRECT PLACEMENTS FOR ALL THE BUTTONS
## nullMain referenced before assignment
## make it so that you send a message through the prompt to get their ip, then it automatically puts the ip in the nodes
## window. Like you send the person a message, and then it gets the ip and puts it in the nodes window
## take away the buttons in the nodes window, just at labels where it points to the host's ip address.
import random
import time
import sys
import os
import tkinter as tk
from tkinter import *
#def nodes():
# nodeWindow = tk.Tk()
# frame = tk.Frame(nodeWindow, width=700, height=400)
# frame.grid_propagate(0)
# frame.grid()
# nodeWindow.title("||| Nodes |||")
# nullIp = tk.Label(nodeWindow, text="Ip: 221.153.52.216")
# nullIp.grid(row=0, column=0)
# nullMain = tk.Button(nodeWindow, text="Null", function=nullMainCallback())
# nullMain.config(height=1, width=100)
# nullMain.grid(row=0, column=0)
# def nullMainCallback():
# nullMain.destroy()
# nullIp = tk.Label(nodeWindow, text="Ip: 221.153.52.216")
# nullIp.grid(row=0, column=0)
#def commands():
def numbers():
number1 = random.randint(1, 99)
number2 = random.randint(1, 99)
print(number1)
if number1 != number2:
numbers()
if number1 == number2:
os.system('cls')
def ips():
nullIp = ('18.279.332')
def getIp():
x = random.randint(1, 222)
if x == 127:
x += 1
return '{}.{}.{}.{}'.format(
x,
random.randint(0, 255),
random.randint(0, 255),
random.randint(0, 255))
def commandInput():
CommandInput = input(">>> ")
if CommandInput == ("myNodes()"):
nodes()
else:
commandInput()
commandInput()
def usernameCreation():
username = input(">>> ")
print("'" + username + "' is that correct?")
usernameInput = input(">>> ")
if usernameInput == ("yes"):
print("Okay...")
if usernameInput ==("no"):
usernameCreation()
def game():
def tutorial():
print('Hello.')
time.sleep(3)
print('Welcome back.')
time.sleep(3)
print('How was it?')
time.sleep(3)
print('Being hacked for the first time?')
time.sleep(3)
print("You're probably wondering who I am.")
time.sleep(5)
print("Well, my name is Null.")
time.sleep(3)
print("Only because I am well known for nothing.")
time.sleep(3)
print("Other than not being alive.")
time.sleep(3)
os.system('cls')
print("First thing's first, what shall I call you?")
usernameCreation()
print("Let's give you a bit of movement.")
time.sleep(3)
print("""The first thing you will want to do would be to connect to my computer, but
to do that, you have to find my ip address. Here. I just uploaded new software to your computer.""")
time.sleep(3)
print("""You will now be able to access my ip, nad many other's with a simple command. The command is
getIp(). Input that command below, but inside the parenthesis, you type in the screen name. For instance: getIp(Null).
type that command in to get my ip.""")
input(">>> ")
if ("getIp(Null)"):
numbers()
print("""My ip was just added to your nodes, which you can access by typing myNodes().""")
game()
I just want to note that when I run the program, it doesn't list any errors or anything, it just doesn't execute at all... Any ideas????
You define the function tutorial inside game (which you shouldn't really do – there's no point in defining it that way) but never call tutorial.
Inside of game you want to call tutorial:
def game():
def tutorial():
# code for tutorial
tutorial()
A better way to structure your code, however, is to use a main method (which is the standard way to start the execution of a program` and keep every other function separate. There's no need to nest functions as you've done.
So, for example:
def main():
tutorial()
# all other function definitions
def tutorial():
# code for tutorial
if __name__ == "__main__":
main()
You never call tutorial() although you shouldn't nest functions like this.

Separating Tkinter UI concerns from Logic in Python app

This is my first app ever. It is working well but I would like to separate the UI concerns like getting input and creating labels, from the translation logic. I would then like to remove the output from the previous translation, i.e., only showing one translation on the screen at a time.
How can I separate the translation logic from my Tkinter GUI?
from Tkinter import *
import tkMessageBox
def start():
inputg = input.get()
if len(inputg) >= 2 and inputg.isalpha():
new_word_out = Label(text=(inputg[1:] + (inputg[0] + "ay")).lower().title()).pack()
out_message = Label(text="Cool! Try another!").pack()
# restart()
elif len(inputg) <= 1 and inputg.isalpha():
show_error(message="Whoops! I need 2 or more characters to translate! Try again!")
return
elif len(inputg) >= 1 and not inputg.isalpha():
show_error(message="Whoops! No numbers or symbols please! Try again!")
return
elif len(inputg) == 0:
show_error(message="It seems you haven't given me anything to translate!")
return
def show_error(message):
tkMessageBox.showerror(title="Error", message=message)
return
def quit():
ask_exit = tkMessageBox.askyesno(title="Quit", message="Are you sure you want to quit?")
if ask_exit > 0:
root.destroy()
return
root = Tk()
input = StringVar() # stores user input into this variable as a string.
root.title("The Pig Translator")
root.protocol("WM_DELETE_WINDOW", quit)
labeltitle1 = Label(text="Hello there! This is my Pig Latin Translator!").pack()
labeltitle2 = Label(text="Please enter a word to continue!", fg='darkgreen', bg='grey').pack()
original_entry = Entry(textvariable=input, bd=5, fg='darkgreen').pack()
translate_button = Button(text="Translate", command=start).pack()
root.bind('<Return>', lambda event: start()) # essentially binds 'Return' keyboard event to translate_button
root.mainloop()
There are many ways you can separate logic from GUI. generally I would recommend using classes and callback functions. Thus, I made a class that generates the gui. However, the translation is performed by external function called do_translation.
MyFrame does not know much about how do_translation. It only knows it returns translated_str, message and takes string as argument. do_translation does not relay on any gui as well. The do_translation takes only an input string, does what it wants, and returns translated string and message. The MyFrame take this function as a callback. You can make any other translation function, and as long as the input and output are same, it will work.
I rely here on a "Cool" in a massage which indicates that translation was ok. Its poor idea to make it relay on 'Cool' word, but did not want to change your code too much. Probably better to raise some error, or use message codes, etc.
from Tkinter import *
import tkMessageBox
class MyFrame(Frame):
def __init__(self, master, input_callback=None, **kwargs):
Frame.__init__(self, master)
self.set_input_callback(input_callback)
self.create_widgets()
self.pack()
def create_widgets(self):
self.input = StringVar() # stores user input into this variable as a string.
self.labeltitle1 = Label(text="Hello there! This is my Pig Latin Translator!")
self.labeltitle1.pack()
self.labeltitle2 = Label(text="Please enter a word to continue!", fg='darkgreen', bg='grey')
self.labeltitle2.pack()
self.original_entry = Entry(textvariable=self.input, bd=5, fg='darkgreen')
self.original_entry.pack()
self.translate_button = Button(text="Translate", command=self.start)
self.translate_button.pack()
self.new_word_out = Label(text='')
self.out_message = Label(text='')
def set_input_callback(self, some_fun):
self.input_callback = some_fun
def show_error(self, message):
tkMessageBox.showerror(title="Error", message=message)
return
def start(self):
inputg = self.input.get()
if self.input_callback:
translated_str, message = self.input_callback(inputg)
if 'Cool' in message:
self.new_word_out['text'] = translated_str
self.new_word_out.pack()
self.out_message['text'] = message
self.out_message.pack()
else:
self.show_error(message)
def do_translation(inputg):
translated_str = message = ''
if len(inputg) >= 2 and inputg.isalpha():
translated_str = (inputg[1:] + (inputg[0] + "ay")).lower()
message = "Cool! Try another!"
elif len(inputg) <= 1 and inputg.isalpha():
message = "Whoops! I need 2 or more characters to translate! Try again!"
elif len(inputg) >= 1 and not inputg.isalpha():
message = "Whoops! No numbers or symbols please! Try again!"
elif len(inputg) == 0:
message = "It seems you haven't given me anything to translate!"
return translated_str, message
def quit():
ask_exit = tkMessageBox.askyesno(title="Quit", message="Are you sure you want to quit?")
if ask_exit > 0:
root.destroy()
return
root = Tk()
root.title("The Pig Translator")
root.protocol("WM_DELETE_WINDOW", quit)
mf = MyFrame(root)
mf.set_input_callback(do_translation)
root.bind('<Return>', lambda event: start()) # essentially binds 'Return' keyboard event to translate_button
root.mainloop()
Hopefully this will be useful. I know, that there is not too much explanation what is happening here, but, don't have much time to write it. Your problem is very general.

Categories

Resources