Search code examples
pythonpyqtqpainterpaintevent

PyQT Painter drawing Polygons


In my QT application I'm drawing lots of polygons like this:

I'm animating these, so some polygons will receive a new color. This animation runs 4-5 times per second.

However, calling the paintEvent() of the Qt.Painter() 4-5 times/second redraws ALL polygons which results in performance issues. Its only updated once a second, which is too slow. As you may see in the picture below, only some polygons in the first 12 rows needs to be updated:

[![enter image description here][2]][2]

In the QT docs I have read that you can't really save the state of the things you've already drawn. So you have to redraw everything again. Am I missing something? Is there a trick to still achieve this?

This is what my paintEvent() basically looks like (simplified, reduced cyclomatic complexity)

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, 0, 255)))
    elif(that):
      painter.setBrush(QBrush(QColor(175, 175, 175)))
    else:
      painter.setBrush(QBrush(QColor(0, 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()

call (update on each Pin change):

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)

Solution

  • Solved it myself by using a QGraphicsScene + QGraphicsView:

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

    Creating a list where all polygons are being saved:

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

    Initial drawing of all polygons:

    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()
    

    Update rows indivudually:

    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, 0, 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)))