QT<八> 绘图事件、绘图设备

Qt 的绘图系统允许使用相同的 API 在屏幕和其它打印设备上进行绘制。整个绘图系统基于QPainter,QPainterDevice和QPaintEngine三个类。

下图给出了这三个类之间的层次结构:
在这里插入图片描述
上面的示意图告诉我们,Qt 的绘图系统实际上是,使用QPainter在QPainterDevice上进行绘制,它们之间使用QPaintEngine进行通讯(也就是翻译QPainter的指令)。

一、QPainter 绘图

1)绘图事件 void paintEvent()
重写QWidget的paintEvent()函数。

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

namespace Ui {
class Widget;
}
class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = nullptr);
    ~Widget();

    //绘图事件
    void paintEvent(QPaintEvent* qe);
private:
    Ui::Widget *ui;
};

2)声明一个画家对象

QPainter接收一个QPaintDevice指针作为参数。QPaintDevice有很多子类,比如QImage,以及QWidget。QPaintDevice可以理解成要在哪里去绘制,而现在我们希望画在这个组件,因此传入的是 this 指针。

 //实例画家类 this指定的是绘图的设备
QPainter painter(this);

3)画线、画圆、画矩形、画文字

//画线
void drawLine(const QLineF &line)
void drawLine(const QLine &line)
void drawLine(int x1, int y1, int x2, int y2)
void drawLine(const QPoint &p1, const QPoint &p2)
void drawLine(const QPointF &p1, const QPointF &p2)

//画圆
void drawEllipse(const QRectF &rectangle)
void drawEllipse(const QRect &rectangle)
void drawEllipse(int x, int y, int width, int height)
void drawEllipse(const QPointF &center, qreal rx, qreal ry)
void drawEllipse(const QPoint &center, int rx, int ry)

//画矩形
void drawRect(const QRectF &rectangle)
void drawRect(int x, int y, int width, int height)
void drawRect(const QRect &rectangle)

//画文字

void drawText(const QPointF &position, const QString &text)
void drawText(const QPoint &position, const QString &text)
void drawText(int x, int y, const QString &text)
void drawText(const QRectF &rectangle, int flags, const QString &text, QRectF *boundingRect = nullptr)
void drawText(const QRect &rectangle, int flags, const QString &text, QRect *boundingRect = nullptr)
void drawText(int x, int y, int width, int height, int flags, const QString &text, QRect *boundingRect = nullptr)
void drawText(const QRectF &rectangle, const QString &text, const QTextOption &option = QTextOption())
    //画直线
    painter.drawLine(QPoint(0,0),QPoint(100,100));
    //画圆  圆心 rx ry
    painter.drawEllipse(QPoint(100,100),50,50);
    //画矩形 x,y,w ,h
    painter.drawRect(QRect(20,20,50,50));
    //画文字 在一个范围内的框内 QRect(x,y,w,h),string
    painter.drawText(QRect(10,200,100,50),"啦啦啦啦啦啦");

4)设置画笔 QPen
设置画笔宽度 、风格

//设置画笔
    QPen pen(QColor(255,0,0));
    //设置画笔宽度
    pen.setWidth(5);
    //设置画笔风格
    pen.setStyle(Qt::DotLine);//虚线
    //使用画笔
    painter.setPen(pen);

5)设置画刷 QBrush
设置画刷 风格

    //设置画刷 填充实心图形颜色
    QBrush brush(Qt::green);
    //设置画刷风格
    brush.setStyle(Qt::Dense7Pattern);
    //使用画刷
    painter.setBrush(brush);

6 示例
在这里插入图片描述

#include "widget.h"
#include "ui_widget.h"
#include <QPainter>//画家类
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
}
void Widget::paintEvent(QPaintEvent* qe)
{
    //实例画家类 this指定的是绘图的设备
    QPainter painter(this);


    //画直线
    painter.drawLine(QPoint(0,0),QPoint(100,100));

    //画圆  圆心 rx ry
    painter.drawEllipse(QPoint(100,100),50,50);

    //设置画笔
    QPen pen(QColor(255,0,0));
    //设置画笔宽度
    pen.setWidth(5);
    //设置画笔风格
    pen.setStyle(Qt::DotLine);//虚线
    //使用画笔
    painter.setPen(pen);

    //设置画刷 填充实心图形颜色
    QBrush brush(Qt::green);
    //设置画刷风格
    brush.setStyle(Qt::Dense7Pattern);
    //使用画刷
    painter.setBrush(brush);

    //画矩形 x,y,w ,h
    painter.drawRect(QRect(20,20,50,50));

    //画文字 在一个范围内的框内 QRect(x,y,w,h),string
    painter.drawText(QRect(10,200,100,50),"啦啦啦啦啦啦");
}
Widget::~Widget()
{
    delete ui;
}

二、绘图高级设置

1、抗锯齿
效率低

void setRenderHint(QPainter::RenderHint hint, bool on = true)
enum QPainter::RenderHint
{
	QPainter::Antialiasing
	QPainter::HighQualityAntialiasing
	...
}

具体使用:

QPainter painter(this);
painter.drawEllipse(QPoint(100,100),100,100);
//设置抗锯齿能力 效率较低
painter.setRenderHint(QPainter::Antialiasing);
painter.drawEllipse(QPoint(300,100),100,100);

在这里插入图片描述

2、对画家进行移动

//移动
void QPainter::translate(const QPointF &offset)
void QPainter::translate(const QPoint &offset)
void QPainter::translate(qreal dx, qreal dy)
//保存画家坐标状态
void QPainter::save()
//还原画家坐标状态
void QPainter::restore()

具体使用:

    //画矩形
    painter.drawRect(20,20,100,100);
    //移动画家
    painter.translate(150,0);
    //保存画家状态
    painter.save();

    painter.drawRect(20,20,100,100);
    painter.translate(150,0);

    //还原画家保存的状态
    painter.restore();//恢复到之前保存的坐标

    painter.drawRect(20,20,150,150);

在这里插入图片描述
3、利用画家画图片

void QPainter::drawPixmap(const QRectF &target, const QPixmap &pixmap, const QRectF &source)
void QPainter::drawPixmap(const QRect &target, const QPixmap &pixmap, const QRect &source)
void QPainter::drawPixmap(int x, int y, int w, int h, const QPixmap &pixmap, int sx, int sy, int sw, int sh)
void QPainter::drawPixmap(int x, int y, const QPixmap &pixmap, int sx, int sy, int sw, int sh)
void QPainter::drawPixmap(const QPointF &point, const QPixmap &pixmap, const QRectF &source)
void QPainter::drawPixmap(const QPoint &point, const QPixmap &pixmap, const QRect &source)
void QPainter::drawPixmap(const QPointF &point, const QPixmap &pixmap)
void QPainter::drawPixmap(const QPoint &point, const QPixmap &pixmap)
void QPainter::drawPixmap(int x, int y, const QPixmap &pixmap)
void QPainter::drawPixmap(const QRect &rectangle, const QPixmap &pixmap)
void QPainter::drawPixmap(int x, int y, int width, int height, const QPixmap &pixmap)

具体使用:

    ///利用画家画资源图片
    QPainter painter(this);
    painter.drawPixmap(posX,posY,QPixmap(":/Image/up.png"));

4、手动调用绘图事件
利用update

void QWidget::update()

具体示例:
图片会每秒自动往下移动,点击按钮会往右移动

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = nullptr);
    ~Widget();

    //绘图事件
    void paintEvent(QPaintEvent* qe);
private:
    Ui::Widget *ui;
    int posX= 0;
    int posY= 0;
};

#endif // WIDGET_H

#include "widget.h"
#include "ui_widget.h"
#include <QPainter>//画家类
#include <QTimer>
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    //点击移动按钮 移动图片
    connect(ui->btn,&QPushButton::clicked,[=]()
    {
        posX += 20;
        //如果超出屏幕宽度 从0开始
        if(posX > this->width())  //this->height()高
            posX = 0;
        //如果要手动调用绘图事件,用update更新
        update();
    });

    //自动移动
    QTimer* timer = new QTimer(this);
    timer->start(1000);//1S

    connect(timer,&QTimer::timeout,this,[=](){
        posY += 20;
        //如果超出屏幕高度 从0开始
        if(posY > this->height())
            posY = 0;
        //如果要手动调用绘图事件,用update更新
        update();
    });

}
void Widget::paintEvent(QPaintEvent* qe)
{
    ///利用画家画资源图片
    QPainter painter(this);
    painter.drawPixmap(posX,posY,QPixmap(":/Image/up.png"));
}
Widget::~Widget()
{
    delete ui;
}

在这里插入图片描述

三、绘图设备

绘图设备是指继承QPainterDevice的子类。Qt一共提供了四个这样的类,分别是QPixmap、QBitmap、QImage和 QPicture。其中,

  • QPixmap专门为图像在屏幕上的显示做了优化
  • QBitmap是QPixmap的一个子类,它的色深限定为1,可以使用 QPixmap的isQBitmap()函数来确定这个QPixmap是不是一个QBitmap。
  • QImage专门为图像的像素级访问做了优化。
  • QPicture则可以记录和重现QPainter的各条命令。

1、QPixmap

QPixmap继承了QPaintDevice,因此,可以使用QPainter直接在上面绘制图形。
QPixmap也可以接受一个字符串作为一个文件的路径来显示这个文件,比如想在程序之中打开png、jpeg之类的文件,就可以使用 QPixmap。
使用QPainter的drawPixmap()函数可以把这个文件绘制到一个QLabel、QPushButton或者其他的设备上面。
QPixmap是针对屏幕进行特殊优化的,因此,它与实际的底层显示设备息息相关

//PixMap绘图设备 专门为平台做了显示的优化
QPixmap pix(300,300);
//默认为黑色
//填充颜色 白色
pix.fill(Qt::white);
//声明画家
QPainter painter(&pix);
//设置画笔颜色
painter.setPen(Qt::green);
painter.drawEllipse(QPoint(150,150),100,100);
//保存到电脑上
pix.save("D:/pix.png");

在这里插入图片描述
2、QBitmap
QBitmap继承自QPixmap,因此具有QPixmap的所有特性,提供单色图像
QBitmap的色深始终为1. 色深这个概念来自计算机图形学,是指用于表现颜色的二进制的位数。计算机里面的数据都是使用二进制表示的。为了表示一种颜色,我们也会使用二进制。比如我们要表示8种颜色,需要用3个二进制位,这时色深是3. 因此,所谓色深为1,也就是使用1个二进制位表示颜色。1个位只有两种状态:0和1,因此它所表示的颜色就有两种,黑和白。所以说,QBitmap实际上是只有黑白两色的图像数据
由于QBitmap色深小,因此只占用很少的存储空间,所以适合做光标文件和笔刷。

void PaintWidget::paintEvent(QPaintEvent *)
{
    QPixmap pixmap(":/Image/butterfly.png");
    QPixmap pixmap1(":/Image/butterfly1.png");
    QBitmap bitmap(":/Image/butterfly.png");
    QBitmap bitmap1(":/Image/butterfly1.png");

    QPainter painter(this);
    painter.drawPixmap(0, 0, pixmap);
    painter.drawPixmap(200, 0, pixmap1);
    painter.drawPixmap(0, 130, bitmap);
    painter.drawPixmap(200, 130, bitmap1);
}

在这里插入图片描述
白色的背景在QBitmap中消失了,而透明色在QBitmap中转换成了黑色;其他颜色则是使用点的疏密程度来体现的。

3、QImage

QImage则是使用独立于硬件的绘制系统,实际上是自己绘制自己,因此提供了像素级别的操作,并且能够在不同系统之上提供一个一致的显示形式

简单使用例子:
声明了一个QImage对象,大小是300 x 300,颜色模式是RGB32,即使用32位数值表示一个颜色的RGB值,也就是说每种颜色使用8位。

//QImage绘图设备 可以对像素进行访问
QImage img(300,300,QImage::Format_RGB32);
//默认黑色 填充颜色
img.fill(Qt::white);

QPainter painter(&img);
painter.setPen(Qt::blue);
painter.drawEllipse(QPoint(150,150),100,100);
//保存
img.save("D:/img.png");

在这里插入图片描述

利用QImage 对像素进行修改:

//绘图事件
void Widget::paintEvent(QPaintEvent *pe)
{//利用QImage 对像素进行修改
   QPainter painter(this);
   QImage img;
   //加载资源文件
   img.load(":/Image/sunny.png");
   //修改像素点
   for(int i =100;i<150;i++)
   {
       for(int j =100;j<150;j++)
       {
           QRgb value = qRgb(0,255,255);
           img.setPixel(i,j,value);
       }
   }
   painter.drawImage(0,0,img);
}

在这里插入图片描述
QImage与QPixmap的区别

  • QPixmap主要是用于绘图,针对屏幕显示而最佳化设计,QImage主要是为图像I/O、图片访问和像素修改而设计的
  • QPixmap依赖于所在的平台的绘图引擎,故例如反锯齿等一些效果在不同的平台上可能会有不同的显示效果,QImage使用Qt自身的绘图引擎,可在不同平台上具有相同的显示效果
  • 由于QImage是独立于硬件的,也是一种QPaintDevice,因此我们可以在另一个线程中对其进行绘制,而不需要在GUI线程中处理,使用这一方式可以很大幅度提高UI响应速度。
  • QImage可通过setPixpel()和pixel()等方法直接存取指定的像素。

QImage与QPixmap之间的转换:

  • QImage转QPixmap
    使用QPixmap的静态成员函数: fromImage()

    QPixmap	fromImage(const QImage & image, 
    Qt::ImageConversionFlags flags = Qt::AutoColor)
    
  • QPixmap转QImage:
    使用QPixmap类的成员函数: toImage()

    QImage toImage() const
    

4、QPicture

QPicture是一个可以记录和重现QPainter命令的绘图设备

QPicture将QPainter的命令序列化到一个IO设备,保存为一个平台独立的文件格式。这种格式有时候会是“元文件(meta- files)”。Qt的这种格式是二进制的,不同于某些本地的元文件,Qt的pictures文件没有内容上的限制,只要是能够被QPainter绘制的元素,不论是字体还是pixmap,或者是变换,都可以保存进一个picture中。

QPicture是平台无关的,因此它可以使用在多种设备之上,比如svg、pdf、ps、打印机或者屏幕。QPicture使用系统的分辨率,并且可以调整 QPainter来消除不同设备之间的显示差异。

如果要记录下QPainter的命令,首先要使用QPainter::begin()函数,将QPicture实例作为参数传递进去,以便告诉系统开始记录,记录完毕后使用QPainter::end()命令终止。代码示例如下:

 //QPicture 绘图设备 可以记录和重现绘图指令
 QPicture pic;
 QPainter painter;
 //指定画图设备 开始往Pic上画
 painter.begin(&pic);
 painter.setPen(QPen(Qt::black));
 painter.drawEllipse(QPoint(150,150),100,100);
 painter.end();//结束
 //保存到磁盘
 pic.save("D:/pic.aaa");//后缀无法直接打开
//绘图事件
void Widget::paintEvent(QPaintEvent *pe)
{
	//重现QPicture绘图指令
	QPicture pic;
	pic.load("D:/pic.aaa");//加载绘图指令
	QPainter painter(this);
	painter.drawPicture(0,0,pic);
}

在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
画出GraphItem类的UML类图: #ifndef GRAPHITEM_H #define GRAPHITEM_H #include <QGraphicsItem> #include <QVector> #include <QPointF> #include <QtMath> #include <QTimer> #include <QPaintEvent> #include <QPushButton> #include "mainwindow.h" #include <QObject> class MainWindow; // 前向声明 class GraphItem : public QGraphicsItem { public: //构造函数,接受带权无向图临界矩阵adjMatrix和一个parent GraphItem(const QVector<QVector<int>> &adjMatrix, const QVector<int> &shortestPath, QGraphicsItem *parent = nullptr); //纯虚函数,在.cpp中重新编写以实现图形项的边界框绘制 QRectF boundingRect() const override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; QVector<QPointF> m_nodePositions;//存储节点的位置信息 QVector<int> m_shortestPath; // 最短路径 QVector<int> m_userCity; QVector<int> m_userSchool; bool m_drawArrow; // 是否需要绘制箭头 bool m_draw1; bool m_draw2; bool m_draw3; private: MainWindow * m_mainWindow; // 指向MainWindow对象的指针 QVector<QVector<int>> m_adjMatrix;//存储带权无向图的邻接矩阵 QPointF startPoint; QPointF endPoint; QPainter * m_painter; QPainter * m_drawNodes; void initializeNodePositions();//初始化节点位置信息 void drawEdges(QPainter * painter);//用于绘制边 void drawNodes1(QPainter * painter);//用于绘制节点 void drawNodes2(QPainter * painter);//用于绘制节点 void drawNodes3(QPainter * painter);//用于绘制节点 void paintArrow(QPainter * painter); }; #endif // GRAPHITEM_H
07-14
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值