【Qt】2D绘图之涂鸦板

00. 目录

01. 概述

结合前面所学内容,编写一个简单的涂鸦板程序。

02. 开发环境

Windows系统:Windows10

Qt版本:Qt5.15或者Qt6

03. 程序设计(基本功能)

3.1 新建Qt Widgets应用,项目名称为12Draw,基类这次还用QDialog,类名保持Dialog不变即可。

3.2 到dialog.h文件中,先添加头文件包含:#include ,然后添加几个事件处理函数的声明:

protected:
    void paintEvent(QPaintEvent *);
    void mousePressEvent(QMouseEvent *);
    void mouseMoveEvent(QMouseEvent *);
    void mouseReleaseEvent(QMouseEvent *);

3.3 再添加几个private私有变量定义:

private:
    QPixmap pix;
    QPoint lastPoint;
    QPoint endPoint;

因为在前面教程中函数里定义的QPixmap类对象是临时的,不能存储以前的值,为了实现保留上次的绘画结果,我们需要将其设为全局变量。后面两个QPoint变量存储鼠标指针的两个坐标值,我们需要用这两个坐标值完成绘图。

3.4 在dialog.cpp文件中,先添加头文件包含:#include ,然后在构造函数中添加如下初始代码:


    //设置窗口大小
    resize(600, 500);

    //设置画布大小和背景
    pix = QPixmap(200, 200);
    pix.fill(Qt::white);

3.5 在paintEvent添加如下代码

//绘图事件
void Dialog::paintEvent(QPaintEvent *)
{
    QPainter p(&pix);

    //根据鼠标指针前后两个位置绘制直线
    p.drawLine(lastPoint,  endPoint);

    //前一个坐标值等于后一个坐标值
    lastPoint = endPoint;

    QPainter painter(this);
    painter.drawPixmap(0, 0, pix);
}

3.6 在mousePressEvent添加如下代码

void Dialog::mousePressEvent(QMouseEvent *e)
{
    if (e->button() == Qt::LeftButton)
    {
        lastPoint = e->pos();
    }

    endPoint = lastPoint;
}

当鼠标左键按下时获得开始点,每次开始绘制都让结束点和开始点重合,这样确保这两个点的值都是预期的值。

3.7 在mouseMoveEvent添加如下代码

//鼠标移动事件
void Dialog::mouseMoveEvent(QMouseEvent *e)
{
    if (e->button() & Qt::LeftButton)
    {
        endPoint = e->pos();
        update();
    }
}

当鼠标移动时获得结束点,并更新绘制,注意这里的buttons()函数可以获取鼠标移动过程中按下的所有按键,然后用&Qt::LeftButton来判断是否按下了左键,在mouseMoveEvent()中必须使用该方法来判断按下的鼠标按键。最后调用update()函数会执行paintEvent()函数进行重新绘制。

3.8 在mouseReleaseEvent添加如下代码

//鼠标释放事件
void Dialog::mouseReleaseEvent(QMouseEvent *e)
{
    if (e->button() == Qt::LeftButton)
    {
        endPoint = e->pos();
        update();
    }

}

当鼠标按键释放时也进行重绘。现在运行程序,使用鼠标在白色画布上进行绘制,发现已经实现了简单的涂鸦板功能,效果如下图所示。
在这里插入图片描述

04. 程序设计(放大功能)

4.1 添加放大按钮。到dialog.h文件中,先添加头文件:

#include <QDialog>
#include <QMouseEvent>
#include <QPushButton>


QT_BEGIN_NAMESPACE
namespace Ui { class Dialog; }
QT_END_NAMESPACE

class Dialog : public QDialog
{
    Q_OBJECT

public:
    Dialog(QWidget *parent = nullptr);
    ~Dialog();

protected:
    void paintEvent(QPaintEvent *);
    void mousePressEvent(QMouseEvent *);
    void mouseMoveEvent(QMouseEvent *);
    void mouseReleaseEvent(QMouseEvent *);

private slots:
    void zoomIn();

private:
    Ui::Dialog *ui;
    QPixmap pix;
    QPoint lastPoint;
    QPoint endPoint;

    qreal scale;
    QPushButton *button;
};
#endif // DIALOG_H

4.2 到dialog.cpp文件中,先在构造函数中添加如下代码:

Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
    , ui(new Ui::Dialog)
{
    ui->setupUi(this);

    //设置窗口大小
    resize(600, 500);

    //设置画布大小和背景
    pix = QPixmap(300, 250);
    pix.fill(Qt::white);

    //设置初始放大倍数为1
    scale = 1;
    //新建按钮对象
    button = new QPushButton(this);

    //设置按钮显示文本
    button->setText(tr("放大"));
    //设置按钮的位置
    button->move(500, 450);
    //信号与槽关联
    connect(button, &QPushButton::clicked, this, &Dialog::zoomIn);

}

这里使用代码创建了一个按钮对象,并将其单击信号关联到了放大槽上,也就是说按下这个按钮,就会执行zoomIn()槽。

4.3 添加zoomIn函数

//槽函数 放大
void Dialog::zoomIn()
{
    scale *= 2;
    update();
}

这里实现每按下这个按钮,放大值都扩大两倍。后面调用update()函数来更新显示。

4.4 让画布的内容放大有两个办法,一个是直接放大画布的坐标系统,一个是放大窗口的坐标系统。下面我们先来放大窗口的坐标系统。更改paintEvent()函数如下:

//绘图事件
void Dialog::paintEvent(QPaintEvent *)
{
    QPainter p(&pix);

    //根据鼠标指针前后两个位置绘制直线
    p.drawLine(lastPoint,  endPoint);

    //前一个坐标值等于后一个坐标值
    lastPoint = endPoint;

    QPainter painter(this);
    painter.scale(scale, scale);
    painter.drawPixmap(0, 0, pix);
}

现在运行程序,效果如下图所示。
在这里插入图片描述

然后按下zoomIn按钮,效果如下图所示。
在这里插入图片描述

现在再用鼠标进行绘制,发现图形已经不能和鼠标轨迹重合了,效果如下图所示。
在这里插入图片描述

窗口的坐标扩大了,但是画布的坐标并没有扩大,而我们画图用的坐标值是鼠标指针的,鼠标指针又是获取的窗口的坐标值。现在窗口和画布的同一点的坐标并不相等,所以就出现了这样的问题。

其实解决办法很简单,窗口放大了多少倍,就将获得的鼠标指针的坐标值缩小多少倍就行了。我们将paintEvent()函数更改如下:

//绘图事件
void Dialog::paintEvent(QPaintEvent *)
{
    QPainter p(&pix);

    //根据鼠标指针前后两个位置绘制直线
    p.drawLine(lastPoint / scale,  endPoint / scale);

    //前一个坐标值等于后一个坐标值
    lastPoint = endPoint;

    QPainter painter(this);
    painter.scale(scale, scale);
    painter.drawPixmap(0, 0, pix);
}

在这里插入图片描述

这种用改变窗口坐标大小来改变画布面积的方法,实际上是有损图片质量的。就像将一张位图放大一样,越放大越不清晰。原因就是,它的像素的个数没有变,如果将可视面积放大,那么单位面积里的像素个数就变少了,所以画质就差了。

05. 程序设计(放大功能)

方法二

5.1 扩大画布坐标系统。先将paintEvent()更改如下:

//绘图事件
void Dialog::paintEvent(QPaintEvent *)
{
    QPainter p(&pix);

    p.scale(scale, scale);

    //根据鼠标指针前后两个位置绘制直线
    p.drawLine(lastPoint,  endPoint);

    //前一个坐标值等于后一个坐标值
    lastPoint = endPoint;

    QPainter painter(this);
    painter.drawPixmap(0, 0, pix);
}

这时运行程序,先进行绘制,然后点击zoomIn按钮,发现以前的内容并没有放大,而当我们再次绘画时,发现鼠标指针和绘制的线条又不重合了。效果如下图所示。
在这里插入图片描述

这并不是我们想要的结果,为了实现按下放大按钮,画布和图形都进行放大,我们可以使用缓冲画布(就是一个辅助画布)来实现。将paintEvent()函数内容更改如下。


//绘图事件
void Dialog::paintEvent(QPaintEvent *)
{
    if (1 != scale)
    {
        //临时画布
        QPixmap copyPix(pix.size() * scale);
        QPainter pp(&copyPix);
        pp.scale(scale, scale);

        //将以前画布上的内容复制到现在的画布上
        pp.drawPixmap(0, 0, pix);
        //将放大后的内容在复制到原来的画布上
        pix = copyPix;
        //让scale重新置1
        scale = 1;
    }


    QPainter p(&pix);

    p.scale(scale, scale);

    //根据鼠标指针前后两个位置绘制直线
    p.drawLine(lastPoint / scale,  endPoint / scale);

    //前一个坐标值等于后一个坐标值
    lastPoint = endPoint;

    QPainter painter(this);
    painter.drawPixmap(0, 0, pix);
}

运行结果

在这里插入图片描述

06. 附录

源码下载:【Qt】2D绘图之涂鸦板.rar

© 版权声明
THE END
喜欢就支持一下吧
点赞263 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容