イベントシステム

Qt では、イベントはQEvent という抽象クラスから派生したオブジェクトで、アプリケーショ ン内部で起こったこと、あるいは外部のアクティビティの結果として起こったことのうち、アプリケーショ ンが知る必要のあるものを表します。イベントは、QObject のサブクラスのどのインスタンスでも受信して処理することができますが、特にウィジェットに関係します。このドキュメントでは、典型的なアプリケーションでイベントがどのように配信され、処理されるかについて説明します。

イベントの配信方法

イベントが発生すると、Qt は適切なQEvent サブクラスのインスタンスを作成することで、そのイベントを表すイベントオブジェクトを作成し、event() 関数を呼び出すことでQObject の特定のインスタンス(またはそのサブクラスの 1 つ)にイベントを配信します。

この関数はイベント自体を処理しない。配信されたイベントのタ イプに基づいて、その特定のタイプのイベントハンドラを呼び出し、イ ベントが受け入れられたか無視されたかに基づいて応答を送信する。

QMouseEventQKeyEvent のようにウィンドウ・システムから来るイベントもあれば、QTimerEvent のように他のソースから来るイベントもあり、アプリケーション自体から来るイベントもあります。

イベントタイプ

ほとんどのイベント・タイプには特別なクラスがあり、特にQResizeEventQPaintEventQMouseEventQKeyEventQCloseEvent が有名です。各クラスはQEvent をサブクラス化し、イベント固有の関数を追加します。例えば、QResizeEvent は、size() とoldSize() を追加し、ウィジェットがどのように寸法が変更されたかを検出できるようにします。

QMouseEvent は、マウスボタンの押下、ダブルクリック、移動、その他の関連操作をサポートしています。

各イベントは、QEvent::Type で定義された、関連する型を持っており、これは、与えられたイベントオブジェクトがどのサブクラスから構築されたかを素早く決定するための、ランタイム型情報の便利なソースとして使用することができます。

プログラムは多様で複雑な方法で反応する必要があるため、Qt のイベント配信メカニズムは柔軟です。QCoreApplication::notifyQt Quarterlyの Another Look at Eventsという記事では、あまり簡潔に説明されていません。ここでは、95%のアプリケーションに必要なことを説明します。

イベントハンドラ

イベントが配信される通常の方法は、仮想関数を呼び出すことです。例えば、QPaintEventQWidget::paintEvent() を呼び出すことで配信される。この仮想関数は、通常ウィジェットを再描画するなど、適切に反応する責任を負います。仮想関数の実装で必要な処理をすべて行わない場合は、ベース・クラスの実装を呼び出す必要があるかもしれません。

たとえば、次のコードは、カスタムチェックボックス・ウィジェットのマウスの左ボタン・クリックを処理する一方で、他のすべてのボタン・クリックをベース・クラスQCheckBox に渡しています:

void MyCheckBox::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        // handle left mouse button here
    } else {
        // pass on other buttons to base class
        QCheckBox::mousePressEvent(event);
    }
}

基底クラスの関数を置き換える場合は、すべて自分で実装する必要があります。しかし、基底クラスの機能を拡張したいだけなら、望むものを実装し、処理したくないケースのデフォルト動作を得るために基底クラスを呼び出します。

時には、そのようなイベント固有の関数がなかったり、イベント固有の関数が十分でなかったりすることがある。最も一般的な例は、Tabキーの押下です。通常、QWidget は、キーボード・フォーカスを移動するために、これをインターセプトしますが、いくつかのウィジェットは、それ自身のためにTabキーを必要とします。

これらのオブジェクトは、一般的なイベント・ハンドラであるQObject::event() を再実装し、通常の処理の前後にイベント処理を行うか、関数を完全に置き換えることができます。Tabを解釈し、アプリケーション固有のカスタム・イベントを持つ非常に珍しいウィジェットは、次のevent ()関数を含むかもしれません:

bool MyWidget::event(QEvent *event)
{
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent *ke = static_cast<QKeyEvent *>(event);
        if (ke->key() == Qt::Key_Tab) {
            // special tab handling here
            return true;
        }
    } else if (event->type() == MyCustomEventType) {
        MyCustomEvent *myEvent = static_cast<MyCustomEvent *>(event);
        // custom event handling here
        return true;
    }

    return QWidget::event(event);
}

QWidget::event() は、まだ処理されないすべてのケースで呼び出され、戻り値は、イベントが処理されたかどうかを示すことに注意してください;true 値は、イベントが他のオブジェクトに送信されるのを防ぎます。

イベントフィルター

時々、オブジェクトは他のオブジェクトに送られるイベントを見たり、場合によってはインターセプトしたりする必要があります。例えば、ダイアログは一般的に、いくつかのウィジェットのキー押下をフィルタリングしたいと考えます。

QObject::installEventFilter() 関数は、イベントフィルタを設定することで、これを可能にします。これにより、指名されたフィルタオブジェクトは、QObject::eventFilter() 関数でターゲットオブジェクトのイベントを受け取るようになります。イベント・フィルターは、ターゲット・オブジェクトがイベントを処理する前にイベントを処理するようになり、必要に応じてイベントを検査したり破棄したりできるようになります。既存のイベント・フィルターは、QObject::removeEventFilter ()関数を使用して削除することができます。

フィルター・オブジェクトのeventFilter() 実装が呼び出されると、イベントを受諾または拒否し、イベントのさらなる処理を許可または拒否することができる。すべてのイベント・フィルタがイベントのさらなる処理を許可する場合(それぞれfalse を返す)、イベントはターゲット・オブジェクト自身に送られます。そのうちの1つが(true を返すことによって)処理を停止した場合、ターゲットとそれ以降のイベント・フィルターはイベントをまったく見ることができません。

bool FilterObject::eventFilter(QObject *object, QEvent *event)
{
    if (object == target && event->type() == QEvent::KeyPress) {
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
        if (keyEvent->key() == Qt::Key_Tab) {
            // Special tab handling
            return true;
        } else
            return false;
    }
    return false;
}

上記のコードは、特定のターゲット・ウィジェットに送られるTabキー押下イベントをインターセプトする別の方法を示しています。この場合、フィルタは関連するイベントを処理し、true を返して、それ以上処理されないようにします。他のイベントはすべて無視され、フィルタはfalse を返し、ターゲットウィジェットにインストールされている他のイベントフィルタを経由して、ターゲットウィジェットに送られるようにします。

また、QApplication またはQCoreApplication オブジェクトにイベントフィルターをインストールすることで、アプリケーション全体のすべてのイベントをフィルターすることも可能です。このようなグローバル・イベント・フィルターは、オブジェクト固有のフィルターの前に呼び出されます。これは非常に強力ですが、アプリケーション全体のすべてのイベントの配信が遅くなります。

イベントの送信

多くのアプリケーションは、独自のイベントを作成して送信したいと考えます。適切なイベントオブジェクトを作成し、QCoreApplication::sendEvent() やQCoreApplication::postEvent() を使って送信することで、Qt独自のイベントループとまったく同じ方法でイベントを送信することができます。

sendEvent() はイベントを即座に処理します。()はイベントを即座に処理し、それが戻ったときには、イベント・フィルタやオブジェクト自体がすでにイベントを処理しています。多くのイベント・クラスには、isAccepted ()と呼ばれる関数があり、最後に呼ばれたハンドラによってイベントが受け入れられたか拒否されたかを教えてくれます。

postEvent()は、後でディスパッチするためにキューにイベントをポストします。次にQtのメイン・イベント・ループが実行されると、いくつかの最適化を行いながら、ポストされたすべてのイベントをディスパッチします。例えば、リサイズイベントが複数ある場合、それらは1つに圧縮されます。ペイントイベントも同様です:QWidget::update() はpostEvent() を呼び出し、ちらつきをなくし、複数の再描画を避けることで速度を向上させます。

postEvent() は、オブジェクトの初期化中にも使用されます。通常、ポストされるイベントは、オブジェクトの初期化が完了した直後にディスパッチされるからです。ウィジェットを実装する際に重要なことは、イベントはそのライフタイムの非常に早い段階で配信される可能性があることを認識することです。したがって、コンストラクタでは、イベントを受け取る可能性がある前に、早い段階でメンバ変数を初期化するようにしてください。

カスタム・タイプのイベントを作成するには、イベント番号を定義する必要があります。この番号はQEvent::User より大きくなければなりません。また、カスタム・イベントに関する特定の情報を渡すために、QEvent をサブクラス化する必要があるかもしれません。詳細はQEvent のドキュメントを参照してください。

本ドキュメントに含まれる文書の著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。