pyqt5中的QPainter在循环中调用repaint()时不会绘制任何内容。我该如何解决?

时间:2019-06-27 08:28:04

标签: python python-3.x animation pyqt pyqt5

我对PyQt完全陌生。我想使用PyQt5做动画,这是我正在做的简单测试,所以我只是试图将矩形从窗口的顶部移到底部。这是我要实现这一目标的要旨。

    1。我已将想要绘制的内容放入paintEvent()方法中。我已经使用变量而不是常量值绘制了矩形
    2。我还创建了一个update()函数来更新所有变量
    3。我创建了一个循环函数,每100毫秒调用一次self.update()和self.repaint()
import sys
import random
from PyQt5.QtWidgets import ( QApplication, QWidget, QToolTip, QMainWindow)             
from PyQt5.QtGui import QPainter, QBrush, QPen, QColor, QFont
from PyQt5.QtCore import Qt, QDateTime

class rain_animation(QMainWindow):

    def __init__(self):
        super().__init__()

        self.painter = QPainter()

        """ Variables for the Window """
        self.x = 50
        self.y = 50
        self.width = 500
        self.height = 500

        """Variables for the rain"""
        self.rain_x = self.width/2
        self.rain_y = 0
        self.rain_width = 5
        self.rain_height = 30
        self.rain_vel_x = 0
        self.rain_vel_y = 5

        self.start()
        self.loop()

    def paintEvent(self, a0):

        self.painter.begin(self)

        # Draw a White Background
        self.painter.setPen(QPen(Qt.white, 5, Qt.SolidLine))
        self.painter.setBrush(QBrush(Qt.white, Qt.SolidPattern))
        self.painter.drawRect(0, 0, self.width, self.height)

        #Draw the rain
        self.painter.setPen(QPen(Qt.blue, 1, Qt.SolidLine))
        self.painter.setBrush(QBrush(Qt.blue, Qt.SolidPattern))
        self.painter.drawRect(self.rain_x, self.rain_y, self.rain_width, self.rain_height)

        self.painter.end(self)

    def update(self, diff):
        self.rain_x += self.rain_vel_x
        self.rain_y += self.rain_vel_y

    def start(self):
        self.setWindowTitle("Rain Animation")
        self.setGeometry(self.x, self.y, self.width, self.height)
        self.show()

    def loop(self):
        start = QDateTime.currentDateTime()
        while True :    
            diff = start.msecsTo(QDateTime.currentDateTime())
            if diff >= 100 :
                print("time : {0} ms rain_x : {1} rain_y : {2}".format(diff, self.rain_x, self.rain_y))
                start = QDateTime.currentDateTime()
                self.update(diff)
                self.repaint()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    animation = rain_animation()
    sys.exit(app.exec_())

我应该看到的是一个从窗口顶部移动到屏幕底部的矩形,但是我所看到的只是一个黑色背景的窗口。 loop()函数似乎正常工作,因为我正在打印的数据表明变量每100毫秒进行更新。

尽管问题似乎出在loop()函数中,因为删除self.loop()后,我可以在窗口顶部看到带有白色背景的蓝色框的静态图片。

1 个答案:

答案 0 :(得分:1)

问题:

具有连续循环不允许GUI执行绘画,与OS交互等任务。每个GUI提供一种制作动画的方式,且不会阻塞窗口。


Qt提供了各种类,可让您将动画实现为:

  • QTimer,
  • QTimeLine,
  • QVariantAnimation,
  • QPropertyAnimation。

另一方面,建议:

  • 如果它将负责GUI绘画,请不要在paintEvent外部创建QPainter。
  • 使用update()方法(不是您的方法,而是提供Qt的方法)来代替重绘,以防万一,重绘会迫使窗口不必要地进行绘制,相反,update()将在必要时执行此操作,请记住,使用屏幕刷新率(60 Hz)完成。例如,如果您在20毫秒内调用repaint 5次,则paintEvent()将被调用3次,但屏幕上的绘制每隔16.6ms,因此,如果考虑使用update(),则只需要绘制1次。

考虑到上述情况,最好使用QPropertyAnimation:

import sys
from PyQt5 import QtCore, QtGui, QtWidgets


class RainAnimation(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle("Rain Animation")
        self.setGeometry(50, 50, 500, 500)
        self.m_rect_rain = QtCore.QRect()

        animation = QtCore.QPropertyAnimation(
            self,
            b"rect_rain",
            parent=self,
            startValue=QtCore.QRect(self.width() / 2, 0, 5, 30),
            endValue=QtCore.QRect(self.width() / 2, self.height() - 30, 5, 30),
            duration=5 * 1000,
        )

        animation.start()

    def paintEvent(self, a0):
        painter = QtGui.QPainter(self)
        # Draw a White Background
        painter.setPen(QtGui.QPen(QtCore.Qt.white, 5, QtCore.Qt.SolidLine))
        painter.setBrush(QtGui.QBrush(QtCore.Qt.white, QtCore.Qt.SolidPattern))
        painter.drawRect(self.rect())
        #Draw the rain
        painter.setPen(QtGui.QPen(QtCore.Qt.blue, 1, QtCore.Qt.SolidLine))
        painter.setBrush(QtGui.QBrush(QtCore.Qt.blue, QtCore.Qt.SolidPattern))
        painter.drawRect(self.rect_rain)

    @QtCore.pyqtProperty(QtCore.QRect)
    def rect_rain(self):
        return self.m_rect_rain

    @rect_rain.setter
    def rect_rain(self, r):
        self.m_rect_rain = r
        self.update()


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    w = RainAnimation()
    w.show()
    sys.exit(app.exec_())

另一个选择是使用QVarianAnimation:

import sys
from PyQt5 import QtCore, QtGui, QtWidgets


class RainAnimation(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle("Rain Animation")
        self.setGeometry(50, 50, 500, 500)
        self.m_rect_rain = QtCore.QRect()

        animation = QtCore.QVariantAnimation(
            parent=self,
            startValue=QtCore.QRect(self.width() / 2, 0, 5, 30),
            endValue=QtCore.QRect(self.width() / 2, self.height() - 30, 5, 30),
            duration=5 * 1000,
            valueChanged=self.set_rect_rain,
        )
        animation.start()

    def paintEvent(self, a0):
        painter = QtGui.QPainter(self)
        # Draw a White Background
        painter.setPen(QtGui.QPen(QtCore.Qt.white, 5, QtCore.Qt.SolidLine))
        painter.setBrush(QtGui.QBrush(QtCore.Qt.white, QtCore.Qt.SolidPattern))
        painter.drawRect(self.rect())
        # Draw the rain
        painter.setPen(QtGui.QPen(QtCore.Qt.blue, 1, QtCore.Qt.SolidLine))
        painter.setBrush(QtGui.QBrush(QtCore.Qt.blue, QtCore.Qt.SolidPattern))
        painter.drawRect(self.m_rect_rain)

    @QtCore.pyqtSlot(QtCore.QVariant)
    def set_rect_rain(self, r):
        self.m_rect_rain = r
        self.update()


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    w = RainAnimation()
    w.show()
    sys.exit(app.exec_())

以下示例使用您的逻辑,但使用了QTimer:

import sys
from PyQt5 import QtCore, QtGui, QtWidgets


class RainAnimation(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle("Rain Animation")
        self.setGeometry(50, 50, 500, 500)
        self.m_rect_rain = QtCore.QRect(self.width() / 2, 0, 5, 30)

        timer = QtCore.QTimer(self, timeout=self.update_rain, interval=100)
        timer.start()

    def paintEvent(self, a0):
        painter = QtGui.QPainter(self)
        # Draw a White Background
        painter.setPen(QtGui.QPen(QtCore.Qt.white, 5, QtCore.Qt.SolidLine))
        painter.setBrush(QtGui.QBrush(QtCore.Qt.white, QtCore.Qt.SolidPattern))
        painter.drawRect(self.rect())
        # Draw the rain
        painter.setPen(QtGui.QPen(QtCore.Qt.blue, 1, QtCore.Qt.SolidLine))
        painter.setBrush(QtGui.QBrush(QtCore.Qt.blue, QtCore.Qt.SolidPattern))
        painter.drawRect(self.m_rect_rain)

    @QtCore.pyqtSlot()
    def update_rain(self):
        self.m_rect_rain.moveTop(self.m_rect_rain.top() + 5)
        self.update()


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    w = RainAnimation()
    w.show()
    sys.exit(app.exec_())