이 페이지에서

이미지 제스처 예시

위젯에서 간단한 제스처를 사용하는 방법을 보여줍니다.

이 예제는 위젯에서 제스처를 활성화하고 제스처 입력을 통해 작업을 수행하는 방법을 보여줍니다.

꽃 사진에 대한 제스처 입력

애플리케이션의 사용자 인터페이스를 생성하기 위해 ` MainWidget `와 ` ImageWidget`라는 두 가지 클래스를 사용합니다. ` 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()`와, 제스처에 기반하여 동작을 수행하는 세 가지 함수인 ` 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()를 호출하여 필요한 제스처 유형을 지정함으로써 위젯에 세 가지 표준 제스처를 활성화합니다. 이러한 제스처는 애플리케이션의 기본 제스처 인식기에 의해 인식되며, 이벤트는 해당 위젯으로 전달됩니다.

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 에서 제공한 제스처를 검사합니다. 특정 시점에 위젯에서 특정 유형의 제스처는 하나만 사용할 수 있으므로, 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() 함수의 구현 예를 보여드리겠습니다:

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 클래스는 두 터치 지점 간의 거리 변화를 확대/축소 배율로, 각도 변화를 이미지에 적용할 회전 각도로 해석하는 속성을 제공합니다. 터치 지점 사이의 중심점을 사용하여 이미지를 드래그할 수도 있지만, 이 예제에서는 그 목적으로 팬 제스처를 사용합니다.

scaleFactor() 은 한 이벤트에서 다음 이벤트로 줌이 얼마나 변경되어야 하는지를 나타내는 상대적 값인 반면, totalScaleFactor() 는 제스처가 시작된 이후로 표현된 줌의 양을 제공합니다. 터치 지점을 떼고 다른 제스처가 시작되면, ` totalScaleFactor() `는 1.0에서 다시 시작됩니다. 이 경우, ` totalScaleFactor() `를 ` currentStepScaleFactor ` 변수에 저장하여 ` paintEvent() `에서 이미지를 확대/축소하는 데 사용할 수 있도록 합니다. 또는 핀치 핸들러 내에서 저장된 총 확대/축소 계수에 ` scaleFactor() `를 단순히 곱하는 방법도 가능합니다.

반면, rotationAngle() 는 핀치 제스처가 시작된 이후의 회전량을 나타내는 반면, lastRotationAngle() 는 이전 값을 제공합니다. 따라서 증분 델타를 얻으려면 차감을 해야 합니다. 사용자가 새로운 핀치 제스처를 시작하면 ` rotationAngle() `는 0부터 시작하며, 이미지는 현재 각도에서 회전을 시작해야 합니다. 이는 델타를 저장된 ` rotationAngle `에 더함으로써 달성됩니다(이 값은 ` paintEvent()`에서 적용될 것입니다). 만약 단순히 ` totalRotationAngle() `를 저장된 ` rotationAngle`에 할당한다면, 새로운 제스처가 발생했을 때 이미지는 다시 회전을 시작하기 전에 정상 방향으로 초기화될 것입니다. 하지만 제스처가 시작된 시점부터의 회전 각도를 저장해 두었다가, 제스처가 시작된 시점부터의 줌 양을 저장하는 것과 마찬가지로 ` paintEvent()`에서 ` rotationAngle `에 이를 더할 수 있습니다.

이 예제의 팬(pan) 및 스와이프(swipe) 제스처도 별도의 함수에서 처리되며, 해당 함수로 전달된 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

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.