使用Tkinter,我有一个启动外部线程的按钮。并且tkinter文本框通过管道输出到外部线程stdout和stderr。执行外部线程时,我只收到一次此错误:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python33\lib\tkinter\__init__.py", line 1475, in __call__
return self.func(*args)
File "C:\Python33\lib\tkinter\__init__.py", line 2923, in set
self.tk.call((self._w, 'set') + args)
_tkinter.TclError: bad option "set TCL VALUE NUMBER": must be activate, cget, configure, delta, fraction, get, identify, or set
即使它没有再次发生,我很想知道是什么导致了它。看起来错误不是来自外部线程,而是来自运行GUI的主线程。它可能只是一些内部Tkinter错误吗?
答案 0 :(得分:2)
这可能有点迟了,但无论如何......
我自己的研究发现建议添加"当=" tail"'到event_generate调用,确保事件正确排队。如果你不这样做,Tk事件规则可以对事件队列做些奇怪的事情。这不是(还有?)记录在Tkinter中。
即便如此,我自己的研究(和经验!)也发现Tkinter从根本上说不是线程安全的。这意味着你无法保证在自己的线程之外使用Tkinter做任何事都可以。最安全的方法是生成事件,当然,但即使这样也会定期抛出异常,这些异常表明Tkinter内部的线程没有用于添加事件。通常这些例外是可以存活的,所以我使用重试来获得另一个裂缝,这主要是有效的。
retries = 3
done = False
unknownException = False
while (retries != 0) and (not done) and (not unknownException):
try:
self._applicationWindow.event_generate("<<MyNewEvent>>", when="tail")
done = True
except Tk.TclError as err:
if "application has been destroyed" in err.message:
# If application is destroyed, this event is not needed
done = True
elif ("expected boolean value but got" in err.message) or ("bad option" in err.message):
# These are bugs in Tk/Tcl/Tkinter, not in our code. They seem to be uncommon,
# and seem to be survivable. Hopefully retrying will have the system in a
# slightly different state where this doesn't happen next time.
print "Tkinter/Tk/Tcl error, retry " + str(retries)
retries = retries - 1
else:
unknownException = True
except:
unknownException = True
# Unlock afterwards
self._eventLock.release()
if not done:
# If retries haven't helped or we have an exception which isn't one we know about,
# then pass the exception up the tree.
raise
非线程安全的UI层是一回事 - 我想它简化了设计,所以它几乎可以接受。但是,非线程安全事件队列应该是编程入门类中的失败等级。如果您认为Tkinter已被破坏且永远不能用于任何真实世界的应用程序 - 请加入俱乐部。就我而言,对任何Tkinter问题的正确修复是使用另一个适合目的的UI层。 :/
PS。我使用的是Python 2.7.6。 YMMV on Python 3。