Ejemplo de transformaciones
El ejemplo de Transformaciones muestra cómo las transformaciones influyen en la forma en que QPainter renderiza las primitivas gráficas.

La aplicación permite al usuario manipular el renderizado de una forma cambiando la traslación, rotación y escala del sistema de coordenadas de QPainter.
El ejemplo consta de dos clases y un enum global:
- La clase
RenderAreacontrola la representación de una forma determinada. - La clase
Windowes la ventana principal de la aplicación. - El enum
Operationdescribe las distintas operaciones de transformación disponibles en la aplicación.
Primero echaremos un vistazo rápido al enum Operation, después revisaremos la clase RenderArea para ver cómo se renderiza una forma. Por último, echaremos un vistazo a las funciones de la aplicación Transformaciones implementadas en la clase Window.
Operaciones de transformación
Normalmente, QPainter opera en el propio sistema de coordenadas del dispositivo asociado, pero también tiene un buen soporte para transformaciones de coordenadas.
El sistema de coordenadas por defecto de un dispositivo paint tiene su origen en la esquina superior izquierda. Los valores x aumentan hacia la derecha y los valores y aumentan hacia abajo. Puede escalar el sistema de coordenadas con un desplazamiento determinado mediante la función QPainter::scale(), puede girarlo en el sentido de las agujas del reloj mediante la función QPainter::rotate() y puede trasladarlo (es decir, añadir un desplazamiento determinado a los puntos) mediante la función QPainter::translate(). También se puede girar el sistema de coordenadas alrededor del origen (lo que se denomina cizallamiento) mediante la función QPainter::shear().
Todas las operaciones de transformación se basan en la matriz de transformación de QPainter, que puede obtenerse con la función QPainter::worldTransform(). Una matriz transforma un punto del plano en otro punto. Para más información sobre la matriz de transformación, consulte la documentación de Sistema de Coordenadas y QTransform.
enum Operation { NoTransformation, Translate, Rotate, Scale };
El enum global Operation se declara en el archivo renderarea.h y describe las distintas operaciones de transformación disponibles en la aplicación Transformaciones.
Definición de la clase RenderArea
La clase RenderArea hereda de QWidget, y controla el renderizado de una forma dada.
class RenderArea : public QWidget { Q_OBJECT public: RenderArea(QWidget *parent = nullptr); void setOperations(const QList<Operation> &operations); void setShape(const QPainterPath &shape); QSize minimumSizeHint() const override; QSize sizeHint() const override; protected: void paintEvent(QPaintEvent *event) override;
Declaramos dos funciones públicas, setOperations() y setShape(), para poder especificar la forma del widget RenderArea y transformar el sistema de coordenadas en el que se renderiza la forma.
Reimplementamos las funciones QWidget's minimumSizeHint() y sizeHint() para dar al widget RenderArea un tamaño razonable dentro de nuestra aplicación, y reimplementamos el manejador de eventos QWidget::paintEvent() para dibujar la forma del área de renderizado aplicando las opciones de transformación del usuario.
private: void drawCoordinates(QPainter &painter); void drawOutline(QPainter &painter); void drawShape(QPainter &painter); void transformPainter(QPainter &painter); QList<Operation> operations; QPainterPath shape; QRect xBoundingRect; QRect yBoundingRect; };
También declaramos varias funciones de conveniencia para dibujar la forma, el contorno del sistema de coordenadas y las coordenadas, y para transformar el pintor de acuerdo con las transformaciones elegidas.
Además, el widget RenderArea mantiene una lista de las operaciones de transformación aplicadas actualmente, una referencia a su forma, y un par de variables de conveniencia que utilizaremos cuando rendericemos las coordenadas.
Implementación de la clase RenderArea
El widget RenderArea controla el renderizado de una forma dada, incluyendo las transformaciones del sistema de coordenadas, reimplementando el manejador de eventos QWidget::paintEvent(). Pero primero echaremos un vistazo rápido al constructor y a las funciones que proporcionan acceso al widget RenderArea:
RenderArea::RenderArea(QWidget *parent) : QWidget(parent) { QFont newFont = font(); newFont.setPixelSize(12); setFont(newFont); QFontMetrics fontMetrics(newFont); xBoundingRect = fontMetrics.boundingRect(tr("x")); yBoundingRect = fontMetrics.boundingRect(tr("y")); }
En el constructor pasamos el parámetro parent a la clase base, y personalizamos la fuente que usaremos para representar las coordenadas. La función QWidget::font() devuelve la fuente actualmente configurada para el widget. Mientras no se haya establecido una fuente especial, o después de llamar a QWidget::setFont(), ésta será una fuente especial para la clase del widget, la fuente del padre o (si este widget es un widget de nivel superior) la fuente por defecto de la aplicación.
Tras asegurarnos de que el tamaño de la fuente es de 12 puntos, extraemos los rectángulos que encierran las letras de coordenadas, 'x' e 'y', usando la clase QFontMetrics.
QFontMetrics proporciona funciones para acceder a las métricas individuales de la fuente, sus caracteres y para las cadenas representadas en la fuente. La función QFontMetrics::boundingRect() devuelve el rectángulo delimitador del carácter dado en relación con el punto más a la izquierda de la línea base.
void RenderArea::setOperations(const QList<Operation> &operations) { this->operations = operations; update(); } void RenderArea::setShape(const QPainterPath &shape) { this->shape = shape; update(); }
En las funciones setShape() y setOperations() actualizamos el widget RenderArea almacenando el nuevo valor o valores seguidos de una llamada a la ranura QWidget::update() que programa un evento paint para ser procesado cuando Qt vuelva al bucle de eventos principal.
QSize RenderArea::minimumSizeHint() const { return QSize(182, 182); } QSize RenderArea::sizeHint() const { return QSize(232, 232); }
Reimplementamos las funciones minimumSizeHint() y sizeHint() de QWidget para dar al widget RenderArea un tamaño razonable dentro de nuestra aplicación. Las implementaciones por defecto de estas funciones devuelven un tamaño inválido si no hay diseño para este widget, y devuelven el tamaño mínimo del diseño o el tamaño preferido, respectivamente, en caso contrario.
void RenderArea::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.fillRect(event->rect(), QBrush(Qt::white)); painter.translate(66, 66);
El manejador de eventos paintEvent() recibe los eventos de pintura del widget RenderArea. Un evento paint es una petición para repintar todo o parte del widget. Puede ocurrir como resultado de QWidget::repaint() o QWidget::update(), o porque el widget estaba oculto y ahora ha sido descubierto, o por muchas otras razones.
Primero creamos un QPainter para el widget RenderArea. La sugerencia de renderizado QPainter::Antialiasing indica que el motor debe antialiasear los bordes de las primitivas si es posible. Luego borramos el área que necesita ser repintada usando la función QPainter::fillRect().
También trasladamos el sistema de coordenadas con un desplazamiento constante para asegurarnos de que la forma original se renderiza con un margen adecuado.
{
QPainterStateGuard guard(&painter);
transformPainter(painter);
drawShape(painter);
}Antes de empezar a renderizar la forma, instanciamos un QPainterStateGuard para guardar el estado actual del pintor (es decir, empujamos el estado a una pila) incluyendo el sistema de coordenadas actual mientras está en scope. La razón para guardar el estado del pintor es que la siguiente llamada a la función transformPainter() transformará el sistema de coordenadas dependiendo de las operaciones de transformación elegidas en ese momento, y necesitamos una forma de volver al estado original para dibujar el contorno.
Después de transformar el sistema de coordenadas, dibujamos la forma de RenderArea, y luego restauramos el estado del pintor utilizando la función QPainter::restore() (es decir, sacando el estado guardado de la pila).
drawOutline(painter);
A continuación dibujamos el contorno del cuadrado.
transformPainter(painter);
drawCoordinates(painter);
}Como queremos que las coordenadas se correspondan con el sistema de coordenadas en el que se representa la forma, debemos hacer otra llamada a la función transformPainter().
El orden de las operaciones de pintado es esencial con respecto a los píxeles compartidos. La razón por la que no renderizamos las coordenadas cuando el sistema de coordenadas ya está transformado para renderizar la forma, sino que aplazamos su renderización hasta el final, es que queremos que las coordenadas aparezcan encima de la forma y su contorno.
No es necesario guardar el estado QPainter esta vez, ya que dibujar las coordenadas es la última operación de pintura.
void RenderArea::drawCoordinates(QPainter &painter) { painter.setPen(Qt::red); painter.drawLine(0, 0, 50, 0); painter.drawLine(48, -2, 50, 0); painter.drawLine(48, 2, 50, 0); painter.drawText(60 - xBoundingRect.width() / 2, 0 + xBoundingRect.height() / 2, tr("x")); painter.drawLine(0, 0, 0, 50); painter.drawLine(-2, 48, 0, 50); painter.drawLine(2, 48, 0, 50); painter.drawText(0 - yBoundingRect.width() / 2, 60 + yBoundingRect.height() / 2, tr("y")); } void RenderArea::drawOutline(QPainter &painter) { painter.setPen(Qt::darkGreen); painter.setPen(Qt::DashLine); painter.setBrush(Qt::NoBrush); painter.drawRect(0, 0, 100, 100); } void RenderArea::drawShape(QPainter &painter) { painter.fillPath(shape, Qt::blue); }
drawCoordinates(), drawOutline() y drawShape() son funciones de conveniencia llamadas desde el manejador de eventos paintEvent(). Para más información sobre las operaciones básicas de dibujo de QPainter y cómo mostrar primitivas gráficas básicas, vea el ejemplo Dibujo Básico.
void RenderArea::transformPainter(QPainter &painter) { for (int i = 0; i < operations.size(); ++i) { switch (operations[i]) { case Translate: painter.translate(50, 50); break; case Scale: painter.scale(0.75, 0.75); break; case Rotate: painter.rotate(60); break; case NoTransformation: default: ; } } }
La función de conveniencia transformPainter() también es llamada desde el manejador de eventos paintEvent(), y transforma el sistema de coordenadas QPainter dado de acuerdo a las opciones de transformación del usuario.
Definición de la clase Window
La clase Window es la ventana principal de la aplicación de Transformaciones.
La aplicación muestra cuatro widgets RenderArea. El widget situado más a la izquierda representa la forma en el sistema de coordenadas por defecto de QPainter, los otros representan la forma con la transformación elegida además de todas las transformaciones aplicadas a los widgets RenderArea situados a su izquierda.
class Window : public QWidget { Q_OBJECT public: Window(); public slots: void operationChanged(); void shapeSelected(int index);
Declaramos dos ranuras públicas para que la aplicación pueda responder a la interacción del usuario, actualizando los widgets RenderArea mostrados según las transformaciones elegidas por el usuario.
La ranura operationChanged() actualiza cada uno de los widgets RenderArea aplicando las operaciones de transformación elegidas en ese momento, y es llamada cada vez que el usuario cambia las operaciones seleccionadas. La ranura shapeSelected() actualiza las formas de los widgets RenderArea cada vez que el usuario cambia la forma preferida.
private: void setupShapes(); enum { NumTransformedAreas = 3 }; RenderArea *originalRenderArea; RenderArea *transformedRenderAreas[NumTransformedAreas]; QComboBox *shapeComboBox; QComboBox *operationComboBoxes[NumTransformedAreas]; QList<QPainterPath> shapes; };
También declaramos una función de conveniencia privada, setupShapes(), que se utiliza al construir el widget Window, y declaramos punteros a los distintos componentes del widget. Elegimos mantener las formas disponibles en un QList de QPainterPaths. Además declaramos un enum privado que cuenta el número de widgets RenderArea mostrados excepto el widget que representa la forma en QPainter's sistema de coordenadas por defecto.
Implementación de la clase Window
En el constructor creamos e inicializamos los componentes de la aplicación:
Window::Window() { originalRenderArea = new RenderArea; shapeComboBox = new QComboBox; shapeComboBox->addItem(tr("Clock")); shapeComboBox->addItem(tr("House")); shapeComboBox->addItem(tr("Text")); shapeComboBox->addItem(tr("Truck")); QGridLayout *layout = new QGridLayout; layout->addWidget(originalRenderArea, 0, 0); layout->addWidget(shapeComboBox, 1, 0);
Primero creamos el widget RenderArea que renderizará la forma en el sistema de coordenadas por defecto. También creamos el widget asociado QComboBox que permite al usuario elegir entre cuatro formas diferentes: Un reloj, una casa, un texto y un camión. Las formas se crean al final del constructor, utilizando la función setupShapes().
for (int i = 0; i < NumTransformedAreas; ++i) { transformedRenderAreas[i] = new RenderArea; operationComboBoxes[i] = new QComboBox; operationComboBoxes[i]->addItem(tr("No transformation")); operationComboBoxes[i]->addItem(tr("Rotate by 60\xC2\xB0")); operationComboBoxes[i]->addItem(tr("Scale to 75%")); operationComboBoxes[i]->addItem(tr("Translate by (50, 50)")); connect(operationComboBoxes[i], &QComboBox::activated, this, &Window::operationChanged); layout->addWidget(transformedRenderAreas[i], 0, i + 1); layout->addWidget(operationComboBoxes[i], 1, i + 1); }
A continuación creamos los widgets RenderArea que renderizarán sus formas con transformaciones de coordenadas. Por defecto la operación aplicada es No Transformation, es decir, las formas se renderizan dentro del sistema de coordenadas por defecto. Creamos e inicializamos los QComboBoxes asociados con elementos correspondientes a las diversas operaciones de transformación descritas por el enum global Operation.
También conectamos la señal activated() de los QComboBoxes a la ranura operationChanged() para actualizar la aplicación cada vez que el usuario cambie las operaciones de transformación seleccionadas.
setLayout(layout);
setupShapes();
shapeSelected(0);
setWindowTitle(tr("Transformations"));
}Por último, establecemos el diseño de la ventana de la aplicación utilizando la función QWidget::setLayout(), construimos las formas disponibles utilizando la función privada setupShapes() y hacemos que la aplicación muestre la forma del reloj al iniciarse utilizando la ranura pública shapeSelected() antes de establecer el título de la ventana.
void Window::setupShapes() { QPainterPath truck; QPainterPath clock; QPainterPath house; QPainterPath text; ... shapes.append(clock); shapes.append(house); shapes.append(text); shapes.append(truck); connect(shapeComboBox, &QComboBox::activated, this, &Window::shapeSelected); }
La función setupShapes() es llamada desde el constructor y crea los objetos QPainterPath que representan las formas que se utilizan en la aplicación. Para más detalles sobre la construcción, véase el archivo de ejemplo painting/transformations/window.cpp. Las formas se almacenan en una lista QList. La función QList::append() inserta la forma dada al final de la lista.
También conectamos la señal asociada QComboBox's activated() a la ranura shapeSelected() para actualizar la aplicación cuando el usuario cambia la forma preferida.
void Window::operationChanged() { static const Operation operationTable[] = { NoTransformation, Rotate, Scale, Translate }; QList<Operation> operations; for (int i = 0; i < NumTransformedAreas; ++i) { int index = operationComboBoxes[i]->currentIndex(); operations.append(operationTable[index]); transformedRenderAreas[i]->setOperations(operations); } }
La ranura pública operationChanged() se llama cada vez que el usuario cambia las operaciones seleccionadas.
Recuperamos la operación de transformación elegida para cada uno de los widgets RenderArea transformados consultando el QComboBoxes asociado. Se supone que los widgets RenderArea transformados representan la forma con la transformación especificada por su combobox asociado , además de todas las transformaciones aplicadas a los widgets RenderArea situados a su izquierda. Por ello, para cada widget que consultamos, añadimos la operación asociada a un QList de transformaciones que aplicamos al widget antes de pasar al siguiente.
void Window::shapeSelected(int index) { QPainterPath shape = shapes[index]; originalRenderArea->setShape(shape); for (int i = 0; i < NumTransformedAreas; ++i) transformedRenderAreas[i]->setShape(shape); }
La ranura shapeSelected() se llama cada vez que el usuario cambia la forma preferida, actualizando los widgets RenderArea mediante su función pública setShape().
Resumen
El ejemplo de Transformaciones muestra cómo las transformaciones influyen en la forma en que QPainter renderiza las primitivas gráficas. Normalmente, QPainter opera en el propio sistema de coordenadas del dispositivo, pero también tiene un buen soporte para transformaciones de coordenadas. Con la aplicación Transformaciones puede escalar, rotar y trasladar el sistema de coordenadas de QPainter. El orden en que se aplican estas transformaciones es esencial para el resultado.
Todas las operaciones de transformación operan sobre la matriz de transformación de QPainter. Para más información sobre la matriz de transformación, consulte la documentación sobre el sistema de co ordenadas y QTransform.
La documentación de referencia de Qt proporciona varios ejemplos de pintado. Entre ellos se encuentra el ejemplo Affine Transformations que muestra la capacidad de Qt para realizar transformaciones sobre operaciones de pintura. El ejemplo también permite al usuario experimentar con las distintas operaciones de transformación.
© 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.