Qtに関する覚え書き[イベント全般]

シンプルなイベント処理

イベント取得関数について

すでに用意されている関数でイベントの処理はできますが、優先順位は低いです。
イベントフィルター(eventFilter)で破棄されたイベントは受け取れません。
フォーカス可能なオブジェクトが少なくて単純な処理しか行わない場合は、 こちらのほうが簡単かもしれません。

用意されている関数(抜粋)

virtual関数発生要因
closeEvent(QCloseEvent *event)ウインドウを閉じた
dragEnterEvent(QDragEnterEvent *event)ドラッグカーソルが入った
dragLeaveEvent(QDragLeaveEvent *event)ドラッグカーソルが出た
dragMoveEvent(QDragMoveEvent *event)ドラッグカーソルが動いた
dropEvent(QDropEvent *event)ドロップした
enterEvent(QEvent *event)カーソルが中に入った
focusInEvent(QFocusEvent *event)フォーカスを持った
focusNextPrevChild(bool next)フォーカスが移った
focusOutEvent(QFocusEvent *event)フォーカスを失った
hideEvent(QHideEvent *event)非表示になった
keyPressEvent(QKeyEvent *event)キーを押した
keyReleaseEvent(QKeyEvent *event)キーを離した
leaveEvent(QEvent *event)カーソルが外に出た
mouseDoubleClickEvent(QMouseEvent *event)ダブルクリック
mouseMoveEvent(QMouseEvent *event)カーソルが移動した
mousePressEvent(QMouseEvent *event)マウスボタンを押した
mouseReleaseEvent(QMouseEvent *event)マウスボタンを離した
moveEvent(QMoveEvent *event)オブジェクトが移動した
paintEvent(QPaintEvent *event)再描画
resizeEvent(QResizeEvent *event)サイズ変更
showEvent(QShowEvent *event)表示された
※戻り値はvoidです。QWidgetクラスのドキュメントより。

リサイズイベントの例

イベント関数の宣言 (mainwindow.h)

イベント処理 (mainwindow.cpp)

ウインドウの幅と高さをデバッグ出力します。
※このイベントはウィジェットのサイズが変わった時に発生します。

クローズイベントの例

イベント関数の宣言 (mainwindow.h)

スイッチの初期化 (mainwindow.cpp)

コンストラクタでスイッチ(checkQuit)を初期化します。
スイッチをtrueにすると、ウインドウを閉じる直前に確認メッセージを表示します。

イベント処理 (mainwindow.cpp)

スイッチがtrueの時は、確認用のYes/Noメッセージボックスを表示します。
Noが選択された場合は終了処理をしないで元の画面に戻ります。
その他の場合はウインドウを閉じて終了します。
※このイベントはタイトルバーの×ボタンを押した時や close()を実行した時に発生します。

マウスイベントの例

イベント関数の宣言 (mainwindow.h)

イベント処理 (mainwindow.cpp)

マウスの左ボタンが押されたらXY座標をデバッグ出力します。
※その座標に存在するオブジェクトが使ってしまったイベントは、 mousePressEventでは受け取れません。
※イベント一括処理で破棄されたイベントは受け取れません。
※このイベントはマウスのボタンを押した時に発生します。

マウスボタン(抜粋)

■マウスボタン定義[enum Qt::MouseButton]
定義説明
Qt::NoButtonなし
Qt::AllButtons全部のボタン
Qt::LeftButton左ボタン
Qt::RightButton右ボタン
Qt::MiddleButton中央ボタン

キー入力イベントの例

イベント関数の宣言 (mainwindow.h)

イベント処理 (mainwindow.cpp)

キーの名前をデバッグ出力します。その他のキーは16進コードを出力します。
※フォーカスを持つオブジェクトに破棄されたイベントは、keyPressEventで受け取れません。
※イベント一括処理で破棄されたイベントは受け取れません。
※このイベントはキーを押した時に発生します。

イベントの一括処理

一括処理について

イベントフィルターで、キーやマウスのイベントを一括処理します。
ただし微妙なところもあるので、本当にすべてのイベントを処理できるか疑問です。

イベント処理の順番は次のようになっていると思われます。
  1. フォーカスされているオブジェクトのイベント処理
  2. イベントフィルター(eventFilter関数)
  3. mousePressEvent関数やkeyPressEvent関数など

KeyPressイベントなどはフィルターに来る前にオブジェクトで消費されてしまいます。
オブジェクトで使われなかったイベントだけをフィルターで受け取ることができます。

イベント関数の宣言 (mainwindow.h)

イベントフィルター関数とイベント処理関数を宣言します。

イベントフィルターのインストール (mainwindow.cpp)

コンストラクタでイベントフィルターをインストールします。

イベント処理 (mainwindow.cpp)

イベントごとに必要な処理を行います。trueを返すとイベントは破棄されます。
※installEventFilterのオブジェクトが第1引数(QObject)として渡されますが、 ここでは使わないので名前を指定しません。

キーを押した時の処理

キーを判定してデバッグ出力します。その他のキーはfalseを返してイベントを残します。
※キーを押している間のリピートキーは破棄します。
※イベントが取れるか取れないかはフォーカス次第です。

キーを離した時の処理

※KeyReleaseイベントはオブジェクトであまり使われてないようで、大体取れそうです。

KeyPressイベントの取得

無理にKeyPressを取る

どうしても一括処理でKeyPressイベントを取りたい場合は、 ShortcutOverrideで取得します。
ただし、同じキーが2個セット(PressとRelease?)で来るので注意が必要です。

イベント関数の宣言 (mainwindow.h)

イベントフィルターと処理関数を宣言します。

イベントフィルターのインストール (mainwindow.cpp)

コンストラクタでイベントフィルターをインストールします。

イベント処理

ShortcutOverrideでKeyPressを取ります。

キーを押した時の処理

スイッチを使って1個捨てます。

オブジェクトのイベント

オブジェクトのイベントについて

イベントフィルターで、フォーカスを持つオブジェクトのイベントを処理することができます。
オブジェクトにイベントフィルターをインストールして、そのイベント処理をオーバーライドします。

例として、KeyPressとKeyReleaseイベントを破棄して、スライダーのキー操作ができないようにします。
通常は水平スライダーをクリックしてフォーカスし、右矢印(→)キーを押すと右方向に移動します。
以下の方法でキーイベントを破棄すれば、キーを押してもスライダーのハンドルが動かなくなります。

イベント関数の宣言 (mainwindow.h)

イベントフィルターとスライダー作成関数を宣言します。

スライダー作成 (mainwindow.cpp)

水平スライダーを作ってイベントフィルターをインストールします。
※このスライダーを作っただけでは表示されません。適当な所に配置する必要があります。

キーイベントの破棄 (mainwindow.cpp)

オブジェクトがQSliderの場合は、KeyPressとKeyReleaseを破棄します。
※イベントフィルターをインストールしてないQSliderのキーイベントは破棄されません。

【補足】全部にフィルター

フォーカス可能な全ウィジェットにイベントフィルターをインストールする方法です。
※これをやっておけば、すべてのイベントをeventFilterで処理することができます、多分。

ラベルのクリック

ラベルのクリックイベントについて

ラベル(QLabel)でも、クリックイベントを取りたい時があります。
クラスを作るのも面倒なので、イベントフィルターでMouseButtonPressを取ってそれっぽく処理します。

イベント関連の宣言 (mainwindow.h)

イベントフィルターとラベル作成関数を宣言します。

ラベル作成 (mainwindow.cpp)

ラベルにオブジェクト名を付けて、イベントフィルターをインストールします。
※このラベルを作っただけでは表示されません。適当な所に配置する必要があります。

イベント処理 (mainwindow.cpp)

オブジェクト名を調べて、目的のラベルの場合はテキストをデバッグ出力します。
※この方法ではダブルクリックもクリックと見なします。

ラベルのダブルクリック

ダブルクリックイベントについて

厄介なことに、MouseButtonDblClickイベントには必ずMouseButtonPressが付いています。
それもMouseButtonDblClickの直前にMouseButtonPressがあるという面倒な事態です。
つまり単独のクリックなのか、クリック+ダブルクリックの2個セットなのか、 最初のクリックが来た時には分からないわけです。
あまり気は進みませんが、タイマーを使ってMouseButtonPressをやり過ごすのが、 もっともシンプルな手段かと思います。

イベント関連の宣言 (mainwindow.h)

タイマーのスロット関数と、MouseButtonPressイベント発生ラベルの保存用ポインタを宣言します。

ポインタ初期化 (mainwindow.cpp)

コンストラクタで保存用ポインタを初期化します。
※これをしないと実行時にエラーが発生します。

ラベル作成 (mainwindow.cpp)

ラベルにオブジェクト名を付けて、イベントフィルターをインストールします。
※このラベルを作っただけでは表示されません。適当な所に配置する必要があります。

イベント処理 (mainwindow.cpp)

イベントフィルターインストール済の全QLabelを対象に、クリックとダブルクリックを取得します。
MouseButtonPressでオブジェクトがQLabelの時、 保存用ポインタが空の場合はラベルを保存してタイマーをセットします。
MouseButtonDblClickでは、保存用ポインタをクリアしてオブジェクト名をデバッグ出力します。
※保存用ポインタがクリアされるまでは、QLabelのMouseButtonPressを無視します。
※タイマーは300ミリ秒にしていますが、環境によっては500ミリ秒ぐらいのほうが安全かもしれません。

タイマーイベント処理 (mainwindow.cpp)

保存用ポインタが空でなければ、オブジェクト名をデバッグ出力してからクリアします
※保存用ポインタが空でなければMouseButtonDblClickが来なかったということで、 単独のMouseButtonPressと見なします。

複合キーとシグナル

シグナル送信について

あまり使う機会があるとは思いませんが、一応使い方だけはメモしておきます。

例として、[Ctrl]+[Shift]+[↓]キーを同時に押した時にシグナルを発行します。
シグナルを受け取った関数は、メッセージボックスを表示してプログラムを終了します。
わざわざシグナルを使わなくてもスロット関数を呼べば済むことですが。

イベント関連の宣言 (mainwindow.h)

※シグナルは任意の名前を付けることができます。

初期処理 (mainwindow.cpp)

コンストラクタでシグナルを終了処理関数に接続し、イベントフィルターをインストールします。
※シグナルの送信側と受信側の両方に自分(this)を指定しています。

イベント処理 (mainwindow.cpp)

キーが下矢印(↓)の場合は、 シフトキーとコントロールキーが押されていることを確認してシグナルを発行します。
※フォーカスにかかわらずキーのイベントが取れるようにShortcutOverrideで拾っています。

■併用キー[enum Qt::KeyboardModifier]
定義説明
Qt::NoModifierなし
Qt::ShiftModifierShift
Qt::ControlModifier Ctrl
Qt::AltModifierAlt
Qt::MetaModifierMeta
Qt::KeypadModifierKeypad

終了処理 (mainwindow.cpp)

シグナルの接続を解除してから、メッセージを表示してプログラムを終了します。
※この関数は1回限りです。
※closeEventでignoreを指定して戻ってきても、 接続が切れているのでこの関数が呼ばれることはありません。

タイマーイベント

タイマー使用例

ダイアログを開いて画像を表示します。
1秒ごとに画像の大きさを変化させます。

イベント関連の宣言 (mainwindow.h)

ダイアログ表示 (mainwindow.cpp)

モーダルダイアログに画像を貼り付けて、1秒のタイマーをセットしてから表示します。
※ダイアログが閉じたらタイマーを解除します。
※deleteでダイアログを破棄していますが、必要ないかもしれません。

イベント処理 (mainwindow.cpp)

ラベルのサイズを32pxから200pxの範囲で16pxずつ増減します。
adjustGazoSizeの値によって大きくするか小さくするか決めます。
※タイマーのイベントはすべてこの関数に来るので、タイマーIDで処理を振り分けます。
※adjustGazoSizeには1か-1が入ります。

ダイアログ終了処理 (mainwindow.cpp)

送信元のウインドウがダイアログの場合は、doneで終了します。
※doneの引数はexecの戻り値になります。

2016年12月