{ "info": { "author": "Roger D. Serwy", "author_email": "roger.serwy@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Operating System :: OS Independent", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Software Development :: User Interfaces" ], "description": "# tkthread\n\nEasy multithreading with Tkinter on CPython 2.7/3.x and PyPy 2.7/3.x.\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 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.\n\n## Usage\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## 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", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "http://github.com/serwy/tkthread", "keywords": "tkinter threading", "license": "Apache Version 2.0", "maintainer": "", "maintainer_email": "", "name": "tkthread", "package_url": "https://pypi.org/project/tkthread/", "platform": "Windows", "project_url": "https://pypi.org/project/tkthread/", "project_urls": { "Homepage": "http://github.com/serwy/tkthread" }, "release_url": "https://pypi.org/project/tkthread/0.3.0/", "requires_dist": null, "requires_python": "", "summary": "Easy multithreading with Tkinter", "version": "0.3.0" }, "last_serial": 4557923, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "0348146a48e313f81829ef8fc719b381", "sha256": "326cca5c94c5a3ec987001200a4a7c67ecda38605ab38441f6c17d0f001a9f57" }, "downloads": -1, "filename": "tkthread-0.1.0.zip", "has_sig": false, "md5_digest": "0348146a48e313f81829ef8fc719b381", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 4919, "upload_time": "2018-11-06T09:34:26", "url": "https://files.pythonhosted.org/packages/45/7b/369100132a1d8090d41bdfd19669b8a02093153154fcce7f02f9d445063c/tkthread-0.1.0.zip" } ], "0.2.0": [ { "comment_text": "", "digests": { "md5": "76f2939a5b1f39449b40af7104a9b26b", "sha256": "80be7f15e3cf43fb04af3b72c3a5f173383707c92a8ab9042ffbf38dae1b71fb" }, "downloads": -1, "filename": "tkthread-0.2.0.zip", "has_sig": false, "md5_digest": "76f2939a5b1f39449b40af7104a9b26b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11457, "upload_time": "2018-11-16T04:06:04", "url": "https://files.pythonhosted.org/packages/ec/04/92c61030a910b36405f534a6b535537965fb2d900c110d32826530a2a355/tkthread-0.2.0.zip" } ], "0.3.0": [ { "comment_text": "", "digests": { "md5": "80f9a8a0e4ad7f0eef6521e96e2818ec", "sha256": "fd34423354e0d113a350a6ab78482c9b878c02694f799397a2b24b5745cf7056" }, "downloads": -1, "filename": "tkthread-0.3.0.zip", "has_sig": false, "md5_digest": "80f9a8a0e4ad7f0eef6521e96e2818ec", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14429, "upload_time": "2018-12-04T01:14:15", "url": "https://files.pythonhosted.org/packages/20/8b/a5f12ac803999264e075c83a7639209915af221ddb18782b8fb711d93b56/tkthread-0.3.0.zip" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "80f9a8a0e4ad7f0eef6521e96e2818ec", "sha256": "fd34423354e0d113a350a6ab78482c9b878c02694f799397a2b24b5745cf7056" }, "downloads": -1, "filename": "tkthread-0.3.0.zip", "has_sig": false, "md5_digest": "80f9a8a0e4ad7f0eef6521e96e2818ec", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14429, "upload_time": "2018-12-04T01:14:15", "url": "https://files.pythonhosted.org/packages/20/8b/a5f12ac803999264e075c83a7639209915af221ddb18782b8fb711d93b56/tkthread-0.3.0.zip" } ] }