python - 在 PySide/PyQt 中使用 drawPolyline 制作动画波

标签 python pyqt pyqt4 pyside qpainter

我正在尝试为多段线设置动画(它必须表现得像波浪)。我试过这种方式:

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 中做太多处理
  2. 在那个 paintEvent 中 sleep (不好)
  3. 在 paintEvent 中调用 self.update()

好的。绘制事件是小部件想要重绘的地方,应该尽可能快。你不应该在这个事件中做任何递归的事情,或者花太多时间,因为它会减慢你的平局。此外,在事件内部调用 update() 可能是递归的。 paint事件的目标应该是响应widget的当前状态,paint,然后退出。

这是您的代码的修改版本。这不是最理想的方法,但我会在下面详细解释......

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 事件。所以这里有一些可能的建议:

  1. 您可以创建一个 QTimer使用 0.0025 动画速度作为超时。连接timeout()向执行单个步骤并调用更新的 wave() 方法发出信号。这里不再需要 sleep 了。一旦您的波浪计算结束,您将在 wave() 中检查并调用 stop()在计时器上。

  2. 将上例中的整个 wave() 循环和初始数据集移动到 QThread 中.这个 QThread 将 emit a custom signalwaveUpdate(QPolygonF)。当你启动这个线程时,它会执行循环,并处理创建 QPolygonF 并且在每一步它都会发出信号并休眠。您可以将此信号连接到主窗口上的一个方法,该方法将接收新的多边形,将其分配给 self._polygon,然后调用 update(),后者将获取 self._polygon 并绘制它.这里的想法是将尽可能多的繁重工作转移到线程中,并且只告诉您的主 GUI 线程使用新值重新绘制。

关于python - 在 PySide/PyQt 中使用 drawPolyline 制作动画波,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9465047/

相关文章:

python - 如何抓取 HTML 表格格式的数据?

python - 在 Python 中使用个人模块的文件夹

python - QScrollArea.ensureWidgetVisible 方法不显示目标小部件

PyQt 从 URL 加载图像到 QPixmap 会导致卡住和崩溃

python - PyQt6在QSlider的paintEvent中设置自定义矩形

python - PyQt ComboBox 小部件在空时不发出信号

python - 安装requirements.txt错误,语法错误-python2

Python 修剪/过滤掉点

python - PyQt4 pyuic4 spacerItem 作为类的成员

webkit - 发送自定义 header 和 qtwebkit 请求