QGraphicsScene::focusItem() で実現するインタラクティブなQtアプリケーション

2025-04-26

  • QGraphicsScene::focusItem() は、現在フォーカスを持っている QGraphicsItem へのポインタを返します。もしシーン内にフォーカスを持っているアイテムがない場合、nullptr (または C++ では NULL) を返します。
  • フォーカスは、キーボードイベントを受け取るアイテムを指します。ユーザーがキーボードで何かを入力すると、フォーカスを持っているアイテムがその入力を受け取ります。
  • QGraphicsItem は、シーン内で描画されるグラフィカルな要素(長方形、円、画像、カスタム図形など)です。
  • QGraphicsScene は、QGraphicsItem オブジェクトを管理するコンテナです。

詳細

    • グラフィカルシーンでは、複数のアイテムが存在する可能性があります。
    • ユーザーがキーボード入力をするとき、どのアイテムがその入力を受け取るべきかを決定する必要があります。
    • フォーカスは、この目的で使用されます。フォーカスを持っているアイテムだけがキーボードイベントを受け取ります。
    • フォーカスは、クリックやタブキー操作などで変更できます。
  1. QGraphicsScene::focusItem() の役割

    • この関数は、現在アクティブな(フォーカスを持っている)QGraphicsItem を特定するために使用されます。
    • これにより、プログラムはフォーカスを持っているアイテムに対して特定のアクションを実行できます。
    • 例えば、フォーカスを持っているアイテムのプロパティを変更したり、そのアイテムからの入力を処理したりできます。
  2. 戻り値

    • フォーカスを持っている QGraphicsItem へのポインタ。
    • シーン内にフォーカスを持っているアイテムがない場合は nullptr を返します。
  3. 使用例

#include <QGraphicsScene>
#include <QGraphicsRectItem>
#include <QDebug>

int main(int argc, char *argv[]) {
  //...QApplicationの初期化...
  QGraphicsScene scene;
  QGraphicsRectItem *rect1 = scene.addRect(0, 0, 100, 50);
  QGraphicsRectItem *rect2 = scene.addRect(150, 0, 100, 50);

  rect1->setFlag(QGraphicsItem::ItemIsFocusable);
  rect2->setFlag(QGraphicsItem::ItemIsFocusable);

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

  QGraphicsItem *focusedItem = scene.focusItem();

  if (focusedItem) {
    qDebug() << "フォーカスを持っているアイテム:" << focusedItem;
  } else {
    qDebug() << "フォーカスを持っているアイテムはありません。";
  }

  //...QApplicationの実行...
  return 0;
}

この例では、rect1 にフォーカスを設定し、scene.focusItem() を使用してフォーカスを持っているアイテムを取得しています。



  1. nullptr が返される
    • 原因
      • シーン内にフォーカス可能なアイテムがない。
      • アイテムがフォーカスを持っていない。
      • アイテムの QGraphicsItem::ItemIsFocusable フラグが設定されていない。
    • トラブルシューティング
      • QGraphicsItem::setFlag(QGraphicsItem::ItemIsFocusable) を使用して、アイテムがフォーカスを受け取れるように設定してください。
      • QGraphicsItem::setFocus() を使用して、アイテムに明示的にフォーカスを設定してください。
      • シーン内に期待されるアイテムが追加されているか確認してください。
      • プログラムの実行順序を再確認し、focusItem()を呼び出す前に、focusが設定されているか確認してください。
  2. 意図しないアイテムがフォーカスを持つ
    • 原因
      • 複数のアイテムが QGraphicsItem::ItemIsFocusable フラグを持っている。
      • フォーカスが予期しないタイミングで変更されている。
      • フォーカスを管理するロジックが複雑すぎる。
    • トラブルシューティング
      • どのアイテムがフォーカスを受け取るべきかを明確に定義し、不要なアイテムの QGraphicsItem::ItemIsFocusable フラグを無効にしてください。
      • フォーカス変更のイベント(マウスイベント、キーボードイベントなど)を監視し、意図しない変更が発生していないか確認してください。
      • フォーカス管理のロジックを簡素化し、デバッグしやすいようにしてください。
  3. フォーカスが設定されない
    • 原因
      • setFocus()を呼び出す前に、アイテムがシーンに追加されていない。
      • アイテムが、親アイテムにクリップされている。
      • アイテムが、他のアイテムによって隠されている。
      • アイテムが、無効化されている。
    • トラブルシューティング
      • setFocus()を呼び出す前に、アイテムがシーンに追加されていることを確認してください。
      • 親アイテムのクリッピング設定を確認してください。
      • アイテムのZ値を確認し、他のアイテムによって隠されていないか確認してください。
      • アイテムの有効/無効の状態を確認してください。setEnabled()
  4. フォーカスイベントが期待通りに処理されない
    • 原因
      • QGraphicsItem::focusInEvent() または QGraphicsItem::focusOutEvent() を正しく実装していない。
      • イベントフィルターがフォーカスイベントを妨害している。
    • トラブルシューティング
      • QGraphicsItem::focusInEvent()QGraphicsItem::focusOutEvent() の実装を慎重に確認し、必要な処理が正しく実行されているか確認してください。
      • イベントフィルターを使用している場合は、フォーカスイベントが適切に処理されているか確認してください。
  5. ビューのフォーカスとシーンのフォーカスの混同
    • 原因
      • QGraphicsViewQGraphicsScene のフォーカスを混同している。
      • QGraphicsView もフォーカスを持つことができ、QGraphicsScene 内のアイテムとは別のフォーカス管理が行われます。
    • トラブルシューティング
      • QGraphicsView のフォーカスと QGraphicsScene のフォーカスを区別してください。
      • QGraphicsView::setFocus()QGraphicsScene::focusItem() は異なる目的で使用されます。
      • QGraphicsView のフォーカスはビュー全体に影響し、QGraphicsScene::focusItem() はシーン内の個々のアイテムに影響します。
  6. デバッグのヒント
    • qDebug() を使用して、focusItem() の戻り値とフォーカスイベントの発生をログに出力してください。
    • Qt のデバッガを使用して、コードをステップ実行し、フォーカス関連の変数の値を監視してください。
    • Qt のイベントシステムを理解し、フォーカスイベントの流れを把握してください。


例1: フォーカスを持つアイテムの取得と操作

#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QDebug>

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

  QGraphicsScene scene;
  QGraphicsView view(&scene);

  QGraphicsRectItem *rect1 = scene.addRect(0, 0, 100, 50);
  QGraphicsRectItem *rect2 = scene.addRect(150, 0, 100, 50);

  rect1->setFlag(QGraphicsItem::ItemIsFocusable);
  rect2->setFlag(QGraphicsItem::ItemIsFocusable);

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

  // フォーカスを持つアイテムを取得し、色を変更する
  QGraphicsItem *focusedItem = scene.focusItem();
  if (focusedItem) {
    QGraphicsRectItem *rect = qgraphicsitem_cast<QGraphicsRectItem*>(focusedItem);
    if (rect) {
      rect->setBrush(Qt::red);
      qDebug() << "フォーカスを持つアイテムの色を赤に変更しました。";
    }
  }

  view.show();
  return app.exec();
}

説明

  1. QGraphicsSceneQGraphicsView を作成し、2 つの QGraphicsRectItem をシーンに追加します。
  2. setFlag(QGraphicsItem::ItemIsFocusable) を使用して、両方の長方形をフォーカス可能にします。
  3. rect1->setFocus() で、初期フォーカスを rect1 に設定します。
  4. scene.focusItem() を使用して、フォーカスを持つアイテムを取得します。
  5. qgraphicsitem_cast を使用して、取得したアイテムを QGraphicsRectItem にキャストし、その色を赤に変更します。
  6. qDebug() で、操作が成功したことをコンソールに表示します。

例2: フォーカスイベントの処理

#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QDebug>

class FocusRectItem : public QGraphicsRectItem {
public:
  FocusRectItem(qreal x, qreal y, qreal width, qreal height, QGraphicsItem *parent = nullptr)
      : QGraphicsRectItem(x, y, width, height, parent) {
    setFlag(ItemIsFocusable);
  }

protected:
  void focusInEvent(QFocusEvent *event) override {
    Q_UNUSED(event);
    setBrush(Qt::green);
    qDebug() << "フォーカスが入りました。";
    QGraphicsRectItem::focusInEvent(event);
  }

  void focusOutEvent(QFocusEvent *event) override {
    Q_UNUSED(event);
    setBrush(Qt::white);
    qDebug() << "フォーカスが抜けました。";
    QGraphicsRectItem::focusOutEvent(event);
  }
};

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

  QGraphicsScene scene;
  QGraphicsView view(&scene);

  FocusRectItem *rect1 = new FocusRectItem(0, 0, 100, 50);
  FocusRectItem *rect2 = new FocusRectItem(150, 0, 100, 50);

  scene.addItem(rect1);
  scene.addItem(rect2);

  view.show();
  return app.exec();
}

説明

  1. QGraphicsRectItem を継承した FocusRectItem クラスを作成します。
  2. focusInEvent()focusOutEvent() をオーバーライドし、フォーカスが入ったときと抜けたときに色を変更し、メッセージをコンソールに出力します。
  3. FocusRectItem のインスタンスをシーンに追加します。
  4. 実行すると、長方形をクリックしてフォーカスを変更すると、色とメッセージが変化します。

例3: キーイベントによるフォーカス制御

#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QKeyEvent>
#include <QDebug>

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

protected:
  void keyPressEvent(QKeyEvent *event) override {
    if (event->key() == Qt::Key_Tab) {
      QGraphicsItem *focusedItem = scene()->focusItem();
      if (focusedItem) {
        QList<QGraphicsItem *> items = scene()->items();
        int index = items.indexOf(focusedItem);
        if (index != -1) {
          int nextIndex = (index + 1) % items.size();
          items[nextIndex]->setFocus();
        }
      } else if (!scene()->items().isEmpty()){
          scene()->items().first()->setFocus();
      }
    }
    QGraphicsView::keyPressEvent(event);
  }
};

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

  QGraphicsScene scene;
  FocusView view(&scene);

  QGraphicsRectItem *rect1 = scene.addRect(0, 0, 100, 50);
  QGraphicsRectItem *rect2 = scene.addRect(150, 0, 100, 50);

  rect1->setFlag(QGraphicsItem::ItemIsFocusable);
  rect2->setFlag(QGraphicsItem::ItemIsFocusable);

  view.show();
  return app.exec();
}
  1. QGraphicsView を継承した FocusView クラスを作成し、keyPressEvent() をオーバーライドします。
  2. keyPressEvent() で、Tab キーが押されたときに、シーン内の次のフォーカス可能なアイテムにフォーカスを移動します。
  3. scene()->focusItem() で現在のフォーカスアイテムを取得し、リスト内で次のアイテムにフォーカスを設定します。
  4. もしフォーカスアイテムが存在しない場合、最初のアイテムにフォーカスを設定します。


  1. イベントフィルターの使用
    • 説明
      QObject::installEventFilter() を使用して、シーンまたはビューにイベントフィルターをインストールし、フォーカスイベント (QEvent::FocusIn, QEvent::FocusOut) を監視します。これにより、フォーカスが変更されたときにカスタムの処理を実行できます。
    • 利点
      • 集中管理が容易: シーンまたはビュー全体でフォーカスイベントを監視できます。
      • 柔軟性: フォーカス変更時に、他のイベントや状態に基づいて複雑なロジックを実行できます。

#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QEvent>
#include <QDebug>

class FocusFilter : public QObject {
public:
    FocusFilter(QObject *parent = nullptr) : QObject(parent) {}

protected:
    bool eventFilter(QObject *obj, QEvent *event) override {
        if (event->type() == QEvent::FocusIn) {
            QGraphicsItem *item = qobject_cast<QGraphicsItem*>(obj);
            if (item) {
                qDebug() << "フォーカスが入りました:" << item;
            }
        } else if (event->type() == QEvent::FocusOut) {
            QGraphicsItem *item = qobject_cast<QGraphicsItem*>(obj);
            if (item) {
                qDebug() << "フォーカスが抜けました:" << item;
            }
        }
        return QObject::eventFilter(obj, event);
    }
};

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

    QGraphicsScene scene;
    QGraphicsView view(&scene);

    QGraphicsRectItem *rect1 = scene.addRect(0, 0, 100, 50);
    QGraphicsRectItem *rect2 = scene.addRect(150, 0, 100, 50);

    rect1->setFlag(QGraphicsItem::ItemIsFocusable);
    rect2->setFlag(QGraphicsItem::ItemIsFocusable);

    FocusFilter *filter = new FocusFilter(&scene);
    scene.installEventFilter(filter);

    view.show();
    return app.exec();
}
  1. 独自のフォーカス管理
    • 説明
      シーン内のアイテムを追跡し、独自のフォーカス管理ロジックを実装します。例えば、フォーカスを持つアイテムをメンバ変数に保持し、フォーカス変更時にその変数を更新します。
    • 利点
      • 完全にカスタマイズ可能: アプリケーションの特定のニーズに合わせてフォーカス管理を調整できます。
      • パフォーマンスの最適化: 複雑なシーンで、Qt のデフォルトのフォーカス管理よりも効率的な場合があります。

#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QDebug>

class CustomFocusScene : public QGraphicsScene {
public:
    CustomFocusScene(QObject *parent = nullptr) : QGraphicsScene(parent), focusedItem(nullptr) {}

    void setFocusedItem(QGraphicsItem *item) {
        if (focusedItem != item) {
            if (focusedItem) {
                // 前のフォーカスアイテムの処理
                qDebug() << "フォーカスが抜けました:" << focusedItem;
            }
            focusedItem = item;
            if (focusedItem) {
                // 新しいフォーカスアイテムの処理
                qDebug() << "フォーカスが入りました:" << focusedItem;
            }
        }
    }

    QGraphicsItem *getFocusedItem() const {
        return focusedItem;
    }

private:
    QGraphicsItem *focusedItem;
};

class FocusRectItem : public QGraphicsRectItem {
public:
    FocusRectItem(qreal x, qreal y, qreal width, qreal height, CustomFocusScene *scene, QGraphicsItem *parent = nullptr)
        : QGraphicsRectItem(x, y, width, height, parent), scene(scene) {
        setFlag(ItemIsFocusable);
    }

protected:
    void focusInEvent(QFocusEvent *event) override {
        scene->setFocusedItem(this);
        QGraphicsRectItem::focusInEvent(event);
    }

    void focusOutEvent(QFocusEvent *event) override {
        if (scene->getFocusedItem() == this){
            scene->setFocusedItem(nullptr);
        }
        QGraphicsRectItem::focusOutEvent(event);
    }

private:
    CustomFocusScene *scene;
};

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

    CustomFocusScene scene;
    QGraphicsView view(&scene);

    FocusRectItem *rect1 = new FocusRectItem(0, 0, 100, 50, &scene);
    FocusRectItem *rect2 = new FocusRectItem(150, 0, 100, 50, &scene);

    scene.addItem(rect1);
    scene.addItem(rect2);

    view.show();
    return app.exec();
}
  1. シグナルとスロットの使用
    • 説明
      QGraphicsItem のカスタムシグナルを使用して、フォーカスが変更されたことを通知します。シーンまたはビューは、これらのシグナルに接続して、必要な処理を実行します。
    • 利点
      • 疎結合: フォーカス管理ロジックとアイテムの間の依存関係を減らすことができます。
      • 柔軟性: シグナルを使用して、フォーカス変更に関する追加の情報を送信できます。