Threading and Tk don't mix well, as they violate the Tcl/Tk threading model of using Tk just from one thread and your code doesnt work, because you occupied the main (one and only) thread!
While your print-functions keeps printing - GUI is unresponsible because of that and you can check it easily with another print
in your code:
>>> print('**** Active threads: %d ****' % threading.active_count())
**** Active threads: 1 ****
So what you really need is just create another one!
try:
import tkinter as tk
from tkinter import messagebox as msgbox
except ImportError:
import Tkinter as tk
import TkMessageBox as msgbox
import threading
def setInterval(func,time,args):
e = threading.Event()
while not e.wait(time):
print('**** Active threads: %d ****' % threading.active_count())
func(args)
def foo(data):
print(data)
aa("what")
def aa(a):
print(a)
root = tk.Tk()
print('**** Active threads: %d ****' % threading.active_count())
thread = threading.Thread(target=setInterval, args=(foo, 5, "fsrty"))
msgbox.showinfo("Regret", "nope")
thread.start()
root.mainloop()
But here's another problem - threads keep running even after your close GUI (when you escaped mainloop
)! So if you trying to achieve something simple - .after()
is an option (quick and small alternative).
But if you stubborny or really need this - create (override) a custom subclass of the thread object, it's gives you more flexibility to control your flow!
try:
import tkinter as tk
from tkinter import messagebox as msgbox
except ImportError:
import Tkinter as tk
import TkMessageBox as msgbox
import threading
class Thread(threading.Thread):
def __init__(self):
super(Thread, self).__init__()
self.running = False
self.function_to_execute = None
self._stop = threading.Event()
def start_thread(self, func, *args, **kwargs):
self.function_to_execute = (func, args, kwargs)
self.running = True
self.start()
def run(self):
print('### STARTED ###')
while self.running:
try:
print('### RUNNING ###')
function, args, kwargs = self.function_to_execute
function(*args, **kwargs)
except:
self.stop()
def stop(self):
print('### STOPPING ###')
self.running = False
self._stop.set()
def setInterval(func, time, args):
e = threading.Event()
e.wait(time)
print('**** Active threads: %d ****' % threading.active_count())
func(args)
def foo(data):
print(data)
aa('what')
def aa(a):
print(a)
def clear():
thread.stop()
while True:
try:
thread.is_alive()
except TypeError:
root.destroy()
print('### STOPPED ###')
print('**** Active threads: %d ****' % threading.active_count())
break
root = tk.Tk()
thread = Thread()
print('**** Active threads: %d ****' % threading.active_count())
msgbox.showinfo("Regret", "nope")
thread.start_thread(setInterval, foo, 5, 'fsrty')
root.protocol('WM_DELETE_WINDOW', clear)
root.mainloop()
As you see - things are getting more complicated when we trying to clear out before exiting, but it's works!
Conclusion:
- For simple things -
.after()
is a good option!
- For complicated and long-executed things -
threading
is your choise!
Links: