gpt4 book ai didi

python - QPainters 同步背后的机制

转载 作者:行者123 更新时间:2023-12-01 00:51:47 27 4
gpt4 key购买 nike

上下文

我有一个自定义小部件,它应该制作点移动的动画,以便制作一种加载小部件。为了实现这个目标,我开始使用 QPainter 和 QVariantAnimation 对象,这似乎是完成这项工作的不错的工具。问题是我认为我在绘图时初始化的 QPainters 相互冲突。

技术

为了实现这一点,我初始化了多个 QVariantAnimation,它向 .valueChanged() 发出信号,我连接到函数 update(),该函数应该启动 painEvent(),如文档中所写

A paint event is a request to repaint all or part of a widget. It can happen for one of the following reasons:repaint() or update() was invoked, the widget was obscured and has now been uncovered, or many other reasons.

由于我在不同的时间启动不同的动画,我认为 update() 被调用了很多次,从而干扰了另一个已经工作的 QPainter。但是,正如我在文档中读到的,

When update() is called several times or the window system sends several paint events, Qt merges these events into one event with a larger region.

但它没有指定 QPainter 具有相同区域的任何内容,这就是我认为它崩溃的原因。它记录如下消息:

QBackingStore::endPaint() called with active painter on backingstore paint device

最小工作示例

from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QDialog, QPushButton
from PyQt5.QtCore import Qt, pyqtSlot, QVariantAnimation, QVariant, QTimer
from PyQt5.QtGui import QColor, QPainter, QBrush
import time

class Dialog(QDialog):
def __init__(self, *args, **kwargs):
QDialog.__init__(self, *args, **kwargs)
self.resize(500, 500)
self.setLayout(QVBoxLayout())
self.button = QPushButton()
self.layout().addWidget(self.button)
self.paintWidget = PaintWidget()
self.layout().addWidget(self.paintWidget)
self.button.clicked.connect(self.paintWidget.startPainting)
self.button.clicked.connect(self.reverse)

def reverse(self):
if self.paintWidget.isMoving:
self.paintWidget.stopPainting()

class PaintWidget(QWidget):
def __init__(self):
super(PaintWidget, self).__init__()
self.dotRadius = 10
self.dotColor = QColor(255, 100, 100)
self.numberOfDots = 3

self.isMoving = False

self.animation = []
self.createAnimation()

self.dotPosition = [[0, 0], [0, 0], [0, 0]]

def startPainting(self):
for i in range(self.numberOfDots):
self.animation[i].start()
time.sleep(200)
self.isActive = True

def createAnimation(self):
for i in range(self.numberOfDots):
self.animation.append(QVariantAnimation(self, startValue=0, endValue=500, duration=3000))
self.animation[i].valueChanged.connect(self.updatePosition)

@pyqtSlot(QVariant)
def updatePosition(self, position):
self.dotPosition = [position, 0]
self.update()

def paintEvent(self, event):
painter = QPainter(self)
painter.fillRect(self.rect(), Qt.transparent)
painter.setRenderHint(QPainter.Antialiasing, True)
painter.setPen(Qt.NoPen)

for i in range(self.numberOfDots):
painter.save()
painter.translate(0, 0)
position = (self.dotPosition[i][0], self.dotPosition[i][1])
color = self.dotColor
painter.setBrush(QBrush(color, Qt.SolidPattern))
painter.drawEllipse(position[0], position[1], self.dotRadius, self.dotRadius)
painter.restore()


if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
dial = Dialog()
dial.show()
sys.exit(app.exec_())

结果

我知道目前这段代码不起作用,因为我无法检索哪个点的动画已更新,但我相信这里的主要问题是画家之间的干扰。因此,可以有人告诉我为什么会发生这种情况并指出我可能的解决方案吗?另外,为了知道更新的点并选择了好的位置,我真的不确定如何做到这一点。

最佳答案

您的代码有以下错误:

  • 切勿在主 GUI 线程中使用 time.sleep(),因为它会阻止事件循环,从而导致应用程序卡住。

  • 变量 dotPosition 必须存储您在 updatePosition 方法中仅用一个位置替换它的所有位置。

  • 如果您要存储位置而不是列表,则应该使用 QPoint,使用列表还不错,但使用 QPoint 可以使您的代码更具可读性。

  • 不必要时不要使用painter.save()和painter.restore(),也不要使用painter.translate()。

综合以上情况,解决方案如下:

from functools import partial
from PyQt5 import QtCore, QtGui, QtWidgets


class Dialog(QtWidgets.QDialog):
def __init__(self, *args, **kwargs):
super(Dialog, self).__init__(*args, **kwargs)
self.resize(500, 500)

self.button = QtWidgets.QPushButton()
self.paintWidget = PaintWidget()

self.button.clicked.connect(self.paintWidget.startPainting)

lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.button)
lay.addWidget(self.paintWidget)


class PaintWidget(QtWidgets.QWidget):
def __init__(self):
super(PaintWidget, self).__init__()
self.dotRadius = 10
self.dotColor = QtGui.QColor(255, 100, 100)

self.animations = []

self.dotPosition = [
QtCore.QPoint(0, 0),
QtCore.QPoint(0, 0),
QtCore.QPoint(0, 0),
]

self.createAnimation()

def startPainting(self):
for i, animation in enumerate(self.animations):
QtCore.QTimer.singleShot(i * 200, animation.start)

def createAnimation(self):
for i, _ in enumerate(self.dotPosition):
wrapper = partial(self.updatePosition, i)
animation = QtCore.QVariantAnimation(
self,
startValue=0,
endValue=500,
duration=3000,
valueChanged=wrapper,
)
self.animations.append(animation)

@QtCore.pyqtSlot(int, QtCore.QVariant)
def updatePosition(self, i, position):
self.dotPosition[i] = QtCore.QPoint(position, 0)
self.update()

def paintEvent(self, event):
painter = QtGui.QPainter(self)
painter.fillRect(self.rect(), QtCore.Qt.transparent)
painter.setRenderHint(QtGui.QPainter.Antialiasing, True)
painter.setPen(QtCore.Qt.NoPen)
painter.setBrush(
QtGui.QBrush(self.dotColor, QtCore.Qt.SolidPattern)
)

for position in self.dotPosition:
painter.drawEllipse(position, self.dotRadius, self.dotRadius)


if __name__ == "__main__":
import sys

app = QtWidgets.QApplication(sys.argv)
dial = Dialog()
dial.show()
sys.exit(app.exec_())

关于python - QPainters 同步背后的机制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56516618/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com