PyQT Painter 绘制多边形

问题描述

在我的 QT 应用程序中,我绘制了很多这样的多边形:

enter image description here

我正在为这些动画制作动画,因此某些多边形将获得新颜色。此动画每秒运行 4-5 次。

但是,以 4-5 次/秒的速度调用 paintEvent()Qt.Painter() 重绘所有多边形会导致性能问题。每秒只更新一次,太慢了。如下图所示,只有前 12 行中的一些多边形需要更新:

enter image description here

在我读过的 QT 文档中,您无法真正保存已经绘制的事物的状态。所以你必须重新绘制所有内容。我错过了什么吗?有什么技巧可以实现这一目标吗?

这就是我的 paintEvent() 的基本样子(简化,降低圈复杂度)

for y in range(len(self.array)):
  for x in range(len(self.array[0])):
    if(this): # simplified to reduce cyclomatic complexity
      painter.setBrush(QBrush(QColor(20,255)))
    elif(that):
      painter.setBrush(QBrush(QColor(175,175,175)))
    else:
      painter.setBrush(QBrush(QColor(0,0)))
    hexa_size = self.array[y][x]
    hexas = createHexagon(x,y,hexa_size) # external functions to calculate the hexagon size and position
    painter.drawpolygon(hexas)
painter.end()

调用(每次 Pin 更改时更新):

while True:
  while(stempel.readPin(0) == 0):
    QApplication.processEvents()
    time.sleep(0.01)
  self.draw_area.update() # Pin state changed,update polygons
  while(stempel.readPin(0) == 1):
    QApplication.processEvents()
    time.sleep(0.01)

解决方法

Qt 只允许为小部件的一部分(区域)安排更新,从而优化结果。这需要两步:

  1. 使用适当的矩形调用 update(QRect),该矩形仅覆盖需要重新绘制的小部件部分;
  2. 检查 event.rect(),然后实现绘制以仅绘制该区域;

如果您确定只有前 X 行会改变颜色,那么:

self.draw_area.update(
    QRect(0,self.draw_area.width(),<height of the repainted rows>)

然后,在paintEvent中:

if event.rect().bottom() < <height of the repainted rows>:
    rowRange = range(indexOfTheLastRowToRepaint + 1)
else:
    rowRange = range(len(self.array))

请注意,另一种解决方案可能是使用 QPicture,这是一种“序列化”QPainter 以提高性能并避免不必要的计算的方法。

class DrawArea(QWidget):
    cache = None
    def paintEvent(self,event):
        if not self.cache:
            self.cache = QPicture()
            cachePainter = QPainter(self.cache)
            # draw on the painter
            cachePainter.end()
        painter = QPainter(self)
        painter.drawPicture(0,self.cache)

    def resizeEvent(self,event):
        self.cache = None

上面的代码非常简约,您可以为每一组行创建多个 QPictures,然后在需要时决定哪一个绘制,甚至可以结合上面解释的 event.rect() 检查。

这种技术的主要好处是 QPainter 通常处理 QPicture 的速度非常快,因此您不必为行、多边形等进行所有所需的计算。

最后,您提供的图像似乎非常重复,几乎就像纹理一样。在这种情况下,您可能会考虑对每组行使用 QPixmap,然后使用该 QPixmap 创建 QBrush。在这种情况下,您只需调用 painter.fillRect(self.rect(),self.textureBrush)

,

通过使用 QGraphicsScene + QGraphicsView 自己解决:

self.scene = QGraphicsScene()
self.graphicView = QGraphicsView(self.scene,self)

创建一个保存所有多边形的列表:

self.polygons = [ [0] * len(array[0]) for _ in range(len(array))]

所有多边形的初始绘制:

for y in range(len(array)):
  for x in range(len(array[0])):
    polygon_size = self.array[y][x]
    polygon = createPoly(x,y,polygon_size)
    self.polygons[y][x] = self.scene.addPolygon(polygon,QPen(Qt.NoPen),QBrush(Qt.black))
  if(y % 50 == 0): QApplication.processEvents()

单独更新行:

for poly_size in active_rows:
  for active_row in active_rows[poly_size]:
    for x in range(0,len(array[0])):
      if(array[active_row][x] == int(poly_size)):
        self.polygons[active_row][x].setBrush(QBrush(QColor(20,255)))
      if(array[active_row - 2][x] > 0 and array[active_row - 2][x] == int(poly_size)):
        self.polygons[active_row - 2][x].setBrush(QBrush(QColor(175,175,175)))