初心者でも安心!QtのQGraphicsSceneフォーカス移動をステップバイステップで解説

2025-04-07

以下に詳しく説明します。

  • focusNextPrevChild() は、ユーザーが Tab キーや Shift+Tab キーを押した際に、フォーカスを次のアイテムまたは前のアイテムに移動させるために使用されます。
  • キーボードフォーカスは、キーボード入力がどのアイテムに送られるかを決定します。
  • QGraphicsItem は、シーンに配置されるグラフィック要素(矩形、円、画像など)です。
  • QGraphicsScene は、グラフィックアイテムを管理するシーンです。

動作

focusNextPrevChild(bool next)

  • フォーカス可能なアイテムが見つからない場合、フォーカスは変更されません。
  • アイテムのフォーカス順序は、シーンに追加された順序によって暗黙的に決定されます。必要に応じて、QGraphicsItem::setFocus()で特定のアイテムにフォーカスをあらかじめ設定する事も可能です。
  • フォーカス可能なアイテムは、QGraphicsItem::setFlag(QGraphicsItem::ItemIsFocusable, true) を呼び出してフォーカス可能に設定されたアイテムです。
  • next パラメータが false の場合、関数は現在のフォーカスを持つアイテムから、前のフォーカス可能なアイテムへとフォーカスを移動させます。
  • next パラメータが true の場合、関数は現在のフォーカスを持つアイテムから、次のフォーカス可能なアイテムへとフォーカスを移動させます。

使用例

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

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

  QGraphicsScene scene;
  QGraphicsView view(&scene);

  QGraphicsRectItem *rect1 = scene.addRect(0, 0, 100, 100);
  rect1->setFlag(QGraphicsItem::ItemIsFocusable);
  QGraphicsRectItem *rect2 = scene.addRect(150, 0, 100, 100);
  rect2->setFlag(QGraphicsItem::ItemIsFocusable);
  QGraphicsRectItem *rect3 = scene.addRect(300, 0, 100, 100);
  rect3->setFlag(QGraphicsItem::ItemIsFocusable);

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

この例では、3つの矩形アイテムがシーンに追加され、それぞれがフォーカス可能に設定されています。Tab キーを押すと、これらの矩形アイテム間でフォーカスが順番に移動します。

QGraphicsScene::focusNextPrevChild() は、QGraphicsScene 内のグラフィックアイテム間でキーボードフォーカスを移動させるために使用される重要な関数です。これにより、ユーザーはキーボードを使用してシーン内のアイテムをナビゲートできます。



フォーカスが期待通りに移動しない

  • トラブルシューティング
    • QGraphicsItem::setFlag(QGraphicsItem::ItemIsFocusable, true) を呼び出して、フォーカス可能なアイテムを正しく設定しているか確認します。
    • アイテムがシーンに追加された順序を確認し、必要に応じて QGraphicsItem::setFocus() を使用して特定のアイテムにフォーカスを強制的に設定します。
    • シーンに少なくとも1つのフォーカス可能なアイテムがあることを確認します。
    • ビューの上に他のwidgetが重なっていないか確認し、重なっている場合は、widgetの表示順序を調整する。
  • 原因
    • アイテムが QGraphicsItem::ItemIsFocusable フラグを設定していない。
    • フォーカス順序が期待通りでない。
    • シーンにフォーカス可能なアイテムが全くない。
    • フォーカスを奪う他のwidgetが重なっている。

フォーカスがループしない

  • トラブルシューティング
    • focusNextPrevChild() が、Tab キーや Shift+Tab キーのイベントハンドラ内で正しく呼び出されているか確認します。
    • デフォルトで、フォーカスは自動的にループしません。ループが必要な場合は、QGraphicsScene のサブクラスを作成し、focusNextPrevChild() をオーバーライドして、フォーカスをループさせるカスタムロジックを実装する必要があります。
  • 原因
    • focusNextPrevChild() の呼び出しが適切に行われていない。
    • フォーカスがループするように、自分で実装していない。

フォーカスが特定のアイテムに移動しない

  • トラブルシューティング
    • アイテムが setVisible(true) および setEnabled(true) で表示および有効になっているか確認します。
    • アイテムがシーンの境界内に存在することを確認します。
    • アイテムが他のアイテムに隠されていないか確認し、アイテムのz値を調整する。
  • 原因
    • アイテムが非表示または無効になっている。
    • アイテムがシーンの境界外にある。
    • アイテムが他のアイテムに隠されている。

予期しないフォーカス変更

  • トラブルシューティング
    • 他のイベントハンドラで setFocus() が呼び出されていないか確認します。
    • 他のwidgetがフォーカスを奪っていないか確認し、必要に応じて、widgetのフォーカスポリシーを調整します。
  • 原因
    • 他のイベントハンドラがフォーカスを変更している。
    • フォーカスを奪う他のwidgetが影響している。

カスタムアイテムのフォーカス処理

  • トラブルシューティング
    • カスタムアイテムの focusInEvent()focusOutEvent()、および keyPressEvent() などのイベントハンドラをオーバーライドして、フォーカスに関連する動作を適切に処理しているか確認します。
    • カスタムアイテムの QGraphicsItem::ItemIsFocusable フラグを正しく設定しているか確認します。
  • 原因
    • カスタム QGraphicsItem サブクラスで、フォーカス処理が正しく実装されていない。
  • フォーカス関連のイベントを追跡するために、イベントフィルターを使用する。
  • ブレークポイントを使用して、フォーカス関連のイベントハンドラをステップ実行し、問題を特定します。
  • qDebug() を使用して、フォーカスがどのアイテムに設定されているか、および focusNextPrevChild() の呼び出しがどのように動作しているかを確認します。


例1: 基本的なフォーカス移動

この例では、複数の矩形アイテムを QGraphicsScene に追加し、Tab キーと Shift+Tab キーを使用してフォーカスを移動させます。

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

class MyView : public QGraphicsView {
public:
  MyView(QGraphicsScene *scene) : QGraphicsView(scene) {}

protected:
  void keyPressEvent(QKeyEvent *event) override {
    if (event->key() == Qt::Key_Tab) {
      scene()->focusNextPrevChild(event->modifiers() != Qt::ShiftModifier);
      event->accept();
    } else {
      QGraphicsView::keyPressEvent(event);
    }
  }
};

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

  QGraphicsScene scene;
  MyView view(&scene); // カスタムビューを使用

  QGraphicsRectItem *rect1 = scene.addRect(0, 0, 100, 100);
  rect1->setFlag(QGraphicsItem::ItemIsFocusable);
  QGraphicsRectItem *rect2 = scene.addRect(150, 0, 100, 100);
  rect2->setFlag(QGraphicsItem::ItemIsFocusable);
  QGraphicsRectItem *rect3 = scene.addRect(300, 0, 100, 100);
  rect3->setFlag(QGraphicsItem::ItemIsFocusable);

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

説明

  1. MyView クラスは QGraphicsView を継承し、keyPressEvent() をオーバーライドしています。
  2. keyPressEvent() で、Tab キーが押されたかどうかをチェックします。
  3. Tab キーが押された場合、scene()->focusNextPrevChild() を呼び出してフォーカスを移動させます。
  4. event->modifiers() != Qt::ShiftModifier を使用して、Shift キーが押されているかどうかを判断し、順方向または逆方向にフォーカスを移動させます。
  5. QGraphicsRectItemsetFlag(QGraphicsItem::ItemIsFocusable, true) を呼び出して、矩形アイテムをフォーカス可能にします。

例2: カスタムフォーカス順序

この例では、アイテムの追加順序に関係なく、特定の順序でフォーカスを移動させる方法を示します。

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

class MyView : public QGraphicsView {
public:
  MyView(QGraphicsScene *scene, const QList<QGraphicsItem *> &focusOrder)
      : QGraphicsView(scene), focusOrder_(focusOrder) {}

protected:
  void keyPressEvent(QKeyEvent *event) override {
    if (event->key() == Qt::Key_Tab) {
      QGraphicsItem *currentFocus = scene()->focusItem();
      int currentIndex = focusOrder_.indexOf(currentFocus);
      int nextIndex = (currentIndex + (event->modifiers() != Qt::ShiftModifier ? 1 : -1) + focusOrder_.size()) % focusOrder_.size();
      focusOrder_[nextIndex]->setFocus();
      event->accept();
    } else {
      QGraphicsView::keyPressEvent(event);
    }
  }

private:
  QList<QGraphicsItem *> focusOrder_;
};

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

  QGraphicsScene scene;

  QGraphicsRectItem *rect1 = scene.addRect(0, 0, 100, 100);
  rect1->setFlag(QGraphicsItem::ItemIsFocusable);
  QGraphicsRectItem *rect2 = scene.addRect(150, 0, 100, 100);
  rect2->setFlag(QGraphicsItem::ItemIsFocusable);
  QGraphicsRectItem *rect3 = scene.addRect(300, 0, 100, 100);
  rect3->setFlag(QGraphicsItem::ItemIsFocusable);

  QList<QGraphicsItem *> focusOrder = {rect3, rect1, rect2}; // カスタム順序
  MyView view(&scene, focusOrder);

  view.show();
  return app.exec();
}
  1. MyView クラスは、フォーカス順序を格納する QList<QGraphicsItem *> を受け取ります。
  2. keyPressEvent() で、現在のフォーカスアイテムのインデックスを focusOrder_ リスト内で検索します。
  3. 次のフォーカスアイテムのインデックスを計算し、そのアイテムに setFocus() を呼び出してフォーカスを設定します。
  4. これにより、アイテムがシーンに追加された順序に関係なく、focusOrder_ リストで定義された順序でフォーカスが移動します。
  5. リストの要素数でmodをとることで、リストの末尾、先頭でループさせる。


カスタムフォーカス管理

  • この方法により、フォーカス移動の順序や条件を完全に制御できます。
  • QGraphicsItem のフォーカス順序を明示的に管理するために、QListQMap などのデータ構造を使用できます。
  • QGraphicsScene のサブクラスを作成し、keyPressEvent() などのイベントハンドラをオーバーライドして、カスタムフォーカス移動ロジックを実装します。
  • focusNextPrevChild() の代わりに、独自のフォーカス管理ロジックを実装できます。


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

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

  void setFocusOrder(const QList<QGraphicsItem *> &order) {
    focusOrder_ = order;
  }

protected:
  void keyPressEvent(QKeyEvent *event) override {
    if (event->key() == Qt::Key_Tab) {
      QGraphicsItem *currentFocus = focusItem();
      int currentIndex = focusOrder_.indexOf(currentFocus);
      int nextIndex = (currentIndex + (event->modifiers() != Qt::ShiftModifier ? 1 : -1) + focusOrder_.size()) % focusOrder_.size();
      focusOrder_[nextIndex]->setFocus();
      event->accept();
    } else {
      QGraphicsScene::keyPressEvent(event);
    }
  }

private:
  QList<QGraphicsItem *> focusOrder_;
};

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

  MyScene scene;
  QGraphicsView view(&scene);

  QGraphicsRectItem *rect1 = scene.addRect(0, 0, 100, 100);
  rect1->setFlag(QGraphicsItem::ItemIsFocusable);
  QGraphicsRectItem *rect2 = scene.addRect(150, 0, 100, 100);
  rect2->setFlag(QGraphicsItem::ItemIsFocusable);
  QGraphicsRectItem *rect3 = scene.addRect(300, 0, 100, 100);
  rect3->setFlag(QGraphicsItem::ItemIsFocusable);

  scene.setFocusOrder({rect3, rect1, rect2}); // カスタム順序を設定

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

フォーカスグループの作成

  • これにより、複雑なユーザーインターフェイスでフォーカス移動をより細かく制御できます。
  • グループ内のアイテム間でフォーカスを移動するためのカスタムロジックを実装します。
  • カスタム QGraphicsItem サブクラスを作成し、グループ内のアイテムを管理します。

フォーカスポリシーの変更

  • これにより、特定のアイテムがフォーカスを受け取る条件を制御できます。
  • Qt::NoFocusQt::TabFocusQt::ClickFocusQt::StrongFocus などのポリシーを設定できます。
  • QGraphicsItem::setFocusPolicy() を使用して、各アイテムのフォーカスポリシーを変更できます。


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

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

  QGraphicsScene scene;
  QGraphicsView view(&scene);

  QGraphicsRectItem *rect1 = scene.addRect(0, 0, 100, 100);
  rect1->setFlag(QGraphicsItem::ItemIsFocusable);
  rect1->setFocusPolicy(Qt::TabFocus); // Tabキーでのみフォーカスを受け取る

  QGraphicsRectItem *rect2 = scene.addRect(150, 0, 100, 100);
  rect2->setFlag(QGraphicsItem::ItemIsFocusable);
  rect2->setFocusPolicy(Qt::ClickFocus); // クリックでのみフォーカスを受け取る

  view.show();
  return app.exec();
}
  • これにより、フォーカスが変更されたときにアイテムの表示や状態を変更できます。
  • QGraphicsItem::focusInEvent() および QGraphicsItem::focusOutEvent() をオーバーライドして、フォーカスがアイテムに移動したとき、またはアイテムから移動したときにカスタムロジックを実行できます。