在 PySide/PyQt 中使用 drawPolyline 制作动画波浪
Posted
技术标签:
【中文标题】在 PySide/PyQt 中使用 drawPolyline 制作动画波浪【英文标题】:Make an animated wave with drawPolyline in PySide/PyQt 【发布时间】:2012-02-27 12:29:25 【问题描述】:我正在尝试为多段线设置动画(它必须像波浪一样)。我试过这种方式:
from PySide.QtCore import *
from PySide.QtGui import *
import sys, time
class Test(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
def poly(self, pts):
return QPolygonF(map(lambda p: QPointF(*p), pts))
def paintEvent(self, event):
painter = QPainter(self)
pts = [[80, 490], [180, 0], [280, 0], [430, 0], [580, 0], [680, 0], [780, 0]]
for i in pts:
while i[1] < 600:
painter.setPen(QPen(QColor(Qt.darkGreen), 3))
painter.drawPolyline(self.poly(pts))
painter.setBrush(QBrush(QColor(255, 0, 0)))
painter.setPen(QPen(QColor(Qt.black), 1))
for x, y in pts:
painter.drawEllipse(QRectF(x - 4, y - 4, 8, 8))
i[1] += 1
print pts
time.sleep(0.0025)
self.update()
if __name__ == '__main__':
example = QApplication(sys.argv)
test2 = Test()
test2.resize(800, 600)
test2.show()
sys.exit(example.exec_())
但是,它不起作用!程序运行时,屏幕上一片混乱。看来,self.update()
没有更新窗口。
请帮忙。
【问题讨论】:
【参考方案1】:显然,这段代码存在一些问题。我将列出我注意到的所有内容,然后进行解释:
-
在paintEvent 中处理过多
在那个paintEvent里面睡觉(不好)
在paintEvent 中调用self.update()
好的。绘制事件是小部件想要重绘的地方,并且应该尽可能快。你不应该在这个事件中做任何递归,或者花费太多时间,因为它会减慢你的平局。此外,在事件内部调用 update() 可能是递归的。绘制事件的目标应该是响应小部件的当前状态,绘制并退出。
这是您的代码修改后的版本。这不是最理想的方法,但我将在下面详细解释...
from PySide.QtCore import *
from PySide.QtGui import *
import sys, time
class Test(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
self.pts = [[80, 490], [180, 0], [280, 0], [430, 0], [580, 0], [680, 0], [780, 0]]
def poly(self, pts):
return QPolygonF(map(lambda p: QPointF(*p), pts))
def paintEvent(self, event):
painter = QPainter(self)
pts = self.pts[:]
painter.setPen(QPen(QColor(Qt.darkGreen), 3))
painter.drawPolyline(self.poly(pts))
painter.setBrush(QBrush(QColor(255, 0, 0)))
painter.setPen(QPen(QColor(Qt.black), 1))
for x, y in pts:
painter.drawEllipse(QRectF(x - 4, y - 4, 8, 8))
# print pts
def wave(self):
for point in self.pts:
while point[1] < 600:
point[1] += 1
self.update()
QApplication.processEvents()
time.sleep(0.0025)
if __name__ == '__main__':
example = QApplication(sys.argv)
test2 = Test()
test2.resize(800, 600)
test2.show()
test2.raise_()
test2.wave()
sys.exit(example.exec_())
请注意,点已移至成员属性self.pts
,而paintEvent()
现在仅绘制点的当前状态。然后,动画逻辑被移动到另一个名为wave()
的方法。在这个方法中,它循环和修改每个点并调用update()
来触发重绘。请注意,我们在paintEvent 之外调用update()
。这很重要,因为如果您的应用程序中发生任何其他导致窗口重绘(调整大小等)的事件,您的paintEvent 可能会永远循环。
所以我们修改了这个点列表,sleep 和一个重要的补充它叫QApplication.processEvents()。通常,当应用程序空闲(离开当前调用)时会处理事件。因为您不断地调用重绘,并将这些事件堆叠起来,所以您需要告诉事件循环继续并刷新所有内容。尝试注释掉 processEvents()
命令,看看会发生什么。在循环完成之前,您的应用程序将简单地旋转,然后生成的行将弹出到位。
现在我建议这并不是最理想的方法,尽管它只是一个例子。当前示例在执行波时阻塞了主线程。您应该始终避免阻塞主线程,因为它纯粹是为了响应 GUI 事件。所以这里有一些可能的建议:
您可以使用0.0025
动画速度作为超时来创建QTimer。将timeout() 信号连接到执行单个步骤并调用更新的wave()
方法的一个版本。这里不再需要睡觉了。一旦您的波浪计算结束,您将在wave()
中进行检查并在计时器上调用stop()。
将上面示例中的整个wave()
循环和初始数据集移动到QThread 中。这个 QThread 会像 waveUpdate(QPolygonF)
一样 emit a custom signal。当你启动这个线程时,它会执行循环,并处理创建 QPolygonF 并且在每一步它都会发出信号并休眠。您可以将此信号连接到主窗口上接收新多边形的方法,将其分配给self._polygon
,然后调用 update(),然后它会抓取 self._polygon 并绘制它。这里的想法是将尽可能多的繁重工作转移到线程中,并且只告诉您的主 GUI 线程使用新值重新绘制。
【讨论】:
以上是关于在 PySide/PyQt 中使用 drawPolyline 制作动画波浪的主要内容,如果未能解决你的问题,请参考以下文章
Python/pyside,pyqt(pyside,pyqt optional): 控制文本选择的函数