Qt之绘图的应用

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

扫描二维码关注公众号,回复: 4987715 查看本文章
以不同的模式和状态提供可伸缩的图标

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<int>::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("测试<font color='yellow'>drawStaticText</font>");
        painter.drawStaticText(pointF,staticText);
    }
        break;
    case 15:
    {
        QPointF pointF(50.0,100.0);
        painter.drawText(pointF,"测试<font color='blue'>drawText</font>");
    }
        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<QPrinter*>::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() << "图片的格式:"<<reader.format();
            qDebug() << "图像的格式:"<<reader.imageFormat();
            //设置只读取左上角的部分区域
            QRect leftRect(0,0,100,100);
            //在原图上直接裁剪
//            reader.setClipRect(leftRect);
            //先缩放之后再裁剪
            reader.setScaledSize(QSize(300,300));
            reader.setScaledClipRect(leftRect);
            QImage image = reader.read();
            label->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();
    }
}

猜你喜欢

转载自blog.csdn.net/wei375653972/article/details/86491492
今日推荐