Auf dieser Seite

Beispiel für 2D-Zeichnung

Das Beispiel „2D-Zeichnung“ zeigt, wie „ QPainter “ und „ QOpenGLWidget “ gemeinsam verwendet werden können, um beschleunigte 2D-Grafiken auf unterstützter Hardware darzustellen.

Screenshot, der einen Vergleich eines Bildes zwischen Native und OpenGL zeigt

Die Klasse „ QPainter “ wird verwendet, um 2D-Grafikprimitive auf Zeichen-Geräte zu zeichnen, die von Unterklassen von „ QPaintDevice “ bereitgestellt werden, wie beispielsweise „ QWidget “ und „ QImage “.

Da „ QOpenGLWidget “ eine Unterklasse von „ QWidget “ ist, ist es möglich, die Methode „ paintEvent()“ neu zu implementieren und „ QPainter “ zum Zeichnen auf dem Gerät zu verwenden, genau wie bei einem „ QWidget “. Der einzige Unterschied besteht darin, dass die Zeichenvorgänge hardwaremäßig beschleunigt werden, sofern dies von den OpenGL-Treibern Ihres Systems unterstützt wird.

In diesem Beispiel führen wir dieselben Zeichenvorgänge auf einem „ QWidget “ und einem „ QOpenGLWidget “ durch. Die „ QWidget “ wird mit aktiviertem Anti-Aliasing dargestellt, und die „ QOpenGLWidget “ verwendet ebenfalls Anti-Aliasing, sofern die erforderlichen Erweiterungen vom OpenGL-Treiber Ihres Systems unterstützt werden.

Übersicht

Um die Ergebnisse des Zeichnens auf einer Unterklasse von ` QOpenGLWidget ` mit dem nativen Zeichnen in einer Unterklasse von ` QWidget ` vergleichen zu können, möchten wir beide Arten von Widgets nebeneinander anzeigen. Dazu leiten wir Unterklassen von ` QWidget ` und ` QOpenGLWidget` ab, verwenden eine separate Klasse ` Helper `, um für jede davon die gleichen Zeichenoperationen durchzuführen, und ordnen sie in einem Widget der obersten Ebene an, das selbst von der Klasse ` Window ` bereitgestellt wird.

Definition der Hilfsklasse

In diesem Beispiel werden die Zeichenvorgänge von einer Hilfsklasse ausgeführt. Wir verfahren so, weil wir möchten, dass sowohl für unsere Unterklasse „ QWidget “ als auch für die Unterklasse „ QOpenGLWidget “ dieselben Zeichenvorgänge ausgeführt werden.

Die Klasse „ Helper “ ist sehr einfach aufgebaut:

class Helper
{
public:
    Helper();

public:
    void paint(QPainter *painter, QPaintEvent *event, int elapsed);

private:
    QBrush background;
    QBrush circleBrush;
    QFont textFont;
    QPen circlePen;
    QPen textPen;
};

Abgesehen vom Konstruktor stellt sie lediglich eine Funktion „ paint() “ bereit, um mithilfe eines von einer unserer Widget-Unterklassen bereitgestellten „Painter“ zu zeichnen.

Implementierung der Hilfsklasse

Der Konstruktor der Klasse richtet die Ressourcen ein, die zum Zeichnen von Inhalten auf ein Widget benötigt werden:

Helper::Helper()
{
    QLinearGradient gradient(QPointF(50, -20), QPointF(80, 20));
    gradient.setColorAt(0.0, Qt::white);
    gradient.setColorAt(1.0, QColor(0xa6, 0xce, 0x39));

    background = QBrush(QColor(64, 32, 64));
    circleBrush = QBrush(gradient);
    circlePen = QPen(Qt::black);
    circlePen.setWidth(1);
    textPen = QPen(Qt::white);
    textFont.setPixelSize(50);
}

Das eigentliche Zeichnen erfolgt in der Funktion ` paint() `. Diese nimmt einen ` QPainter ` entgegen, der bereits für das Zeichnen auf ein Zeichen-Gerät (entweder ein ` QWidget ` oder ein ` QOpenGLWidget`) eingerichtet wurde, einen ` QPaintEvent `, der Informationen über den zu zeichnenden Bereich liefert, sowie eine Messung der verstrichenen Zeit (in Millisekunden) seit der letzten Aktualisierung des Zeichen-Geräts.

void Helper::paint(QPainter *painter, QPaintEvent *event, int elapsed)
{
    painter->fillRect(event->rect(), background);
    painter->translate(100, 100);

Wir beginnen mit dem Zeichnen, indem wir den im Zeichnungsereignis enthaltenen Bereich ausfüllen, bevor wir den Ursprung des Koordinatensystems verschieben, sodass die restlichen Zeichenoperationen in Richtung der Mitte des Zeichenobjekts versetzt werden.

Wir zeichnen ein spiralförmiges Muster aus Kreisen und nutzen dabei die angegebene verstrichene Zeit, um sie so zu animieren, dass sie sich scheinbar nach außen und um den Ursprung des Koordinatensystems herum bewegen:

    painter->save();
    painter->setBrush(circleBrush);
    painter->setPen(circlePen);
    painter->rotate(elapsed * 0.030);

    qreal r = elapsed / 1000.0;
    int n = 30;
    for (int i = 0; i < n; ++i) {
        painter->rotate(30);
        qreal factor = (i + r) / n;
        qreal radius = 0 + 120.0 * factor;
        qreal circleRadius = 1 + factor * 20;
        painter->drawEllipse(QRectF(radius, -circleRadius,
                                    circleRadius * 2, circleRadius * 2));
    }
    painter->restore();

Da das Koordinatensystem während dieses Vorgangs mehrfach gedreht wird, speichern wir den Zustand des „ QPainter “ vorab mit ` save()` und stellen ihn anschließend mit ` restore()` wieder her.

    painter->setPen(textPen);
    painter->setFont(textFont);
    painter->drawText(QRect(-50, -50, 100, 100), Qt::AlignCenter, QStringLiteral("Qt"));
}

Um den Effekt zu vervollständigen, zeichnen wir am Ursprung etwas Text.

Definition der Widget-Klasse

Die Klasse „ Widget “ stellt ein einfaches benutzerdefiniertes Widget bereit, mit dem wir die von der Klasse „ Helper “ gezeichnete einfache Animation anzeigen.

class Helper;

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(Helper *helper, QWidget *parent);

public slots:
    void animate();

protected:
    void paintEvent(QPaintEvent *event) override;

private:
    Helper *helper;
    int elapsed;
};

Abgesehen vom Konstruktor enthält sie lediglich eine Funktion ` paintEvent()`, mit der wir benutzerdefinierte Inhalte zeichnen können, sowie einen Slot, der zur Animation des Inhalts dient. Eine Member-Variable verwaltet die ` Helper `-Instanz, die das Widget zum Zeichnen seines Inhalts verwendet, und die andere speichert die seit der letzten Aktualisierung verstrichene Zeit.

Implementierung der Widget-Klasse

Der Konstruktor initialisiert lediglich die Membervariablen, speichert das übergebene ` Helper `-Objekt, ruft den Konstruktor der Basisklasse auf und legt eine feste Größe für das Widget fest:

Widget::Widget(Helper *helper, QWidget *parent)
    : QWidget(parent), helper(helper)
{
    elapsed = 0;
    setFixedSize(200, 200);
}

Der Slot ` animate() ` wird immer dann aufgerufen, wenn ein Timer – den wir später definieren – abläuft:

void Widget::animate()
{
    elapsed = (elapsed + qobject_cast<QTimer*>(sender())->interval()) % 1000;
    update();
}

Hier ermitteln wir das Intervall, das seit dem letzten Ablauf des Timers verstrichen ist, und addieren es zu einem eventuell vorhandenen Wert, bevor wir das Widget neu zeichnen. Da die in der Klasse „ Helper “ verwendete Animation jede Sekunde wiederholt wird, können wir den Modulo-Operator verwenden, um sicherzustellen, dass die Variable „ elapsed “ immer kleiner als 1000 ist.

Da die Klasse „ Helper “ das eigentliche Zeichnen übernimmt, müssen wir lediglich ein „paint“-Ereignis implementieren, das ein „ QPainter “ für das Widget einrichtet und die Funktion „ paint() “ des Helpers aufruft:

void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter;
    painter.begin(this);
    painter.setRenderHint(QPainter::Antialiasing);
    helper->paint(&painter, event, elapsed);
    painter.end();
}

Definition der Klasse `GLWidget`

Die Klassendefinition von ` GLWidget ` entspricht im Wesentlichen der von ` Widget `, mit dem Unterschied, dass sie von ` QOpenGLWidget` abgeleitet ist.

class Helper;

class GLWidget : public QOpenGLWidget
{
    Q_OBJECT

public:
    GLWidget(Helper *helper, QWidget *parent);

public slots:
    void animate();

protected:
    void paintEvent(QPaintEvent *event) override;

private:
    Helper *helper;
    int elapsed;
};

Auch hier speichern die Membervariablen das ` Helper `, das zum Zeichnen des Widgets verwendet wird, sowie die seit der letzten Aktualisierung verstrichene Zeit.

Implementierung der Klasse „GLWidget“

Der Konstruktor unterscheidet sich geringfügig vom Konstruktor der Klasse „ Widget “:

GLWidget::GLWidget(Helper *helper, QWidget *parent)
    : QOpenGLWidget(parent), helper(helper)
{
    elapsed = 0;
    setFixedSize(200, 200);
    setAutoFillBackground(false);
}

Die Membervariable „ elapsed “ wird initialisiert und das „ Helper “-Objekt, das zum Zeichnen des Widgets verwendet wird, wird gespeichert.

Der Slot „ animate() “ entspricht genau dem der Klasse „ Widget “:

void GLWidget::animate()
{
    elapsed = (elapsed + qobject_cast<QTimer*>(sender())->interval()) % 1000;
    update();
}

Der „ paintEvent() “-Slot entspricht fast vollständig dem in der Klasse „ Widget “ zu findenden:

void GLWidget::paintEvent(QPaintEvent *event)
{
    QPainter painter;
    painter.begin(this);
    painter.setRenderHint(QPainter::Antialiasing);
    helper->paint(&painter, event, elapsed);
    painter.end();
}

Da Anti-Aliasing aktiviert wird, sofern verfügbar, müssen wir lediglich ein „ QPainter “ für das Widget einrichten und die Funktion „ paint() “ des Helpers aufrufen, um den Inhalt des Widgets anzuzeigen.

Definition der Window-Klasse

Die Klasse „ Window “ hat eine einfache, minimale Definition:

class Window : public QWidget
{
    Q_OBJECT

public:
    Window();

private:
    Helper helper;
};

Sie enthält ein einziges „ Helper “-Objekt, das von allen Widgets gemeinsam genutzt wird.

Implementierung der „Window“-Klasse

Der Konstruktor übernimmt die gesamte Arbeit: Er erstellt ein Widget jedes Typs und fügt diese zusammen mit Beschriftungen in ein Layout ein:

Window::Window()
{
    setWindowTitle(tr("2D Painting on Native and OpenGL Widgets"));

    Widget *native = new Widget(&helper, this);
    GLWidget *openGL = new GLWidget(&helper, this);
    QLabel *nativeLabel = new QLabel(tr("Native"));
    nativeLabel->setAlignment(Qt::AlignHCenter);
    QLabel *openGLLabel = new QLabel(tr("OpenGL"));
    openGLLabel->setAlignment(Qt::AlignHCenter);

    QGridLayout *layout = new QGridLayout;
    layout->addWidget(native, 0, 0);
    layout->addWidget(openGL, 0, 1);
    layout->addWidget(nativeLabel, 1, 0);
    layout->addWidget(openGLLabel, 1, 1);
    setLayout(layout);

    QTimer *timer = new QTimer(this);
    connect(timer, &QTimer::timeout, native, &Widget::animate);
    connect(timer, &QTimer::timeout, openGL, &GLWidget::animate);
    timer->start(50);
}

Für Animationszwecke wird ein Timer mit einer Zeitüberschreitung von 50 Millisekunden erstellt und mit den „ animate() “-Slots der Objekte „ Widget “ und „ GLWidget “ verbunden. Nach dem Start sollten die Widgets mit etwa 20 Bildern pro Sekunde aktualisiert werden.

Ausführen des Beispiels

Das Beispiel zeigt dieselben Zeichenvorgänge, die gleichzeitig in einem „ Widget “ und einem „ GLWidget “ ausgeführt werden. Die Qualität und Geschwindigkeit der Darstellung im „ GLWidget “ hängt davon ab, inwieweit der OpenGL-Treiber Ihres Systems Multisampling und Hardwarebeschleunigung unterstützt. Fehlt die Unterstützung für eine dieser Funktionen, greift der Treiber möglicherweise auf einen Software-Renderer zurück, der zugunsten der Geschwindigkeit Abstriche bei der Qualität macht.

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.