1

我有一个ttk::progressbar在我的顶层(唯一的.),我通过以下函数更新:

proc progress {x} {
  global prog
  set prog [expr fmod(($prog +$x),100)]
  update idletasks
}

prog-variable是通过选项绑定到进度条的变量。

如果我把焦点放在我的窗口上,一切都很好。如果我切换到另一个窗口,进度条将停止更新,即使我切换回我的应用程序也不会重新启动。

我在 Windows 7 上使用 tcl/tk 8.6(以防万一它可能相关)。

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

编辑

似乎完全update可以解决问题,所以问题是为什么会出现这种情况以及是否有任何方法可以避免完全更新刷新。

4

1 回答 1

3

当您更新进度条小部件的值(或者,通常是任何小部件的值或配置)时,Tk 会创建一个内部空闲*事件来进行重绘;这个想法是,如果您连续更新多个事物以响应一系列事件(这是一件非常常见的事情!),那么您只会重绘一个小部件。这在大多数情况下都非常有效,但在您执行某种繁忙的动画循环时就不行了。执行 anupdate idletasks会使任何预定的空闲事件立即发生;此时会处理内部生成的重绘。

但这还不是全部。还有来自外部世界(即主机窗口系统)通过真实事件的外部生成的重绘:在 Windows 上WM_PAINT,在 X11 上Expose,在 OSX 上,......,在 OSX 上很复杂。(为了争论,忽略那个平台。)因为这些外部事件来自外部世界,它们只有在事件循环的完整运行中才会被注意到——低级事件处理系统不知道它们是请求重绘,直到收到它们 - 这意味着使用主事件循环,或由 开始的辅助事件循环,vwait完整的update,或tkwait. (实际上,处理这些外部重绘事件的方式是在空闲事件中安排重绘,因为可能还有其他事件,例如小部件调整大小或焦点更改,也需要同时处理,并且也需要重绘。)

处理此问题的推荐方法是将执行长时间运行处理的代码拆分,以便您可以定期返回事件循环并允许它处理任何未决的外部事件。在 8.5 及之前的版本中,您通过after $aFewMilliseconds doTheNextBit不时使用来做到这一点;这需要对代码进行结构化,使其以连续传递方式工作,这可能会有点痛苦。另一方面,在 8.6 中,您可以将长时间运行的代码放入协程中,并在after $aFewMilliseconds [info coroutine];yield不显着中断代码流的情况下进行停顿。

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


* 您可以制作自己的空闲事件,after idle但通常不需要它们。
** 真正的多线程 GUI 非常疯狂,而且 Tk 无论如何也不能那样工作。每个带有 Tk 的线程都有自己完整的副本。

于 2013-04-30T08:26:42.763 回答