一、基础图像的绘制
基础图形的绘制是qt中作为用户输入是比较重要的,可以作为用户实际交互的重要手段,下面将介绍一些重要的关于qt基础图像绘制的知识。
下图所示的两个类是qt中图像绘制最重要的结构体,第一个类相当于画家,第二个类相当于画布,注意,所有的QWidget类都继承了“QPainDevice”类,所以所有的“QWidget”类都可以作为画布使用。
下面是“QPainter”类绘画所使用的基本的绘画工具,也是重要的工具中的几个。
下面是“QPainter”的基本的绘图能力。
下面是qt图形绘制的一个重要规则,就是图像的绘制只能在“paintEvent”绘画事件函数中进行处理,在其他函数中进行绘制时不起作用的。
因为上面的规则,qt页面的绘制只有在“QWidget::paintEvent”事件函数中进行绘制,那么数据与绘制函数之间的交互就需要使用到链表,当只要有事件产生,就会运行这个事件函数,在事件函数中查询链表数据是否有更新,有更新的话,就进行页面的绘制,在外面只需要想链表中存放结构体数据就可以了。
下面是使用的示例代码:
#ifndef WIDGET_H
#define WIDGET_H
#include <QtGui/QWidget>
#include <QPushButton>
#include <QPoint>
#include <QList>
class Widget : public QWidget
{
Q_OBJECT
/* 枚举,用于区分绘制那种图形 */
enum
{
LINE,
RECT,
ELLIPSE
};
struct DrawParam
{
int type; //用于判断绘画的形式,线,椭圆,矩形
Qt::PenStyle pen; //画笔画的线的形式
QPoint begin; //起始点
QPoint end; //结束点
};
QPushButton m_testBtn;
/* 存储绘制结构体数据的链表 */
QList<DrawParam> m_list;
protected slots:
void onTestBtnClicked();
protected:
/* 绘画事件函数 */
void paintEvent(QPaintEvent *);
public:
Widget(QWidget *parent = 0);
~Widget();
};
#endif // WIDGET_H
#include "Widget.h"
#include <QPainter>
#include <QPoint>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
m_testBtn.setParent(this);
m_testBtn.move(400, 300);
m_testBtn.resize(70, 30);
m_testBtn.setText("Test");
resize(500, 350);
/* 按钮的信号与槽函数 */
connect(&m_testBtn, SIGNAL(clicked()), this, SLOT(onTestBtnClicked()));
}
void Widget::onTestBtnClicked()
{ /* 绘制数据的结构体的赋值 */
DrawParam dp =
{
qrand() % 3, /* 产生随机数 */
static_cast<Qt::PenStyle>(qrand() % 5 + 1), /* 随机选择绘制线样式 */
/* 有参构造函数 */
QPoint(qrand() % 400, qrand() % 300),
QPoint(qrand() % 400, qrand() % 300)
};
/* 链表中值储存5个图型 */
if( m_list.count() == 5 )
{
m_list.clear();
}
/* 将绘制数据结构体添加到链表中 */
m_list.append(dp);
/* 数据更新,就会触发绘制事件 */
update();
}
/* 将数据放到链表中,绘画事件处理函数中查询链表中是否有数据,有数据就会进行绘制 */
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter;
/* 绘图类打开 */
painter.begin(this);
/* 链表中有数据就进行绘制 */
for(int i=0; i<m_list.count(); i++)
{
/* 计算矩形和椭圆的左上角的x,y值,以及矩形的长和宽 */
int x = (m_list[i].begin.x() < m_list[i].end.x()) ? m_list[i].begin.x() : m_list[i].end.x();
int y = (m_list[i].begin.y() < m_list[i].end.y()) ? m_list[i].begin.y() : m_list[i].end.y();
int w = qAbs(m_list[i].begin.x() - m_list[i].end.x()) + 1; //qAbs()是qt中的求绝对值的函数
int h = qAbs(m_list[i].begin.y() - m_list[i].end.y()) + 1;
painter.setPen(m_list[i].pen);
switch(m_list[i].type)
{
case LINE: /* 画线 */
painter.drawLine(m_list[i].begin, m_list[i].end);
break;
case RECT: /* 画矩形 */
painter.drawRect(x, y, w, h);
break;
case ELLIPSE: /* 画椭圆 */
painter.drawEllipse(x, y, w, h);
break;
default:
break;
}
}
/* 绘图类关闭 */
painter.end();
}
Widget::~Widget()
{
}
注意:只有在运行了“update()”函数之后,QPainter实例化的对象才会进行图形的绘制。 没有这个函数,及时更新了链表数据,也不会进行绘制。
下面是运行的结果:
二、视口、窗口的设置与正弦波的绘制
这里就是在绘制视图的时候进行设置的,可以在用户自己想绘制的区域进行图形的绘制。视口是物理窗口的大小,坐标也是lcd的坐标,窗口的坐标是笛卡尔坐标,是用户可以设置的坐标。
下面是lcd坐标和笛卡尔坐标的变换:实际的测试中发现,窗口的尺寸的设置总是填满视口的。
下面是视口和窗口尺寸设置的函数:
下面是正弦波绘制的解决方案:
下面是示例代码:
void Widget::paintEvent(QPaintEvent *)
{ /* 实例化画师 */
QPainter painter(this);
QPen pen; /* 实例化画笔 */
pen.setColor(Qt::green); /* 绘画的线的颜色设置 */
pen.setStyle(Qt::SolidLine); /* 绘制的线的形式设置 */
pen.setWidthF(0.01); /* 绘制间隔的设置 */
painter.setPen(pen); /* 给painter对象设置pen,也就是将画笔给画师 */
/* 设置视口的尺寸 */
painter.setViewport(50, 50, width()-100, height()-100);
/* 设置窗口的尺寸,注意这里的尺寸和上面视图的尺寸不相同,
* 这里的20已经是视图里面的几百的距离了 */
painter.setWindow(-10, 2, 20, -4); // (-10, 2) (10, -2)
/* 设置窗口背景的颜色,fillRect()进行矩形填充 */
painter.fillRect(-10, 2, 20, -4, Qt::black);
/* 绘制x和y周的直线,drawLine()需要起始点和结束点,QPointF()给坐标就可以了 */
painter.drawLine(QPointF(-10, 0), QPointF(10, 0)); // x
painter.drawLine(QPointF(0, 2), QPointF(0, -2)); // y
/* 绘制sin的正弦曲线,其中“0.01” 是绘制曲线的步长 */
for(float x=-10; x<10; x+=0.01)
{
float y = qSin(x);
/* 绘制点 */
painter.drawPoint(QPointF(x, y));
}
}
运行的结果:
三,基础图形的绘制,随意绘制
上面第二章的正弦波的绘制主要就是视图和窗口的设置的使用,以及直线和点的绘制,上面的实现就可以绘制多种图形的显示。
本章介绍的主要就是用户随意绘制的实现,以及矩形和椭圆形的绘制。下面就是实现的要求。
下面是自由绘制图像的实施方案:
下面是绘制基础图形的解决方案:
下面是实现的示例代码:
#ifndef WIDGET_H
#define WIDGET_H
#include <QtGui/QWidget>
#include <QRadioButton>
#include <QComboBox>
#include <QGroupBox>
#include <QList>
#include <QPoint>
class Widget : public QWidget
{
Q_OBJECT
/* 不同的绘制图案情况的枚举,与按钮的选择有关 */
enum DrawType
{
NONE, /* 不进行绘制 */
FREE, /* 自由绘制 */
LINE, /* 绘制线 */
RECT, /* 绘制矩形 */
ELLIPSE /* 绘制椭圆 */
};
/* 存储数据点的结构体 */
struct DrawParam
{
DrawType type; //绘制的形式
Qt::GlobalColor color; //绘制的颜色
QList<QPoint> points; //绘制点存储的链表
};
QGroupBox m_group; /* group容器,可以框选和排序多个期间 */
QRadioButton m_freeBtn; /* 自由绘制的勾选按钮 */
QRadioButton m_lineBtn; /* 绘制直线的勾选按钮 */
QRadioButton m_rectBtn; /* 绘制矩形的勾选按钮 */
QRadioButton m_ellipseBtn; /* 绘制椭圆的勾选按钮 */
/* QComboBox 提供了一种以占用最小屏幕空间的方式向用户显示选项列表的方法。 */
QComboBox m_colorBox;/* 复选框,可以实现只能选中其中一个勾选按钮 */
QList<DrawParam> m_drawList; /* 将数据放到这个链表中,就相当于将绘制数据存储了 */
DrawParam m_current; /* 绘画数据结构体 */
DrawType drawType(); //绘画形式的函数
Qt::GlobalColor drawColor(); //绘画颜色的函数
void draw(QPainter& painter, DrawParam& param); //绘画函数
void append(QPoint p); //链表数据添加函数
protected:
void mousePressEvent(QMouseEvent *evt);
void mouseMoveEvent(QMouseEvent *evt);
void mouseReleaseEvent(QMouseEvent *evt);
void paintEvent(QPaintEvent *);
public:
Widget(QWidget *parent = 0);
~Widget();
};
#endif // WIDGET_H
#include "Widget.h"
#include <QMouseEvent>
#include <QPainter>
#include <QPen>
#include <QBrush>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
m_group.setParent(this); /* 容器group */
m_group.setTitle("Setting");
m_group.resize(600, 65);
m_group.move(20, 20);
m_freeBtn.setParent(&m_group); /* 自由绘制按钮 */
m_freeBtn.setText("Free");
m_freeBtn.resize(70, 30);
m_freeBtn.move(35, 20);
m_freeBtn.setChecked(true);
m_lineBtn.setParent(&m_group); /* 线的绘制按钮 */
m_lineBtn.setText("Line");
m_lineBtn.resize(70, 30);
m_lineBtn.move(140, 20);
m_rectBtn.setParent(&m_group); /* 矩形的绘制按钮 */
m_rectBtn.setText("Rect");
m_rectBtn.resize(70, 30);
m_rectBtn.move(245, 20);
m_ellipseBtn.setParent(&m_group); /* 椭圆的绘制按钮 */
m_ellipseBtn.setText("Ellipse");
m_ellipseBtn.resize(70, 30);
m_ellipseBtn.move(350, 20);
/* 颜色选择下拉框box */
m_colorBox.setParent(&m_group);
m_colorBox.resize(80, 25);
m_colorBox.move(480, 23);
m_colorBox.addItem("Black");
m_colorBox.addItem("Blue");
m_colorBox.addItem("Green");
m_colorBox.addItem("Red");
m_colorBox.addItem("Yellow");
/* 固定:fixed */
setFixedSize(width(), 600);
/* 设置绘制点结构体初始的状态 */
m_current.type = NONE;
m_current.color = Qt::white;
m_current.points.clear();
}
/* 不同按钮的选择 */
Widget::DrawType Widget::drawType()
{
DrawType ret = NONE;
/* 根据绘制的按钮是否被勾选,选择绘制对应的模式 */
if( m_freeBtn.isChecked() ) ret = FREE;
if( m_lineBtn.isChecked() ) ret = LINE;
if( m_rectBtn.isChecked() ) ret = RECT;
if( m_ellipseBtn.isChecked() ) ret = ELLIPSE;
return ret;
}
/* 绘画颜色的选择 */
Qt::GlobalColor Widget::drawColor()
{
Qt::GlobalColor ret = Qt::black;
/* 根据下拉框的文本,设置绘制的不同颜色 */
if( m_colorBox.currentText() == "Black") ret = Qt::black;
if( m_colorBox.currentText() == "Blue") ret = Qt::blue;
if( m_colorBox.currentText() == "Green") ret = Qt::green;
if( m_colorBox.currentText() == "Red") ret = Qt::red;
if( m_colorBox.currentText() == "Yellow") ret = Qt::yellow;
return ret;
}
/* 鼠标按键按下事件 */
void Widget::mousePressEvent(QMouseEvent *evt)
{ /* 鼠标按下事件中保存绘画形式,绘画的颜色,起始点,
* 这时候有个绘画的第一个结构体,在鼠标运动过程中会有数据点的数据放入链表中。
*/
m_current.type = drawType();
m_current.color = drawColor();
/* evt->pos()获得位置数据 */
m_current.points.append(evt->pos());
}
/* 鼠标按下的时候移动事件 */
void Widget::mouseMoveEvent(QMouseEvent *evt)
{
append(evt->pos());
/* 只有进行下面的函数,绘画事件函数才能绘画 */
update();
}
/* 鼠标释放事件 */
void Widget::mouseReleaseEvent(QMouseEvent *evt)
{
append(evt->pos());
/* 将绘画数据放入链表中,放入链表中数据之后,就相当于对绘图数据进行保存了,
* 绘制的时候,是根据这里链表里面的数据提取进行绘制。
*/
m_drawList.append(m_current);
/* 将数据点的结构体清空 */
m_current.type = NONE;
m_current.color = Qt::white;
/* 结构体中数据点链表的数据清空 */
m_current.points.clear();
/* 只有进行下面的函数,绘画事件函数才能绘画 */
update();
}
/* 向数据点链表中添加数据 */
void Widget::append(QPoint p)
{
if( m_current.type != NONE )
{ /* 如果是自由绘画的形式,那么数据点链表中就可以保存多个点 */
if( m_current.type == FREE )
{
m_current.points.append(p);
}
else
{ /* 如果图形的绘制,那么数据点链表就需要有两个数据点 */
if( m_current.points.count() == 2 )
{ /* 删除最后一个数据点 */
m_current.points.removeLast();
}
/* 将新的数据点添加到链表中 */
m_current.points.append(p);
}
}
}
/* 绘画事件 */
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
/* 数据链表中是否有数据机型绘制 */
for(int i=0; i<m_drawList.count(); i++)
{
draw(painter, m_drawList[i]);
}
/*
* 下面的绘制时非常重要的,可以临时的绘制曲线或者图形,只有鼠标按下松开之后,
* 才会将数据点存储到链表中,如果没有下面的语句,就会出现只有鼠标松开的时候才能进行绘制,
* 但是下面的绘制就可以实时绘制没有存到画板上。
*/
draw(painter, m_current);
}
/* 根据绘画结构体进行图像绘制的函数 */
void Widget::draw(QPainter& painter, DrawParam& param)
{
if( (param.type != NONE) && (param.points.count() >= 2) )
{ /* 算出来左上角的坐标点,以及长宽 */
int x = (param.points[0].x() < param.points[1].x()) ? param.points[0].x() : param.points[1].x();
int y = (param.points[0].y() < param.points[1].y()) ? param.points[0].y() : param.points[1].y();
int w = qAbs(param.points[0].x() - param.points[1].x()) + 1;
int h = qAbs(param.points[0].y() - param.points[1].y()) + 1;
/* 实例化一个画笔并且设置画笔的颜色,将画笔给绘画师 */
painter.setPen(QPen(param.color));
/* 实例化一个填充对对象并且赋予颜色,将填充色给绘画师(painter)的填充器(setBrush) */
painter.setBrush(QBrush(param.color));
/* 根据类型进行图形的绘制 */
switch(param.type)
{
case FREE: /* 根据获得的绘画数据点进行图像得绘制 */
for(int i=0; i<param.points.count()-1; i++)
{
painter.drawLine(param.points[i], param.points[i+1]);
}
break;
case LINE:
painter.drawLine(param.points[0], param.points[1]);
break;
case RECT:
painter.drawRect(x, y, w, h);
break;
case ELLIPSE:
painter.drawEllipse(x, y, w, h);
break;
default:
break;
}
}
}
Widget::~Widget()
{
}
运行的结果:
四、图像的处理、绘制、提取
qt中关于图像的处理和绘制的类主要有两个:“QImage”和“QPixmap”。下面的将针对这两个类进行描述.
因为qt中的“QImage”和“QPixmap”都是继承于“QPaintDevice”类,“QPaintDevice”类实例化的对象是直接可以在上面绘图的,所以在“QImage”和“QPixmap”类实例化的对象都可以进行图形的绘制。
下面是两个类的使用场景,两种类的区分还是比较大的。
(1)灰度处理(利用QImage)
QImage主要适用于图像处理方面,对图片的每一帧数据进行处理,下面是“QImage”类利用与图片的灰度化处理的代码框架。
下面是使用的一个代码示例:
#include <QtCore/QCoreApplication>
#include <QImage>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QImage img;
if( img.load("test.jpg") ) /* 打开图片 */
{
/* 对输出图像的像素进行从新设置,返回根据给定的纵横比模式和变换模式缩放到由给定大小定义的矩形的图像副本。 */
img = img.scaled(QSize(img.width() / 2, img.height() / 2));
/* 由像素的长和宽获取rgb像素点,下满的像素处理方式对于分辨率比较高的图片处理过程事件是比较长的。 */
for(int i=0; i<img.width(); i++)
{
for(int j=0; j<img.height(); j++)
{
/* 获取像素点 */
QRgb rgb = img.pixel(i, j);
int r = qRed(rgb); //red
int g = qGreen(rgb); //green
int b = qBlue(rgb); //blue
int gray = (r + g + b) / 3; //将像素点灰度化的处理方式
/* 将像素点处理之后进行存储 */
img.setPixel(i, j, qRgb(gray, gray, gray));
}
}
/* 保存图片 */
img.save("new.jpg");
}
return a.exec();
}
下面是灰度化处理的结果:
(2)截图和图像的显示(QPixmap)
QPixmap主要适用于图像的显示方面,会利用显卡等硬件进行图像显示的加速。下面是截图实现的知识。如下图所示的关于屏幕截图和窗口截图的函数时不同的,使用方式是相似的。
下面是代码的一个使用案例。
#ifndef WIDGET_H
#define WIDGET_H
#include <QtGui/QWidget>
#include <QPushButton>
#include <QPixmap>
class Widget : public QWidget
{
Q_OBJECT
QPushButton m_loadBtn;//加载图像的button
QPushButton m_grabBtn;//截图的按钮
/* QPixmap 类是一种可用作绘制设备的屏幕外图像表示形式. */
QPixmap m_pmap; //图像数据保存的对象
private slots:
void onLoadBtnClicked(); /* 加载图片的槽函数 */
void onGrabBtnClicked(); /* 截图的槽函数 */
protected:
void paintEvent(QPaintEvent *); /* 绘图事件的槽函数 */
public:
Widget(QWidget *parent = 0);
~Widget();
};
#endif // WIDGET_H
#include <QPainter>
#include <QFileDialog>
#include <QImage>
#include <QMessageBox>
#include <QApplication>
#include <QDesktopWidget>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
m_loadBtn.setParent(this);
m_loadBtn.move(10, 10);
m_loadBtn.resize(70, 30);
m_loadBtn.setText("Load");
m_grabBtn.setParent(this);
m_grabBtn.move(90, 10);
m_grabBtn.resize(70, 30);
m_grabBtn.setText("Grab");
resize(500, 350);
connect(&m_loadBtn, SIGNAL(clicked()), this, SLOT(onLoadBtnClicked()));
connect(&m_grabBtn, SIGNAL(clicked()), this, SLOT(onGrabBtnClicked()));
}
void Widget::onLoadBtnClicked()
{ /* 文件选择对话框 */
QFileDialog fd(this);
/* 文件选择对话框的设置 */
fd.setAcceptMode(QFileDialog::AcceptOpen);
fd.setFileMode(QFileDialog::ExistingFile);
if( fd.exec() == QFileDialog::Accepted )
{
QImage img;
/* 加载选择的所有文件中的第一个文件 */
if( img.load(fd.selectedFiles()[0]) )
{ /* 获取图片的所有的像素点 */
m_pmap = QPixmap::fromImage(img);
/* 更新绘图显示 */
update();
}
else
{ /* 错误消息提示的对话框 */
QMessageBox(QMessageBox::Critical, "Error", "Invalid image file!").exec();
}
}
}
void Widget::onGrabBtnClicked()
{
/* 对整个桌面的图像的截图抓取 */
m_pmap = QPixmap::grabWindow(QApplication::desktop()->winId());
/* 更新图像,显示数据。*/
update();
}
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter;
/* 如果存储像素的独享内容不为空,就进行显示 */
if( !m_pmap.isNull() )
{
painter.begin(this);
/* 指定长宽的图像绘制处理 */
painter.drawPixmap(0, 0, width(), height(), m_pmap);
painter.end();
}
}
Widget::~Widget()
{
}
运行的结果如下所示:(可以观察出截图之后的分辨率是下降的)。
注意:“QPixmap”和“QImage”之间是可以相互转化的。
所以就可以实现将图片截图之后进行灰度化处理之后,在将图像显示出来。二值化处理需要使用到“QImage”和“QPixmap”之间数据类型的相互转化的问题,下面是相互转化的代码示例:
QPixmap转为Image:
1 QPixmap pixmap;
2 pixmap.load("../Image/1.jpg");
3 QImage tempImage = pixmap.toImage();
QImage转为QPixmap:
1 QImage image;
2 image.load("../Image/1.jpg");
3 QPixmap tempPixmap = QPixmap::fromImage(image);
下面是运行的结果:
下面是示例代码:
#include "Widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
m_loadBtn.setParent(this);
m_loadBtn.move(10, 10);
m_loadBtn.resize(70, 30);
m_loadBtn.setText("Load");
m_grabBtn.setParent(this);
m_grabBtn.move(90, 10);
m_grabBtn.resize(70, 30);
m_grabBtn.setText("Grab");
resize(500, 350);
connect(&m_loadBtn, SIGNAL(clicked()), this, SLOT(onLoadBtnClicked()));
connect(&m_grabBtn, SIGNAL(clicked()), this, SLOT(onGrabBtnClicked()));
}
Widget::~Widget()
{
}
void Widget::onLoadBtnClicked()
{
qDebug()<<"onLoadBtnClicked"<<endl;
/* 文件选择对话框 */
QFileDialog fd(this);
/* 文件选择对话框的设置 */
fd.setAcceptMode(QFileDialog::AcceptOpen);
fd.setFileMode(QFileDialog::ExistingFile);
if( fd.exec() == QFileDialog::Accepted )
{
QImage img;
if(img.load(fd.selectedFiles()[0]))
{
/* 获取图片的所有的像素点 */
m_pmap = QPixmap::fromImage(img);
/* 更新绘图显示 */
update();
}else
{
/* 错误消息提示的对话框 */
QMessageBox(QMessageBox::Critical, "Error", "Invalid image file!").exec();
}
}
}
void Widget::onGrabBtnClicked()
{
qDebug()<<"onGrabBtnClicked"<<endl;
/* 对整个桌面的图像的截图抓取 */
//m_pmap = QPixmap::grabWindow(QApplication::desktop()->winId());
m_pmap = QPixmap::grabWidget(this);
photosDeal(m_pmap, m_image);
m_pmap.save("/home/book/My_qtCode/My_code_15/Image/photos/new1.jpg");
/* 更新图像,显示数据。*/
update();
}
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter;
if(!m_pmap.isNull())
{
painter.begin(this);
painter.drawPixmap(0, 0, width(), height(), m_pmap);
painter.end();
}
}
void Widget::photosDeal(QPixmap& pixmap, QImage& img)
{
img = pixmap.toImage();
/* 对输出图像的像素进行从新设置,返回根据给定的纵横比模式和变换模式缩放到由给定大小定义的矩形的图像副本。 */
img = img.scaled(QSize(img.width() / 2, img.height() / 2));
/* 由像素的长和宽获取rgb像素点,下满的像素处理方式对于分辨率比较高的图片处理过程事件是比较长的。 */
for(int i=0; i<img.width(); i++)
{
for(int j=0; j<img.height(); j++)
{QPixmap::fromImage(img);
/* 获取像素点 */
QRgb rgb = img.pixel(i, j);
int r = qRed(rgb); //red
int g = qGreen(rgb); //green
int b = qBlue(rgb); //blue
int gray = (r + g + b) / 3; //将像素点灰度化的处理方式
/* 将像素点处理之后进行存储 */
img.setPixel(i, j, qRgb(gray, gray, gray));
}
}
pixmap = QPixmap::fromImage(img);
}
五、文本的绘制(利用QPainter)
这里对文本的绘制时使用的图像绘制的方法,不是使用的控件。
重要的内容:绘画的方式主要由下面的两种,第一种是“drawText()”以坐标点进行图像的绘制;第二种是“drawText()”是以矩形的方式进行图形绘制的方式。
下面是进行文本绘制的一些基本的绘制参数:
下面是坐标点绘制和矩形绘制的两种绘制方式的示例演示:
下面是图形绘制的示例代码示例:
#include "Widget.h"
#include <QPainter>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
}
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this); /* 画师的实例化 */
painter.save(); /* 将绘制的格式进行保存 */
painter.setPen(Qt::black); /* 设置绘画的颜色 */
painter.setFont(QFont("Arial", 16)); /* 设置font,字体形式和字体的大小 */
painter.rotate(0); /* 设置旋转的角度 */
painter.drawText(30, 40, "D.T.Software"); /* 需要绘画的文本 */
painter.setPen(Qt::red);
painter.setFont(QFont("Comic Sans MS", 20));
painter.rotate(20);
painter.drawText(30, 40, "D.T.Software");
painter.restore(); /* 恢复保存的绘制的格式 */
painter.drawText(130, 140, "D.T.Software");
}
Widget::~Widget()
{
}
下面是运行的结果:
下面是进行图形的从小到大的显示的示例:
#include "Widget.h"
#include <QPainter>
#include <QFontMetrics>
#include <QFont>
#include <QRect>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
m_sizeFactor = 0;
m_timer.setParent(this);
connect(&m_timer, SIGNAL(timeout()), this, SLOT(onTimeout()));
m_timer.start(50);/* 50毫秒的定时器 */
}
void Widget::onTimeout()
{
/* 这里必须强制进行图像的更新 */
update();
}
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter; /* 绘画师QPainter的实例化 */
const QString text = "D.T.Software"; /* 绘制的文本字符串的设置 */
QFont font("Comic Sans MS", 5 + (m_sizeFactor++) % 100); /* 绘制字体样式和大小的设置,字体的大小每次都进行更新 */
QFontMetrics metrics(font); /* 获得字体font的矩形绘制指标 */
const int w = metrics.width(text); /* 根据指标对象获得矩形绘制的长和宽 */
const int h = metrics.height();
QRect rect((width()-w)/2, (height()-h)/2, w, h); /* 实例化并初始化矩形对象 */
painter.begin(this);
painter.setPen(Qt::blue); /* 设置绘制的颜色 */
painter.setFont(font); /* 设置绘制的字体 */
painter.drawText(rect, Qt::AlignCenter, text); /* 进行矩形文本的绘制 */
painter.end();
}
Widget::~Widget()
{
}
运行结果:
注意:在设置了字符串的字体之后,如何获得字符串整个矩形的的长度和宽度,从而计算出来坐标。下图中的代码就是解决方案
六、登录对话框的验证码
登录对话框前面已经做了一部分,这里也会从新介绍,主要就是文本位置的设置。验证码的显示主要使用到的知识点是:随机数的产生、以及利用随机数产生验证码的字符串以及字符串的颜色、规定区域色点的设置、字符串和色点的绘制、用户输入的字符串与验证码字符串的比较。
下面是关于随机数产生的解决方案:
下面是随机产生验证码:
下面是实际项目的示例代码:
#ifndef _QLOGINDIALOG_H_
#define _QLOGINDIALOG_H_
#include <QtGui/QDialog>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QTimer>
class QLoginDialog : public QDialog
{
Q_OBJECT
private:
QLabel UserLabel;
QLabel PwdLabel;
QLabel CaptLabel;
QLineEdit UserEdit;
QLineEdit PwdEdit;
QLineEdit CaptEdit;
QPushButton LoginBtn;
QPushButton CancelBtn;
QString m_user;
QString m_pwd;
QString m_captcha;
Qt::GlobalColor* m_colors;
QTimer m_timer;
private slots:
void LoginBtn_Clicked();
void CancelBtn_Clicked();
void Timer_Timeout();
protected:
void paintEvent(QPaintEvent *);
QString getCaptcha();
Qt::GlobalColor* getColors();
public:
QLoginDialog(QWidget *parent = 0);
QString getUser();
QString getPwd();
~QLoginDialog();
};
#endif
#include "QLoginDialog.h"
#include <QPainter>
#include <QTime>
#include <QDebug>
#include <QMessageBox>
QLoginDialog::QLoginDialog(QWidget* parent) : QDialog(parent, Qt::WindowCloseButtonHint),
UserLabel(this), PwdLabel(this), CaptLabel(this),
UserEdit(this), PwdEdit(this), CaptEdit(this),
LoginBtn(this), CancelBtn(this)
{
UserLabel.setText("User ID:"); //id标签
UserLabel.move(20, 30);
UserLabel.resize(60, 25);
UserEdit.move(85, 30); //id输入框
UserEdit.resize(180, 25);
PwdLabel.setText("Password:"); //Password标签
PwdLabel.move(20, 65);
PwdLabel.resize(60,25);
PwdEdit.move(85, 65); //Password输入框
PwdEdit.resize(180, 25);
PwdEdit.setEchoMode(QLineEdit::Password);
CaptLabel.setText("Captcha:"); //Captcha标签
CaptLabel.move(20, 100);
CaptLabel.resize(60, 25);
CaptEdit.move(85, 100); //Captcha输入框
CaptEdit.resize(85, 25);
CancelBtn.setText("Cancel"); //取消按钮
CancelBtn.move(85, 145);
CancelBtn.resize(85, 30);
LoginBtn.setText("Login"); //登录按钮
LoginBtn.move(180, 145);
LoginBtn.resize(85, 30);
m_timer.setParent(this);
setWindowTitle("Login"); //设置窗口标题
setFixedSize(285, 205); //设置窗口的尺寸
connect(&m_timer, SIGNAL(timeout()), this, SLOT(Timer_Timeout()));
connect(&LoginBtn, SIGNAL(clicked()), this, SLOT(LoginBtn_Clicked()));
connect(&CancelBtn, SIGNAL(clicked()), this, SLOT(CancelBtn_Clicked()));
/* 设置随机数种子,这里的QTime与定时器无关,需要包含这个头文件 */
qsrand(QTime::currentTime().second() * 1000 + QTime::currentTime().msec());
/* 首先获得验证码字符串和颜色,不然数据为空,进行绘制的时候就会报错,程序就不能运行 */
m_captcha = getCaptcha();
m_colors = getColors();
/* 定时器设置定时时间并开始运行 */
m_timer.start(100);
}
/* login按钮 */
void QLoginDialog::LoginBtn_Clicked()
{
qDebug() << "LoginBtn_Clicked() Begin";
/* 获得用户输入的验证码,并且去除空格 */
QString captcha = CaptEdit.text().replace(" ", "");
/* toLower是全部转化为小写的字符,在进行比较 */
if( m_captcha.toLower() == captcha.toLower() )
{ /* 获得用户输入的账号和密码 */
m_user = UserEdit.text().trimmed();
m_pwd = PwdEdit.text();
if( m_user == "" )
{ /* 消息提示对话框,直接使用的使用函数 */
QMessageBox::information(this, "Info", "User ID can NOT be empty!");
}
else if( m_pwd == "" )
{
QMessageBox::information(this, "Info", "Password can NOT be empty!");
}
else
{ /* 发送“accepted”事件结束登录对话框,并且返回“accepted”表示登录成功 */
done(Accepted);
}
}
else
{ /* 验证码输入错误的提示对话框 */
QMessageBox::critical(this, "Error", "The captcha is NOT matched!");
/* 获得验证码 */
m_captcha = getCaptcha();
CaptEdit.selectAll();
}
qDebug() << "LoginBtn_Clicked() End";
}
void QLoginDialog::CancelBtn_Clicked()
{
qDebug() << "CancelBtn_Clicked() Begin";
/* 对话框运行结束,并返回“Rejected”表示对话框登录失败,反馈给后端 */
done(Rejected);
qDebug() << "CancelBtn_Clicked() End";
}
QString QLoginDialog::getUser()
{
return m_user;
}
QString QLoginDialog::getPwd()
{
return m_pwd;
}
void QLoginDialog::Timer_Timeout()
{
m_colors = getColors();
/* 时间溢出,强制从新绘画数据 */
update();
}
/* 验证码的绘画 */
void QLoginDialog::paintEvent(QPaintEvent *)
{
QPainter painter(this);
/* 绘制矩形 */
painter.fillRect(180, 100, 84, 24, Qt::white);
/* 设置绘制的font */
painter.setFont(QFont("Comic Sans MS", 12));
for(int i=0; i<150; i++)
{
painter.setPen(m_colors[i%4]); /* 设置绘制的颜色 */
/* 利用随机数在矩形区域内绘制色点 */
painter.drawPoint(180 + qrand() % 84, 100 + qrand() % 24);
}
for(int i=0; i<4; i++)
{
painter.setPen(m_colors[i]); /* 设置绘制的颜色 */
/* 绘制文本,每个字符进行单独的绘制,注意这里的m_captcha[i]是字符,要转换为字符串的形式 */
painter.drawText(180 + 20 * i, 100, 20, 24, Qt::AlignCenter, QString(m_captcha[i]));
}
}
/* 随机产生验证码字符串 */
QString QLoginDialog::getCaptcha()
{
QString ret = "";
for(int i=0; i<4; i++)
{
int c = (qrand() % 2) ? 'a' : 'A'; /* 利用随机数确定a开始还是A开始 */
/* 利用随机数获得后续的字符串 */
ret += static_cast<QChar>(c + qrand() % 26);
}
return ret;
}
/* 随机获得颜色 */
Qt::GlobalColor* QLoginDialog::getColors()
{
static Qt::GlobalColor colors[4];
/* 随机产生四种颜色 */
for(int i=0; i<4; i++)
{ /* GlobalColor是颜色的枚举类型 */
colors[i] = static_cast<Qt::GlobalColor>(2 + qrand() % 16);
}
return colors;
}
QLoginDialog::~QLoginDialog()
{
}
运行结果: