Qt的绘图主要在以下方面应用:
一、QPainter类:绘制2D图形。
1、Qt的2D绘图主要基于QPainter、QPaintDevice和QPaintEngine类。常用的是QPainter。
2、QPainter相关的类
类名 | 描述 |
QLine、QLineF |
表示线,包含两个坐标点 |
QMargins、QMarginsF |
表示了矩形的4个边距 |
QPoint、QPointF |
坐标点 |
QRect、QRectF |
矩形框 |
QSize、QSizeF |
表示宽度和高度 |
QBitmap |
用来绘制黑白图形 |
QIcon、QIconEngine |
以不同的模式和状态提供可伸缩的图标 |
QImage |
可以用来显示图片,并可以修改图片像素内容 |
QImageReader |
只能读取显示图片 |
QImageWriter |
用来生成图片 |
QPixmap |
用来显示图片,不可编辑 |
QBrush |
用来填充颜色 |
QConicalGradient、 QGradient、QLinearGradient、 QRadialGradient |
和QBrush结合实现颜色渐变填充 |
QColor |
为绘图提供颜色 |
QPainter |
绘制的核心类,画家 |
QPainterPath |
提供用于绘制操作的容器,允许构造和重用图形形状 |
QPainterPathStroker |
用于为给定的绘制器路径生成可填充的轮廓 |
QPdfWriter |
用来生成PDF文件 |
QPen |
画笔 |
QPolygon、QPolygonF |
保存多个坐标点的容器 |
QRegion |
指定画家的剪辑区域 |
QRgba64 |
包含64位RGB颜色 |
QTransform |
用于坐标系统的2D转换 |
QSvgGenerator |
用于创建SVG绘图的绘图设备 |
QSvgRenderer |
用于将SVG文件的内容绘制到绘图设备上 |
QSvgWidget |
用于显示可伸缩矢量图形(SVG)文件的内容 |
3、Qt中自带的可用于绘图的设备:QWidget、QImage、QPixmap、QPicture。
Widget::Widget(QWidget *parent)
: QWidget(parent),currentIndex(0)
{
resize(300,200);
QComboBox *comboBox = new QComboBox(this);
QStringList items;
items << "drawArc(弧形)" << "drawChord(弦)" << "drawConvexPolygon(凸多边形)" << "drawEllipse(圆形)"
<< "drawImage(图片)" << "drawLine(线)" << "drawPath(路径)" << "drawPie(饼形)" << "drawPixmap(图片)"
<< "drawPoint(点)" << "drawPoints(多个点)" << "drawPolyline(折线)" << "drawRect(矩形)"
<< "drawRoundedRect(圆角矩形)" << "drawStaticText(静态文本)" << "drawText(文本)"
<< "drawTiledPixmap(平铺图片)" << "eraseRect(擦除矩形)" << "fillRect(填充矩形)";
comboBox->addItems(items);
connect(comboBox,QOverload::of(&QComboBox::currentIndexChanged),[=](int index){
currentIndex = index;
this->update();
});
}
Widget::~Widget()
{
}
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
//设置填充颜色
painter.setBrush(QBrush(Qt::blue));
//获得画笔
QPen pen = painter.pen();
//设置颜色 和 宽度
pen.setColor(Qt::red);
pen.setWidthF(2);
painter.setPen(pen);
switch(currentIndex)
{
case 0:
{
//绘制圆弧 圆弧的起始弧度和跨度都必须是16的倍数
int startAngle = 0 * 16;//从0°角开始
int spanAngle = 60 * 16;//跨度为 60°
painter.drawArc(rect(),startAngle,spanAngle);
}
break;
case 1:
{
//绘制弦 和圆弧差不多
pen.setColor(Qt::green);
painter.setPen(pen);
int startAngle = 90*16;
int spanAngle = 90*16;
painter.drawChord(rect(),startAngle,spanAngle);
}
break;
case 2:
{
//绘制凸多边形
pen.setColor(Qt::yellow);
painter.setPen(pen);
painter.setBrush(QBrush(Qt::red));
static const QPointF points[4] = {
QPointF(10.0, 80.0),
QPointF(20.0, 10.0),
QPointF(80.0, 30.0),
QPointF(90.0, 70.0)
};
painter.drawConvexPolygon(points,4);
QPolygonF poly;
poly << QPointF(100.0,80.0) << QPointF(110.0,10.0) << QPointF(180.0,30.0) << QPointF(190.0,70.0);
painter.drawConvexPolygon(poly);
}
break;
case 3:
{
//绘制圆形
painter.setBrush(QBrush(Qt::green));
painter.drawEllipse(QPointF(200.0,100.0),30,30);
}
break;
case 4:
{
//绘制图片
QImage image("C:\\Users\\Pictures\\IMG_00000001.jpg");
//绘制图片全部
painter.drawImage(rect(),image);
//绘制该图片指定部分到指定矩形框内 如果图像和矩形大小不一致,则对图像进行缩放以适应矩形
painter.drawImage(QRectF(10.0,100,100.0,80.0),image,QRectF(0,0,image.size().width()*0.8,image.size().height()*0.8));
}
break;
case 5:
{
//绘制线
painter.drawLine(QLineF(10,50,250,150));
}
break;
case 6:
{
//绘制路径
//有了点就可以绘制路径了
pen.setColor(Qt::darkGreen);
painter.setPen(pen);
QPainterPath path;
if(!poly.isEmpty()){
path.moveTo(poly.first());
}
foreach (QPointF point, poly) {
path.lineTo(point);
painter.drawPath(path);
path.moveTo(point);
}
}
break;
case 7:
{
//绘制饼图
QRectF rectangle(10.0, 20.0, 80.0, 60.0);
int startAngle = 30 * 16;
int spanAngle = 120 * 16;
painter.drawPie(rectangle, startAngle, spanAngle);
}
break;
case 8:
{
QPixmap pixmap;
bool ok = pixmap.load("C:\\Users\\Pictures\\IMG_00000001.jpg");
if(ok){
painter.drawPixmap(rect(),pixmap);
}
}
break;
case 9:
{
pen.setWidth(10);
painter.setPen(pen);
QPoint point(rect().width()*0.3,rect().height()*0.3);
painter.drawPoint(point);
}
break;
case 10:
{
pen.setWidth(10);
pen.setColor(Qt::blue);
painter.setPen(pen);
QPolygonF polyF;
polyF << QPointF(100.0,80.0) << QPointF(110.0,100.0) << QPointF(180.0,30.0) << QPointF(190.0,70.0);
painter.drawPoints(polyF);
}
break;
case 11:
{
static const QPointF points[3] = {
QPointF(10.0, 80.0),
QPointF(20.0, 50.0),
QPointF(80.0, 30.0),
};
painter.drawPolyline(points, 3);
}
break;
case 12:
{
painter.drawRect(QRectF(100,100,150,50));
}
break;
case 13:
{
QRect rect(100,100,150,50);
//xRadius和yRadius参数指定定义圆角矩形的椭圆的半径。
painter.drawRoundedRect(rect,50,10);
}
break;
case 14:
{
QPointF pointF(100.0,100.0);
QStaticText staticText;
staticText.setTextFormat(Qt::AutoText);
staticText.setText("测试drawStaticText");
painter.drawStaticText(pointF,staticText);
}
break;
case 15:
{
QPointF pointF(50.0,100.0);
painter.drawText(pointF,"测试drawText");
}
break;
case 16:
{
QRectF rect(30,30,200,100);
//不会自动伸缩图片
painter.drawTiledPixmap(rect,QPixmap("C:\\Users\\Pictures\\IMG_00000001.jpg"));
}
break;
case 17:
{
painter.eraseRect(QRectF(100,100,150,50));
}
break;
case 18:
{
painter.fillRect(QRectF(100,100,150,50),Qt::yellow);
}
break;
default:
break;
}
//QPicture类是一个绘图设备,它记录和回放QPainter命令。
//图片以独立于平台的格式将画家的命令序列化到IO设备。
//可以在小部件或pixmap上绘制的所有内容也可以存储在图片中。
QWidget::paintEvent(event);
}
void Widget::mouseMoveEvent(QMouseEvent *event)
{
//获得当前坐标点
poly << event->pos();
update();
QWidget::mouseMoveEvent(event);
}
void Widget::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton){
if(currentIndex == 6){
poly.clear();
poly << event->pos();
}
}
QWidget::mousePressEvent(event);
}
还有一些高级用法包括:绘画状态的保存和恢复、在绘制设备上设置一个裁剪区域并在此区域内绘图、转换坐标系绘图等等。
二、Qt Quick模块:快速绘图(基于OpenGL来绘制),绘制2D、3D图形
三、以QOpenGL开头的类:绘制2D、3D图形
1、我对Qt中OpenGL的使用总结:
1)Qt是自带OpenGL的,所以不需要我们自己再去安装OpenGL。而且最新的Qt5已经把opengl模块集成到gui模块了
2)那么如何使用呢?我在看了网上很多资料,再结合帮助文档算是搞明白了。首先,Qt封装了opengl的接口,表示为以下的类:
QOpenGLWidget |
一个用于呈现OpenGL图形的控件 |
QOpenGLWindow |
是QWindow执行OpenGL绘制的一个方便的子类。 |
QOpenGLFunctions |
封装了OpenGL ES 2.0 API |
QOpenGLShader |
支持用OpenGL Shading Language (GLSL)和OpenGL/ES Shading Language (GLSL/ES)编写的着色器。 |
QOpenGLFramebufferObjectFormat |
指定OpenGL framebuffer对象的格式。 |
QOpenGLExtraFunctions |
封装了OpenGL ES 3.0和3.1 API |
QOpenGLDebugMessage |
包装OpenGL调试消息。 |
QOpenGLContextGroup |
表示一组共享OpenGL资源的上下文。 |
QOpenGLContext |
表示一个本地OpenGL上下文,支持在QSurface上呈现OpenGL。 |
QOpenGLBuffer |
创建和管理OpenGL缓冲对象 |
QOpenGLFramebufferObject |
封装了一个OpenGL framebuffer对象 |
QOpenGLShaderProgram |
允许OpenGL shader程序被链接和使用 |
QOpenGLTexture |
封装了一个OpenGL纹理对象 |
QOpenGLDebugLogger |
允许记录OpenGL调试消息 |
QOpenGLTimerQuery |
包装了一个OpenGL计时器查询对象 |
QOpenGLVertexArrayObject |
包装了一个OpenGL顶点数组对象 |
其中红色的是最基础的,QOpenGLWidget和QOpenGLWindow相当于绘图设备,所有基于OpenGL的绘图操作都是在这两个控件中进行,而如何绘图呢?使用QOpenGLFunctions中提供的方法即可(这个类似于QPinter类)。
其次,还可以在Qt Quick中使用,这个就涉及到QML编程了。
最后,仍然是在Qt Quick中支持绘制3D图形,在qml文件中添加import QtCanvas3D 1.0即可。这个也涉及到QML编程了。
2、基于QOpenGLWidget、QOpenGLWindow的OpenGL绘图。
1)实现三个虚函数:
void QOpenGLWidget::paintGL():在此函数内进行绘图操作,调用update()会调用此函数
void QOpenGLWidget::resizeGL(int w, int h):设置控件视图的宽高
void QOpenGLWidget::initializeGL():初始化OpenGL的资源和状态
2)在paintGL()中,当前上下文总是可以通过调用QOpenGLContext::currentContext()来访问。通过调用QOpenGLContext::functions(),可以从这个上下文中检索一个已经初始化的、随时可用的QOpenGLFunctions实例。
简单实例:
OpenGLWidget::OpenGLWidget(QWidget *parent)
:QOpenGLWidget(parent)
{
}
void OpenGLWidget::initializeGL()
{
//设置渲染上下文、加载着色器和其他资源等。
QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
funcs->glClearColor(1.0f,1.0f,1.0f,1.0f);//设置颜色缓存的清除值即清空当前的所有颜色
}
void OpenGLWidget::resizeGL(int w, int h)
{
//不处理
qDebug() << w << h;
}
void OpenGLWidget::paintGL()
{
//绘制图形
QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
funcs->glClear(GL_COLOR_BUFFER_BIT);//使用设置的颜色缓冲来清除缓冲区
}
四、Qt支持直接打印到实际打印机,本地或网络上,以及生成PDF输出。
1、打印相关的类
QAbstractPrintDialog |
用于配置打印机的打印对话框的基本实现。 |
QPageSetupDialog |
打印机页面设置对话框。 |
QPrintDialog |
打印机设置对话框。 |
QPrintPreviewDialog |
打印机预览对话框 |
QPrintEngine |
打印引擎 |
QPrinter |
打印机对象 |
QPrinterInfo |
打印机信息 |
QPrintPreviewWidget |
打印机预览界面 |
2、QPrinterInfo类。可以获取系统中所有的打印机信息
QString getState(QPrinter::PrinterState state)
{
switch (state) {
case QPrinter::Idle:
return "空闲";
case QPrinter::Active:
return "活动中";
case QPrinter::Aborted:
return "中止";
case QPrinter::Error:
return "出错";
default:
break;
}
return "";
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QTextEdit *edit = new QTextEdit;
edit->setReadOnly(true);
setCentralWidget(edit);
edit->append("此系统上所有可用打印机名称的列表:");
foreach (QString name, QPrinterInfo::availablePrinterNames()) {
edit->append(name);
}
edit->append("默认的打印机名称:");
edit->append(QPrinterInfo::defaultPrinterName());
edit->append("默认的打印机对象:");
QPrinterInfo info = QPrinterInfo::defaultPrinter();
if(info.isNull()){
edit->append("没有默认打印机");
}else{
edit->append(QString("此打印机名称:%1").arg(info.printerName()));
edit->append(QString("指示每张纸的一面或两面是否会印刷:%1").arg(info.defaultDuplexMode()));
edit->append(QString("当前打印机纸张默认大小:%1").arg(info.defaultPageSize().name()));
edit->append(QString("打印机的详细描述:%1").arg(info.description()));
edit->append(QString("是否是网络远程打印机:%1").arg(info.isRemote()?"true":"false"));
edit->append(QString("打印机的位置:%1").arg(info.location()));
edit->append(QString("打印机型号:%1").arg(info.makeAndModel()));
edit->append(QString("此打印机支持的最大物理页面大小:%1").arg(info.maximumPhysicalPageSize().name()));
edit->append(QString("此打印机支持的最小物理页面大小:%1").arg(info.minimumPhysicalPageSize().name()));
edit->append(QString("此打印机的当前状态:%1").arg(getState(info.state())));
edit->append("支持的分辨率大小:");
foreach (int dpi, info.supportedResolutions()) {
edit->append(QString::number(dpi)+"dpi");
}
edit->append(QString("是否支持自定义打印纸张大小:%1").arg(info.supportsCustomPageSizes()?"true":"false"));
}
}
3、QPrinter 类是打印的核心类,而QPrintPreviewWidget、QPageSetupDialog、QPrintPreviewDialog、QPrintDialog这些类都是界面化的api,实质上QPrinter类中都包含了这些功能。
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
resize(600,500);
QPrinter *myPrinter = new QPrinter;
QTextEdit *edit = new QTextEdit;
edit->setReadOnly(true);
setCentralWidget(edit);
QAction *openFile = menuBar()->addAction("打开文件");
QAction *openImage = menuBar()->addAction("打开图片");
QAction *printDialog = menuBar()->addAction("打印设置");
QAction *printPreviewDialog = menuBar()->addAction("打印预览");
QAction *printPreviewWidget = menuBar()->addAction("预览窗口");
QAction *pageSetupDialog = menuBar()->addAction("页面设置");
connect(openFile,&QAction::triggered,[=]{
QString path = QFileDialog::getOpenFileName(this,"选择文件","F:/","Text(*.txt)");
if(path.isEmpty())
return;
edit->clear();
QFile file(path);
file.open(QIODevice::ReadOnly);
QTextCodec *codec = QTextCodec::codecForName("GBK");
edit->textCursor().insertText(codec->toUnicode(file.readAll()));
qDebug() << edit->document()->blockCount();
});
connect(openImage,&QAction::triggered,[=]{
QString path = QFileDialog::getOpenFileName(this,"选择文件","F:/","Image(*.jpg *.png)");
if(path.isEmpty())
return;
QImage image;
if(image.load(path)){
edit->clear();
// edit->textCursor().insertImage(image);
QTextImageFormat format;
format.setObjectType(1);
format.setName(path);
edit->textCursor().insertImage(format);
qDebug() << edit->document()->blockCount();
}
});
connect(printDialog,&QAction::triggered,[=]{
QPrintDialog dialog(myPrinter);
connect(&dialog,QOverload::of(&QPrintDialog::accepted),[](QPrinter *printer){
//打印相关的设置变化了
qDebug() << printer->printerName();//打印机名称
qDebug() << printer->copyCount();//虚拟打印机默认的都是1
qDebug() << printer->fromPage();//起始页码
qDebug() << printer->pageLayout().orientation();//文档打印的方向
});
dialog.exec();
});
connect(printPreviewDialog,&QAction::triggered,[=]{
if(edit->document()->isEmpty()){
QMessageBox::warning(this,"提示","请先打开一个文件/图片");
return;
}
QPrintPreviewDialog dialog(myPrinter);
connect(&dialog,&QPrintPreviewDialog::paintRequested,[=](QPrinter *printer){
//判断是文件还是图片
bool isImage = edit->textCursor().block().textFormats().first().format.isImageFormat();
if(isImage){
edit->print(printer);
}else{
edit->print(printer);
}
});
dialog.exec();
});
connect(printPreviewWidget,&QAction::triggered,[=]{
QPrintPreviewWidget *widget = new QPrintPreviewWidget(myPrinter);
widget->setAttribute(Qt::WA_DeleteOnClose);
//必须先设置一下内容间距 否则 菜单无法显示
widget->setContentsMargins(QMargins(20,20,20,20));
widget->setContextMenuPolicy(Qt::CustomContextMenu);
connect(widget,&QPrintPreviewWidget::customContextMenuRequested,[=](const QPoint &pos){
QMenu menu;
QAction *printAction = menu.addAction("打印");
QAction *quitAction = menu.addAction("退出");
connect(printAction,&QAction::triggered,[=]{
widget->print();
});
connect(quitAction,&QAction::triggered,widget,&QPrintPreviewWidget::close);
menu.exec(widget->mapToGlobal(pos));
});
connect(widget,&QPrintPreviewWidget::destroyed,[]{
qDebug() << "销毁了...";
});
connect(widget,&QPrintPreviewWidget::paintRequested,[=](QPrinter *printer){
//判断是文件还是图片
bool isImage = true;//edit->textCursor().block().textFormats().first().format.isImageFormat();
if(isImage){
edit->print(printer);
}else{
edit->print(printer);
}
});
widget->show();
});
connect(pageSetupDialog,&QAction::triggered,[=]{
QPageSetupDialog dialog(myPrinter);
if(dialog.exec() == QPrintDialog::Accepted){
qDebug() << "Accepted";
//打印机的一些参数改变了
qDebug() << myPrinter->pageLayout().pageSize().name();
qDebug() << myPrinter->pageLayout().margins();
}else{
qDebug() << "Rejected";
}
});
}
五、Qt支持通过QImage类方便地读取、写入和操作图像。此外,对于如何加载或保存图像的更细粒度控制,可以分别使用QImageReader和QImageWriter类。要在Qt提供的图像格式之外添加对其他图像格式的支持,可以使用QImageIOHandler和QImageIOPlugin创建图像格式插件。
1、QImageReader是一个特殊的类,它在读取图像时提供了更多的控制。例如,可以通过调用setScaledSize()将图像读入特定大小,也可以通过调用setClipRect()选择剪辑矩形,从而有效地只加载图像的一部分。根据图像格式中的底层支持,这可以节省内存并加快图像的加载速度。要读取图像,首先构造一个QImageReader对象。将文件名或设备指针以及图像格式传递给QImageReader的构造函数。然后可以设置几个选项,例如clip rect(通过调用setClipRect())和scaled size(通过调用setScaledSize())。如果QImageReader能够读取图像(即,支持图像格式,设备打开读取)。调用read()读取图像。
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QLabel *label = new QLabel("这里显示图片");
label->setAlignment(Qt::AlignCenter);
setCentralWidget(label);
QAction *openFile = menuBar()->addAction("打开图片");
connect(openFile,&QAction::triggered,[=]{
QString path = QFileDialog::getOpenFileName(this,"选择图片","F:/","Image(*.jpg *.png *.txt)");
//普通方式
// QPixmap pixmap;
// if(pixmap.load(path)){
// label->setPixmap(pixmap);
// this->adjustSize();//主窗口自适应label大小
// }
QImageReader reader;
reader.setFileName(path);
if(reader.canRead()){
qDebug() << "图片的格式:"<setPixmap(QPixmap::fromImage(image));
}else{
QMessageBox::warning(this,"提示",tr("无法读取图片:%1").arg(reader.errorString()));
}
});
}
2、QImageWriter支持在存储图像之前设置特定格式的选项,比如gamma级别、压缩级别和质量。如果不需要这些选项,可以使用QImage::save()或QPixmap::save()。要存储图像,首先要构造QImageWriter对象。将文件名或设备指针以及图像格式传递给QImageWriter的构造函数。然后可以设置几个选项,例如gamma级别(通过调用setGamma())和quality(通过调用setQuality())。如果QImageWriter可以写入图像,则canWrite()返回true。,支持图像格式,设备是开放的,可以写)。调用write()将图像写入设备。
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
resize(300,200);
QAction *writerAction = menuBar()->addAction("截取窗口");
connect(writerAction,&QAction::triggered,[=]{
QString path = QFileDialog::getSaveFileName(this,"保存图片","F:/","Image(*.png)");
if(path.isEmpty())
return;
QImageWriter writer(path,"png");
//设置一些选项
// writer.setGamma();
// writer.setQuality();
if(writer.canWrite()){
//截窗口
QPixmap pixmap = this->grab();
writer.write(pixmap.toImage());
}
});
QAction *writerAction1 = menuBar()->addAction("截屏");
connect(writerAction1,&QAction::triggered,[=]{
QString path = QFileDialog::getSaveFileName(this,"保存图片","F:/","Image(*.jpg)");
if(path.isEmpty())
return;
QImageWriter writer(path);
//设置一些选项
writer.setFormat("jpg");
// writer.setGamma();
// writer.setQuality();
if(writer.canWrite()){
//截屏
QPixmap pixmap = QPixmap::grabWindow(0);
writer.write(pixmap.toImage());
}
});
}
MainWindow::~MainWindow()
{
}
void MainWindow::paintEvent(QPaintEvent *event)
{
QPainter painter;
if(painter.begin(this)){
//矩形
painter.drawRect(50,50,100,100);
//文字
painter.drawText(QRect(60,60,100,100),"hello world");
painter.end();
}
}