Qt イベント配信と関連するパフォーマンスの問題

Qt を使用する場合、mousePressEvent やeventFilter などの仮想関数を書き換えることでイベントを処理できることは誰もが知っていますが、インターフェイスにイベントを送信するとき、コントロールとその親コン​​トロールの間のイベント配信プロセスはどのようなものになるでしょうか?
この記事では、下図に示すインターフェースを例に、Qt イベント配信のプロセスをソースコードに基づいて紹介します。
親から子への関係は、
MyWindow->MyButton->MyEdit です。
ここに画像の説明を挿入します

プログラムを起動後、「MyEdit」をマウスでクリックすると、以下のようにイベント配信が行われます。

最初のステップでは、QCoreApplication (qApp) がイベントを処理します。

プロセス

ここに画像の説明を挿入します
ここでクリック イベントを最後に受け取るのは、トップレベル コントロールです。この例では MyEdit です。

関連するソースコード

ソースコードは実行順に並べられています。
qwindowsysteminterface.cpp->QWindowSystemInterface::sendWindowSystemEvents
ここに画像の説明を挿入します
qapplication->QApplicationPrivate::notify_helper
ここに画像の説明を挿入します
qwidgetwindow->QWidgetWindow::handleMouseEvent
ここに画像の説明を挿入します
childAt プロセス:
1. MyWindow の子コントロールをトラバースし、クリック座標を含む MyButton を見つけます。
2. MyButton のサブコントロールを移動し、クリック座標を含む MyEdit を見つけます。
3. MyEdit のサブコントロールを移動し、サブコントロールが見つからない場合は、MyEdit に戻ります。

2 番目のステップでは、コントロールがイベントを処理します。

プロセス

ここに画像の説明を挿入します
このプロセスは少し複雑に思えますが、次の点を言葉で表現します。
1. 各コントロールがイベントを処理するとき、最初に qApp によってインストールされたイベント フィルター、つまり qApp->installEventFilter によってインストールされたイベント フィルターが実行されます。次に、自分でインストールしたイベント フィルター、つまり this->installEventFilter でインストールしたイベント フィルターを実行し、最後に MousePressEvent などのイベント処理を実行します。
2. コントロールがイベントを処理するとき、setAccepted を使用してイベントが吸収されるかどうかを設定できます。setAccepted(true) の場合、イベントは親コントロールに渡されません。
3. イベント処理は、すべてのレベルで子コントロールから親コントロールに渡されます。
4. true を返すイベント フィルターと e->setAccepted(true) は両方とも、親コントロールへのイベントの送信をブロックできますが、イベント フィルターで e->setAccepted を設定しても、現在のコントロールのイベント処理は妨げられません。

関連するソースコード

qapplication->QApplication::notify
ここに画像の説明を挿入します
qapplication->QApplicationPrivate::notify_helper
ここに画像の説明を挿入します

要約する

Qt のイベント配信は大まかに上記の 2 ステップですが、詳細を説明する必要があるため、あまり直感的ではありませんが、全体のプロセスを示す概略コードを以下に示します。
既存の appFilter(obj, e) および widgetFilter(obj, e) イベント フィルターは、それぞれ qApp と各コントロールによってインストールされます。

void handle(systemEvent)
{
    
    
    e = toEvent(systemEvent); //转换为QEvent

    /* qApp的事件过滤 */
    if (appFilter(systemEvent.qApp, e) == true)
        return;

    w = qApp.m_widget.childAt(e.pos());  //获取MyEdit

    while (w)
    {
    
    
        /* 事件过滤 */
        if (appFilter(w, e) == true)
            break;
        if (widgetFilter(w, e) == true)
            break;

        w.event(e); //事件处理

        /* 判断事件是否被接收 */
        if (e.isAccept())
            break;

        w = w.parentWidget();
    }
}

関連するパフォーマンスの問題

上記の例からわかるように、コントロールをクリックすると、2 つのステップがあります。
1. Qt はまずウィンドウから MyWindow を見つけ、次に MyWindow から MyButton を見つけ、次に MyButton から MyEdit を見つけます。
2. MyEdit を見つけた後、MyEdit のイベント フィルタリングとイベント処理が実行され、その後、MyButton と MyWindow に対して同様の操作が順番に実行されます。
これら 2 つの手順により、パフォーマンス上の問題が発生します。
1. MyEdit を見つけるプロセスは再帰的であるため、関数スタックのオーバーヘッドが生じます。
2. MyEdit の親コントロールが本当にクリック イベントを処理する必要がある場合、イベント フィルタリングとイベント処理を複数回呼び出す必要がありますが、描画イベントが渡された場合はどうなるでしょうか。子コントロールが描画イベントを受信すると、このイベントも親コントロールに渡され、UI のパフォーマンスを向上させ、不要な更新を減らすことが 1 つの方向です。

テストを行って、MyWindow で次の変更を加えてみましょう。
ここに画像の説明を挿入します
複数の QFrame コントロールをネストして配置します。
MyButton コントロールを作成し、frame_14 とウィンドウの子コントロールとして使用した場合の更新にかかる時間を比較します。

Update は基本的に、描画イベントを送信してコントロールを返します。

インターフェイスのボタン バインディング スロット。

void MyWindow::on_pushButton_clicked()
{
    
    
    QElapsedTimer timer;
    timer.start();

    for (int i = 0; i < 1000000; ++i)
        button->update();

    qDebug() << "cost time: " << timer.elapsed() << "ms";
}

テスト結果:
ウィンドウのサブコントロールとして使用した場合:
ここに画像の説明を挿入します
Frame_14 のサブコントロールとして使用した場合:
ここに画像の説明を挿入します
大きな違いがあることがわかります。
したがって、UI アーキテクチャを設計するときは、レイヤーが多すぎることを避ける必要があります。

おすすめ

転載: blog.csdn.net/weixin_45001971/article/details/130658132