PyQT Painter 绘制多边形
PyQT Painter drawing Polygons
在我的 QT 应用程序中,我绘制了很多这样的多边形:
我正在制作这些动画,因此一些多边形将获得新颜色。此动画每秒运行 4-5 次。
但是,调用 Qt.Painter()
4-5 times/second 的 paintEvent()
会重绘所有多边形 ,这会导致性能问题。它每秒只更新一次,太慢了。如下图所示,只有前 12 行的部分多边形需要更新:
[![在此处输入图片描述][2]][2]
在我读到的 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, 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()
调用(每次 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 允许仅对小部件的一部分(区域)安排更新,从而优化结果。这需要两个步骤:
- 调用
update(QRect)
使用适当的矩形,该矩形仅覆盖需要重新绘制的小部件部分;
- 检查
event.rect()
然后执行绘画以便只绘画该区域;
如果您确定只有前X行会改变颜色,那么:
self.draw_area.update(
QRect(0, 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, 0, self.cache)
def resizeEvent(self, event):
self.cache = None
上面的代码非常简单,您可以为每组行创建多个 QPicture,然后在需要时决定绘制哪一个,甚至可以结合上面解释的 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, 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)))
在我的 QT 应用程序中,我绘制了很多这样的多边形:
我正在制作这些动画,因此一些多边形将获得新颜色。此动画每秒运行 4-5 次。
但是,调用 Qt.Painter()
4-5 times/second 的 paintEvent()
会重绘所有多边形 ,这会导致性能问题。它每秒只更新一次,太慢了。如下图所示,只有前 12 行的部分多边形需要更新:
[![在此处输入图片描述][2]][2]
在我读到的 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, 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()
调用(每次 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 允许仅对小部件的一部分(区域)安排更新,从而优化结果。这需要两个步骤:
- 调用
update(QRect)
使用适当的矩形,该矩形仅覆盖需要重新绘制的小部件部分; - 检查
event.rect()
然后执行绘画以便只绘画该区域;
如果您确定只有前X行会改变颜色,那么:
self.draw_area.update(
QRect(0, 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, 0, self.cache)
def resizeEvent(self, event):
self.cache = None
上面的代码非常简单,您可以为每组行创建多个 QPicture,然后在需要时决定绘制哪一个,甚至可以结合上面解释的 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, 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)))