Как обрабатывать QPropertyAnimation для большого количества виджетов - PullRequest
1 голос
/ 18 февраля 2020

Я хочу, чтобы много цветных точек постоянно перемещались по фону. (Описание кода ниже): виджет PolkaDot создается со случайным цветом, размером, положением и продолжительностью. QPropertyAnimation перемещает виджет по экрану слева направо, перезапускаясь на новую высоту, когда анимация заканчивается. 100 виджетов PolkaDot построены в виджете «Фон», и этого достаточно, чтобы он выглядел так, как будто тонны новых точек постоянно врываются с левой стороны экрана.

Однако 100 анимаций свойств, по-видимому, потребляют много ресурсов ЦП, в результате чего они замедляются и выглядят неровно. Есть ли другой способ добиться подобного результата? Попробуйте запустить код ниже.

import sys, random
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *

colors = [QColor('#f00'), QColor('#00f'), QColor('#0f0'), QColor('#ff0'),
          QColor('#fff'), QColor('#ff6000'), QColor('#6b00ff'), QColor('#f0f')]

class PolkaDot(QWidget):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setFixedSize(50, 50)
        self.color = random.choice(colors)
        self.r = random.randrange(5, 22)
        self.move(random.randrange(w), random.randrange(h))
        self.anim = QPropertyAnimation(self, b'pos')
        self.anim.finished.connect(self.run)
        self.anim.setDuration(random.randrange(3000, 9000))
        self.anim.setStartValue(QPoint(self.x() - (w + 60), self.y()))
        self.anim.setEndValue(QPoint(w + 60, self.y()))
        self.anim.start()

    def paintEvent(self, event):
        qp = QPainter(self)
        qp.setRenderHint(QPainter.Antialiasing)
        qp.setBrush(self.color)
        qp.setPen(QPen(self.color.darker(130), self.r / 5))
        qp.drawEllipse(QPoint(25, 25), self.r, self.r)

    def run(self):
        y = random.randrange(h)
        self.anim.setDuration(random.randrange(3000, 9000))
        self.anim.setStartValue(QPoint(-60, y))
        self.anim.setEndValue(QPoint(w + 60, y + random.randrange(-50, 50)))
        self.anim.start()


class Background(QWidget):

    def __init__(self):
        super().__init__()
        self.setGeometry(window)
        polka_dots = [PolkaDot(self) for i in range(100)]
        self.setStyleSheet('background-color: #000')


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = QDesktopWidget().availableGeometry()
    w, h = window.width(), window.height()
    gui = Background()
    gui.show()
    sys.exit(app.exec_())

1 Ответ

1 голос
/ 18 февраля 2020

Я вроде нашел решение. По некоторым причинам создание точек в событии рисования фонового виджета и вызов функции repaint () для QTimer.timeout () для обновления координат XY намного эффективнее, чем использование QPropertyAnimation.

tick = 24

class Dot(object):

    def __init__(self):
        self.x = random.randrange(-w - 60, 0)
        self.randomize()

    def randomize(self):
        self.color = random.choice(colors)
        self.r = random.randrange(5, 22)
        self.y = random.randrange(h)
        self.x_speed = random.randrange(3000, 9000)
        self.y_speed = random.randrange(-50, 50)

    def move(self):
        self.x += w * tick / self.x_speed
        self.y += self.y_speed * tick / self.x_speed
        if self.x > w:
            self.x = -60
            self.randomize()


class Background(QWidget):

    def __init__(self):
        super().__init__()
        self.setGeometry(window)
        self.setStyleSheet('background-color: #000')
        self.dots = [Dot() for i in range(150)]
        self.timer = QTimer()
        self.timer.setInterval(tick)
        self.timer.timeout.connect(self.animate)
        self.timer.start()

    def paintEvent(self, event):
        qp = QPainter(self)
        qp.setRenderHint(QPainter.Antialiasing)
        for dot in self.dots:
            qp.setBrush(dot.color)
            qp.setPen(QPen(dot.color.darker(130), dot.r / 5))
            qp.drawEllipse(QPoint(dot.x, dot.y), dot.r, dot.r)

    def animate(self):
        for d in self.dots:
            d.move()
        self.repaint()
...