Sur cette page

Exemple de gestes sur une image

Illustre l'utilisation de gestes simples dans un widget.

Cet exemple montre comment activer les gestes pour un widget et utiliser la saisie par gestes pour effectuer des actions.

Commandes gestuelles sur une image représentant une fleur

Nous utilisons deux classes pour créer l'interface utilisateur de l'application : ` MainWidget ` et ` ImageWidget`. La classe ` MainWidget ` sert simplement de conteneur à la classe ` ImageWidget `, que nous allons configurer pour accepter les entrées par gestes. Comme nous nous intéressons à la manière dont les gestes sont utilisés, nous allons nous concentrer sur l'implémentation de la classe ` ImageWidget `.

Définition de la classe ImageWidget

La classe ImageWidget est une simple sous-classe de QWidget qui réimplémente la fonction de gestion générale QWidget::event() en plus de plusieurs gestionnaires d’événements plus spécifiques :

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*);
    ...
};

Nous implémentons également une fonction d'aide privée, gestureEvent(), pour faciliter la gestion des événements de geste transmis au widget, ainsi que trois fonctions permettant d'effectuer des actions en fonction des gestes : panTriggered(), pinchTriggered() et swipeTriggered().

Implémentation de la classe ImageWidget

Dans le constructeur du widget, nous commençons par configurer divers paramètres qui serviront à contrôler l’affichage des images.

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

Nous activons trois des gestes standard pour le widget en appelant la méthode ` QWidget::grabGesture()` avec les types de gestes dont nous avons besoin. Ceux-ci seront reconnus par le système de reconnaissance de gestes par défaut de l’application, et les événements seront transmis à notre widget.

Comme la méthode ` QWidget ` ne définit pas de gestionnaire d’événements spécifique pour les gestes, le widget doit réimplémenter la méthode générale ` QWidget::event()` pour recevoir les événements de geste.

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

Nous implémentons le gestionnaire d’événements de manière à déléguer les événements de geste à une fonction privée spécialement écrite pour cette tâche, et transmettons tous les autres événements à l’implémentation de ` QWidget`.

La fonction gestureHandler() examine les gestes fournis par le QGestureEvent nouvellement transmis. Étant donné qu’un seul geste d’un type donné peut être utilisé sur un widget à un moment donné, nous pouvons vérifier chaque type de geste à l’aide de la fonction 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;
}

Si un objet ` QGesture ` est fourni pour un certain type de geste, nous appelons une fonction dédiée pour le traiter, en convertissant l’objet `gesture` en sous-classe appropriée de ` QGesture `.

Pour illustrer comment un geste standard peut être interprété par une application, nous présentons ici l’implémentation de la fonction ` pinchTriggered() `, qui gère le geste de pincement lorsque l’utilisateur déplace deux doigts sur l’écran ou le périphérique d’entrée :

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();
}

La classe ` QPinchGesture ` fournit des propriétés permettant d’interpréter la variation de distance entre les deux points de contact comme un facteur de zoom, et le delta d’angle comme une rotation à appliquer à l’image. Le point central entre les points de contact pourrait être utilisé pour faire glisser l’image, mais dans cet exemple, nous utilisons le geste de panoramique à cette fin.

La propriété ` scaleFactor() ` est une valeur relative indiquant dans quelle mesure le zoom doit varier d’un événement à l’autre, tandis que la propriété ` totalScaleFactor() ` fournit la valeur de zoom atteinte depuis le début du geste. Lorsque les points de contact sont relâchés et qu’un autre geste commence, la valeur « totalScaleFactor() » repart de 1,0. Dans ce cas, nous stockons la valeur « totalScaleFactor() » dans la variable « currentStepScaleFactor » afin de pouvoir l’utiliser dans « paintEvent() » pour redimensionner l’image. Il serait également possible de multiplier simplement le facteur d’échelle total stocké par la valeur « scaleFactor() » ici, dans le gestionnaire de pincement.

En revanche, rotationAngle() représente l’angle de rotation depuis le début du geste de pincement, tandis que lastRotationAngle() fournit la valeur précédente. Il est donc nécessaire d’effectuer une soustraction afin d’obtenir un delta incrémental. Lorsque l’utilisateur commence un nouveau geste de pincement, rotationAngle() part de zéro, et nous voulons que l’image commence à pivoter à partir de son angle actuel. Pour ce faire, il suffit d’ajouter le delta à la valeur rotationAngle enregistrée (qui sera appliquée dans paintEvent()). Si nous attribuions simplement totalRotationAngle() à la valeur stockée rotationAngle, un nouveau geste entraînerait la réinitialisation de l’image à une orientation normale avant qu’elle ne recommence à pivoter. Mais il serait possible de stocker l’angle de rotation depuis le début du geste et de l’ajouter à rotationAngle dans paintEvent(), tout comme nous stockons la valeur du zoom depuis le début du geste.

Les gestes de panoramique et de balayage de cet exemple sont également gérés dans des fonctions distinctes et utilisent les valeurs des propriétés des objets QGesture qui leur sont transmis.

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);
}

Dans paintEvent(), scaleFactor représente le niveau de zoom avant le début du geste de pincement, tandis que currentStepScaleFactor représente le facteur de zoom supplémentaire pendant qu’un geste de pincement est en cours. En revanche, pour la rotation, seul l’angle de rotation actuel (rotationAngle) est enregistré. Les décalages horizontal et vertical représentent la distance sur laquelle l’image a été déplacée par le geste de panoramique.

Exemple de projet sur 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.