2013-04-29 16 views
1

我有一个ttk::progressbar在我的顶层,我通过以下功能更新(独一无二.):更新idletasks顶层时,已经不是焦点

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

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

如果我将注意力集中在我的窗户上,一切正常。如果我切换到另一个窗口,进度条会停止更新,即使切换回我的应用程序,它也不会重新启动。

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

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

编辑

看来,一个完整的update的伎俩,这样的问题是,为什么是这样的话,如果有任何方式,以避免完全更新刷新。

+0

你在哪个平台上运行? – 2013-04-29 15:30:19

+0

赢了7.我会更新这个问题。谢谢。 – 2013-04-29 15:33:36

回答

3

当更新进度条控件的值(或者,一般地,值或任何构件的构型)Tk的创建内部空闲*事件做重绘;我们的想法是,如果您为了响应一系列事件而更新多个事物(一个非常常见的事情!),那么您只能重新绘制一个小部件。这在大多数情况下运行得非常好,但不是在做某种繁忙的动画循环时。 做一个update idletasks会立即发生任何预定的空闲事件;内部生成的重绘会在该点处处理。

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

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

不太推荐的是洒在update。这有一个问题,即启动一个辅助事件循环,如果您尝试重新输入任何长时间运行的处理,这会导致堆栈耗尽问题。可以通过适当的互锁来使其工作(例如,,禁用可能会导致问题,而你正在做长时间处理的按钮),但这真的更棘手。您也可以考虑将农作业处理为单独的非GUI线程**只是不时发送消息回主GUI线程以导致进度栏更改。 (Tcl将线程间消息视为事件,顺便说一下。)多线程化可能会或可能不会对您的情况有意义。


*您可以用after idle自己闲置的事件,但你通常不会需要他们。
**真正的多线程图形用户界面非常疯狂,而Tk无论如何都不能这样工作。 Tk的每个线程都有其完整的副本。

+0

谢谢Donal。非常全面 – 2013-04-30 08:57:03

+0

我喜欢Tcl/Tk的公寓模型。在Java中,没有任何东西阻止您访问其他线程中的某些GUI内容,这可能会导致错误/未定义的行为。很多新来的Java人做错了(我也曾这么做过)。 – 2013-04-30 09:23:32

相关问题