添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

在PySide/PyQt中用drawPolyline制作一个动画波段

6 人关注

我想把一条多段线做成动画(它必须像波浪一样)。我试过这种方法。

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()没有更新窗口。 请帮助我们。

python
pyqt
pyqt4
pyside
qpainter
SaulTigh
SaulTigh
发布于 2012-02-27
1 个回答
jdi
jdi
发布于 2012-03-04
已采纳
0 人赞同

显然,这段代码有一些问题。我将列出我注意到的所有问题,然后再进行解释。

  • doing far too much processing in a paintEvent
  • doing a sleep inside of that paintEvent (bad)
  • calling self.update() while inside of a paintEvent
  • 好吧。绘画事件是小部件想要重绘的地方,应该尽可能快。你不应该在这个事件中做任何递归的事情,或者花太多的时间,因为这将减慢你的绘制速度。另外,在你的事件中调用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可能会永远循环下去。

    因此,我们修改了这个点列表,睡眠,还有一个重要的补充,就是调用QApplication.processEvents().通常情况下,事件在应用程序变得空闲(离开当前调用)时被处理。因为你在不断地调用repaint,并将这些事件堆积起来,你需要告诉事件循环继续进行,并将所有的事情都冲刷掉。试着注释掉那个processEvents()命令,看看会发生什么。你的应用程序会简单地旋转,什么都不做,直到循环完成,结果这一行会弹出。

    现在我想说的是,这并不是最理想的方法,尽管它可以作为一个例子。目前这个例子在主线程执行波段时阻塞了它。你应该始终避免阻断主线程,因为它的目的是纯粹地响应GUI事件。因此,这里有一些可能的建议。