本节对应的视频讲解:B_站_视_频
https://www.bilibili.com/video/BV1x14y1J7rn
完成了界面布局,以及添加了初始化数据,就可以开始真正绘制图形了
本节讲解如何绘制点、线
1. 基本点线的绘制
1.1 为 cboShape 关联信号槽
首先,在 widget.h 中,声明一个槽函数
class Widget : public QWidget
{
private slots:
void shapeChanged();
};
然后,在 widget.cpp 中,实现这个槽函数,先空着,一会再来实现
void Widget::shapeChanged()
{
}
接着,在 widget.cpp 的构造中,关联信号和槽
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget)
{
ui->setupUi(this);
// 5. 信号槽
// 形状
connect(ui->cboShape, SIGNAL(activated(QString)), this, SLOT(shapeChanged())); // 槽的参数可以比信号少
}
1.2 实现 shapeChanged 槽函数
shapeChanged 槽函数会调用 PaintWidget 类的方法完成绘制,如下:
void Widget::shapeChanged()
{
int index = ui->cboShape->currentIndex();
Shape shape = (Shape)ui->cboShape->itemData(index).toInt();
ui->paintWidget->setShape(shape);
}
1.3 实现 setShape 函数
接下来,在 PaintWidget 类中,来实现这个 setShape 函数
首先,在 PaintWidget.h 中声明一个该函数,并声明一个成员变量 mShape
class PaintWidget : public QWidget
{
public slots:
void setShape(Shape shape);
private:
Shape mShape;
};
然后,实现这个 setShape 函数
void PaintWidget::setShape(Shape shape)
{
this->mShape = shape;
update();
}
这里调用 update 函数,会自动调用该类的 paintEvent 函数,paintEvent 是重载的父类的
1.4 重载 paintEvent
接下来,就实现这个 paintEvent 函数,来完成真正的绘制
paintEvent 是什么?
paintEvent 是一个绘图函数,它是并不是由我们手动调用的, 而是由系统自动调用的。
调用时机:
-
窗口大小变化时
比如最大化、最小化、缩放窗口时,系统都会调用该函数
-
手动调用
update手动调用 update 函数,同样会触发系统调用 paintEvent,从而完成界面的刷新。
首先,在 paintwidget.h 中声明 paintEvent
class PaintWidget : public QWidget
{
protected:
void paintEvent(QPaintEvent *event) override;
};
然后,在 paintwidget.cpp 中实现它
#include <QPainter>
void PaintWidget::paintEvent(QPaintEvent *event)
{
static const QPoint points[4] = {
QPoint(10, 80), //
QPoint(20, 10), //
QPoint(80, 30), //
QPoint(90, 70) //
};
// 创建画家类的对象,参数为绘图设备。指定为this,表示在当前窗口绘制
QPainter painter(this);
switch ( mShape ) {
// 点、线
case _Point:
painter.drawPoints(points, 4);
break;
case _Line:
painter.drawLine(points[0], points[2]);
break;
}
}
最后,在 widget.cpp 的构造中,手动调用一个 shapeChanged 槽函数,触发一下绘制
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget)
{
ui->setupUi(this);
shapeChanged();
}
这样就完成了点、线的绘制,如下:


2. 循环绘制
由于在 Qt 中,坐标原点默认在左上角,因此以上将点、线绘制在了 PaintWidget 的左上角
接下来实现:将绘制的点、线铺满整个 PaintWidget,也就是如下效果:


该如何实现呢?
以绘制线为例,由于向右 x 轴坐标增加,向下 y 轴坐标增加。因此,可以依次计算出每根线的两个端点的坐标,如下:

2.1 平移 translate
这种方式肯定可以实现的,不过 QPainter 为我们提供了更加简便的方法:
平移-translate:它的作用是,移动绘图的坐标原点
有了平移的方法,就可以直接移动绘图的坐标原点,这样线段的两个端点的坐标就不需要改变,如下:

绘制线段a时:绘图的坐标原点为A(0, 0),此时,两个线段的两个端点为 (10, 80)、(80, 30)
绘制线段b时:先将绘图的坐标原点移动到B(100, 0),此时,两个线段的两个端点仍然为 (10, 80)、(80, 30)
绘制线段c时:先将绘图的坐标原点移动到C(0, 100),此时,两个线段的两个端点仍然为 (10, 80)、(80, 30)
2.2 QPainter 的保存和恢复
save 和 restore 用于保存和恢复 QPainter 的状态
状态包括画笔的状态,画刷的状态,以及绘图的坐标原点等
save 和 restore是一一对应的,要成对出现。一个 save 就要对应一个 restore
2.3 具体实现
具体代码实现如下:
void PaintWidget::paintEvent(QPaintEvent *event)
{
static const QPoint points[4] = {
QPoint(10, 80), //
QPoint(20, 10), //
QPoint(80, 30), //
QPoint(90, 70) //
};
// 创建画家类的对象,参数为绘图设备。指定为this,表示在当前窗口绘制
QPainter painter(this);
for ( int x = 0; x < width(); x += 100 ) {
for ( int y = 0; y < height(); y += 100 ) {
// 先保存原来的状态
// (0,0),(0,100),(0,200),(0,300)...(0,600)
// (100,0),(100,100),(100,200),(100,300)...(100,600)
// ...
// (800,0),(800,100),(800,200),(800,300)...(800,600)
painter.save();
painter.translate(x, y); // translate 修改的是坐标系
switch ( mShape ) {
// 点、线
case _Point:
painter.drawPoints(points, 4);
break;
case _Line:
painter.drawLine(points[0], points[2]);
break;
}
painter.restore();
}
}
}
说明:
由于 save 和 restore要成对出现。一个 save 就要对应一个 restore
因此把它们都放在第二个 for 循环中,这样才能保证,它们的一一对应。
经过以上修改,就可以循环绘制点、线了!












![[附源码]计算机毕业设计的家政服务平台Springboot程序](https://img-blog.csdnimg.cn/26e5a590d1b24016b7f2be2e9f67324b.png)






