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

Demonstriert das Ziehen und Ablegen von Elementen in einer Grafikansicht.

Das Drag and Drop Robot Beispiel zeigt, wie man Drag and Drop in einer QGraphicsItem Unterklasse implementiert und wie man Elemente mit dem Qt Animation Framework animiert.

Graphics View bietet die Klasse QGraphicsScene für die Verwaltung und Interaktion mit einer großen Anzahl von benutzerdefinierten 2D-Grafikelementen, die von der Klasse QGraphicsItem abgeleitet sind, und ein Widget QGraphicsView für die Visualisierung der Elemente mit Unterstützung für Zoomen und Drehen.

Dieses Beispiel besteht aus einer Robot Klasse, einer ColorItem Klasse und einer Hauptfunktion: die Robot Klasse beschreibt einen einfachen Roboter, der aus mehreren RobotPart abgeleiteten Gliedern besteht, einschließlich RobotHead und RobotLimb, die ColorItem Klasse bietet eine verschiebbare farbige Ellipse und die main() Funktion stellt das Hauptanwendungsfenster bereit.

Wir werden zunächst die Klasse Robot betrachten, um zu sehen, wie die verschiedenen Teile zusammengesetzt werden, so dass sie einzeln gedreht und mit QPropertyAnimation animiert werden können. Anschließend werden wir die Klasse ColorItem betrachten, um zu demonstrieren, wie Drag and Drop zwischen den Elementen implementiert wird. Zum Schluss werden wir uns die main()-Funktion ansehen, um zu sehen, wie wir alle Teile zusammensetzen können, um die endgültige Anwendung zu erstellen.

Definition der Roboterklasse

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

Beginnen wir mit der Deklaration der Klasse 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 QGraphicsObject. QGraphicsObject stellt Signale und Slots durch die Vererbung von QObject zur Verfügung und deklariert außerdem die Eigenschaften von QGraphicsItem über Q_PROPERTY, wodurch die Eigenschaften für QPropertyAnimation zugänglich werden.

RobotPart implementiert auch die drei wichtigsten Ereignishandler für die Annahme von Drop-Ereignissen: dragEnterEvent(), dragLeaveEvent(), und dropEvent().

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

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

RobotPartDer Konstruktor dieser Klasse initialisiert das dragOver-Mitglied und setzt die Farbe auf Qt::lightGray. Im Konstruktorkörper aktivieren wir die Unterstützung für die Annahme von Drop-Ereignissen durch den Aufruf von setAcceptDrops(true).

Der Rest der Implementierung dieser Klasse dient der Unterstützung von Drag and 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 Roboterteils gezogen wird.

Die Implementierung des Handlers bestimmt, ob dieses Element als Ganzes die Mime-Daten akzeptieren kann, die mit dem eingehenden Drag-Objekt assoziiert sind. RobotPart bietet ein Basisverhalten für alle Teile, das Farbdrops akzeptiert. Wenn also das eingehende Drag-Objekt 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, was wiederum die Weitergabe des Ereignisses an übergeordnete Elemente ermöglicht.

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 Roboterteils herausgezogen wird. Unsere Implementierung setzt dragOver einfach auf false zurück und ruft update() auf, um eine visuelle Rückmeldung darüber zu geben, dass das Ziehen 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 einem Element abgelegt wird (d. h., wenn die Maustaste beim Ziehen ü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 RobotPart und stellt die notwendigen Implementierungen von boundingRect() und paint() bereit. Außerdem implementiert sie dragEnterEvent() und dropEvent() neu, um eine spezielle Behandlung von Bildabwürfen zu ermöglichen.

Die Klasse enthält ein privates pixmap-Mitglied, das wir verwenden können, um die Unterstützung für die Annahme von Bildabwürfen zu implementieren.

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

RobotHead hat einen recht einfachen Konstruktor, der einfach an den Konstruktor von RobotPart weiterleitet.

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

Die Neuimplementierung von boundingRect() gibt die Ausmaße für den Kopf zurück. Da das Rotationszentrum der untere Mittelpunkt des Objekts sein soll, haben wir ein Begrenzungsrechteck gewählt, das bei (-15, -50) beginnt und sich auf 30 Einheiten Breite und 50 Einheiten Höhe erstreckt. Beim Drehen des Kopfes bleibt der "Hals" ruhig, während der obere Teil des Kopfes von einer Seite zur anderen kippt.

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 gelegt wurde, zeichnen wir das Bild, ansonsten zeichnen wir einen runden, rechteckigen Roboterkopf mit einfachen Vektorgrafiken.

Je nach Komplexität des gemalten Bildes kann es aus Leistungsgründen oft schneller sein, den Kopf als Bild zu zeichnen, als eine Folge 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 Bildtropfen akzeptieren. Um dies zu unterstützen, prüft die Neuimplementierung von dragEnterEvent(), ob das Drag-Objekt Bilddaten enthält, und wenn ja, dann 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 weiter zu verfolgen, müssen wir auch dropEvent() implementieren. Wir prüfen, ob das Drag-Objekt Bilddaten enthält, und wenn ja, speichern wir diese Daten als Pixmap und rufen update() auf. Diese pixmap wird in der Implementierung paint() verwendet, die wir zuvor besprochen haben.

RobotTorso und RobotLimb sind ähnlich wie RobotHead, also 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 wie die anderen Teile implementiert sie auch 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 nur als Basisknoten für den Rest des Roboters verwendet wird, hat sie keine visuelle Darstellung. Ihre boundingRect()-Implementierung kann daher eine Null QRectF zurückgeben, und ihre paint()-Funktion tut nichts.

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 beginnt mit dem Setzen des Flags ItemHasNoContents, was eine geringfügige Optimierung für Elemente darstellt, die keine visuelle Darstellung haben.

Anschließend werden alle Roboterteile (Kopf, Rumpf, Ober- und Unterarme sowie Beine) konstruiert. Die Stapelreihenfolge ist sehr wichtig, und wir verwenden die Eltern-Kind-Hierarchie, um sicherzustellen, dass sich die Elemente richtig drehen und bewegen. Wir konstruieren zuerst den Torso, da er das Wurzelelement ist. Anschließend konstruieren wir den Kopf und übergeben den Torso an den Konstruktor von HeadItem. Dadurch wird der Kopf zu einem Kind des Torsos; wenn Sie den Torso drehen, wird der Kopf folgen. Dasselbe Muster wird auf den Rest der Gliedmaßen angewandt.

    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 genau in den linken oberen Bereich des Torsos und der rechte Oberarm in den rechten oberen 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 Ausschnitt zeigt die beiden Animationen, die auf die Skalierung und Drehung des Kopfes wirken. Die beiden Instanzen von QPropertyAnimation legen einfach das Objekt, die Eigenschaft und die jeweiligen Start- und Endwerte fest.

Alle Animationen werden von einer parallelen Animationsgruppe auf oberster Ebene gesteuert. Die Skalierungs- und Rotationsanimationen werden zu dieser Gruppe hinzugefügt.

Der Rest der Animationen wird 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();

Schließlich legen wir für jede Animation eine Lockerungskurve und eine Dauer fest, stellen sicher, dass die Toplevel-Animationsgruppe eine Endlosschleife durchläuft, und starten die Toplevel-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 hat keinen Bedarf an Eigenschaften, Signalen und Slots. Um Ressourcen zu sparen, ist es daher am naheliegendsten, dass sie von QGraphicsItem (im Gegensatz zu QGraphicsObject) erbt.

Es deklariert die obligatorischen Funktionen boundingRect() und paint() und fügt Neuimplementierungen von mousePressEvent(), mouseMoveEvent() und mouseReleaseEvent() hinzu. Sie enthält ein einziges privates Farbmitglied.

Werfen wir einen Blick auf seine 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 des Programms weist seinem Farbmitglied eine undurchsichtige Zufallsfarbe zu, indem er QRandomGenerator verwendet. Um die Benutzerfreundlichkeit zu verbessern, weist er einen Tooltip zu, der dem Benutzer einen nützlichen Hinweis gibt, und er setzt auch einen geeigneten Cursor. Dadurch wird sichergestellt, dass der Cursor zufällig auf Qt::OpenHandCursor erscheint, wenn der Mauszeiger über dem Element verweilt.

Schließlich rufen wir setAcceptedMouseButtons() auf, um sicherzustellen, dass dieses Element nur Qt::LeftButton verarbeiten kann. Dies vereinfacht die Mausereignishandler erheblich, da wir immer 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);
}

Das Begrenzungsrechteck des Elements ist ein festes 30x30 Einheiten großes Rechteck, das um den Ursprung des Elements (0, 0) zentriert ist und um 0,5 Einheiten in alle Richtungen angepasst wird, damit ein skalierbarer Stift den Umriss des Elements zeichnen kann. Für einen abschließenden visuellen Touch werden die Grenzen auch um einige Einheiten nach unten und nach rechts korrigiert, 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 paint() zeichnet eine Ellipse mit einem schwarzen Umriss von 1 Einheit, 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 zurück auf Qt::OpenHandCursor. Die Handler für das Drücken und Loslassen der Maustaste geben dem Benutzer nützliches visuelles Feedback: Wenn Sie den Mauszeiger über ein CircleItem bewegen, ändert sich der Cursor in eine offene Hand. Wenn Sie das Element drücken, wird ein geschlossener Handcursor angezeigt. Wenn Sie den Mauszeiger loslassen, wird wieder eine offene 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 nach dem Drücken der Maustaste innerhalb des Bereichs von ColorItem herumbewegen. Diese Implementierung stellt den wichtigsten Teil der Logik für CircleItem bereit: den Code, der Ziehvorgänge startet und verwaltet.

Die Implementierung beginnt mit der Überprüfung, ob die Maus weit genug gezogen wurde, um das Ruckeln der Maus zu eliminieren. Wir wollen nur dann einen Ziehvorgang starten, wenn die Maus weiter gezogen wurde als die Start-Ziehdistanz der Anwendung.

Anschließend erstellen wir ein QDrag Objekt und übergeben das Ereignis widget (d.h. das QGraphicsView Ansichtsfenster) an seinen Konstruktor. Qt wird dafür sorgen, dass dieses Objekt zum richtigen Zeitpunkt gelöscht wird. Wir erstellen auch 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));

Dieses Snippet hat ein etwas zufälliges Ergebnis: Ab und zu wird ein spezielles Bild den Mime-Daten des Drag-Objekts zugewiesen. Die Pixmap wird auch als Pixmap des Ziehobjekts 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 Mimikdaten des Ziehobjekts eine einfache Farbe zugewiesen. Wir rendern diese ColorItem in eine neue Pixmap, um dem Benutzer eine visuelle Rückmeldung zu geben, dass die Farbe "gezogen" wird.

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

Schließlich führen wir das Ziehen aus. QDrag::exec() kehrt in die Ereignisschleife zurück und verlässt sie nur, wenn das Ziehen entweder abgesetzt oder abgebrochen wurde. In jedem Fall setzen wir den Cursor auf Qt::OpenHandCursor zurück.

Die Funktion main()

Nun, da die Klassen Robot und ColorItem vollständig sind, können wir alle Teile in der main()-Funktion zusammenfügen.

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

Wir beginnen mit der Konstruktion von QApplication und der Initialisierung des Zufallszahlengenerators. Dadurch wird sichergestellt, dass die Farbelemente bei jedem Start der Anwendung eine andere Farbe 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 konstruieren eine Szene fester Größe und erstellen 10 ColorItem Instanzen, die in einem Kreis 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 ein paar Einheiten nach oben verschoben. Dann 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();
}

Schließlich erstellen wir ein QGraphicsView Fenster und weisen ihm die Szene zu.

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

Dann wird die Ansicht angezeigt. Die Animationen beginnen unmittelbar nach dem Eintritt der Steuerung in die Ereignisschleife.

Beispielprojekt @ code.qt.io

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