im working on a selenium project using tkinter in python. I want to execute many selenium task at one time(could be up to 100), so I used threading to accomplish this goal, but I came to a problem, due to the fact that I need all individual selenium task to wait a couple seconds between instructions, I used the '.after' method but for some reason my program is still freezing up, I've done a lot of research but I cant seem to find the answer yet. Isn't there a way to use tkinter, threading and some sort of sleeping in python? Do I need to switch to multiprocessing? but I would need all selenium processes to finish within the same 2 minutes and each process takes about a minute.(e.g lets say i start 100, 1 minute processes at 6:00 all task would need to be finished by 6:02)
I created minimal code which mimics my selenium script, so its easier to read, here it is:
from tkinter import *
import tkinter as tk
import time
root = tk.Tk()
root.geometry('700x700')
import threading
class Make:
def __init__(self,num):
self.num = num.get()
Label(root,text='HELLO WORLD WILL PRINT: '+str(self.num)+' times, press go to confirm').pack()
Button(root, text='go', command=lambda: self.starting()).pack()
def starting(self):
for count in range(self.num):
t = threading.Thread(target=gogogo())
t.start()
def gogogo():
tk.Label(root,text='HELLO WORLD').pack()
root.after(2000,print('Please wait 2 seconds'))
tk.Label(root,text='You are inside my world').pack()
Label(root,text='How many times should I print: HELLO WORLD').pack()
num = IntVar()
Entry(root, textvariable=num).pack()
Button(root, text='Start', command=lambda: Make(num)).pack()
root.mainloop()
You main problem is that after() and Thread() similar to command= needs only function's name without () and it will later use () to execute it.
Other problem is that after() doesn't stop code but it only send information to mainloop to execute function after 2000ms and later Python runs at once next line after after(). You have to add Label in fucntion executed by after()
def gogogo():
tk.Label(root, text='HELLO WORLD').pack()
print('Please wait 2 seconds')
root.after(2000, next_message) # function's name without ()
def next_message():
tk.Label(root, text='You are inside my world').pack()
# from tkinter import * # PEP8: `import *` is not preferred
import tkinter as tk
import threading
import time
class Make:
def __init__(self, num):
self.num = num.get()
text = 'HELLO WORLD WILL PRINT: {} times, press go to confirm'.format(self.num)
tk.Label(root, text=text).pack()
tk.Button(root, text='go', command=self.starting).pack()
def starting(self):
for count in range(self.num):
t = threading.Thread(target=gogogo) # function's name without ()
t.start()
def gogogo():
tk.Label(root, text='HELLO WORLD').pack()
print('Please wait 2 seconds')
root.after(2000, next_message) # function's name without ()
def next_message():
tk.Label(root, text='You are inside my world').pack()
# --- main ---
root = tk.Tk()
root.geometry('700x700')
tk.Label(root, text='How many times should I print: HELLO WORLD').pack()
num = tk.IntVar()
tk.Entry(root, textvariable=num).pack()
tk.Button(root, text='Start', command=lambda:Make(num)).pack()
root.mainloop()
EDIT: Because gogogo() runs in separated thread so you can also use time.sleep() because it doesn't block mainloop() in main thread
def gogogo():
tk.Label(root, text='HELLO WORLD').pack()
print('Please wait 2 seconds')
time.sleep(2)
tk.Label(root, text='You are inside my world').pack()
I'm trying to update my cursor while my program is busy.
This snippet works:
import tkinter as tk
def button():
root.configure(cursor="watch")
root = tk.Tk()
root.geometry("300x500")
button_1 = tk.Button(master=root,command=button,width=10)
button_1.grid()
root.mainloop()
When I click the button the cursor changes
But this snippet fails:
import tkinter as tk
def button():
root.configure(cursor="watch")
input("Force a pause")
root = tk.Tk()
root.geometry("300x500")
button_1 = tk.Button(master=root,command=button,width=10)
button_1.grid()
root.mainloop()
It only updates the cursor if I make another window active (or after entering some dummy input)
I've tried adding
root.configure(cursor="watch")
root.update()
but it still doesn't work (and anyway the tk man says it's a bad idea to put an update() in a callback)
Any suggestions would be welcome.
Thanks for your time.
Your code update the cursor but it's only done after your busy process is terminated.
So you can execute your busy process in a thread to prevent the user interface to freeze.
import tkinter as tk
import threading
def worker():
for x in range(0, 100000):
print(x)
root.config(cursor="arrow")
def button():
root.config(cursor="watch")
threading.Thread(target=worker).start()
root = tk.Tk()
root.geometry("300x500")
root.config(cursor="arrow")
button_1 = tk.Button(master=root, command=button, width=10)
button_1.grid()
root.mainloop()
Lets say I have two python files. Both with an GUI. First is "Main" second is "Calculator". From Main I will start Calculator. So I have to import calculator. In Calculator I will do a calculation. Lets keep I easy an say 1+1=2. Now I want to "send" this Result to an Text in Main.
How do I do that without an circular import? I cant find an good tutorial/example for that!
My code so far:
Main:
from tkinter import *
import Test_2
window = Tk()
window.title("First Window")
def start():
Test_2.start_second()
Input1 = Entry(window)
Input1.grid(row=0,column=0, padx=10, pady=5)
Start = Button(window,text="Start", command=start)
Start.grid(row=1,column=0, padx=10, pady=5)
window.mainloop()
Second:
from tkinter import *
def start_second():
window2 = Tk()
window2.title("Second Window")
def send():
x = Input.get()
Input2 = Entry(window2)
Input2.grid(row=0,column=0, padx=10, pady=5)
Send = Button(window2,text="Send", command=send)
Send.grid(row=1,column=0, padx=10, pady=5)
window2.mainloop()
This code does exactly what you asked for (as opposed to what I suggested in the comment; but anyway, you either get a value from a module function or you send a reference for it to alter)
I tried to follow your structure.
Basically it is a matter of sending the parent window and the first entry as parameters to the second window creation function. Don't call mainloop two times, just once in the end, and use Toplevel for all other windows after the main Tk one. This is not to say that I like the use of an inner function and of the lambda, for readability, but lambdas are necessary in tkinter everytime you want to send parameters to a command callback, otherwise it will get called right way in command definition.
tkinter_w1.py (your main.py)
from tkinter import Tk, ttk
import tkinter as tk
from tkinter_w2 import open_window_2
root = Tk()
entry1 = ttk.Entry(root)
button1 = ttk.Button(root, text='Open Window 2',
command=lambda parent=root, entry=entry1:open_window_2(parent, entry))
entry1.pack()
button1.pack()
root.mainloop()
tkinter_w2.py (your Test_2.py)
from tkinter import Tk, ttk, Toplevel
import tkinter as tk
def open_window_2(parent, entry):
def send():
entry.delete(0,tk.END)
entry.insert(0,entry2.get())
window2 = Toplevel(parent)
entry2 = ttk.Entry(window2)
button2 = ttk.Button(window2, text='Send', command=send)
entry2.pack()
button2.pack()
I am using python 3.
If I opan an error messagebox, i get two frames, one is emty and one is the error-window. That is my code:
from tkinter import messagebox
messagebox.showwarning('warning', 'warning')
Everything works correctly in your example. The empty window is the main window of Tk. It is always open when you start any Tk program. You can minimize it if you want, but closing it terminates the main loop.
Try this:
root = tkinter.Tk()
root.withdraw()
messagebox.showwarning('warning', 'warning')
Thank you DYZ,
in my code is no main window, (eg.: main = Tk() ... main.mainloop), because of that the warning massage create one. I could solve the problem by create one and minimize it. at the end of massagebox I destroyed it to continue in code.
from tkinter import *
from tkinter import messagebox
main = Tk()
main.geometry("500x400+300+300")
def message():
main.geometry("0x0")
messagebox.showwarning("Say Hello", "Hello World")
main.destroy()
B1 = Button(main, text = "Start Dialog",fg="dark green", command = message)
B1.pack()
main.mainloop()
print("finish dialog")
How do I end a Tkinter program? Let's say I have this code:
from Tkinter import *
def quit():
# code to exit
root = Tk()
Button(root, text="Quit", command=quit).pack()
root.mainloop()
How should I define the quit function to exit my application?
You should use destroy() to close a Tkinter window.
from Tkinter import *
#use tkinter instead of Tkinter (small, not capital T) if it doesn't work
#as it was changed to tkinter in newer Python versions
root = Tk()
Button(root, text="Quit", command=root.destroy).pack() #button to close the window
root.mainloop()
Explanation:
root.quit()
The above line just bypasses the root.mainloop(), i.e., root.mainloop() will still be running in the background if quit() command is executed.
root.destroy()
While destroy() command vanishes out root.mainloop(), i.e., root.mainloop() stops. <window>.destroy() completely destroys and closes the window.
So, if you want to exit and close the program completely, you should use root.destroy(), as it stops the mainloop() and destroys the window and all its widgets.
But if you want to run some infinite loop and don't want to destroy your Tkinter window and want to execute some code after the root.mainloop() line, you should use root.quit(). Example:
from Tkinter import *
def quit():
global root
root.quit()
root = Tk()
while True:
Button(root, text="Quit", command=quit).pack()
root.mainloop()
#do something
See What is the difference between root.destroy() and root.quit()?.
def quit()
root.quit()
or
def quit()
root.destroy()
import tkinter as tk
def quit(root):
root.destroy()
root = tk.Tk()
tk.Button(root, text="Quit", command=lambda root=root:quit(root)).pack()
root.mainloop()
Illumination in case of confusion...
def quit(self):
self.destroy()
exit()
A) destroy() stops the mainloop and kills the window, but leaves python running
B) exit() stops the whole process
Just to clarify in case someone missed what destroy() was doing, and the OP also asked how to "end" a tkinter program.
I think you wrongly understood the quit function of Tkinter. This function does not require you to define.
First, you should modify your function as follows:
from Tkinter import *
root = Tk()
Button(root, text="Quit", command=root.quit).pack()
root.mainloop()
Then, you should use '.pyw' suffix to save this files and double-click the '.pyw' file to run your GUI, this time, you can end the GUI with a click of the Button, and you can also find that there will be no unpleasant DOS window. (If you run the '.py' file, the quit function will fail.)
The usual method to exit a Python program:
sys.exit()
(to which you can also pass an exit status) or
raise SystemExit
will work fine in a Tkinter program.
In case anyone wants to bind their Escape button to closing the entire GUI:
master = Tk()
master.title("Python")
def close(event):
sys.exit()
master.bind('<Escape>',close)
master.mainloop()
The easiest way would be to click the red button (leftmost on macOS and rightmost on Windows).
If you want to bind a specific function to a button widget, you can do this:
class App:
def __init__(self, master)
frame = Tkinter.Frame(master)
frame.pack()
self.quit_button = Tkinter.Button(frame, text = 'Quit', command = frame.quit)
self.quit_button.pack()
Or, to make things a little more complex, use protocol handlers and the destroy() method.
import tkMessageBox
def confirmExit():
if tkMessageBox.askokcancel('Quit', 'Are you sure you want to exit?'):
root.destroy()
root = Tk()
root.protocol('WM_DELETE_WINDOW', confirmExit)
root.mainloop()
you only need to type this:
root.destroy()
and you don't even need the quit() function cause when you set that as commmand it will quit the entire program.
you dont have to open up a function to close you window, unless you're doing something more complicated:
from Tkinter import *
root = Tk()
Button(root, text="Quit", command=root.destroy).pack()
root.mainloop()
In idlelib.PyShell module, root variable of type Tk is defined to be global
At the end of PyShell.main() function it calls root.mainloop() function which is an infinite loop and it runs till the loop is interrupted by root.quit() function. Hence, root.quit() will only interrupt the execution of mainloop
In order to destroy all widgets pertaining to that idlelib window, root.destroy() needs to be called, which is the last line of idlelib.PyShell.main() function.
I normally use the default tkinter quit function, but you can do your own, like this:
from tkinter import *
from tkinter.ttk import *
window = Tk()
window.geometry('700x700') # 700p x 700p screen
def quit(self):
proceed = messagebox.askyesno('Quit', 'Quit?')
proceed = bool(proceed) # So it is a bool
if proceed:
window.quit()
else:
# You don't really need to do this
pass
btn1 = Button(window, text='Quit', command=lambda: quit(None))
window.mainloop()
For menu bars:
def quit():
root.destroy()
menubar = Menu(root)
filemenu = Menu(menubar, tearoff=0)
filemenu.add_separator()
filemenu.add_command(label="Exit", command=quit)
menubar.add_cascade(label="menubarname", menu=filemenu)
root.config(menu=menubar)
root.mainloop()
I use below codes for the exit of Tkinter window:
from tkinter import*
root=Tk()
root.bind("<Escape>",lambda q:root.destroy())
root.mainloop()
or
from tkinter import*
root=Tk()
Button(root,text="exit",command=root.destroy).pack()
root.mainloop()
or
from tkinter import*
root=Tk()
Button(root,text="quit",command=quit).pack()
root.mainloop()
or
from tkinter import*
root=Tk()
Button(root,text="exit",command=exit).pack()
root.mainloop()
Code snippet below. I'm providing a small scenario.
import tkinter as tk
from tkinter import *
root = Tk()
def exit():
if askokcancel("Quit", "Do you really want to quit?"):
root.destroy()
menubar = Menu(root, background='#000099', foreground='white',
activebackground='#004c99', activeforeground='white')
fileMenu = Menu(menubar, tearoff=0, background="grey", foreground='black',
activebackground='#004c99', activeforeground='white')
menubar.add_cascade(label='File', menu=fileMenu)
fileMenu.add_command(label='Exit', command=exit)
root.config(bg='#2A2C2B',menu=menubar)
if __name__ == '__main__':
root.mainloop()
I have created a blank window here & add file menu option on the same window(root window), where I only add one option exit.
Then simply run mainloop for root.
Try to do it once
Of course you can assign the command to the button as follows, however, if you are making a UI, it is recommended to assign the same command to the "X" button:
def quit(self): # Your exit routine
self.root.destroy()
self.root.protocol("WM_DELETE_WINDOW", self.quit) # Sets the command for the "X" button
Button(text="Quit", command=self.quit) # No ()
There is a simple one-line answer:
Write - exit() in the command
That's it!