En esta página

Arrastrar y soltar

Arrastrar y soltar proporciona un mecanismo visual simple que los usuarios pueden utilizar para transferir información entre y dentro de las aplicaciones. La función de arrastrar y soltar es similar al mecanismo de cortar y pegar del portapapeles.

Este documento describe el mecanismo básico de arrastrar y soltar y describe el enfoque utilizado para habilitarlo en los controles personalizados. Las operaciones de arrastrar y soltar también son compatibles con muchos de los controles de Qt, como las vistas de elementos y el marco de vista de gráficos, así como los controles de edición para Qt Widgets y Qt Quick. Más información sobre las vistas de ítems y la vista gráfica está disponible en Using drag and drop with item views and Graphics View Framework.

Clases de arrastrar y soltar

Estas clases se ocupan de la función de arrastrar y soltar y de la codificación y descodificación necesarias de los tipos MIME.

QDrag

Soporte para la transferencia de datos de arrastrar y soltar basada en MIME

QDragEnterEvent

Evento que se envía a un widget cuando una acción de arrastrar y soltar entra en él

QDragLeaveEvent

Evento que se envía a un widget cuando una acción de arrastrar y soltar lo abandona

QDragMoveEvent

Evento que se envía mientras se realiza una acción de arrastrar y soltar

QDropEvent

Evento que se envía cuando finaliza una acción de arrastrar y soltar

QUtiMimeConverter

Convierte entre un tipo MIME y un formato de identificador de tipo uniforme (UTI)

Configuración

El objeto QStyleHints proporciona algunas propiedades relacionadas con las operaciones de arrastrar y soltar:

  • QStyleHints::startDragTime() describe la cantidad de tiempo en milisegundos que el usuario debe mantener pulsado el botón del ratón sobre un objeto antes de que comience el arrastre.
  • QStyleHints::startDragDistance() indica la distancia que el usuario debe mover el ratón mientras mantiene pulsado un botón del ratón antes de que el movimiento se interprete como arrastre.
  • QStyleHints::startDragVelocity() indica la velocidad (en píxeles/segundo) a la que el usuario debe mover el ratón para iniciar un arrastre. Un valor de 0 significa que no existe tal límite.

Estas cantidades proporcionan valores sensibles por defecto que son compatibles con el sistema de ventanas subyacente para que los utilices si proporcionas soporte de arrastrar y soltar en tus controles.

Arrastrar y soltar en Qt Quick

El resto del documento se centra principalmente en cómo implementar arrastrar y soltar en C++. Para usar arrastrar y soltar dentro de una escena Qt Quick, por favor lea la documentación de los elementos Qt Quick Drag , DragEvent, y DropArea, así como los ejemplos de arrastrar y soltar deQt Quick .

Arrastrar

Para iniciar un arrastre, cree un objeto QDrag, y llame a su función exec(). En la mayoría de las aplicaciones, es una buena idea comenzar una operación de arrastrar y soltar sólo después de que se haya pulsado un botón del ratón y el cursor se haya movido una cierta distancia. Sin embargo, la forma más sencilla de habilitar el arrastre desde un widget es reimplementar la función mousePressEvent() del widget e iniciar una operación de arrastrar y soltar:

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

Aunque el usuario puede tardar algún tiempo en completar la operación de arrastre, en lo que respecta a la aplicación, la función exec() es una función de bloqueo que devuelve con one of several values. Estos indican cómo terminó la operación, y se describen con más detalle a continuación.

Nótese que la función exec() no bloquea el bucle de eventos principal.

Para los widgets que necesitan distinguir entre clics de ratón y arrastres, es útil reimplementar la función mousePressEvent() del widget para registrar la posición inicial del arrastre:

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

Más tarde, en mouseMoveEvent(), podemos determinar si un arrastre debe comenzar, y construir un objeto de arrastre para manejar la operación:

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

Este enfoque particular utiliza la función QPoint::manhattanLength() para obtener una estimación aproximada de la distancia entre el lugar donde se produjo el clic del ratón y la posición actual del cursor. Esta función cambia precisión por velocidad, y suele ser adecuada para este propósito.

Soltar

Para poder recibir medios soltados en un widget, llame a setAcceptDrops(true) para el widget, y reimplemente las funciones manejadoras de eventos dragEnterEvent() y dropEvent().

Por ejemplo, el siguiente código habilita los eventos de caída en el constructor de una subclase de QWidget, lo que permite implementar de forma útil los manejadores de eventos de caída:

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

La función dragEnterEvent() se utiliza normalmente para informar a Qt sobre los tipos de datos que acepta el widget. Debes reimplementar esta función si quieres recibir QDragMoveEvent o QDropEvent en tus reimplementaciones de dragMoveEvent() y dropEvent().

El siguiente código muestra cómo dragEnterEvent() puede ser reimplementado para decirle al sistema de arrastrar y soltar que sólo podemos manejar texto plano:

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

dropEvent() se utiliza para desempaquetar los datos soltados y manejarlos de forma adecuada para su aplicación.

En el siguiente código, el texto suministrado en el evento se pasa a un QTextBrowser y se rellena un QComboBox con la lista de tipos MIME que se utilizan para describir los datos:

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

    event->acceptProposedAction();
}

En este caso, aceptamos la acción propuesta sin comprobar de qué se trata. En una aplicación del mundo real, puede ser necesario volver de la función dropEvent() sin aceptar la acción propuesta o manejar los datos si la acción no es relevante. Por ejemplo, podemos optar por ignorar las acciones de Qt::LinkAction si no admitimos enlaces a fuentes externas en nuestra aplicación.

Anulación de las acciones propuestas

También podemos ignorar la acción propuesta y realizar alguna otra acción sobre los datos. Para hacer esto, llamaríamos al objeto de evento setDropAction() con la acción preferida de Qt::DropAction antes de llamar a accept(). Esto garantiza que se utilice la acción drop sustitutiva en lugar de la acción propuesta.

Para aplicaciones más sofisticadas, reimplementar dragMoveEvent() y dragLeaveEvent() te permitirá hacer ciertas partes de tus widgets sensibles a eventos drop, y te dará más control sobre arrastrar y soltar en tu aplicación.

Subclasificación de widgets complejos

Algunos widgets estándar de Qt proporcionan su propio soporte para arrastrar y soltar. Cuando se subclasifican estos widgets, puede ser necesario reimplementar dragMoveEvent() además de dragEnterEvent() y dropEvent() para evitar que la clase base proporcione un manejo de arrastrar y soltar por defecto, y para manejar cualquier caso especial en el que estés interesado.

Acciones de arrastrar y soltar

En el caso más sencillo, el destino de una acción de arrastrar y soltar recibe una copia de los datos que se están arrastrando, y el origen decide si elimina el original. Esto se describe en la acción CopyAction. El destino también puede optar por gestionar otras acciones, concretamente las acciones MoveAction y LinkAction. Si el origen llama a QDrag::exec(), y éste devuelve MoveAction, el origen es responsable de borrar cualquier dato original si decide hacerlo. Los objetos QMimeData y QDrag creados por el widget fuente no deben ser borrados - serán destruidos por Qt. El destino es responsable de tomar posesión de los datos enviados en la operación de arrastrar y soltar; esto se hace normalmente manteniendo referencias a los datos.

Si el destino entiende la acción LinkAction, debería almacenar su propia referencia a la información original; el origen no necesita realizar ningún procesamiento posterior sobre los datos. El uso más común de las acciones de arrastrar y soltar es cuando se realiza un Movimiento dentro del mismo widget; consulta la sección sobre Acciones de soltar para obtener más información sobre esta función.

El otro uso importante de las acciones de arrastrar y soltar es cuando se utiliza un tipo de referencia como texto/uri-lista, donde los datos arrastrados son en realidad referencias a archivos u objetos.

Añadir nuevos tipos de arrastrar y soltar

Arrastrar y soltar no se limita a texto e imágenes. Cualquier tipo de información puede ser transferida en una operación de arrastrar y soltar. Para arrastrar información entre aplicaciones, éstas deben poder indicarse mutuamente qué formatos de datos pueden aceptar y cuáles pueden producir. Para ello se utilizan los tipos MIME. El objeto QDrag construido por el origen contiene una lista de tipos MIME que utiliza para representar los datos (ordenados del más apropiado al menos apropiado), y el destino de la operación de arrastrar y soltar utiliza uno de ellos para acceder a los datos. Para los tipos de datos comunes, las funciones de conveniencia manejan los tipos MIME utilizados de forma transparente pero, para los tipos de datos personalizados, es necesario indicarlos explícitamente.

Para implementar acciones de arrastrar y soltar para un tipo de información que no esté cubierto por las funciones de conveniencia de QDrag, el primer paso y el más importante es buscar formatos existentes que sean apropiados: La Autoridad de Números Asignados de Internet(IANA) proporciona una lista jerárquica de tipos de medios MIME en el Instituto de Ciencias de la Información(ISI). El uso de tipos MIME estándar maximiza la interoperabilidad de su aplicación con otro software ahora y en el futuro.

Para soportar un tipo de medio adicional, basta con establecer los datos en el objeto QMimeData con la función setData(), suministrando el tipo MIME completo y un QByteArray que contenga los datos en el formato apropiado. El siguiente código toma un pixmap de una etiqueta y lo almacena como un archivo Portable Network Graphics (PNG) en un objeto QMimeData:

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

Por supuesto, para este caso podríamos haber utilizado simplemente setImageData() en su lugar para suministrar datos de imagen en una variedad de formatos:

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

El enfoque QByteArray sigue siendo útil en este caso porque proporciona un mayor control sobre la cantidad de datos almacenados en el objeto QMimeData.

Tenga en cuenta que los tipos de datos personalizados utilizados en las vistas de elementos deben declararse como meta objects y que deben implementarse operadores de flujo para ellos.

Acciones de soltar

En el modelo de portapapeles, el usuario puede cortar o copiar la información de origen y pegarla posteriormente. Del mismo modo, en el modelo de arrastrar y soltar, el usuario puede arrastrar una copia de la información o puede arrastrar la propia información a un nuevo lugar(moverla ). El modelo de arrastrar y soltar tiene una complicación adicional para el programador: El programa no sabe si el usuario quiere cortar o copiar la información hasta que la operación se ha completado. Esto no suele suponer ninguna diferencia cuando se arrastra información entre aplicaciones, pero dentro de una aplicación es importante comprobar qué acción de soltar se ha utilizado.

Podemos reimplementar mouseMoveEvent() para un widget, e iniciar una operación de arrastrar y soltar con una combinación de posibles acciones de soltar. Por ejemplo, podemos querer asegurarnos de que al arrastrar siempre se mueven los objetos del 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);
    ...
}

La acción devuelta por la función exec() puede ser por defecto CopyAction si la información se suelta en otra aplicación pero, si se suelta en otro widget de la misma aplicación, podemos obtener una acción de soltar diferente.

Las acciones drop propuestas pueden filtrarse en la función dragMoveEvent() de un widget. Sin embargo, es posible aceptar todas las acciones propuestas en la función dragEnterEvent() y dejar que el usuario decida cuáles quiere aceptar más tarde:

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

Cuando se produce un drop en el widget, se llama a la función dropEvent() handler, y podemos tratar cada posible acción por turnos. En primer lugar, tratamos las operaciones de arrastrar y soltar dentro del mismo widget:

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

En este caso, rechazamos las operaciones de movimiento. Cada tipo de acción de soltar que aceptamos se comprueba y se trata en consecuencia:

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

Ten en cuenta que en el código anterior hemos comprobado las acciones de arrastrar y soltar individuales. Como se mencionó anteriormente en la sección sobre Anulación de acciones propuestas, a veces es necesario anular la acción de soltar propuesta y elegir una diferente de la selección de posibles acciones de soltar. Para hacer esto, necesitas comprobar la presencia de cada acción en el valor proporcionado por el evento possibleActions(), establecer la acción de caída con setDropAction(), y llamar a accept().

Soltar rectángulos

El evento dragMoveEvent() del widget puede usarse para restringir las acciones de soltar a ciertas partes del widget, aceptando sólo las acciones de soltar propuestas cuando el cursor está dentro de esas áreas. Por ejemplo, el siguiente código acepta cualquier acción de soltar propuesta cuando el cursor está sobre un widget hijo (dropFrame):

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

        event->acceptProposedAction();
}

El dragMoveEvent() también puede usarse si necesitas dar información visual durante una operación de arrastrar y soltar, para desplazar la ventana, o lo que sea apropiado.

El portapapeles

Las aplicaciones también pueden comunicarse entre sí poniendo datos en el portapapeles. Para acceder a él, es necesario obtener un objeto QClipboard a partir del objeto QApplication.

La clase QMimeData se utiliza para representar los datos que se transfieren hacia y desde el portapapeles. Para colocar datos en el portapapeles, puede utilizar las funciones setText(), setImage() y setPixmap() para los tipos de datos más comunes. Estas funciones son similares a las que se encuentran en la clase QMimeData, salvo que también toman un argumento adicional que controla dónde se almacenan los datos: Si se especifica Clipboard, los datos se colocan en el portapapeles; si se especifica Selection, los datos se colocan en la selección del ratón (sólo en X11). Por defecto, los datos se colocan en el portapapeles.

Por ejemplo, podemos copiar el contenido de un QLineEdit en el portapapeles con el siguiente código:

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

También se pueden poner en el portapapeles datos con diferentes tipos MIME. Construya un objeto QMimeData y establezca los datos con la función setData() de la forma descrita en la sección anterior; a continuación, este objeto puede colocarse en el portapapeles con la función setMimeData().

La clase QClipboard puede notificar a la aplicación sobre cambios en los datos que contiene a través de su señal dataChanged(). Por ejemplo, podemos monitorizar el portapapeles conectando esta señal a una ranura de un widget:

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

La ranura conectada a esta señal puede leer los datos del portapapeles utilizando uno de los tipos MIME que se pueden utilizar para representarlos:

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

La señal selectionChanged() puede utilizarse en X11 para monitorizar la selección del ratón.

Ejemplos

Interoperando con otras aplicaciones

En X11, se usa el protocolo público XDND, mientras que en Windows Qt usa el estándar OLE, y Qt para macOS usa el Cocoa Drag Manager. En X11, XDND utiliza MIME, por lo que no es necesaria la traducción. La API de Qt es la misma independientemente de la plataforma. En Windows, las aplicaciones compatibles con MIME pueden comunicarse utilizando nombres de formato de portapapeles que sean tipos MIME. Algunas aplicaciones de Windows ya utilizan convenciones de nomenclatura MIME para sus formatos de portapapeles.

Las clases personalizadas para traducir formatos propietarios de portapapeles pueden registrarse reimplementando QWindowsMimeConverter en Windows o QUtiMimeConverter en 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.