読者です 読者をやめる 読者になる 読者になる

gl5_progのメモ

自分のためのメモとかまとめとか

QWidgetのWindowModifiedの怪しい挙動について

Qt4.7、Mac 10.8での話。

QWidgetのWindowModifiedとは

QWidgetにはユーザーによるデータ変更を表すフラグWindowModifiedがあり、これを使うとWindowsならタイトルバーに"*"がついたり、Macなら閉じるボタンの中に黒丸がついたりする。つまりテキストエディタなどでよくある挙動を実現できる機能。

さらに便利なことに、WindowModified(trueのみ)は親に伝搬するらしい。Qtではレイアウトなどで親子階層が深くなることがよくあるのでこの挙動は納得。

怪しい挙動

しかし、この親への伝搬があまりうまくいった記憶がない。過去の自分のソースコードを見ても試して利用を諦めた痕跡が見つかる。そして今日もうまくいかなかった。今までは調べるのが面倒だったので、諦めて他の方法で親への伝搬を実現していたようだけど、今日はなんとなく書いたチェック処理で明らかに挙動がおかしいことに気付いたので、頑張って調べてみることにした。

今日書いてみて明らかにおかしいと思ったのは以下のコード

widget->setWindowModified( true );
assert( widget->->parentWidget()->isWindowModified() ); // アサートに引っかかる

setWindowModified( true )をした直後に親のWindowModifiedを調べるとフラグが立っていない。これは明らかにおかしい。もしかして、挙動を勘違いしている?

ドキュメントを改めて読んでみる

QWidget | Documentation | Qt Project

Note that if a widget is set as modified, all its ancestors will also be set as modified. However, if you call setWindowModified(false) on a widget, this will not propagate to its parent because other children of the parent might have been modified.

訳すとこんな感じ?( 合ってるよね? )

Modifiedとして設定された場合、そのすべての先祖にもModifiedがセットされるだろう。しかし、falseをセットした場合は、親に伝搬されない。なぜなら、他の子はまだModifiedかもしれないからだ。

やっぱりドキュメントには親にもセットされると書いてある。でも実際にはされない。おかしい。

バグ?仕様?

ググってもたいした情報は見つからなかったが、バグチケットでの報告が1つだけ見つかった。 [#QTBUG-20150] setWindowModified(true) does not propagate to parents

やっぱりバグなのか?

Qtのソースコードを追ってみると以下のような流れになっているようだ。

  • QWidget::setWindowModified()
  • QApplication::sendEvent( widget, QEvent::ModifiedChange )
  • QWidget::event()
  • QWidget::changeEvent()

最終的にQWidget::changeEvent()に行き着くが結局そこで何もやっていない。このバグ最近入ったのか?と思って4.5のソースコードも見てみるが全く同じ挙動。え、そんな昔から?それってもうバグじゃなくて仕様なんじゃ…。

仕様だと思ってみる

ずっとこの挙動みたいだし、ググってもほとんど情報ないし、これはそういう仕様なんだと納得してみる。そうするとこのWindowModifiedの正しい扱い方が見えてきた。

まず、挙動を確認すると…

  • setWindowModifiedを呼ぶことでウインドウタイトルが変わったりする。isWindowModifiedで現在の状態を取得できる。
  • このWindowModifiedは親には伝搬しない。ただし、QEvent::ModifiedChangeは親に飛ぶ。

この挙動から、子でのsetWindowModifiedを親で補足する正しい方法は、「親のevent()でQEvent::ModifiedChangeを捉える」ということになる。きっとこれが正しい方法なんでしょう。試してないけど。

最後に

でも、むかし普通にできた気もするんだよなぁ。勘違いだったか、それともWindowsでは問題ないのか…。もう面倒だから深追いはしたくない。