本文目的
在 paintEvent() 函数里用 QPainter 将一张CD图片画在窗口的中心,并让其每 10ms 旋转 1°, 达到CD自转的效果。
一、Qt绘图系统理解
Qt绘图系统基于 QPainter、QPaintDevice 和 QPainEngine 三个类:
1.QPainter 用来执行绘制图像的操作,可以理解为一个画家;
2.QPaintDevice 是一个二维空间的抽象,这个二维空间可以让QPainter 在上面进行绘制,相当于画家绘画的空间平台;
3.QPaintEngine 提供了画家 Painter 在不同的设备上进行绘制的统一的接口。
QPaintEngine 类用在 QPainter 和 QPaintDevice 之间,通常情况下对开发人员而言它是透明的,我们无需重新定义 QPaintEngine ,除非需要自定义一个新设备。
综上我们可以知道,Qt 的绘图系统实际是 QPainter 在 QPainterDevice 上面绘图,而它们之间通过 QPaintEngine 进行通讯。
二、使用步骤
1.mainwindow.h
mainwindow.h 文件中定义了本次实验的子类内容,在 protected 修饰符下声明了从 QWidget 类继承而来的 virtual void paintEvent(QPaintEvent *event)函数;paintEvent()是父类 QWidget 的 protected 修饰符下的虚函数,这里我们也重写到子类的 protected 修饰符下。
从名字可以看出,paintEvent()是一个绘图事件的回调函数,并且该事件可以重写;在界面初始化或者界面刷新时paintEvent()绘图事件便会执行,也就是说paintEvent()绘图事件在构造对象实例化时会执行,在使用 update()刷新界面时也会执行。
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPainter>
#include <QPaintEvent>
#include <QTimer>
#define STACK_ALLOCATION 0
#define HEAP_ALLOCATION 1
#define MY_PAINTER_ALLOCATION HEAP_ALLOCATION
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
protected:
void paintEvent(QPaintEvent *);
private:
QTimer *pTimer;
int angle;
#if(MY_PAINTER_ALLOCATION == HEAP_ALLOCATION)
QPainter *pPainter;
QPixmap *pImage;
QRectF *pRectF;
#endif
private slots:
void TimerTimeOut();
};
#endif // MAINWINDOW_H
2.mainwindow.cpp
mainwindow.cpp文件中我们主要是实现paintEvent()函数,大致思路如下:
1.paintEvent()函数中我们需要指定一个画家QPainter;
2.使用 translate()函数平移坐标系统,因为系统默认左上角那个点是坐标系统原点,所以我们需要使用 translate()函数按照给定的偏移量平移坐标系统,这里我们的偏移量是让图片中点在画布中点时它到左上角那个点的向量偏移大小;
3.使用rotate()函数顺时针将坐标系统旋转1°,注意这里的坐标系统是平移后的新的坐标系统;
4.旋转完成后再次使用 translate()函数将坐标系统平移回左上角那个点,因为后面画家画图需要以最初那个坐标系统的原点为参考,在我们先前用 QRectF 指定的区域里进行绘画,所以需要把坐标系统平移回去;
5.使用drawImage()函数在指定的区域里绘画图片,该指定区域我们是以最初的那个坐标系统的原点作为参考,也就是窗口左上角那个点。
#include "mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
this->setGeometry(0, 0, 800, 480);
setPalette(QPalette(Qt::gray));
setAutoFillBackground(true);
pTimer = new QTimer(this);
angle = 0;
pTimer->start(10); /* 定时 10ms;每10ms刷新一次界面 */
#if(MY_PAINTER_ALLOCATION == HEAP_ALLOCATION)
pImage = new QPixmap();
pImage->load("/home/yxm/Qt_Program/YXM_Qt/02_Improve/03_QPainter_Example/Image/cd.png"); /* 加载图片 */
pRectF = new QRectF((this->width() - pImage->width()) / 2, \
(this->height() - pImage->height()) / 2, \
pImage->width(), pImage->height()); /* 画一个矩形,同时也是给要绘制的图片框选一个区域 */
#endif
connect(pTimer, SIGNAL(timeout()), this, SLOT(TimerTimeOut()));
}
MainWindow::~MainWindow()
{
}
void MainWindow::TimerTimeOut()
{
this->update(); /* 刷新界面 */
}
#if(MY_PAINTER_ALLOCATION == STACK_ALLOCATION)
void MainWindow::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); /* 设置抗锯齿,流畅转换 */
if(angle++ == 360)
angle = 0;
QPixmap image;
image.load("/home/yxm/Qt_Program/YXM_Qt/02_Improve/03_QPainter_Example/Image/cd.png"); /* 加载图片 */
/*
* QRectF(qreal x, qreal y, qreal width, qreal height)
* this->width() is 800 pixel;
* this->height() is 480 pixel;
* image.width() is 200 pixel;
* image.height() is 200 pixel;
* (this->width() - image.width()) / 2 is 300 pixel;
* (this->height() - image.height()) / 2 is 140 pixel;
*/
QRectF rect((this->width() - image.width()) / 2, \
(this->height() - image.height()) / 2, \
image.width(), image.height()); /* 画一个矩形,同时也是给要绘制的图片框选一个区域 */
/*
* Translates the coordinate system by the vector (dx, dy).
* Translates the coordinate system by the given offset; i.e. the given offset is added to points.
* 将坐标系平移到给定的偏移量;例如,给定的偏移量被添加到点上。
* rect.x() + rect.width() / 2 is 400 pixel; 300 + 100
* rect.y() + rect.height() / 2 is 240 pixel; 140 + 100
*/
painter.translate(0 + rect.x() + rect.width() / 2, 0 + rect.y() + rect.height() / 2);
painter.rotate(angle); /* 顺时针把坐标系统旋转1° */
painter.translate(0 - (rect.x() + rect.width() / 2), 0 - (rect.y() + rect.height() / 2)); /* 将坐标系移动回画布左上角的原点 */
painter.drawImage(rect, image.toImage(), image.rect());
painter.drawRect(rect.toRect());
}
#elif(MY_PAINTER_ALLOCATION == HEAP_ALLOCATION)
void MainWindow::paintEvent(QPaintEvent *)
{
pPainter = new QPainter(this);
if(angle++ == 360)
angle = 0;
pPainter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); /* 设置抗锯齿,流畅转换 */
/*
* Translates the coordinate system by the vector (dx, dy).
* Translates the coordinate system by the given offset; i.e. the given offset is added to points.
* 将坐标系平移到给定的偏移量;例如,给定的偏移量被添加到点上。
* rect.x() + rect.width() / 2 is 400 pixel; 300 + 100
* rect.y() + rect.height() / 2 is 240 pixel; 140 + 100
*/
pPainter->translate(0 + pRectF->x() + pRectF->width() / 2, 0 + pRectF->y() + pRectF->height() / 2);
/*
* Rotates the coordinate system clockwise. The given angle parameter is in degree
* 通过给定的角度数,顺时针旋转坐标系统
*/
pPainter->rotate(angle); /* 顺时针把坐标系统旋转1° */
pPainter->translate(0 - (pRectF->x() + pRectF->width() / 2), 0 - (pRectF->y() + pRectF->height() / 2)); /* 将坐标系移动回画布左上角的原点 */
pPainter->drawImage((const QRectF)*pRectF, pImage->toImage(), pImage->rect());
pPainter->drawRect(pRectF->toRect());
delete pPainter;
}
#endif
总结
正常:translate设置新的坐标系统原点 + rotate顺时针旋转新坐标系统 + translate把坐标系统原点设置回左上角那个点 + 绘图
这个流程绘制的图是正常的,程序运行后可以看到CD图片顺时针自转。
正常运行
不正常:translate设置新的坐标系统原点 + rotate顺时针旋转新坐标系统 + 绘图
这个流程绘制的图是不正常的,运行后可以看到CD图片会绕着(X’,Y’)这个新的坐标系统的原点转圈圈。
不正常运行
以上是今天要记录的关于QPainter绘画系统的内容,本文通过translate函数平移坐标系统 + 利用rotate函数旋转图片 + 利用drawImage在旋转后的坐标系统里画图,从而实现CD图片自转的效果。