Tkinter code isnt executed after destroying a window - python

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 = Toplevel()
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 = Toplevel()
y = randint(1, 6)
# i do something with this number
quitButton = Button(result, text="Ok!", bg="white", command=lambda: [result.destroy(), window.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()
With this code i basically create a roll the dice simulator. In my actual code i give the function turn() player1/player2(which are class objects) so i can track whose turn it is. Thats why i call turn() 2 times in the while.
The problem is that the code after the first turn() isnt executed(until i manually close the root window which is weird) anymore. At my knowledge this should work.
I open the turn function which opens the diceAction function upon button press. diceAction() gives me the number and kills both windows. Then the second turn() should be called and the process continues until someone wins(which i havent implemented in this code).
The print("Hi") isnt executed either. Am i missing something? You can copy this code and execute it yourself.

The short answer is "Infinite loops and tkinter don't play well together". The long answer is that you never escape window.mainloop(). I don't see a good enough reason that you need to have window.mainloop() and result.mainloop() running to justify the headache of multiple loops in tkinter.
A subjectively better way of doing this is to have the end of the first turn() trigger the start of the next one:
from tkinter import *
from random import *
root = Tk()
global turnCount
turnCount = 0
def turn():
window = Toplevel()
dice = Button(window, text="Roll the dice!", bg="white", command=lambda:diceAction())
dice.pack()
def diceAction():
result = Toplevel()
y = randint(1, 6)
quitButton = Button(result, text="Ok!", bg="white", command=lambda: nextTurn())
quitButton.pack()
def nextTurn():
global turnCount
turnCount = turnCount + 1
for i in root.winfo_children():
if str(type(i)) == "<class 'tkinter.Toplevel'>":
i.destroy()
turn()
def main():
label1 = Button(root, text="hi", bg="black")
label1.pack()
turn()
main()
root.mainloop()
I would recommend attempting to use OOP on a project like this instead of the global that I declared above.

Related

on a button click, copy whats inside entry() and store it to a variable (tkinter)

I have two widgets to work with, a text input, and a button, both are created inside a function. What I want to happen is the user types in their name and then clicks the button to submit the answer. What I want the computer to do, is on the button press it will read whats inside the text and the save it to a variable. Once it saves it, it will print it out.
The code below is bad because it runs through the if statement immediately without the consulting of the button press.
There has to be a simpler solution. Also this may not be PEP 8 or whatever please be patient because I'm new.
import tkinter as tk
from tkinter import Tk, Label, Button
import sys
import time
import random
import threading
from tkinter import *
window = tk.Tk()
window.geometry("300x300")
window.title("GUI")
def start_screen():
reset()
start = tk.Label(window, text="start of game")
start.place(x=110,y=20)
play = Button(window, text= "play", command = start_game)
play.place(x=110,y=50)
helper = Button(window, text="help", command = help_screen)
helper.place(x=110,y=70)
def stuff():
global t
t = True
print(t)
return t
def text_handling():
global t
t = False
reset()#clears the screen
label = Label(window, text='')
question1= "what is your name?"
label.pack()
print_slow(label, question1, 40)#prints out letters slowly
#here is the part I'm having problems with
name = Entry(window)
name.pack()
but = Button(window, text="enter", command= stuff)
but.pack()
print(t)
if t == True:
myPlayer.name = name.get()
print(myPlayer.name)
def start_game():
reset()
bt = tk.Button(window,text="Enter", bg="orange", command =
text_handling)
bt.place(x=100,y=100)
start_screen()

How to add time (ticks) in a GUI for a game

I want to add the ticking of time in seconds to a GUI, for a clicker game. So the idea is to have a function that gets called every n ticks, and this function increments X objects.
I have tried to use a while loop, both before and after calling the .mainloop() method. It didn't work in either occasion, I also tried the crazy idea of having the mainloop() method inside the while loop (aware of what that would do lol).
from tkinter import *
import time
result = 0
window = Tk()
window.title("Numbers Game")
window.geometry('360x240')
label = Label(window, text=result)
label.grid(column=0,row=0)
def clicked():
global result
result += 1
label.config(text=result)
button = Button(window, text="Push Me", command=clicked)
button.grid(column=1, row=2)
window.mainloop()
while True:
time.sleep(1)
clicked()
The current version of my code produces an error that mentions the function doing GUI related things outside of the window. But I don't have the slightest clue of how to achieve this.
You mean you want to have the result counter increment every second? You can't use infinite loops with a GUI, because they interfere with the GUI's mainloop. You have to integrate your code into the mainloop using the after method.
from tkinter import *
import time
result = 0
window = Tk()
window.title("Numbers Game")
window.geometry('360x240')
label = Label(window, text=result)
label.grid(column=0,row=0)
def clicked():
global result
result += 1
label.config(text=result)
def tick():
clicked()
window.after(1000, tick) # after 1,000 milliseconds, call tick() again
button = Button(window, text="Push Me", command=clicked)
button.grid(column=1, row=2)
tick() # start the "loop"
window.mainloop()

How to fix my Function so it works in my Tkinter Application (Guessing Game)

Basically, I want to go through answers and check every time if that's correct with a button and then pop it from the list. In another file/code/application it's working when I try to use it on my Gui Application it doesn't iterate and only keeps the first answer right.
I tried multiple things like using Dictionaries and getting the key but it still doesn't iterate over after the Button press.
This is the code that works fine:
while answers == True:
answers = ["Sayajin", "Namek", "Cell", "TournamentOfPower"]
for a in range(4):
s = input()
if s in answers[0]:
print("Richtig")
answers.pop(0)
This is the code that doesnt work:
def check(event):
answers = ["Sayajin", "Namek", "Cell", "TournamentOfPower"]
for a in range(4):
s = entrysaga.get()
if s in answers[0]:
print("Richtig")
answers.pop(0)
Complete Code:
from tkinter import *
import tkinter.messagebox
import time
import random
root = Tk()
#Hint Button
hint = Button(root, text= "Hint")
hint.bind("<Button-1>")
hint.place(x=50, y=20)
#How to Play Button + Info Message how to play
def Howtoplay():
tkinter.messagebox.showinfo("How to play", "To start the game u have
to press the button (Start)\n---------------------------------------------
----------------\n"
""
"Then the Picture will switch
and its going to show u a Character and u have to guess from which Dragon
Ball Saga he is.\n--------------------------------------------------------
-----\n"
"Just type it in the Entry and
press Check after that if u were right the next picture shows up")
info = Button(root, text="How to Play", command=Howtoplay)
info.bind("<Button-1>")
info.place(x=150, y=20)
#textwidget
textwidget = Label(root, text="Entry the DragonBall Saga:")
#entry widget
entrysaga = Entry(root)
#Pictures for guessing the Saga
Sayajin = PhotoImage(file="sayajinsaga.png")
Namek = PhotoImage(file="NamekSaga.png")
Cell = PhotoImage(file="CellSaga.png")
Buu = PhotoImage(file="BuuSaga.png")
TournamentOfPower = PhotoImage(file="TournamentOfPowersaga.png")
#Start function
def start():
labelSagas.config(image=Sayajin)
#define check for pictures
def check(event):
answers = ["Sayajin", "Namek", "Cell", "TournamentOfPower"]
for a in range(4):
s = entrysaga.get()
if s in answers[0]:
print("Richtig")
answers.pop(0)
#button check
buttonsaga = Button(root, text="Check")
buttonsaga.bind("<Button-1>", check)
textwidget.place(x=300, y=170)
entrysaga.place(x=300, y= 200)
buttonsaga.place(x=440, y=195)
#Start Button
start = Button(root, text="Start", command=start)
start.bind("<Button-1")
start.place(x=400, y=20)
# Label with start picture,
startpic = PhotoImage(file="dbzsagas.png")
labelSagas = Label(root, image=startpic)
labelSagas.place(x=25, y=80)
#size of window
root.geometry("500x280")
#window title
root.title("Dragon Ball Saga´s guessing game")
#start of the window
root.mainloop()
My excepted output should be like in the first code that it iterates over and after getting the first answer right ur on to the next one. But the actual result it that stays on the first.
I have moved the widgets which should appear when you press the Start button. They are all in the def start(): function as they would not appear otherwise.
As entrysaga was being called within a different function it could not find the inputted value as the variable is not a global variable. By using global entrysaga it means that it is and can be called from anywhere within the script.
This is how it looks:
from tkinter import *
import tkinter.messagebox
import time
import random
root = Tk()
#Hint Button
hint = Button(root, text= "Hint")
hint.bind("<Button-1>")
hint.place(x=50, y=20)
#How to Play Button + Info Message how to play
def Howtoplay():
tkinter.messagebox.showinfo("How to play", "To start the game u have to press the button (Start)\n--------------------------------------------- ----------------\n"
""
"Then the Picture will switch and its going to show u a Character and u have to guess from which Dragon Ball Saga he is.\n-------------------------------------------------------- -----\n"
"Just type it in the Entry and press Check after that if u were right the next picture shows up")
info = Button(root, text="How to Play", command=Howtoplay)
info.bind("<Button-1>")
info.place(x=150, y=20)
#Pictures for guessing the Saga
Sayajin = PhotoImage(file="sayajinsaga.png")
Namek = PhotoImage(file="NamekSaga.png")
Cell = PhotoImage(file="CellSaga.png")
Buu = PhotoImage(file="BuuSaga.png")
TournamentOfPower = PhotoImage(file="TournamentOfPowersaga.png")
#Start function
def start():
labelSagas.config(image=Sayajin)
global entrysaga
entrysaga = tkinter.Entry(root)
entrysaga.place(x=300, y= 200)
buttonsaga = Button(root, text="Check")
buttonsaga.bind("<Button-1>", check)
buttonsaga.place(x=440, y=195)
textwidget = Label(root, text="Entry the DragonBall Saga:")
textwidget.place(x=300, y=170)
#define check for pictures
def check(event):
answers = ["Sayajin", "Namek", "Cell", "TournamentOfPower"]
for a in range(4):
s = entrysaga.get()
if s in answers[0]:
print("Richtig")
answers.pop(0)
#Start Button
start = Button(root, text="Start", command=start)
start.bind("<Button-1")
start.place(x=400, y=20)
# Label with start picture,
startpic = PhotoImage(file="dbzsagas.png")
labelSagas = Label(root, image=startpic)
labelSagas.place(x=25, y=80)
#size of window
root.geometry("500x280")
#window title
root.title("Dragon Ball Saga´s guessing game")
#start of the window
root.mainloop()
Hope this helps! :)

Python Tkinter Progressbar After [duplicate]

Imagine the following simple example:
def doNothing():
sleep(0.5)
barVar.set(10)
sleep(0.5)
barVar.set(20)
sleep(0.5)
barVar.set(30)
mainWindow = Tk()
barVar = DoubleVar()
barVar.set(0)
bar = Progressbar(mainWindow, length=200, style='black.Horizontal.TProgressbar', variable=barVar, mode='determinate')
bar.grid(row=1, column=0)
button= Button(mainWindow, text='Click', command=doNothing)
button.grid(row=0, column=0)
mainWindow.mainloop()
What I get when I run this, the progressbar is already at 30% when clicking the button, no progress in front of me. Like attached:
What I need: I can see the progress in front of me (not hanging then suddenly 30%)
Update:
I upadted the code according to #Bernhard answer, but still I can not see the progress in front of me. Just a sudden jump of 30% after waiting 1.5 sec
Seocnd Update:
I'm only using sleep here as a simulation for a process that takes time, like connecting over ssh and grabing some info.
Do not use sleep() in tkinter. The entire reason for you problem is sleep() will freeze tkinter until it is done with its count so what you are seeing is a frozen program and when the program is finally released its already set to 30 percent on the next mainloop update.
Instead we need to use Tkinter's built in method called after() as after is specifically for this purpose.
import tkinter as tk
import tkinter.ttk as ttk
mainWindow = tk.Tk()
def update_progress_bar():
x = barVar.get()
if x < 100:
barVar.set(x+10)
mainWindow.after(500, update_progress_bar)
else:
print("Complete")
barVar = tk.DoubleVar()
barVar.set(0)
bar = ttk.Progressbar(mainWindow, length=200, style='black.Horizontal.TProgressbar', variable=barVar, mode='determinate')
bar.grid(row=1, column=0)
button= tk.Button(mainWindow, text='Click', command=update_progress_bar)
button.grid(row=0, column=0)
mainWindow.mainloop()
If you want the bar to appear to move smoothly you will need to speed up the function call and reduce the addition to the DoubbleVar.
import tkinter as tk
import tkinter.ttk as ttk
mainWindow = tk.Tk()
def update_progress_bar():
x = barVar.get()
if x < 100:
barVar.set(x+0.5)
mainWindow.after(50, update_progress_bar)
else:
print("Complete")
barVar = tk.DoubleVar()
barVar.set(0)
bar = ttk.Progressbar(mainWindow, length=200, style='black.Horizontal.TProgressbar', variable=barVar, mode='determinate')
bar.grid(row=1, column=0)
button= tk.Button(mainWindow, text='Click', command=update_progress_bar)
button.grid(row=0, column=0)
mainWindow.mainloop()
Because you are calling the function when the buttion is initialized, you need to loose the '(barVar') in the command=(barVar)). This way you bind the function to the button and don't call it when initializing it.
button= Button(mainWindow, text='Click', command=doNothing)
If you need to pass an argument you need to bypass the calling by using lambda:
button= Button(mainWindow, text='Click', command= lambda: doNothing(barVar))
I think I find the solution.
simply add mainWindow.update() after each progress. So the final code would be:
def doNothing():
sleep(0.5)
barVar.set(10)
mainWindow.update()
sleep(0.5)
barVar.set(20)
mainWindow.update()
sleep(0.5)
barVar.set(30)
mainWindow.update()

how to prevent tkinter from freezing when I click somewhere else?

My tkinter gui starts to freeze when I click on somewhere else. Is there a way to prevent that?
Here's my code:
#=========================
from tkinter import *
from time import sleep
import random
#=====================
root=Tk()
root.title("Wise Words")
root.geometry("500x180+360+30")
root.resizable(0,0)
root.call("wm", "attributes", ".", "-topmost", "1")
#===================
def display(random):
if random == 1:
return "Be wise today so you don't cry tomorrow"
elif random == 2:
return "Frustration is the result of failed expectations"
elif random == 3:
return "Wishes are possibilities. Dare to make a wish"
if True:
sleep(4)
r=random.randint(1,3)
sentence=display(r)
label.configure(text=str(sentence))
label.update_idletasks()
root.after(5000, display(random))
#==================
def Click(event):
display(random)
#======================
label=Button(root, fg="white", bg="blue", text="Click to start!",
font=("Tahoma", 20, "bold"), width=40, height=4,
wraplength=400)
label.bind("<Button-1>", Click)
label.pack()
#================
root.mainloop()
Note: The label for display is the Button itself, so I name it 'label'.
You're doing several strange things in your code:
Using time.sleep in a Tkinter application
Calling the button a label (there is a Label Tkinter widget)
Binding the left mouse button to a button instead of just giving the button a command
Passing the random module around and expecting it to evaluate to an integer
Returning strings to a button
Using an unconditional branching statement (if True:)
Masking a module name with a parameter name
Expecting the name random to refer to both the random module and a passed argument, at the same time
Making a recursive call in a function that already calls after
Leaving the button bound to a function that already schedules calls to itself with after, allowing you to schedule many calls
Using an if structure to choose a random string instead of using random.choice
Scheduling an after call with the result of a function call (display(random)) instead of the function itself
That's not necessarily a complete list.
The following fixes the above issues.
from tkinter import *
import random
def display():
strings = ("Be wise today so you don't cry tomorrow",
"Frustration is the result of failed expectations",
"Wishes are possibilities. Dare to make a wish")
button.config(text=random.choice(strings))
root.after(5000, display)
def click(event=None):
button.config(command='')
display()
root=Tk()
root.title("Wise Words")
root.geometry("500x180+360+30")
root.resizable(0,0)
root.call("wm", "attributes", ".", "-topmost", "1")
button = Button(root, fg="white", bg="blue", text="Click to start!",
font=("Tahoma", 20, "bold"), width=40, height=4,
wraplength=400, command=click)
button.pack()
root.mainloop()

Categories

Resources