|
41 | 41 | interface to the Tk toolkit. The Tcl library has a C interface to
|
42 | 42 | create and manage one or more instances of a Tcl interpreter, run Tcl
|
43 | 43 | commands and scripts in those instances, and add custom commands
|
44 |
| - implemented in either Tcl or C. Each interpreter instance also provides |
45 |
| - an event queue, used for I/O, timers, and by modules. |
| 44 | + implemented in either Tcl or C. Tcl provides for an event queue, used |
| 45 | + for I/O, timers, and by modules. |
46 | 46 |
|
47 | 47 | Tk
|
48 | 48 | Tk is a module that can be loaded into a Tcl interpreter instance. It adds
|
49 | 49 | Tcl commands (implemented in C) to create and manipulate GUI widgets. Each
|
50 | 50 | :class:`Tk` object embeds its own Tcl interpreter instance with Tk loaded into
|
51 | 51 | it. Tk's widgets are very customizable, though at the cost of a dated appearance.
|
52 |
| - Tk uses the interpreter's event queue to generate and process GUI events. Note |
| 52 | + Tk uses Tcl's event queue to generate and process GUI events. Note |
53 | 53 | that unlike some GUI libraries, each interpreter uses only a single thread,
|
54 | 54 | which has implications for :mod:`tkinter` users (see `Threading model`_).
|
55 | 55 |
|
@@ -85,59 +85,54 @@ Python interpreter.
|
85 | 85 | Threading model
|
86 | 86 | ---------------
|
87 | 87 |
|
88 |
| -A Tcl interpreter has one stream of execution and, unlike most other GUI |
89 |
| -toolkits, doesn't provide a blocking event loop. |
90 |
| -Instead, Tcl code is supposed to pump the event queue regularly at strategic |
91 |
| -moments -- after an action that triggers an event, before a later action that |
92 |
| -needs the result of that event. As such, all Tcl commands are designed to work |
93 |
| -without an event loop running -- only event handlers will not be executed |
94 |
| -until the queue is processed. This allows to work completely in one thread, |
95 |
| -i.e. even in environments that have no thread support at all. |
96 |
| - |
97 |
| -This is in stark constrast to the execution model for most other GUI toolkits: |
98 |
| -a dedicated OS thread (usually called the "UI thread") runs the GUI event loop |
99 |
| -constantly, and other threads send work items to it -- either complete with |
100 |
| -payload reference, or as agreed-upon "events" which result in it executing a |
101 |
| -predefined "event handler". |
102 |
| -Likewise, for any lengthy tasks, the UI thread can launch worker threads that |
103 |
| -report back on their progress via the same event queue. |
104 |
| - |
105 |
| -Tkinter strives to provide the best of both worlds. The "modern" multithreaded |
106 |
| -GUI model is the primary mode of execution, but pumping messages manually |
107 |
| -instead is also supported. What is more important, any Tkinter calls can be |
108 |
| -made from any Python threads, only subject to Tcl's architectural restictions: |
109 |
| - |
110 |
| -* A nonthreaded Tcl doesn't know anything about threads, so all Tcl calls are |
111 |
| - wrapped with a global lock to enforce sequential access. |
112 |
| - Any Tkinter calls can be made from any threads, but under the hood, only one |
113 |
| - is active at any moment. |
114 |
| - |
115 |
| -* A threaded Tcl interpreter instance, when created, becomes tied to the |
116 |
| - creating OS thread ("the interpreter thread"). Tcl mandates that all calls |
117 |
| - to the instance must come from this thread, save for special inter-thread |
118 |
| - communication APIs. Tkinter implements calls from outside the interpreter |
119 |
| - thread by posting an event to the interpreter's queue, then waits for the |
120 |
| - result of its processing. As such: |
121 |
| - |
122 |
| - * To make calls from outside the interpreter queue, :func:`mainloop` |
123 |
| - must be running in the interpreter queue. Otherwise, a |
124 |
| - :class:`RuntimeError` is raised. |
125 |
| - |
126 |
| - * A few select functions can only be called from the interpreter thread. |
127 |
| - |
128 |
| -Tcl event queue is per-thread rather than per-interpreter. For nonthreaded Tcl, |
129 |
| -there's only one global queue shared by all interpreters; for threaded Tcl, |
130 |
| -one queue per OS thread that's shared by all interpreters bound to that thread. |
131 |
| -So, when an event processing function like :func:`Tk.mainloop` is called, it |
132 |
| -will process events not only for the calling :class:`Tk` instance |
133 |
| -but for all whose interpreters share the queue. |
134 |
| -Events handlers will be called within the same Tcl interpreter that they were |
135 |
| -bound to as per the `bind <https://www.tcl.tk/man/tcl8.6/TkCmd/bind.htm>`_ man |
136 |
| -page, but any exceptions will be raised from the event processing function. |
137 |
| -There's no harm in calling :func:`mainloop` for two :class:`Tk`s at the same |
138 |
| -time: with nonthreaded Tcl, they will take turns handling events, and with |
139 |
| -threaded Tcl, this is only allowed if the :class:`Tk`s are associated with |
140 |
| -different threads anyway. |
| 88 | +Python and Tcl/Tk have very different threading models, which :mod:`tkinter` |
| 89 | +tries to bridge. If you use threads, you may need to be aware of this. |
| 90 | + |
| 91 | +A Python interpreter may have many threads associated with it. In Tcl, multiple |
| 92 | +threads can be created, but each thread has a separate Tcl interpreter instance |
| 93 | +associated with it. Threads can also create more than one interpreter instance, |
| 94 | +though each interpreter instance can be used only by the one thread that created it. |
| 95 | + |
| 96 | +Each :class:`Tk` object created by :mod:`tkinter` contains a Tcl interpreter. |
| 97 | +It also keeps track of which thread created that interpreter. Calls to |
| 98 | +:mod:`tkinter` can be made from any Python thread. Internally, if a call comes |
| 99 | +from a thread other than the one that created the :class:`Tk` object, an event |
| 100 | +is posted to the interpreter's event queue, and when executed, the result is |
| 101 | +returned to the calling Python thread. |
| 102 | + |
| 103 | +Tcl/Tk applications are normally event-driven, meaning that after initialization, |
| 104 | +the interpreter runs an event loop (i.e. :func:`Tk.mainloop`) and responds to events. |
| 105 | +Because it is single-threaded, event handlers must respond quickly, otherwise they |
| 106 | +will block other events from being processed. To avoid this, any long-running |
| 107 | +computations should not run in an event handler, but are either broken into smaller |
| 108 | +pieces using timers, or run in another thread. This is different from many GUI |
| 109 | +toolkits where the GUI runs in a completely separate thread from all application |
| 110 | +code including event handlers. |
| 111 | + |
| 112 | +If the Tcl interpreter is not running the event loop and processing events, any |
| 113 | +:mod:`tkinter` calls made from threads other than the one running the Tcl |
| 114 | +interpreter will fail. |
| 115 | + |
| 116 | +A number of special cases exist: |
| 117 | + |
| 118 | + * Tcl/Tk libraries can be built so they are not thread-aware. In this case, |
| 119 | + :mod:`tkinter` calls the library from the originating Python thread, even |
| 120 | + if this is different than the thread that created the Tcl interpreter. A global |
| 121 | + lock ensures only one call occurs at a time. |
| 122 | + |
| 123 | + * While :mod:`tkinter` allows you to create more than one instance of a :class:`Tk` |
| 124 | + object (with it's own interpreter), all interpreters that are part of the same |
| 125 | + thread share a common event queue, which gets ugly fast. In practice, don't create |
| 126 | + more than one instance of :class:`Tk` at a time. Otherwise, it's best to create |
| 127 | + them in separate threads and ensure you're running a thread-aware Tcl/Tk build. |
| 128 | + |
| 129 | + * Blocking event handlers are not the only way to prevent the Tcl interpreter from |
| 130 | + reentering the event loop. It is even possible to run multiple nested event loops |
| 131 | + or abandon the event loop entirely. If you're doing anything tricky when it comes |
| 132 | + to events or threads, be aware of these possibilities. |
| 133 | + |
| 134 | + * There are a few select :mod:`tkinter` functions that presently work only when |
| 135 | + called from the thread that created the Tcl interpreter. |
141 | 136 |
|
142 | 137 |
|
143 | 138 | Module contents
|
|
0 commit comments