update idletasks when toplevel hasn't the focus

假装没事ソ 提交于 2019-12-02 06:54:05

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.

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.)

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.

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.


* 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.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!