En esta página

Ejemplo de pintura 2D

El ejemplo de pintura 2D muestra cómo QPainter y QOpenGLWidget pueden utilizarse conjuntamente para mostrar gráficos 2D acelerados en hardware compatible.

Captura de pantalla que muestra una comparación de una imagen entre Native y OpenGL

La clase QPainter se utiliza para dibujar primitivas gráficas 2D en dispositivos de pintura proporcionados por subclases de QPaintDevice, como QWidget y QImage.

Dado que QOpenGLWidget es una subclase de QWidget, es posible reimplementar su paintEvent() y utilizar QPainter para dibujar en el dispositivo, tal y como se haría con un QWidget. La única diferencia es que las operaciones de pintado se acelerarán en el hardware si está soportado por los drivers OpenGL de tu sistema.

En este ejemplo, realizamos las mismas operaciones de pintura en un QWidget y un QOpenGLWidget. El QWidget se muestra con anti-aliasing activado, y el QOpenGLWidget también utilizará anti-aliasing si las extensiones requeridas son soportadas por el controlador OpenGL de su sistema.

Visión general

Para poder comparar los resultados de pintar en una subclase QOpenGLWidget con el dibujo nativo en una subclase QWidget, queremos mostrar ambos tipos de widget uno al lado del otro. Para ello, derivamos subclases de QWidget y QOpenGLWidget, utilizando una clase Helper separada para realizar las mismas operaciones de pintura para cada una, y las presentamos en un widget de nivel superior, proporcionado por la clase Window.

Definición de la clase auxiliar

En este ejemplo, las operaciones de pintado son realizadas por una clase ayudante. Hacemos esto porque queremos que se realicen las mismas operaciones de pintado tanto para nuestra subclase QWidget como para la subclase QOpenGLWidget.

La clase Helper es mínima:

class Helper
{
public:
    Helper();

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

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

Aparte del constructor, sólo proporciona una función paint() para pintar utilizando un pintor suministrado por una de nuestras subclases de widgets.

Implementación de la clase Helper

El constructor de la clase configura los recursos que necesita para pintar contenido en un widget:

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

El pintado real se realiza en la función paint(). Esta toma un QPainter que ya ha sido configurado para pintar en un dispositivo de pintura (ya sea un QWidget o un QOpenGLWidget), un QPaintEvent que proporciona información sobre la región a pintar, y una medida del tiempo transcurrido (en milisegundos) desde que el dispositivo de pintura fue actualizado por última vez.

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

Comenzamos a pintar rellenando la región contenida en el evento de pintura antes de trasladar el origen del sistema de coordenadas para que el resto de las operaciones de pintura se desplacen hacia el centro del dispositivo de pintura.

Dibujamos un patrón en espiral de círculos, utilizando el tiempo transcurrido especificado para animarlos de forma que parezca que se mueven hacia fuera y alrededor del origen del sistema de coordenadas:

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

Dado que el sistema de coordenadas se rota muchas veces durante este proceso, save() el estado de QPainter's de antemano y restore() después.

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

Dibujamos algo de texto en el origen para completar el efecto.

Definición de la clase Widget

La clase Widget proporciona un widget personalizado básico que utilizamos para mostrar la animación simple pintada por la clase Helper.

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

Aparte del constructor, sólo contiene una función paintEvent(), que nos permite dibujar contenido personalizado, y una ranura que se utiliza para animar su contenido. Una variable miembro mantiene un registro del Helper que el widget utiliza para pintar su contenido, y la otra registra el tiempo transcurrido desde la última actualización.

Implementación de la clase Widget

El constructor sólo inicializa las variables miembro, almacenando el objeto Helper suministrado y llamando al constructor de la clase base, e impone un tamaño fijo para el widget:

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

La ranura animate() es llamada cada vez que un temporizador, que definiremos más adelante, se agota:

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

Aquí, determinamos el intervalo que ha transcurrido desde que el temporizador expiró por última vez, y lo añadimos a cualquier valor existente antes de repintar el widget. Como la animación utilizada en la clase Helper se repite cada segundo, podemos utilizar el operador módulo para asegurarnos de que la variable elapsed es siempre menor que 1000.

Como la clase Helper hace todo el pintado, sólo tenemos que implementar un evento paint que configure un QPainter para el widget y llame a la función paint() del helper:

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

Definición de la clase GLWidget

La definición de la clase GLWidget es básicamente la misma que la de la clase Widget excepto que deriva de QOpenGLWidget.

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

De nuevo, las variables miembro registran el Helper utilizado para pintar el widget y el tiempo transcurrido desde la última actualización.

Implementación de la clase GLWidget

El constructor difiere un poco del constructor de la clase Widget:

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

Se inicializa la variable miembro elapsed y se almacena el objeto Helper utilizado para pintar el widget.

El slot animate() es exactamente el mismo que el proporcionado por la clase Widget:

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

El paintEvent() es casi el mismo que el de la clase Widget:

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

Dado que el antialiasing se activará si está disponible, sólo necesitamos configurar un QPainter en el widget y llamar a la función paint() del helper para mostrar el contenido del widget.

Definición de la clase Window

La clase Window tiene una definición básica y mínima:

class Window : public QWidget
{
    Q_OBJECT

public:
    Window();

private:
    Helper helper;
};

Contiene un único objeto Helper que será compartido por todos los widgets.

Implementación de la clase Window

El constructor hace todo el trabajo, creando un widget de cada tipo e insertándolos con etiquetas en un diseño:

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

Se construye un temporizador con un tiempo de espera de 50 milisegundos para propósitos de animación, y se conecta a las ranuras animate() de los objetos Widget y GLWidget. Una vez iniciado, los widgets deberían actualizarse a unos 20 fotogramas por segundo.

Ejecución del ejemplo

El ejemplo muestra las mismas operaciones de pintura realizadas al mismo tiempo en un Widget y un GLWidget. La calidad y velocidad de renderizado en el GLWidget depende del nivel de soporte para multimuestreo y aceleración por hardware que proporcione el controlador OpenGL de su sistema. Si no hay soporte para alguno de estos, el controlador puede recurrir a un renderizador de software que puede cambiar la calidad por la velocidad.

Proyecto de ejemplo @ 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.