首先说明双缓冲不能用在使用画笔或者橡皮擦的时候,学习完之后就能理解了,如果我们不使用双缓冲绘图,我们画矩形,椭圆等就会出现bug情况。
不是双缓冲即只有一个image画布,直接在上面作图。在我们的鼠标移动事件里,移动鼠标会更新结束点,注意这个时候我们的起点并没有改变,既鼠标按下在哪儿,起点就在哪儿,鼠标移动事件调用paint(image)函数将图形画在image画布上,,现在我们想一想会出现什么bug,对,会出现无数个图形,这个时候我们的图形如下:
看懂了吗?看懂了我们就可以接着往下走了。
现在我们学习双缓冲制图,双缓冲的优点是可以在画直线,矩形,椭圆等图形的时候移动鼠标时只让用户看到痕迹并且在鼠标释放后才生成图形,那么我们就得明白它的原理是什么。
首先你想象有两张空白画板QImage image和QImage tempImage(QPixmap也一样,都是画布),现在两张画板上什么都没有。
1.按鼠标,移动画图,现在你看到画的的图形是在tempImage上,image待机.
2.松开鼠标,将tempImage上的图形复制到image上,但是tempImage上的图形并没有消失,因为它不是临时变量,这时看到的图形其实就是image和tempImage上两张图重合在一起的图像。
3.按下鼠标再次绘图(这时image和tempImage上都有刚才画好的图了哟既历史记录都保存了下来),这时的图又画在tempImage上,此时image待机,松开鼠标,tempImage的图形又画在了image上.
现在我们可以总结出这样做的特点就是tempImage总是领先image一个图形。那么这样有什么用呢?我们想一下画图的时候移动鼠标,这时并不会改变lastPoint(既起点)的值,而是更新endPoint(终点)的值,我们每移动一下,就会调用paint(tempImage)把图画在tempImage显现出来,然而这是我们不希望的,那么怎么办呢?这时image画布就发挥作用了,image因为只在鼠标释放时才复制下tempImage上的图,所以你现在移动鼠标只是在tempImage上的某个区域绘制图形,而这个区域image上并没有任何图形。这时把image复制给tempImage,那么在tempImage上这块区域也变成空白的了,但是这时tempImage里保存着lastPoint与endPoint,所以就会在tempImage上画出唯一的一个图形。松开鼠标,image上也保存下来了。专业人士称之为交叉绘图。
现在我们回到开头,应该就能明白双缓冲不能在画笔或者橡皮擦上的原因了,因为画过的痕迹不会被保留,只有最后释放鼠标时才会成图,所以最后只会有一个点。
paintwidget.h代码
#ifndef PAINTWIDGET_H
#define PAINTWIDGET_H
#include
#include
#include
#include
class PaintWidget : public QWidget
{
Q_OBJECT
public:
explicit PaintWidget(QWidget *parent = 0);
enum shape{//--------------------------------图形选择
Line=1,
Rect,
Ellipse,
Pen
};
public slots:
void setShape(PaintWidget::shape t){//--------接受从mainwindow发射过来的信号
if(t!=type)
type = t;
qDebug()<<type;// ------------------------检查是否接收成功
}
protected:
void paintEvent(QPaintEvent *);
void mousePressEvent(QMouseEvent *);
void mouseMoveEvent(QMouseEvent *);
void mouseReleaseEvent(QMouseEvent *);
void paint(QImage &theImage);
signals:
private:
PaintWidget::shape type;
QImage image;
QImage tempImage;
QRgb backColor;
QPoint lastPoint;
QPoint endPoint;
bool isDrawing;
};
#endif // PAINTWIDGET_H
#include "paintwidget.h"
#include
#include
PaintWidget::PaintWidget(QWidget *parent) :
QWidget(parent)
{
isDrawing = false;
type = PaintWidget::Line;//-------------默认为线类型
image = QImage(700,500,QImage::Format_RGB32);
backColor = qRgb(255,255,255);
image.fill(backColor);
tempImage = image;//--------------------tempImage也填充为白色
}
void PaintWidget :: paintEvent(QPaintEvent *){
QPainter painter(this);
if(isDrawing == true){
painter.drawImage(0,0,tempImage);//-------如果正在绘图,既鼠标点击或者正在移动,画在tempImage上
}else{
painter.drawImage(0,0,image);//-----------如果鼠标释放,将图保存在image上
}
}
void PaintWidget :: mousePressEvent(QMouseEvent *event){
if(event->button() == Qt::LeftButton){
lastPoint = event->pos();
isDrawing = true;//-------------------鼠标点击开始绘图,移动表示正在绘图
}
}
void PaintWidget :: mouseMoveEvent(QMouseEvent *event){
if(event->buttons() & Qt::LeftButton){//-----鼠标为左键且正在移动
endPoint = event->pos();
tempImage = image;
if(type == Pen){//------------如果工具为画笔,不用双缓冲直接画在画板上
paint(image);
}else{ //-------------------------否则用双缓冲绘图
paint(tempImage);
}
}
}
void PaintWidget :: mouseReleaseEvent(QMouseEvent *event){
isDrawing = false;
if(type != Pen){
paint(image);
}
}
void PaintWidget :: paint (QImage &theImage){
QPainter p(&theImage);
QPen apen;
apen.setWidth(4);
p.setPen(apen);//-----------设置绘图工具画笔线条宽度为4
switch (type) {//---------------画图,自己脑补百度
case PaintWidget::Line:{
p.drawLine(lastPoint,endPoint);
break;
}
case PaintWidget::Rect:{
p.drawRect(lastPoint.x(),lastPoint.y(),endPoint.x()-lastPoint.x(),endPoint.y()-lastPoint.y());
break;
}
case PaintWidget::Ellipse:{
p.drawEllipse(lastPoint.x(),lastPoint.y(),endPoint.x()-lastPoint.x(),endPoint.y()-lastPoint.y());
break;
}
case PaintWidget::Pen:{
p.drawLine(lastPoint,endPoint);
lastPoint = endPoint;
break;
}
default:
break;
}
update();//----------------------重绘
}
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
#include "paintwidget.h"
#include
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
protected:
signals:
void drawShape(PaintWidget::shape newShape);//-------发送给paintwidget,改变线条类型
private slots://----------------------------槽函数 接受工具栏按钮触发事件
void drawLineActionTriggered();
void drawRectActionTriggered();
void drawEllipseActionTriggered();
void drawPenActionTriggered();
private:
PaintWidget *area;//-----------------新建paintwidget部件
QScrollArea *scrollArea;//-----------滚动条
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include
#include
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
//-------------加工具栏,加动作,状态栏,基础不懂自学
this->setWindowTitle(tr("Drawing Board"));
QToolBar *bar = this->addToolBar("Tools");
QActionGroup *group = new QActionGroup(bar);//-----------把所有的按钮动作放在group里面,这样触发了一个按钮后之前的按钮会自动关闭
QAction *drawLineAction = new QAction("Line", bar);
drawLineAction->setIcon(QIcon(":/images/zhixian.png"));//-------图标自己去找吧,或者最后去我给的链接下,不过要积分哈。
drawLineAction->setToolTip(tr("Line"));
drawLineAction->setStatusTip(tr("Draw a line."));
drawLineAction->setCheckable(true);
drawLineAction->setChecked(true);//---------------------因为在paintwidget构造函数里面默认为线类型,所以这里默认为触发了画线动作
group->addAction(drawLineAction);
bar->addAction(drawLineAction);
QAction *drawRectAction = new QAction("Rectangle", bar);
drawRectAction->setIcon(QIcon(":/images/juxing.png"));
drawRectAction->setToolTip(tr("Rectangle"));
drawRectAction->setStatusTip(tr("Draw a rectangle."));
drawRectAction->setCheckable(true);
group->addAction(drawRectAction);
bar->addAction(drawRectAction);
QAction *drawEllipseAction = new QAction("Ellipse",bar);
drawEllipseAction->setIcon(QIcon(":/images/yuan.png"));
drawEllipseAction->setToolTip(tr("Ellipse"));
drawEllipseAction->setCheckable(true);
group->addAction(drawEllipseAction);
bar->addAction(drawEllipseAction);
QAction *drawPenAction = new QAction("Pen",bar);
drawPenAction->setIcon(QIcon(":/images/huabi.png"));
drawPenAction->setText(tr("Pen"));
drawPenAction->setCheckable(true);
group->addAction(drawPenAction);
bar->addAction(drawPenAction);
statusBar();
area = new PaintWidget;// -------------------------------area为paintwidget的组件
resize(700,500);
scrollArea = new QScrollArea;
scrollArea->setBackgroundRole(QPalette::Dark);
scrollArea->setWidget(area);
scrollArea->widget()->setMinimumSize(800,600);
setCentralWidget(scrollArea);
connect(drawLineAction,SIGNAL(triggered()),//------------触发了某个动作按钮就触发对应的触发动作函数
this,SLOT(drawLineActionTriggered()));
connect(drawRectAction,SIGNAL(triggered()),
this,SLOT(drawRectActionTriggered()));
connect(drawEllipseAction,SIGNAL(triggered()),
this,SLOT(drawEllipseActionTriggered()));
connect(drawPenAction,SIGNAL(triggered()),
this,SLOT(drawPenActionTriggered()));
connect(this,SIGNAL(drawShape(PaintWidget::shape)),//------把触发动作函数与paintwidget连接起来,发送信号给paintwidget
area,SLOT(setShape(PaintWidget::shape)));
}
void MainWindow::drawLineActionTriggered()
{
emit drawShape(PaintWidget::Line);//--------------发射信号
}
void MainWindow::drawRectActionTriggered()
{
emit drawShape(PaintWidget::Rect);
}
void MainWindow::drawEllipseActionTriggered()
{
emit drawShape(PaintWidget::Ellipse);
}
void MainWindow::drawPenActionTriggered()
{
emit drawShape(PaintWidget::Pen);
}
MainWindow::~MainWindow()
{
}
大功高成,运行,OK。
需要的图标,链接:http://download.csdn.net/detail/u012891055/8226205