I want the button to start the command, then be disabled while executing and enabled again after the execution finished.
When I click the button, it appears to be disabled and the command is executed. But when I click the button while it is disabled, the command is executed a second time after it finished the first execution.
It seems like after the second click, the button is really disabled because I can click several times while it's disabled and it's only repeated once.
import tkinter as tk
import time
class Button:
def __init__(self, master):
frame=tk.Frame(master)
frame.pack()
self.button1=tk.Button(frame, text="Ready",bg="green", fg="white", command=self.click)
self.button1.pack()
def click(self):
self.button1.config(bg="red", text="Busy", state="disabled")
self.button1.update()
doSth()
self.button1.config(bg="green", fg="white", text="Ready", state="normal")
self.button1.update()
def doSth():
time.sleep(3)
print("done")
root = tk.Tk()
b = Button(root)
root.mainloop()
When you click the button during the sleep, you queue a button click to be processed in the next update cycle. During the sleep tkinter doesn't update. After the sleep, you change the button back to normal state before the function returns and the click is processed. Since the button is active again, click is called again.
You can counter this by letting tkinter update before you activate the button again, this gets rid of any queued click events while the button still is deactivated.
def click(self):
self.button1.config(bg="red", text="Busy", state="disabled")
self.button1.update()
doSth()
self.button1.update()
self.button1.config(bg="green", fg="white", text="Ready", state="normal")
Related
I'm using tkinter to create a window with a button, the button's command is to destroy the window and create a similar new one on click, but the moment you run the program it automatically executes the button command which leads to a spam of windows creation and destruction
from tkinter import *
import random
import tkinter as tk
j=0
def func(j):
f=Tk()
f.title("game")
f.geometry("400x350")
f["bg"]="black"
#message
Label(f,bg="black",fg="white",height=2,width=20,text=" ").pack()
Label(f,bg="black",fg="white",height=3,width=20,text="click continue to proceed\n to main menu",font=("Arial",13)).pack()
#frame
global d
d=Frame(f,bg="white",bd=1)
d.pack(fill="both",expand=True,side="bottom")
print(d.__format__)
#cancel button
def canccel():
f.destroy()
c=Canvas(d,bg="white")
b1=Button(c,bg="white",text="cancel",command=canccel)
b1.pack()
c.pack(padx=90, pady=22,side=tk.LEFT)
#play button
if j==0:
k=Canvas(d,bg="white")
b2=Button(k,bg="red",text="continue!",command= [print("hehe"),func(-1)])
b2.pack()
k.pack(padx=15,pady=0,side=LEFT)
else:
j=random.randrange(0,400)
i=random.randrange(0,300)
k=Canvas(d,bg="white")
b2=Button(k,bg="white",text="continue!",command= [print("hehe"),func(j)])
b2.pack()
k.pack(padx=i,pady=j,side=LEFT)
f.mainloop()
func(j)
I'm trying to make a button where it will start a program. So first, a button called 'Run' will appear, subsequently after 3 seconds it should come up with a new button that says 'Stop'.
The reason I want it that way. That it's because I have tried and add two buttons on an interface panel, but the problem is then, every time I run an application, the interface freezes, so it was not possible to 'Stop' the program. So, I was wondering if it would be possible to do something like that?
What I've done:
from tkinter import *
tkWindow = Tk()
tkWindow.geometry('150x50')
tkWindow.title('Tkinter Example')
print("Tkinter button is appearing...")
def Action():
from Launch import Launch
run = Launch()
run
def Off():
import sys
sys.exit()
button = Button(tkWindow,
text='Start',
command=Action)
button1 = Button(tkWindow,
text='Stop',
command=Off)
button.pack()
button1.pack()
tkWindow.mainloop()
Try something like this:
from tkinter import *
from threading import Thread
from Launch import Launch
tkWindow = Tk()
tkWindow.geometry('150x50')
tkWindow.title('Tkinter Example')
print("Tkinter button is appearing...")
def Action():
thread = Thread(target=Launch, daemon=True)
thread.start()
# If you want to disable the button use:
# button.config(state="disabled")
button = Button(tkWindow,
text='Start',
command=Action)
# Here I am going to use the built-in `exit` function as per #Matiiss' suggestion
button1 = Button(tkWindow,
text='Stop',
command=exit)
button.pack()
button1.pack()
tkWindow.mainloop()
It starts a new thread when the "Start" button is pressed. That new thread calls Launch, leaving the main thread for tkinter. Please make sure that there isn't any references to your main GUI in your Launch function.
I have a simple program with start and exit buttons. The start button makes a notification using win10toast, but the button remains visibly pressed down and the window becomes unresponsive. The exit button works fine before the start button is pressed. Here's my code:
from tkinter import *
from win10toast import ToastNotifier
root = Tk()
def exit_p():
exit()
def new():
hr.show_toast("New", "Alert")
return
#creates a label widget
myLabel1 = Label(root, text="Full Moon Notification!")
myLabel2 = Label(root, text="Here you can start and exit the program")
button1 = Button(root, text="Start",padx=50,command=new).grid(row=3,column=0)
button2 = Button(root, text="Exit", padx=50,command=exit_p).grid(row=4,column=0)
#puts the widget on the screen
myLabel1.grid(row=0,column=0)
myLabel2.grid(row=1,column=0)
#loop to keep program running
root.mainloop()
The issue is likely because hr.show_toast("New", "Alert") blocks.
The win10toast library conveniently provides an option threaded=True, so just change that code to
hr.show_toast("New", "Alert", threaded=True)
should make it work.
Basically, I am currently writing a python program (technically its a game) in which a certain (TKinter Widget) button should only be able to be clicked every 1 second. here is an example of what I mean:
import time
from tkinter import *
def button_click():
button["state"] = DISABLED
print("button clicked! Please wait 1 second...")
time.sleep(1)
button["state"] = NORMAL
root = Tk()
button = Button(root, text="Click Me!", command=button_click)
button.pack() #Please Dont Tell Me Not To Use Pack() ; I Use Place()
So anyways, for example, when running this program, if I just keep clicking the button, it will increase the count by 1 each second. instead, I would like it to not count all the clicks that happen between the first click, and 1 second after that.
It is because the tkinter does not take control when the button is disabled, so it is not updated. You need to call, for example, button.update() after it is disabled to force the update:
def button_click():
button["state"] = DISABLED
button.update() # force the update
print("button clicked! Please wait 1 second...")
time.sleep(1)
button["state"] = NORMAL
However, it is better to use after() instead of time.sleep():
def button_click():
button["state"] = DISABLED
print("button clicked! Please wait 1 second...")
# enable the button after one second
button.after(1000, lambda: button.config(state='normal'))
Maybe you forgot the "root.mainloop" at the end of the code.
import time
from tkinter import *
def button_click():
button["state"] = DISABLED
print("button clicked! Please wait 1 second...")
time.sleep(1)
button["state"] = NORMAL
root = Tk()
button = Button(root, text="Click Me!", command=button_click)
button.pack()
root.mainloop()
This works for me. You can only press the button every 1 second.
How can I spawn a window, and halt the execution of the GUI until this window is closed by the user?
That's exactly what functions from the tkinter.messagebox submodule will do.
These will spawn a dialog, and halt the execution until closed.
For instance, the showinfo function will spawn a window with the first parameter as title and the second as message.
Until the window is closed, the remaining of the GUI will not be interactable.
Here is an example demonstrating this.
import tkinter as tk
import tkinter.messagebox as tkmb
root = tk.Tk()
button = tk.Button(
root,
text="Spawn a dialog",
command=lambda: tkmb.showinfo(
"Information",
"Please close this window or press OK to continue"))
button.pack()
root.mainloop()
When the button is clicked, a window spawns.
As long as this window is open, the button will not be clickable once again.