I'm running a basic simulation of a pendulum but whenever I tab out, the pygame window stops responding (and eventually crashes) but the console continues to output like the program is running as normal; or at least the maths for simulating the pendulum works but not the pygame side of it.
I'm not sure where to even start for fixing it, I assume its some small error in the actual program setup rather than the code for simulating the pendulum itself and I'm just missing something.
Here is the code:
import pygame
import numpy as np
pygame.init()
WIDTH = 800
HEIGHT = 500
res = (WIDTH, HEIGHT)
screen = pygame.display.set_mode(res)
clock = pygame.time.Clock()
WHITE = [255, 255, 255]
RED = [255,0,0]
BLACK = [0, 0, 0]
BLUE = [0, 0, 155]
class Pendulum:
def __init__(self, theta0, L, tMax, dt, scale, pivotRadius, bobRadius, pivot, bobMass):
self.g = 9.81
self.t = np.arange(0, tMax, dt)
self.theta0 = theta0
self.L = L
self.tMax = tMax
self.dt = dt
self.pivot = pivot
self.bobMass = bobMass
self.scale = scale
self.pivotRadius = pivotRadius
self.bobRadius = bobRadius
self.screen = screen
self.width = scale
self.height = scale
def simulatePendulum(self):
g = 9.81
t = np.arange(0, self.tMax, self.dt)
theta = np.zeros_like(t)
omega = np.zeros_like(t)
theta[0] = self.theta0
x = np.zeros_like(t)
y = np.zeros_like(t)
for i in range(len(t)-1):
omega[i+1] = omega[i] + self.dt*(-g/self.L * np.sin(theta[i]))
theta[i+1] = theta[i] + self.dt*omega[i+1]
x[i+1] = self.L*np.sin(theta[i+1])
y[i+1] = self.L*np.cos(theta[i+1])
self.draw(x[i+1], y[i+1])
return x[i+1], y[i+1]
def draw(self, x, y):
bobPos = (x+400,y+300)
pivotPos = (self.pivot[0]+400, self.pivot[1]+300)
print(bobPos)
screen.fill(WHITE)
pygame.draw.line(self.screen, RED, pivotPos, bobPos, 2)
pygame.draw.circle(self.screen, BLUE, pivotPos, 5)
pygame.draw.circle(self.screen, BLACK, bobPos, 10)
pygame.display.update()
def main():
theta0 = np.pi/4
L = 100
tMax = 10000
dt = 0.01
scale = 1
pivotRadius = 0.02
bobRadius = 0.05
pivot =(0,0)
bobMass=1
p = Pendulum(theta0, L, tMax, dt, scale, pivotRadius, bobRadius, pivot, bobMass)
done = False
while not done:
event_list = pygame.event.get()
for event in event_list:
if event.type == pygame.QUIT:
done = True
screen.fill((255, 255, 255))
x, y = p.simulatePendulum()
p.draw(x, y)
clock.tick(60)
pygame.quit()
main()
Your problem has absolutely nothing to do with "tabbing out of focus". The problem is for i in range(len(t)-1):
and tMax = 10000
. Therefore, you have a loop that updates the display 10000 times within the application loop. All the time the loop is dunning, the events are not handled and this causes the system to freeze. Also see PyGame window not responding after a few seconds.
To solve the problem, do not calculate 10000 angles and positions of the pendulum in a loop, but calculate the next position of the pendulum depending on the current angle and position in simulatePendulum
and update the display once per frame:
import pygame
import numpy as np
pygame.init()
WIDTH = 800
HEIGHT = 500
res = (WIDTH, HEIGHT)
screen = pygame.display.set_mode(res)
clock = pygame.time.Clock()
class Pendulum:
def __init__(self, theta0, L, dt, scale, pivotRadius, bobRadius, pivot, bobMass):
self.g = 9.81
self.theta0 = theta0
self.L = L
self.dt = dt
self.pivot = pivot
self.bobMass = bobMass
self.scale = scale
self.pivotRadius = pivotRadius
self.bobRadius = bobRadius
self.screen = screen
self.width = scale
self.height = scale
self.omega = 0
self.theta = self.theta0
self.x = 0
self.y = 0
def simulatePendulum(self):
g = 9.81
self.omega += self.dt*(-g/self.L * np.sin(self.theta))
self.theta += self.dt*self.omega
self.x = self.L*np.sin(self.theta)
self.y = self.L*np.cos(self.theta)
def draw(self):
bobPos = (self.x+400, self.y+300)
pivotPos = (self.pivot[0]+400, self.pivot[1]+300)
screen.fill("white")
pygame.draw.line(self.screen, "red", pivotPos, bobPos, 2)
pygame.draw.circle(self.screen, "blue", pivotPos, 5)
pygame.draw.circle(self.screen, "black", bobPos, 10)
pygame.display.update()
def main():
theta0 = np.pi/4
L, dt, bobMass = 100, 0.1, 1
scale, pivotRadius, bobRadius = 1, 0.02, 0.05
pivot = (0,0)
p = Pendulum(theta0, L, dt, scale, pivotRadius, bobRadius, pivot, bobMass)
done = False
while not done:
event_list = pygame.event.get()
for event in event_list:
if event.type == pygame.QUIT:
done = True
p.simulatePendulum()
p.draw()
clock.tick(60)
pygame.quit()
main()