示例1:绘制3个五角星
// Widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
protected:
// 先在Widget.h里声明paintEvent()函数的重载,权限protected
void paintEvent(QPaintEvent *event) override;
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
// Widget.cpp
#include <QPaintEvent>
#include <QPainter>
#include <math.h>
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
setPalette(QPalette(Qt::green)); // 设置窗口背景
setAutoFillBackground(true);
resize(600, 300); // 固定初始化窗口大小
}
Widget::~Widget()
{
delete ui;
}
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::TextAntialiasing);
// 生成五角星5个顶点的坐标,假设原点在五角星中心
qreal R = 100;
const qreal Pi = 3.14159;
qreal deg = Pi * 72 / 180;
QPoint points[5] = {
QPoint(R, 0), // 从x轴正半轴开始,x轴y轴方向是一般直角坐标系方向
QPoint(R * std::cos(deg), -R * std::sin(deg)),
QPoint(R * std::cos(2 * deg), -R * std::sin(2 * deg)),
QPoint(R * std::cos(3 * deg), -R * std::sin(3 * deg)),
QPoint(R * std::cos(4 * deg), -R * std::sin(4 * deg)),
};
// 设置字体
QFont font;
font.setPointSize(12);
font.setBold(true);
painter.setFont(font);
// 设置画笔
QPen penLine;
penLine.setWidth(2); // 线宽
penLine.setColor(Qt::blue); // 划线颜色
penLine.setStyle(Qt::SolidLine); // 线的类型
penLine.setCapStyle(Qt::FlatCap); // 线端点样式
penLine.setJoinStyle(Qt::BevelJoin); // 线的连接点样式
painter.setPen(penLine);
// 设置画刷
QBrush brush;
brush.setColor(Qt::yellow); // 画刷颜色
brush.setStyle(Qt::SolidPattern); // 画刷填充样式
painter.setBrush(brush);
// 设计绘制五角星的PainterPath,以便重复使用
QPainterPath starPath;
starPath.moveTo(points[0]);
starPath.lineTo(points[2]);
starPath.lineTo(points[4]);
starPath.lineTo(points[1]);
starPath.lineTo(points[3]);
starPath.closeSubpath(); // 闭合路径,最后一个点与第一个点相连
starPath.addText(points[0], font, "0");
starPath.addText(points[1], font, "1");
starPath.addText(points[2], font, "2");
starPath.addText(points[3], font, "3");
starPath.addText(points[4], font, "4");
// 绘图
painter.save(); // 保存坐标状态
painter.translate(100, 120); // 平移
painter.drawPath(starPath); // 画星星
painter.drawText(0, 0, "S1"); // 在坐标的(0, 0)点绘制
painter.restore(); // 恢复坐标状态
painter.translate(300, 120); // 平移
painter.scale(0.8, 0.8); // 缩放
painter.rotate(90); // 顺时针旋转90度
painter.drawPath(starPath); // 画星星
painter.drawText(0, 0, "S2");
painter.resetTransform(); // 复位所有坐标变换
painter.translate(500, 120); // 平移
painter.rotate(-145); // 逆时针旋转145度
painter.drawPath(starPath); // 画星星
painter.drawText(0, 0, "S3");
}
8.2.3 视口和窗口
视口表示绘图设备的任意一个矩形区域的物理坐标(就是真实的以像素为单位的坐标),可以只选取物理坐标的一个矩形区域用于绘图。默认情况下,视口等于绘图设备的整个矩形区。
窗口可以和视口是同一个矩形(注意!同一个 QPainter 对象里所 set 的窗口和视口代表了绘图设备的同一个矩形区域!只是你可以用窗口和视口两种不同的坐标表示方法来描述这块矩形。),只不过是用逻辑坐标定义的坐标系。窗口可以直接定义矩形区逻辑坐标范围。
定义视口可以使用 QPainter 的 setViewport() 函数,原型为:
void QPainter::setViewport(int x, int y, int width, int height)
示例:
painter.setViewport(50, 0, 200, 200);
定义窗口可以使用 QPainter 的 setWindow() 函数,其原型为:
void QPainter::setWindow(int x, int y, int widht, int height)
示例:定义一个窗口,窗口坐标的中心在正方形中心,并设置正方形的逻辑边长为100
painter.setWindow(-50, -50, 100, 100);
使用窗口坐标的优点是,只需按照窗口坐标定义来绘图,而不用管实际的屋里坐标范围的大小。例如在一个固定边长100的正方形窗口内绘图,当实际绘图设备大小变化时,绘制的图形会自动变化大小。这样,就可以将绘图功能与绘图设备隔离开来,使得绘图功能适用于不同大小、不同类型的设备。
视口和窗口的使用实例:
// widget.cpp
#include <QPainter>
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
int W = width();
int H = height();
int side = qMin(W, H); // 取长和宽中的较小值
QRect rect((W - side) / 2, (H - side) / 2, side, side); // 定义一个用于建立 viewport 的矩形区
painter.drawRect(rect); // Viewport 的矩形区域
painter.setViewport(rect); // 设置Viewport
painter.setWindow(-100, -100, 200, 200); // 设置窗口大小,逻辑坐标
painter.setRenderHint(QPainter::Antialiasing);
// 设置画笔
QPen pen;
pen.setWidth(1);
pen.setColor(Qt::red);
pen.setStyle(Qt::SolidLine);
painter.setPen(pen);
for(int i = 0; i < 36; ++i)
{
painter.drawEllipse(QPoint(50, 0), 50, 50);
painter.rotate(10);
}
}
其中,painter.drawEllipse(QPoint(50, 0), 50, 50); 在 painter 对象设置的窗口中,以(50,0)为圆心,画了一个长轴50、短轴50的椭圆(圆形)。painter.rotate(10); 把坐标系顺时针旋转了10度。
8.2.4 绘图叠加的效果