在 Qt5 中绘图
最后修改于 2023 年 10 月 18 日
在 Qt5 C++ 编程教程的这一部分中,我们将进行一些绘图。
当我们在 Qt5 中进行一些绘图时,QPainter 类起着重要作用。 绘图是在响应 paintEvent 方法时使用 QPainter 类完成的。
线条
在第一个例子中,我们在窗口的客户区绘制一些线条。
#pragma once
#include <QWidget>
class Lines : public QWidget {
public:
Lines(QWidget *parent = 0);
protected:
void paintEvent(QPaintEvent *event);
void drawLines(QPainter *qp);
};
这是头文件。
#include <QPainter>
#include "lines.h"
Lines::Lines(QWidget *parent)
: QWidget(parent)
{ }
void Lines::paintEvent(QPaintEvent *e) {
Q_UNUSED(e);
QPainter qp(this);
drawLines(&qp);
}
void Lines::drawLines(QPainter *qp) {
QPen pen(Qt::black, 2, Qt::SolidLine);
qp->setPen(pen);
qp->drawLine(20, 40, 250, 40);
pen.setStyle(Qt::DashLine);
qp->setPen(pen);
qp->drawLine(20, 80, 250, 80);
pen.setStyle(Qt::DashDotLine);
qp->setPen(pen);
qp->drawLine(20, 120, 250, 120);
pen.setStyle(Qt::DotLine);
qp->setPen(pen);
qp->drawLine(20, 160, 250, 160);
pen.setStyle(Qt::DashDotDotLine);
qp->setPen(pen);
qp->drawLine(20, 200, 250, 200);
QVector<qreal> dashes;
qreal space = 4;
dashes << 1 << space << 5 << space;
pen.setStyle(Qt::CustomDashLine);
pen.setDashPattern(dashes);
qp->setPen(pen);
qp->drawLine(20, 240, 250, 240);
}
我们在窗口上绘制六条线;每条线都有不同的笔样式。
void Lines::paintEvent(QPaintEvent *e) {
Q_UNUSED(e);
QPainter qp(this);
drawLines(&qp);
}
当更新小部件时,会调用 paintEvent。 它是我们创建 QPainter 对象并进行绘图的地方。 由于我们没有使用 QPaintEvent 对象,因此我们使用 Q_UNUSED 宏来抑制编译器警告。 实际的绘图委托给 drawLines 方法。
QPen pen(Qt::black, 2, Qt::SolidLine); qp->setPen(pen);
我们创建一个 QPen 对象。 笔是实心的,2 像素粗,并且是黑色的。 笔用于绘制线条和形状的轮廓。 使用 setPen 方法将笔设置为画笔对象。
qp->drawLine(20, 40, 250, 40);
drawLine 方法绘制一条线。 这四个参数是窗口上两个点的坐标。
pen.setStyle(Qt::DashLine);
QPen 线的 setStyle 方法设置笔样式 - Qt::DashLine。
#include <QApplication>
#include "lines.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
Lines window;
window.resize(280, 270);
window.setWindowTitle("Lines");
window.show();
return app.exec();
}
这是主文件。
颜色
颜色是表示红色、绿色和蓝色 (RGB) 强度值组合的对象。 有效的 RGB 值在 0 到 255 的范围内。在下面的示例中,我们绘制了九个用九种不同颜色填充的矩形。
#pragma once
#include <QWidget>
class Colours : public QWidget {
public:
Colours(QWidget *parent = 0);
protected:
void paintEvent(QPaintEvent *e);
private:
void doPainting();
};
这是头文件。
#include <QPainter>
#include "colours.h"
Colours::Colours(QWidget *parent)
: QWidget(parent)
{ }
void Colours::paintEvent(QPaintEvent *e) {
Q_UNUSED(e);
doPainting();
}
void Colours::doPainting() {
QPainter painter(this);
painter.setPen(QColor("#d4d4d4"));
painter.setBrush(QBrush("#c56c00"));
painter.drawRect(10, 15, 90, 60);
painter.setBrush(QBrush("#1ac500"));
painter.drawRect(130, 15, 90, 60);
painter.setBrush(QBrush("#539e47"));
painter.drawRect(250, 15, 90, 60);
painter.setBrush(QBrush("#004fc5"));
painter.drawRect(10, 105, 90, 60);
painter.setBrush(QBrush("#c50024"));
painter.drawRect(130, 105, 90, 60);
painter.setBrush(QBrush("#9e4757"));
painter.drawRect(250, 105, 90, 60);
painter.setBrush(QBrush("#5f3b00"));
painter.drawRect(10, 195, 90, 60);
painter.setBrush(QBrush("#4c4c4c"));
painter.drawRect(130, 195, 90, 60);
painter.setBrush(QBrush("#785f36"));
painter.drawRect(250, 195, 90, 60);
}
我们绘制了九个具有不同颜色填充的矩形。 矩形的轮廓是灰色的。
painter.setBrush(QBrush("#c56c00"));
painter.drawRect(10, 15, 90, 60);
QBrush 类定义了由 QPainter 绘制的形状的填充图案。 drawRect 方法绘制一个矩形。 它绘制一个矩形,其左上角位于 x, y 点,并具有给定的宽度和高度。 我们使用十六进制表示法来指定颜色值。
#include <QApplication>
#include "colours.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
Colours window;
window.resize(360, 280);
window.setWindowTitle("Colours");
window.show();
return app.exec();
}
这是主文件。
图案
以下编程代码示例类似于上一个。 这一次我们用各种预定义的图案填充矩形。
#pragma once
#include <QWidget>
class Patterns : public QWidget {
public:
Patterns(QWidget *parent = 0);
protected:
void paintEvent(QPaintEvent *e);
private:
void doPainting();
};
这是头文件。
#include <QApplication>
#include <QPainter>
#include "patterns.h"
Patterns::Patterns(QWidget *parent)
: QWidget(parent)
{ }
void Patterns::paintEvent(QPaintEvent *e) {
Q_UNUSED(e);
doPainting();
}
void Patterns::doPainting() {
QPainter painter(this);
painter.setPen(Qt::NoPen);
painter.setBrush(Qt::HorPattern);
painter.drawRect(10, 15, 90, 60);
painter.setBrush(Qt::VerPattern);
painter.drawRect(130, 15, 90, 60);
painter.setBrush(Qt::CrossPattern);
painter.drawRect(250, 15, 90, 60);
painter.setBrush(Qt::Dense7Pattern);
painter.drawRect(10, 105, 90, 60);
painter.setBrush(Qt::Dense6Pattern);
painter.drawRect(130, 105, 90, 60);
painter.setBrush(Qt::Dense5Pattern);
painter.drawRect(250, 105, 90, 60);
painter.setBrush(Qt::BDiagPattern);
painter.drawRect(10, 195, 90, 60);
painter.setBrush(Qt::FDiagPattern);
painter.drawRect(130, 195, 90, 60);
painter.setBrush(Qt::DiagCrossPattern);
painter.drawRect(250, 195, 90, 60);
}
我们绘制了九个具有各种笔刷图案的矩形。
painter.setBrush(Qt::HorPattern); painter.drawRect(10, 15, 90, 60);
我们绘制一个具有特定图案的矩形。 Qt::HorPattern 是一个用于创建水平线图案的常量。
#include <QDesktopWidget>
#include <QApplication>
#include "patterns.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
Patterns window;
window.resize(350, 280);
window.setWindowTitle("Patterns");
window.show();
return app.exec();
}
这是主文件。
透明矩形
透明性是指能够透过材料看到事物的质量。 理解透明性的最简单方法是想象一块玻璃或水。 从技术上讲,光线可以穿过玻璃,这样我们就可以看到玻璃后面的物体。
在计算机图形学中,我们可以使用 alpha 合成来实现透明效果。 alpha 合成是将图像与背景组合以创建部分透明外观的过程。 合成过程使用 alpha 通道。
#pragma once
#include <QWidget>
class TransparentRectangles : public QWidget {
public:
TransparentRectangles(QWidget *parent = 0);
protected:
void paintEvent(QPaintEvent *event);
void doPainting();
};
这是头文件。
#include <QApplication>
#include <QPainter>
#include <QPainterPath>
#include "transparent_rectangles.h"
TransparentRectangles::TransparentRectangles(QWidget *parent)
: QWidget(parent)
{ }
void TransparentRectangles::paintEvent(QPaintEvent *e) {
Q_UNUSED(e);
doPainting();
}
void TransparentRectangles::doPainting() {
QPainter painter(this);
for (int i=1; i<=11; i++) {
painter.setOpacity(i*0.1);
painter.fillRect(50*i, 20, 40, 40, Qt::darkGray);
}
}
该示例绘制了十个具有不同透明度级别的矩形。
painter.setOpacity(i*0.1);
setOpacity 方法设置画笔的不透明度。 值应在 0.0 到 1.0 范围内,其中 0.0 表示完全透明,1.0 表示完全不透明。
#include <QDesktopWidget>
#include <QApplication>
#include "transparent_rectangles.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
TransparentRectangles window;
window.resize(630, 90);
window.setWindowTitle("Transparent rectangles");
window.show();
return app.exec();
}
这是主文件。
甜甜圈
在下面的示例中,我们创建一个甜甜圈形状。
#pragma once
#include <QWidget>
class Donut : public QWidget {
public:
Donut(QWidget *parent = 0);
protected:
void paintEvent(QPaintEvent *e);
private:
void doPainting();
};
这是头文件。
#include <QApplication>
#include <QPainter>
#include "donut.h"
Donut::Donut(QWidget *parent)
: QWidget(parent)
{ }
void Donut::paintEvent(QPaintEvent *e) {
Q_UNUSED(e);
doPainting();
}
void Donut::doPainting() {
QPainter painter(this);
painter.setPen(QPen(QBrush("#535353"), 0.5));
painter.setRenderHint(QPainter::Antialiasing);
int h = height();
int w = width();
painter.translate(QPoint(w/2, h/2));
for (qreal rot=0; rot < 360.0; rot+=5.0 ) {
painter.drawEllipse(-125, -40, 250, 80);
painter.rotate(5.0);
}
}
“甜甜圈”是一种高级的几何形状,类似于这种食物。 我们通过绘制 72 个旋转的椭圆来创建它。
painter.setRenderHint(QPainter::Antialiasing);
我们以抗锯齿模式绘画。 渲染将具有更高的质量。
int h = height(); int w = width(); painter.translate(QPoint(w/2, h/2));
这些行将坐标系的开始移到窗口的中间。 默认情况下,它位于 0, 0 点。 换句话说,在窗口的左上角。 通过移动坐标系,绘图将变得更加容易。
for (qreal rot=0; rot < 360.0; rot+=5.0 ) {
painter.drawEllipse(-125, -40, 250, 80);
painter.rotate(5.0);
}
在这个 for 循环中,我们绘制 72 个旋转的椭圆。
#include <QDesktopWidget>
#include <QApplication>
#include "donut.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
Donut window;
window.resize(350, 280);
window.setWindowTitle("Donut");
window.show();
return app.exec();
}
这是主文件。
形状
Qt5 绘图 API 可以绘制各种形状。 以下编程代码示例显示了其中一些。
#pragma once
#include <QWidget>
class Shapes : public QWidget {
public:
Shapes(QWidget *parent = 0);
protected:
void paintEvent(QPaintEvent *e);
private:
void doPainting();
};
这是头文件。
#include <QApplication>
#include <QPainter>
#include <QPainterPath>
#include "shapes.h"
Shapes::Shapes(QWidget *parent)
: QWidget(parent)
{ }
void Shapes::paintEvent(QPaintEvent *e) {
Q_UNUSED(e);
doPainting();
}
void Shapes::doPainting() {
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(QPen(QBrush("#888"), 1));
painter.setBrush(QBrush(QColor("#888")));
QPainterPath path1;
path1.moveTo(5, 5);
path1.cubicTo(40, 5, 50, 50, 99, 99);
path1.cubicTo(5, 99, 50, 50, 5, 5);
painter.drawPath(path1);
painter.drawPie(130, 20, 90, 60, 30*16, 120*16);
painter.drawChord(240, 30, 90, 60, 0, 16*180);
painter.drawRoundRect(20, 120, 80, 50);
QPolygon polygon({QPoint(130, 140), QPoint(180, 170), QPoint(180, 140),
QPoint(220, 110), QPoint(140, 100)});
painter.drawPolygon(polygon);
painter.drawRect(250, 110, 60, 60);
QPointF baseline(20, 250);
QFont font("Georgia", 55);
QPainterPath path2;
path2.addText(baseline, font, "Q");
painter.drawPath(path2);
painter.drawEllipse(140, 200, 60, 60);
painter.drawEllipse(240, 200, 90, 60);
}
我们绘制了九个不同的形状。
QPainterPath path1; path1.moveTo(5, 5); path1.cubicTo(40, 5, 50, 50, 99, 99); path1.cubicTo(5, 99, 50, 50, 5, 5); painter.drawPath(path1);
QPainterPath 是一个用于创建复杂形状的对象。 我们用它来绘制贝塞尔曲线。
painter.drawPie(130, 20, 90, 60, 30*16, 120*16); painter.drawChord(240, 30, 90, 60, 0, 16*180); painter.drawRoundRect(20, 120, 80, 50);
这些代码行绘制一个饼图、一个弦和一个圆角矩形。
QPolygon polygon({QPoint(130, 140), QPoint(180, 170), QPoint(180, 140),
QPoint(220, 110), QPoint(140, 100)});
painter.drawPolygon(polygon);
在这里,我们使用 drawPolygon 方法绘制一个多边形。 多边形由五个点组成。
QPointF baseline(20, 250);
QFont font("Georgia", 55);
QPainterPath path2;
path2.addText(baseline, font, "Q");
painter.drawPath(path2);
Qt5 允许基于字体字符创建路径。
painter.drawEllipse(140, 200, 60, 60); painter.drawEllipse(240, 200, 90, 60);
drawEllipse 绘制一个椭圆和一个圆。 圆是椭圆的特殊情况。 参数是矩形开始的 x 和 y 坐标以及椭圆的边界矩形的宽度和高度。
#include <QDesktopWidget>
#include <QApplication>
#include "shapes.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
Shapes window;
window.resize(350, 280);
window.setWindowTitle("Shapes");
window.show();
return app.exec();
}
这是示例的主文件。
渐变
在计算机图形学中,渐变是从亮到暗或从一种颜色到另一种颜色的阴影的平滑混合。 在 2D 绘图程序和绘图程序中,渐变用于创建彩色背景和特殊效果,以及模拟光线和阴影。
以下代码示例展示了如何创建线性渐变。
#pragma once
#include <QWidget>
class LinearGradients : public QWidget {
public:
LinearGradients(QWidget *parent = 0);
protected:
void paintEvent(QPaintEvent *e);
private:
void doPainting();
};
这是头文件。
#include <QApplication>
#include <QPainter>
#include "linear_gradients.h"
LinearGradients::LinearGradients(QWidget *parent)
: QWidget(parent)
{ }
void LinearGradients::paintEvent(QPaintEvent *e) {
Q_UNUSED(e);
doPainting();
}
void LinearGradients::doPainting() {
QPainter painter(this);
QLinearGradient grad1(0, 20, 0, 110);
grad1.setColorAt(0.1, Qt::black);
grad1.setColorAt(0.5, Qt::yellow);
grad1.setColorAt(0.9, Qt::black);
painter.fillRect(20, 20, 300, 90, grad1);
QLinearGradient grad2(0, 55, 250, 0);
grad2.setColorAt(0.2, Qt::black);
grad2.setColorAt(0.5, Qt::red);
grad2.setColorAt(0.8, Qt::black);
painter.fillRect(20, 140, 300, 100, grad2);
}
在代码示例中,我们绘制了两个矩形,并用线性渐变填充它们。
QLinearGradient grad1(0, 20, 0, 110);
QLinearGradient 构造一个线性渐变,其插值区域位于作为参数提供的两个点之间。
grad1.setColorAt(0.1, Qt::black); grad1.setColorAt(0.5, Qt::yellow); grad1.setColorAt(0.9, Qt::black);
渐变中的颜色使用停止点定义。 setColorAt 在给定位置使用给定颜色创建停止点。
painter.fillRect(20, 20, 300, 90, grad1);
我们用渐变填充矩形。
#include <QApplication>
#include "linear_gradients.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
LinearGradients window;
window.resize(350, 260);
window.setWindowTitle("Linear gradients");
window.show();
return app.exec();
}
这是主文件。
径向渐变
径向渐变是两种颜色或颜色阴影在两个圆之间的混合。
#pragma once
#include <QWidget>
class RadialGradient : public QWidget {
public:
RadialGradient(QWidget *parent = 0);
protected:
void paintEvent(QPaintEvent *e);
private:
void doPainting();
};
这是头文件。
#include <QApplication>
#include <QPainter>
#include "radial_gradient.h"
RadialGradient::RadialGradient(QWidget *parent)
: QWidget(parent)
{ }
void RadialGradient::paintEvent(QPaintEvent *e) {
Q_UNUSED(e);
doPainting();
}
void RadialGradient::doPainting() {
QPainter painter(this);
int h = height();
int w = width();
QRadialGradient grad1(w/2, h/2, 80);
grad1.setColorAt(0, QColor("#032E91"));
grad1.setColorAt(0.3, Qt::white);
grad1.setColorAt(1, QColor("#032E91"));
painter.fillRect(0, 0, w, h, grad1);
}
该示例创建了一个径向渐变;渐变从窗口的中心扩散。
QRadialGradient grad1(w/2, h/2, 80);
QRadialGradient 创建一个径向渐变;它在焦点和围绕它的圆上的端点之间插值颜色。 参数是圆心的坐标和它的半径。 焦点位于圆的中心。
grad1.setColorAt(0, QColor("#032E91"));
grad1.setColorAt(0.3, Qt::white);
grad1.setColorAt(1, QColor("#032E91"));
setColorAt 方法定义彩色停止点。
painter.fillRect(0, 0, w, h, grad1);
整个窗口区域都填充了径向渐变。
#include <QApplication>
#include "radial_gradient.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
RadialGradient window;
window.resize(300, 250);
window.setWindowTitle("Radial gradient");
window.show();
return app.exec();
}
这是主文件。
喷雾
在 C++ Qt5 教程的最后一个例子中,我们创建了一个蓬松的效果。 该示例显示了一个不断增长的居中文本,该文本逐渐从某个点淡出。 这是一个非常常见的效果,您经常可以在网络上的闪光动画中看到。
#pragma once
#include <QWidget>
class Puff : public QWidget {
public:
Puff(QWidget *parent = 0);
protected:
void paintEvent(QPaintEvent *event);
void timerEvent(QTimerEvent *event);
private:
int x;
qreal opacity;
int timerId;
void doPainting();
};
在头文件中,我们定义了两个事件处理程序:paint 事件处理程序和计时器处理程序。
#include <QPainter>
#include <QTimer>
#include <QTextStream>
#include "puff.h"
Puff::Puff(QWidget *parent)
: QWidget(parent) {
x = 1;
opacity = 1.0;
timerId = startTimer(15);
}
void Puff::paintEvent(QPaintEvent *e) {
Q_UNUSED(e);
doPainting();
}
void Puff::doPainting() {
QPainter painter(this);
QTextStream out(stdout);
QString text = "ZetCode";
painter.setPen(QPen(QBrush("#575555"), 1));
QFont font("Courier", x, QFont::DemiBold);
QFontMetrics fm(font);
int textWidth = fm.width(text);
painter.setFont(font);
if (x > 10) {
opacity -= 0.01;
painter.setOpacity(opacity);
}
if (opacity <= 0) {
killTimer(timerId);
out << "timer stopped" << endl;
}
int h = height();
int w = width();
painter.translate(QPoint(w/2, h/2));
painter.drawText(-textWidth/2, 0, text);
}
void Puff::timerEvent(QTimerEvent *e) {
Q_UNUSED(e);
x += 1;
repaint();
}
这是 puff.cpp 文件。
Puff::Puff(QWidget *parent)
: QWidget(parent) {
x = 1;
opacity = 1.0;
timerId = startTimer(15);
}
在构造函数中,我们启动计时器。 每 15 毫秒生成一个计时器事件。
void Puff::timerEvent(QTimerEvent *e) {
Q_UNUSED(e);
x += 1;
repaint();
}
在 timerEvent 内部,我们增加了字体大小并重新绘制小部件。
if (x > 10) {
opacity -= 0.01;
painter.setOpacity(opacity);
}
如果字体大小大于 10 磅,我们会逐渐降低不透明度;文本开始消失。
if (opacity <= 0) {
killTimer(timerId);
out << "timer stopped" << endl;
}
如果文本完全消失,我们停止计时器。
#include <QApplication>
#include "puff.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
Puff window;
window.resize(350, 280);
window.setWindowTitle("Puff");
window.show();
return app.exec();
}
这是主文件。
本章是关于在 Qt5 中绘图的。