2008年5月1日木曜日

ProgressDialog(その5)

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
::wxSafeYield

bool wxSafeYield(wxWindow* win = NULL, bool onlyIfNeeded = false)

This function is similar to wxYield, except that it disables the user input to all program windows before calling wxYield and re-enables it again afterwards. If win is not NULL, this window will remain enabled, allowing the implementation of some limited user interaction.

Returns the result of the call to ::wxYield.

で、実装はこうなっている。

// Yield to other apps/messages and disable user input to all windows except
// the given one
bool wxSafeYield(wxWindow *win, bool onlyIfNeeded)
{
wxWindowDisabler wd(win);

bool rc;
if (onlyIfNeeded)
rc = wxYieldIfNeeded();
else
rc = wxYield();

return rc;
}


あとはwxYieldだが次のようになっている。

bool wxYield()
{
return wxTheApp && wxTheApp->Yield();
}

bool wxYieldIfNeeded()
{
return wxTheApp && wxTheApp->Yield(true);
}


で、wxApp::Yieldに帰結する。あー、ながかった。

Yieldするとイベントがプロセスされるが、その中でYieldしたDialogを殺したら当然、戻ってきたときにthisがNULLなのでアプリがクラッシュしてしまいます!

今回の場合はDialogのEventHandlerがYield中であることは知らないのでこれに該当するでしょう。ネットワークから届いたデータが引き金となって受信スレッドからwx.CallAfterで呼ばれている。CallAfterはイベントの処理が終わった後に実行されるので、CallAfterの処理がYieldで行われている可能性は否定できない(どこを読めばいいのだ?)。

windowを開いたままイベントハンドラを抜けてしまうなら、そのwindowはwxのDialogである必要がなく(Yieldしてもらいたくない)て、親のWindow側からpollできることになるので親をプログラム的にDisableしてprogress barを自前で描くwindowを作るというのがひとつの解。

もうひとつは、前の可能性が正しいとしてYield中だったらもういちどCallAfterかなんかしてYieldを抜けてから処理が行われるようにする。wxの側でこの手のガードをすることも考えられるが、微妙な気がする。

0 件のコメント: