Python Pyglet тормозит анимация. Медленно отрисовываются объекты на экране

Уже не однократно сталкиваюсь с проблемой торможения анимации при отрисовки различных объектов на Python. Проблема особенно обостряется, когда количество отрисовываемых объектов увеличивается. Я бы не сказал, что объектов прям слишком много: >= 200 (r=10) окружностей - уже слайд-шоу.

Я думаю, что дело не в библиотеке, которую я использую (pyglet). До этого я использовал p5py аналог p5.js тормоза были ещё сильнее (по правде говоря, создатели p5py знают что она сильно медленнее чем аналоги (Issues с GitHub'a)). Также пробовал Tkinter - думал, что чистый canvas решит мои проблемы. Но этого не произошло. И вот сейчас я запускаю код на Pyglet - получаю слайд-шоу.

Также заметил, что компьютер при тормозящей анимации начинает взлетать на реактивной тяге кулеров. При этом загрузка процессора процессом python'а никогда не превышает 20% (очень странно). Компьютер не совсем калькулятор: Intel i5-8300H 4 ядра (8 виртуальных) 2.30 GHz.

Собственно вопрос, в чем может быть проблема? Анимация 1000 точек не должна превращаться в слайд-шоу, даже если это Python.

PS. Так же я думаю, что проблема не в моем коде. Точно такой же (не про код ниже) код на p5.js дает плавную анимацию в браузере (но браузер использует видеокарту). Но на всякий случай прикладываю мой последний кол на Pyglet - это не верх оптимизации, но так медленно это работать не должно:

sketch.py

import pyglet
import random
from firework import Firework


window = pyglet.window.Window(400, 400)
main_batch = pyglet.graphics.Batch()

fireworks = [Firework(main_batch)]


@window.event
def on_draw():
    window.clear()
    main_batch.draw()


def update(dt):
    if random.random() < 0.03:
        fireworks.append(Firework(main_batch))

    for f in fireworks:
        f.update()
        if f.done():
            fireworks.remove(f)


if __name__ == '__main__':
    pyglet.clock.schedule_interval(update, 1 / 120.0)
    pyglet.app.run()

particle.py

import p5
import pyglet


class Particle(pyglet.shapes.Circle):

    def __init__(self, x, y, color, firework, batch=None):
        radius = 2
        if firework:
            radius = 4
        super().__init__(x, y, radius, radius * 5, color, batch)

        self.color = color
        self.firework = firework
        self.opacity = 255
        self._done = False

        self.acc = p5.Vector(0, 0)
        if self.firework:
            self.vel = p5.Vector(0, p5.random_uniform(8, 12))
        else:
            self.vel = p5.Vector.random_2D() * p5.random_uniform(-10, -2)

    @property
    def batch(self):
        return self._batch

    def update(self):
        self.acc += p5.Vector(0, -0.2)

        if not self.firework:
            self.vel *= 0.9
            if self.opacity > 4:
                self.opacity -= 4
            else:
                self._done = True
                self.opacity = 0

        self.vel += self.acc
        pos = p5.Vector(self.x, self.y) + self.vel

        self.x = pos.x
        self.y = pos.y

        self.acc *= 0

    def done(self):
        return self._done

firework.py

import p5
import random
from particle import Particle


class Firework:

    def __init__(self, batch):
        self.color = tuple(random.randint(0, 255) for _ in range(3))
        self.firework = Particle(p5.random_uniform(400), 0, self.color, True, batch=batch)
        self.bach = batch
        self._exploded = False
        self.particles = []

    def done(self):
        if self._exploded and len(self.particles) == 0:
            return True
        else:
            return False

    def explode(self):
        for _ in range(70):
            p = Particle(self.firework.x, self.firework.y, self.color, False, batch=self.firework.batch)
            self.particles.append(p)

    def update(self):
        if not self._exploded:
            self.firework.update()

            if self.firework.vel.y < 0:
                self._exploded = True
                self.explode()
                self.firework.delete()

        for p in self.particles:
            p.update()

            if p.done():
                self.particles.remove(p)
                p.delete()

Ответы (1 шт):

Автор решения: Mango

модуль threading

import threading
def explode(self):
   ваш_код_который_хотите_сделать_многопотоковым
   
for i in range(5):
        my_thread = threading.Thread(target=explode, args=(i,))
        my_thread.start()
→ Ссылка