Sur cette page

Glisser-déposer

Le glisser-déposer offre un mécanisme visuel simple permettant aux utilisateurs de transférer des informations entre applications et au sein de celles-ci. Le glisser-déposer fonctionne de manière similaire au mécanisme de coupure et de collage du presse-papiers.

Ce document décrit le mécanisme de base du glisser-déposer et présente 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 Qt, tels que les vues d'éléments et le framework de vue graphique, ainsi que les contrôles d'édition pour Qt Widgets et Qt Quick. Vous trouverez plus d'informations sur les vues d'éléments et le framework de vue graphique dans la section Utilisation du glisser-déposer avec les vues d'éléments et le framework de vue graphique.

Classes de glisser-déposer

Ces classes gèrent le glisser-déposer ainsi que l’encodage et le décodage des types MIME nécessaires.

QDrag

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

QDragEnterEvent

Événement envoyé à un widget lorsqu’une action de glisser-déposer y pénètre

QDragLeaveEvent

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

QDragMoveEvent

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

QDropEvent

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

QUtiMimeConverter

Convertit un type MIME en un format UTI (Uniform Type Identifier) et inversement

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 un bouton de souris enfoncé sur un objet avant qu’un glissement ne commence.
  • QStyleHints::startDragDistance() indique la distance que l'utilisateur doit parcourir avec la souris tout en maintenant un bouton 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 déclencher un glissement. Une valeur de 0 signifie qu’il n’y a pas de limite.

Ces paramètres fournissent des valeurs par défaut raisonnables, conformes au système de fenêtrage sous-jacent, que vous pouvez utiliser si vous intégrez la prise en charge du glisser-déposer dans vos contrôles.

Glisser-déposer dans Qt Quick

La suite de ce document se concentre principalement sur la mise en œuvre du glisser-déposer en C++. Pour utiliser le glisser-déposer au sein d’une scène Qt Quick, veuillez consulter la documentation relative aux éléments Qt Quick, Drag, DragEvent et DropArea, ainsi que les exemples de glisser-déposer disponibles surQt Quick .

Glisser-déposer

Pour lancer un glissement, créez un objet ` QDrag ` et appelez sa fonction `exec()`. Dans la plupart des applications, il est recommandé de ne lancer une opération de glisser-déposer qu’après avoir cliqué sur un bouton de la souris et déplacé le curseur sur une certaine distance. Cependant, la manière la plus simple d’activer le glissement à partir d’un widget consiste à réimplémenter la méthode ` mousePressEvent()` du widget et à 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 glisser-déposer, du point de vue de l’application, la fonction `exec()` est une fonction bloquante qui renvoie des codes d’erreur ( one of several values). Ceux-ci indiquent comment l’opération s’est terminée et sont décrits plus en détail ci-dessous.

Notez que la fonction `exec()` ne bloque pas la boucle d’événements principale.

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

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

Plus tard, dans ` mouseMoveEvent()`, nous pouvons déterminer si un glissement doit commencer, et créer un objet de glissement 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 souris s’est produit et la position actuelle du curseur. Cette fonction privilégie la rapidité au détriment de la précision, et convient généralement à cet usage.

Déposer

Pour pouvoir recevoir des éléments déposés sur un widget, appelez ` setAcceptDrops(true)` pour ce widget, puis réimplémentez les fonctions de gestion d’événements ` dragEnterEvent()` et ` dropEvent()`.

Par exemple, le code suivant active les événements de dépôt dans le constructeur d’une sous-classe de ` QWidget `, ce qui permet de mettre en œuvre utilement des gestionnaires d’événements de dépôt :

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

La fonction dragEnterEvent() est généralement utilisée pour indiquer à Qt les types de données acceptés par le widget. Vous devez réimplémenter cette fonction si vous souhaitez recevoir soit QDragMoveEvent, soit QDropEvent dans vos réimplémentations de dragMoveEvent() et dropEvent().

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

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

La méthode ` dropEvent()` sert à extraire les données déposées et à les traiter de manière adaptée à votre application.

Dans le code suivant, le texte fourni dans l’événement est transmis à une fonction ` QTextBrowser `, et un objet ` QComboBox ` est renseigné 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 s’avérer nécessaire de quitter la fonction dropEvent() sans accepter l’action proposée ni traiter les données si l’action n’est pas pertinente. Par exemple, nous pouvons choisir d’ignorer les actions de Qt::LinkAction si notre application ne prend pas en charge les liens vers des sources externes.

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 appellerions la méthode ` setDropAction()` de l’objet événement avec l’action préférée issue de ` Qt::DropAction ` avant d’appeler ` accept()`. Cela garantit que l’action de glisser-déposer de remplacement est utilisée à la place de l’action proposée.

Pour des applications plus sophistiquées, la réimplémentation des méthodes ` dragMoveEvent()` et ` dragLeaveEvent()` vous permettra de rendre certaines parties de vos widgets sensibles aux événements de dépose et vous offrira un meilleur contrôle sur les opérations de glisser-déposer dans votre application.

Sous-classement de widgets complexes

Certains widgets Qt standard prennent en charge le glisser-déposer de manière native. Lorsque vous créez des sous-classes de ces widgets, il peut s’avérer 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, notamment les actions « MoveAction » et « LinkAction ». Si la source appelle la fonction ` QDrag::exec()` et que celle-ci renvoie « MoveAction », la source est alors chargée de supprimer les 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 Qt. La cible est chargée de prendre possession 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 à ces données.

Si la cible prend en charge l’action « LinkAction », elle doit stocker sa propre référence aux informations d’origine ; la source n’a pas besoin d’effectuer d’autre traitement sur les données. L’utilisation la plus courante des actions de glisser-déposer concerne les opérations de déplacement au sein d’un même widget ; consultez la section consacrée aux actions de dépôt pour plus d’informations sur cette fonctionnalité.

L’autre utilisation majeure des actions de glisser-déposer concerne l’utilisation d’un type de référence tel que « text/uri-list », où les données glissées sont en réalité 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é via une opération de glisser-déposer. Pour faire glisser des informations d’une application à l’autre, celles-ci doivent pouvoir s’indiquer mutuellement quels formats de données elles peuvent accepter et lesquels elles peuvent produire. Cela s’effectue à l’aide des types MIME. L’objet ` QDrag ` créé par la source contient une liste des types MIME qu’il utilise pour représenter les données (classés du plus approprié au moins approprié), et la cible de dépôt utilise l’un d’entre eux 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 ; en revanche, pour les types de données personnalisés, il est nécessaire de les spécifier explicitement.

Pour implémenter des actions de glisser-déposer pour un type d’information qui n’est pas pris en charge par les fonctions de commodité d’ QDrag, la première étape, et la plus importante, consiste à rechercher des formats existants adaptés : l’Internet Assigned Numbers Authority (IANA) fournit une liste hiérarchique des types de médias MIME sur le site de l’Information Sciences Institute (ISI). L’utilisation de types MIME standard optimise l’interopérabilité de votre application avec d’autres logiciels, aujourd’hui comme à 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 au format approprié. Le code suivant récupère une image pixmap à partir d’une étiquette et la stocke sous forme de fichier Portable Network Graphics (PNG) 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 précis, nous aurions pu simplement utiliser ` setImageData()` à la place pour fournir des données d’image dans divers formats :

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

L’approche QByteArray reste toutefois utile dans ce cas, car elle offre un meilleur contrôle sur 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 correspondants doivent être implémentés.

Actions de dépôt

Dans le modèle du presse-papiers, l’utilisateur peut couper ou copier les informations source, puis les coller ultérieurement. De même, dans le modèle glisser-déposer, l’utilisateur peut faire glisser une copie des informations ou faire glisser les informations elles-mêmes vers un nouvel emplacement (lesdéplacer ). Le modèle de glisser-déposer présente une complication supplémentaire pour le programmeur : le programme ne sait pas si l’utilisateur souhaite couper ou copier les informations tant que l’opération n’est pas terminée. Cela n’a souvent aucune importance lors du glissement d’informations entre applications, 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 méthode `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 glissement déplace toujours les objets au sein du 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 une action « CopyAction » si les informations sont déposées dans une autre application, mais si elles sont déposées 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. Cependant, il est possible d’accepter toutes les actions proposées dans la fonction dragEnterEvent() et de laisser l’utilisateur décider ultérieurement celles qu’il souhaite accepter :

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

Lorsqu’un dépôt a lieu dans le widget, la fonction de gestion dropEvent() est appelée, et nous pouvons traiter chacune des actions possibles à tour de rôle. Tout d’abord, nous traitons les opérations de glisser-déposer au sein du 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 mentionné plus haut dans la section consacrée à la redéfinition des actions proposées, il est parfois nécessaire de redéfinir 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 la méthode ` possibleActions()` de l’événement, définir l’action de dépôt avec ` setDropAction()` et appeler ` accept()`.

Rectangles de dépôt

La méthode dragMoveEvent() du widget peut être utilisée pour restreindre les déposes à 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 sur un widget enfant (dropFrame) :

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

        event->acceptProposedAction();
}

La méthode `dragMoveEvent()` peut également être utilisée si vous avez besoin de fournir un retour visuel pendant une opération de glisser-déposer, pour faire défiler la fenêtre, ou pour toute autre action 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 sert à 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 `, à la différence qu’elles prennent également un argument supplémentaire qui contrôle l’emplacement de stockage des donné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 (sous X11 uniquement). Par défaut, les données sont placées dans le presse-papiers.

Par exemple, on peut copier le contenu d'un objet ` QLineEdit ` dans le presse-papiers à l'aide du 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. Créez un objet ` QMimeData ` et définissez les données à l'aide de la fonction `setData()` comme décrit dans la section précédente ; cet objet peut ensuite être placé dans le presse-papiers à l'aide de la fonction ` setMimeData()`.

La classe QClipboard peut signaler à l’application les modifications apportées aux données qu’elle contient via son signal dataChanged(). Par exemple, on peut surveiller le presse-papiers en connectant ce signal à un slot d’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 permettant de 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é sous X11 pour surveiller la sélection de la souris.

Exemples

Interopérabilité avec d’autres applications

Sous X11, on utilise le protocole public XDND, tandis que sous Windows, Qt utilise la norme OLE, et Qt pour macOS utilise le gestionnaire de glisser-déposer Cocoa. Sous X11, XDND utilise MIME, donc aucune conversion n'est nécessaire. L'API Qt est la même quelle que soit la plateforme. Sous Windows, les applications prenant en charge MIME peuvent communiquer en utilisant des noms de format de presse-papiers correspondant à des types MIME. Certaines applications Windows utilisent déjà les conventions de nommage MIME pour leurs formats de presse-papiers.

Des classes personnalisées permettant de convertir des 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.