• 第17篇 2D绘图(七)涂鸦板


    导语

    通过前面几节的学习,大家应该已经对Qt中2D绘图有了一定的认识,这一节我们将应用前面讲到的内容,编写一个简单的涂鸦板程序,这一节只是实现最基本的鼠标画线功能。

    环境:Windows Xp + Qt 4.8.4+QtCreator 2.6.2

    目录

    • 一、实现涂鸦板
    • 二、实现放大功能

    正文

    一、实现涂鸦板

    1.新建Qt Gui应用,项目名称为pianter_3,基类这次还用QDialog,类名保持Dialog不变即可。

    2.到dialog.h文件中,先添加头文件包含:#include

    然后添加几个函数的声明:

    1. protected:
    2. void paintEvent(QPaintEvent *);
    3. void mousePressEvent(QMouseEvent *);
    4. void mouseMoveEvent(QMouseEvent *);
    5. void mouseReleaseEvent(QMouseEvent *);

    第一个是绘制事件处理函数,后面分别是鼠标按下、移动和释放事件的处理函数。

    下面再添加几个private私有变量声明:

    1. QPixmap pix;
    2. QPoint lastPoint;
    3. QPoint endPoint;

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

    2.到dialog.cpp文件中,先添加头文件包含:#include

    然后在构造函数中添加如下初始代码:

    1. resize(600, 500); //窗口大小设置为600*500
    2. pix = QPixmap(200, 200);
    3. pix.fill(Qt::white);

    下面添加几个函数的定义:

    1. void Dialog::paintEvent(QPaintEvent *)
    2. {
    3. QPainter pp(&pix); // 根据鼠标指针前后两个位置就行绘制直线
    4. pp.drawLine(lastPoint, endPoint); // 让前一个坐标值等于后一个坐标值,这样就能实现画出连续的线
    5. lastPoint = endPoint;
    6. QPainter painter(this);
    7. painter.drawPixmap(0, 0, pix);
    8. }

    这里使用了两个点来绘制线条,这两个点在下面的鼠标事件中获得。

    1. void Dialog::mousePressEvent(QMouseEvent *event)
    2. {
    3. if(event->button()==Qt::LeftButton) //鼠标左键按下
    4. lastPoint = event->pos();
    5. }

    当鼠标左键按下时获得开始点。

    1. void Dialog::mouseMoveEvent(QMouseEvent *event)
    2. {
    3. if(event->buttons()&Qt::LeftButton) //鼠标左键按下的同时移动鼠标
    4. {
    5. endPoint = event->pos();
    6. update(); //进行绘制
    7. }
    8. }

    当鼠标移动时获得结束点,并更新绘制。调用update()函数会执行paintEvent()函数进行重新绘制。

    1. void Dialog::mouseReleaseEvent(QMouseEvent *event)
    2. {
    3. if(event->button() == Qt::LeftButton) //鼠标左键释放
    4. {
    5. endPoint = event->pos();
    6. update();
    7. }
    8. }

    当鼠标按键释放时也进行重绘。

    现在运行程序,使用鼠标在白色画布上进行绘制,发现已经实现了简单的涂鸦板功能,效果如下图所示。

    二、实现放大功能

    前面已经实现了简单的绘制功能,下面我们将实现放大功能,将画布放大后继续进行涂鸦。这里将使用两种方法来实现,也是对上一节坐标系统后面的问题的更进一步的应用实践。

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

    #include 
    

    然后添加下面private私有变量声明:

    1. qreal scale;
    2. QPushButton *button;

    最后再添加一个私有槽声明:

    1. private slots:
    2. void zoomIn();

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

    1. //设置初始放大倍数为1,即不放大
    2. scale =1;
    3. //新建按钮对象
    4. button = new QPushButton(this);
    5. //设置按钮显示文本
    6. button->setText(tr("zoomIn"));
    7. //设置按钮放置位置
    8. button->move(500, 450);
    9. //对按钮的单击事件和其槽函数进行关联
    10. connect(button, SIGNAL(clicked()), this, SLOT(zoomIn()));

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

    3.下面添加zoomIn()的定义:

    1. void Dialog::zoomIn()
    2. {
    3. scale *=2;
    4. update();
    5. }

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

    4.通过上一节的学习,我们应该已经知道想让画布的内容放大有两个办法,一个是直接放大画布的坐标系统,一个是放大窗口的坐标系统。下面我们先来放大窗口的坐标系统。更改paintEvent()函数如下:

    1. void Dialog::paintEvent(QPaintEvent *)
    2. {
    3. QPainter pp(&pix);
    4. pp.drawLine(lastPoint, endPoint);
    5. lastPoint = endPoint;
    6. QPainter painter(this);
    7. //进行放大操作
    8. painter.scale(scale, scale);
    9. painter.drawPixmap(0, 0, pix);
    10. }

    现在运行程序,先在白色画布上任意绘制一个图形,效果如下图所示。

    然后按下zoomIn按钮,效果如下图所示。

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

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

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

    1. void Dialog::paintEvent(QPaintEvent *)
    2. {
    3. QPainter pp(&pix);
    4. pp.drawLine(lastPoint/scale, endPoint/scale);
    5. lastPoint = endPoint;
    6. QPainter painter(this);
    7. painter.scale(scale, scale);
    8. painter.drawPixmap(0, 0, pix);
    9. }

    运行程序,效果如下图所示。可以看到,已经能够在放大以后继续绘图了。

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

    5.方法二。扩大画布坐标系统。先将paintEvent()更改如下:

    1. void Dialog::paintEvent(QPaintEvent *)
    2. {
    3. QPainter pp(&pix);
    4. pp.scale(scale, scale);
    5. pp.drawLine(lastPoint,endPoint);
    6. lastPoint = endPoint;
    7. QPainter painter(this);
    8. painter.drawPixmap(0, 0, pix);
    9. }

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

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

    1. void Dialog::paintEvent(QPaintEvent *)
    2. {
    3. if(scale!=1) //如果进行放大操作
    4. {
    5. //临时画布,大小变化了scale倍
    6. QPixmap copyPix(pix.size()*scale);
    7. QPainter pter(&copyPix);
    8. pter.scale(scale, scale);
    9. //将以前画布上的内容复制到现在的画布上
    10. pter.drawPixmap(0, 0, pix);
    11. //将放大后的内容再复制回原来的画布上
    12. pix = copyPix;
    13. //让scale重新置1
    14. scale =1;
    15. }
    16. QPainter pp(&pix);
    17. pp.scale(scale,scale);
    18. pp.drawLine(lastPoint/scale,endPoint/scale);
    19. lastPoint = endPoint;
    20. QPainter painter(this);
    21. painter.drawPixmap(0,0,pix);
    22. }

    现在运行程序,效果如下图所示。

    结语

    本节讲到的涂鸦板,只是为了实践前面的知识,起到抛砖引玉的作用。大家可以根据自己的理解继续探究下去。在下一节,我们将讲解怎样在涂鸦板上绘制出矩形、椭圆等图形。

    本程序中存在一些问题,如果大家想进一步学习研究,可以参考下载页面的涂鸦板开源软件。

  • 相关阅读:
    HMM隐马尔可夫模型最详细讲解与代码实现
    【解决方案】SkeyeVSS+ SkeyeARS“国土卫士”农田水利视频监管系统,实现国土资源监管智能化
    9.4-学习ing
    什么是JSF AV C++编码规范?
    python 计算最大回撤
    找不到d3dx9_43.dll如何修复?d3dx9_43.dll丢失的解决办法分享
    企业微信hook接口协议,标签变动回调
    Java_线程的概念和线程的创建的方法
    必备表格软件-FineReport正则表达式简介
    C标准文档
  • 原文地址:https://blog.csdn.net/qq_21137441/article/details/127682780