In python, using tkinter, my random numbers only generate once? - python

Basically, I have this code:
from tkinter import *
from tkinter import messagebox
import random
import string
from tkinter import filedialog
ktwoWin = Tk() #window
qLabel = StringVar()
userAnswer = StringVar()
ktwoWin.withdraw() #hide the ktwoWin window
pass
num1= random.randint(1, 12) #random numbers
num2= random.randint(1, 12)
Answer= num1 * num2
def ktwoOpen():
ktwoWin.deiconify() #show the ktwoWin window
ktwoWin.title("Kindergarten to Grade Two")
ktwoWin.geometry("400x300")
ktwoWin.grid()
askbutton= Button(ktwoWin, text="ask me a question!", command = askquestion, height=3, width=16, bg="blue")
askbutton.grid(column= 0, row= 0)
submitbtn= Button(ktwoWin, text="Submit Answer", command=checkanswer, height=3, width=12, bg= "red")
submitbtn.grid(column=1, row=0)
q=Label(ktwoWin,textvariable=qLabel)
q.grid(column=1, row=1)
q.config(text="text to go here")
qLabel.set("some text")
answerentry= Entry(ktwoWin, textvariable=userAnswer)
answerentry.grid(column=3, row=3)
pass
def askquestion():
qLabel.set("what is" +str(num1) + "x" + str(num2) + "?")
def checkanswer():
useranswer=userAnswer.get()
if int(useranswer) != Answer:
messagebox.showwarning(message="the answer is " + str(Answer))
else:
messagebox.showinfo(message="correct!")
ktwoWin.mainloop
Which, when I run the program, the random numbers I have, will only randomise once, if that makes sense? My question, ultimately, is there a way for me to loop the random number part of the code?
any help is appreciated, thanks:)

It should be pretty straight forward to set up a new question each time one is answered. I'm not an expert at tkinter, so this is fairly general advice.
To start with, set up your two random numbers in the askquestion function, rather than at the top level of your module. Since you need to be able to access the product later, use the global keyword to make sure that the answer is available outside the function's scope (or you could reorganize the code into a class and use member variables).
You may also need to add some extra logic to the end of the checkanswer function to reset the program's state after an answer has been provided (e.g. clear the old question label and remove the old answer from the input box). Depending on how you want the program to play, you might immediately ask a new question, or you might go back to the original starting state.

Related

Random Number Generator with Tkinter module - Unable to clear old label data and need to populate it with the new data on click

This morning I wrote a simple random number generator (using Tkinter module), which generates a random number from 1-100. With my below code, it is functional and not generating any errors, however when I roll the next number, if it is a single digit vs a double digit.. The single digit will have overlapping text on the next generated label.
Essentially what I am aiming to do is either use configure() or destroy() to clear the old number / text that was generated on click, and update it with the new text / number data. I want this to occur after every click so I do not get any overlapping or funky looking output.
I tried researching on stackoverflow, and other people's code was confusing to me and didn't really apply to what I wrote. Searched through the Python Docs and it gave me the commands to utilize, but not how to utilize it in my example program... it was quite confusing. I also googled a bunch and watched several YouTube videos... and I still can't solve this supposedly simple task. Any help is much needed and appreciated! Expecting it to be cleared and then populated with the new text and rolled number. Thanks!
# Creating a Program to roll a # between 1-100.
import random
from tkinter import *
import tkinter as tk
root = tk.Tk()
def action():
roll = random.randint(1, 100)
result = tk.Label(text=f"You rolled a {roll}! ", font='Helsinki, 30')
result.place(x=300, y=220)
root.geometry("800x600")
title = root.title("Random Roll Generator")
description = tk.Label(text="Welcome you degenerate! Click the button below to roll a random number from 1-100.", font='Helsinki, 20')
description.place(x=20, y=100)
roll_button = tk.Button(text="Roll", padx=20, pady=0, command=action)
roll_button.place(x=360, y=520)
root.mainloop()
My solution is to create the label first, and then use the config command to update its text, without the need to create and package a new one.
Note that the fstring allows formatting, that is, you can always leave your number with 3 digits, avoiding unnecessary movements in the layout
# Creating a Program to roll a # between 1-100.
import random
import tkinter as tk
root = tk.Tk()
root.geometry('300x300')
rand_label = tk.Label(master=root, text='Click to roll New randon number.')
rand_label.pack()
def new_rng():
rand_label.config(text=f'New randon number: {random.randint(0, 100):03}')
rand_button = tk.Button(master=root, text='Roll', command=new_rng)
rand_button.pack()
root.mainloop()
You are placing a new label each time you roll with the current script, and that is why sometimes you see the overlapping label.
Use a string variable and update the variable.
# Creating a Program to roll a # between 1-100.
import random
from tkinter import *
import tkinter as tk
root = tk.Tk()
def action():
roll = random.randint(1, 100)
result_text.set(f"You rolled a {roll}! ")
root.geometry("800x600")
title = root.title("Random Roll Generator")
description = tk.Label(text="Welcome you degenerate! Click the button below to roll a random number from 1-100.", font='Helsinki, 20')
description.place(x=20, y=100)
result_text = StringVar("") # string variable to store result
result = tk.Label(textvariable = result_text, font='Helsinki, 30')
result.place(x=300, y=220)
roll_button = tk.Button(text="Roll", padx=20, pady=0, command=action)
roll_button.place(x=360, y=520)
root.mainloop()
The correct way to used widget.config in line 11. No needed to used StringVar(). Do not use this from tkinter import * is not widely used.
Code:
import random
import tkinter as tk
root = tk.Tk()
def action():
roll = random.randint(1, 100)
result.config(text=f"You rolled a {roll}! ")
root.geometry("800x600")
title = root.title("Random Roll Generator")
description = tk.Label(text="Welcome you degenerate! Click the button below to roll a random number from 1-100.", font='Helsinki, 20')
description.place(x=20, y=100)
result = tk.Label( font='Helsinki, 30')
result.place(x=300, y=220)
roll_button = tk.Button(text="Roll", padx=20, pady=0, command=action)
roll_button.place(x=360, y=520)
root.mainloop()
Screenshot:

How can I get the outcome of the two dice to show as a result in a "scorebox" in python?

It's pretty simple, I wish to show the value of the two dice combined in a "scorebox" somewhere in the GUI. I have tried to look everywhere, but can't come up with a simple solution anywhere. Anyone able to help a python noob? :)
(The code is copied from a YouTube tutorial, so not my code.)
import tkinter as tk
import random
#creating the GUI itself
root = tk.Tk()
root.geometry('600x400')
root.title('Roll Dice')
label = tk.Label(root, text='', font=('Helvetica', 260))
#dice function
def roll_dice():
dice = ['\u2680', '\u2681', '\u2682', '\u2683', '\u2684', '\u2685']
label.configure(text=f'{random.choice(dice)} {random.choice(dice)}')
label.pack()
#button to press to start rolling the dice
button = tk.Button(root, text='Roll Dice', foreground='black', command=roll_dice)
button.pack()
root.mainloop()
You can simply map the text inside a dictionary and then use indexing on the key to get the scores, and then just add the scores.
dice = {'\u2680':1, '\u2681':2, '\u2682':3, '\u2683':4, '\u2684':5, '\u2685':6}
scorebox = tk.Label(root, font=('Helvetica', 20)) # Make a scoresheet label
def roll_dice():
first,second = random.choice(list(dice)), random.choice(list(dice)) # Two random rolls
score_text = dice[first] + dice[second] # Corresponding texts
label.configure(text=f'{first} {second}') # Change the label
scorebox.configure(text=score_text)
label.pack()
scorebox.pack()
Why is the dictionary defined outside the function? So that it is just defined once and does not overwrite unnecessarily inside the function.

How to run a function from a button based GUI window?

I'm trying to make a menu driven program that has GUI capability. For the sake of this post I edited it down so the menu has 2 options, to quit and to calculate an average of a set of numbers. I ran this before I used the GUI code and it worked perfectly.
The issue is that when I click "Average", it runs the function and allows 1 input but then I can't enter another number. Same issue with other functions I had. They won't run past the first input and then the Quit button also won't quit.
Feel free to ask any questions in the comments if I wasn't good at describing what I needed. It's my first day using GUI so sorry if anything looks weird, I'm trying to teach myself but got stuck here. End goal is to have the whole program run in a GUI environment but I figured starting with the menu would be the best place to start.
from tkinter import *
import tkinter as tk
def average():
print("Enter test scores to get the average of. Type '-99' to quit.")
num = 1
count = 0
total = 0
while num != -99:
num = int(input("Enter numbers: "))
total += num
count += 1
average = (total + 99) / (count-1)
print("The average is: ", average)
print("-----------")
main()
def main():
r = tk.Tk()
r.title('Number games')
button1 = tk.Button(r, activebackground = "blue", bg = "light blue", text='Average', width=25, command= average)
button4 = tk.Button(r, activebackground = "blue", bg = "light blue", text='Quit', width=25, command=r.destroy)
button1.pack()
button4.pack()
r.mainloop()
main()
You don't need to call main() from inside average(). When I delete that line, your program works exactly as expected for me (debian 9, python3).
Ps. Don't use import *.
Globalize the variable r and declare r.destroy() in another function above main function. Note from tkinter import * is enough no need to import tkinter again with an alias.

Problem with calling functions with tkinter

from tkinter import *
from random import *
root = Tk()
#A function to create the turn for the current player. The current player isnt in this code as it is not important
def turn():
window = Tk()
dice = Button(window, text="Roll the dice!", bg= "white", command=lambda:diceAction(window))
dice.pack()
window.mainloop()
#a function to simulate a dice. It kills the function turn.
def diceAction(window):
result = Tk()
y = randint(1, 6)
quitButton = Button(result, text="Ok!", bg="white", command=result.destroy)
quitButton.pack()
window.destroy()
result.mainloop()
#A function to create the playing field and to start the game
def main():
label1 = Button(root, text="hi", bg="black")
label1.pack()
while 1:
turn()
print("Hi")
turn()
main()
root.mainloop()
My problem is that the code in the while function after the first turn() the code isnt executed until i close the root window(which i dont want because it represents the playing field). You can copy this code and execute it yourself if you want.
I have no idea what causes this and havent found anything online. Sorry for the long code but i wrote it so that it is executeable.
I don't know why this particular problem is occurring, but there are a couple of things in your code that are considered bad practice.
Instead of creating multiple instances of Tk(), you should use Toplevel widgets for any pop-up windows needed. Also, it's better to use root.mainloop() to run the program rather than a while loop.
I've made some edits to your code so that it uses a Toplevel widget and discards of the while loop.
from tkinter import *
from random import *
#A function to create the turn for the current player. The current player isnt in this code as it is not important
def turn(prev=None):
# destroy the previous turn
if prev:
prev.destroy()
# pop up with dice
window = Toplevel()
dice = Button(window, text="Roll the dice!", bg= "white")
dice.config(command=lambda b=dice, w=window:diceAction(b, w))
dice.pack()
#a function to simulate a dice, reconfigures the pop-up
def diceAction(button, window):
# roll dice
y = randint(1, 6)
# use dice result here?
print(y)
# reconfigure button, the command now starts a new turn
button.config(text='ok', command=lambda w=window:turn(prev=w))
root = Tk()
# I hijacked this button to use as a start button
label1 = Button(root, text="hi", bg="black", command=turn)
label1.pack()
root.mainloop()
I don't know if this is what you need, but it functions as the code in the question would if it worked.
Sorry I couldn't help with the cause of the error.

Python Tkinter project

I'm working on a final for python. My goal is to make a code that uses Tkinter to ask the user to input the answer to a math problem using an entry box then press submit. I want to be able to have python do something depending on whether the answer is right or wrong, but I'm not sure how to do this with Tkinter. How do I make it "check" the answer? For example,
ent = Entry(pyfinal)
btn = Button(pyfinal, text="Submit", bg="#000000")
lbl = Label(pyfinal, text="What is the answer to 5 x 5?")
If I was using regular python, I would do this,
ent = int(input('What is 5 x 5? '))
if ent == int ('25'):
print ("correct")
else:
print ("wrong, try again.")
How would I do that with Tkinter while keeping it all in the Tkinter window?
Thanks
Try using if statements for example:
Mp = Answer to math problem
Answer = What user wrote
If Mp == Answer:
Lbl = Label("You are right")
Lbl2.pack()
Lbl.after(3000, lambda: label.destroy())
Else:
Lbl2 = Label("You are wrong')
Lbl2.pack()
Lbl2.after(3000, lambda: label2.destroy())
Only write the .after statement if you want to delete this label in a certain amount of time

Categories