Basic Drawing Example¶
The Basic Drawing example shows how to display basic graphics primitives in a variety of styles using the
QPainter
class.The Basic Drawing example shows how to display basic graphics primitives in a variety of styles using the
QPainter
class.
QPainter
performs low-level painting on widgets and other paint devices. The class can draw everything from simple lines to complex shapes like pies and chords. It can also draw aligned text and pixmaps. Normally, it draws in a “natural” coordinate system, but it can in addition do view and world transformation.The example provides a render area, displaying the currently active shape, and lets the user manipulate the rendered shape and its appearance using the
QPainter
parameters: The user can change the active shape (Shape), and modify theQPainter
‘s pen (Pen Width, Pen Style, Pen Cap, Pen Join), brush (Brush Style) and render hints (Antialiasing). In addition the user can rotate a shape (Transformations); behind the scenes we useQPainter
‘s ability to manipulate the coordinate system to perform the rotation.The Basic Drawing example consists of two classes:
RenderArea
is a custom widget that renders multiple copies of the currently active shape.
Window
is the application’s main window displaying aRenderArea
widget in addition to several parameter widgets.First we will review the
Window
class, then we will take a look at theRenderArea
class.
Window Class Definition¶
The Window class inherits
QWidget
, and is the application’s main window displaying aRenderArea
widget in addition to several parameter widgets.class Window : public QWidget { Q_OBJECT public: Window(); private slots: void shapeChanged(); void penChanged(); void brushChanged(); private: RenderArea *renderArea; QLabel *shapeLabel; QLabel *penWidthLabel; QLabel *penStyleLabel; QLabel *penCapLabel; QLabel *penJoinLabel; QLabel *brushStyleLabel; QLabel *otherOptionsLabel; QComboBox *shapeComboBox; QSpinBox *penWidthSpinBox; QComboBox *penStyleComboBox; QComboBox *penCapComboBox; QComboBox *penJoinComboBox; QComboBox *brushStyleComboBox; QCheckBox *antialiasingCheckBox; QCheckBox *transformationsCheckBox; };We declare the various widgets, and three private slots updating the
RenderArea
widget: TheshapeChanged()
slot updates theRenderArea
widget when the user changes the currently active shape. We call thepenChanged()
slot when either of theQPainter
‘s pen parameters changes. And thebrushChanged()
slot updates theRenderArea
widget when the user changes the painter’s brush style.
Window Class Implementation¶
In the constructor we create and initialize the various widgets appearing in the main application window.
Window::Window() { renderArea = new RenderArea; shapeComboBox = new QComboBox; shapeComboBox->addItem(tr("Polygon"), RenderArea::Polygon); shapeComboBox->addItem(tr("Rectangle"), RenderArea::Rect); shapeComboBox->addItem(tr("Rounded Rectangle"), RenderArea::RoundedRect); shapeComboBox->addItem(tr("Ellipse"), RenderArea::Ellipse); shapeComboBox->addItem(tr("Pie"), RenderArea::Pie); shapeComboBox->addItem(tr("Chord"), RenderArea::Chord); shapeComboBox->addItem(tr("Path"), RenderArea::Path); shapeComboBox->addItem(tr("Line"), RenderArea::Line); shapeComboBox->addItem(tr("Polyline"), RenderArea::Polyline); shapeComboBox->addItem(tr("Arc"), RenderArea::Arc); shapeComboBox->addItem(tr("Points"), RenderArea::Points); shapeComboBox->addItem(tr("Text"), RenderArea::Text); shapeComboBox->addItem(tr("Pixmap"), RenderArea::Pixmap); shapeLabel = new QLabel(tr("&Shape:")); shapeLabel->setBuddy(shapeComboBox);First we create the
RenderArea
widget that will render the currently active shape. Then we create the Shape combobox, and add the associated items (i.e. the different shapes aQPainter
can draw).penWidthSpinBox = new QSpinBox; penWidthSpinBox->setRange(0, 20); penWidthSpinBox->setSpecialValueText(tr("0 (cosmetic pen)")); penWidthLabel = new QLabel(tr("Pen &Width:")); penWidthLabel->setBuddy(penWidthSpinBox);
QPainter
‘s pen is aQPen
object; theQPen
class defines how a painter should draw lines and outlines of shapes. A pen has several properties: Width, style, cap and join.A pen’s width can be zero or greater, but the most common width is zero. Note that this doesn’t mean 0 pixels, but implies that the shape is drawn as smoothly as possible although perhaps not mathematically correct.
We create a
QSpinBox
for the Pen Width parameter.penStyleComboBox = new QComboBox; penStyleComboBox->addItem(tr("Solid"), static_cast<int>(Qt::SolidLine)); penStyleComboBox->addItem(tr("Dash"), static_cast<int>(Qt::DashLine)); penStyleComboBox->addItem(tr("Dot"), static_cast<int>(Qt::DotLine)); penStyleComboBox->addItem(tr("Dash Dot"), static_cast<int>(Qt::DashDotLine)); penStyleComboBox->addItem(tr("Dash Dot Dot"), static_cast<int>(Qt::DashDotDotLine)); penStyleComboBox->addItem(tr("None"), static_cast<int>(Qt::NoPen)); penStyleLabel = new QLabel(tr("&Pen Style:")); penStyleLabel->setBuddy(penStyleComboBox); penCapComboBox = new QComboBox; penCapComboBox->addItem(tr("Flat"), Qt::FlatCap); penCapComboBox->addItem(tr("Square"), Qt::SquareCap); penCapComboBox->addItem(tr("Round"), Qt::RoundCap); penCapLabel = new QLabel(tr("Pen &Cap:")); penCapLabel->setBuddy(penCapComboBox); penJoinComboBox = new QComboBox; penJoinComboBox->addItem(tr("Miter"), Qt::MiterJoin); penJoinComboBox->addItem(tr("Bevel"), Qt::BevelJoin); penJoinComboBox->addItem(tr("Round"), Qt::RoundJoin); penJoinLabel = new QLabel(tr("Pen &Join:")); penJoinLabel->setBuddy(penJoinComboBox);The pen style defines the line type. The default style is solid (
SolidLine
). Setting the style to none (NoPen
) tells the painter to not draw lines or outlines. The pen cap defines how the end points of lines are drawn. And the pen join defines how two lines join when multiple connected lines are drawn. The cap and join only apply to lines with a width of 1 pixel or greater.We create
QComboBox
es for each of the Pen Style, Pen Cap and Pen Join parameters, and adds the associated items (i.e the values of thePenStyle
,PenCapStyle
andPenJoinStyle
enums respectively).brushStyleComboBox = new QComboBox; brushStyleComboBox->addItem(tr("Linear Gradient"), static_cast<int>(Qt::LinearGradientPattern)); brushStyleComboBox->addItem(tr("Radial Gradient"), static_cast<int>(Qt::RadialGradientPattern)); brushStyleComboBox->addItem(tr("Conical Gradient"), static_cast<int>(Qt::ConicalGradientPattern)); brushStyleComboBox->addItem(tr("Texture"), static_cast<int>(Qt::TexturePattern)); brushStyleComboBox->addItem(tr("Solid"), static_cast<int>(Qt::SolidPattern)); brushStyleComboBox->addItem(tr("Horizontal"), static_cast<int>(Qt::HorPattern)); brushStyleComboBox->addItem(tr("Vertical"), static_cast<int>(Qt::VerPattern)); brushStyleComboBox->addItem(tr("Cross"), static_cast<int>(Qt::CrossPattern)); brushStyleComboBox->addItem(tr("Backward Diagonal"), static_cast<int>(Qt::BDiagPattern)); brushStyleComboBox->addItem(tr("Forward Diagonal"), static_cast<int>(Qt::FDiagPattern)); brushStyleComboBox->addItem(tr("Diagonal Cross"), static_cast<int>(Qt::DiagCrossPattern)); brushStyleComboBox->addItem(tr("Dense 1"), static_cast<int>(Qt::Dense1Pattern)); brushStyleComboBox->addItem(tr("Dense 2"), static_cast<int>(Qt::Dense2Pattern)); brushStyleComboBox->addItem(tr("Dense 3"), static_cast<int>(Qt::Dense3Pattern)); brushStyleComboBox->addItem(tr("Dense 4"), static_cast<int>(Qt::Dense4Pattern)); brushStyleComboBox->addItem(tr("Dense 5"), static_cast<int>(Qt::Dense5Pattern)); brushStyleComboBox->addItem(tr("Dense 6"), static_cast<int>(Qt::Dense6Pattern)); brushStyleComboBox->addItem(tr("Dense 7"), static_cast<int>(Qt::Dense7Pattern)); brushStyleComboBox->addItem(tr("None"), static_cast<int>(Qt::NoBrush)); brushStyleLabel = new QLabel(tr("&Brush:")); brushStyleLabel->setBuddy(brushStyleComboBox);The
QBrush
class defines the fill pattern of shapes drawn by aQPainter
. The default brush style isNoBrush
. This style tells the painter to not fill shapes. The standard style for filling isSolidPattern
.We create a
QComboBox
for the Brush Style parameter, and add the associated items (i.e. the values of theBrushStyle
enum).otherOptionsLabel = new QLabel(tr("Options:")); antialiasingCheckBox = new QCheckBox(tr("&Antialiasing"));Antialiasing is a feature that “smoothes” the pixels to create more even and less jagged lines, and can be applied using
QPainter
‘s render hints.RenderHints
are used to specify flags toQPainter
that may or may not be respected by any given engine.We simply create a
QCheckBox
for the Antialiasing option.transformationsCheckBox = new QCheckBox(tr("&Transformations"));The Transformations option implies a manipulation of the coordinate system that will appear as if the rendered shape is rotated in three dimensions.
We use the
translate()
,rotate()
andscale()
functions to implement this feature represented in the main application window by a simpleQCheckBox
.connect(shapeComboBox, QOverload<int>::of(&QComboBox::activated), this, &Window::shapeChanged); connect(penWidthSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), this, &Window::penChanged); connect(penStyleComboBox, QOverload<int>::of(&QComboBox::activated), this, &Window::penChanged); connect(penCapComboBox, QOverload<int>::of(&QComboBox::activated), this, &Window::penChanged); connect(penJoinComboBox, QOverload<int>::of(&QComboBox::activated), this, &Window::penChanged); connect(brushStyleComboBox, QOverload<int>::of(&QComboBox::activated), this, &Window::brushChanged); connect(antialiasingCheckBox, &QAbstractButton::toggled, renderArea, &RenderArea::setAntialiased); connect(transformationsCheckBox, &QAbstractButton::toggled, renderArea, &RenderArea::setTransformed);Then we connect the parameter widgets with their associated slots using the static
connect()
function, ensuring that theRenderArea
widget is updated whenever the user changes the shape, or any of the other parameters.QGridLayout *mainLayout = new QGridLayout; mainLayout->setColumnStretch(0, 1); mainLayout->setColumnStretch(3, 1); mainLayout->addWidget(renderArea, 0, 0, 1, 4); mainLayout->addWidget(shapeLabel, 2, 0, Qt::AlignRight); mainLayout->addWidget(shapeComboBox, 2, 1); mainLayout->addWidget(penWidthLabel, 3, 0, Qt::AlignRight); mainLayout->addWidget(penWidthSpinBox, 3, 1); mainLayout->addWidget(penStyleLabel, 4, 0, Qt::AlignRight); mainLayout->addWidget(penStyleComboBox, 4, 1); mainLayout->addWidget(penCapLabel, 3, 2, Qt::AlignRight); mainLayout->addWidget(penCapComboBox, 3, 3); mainLayout->addWidget(penJoinLabel, 2, 2, Qt::AlignRight); mainLayout->addWidget(penJoinComboBox, 2, 3); mainLayout->addWidget(brushStyleLabel, 4, 2, Qt::AlignRight); mainLayout->addWidget(brushStyleComboBox, 4, 3); mainLayout->addWidget(otherOptionsLabel, 5, 0, Qt::AlignRight); mainLayout->addWidget(antialiasingCheckBox, 5, 1, 1, 1, Qt::AlignRight); mainLayout->addWidget(transformationsCheckBox, 5, 2, 1, 2, Qt::AlignRight); setLayout(mainLayout); shapeChanged(); penChanged(); brushChanged(); antialiasingCheckBox->setChecked(true); setWindowTitle(tr("Basic Drawing")); }Finally, we add the various widgets to a layout, and call the
shapeChanged()
,penChanged()
, andbrushChanged()
slots to initialize the application. We also turn on antialiasing.void Window::shapeChanged() { RenderArea::Shape shape = RenderArea::Shape(shapeComboBox->itemData( shapeComboBox->currentIndex(), IdRole).toInt()); renderArea->setShape(shape); }The
shapeChanged()
slot is called whenever the user changes the currently active shape.First we retrieve the shape the user has chosen using the
itemData()
function. This function returns the data for the given role in the given index in the combobox. We usecurrentIndex()
to retrieve the index of the shape, and the role is defined by theItemDataRole
enum;IdRole
is an alias forUserRole
.Note that
UserRole
is only the first role that can be used for application-specific purposes. If you need to store different data in the same index, you can use different roles by simply incrementing the value ofUserRole
, for example: ‘UserRole
+ 1’ and ‘UserRole
+ 2’. However, it is a good programming practice to give each role their own name: ‘myFirstRole =UserRole
+ 1’ and ‘mySecondRole =UserRole
+ 2’. Even though we only need a single role in this particular example, we add the following line of code to the beginning of thewindow.cpp
file.const int IdRole = Qt::UserRole;The
itemData()
function returns the data as aQVariant
, so we need to cast the data toRenderArea::Shape
. If there is no data for the given role, the function returnsInvalid
.In the end we call the
RenderArea::setShape()
slot to update theRenderArea
widget.void Window::penChanged() { int width = penWidthSpinBox->value(); Qt::PenStyle style = Qt::PenStyle(penStyleComboBox->itemData( penStyleComboBox->currentIndex(), IdRole).toInt()); Qt::PenCapStyle cap = Qt::PenCapStyle(penCapComboBox->itemData( penCapComboBox->currentIndex(), IdRole).toInt()); Qt::PenJoinStyle join = Qt::PenJoinStyle(penJoinComboBox->itemData( penJoinComboBox->currentIndex(), IdRole).toInt()); renderArea->setPen(QPen(Qt::blue, width, style, cap, join)); }We call the
penChanged()
slot whenever the user changes any of the pen parameters. Again we use theitemData()
function to retrieve the parameters, and then we call theRenderArea::setPen()
slot to update theRenderArea
widget.void Window::brushChanged() { Qt::BrushStyle style = Qt::BrushStyle(brushStyleComboBox->itemData(The brushChanged() slot is called whenever the user changes the brush parameter which we retrieve using the
itemData()
function as before.if (style == Qt::LinearGradientPattern) { QLinearGradient linearGradient(0, 0, 100, 100); linearGradient.setColorAt(0.0, Qt::white); linearGradient.setColorAt(0.2, Qt::green); linearGradient.setColorAt(1.0, Qt::black); renderArea->setBrush(linearGradient);If the brush parameter is a gradient fill, special actions are required.
The
QGradient
class is used in combination withQBrush
to specify gradient fills. Qt currently supports three types of gradient fills: linear, radial and conical. Each of these is represented by a subclass ofQGradient
:QLinearGradient
,QRadialGradient
andQConicalGradient
.So if the brush style is
LinearGradientPattern
, we first create aQLinearGradient
object with interpolation area between the coordinates passed as arguments to the constructor. The positions are specified using logical coordinates. Then we set the gradient’s colors using thesetColorAt()
function. The colors is defined using stop points which are composed by a position (between 0 and 1) and aQColor
. The set of stop points describes how the gradient area should be filled. A gradient can have an arbitrary number of stop points.In the end we call
RenderArea::setBrush()
slot to update theRenderArea
widget’s brush with theQLinearGradient
object.} else if (style == Qt::RadialGradientPattern) { QRadialGradient radialGradient(50, 50, 50, 70, 70); radialGradient.setColorAt(0.0, Qt::white); radialGradient.setColorAt(0.2, Qt::green); radialGradient.setColorAt(1.0, Qt::black); renderArea->setBrush(radialGradient); } else if (style == Qt::ConicalGradientPattern) { QConicalGradient conicalGradient(50, 50, 150); conicalGradient.setColorAt(0.0, Qt::white); conicalGradient.setColorAt(0.2, Qt::green); conicalGradient.setColorAt(1.0, Qt::black); renderArea->setBrush(conicalGradient);A similar pattern of actions, as the one used for
QLinearGradient
, is used in the cases ofRadialGradientPattern
andConicalGradientPattern
.The only difference is the arguments passed to the constructor: Regarding the
QRadialGradient
constructor the first argument is the center, and the second the radial gradient’s radius. The third argument is optional, but can be used to define the focal point of the gradient inside the circle (the default focal point is the circle center). Regarding theQConicalGradient
constructor, the first argument specifies the center of the conical, and the second specifies the start angle of the interpolation.} else if (style == Qt::TexturePattern) { renderArea->setBrush(QBrush(QPixmap(":/images/brick.png")));If the brush style is
TexturePattern
we create aQBrush
from aQPixmap
. Then we callRenderArea::setBrush()
slot to update theRenderArea
widget with the newly created brush.} else { renderArea->setBrush(QBrush(Qt::green, style)); } }Otherwise we simply create a brush with the given style and a green color, and then call
RenderArea::setBrush()
slot to update theRenderArea
widget with the newly created brush.
RenderArea Class Definition¶
The
RenderArea
class inheritsQWidget
, and renders multiple copies of the currently active shape using aQPainter
.class RenderArea : public QWidget { Q_OBJECT public: enum Shape { Line, Points, Polyline, Polygon, Rect, RoundedRect, Ellipse, Arc, Chord, Pie, Path, Text, Pixmap }; explicit RenderArea(QWidget *parent = nullptr); QSize minimumSizeHint() const override; QSize sizeHint() const override; public slots: void setShape(Shape shape); void setPen(const QPen &pen); void setBrush(const QBrush &brush); void setAntialiased(bool antialiased); void setTransformed(bool transformed); protected: void paintEvent(QPaintEvent *event) override; private: Shape shape; QPen pen; QBrush brush; bool antialiased; bool transformed; QPixmap pixmap; };First we define a public
Shape
enum to hold the different shapes that can be rendered by the widget (i.e the shapes that can be rendered by aQPainter
). Then we reimplement the constructor as well as two ofQWidget
‘s public functions:minimumSizeHint()
andsizeHint()
.We also reimplement the
paintEvent()
function to be able to draw the currently active shape according to the specified parameters.We declare several private slots: The
setShape()
slot changes theRenderArea
‘s shape, thesetPen()
andsetBrush()
slots modify the widget’s pen and brush, and thesetAntialiased()
andsetTransformed()
slots modify the widget’s respective properties.
RenderArea Class Implementation¶
In the constructor we initialize some of the widget’s variables.
RenderArea::RenderArea(QWidget *parent) : QWidget(parent) { shape = Polygon; antialiased = false; transformed = false; pixmap.load(":/images/qt-logo.png"); setBackgroundRole(QPalette::Base); setAutoFillBackground(true); }We set its shape to be a Polygon, its antialiased property to be false and we load an image into the widget’s pixmap variable. In the end we set the widget’s background role, defining the brush from the widget’s
palette
that will be used to render the background.Base
is typically white.QSize RenderArea::sizeHint() const { return QSize(400, 200); }The
RenderArea
inheritsQWidget
‘ssizeHint
property holding the recommended size for the widget. If the value of this property is an invalid size, no size is recommended.The default implementation of the
sizeHint()
function returns an invalid size if there is no layout for the widget, and returns the layout’s preferred size otherwise.Our reimplementation of the function returns a
QSize
with a 400 pixels width and a 200 pixels height.QSize RenderArea::minimumSizeHint() const { return QSize(100, 100); }
RenderArea
also inheritsQWidget
‘sminimumSizeHint
property holding the recommended minimum size for the widget. Again, if the value of this property is an invalid size, no size is recommended.The default implementation of
minimumSizeHint()
returns an invalid size if there is no layout for the widget, and returns the layout’s minimum size otherwise.Our reimplementation of the function returns a
QSize
with a 100 pixels width and a 100 pixels height.void RenderArea::setShape(Shape shape) { this->shape = shape; update(); } void RenderArea::setPen(const QPen &pen) { this->pen = pen; update(); } void RenderArea::setBrush(const QBrush &brush) { this->brush = brush; update(); }The public
setShape()
,setPen()
andsetBrush()
slots are called whenever we want to modify aRenderArea
widget’s shape, pen or brush. We set the shape, pen or brush according to the slot parameter, and callupdate()
to make the changes visible in theRenderArea
widget.The
update()
slot does not cause an immediate repaint; instead it schedules a paint event for processing when Qt returns to the main event loop.void RenderArea::setAntialiased(bool antialiased) { this->antialiased = antialiased; update(); } void RenderArea::setTransformed(bool transformed) { this->transformed = transformed; update(); }With the
setAntialiased()
andsetTransformed()
slots we change the state of the properties according to the slot parameter, and call theupdate()
slot to make the changes visible in theRenderArea
widget.void RenderArea::paintEvent(QPaintEvent * /* event */) { static const QPoint points[4] = { QPoint(10, 80), QPoint(20, 10), QPoint(80, 30), QPoint(90, 70) }; QRect rect(10, 20, 80, 60); QPainterPath path; path.moveTo(20, 80); path.lineTo(20, 30); path.cubicTo(80, 0, 50, 50, 80, 80); int startAngle = 20 * 16; int arcLength = 120 * 16;Then we reimplement the
paintEvent()
function. The first thing we do is to create the graphical objects we will need to draw the various shapes.We create a vector of four
QPoint
s. We use this vector to render the Points, Polyline and Polygon shapes. Then we create aQRect
, defining a rectangle in the plane, which we use as the bounding rectangle for all the shapes excluding the Path and the Pixmap.We also create a
QPainterPath
. TheQPainterPath
class provides a container for painting operations, enabling graphical shapes to be constructed and reused. A painter path is an object composed of a number of graphical building blocks, such as rectangles, ellipses, lines, and curves. For more information about theQPainterPath
class, see the Painter Paths example. In this example, we create a painter path composed of one straight line and a Bezier curve.In addition we define a start angle and an arc length that we will use when drawing the Arc, Chord and Pie shapes.
QPainter painter(this); painter.setPen(pen); painter.setBrush(brush); if (antialiased) painter.setRenderHint(QPainter::Antialiasing, true);We create a
QPainter
for theRenderArea
widget, and set the painters pen and brush according to theRenderArea
‘s pen and brush. If the Antialiasing parameter option is checked, we also set the painter’s render hints.Antialiasing
indicates that the engine should antialias edges of primitives if possible.for (int x = 0; x < width(); x += 100) { for (int y = 0; y < height(); y += 100) { painter.save(); painter.translate(x, y);Finally, we render the multiple copies of the
RenderArea
‘s shape. The number of copies is depending on the size of theRenderArea
widget, and we calculate their positions using twofor
loops and the widgets height and width.For each copy we first save the current painter state (pushes the state onto a stack). Then we translate the coordinate system, using the
translate()
function, to the position determined by the variables of thefor
loops. If we omit this translation of the coordinate system all the copies of the shape will be rendered on top of each other in the top left cormer of theRenderArea
widget.if (transformed) { painter.translate(50, 50); painter.rotate(60.0); painter.scale(0.6, 0.9); painter.translate(-50, -50); }If the Transformations parameter option is checked, we do an additional translation of the coordinate system before we rotate the coordinate system 60 degrees clockwise using the
rotate()
function and scale it down in size using thescale()
function. In the end we translate the coordinate system back to where it was before we rotated and scaled it.Now, when rendering the shape, it will appear as if it was rotated in three dimensions.
switch (shape) { case Line: painter.drawLine(rect.bottomLeft(), rect.topRight()); break; case Points: painter.drawPoints(points, 4); break; case Polyline: painter.drawPolyline(points, 4); break; case Polygon: painter.drawPolygon(points, 4); break; case Rect: painter.drawRect(rect); break; case RoundedRect: painter.drawRoundedRect(rect, 25, 25, Qt::RelativeSize); break; case Ellipse: painter.drawEllipse(rect); break; case Arc: painter.drawArc(rect, startAngle, arcLength); break; case Chord: painter.drawChord(rect, startAngle, arcLength); break; case Pie: painter.drawPie(rect, startAngle, arcLength); break; case Path: painter.drawPath(path); break; case Text: painter.drawText(rect, Qt::AlignCenter, tr("Qt by\nThe Qt Company")); break; case Pixmap: painter.drawPixmap(10, 10, pixmap); }Next, we identify the
RenderArea
‘s shape, and render it using the associatedQPainter
drawing function:
drawLine()
,
drawPoints()
,
drawPolyline()
,
drawPolygon()
,
drawRect()
,
drawRoundedRect()
,
drawEllipse()
,
drawArc()
,
drawChord()
,
drawPie()
,
drawPath()
,
drawText()
or
drawPixmap()
Before we started rendering, we saved the current painter state (pushes the state onto a stack). The rationale for this is that we calculate each shape copy’s position relative to the same point in the coordinate system. When translating the coordinate system, we lose the knowledge of this point unless we save the current painter state before we start the translating process.
painter.restore(); } } painter.setRenderHint(QPainter::Antialiasing, false); painter.setPen(palette().dark().color()); painter.setBrush(Qt::NoBrush); painter.drawRect(QRect(0, 0, width() - 1, height() - 1)); }Then, when we are finished rendering a copy of the shape we can restore the original painter state, with its associated coordinate system, using the
restore()
function. In this way we ensure that the next shape copy will be rendered in the correct position.We could translate the coordinate system back using
translate()
instead of saving the painter state. But since we in addition to translating the coordinate system (when the Transformation parameter option is checked) both rotate and scale the coordinate system, the easiest solution is to save the current painter state.
© 2022 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.