1

使用 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 错误吗?

4

1 回答 1

2

这可能有点晚了,但无论如何......

我自己的研究发现了在 event_generate 调用中添加 'when="tail"' 的建议,以确保事件正确排队。如果你不这样做,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。Python 3 上的 YMMV。

于 2014-04-28T12:49:54.680 回答