プログレス バー ウィジェットの値 (または、一般に、任意のウィジェットの値または構成) を更新すると、Tk は内部のidle *イベントを作成して再描画を行います。アイデアは、一連のイベントに応答して複数のものを連続して更新する場合 (非常に一般的なことです!)、ウィジェットの再描画を 1 回だけ取得するというものです。ほとんどの場合、これは非常にうまく機能しますが、ある種の忙しいアニメーション ループを実行しているときはそうではありません。を実行するupdate idletasks
と、スケジュールされたアイドル イベントがすぐに発生します。内部生成された再描画はその時点で処理されます。
しかし、それだけではありません。WM_PAINT
実際のイベントを介して外部世界 (つまり、ホスト ウィンドウ システム) から来る外部生成の再描画もありますExpose
。(議論のためにそのプラットフォームは無視してください。) これらの外部イベントは外部の世界から来るため、イベント ループの完全な実行によってのみ通知されます。低レベルのイベント処理システムは、それらが要求された再描画は、それらが受信されるまで — つまり、メイン イベント ループ、または 、vwait
完全なupdate
、またはtkwait
. (実際、これらの外部再描画イベントを処理する方法は、アイドル イベントで再描画をスケジュールすることです。これは、ウィジェットのサイズ変更やフォーカスの変更など、同時に処理する必要がある他のイベントが存在する可能性があるためです。再描画も必要です。)
これを処理するための推奨される方法は、長時間実行される処理を行っているコードを分割して、定期的にイベント ループに戻り、保留中の外部イベントを処理できるようにすることです。8.5 以前では、after $aFewMilliseconds doTheNextBit
時々 を使用してこれを行います。これには、継続渡しスタイルで機能するようにコードを構造化する必要がありますが、これは少し面倒です。一方、8.6 では、長時間実行されるコードをコルーチン内に配置し、after $aFewMilliseconds [info coroutine];yield
コードの流れを大幅に中断することなく、少し停止することができます。
あまりお勧めしませんupdate
。これには、補助的なイベント ループが開始されるという問題があり、長時間実行される処理に再度入ろうとすると、スタックが枯渇するという問題が発生する可能性があります。適切なインターロック (たとえば、長い処理を行っているときに問題を引き起こす可能性のあるボタンを無効にする) を使用してこれを機能させることは可能ですが、それは本当に難しいことです。また、進行状況バーを変更するために時々メインの GUI スレッドにメッセージを送り返すだけの別の非 GUIスレッド**に処理を任せることを検討することもできます。(ところで、Tclはスレッド間メッセージをイベントとして扱います。)マルチスレッド化は、あなたのケースでは意味があるかもしれませんし、意味がないかもしれません。
* で独自のアイドル イベントを作成できますafter idle
が、通常は必要ありません。
** 真のマルチスレッド GUI は非常にクレイジーであり、Tk はとにかくそのようには機能しません。Tk を持つ各スレッドには、完全な独自のコピーがあります。