Tkinter: Taking variable from Label in the for loop - python

I'm currently in a project of making "Shut The Door, Dice Game". I've used the for loop to label the "door", now my problem is how to take the "door" to update to "-" after click the correct "door".
import tkinter, random
window = tkinter.Tk(); window.title("Dice Game - Shut the DOOR")
def roll():
result["text"] = int(random.randint(1,6))
result_2["text"] = int(random.randint(1,6))
for door in range(12):
number = tkinter.Label(window, text=door+1)
number.grid(row=0, column=door, sticky="e")
result = tkinter.Label(text="-")
result_2= tkinter.Label(text="-")
result.grid(row=1, column=0)
result_2.grid(row=1,column=1, sticky="w")
dice = tkinter.Button(window, text="Roll", command=roll)
dice.grid(row=2, column=0)
Appreciate for the help

Use .bind("<Button>", function) to do something when a label (or any other widget) is clicked. First, define a function to handle the event, then add number.bind("<Button>", function_name) to make the function run when number is clicked.
Tkinter will automatically pass an argument to the function containing information about the event so the function must accept one argument. This is used to get which number was clicked.
Full code:
import tkinter, random
window = tkinter.Tk(); window.title("Dice Game - Shut the DOOR")
def change_text(event):
number = event.widget
if result["text"] == "-" or number["text"] == "-": return
if int(result["text"]) + int(result_2["text"]) == int(number["text"]):
number["text"] = "-"
def roll():
result["text"] = int(random.randint(1,6))
result_2["text"] = int(random.randint(1,6))
for door in range(12):
number = tkinter.Label(window, text=door+1, width=1)
number.grid(row=0, column=door, sticky="e")
number.bind("<Button>", change_text)
result = tkinter.Label(text="-")
result_2= tkinter.Label(text="-")
result.grid(row=1, column=0)
result_2.grid(row=1,column=1, sticky="w")
dice = tkinter.Button(window, text="Roll", command=roll)
dice.grid(row=2, column=0)

Related

how do I make Python tkinter message box show a number from elsewhere in the script

Will preface this by saying i am very new to python and coding in general,
I followed a tutorial on how to make a countdown timer, and have managed to code a button that measures the amount of times it has been clicked by mashing my code for the button with a post i found on a forum, the count down timer displays a "Times up" message box at the end, what i want to do is have it also display the amount of times the button is clicked in the allotted time. I've tried calling the global count from within the countdown timer and reusing the line that displays the count in the GUI but this seems to break it and doing it without it as displayed here simply shows the string as it is written, any help or guidance is appreciated
'''
import time
from tkinter import *
from tkinter import messagebox
f = ("Arial",24)
root = Tk()
root.title("Click Counter")
root.config(bg='#345')
root.state('zoomed') #opens as maximised, negating the need for the 'Geometry' command
count = 0
def clicked():
global count
count = count + 1
myLabel.configure(text=f'Button was clicked {count} times!!!')
hour=StringVar()
minute=StringVar()
second=StringVar()
hour.set("00")
minute.set("00")
second.set("10")
hour_tf= Entry(
root,
width=3,
font=f,
textvariable=hour
)
hour_tf.place(x=80,y=20)
mins_tf= Entry(
root,
width=3,
font=f,
textvariable=minute)
mins_tf.place(x=130,y=20)
sec_tf = Entry(
root,
width=3,
font=f,
textvariable=second)
sec_tf .place(x=180,y=20)
TheButton = Button(root, height= 30, width=100, bg="light blue", text="Click For Your Life", command=clicked) #tells the button to call the function when clicked
TheButton.pack()
myLabel = Label(root)
myLabel.pack()
def startCountdown():
try:
userinput = int(hour.get())*3600 + int(minute.get())*60 + int(second.get())
except:
messagebox.showwarning('', 'Invalid Input!')
while userinput >-1:
mins,secs = divmod(userinput,60)
hours=0
if mins >60:
hours, mins = divmod(mins, 60)
hour.set("{0:2d}".format(hours))
minute.set("{0:2d}".format(mins))
second.set("{0:2d}".format(secs))
root.update()
time.sleep(1)
if (userinput == 0):
messagebox.showinfo("Time's Up!!", "you clicked (count) times")
userinput -= 1
start_btn = Button(
root,
text='START',
bd='5',
command= startCountdown
)
start_btn.place(x = 120,y = 120)
root.mainloop()
'''
You need to use an f-String with "{}", like you did in the clicked function:
if (userinput == 0):
messagebox.showinfo(f"Time's Up!! you clicked {count} times")
A kind user provided the correct answer but has seen fit to delete their comment so in case anyone like me is trawling through old posts looking for help here is what worked:
To have your messagebox display the number of clicks (in this case
count), you can simply use
messagebox.showinfo("Time's Up!!", "you clicked " + str(count) + " times")
instead of
messagebox.showinfo("Time's Up!!", "you clicked (count) times")
Your attempt won't work, because you included count inside the
quotation marks, meaning that Python will completely ignore the fact
that count is also a variable and just print it as a string. There are
a lot of ways you can format strings to include variables etc. Check
out here for a good tutorial/explanation.

Tkinter GUI program issue. Entry.get() problem

Working on a project in which I use Tkinter in order to create a GUI that gives a list of software in a drop-down and when a particular software is chosen, it takes you to a separate window where a user's name will be entered and they would be added to a database. With the code I have so far, I am able to link a "submit" button on the second window to a function that prints a confirmation message as a test to make sure the button works. My issue now is trying to get the input from the entry field and link the input to the "Submit" button but I can't seem to find a way to do so. I was wondering if I could get some advice on how to go about this. Would classes need to be used in order to make it work? or can I stick with functions and keep the code relatively simple?
I have added the code for my program below.
import tkinter as tk
from tkinter import *
from tkinter import ttk
root = tk.Tk() # Main window
root.title("Software Licences")
root.geometry("300x300")
frame = ttk.Frame(root, padding="50 0 50 50")
frame.pack(fill=tk.BOTH, expand=True)
tkvar = StringVar()
choices = ['Imagenow', # Dropdown menu with software options
'FileMakerPro',
'Acrobat',
'Office',
'Lotus Notes']
tkvar.set('Acrobat') # Shows default dropdown menu option when program is opened
popupMenu = OptionMenu(frame, tkvar, *sorted(choices))
popupLabel = ttk.Label(frame, text="Choose Software")
popupLabel.pack()
popupMenu.pack()
def software_pages(): # In this function is the 2nd window with for each individual software
top = Toplevel()
top.title("Software Licences")
top.geometry("300x300")
myLabel = Label(top, text=tkvar.get()).pack()
employee_entrylbl = Label(top, text="Employee name").pack()
employee_entry = Entry(top, width=25, textvariable=tk.StringVar) # Entry field for adding user's name
employee_entry.pack() # Entry field is displayed
if tkvar.get() == "Acrobat": # for each if statement, button command is link to the functions
# defined below
button = ttk.Button(top, text="Submit", command=add_to_acrobat).pack()
elif tkvar.get() == "Imagenow":
button = ttk.Button(top, text="Submit", command=add_to_imagenow).pack()
elif tkvar.get() == "FileMakerPro":
button = ttk.Button(top, text="Submit", command=add_to_filemakerpro).pack()
elif tkvar.get() == "Office":
button = ttk.Button(top, text="Submit", command=add_to_office).pack()
else:
button = ttk.Button(top, text="Submit", command=add_to_lotusnotes).pack()
exit_button = ttk.Button(top, text="Exit", command=top.destroy).pack() # Exit button for second window
add_emp_button = ttk.Button(frame, text="Next", command=software_pages) # "Next" button in the main window takes the
# user to the second window
add_emp_button.pack()
# Functions below are linked to the button commands of each software in the second window function defined earlier.
# They print out specified messages that confirm the user had been added
def add_to_acrobat():
return print("User added to Acrobat")
def add_to_lotusnotes():
print("User added to IBM")
def add_to_imagenow():
print("User added to imagenow")
def add_to_office():
print("User added to 365")
def add_to_filemakerpro():
print("User added to FMP")
def click_button(): # Function for Exit button for main window
root.destroy()
exit_button = ttk.Button(frame, text="Exit", command=click_button) # Exit button for main window
exit_button.pack()
root.mainloop()
You can pass parameters to the command of tkinter.command using partial from the functools module.
in your case:
button = ttk.Button(top, text="Submit", command=partial(add_to_acrobat, employee_entry)).pack()
in the above line, I send the employee_entry(Which holds your desired text) to the add_to_acrobat function
and the add_acrobat function should look like this:
def add_to_acrobat(e):
print(e.get())
return print("User added to Acrobat")
Hope it helps

How to add a reset button that clears my code?

I'm making a basic TicTacToe UI in python, and I believe that a fundamental item to this code is a reset button which resets your codes back to the default. is there any other way to do this?
I've Tried to define a function which resets the text of the button back to " " but I don't think that's a great idea because of a lot of other complexities within the cod.
from tkinter import *
root = Tk()
def changetext():
BTN1["text"] = "X"
BTN1 = Button(root, text=" ", command=changetext)
BTN1.pack()
root.mainloop()
So I want to add a button here that says "Reset Text" and it resets all the codes to defaults.
The easiest way to reset the game would be to
Reset the UI as you suggest, with a single dedicated reset_UI() function
Reset the board state by creating a new game board object, and discarding the old one
This of course means that you'll need to wrap all your variables and functions in a board class Board, so that there aren't a billion global variables you have to worry about resetting. The only thing that should persist between resets are your UI buttons, which can be created in your main() function before initializing the game board.
Here's code demonstrating how something like that could be done (plus a few other things):
import tkinter as tk
def toggle(btn):
if btn["text"] == "X":
btn["text"] = " "
else:
btn["text"] = "X"
def reset(buttons):
for btn in buttons.values():
btn["text"] = " "
root = tk.Tk()
buttons = {}
for row in range(3):
for col in range(3):
button = tk.Button(root, text=" ", width=1, height=1)
button.config(command=lambda btn=button: toggle(btn))
button.grid(row=row, column=col)
buttons[row, col] = button
reset_button = tk.Button(root, text="Reset", command=lambda: reset(buttons))
reset_button.grid(columnspan=3)
root.mainloop()

Is there a way to define a command for radio buttons to show right or false answer in multiple choice quiz?

I am a beginner trying to create a multiple choice quiz in python with Tkinter. Sorry if I created a messy code. I am using radio buttons for different answers. I would like to show the message "This is the correct answer" when selecting Option 1 and "This is the wrong answer" when selecting any other option. Currently, the message is always "This is the wrong answer" regardless of the Option that is chosen. I understood that the value has nothing to do with it but I did not find the right way to do it. Is there a way to define this kind of command? Thank you so much for any help, recommendations and answers.
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
window = Tk()
window.title("Quiz")
window.geometry("500x150")
score = 0
def inst():
t = Label(window, text="Choose the correct statement to answer the
question")
t.pack()
def start():
start_game()
greet = Label(window, text="Welcome to the Quiz")
greet.pack()
startButton = Button(window, command=start, text="Start")
startButton.pack()
instr = Button(window, text="Instructions", command=inst)
instr.pack()
end = Button(window, text="Exit", command=window.destroy)
end.pack()
def start_game():
top = Toplevel()
top.title("Question 1")
var = StringVar()
def check():
if var.get() is True:
messagebox.showinfo('Congrats', message='This is the correct
answer.Score is {}'.format(score))
else:
messagebox.showinfo('Lose', message='This answer is wrong.')
R1 = Radiobutton(top,
text="Option 1",
indicatoron=0,
width=20,
padx=20,
pady=10,
variable=var,
value=True,
command=check)
R1.pack( anchor = W )
R2 = Radiobutton(top,
text="Option 2",
indicatoron=0,
width=20,
padx=20,
pady=10,
variable=var,
value=False,
command=check)
R2.pack( anchor = W )
R3 = Radiobutton(top,
text="Option 3",
indicatoron=0,
width=20,
padx=20,
pady=10,
variable=var,
value=False,
command=check)
R3.pack( anchor = W)
label = Label(top)
label.pack()
window.mainloop()
Since you're working with boolean values, I thought it made more sense to use a BooleanVar instead.
You can simply pass the buttons to the check function and change their colours if you know which one is correct:
def check(btn1, btn2):
btn1.config(bg='green')
btn2.config(bg='red')
Then modify the radiobuttons as such (just below where you've defined them):
for btn in (R1, R2):
btn.config(command=lambda btn1=R1,btn2=R2:check(btn1,btn2))
Note, I used two buttons as R2 and R3 had the same values so they'd be grouped as one effectively.
Here is an example; it uses a button list to store all radiobuttons that were created and changes the colour of each of them depending on their text whilst also checking if the player got the right answer.
import tkinter as tk
def check_answer():
if question_answer.get() == 2: #get the value of the integer variable
print('you got it right') #if it has been set to 2 by the player, they got it right
for btn in btnlist: #check each of our radiobuttons
if int(btn['text']) == 2: #if the text of that button is equal to the correct answer
btn.config(bg='green') #make it green
else:
btn.config(bg='red') #otherwise make it red
win = tk.Tk()
question = 'What is 1+1?' #put your question here
question_answer = tk.IntVar() #we use an Integer Variable to store the value of the answer
question_answer.set(0) #we set the value of the correct answer to 0
lbl = tk.Label(win, text=question)
lbl.grid(columnspan=4)
column = 0
btnlist = []
for answer in range(4): #create radiobuttons in a for loop
btn = tk.Radiobutton(win, text=str(answer), variable=question_answer,
value=answer) #associate each button with the answer variable
#but give each button its own unique value
btnlist.append(btn)
btn.grid(row=1, column=column)
column += 1
confirm_btn = tk.Button(win, text='Confirm', command=check_answer)
confirm_btn.grid(columnspan=4)
win.mainloop()
In this example I used an IntVar as the answer is an integer, you could also use a BooleanVar or StringVar in the same manner as needed.
EDIT: As per you request in the comments:
import tkinter as tk
win = tk.Tk()
text_to_add_to_btns = ['A', 'B', 'C', 'D', 'E'] #change to whatever text you like
#with however many elements which represent each individual button
btn_list = []
Column = 0
for txt in text_to_add_to_btns:
btn = tk.Button(win, text=txt)
btn.grid(row=0, column=Column, sticky='nesw')
btn_list.append(btn)
Column += 1
win.mainloop()
We create a default list containing text to add to each button as individual list elements. We then loop over that list to create each button for every element and set the text of the button to that element and then append it to our separate button list.

Tkinter Buttons: How make sure certain buttons have been pressed before running the main part of your script

I apologize for the terribly worded question, I just could not figure out a better way to present my problem. I am writing a script that demonstrates the Monty Hall Problem using an interactive GUI. On my first frame I have 3 Buttons labeled "Door A", "Door B" and "Door C." And underneath those 3 buttons I have 2 other buttons labeled "Switch Choice" and "Keep Choice."
The 3 buttons that represent the "Doors" call my method named initialGuess(self,door) while the bottom 2 buttons which represent whether or not the user would like to keep or switch his choice call the method switch_choice(self, val). I want to make sure the user chooses a door and whether he wants to switch his choice BEFORE the simulation of the Monty Hall problem. Both buttons run two different methods, How do I write a script that makes sure both methods have been run and that once it has been confirmed the user made both choices it will run the main part of the simulation.
To help with this here are my buttons and corresponding methods for them. (I apologize in advance for all the global variables I plan on creating other methods for them, this is just an blueprint to start off).
class MontyHallSim(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = Label(self, text="Monty Hall Problem", font=Large_Font)
label.place(x=150, y=100, width=500, height=50)
doorA = Button(self, text="Door A", font=Small_Font,
relief=RAISED, height=10, width=50,
command=lambda: self.initialGuess("A"))
doorA.place(x=125, y=200, width=150, height=400)
doorB = Button(self, text="Door B", font=Small_Font,
relief=RAISED, height=10, width=50,
command=lambda: self.initialGuess("B"))
doorB.place(x=325, y=200, width=150, height=400)
doorC = Button(self, text="Door C", font=Small_Font,
relief=RAISED , height=10, width=50,
command=lambda: self.initialGuess("C"))
doorC.place(x=525, y=200, width=150, height=400)
switch= Button(self, text="Switch Choice",
relief=RAISED, height=10, width=50,
command=lambda: self.switch_choice("Y"))
switch.place(x=100, y=600, width=100, height=50)
no_switch = Button(self, text="Keep Choice",
relief=RAISED, height=10, width=50,
command=lambda: self.switch_choice("N"))
no_switch.place(x=600, y=600, width=100, height=50)
global doors
global game
global prize
global guess
global empty_door
global initial
global moderator
global stay
global switch
global selection
selection=None
moderator="Moderator"
guess=None
empty_door="Empty"
prize="Prize"
initial="Guess"
doors=("A", "B", "C")
game={"A":"Empty","B":"Empty", "C":"Empty"}
def initialGuess(self,door):
guess=door
game[guess]=initial
tm.showinfo("Monty Hall", "You Chose Door %.2s" %guess)
return guess
def switch_choice(self,val):
switch="Y"
stay="N"
if val==switch:
selection=switch
tm.showinfo("Monty Hall", "You Chose To Switch Your Guess!")
else:
selection=stay
tm.showinfo("Monty Hall", "You Chose The Original Door")
return selection
def run_sim(self):
iterations=1000
win_count=0
lose_count=0
for _ in range(iterations):
#Sets the Prize stores Prize "Key" in a value for later use
placed=random.choice(doors)
game[placed]=prize
prize_key=list(game.keys())[list(game.values()).index(prize)]
#Sets Moderators Choice
moder=list(game.keys())[list(game.values()).index(empty_door)]
game[moder]=moderator
#checks to see if switch is performed
if selection=="Y":
new_guess=random.choice(
[x for x in doors if x != guess and x!=moderator])
game[new_guess]=initial
game[guess]=empty_door
guess=new_guess
game[prize_key]=prize
else:
pass
#Checks Game dictionary for "Guess"; If guess is present you
#lose...
#If guess is NOT present that means the Prize is in the door that
#you guessed
try:
open_guess = (
list(game.keys())[list(game.values()).index(guess)])
if open_guess in game:
lose_count += 1
except ValueError:
win_count += 1
percent_won=float(win_count/iterations*100.)
percent_lost=float(lose_count/iterations*100)
tm.showinfo("Percent Won: %.2f" %percent_won)
tm.showinfo("Percent Lost: %.2f" %percent_lost)
You can disable (tk.DISABLED) and enable (tk.NORMAL) buttons and other widgets. So the keep choice button can be disabled until a choice is made. You might want to use Radiobutton instead perhaps? And maybe a messagebox to check if the player wants to switch or not?
Here is a quick and messy code example, it might give you some ideas.
import tkinter as tk
from tkinter import ttk, messagebox
def reset_choice():
button_run.configure(state=tk.DISABLED)
button_a.configure(state=tk.NORMAL)
button_b.configure(state=tk.NORMAL)
button_c.configure(state=tk.NORMAL)
def reset_sim():
global choice
global switch
reset_choice()
choice.set('')
switch = False
def run_sim():
global choice
global switch
if messagebox.askyesno('Are you sure?', 'You chose {}, do you want to switch?'.format(choice.get())):
reset_choice()
switch = True
else:
print('You chose door {}'.format(choice.get()))
if switch:
print('You switched choice before running')
print('Running sim...')
reset_sim()
def select_door():
button_run.configure(state=tk.NORMAL)
button_a.configure(state=tk.DISABLED)
button_b.configure(state=tk.DISABLED)
button_c.configure(state=tk.DISABLED)
root = tk.Tk()
frame = ttk.Frame(root)
frame.grid(column=0, row=0)
choice = tk.StringVar()
switch = False
button_a = ttk.Radiobutton(frame, text="A", variable=choice, value="A", command=select_door)
button_b = ttk.Radiobutton(frame, text="B", variable=choice, value="B", command=select_door)
button_c = ttk.Radiobutton(frame, text="C", variable=choice, value="C", command=select_door)
button_run = ttk.Button(frame, text="Run", command=run_sim, state=tk.DISABLED)
button_a.grid(column=0, row=0)
button_b.grid(column=1, row=0)
button_c.grid(column=2, row=0)
button_run.grid(column=1, row=1)
root.mainloop()
The Run button isn't enabled before an alternative is chosen, and when you pick a choice you can't switch. When you press Run a messagebox asks if you want to switch. If yes, the choices are enabled again and a switch variable is set to True.
When you choose to not switch, it prints what your choice and if you switched it prints a line stating just that. And then it resets the choice and switch set to False (just so it is back to scratch again).

Categories