本页内容

图像手势示例

演示了在小部件中使用简单手势的方法。

本示例展示了如何为小部件启用手势功能,并利用手势输入执行操作。

在花朵图片上的手势输入

我们使用两个类来创建应用程序的用户界面: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() 将从零开始,而我们希望图像从其当前角度开始旋转。这可以通过将该增量差值加到已存储的rotationAngle 上来实现(该值将在paintEvent() 中应用)。 如果我们只是将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

© 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.