Tcl 当toplevel尚未';这不是焦点

Tcl 当toplevel尚未';这不是焦点,tcl,tk,Tcl,Tk,我的顶层有一个ttk::progressbar(唯一的),我通过以下功能更新它: proc progress {x} { global prog set prog [expr fmod(($prog +$x),100)] update idletasks } prog是通过-variable选项绑定到progressbar的变量 如果我把注意力集中在窗户上,一切都会好起来的。如果切换到另一个窗口,progressbar将停止更新,即使切换回应用程序,它也不会重新启动 我在Windo

我的顶层有一个
ttk::progressbar
(唯一的
),我通过以下功能更新它:

proc progress {x} {
  global prog
  set prog [expr fmod(($prog +$x),100)]
  update idletasks
}
prog
是通过
-variable
选项绑定到progressbar的变量

如果我把注意力集中在窗户上,一切都会好起来的。如果切换到另一个窗口,progressbar将停止更新,即使切换回应用程序,它也不会重新启动

我在Windows7上使用tcl/tk 8.6(以防万一)

这种行为的原因可能是什么?我是否错过了有关如何更新进度条的内容

编辑


似乎一个完整的
更新
会起作用,所以问题是为什么会出现这种情况,以及是否有任何方法可以避免完全更新刷新。

当您更新进度条小部件的值(或者,通常,任何小部件的值或配置)时,Tk会创建一个内部空闲*事件来进行重绘;其思想是,如果您更新一行中的多个内容以响应一系列事件(这是一件非常常见的事情!),那么您只会得到一个小部件的重画。这在大多数情况下都能很好地工作,但当你在做一些繁忙的动画循环时就不行了。 执行
更新idletasks
可立即发生任何计划的空闲事件;此时将处理内部生成的重画

但这还不是全部。还有通过真实事件从外部世界(即主机窗口系统)生成的外部重画:在Windows上,
WM_PAINT
,在X11上,
Expose
,在OSX上,…,这在OSX上很复杂。(为了论证而忽略该平台。)因为这些外部事件来自外部世界,它们只会被完整的事件循环注意到-低级事件处理系统在收到它们之前不知道它们被请求重画-这意味着使用主事件循环,或由
vwait
启动的附属系统,完整的
更新
,或
tkwait
。(事实上,处理这些外部重画事件的方式是在空闲事件中安排重画,因为可能还有其他事件,如小部件大小调整或焦点更改,也需要同时处理,并且也需要重画。)

建议的处理方法是将正在执行长时间运行的处理的代码拆分,以便可以定期返回事件循环,并允许它处理任何挂起的外部事件。在8.5及之前的版本中,您可以不时在$afewmillseconds doTheNextBit之后使用
;这需要对代码进行结构化,使其以延续传递样式工作,这可能有点痛苦。另一方面,在8.6中,您可以将长时间运行的代码放入一个协同程序中,并在$afewmillseconds[info corroutine]之后使用
停止一点;在不显著中断代码流的情况下产生

不太推荐的做法是加入
更新
。问题是它会启动一个辅助事件循环,如果您尝试重新进入任何长时间运行的处理,这可能会导致堆栈耗尽问题。通过适当的互锁(例如,禁用可能会在长时间处理过程中导致问题的按钮)可以实现这一点,但这确实更加棘手。您还可以考虑将处理从一个单独的非GUI线程**,只需将消息发送回主GUI线程,以使进度条发生变化。(顺便说一句,Tcl将线程间消息视为事件)在您的情况下,使用多线程可能有意义,也可能没有意义


*您可以在空闲后使用
创建自己的空闲事件,但通常不需要它们。

**真正的多线程GUI非常疯狂,而Tk无论如何也不能以这种方式工作。带有Tk的每个线程都有自己的完整副本。

您在哪个平台上运行?Win 7。我会更新这个问题。谢谢,多纳尔。非常全面,我喜欢Tcl/Tk的公寓模式。在Java中,没有任何东西阻止您访问其他线程中的某些GUI内容,这可能会导致错误/未定义的行为。很多刚接触Java的人都犯了这个错误(我也犯过一次)。