Package: tkthread
tkthreadEasy multithreading with Tkinter on CPython 2.7/3.x and PyPy 2.7/3.x.import tkthread; tkthread.patch()   # do this before importing tkinterBackgroundThe Tcl/Tk language that comes with Python follows a different threading
model than Python itself which can raise obtuse errors when mixing Python
threads with Tkinter, such as:RuntimeError: main thread is not in main loop
RuntimeError: Calling Tcl from different apartment
NotImplementedError: Call from another threadTcl can have many isolated interpreters, and each are tagged to the
its particular OS thread when created. Python's_tkintermodule checks
if the calling Python thread is different than the Tcl/Tk thread, and if so,waits one secondfor the Tcl/Tk main loop to begin
dispatching. If there is a timeout, a RuntimeError is raised. On PyPy,
aNotImplementedErroris raised.For non-Tk calls into Tcl, Python will raise an apartment RuntimeError
when calling a Tcl interpreter from a different thread.A common approach to avoid these errors involves using.afterto set upperiodic pollingof amessage queuefrom
the Tcl/Tk main loop, which can slow the responsiveness of the GUI.The initial approach used intkthreadis to use the Tcl/Tkthread::sendmessaging to notify the Tcl/Tk main loop of a call for execution.
This interrupt-style architecture has lower latency and better
CPU utilization than periodic polling. This works with CPython and PyPy.The newer approach used intkthreadis to usetkthread.tkinstall()to patch Tkinter when make calls into Tcl/Tk. This only works on CPython and
it does not require theThreadpackage in Tcl.Usage on CPython (simplest)For CPython 2.7/3.x,tkthread.patch()(same astkthread.tkinstall())
can be called first, and will patch Tkinter to re-route threaded calls to the
Tcl interpreter using thewilldispatchinternal API call.import tkthread; tkthread.patch()
import tkinter as tk

root = tk.Tk()

import threading
def thread_run(func): threading.Thread(target=func).start()

@thread_run
def func():
    root.wm_title(threading.current_thread())

    @tkthread.main(root)
    @tkthread.current(root)
    def testfunc():
        tk.Label(text=threading.current_thread()).pack()

root.mainloop()Usage on CPython/PyPy (compatibility/legacy)Thetkthreadmodule provides theTkThreadclass, which can
synchronously interact with the main thread.from tkthread import tk, TkThread

root = tk.Tk()        # create the root window
tkt = TkThread(root)  # make the thread-safe callable

import threading, time
def run(func):
    threading.Thread(target=func).start()

run(lambda:     root.wm_title('FAILURE'))
run(lambda: tkt(root.wm_title,'SUCCESS'))

root.update()
time.sleep(2)  # _tkinter.c:WaitForMainloop fails
root.mainloop()Thetktinstance is callable, and will wait for the Tcl/Tk main loop
to execute and compute a result which is then passed back for
return in the calling thread. A non-synchronous version also exists that
does not block:tkt.nosync(root.wm_title, 'ALSO SUCCESS')There is an optionaltkt.install()method which intercepts Python-to-Tk
calls. This must be called on the default root, before the creation of child
widgets. If installed, then wrapping Tk widget calls in threaded code withtktis not necessary. There is, however, a slight performance penalty for
Tkinter widgets that operate only on the main thread because of the
thread-checking indirection.TherootTcl/Tk interpreter must be the primary interpreter on the
main thread. If it is not, then you will receive a TclError of the form:_tkinter.TclError: invalid command name "140520536224520_call_from"For example, creating severalTk()instances and then using TkThread
on those will cause this error.A good practice is to create a root window and then callroot.withdraw()to keep the primary Tcl/Tk interpreter active. Future Toplevel windows
userootas the master.Installpip install tkthreadAPItkthread.patch()patch Tkinter to support multi-threading.tkthread.tkinstall()same as.patch()tkthread.call(func, *args, **kw)callfuncon the main thread, with arguments and keyword arguments.waits for return value (or raises error)tkthread.call_nosync(func, *args, **kw)callfuncon the main thread, with arguments and keyword arguments.returns immediately, ignore returnfuncreturn or error.@tkthread.called_on_maindecorator to dispatch the function call on the main thread from the calling thread.@tkthread.main()decorator to call a function immediately on the main thread.@tkthread.current()decorator to call a function immediately on the current thread.TkThread(root)class to dispatch thread calls to Tk usingthread::sendKnown (and solved) Error MessagesYou may receive this error when usingtkthread.TkThread(root):_tkinter.TclError: can't find package ThreadThis means that Python's Tcl/Tk libraries do not include theThreadpackage,
which is needed byTkThread.On Debian/Ubuntu:apt install tcl-threadOn Windows, you'll need to manually update your Tcl installation to include
theThreadpackage.The simpler solution is to usetkthread.patch()instead.When using Matplotlib, you may receive a warning message that can be ignored:UserWarning: Starting a Matplotlib GUI outside of the main thread will likely fail.Thedemo/mpl_plot.pyscript shows an example with this message.LicenseLicensed under the Apache License, Version 2.0 (the "License")See AlsoThese libraries offer similar functionality, using periodic polling:https://github.com/RedFantom/mtTkinterhttps://github.com/abarnert/mttkinterhttps://pypi.org/project/threadsafe-tkinter