在使用PyQt5绘制图形的过程中,如果想要避免绘制过程中产生的拖影,就必须使用双缓冲绘图。但是网上的中文资料并没有很好的演示这一点,《PyQt5快速开发与实战》这本书中给出的例子也是残缺的(可以参考GitHub上的代码 https://github.com/cxinping/PyQt5/blob/master/Chapter08/qt08_winDraw03.py)。
废话不多说,下面的代码示例演示了:如何在空白画布上绘制矩形图案,避免拖影且能实时看到矩形。
import sys
from PyQt5.QtWidgets import QApplication ,QWidget
from PyQt5.QtGui import QPainter ,QPixmap
from PyQt5.QtCore import Qt , QPoint
class Winform(QWidget):
def __init__(self, parent=None):
super(Winform, self).__init__(parent)
self.setWindowTitle("双缓冲绘图示意")
self.resize(600, 500)
self.pix = QPixmap(400, 400) # 400*400画布
self.pix.fill(Qt.white) # 白色填充
self.temp = QPixmap() # 辅助画布
self.lastPoint = QPoint() # 起点
self.endPoint = QPoint() # 终点
def paintEvent(self, event):
"""
重载绘制事件。
将self.pix中的内容复制到缓存中,在缓存上绘图。
"""
x = self.lastPoint.x()
y = self.lastPoint.y()
w = self.endPoint.x() - x
h = self.endPoint.y() - y
self.temp = self.pix.copy() # 如果直接赋值,两者的内存是相同的,因此这里需要调用copy。
pp = QPainter(self.temp)
pp.drawRect(x,y,w,h)
painter = QPainter(self)
painter.drawPixmap(0, 0, self.temp)
def mousePressEvent(self, event):
"""
按下鼠标左键后,将当前位置存储到起点中。
"""
if event.button() == Qt.LeftButton :
self.lastPoint = event.pos()
def mouseMoveEvent(self, event):
"""
鼠标左键被按下且在滑动中,调用绘图函数,更新画布内容。
"""
if event.buttons() and Qt.LeftButton: # 这里的写法非常重要
self.endPoint = event.pos()
self.update()
def mouseReleaseEvent(self, event):
"""
松开鼠标左键后,将当前位置存储到终点中;绘制图案,并将缓存中的内容更新到self.pix中。
"""
self.endPoint = event.pos()
self.update()
self.pix = self.temp.copy()
if __name__ == "__main__":
app = QApplication(sys.argv)
form = Winform()
form.show()
sys.exit(app.exec_())
在这里需要注意的要点有:
1. 每次绘图前进行复制,将已经绘制好的矩形复制到缓存画布上。这样一来,画布上的内容不受影响,且能够通过缓存画布实时显示当前正在绘制的矩形。(复制过程让缓存画布丢弃了上一次绘制中的矩形,因此也就消除了拖影。)
2. 绘制结束后,即鼠标释放时,将缓存画布上的内容复制到画布上,保证其内容得到更新。
3. 调用copy函数,以便画布和缓存画布的内存是不同的。
4. 在`mouseMoveEvent`中使用` event.buttons() and Qt.LeftButton`做条件判断保证绘制事件可以随鼠标的移动而触发。