Как анимировать отрисованные в QPainter объекты?
Не могу разобраться, как добавить анимацию к уже отрисованным объектам в QPainter.
Есть одно условие, новые объекты могут добавляться в любое время и их перемещения тоже должны быть анимированы.
Пробовал реализовать этот пример, но он не подходит для динамического изменения количества объектов.
from PyQt5.QtWidgets import QWidget, QApplication
from PyQt5.QtCore import pyqtSignal, QRect, QPoint, Qt, QThread
from PyQt5 import QtGui
import time
import random
import queue
class MyThread(QThread):
task_update_coord = pyqtSignal(QPoint, int, name = "updateCoordinate")
def __init__(self, queue, parent=None):
super().__init__(parent)
self.x = 0
self.y = 0
self.queue = queue
def run(self):
while True:
self.x = random.randint(20, 200)
self.y = random.randint(20, 200)
self.id = random.randint(1, 2)
self.task_update_coord.emit(QPoint(self.x, self.y), self.id)
self.queue.put((self.x, self.y, self.id))
time.sleep(1)
print((self.x, self.y, self.id))
class Dialog(QWidget):
def __init__(self):
super().__init__()
self.queue = queue.Queue()
thread = MyThread(self.queue, parent=self)
self.setFixedSize(600, 600)
self.rect = QRect(0, 0, 10, 10)
self.rect2 = QRect(0, 0, 10, 10)
self.point = QPoint(10, 20)
self.child = QWidget()
thread.task_update_coord.connect(self.changing_coords, Qt.ConnectionType.QueuedConnection)
thread.start()
def changing_coords(self):
msg = self.queue.get()
print(msg)
x = msg[0]
y = msg[1]
id = msg[2]
self.rect = QRect(x, y, 30, 30)
self.rect2 = QRect(x+30, y+30, 30, 30)
self.update()
def paintEvent(self, event):
painter = QtGui.QPainter(self)
painter.drawEllipse(self.rect)
painter.drawText(self.rect, Qt.TextFlag.TextDontClip|Qt.AlignmentFlag.AlignCenter, str(1))
painter.drawEllipse(self.rect2)
painter.drawText(self.rect2, Qt.TextFlag.TextDontClip|Qt.AlignmentFlag.AlignCenter, str(2))
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
w = Dialog()
w.show()
sys.exit(app.exec_())
Ответы (1 шт):
Автор решения: S. Nick
→ Ссылка
Как вариант:
from random import randint
from typing import List
from PyQt5.Qt import *
class Ball:
def __init__(self, x, y, r, v_x, v_y, color):
self.x = x
self.y = y
self.r = r
self.v_x = v_x
self.v_y = v_y
self.color = color
def update(self):
self.x += self.v_x
self.y += self.v_y
def draw(self, painter: QPainter):
r, g, b = self.color
color = QColor(r, g, b)
painter.save()
painter.setPen(Qt.black)
painter.setBrush(color)
painter.drawEllipse(self.x, self.y, self.r, self.r)
painter.restore()
@property
def center(self):
return self.x, self.y
@property
def top(self):
return self.y - self.r
@property
def bottom(self):
return self.y + self.r
@property
def left(self):
return self.x - self.r
@property
def right(self):
return self.x + self.r
class Widget(QWidget):
def __init__(self):
super().__init__()
self.setWindowFlags(Qt.FramelessWindowHint)
self.setAttribute(Qt.WA_TranslucentBackground)
self._old_pos = None
self.frame_color = Qt.darkCyan
self.balls: List[Ball] = []
timeout = 1000 // 60
self.timer = QTimer()
self.timer.timeout.connect(self.tick)
self.timer.start(timeout)
layout = QHBoxLayout(self)
layout.addStretch()
layout.addWidget(QPushButton(
"Добавить пару шариков",
clicked=self._add), alignment=Qt.AlignRight | Qt.AlignBottom)
layout.addWidget(QPushButton(
"Закрыть окно",
clicked=self.close), alignment=Qt.AlignRight | Qt.AlignBottom)
def _add(self):
for i in range(2):
self.append_random_ball()
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self._old_pos = event.pos()
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
self._old_pos = None
def mouseMoveEvent(self, event):
if not self._old_pos:
return
delta = event.pos() - self._old_pos
self.move(self.pos() + delta)
def tick(self):
for ball in self.balls:
ball.update()
# Условия отскакивания шарика от левого и правого края
if ball.left <= 0 or ball.right >= self.width():
ball.v_x = -ball.v_x
# Условия отскакивания шарика верхнего и нижнего края
if ball.top <= 0 or ball.bottom >= self.height():
ball.v_y = -ball.v_y
self.update()
def paintEvent(self, event):
painter = QPainter(self)
painter.setBrush(QColor(0, 0, 0, 1))
painter.setPen(QPen(self.frame_color, 10))
painter.drawRect(self.rect())
for ball in self.balls:
ball.draw(painter)
def append_random_ball(self):
def get_random_vector():
pos = 0, 0
# Если pos равен (0, 0), пересчитываем значения, т.к. шарик должен двигаться
while pos == (0, 0):
pos = randint(-3, 3), randint(-3, 3)
return pos
def get_random_color():
return randint(0, 255), randint(0, 255), randint(0, 255)
x = self.width() // 2 + randint(-self.width() // 4, self.width() // 4)
y = self.height() // 2 + randint(-self.height() // 4, self.height() // 4)
r = randint(22, 33)
v_x, v_y = get_random_vector()
color = get_random_color()
ball = Ball(x, y, r, v_x, v_y, color)
self.balls.append(ball)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
WIDTH = 600
HEIGHT = 600
BALL_NUMBER = 10
w = Widget()
w.resize(WIDTH, HEIGHT)
for i in range(BALL_NUMBER):
w.append_random_ball()
w.show()
sys.exit(app.exec_())
