Auf dieser Seite

Beispiel für einen Drag-and-Drop-Roboter

Veranschaulicht, wie Elemente in einer Grafikansicht per Drag & Drop verschoben werden können.

Das Beispiel „Drag-and-Drop-Roboter“ zeigt, wie Drag-and-Drop in einer Unterklasse von „ QGraphicsItem “ implementiert wird und wie Elemente mithilfe des Qt-Animations-Frameworks animiert werden können.

Anwendung eines Roboters, der auf Drag-and-Drop-Ereignisse reagiert

Die „Graphics View“ stellt die Klasse „ QGraphicsScene “ zur Verwaltung und Interaktion mit einer großen Anzahl benutzerdefinierter 2D-Grafikelemente bereit, die von der Klasse „ QGraphicsItem “ abgeleitet sind, sowie das Widget „ QGraphicsView “ zur Visualisierung der Elemente mit Unterstützung für Zoomen und Drehen.

Dieses Beispiel besteht aus einer ` Robot `-Klasse, einer ` ColorItem `-Klasse und einer `main`-Funktion: Die ` Robot `-Klasse beschreibt einen einfachen Roboter, der aus mehreren von ` RobotPart ` abgeleiteten Gliedmaßen besteht, darunter ` RobotHead ` und ` RobotLimb`; die ` ColorItem `-Klasse stellt eine verschiebbare farbige Ellipse bereit, und die Funktion ` main() ` stellt das Hauptanwendungsfenster bereit.

Zunächst werden wir die Klasse „ Robot “ betrachten, um zu sehen, wie die verschiedenen Teile so zusammengesetzt werden, dass sie mithilfe von „ QPropertyAnimation “ einzeln gedreht und animiert werden können. Anschließend werden wir die Klasse „ ColorItem “ betrachten, um zu demonstrieren, wie Drag-and-Drop zwischen Elementen implementiert wird. Schließlich werden wir die Funktion „main()“ betrachten, um zu sehen, wie wir alle Teile zusammenfügen können, um die endgültige Anwendung zu erstellen.

Definition der Klasse „Robot“

Der Roboter besteht aus drei Hauptklassen: der „ RobotHead “, der „ RobotTorso “ und der „ RobotLimb “, die für die Ober- und Unterarme sowie die Beine verwendet wird. Alle Teile leiten sich von der Klasse „ RobotPart “ ab, die wiederum von „ QGraphicsObject “ erbt. Die Klasse „ Robot “ selbst hat kein visuelles Erscheinungsbild und dient lediglich als Stammknoten für den Roboter.

Beginnen wir mit der Klassendeklaration „ RobotPart “.

class RobotPart : public QGraphicsObject
{
public:
    RobotPart(QGraphicsItem *parent = nullptr);

protected:
    void dragEnterEvent(QGraphicsSceneDragDropEvent *event) override;
    void dragLeaveEvent(QGraphicsSceneDragDropEvent *event) override;
    void dropEvent(QGraphicsSceneDragDropEvent *event) override;

    QColor color = Qt::lightGray;
    bool dragOver = false;
};

Diese Basisklasse erbt von „ QGraphicsObject “. „ QGraphicsObject “ stellt Signale und Slots bereit, indem es von „ QObject “ erbt, und deklariert zudem die Eigenschaften von „ QGraphicsItem “ mithilfe von „ Q_PROPERTY “, wodurch diese Eigenschaften für „ QPropertyAnimation “ zugänglich werden.

RobotPart implementiert außerdem die drei wichtigsten Ereignisbehandler für die Annahme von Drop-Ereignissen: ` dragEnterEvent()`, ` dragLeaveEvent()` und ` dropEvent()`.

Die Farbe wird als Member-Variable gespeichert, zusammen mit der Variablen „ dragOver “, die wir später verwenden werden, um visuell anzuzeigen, dass das Glied Farben annehmen kann, die darauf gezogen werden.

RobotPart::RobotPart(QGraphicsItem *parent)
    : QGraphicsObject(parent), color(Qt::lightGray)
{
    setAcceptDrops(true);
}

RobotPartDer Konstruktor initialisiert das Mitglied „dragOver“ und setzt die Farbe auf „ Qt::lightGray “. Im Konstruktor aktivieren wir die Unterstützung für Drop-Ereignisse durch den Aufruf von „ setAcceptDrops(true)“.

Der Rest der Implementierung dieser Klasse dient der Unterstützung von Drag & Drop.

void RobotPart::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
    if (event->mimeData()->hasColor()) {
        event->setAccepted(true);
        dragOver = true;
        update();
    } else {
        event->setAccepted(false);
    }
}

Der Handler ` dragEnterEvent()` wird aufgerufen, wenn ein Drag-and-Drop-Element in den Bereich des Roboter-Teils gezogen wird.

Die Implementierung des Handlers bestimmt, ob dieses Element als Ganzes die mit dem eingehenden Drag-Objekt verknüpften MIME-Daten akzeptieren kann. ` RobotPart ` stellt ein Basisverhalten für alle Teile bereit, die das Ablegen von Farben akzeptieren. Wenn das eingehende Drag-Objekt also eine Farbe enthält, wird das Ereignis akzeptiert: Wir setzen ` dragOver ` auf ` true ` und rufen `update()` auf, um dem Benutzer ein positives visuelles Feedback zu geben; andernfalls wird das Ereignis ignoriert, wodurch es an übergeordnete Elemente weitergeleitet werden kann.

void RobotPart::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
{
    Q_UNUSED(event);
    dragOver = false;
    update();
}

Der Handler ` dragLeaveEvent()` wird aufgerufen, wenn ein Drag-and-Drop-Element aus dem Bereich des Roboter-Bauteils herausgezogen wird. Unsere Implementierung setzt `dragOver` einfach auf `false` zurück und ruft ` update()` auf, um visuelles Feedback zu geben, dass das gezogene Objekt dieses Element verlassen hat.

void RobotPart::dropEvent(QGraphicsSceneDragDropEvent *event)
{
    dragOver = false;
    if (event->mimeData()->hasColor())
        color = qvariant_cast<QColor>(event->mimeData()->colorData());
    update();
}

Der Handler ` dropEvent()` wird aufgerufen, wenn ein Drag-and-Drop-Element auf ein Element abgelegt wird (d. h., wenn die Maustaste während des Ziehens über dem Element losgelassen wird).

Wir setzen ` dragOver ` auf `false` zurück, weisen dem Element die neue Farbe zu und rufen ` update()` auf.

Die Deklaration und Implementierung von ` RobotHead`, ` RobotTorso` und ` RobotLimb ` sind praktisch identisch. Wir werden ` RobotHead ` im Detail betrachten, da diese Klasse einen kleinen Unterschied aufweist, und die anderen Klassen als Übung für den Leser belassen.

class RobotHead : public RobotPart
{
public:
    RobotHead(QGraphicsItem *parent = nullptr);

    QRectF boundingRect() const override;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;

protected:
    void dragEnterEvent(QGraphicsSceneDragDropEvent *event) override;
    void dropEvent(QGraphicsSceneDragDropEvent *event) override;

private:
    QPixmap pixmap;
};

Die Klasse „ RobotHead “ erbt von „ RobotPart “ und stellt die erforderlichen Implementierungen von „ boundingRect()“ und „ paint()“ bereit. Außerdem implementiert sie „ dragEnterEvent()“ und „dropEvent()“ neu, um eine spezielle Behandlung von Bild-Drops zu ermöglichen.

Die Klasse enthält ein privates „pixmap“-Mitglied, mit dem wir die Unterstützung für das Ablegen von Bildern implementieren können.

RobotHead::RobotHead(QGraphicsItem *parent)
    : RobotPart(parent)
{
}

RobotHead Sie verfügt über einen recht einfachen Konstruktor, der lediglich an den Konstruktor von ` RobotPart` weiterleitet.

QRectF RobotHead::boundingRect() const
{
    return QRectF(-15, -50, 30, 50);
}

Die Neuimplementierung von ` boundingRect()` gibt die Ausmaße des Kopfes zurück. Da wir möchten, dass der Drehpunkt in der unteren Mitte des Elements liegt, haben wir ein Begrenzungsrechteck gewählt, das bei (-15, -50) beginnt und sich über eine Breite von 30 Einheiten und eine Höhe von 50 Einheiten erstreckt. Beim Drehen des Kopfes bleibt der „Hals“ unbewegt, während sich die Oberseite des Kopfes von einer Seite zur anderen neigt.

void RobotHead::paint(QPainter *painter,
           const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(option);
    Q_UNUSED(widget);
    if (pixmap.isNull()) {
        painter->setBrush(dragOver ? color.lighter(130) : color);
        painter->drawRoundedRect(-10, -30, 20, 30, 25, 25, Qt::RelativeSize);
        painter->setBrush(Qt::white);
        painter->drawEllipse(-7, -3 - 20, 7, 7);
        painter->drawEllipse(0, -3 - 20, 7, 7);
        painter->setBrush(Qt::black);
        painter->drawEllipse(-5, -1 - 20, 2, 2);
        painter->drawEllipse(2, -1 - 20, 2, 2);
        painter->setPen(QPen(Qt::black, 2));
        painter->setBrush(Qt::NoBrush);
        painter->drawArc(-6, -2 - 20, 12, 15, 190 * 16, 160 * 16);
    } else {
        painter->scale(.2272, .2824);
        painter->drawPixmap(QPointF(-15 * 4.4, -50 * 3.54), pixmap);
    }
}

In ` paint()` zeichnen wir den eigentlichen Kopf. Die Implementierung ist in zwei Abschnitte unterteilt: Wenn ein Bild auf den Kopf gezogen wurde, zeichnen wir das Bild; andernfalls zeichnen wir einen runden, rechteckigen Roboterkopf mit einfacher Vektorgrafik.

Aus Leistungsgründen kann es je nach Komplexität der zu zeichnenden Elemente oft schneller sein, den Kopf als Bild zu zeichnen, anstatt eine Abfolge von Vektoroperationen zu verwenden.

void RobotHead::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
    if (event->mimeData()->hasImage()) {
        event->setAccepted(true);
        dragOver = true;
        update();
    } else {
        RobotPart::dragEnterEvent(event);
    }
}

Der Roboterkopf kann das Ablegen von Bildern akzeptieren. Um dies zu unterstützen, prüft die Neuimplementierung von ` dragEnterEvent()` ob das Drag-Objekt Bilddaten enthält; ist dies der Fall, wird das Ereignis akzeptiert. Andernfalls greifen wir auf die Basisimplementierung von ` RobotPart ` zurück.

void RobotHead::dropEvent(QGraphicsSceneDragDropEvent *event)
{
    if (event->mimeData()->hasImage()) {
        dragOver = false;
        pixmap = qvariant_cast<QPixmap>(event->mimeData()->imageData());
        update();
    } else {
        RobotPart::dropEvent(event);
    }
}

Um die Bildunterstützung weiterzuführen, müssen wir auch ` dropEvent()` implementieren. Wir prüfen, ob das Drag-Objekt Bilddaten enthält, und falls ja, speichern wir diese Daten als Mitglied `pixmap` und rufen ` update()` auf. Diese `pixmap` wird innerhalb der zuvor besprochenen Implementierung von ` paint()` verwendet.

RobotTorso Da RobotLimb und RobotHead ähnlich sind, gehen wir direkt zur Klasse Robot über.

class Robot : public RobotPart
{
public:
    Robot(QGraphicsItem *parent = nullptr);

    QRectF boundingRect() const override;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
};

Die Klasse „ Robot “ erbt ebenfalls von „ RobotPart “ und implementiert, wie die anderen Teile auch, die Methoden „ boundingRect()“ und „ paint()“. Sie bietet jedoch eine ganz besondere Implementierung:

QRectF Robot::boundingRect() const
{
    return QRectF();
}

void Robot::paint(QPainter *painter,
                  const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(painter);
    Q_UNUSED(option);
    Q_UNUSED(widget);
}

Da die Klasse „ Robot “ lediglich als Basis-Knoten für den Rest des Roboters dient, verfügt sie über keine visuelle Darstellung. Ihre Implementierung von „ boundingRect()“ kann daher einen null-Wert für „ QRectF “ zurückgeben, und ihre Funktion „paint()“ führt keine Aktion aus.

Robot::Robot(QGraphicsItem *parent)
    : RobotPart(parent)
{
    setFlag(ItemHasNoContents);

    QGraphicsObject *torsoItem = new RobotTorso(this);
    QGraphicsObject *headItem = new RobotHead(torsoItem);
    QGraphicsObject *upperLeftArmItem = new RobotLimb(torsoItem);
    QGraphicsObject *lowerLeftArmItem = new RobotLimb(upperLeftArmItem);
    QGraphicsObject *upperRightArmItem = new RobotLimb(torsoItem);
    QGraphicsObject *lowerRightArmItem = new RobotLimb(upperRightArmItem);
    QGraphicsObject *upperRightLegItem = new RobotLimb(torsoItem);
    QGraphicsObject *lowerRightLegItem = new RobotLimb(upperRightLegItem);
    QGraphicsObject *upperLeftLegItem = new RobotLimb(torsoItem);
    QGraphicsObject *lowerLeftLegItem = new RobotLimb(upperLeftLegItem);

Der Konstruktor setzt zunächst das Flag ` ItemHasNoContents`, was eine kleine Optimierung für Elemente darstellt, die keine visuelle Darstellung haben.

Anschließend konstruieren wir alle Roboterteile (Kopf, Rumpf sowie Ober- und Unterarme und -beine). Die Reihenfolge der Stapelung ist sehr wichtig, und wir nutzen die Eltern-Kind-Hierarchie, um sicherzustellen, dass sich die Elemente korrekt drehen und bewegen. Wir erstellen zuerst den Torso, da dieser das Stammelement ist. Anschließend erstellen wir den Kopf und übergeben den Torso an den Konstruktor von ` HeadItem`. Dadurch wird der Kopf zu einem Kind des Torsos; wenn man den Torso dreht, folgt der Kopf. Das gleiche Muster wird auf die übrigen Gliedmaßen angewendet.

    headItem->setPos(0, -18);
    upperLeftArmItem->setPos(-15, -10);
    lowerLeftArmItem->setPos(30, 0);
    upperRightArmItem->setPos(15, -10);
    lowerRightArmItem->setPos(30, 0);
    upperRightLegItem->setPos(10, 32);
    lowerRightLegItem->setPos(30, 0);
    upperLeftLegItem->setPos(-10, 32);
    lowerLeftLegItem->setPos(30, 0);

Jedes Roboterteil wird sorgfältig positioniert. So wird beispielsweise der linke Oberarm präzise in den oberen linken Bereich des Torsos verschoben, und der rechte Oberarm wird in den oberen rechten Bereich verschoben.

    QParallelAnimationGroup *animation = new QParallelAnimationGroup(this);

    QPropertyAnimation *headAnimation = new QPropertyAnimation(headItem, "rotation");
    headAnimation->setStartValue(20);
    headAnimation->setEndValue(-20);
    QPropertyAnimation *headScaleAnimation = new QPropertyAnimation(headItem, "scale");
    headScaleAnimation->setEndValue(1.1);
    animation->addAnimation(headAnimation);
    animation->addAnimation(headScaleAnimation);

Im nächsten Abschnitt werden alle Animationsobjekte erstellt. Dieser Codeausschnitt zeigt die beiden Animationen, die die Skalierung und Rotation des Kopfes steuern. Die beiden „ QPropertyAnimation “-Instanzen legen lediglich das Objekt, die Eigenschaft sowie die jeweiligen Start- und Endwerte fest.

Alle Animationen werden von einer übergeordneten parallelen Animationsgruppe gesteuert. Die Animationen für Skalierung und Rotation werden dieser Gruppe hinzugefügt.

Die übrigen Animationen werden auf ähnliche Weise definiert.

    for (int i = 0; i < animation->animationCount(); ++i) {
        QPropertyAnimation *anim = qobject_cast<QPropertyAnimation *>(animation->animationAt(i));
        anim->setEasingCurve(QEasingCurve::SineCurve);
        anim->setDuration(2000);
    }

    animation->setLoopCount(-1);
    animation->start();

Abschließend legen wir für jede Animation eine Beschleunigungskurve und eine Dauer fest, stellen sicher, dass die oberste Animationsgruppe endlos wiederholt wird, und starten die oberste Animation.

Definition der Klasse „ColorItem“

Die Klasse „ ColorItem “ stellt ein kreisförmiges Element dar, das gedrückt werden kann, um Farben auf Roboterteile zu ziehen.

class ColorItem : public QGraphicsItem
{
public:
    ColorItem();

    QRectF boundingRect() const override;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;

protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;

private:
    QColor color;
};

Diese Klasse ist sehr einfach. Sie verwendet keine Animationen und benötigt weder Eigenschaften noch Signale und Slots; um Ressourcen zu sparen, ist es daher am naheliegendsten, dass sie von ` QGraphicsItem ` (im Gegensatz zu ` QGraphicsObject`) erbt.

Sie deklariert die obligatorischen Funktionen „ boundingRect()“ und „ paint()“ und fügt Neuimplementierungen von „ mousePressEvent()“, „ mouseMoveEvent()“ und „ mouseReleaseEvent()“ hinzu. Sie enthält ein einziges privates Farb-Member.

Werfen wir einen Blick auf die Implementierung.

ColorItem::ColorItem()
    : color(QRandomGenerator::global()->bounded(256), QRandomGenerator::global()->bounded(256), QRandomGenerator::global()->bounded(256))
{
    setToolTip(QString("QColor(%1, %2, %3)\n%4")
              .arg(color.red()).arg(color.green()).arg(color.blue())
              .arg("Click and drag this color onto the robot!"));
    setCursor(Qt::OpenHandCursor);
    setAcceptedMouseButtons(Qt::LeftButton);
}

ColorItemDer Konstruktor weist dem Farb-Member mithilfe von ` QRandomGenerator` eine undurchsichtige Zufallsfarbe zu. Zur Verbesserung der Benutzerfreundlichkeit wird ein Tooltip zugewiesen, der dem Benutzer einen nützlichen Hinweis gibt, und es wird ein passender Cursor festgelegt. Dadurch wird sichergestellt, dass sich der Cursor in ` Qt::OpenHandCursor ` ändert, wenn der Mauszeiger über das Element bewegt wird.

Schließlich rufen wir ` setAcceptedMouseButtons()` auf, um sicherzustellen, dass dieses Element nur ` Qt::LeftButton` verarbeiten kann. Dies vereinfacht die Mausereignis-Handler erheblich, da wir stets davon ausgehen können, dass nur die linke Maustaste gedrückt und losgelassen wird.

QRectF ColorItem::boundingRect() const
{
    return QRectF(-15.5, -15.5, 34, 34);
}

Der Begrenzungsrahmen des Elements ist ein festes Rechteck von 30 × 30 Einheiten, das um den Ursprung des Elements (0, 0) zentriert ist und in alle Richtungen um 0,5 Einheiten angepasst wird, damit ein skalierbarer Stift seinen Umriss zeichnen kann. Für den letzten optischen Schliff werden die Begrenzungen außerdem um einige Einheiten nach unten und nach rechts verschoben, um Platz für einen einfachen Schlagschatten zu schaffen.

void ColorItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(option);
    Q_UNUSED(widget);
    painter->setPen(Qt::NoPen);
    painter->setBrush(Qt::darkGray);
    painter->drawEllipse(-12, -12, 30, 30);
    painter->setPen(QPen(Qt::black, 1));
    painter->setBrush(QBrush(color));
    painter->drawEllipse(-15, -15, 30, 30);
}

Die Implementierung von ` paint()` zeichnet eine Ellipse mit einem 1-Einheiten-dicken schwarzen Umriss, einer einfarbigen Füllung und einem dunkelgrauen Schlagschatten.

void ColorItem::mousePressEvent(QGraphicsSceneMouseEvent *)
{
    setCursor(Qt::ClosedHandCursor);
}

Der Handler ` mousePressEvent()` wird aufgerufen, wenn Sie die Maustaste innerhalb des Bereichs des Elements drücken. Unsere Implementierung setzt den Cursor einfach auf ` Qt::ClosedHandCursor`.

void ColorItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *)
{
    setCursor(Qt::OpenHandCursor);
}

Der Handler „ mouseReleaseEvent()“ wird aufgerufen, wenn Sie die Maustaste loslassen, nachdem Sie sie innerhalb des Bereichs eines Elements gedrückt haben. Unsere Implementierung setzt den Cursor wieder auf „ Qt::OpenHandCursor “ zurück. Die Ereignisbehandler für das Drücken und Loslassen der Maustaste bieten dem Benutzer zusammen ein nützliches visuelles Feedback: Wenn Sie den Mauszeiger über ein „ CircleItem “ bewegen, wechselt der Cursor zu einer offenen Hand. Beim Drücken des Elements wird ein Cursor in Form einer geschlossenen Hand angezeigt. Beim Loslassen wird wieder der Cursor in Form einer offenen Hand angezeigt.

void ColorItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    if (QLineF(event->screenPos(), event->buttonDownScreenPos(Qt::LeftButton))
        .length() < QApplication::startDragDistance()) {
        return;
    }

    QDrag *drag = new QDrag(event->widget());
    QMimeData *mime = new QMimeData;
    drag->setMimeData(mime);

Der Handler „ mouseMoveEvent()“ wird aufgerufen, wenn Sie die Maus bewegen, nachdem Sie die Maustaste innerhalb des Bereichs von „ ColorItem “ gedrückt haben. Diese Implementierung liefert den wichtigsten Teil der Logik für „ CircleItem “: den Code, der das Ziehen startet und verwaltet.

Die Implementierung prüft zunächst, ob die Maus weit genug gezogen wurde, um Maus-Jitter-Störungen auszuschließen. Wir möchten einen Ziehvorgang nur dann starten, wenn die Maus weiter gezogen wurde als die von der Anwendung festgelegte Startdistanz für das Ziehen.

Anschließend erstellen wir ein ` QDrag `-Objekt und übergeben das Ereignis ` widget ` (d. h. den Ansichtsbereich von ` QGraphicsView `) an dessen Konstruktor. Qt sorgt dafür, dass dieses Objekt zum richtigen Zeitpunkt gelöscht wird. Außerdem erstellen wir eine ` QMimeData `-Instanz, die unsere Farb- oder Bilddaten enthalten kann, und weisen diese dem Drag-Objekt zu.

    static int n = 0;
    if (n++ > 2 && QRandomGenerator::global()->bounded(3) == 0) {
        QImage image(":/images/head.png");
        mime->setImageData(image);

        drag->setPixmap(QPixmap::fromImage(image).scaled(30, 40));
        drag->setHotSpot(QPoint(15, 30));

Dieser Codeausschnitt führt zu einem etwas zufälligen Ergebnis: Hin und wieder wird den MIME-Daten des Drag-Objekts ein spezielles Bild zugewiesen. Die Pixmap wird zudem als Pixmap des Drag-Objekts zugewiesen. Dadurch wird sichergestellt, dass Sie das Bild, das gezogen wird, als Pixmap unter dem Mauszeiger sehen können.

    } else {
        mime->setColorData(color);
        mime->setText(QString("#%1%2%3")
                      .arg(color.red(), 2, 16, QLatin1Char('0'))
                      .arg(color.green(), 2, 16, QLatin1Char('0'))
                      .arg(color.blue(), 2, 16, QLatin1Char('0')));

        QPixmap pixmap(34, 34);
        pixmap.fill(Qt::white);

        QPainter painter(&pixmap);
        painter.translate(15, 15);
        painter.setRenderHint(QPainter::Antialiasing);
        paint(&painter, nullptr, nullptr);
        painter.end();

        pixmap.setMask(pixmap.createHeuristicMask());

        drag->setPixmap(pixmap);
        drag->setHotSpot(QPoint(15, 20));
    }

Andernfalls – und das ist das häufigste Ergebnis – wird den MIME-Daten des Drag-Objekts eine einfache Farbe zugewiesen. Wir rendern diese ` ColorItem ` in eine neue Pixmap, um dem Benutzer visuelles Feedback zu geben, dass die Farbe „gezogen“ wird.

    drag->exec();
    setCursor(Qt::OpenHandCursor);
}

Schließlich führen wir den Drag-Vorgang aus. ` QDrag::exec()` kehrt in die Ereignisschleife zurück und verlässt diese erst, wenn der Drag entweder abgelegt oder abgebrochen wurde. In jedem Fall setzen wir den Cursor auf ` Qt::OpenHandCursor` zurück.

Die Funktion `main()`

Nachdem die Klassen ` Robot ` und ` ColorItem ` nun fertiggestellt sind, können wir alle Teile in der Funktion `main()` zusammenfügen.

int main(int argc, char **argv)
{
    QApplication app(argc, argv);

Zunächst erstellen wir eine Instanz von ` QApplication` und initialisieren den Zufallszahlengenerator. Dadurch wird sichergestellt, dass die Farbobjekte bei jedem Start der Anwendung unterschiedliche Farben haben.

    QGraphicsScene scene(-200, -200, 400, 400);

    for (int i = 0; i < 10; ++i) {
        ColorItem *item = new ColorItem;
        item->setPos(::sin((i * 6.28) / 10.0) * 150,
                     ::cos((i * 6.28) / 10.0) * 150);

        scene.addItem(item);
    }

    Robot *robot = new Robot;
    robot->setTransform(QTransform::fromScale(1.2, 1.2), true);
    robot->setPos(0, -20);
    scene.addItem(robot);

Wir erstellen eine Szene mit fester Größe und erzeugen 10 „ ColorItem “-Instanzen, die kreisförmig angeordnet sind. Jedes Element wird der Szene hinzugefügt.

In der Mitte dieses Kreises erstellen wir eine „ Robot “-Instanz. Der Roboter wird skaliert und um einige Einheiten nach oben verschoben. Anschließend wird er der Szene hinzugefügt.

    GraphicsView view(&scene);
    view.setRenderHint(QPainter::Antialiasing);
    view.setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
    view.setBackgroundBrush(QColor(230, 200, 167));
    view.setWindowTitle("Drag and Drop Robot");
    view.show();

    return app.exec();
}

Abschließend erstellen wir ein „ QGraphicsView “-Fenster und weisen ihm die Szene zu.

Um die visuelle Qualität zu verbessern, aktivieren wir Antialiasing. Außerdem entscheiden wir uns für die Verwendung von Bounding-Rectangle-Updates, um die Handhabung der visuellen Aktualisierungen zu vereinfachen. Die Ansicht erhält einen festen, sandfarbenen Hintergrund und einen Fenstertitel.

Anschließend zeigen wir die Ansicht an. Die Animationen starten sofort, nachdem die Steuerung in die Ereignisschleife eintritt.

Beispielprojekt @ 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.