画像ジェスチャーの例

ウィジェットでの簡単なジェスチャの使い方を示します。

この例では、ウィジェットでジェスチャを有効にし、ジェスチャ入力を使用してアクションを実行する方法を示します。

アプリケーションのユーザー・インタフェースを作成するために、MainWidgetImageWidget の 2 つのクラスを使用します。MainWidget クラスは、ジェスチャ入力を受け付けるように設定するImageWidget クラスのコンテナとして使用します。ジェスチャの使用方法に興味があるので、ImageWidget クラスの実装に集中します。

ImageWidgetクラスの定義

ImageWidget クラスは単純なQWidget サブクラスで、いくつかの特定のイベント・ハンドラに加え、一般的なQWidget::event() ハンドラ関数を再実装しています:

class ImageWidget : public QWidget
{
    Q_OBJECT

public:
    ImageWidget(QWidget *parent = nullptr);
    void openDirectory(const QString &url);
    void grabGestures(const QList<Qt::GestureType> &gestures);

protected:
    bool event(QEvent *event) override;
    void paintEvent(QPaintEvent *event) override;
    void resizeEvent(QResizeEvent *event) override;
    void mouseDoubleClickEvent(QMouseEvent *event) override;

private:
    bool gestureEvent(QGestureEvent *event);
    void panTriggered(QPanGesture*);
    void pinchTriggered(QPinchGesture*);
    void swipeTriggered(QSwipeGesture*);
    ...
};

また、ウィジェットに配信されるジェスチャ・イベントを管理するためのプライベート・ヘルパー関数gestureEvent() と、ジェスチャに基づいてアクションを実行するための3つの関数も実装しています:panTriggered() pinchTriggered() およびswipeTriggered()

ImageWidgetクラスの実装

ウィジェットのコンストラクタでは、まず、画像の表示方法を制御するために使用する各種パラメータを設定します。

ImageWidget::ImageWidget(QWidget *parent)
    : QWidget(parent), position(0), horizontalOffset(0), verticalOffset(0)
    , rotationAngle(0), scaleFactor(1), currentStepScaleFactor(1)
{
    setMinimumSize(QSize(100, 100));
}

必要なジェスチャの種類を指定してQWidget::grabGesture() を呼び出すことで、ウィジェットの標準ジェスチャを3つ有効にします。これらはアプリケーションのデフォルトのジェスチャ認識機能によって認識され、イベントがウィジェットに配信されます。

QWidget はジェスチャ用の特定のイベント・ハンドラを定義していないので、ウィジェットはジェスチャ・イベントを受信するために一般的なQWidget::event() を再実装する必要があります。

bool ImageWidget::event(QEvent *event)
{
    if (event->type() == QEvent::Gesture)
        return gestureEvent(static_cast<QGestureEvent*>(event));
    return QWidget::event(event);
}

イベント・ハンドラを実装して、ジェスチャ・イベントをこのタスクのために特別に書かれたプライベート関数に委譲し、他のすべてのイベントをQWidget の実装に渡します。

gestureHandler() 関数は、新しく配信されたQGestureEvent から提供されたジェスチャを調べます。指定されたタイプのジェスチャは、常に 1 つのウィジェットで 1 つだけ使用できるため、QGestureEvent::gesture() 関数を使用して、各ジェスチャのタイプをチェックできます:

bool ImageWidget::gestureEvent(QGestureEvent *event)
{
    qCDebug(lcExample) << "gestureEvent():" << event;
    if (QGesture *swipe = event->gesture(Qt::SwipeGesture))
        swipeTriggered(static_cast<QSwipeGesture *>(swipe));
    else if (QGesture *pan = event->gesture(Qt::PanGesture))
        panTriggered(static_cast<QPanGesture *>(pan));
    if (QGesture *pinch = event->gesture(Qt::PinchGesture))
        pinchTriggered(static_cast<QPinchGesture *>(pinch));
    return true;
}

ジェスチャの特定のタイプに対してQGesture オブジェクトが提供されている場合、それを処理する特別な目的の関数を呼び出し、ジェスチャ・オブジェクトを適切なQGesture サブクラスにキャストします。

標準的なジェスチャがアプリケーションによってどのように解釈されるかを説明するために、pinchTriggered() 関数の実装を示します。この関数は、ユーザがディスプレイまたは入力デバイス上で2本の指を動かすときのピンチ・ジェスチャを処理します:

void ImageWidget::pinchTriggered(QPinchGesture *gesture)
{
    QPinchGesture::ChangeFlags changeFlags = gesture->changeFlags();
    if (changeFlags & QPinchGesture::RotationAngleChanged) {
        qreal rotationDelta = gesture->rotationAngle() - gesture->lastRotationAngle();
        rotationAngle += rotationDelta;
        qCDebug(lcExample) << "pinchTriggered(): rotate by" <<
            rotationDelta << "->" << rotationAngle;
    }
    if (changeFlags & QPinchGesture::ScaleFactorChanged) {
        currentStepScaleFactor = gesture->totalScaleFactor();
        qCDebug(lcExample) << "pinchTriggered(): zoom by" <<
            gesture->scaleFactor() << "->" << currentStepScaleFactor;
    }
    if (gesture->state() == Qt::GestureFinished) {
        scaleFactor *= currentStepScaleFactor;
        currentStepScaleFactor = 1;
    }
    update();
}

QPinchGesture クラスは、2つのタッチ点間の距離の変化をズーム係数として、また角度デルタを画像に適用される回転として解釈するプロパティを提供します。タッチ・ポイント間の中心点を使用して画像をドラッグすることもできますが、この例ではパン・ジェスチャーを使用しています。

scaleFactor() は、あるイベントから次のイベントへのズームの変化量を表す相対値です。一方、totalScaleFactor() は、ジェスチャを開始してからのズーム量を表します。タッチポイントが離され、別のジェスチャが始まると、totalScaleFactor() は再び 1.0 から始まります。この場合、totalScaleFactor()currentStepScaleFactor 変数に格納し、paintEvent() で画像のスケーリングに使用できるようにします。あるいは、ピンチハンドラで、保存された総スケールファクターにscaleFactor() を単純に乗算することも可能です。

対照的に、rotationAngle() はピンチジェスチャーが始まってからの回転量を表し、lastRotationAngle() は前の値を提供します。そのため、増分デルタを得るためには減算する必要があります。ユーザーが新しいピンチジェスチャーを開始すると、rotationAngle() はゼロから始まり、画像は現在の角度から回転を開始します。これは、保存されているrotationAnglepaintEvent() で適用される)にデルタを追加することで実現されます。単純にtotalRotationAngle() を保存されたrotationAngle に代入すると、新しいジェスチャーによって、画像は再び回転を始める前に右上向きにリセットされます。しかし、ジェスチャを開始してからのズーム量を保存するのと同じように、ジェスチャを開始してからの回転角度を保存し、paintEvent()rotationAngle に追加することは可能です。

この例のパンとスワイプのジェスチャも別の関数で処理され、渡されたQGesture オブジェクトのプロパティの値を使用します。

void ImageWidget::paintEvent(QPaintEvent*)
{
    QPainter p(this);

    if (files.isEmpty() && !path.isEmpty()) {
        p.drawText(rect(), Qt::AlignCenter|Qt::TextWordWrap,
                         tr("No supported image formats found"));
        return;
    }

    const qreal iw = currentImage.width();
    const qreal ih = currentImage.height();
    const qreal wh = height();
    const qreal ww = width();

    p.translate(ww / 2, wh / 2);
    p.translate(horizontalOffset, verticalOffset);
    p.rotate(rotationAngle);
    p.scale(currentStepScaleFactor * scaleFactor, currentStepScaleFactor * scaleFactor);
    p.translate(-iw / 2, -ih / 2);
    p.drawImage(0, 0, currentImage);
}

paintEvent() では、scaleFactor はピンチ・ジェスチャーが始まる前のズーム・レベルを表し、currentStepScaleFactor はピンチ・ジェスチャーが進行中の追加ズーム・ファクターを表します。ただし回転については、現在のrotationAngleのみが保存されます。水平オフセットと垂直オフセットは、パンジェスチャによって画像がドラッグされた距離を表します。

プロジェクト例 @ code.qt.io

©2024 The Qt Company Ltd. 本文書に含まれる文書の著作権は、それぞれの所有者に帰属します。 ここで提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。