本文主要是介绍QT之涂鸦板实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
源代码下载地址:
https://github.com/onlyou2030/TuYaBan-BaiBan
(说明:这是我们Qt系列文章的一个例子,请先学习2D绘图部分内容,再学习本教程)
前面学习了Qt 2D绘图的基本知识,现在我们将所学习的知识进行一下综合,设计一个简单的画图软件。因为前面已经有了记事本的例子,所以这里对一些细节知识将不会多讲。而且,这只是个例子,说是软件,其实只是个较大点的程序而已,并不是实际的项目,所以通过这个例子,我们主要为了掌握一个方法和两个知识点。
一个方法:写较复杂程序的方法。
两个知识点:文件菜单的功能实现和利用双缓冲进行绘图。
这个例子共分为三部分进行介绍:
第一部分:进行软件界面的设计。其中一些知识可以参考我博客中的Qt教程四。
第二部分:实现基本的功能,这里会再次详细介绍文件菜单中新建,打开,保存,另存为,关闭等菜单的功能实现。这里你可以参考我博客中的Qt教程六。
第三部分:我们在这里会详细讲解所谓的双缓冲绘图,利用它实现图形(例如矩形,椭圆)的交互式绘制。
第一部分:设计界面
在windows上自带了画图软件,在Qt Creator中也有个绘图软件的例子scribble example,你可以在帮助中进行查看。而现在我们要写的绘图程序的例子,也应该包含这些功能,如绘画涂鸦,添加直线或矩形等常用图形,可以插入图片,在图片上进行绘画,可以让它缩放,旋转,拉伸等。想到了这些功能,我们就可以设计软件的界面了。
一.设计菜单
1.在QtCreator中新建Qt4 Gui Application工程,我这里使用的工程名为paint,使用默认的QMainWindow作为主窗口。
2.为了让程序中可以使用中文,我们先在main.cpp中加入头文件#include <QTextCodec>,并加入下面一行代码:QTextCodec::setCodecForTr(QTextCodec::codecForLocale());
3.打开mainwindow.ui,先设计菜单。依次是文件菜单,编辑菜单,工具菜单和帮助菜单。其内容分别如下:
文件菜单
编辑菜单
工具菜单
帮助菜单
4.向工程中添加资源文件,向其中添加要使用的菜单图标。
添加完后记着保存一下资源文件,不然的话,在资源管理器中可能看不到添加的图标。
5.打开动作编辑器Action Editor,编辑已添加的菜单动作。
我们下面只介绍其中“新建”菜单的编辑,其余菜单照做就行了。
双击action_N,弹出如下对话框:
单击Icon后面的按钮,弹出资源管理器:
我们点击上面的Reload,便能显示出我们已经添加的图标,我们选中其中的新建菜单的图标,点击Ok确认,如下图所示:
添加完图标后,我们单击一下快捷键Shortcut后面的输入框,然后同时按下Ctrl键和N键,这样就将新建菜单的快捷键设为了Ctrl+N,如下图所示:
然后我们再在右面的属性窗口中更改statusTip的内容为“新建文件”,这样在鼠标放在新建菜单上是,状态栏就会显示“新建文件”。如下图:
我们按照同样的方法设置其它菜单,设置完成后如下图所示:
6.我们把其中的一些图标放到工具栏上,如下图所示:
这样就完成了菜单的设计。
二.添加绘图工具栏
我们这里的绘图工具栏使用的是一种叫做Dock的窗口,它与其它窗口的不同就是它可以在其父窗口中浮动,也可以停靠在父窗口的边界,就像一个工具栏一样。
1.我们在左边的部件栏中找到Dock Widget,将其拖入到设计区。
添加后它默认在左边框上停靠着。如下图:
2.我们在其属性栏里将其windowTitle改为“画图工具”。
3.向其中拖入相关部件,效果如下图:
其中,“画笔线宽”下的部件为Spin Box,其属性中的objectName为penWidthSpinBox,属性栏最下面的minimum属性改为1,即最小值为1。其余部件均为组合框ComboBox,objectName依次为:选择图形:shapeComboBox
4.我们给选择框添加条目。
右击“选择图形”下面的组合框,弹出菜单,如下:
我们点击Edit Items菜单,弹出下面的条目编辑框,我们点击“+”按钮,添加新的条目:
我们依次添加“无”“直线”“矩形”“椭圆”四个条目,如下所示:
同样的我们给“画笔类型“下的组合框添加两个条目“实线”和“点线”。两个颜色组合框的条目以后再添加。
5.最后可以让所有部件处于一个网格布局管理器中。
此时运行程序,效果如下:
拖动画图工具栏后效果如下:
三.添加画布
因为画布是真正实现绘图功能的,所以我们新建一个类来实现所有跟绘图有关的功能。这里先进行操作,对于一些内容到后面我们会详细解释的。
1.往工程中添加新的C++类,类名为PaintArea,以QWidget作为基类,如下所示:
2.在paintArea.h中声明对象和函数。
class PaintArea : public QWidget
{
public:
protected:
private:
};
我们这里使用了QImage类对象进行绘图,其实使用以前讲过的QPixmap类对象也是可以的。
3.在paintarea.cpp中的构造函数里初始化对象。
先加入头文件声明:#include <QPainter>
再更改构造函数:
PaintArea::PaintArea()
{
}
4.在paintarea.cpp中定义重绘函数。
void PaintArea::paintEvent(QPaintEvent *)
{
}
5.将画布添加到主界面的中心区。
因为以后可能要使用很大的画布,这样为了使画布很大时还能显示,我们需要加入滚动条,所以这里使用了QScrollArea类对象,它提供了横向和纵向的滚动条,如果你有兴趣,可以在帮助中查看它的介绍。
首先,在mainwindow.h文件中进行如下操作:
添加头文件声明:
#include “paintarea.h”
#include <QScrollArea>
在下面的private中添加对象的声明:
PaintArea *area;
QScrollArea *scrollArea;
然后在mainwindow.cpp文件中的构造函数里添加代码:
MainWindow::MainWindow(QWidget *parent) :
{
}
这时运行程序,效果如下:
四.实现涂鸦的功能
我们在画布类中添加一些代码,实现最基本的涂鸦功能。
1.在paintarea.h中做更改。
添加头文件:
#include <QMouseEvent>
#include <QPoint>
在protected中添加函数声明:
void mousePressEvent(QMouseEvent *);
void mouseMoveEvent(QMouseEvent *);
void mouseReleaseEvent(QMouseEvent *);
在private中添加对象声明:
QPoint lastPoint,endPoint; //定义两个坐标对象存放鼠标指针的前后两个坐标
2.在paintarea.cpp中添加函数的定义。
void PaintArea::mousePressEvent(QMouseEvent *event)
{
}
void PaintArea::mouseMoveEvent(QMouseEvent *event)
{
}
void PaintArea::mouseReleaseEvent(QMouseEvent *event)
{
}
void PaintArea::paint(QImage &theImage)
{
}
这样,再次运行程序就能实现涂鸦的功能了,效果如下:
在下一讲中我们将实现所有菜单的功能。
在退出软件之后,我们可以先将工程文件夹进行备份,比如这里命名为paint20100206,后面就是该工程修改的日期。我们也可以在工程文件夹中新建文本文档,命名为readme.txt,用来记录该备份对程序进行了哪些修改。
第二部分:实现基本功能
上一节我们已经将界面做好了,在这一节中我们将实现所有菜单的功能。因为文件菜单是很多软件都有的,所以我们一定要搞清楚其中几个菜单之间的逻辑关系,虽然在以前的记事本程序中已经对其进行了讲解,但是我感觉当时讲的还是不够清楚,所以这次再对其进行详细讲解,希望大家务必掌握几个函数的写法。
我们用Qt Creator打开上次的工程,即打开工程文件夹中的paint.pro文件。
一.添加设置画布的对话框
首先,因为新建画布时,我们想要可以设置画布的大小和背景颜色,所以在开始讲解之前,我们先添加一个对话框,用来让用户进行相关设置。
1.我们添加新的Qt Designer Form Class,选择带有按钮的对话框作为模板,如下图所示。
然后类名使用DoNewDialog。
2.我们将其界面设计如下。
其中“宽”后的Spin Box,将其object Name修改为widthSpinBox,修改其minimum为400,maximum为10000;修改“高”后的Spin Box,将其object Name改为heightSpinBox,修改其minimum为300,maximum为10000;“背景颜色”后的部件是一个Tool Button,点击它显示颜色对话框,和一个Text Browser,用于显示颜色对话框返回的颜色,。
3.我们在donewdialog.h文件中声明函数和对象。
在public中添加:
int getWidth();
int getHeight();
QColor getBackColor();
在private中添加:
QColor backColor;
4.然后在donewdialog.cpp中进行更改。
先在构造函数中对backColor进行初始化:
backColor = Qt::white;
然后进行函数的定义。
int DoNewDialog::getWidth()
{
}
int DoNewDialog::getHeight()
{
}
QColor DoNewDialog::getBackColor()
{
}
5. 在设计器界面上右击那个Tool Button,进入其单击事件槽函数。修改如下。
void DoNewDialog::on_toolButton_clicked()
{
}
我们需要加入#include <QColorDialog>的头文件。这里我们将获得的颜色在那个TextBrowser上显示了出来。其中palette对象获取了textBrowser的调色板,这样来更改它的显示颜色。
然后,我们需要更改PaintArea类的内容,让其画布可以更改大小和颜色。
1.在paintarea.h中的public中添加函数声明。
void setImageSize(int width,int height);
void setImageColor(QColor color);
2.在paintarea.cpp中进行函数的定义。
void PaintArea::setImageSize(int width, int height)
{
}
void PaintArea::setImageColor(QColor color)
{
}
最后我们在主界面中进入新建菜单的triggered()事件,如下图所示。
然后在mainwindow.h中对其内容更改如下:
void MainWindow::on_action_N_triggered()
{
}
并添加头文件:#include “donewdialog.h”
此时运行程序,效果如下。
点击新建按钮并设置画布属性:
新的画布:
二.完成文件菜单的功能
文件菜单中一般都包含新建,打开,保存,另存为和关闭或退出操作。这几个菜单之间存在着逻辑上的顺序关系,因为它们很常用,所以在这里我们再次详细地介绍一下这几个菜单的实现方法,希望能帮助大家很好地掌握。
其中利用到几个函数的原型如下:
bool maybeSave() :进行是否保存的判断,当返回值为true时说明整个判断过程已经进行过了。
bool isModified():进行文件是否被更改过的判断,其实就是返回一个bool型的变量modified的值。
bool doFileSave():进行文件保存操作,文件保存时先利用bool型变量isSaved判断文件是否保存过,如果保存过,就直接将现在的文件进行存储就行了;如果没有保存过,就要进行另存为操作,这样才能获得文件的保存路径。
bool saveFile(Qstring fileName) :以文件的路径对文件进行存储。存储成功返回true。
1.在画布类中添加函数,实现具体的文件打开和保存操作。
在paintarea.h中的public中添加函数声明:
bool isModified() const { return modified; }
bool saveImage(const QString &fileName, const char *fileFormat); //保存图片
bool openImage(const QString &fileName);
在private中声明变量:
bool modified;
在paintarea.cpp中对函数进行定义。
bool PaintArea::saveImage(const QString &fileName, const char *fileFormat)
{
}
bool PaintArea::openImage(const QString &fileName)
{
}
在paintarea.cpp中相关位置设置modified变量。
在构造函数里添加:modified = false;
在paint()函数里添加:modified = true;
这样就表示只有执行了paint() 函数,才表明画布内容被更改过。
2.在主窗口类中实现文件菜单的功能。
在mainwindow.h中进行函数和变量的声明。
在public中声明函数:
void doNew();
void doOpen();
bool doFileSave();
bool doFileSaveAs();
在private中声明变量和函数:
bool isSaved;
QString curFile;
bool maybeSave();
bool saveFile(QString fileName);
在mainwindow.cpp中进行更改。
先添加头文件:
#include <QMessageBox>
#include <QFileDialog>
再在构造函数里进行变量初始化:
isSaved = false;
curFile = tr(“未命名.png”);
然后进行那几个函数的定义:
void MainWindow::doOpen()
{
}
void MainWindow::doNew()
{
}
bool MainWindow::maybeSave()
{
}
bool MainWindow::doFileSave()
{
}
bool MainWindow::saveFile(QString fileName)
{
}
bool MainWindow::doFileSaveAs()
{
}
这里的doOpen()函数中用到了area->getImageSize(),所以我们还要在画布类里添加该函数。
在paintarea.h中的public中添加函数的声明:QSize getImageSize();
在paintarea.cpp中添加其定义:
QSize PaintArea::getImageSize()
{
}
3.编写菜单的triggered事件函数。
我们更改“新建”菜单的槽函数的内容:
void MainWindow::on_action_N_triggered()
{
}
然后按照进入“新建”菜单的方法,我们从ui文件依次进入其他菜单的槽函数,更改如下:
void MainWindow::on_action_O_triggered()
{
}
void MainWindow::on_action_S_triggered()
{
}
void MainWindow::on_action_A_triggered()
{
}
void MainWindow::on_action_X_triggered()
{
}
这时运行程序,应用这几个菜单进行各种相关操作。
是否保存文件提示如下:
文件另存为效果如下:
打开文件:
打开文件后效果如下:
为了更完善,我们在点击窗口右上角的关闭按钮时,也应该出现和点击退出菜单一样的效果。
所以下面我们重写窗口的关闭事件函数。
在mainwindow.h中的protected中声明函数:void closeEvent(QCloseEvent *);
然后在mainwindow.cpp中定义该函数:
void MainWindow::closeEvent(QCloseEvent *event)
{
}
这时你可以运行程序,测试一下效果。
4.实现打印菜单的功能。
在paintarea.h中public中添加函数声明:void doPrint();
在paintarea.cpp中添加头文件:
#include <QPrintDialog>
#include <QPrinter>
然后定义该函数:
void PaintArea::doPrint()
{
}
然后从mainwindow.ui中的动作编辑器中进入“打印”菜单的triggered事件槽函数:
void MainWindow::on_action_P_triggered()
{
}
这样就实现了打印功能。对于上面函数里的代码我们不做过多解释,如果有兴趣可以查看与打印相关的类的帮助。
我们运行程序,查看其效果:
如果你装有打印机,或装有PDF制作软件,就可以打印文件了。因为我这里装有Foxit PDF Creator软件,所以有一个Foxit PDF Printer,利用它可以将文件打印成PDF文档。
这时我们可以对工程文件夹再次进行备份,例如我这里命名为“paint20100207”。
三.完成文件编辑菜单的功能
编辑菜单主要完成图片的一些形状变化操作,所以我们需要在画布类里添加一些函数和变量来实现这些功能。
1.在paintarea.h中添加代码。
在public中添加函数声明:
void zoomIn();
void zoomOut();
void zoom_1();
void doRotate();
void doShear();
void doClear();
在private中添加变量声明:
qreal scale;
int angle;
qreal shear;
2.在paintarea.cpp中进行更改。
在构造函数里进行变量初始:
scale = 1;
angle = 0;
shear = 0;
然后进行那几个函数的定义:
void PaintArea::zoomIn()
{
}
void PaintArea::zoomOut()
{
}
void PaintArea::zoom_1()
{
}
void PaintArea::doRotate()
{
}
void PaintArea::doShear()
{
}
void PaintArea::doClear()
{
}
下面我们更改重绘事件函数,实现相关的效果。
void PaintArea::paintEvent(QPaintEvent *)
{
}
添加的几行代码并不难理解,不再解释。
然后我们从mainwindow.ui中进入相关菜单的triggered事件槽函数,更改如下:
void MainWindow::on_action_4_triggered()
{
}
void MainWindow::on_action_5_triggered()
{
}
void MainWindow::on_action_6_triggered()
{
}
void MainWindow::on_action_7_triggered()
{
}
void MainWindow::on_action_8_triggered()
{
}
void MainWindow::on_action_10_triggered()
{
}
现在运行程序,查看这些菜单的功能效果。
先随意画一个图形:
下面是点击“放大”菜单的效果:
下面是点击“缩小”菜单的效果:
下面是点击“还原”菜单的效果:
下面是点击“旋转”菜单的效果:
下面是点击“拉伸”菜单的效果:
下面是点击“清空”菜单的效果:
问题一:画布无限放大后,滚动条不随着放大。
我们随意画个图形,然后放大几次,最后只能看到它的一部分了。效果如下:
也许你会想到,当时我们在“打开”菜单的函数里加了一行代码来让滚动条放大:
scrollArea->widget()->resize(area->getImageSize());
我们把它放到“放大”菜单的函数里:
void MainWindow::on_action_4_triggered()
{
}
这时你再编译运行程序,你会发现,效果并没有改变。这是为什么呢?这个问题我们会在下一部分讲述。
四.完成工具菜单的功能
工具菜单只有一个“绘图工具栏”菜单,它的作用就是当绘图工具栏(就是那个主窗口上的Dock Widget)被关闭后,点击该菜单,让其可以重新显示出来。
我们从mainwindow.ui中进入该菜单的triggered事件槽函数,更改如下:
void MainWindow::on_action_11_triggered()
{
}
五.完成帮助菜单的功能
帮助菜单就是写一些关于软件使用的帮助和一些版本版权声明。我们这里就不再介绍。
六.完成绘图工具栏的功能
在前一部分设计界面时,绘图工具栏还有两个颜色组合框没有设置。所以我们先对其进行设置。
我们先在mainwindow.h中添加头文件:#include <QComboBox>
然后在public中添加函数声明:
void creatColorComboBox(QComboBox *comboBox);
再到mainwindow.cpp中进行如下更改。
添加头文件:#include <QPainter>
进行该函数的定义:
void MainWindow::creatColorComboBox(QComboBox *comboBox)
{
}
在构造函数里对那两个颜色组合框进行初始化:
creatColorComboBox(ui->penColorComboBox);
creatColorComboBox(ui->brushColorComboBox);
此时运行程序,两个颜色组合框就有相应的条目了。
下面我们将开始实现绘图工具栏的功能。
1.在paintarea.h文件中进行更改。
在public中进行函数声明:
void setPenStyle(Qt::PenStyle style); //设置画笔风格
void setPenWidth(int width);
void setPenColor(QColor color);
void setBrushColor(QColor color);
enum ShapeType
{
};
void setShape(ShapeType shape);
在private中进行变量的声明:
QColor penColor;
QColor brushColor;
int penWidth;
Qt::PenStyle penStyle;
ShapeType curShape;
2.在paintarea.cpp中进行更改。
在构造函数中进行变量的初始化:
penColor = Qt::black;
brushColor = Qt::black;
penWidth = 1;
penStyle = Qt::SolidLine;
curShape = None;
对上面的函数进行定义:
void PaintArea::setPenStyle(Qt::PenStyle style)
{
}
void PaintArea::setPenWidth(int width)
{
}
void PaintArea::setPenColor(QColor color)
{
}
void PaintArea::setBrushColor(QColor color)
{
}
void PaintArea::setShape(ShapeType shape)
{
}
然后更改paint()函数:
void PaintArea::paint(QImage &theImage)
{
}
3.写绘图栏中部件的槽函数。
进入mainwindow.ui文件,右击“选择图形”下的组合框,选择Go to slot,如下图:
然后选择当前条目内容更改事件currentIndexChanged(QString ),如下图:
进入其槽函数,更改如下:
void MainWindow::on_shapeComboBox_currentIndexChanged(QString shape) //选择图形组合框
{
}
同样的,进入“画笔类型”组合框的currentIndexChanged(QString )事件槽函数,更改如下:
void MainWindow::on_penStyleComboBox_currentIndexChanged(QString style)
{
}
进入“画笔线宽”的选择框的valueChanged(int )事件的槽函数,更改如下:
void MainWindow::on_penWidthSpinBox_valueChanged(int width)
{
}
进入两个颜色组合框的currentIndexChanged(int )事件的槽函数,分别更改如下:
void MainWindow::on_penColorComboBox_currentIndexChanged(int index)
{
}
void MainWindow::on_brushColorComboBox_currentIndexChanged(int index)
{
}
这里的itemData(index,Qt::UserRole).value<QColor>()就是和前面进行生成颜色组合框时的
void MainWindow::creatColorComboBox(QComboBox *comboBox)函数里的代码对应的,如
comboBox->addItem(QIcon(pix),tr(“黑色”),Qt::black);
这里的Qt::UserRole就是Qt::black,所以我们这里使用了value<QColor>()函数来获取颜色类型。你可以在帮助中查看QComboBox类的addItem()函数。
现在基本的功能我们已经全部实现了。运行程序,进行测试。
我们在画布上用“无”,“直线”,“矩形”,“椭圆”分别进行了绘制,效果如下:
我们发现用“直线”和“椭圆”时都出现了问题,那到底问题出在哪里呢?
问题二:绘制图形时出现混乱。
我们把“填充颜色”设置为“无颜色”,再进行绘制,效果如下:
这下你应该看出是什么问题了吧?!比如绘制矩形,这里是绘制了无数的小矩形,因为以前有填充颜色,把其它矩形的边框挡住了,看不到了而已。
问题三:图形变化后无法进行正常绘图。
我们再看下面的情况。
当随便绘制一个图形后,将其放大,这时如果继续绘制,那么绘制出的图形不会和鼠标指针的轨迹重合。
当我们把画布进行旋转后,纵向绘制直线,可画出来的直线却是横向的。
三个问题的解决:
这一篇虽然完成了所有的功能,但是遗留了三个致命的问题,对于这三个问题的原因和解决办法,我们将在下一部分给出。
我们现在保存文件,并再次对工程文件夹进行备份。我这里把备份文件夹命名为paint20100208。
第三部分:双缓冲绘图
在上一篇中我们留下了三个问题,下面再叙述一下:
(1)画布放大后,滚动条不跟着放大。
(2)绘制直线,矩形等图形时有重影。
(3)画布进行放大,旋转等操作后无法正常绘图。
对于其中的第一个和第三个问题,它们都与放大等操作有关,而放大等操作又是通过改变坐标系完成的。那么是不是坐标系的原因呢?
解决第(1)个问题:
我们在paintarea.cpp中更改getImageSize()函数如下:
QSize PaintArea::getImageSize()
{
}
这样就能返回我们需要的画布的大小的值了。
然后在mainwindow.cpp中对放大菜单进行更改:
void MainWindow::on_action_4_triggered()
{
}
这时添加这一行代码就有效果了。运行程序后效果如下:
解决完第一个问题,那么第三个问题也就应该有思路了。我们将图片放大后,鼠标指针所在点的坐标值与我们想要绘画点的坐标值已经不同了。比方说鼠标指针放在坐标值为(100,100)的点处,因为鼠标指针的坐标值是由窗口部件提供的,而这时画布的坐标是那个程序中的image变量的坐标,在没有改变画布大小时,鼠标指针所在点(100,100)和画布上的点(100,100)是重合的。但是将画布利用painter.scale(2,2)扩大两倍后,鼠标指针所在的点(100,100)处,在画布上已经是(50,50)了,而我们绘图是获取的鼠标指针的坐标值,所以就出现了绘制的线条与鼠标指针的轨迹不重合的问题。
解决第(3)个问题:
在paintarea.cpp中更改paint()函数:
void PaintArea::paint(QImage &theImage)
{
}
这里就是将鼠标的坐标缩小了要放大的倍数。效果如下:
放大时绘图的问题已经解决了,那么旋转、拉伸等操作后的绘图问题怎么解决呢?比方说旋转操作,我们将图片旋转了90度,我们想保存现在的图片效果,又不想改变坐标系的状态,只有这样我们才能在旋转后的图片上正常绘图。在这里,我们可以添加一个QImage临时变量,把图片内容先给它,然后用它完成旋转操作,再把旋转后的图片传回来。
我们在paintarea.cpp中的重绘函数里进行如下更改:
void PaintArea::paintEvent(QPaintEvent *)
{
}
这样更改后,运行程序,效果如下:
随意绘制一个图形:
旋转90度后再绘图:
拉伸后再绘图:
这样第三个问题就解决了。
到这里,我们应该对坐标系统有了一个更高层次的理解。而对于利用临时变量进行绘图也应该有些感觉了。这时我们再考虑第二个问题,我们绘制出的矩形是无数个小矩形的重叠,更确切地说是很多个矩形由小变大。我们回想一下我们所写的程序,每移动一下鼠标,获取一个坐标值,马上就进行绘制,所以这样就出现了移动完鼠标,绘制出很多个矩形的问题。我们能不能也再引入一个变量来解决这个问题呢?
解决第(2)个问题:
我们现在进行程序的更改:
在paintarea.h中的private中我们声明两个变量:
QImage tempImage;
bool isDrawing;
在paintarea.cpp中我们在构造函数里对变量进行初始化:isDrawing = false;
然后对鼠标事件进行如下更改:
void PaintArea::mousePressEvent(QMouseEvent *event)
{
}
void PaintArea::mouseMoveEvent(QMouseEvent *event)
{
}
void PaintArea::mouseReleaseEvent(QMouseEvent *event)
{
}
最后对重绘事件进行更改:
void PaintArea::paintEvent(QPaintEvent *)
{
}
这时运行程序,效果如下:
可以看到绘制特殊图形时已经没有问题了。
我们注意一下上面程序中的这一句:
tempImage = image;
这就是最关键的一步,就是这句代码,让我们实现了可以看着图形的大小变化进行绘图,也就是所谓的交互式绘图。其中的那个isDrawing变量,作为是否绘制特殊图形的标志,如果绘制特殊图形,就在临时绘图区进行,如果不是,就直接绘制在真实的画布上。
我们上面所用的方法,有个专业名称,叫做“双缓冲绘图”。
双缓冲绘图简介:
到底为什么叫双缓冲绘图呢?那我们现在就来简单总结一下以前进行过的绘图。
无缓冲绘图:
如果在重绘事件函数里直接进行绘图,如:
void Dialog::paintEvent(QPaintEvent *)
{
}
这样直接在窗口上绘图就可以叫做无缓冲绘图。
单缓冲绘图:
而如果先在QImage或QPixmap上进行绘图,然后再将其绘制好的内容整体在窗口上绘制出来,就是单缓冲绘图。我们上面的程序开始时就是单缓冲绘图。
双缓冲绘图:
就像我们程序最后一样,利用两个QImage对象,一个保存临时图像,一个保存最终图像,这样实现交互绘图的方法就叫做双缓冲绘图。
到这里,整个绘图软件就完成了,我们从这篇文章中应该学到一些东西,不光是Qt应用的知识,更重要的是写程序,写一个较大点软件的流程和技巧。
我们2D绘图的内容讲到这里也就结束了。你可以发现一个大点的程序是很难讲述清楚地,而Qt 的2D绘图的内容还有很多,我们不可能全部像这样详细讲述,这些教程也只是起一个基本入门的作用,要想深入学习,还要靠自己的多加练习。
这篇关于QT之涂鸦板实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!