Ziehen und Ablegen
Drag and Drop bietet einen einfachen visuellen Mechanismus, mit dem Benutzer Informationen zwischen und innerhalb von Anwendungen übertragen können. Die Funktion von Drag and Drop ähnelt dem Ausschneiden und Einfügen in der Zwischenablage.
Dieses Dokument beschreibt den grundlegenden Drag-and-Drop-Mechanismus und skizziert den Ansatz, der verwendet wird, um ihn in benutzerdefinierten Steuerelementen zu aktivieren. Drag-and-Drop-Operationen werden auch von vielen Qt-Steuerelementen unterstützt, z. B. von den Elementansichten und dem Framework für Grafikansichten sowie von den Bearbeitungssteuerelementen für Qt Widgets und Qt Quick. Weitere Informationen über Elementansichten und Grafikansichten finden Sie in Verwenden von Drag and Drop mit Elementansichten und dem Grafikansichts-Framework.
Drag-and-Drop-Klassen
Diese Klassen befassen sich mit Drag and Drop und der notwendigen Mime-Type-Kodierung und -Dekodierung.
Unterstützung für MIME-basierte Drag&Drop-Datenübertragung | |
Ereignis, das an ein Widget gesendet wird, wenn eine Drag-and-Drop-Aktion es betritt | |
Ereignis, das an ein Widget gesendet wird, wenn eine Drag&Drop-Aktion es verlässt | |
Ereignis, das gesendet wird, während eine Drag&Drop-Aktion ausgeführt wird | |
Ereignis, das gesendet wird, wenn eine Drag&Drop-Aktion abgeschlossen ist | |
Konvertierung zwischen einem MIME-Typ und einem Uniform Type Identifier (UTI) Format |
Konfiguration
Das Objekt QStyleHints bietet einige Eigenschaften, die sich auf Drag&Drop-Aktionen beziehen:
- QStyleHints::startDragTime() beschreibt die Zeitspanne in Millisekunden, die der Benutzer die Maustaste über einem Objekt gedrückt halten muss, bevor der Ziehvorgang beginnt.
- QStyleHints::startDragDistance() gibt an, wie weit der Benutzer die Maus bewegen muss, während er die Maustaste gedrückt hält, bevor die Bewegung als Ziehen interpretiert wird.
- QStyleHints::startDragVelocity() gibt an, wie schnell (in Pixel/Sekunde) der Benutzer die Maus bewegen muss, um ein Ziehen zu starten. Ein Wert von
0
bedeutet, dass es keine solche Grenze gibt.
Diese Größen bieten sinnvolle Standardwerte, die mit dem zugrundeliegenden Fenstersystem konform sind und die Sie verwenden können, wenn Sie in Ihren Steuerelementen Drag-and-Drop-Unterstützung anbieten.
Ziehen und Ablegen in Qt Quick
Der Rest des Dokuments konzentriert sich hauptsächlich darauf, wie Drag and Drop in C++ implementiert wird. Für die Verwendung von Drag and Drop innerhalb einer Qt Quick Szene, lesen Sie bitte die Dokumentation für die Qt Quick Drag , DragEvent und DropArea Elemente, sowie die Qt Quick Drag and Drop Beispiele.
Ziehen von
Um ein Drag zu starten, erstellen Sie ein QDrag Objekt und rufen dessen exec() Funktion auf. In den meisten Anwendungen ist es ratsam, einen Drag&Drop-Vorgang erst dann zu starten, wenn eine Maustaste gedrückt und der Cursor um eine bestimmte Strecke bewegt wurde. Der einfachste Weg, das Ziehen von einem Widget aus zu ermöglichen, besteht jedoch darin, die Funktion mousePressEvent() des Widgets neu zu implementieren und einen Drag&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 einige Zeit braucht, um den Ziehvorgang abzuschließen, ist die Funktion exec() für die Anwendung eine blockierende Funktion, die mit one of several values zurückkehrt. Diese zeigen an, wie der Vorgang beendet wurde, und werden im Folgenden näher beschrieben.
Beachten Sie, dass die exec()-Funktion die Hauptereignisschleife nicht blockiert.
Für Widgets, die zwischen Mausklicks und Ziehen unterscheiden müssen, ist es nützlich, die Funktion mousePressEvent() des Widgets neu zu implementieren, um die Startposition des Ziehens aufzuzeichnen:
void DragWidget::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) dragStartPosition = event->pos(); }
Später können wir in mouseMoveEvent() bestimmen, ob ein Ziehen beginnen soll, und ein Ziehen-Objekt konstruieren, um die Operation zu behandeln:
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); ... }
Bei diesem Ansatz wird die Funktion QPoint::manhattanLength() verwendet, um eine grobe Schätzung der Entfernung zwischen der Stelle, an der der Mausklick erfolgte, und der aktuellen Cursorposition zu erhalten. Diese Funktion tauscht Genauigkeit gegen Geschwindigkeit und ist für diesen Zweck normalerweise geeignet.
Ablegen
Um Medien zu empfangen, die auf einem Widget abgelegt werden, rufen Sie setAcceptDrops(true) für das Widget auf und implementieren Sie die Ereignisbehandlungsfunktionen dragEnterEvent() und dropEvent() neu.
Der folgende Code aktiviert beispielsweise Drop-Ereignisse im Konstruktor einer QWidget Unterklasse, wodurch es möglich ist, Drop-Ereignishandler sinnvoll zu implementieren:
Die Funktion dragEnterEvent() wird normalerweise verwendet, um Qt über die Datentypen zu informieren, die das Widget akzeptiert. Sie müssen diese Funktion neu implementieren, wenn Sie entweder QDragMoveEvent oder QDropEvent in Ihren Neuimplementierungen von dragMoveEvent() und dropEvent() empfangen möchten.
Der folgende Code zeigt, wie dragEnterEvent() neu implementiert werden kann, um dem Drag&Drop-System mitzuteilen, dass wir nur einfachen Text verarbeiten können:
void Window::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasFormat("text/plain")) event->acceptProposedAction(); }
dropEvent() wird verwendet, um abgelegte Daten zu entpacken und sie so zu behandeln, wie es für Ihre Anwendung geeignet ist.
Im folgenden Code wird der im Ereignis gelieferte Text an QTextBrowser übergeben und 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, worum es sich handelt. In einer realen Anwendung kann es notwendig sein, von der Funktion dropEvent() zurückzukehren, ohne die vorgeschlagene Aktion zu akzeptieren oder die Daten zu verarbeiten, wenn die Aktion nicht relevant ist. Zum Beispiel können wir uns dafür entscheiden, Qt::LinkAction Aktionen zu ignorieren, wenn wir in unserer Anwendung keine Links zu externen Quellen unterstützen.
Vorgeschlagene Aktionen außer Kraft setzen
Wir können auch die vorgeschlagene Aktion ignorieren und eine andere Aktion mit den Daten durchführen. Dazu würden wir setDropAction() des Ereignisobjekts mit der bevorzugten Aktion von Qt::DropAction aufrufen, bevor wir accept() aufrufen. Auf diese Weise wird sichergestellt, dass die Ersatz-Aktion anstelle der vorgeschlagenen Aktion verwendet wird.
Für anspruchsvollere Anwendungen können Sie durch die Neuimplementierung von dragMoveEvent() und dragLeaveEvent() bestimmte Teile Ihrer Widgets für Drop-Ereignisse empfindlich machen und so mehr Kontrolle über Drag & Drop in Ihrer Anwendung erhalten.
Unterklassifizierung komplexer Widgets
Bestimmte Standard Qt Widgets bieten ihre eigene Unterstützung für Drag & Drop. Wenn Sie diese Widgets subklassifizieren, kann es notwendig sein, dragMoveEvent() zusätzlich zu dragEnterEvent() und dropEvent() zu reimplementieren, um zu verhindern, dass die Basisklasse eine Standard-Drag-and-Drop-Behandlung anbietet, und um alle Spezialfälle zu behandeln, an denen Sie interessiert sind.
Drag and Drop-Aktionen
Im einfachsten Fall erhält das Ziel einer Drag&Drop-Aktion eine Kopie der gezogenen Daten, und die Quelle entscheidet, ob das Original gelöscht werden soll. Dies wird durch die Aktion CopyAction
beschrieben. Das Ziel kann sich auch für die Verarbeitung anderer Aktionen entscheiden, insbesondere für die Aktionen MoveAction
und LinkAction
. Wenn die Quelle QDrag::exec() aufruft und MoveAction
zurückgibt, ist die Quelle für das Löschen der Originaldaten verantwortlich, wenn sie sich dafür entscheidet. Die vom Quell-Widget erstellten Objekte QMimeData und QDrag sollten nicht gelöscht werden - sie werden von Qt zerstört. Das Ziel ist verantwortlich für die Übernahme der Daten, die beim Ziehen und Ablegen gesendet wurden; dies geschieht normalerweise durch das Beibehalten von Referenzen auf die Daten.
Wenn das Ziel die Aktion LinkAction
versteht, sollte es seine eigene Referenz auf die ursprünglichen Informationen speichern; die Quelle muss keine weitere Verarbeitung der Daten vornehmen. Die häufigste Verwendung von Drag & Drop-Aktionen ist das Verschieben innerhalb desselben Widgets; weitere Informationen zu dieser Funktion finden Sie im Abschnitt über Drop-Aktionen.
Die andere wichtige Verwendung von Drag & Drop-Aktionen ist die Verwendung eines Referenztyps wie Text/uri-Liste, bei dem die gezogenen Daten tatsächlich Referenzen auf Dateien oder Objekte sind.
Hinzufügen neuer Drag&Drop-Typen
Das Ziehen und Ablegen ist nicht auf Text und Bilder beschränkt. Jede Art von Information kann per Drag&Drop übertragen werden. Um Informationen zwischen Anwendungen zu ziehen, müssen die Anwendungen einander mitteilen können, welche Datenformate sie akzeptieren und welche sie erzeugen können. Dies wird durch die Verwendung von MIME-Typen erreicht. Das von der Quelle erstellte Objekt QDrag enthält eine Liste von MIME-Typen, die zur Darstellung der Daten verwendet werden (in der Reihenfolge vom am besten geeigneten bis zum am wenigsten geeigneten), und das Ablageziel verwendet einen dieser Typen für den Zugriff auf die Daten. Für gängige Datentypen handhaben die Komfortfunktionen die verwendeten MIME-Typen transparent, aber für benutzerdefinierte Datentypen ist es notwendig, sie explizit anzugeben.
Um Drag&Drop-Aktionen für einen Informationstyp zu implementieren, der nicht von den QDrag Convenience-Funktionen abgedeckt wird, besteht der erste und wichtigste Schritt darin, nach geeigneten bestehenden Formaten zu suchen: Die Internet Assigned Numbers Authority(IANA) stellt eine hierarchische Liste von MIME-Medientypen beim Information Sciences Institute(ISI) zur Verfügung. Durch die Verwendung von Standard-MIME-Typen wird die Interoperabilität Ihrer Anwendung mit anderer Software jetzt und in Zukunft maximiert.
Um einen zusätzlichen Medientyp zu unterstützen, setzen Sie einfach die Daten im Objekt QMimeData mit der Funktion setData(), wobei Sie den vollständigen MIME-Typ und ein QByteArray mit den Daten im entsprechenden Format angeben. Der folgende Code nimmt eine Pixmap von einem Etikett und speichert sie als PNG-Datei (Portable Network Graphics) 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 für diesen Fall auch einfach setImageData() verwenden können, um Bilddaten in einer Vielzahl von Formaten bereitzustellen:
mimeData->setImageData(QVariant(*imageLabel->pixmap()));
Der Ansatz QByteArray ist in diesem Fall immer noch nützlich, da er eine bessere Kontrolle über die Menge der im QMimeData Objekt gespeicherten Daten bietet.
Beachten Sie, dass benutzerdefinierte Datentypen, die in Elementansichten verwendet werden, als meta objects deklariert werden müssen und dass Stream-Operatoren für sie implementiert werden müssen.
Drop-Aktionen
Im Zwischenablagemodell kann der Benutzer die Quellinformationen ausschneiden oder kopieren und später einfügen. In ähnlicher Weise kann der Benutzer im Drag&Drop-Modell eine Kopie der Information ziehen oder die Information selbst an eine neue Stelle ziehen(verschieben ). Das Drag&Drop-Modell ist für den Programmierer mit einer zusätzlichen Komplikation verbunden: Das Programm weiß nicht, ob der Benutzer die Informationen ausschneiden oder kopieren will, bis der Vorgang abgeschlossen ist. Beim Ziehen von Informationen zwischen Anwendungen macht dies oft keinen Unterschied, aber innerhalb einer Anwendung ist es wichtig, zu prüfen, welche Ablegeaktion verwendet wurde.
Wir können das mouseMoveEvent() für ein Widget neu implementieren und einen Drag&Drop-Vorgang mit einer Kombination möglicher Drop-Aktionen starten. Zum Beispiel können wir sicherstellen, dass das Ziehen immer Objekte im Widget 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 exec()-Funktion zurückgegebene Aktion kann standardmäßig CopyAction
sein, wenn die Informationen in einer anderen Anwendung abgelegt werden, aber wenn sie in einem anderen Widget in derselben Anwendung abgelegt werden, können wir eine andere Ablegeaktion erhalten.
Die vorgeschlagenen Drop-Aktionen können in der dragMoveEvent()-Funktion eines Widgets gefiltert werden. Es ist jedoch auch möglich, alle vorgeschlagenen Aktionen im dragEnterEvent() zu akzeptieren und den Benutzer entscheiden zu lassen, welche er später akzeptieren möchte:
void DragWidget::dragEnterEvent(QDragEnterEvent *event) { event->acceptProposedAction(); }
Wenn ein Drop im Widget stattfindet, wird die dropEvent()-Handler-Funktion aufgerufen, und wir können jede mögliche Aktion der Reihe nach behandeln. Zunächst behandeln wir Drag- und Drop-Operationen innerhalb desselben Widgets:
void DragWidget::dropEvent(QDropEvent *event) { if (event->source() == this && event->possibleActions() & Qt::MoveAction) return;
In diesem Fall verweigern wir die Behandlung von Verschiebeoperationen. Jede Art von Drop-Aktion, 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 einzelne Ablegeaktionen geprüft haben. Wie oben im Abschnitt über das Überschreiben vorgeschlagener Aktionen erwähnt, ist es manchmal notwendig, die vorgeschlagene Absetzaktion zu überschreiben und eine andere aus der Auswahl möglicher Absetzaktionen zu wählen. Dazu müssen Sie das Vorhandensein jeder Aktion in dem von possibleActions() des Ereignisses gelieferten Wert überprüfen, die Drop-Aktion mit setDropAction() festlegen und accept() aufrufen.
Drop-Rechtecke
Das dragMoveEvent() des Widgets kann verwendet werden, um Drop-Aktionen auf bestimmte Teile des Widgets zu beschränken, indem die vorgeschlagenen Drop-Aktionen nur akzeptiert werden, wenn sich der Cursor innerhalb dieser Bereiche befindet. Zum Beispiel akzeptiert der folgende Code alle vorgeschlagenen Drop-Aktionen, wenn sich der Cursor über einem untergeordneten Widget (dropFrame
) befindet:
void Window::dragMoveEvent(QDragMoveEvent *event) { if (event->mimeData()->hasFormat("text/plain") && event->answerRect().intersects(dropFrame->geometry())) event->acceptProposedAction(); }
Das dragMoveEvent() kann auch verwendet werden, wenn Sie während einer Drag-and-Drop-Operation eine visuelle Rückmeldung geben müssen, um das Fenster zu scrollen oder was auch immer angemessen ist.
Die Zwischenablage
Anwendungen können auch miteinander kommunizieren, indem sie Daten in der Zwischenablage ablegen. Um darauf zuzugreifen, müssen Sie ein QClipboard Objekt vom QApplication Objekt erhalten.
Die Klasse QMimeData wird verwendet, um Daten zu repräsentieren, die in die und aus der Zwischenablage übertragen werden. Um Daten in der Zwischenablage abzulegen, können Sie die Komfortfunktionen setText(), setImage() und setPixmap() für gängige Datentypen verwenden. Diese Funktionen ähneln denen der Klasse QMimeData, mit dem Unterschied, dass sie ein zusätzliches Argument annehmen, das bestimmt, wo die Daten gespeichert werden: Wenn Clipboard angegeben wird, werden die Daten in der Zwischenablage abgelegt; wenn Selection angegeben wird, werden die Daten in der Mausauswahl abgelegt (nur unter X11). Standardmäßig werden die Daten in der Zwischenablage abgelegt.
Mit dem folgenden Code können wir zum Beispiel den Inhalt von QLineEdit in die Zwischenablage kopieren:
QGuiApplication::clipboard()->setText(lineEdit->text(), QClipboard::Clipboard);
Es können auch Daten mit anderen MIME-Typen in die Zwischenablage kopiert werden. Konstruieren Sie ein QMimeData Objekt und setzen Sie die Daten mit der Funktion setData() wie im vorherigen Abschnitt beschrieben; dieses Objekt kann dann mit der Funktion setMimeData() in die Zwischenablage gelegt werden.
Die Klasse QClipboard kann die Anwendung über ihr Signal dataChanged() über Änderungen an den enthaltenen Daten informieren. Wir können zum Beispiel 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 mit einem der MIME-Typen lesen, 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
Interoperabilität 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 verwendet. Unter X11 verwendet XDND MIME, so dass keine Übersetzung erforderlich ist. Die Qt-API ist unabhängig von der Plattform die gleiche. Unter Windows können MIME-fähige Anwendungen kommunizieren, indem sie Namen von Zwischenablageformaten verwenden, die MIME-Typen sind. Einige Windows-Anwendungen verwenden bereits MIME-Namenskonventionen für ihre Zwischenablageformate.
Benutzerdefinierte Klassen für die Übersetzung von proprietären Zwischenablageformaten können durch Neuimplementierung von QWindowsMimeConverter unter Windows oder QUtiMimeConverter unter macOS registriert werden.
© 2025 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.