1. QPaintEngine ,QPainterDvice , QPainter的关系
a. QPaintEngine跟平台相关,提供了一些接口给QPainter使用,可用于 QPainter 在不同的设备上进行绘制,如继承自QPaintEngine的QRasterPaintEngine(一般windows),QX11PaintEngine(linux)
b. QPainterDevice是一个可使用QPainter在之上进行绘制的基类,如QWidget,QImage,QPixmap等都是继承自QPainterDevice,有实际的高度height、宽度width,会将绘制出来的结果保存起来,所以会占用内存。另外QPainterDevice的虚函数QPaintEngine* paintEngine()返回了这个设备相关的QPaintEngine指针。
c.QPainter是一个与平台无关的负责绘制的类,它在绘制时必须要绑定一个QPainterDevice设备,这样才能在这个设备上进行绘制,在绘制一个图形时,通过它的接口实际它会调用与之绑定的QPainterDevice的QPaintEngine指针去实现具体的绘制。
2.关于QPainter
由上可知QPainter其实是与平台不相关的一个负责绘制的类,里面实现了几乎所有图形绘制的接口,包括绘制点、线、矩形、弧形、饼状图、多边形、贝塞尔弧线等 。并且还提供了反走样等对绘制效果就行修正的高级设置,同时还可以通过它对设备上的任意位置进行快速绘制,接下来就来讲讲QPainter的具体用法。
i.在哪里可以使用QPainter?
一般地,QPainter通常在paintEvent事件中使用的最多,也是Qt基于QWidget进行绘制开发的最基本的显示实现情况,所有基于QWidget的Qt控件,其实都是通过paintEvent中用QPainter进行绘制显示的。其他使用的情况如QPixmap,QImage设备的绘制也是可以通过QPainter进行绘制的,例如:
QPixmap pixmap(QSize(300,300));
QPainter painter(&pixmap);
painter.setPen(QColor(Qt::red));
painter.drawRect(QRect(10,10,200,200));
这样就实现了在QPixmap上绘制了一个红色边线矩形,并保存到了pixmap中(不会被局限于QWidget的paintEvent中,可以试试不在QWidget的paintEvent中进行绘制,看会有什么结果?)
ii.QPainter的绘制接口
绘制接口Qt的帮助文档中一大推,输入QPainter查看拿来用就好 这里介绍一些注意事项。
ii-1.关于边线的问题
我们在使用QPainter进行绘制的时候,默认边线的宽度是1px宽, 如果我 们绘制一个矩形,并设置线的宽度为6,其实结果矩形的线条会和填充区域有线宽一般的重叠部分(这里就是3),
QPen p(painter.pen());
p.setWidth(6);
painter.setPen(p);
painter.drawRect(QRect(10,10,200,200));
可能想要的预期结果 vs 实际的结果:
黄色区域即实际显示出来的填充区域
所有的描边都是这样的,线宽的一般会和填充部分有重叠(这个在处理线条带有alpha时如果线宽很大然后又有填充值那么就会出现第三种颜色)。如果要做到不重叠,就需要计算到这个线宽值把填充和描边分开绘制(坑啊…)
ii-2.关于QWidget的update和repaint
关于update和repaint,都可以实现用户调用后触发QWidget的paintEvent事件(除非自己取消的Qt的双缓存机制 自己去渲染),区别在于,前者是通过消息队列的方式产生驱动绘制事件的执行,后者是直接产生执行绘制事件。并且以界面绘制次数频率等考虑 ,update是要优于repaint的它会进行一些重复绘制请求的合并,从而降低绘制的频率,而repaint就是很老实,你调用多少次他就会马上执行绘制多少次,所以很容易不小心就会导致界面卡死。这下知道调用哪一个吧?你懂得。
ii-3.关于qpainter的save和restore
是否有过绘制很多不同样式颜色图形的经历?如果给你这样的需求:
1.先在当前位置绘制一个无边线的红色填充的矩形,
2.移动QPointF(20,20)然后再绘制一个黑色边线的无填充矩形
3.再回到之前的位置绘制一个无边线的绿色矩形
可能最开始的时候都是这样写的
//无边线的红色填充的矩形
painter.setPen(Qt::NoPen);
painter.setBrush(Qt::red);
painter.drawRect(rect());
//黑色边线的无填充矩形
painter.transform(20,20);
painter.setPen(Qt::Black);
painter.setBrush(Qt::NoBrush);
painter.drawRect(rect());
//无边线的红色填充的矩形
painter.transform(-20,-20); //移动回去
painter.setPen(Qt::NoPen);
painter.setBrush(Qt::green);
painter.drawRect(rect());
可能这个列子不是那么适合,但这里我想说的是其实在绘制第二个矩形前,可以加上painter.save()以保留当前painter的状态,然后在绘制完成后painter.restore()恢复状态,从而就不用再在绘制第三个矩形前调用painter.transform(-20,-20)和painter.setPen(Qt::NoPen);修改后如下:
//无边线的红色填充的矩形
painter.setPen(Qt::NoPen);
painter.setBrush(Qt::red);
painter.drawRect(rect());
//保存当前的painter状态
painter.save();
//黑色边线的无填充矩形
painter.transform(20,20);
painter.setPen(Qt::Black);
painter.setBrush(Qt::NoBrush);
painter.drawRect(rect());
//还原painter状态
painter.restore();
//无边线的红色填充的矩形
painter.setBrush(Qt::green);
painter.drawRect(rect());
暂时就分享这些 以后再添加更多的知识吧 谢谢。
(以上为个人理解,仅供参考哈,原文https://www.jdeverything.com/index.php/2020/04/21/qt%e5%85%b3%e4%ba%8e%e4%ba%8c%e7%bb%b4%e5%9b%be%e5%bd%a2%e7%9a%84%e7%bb%98%e5%88%b6/)