Auf dieser Seite

Drag & Drop

Drag & Drop bietet einen einfachen visuellen Mechanismus, mit dem Benutzer Informationen zwischen und innerhalb von Anwendungen übertragen können. Drag & Drop ähnelt in seiner Funktionsweise dem Mechanismus zum Ausschneiden und Einfügen über die Zwischenablage.

Dieses Dokument beschreibt den grundlegenden Drag-and-Drop-Mechanismus und erläutert den Ansatz, mit dem er in benutzerdefinierten Steuerelementen implementiert werden kann. Drag-and-Drop-Operationen werden auch von vielen Qt-Steuerelementen unterstützt, beispielsweise von den Item-Ansichten und dem Graphics View Framework sowie von Bearbeitungssteuerelementen für „ Qt Widgets “ und „ Qt Quick “. Weitere Informationen zu Item-Ansichten und dem Graphics View Framework finden Sie unter „Verwendung von Drag-and-Drop mit Item-Ansichten und dem Graphics View Framework“.

Drag-and-Drop-Klassen

Diese Klassen befassen sich mit Drag-and-Drop sowie der erforderlichen Kodierung und Dekodierung von MIME-Typen.

QDrag

Unterstützung für MIME-basierte Drag-and-Drop-Datenübertragung

QDragEnterEvent

Ereignis, das an ein Widget gesendet wird, wenn eine Drag-and-Drop-Aktion in dieses Widget eintritt

QDragLeaveEvent

Ereignis, das an ein Widget gesendet wird, wenn eine Drag-and-Drop-Aktion das Widget verlässt

QDragMoveEvent

Ereignis, das gesendet wird, während eine Drag-and-Drop-Aktion läuft

QDropEvent

Ereignis, das gesendet wird, wenn eine Drag-and-Drop-Aktion abgeschlossen ist

QUtiMimeConverter

Konvertiert zwischen einem MIME-Typ und einem Uniform Type Identifier (UTI)-Format

Konfiguration

Das Objekt „ QStyleHints “ stellt einige Eigenschaften bereit, die sich auf Drag-and-Drop-Vorgänge beziehen:

  • QStyleHints::startDragTime() beschreibt die Zeitdauer in Millisekunden, die der Benutzer eine Maustaste über einem Objekt gedrückt halten muss, bevor ein Ziehen beginnt.
  • QStyleHints::startDragDistance() gibt an, wie weit der Benutzer die Maus bei gedrückter Maustaste bewegen muss, bevor die Bewegung als Ziehen interpretiert wird.
  • QStyleHints::startDragVelocity() gibt an, wie schnell (in Pixeln pro Sekunde) der Benutzer die Maus bewegen muss, um einen Drag-Vorgang zu starten. Ein Wert von „ 0 “ bedeutet, dass es keine solche Begrenzung gibt.

Diese Größen bieten sinnvolle Standardwerte, die mit dem zugrunde liegenden Fenstersystem kompatibel sind und die Sie verwenden können, wenn Sie Drag-and-Drop-Unterstützung in Ihren Steuerelementen bereitstellen.

Drag & Drop in Qt Quick

Der Rest des Dokuments konzentriert sich hauptsächlich darauf, wie Drag & Drop in C++ implementiert wird. Zur Verwendung von Drag & Drop innerhalb einer „ Qt Quick “-Szene lesen Sie bitte die Dokumentation zu den Elementen „ Qt Quick “ ( Drag), „ DragEvent “ und „ DropArea “ sowie die Drag-&-Drop-Beispiele unterQt Quick .

Ziehen

Um einen Drag-Vorgang zu starten, erstellen Sie ein „ QDrag “-Objekt und rufen Sie dessen Funktion „exec()“ auf. In den meisten Anwendungen ist es ratsam, einen Drag-and-Drop-Vorgang erst dann zu starten, wenn eine Maustaste gedrückt und der Cursor eine bestimmte Strecke bewegt wurde. Die einfachste Möglichkeit, das Ziehen aus einem Widget heraus zu ermöglichen, besteht jedoch darin, die Methode ` mousePressEvent()` des Widgets neu zu implementieren und einen Drag-and-Drop-Vorgang zu starten:

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

Auch wenn der Benutzer möglicherweise einige Zeit benötigt, um den Drag-and-Drop-Vorgang abzuschließen, ist die Funktion `exec()` aus Sicht der Anwendung eine blockierende Funktion, die die Werte ` one of several values` zurückgibt. Diese geben an, wie der Vorgang beendet wurde, und werden im Folgenden näher beschrieben.

Beachten Sie, dass die Funktion `exec()` die Haupt-Ereignisschleife nicht blockiert.

Für Widgets, die zwischen Mausklicks und Ziehvorgängen unterscheiden müssen, ist es sinnvoll, die Funktion ` mousePressEvent()` des Widgets neu zu implementieren, um die Startposition des Ziehvorgangs zu erfassen:

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

Später können wir in ` mouseMoveEvent()` feststellen, ob ein Ziehen beginnen soll, und ein Ziehobjekt erstellen, um den Vorgang zu verarbeiten:

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

Dieser spezielle Ansatz nutzt die Funktion ` QPoint::manhattanLength()` (Ziehen), um eine grobe Schätzung der Entfernung zwischen der Stelle, an der der Mausklick erfolgte, und der aktuellen Cursorposition zu erhalten. Diese Funktion opfert Genauigkeit zugunsten der Geschwindigkeit und ist für diesen Zweck in der Regel gut geeignet.

Ablegen

Um Medien empfangen zu können, die auf ein Widget abgelegt werden, rufen Sie für das Widget ` setAcceptDrops(true)` auf und implementieren Sie die Ereignisbehandlungsfunktionen ` dragEnterEvent()` und ` dropEvent()` neu.

Der folgende Code aktiviert beispielsweise Drop-Ereignisse im Konstruktor einer Unterklasse von ` QWidget ` und ermöglicht so die sinnvolle Implementierung von Drop-Ereignisbehandlungsfunktionen:

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

Die Funktion `dragEnterEvent()` wird in der Regel verwendet, um Qt über die Datentypen zu informieren, die das Widget akzeptiert. Sie müssen diese Funktion neu implementieren, wenn Sie in Ihren Neuimplementierungen von ` dragMoveEvent()` und ` dropEvent()` entweder ` QDragMoveEvent ` oder ` QDropEvent ` empfangen möchten.

Der folgende Code zeigt, wie ` dragEnterEvent()` neu implementiert werden kann, um dem Drag-and-Drop-System mitzuteilen, dass wir nur einfachen Text verarbeiten können:

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

Die Methode ` dropEvent()` wird verwendet, um abgelegte Daten zu entpacken und sie auf eine für Ihre Anwendung geeignete Weise zu verarbeiten.

Im folgenden Code wird der im Ereignis übergebene Text an eine ` QTextBrowser `-Funktion weitergeleitet, und ein ` QComboBox ` wird mit der Liste der MIME-Typen gefüllt, die zur Beschreibung der Daten verwendet werden:

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

    event->acceptProposedAction();
}

In diesem Fall akzeptieren wir die vorgeschlagene Aktion, ohne zu prüfen, um welche es sich handelt. In einer realen Anwendung kann es erforderlich sein, aus der Funktion ` dropEvent()` zurückzuspringen, ohne die vorgeschlagene Aktion zu akzeptieren oder die Daten zu verarbeiten, wenn die Aktion nicht relevant ist. Beispielsweise könnten wir uns dafür entscheiden, Aktionen von ` Qt::LinkAction ` zu ignorieren, wenn wir in unserer Anwendung keine Links zu externen Quellen unterstützen.

Vorgeschlagene Aktionen überschreiben

Wir können die vorgeschlagene Aktion auch ignorieren und eine andere Aktion mit den Daten ausführen. Dazu würden wir die Methode ` setDropAction()` des Ereignisobjekts mit der bevorzugten Aktion aus ` Qt::DropAction ` aufrufen, bevor wir ` accept()` aufrufen. Dadurch wird sichergestellt, dass anstelle der vorgeschlagenen Aktion die Aktion „Ersetzen“ verwendet wird.

Für komplexere Anwendungen ermöglicht die Neuimplementierung von ` dragMoveEvent()` und ` dragLeaveEvent()`, bestimmte Teile Ihrer Widgets für Drop-Ereignisse empfindlich zu machen und Ihnen mehr Kontrolle über Drag-and-Drop in Ihrer Anwendung zu geben.

Unterklassen komplexer Widgets

Bestimmte Standard-Qt-Widgets bieten eine eigene Unterstützung für Drag-and-Drop. Beim Erstellen von Unterklassen dieser Widgets kann es erforderlich sein, zusätzlich zu ` dragEnterEvent()` und ` dropEvent()` auch ` dragMoveEvent()` neu zu implementieren, um zu verhindern, dass die Basisklasse eine Standard-Drag-and-Drop-Verarbeitung bereitstellt, und um spezielle Fälle zu behandeln, die für Sie von Interesse sind.

Drag-and-Drop-Aktionen

Im einfachsten Fall erhält das Ziel einer Drag-and-Drop-Aktion eine Kopie der verschobenen Daten, und die Quelle entscheidet, ob das Original gelöscht werden soll. Dies wird durch die Aktion ` CopyAction ` beschrieben. Das Ziel kann auch andere Aktionen verarbeiten, insbesondere die Aktionen „ MoveAction “ und „ LinkAction “. Wenn die Quelle „ QDrag::exec()“ aufruft und diese Funktion „ MoveAction “ zurückgibt, ist die Quelle dafür verantwortlich, die Originaldaten zu löschen, falls sie dies wünscht. Die vom Quell-Widget erstellten Objekte ` QMimeData ` und ` QDrag ` sollten nicht gelöscht werden – sie werden von Qt vernichtet. Das Ziel ist dafür verantwortlich, die Verantwortung für die im Drag-and-Drop-Vorgang gesendeten Daten zu übernehmen; dies geschieht in der Regel durch das Speichern von Verweisen auf die Daten.

Wenn das Ziel die Aktion „ LinkAction “ versteht, sollte es eine eigene Referenz auf die ursprünglichen Informationen speichern; die Quelle muss keine weitere Verarbeitung der Daten vornehmen. Am häufigsten werden Drag-and-Drop-Aktionen bei einer Verschiebung innerhalb desselben Widgets verwendet; weitere Informationen zu dieser Funktion finden Sie im Abschnitt über „Drop-Aktionen “.

Eine weitere wichtige Anwendung von Drag-Aktionen ist die Verwendung eines Referenztyps wie „text/uri-list“, bei dem es sich bei den verschobenen Daten tatsächlich um Verweise auf Dateien oder Objekte handelt.

Hinzufügen neuer Drag-and-Drop-Typen

Drag-and-Drop ist nicht auf Text und Bilder beschränkt. Jede Art von Informationen kann im Rahmen eines Drag-and-Drop-Vorgangs übertragen werden. Um Informationen zwischen Anwendungen zu verschieben, müssen die Anwendungen einander mitteilen können, welche Datenformate sie akzeptieren und welche sie erzeugen können. Dies wird mithilfe von MIME-Typen erreicht. Das von der Quelle erstellte „ QDrag “-Objekt enthält eine Liste der MIME-Typen, die es zur Darstellung der Daten verwendet (geordnet vom am besten geeigneten zum am wenigsten geeigneten), und das Ablageziel nutzt einen dieser Typen, um auf die Daten zuzugreifen. Bei gängigen Datentypen verwalten die Hilfsfunktionen die verwendeten MIME-Typen transparent; bei benutzerdefinierten Datentypen müssen diese jedoch explizit angegeben werden.

Um Drag-and-Drop-Aktionen für einen Datentyp zu implementieren, der nicht von den „ QDrag “-Hilfsfunktionen abgedeckt wird, besteht der erste und wichtigste Schritt darin, nach geeigneten bestehenden Formaten zu suchen: Die Internet Assigned Numbers Authority (IANA) stellt beim Information Sciences Institute (ISI) eine hierarchische Liste von MIME-Medientypen zur Verfügung. Die Verwendung von Standard-MIME-Typen maximiert die Interoperabilität Ihrer Anwendung mit anderer Software – heute und in Zukunft.

Um einen zusätzlichen Medientyp zu unterstützen, legen Sie die Daten einfach im ` QMimeData `-Objekt mit der Funktion ` setData()` fest, wobei Sie den vollständigen MIME-Typ und eine ` QByteArray ` angeben, die die Daten im entsprechenden Format enthält. Der folgende Code übernimmt ein Pixmap aus einem Label und speichert es als Portable Network Graphics (PNG)-Datei in einem ` QMimeData `-Objekt:

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

Natürlich hätten wir in diesem Fall stattdessen einfach ` setImageData()` verwenden können, um Bilddaten in verschiedenen Formaten bereitzustellen:

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

Der Ansatz mit ` QByteArray ` ist in diesem Fall dennoch nützlich, da er eine bessere Kontrolle über die im ` QMimeData `-Objekt gespeicherte Datenmenge ermöglicht.

Beachten Sie, dass benutzerdefinierte Datentypen, die in Elementansichten verwendet werden, als ` meta objects ` deklariert werden müssen und dass die entsprechenden Stream-Operatoren implementiert werden müssen.

Ablegeaktionen

Im Zwischenablage-Modell kann der Benutzer die Quellinformationen ausschneiden oder kopieren und später einfügen. Ähnlich verhält es sich im Drag-and-Drop-Modell: Der Benutzer kann eine Kopie der Informationen ziehen oder die Informationen selbst an einen neuen Ort ziehen (verschieben ). Das Drag-and-Drop-Modell bringt für den Programmierer eine zusätzliche Komplikation mit sich: Das Programm weiß erst nach Abschluss des Vorgangs, ob der Benutzer die Informationen ausschneiden oder kopieren wollte. Beim Verschieben von Informationen zwischen Anwendungen spielt dies oft keine Rolle, doch innerhalb einer Anwendung ist es wichtig zu prüfen, welche Ablageaktion verwendet wurde.

Wir können die Funktion `mouseMoveEvent()` für ein Widget neu implementieren und einen Drag-and-Drop-Vorgang mit einer Kombination möglicher Ablageaktionen starten. Beispielsweise möchten wir vielleicht sicherstellen, dass das Ziehen Objekte im Widget immer verschiebt:

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

Die von der Funktion `exec()` zurückgegebene Aktion ist standardmäßig möglicherweise „ CopyAction “, wenn die Informationen in einer anderen Anwendung abgelegt werden; werden sie jedoch in einem anderen Widget derselben Anwendung abgelegt, erhalten wir möglicherweise eine andere Ablageaktion.

Die vorgeschlagenen Ablageaktionen können in der dragMoveEvent()-Funktion eines Widgets gefiltert werden. Es ist jedoch möglich, alle vorgeschlagenen Aktionen in der dragEnterEvent()-Funktion zu akzeptieren und den Benutzer später entscheiden zu lassen, welche er akzeptieren möchte:

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

Wenn ein Ablegen im Widget erfolgt, wird die Handler-Funktion `dropEvent()` aufgerufen, und wir können nacheinander jede mögliche Aktion bearbeiten. Zunächst behandeln wir Drag-and-Drop-Vorgänge innerhalb desselben Widgets:

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

In diesem Fall lehnen wir die Bearbeitung von Verschiebungsvorgängen ab. Jede Art von Ablageaktion, die wir akzeptieren, wird geprüft und entsprechend behandelt:

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

Beachten Sie, dass wir im obigen Code auf einzelne Drop-Aktionen geprüft haben. Wie oben im Abschnitt „Überschreiben vorgeschlagener Aktionen“ erwähnt, ist es manchmal notwendig, die vorgeschlagene Drop-Aktion zu überschreiben und eine andere aus der Auswahl möglicher Drop-Aktionen zu wählen. Dazu müssen Sie im Wert, der von der Methode ` possibleActions()` des Ereignisses zurückgegeben wird, auf das Vorhandensein der jeweiligen Aktion prüfen, die Ablegeaktion mit ` setDropAction()` festlegen und ` accept()` aufrufen.

Ablegebereiche

Die Methode `dragMoveEvent()` des Widgets kann verwendet werden, um das Ablegen auf bestimmte Bereiche des Widgets zu beschränken, indem vorgeschlagene Ablegeaktionen nur akzeptiert werden, wenn sich der Cursor innerhalb dieser Bereiche befindet. Der folgende Code akzeptiert beispielsweise alle vorgeschlagenen Ablegeaktionen, wenn sich der Cursor über einem untergeordneten Widget befindet (`dropFrame`):

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

        event->acceptProposedAction();
}

Die Methode `dragMoveEvent()` kann auch verwendet werden, wenn Sie während eines Drag-and-Drop-Vorgangs visuelles Feedback geben, das Fenster scrollen oder andere geeignete Aktionen ausführen möchten.

Die Zwischenablage

Anwendungen können auch miteinander kommunizieren, indem sie Daten in die Zwischenablage kopieren. Um darauf zuzugreifen, müssen Sie ein ` QClipboard `-Objekt vom ` QApplication `-Objekt abrufen.

Die Klasse „ QMimeData “ dient zur Darstellung von Daten, die zur Zwischenablage übertragen oder von dieser abgerufen werden. Um Daten in die Zwischenablage zu kopieren, können Sie für gängige Datentypen die Hilfsfunktionen „setText()“, „setImage()“ und „setPixmap()“ verwenden. Diese Funktionen ähneln denen der Klasse ` QMimeData `, mit dem Unterschied, dass sie ein zusätzliches Argument akzeptieren, das steuert, wo die Daten gespeichert werden: Wird ` Clipboard ` angegeben, werden die Daten in die Zwischenablage kopiert; wird ` Selection ` angegeben, werden die Daten in die Mausauswahl kopiert (nur unter X11). Standardmäßig werden die Daten in die Zwischenablage kopiert.

Beispielsweise können wir den Inhalt eines „ QLineEdit “-Objekts mit dem folgenden Code in die Zwischenablage kopieren:

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

Auch Daten mit unterschiedlichen MIME-Typen können in die Zwischenablage kopiert werden. Erstellen Sie ein ` QMimeData `-Objekt und legen Sie Daten mit der Funktion `setData()` wie im vorherigen Abschnitt beschrieben fest; dieses Objekt kann dann mit der Funktion ` setMimeData()` in die Zwischenablage kopiert werden.

Die Klasse ` QClipboard ` kann die Anwendung über Änderungen an den darin enthaltenen Daten mittels ihres Signals ` dataChanged()` benachrichtigen. So können wir beispielsweise die Zwischenablage überwachen, indem wir dieses Signal mit einem Slot in einem Widget verbinden:

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

Der mit diesem Signal verbundene Slot kann die Daten in der Zwischenablage mithilfe eines der MIME-Typen auslesen, die zu ihrer Darstellung verwendet werden können:

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

Das Signal ` selectionChanged()` kann unter X11 verwendet werden, um die Mausauswahl zu überwachen.

Beispiele

Zusammenarbeit mit anderen Anwendungen

Unter X11 wird das öffentliche XDND-Protokoll verwendet, während Qt unter Windows den OLE-Standard und Qt für macOS den Cocoa Drag Manager nutzt. Unter X11 verwendet XDND MIME, sodass keine Übersetzung erforderlich ist. Die Qt-API ist plattformunabhängig identisch. Unter Windows können MIME-fähige Anwendungen mithilfe von Namen für Zwischenablageformate kommunizieren, die MIME-Typen sind. Einige Windows-Anwendungen verwenden bereits MIME-Namenskonventionen für ihre Zwischenablageformate.

Benutzerdefinierte Klassen zur Konvertierung proprietärer Zwischenablageformate können registriert werden, indem unter Windows ` QWindowsMimeConverter ` oder unter macOS ` QUtiMimeConverter ` neu implementiert wird.

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