【Qt入門】QGraphicsView::keyReleaseEvent()を使ったインタラクティブな描画アプリ開発

2025-05-27

QGraphicsView::keyReleaseEvent() は、Qt の Graphics View フレームワークで使用される仮想関数で、QGraphicsView ウィジェットがキーボードのキーが離された(リリースされた)イベントを受け取った際に呼び出されます。

これは QGraphicsView クラスの保護された仮想関数です。つまり、通常は直接呼び出すのではなく、QGraphicsView を継承したカスタムクラスを作成し、その中でこの関数を**オーバーライド(再実装)**することで、キーが離された時の独自の振る舞いを定義します。

引数: QKeyEvent *event

処理の流れ

  1. ユーザーが QGraphicsView ウィジェットにフォーカスがある状態でキーボードのキーを離す。
  2. Qt のイベントシステムがこのキーリリースイベントを検出する。
  3. QGraphicsView オブジェクトの keyReleaseEvent() 関数が呼び出される。
  4. デフォルトの QGraphicsView::keyReleaseEvent() の実装では、このキーイベントを QGraphicsScene に伝播し、さらにシーン上の適切な QGraphicsItem にイベントが渡されます。これにより、シーン上のアイテムがキーリリースイベントに応答できるようになります。

keyReleaseEvent() をオーバーライドする理由

QGraphicsView のデフォルトのキー操作(例えば、カーソルキーでのスクロールなど)を変更したい場合や、特定のキーが離されたときにカスタムのアクションを実行したい場合に、この keyReleaseEvent() をオーバーライドします。


例えば、特定のキー(例: 'Delete' キー)が離されたときに、選択中のアイテムをシーンから削除するような機能を実装する場合、以下のように QGraphicsView を継承したクラスで keyReleaseEvent() をオーバーライドします。

#include <QGraphicsView>
#include <QKeyEvent>
#include <QGraphicsScene>
#include <QGraphicsItem>

class MyGraphicsView : public QGraphicsView
{
public:
    MyGraphicsView(QGraphicsScene *scene, QWidget *parent = nullptr)
        : QGraphicsView(scene, parent)
    {}

protected:
    void keyReleaseEvent(QKeyEvent *event) override
    {
        if (event->key() == Qt::Key_Delete) {
            // "Delete" キーが離された場合
            QList<QGraphicsItem*> selectedItems = scene()->selectedItems();
            for (QGraphicsItem* item : selectedItems) {
                scene()->removeItem(item);
                delete item; // アイテムをメモリから解放
            }
        } else {
            // それ以外のキーリリースイベントは、親クラスの処理に任せる
            QGraphicsView::keyReleaseEvent(event);
        }
    }
};

この例では、Delete キーが離されたときに選択されているすべてのアイテムをシーンから削除し、それ以外のキーリリースイベントは、QGraphicsView のデフォルトの動作(例えば、シーンにイベントを伝播する)に任せています。

注意点

  • フォーカス
    QGraphicsView がキーイベントを受け取るためには、そのウィジェットがフォーカスを持っている必要があります。
  • イベントの伝播
    keyReleaseEvent() をオーバーライドする場合、特定のキーを処理した後に、QGraphicsView::keyReleaseEvent(event); を呼び出すかどうかを考慮する必要があります。これを呼び出すと、イベントは引き続き QGraphicsScene やその上の QGraphicsItem に伝播されます。もし、独自の処理でイベントを完全に消費し、それ以上伝播させたくない場合は、親クラスのメソッドを呼び出さないことも可能です。


QGraphicsView::keyReleaseEvent() を扱う際に遭遇しやすい問題はいくつかあります。主なものと、それらの解決策を見ていきましょう。

keyReleaseEvent が全く発生しない

原因

  • イベントの伝播停止
    別のイベントハンドラがイベントを消費し、それ以上伝播させていない。
  • イベントフィルタリング
    親ウィジェットや他のオブジェクトがイベントフィルタを設定していて、イベントを途中で消費している可能性があります。
  • フォーカスがない
    QGraphicsView ウィジェットがキーボードフォーカスを持っていないと、キーイベントを受け取りません。

トラブルシューティング

  • イベントフィルタの確認
    親ウィジェットやアプリケーション全体にインストールされているイベントフィルタを確認し、keyReleaseEvent をブロックしていないか調べます。
  • クリックによるフォーカス取得
    ユーザーが QGraphicsView をクリックしたときにフォーカスが移るように、setFocusPolicy を適切に設定します。
  • フォーカスの設定
    QGraphicsView にフォーカスを設定します。
    • view->setFocusPolicy(Qt::StrongFocus); または Qt::WheelFocus など、適切なフォーカスポリシーを設定します。
    • view->setFocus(); を呼び出して、プログラム的にフォーカスを与えます。
    • アプリケーション起動時に自動的にフォーカスを与える場合は、Qt::StrongFocus を設定し、setFocus() を呼び出すのが一般的です。

keyReleaseEvent が意図したとおりにオーバーライドされない/呼び出されない

原因

  • 多重継承の問題
    複数のクラスから継承している場合、意図しない基底クラスの関数が呼び出されている可能性があります。
  • 仮想関数のオーバーライド忘れ
    keyReleaseEvent は仮想関数であり、継承したクラスで正確に override キーワードを付けて再実装する必要があります。スペルミスや引数の不一致があると、新しい関数が定義されてしまい、仮想関数がオーバーライドされません。

トラブルシューティング

  • デバッガの使用
    keyReleaseEvent 関数の先頭にブレークポイントを設定し、実際にこの関数が呼び出されているか、いつ呼び出されているかを確認します。
  • 関数のシグネチャの一致
    引数の型(QKeyEvent *event)が正確に一致しているか確認します。
  • override キーワードの確認
    void MyGraphicsView::keyReleaseEvent(QKeyEvent *event) override のように、override キーワードが正しく付加されているか確認します。これにより、コンパイラがオーバーライドのミスを検出してくれます。

イベントを処理しても、デフォルトの動作が実行されてしまう

原因

  • イベントの消費不足
    イベントを処理したにもかかわらず、event->accept() を呼び出すのを忘れているか、またはイベントを完全に消費しないと、他のイベントハンドラがそのイベントを受け取ってしまう可能性があります。
  • 親クラスの呼び出し
    keyReleaseEvent をオーバーライドした関数内で、QGraphicsView::keyReleaseEvent(event); を呼び出している場合、イベントは引き続きデフォルトの動作(例えば、シーンへの伝播)に引き渡されます。

トラブルシューティング

  • event->accept() の使用
    通常、イベントを処理してそれ以上伝播させたくない場合は、event->accept() を呼び出すのが慣習です。ただし、keyReleaseEvent の場合は、親クラスのメソッドを呼び出さないことでイベント伝播を停止させる方が直感的かもしれません。event->ignore() は、イベントを無視し、親ウィジェットなどに伝播させたい場合に使用します。
  • 親クラスの呼び出しの制御
    • 独自の処理でイベントを完全に消費し、デフォルトの動作をさせたくない場合は、QGraphicsView::keyReleaseEvent(event);呼び出さないようにします。
    • デフォルトの動作も行いつつ、追加で独自の処理をしたい場合は、まず独自の処理を行い、その後に QGraphicsView::keyReleaseEvent(event); を呼び出します。

QGraphicsItem がキーリリースイベントを受け取らない

原因

  • ItemIsFocusable フラグの欠如
    QGraphicsItem がキーボードフォーカスを受け取れるように、setFlags(ItemIsFocusable) を設定していない。
  • QGraphicsItem でのキーイベント処理の欠如
    QGraphicsItemkeyReleaseEvent をオーバーライドしていない、またはその中でイベントを処理していない。
  • QGraphicsView からの伝播停止
    上記の「イベントを処理しても、デフォルトの動作が実行されてしまう」問題に関連し、QGraphicsView 側でイベントの伝播が停止されている可能性があります。
  • アイテムのフォーカス
    QGraphicsItem がキーイベントを受け取るためには、そのアイテム自身がフォーカスを持っている必要があります。

トラブルシューティング

  • QGraphicsItem::keyReleaseEvent のオーバーライド
    class MyGraphicsItem : public QGraphicsRectItem
    {
    public:
        MyGraphicsItem(const QRectF& rect, QGraphicsItem* parent = nullptr)
            : QGraphicsRectItem(rect, parent)
        {
            setFlags(QGraphicsItem::ItemIsFocusable); // フォーカス可能にする
        }
    
    protected:
        void keyReleaseEvent(QKeyEvent *event) override
        {
            if (event->key() == Qt::Key_Space) {
                qDebug() << "Space key released on item!";
                event->accept(); // イベントを消費
            } else {
                QGraphicsItem::keyReleaseEvent(event); // デフォルトの動作
            }
        }
    };
    
  • QGraphicsView でのイベント伝播の確認
    QGraphicsViewkeyReleaseEvent オーバーライドで、QGraphicsView::keyReleaseEvent(event); を呼び出しているか確認します。これを呼び出さないと、イベントはシーンやアイテムには伝播しません。
  • アイテムにフォーカスを設定
    • item->setFlags(QGraphicsItem::ItemIsFocusable); を呼び出します。
    • item->setFocus(); を呼び出して、プログラム的にフォーカスを与えます。

特定のキーだけが反応しない

原因

  • 修飾キーの考慮不足
    Shift, Ctrl, Alt などの修飾キーとの組み合わせでイベントを処理したい場合、event->modifiers() を確認する必要があります。
  • キーコードの誤り
    event->key() で比較しているキーコードが間違っている可能性があります。
  • 修飾キーのチェック
    特定の修飾キーとの組み合わせで反応させたい場合は、if ((event->modifiers() & Qt::ControlModifier) && event->key() == Qt::Key_S) のように、event->modifiers() をチェックします。
  • 正確なキーコードの使用
    Qt::Key_A, Qt::Key_Space, Qt::Key_Delete など、Qt::Key 列挙子を正確に使用しているか確認します。


QGraphicsView::keyReleaseEvent() を使用する最も一般的なケースは、QGraphicsView を継承したカスタムビューを作成し、その中で特定のキーが離されたときの動作を定義することです。

基本的なオーバーライドとキーの検出

この例では、ユーザーが Delete キーを離したときに、現在選択されているすべてのアイテムをシーンから削除するカスタムビューを作成します。

mygraphicsview.h

#ifndef MYGRAPHICSVIEW_H
#define MYGRAPHICSVIEW_H

#include <QGraphicsView>
#include <QKeyEvent> // QKeyEvent を使用するために必要

class MyGraphicsView : public QGraphicsView
{
    Q_OBJECT // シグナル/スロットを使用する場合は必須

public:
    // コンストラクタ: QGraphicsScene へのポインタを受け取る
    MyGraphicsView(QGraphicsScene *scene, QWidget *parent = nullptr);

protected:
    // keyReleaseEvent をオーバーライド(再実装)する
    void keyReleaseEvent(QKeyEvent *event) override;
};

#endif // MYGRAPHICSVIEW_H

mygraphicsview.cpp

#include "mygraphicsview.h"
#include <QGraphicsScene> // QGraphicsScene のremoveItem, selectedItems を使用するために必要
#include <QDebug>         // デバッグ出力のために必要

MyGraphicsView::MyGraphicsView(QGraphicsScene *scene, QWidget *parent)
    : QGraphicsView(scene, parent)
{
    // キーイベントを受け取るためにフォーカスポリシーを設定
    setFocusPolicy(Qt::StrongFocus);
    // マウスでクリックしたときにフォーカスが移るように設定することもできます
    // setMouseTracking(true); // 必要に応じて
}

void MyGraphicsView::keyReleaseEvent(QKeyEvent *event)
{
    qDebug() << "キーが離されました。キーコード:" << event->key();

    if (event->key() == Qt::Key_Delete) {
        qDebug() << "Delete キーが離されました。選択アイテムを削除します。";

        // 現在シーン上で選択されているすべてのアイテムを取得
        QList<QGraphicsItem*> selectedItems = scene()->selectedItems();

        // 各アイテムを削除
        for (QGraphicsItem* item : selectedItems) {
            scene()->removeItem(item); // シーンからアイテムを削除
            delete item;               // メモリからアイテムを解放
        }
        event->accept(); // イベントを処理済みとしてマークし、それ以上の伝播を停止
    } else {
        // その他のキーイベントは、親クラスのデフォルトの処理に任せる
        // これにより、ビューのデフォルトのスクロール動作などが維持されます
        QGraphicsView::keyReleaseEvent(event);
    }
}

main.cpp (アプリケーションのエントリポイント)

#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsRectItem> // 四角形アイテムのために必要
#include "mygraphicsview.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    // シーンを作成
    QGraphicsScene scene;
    scene.setSceneRect(0, 0, 800, 600); // シーンの範囲を設定

    // シーンにいくつかのアイテムを追加
    QGraphicsRectItem *rect1 = scene.addRect(10, 10, 80, 80, QPen(Qt::black), QBrush(Qt::blue));
    QGraphicsRectItem *rect2 = scene.addRect(100, 50, 60, 60, QPen(Qt::black), QBrush(Qt::red));
    QGraphicsEllipseItem *ellipse1 = scene.addEllipse(200, 20, 70, 70, QPen(Qt::black), QBrush(Qt::green));

    // アイテムを選択可能にする
    rect1->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable);
    rect2->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable);
    ellipse1->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable);

    // カスタムビューを作成し、シーンを設定
    MyGraphicsView view(&scene);
    view.setWindowTitle("カスタム QGraphicsView のキーリリースイベント");
    view.resize(850, 650); // ウィンドウサイズを設定
    view.show();           // ビューを表示

    // ビューにフォーカスを設定 (重要!)
    view.setFocus();

    return a.exec();
}

この例のポイント

  • setFocusPolicy(Qt::StrongFocus);
    QGraphicsView がキーイベントを受け取るためには、フォーカスを持っている必要があります。Qt::StrongFocus は、タブキーやクリックでフォーカスを受け取ることを可能にします。main.cppview.setFocus(); を呼び出すことも重要です。
  • event->accept() vs. QGraphicsView::keyReleaseEvent(event);
    • event->accept() を呼び出すと、イベントは処理済みとマークされ、それ以上他のイベントハンドラに伝播しません。この例では、Delete キーの処理が終わった後、他のオブジェクトにこのキーリリースイベントが渡されるのを防いでいます。
    • QGraphicsView::keyReleaseEvent(event); を呼び出すと、イベントは親クラスのデフォルトの実装に渡されます。これにより、ビューの標準的なキーボード操作(例えば、カーソルキーでのスクロールなど)が引き続き機能します。特定のキー(この場合は Delete)以外は、このデフォルトの動作を継続させたい場合に重要です。
  • scene()->removeItem(item); delete item;
    アイテムをシーンから削除し、メモリも解放します。
  • scene()->selectedItems()
    現在シーン上で選択されているすべての QGraphicsItem のリストを取得します。
  • QKeyEvent の利用
    event->key() を使って、離されたキーのキーコードを取得します。Qt::Key_Delete のような Qt::Key 列挙子と比較することで、特定のキーを識別できます。
  • keyReleaseEvent のオーバーライド
    protected セクションで void keyReleaseEvent(QKeyEvent *event) override; と宣言し、.cpp ファイルで実装します。
  • MyGraphicsView の継承
    QGraphicsView を継承して MyGraphicsView クラスを作成します。

修飾キーとの組み合わせ

特定の修飾キー(Shift, Ctrl, Alt)が押されている状態でキーが離されたことを検出する例です。

// MyGraphicsView::keyReleaseEvent の中
void MyGraphicsView::keyReleaseEvent(QKeyEvent *event)
{
    qDebug() << "キーが離されました。キーコード:" << event->key() << "修飾キー:" << event->modifiers();

    // Ctrl キーが押された状態で 'S' キーが離された場合
    if (event->key() == Qt::Key_S && (event->modifiers() & Qt::ControlModifier)) {
        qDebug() << "Ctrl + S が離されました。保存アクションを実行します。";
        // ここに保存処理のコードを記述
        event->accept();
    }
    // Alt キーが押された状態で 'Z' キーが離された場合
    else if (event->key() == Qt::Key_Z && (event->modifiers() & Qt::AltModifier)) {
        qDebug() << "Alt + Z が離されました。ズームリセットアクションを実行します。";
        // ここにズームリセット処理のコードを記述
        event->accept();
    }
    else {
        // それ以外のキーイベントは親クラスに任せる
        QGraphicsView::keyReleaseEvent(event);
    }
}

QGraphicsItem 側でのキーイベント処理

もし、キーイベントを QGraphicsView ではなく、特定の QGraphicsItem に処理させたい場合は、QGraphicsItem を継承したカスタムアイテムを作成し、その中で keyReleaseEvent をオーバーライドします。

mygraphicsitem.h

#ifndef MYGRAPHICSITEM_H
#define MYGRAPHICSITEM_H

#include <QGraphicsRectItem>
#include <QKeyEvent>

class MyGraphicsItem : public QGraphicsRectItem
{
public:
    MyGraphicsItem(const QRectF& rect, QGraphicsItem *parent = nullptr);

protected:
    // キーリリースイベントをオーバーライド
    void keyReleaseEvent(QKeyEvent *event) override;
};

#endif // MYGRAPHICSITEM_H

mygraphicsitem.cpp

#include "mygraphicsitem.h"
#include <QDebug>

MyGraphicsItem::MyGraphicsItem(const QRectF& rect, QGraphicsItem *parent)
    : QGraphicsRectItem(rect, parent)
{
    // アイテムがキーボードフォーカスを受け取れるようにする
    setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
}

void MyGraphicsItem::keyReleaseEvent(QKeyEvent *event)
{
    qDebug() << "アイテム上でキーが離されました。キーコード:" << event->key();

    if (event->key() == Qt::Key_Space) {
        qDebug() << "アイテム上で Space キーが離されました!";
        // ここにアイテム固有の処理を記述
        event->accept(); // イベントを処理済みとしてマーク
    } else {
        // 他のキーイベントは、親クラスのデフォルトの処理に任せる
        // これにより、アイテムのデフォルトの移動動作などが維持されます
        QGraphicsItem::keyReleaseEvent(event);
    }
}

main.cpp でこのカスタムアイテムを使用する場合、QGraphicsView はデフォルトでキーイベントをフォーカスを持つ QGraphicsItem に伝播させます。

main.cpp での利用例 (抜粋)

    // シーンにカスタムアイテムを追加
    MyGraphicsItem *customItem = new MyGraphicsItem(QRectF(150, 150, 100, 100));
    customItem->setBrush(Qt::yellow);
    scene.addItem(customItem);

    // customItem にフォーカスを設定
    customItem->setFocus();

この方法では、ユーザーが QGraphicsView 上で customItem をクリックしてフォーカスを与えたり、タブキーでフォーカスを移動させたりすると、customItem がキーイベントを受け取るようになります。



イベントフィルタ (QObject::installEventFilter と QObject::eventFilter)

イベントフィルタは、特定のオブジェクトが受け取るすべてのイベントを、そのオブジェクト自身が処理する前に横取りして検査・処理できる非常に強力なメカニズムです。

メリット:

  • 分離
    イベント処理ロジックを、イベントを受け取るオブジェクトのクラスから分離できます。
  • イベントの横取り
    ターゲットオブジェクトがイベントを処理する前に、イベントを消費したり、変更したり、別のオブジェクトに転送したりできます。
  • 柔軟性
    任意の QObject に対してイベントフィルタをインストールできます。複数のオブジェクトに同じフィルタを適用したり、1つのオブジェクトに複数のフィルタを適用したりすることも可能です。

デメリット:

  • QGraphicsView の特殊性
    QGraphicsView 自体にイベントフィルタをインストールしても、キーイベントが直接届かない場合があります。これは、QGraphicsView が内部的にビューポートウィジェット (viewport()) を持っており、ほとんどの入力イベントはこのビューポートが受け取るためです。そのため、QGraphicsView のキーイベントをフィルタリングしたい場合は、view->viewport()->installEventFilter(myFilter); のようにビューポートにフィルタをインストールする必要があります。
  • 複雑さ
    複数のイベントフィルタが連鎖する場合、イベントの流れを追うのが複雑になることがあります。

使用例:

MyEventFilter という QObject を継承したクラスを作成し、eventFilter メソッドをオーバーライドします。

myeventfilter.h

#ifndef MYEVENTFILTER_H
#define MYEVENTFILTER_H

#include <QObject>
#include <QEvent>
#include <QKeyEvent> // QKeyEvent のために必要
#include <QDebug>

class MyEventFilter : public QObject
{
    Q_OBJECT
public:
    explicit MyEventFilter(QObject *parent = nullptr) : QObject(parent) {}

protected:
    bool eventFilter(QObject *obj, QEvent *event) override {
        // 対象が QGraphicsView のビューポートであるか確認
        // あるいは、特定の QGraphicsView のインスタンスかをチェックすることも可能
        // if (obj == myGraphicsView->viewport()) {
            if (event->type() == QEvent::KeyRelease) {
                QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
                qDebug() << "イベントフィルタでキーリリースを検出: " << keyEvent->key();

                if (keyEvent->key() == Qt::Key_Space) {
                    qDebug() << "イベントフィルタで Space キーが離されました!";
                    // ここでカスタムロジックを実行
                    return true; // イベントを消費し、ビューポートやビューには伝播させない
                }
            }
        // }
        // それ以外のイベントや、処理しないキーイベントはデフォルトの動作に任せる
        return QObject::eventFilter(obj, event);
    }
};

#endif // MYEVENTFILTER_H

main.cpp での使用例 (抜粋)

#include "mygraphicsview.h" // MyGraphicsView は先の例で定義したものをそのまま利用
#include "myeventfilter.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QGraphicsScene scene;
    MyGraphicsView view(&scene); // MyGraphicsView のインスタンス

    // イベントフィルタを作成
    MyEventFilter *filter = new MyEventFilter(&a); // アプリケーションオブジェクトを親にすることも可能

    // QGraphicsView のビューポートにイベントフィルタをインストール
    view.viewport()->installEventFilter(filter); // ここが重要!

    // ... (アイテムの追加など、他のセットアップ) ...

    view.show();
    view.setFocus();

    return a.exec();
}

ショートカット (QShortcut)

QShortcut クラスは、特定のキーシーケンス(ショートカット)が押されたときにシグナルを発行するための便利な方法を提供します。メニューやツールバーアクションのショートカットとは異なり、ウィジェットレベルでのショートカットを直接定義できます。

  • 自動重複
    キーのリピートにも対応します(デフォルトで有効)。
  • コンテキスト
    ショートカットが有効になるコンテキスト(例: ウィンドウ内、アプリケーション全体など)を制御できます。
  • 簡潔
    特定のキーシーケンスに直接スロットを接続できるため、コードが簡潔になります。
  • 柔軟性の限界
    キーイベントの詳細な情報(例: どの修飾キーが押されていたか)を直接扱うには、keyReleaseEvent のオーバーライドの方が優れています。
  • 単一キーリリースには不向き
    keyReleaseEvent のように、任意のキーが離されたことを検出する用途には向いていません。あくまで特定のキーシーケンスが有効になったことをトリガーとするため、単一のキープレス/リリースを詳細に制御するにはオーバーライドの方が適しています。

MyGraphicsView のコンストラクタで、Ctrl+Z が離されたときに特定の処理を行うショートカットを設定します。

#include <QShortcut>
#include <QKeySequence> // QKeySequence のために必要
#include <QDebug>

// MyGraphicsView のコンストラクタ内で
MyGraphicsView::MyGraphicsView(QGraphicsScene *scene, QWidget *parent)
    : QGraphicsView(scene, parent)
{
    setFocusPolicy(Qt::StrongFocus);

    // Ctrl+Z キーシーケンスのショートカットを作成
    // 親ウィジェットは MyGraphicsView 自身
    QShortcut *undoShortcut = new QShortcut(QKeySequence(Qt::ControlModifier + Qt::Key_Z), this);

    // ショートカットがアクティブになったときにスロットを呼び出す
    connect(undoShortcut, &QShortcut::activated, this, [this]() {
        qDebug() << "ショートカット: Ctrl+Z が離されました!元に戻すアクションを実行します。";
        // ここに元に戻す処理を記述
    });

    // QShortcut は keyPress イベントを処理しますが、keyRelease を直接トリガーすることは稀です。
    // そのため、厳密には「キーが離されたとき」ではなく、「ショートカットが有効になったとき」の代替です。
    // 特に、キーが押されてから離されるまでの間ではなく、「キーが離された時点」で何かをしたい場合に、
    // QShortcut は最適ではありません。
}

アクション (QAction とショートカット)

QAction は、メニューアイテム、ツールバーボタン、コンテキストメニューアイテム、そしてキーボードショートカットなど、GUIの様々な場所から呼び出される「アクション」をカプセル化するクラスです。

  • UI要素との連携
    メニューやツールバーとの連携が非常に簡単です。ショートカットとUI要素の状態(有効/無効、チェック状態)が自動的に同期されます。
  • 複雑さ
    単にキーイベントを処理したいだけであれば、QAction を導入するのは少しオーバーキルかもしれません。
  • 単一キーリリースには不向き
    QShortcut と同様に、個々のキーのリリースイベントを詳細に制御するのには適していません。

メニューの「元に戻す」アクションにショートカット (Ctrl+Z) を割り当てる例です。

#include <QAction>
#include <QKeySequence>
#include <QDebug>
#include <QMainWindow>
#include <QMenu>
#include <QMenuBar>

// 例えば MainWindow クラス内で
class MyMainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MyMainWindow(QWidget *parent = nullptr) : QMainWindow(parent) {
        QMenu *fileMenu = menuBar()->addMenu(tr("&File"));

        QAction *undoAction = new QAction(tr("&Undo"), this);
        undoAction->setShortcut(QKeySequence(Qt::ControlModifier + Qt::Key_Z)); // Ctrl+Z をショートカットに設定
        fileMenu->addAction(undoAction);

        connect(undoAction, &QAction::triggered, this, [this]() {
            qDebug() << "アクション: Ctrl+Z (Undo) がトリガーされました!";
            // 元に戻す処理を実行
        });

        // ここに QGraphicsView を中央ウィジェットとして設定
        // QGraphicsView *view = new QGraphicsView(this);
        // setCentralWidget(view);
        // view->setFocusPolicy(Qt::StrongFocus); // フォーカス設定
    }
};
  • メニューやツールバーと連携したショートカットを作成し、UI全体でアクションを一元管理したい場合
    QAction を使用するのが最も適切な方法です。

  • 特定のキーシーケンス(ショートカット)によって「アクション」をトリガーしたい場合
    QShortcut が最も簡潔です。

  • 複数のオブジェクトやアプリケーション全体でキーイベントを監視・横取り・変更したい場合
    イベントフィルタ (QObject::installEventFilter) が適しています。ただし、QGraphicsView の場合はビューポートにインストールすることを忘れないでください。

  • 特定のキーが離されたときの詳細な制御が必要な場合(例えば、単一のキーリリースで何かをする、キーのリピートを無視する、特定の修飾キーの組み合わせを細かくチェックするなど)
    QGraphicsView::keyReleaseEvent() をオーバーライドするのが最も直接的で柔軟な方法です。