利用QPainter的成员函数translate+rotate实现图片旋转

本文目的

在 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图片自转的效果。

实现鼠标移动图片的功能,可以按照以下步骤: 1. 创建一个Qt项目,选择“Qt Widgets Application”模板,命名为“MoveImage”。 2. 将要使用的图片文件放到项目目录下,例如命名为“image.jpg”。 3. 打开MainWindow类的头文件,添加以下代码: ``` #include <QMainWindow> #include <QPixmap> class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); protected: void paintEvent(QPaintEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; private: QPixmap m_pixmap; QPoint m_position; }; ``` 其中,m_pixmap表示要显示的图片,m_position表示图片的位置。 4. 打开MainWindow类的实现文件,添加以下代码: ``` #include "mainwindow.h" #include "ui_mainwindow.h" #include <QPainter> #include <QMouseEvent> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { m_pixmap.load(":/image/image.jpg"); } MainWindow::~MainWindow() { } void MainWindow::paintEvent(QPaintEvent* event) { QPainter painter(this); painter.drawPixmap(m_position, m_pixmap); } void MainWindow::mouseMoveEvent(QMouseEvent* event) { if (event->buttons() & Qt::LeftButton) { m_position = event->pos(); update(); } } ``` 其中,paintEvent函数用于绘制图片,mouseMoveEvent函数用于处理鼠标移动事件,如果鼠标左键按下,则更新图片位置,并调用update函数触发paintEvent函数重新绘制。 5. 在项目资源文件中,添加要使用的图片文件。 6. 编译并运行程序,可以看到图片随着鼠标移动而移动的效果。 注意:在实际开发中,需要考虑图片边界的判断,以防止图片移动出窗口范围。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值