{"id":24224396,"url":"https://github.com/serwy/tkthread","last_synced_at":"2025-08-09T02:22:21.152Z","repository":{"id":40257377,"uuid":"156326040","full_name":"serwy/tkthread","owner":"serwy","description":"Easy multithreading with Python's Tkinter","archived":false,"fork":false,"pushed_at":"2022-10-16T22:55:21.000Z","size":56,"stargazers_count":25,"open_issues_count":1,"forks_count":2,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-01-10T17:59:09.728Z","etag":null,"topics":["multithreading","threading","tkinter","tkinter-python"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/serwy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-11-06T04:29:26.000Z","updated_at":"2024-02-16T15:48:28.000Z","dependencies_parsed_at":"2022-06-27T07:23:50.208Z","dependency_job_id":null,"html_url":"https://github.com/serwy/tkthread","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/serwy%2Ftkthread","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/serwy%2Ftkthread/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/serwy%2Ftkthread/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/serwy%2Ftkthread/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/serwy","download_url":"https://codeload.github.com/serwy/tkthread/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":233856656,"owners_count":18740989,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["multithreading","threading","tkinter","tkinter-python"],"created_at":"2025-01-14T07:17:08.957Z","updated_at":"2025-01-14T07:17:10.161Z","avatar_url":"https://github.com/serwy.png","language":"Python","readme":"# tkthread\n\nEasy multithreading with Tkinter on CPython 2.7/3.x and PyPy 2.7/3.x.\n\n    import tkthread; tkthread.patch()   # do this before importing tkinter\n\n## Background\n\nThe Tcl/Tk language that comes with Python follows a different threading\nmodel than Python itself which can raise obtuse errors when mixing Python\nthreads with Tkinter, such as:\n\n    RuntimeError: main thread is not in main loop\n    RuntimeError: Calling Tcl from different apartment\n    NotImplementedError: Call from another thread\n\nTcl can have many isolated interpreters, and each are tagged to the\nits particular OS thread when created. Python's `_tkinter` module checks\nif the calling Python thread is different than the Tcl/Tk thread, and if so,\n[waits one second][WaitForMainloop] for the Tcl/Tk main loop to begin\ndispatching. If there is a timeout, a RuntimeError is raised. On PyPy,\na [NotImplementedError][PyPyNotImplemented] is raised.\n\nFor non-Tk calls into Tcl, Python will raise an apartment RuntimeError\nwhen calling a Tcl interpreter from a different thread.\n\nA common approach to avoid these errors involves using `.after` to set up\n[periodic polling][PollQueue] of a [message queue][PollRecipe] from\nthe Tcl/Tk main loop, which can slow the responsiveness of the GUI.\n\nThe initial approach used in `tkthread` is to use the Tcl/Tk `thread::send`\nmessaging to notify the Tcl/Tk main loop of a call for execution.\nThis interrupt-style architecture has lower latency and better\nCPU utilization than periodic polling. This works with CPython and PyPy.\n\nThe newer approach used in `tkthread` is to use `tkthread.tkinstall()`\nto patch Tkinter when make calls into Tcl/Tk. This only works on CPython and\nit does not require the `Thread` package in Tcl.\n\n## Usage on CPython (simplest)\n\nFor CPython 2.7/3.x, `tkthread.patch()` (same as `tkthread.tkinstall()`)\ncan be called first, and will patch Tkinter to re-route threaded calls to the\nTcl interpreter using the `willdispatch` internal API call.\n\n    import tkthread; tkthread.patch()\n    import tkinter as tk\n\n    root = tk.Tk()\n\n    import threading\n    def thread_run(func): threading.Thread(target=func).start()\n\n    @thread_run\n    def func():\n        root.wm_title(threading.current_thread())\n\n        @tkthread.main(root)\n        @tkthread.current(root)\n        def testfunc():\n            tk.Label(text=threading.current_thread()).pack()\n\n    root.mainloop()\n\n\n## Usage on CPython/PyPy (compatibility/legacy)\n\nThe `tkthread` module provides the `TkThread` class, which can\nsynchronously interact with the main thread.\n\n    from tkthread import tk, TkThread\n\n    root = tk.Tk()        # create the root window\n    tkt = TkThread(root)  # make the thread-safe callable\n\n    import threading, time\n    def run(func):\n        threading.Thread(target=func).start()\n\n    run(lambda:     root.wm_title('FAILURE'))\n    run(lambda: tkt(root.wm_title,'SUCCESS'))\n\n    root.update()\n    time.sleep(2)  # _tkinter.c:WaitForMainloop fails\n    root.mainloop()\n\nThe `tkt` instance is callable, and will wait for the Tcl/Tk main loop\nto execute and compute a result which is then passed back for\nreturn in the calling thread. A non-synchronous version also exists that\ndoes not block:\n\n    tkt.nosync(root.wm_title, 'ALSO SUCCESS')\n\nThere is an optional `tkt.install()` method which intercepts Python-to-Tk\ncalls. This must be called on the default root, before the creation of child\nwidgets. If installed, then wrapping Tk widget calls in threaded code with\n`tkt` is not necessary. There is, however, a slight performance penalty for\nTkinter widgets that operate only on the main thread because of the\nthread-checking indirection.\n\nThe `root` Tcl/Tk interpreter must be the primary interpreter on the\nmain thread. If it is not, then you will receive a TclError of the form:\n\n    _tkinter.TclError: invalid command name \"140520536224520_call_from\"\n\nFor example, creating several `Tk()` instances and then using TkThread\non those will cause this error.\n\nA good practice is to create a root window and then call `root.withdraw()`\nto keep the primary Tcl/Tk interpreter active. Future Toplevel windows\nuse `root` as the master.\n\n## Install\n\n    pip install tkthread\n\n## API\n\n- `tkthread.patch()`\n    - patch Tkinter to support multi-threading.\n- `tkthread.tkinstall()`\n    - same as `.patch()`\n- `tkthread.call(func, *args, **kw)`\n    - call `func` on the main thread, with arguments and keyword arguments.\n    - waits for return value (or raises error)\n- `tkthread.call_nosync(func, *args, **kw)`\n    - call `func` on the main thread, with arguments and keyword arguments.\n    - returns immediately, ignore return `func` return or error.\n- `@tkthread.called_on_main`\n    - decorator to dispatch the function call on the main thread from the calling thread.\n- `@tkthread.main()`\n    - decorator to call a function immediately on the main thread.\n- `@tkthread.current()`\n    - decorator to call a function immediately on the current thread.\n- `TkThread(root)`\n    - class to dispatch thread calls to Tk using `thread::send`\n\n## Known (and solved) Error Messages\n\nYou may receive this error when using `tkthread.TkThread(root)`:\n\n    _tkinter.TclError: can't find package Thread\n\nThis means that Python's Tcl/Tk libraries do not include the `Thread` package,\nwhich is needed by `TkThread`.\n\nOn Debian/Ubuntu:\n\n    apt install tcl-thread\n\nOn Windows, you'll need to manually update your Tcl installation to include\nthe `Thread` package.\n\nThe simpler solution is to use `tkthread.patch()` instead.\n\n\nWhen using Matplotlib, you may receive a warning message that can be ignored:\n\n    UserWarning: Starting a Matplotlib GUI outside of the main thread will likely fail.\n\nThe `demo/mpl_plot.py` script shows an example with this message.\n\n## License\n\nLicensed under the Apache License, Version 2.0 (the \"License\")\n\n## See Also\n\nThese libraries offer similar functionality, using periodic polling:\n* https://github.com/RedFantom/mtTkinter\n* https://github.com/abarnert/mttkinter\n* https://pypi.org/project/threadsafe-tkinter\n\n[PollQueue]: http://effbot.org/zone/tkinter-threads.htm\n[PollRecipe]: https://www.oreilly.com/library/view/python-cookbook/0596001673/ch09s07.html\n[WaitForMainloop]: https://github.com/python/cpython/blob/38df97a03c5102e717a110ab69bff8e5c9ebfd08/Modules/_tkinter.c#L342\n[PyPyNotImplemented]: https://bitbucket.org/pypy/pypy/src/d19ac6eec77b4e1859ab3dd8a5843989c4d4df99/lib_pypy/_tkinter/app.py?fileviewer=file-view-default#app.py-281\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fserwy%2Ftkthread","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fserwy%2Ftkthread","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fserwy%2Ftkthread/lists"}