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 は内部の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 を持つ各スレッドには、完全な独自のコピーがあります。

于 2013-04-30T08:26:42.763 に答える