Sur cette page

Glisser-déposer

Le glisser-déposer fournit un mécanisme visuel simple que les utilisateurs peuvent utiliser pour transférer des informations entre les applications et à l'intérieur de celles-ci. La fonction glisser-déposer est similaire au mécanisme couper-coller du presse-papiers.

Ce document décrit le mécanisme de base du glisser-déposer et décrit l'approche utilisée pour l'activer dans les contrôles personnalisés. Les opérations de glisser-déposer sont également prises en charge par de nombreux contrôles de Qt, tels que les vues d'éléments et les vues graphiques, ainsi que les contrôles d'édition pour Qt Widgets et Qt Quick. De plus amples informations sur les vues d'éléments et les vues graphiques sont disponibles dans la section Utilisation du glisser-déposer avec les vues d'éléments et le cadre de vues graphiques.

Classes de glisser-déposer

Ces classes traitent du glisser-déposer et de l'encodage et du décodage des types MIME nécessaires.

QDrag

Prise en charge du transfert de données par glisser-déposer basé sur MIME

QDragEnterEvent

Événement envoyé à un widget lorsqu'une action de glisser-déposer y entre

QDragLeaveEvent

Événement envoyé à un widget lorsqu'une action de glisser-déposer le quitte

QDragMoveEvent

Événement envoyé lorsqu'une action de glisser-déposer est en cours

QDropEvent

Événement envoyé lorsqu'une action de glisser-déposer est terminée

QUtiMimeConverter

Conversion entre un type MIME et un format UTI (Uniform Type Identifier)

Configuration

L'objet QStyleHints fournit certaines propriétés liées aux opérations de glisser-déposer :

  • QStyleHints::startDragTime() décrit le temps en millisecondes pendant lequel l'utilisateur doit maintenir le bouton de la souris au-dessus d'un objet avant que le glissement ne commence.
  • QStyleHints::startDragDistance() indique la distance à laquelle l'utilisateur doit déplacer la souris tout en maintenant le bouton de la souris enfoncé avant que le mouvement ne soit interprété comme un glissement.
  • QStyleHints::startDragVelocity() indique la vitesse (en pixels/seconde) à laquelle l'utilisateur doit déplacer la souris pour commencer un déplacement. Une valeur de 0 signifie qu'il n'y a pas de limite.

Ces quantités fournissent des valeurs par défaut raisonnables, conformes au système de fenêtrage sous-jacent, que vous pouvez utiliser si vos contrôles prennent en charge la fonction "glisser-déposer".

Le glisser-déposer dans les contrôles Qt Quick

Le reste du document se concentre principalement sur la manière d'implémenter le glisser-déposer en C++. Pour utiliser le glisser-déposer dans une scène Qt Quick, veuillez lire la documentation relative aux éléments Qt Quick Drag , DragEvent, et DropArea, ainsi que les exemples de glisser-déposerQt Quick .

Glisser

Pour lancer un déplacement, créez un objet QDrag et appelez sa fonction exec(). Dans la plupart des applications, il est préférable de ne commencer une opération de glisser-déposer qu'après avoir appuyé sur un bouton de la souris et déplacé le curseur d'une certaine distance. Cependant, la manière la plus simple d'activer le glisser-déposer à partir d'un widget est de réimplémenter la fonction mousePressEvent() du widget et de lancer une opération de glisser-déposer :

void MainWindow::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton
        && iconLabel->geometry().contains(event->pos())) {

        QDrag *drag = new QDrag(this);
        QMimeData *mimeData = new QMimeData;

        mimeData->setText(commentEdit->toPlainText());
        drag->setMimeData(mimeData);
        drag->setPixmap(iconPixmap);

        Qt::DropAction dropAction = drag->exec();
        ...
    }
}

Bien que l'utilisateur puisse mettre un certain temps à terminer l'opération de glissement, pour l'application, la fonction exec() est une fonction bloquante qui renvoie one of several values. Celles-ci indiquent comment l'opération s'est terminée et sont décrites plus en détail ci-dessous.

Notez que la fonction exec() ne bloque pas la boucle d'événement principale.

Pour les widgets qui doivent faire la distinction entre les clics de souris et les déplacements, il est utile de réimplémenter la fonction mousePressEvent() du widget pour enregistrer la position de départ du déplacement :

void DragWidget::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
        dragStartPosition = event->pos();
}

Plus tard, dans mouseMoveEvent(), nous pouvons déterminer si un déplacement doit commencer et construire un objet de déplacement pour gérer l'opération :

void DragWidget::mouseMoveEvent(QMouseEvent *event)
{
    if (!(event->buttons() & Qt::LeftButton))
        return;
    if ((event->pos() - dragStartPosition).manhattanLength()
         < QApplication::startDragDistance())
        return;

    QDrag *drag = new QDrag(this);
    QMimeData *mimeData = new QMimeData;

    mimeData->setData(mimeType, data);
    drag->setMimeData(mimeData);

    Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction);
    ...
}

Cette approche particulière utilise la fonction QPoint::manhattanLength() pour obtenir une estimation approximative de la distance entre l'endroit où le clic de la souris s'est produit et la position actuelle du curseur. Cette fonction troque la précision contre la rapidité et convient généralement à cet usage.

Dépose

Pour pouvoir recevoir des médias déposés sur un widget, appelez setAcceptDrops(true) pour le widget et réimplémentez les fonctions de gestion d'événements dragEnterEvent() et dropEvent().

Par exemple, le code suivant active les événements de chute dans le constructeur d'une sous-classe de QWidget, ce qui permet d'implémenter utilement des gestionnaires d'événements de chute :

Window::Window(QWidget *parent)
    : QWidget(parent)
{
    ...
    setAcceptDrops(true);
}

La fonction dragEnterEvent() est généralement utilisée pour informer Qt Widgets des types de données que le widget accepte. Vous devez réimplémenter cette fonction si vous souhaitez recevoir QDragMoveEvent ou QDropEvent dans vos réimplémentations de dragMoveEvent() et dropEvent().

Le code suivant montre comment dragEnterEvent() peut être réimplémenté pour indiquer au système de glisser-déposer que nous ne pouvons traiter que du texte brut :

void Window::dragEnterEvent(QDragEnterEvent *event)
{
    if (event->mimeData()->hasFormat("text/plain"))
        event->acceptProposedAction();
}

dropEvent() est utilisé pour décompresser les données déposées et les traiter d'une manière adaptée à votre application.

Dans le code suivant, le texte fourni dans l'événement est transmis à un QTextBrowser et un QComboBox est rempli avec la liste des types MIME utilisés pour décrire les données :

void Window::dropEvent(QDropEvent *event)
{
    textBrowser->setPlainText(event->mimeData()->text());
    mimeTypeCombo->clear();
    mimeTypeCombo->addItems(event->mimeData()->formats());

    event->acceptProposedAction();
}

Dans ce cas, nous acceptons l'action proposée sans vérifier de quoi il s'agit. Dans une application réelle, il peut être nécessaire de revenir de la fonction dropEvent() sans accepter l'action proposée ou sans traiter les données si l'action n'est pas pertinente. Par exemple, nous pouvons choisir d'ignorer les actions de Qt::LinkAction si nous ne prenons pas en charge les liens vers des sources externes dans notre application.

Remplacer les actions proposées

Nous pouvons également ignorer l'action proposée et effectuer une autre action sur les données. Pour ce faire, nous devons appeler setDropAction() de l'objet événement avec l'action préférée de Qt::DropAction avant d'appeler accept(). Cela permet de s'assurer que l'action de remplacement est utilisée au lieu de l'action proposée.

Pour les applications plus sophistiquées, la réimplémentation de dragMoveEvent() et dragLeaveEvent() vous permettra de rendre certaines parties de vos widgets sensibles aux événements de dépôt et vous donnera plus de contrôle sur le glisser-déposer dans votre application.

Sous-classement des widgets complexes

Certains widgets Qt Widgets standard fournissent leur propre support pour le glisser-déposer. Lors de la sous-classification de ces widgets, il peut être nécessaire de réimplémenter dragMoveEvent() en plus de dragEnterEvent() et dropEvent() afin d'empêcher la classe de base de fournir une gestion par défaut du glisser-déposer et de gérer les cas particuliers qui vous intéressent.

Actions de glisser-déposer

Dans le cas le plus simple, la cible d'une action de glisser-déposer reçoit une copie des données glissées, et la source décide de supprimer ou non l'original. C'est ce que décrit l'action CopyAction. La cible peut également choisir de gérer d'autres actions, en particulier les actions MoveAction et LinkAction. Si la source appelle QDrag::exec() et renvoie MoveAction, la source est responsable de la suppression des données d'origine si elle le souhaite. Les objets QMimeData et QDrag créés par le widget source ne doivent pas être supprimés - ils seront détruits par Qtgets. La cible est responsable de la propriété des données envoyées lors de l'opération de glisser-déposer ; cela se fait généralement en conservant des références aux données.

Si la cible comprend l'action LinkAction, elle doit stocker sa propre référence aux informations d'origine ; la source n'a pas besoin d'effectuer d'autres traitements sur les données. L'utilisation la plus courante des actions de glisser-déposer est l'exécution d'un déplacement dans le même widget ; voir la section sur les actions de glisser-déposer pour plus d'informations sur cette fonctionnalité.

L'autre utilisation majeure des actions de glisser-déposer est l'utilisation d'un type de référence tel que text/uri-list, où les données glissées sont en fait des références à des fichiers ou à des objets.

Ajout de nouveaux types de glisser-déposer

Le glisser-déposer ne se limite pas au texte et aux images. Tout type d'information peut être transféré lors d'une opération de glisser-déposer. Pour faire glisser des informations entre des applications, celles-ci doivent être en mesure de s'indiquer mutuellement les formats de données qu'elles peuvent accepter et ceux qu'elles peuvent produire. Pour ce faire, on utilise les types MIME. L'objet QDrag construit par la source contient une liste de types MIME qu'il utilise pour représenter les données (du plus approprié au moins approprié), et la cible du glissement utilise l'un de ces types pour accéder aux données. Pour les types de données courants, les fonctions de commodité gèrent les types MIME utilisés de manière transparente, mais pour les types de données personnalisés, il est nécessaire de les indiquer explicitement.

Pour mettre en œuvre des actions de glisser-déposer pour un type d'information qui n'est pas couvert par les fonctions de commodité de QDrag, la première étape, et la plus importante, consiste à rechercher les formats existants qui sont appropriés : L'IANA (Internet Assigned Numbers Authority) fournit une liste hiérarchique des types de médias MIME à l'ISI(Information Sciences Institute). L'utilisation de types MIME standard maximise l'interopérabilité de votre application avec d'autres logiciels, aujourd'hui et à l'avenir.

Pour prendre en charge un type de média supplémentaire, il suffit de définir les données dans l'objet QMimeData à l'aide de la fonction setData(), en fournissant le type MIME complet et un QByteArray contenant les données dans le format approprié. Le code suivant prend une image d'une étiquette et la stocke en tant que fichier PNG (Portable Network Graphics) dans un objet QMimeData:

    QByteArray output;
    QBuffer outputBuffer(&output);
    outputBuffer.open(QIODevice::WriteOnly);
    imageLabel->pixmap().toImage().save(&outputBuffer, "PNG");
    mimeData->setData("image/png", output);

Bien sûr, dans ce cas, nous aurions pu simplement utiliser setImageData() pour fournir des données d'image dans une variété de formats :

    mimeData->setImageData(QVariant(*imageLabel->pixmap()));

L'approche QByteArray reste utile dans ce cas, car elle permet de mieux contrôler la quantité de données stockées dans l'objet QMimeData.

Notez que les types de données personnalisés utilisés dans les vues d'éléments doivent être déclarés comme meta objects et que les opérateurs de flux pour eux doivent être implémentés.

Actions de dépôt

Dans le modèle du presse-papiers, l'utilisateur peut couper ou copier l'information source, puis la coller. De même, dans le modèle de glisser-déposer, l'utilisateur peut faire glisser une copie de l'information ou faire glisser l'information elle-même vers un nouvel emplacement(la déplacer ). Le modèle "glisser-déposer" présente une complication supplémentaire pour le programmeur : Le programme ne sait pas si l'utilisateur veut couper ou copier l'information tant que l'opération n'est pas terminée. Cela ne fait souvent aucune différence lorsqu'il s'agit de faire glisser des informations d'une application à l'autre, mais au sein d'une même application, il est important de vérifier quelle action de dépôt a été utilisée.

Nous pouvons réimplémenter la fonction mouseMoveEvent() pour un widget et lancer une opération de glisser-déposer avec une combinaison d'actions de dépôt possibles. Par exemple, nous pouvons vouloir nous assurer que le glisser-déposer déplace toujours les objets dans le widget :

void DragWidget::mouseMoveEvent(QMouseEvent *event)
{
    if (!(event->buttons() & Qt::LeftButton))
        return;
    if ((event->pos() - dragStartPosition).manhattanLength()
         < QApplication::startDragDistance())
        return;

    QDrag *drag = new QDrag(this);
    QMimeData *mimeData = new QMimeData;

    mimeData->setData(mimeType, data);
    drag->setMimeData(mimeData);

    Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction);
    ...
}

L'action renvoyée par la fonction exec() peut être par défaut CopyAction si l'information est déposée dans une autre application, mais si elle est déposée dans un autre widget de la même application, nous pouvons obtenir une action de dépôt différente.

Les actions de dépôt proposées peuvent être filtrées dans la fonction dragMoveEvent() d'un widget. Toutefois, il est possible d'accepter toutes les actions proposées dans la fonction dragEnterEvent() et de laisser l'utilisateur décider de celles qu'il souhaite accepter ultérieurement :

void DragWidget::dragEnterEvent(QDragEnterEvent *event)
{
    event->acceptProposedAction();
}

Lorsqu'une chute se produit dans le widget, la fonction dropEvent() est appelée, et nous pouvons traiter chaque action possible à tour de rôle. Tout d'abord, nous traitons les opérations de glisser-déposer au sein d'un même widget :

void DragWidget::dropEvent(QDropEvent *event)
{
    if (event->source() == this && event->possibleActions() & Qt::MoveAction)
        return;

Dans ce cas, nous refusons de traiter les opérations de déplacement. Chaque type d'action de dépôt que nous acceptons est vérifié et traité en conséquence :

    if (event->proposedAction() == Qt::MoveAction) {
        event->acceptProposedAction();
        // Process the data from the event.
    } else if (event->proposedAction() == Qt::CopyAction) {
        event->acceptProposedAction();
        // Process the data from the event.
    } else {
        // Ignore the drop.
        return;
    }
    ...
}

Notez que nous avons vérifié les actions de dépôt individuelles dans le code ci-dessus. Comme nous l'avons mentionné plus haut dans la section " Remplacer les actions proposées", il est parfois nécessaire de remplacer l'action de dépôt proposée et d'en choisir une autre parmi la sélection d'actions de dépôt possibles. Pour ce faire, vous devez vérifier la présence de chaque action dans la valeur fournie par l'événement possibleActions(), définir l'action de dépôt avec setDropAction() et appeler accept().

Déposer des rectangles

La fonction dragMoveEvent() du widget peut être utilisée pour limiter les dépôts à certaines parties du widget en n'acceptant les actions de dépôt proposées que lorsque le curseur se trouve dans ces zones. Par exemple, le code suivant accepte toutes les actions de dépôt proposées lorsque le curseur se trouve au-dessus d'un widget enfant (dropFrame) :

void Window::dragMoveEvent(QDragMoveEvent *event)
{
    if (event->mimeData()->hasFormat("text/plain")
        && event->answerRect().intersects(dropFrame->geometry()))

        event->acceptProposedAction();
}

La fonction dragMoveEvent() peut également être utilisée pour donner un retour visuel lors d'une opération de glisser-déposer, pour faire défiler la fenêtre ou pour toute autre raison appropriée.

Le presse-papiers

Les applications peuvent également communiquer entre elles en plaçant des données dans le presse-papiers. Pour y accéder, vous devez obtenir un objet QClipboard à partir de l'objet QApplication.

La classe QMimeData est utilisée pour représenter les données transférées vers et depuis le presse-papiers. Pour placer des données dans le presse-papiers, vous pouvez utiliser les fonctions de commodité setText(), setImage() et setPixmap() pour les types de données courants. Ces fonctions sont similaires à celles de la classe QMimeData, sauf qu'elles prennent un argument supplémentaire qui contrôle l'endroit où les données sont stockées : Si Clipboard est spécifié, les données sont placées dans le presse-papiers ; si Selection est spécifié, les données sont placées dans la sélection de la souris (sur X11 uniquement). Par défaut, les données sont placées dans le presse-papiers.

Par exemple, nous pouvons copier le contenu d'un site QLineEdit dans le presse-papiers avec le code suivant :

QGuiApplication::clipboard()->setText(lineEdit->text(), QClipboard::Clipboard);

Des données de différents types MIME peuvent également être placées dans le presse-papiers. Construisez un objet QMimeData et définissez les données avec la fonction setData() de la manière décrite dans la section précédente ; cet objet peut ensuite être placé dans le presse-papiers avec la fonction setMimeData().

La classe QClipboard peut informer l'application des modifications apportées aux données qu'elle contient via son signal dataChanged(). Par exemple, nous pouvons surveiller le presse-papiers en connectant ce signal à un slot dans un widget :

    connect(clipboard, &QClipboard::dataChanged,
            this, &ClipWindow::updateClipboard);

Le slot connecté à ce signal peut lire les données du presse-papiers en utilisant l'un des types MIME qui peuvent être utilisés pour les représenter :

void ClipWindow::updateClipboard()
{
    mimeTypeCombo->clear();

    QStringList formats = clipboard->mimeData()->formats();
    if (formats.isEmpty())
        return;

    for (const auto &format : formats) {
        QByteArray data = clipboard->mimeData()->data(format);
        // ...
    }

Le signal selectionChanged() peut être utilisé sur X11 pour surveiller la sélection de la souris.

Exemples

Interopérabilité avec d'autres applications

Sur X11, le protocole public XDND est utilisé, tandis que sur Windows Qt utilise le standard OLE, et Qt pour macOS utilise le Cocoa Drag Manager. Sur X11, XDND utilise MIME, aucune traduction n'est donc nécessaire. L'API de Qt est la même quelle que soit la plateforme. Sous Windows, les applications compatibles MIME peuvent communiquer en utilisant des noms de format de presse-papiers qui sont des types MIME. Certaines applications Windows utilisent déjà des conventions de dénomination MIME pour leurs formats de presse-papiers.

Des classes personnalisées pour la traduction de formats de presse-papiers propriétaires peuvent être enregistrées en réimplémentant QWindowsMimeConverter sous Windows ou QUtiMimeConverter sous macOS.

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