本文介绍在窗口上绘制最简单的图形的方法。
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,其中的参数为线的起点(0,0),和终点(100,100)。这里的数值指的是像素,详细的坐标设置我们以后再讲,这里知道(0,0)点指的是窗口的左上角即可。运行效果如下:
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方法来生成颜色,即(red,green,blue),它们取值分别是0-255,例如(255,0,0)表示红色,而全0表示黑色,全255表示白色。后面的(0,255,0,125),其中的125是透明度(alpha)设置,其值也是从0到255,0表示全透明。最后将画笔和画刷添加到painter绘制设备中,画出图形。这里的Rect是长方形,其中的参数为(100,100)表示起始坐标,200,200表示长和宽。效果如下:
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::blue,Qt自定义的几个颜色如下:
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。它有起始角度和跨度,还有位置矩形,要想画出自己想要的弧线,就要有一定的几何知识了。这里就不再祥述。
2、Qt 2D绘图(二)渐变填充
在qt中提供了三种渐变方式,分别是线性渐变,圆形渐变和圆锥渐变。如果能熟练应用它们,就能设计出炫目的填充效果。线性渐变:
1.更改函数如下:
void Dialog::paintEvent(QPaintEvent *){ QPainter painter(this); QLinearGradient linearGradient(100,150,300,150); //从点(100,150)开始到点(300,150)结束,确定一条直线 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); //其中参数分别为圆形渐变的圆心(200,100),半径100,和焦点(200,100) //这里让焦点和圆心重合,从而形成从圆心向外渐变的效果 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); //圆心为(50,50),开始角度为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度,效果如下:
3、Qt 2D绘图(三)绘制文字
接着上一次的教程,这次我们学习在窗体上绘制文字。
1.绘制最简单的文字。
我们更改重绘函数如下:
void Dialog::paintEvent(QPaintEvent *){ QPainter painter(this); painter.drawText(100,100,”yafeilinux”);}
我们在(100,100)的位置显示了一行文字,效果如下。
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”);}
效果如下。
这里的所有字体我们可以在设计器中进行查看。如下。
4、Qt 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添加到它上面,然后我们更改了原点坐标为(100,0),这时你发现我们复制了以前的图形。这也就是painterPath类最主要的用途,它能保存你已经绘制好的图形。
5、Qt 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类对象,并为其添加图片,然后在以(0,0)点开始的宽和高都为100的矩形中显示该图片。你可以改变矩形的大小,看一下效果啊。最终程序运行效果如下。
(说明:下面的操作都会和坐标有关,这里请先进行操作,我们在下一节将会讲解坐标系统。)
QMovie *movie = new QMovie("D:/QTexep/exp3_1/drwa/test.gif");
ui->label->setMovie(movie);
movie->start();6、Qt 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*500pix = 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);}
此时运行效果如下:
这样就好了。可以看到,这样放大后再进行绘制,出来的效果是不同的。
上面一节我们实现了涂鸦板的功能,但是如果我们想在涂鸦板上绘制矩形,并且可以动态地绘制这个矩形,也就是说我们可以用鼠标画出随意大小的矩形,那该怎么办呢?
我们先进行下面的三步,最后引出所谓的双缓冲绘图的概念。
第一步:
我们更改上一节的那个程序的重绘函数。
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格式