キー入力に耳を傾け、ユーザーの意図を汲み取る: QAbstractInputContext::keyPressEvent() で実現する直感的な操作性


QWindow::focusObjectChanged() は、Qt GUI における重要なシグナルであり、どのウィジェットが現在キーボードフォーカスを持っているかを通知します。これは、ユーザーがアプリケーション内のさまざまな要素間を移動する際に、アプリケーションロジックを更新したり、UI を調整したりするために使用されます。

詳細

このシグナルは、QObject 型のパラメーター object を渡して発行されます。このパラメーターは、フォーカスが移動したウィジェットを指します。

void QWindow::focusObjectChanged(QObject *object);

次の例は、QWindow::focusObjectChanged() シグナルに接続し、フォーカスが移動したときに std::cout に新しいウィジェットの名前を出力する方法を示します。

void MyWindow::connectFocusSignals() {
  connect(this, &QWindow::focusObjectChanged, this, &MyWindow::onObjectFocusChanged);
}

void MyWindow::onObjectFocusChanged(QObject *object) {
  if (object) {
    std::cout << "Focus moved to: " << object->objectName().toStdString() << std::endl;
  } else {
    std::cout << "Focus lost" << std::endl;
  }
}

応用例

QWindow::focusObjectChanged() シグナルは、さまざまな目的に使用できます。以下は、その例です。

  • フォーカスのあるウィジェットに入力されたテキストを処理する。
  • フォーカスのあるウィジェットに基づいて、メニュー項目を有効化または無効化する。
  • フォーカスが移動したときに、フォーカスのあるウィジェットのスタイルを変更する。
  • フォーカスがアプリケーション外に移動すると、object パラメーターは nullptr になります。
  • フォーカスがウィジェット内のサブウィジェットに移動しても、このシグナルは発行されません。サブウィジェットのフォーカスイベントを処理するには、QFocusEvent シグナルを使用する必要があります。
  • QWindow::focusObjectChanged() シグナルは、ウィジェットがフォーカスを獲得または失うたびに発行されます。


フォーカスが移動したときに、フォーカスのあるウィジェットのスタイルを変更する

void MyWindow::onObjectFocusChanged(QObject *object) {
  if (object) {
    // フォーカスのあるウィジェットのスタイルを強調する
    object->setStyleSheet("background-color: yellow;");
  } else {
    // フォーカスを失ったウィジェットのスタイルを元に戻す
    previousFocusedObject->setStyleSheet("");
  }
}

フォーカスのあるウィジェットに基づいて、メニュー項目を有効化または無効化する

void MyWindow::onObjectFocusChanged(QObject *object) {
  if (object->isInstanceOf<QLineEdit>()) {
    // フォーカスが QLineEdit にある場合、編集メニューを有効化する
    editMenu->setEnabled(true);
  } else {
    // フォーカスが QLineEdit 以外にある場合、編集メニューを無効化する
    editMenu->setEnabled(false);
  }
}
void MyWindow::onObjectFocusChanged(QObject *object) {
  if (object->isInstanceOf<QLineEdit>()) {
    QLineEdit *lineEdit = static_cast<QLineEdit *>(object);

    // フォーカスのある QLineEdit からテキストを取得する
    QString text = lineEdit->text();

    // テキストを処理する
    std::cout << "Entered text: " << text.toStdString() << std::endl;
  }
}


代替方法

  • QFocusEvent シグナル
    • フォーカスがウィジェット内を移動したときに、より詳細な情報を取得したい場合に適しています。
    • QFocusEvent は、フォーカスが獲得されたウィジェット (enter)、失われたウィジェット (leave)、フォーカスが移動した理由 (reason) などに関する情報を提供します。
    • 例: サブウィジェットのフォーカスイベントを処理する。
void MyWidget::focusInEvent(QFocusEvent *event) {
  std::cout << "Focus entered: " << objectName().toStdString() << std::endl;
}

void MyWidget::focusOutEvent(QFocusEvent *event) {
  std::cout << "Focus lost: " << objectName().toStdString() << std::endl;
}
  • QAbstractInputContext::keyPressEvent() シグナル
    • ユーザーが特定のキーを押したときに、キー入力を処理したい場合に適しています。
    • keyPressEvent は、押されたキー (key)、修飾キー (modifiers) などの情報を提供します。
    • 例: ショートカットキーを実装する。
void MyWindow::keyPressEvent(QKeyEvent *event) {
  if (event->key() == Qt::Key_Ctrl && event->modifiers() & Qt::ShiftModifier) {
    // Ctrl+Shift キーが押されたら、アクションを実行する
    std::cout << "Ctrl+Shift pressed" << std::endl;
  }
}
  • タイマー
    • 定期的にフォーカスの状態をチェックしたい場合に適しています。
    • タイマーを使用して、QWidget::hasFocus() メソッドを定期的に呼び出すことができます。
    • 例: フォーカスがアプリケーション外に移動したときにアクションを実行する。
void MyWindow::startTimer() {
  timerId = startTimer(100); // 100 ミリ秒ごとにタイマーを起動
}

void MyWindow::timerEvent(QTimerEvent *event) {
  if (!hasFocus()) {
    // フォーカスが失われたら、アクションを実行する
    std::cout << "Focus lost" << std::endl;
  }
}

選択の指針

どの代替方法が適切かは、具体的な要件によって異なります。

  • 定期的にフォーカスの状態をチェックしたい場合
    タイマーを使用する
  • キー入力を処理したい場合
    QAbstractInputContext::keyPressEvent() シグナルを使用する
  • 詳細なフォーカス情報が必要な場合
    QFocusEvent シグナルを使用する