零基础开始QT绘图(3)

版权声明:本博客所有原创文章未经准许不得转载或保存转发,本人保留版权法律追诉权。 https://blog.csdn.net/haigear/article/details/85028517

在上两篇基础上,我们了解利用Painter的四个对象进行绘图的基本用法,接下来,我们来实战一次,接下来的两篇教程我们来做一个很简单的绘图板小项目。
我们要实现的功能主要两点:一、可随意涂鸦,二、可以保存我们涂鸦的作品。在这前,我们要解决一个问题,那就是如何使用鼠标事件来绘制图形。

一、掌握鼠标事件的实现
利用鼠标绘图,我们必须首先了解三个鼠标事件,mousePressEvent、mouseReleaseEvent和mouseMoveEvent。其中,我么在mouseMove中必须实时获得鼠标的位置信息,这样才可以将实时绘图的效果显示出来。mousePress则是负责结束当前绘图以及将已经绘制好的图形存储在图形元素队列中。
我们首先看看mousePressEvent怎么实现?

在第一篇教程中,我们已经知道了QPainterEvent是需要在Widget中实现的虚函数,mousePressEvent以及mouseMoveEvent其实和QPainterEvent一样,必须手动实现,而不能像QPushButton那样通过右键选择信息槽即可。我们来看看mousePressEvnet的实现:
首先在widget头文件中增加声明:

  void mousePressEvent(QMouseEvent *event);

然后,在cpp文件中,增加对应的实现方法体:

  void Widget::mousePressEvent(QMouseEvent *event)
{
       //利用QMessageBox输出hello的对话框内容
        QMessageBox::about(this,"title","hello"); 
    }

我们来测试一下运行效果,看看鼠标是否有反应了,效果如下:
在这里插入图片描述
紧接下来,我们同样的方法实现mouseMoveEvent、mouseReleaseEvent,我们这次在让我们的鼠标移动式显示出鼠标的实时坐标值。代码如下:

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    setMouseTracking(true); //打开鼠标追踪实时更新
    ui->setupUi(this);
}
void Widget::paintEvent(QPaintEvent *event)
{

      QPainter painters(this);
      QRect rect(this->width()/3,this->height()/2,200,40);
      painters.drawText(rect,"mousePosition:"+QString::number(coursePoint.x())+","+QString::number(coursePoint.y()));


}

void Widget::mousePressEvent(QMouseEvent *event)
{

    QMessageBox::about(this,"title","hello");

}

void Widget::mouseMoveEvent(QMouseEvent *event)
{

    coursePoint = QCursor::pos(); //coursePoint 变量在头文件中定义,获得鼠标位置
    this->update();	//强制更新绘图

}

运行效果如下:
在这里插入图片描述
到这里鼠标事件的准备工作已经完成。我们这里的鼠标位置的显示是通过update不断的刷新来实现的,也就是说老的坐标值不会遗留在上面,始终显示的都是最新的坐标。但如果是这样的话,给我们的涂鸦带来了麻烦,如果我们每次绘制的图形都被刷新了,只剩下最后的一笔,而且一旦鼠标有移动就会因调用了update而刷新消失。

二、开始涂鸦
为了解决上面的刷新绘图和保留前面的绘图的矛盾,我们引入一种机制,这个机制很简单,那就是将前面的绘图先保存起来,等更新的时候全部重绘一次。那么保存起来的方法,一般有三种,一种是存储在一个数组里面,一种是存储在一个容器里面。

我们知道数组的大小是固定的,这个很麻烦,比方说定义1000就只能画1000个图形元素。为了解决这个问题,我们使用的是容器,这里我们使用QList来存储,每次在鼠标移动的时候就将最后的坐标位置存储到QList中去。
我们来看看代码是如何实现的,首先,声明一个QList用于存储坐标点:

 QList<QPoint> plist;

接下来,我们在mouseEvent中不停的存储点位置:

  plist.append(event->pos());
    this->update();

最后,我们在PainterEvent中实施绘制

 QPainter painters(this);
      QRect rect(this->width()-140,this->height()-20,200,40);
      painters.drawText(rect,"mousePosition:"+QString::number(coursePoint.x())+","+QString::number(coursePoint.y()));

      if(plist.count()>2)
      for(int i=0;i<plist.count()-1;i++)
      {

          qDebug()<<i;
          painters.drawLine(plist[i],plist[1+i]);

      }

我们一开始要实现的就是涂鸦,所以不必考虑其他的图形绘制,这种涂鸦实现的思路就是绘制直线,无数的小直线段共同组成了这些不规则的涂鸦(毛线团)轨迹。我们来看看运行效果:
在这里插入图片描述
随手涂鸦了一条似蛇非蛇的东东,这个图形是一条连贯的线组成,仅仅一条线,因为我们的QList存储的是点。那么我们如何实现能够绘制多条线的涂鸦呢?

三、能绘制多条线多个图形的绘图板
这里,我们来改进一下我们的存储容器,是指能够判断我们绘制多条线的意图。思路很简单,每次存储的时候将点分为两类,一类是起点,一类是终点,当然QList无法办到存储两个值,所以我们这里另外再配合plist启用另一个QList来存储点的类型(采用bool类型作为内容),变量名为isEndPoint,当点的类型是不是终点类型的时候则绘制线条,代码如下:

void Widget::paintEvent(QPaintEvent *event)
{


      QPainter painters(this);
      QRect rect(this->width()-140,this->height()-20,200,40);
      painters.drawText(rect,"mousePosition:"+QString::number(coursePoint.x())+","+QString::number(coursePoint.y()));

      if(plist.count()>2)
      for(int i=0;i<plist.count()-1;i++)
      {

          qDebug()<<i;
          if(isEndPoint[i]>0)
          painters.drawLine(plist[i],plist[1+i]);

      }
   }
void Widget::mousePressEvent(QMouseEvent *event)
{

    startPos = event->pos();
    start=true;

}
void Widget::mouseMoveEvent(QMouseEvent *event)
{

        if(start)
        {
        plist.append(startPos);
        isEndPoint.append(true);
        startPos =event->pos();
        }
        coursePoint =event->pos();// QCursor::pos();
        this->update();
}
void Widget::mouseReleaseEvent(QMouseEvent *event)
{

    plist.append(startPos);
    isEndPoint.append(false);
    start=false;

}

运行效果如下,我们依然是绘制那条蛇:
在这里插入图片描述

从这里我们可以看到,我们的思路是成功的。虽然实现了,但是貌似有些麻烦,麻烦就麻烦再每次绘制的时候要用一个for循环来逐个将线条绘制出来。
现在绘制的还是线条,那如果有更多的更复杂的图形,一定会让人抓狂的,因为我们的QList存储的类型是单一的,不能说所有的图形都用点来完成存储。
下面我们说说第三种思路,利用QPixmap来存储各种涂鸦元素。

四、利用QPixmap完成图形的临时存储

我们首先来看代码的实现,首先在头文件中增加一个QPixmap的对象声明:

QPixmap *pix;
    QPoint startPos;    //记录鼠标的当前位置
    QPoint endPos;

然后,在mouseMoveEvent中,直接将线条绘制在pix中,然后再PainterEvent中更新即可,没有见到存储的过程,意思是这个QPixmap是真正意义上的画板,它会自己记录存储曾经画过的内容。

 void Widget::mouseMoveEvent(QMouseEvent *event)
    {
         QPainter *painter = new QPainter;
                if(start)
                {
                painter->begin(pix);
                painter->drawLine(startPos, event->pos());
                painter->end();
        
               // plist.append(startPos);
                //isEndPoint.append(true);
                startPos =event->pos();
                }
                coursePoint =event->pos();// QCursor::pos();
                this->update();
    }

运行效果如下:
在这里插入图片描述
图形虽然画得有点丑,随手涂鸦嘛,应该有点涂鸦感。好了,这次我们就到这里结束。

下一篇,我么继续介绍标准图形(如线条,圆,矩形,填充,渐变等功能)的绘制以及工具栏的制作,让它更像我们的画笔工具,请有需要的小伙伴们关注博客更新。

猜你喜欢

转载自blog.csdn.net/haigear/article/details/85028517
今日推荐