涂鸦板程序设计

发布时间:2012-09-05 10:38:58   来源:文档文库   
字号:

1Qt 2D绘图 绘制简单图形

本文介绍在窗口上绘制最简单的图形的方法。

1.新建Qt4 Gui Application工程,我这里使用的工程名为painter01,选用QDialog作为Base class

2.dialog.h文件中声明重绘事件函数void paintEvent(QPaintEvent *);

3.dialog.cpp中添加绘图类QPainter的头文件包含#include

4.在下面进行该函数的重定义。

void Dialog::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.drawLine(0,0,100,100);
}

其中创建了QPainter类对象,它是用来进行绘制图形的,我们这里画了一条线Line,其中的参数为线的起点(00),和终点(100100)。这里的数值指的是像素,详细的坐标设置我们以后再讲,这里知道(00)点指的是窗口的左上角即可。运行效果如下:

5.qt的帮助里可以查看所有的绘制函数,而且下面还给出了相关的例子。

6.我们下面将几个知识点说明一下,帮助大家更快入门。

将函数改为如下:

void Dialog::paintEvent(QPaintEvent *)
{
    QPainter painter(this);

    QPen pen; //画笔
    pen.setColor(QColor(255,0,0));
    QBrush brush(QColor(0,255,0,125)); //画刷,需要构造

    painter.setPen(pen); //添加画笔
    painter.setBrush(brush); //添加画刷
    painter.drawRect(100,100,200,200); //绘制矩形
}

这里的pen用来绘制边框,brush用来进行封闭区域的填充,QColor类用来提供颜色,我们这里使用了rgb方法来生成颜色,即(redgreenblue),它们取值分别是0-255,例如(25500)表示红色,而全0表示黑色,全255表示白色。后面的(02550125),其中的125是透明度(alpha)设置,其值也是从02550表示全透明。最后将画笔和画刷添加到painter绘制设备中,画出图形。这里的Rect是长方形,其中的参数为(100100)表示起始坐标,200200表示长和宽。效果如下:

7.其实画笔和画刷也有很多设置,大家可以查看帮助。

QPainter painter(this);

    QPen pen(Qt::DotLine);
    QBrush brush(Qt::blue);
    brush.setStyle(Qt::HorPattern);

    painter.setPen(pen);
    painter.setBrush(brush);
    painter.drawRect(100,100,200,200);

这里我们设置了画笔的风格为点线,画刷的风格为并行横线,效果如下:

在帮助里可以看到所有的风格。


我们这里用了Qt::blueQt自定义的几个颜色如下:

8.画弧线,这是帮助里的一个例子。

QRectF rectangle(10.0, 20.0, 80.0, 60.0); //矩形
     int startAngle = 30 * 16;     //起始角度
     int spanAngle = 120 * 16;   //跨越度数

     QPainter painter(this);
     painter.drawArc(rectangle, startAngle, spanAngle);

这里要说明的是,画弧线时,角度被分成了十六分之一,就是说,要想为30度,就得是30*16。它有起始角度和跨度,还有位置矩形,要想画出自己想要的弧线,就要有一定的几何知识了。这里就不再祥述。


2Qt 2D绘图(二)渐变填充

qt中提供了三种渐变方式,分别是线性渐变圆形渐变圆锥渐变。如果能熟练应用它们,就能设计出炫目的填充效果。
线性渐变:

1.更改函数如下:

void Dialog::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    QLinearGradient linearGradient(100,150,300,150);
    //从点(100150)开始到点(300150)结束,确定一条直线
    linearGradient.setColorAt(0,Qt::red);
    linearGradient.setColorAt(0.2,Qt::black);
    linearGradient.setColorAt(0.4,Qt::yellow);
    linearGradient.setColorAt(0.6,Qt::white);
    linearGradient.setColorAt(0.8,Qt::green);
    linearGradient.setColorAt(1,Qt::blue);
    //将直线开始点设为0,终点设为1,然后分段设置颜色
    painter.setBrush(linearGradient);
    painter.drawRect(100,100,200,100);
    //绘制矩形,线性渐变线正好在矩形的水平中心线上
}

效果如下:

圆形渐变:

1.更改函数内容如下:

   QRadialGradient radialGradient(200,100,100,200,100);
   //其中参数分别为圆形渐变的圆心(200100),半径100,和焦点(200100
    //这里让焦点和圆心重合,从而形成从圆心向外渐变的效果
    radialGradient.setColorAt(0,Qt::black);
    radialGradient.setColorAt(1,Qt::yellow);
    //渐变从焦点向整个圆进行,焦点为起始点0,圆的边界为1
    QPainter painter(this);
    painter.setBrush(radialGradient);
    painter.drawEllipse(100,0,200,200);
   //绘制圆,让它正好和上面的圆形渐变的圆重合

效果如下:

2.要想改变填充的效果,只需要改变焦点的位置和渐变的颜色位置即可。

改变焦点位置:QRadialGradient radialGradient(200,100,100,100,100);

效果如下:

锥形渐变:

1.更改函数内容如下:

//圆锥渐变
    QConicalGradient conicalGradient(50,50,0);
    //圆心为(5050),开始角度为0
    conicalGradient.setColorAt(0,Qt::green);
    conicalGradient.setColorAt(1,Qt::white);
   //从圆心的0度角开始逆时针填充
    QPainter painter(this);
    painter.setBrush(conicalGradient);
    painter.drawEllipse(0,0,100,100);

效果如下:

2.可以更改开始角度,来改变填充效果

QConicalGradient conicalGradient(50,50,30);

开始角度设置为30度,效果如下:

 

3Qt 2D绘图(三)绘制文字

接着上一次的教程,这次我们学习在窗体上绘制文字。

1.绘制最简单的文字。

我们更改重绘函数如下:

void Dialog::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.drawText(100,100,yafeilinux);
}

我们在(100100)的位置显示了一行文字,效果如下。

2.如果要使文字更美观,我们就需要使用QFont类来改变字体。先在帮助中查看一下这个类。

可以看到它有好几个枚举变量来设置字体。下面的例子我们对主要的几个选项进行演示。

更改函数如下。

void Dialog::paintEvent(QPaintEvent *)
{
    QFont font(Arial,20,QFont::Bold,true);
    //设置字体的类型,大小,加粗,斜体
    font.setUnderline(true);
    //设置下划线
    font.setOverline(true);
    //设置上划线
    font.setCapitalization(QFont::SmallCaps);
    //设置大小写
    font.setLetterSpacing(QFont::AbsoluteSpacing,5);
    //设置间距
    QPainter painter(this);
    painter.setFont(font);
    //添加字体

painter.setPen(QColor(Qt::red));

painter.drawText(100,100,yafeilinux);


  //QRectF ff(100,100,300,200);
   // painter.drawRect(ff);
   
   // painter.drawText(ff,Qt::AlignCenter,yafeilinux);
}

效果如下。

这里的所有字体我们可以在设计器中进行查看。如下。

 

4Qt 2D绘图(四)绘制路径

接着上一次的教程,这次我们学习在窗体上绘制路径。QPainterPath这个类很有用,这里我们只是说明它最常使用的功能,更深入的以后再讲。

1.我们更改paintEvent函数如下。

void Dialog::paintEvent(QPaintEvent *)
{
    QPainterPath path;

    path.addEllipse(100,100,50,50);
    path.lineTo(200,200);

    QPainter painter(this);
    painter.setPen(Qt::green);
    painter.setBrush(Qt::yellow);

    painter.drawPath(path);
}

这里我们新建了一个painterPath对象,并加入了一个圆和一条线。然后绘制这个路径。

效果如下。

2.上面绘制圆和直线都有对应的函数啊,为什么还要加入一个painterPath呢?

我们再添加几行代码,你就会发现它的用途了。

void Dialog::paintEvent(QPaintEvent *)
{
    QPainterPath path;

    path.addEllipse(100,100,50,50);
    path.lineTo(200,200);

    QPainter painter(this);
    painter.setPen(Qt::green);
    painter.setBrush(Qt::yellow);

    painter.drawPath(path);

    QPainterPath path2;
    path2.addPath(path);
    path2.translate(100,0);

    painter.drawPath(path2);
}

效果如下。

这里我们又新建了一个painterPath对象path2,并将以前的path添加到它上面,然后我们更改了原点坐标为(1000),这时你发现我们复制了以前的图形。这也就是painterPath类最主要的用途,它能保存你已经绘制好的图形。

5Qt 2D绘图(五)显示图片

现在我们来实现在窗口上显示图片,并学习怎样将图片进行平移,缩放,旋转和扭曲。这里我们是利用QPixmap类来实现图片显示的。

一、利用QPixmap显示图片。

1.将以前的工程文件夹进行复制备份,我们这里将工程文件夹改名为painter05。(以前已经说过,经常备份工程目录,是个很好的习惯)

2.在工程文件夹的debug文件夹中新建文件夹,我这里命名为images,用来存放要用的图片。我这里放了一张linux.jpg的图片。如下图所示。

3.Qt Creator中打开工程。(即打开工程文件夹中的.pro文件),如图。

4.dialog.cpp文件中的paintEvent()函数更改如下。

void Dialog::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    QPixmap pix;
    pix.load(images/linux.jpg);
    painter.drawPixmap(0,0,100,100,pix);
}

这里新建QPixmap类对象,并为其添加图片,然后在以(00)点开始的宽和高都为100的矩形中显示该图片。你可以改变矩形的大小,看一下效果啊。最终程序运行效果如下。


(说明:下面的操作都会和坐标有关,这里请先进行操作,我们在下一节将会讲解坐标系统。)

QMovie *movie = new QMovie("D:/QTexep/exp3_1/drwa/test.gif");

ui->label->setMovie(movie);

movie->start();
6Qt 2D绘图---涂鸦板

简单的涂鸦板:

1.我们再在程序中添加函数。

dialog.cpp里的添加头文件:#include

我们在dialog.h里的public中再添加鼠标移动事件和鼠标释放事件的函数声明:

void mouseMoveEvent(QMouseEvent *);

void mouseReleaseEvent(QMouseEvent *);

void mousePressEvent(QMouseEvent *);

private中添加变量声明:

QPixmap pix; //画布
QPoint lastPoint;//起点坐标
QPoint endPoint; //终点坐标

因为在函数里声明的QPixmap类对象是临时变量,不能存储以前的值,所以为了实现保留上次的绘画结果,我们需要将其设为全局变量。

后两个QPoint变量存储鼠标指针的两个坐标值,我们需要用这两个坐标值完成绘图。

2.dialog.cpp中进行修改。

在构造函数里进行变量初始化。

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

然后进行其他几个函数的定义:

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

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

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

void Dialog::mouseMoveEvent(QMouseEvent *event)
{
    if(event->buttons()&Qt::LeftButton) //鼠标左键按下的同时移动鼠标
    {
        endPoint = event->pos();
        update();//界面重绘
    }
}
void Dialog::mouseReleaseEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton) //鼠标左键释放
    {
        endPoint = event->pos();
        update();//界面重绘
    }
}

这里的update()函数,是进行界面重绘,执行该函数时就会执行那个重绘事件函数。

3.这时运行程序,效果如下。点击图片可将其放大

 

 

这样简单的涂鸦板程序就完成了。下面我们进行放大后的涂鸦。

放大后再进行涂鸦:

1.添加放大按钮。

在窗体中添加一个pushbutton

private中添加变量声明:

int scale;

然后再在下面写上按钮的槽函数声明:

private slots:
    void zoomIn();

2.dialog.cpp中进行更改。

在构造函数里添加如下代码:

scale =1;   //设置初始放大倍数为1,即不放大
connect(pushBtn,SIGNAL(clicked()),this,SLOT(zoomIn())); //对按钮的单击事件和其槽函数进行关联

这里我们利用代码添加了一个按钮对象,用它来实现放大操作。

再在构造函数以外进行zoomIn()函数的定义:

void Dialog::zoomIn() //按钮单击事件的槽函数
{
    scale *=2;
    update();
}

3.通过上一节的学习,我们应该已经知道想让画布的内容放大有两个办法,一个是直接放大画布的坐标,一个是放大窗口的坐标。

我们主要讲解放大窗口坐标。

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

    QPainter painter(this);
    painter.scale(scale,scale); //进行放大操作
    painter.drawPixmap(0,0,pix);
}

这时运行程序。

先随意画一个图形,如下图。

再按下zoomIn”按钮,进行放大两倍。可以看到图片放大了,效果如下。

这时我们再进行绘图,绘制出的线条已经不能和鼠标指针的轨迹重合了。效果如下图。

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

 

其实解决办法很简单,窗口放大了多少倍,就将获得的鼠标指针的坐标值缩小多少倍就行了。

void Dialog::paintEvent(QPaintEvent *)
{  
    QPainter pp(&pix);
    pp.drawLine(lastPoint/scale,endPoint/scale);
    lastPoint = endPoint;

    QPainter painter(this);
    painter.scale(scale,scale); //进行放大操作
    painter.drawPixmap(0,0,pix);
}

运行程序,效果如下:

此时已经能进行正常绘图了。

 

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

下面我们说说另一种方法。

放大画布坐标。

void Dialog::paintEvent(QPaintEvent *)
{  
    QPainter pp(&pix);
    pp.scale(scale,scale);
    pp.drawLine(lastPoint/scale,endPoint/scale);
    lastPoint = endPoint;

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

效果如下:

此时,画布中的内容并没有放大,而且画布也没有变大,不是我们想要的,所以我们再更改一下函数。

void Dialog::paintEvent(QPaintEvent *)
{  
    if(scale!=1) //如果进行放大操作
    {
        QPixmap copyPix(pix.size()*scale); //临时画布,大小变化了scale
        QPainter pter(©Pix);
        pter.scale(scale,scale);
        pter.drawPixmap(0,0,pix);   //将以前画布上的内容复制到现在的画布上
        pix = copyPix;     //将放大后的内容再复制回原来的画布上,这样只传递内容,不传递坐标系
        scale =1; //scale重新置1
    }
    QPainter pp(&pix);
    pp.scale(scale,scale);
    pp.drawLine(lastPoint/scale,endPoint/scale);
    lastPoint = endPoint;

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

此时运行效果如下:

这样就好了。可以看到,这样放大后再进行绘制,出来的效果是不同的。

8Qt 2D绘图 双缓冲绘图简介

上面一节我们实现了涂鸦板的功能,但是如果我们想在涂鸦板上绘制矩形,并且可以动态地绘制这个矩形,也就是说我们可以用鼠标画出随意大小的矩形,那该怎么办呢?

我们先进行下面的三步,最后引出所谓的双缓冲绘图的概念。

第一步:

我们更改上一节的那个程序的重绘函数。

void Dialog::paintEvent(QPaintEvent *)
{  
    QPainter painter(this);
    int x,y,w,h;
    x = lastPoint.x();
    y = lastPoint.y();
    w = endPoint.x() – x;
    h = endPoint.y() – y;
    painter.drawRect(x,y,w,h);
}

然后运行,效果如下。

这时我们已经可以拖出一个矩形了,但是这样直接在窗口上绘图,以前画的矩形是不能保存住的。所以我们下面加入画布,在画布上进行绘图。

第二步:

我们先在构造函数里将画布设置大点:pix = QPixmap(400,400);

然后更改函数,如下:

void Dialog::paintEvent(QPaintEvent *)
{  
    int x,y,w,h;
    x = lastPoint.x();
    y = lastPoint.y();
    w = endPoint.x() – x;
    h = endPoint.y() – y;
    QPainter pp(&pix);
    pp.drawRect(x,y,w,h);

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

这时运行程序,效果如下:

现在虽然能画出矩形,但是却出现了无数个矩形,这不是我们想要的结果,我们希望能像第一步那样绘制矩形,所以我们再加入一个临时画布。

第三步:

首先,我们在dialog.h中的private里添加变量声明:

QPixmap tempPix; //临时画布
bool isDrawing;   //标志是否正在绘图

然后在dialog.cpp中的构造函数里进行变量初始化:

isDrawing = false;

最后更改函数如下:

void Dialog::paintEvent(QPaintEvent *)
{  
    int x,y,w,h;
    x = lastPoint.x();
    y = lastPoint.y();
    w = endPoint.x() – x;
    h = endPoint.y() – y;

    QPainter painter(this);
    if(isDrawing)     //如果正在绘图
    {
        tempPix = pix;    //将以前pix中的内容复制到tempPix中,这样实现了交互绘图
        QPainter pp(&tempPix);
        pp.drawRect(x,y,w,h);
        painter.drawPixmap(0,0,tempPix);
    }
    else
    {
        QPainter pp(&pix);
        pp.drawRect(x,y,w,h);
        painter.drawPixmap(0,0,pix);
    }
}

void Dialog::mousePressEvent(QMouseEvent *event)
{
    if(event->button()==Qt::LeftButton) //鼠标左键按下
    {
        lastPoint = event->pos();
        isDrawing = true;   //正在绘图
    }
}

void Dialog::mouseMoveEvent(QMouseEvent *event)
{
    if(event->buttons()&Qt::LeftButton) //鼠标左键按下的同时移动鼠标
    {
        endPoint = event->pos();
        update();
    }
}
void Dialog::mouseReleaseEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton) //鼠标左键释放
    {
        endPoint = event->pos();
        isDrawing = false;    //结束绘图
        update();
    }
}

我们使用两个画布,就解决了绘制矩形等图形的问题。

其中tempPix = pix;一句代码很重要,就是它,才实现了消除那些多余的矩形。

双缓冲绘图简介:

如果将第一步中不用画布,直接在窗口上进行绘图叫做无缓冲绘图,那么第二步中用了一个画布,将所有内容都先画到画布上,在整体绘制到窗口上,就该叫做单缓冲绘图,那个画布就是一个缓冲区。这样,第三步,用了两个画布,一个进行临时的绘图,一个进行最终的绘图,这样就叫做双缓冲绘图。

我们已经看到,利用双缓冲绘图可以实现动态交互绘制。其实,Qt中所有部件进行绘制时,都是使用的双缓冲绘图。就算是第一步中我们没有用画布,Qt在进行自身绘制时也是使用的双缓冲绘图,所以我们刚才那么说,只是为了更好地理解双缓冲的概念。

本文来源:https://www.2haoxitong.net/k/doc/a21aeadfd15abe23482f4da4.html

《涂鸦板程序设计.doc》
将本文的Word文档下载到电脑,方便收藏和打印
推荐度:
点击下载文档

文档为doc格式