En esta página

Ejemplo de arrastrar y soltar

El ejemplo muestra cómo distinguir los distintos formatos MIME disponibles en una operación de arrastrar y soltar.

Captura de pantalla del ejemplo Drop Site

El ejemplo de Drop Site acepta drops de otras aplicaciones, y muestra los formatos MIME proporcionados por el objeto drag.

En este ejemplo hay dos clases, DropArea y DropSiteWindow, y una función main(). Un objeto DropArea es instanciado en DropSiteWindow; un objeto DropSiteWindow es entonces invocado en la función main().

Definición de la clase DropArea

La clase DropArea es una subclase de QLabel con una ranura pública, clear(), y una señal changed().

class DropArea : public QLabel
{
    Q_OBJECT

public:
    explicit DropArea(QWidget *parent = nullptr);

public slots:
    void clear();

signals:
    void changed(const QMimeData *mimeData = nullptr);

Además, DropArea contiene reimplementaciones de cuatro manejadores de eventos de QWidget:

  1. dragEnterEvent()
  2. dragMoveEvent()
  3. dragLeaveEvent()
  4. dropEvent()

Estos manejadores de eventos se explican con más detalle en la implementación de la clase DropArea.

protected:
    void dragEnterEvent(QDragEnterEvent *event) override;
    void dragMoveEvent(QDragMoveEvent *event) override;
    void dragLeaveEvent(QDragLeaveEvent *event) override;
    void dropEvent(QDropEvent *event) override;
};

Implementación de la clase DropArea

En el constructor DropArea, establecemos la propiedad minimum size a 200x200 pixels, la propiedad frame style a QFrame::Sunken y QFrame::StyledPanel, y alineamos su contenido al centro.

DropArea::DropArea(QWidget *parent)
    : QLabel(parent)
{
    setMinimumSize(200, 200);
    setFrameStyle(QFrame::Sunken | QFrame::StyledPanel);
    setAlignment(Qt::AlignCenter);
    setAcceptDrops(true);
    setAutoFillBackground(true);
    clear();
}

También, habilitamos los eventos drop en DropArea estableciendo la propiedad acceptDrops a true. Luego, habilitamos la propiedad autoFillBackground e invocamos la función clear().

El manejador de eventos dragEnterEvent() es llamado cuando un arrastre está en progreso y el ratón entra en el objeto DropArea. Para el ejemplo DropSite, cuando el ratón entra en DropArea, establecemos su texto a "<drop content>" y resaltamos su fondo.

void DropArea::dragEnterEvent(QDragEnterEvent *event)
{
    setText(tr("<drop content>"));
    setBackgroundRole(QPalette::Highlight);

    event->acceptProposedAction();
    emit changed(event->mimeData());
}

A continuación, invocamos acceptProposedAction() sobre event, estableciendo la acción drop a la propuesta. Por último, emitimos la señal changed(), con los datos que se han soltado y la información de su tipo MIME como parámetro.

Para dragMoveEvent(), simplemente aceptamos el objeto QDragMoveEvent propuesto, event, con acceptProposedAction().

void DropArea::dragMoveEvent(QDragMoveEvent *event)
{
    event->acceptProposedAction();
}

La implementación de la clase DropArea de dropEvent() extrae los datos mime de event y los muestra en consecuencia.

void DropArea::dropEvent(QDropEvent *event)
{
    const QMimeData *mimeData = event->mimeData();

El objeto mimeData puede contener uno de los siguientes objetos: una imagen, texto HTML, texto Markdown, texto plano o una lista de URL.

    if (mimeData->hasImage()) {
        setPixmap(qvariant_cast<QPixmap>(mimeData->imageData()));
    } else if (mimeData->hasColor()) {
        const auto color = mimeData->colorData().value<QColor>();
        setText(tr("Color: %1").arg(color.name()));
        setPalette(QPalette(color));
        setBackgroundRole(QPalette::Button);
    } else if (mimeData->hasFormat(u"text/markdown"_s)) {
        setText(QString::fromUtf8(mimeData->data(u"text/markdown"_s)));
        setTextFormat(Qt::MarkdownText);
    } else if (mimeData->hasHtml()) {
        setText(mimeData->html());
        setTextFormat(Qt::RichText);
    } else if (mimeData->hasText()) {
        setText(mimeData->text());
        setTextFormat(Qt::PlainText);
    } else if (mimeData->hasUrls()) {
        QList<QUrl> urlList = mimeData->urls();
        QString text;
        for (qsizetype i = 0, count = qMin(urlList.size(), qsizetype(32)); i < count; ++i)
            text += urlList.at(i).path() + u'\n';
        setText(text);
    } else {
        setText(tr("Cannot display data"));
    }
  • Si mimeData contiene una imagen, la mostramos en DropArea con setPixmap().
  • Si mimeData contiene HTML, lo mostramos con setText() y establecemos el formato de texto de DropArea como Qt::RichText.
  • Si mimeData contiene Markdown, lo mostramos con setText() y establecemos el formato de texto de DropArea como Qt::MarkdownText.
  • Si mimeData contiene texto sin formato, lo mostramos con setText() y establecemos el formato de texto de DropArea como Qt::PlainText. En caso de que mimeData contenga URLs, iteramos a través de la lista de URLs para mostrarlas en líneas individuales.
  • Si mimeData contiene otros tipos de objetos, establecemos el texto de DropArea con setText() en "No se pueden mostrar datos" para informar al usuario.

Entonces establecemos DropArea's backgroundRole a QPalette::Dark y aceptamos event's acción propuesta.

    if (!mimeData->hasColor()) {
        setPalette({});
        setBackgroundRole(QPalette::Dark);
    }
    event->acceptProposedAction();
}

El manejador de eventos dragLeaveEvent() es llamado cuando un arrastre está en progreso y el ratón abandona el widget.

void DropArea::dragLeaveEvent(QDragLeaveEvent *event)
{
    clear();
    event->accept();
}

Para la implementación de DropArea's, invocamos claramente clear() y luego aceptamos el evento propuesto.

La función clear() establece el texto en DropArea a "<drop content>" y establece el backgroundRole a QPalette::Dark. Por último, emite la señal changed().

void DropArea::clear()
{
    setText(tr("<drop content>"));
    setBackgroundRole(QPalette::Dark);

    emit changed();
}

Definición de la clase DropSiteWindow

La clase DropSiteWindow contiene un constructor y un slot público, updateFormatsTable().

class DropSiteWindow : public QWidget
{
    Q_OBJECT

public:
    DropSiteWindow();

public slots:
    void updateFormatsTable(const QMimeData *mimeData);
    void copy();

private:
    DropArea *dropArea;
    QLabel *abstractLabel;
    QTableWidget *formatsTable;

    QPushButton *clearButton;
    QPushButton *copyButton;
    QPushButton *quitButton;
    QDialogButtonBox *buttonBox;
};

La clase también contiene una instancia privada de DropArea, dropArea, QLabel, abstractLabel, QTableWidget, formatsTable, QDialogButtonBox, buttonBox, y dos objetos QPushButton, clearButton y quitButton.

Implementación de la clase DropSiteWindow

En el constructor de DropSiteWindow, instanciamos abstractLabel y establecemos su propiedad wordWrap en true. También llamamos a la función adjustSize() para ajustar el tamaño de abstractLabel en función de su contenido.

DropSiteWindow::DropSiteWindow()
{
    abstractLabel = new QLabel(tr("This example accepts drags from other "
                                  "applications and displays the MIME types "
                                  "provided by the drag object."));
    abstractLabel->setWordWrap(true);
    abstractLabel->adjustSize();

A continuación, instanciamos dropArea y conectamos su señal changed() a la ranura updateFormatsTable() de DropSiteWindow.

    dropArea = new DropArea;
    connect(dropArea, &DropArea::changed,
            this, &DropSiteWindow::updateFormatsTable);

Ahora configuramos el objeto QTableWidget, formatsTable. Su cabecera horizontal se establece utilizando un objeto QStringList, labels. El número de columnas es de dos y la tabla no es editable. Además, la cabecera horizontal de formatTable se formatea para garantizar que su segunda columna se estira para ocupar el espacio adicional disponible.

    formatsTable = new QTableWidget;
    formatsTable->setColumnCount(2);
    formatsTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
    formatsTable->setHorizontalHeaderLabels({tr("Format"),  tr("Content")});
    formatsTable->horizontalHeader()->setStretchLastSection(true);

Tres objetos QPushButton, clearButton, copyButton, y quitButton, se instancian y se añaden a buttonBox - un objeto QDialogButtonBox. Aquí utilizamos QDialogButtonBox para garantizar que los pulsadores se presenten en un diseño que se ajuste al estilo de la plataforma.

    clearButton = new QPushButton(tr("Clear"));
    copyButton = new QPushButton(tr("Copy"));
    quitButton = new QPushButton(tr("Quit"));

    buttonBox = new QDialogButtonBox;
    buttonBox->addButton(clearButton, QDialogButtonBox::ActionRole);
    buttonBox->addButton(copyButton, QDialogButtonBox::ActionRole);
#if !QT_CONFIG(clipboard)
    copyButton->setVisible(false);
#endif

    buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole);

    connect(quitButton, &QAbstractButton::clicked, this, &QWidget::close);
    connect(clearButton, &QAbstractButton::clicked, dropArea, &DropArea::clear);
    connect(copyButton, &QAbstractButton::clicked, this, &DropSiteWindow::copy);

Las señales clicked() para copyButton, clearButton, y quitButton se conectan a copy(), clear() y close(), respectivamente.

Para el diseño, utilizamos QVBoxLayout, mainLayout, para organizar nuestros widgets verticalmente. También fijamos el título de la ventana en "Drop Site" y el tamaño mínimo en 350x500 píxeles.

    QVBoxLayout *mainLayout = new QVBoxLayout(this);
    mainLayout->addWidget(abstractLabel);
    mainLayout->addWidget(dropArea);
    mainLayout->addWidget(formatsTable);
    mainLayout->addWidget(buttonBox);

    setWindowTitle(tr("Drop Site"));
    resize(700, 500);
}

Pasamos a la función updateFormatsTable(). Esta función actualiza formatsTable, mostrando los formatos MIME del objeto soltado sobre el objeto DropArea. En primer lugar, establecemos la propiedad rowCount de QTableWidget en 0. A continuación, validamos para asegurarnos de que el objeto QMimeData pasado es un objeto válido.

void DropSiteWindow::updateFormatsTable(const QMimeData *mimeData)
{
    formatsTable->setRowCount(0);
    copyButton->setEnabled(false);
    if (!mimeData)
        return;

Una vez que estamos seguros de que mimeData es válido, iteramos a través de sus formatos soportados.

Nota: La función formats() devuelve un objeto QStringList, que contiene todos los formatos soportados por mimeData.

    const QStringList formats = mimeData->formats();
    for (const QString &format : formats) {
        QTableWidgetItem *formatItem = new QTableWidgetItem(format);
        formatItem->setFlags(Qt::ItemIsEnabled);
        formatItem->setTextAlignment(Qt::AlignTop | Qt::AlignLeft);

Dentro de cada iteración, creamos un objeto QTableWidgetItem, formatItem y establecemos su flags en Qt::ItemIsEnabled, y su text alignment en Qt::AlignTop y Qt::AlignLeft.

Un objeto QString, text, se personaliza para mostrar los datos según el contenido de format. Invocamos la función simplified() de QString en text, para obtener una cadena que no tenga espacios adicionales antes, después o entre las palabras.

        QString text;
        if (format == u"text/plain") {
            text = mimeData->text().simplified();
        } else if (format == u"text/markdown") {
            text = QString::fromUtf8(mimeData->data(u"text/markdown"_s));
        } else if (format == u"text/html") {
            text = mimeData->html().simplified();
        } else if (format == u"text/uri-list") {
            QList<QUrl> urlList = mimeData->urls();
            for (qsizetype i = 0, count = qMin(urlList.size(), qsizetype(32)); i < count; ++i)
                text.append(urlList.at(i).toString() + u' ');
        } else {
            QByteArray data = mimeData->data(format);
            if (data.size() > 32)
                data.truncate(32);
            text = QString::fromLatin1(data.toHex(' ')).toUpper();
        }

Si format contiene una lista de URL, iteramos a través de ellas, utilizando espacios para separarlas. Por otro lado, si format contiene una imagen, mostramos los datos convirtiendo el texto a hexadecimal.

        int row = formatsTable->rowCount();
        formatsTable->insertRow(row);
        formatsTable->setItem(row, 0, new QTableWidgetItem(format));
        formatsTable->setItem(row, 1, new QTableWidgetItem(text));
    }

    formatsTable->resizeColumnToContents(0);
#if QT_CONFIG(clipboard)
    copyButton->setEnabled(formatsTable->rowCount() > 0);
#endif
}

Una vez que text ha sido personalizado para contener los datos apropiados, insertamos tanto format como text en formatsTable con setItem(). Por último, invocamos resizeColumnToContents() en la primera columna de formatsTable.

La función main()

Dentro de la función main(), instanciamos DropSiteWindow e invocamos su función show().

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    DropSiteWindow window;
    window.show();
    return app.exec();
}

Proyecto de ejemplo @ 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.