当顶层没有焦点时更新空闲任务 [英] update idletasks when toplevel hasn't the focus

查看:20
本文介绍了当顶层没有焦点时更新空闲任务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I've a ttk::progressbar in my toplevel (the one and only .) which I update via the following function:

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

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

prog is the variable bound to the progressbar via the -variable option.

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

Everything works fine if I keep the focus on my window. If I switch to another window the progressbar stops to be updated and it doesn't restart even if I switch back to my application.

我在 Windows 7 上使用 tcl/tk 8.6(以防万一).

I'm using tcl/tk 8.6 on Windows 7 (just in case it might be relevant).

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

What could be the cause of this behaviour? Have I missed something on how to update a progressbar?

编辑

似乎完整的 update 可以解决问题,所以问题是为什么会这样,以及是否有任何方法可以避免完整的更新刷新.

It seems that a full update does the trick, so the question would be why this is the case and if there's any way to avoid a full update refresh.

推荐答案

当您更新进度条小部件的值(或者,通常,任何小部件的值或配置)时,Tk 会创建一个内部 idle* 事件进行重绘;这个想法是,如果您连续更新多个内容以响应一系列事件(非常常见的事情!),那么您只会重新绘制小部件.这在大多数情况下都非常有效,但在您执行某种繁忙的动画循环时则不然.执行 update idletasks 会立即发生任何计划的空闲事件;内部生成的重绘在那个时候得到处理.

When you update the value of a progress bar widget (or, generally, the value or configuration of any widget) Tk creates an internal idle* event to do the redrawing; the idea is that if you update multiple things in a row in response to a sequence of events (a very common thing!) then you only get one redraw of the widget. This works really well most of the time, but not when you're doing some kind of busy animation loop. Doing an update idletasks makes any scheduled idle events happen immediately; the internally-generated redraw gets processed at that point.

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

But that's not all. There are also externally-generated redraws that come from the outside world (i.e., the host windowing system) via real events: on Windows, WM_PAINT, on X11, Expose, on OSX, …, well it's complicated on OSX. (Ignore that platform for the sake of argument.) Because these external events come from the outside world, they're only noticed by a full run of the event loop — the low-level event processing system doesn't know that they're requested redraws until after they're received — which means using the main event loop, or a subsidiary one as started by vwait, a full update, or tkwait. (In fact, the way that those external redraw events are handled is by scheduling a redraw in an idle event because there might be other events, like a widget resize or a focus change, that also need to be handled at the same time and which need a redraw as well.)

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

The recommended way of handling this is to split your code that is doing the long-running processing up so that you can return to the event loop periodically and allow it to process any pending external events. In 8.5 and before, you do this by using after $aFewMilliseconds doTheNextBit from time to time; this requires structuring your code so that it works in continuation-passing style, which can be a bit painful. On the other hand, in 8.6 you can put your long-running code inside a coroutine and do the stop-for-a-bit with after $aFewMilliseconds [info coroutine];yield without significantly breaking the flow of your code.

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

Less recommended is to sprinkle in an update. This has the problem that it starts a subsidiary event loop, which can cause problems with stack exhaustion if you try to reenter any long-running processing. It's possible to make this work with appropriate interlocking (e.g., disabling buttons that could otherwise cause problems while you're doing long processing) but that's genuinely more tricky. You could also consider farming off the processing to a separate non-GUI thread** that just sends messages back to the main GUI thread from time to time to cause the progress bar to change. (Tcl treats inter-thread messages as events, BTW.) Going multithreaded might or might not make sense in your case.

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

* You can make your own idle events with after idle but you don't usually need them.
** True multi-threaded GUIs are pretty crazy, and Tk doesn't work that way anyway. Each thread with Tk has its complete own copy.

这篇关于当顶层没有焦点时更新空闲任务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆