GtkTreeModelFilter使用時の注意点(あるいはバグの反省)
モデルデータ変更時に発生するイベント内でGtkTreeModelFilterを扱う場合の注意点です。
環境依存なのかもしれませんが、ちょっと手順が違うとコア吐いて落ちます。
FreeBSD 7.1-RELEASEのportsコレクションから入れたバージョン2.14.7と2.16.1で確認しました。
トラブル - GtkTreeModelFilterを使用して行を削除するとプログラムが落ちる?
GtkTreeViewのモデルに
GtkTreeModelFilterを使用し、かつ
GtkTreeSelectionの
changedイベントハンドラでモデルデータを参照するようにしているとき、選択中のモデルデータを削除するとコアを吐いて落ちててしまいました。
コンソールには以下のような内容が出力されています。
(gtktreemodelfilter_trouble:1167): Gtk-CRITICAL **: gtk_list_store_get_value: assertion `VALID_ITER (iter, list_store)' failed
(gtktreemodelfilter_trouble:1167): GLib-GObject-WARNING **: gtype.c:3940: type id `0' is invalid
(gtktreemodelfilter_trouble:1167): GLib-GObject-WARNING **: can't peek value table for type `<invalid>' which is not currently referenced
Segmentation fault (core dumped)
連続した行を削除するとコアを吐いて落ちます。
一行ずつ削除しているときや削除する行が連続していないときは落ちないか、落ちる確率が低いようです。
最初は行を削除しているだけなのに、何処から
gtk_list_store_get_value()が呼ばれて、何故
iterが無効になっているかさっぱりわかりませんでした。
よくよく調べてみると、以下のようなことをしていました。
- GtkTreeViewのモデルにGtkTreeModelFilterを使用。
- gtk_tree_list_store_remove()で選択中の行を削除する。
- 選択中の行が削除されたのでGtkTreeSelectionのchangedイベントが発行される。
- changedイベントのハンドラ内で、現在選択中の行のGtkTreePathのリストを取得する。
- GtkTreePathからgtk_tree_model_get_iter()でGtkTreeIterを取得する。
- 取得したGtkTreeIterはGtkTreeModelFilterのものなのでgtk_tree_model_filter_convert_iter_to_child_iter()で大元のGtkTreeModelのGtkTreeIterを取得する。
- gtk_tree_model_get()でGtkTreeModelのデータを取得し、選択データの情報をステータスバーに表示。
上記最後の7.の
gtk_tree_model_get()から呼ばれる
gtk_list_store_get_value()内でエラーが発生して落ちているようです。
落ちる箇所が判明したところで、今度は原因を考えてみます。
gtk_tree_model_get()は受け取った
GtkTreeIterをそのまま
gtk_list_store_get_value()に渡しているので、
changedイベントハンドラ内の処理に原因があるということになります。
コンソールには
iterが不正だというメッセージが出力されているので、どうやら上記の5.と6.で
GtkTreeModelFilterの
GtkTreePathから
GtkTreeModelの
GtkTreeIterを取得する方法に問題がありそうです。
[TOP]
解決策 - GtkTreeIterの取得方法を変更
問題は自分で書いた
GtkTreeSelectionの
changedイベントハンドラ内の処理、
GtkTreeIterの取得方法にありそうだという所まで判ったので、別の方法を試してみます。
別の方法といっても出来る事は一つしかありません。
落ちていた時は
GtkTreeModelFilterの
GtkTreePathから
GtkTreeIterを取得し、その後
GtkTreeModelの
GtkTreeIterに変換していましたが、これを変更して先に
GtkTreePathを
gtk_tree_model_filter_convert_path_to_child_path()で
GtkTreeModelのものに変換し、それから
gtk_tree_model_get_iter()で
GtkTreeIterを取得するようにします。
これでどうやら落ちることは無くなったようです。
ということでコアダンプするコードと対策コードを纏めました。
gtktreemodelfilter_trouble.c
文字コードはUTF-8で保存してください。
GtkTreeSelectionの
changedイベントで選択中の行の値の合計値を計算して表示。削除ボタンを押すと選択中の行を削除します。
GtkTreeModelFilterはただ使用しているだけです。常に全ての行を表示します(フィルタリングしていません)。
普通にコンパイルするとコアを吐いて落ちます。
マクロ
FIX_CONVERT_TO_CHILDを定義すると落ちなくなります。
詳しくは判らないのですが現象だけを見ると、
GtkTreeModelFilterを使用しているとき、フィルタリングしている
GtkTreeModelから行を削除すると、
GtkTreeModelFilterへの反映が遅れるか何かして、
GtkTreeSelectionの
changedイベントで
GtkTreeModelFilterから取得した
GtkTreeIterが不正となるか、または
gtk_tree_model_filter_convert_iter_to_child_iter()での変換が正しく行なわれないということのようです。
最初に
gtk_tree_model_get_iter()で
GtkTreeIterを取得しておく場合と比べて、最初に
gtk_tree_model_filter_convert_path_to_child_path()で変換する場合は、
gtk_tree_path_free()を呼び出す手間が一つ増えるので、それを嫌って自然と落るコードを書いてしまったのが敗因でした。
これはFAQなのかもしれません。とてもFAQっぽいです。
やってはイケナイ事として既にドキュメントになっていそうです。
(>_<)
[TOP]
[戻る]