|
34 | 34 | import traceback |
35 | 35 | import sys |
36 | 36 | import threading |
| 37 | +try: |
| 38 | + import queue |
| 39 | +except: |
| 40 | + import Queue as queue # Py2.7 |
37 | 41 |
|
38 | 42 | from . import tk |
39 | 43 |
|
@@ -76,9 +80,13 @@ def wrapped(self, *args, **kwargs): |
76 | 80 | # and the main thread event loop setting dispatch=0. |
77 | 81 | # As rare as the condition is, it needs to be handled. |
78 | 82 | if n < rcount: |
79 | | - traceback.print_exc(file=sys.stderr) |
80 | | - print('retrying %r %i of %i' % (name, n+1, rcount), |
81 | | - file=sys.stderr) |
| 83 | + try: |
| 84 | + traceback.print_exc(file=sys.stderr) |
| 85 | + print('retrying %r %i of %i' % (name, n+1, rcount), |
| 86 | + file=sys.stderr) |
| 87 | + except: |
| 88 | + # pythonw.exe sys.stderr is None |
| 89 | + pass |
82 | 90 | continue |
83 | 91 | else: |
84 | 92 | raise |
@@ -199,6 +207,40 @@ def _ensure_after_idle(widget, func, tries=None): |
199 | 207 | return result |
200 | 208 |
|
201 | 209 |
|
| 210 | +class _NoSyncHandler: |
| 211 | + ''' |
| 212 | + _tkinter.c will dispatch non-mainthread calls to the mainthread, |
| 213 | + but will create a Tcl_Condition variable that blocks the calling thread. |
| 214 | + ''' |
| 215 | + def __init__(self): |
| 216 | + self.q = queue.Queue() |
| 217 | + self.th = threading.Thread( |
| 218 | + target=self._dispatcher, |
| 219 | + name='tkthread.nosync', |
| 220 | + ) |
| 221 | + self.th.daemon=True |
| 222 | + self.th.start() |
| 223 | + |
| 224 | + def _dispatcher(self): |
| 225 | + while True: |
| 226 | + func, args, kwargs = self.q.get() |
| 227 | + try: |
| 228 | + func(*args, **kwargs) |
| 229 | + except: |
| 230 | + # show the error |
| 231 | + try: |
| 232 | + traceback.print_exc(file=sys.stderr) |
| 233 | + except: |
| 234 | + # pythonw.exe sys.stderr is None |
| 235 | + pass |
| 236 | + |
| 237 | + def call(self, func, *args, **kw): |
| 238 | + self.q.put((func, args, kw)) |
| 239 | + |
| 240 | + |
| 241 | +_nosync_handler = _NoSyncHandler() |
| 242 | + |
| 243 | + |
202 | 244 | def main(widget=None, sync=True): |
203 | 245 | """Decorator to run callable (no arguments) on Tcl/Tk mainthread. |
204 | 246 |
|
@@ -231,12 +273,7 @@ def sync_wrapped(_func=func, _ev=ev): |
231 | 273 | ev.wait() |
232 | 274 |
|
233 | 275 | else: |
234 | | - th = threading.Thread( |
235 | | - target=_ensure_after_idle, |
236 | | - args=(w, func), |
237 | | - name='tkthread.main') |
238 | | - th.daemon = True |
239 | | - th.start() |
| 276 | + _nosync_handler.call(_ensure_after_idle, w, func) |
240 | 277 |
|
241 | 278 | return func |
242 | 279 | return wrapped |
|
0 commit comments