基本绘图示例
“基本绘图”示例演示了如何使用QPainter 类以多种样式显示基本的图形基元。
QPainter 该类可在控件和其他绘图设备上执行低级绘制操作。它能够绘制从简单线条到圆饼、弦等复杂形状的一切图形,还可以绘制对齐文本和位图。通常,它在“自然”坐标系中进行绘制,但同时也支持视图和世界变换。

该示例提供了一个渲染区域,用于显示当前活动的图形,并允许用户通过QPainter 的参数来操作渲染出的图形及其外观:用户可以更改活动图形(Shape ),并修改QPainter 的笔(Pen Width 、Pen Style 、Pen Cap 、Pen Join )、画笔(Brush Style )以及渲染提示(Antialiasing )。 此外,用户还可以旋转图形(Transformations );在后台,我们利用QPainter 对坐标系进行操作的功能来实现旋转。
“基本绘图”示例由两个类组成:
RenderArea是一个自定义控件,用于渲染当前活动图形的多个副本。Window是应用程序的主窗口,除了显示若干参数控件外,还包含一个RenderArea控件。
首先我们将回顾Window 类,然后我们将了解RenderArea 类。
Window 类的定义
Window类继承自QWidget ,是应用程序的主窗口,除了显示RenderArea 控件外,还显示了几个参数控件。
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;
};我们声明了各种控件,以及三个用于更新RenderArea 控件的私有槽:shapeChanged() 槽在用户更改当前活动形状时更新RenderArea 控件;当QPainter 的任一笔参数发生变化时,我们会调用penChanged() 槽;而brushChanged() 槽则在用户更改绘图工具的笔刷样式时更新RenderArea 控件。
窗口类的实现
在构造函数中,我们创建并初始化主应用程序窗口中出现的各种控件。
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);首先,我们创建RenderArea 控件,用于渲染当前活动的形状。然后,我们创建Shape 下拉列表,并添加相关项(即QPainter 可以绘制的各种形状)。
penWidthSpinBox = new QSpinBox;
penWidthSpinBox->setRange(0, 20);
penWidthSpinBox->setSpecialValueText(tr("0 (cosmetic pen)"));
penWidthLabel = new QLabel(tr("Pen &Width:"));
penWidthLabel->setBuddy(penWidthSpinBox);QPainter画笔(pen)是一个QPen 对象;QPen 类定义了绘图器(painter)应如何绘制线条和形状的轮廓。画笔有几个属性:宽度(Width)、样式(style)、笔尖(cap)和连接方式(join)。
画笔的宽度可以为零或大于零,但最常见的宽度是零。请注意,这并不意味着 0 像素,而是表示形状将以尽可能平滑的方式绘制,尽管这在数学上可能并不严格正确。
我们为Pen Width 参数创建一个QSpinBox 对象。
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);笔触样式定义了线条类型。默认样式为实线(Qt::SolidLine )。将样式设置为无(Qt::NoPen )会指示绘图器不绘制线条或轮廓。笔尖定义了线条端点的绘制方式。而笔接头则定义了在绘制多条相连线条时,两条线条的连接方式。 笔尖和连接方式仅适用于宽度为1像素或更大的线条。
我们为每个Pen Style 、Pen Cap 和Pen Join 参数创建QComboBox对象,并添加关联项(即分别对应Qt::PenStyle 、Qt::PenCapStyle 和Qt::PenJoinStyle 枚举的值)。
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);QBrush 类定义了由QPainter 绘制的图形的填充模式。默认画笔样式为Qt::NoBrush 。该样式指示绘图器不填充图形。填充的标准样式为Qt::SolidPattern 。
我们为Brush Style 参数创建一个QComboBox ,并添加相关项(即Qt::BrushStyle 枚举的值)。
otherOptionsLabel = new QLabel(tr("Options:"));
antialiasingCheckBox = new QCheckBox(tr("&Antialiasing"));抗锯齿是一项通过“平滑”像素来生成更均匀、边缘更平滑的线条的功能,可通过QPainter 的渲染提示来应用。QPainter::RenderHints 用于向QPainter 指定标志,但具体是否被某个引擎采纳则因引擎而异。
我们只需为Antialiasing 选项创建一个QCheckBox 即可。
transformationsCheckBox = new QCheckBox(tr("&Transformations"));Transformations 选项意味着对坐标系进行操作,这会使渲染出的形状看起来仿佛在三维空间中进行了旋转。
我们使用QPainter::translate()、QPainter::rotate()和QPainter::scale()函数来实现这一功能,该功能在主应用程序窗口中通过一个简单的QCheckBox 来体现。
connect(shapeComboBox, &QComboBox::activated,
this, &Window::shapeChanged);
connect(penWidthSpinBox, &QSpinBox::valueChanged,
this, &Window::penChanged);
connect(penStyleComboBox, &QComboBox::activated,
this, &Window::penChanged);
connect(penCapComboBox, &QComboBox::activated,
this, &Window::penChanged);
connect(penJoinComboBox, &QComboBox::activated,
this, &Window::penChanged);
connect(brushStyleComboBox, &QComboBox::activated,
this, &Window::brushChanged);
connect(antialiasingCheckBox, &QAbstractButton::toggled,
renderArea, &RenderArea::setAntialiased);
connect(transformationsCheckBox, &QAbstractButton::toggled,
renderArea, &RenderArea::setTransformed);然后,我们使用静态函数QObject::connect()将参数控件与其关联的槽进行连接,从而确保每当用户更改形状或其他任何参数时,RenderArea 控件都会随之更新。
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"));
}最后,我们将各种控件添加到布局中,并调用shapeChanged() 、penChanged() 和brushChanged() 这三个插槽来初始化应用程序。此外,我们还启用了抗锯齿功能。
void Window::shapeChanged()
{
RenderArea::Shape shape = RenderArea::Shape(shapeComboBox->itemData(
shapeComboBox->currentIndex(), IdRole).toInt());
renderArea->setShape(shape);
}每当用户更改当前选中的形状时,都会调用shapeChanged() 槽函数。
首先,我们使用QComboBox::itemData() 函数获取用户选择的形状。该函数返回下拉列表中指定索引处指定角色的数据。我们使用QComboBox::currentIndex() 获取形状的索引,而角色由Qt::ItemDataRole 枚举定义;IdRole 是Qt::UserRole 的别名。
请注意,Qt::UserRole 仅是首个可用于特定应用场景的角色。若需在同一索引下存储不同数据,只需递增Qt::UserRole 的值即可使用不同角色,例如:'Qt::UserRole + 1' 和 'Qt::UserRole + 2'。 不过,为每个角色指定专属名称是一种良好的编程习惯:'myFirstRole =Qt::UserRole + 1' 和 'mySecondRole =Qt::UserRole + 2'。尽管在此示例中我们仅需一个角色,但我们仍需在window.cpp 文件的开头添加以下代码行。
const int IdRole = Qt::UserRole;QComboBox::itemData() 函数将数据作为QVariant 返回,因此我们需要将数据强制转换为RenderArea::Shape 。如果给定角色没有数据,该函数将返回QMetaType::UnknownType 。
最后,我们调用RenderArea::setShape() 槽来更新RenderArea 控件。
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));
}每当用户更改任何画笔参数时,我们都会调用penChanged() 槽。我们再次使用QComboBox::itemData() 函数获取参数,然后调用RenderArea::setPen() 槽来更新RenderArea 控件。
每当用户更改画笔参数时,就会调用 brushChanged() 槽;我们与之前一样,使用QComboBox::itemData() 函数来获取该参数。
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);如果 brush 参数是渐变填充,则需要采取特殊操作。
QGradient QBrush QGradient QLinearGradient 、 和 。QRadialGradient QConicalGradient
因此,如果画笔样式为Qt::LinearGradientPattern ,我们首先创建一个QLinearGradient 对象,其插值区域位于作为构造函数参数传递的坐标之间。位置使用逻辑坐标指定。然后,我们使用QGradient::setColorAt()函数设置渐变的颜色。 颜色通过停点来定义,每个停点由一个位置(介于0和1之间)和一个QColor 组成。停点集描述了渐变区域应如何填充。一个渐变可以包含任意数量的停点。
最后,我们调用RenderArea::setBrush() 插槽,使用QLinearGradient 对象更新RenderArea 控件的画笔。
} 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);在处理Qt::RadialGradientPattern 和Qt::ConicalGradientPattern 时,采用的操作模式与QLinearGradient 类似。
唯一的区别在于传递给构造函数的参数:对于QRadialGradient 构造函数,第一个参数是圆心,第二个参数是径向渐变的半径。第三个参数是可选的,但可用于定义圆内渐变的焦点(默认焦点为圆心)。 关于QConicalGradient 构造函数,第一个参数指定锥形的中心,第二个参数指定插值的起始角度。
} else if (style == Qt::TexturePattern) {
renderArea->setBrush(QBrush(QPixmap(":/images/brick.png")));如果画笔样式为Qt::TexturePattern ,我们会从QPixmap 创建一个QBrush 。然后调用RenderArea::setBrush() 插槽,用新创建的画笔更新RenderArea 控件。
否则,我们只需创建一个具有给定样式和绿色颜色的画笔,然后调用RenderArea::setBrush() 槽来使用新创建的画笔更新RenderArea 控件。
RenderArea 类定义
RenderArea 类继承自QWidget ,并使用QPainter 渲染当前活动形状的多个副本。
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;
};首先,我们定义了一个公共的Shape 枚举,用于存储小部件可渲染的不同形状(即QPainter 可渲染的形状)。然后,我们重写了构造函数以及QWidget 的两个公共函数:minimumSizeHint()和sizeHint()。
我们还重写了QWidget::paintEvent() 函数,以便能够根据指定的参数绘制当前活动的图形。
我们声明了几个私有插槽:setShape() 插槽用于更改RenderArea 的形状,setPen() 和setBrush() 插槽用于修改控件的笔和画笔,而setAntialiased() 和setTransformed() 插槽则用于修改控件的相应属性。
RenderArea 类的实现
在构造函数中,我们初始化了控件的某些变量。
RenderArea::RenderArea(QWidget *parent)
: QWidget(parent)
{
shape = Polygon;
antialiased = false;
transformed = false;
pixmap.load(":/images/qt-logo.png");
setBackgroundRole(QPalette::Base);
setAutoFillBackground(true);
}我们将它的形状设置为Polygon ,将其抗锯齿属性设为false,并将一张图像加载到控件的pixmap变量中。最后,我们设置控件的背景角色,并根据控件的palette 定义用于渲染背景的画笔。QPalette::Base 通常为白色。
RenderArea 继承了QWidget 的sizeHint 属性,该属性保存了小部件的推荐尺寸。如果该属性的值是一个无效尺寸,则不推荐任何尺寸。
QWidget::sizeHint() 函数的默认实现会在小部件没有布局时返回无效尺寸,否则返回布局的首选尺寸。
我们对该函数的重写版本会返回一个宽度为 400 像素、高度为 200 像素的QSize 对象。
RenderArea 还继承了QWidget 的minimumSizeHint 属性,该属性保存了小部件的建议最小尺寸。同样,如果该属性的值是一个无效尺寸,则不建议任何尺寸。
QWidget::minimumSizeHint() 的默认实现会在小部件没有布局时返回无效尺寸,否则返回布局的最小尺寸。
我们对该函数的重写版本会返回一个宽度为 100 像素、高度为 100 像素的QSize 对象。
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();
}每当我们想要修改RenderArea 控件的形状、笔或画笔时,都会调用公共的setShape() 、setPen() 和setBrush() 槽。我们根据槽参数设置形状、笔或画笔,并调用QWidget::update()使更改在RenderArea 控件中显示出来。
QWidget::update() 槽不会立即触发重绘;而是会安排一个绘制事件,待 Qt 返回主事件循环时进行处理。
void RenderArea::setAntialiased(bool antialiased)
{
this->antialiased = antialiased;
update();
}
void RenderArea::setTransformed(bool transformed)
{
this->transformed = transformed;
update();
}通过setAntialiased() 和setTransformed() 槽,我们根据槽参数更改属性的状态,并调用QWidget::update() 槽,使这些更改在RenderArea 控件中显示出来。
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;然后,我们重新实现QWidget::paintEvent()函数。首先,我们需要创建绘制各种图形所需的图形对象。
我们创建了一个包含四个QPoint的向量。利用这个向量来渲染Points 、Polyline 和Polygon 这三个图形。接着,我们创建一个QRect ,在平面上定义一个矩形,将其用作除Path 和Pixmap 之外所有图形的边界矩形。
我们还创建了一个QPainterPath 。QPainterPath 类为绘图操作提供了一个容器,从而能够构建和复用图形形状。 绘图路径是由多个图形构建块(如矩形、椭圆、直线和曲线)组成的对象。有关QPainterPath 类的更多信息,请参阅“绘图路径”示例。在此示例中,我们将创建一个由一条直线和一条贝塞尔曲线组成的绘图路径。
此外,我们还定义了一个起始角度和一个弧长,这些参数将在绘制Arc 、Chord 和Pie 图形时使用。
QPainter painter(this);
painter.setPen(pen);
painter.setBrush(brush);
if (antialiased)
painter.setRenderHint(QPainter::Antialiasing, true);我们为RenderArea 控件创建一个QPainter ,并根据RenderArea 的笔和画笔设置绘图器的笔和画笔。如果勾选了Antialiasing 参数选项,我们还会设置绘图器的渲染提示。QPainter::Antialiasing 表示引擎应在可能的情况下对基元边缘进行抗锯齿处理。
for (int x = 0; x < width(); x += 100) {
for (int y = 0; y < height(); y += 100) {
QPainterStateGuard guard(&painter);
painter.translate(x, y);最后,我们渲染RenderArea 形状的多个副本。副本数量取决于RenderArea 小部件的大小,我们通过两个for 循环以及小部件的高度和宽度来计算它们的位置。
对于每个副本,我们首先通过实例化一个QPainterStateGuard 来保存当前绘制状态。然后,我们使用QPainter::translate()函数将坐标系平移到由for 循环中的变量确定的位置。如果省略此坐标系平移操作,则该形状的所有副本都将重叠渲染在RenderArea 控件的左上角。
if (transformed) {
painter.translate(50, 50);
painter.rotate(60.0);
painter.scale(0.6, 0.9);
painter.translate(-50, -50);
}如果勾选了Transformations 参数选项,我们会在使用QPainter::rotate()函数将坐标系顺时针旋转60度,并使用QPainter::scale()函数缩小其尺寸之前,对坐标系进行一次额外的平移。最后,我们将坐标系平移回旋转和缩放前的位置。
现在,在渲染该形状时,它将呈现出仿佛在三维空间中旋转的效果。
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);
}接下来,我们识别RenderArea 的形状,并使用相关的QPainter 绘图函数将其渲染出来:
- QPainter::drawLine(),
- QPainter::drawPoints(),
- QPainter::drawPolyline(),
- QPainter::drawPolygon(),
- QPainter::drawRect(),
- QPainter::drawRoundedRect(),
- QPainter::drawEllipse(),
- QPainter::drawArc(),
- QPainter::drawChord(),
- QPainter::drawPie(),
- QPainter::drawPath(),
- QPainter::drawText() 或
- QPainter::drawPixmap()
在开始渲染之前,我们保存了当前的绘图状态(将状态压入栈中)。 这样做的理由是,我们计算每个形状副本相对于坐标系中同一点的位置。当平移坐标系时,除非在开始平移过程之前保存当前绘制器状态,否则我们将无法保留该点的坐标信息。
}
}
painter.setRenderHint(QPainter::Antialiasing, false);
painter.setPen(palette().dark().color());
painter.setBrush(Qt::NoBrush);
painter.drawRect(QRect(0, 0, width() - 1, height() - 1));
}随后,当我们完成某个形状副本的渲染后,通过QPainterStateGuard 恢复原始绘图器状态,该方法会调用QPainter::restore()函数。这样,我们就能确保下一个形状副本将在正确的位置被渲染。
我们也可以使用QPainter::translate() 将坐标系还原,而无需保存绘图状态。但由于我们在平移坐标系的同时(当Transformation 参数选项被选中时),还会对坐标系进行旋转和缩放,因此最简单的解决方案是保存当前的绘图状态。
© 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.