blob: 17b540b6cc746036dc7a42079246974b1e7feb67 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Free Documentation License Usage
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file. Please review the following information to ensure
** the GNU Free Documentation License version 1.3 requirements
** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
** $QT_END_LICENSE$
**
****************************************************************************/
/*!
\example painting/transformations
\title Transformations Example
\ingroup examples-painting
\brief The Transformations example shows how transformations
influence the way that QPainter renders graphics primitives.
\brief The Transformations example shows how transformations influence
the way that QPainter renders graphics primitives. In particular
it shows how the order of transformations affect the result.
\image transformations-example.png
The application allows the user to manipulate the rendering of a
shape by changing the translation, rotation and scale of
QPainter's coordinate system.
The example consists of two classes and a global enum:
\list
\li The \c RenderArea class controls the rendering of a given shape.
\li The \c Window class is the application's main window.
\li The \c Operation enum describes the various transformation
operations available in the application.
\endlist
First we will take a quick look at the \c Operation enum, then we
will review the \c RenderArea class to see how a shape is
rendered. Finally, we will take a look at the Transformations
application's features implemented in the \c Window class.
\section1 Transformation Operations
Normally, the QPainter operates on the associated device's own
coordinate system, but it also has good support for coordinate
transformations.
The default coordinate system of a paint device has its origin at
the top-left corner. The x values increase to the right and the y
values increase downwards. You can scale the coordinate system by
a given offset using the QPainter::scale() function, you can
rotate it clockwise using the QPainter::rotate() function and you
can translate it (i.e. adding a given offset to the points) using
the QPainter::translate() function. You can also twist the
coordinate system around the origin (called shearing) using the
QPainter::shear() function.
All the tranformation operations operate on QPainter's
tranformation matrix that you can retrieve using the
QPainter::worldTransform() function. A matrix transforms a point in the
plane to another point. For more information about the
transformation matrix, see the \l {Coordinate System} and
QTransform documentation.
\snippet painting/transformations/renderarea.h 0
The global \c Operation enum is declared in the \c renderarea.h
file and describes the various transformation operations available
in the Transformations application.
\section1 RenderArea Class Definition
The \c RenderArea class inherits QWidget, and controls the
rendering of a given shape.
\snippet painting/transformations/renderarea.h 1
We declare two public functions, \c setOperations() and
\c setShape(), to be able to specify the \c RenderArea widget's shape
and to transform the coordinate system the shape is rendered
within.
We reimplement the QWidget's \l
{QWidget::minimumSizeHint()}{minimumSizeHint()} and \l
{QWidget::sizeHint()}{sizeHint()} functions to give the \c
RenderArea widget a reasonable size within our application, and we
reimplement the QWidget::paintEvent() event handler to draw the
render area's shape applying the user's transformation choices.
\snippet painting/transformations/renderarea.h 2
We also declare several convenience functions to draw the shape,
the coordinate system's outline and the coordinates, and to
transform the painter according to the chosen transformations.
In addition, the \c RenderArea widget keeps a list of the
currently applied transformation operations, a reference to its
shape, and a couple of convenience variables that we will use when
rendering the coordinates.
\section1 RenderArea Class Implementation
The \c RenderArea widget controls the rendering of a given shape,
including the transformations of the coordinate system, by
reimplementing the QWidget::paintEvent() event handler. But first
we will take a quick look at the constructor and at the functions
that provides access to the \c RenderArea widget:
\snippet painting/transformations/renderarea.cpp 0
In the constructor we pass the parent parameter on to the base
class, and customize the font that we will use to render the
coordinates. The QWidget::font() function returns the font
currently set for the widget. As long as no special font has been
set, or after QWidget::setFont() is called, this is either a
special font for the widget class, the parent's font or (if this
widget is a top level widget) the default application font.
After ensuring that the font's size is 12 points, we extract the
rectangles enclosing the coordinate letters, 'x' and 'y', using the
QFontMetrics class.
QFontMetrics provides functions to access the individual metrics
of the font, its characters, and for strings rendered in the
font. The QFontMetrics::boundingRect() function returns the
bounding rectangle of the given character relative to the
left-most point on the base line.
\snippet painting/transformations/renderarea.cpp 1
\codeline
\snippet painting/transformations/renderarea.cpp 2
In the \c setShape() and \c setOperations() functions we update
the \c RenderArea widget by storing the new value or values
followed by a call to the QWidget::update() slot which schedules a
paint event for processing when Qt returns to the main event loop.
\snippet painting/transformations/renderarea.cpp 3
\codeline
\snippet painting/transformations/renderarea.cpp 4
We reimplement the QWidget's \l
{QWidget::minimumSizeHint()}{minimumSizeHint()} and \l
{QWidget::sizeHint()}{sizeHint()} functions to give the \c
RenderArea widget a reasonable size within our application. The
default implementations of these functions returns an invalid size
if there is no layout for this widget, and returns the layout's
minimum size or preferred size, respectively, otherwise.
\snippet painting/transformations/renderarea.cpp 5
The \c paintEvent() event handler receives the \c RenderArea
widget's paint events. A paint event is a request to repaint all
or part of the widget. It can happen as a result of
QWidget::repaint() or QWidget::update(), or because the widget was
obscured and has now been uncovered, or for many other reasons.
First we create a QPainter for the \c RenderArea widget. The \l
{QPainter::RenderHint}{QPainter::Antialiasing} render hint
indicates that the engine should antialias edges of primitives if
possible. Then we erase the area that needs to be repainted using
the QPainter::fillRect() function.
We also translate the coordinate system with an constant offset to
ensure that the original shape is renderend with a suitable
margin.
\snippet painting/transformations/renderarea.cpp 6
Before we start to render the shape, we call the QPainter::save()
function.
QPainter::save() saves the current painter state (i.e. pushes the
state onto a stack) including the current coordinate system. The
rationale for saving the painter state is that the following call
to the \c transformPainter() function will transform the
coordinate system depending on the currently chosen transformation
operations, and we need a way to get back to the original state to
draw the outline.
After transforming the coordinate system, we draw the \c
RenderArea's shape, and then we restore the painter state using
the QPainter::restore() function (i.e. popping the saved state off
the stack).
\snippet painting/transformations/renderarea.cpp 7
Then we draw the square outline.
\snippet painting/transformations/renderarea.cpp 8
Since we want the coordinates to correspond with the coordinate
system the shape is rendered within, we must make another call to
the \c transformPainter() function.
The order of the painting operations is essential with respect to
the shared pixels. The reason why we don't render the coordinates
when the coordinate system already is transformed to render the
shape, but instead defer their rendering to the end, is that we
want the coordinates to appear on top of the shape and its
outline.
There is no need to save the QPainter state this time since
drawing the coordinates is the last painting operation.
\snippet painting/transformations/renderarea.cpp 9
\codeline
\snippet painting/transformations/renderarea.cpp 10
\codeline
\snippet painting/transformations/renderarea.cpp 11
The \c drawCoordinates(), \c drawOutline() and \c drawShape() are
convenience functions called from the \c paintEvent() event
handler. For more information about QPainter's basic drawing
operations and how to display basic graphics primitives, see the
\l {painting/basicdrawing}{Basic Drawing} example.
\snippet painting/transformations/renderarea.cpp 12
The \c transformPainter() convenience function is also called from
the \c paintEvent() event handler, and transforms the given
QPainter's coordinate system according to the user's
transformation choices.
\section1 Window Class Definition
The \c Window class is the Transformations application's main
window.
The application displays four \c RenderArea widgets. The left-most
widget renders the shape in QPainter's default coordinate system,
the others render the shape with the chosen transformation in
addition to all the transformations applied to the \c RenderArea
widgets to their left.
\snippet painting/transformations/window.h 0
We declare two public slots to make the application able to
respond to user interaction, updating the displayed \c RenderArea
widgets according to the user's transformation choices.
The \c operationChanged() slot updates each of the \c RenderArea
widgets applying the currently chosen transformation operations, and
is called whenever the user changes the selected operations. The
\c shapeSelected() slot updates the \c RenderArea widgets' shapes
whenever the user changes the preferred shape.
\snippet painting/transformations/window.h 1
We also declare a private convenience function, \c setupShapes(),
that is used when constructing the \c Window widget, and we
declare pointers to the various components of the widget. We
choose to keep the available shapes in a QList of \l
{QPainterPath}s. In addition we declare a private enum counting
the number of displayed \c RenderArea widgets except the widget
that renders the shape in QPainter's default coordinate system.
\section1 Window Class Implementation
In the constructor we create and initialize the application's
components:
\snippet painting/transformations/window.cpp 0
First we create the \c RenderArea widget that will render the
shape in the default coordinate system. We also create the
associated QComboBox that allows the user to choose among four
different shapes: A clock, a house, a text and a truck. The shapes
themselves are created at the end of the constructor, using the
\c setupShapes() convenience function.
\snippet painting/transformations/window.cpp 1
Then we create the \c RenderArea widgets that will render their
shapes with coordinate tranformations. By default the applied
operation is \uicontrol {No Transformation}, i.e. the shapes are
rendered within the default coordinate system. We create and
initialize the associated \l {QComboBox}es with items
corresponding to the various transformation operations decribed by
the global \c Operation enum.
We also connect the \l {QComboBox}es' \l
{QComboBox::activated()}{activated()} signal to the \c
operationChanged() slot to update the application whenever the
user changes the selected transformation operations.
\snippet painting/transformations/window.cpp 2
Finally, we set the layout for the application window using the
QWidget::setLayout() function, construct the available shapes
using the private \c setupShapes() convenience function, and make
the application show the clock shape on startup using the public
\c shapeSelected() slot before we set the window title.
\snippet painting/transformations/window.cpp 3
\snippet painting/transformations/window.cpp 4
\snippet painting/transformations/window.cpp 5
\snippet painting/transformations/window.cpp 6
\dots
\snippet painting/transformations/window.cpp 7
The \c setupShapes() function is called from the constructor and
create the QPainterPath objects representing the shapes that are
used in the application. For construction details, see the \c
{painting/transformations/window.cpp} example
file. The shapes are stored in a QList. The QList::append()
function inserts the given shape at the end of the list.
We also connect the associated QComboBox's \l
{QComboBox::activated()}{activated()} signal to the \c
shapeSelected() slot to update the application when the user
changes the preferred shape.
\snippet painting/transformations/window.cpp 8
The public \c operationChanged() slot is called whenever the user
changes the selected operations.
We retrieve the chosen transformation operation for each of the
transformed \c RenderArea widgets by querying the associated \l
{QComboBox}{QComboBoxes}. The transformed \c RenderArea widgets
are supposed to render the shape with the transformation specified
by its associated combobox \e {in addition to} all the
transformations applied to the \c RenderArea widgets to its
left. For that reason, for each widget we query, we append the
associated operation to a QList of transformations which we apply
to the widget before proceeding to the next.
\snippet painting/transformations/window.cpp 9
The \c shapeSelected() slot is called whenever the user changes
the preferred shape, updating the \c RenderArea widgets using
their public \c setShape() function.
\section1 Summary
The Transformations example shows how transformations influence
the way that QPainter renders graphics primitives. Normally, the
QPainter operates on the device's own coordinate system, but it
also has good support for coordinate transformations. With the
Transformations application you can scale, rotate and translate
QPainter's coordinate system. The order in which these
tranformations are applied is essential for the result.
All the tranformation operations operate on QPainter's
tranformation matrix. For more information about the
transformation matrix, see the \l {Coordinate System} and
QTransform documentation.
The Qt reference documentation provides several painting
examples. Among these is the \l {painting/affine}{Affine
Transformations} example that shows Qt's ability to perform
transformations on painting operations. The example also allows the
user to experiment with the various transformation operations.
*/