小编典典

有什么魔术可以阻止Tkinter程序在交互式shell中阻塞?

python

注意:这是关于以下问题的后续文章: Tkinter-
什么时候需要调用mainloop?

通常,在使用Tkinter时,您可以调用Tk.mainloop来运行事件循环,并确保事件得到正确处理,并且窗口保持交互性而不会阻塞。

在交互式外壳中使用Tkinter时,似乎不需要运行主循环。举个例子:

>>> import tkinter
>>> t = tkinter.Tk()

将会出现一个窗口,并且该窗口不会被阻止:您可以与之交互,拖动它并关闭它。

因此,交互式外壳程序中的某些内容似乎可以识别出已创建一个窗口并在后台运行事件循环。

现在开始有趣的事情。再次以上面的示例为例,但是在下一个提示(不关闭窗口)中,输入任何内容,而无需实际执行(即,不要按Enter)。例如:

>>> t = tkinter.Tk()
>>> print('Not pressing enter now.') # not executing this

如果现在尝试与Tk窗口进行交互,您将看到它完全被 阻止
。因此,当我们向交互式shell输入命令时,我们以为在后台运行的事件循环停止了。如果我们发送输入的命令,您将看到事件循环 继续,
并且我们在阻塞期间所做的一切将继续进行。

因此,最大的问题是: 交互外壳中发生了什么魔术? 当我们没有明确地执行主循环时,它将运行主循环吗?以及为什么在 输入
命令时需要停止操作(而不是在执行命令时停止操作)?

注意:
上面的内容在命令行解释器(而不是IDLE)中是这样工作的。至于IDLE,我假设GUI不会实际上告诉底层解释器已经输入了某些内容,而只是将输入保留在本地直到执行完毕。


阅读 190

收藏
2020-12-20

共1个答案

小编典典

实际上,这并不是在这里重要的交互式解释器,而是在TTY上等待输入。您可以从如下脚本中获得相同的行为:

import tkinter
t = tkinter.Tk()
input()

(在Windows上,您可能必须在pythonw.exe中而不是python.exe中运行该脚本,但是否则,您不必执行任何特殊操作。)


那么它是怎样工作的?最终,诀窍归结为PyOS_InputHook-readline模块工作的方式相同。

如果stdin是TTY,则每次它尝试使用input()code模块的各个位,内置REPL等来获取一行时,Python都会调用已安装的任何内容,PyOS_InputHook而不仅仅是从stdin中读取。

它可能更容易理解什么readline:它试图select在标准输入或类似,循环为每个输入新的字符,或每0.1秒或每一个信号。

什么Tkinter确实是相似的。因为它必须处理Windows,所以它更加复杂,但是在*
nix上,它的行为与相似readline。除了Tcl_DoOneEvent每次在循环中都在调用。

这就是关键。Tcl_DoOneEvent重复调用与执行完全相同mainloop

(线程使一切变得更复杂,当然,但是让我们假设你还没有创建任何后台线程。在你真正的代码,如果你想创建后台线程,你只需要一个线程所有的Tkinter东西,上街区mainloop反正,
对?)


因此,只要您的Python代码将大部分时间都花在了TTY输入上(就像交互式解释器通常那样),则Tcl解释器会不断变化,而GUI也会响应。如果您在除TTY输入之外的其他位置上使Python解释器块阻塞,则Tcl解释器未运行,并且您的GUI没有响应。


如果您想在纯Python代码中手动执行相同的操作怎么办?如果您想例如将Tkinter
GUI和select基于网络的客户端集成到单线程应用程序中,就需要这样做,对吗?

这很容易:从另一个驱动一个循环。

您可以select设置0.02s的超时(默认输入挂钩使用相同的超时),并在t.dooneevent(Tkinter.DONT_WAIT)每次循环中调用。

或者,您也可以通过致电来让Tk开车mainloop,但是请使用after和朋友来确保您select经常打电话。

2020-12-20