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.
Related
here is my sample code:
from time import sleep
import tkinter as tk
import threading
class Action:
counter = 0
def do_something(self):
while True:
print('Looping')
sleep(5)
action = Action()
root = tk.Tk()
button = tk.Button(root, text='pressme harder', command=threading.Thread(target=action.do_something()).start())
button.grid(row=1, column=0)
root.mainloop()
What am I expecting?
I'm expecting that as soon as I click the button in the UI an new thread is running, which is looping in the background and does not interfere with the UI (or later maybe other threads doing tasks in the background)
What is really happening?
When running the code, the method of the class is executeds immdiately and locking the procedure. root.mainloop() is never reached and therefore no UI is drawn
Alternatively I tried the following change:
button = tk.Button(root, text='pressme harder', command=threading.Thread(target=lambda: action.do_something()).start())
This behaves in the following (imho wrong) way:
The method is also called immediately, without pressing the button. This time the UI is drawn but seems the be locked by the thread (UI is slow/stuttering, pressing the buttom does not work most of the time)
Any Idea whats wrong there? Or how do I handle this in a more stable way?
You shouldn't try to start a thread directly in the button command. I suggest you create another function that launches the thread.
from time import sleep
import tkinter as tk
import threading
class Action:
counter = 0
def do_something(self):
while True:
print('Looping')
sleep(2)
print("Finished looping")
def start_thread(self):
thread = threading.Thread(target=self.do_something, daemon=True)
thread.start()
action = Action()
root = tk.Tk()
button = tk.Button(root, text='pressme harder', command=action.start_thread)
button.grid(row=1, column=0)
root.mainloop()
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.
There have already been several topics on Python/Tkinter, but I did not find an answer in them for the issue described below.
The two Python scripts below are reduced to the bare essentials to keep it simple. The first one is a simple Tkinter window with a button, and the script needs to wait till the button is clicked:
from tkinter import *
windowItem1 = Tk()
windowItem1.title("Item1")
WaitState = IntVar()
def submit():
WaitState.set(1)
print("submitted")
button = Button(windowItem1, text="Submit", command=submit)
button.grid(column=0, row=1)
print("waiting...")
button.wait_variable(WaitState)
print("done waiting.")
windowItem1.mainloop()
This works fine, and we see the printout “done waiting” when the button is clicked.
The second script adds one level: we first have a menu window, and when clicking the select button of the first presented item, we have a new window opening with the same as above. However, when clicking the submit button, I don’t get the “Done waiting”. I’m stuck on the wait_variable.
from tkinter import *
windowMenu = Tk()
windowMenu.title("Menu")
def SelectItem1():
windowItem1 = Tk()
windowItem1.title("Item1")
WaitState = IntVar()
def submit():
WaitState.set(1)
print("submitted")
button = Button(windowItem1, text="Submit", command=submit)
button.grid(column=0, row=1)
print("waiting...")
button.wait_variable(WaitState)
print("done waiting")
lblItem1 = Label(windowMenu, text="Item 1 : ")
lblItem1.grid(column=0, row=0)
btnItem1 = Button(windowMenu, text="Select", command=SelectItem1)
btnItem1.grid(column=1, row=0)
windowMenu.mainloop()
Can you explain it?
Inside your SelectItem1 function, you do windowItem1 = Tk(). You shouldn't use Tk() to initialize multiple windows in your application, the way to think about Tk() is that it creates a specialized tkinter.Toplevel window that is considered to be the main window of your entire application. Creating multiple windows using Tk() means multiple main windows, and each one would need its own mainloop() invokation, which is... yikes.
Try this instead:
windowItem1 = Toplevel()
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")
I created a tkinter Toplevel window for my application and later in the program destroyed it but after destroying the window the program doesnt get executed further and get struck there itself doing nothing . Here is the code that I used :-
#login.py
from tkinter import *
class gui:
def __init__(self):
#does something
def login(self):
self.winLogin.destroy()
def guilogin(self):
self.winLogin = Toplevel()
btn = Button(self.winLogin,command=self.login,text='asd')
btn.pack()
self.winLogin.mainloop()
#main.py
import login
from tkinter import *
main = Tk()
a = login.gui()
a.guilogin()
if True:
#some code and this part doesnot get executed
main.mainloop()
else:
main.destroy()
I run main.py file and the code get struck and do nothing before the if part . I tottaly have no idea whats wrong . Pls. Help!
As furas said in the comments, you should not call mainloop on the toplevel, instead use grab_set to disable the main window and wait_window to wait for the toplevel to be closed:
from tkinter import Tk, Toplevel, Button
def login():
top = Toplevel(root)
Button(top, text="Quit", command=top.destroy).pack()
top.grab_set() # deactivate the main GUI while top is opened
root.wait_window(top) # wait for top to be closed before doing the rest
print("logged in")
root = Tk()
Button(root, text="login", command=login).pack()
root.mainloop()