QGraphicsScene::setActiveWindow() の使い方と代替手法を徹底比較!Qt開発者必見

2025-04-26

基本的な概念

  • アクティブウィンドウ
    ユーザーの入力(キーボード、マウスなど)を受け取るウィンドウです。
  • QGraphicsWidget
    シーンに追加できるウィジェットです。通常の QWidget と同様に、ボタン、テキストボックスなどの要素を持つことができます。
  • QGraphicsScene
    グラフィカルアイテムを管理するシーンです。

QGraphicsScene::setActiveWindow() の役割

QGraphicsScene に複数の QGraphicsWidget が追加されている場合、どのウィジェットがユーザーの入力を受け取るかを決定する必要があります。setActiveWindow() メソッドは、指定された QGraphicsWidget をアクティブなウィンドウに設定し、そのウィジェットにフォーカスを移します。

具体的な動作

  1. QGraphicsScene::setActiveWindow(QGraphicsWidget *widget) を呼び出すと、指定された widget がシーンのアクティブウィンドウとして設定されます。
  2. このウィジェットは、キーボードイベントやマウスイベントなどのユーザー入力を受け取るようになります。
  3. 以前にアクティブだったウィンドウは、アクティブでなくなります。

使用例

#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsWidget>
#include <QPushButton>

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

  QGraphicsScene scene;
  QGraphicsView view(&scene);

  QGraphicsWidget *widget1 = scene.addWidget(new QPushButton("Button 1"));
  QGraphicsWidget *widget2 = scene.addWidget(new QPushButton("Button 2"));

  // widget2 をアクティブウィンドウに設定
  scene.setActiveWindow(widget2);

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

この例では、widget2 (Button 2) がアクティブウィンドウとして設定され、プログラムの起動時にフォーカスが当たります。

  • QGraphicsScene は、複数の QGraphicsView に表示できるので、一つのQGraphicsScene に追加されたQGraphicsWidgetが、複数のQGraphicsViewでアクティブになることもありえます。
  • アクティブウィンドウは、ユーザーの操作を受け付ける対象を定めるために利用されます。
  • setActiveWindow() は、QGraphicsWidget に対してのみ機能します。通常の QGraphicsItem には適用されません。


  1. QGraphicsWidget ではないアイテムを setActiveWindow() に渡した

    • エラー
      setActiveWindow()QGraphicsWidget* を引数として受け取ります。通常の QGraphicsItem* を渡すと、コンパイルエラーまたは実行時エラーが発生します。
    • トラブルシューティング
      setActiveWindow() に渡すオブジェクトが QGraphicsWidget のインスタンスであることを確認してください。必要に応じて、QGraphicsScene::addWidget() を使用して QWidgetQGraphicsWidget に変換します。
    // 間違った例: QGraphicsRectItem を渡す
    // QGraphicsRectItem *rect = scene.addRect(0, 0, 100, 100);
    // scene.setActiveWindow(rect); // エラー!
    
    // 正しい例: QGraphicsWidget を渡す
    QPushButton *button = new QPushButton("Button");
    QGraphicsWidget *widget = scene.addWidget(button);
    scene.setActiveWindow(widget);
    
  2. QGraphicsWidget がシーンに追加されていない

    • エラー
      setActiveWindow() に渡す QGraphicsWidget がシーンに追加されていない場合、予期しない動作が発生する可能性があります。
    • トラブルシューティング
      QGraphicsScene::addWidget() を使用して、QGraphicsWidget をシーンに確実に追加してください。
    // 間違った例: シーンに追加する前に setActiveWindow() を呼び出す
    // QPushButton *button = new QPushButton("Button");
    // QGraphicsWidget *widget = new QGraphicsWidget(); // 空のQGraphicsWidget
    // scene.setActiveWindow(widget); // エラーの可能性あり
    // scene.addWidget(widget);
    
    //正しい例
    QPushButton *button = new QPushButton("Button");
    QGraphicsWidget *widget = scene.addWidget(button);
    scene.setActiveWindow(widget);
    
    
  3. フォーカスが期待通りに設定されない

    • エラー
      setActiveWindow() を呼び出しても、期待した QGraphicsWidget にフォーカスが当たらない場合があります。
    • トラブルシューティング
      • QGraphicsView が正しく表示されているか確認してください。
      • 他のウィジェットがフォーカスを奪っていないか確認してください。
      • QGraphicsWidgetsetFocusPolicy() を確認し、フォーカスを受け取るように設定されているか確認してください。
      • QGraphicsScene::focusItem() を使用して、現在フォーカスを持っているアイテムを確認してください。
      • QGraphicsWidget::setFocus() を使用して、直接フォーカスを設定することも試してください。
    // フォーカスを受け取るように設定
    widget->setFocusPolicy(Qt::StrongFocus);
    
  4. 複数の QGraphicsView を使用している場合の混乱

    • エラー
      複数の QGraphicsView が同じ QGraphicsScene を表示している場合、setActiveWindow() の動作が混乱することがあります。
    • トラブルシューティング
      • どの QGraphicsView がアクティブウィンドウを表示しているかを確認してください。
      • 必要に応じて、QGraphicsView::setFocus() を使用して、特定のビューにフォーカスを設定してください。
      • 一つのQGraphicsSceneに複数のQGraphicsViewを接続する際は、フォーカス管理に注意してください。
  5. イベント伝播の問題

    • エラー
      QGraphicsWidget がイベントを正しく処理しない場合があります。
    • トラブルシューティング
      • QGraphicsWidget のイベントハンドラ(keyPressEvent(), mousePressEvent() など)が正しく実装されているか確認してください。
      • イベントフィルタを使用して、イベントを監視および変更することもできます。
  6. QGraphicsWidget の可視性の問題

    • エラー
      非表示の QGraphicsWidget をアクティブウィンドウに設定しようとすると、期待どおりに動作しないことがあります。
    • トラブルシューティング
      QGraphicsWidget::setVisible(true) を使用して、ウィジェットを表示状態にしてください。


#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsWidget>
#include <QPushButton>
#include <QObject>

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

  QGraphicsScene scene;
  QGraphicsView view(&scene);

  QPushButton *button1 = new QPushButton("Button 1");
  QPushButton *button2 = new QPushButton("Button 2");

  QGraphicsWidget *widget1 = scene.addWidget(button1);
  QGraphicsWidget *widget2 = scene.addWidget(button2);

  // 初期アクティブウィンドウの設定
  scene.setActiveWindow(widget1);

  // ボタンクリック時の処理
  QObject::connect(button1, &QPushButton::clicked, [&scene, widget1, widget2]() {
    scene.setActiveWindow(widget2); // Button 2 をアクティブに
  });

  QObject::connect(button2, &QPushButton::clicked, [&scene, widget1, widget2]() {
    scene.setActiveWindow(widget1); // Button 1 をアクティブに
  });

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

解説

  1. QPushButton を 2 つ作成し、QGraphicsScene::addWidget()QGraphicsWidget としてシーンに追加します。
  2. scene.setActiveWindow(widget1) で、初期状態では widget1 (Button 1) をアクティブウィンドウに設定します。
  3. QObject::connect() を使用して、各ボタンの clicked シグナルにラムダ関数を接続します。
  4. ボタンがクリックされると、ラムダ関数が実行され、scene.setActiveWindow() を呼び出してアクティブウィンドウを切り替えます。

このサンプルでは、カスタム QGraphicsWidget を作成し、クリック時にアクティブウィンドウに設定し、フォーカス処理を実装します。

#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsWidget>
#include <QPainter>
#include <QMouseEvent>
#include <QDebug>

class MyWidget : public QGraphicsWidget {
public:
  MyWidget() {
    setFlag(ItemIsFocusable); // フォーカスを受け取る設定
    setFocusPolicy(Qt::StrongFocus);
  }

protected:
  void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override {
    painter->fillRect(boundingRect(), QColor(Qt::yellow));
    painter->drawText(boundingRect(), "My Widget", QTextOption(Qt::AlignCenter));
  }

  void mousePressEvent(QGraphicsSceneMouseEvent *event) override {
    QGraphicsWidget::mousePressEvent(event);
    scene()->setActiveWindow(this);
    setFocus();
    qDebug() << "MyWidget clicked and activated.";
  }

  QRectF boundingRect() const override {
    return QRectF(0, 0, 200, 100);
  }

  void focusInEvent(QFocusEvent *event) override{
    qDebug() << "MyWidget focused";
  }

  void focusOutEvent(QFocusEvent *event) override{
    qDebug() << "MyWidget unfocused";
  }
};

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

  QGraphicsScene scene;
  QGraphicsView view(&scene);

  MyWidget *myWidget = new MyWidget();
  scene.addItem(myWidget);

  view.show();
  return app.exec();
}
  1. MyWidget クラスを QGraphicsWidget から継承し、カスタムウィジェットを作成します。
  2. setFlag(ItemIsFocusable)setFocusPolicy(Qt::StrongFocus) を設定し、ウィジェットがフォーカスを受け取れるようにします。
  3. paint() メソッドをオーバーライドして、ウィジェットの描画を実装します。
  4. mousePressEvent() メソッドをオーバーライドして、クリック時に scene()->setActiveWindow(this) を呼び出し、ウィジェットをアクティブウィンドウに設定し、setFocus()でフォーカスを与えます。
  5. focusInEvent()focusOutEvent()をオーバーライドして、フォーカスの状態をデバッグ出力します。
  6. boundingRect() をオーバーライドして、ウィジェットのサイズを設定します。


QGraphicsItem::setFocus() と QGraphicsItem::grabKeyboard() / grabMouse() を使用する

QGraphicsWidget ではなく、通常の QGraphicsItem をアクティブにしたい場合や、より細かい制御が必要な場合に有効です。

  • QGraphicsItem::grabKeyboard() / grabMouse(): アイテムがキーボード/マウス入力を独占します。
  • QGraphicsItem::setFocus(): アイテムにフォーカスを与えます。
#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QDebug>
#include <QKeyEvent>

class MyRect : public QGraphicsRectItem {
public:
  MyRect(qreal x, qreal y, qreal w, qreal h) : QGraphicsRectItem(x, y, w, h) {
    setFlag(ItemIsFocusable);
    setFocusPolicy(Qt::StrongFocus);
  }

protected:
  void keyPressEvent(QKeyEvent *event) override {
    qDebug() << "MyRect key press:" << event->text();
  }

  void mousePressEvent(QGraphicsSceneMouseEvent *event) override {
    QGraphicsRectItem::mousePressEvent(event);
    setFocus();
    grabKeyboard(); // キーボード入力を独占
    qDebug() << "MyRect clicked and grabbed.";
  }

  void focusOutEvent(QFocusEvent *event) override {
      ungrabKeyboard(); //キーボード入力を解放
      QGraphicsRectItem::focusOutEvent(event);
  }
};

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

  QGraphicsScene scene;
  QGraphicsView view(&scene);

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

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

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

解説

  • focusOutEvent()ungrabKeyboard()を呼び出し、キーボード入力を解放します。
  • keyPressEvent() をオーバーライドして、キー入力を処理します。
  • mousePressEvent()setFocus()grabKeyboard() を呼び出し、アイテムをクリックするとフォーカスを与え、キーボード入力を独占します。
  • setFlag(ItemIsFocusable)setFocusPolicy(Qt::StrongFocus) を設定し、フォーカスを受け取れるようにします。
  • MyRect クラスは QGraphicsRectItem を継承し、カスタムの矩形アイテムを作成します。

QGraphicsView::focusWidget() を使用する (QWidget ベースのビューの場合)

QGraphicsViewQWidget ベースの場合(通常はデフォルト)、QGraphicsView::focusWidget() を使用して、ビュー内の QWidget にフォーカスを設定できます。これは、QGraphicsScene::addWidget() で追加された QGraphicsWidget の内部 QWidget にアクセスする場合に役立ちます。

#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsWidget>
#include <QPushButton>

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

  QGraphicsScene scene;
  QGraphicsView view(&scene);

  QPushButton *button = new QPushButton("Button");
  QGraphicsWidget *widget = scene.addWidget(button);

  view.show();

  // ビュー内の QPushButton にフォーカスを設定
  view.setFocus();
  view.focusWidget()->setFocus();

  return app.exec();
}

解説

  • view.focusWidget()->setFocus() を呼び出して、ビュー内の QPushButton にフォーカスを設定します。
  • view.setFocus() を呼び出して、ビュー自体にフォーカスを与えます。
  • QGraphicsScene::addWidget() を使用して QPushButton をシーンに追加します。

イベントフィルタを使用する

イベントフィルタを使用すると、イベントを監視および変更して、カスタムのフォーカス処理を実装できます。

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

class FocusFilter : public QObject {
public:
  bool eventFilter(QObject *obj, QEvent *event) override {
    if (event->type() == QEvent::MouseButtonPress) {
      QGraphicsItem *item = qobject_cast<QGraphicsItem *>(obj);
      if (item) {
        item->setFocus();
        qDebug() << "Item focused via event filter.";
      }
    }
    return QObject::eventFilter(obj, event);
  }
};

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

  QGraphicsScene scene;
  QGraphicsView view(&scene);

  QGraphicsRectItem *rect = new QGraphicsRectItem(0, 0, 100, 100);
  scene.addItem(rect);

  FocusFilter *filter = new FocusFilter();
  rect->installEventFilter(filter);

  view.show();
  return app.exec();
}
  • rect->installEventFilter(filter) を呼び出して、イベントフィルタをアイテムにインストールします。
  • eventFilter()QEvent::MouseButtonPress イベントを監視し、アイテムがクリックされた場合に setFocus() を呼び出します。
  • FocusFilter クラスは QObject を継承し、イベントフィルタを実装します。