Python Pygame won't quit - python

The code colors a pixel one by one and essentially prints a picture. I can't get it to quit though if I press the X to close or if I press Esc. I've put all the pg.event.get() code outside the main while loop, inside the main while loop, inside the secondary for loop, inside the tertiary for loop. I've ran it using IDLE and Spyder and same issue, no matter where I put it, I can't get it to quit. How can I get it to work????
loop = True
while loop:
#Event Management for exiting the screen
for event in pg.event.get():
keys = pg.key.get_pressed()
if event.type == pg.QUIT:
loop = False
elif keys[pg.K_ESCAPE]:
loop = False
pg.event.pump()
#Setup pixel transformation loop
for i in range(0,463):
for u in range (0,600):
screen.set_at((u,i),reddat[u,i]-greendat[u,i]-bluedat[u,i])
pg.display.flip()
loop = False

You're doing the whole pixel transformation without checking for events. Here's your flow in pseudo code:
loop until break:
check events
loop through pixels
transform pixels
break
Instead, you should ditch the while loop, make check_for_quit a function and then put that function nested in your transformation loop.
def check_for_quit():
"""Event Management for exiting the screen"""
for event in pg.event.get():
keys = pg.key.get_pressed()
if event.type == pg.QUIT:
return True
elif keys[pg.K_ESCAPE]:
return True
pg.event.pump()
return False
def draw():
"""Setup pixel transformation loop"""
for i in range(0,463):
for u in range (0,600):
if check_for_quit():
return
screen.set_at((u,i),reddat[u,i]-greendat[u,i]-bluedat[u,i])
pg.display.flip()
Instead of using a while loop you can have check_for_quit return True or False to indicate whether or not to quit, and then you can test that in each loop and return to exit the loop early when it's over.
Note I don't work with pygame, so while this should work it might not be the best solution for running code while trying to check for events.

During the pixel transformation loop you are not running the anything to detect events. You were on the right track about moving the loops about.
if you shift the event checking code into the pixel transformation loop you can poll events while generating the pixels
Example:
for i in range(0,463):
for u in range (0,600):
screen.set_at((u,i),reddat[u,i]-greendat[u,i]-bluedat[u,i])
for event in pg.event.get():
if event.type == pg.QUIT:
break
elif event.type == py.KEYDOWN:
if event.key == py.K_SPACE:
break
pg.display.flip()
This removes the need for a while loop aswell.

Thank you all for your help, I did move the event loops into the for loop reorganised them by seperating the key event as below. This worked for me. Regarding the image, the goal of this was to display the image pixel by pixel soo that's why I blitted each loop. Anyway,Appreciate your comments. Next time I think a quit function would be very useful so will set one of them up.
Here's what worked for me
for u in range (0,600):
for i in range(0,463):
screen.set_at((i,u),(reddat[u,i],greendat[u,i],bluedat[u,i]))
pg.display.flip()
#Event Management for exiting the screen
for event in pg.event.get():
if event.type == pg.QUIT:
pg.quit()
keys = pg.key.get_pressed()
if keys[pg.K_ESCAPE]:
pg.quit()
pg.event.pump()

First, your event loop looks weird. I think you should use either for event in pg.event.get(): or keys = pg.key.get_pressed() but not both.
I'm not sure if you just want to create a background surface or a build-up effect. Also, you should've explained what the variables reddat, etc. are.
If you just want a background image you only need to create a surface before the main loop, blit the pixels onto the surface and then blit it on the screen in the main loop.
Elif you want the build-up, you could use a generator to get the pixels and then use itertools.islice to get chunks of it. Here's an example of the build-up:
import sys
import pygame as pg
from itertools import islice
def generate_pixels(width, height):
"""Generate pixel coords for the build-up effect."""
for i in range(height):
for u in range(width):
yield i, u
def main():
clock = pg.time.Clock()
width, height = (640, 480)
screen = pg.display.set_mode((width, height))
pixels = generate_pixels(width, height)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
if event.type == pg.KEYDOWN:
if event.key == pg.K_ESCAPE:
done = True
# Change the second arg of islice to adjust the speed.
for i, u in islice(pixels, 2000):
screen.set_at((u, i), (i % 256, u % 256, (i + u) % 256))
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
sys.exit()
Edit: There's a simpler way to create a build-up effect: You can pass an area argument to the blit method of surfaces. So we can create the final image beforehand (or load one) and then just increase the percentage of the image that we want to blit on the display surface.
import sys
import pygame as pg
def main():
clock = pg.time.Clock()
width, height = (640, 480)
screen = pg.display.set_mode((width, height))
# Create the final image beforehand.
image = pg.Surface((width, height))
for i in range(height):
for u in range(width):
image.set_at((u, i), (i % 256, u % 256, (i + u) % 256))
image_percent = 0
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
if event.type == pg.KEYDOWN:
if event.key == pg.K_ESCAPE:
done = True
screen.fill((90, 10, 200))
# Increase the displayed portion of the image.
if image_percent < 100:
image_percent += .5
# The third argument is the area of the surface on which we'll
# blit the image.
screen.blit(image, (0, 0), (0, 0, width/100*image_percent, height))
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
sys.exit()

Related

what does is it have to do with the screen size

`from turtle import right
import pyautogui, time, random, pygame
from pygame.locals import *
time.sleep(3)
class Square(pygame.sprite.Sprite):
def __init__(self):
super(Square,self).__init__()
self.surf = pygame.Surface((20,20))
self.surf.fill((255,0,0))
self.rect = self.surf.get_rect()
width,height = 960,540
screen = pygame.display.set_mode((width,height))
cont = True
square1 = Square()
while cont:
time.sleep(0.4)
x,y=random.randrange(int(1920/2-
width/2),int(1920/2+width/2)),random.randrange(int(1080/2-height/2),int(1080/2+height/2))
screen.blit(square1.surf,(x,y))
pyautogui.click(x,y)
for event in pygame.event.get():
if event.type == QUIT:
cont=False
elif event.type == KEYDOWN:
if event.key == K_BACKSPACE:
cont=False
pygame.display.flip()`
Hi, so basically I am new to python, now learning the basic pyautogui usage and tried making this useless 'game' which was supposed to move the cursor to some point and draw a square at this point and everything is working completely fine when screen size is set to (1920,1080), but when I change the size to anything else it just fails, like cursor coordinates and square being drawn coordinates are not compatible. Does anybody have an explanation for this?
This happens because you draw a square in a pygame window that has its coordinates (0,0), but the cursor has the coordinates (0,0) of your entire screen. I solve it, that I draw first square with pygame window coordinates and then I couted shift.
Here is my code example:
while True:
time.sleep(0.4)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
exit()
elif event.type == KEYDOWN:
if event.key == K_BACKSPACE:
pygame.quit()
exit()
square1.rect.x = random.randint(0, width - square1.rect.w)
square1.rect.y = random.randint(0, height - square1.rect.h)
pyautogui.click((1920/2 - width/2) + square1.rect.centerx,
(1080/2-height/2) + square1.rect.centery)
screen.blit(square1.surf, square1.rect)
pygame.display.flip()
If you have pygame window size same like your entire size, it works becouse coordinates (0,0) are on the same place.
I hope it will help you, Adam
PS: If you move pygame window it not works, because in counting I presuppose, that pygame screen is in center your entire screen.

How do you switch between multiple backgrounds in pygame?

I was practicing using the pygame package in python by making a graphics oriented game and in my game, I want to switch between multiple backgrounds as the user progresses through it. For the example I am posting, the first background will be a black screen with 5 statements on it and my second background will be a room. However, I am unable to figure this out because whenever I try to run the code, the game window doesn't show anything. I can post my code for the while loop below for further clarification.
while running:
screen.fill((0, 0, 0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
first_line()
second_line()
third_line_1()
third_line_2()
fourth_line()
fifth_line()
sleep(10)
screen.blit(background_room, (0, 0))
pygame.display.update()
The way I would probably do it is something like
import pygame
from pygame.locals import *
class Backround:
FIVE_STATEMENTS = 0
ROOM = 1
viewport_size = (800, 600)
def main():
pygame.init()
screen = pygame.display.set_mode(viewport_size)
backround_room = pygame.Surface(viewport_size)
# load the image...
backround_statements = pygame.Surface(viewport_size)
backround_statements.fill((0, 0, 0))
# blit the five statements onto here
backround_to_render = Backround.FIVE_STATEMENTS
running = True
while running:
for event in pygame.event.get():
if event.type == QUIT:
running = False
if backround_to_render == Backround.FIVE_STATEMENTS:
# if the statements ever change, probably want to fill() and re-blit them on
screen.blit(backround_statements, (0,0))
elif backround_to_render == Backround.ROOM:
screen.blit(backround_room, (0,0))
else:
print("Oh no!!!!!!!")
pygame.display.flip()
if __name__ == "__main__":
main()

Values of variables don't update properly for new loop in pygame

Using pygame and python 3.7, I have created a game in which an image at the center (250, 250) can be dragged around the screen and collide with a radius, in which case a break happens and the next image in the next loop spawns at the exact center, where the first image was located, as well. Despite the game working the way I intended, in principle, it behaves weirdly for fast mouse speed. In my minimal example code, the colored circles are supposed to reappear at the exact center, for every while-loop, however, they somehow don't update properly and therefore reappear not at the center of the screen, most of the time (they only do when I release the mouse-button really early / well-timed). I tested the game on windows and mac and noticed that on my mac, the "lag" seems to be even worse. If anybody knows how I could work around that, I'd be really thankful. Also, the game starts lagging and jumping to the next loop right away for really fast mouse movement, with which I have dealt by changing the speed of my external mouse. Is there an inherent fix for too fast mouse-movement in pygame? Thanks for all suggestions and maybe also other improvements to the idea of my code.
import pygame
import time
from time import sleep
import math
pygame.init()
gameDisplay = pygame.display.set_mode((500, 500))
clock = pygame.time.Clock()
background_surface = pygame.Surface((500, 500))
background_surface.fill((255, 255, 255))
colors = [(0,0,0), (253, 45, 0), (249, 253, 0), (32, 201, 5), (0, 210, 235)]
for color in colors:
done = False
a, b = 250, 250
u, v = 250, 250
while not done:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEMOTION:
if event.buttons[0]:
a += event.rel[0]
b += event.rel[1]
rect = pygame.Rect(u, v, 0.001, 0.001)
radius = 200
corners = [rect.bottomleft, rect.bottomright, rect.topleft, rect.topright]
dist = [math.sqrt((p[0] - a) ** 2 + (p[1] - b) ** 2) for p in corners]
p_out = [i for i, d in enumerate(dist) if d > radius]
if any(p_out):
break
gameDisplay.blit(background_surface, (0, 0))
pygame.draw.circle(gameDisplay, color, (a,b), 50, 0)
pygame.draw.circle(gameDisplay, (0,0,0), (250,250), 200, 2)
pygame.display.flip()
sleep(0.7)
pygame.quit()
quit()
[...] they somehow don't update properly and therefore reappear not at the center of the screen, most of the time (they only do when I release the mouse-button really early / well-timed) [...]
Of course, investigate your code. The 1st thing what happens in your code is, that a and b are modified:
while not done:
# [...]
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEMOTION:
if event.buttons[0]:
a += event.rel[0]
b += event.rel[1]
If the pygame.MOUSEMOTION event is in the queue, then it is handled before the circle is drawn. So the position of the circle is modified and it does not appear in the center.
Note, an event does not reflect the current state of the mouse. It is a notification, which is stored in a queue when it happens, but then event handling may done later. What you actually do is to handle an event, which possibly occurred in the past.
Use a state dragging, which is False at the start of each loop. Set the state when the button is pressed (pygame.MOUSEBUTTONDOWN event) and reset it when the button is released (pygame.MOUSEBUTTONUP event):
for color in colors:
# [...]
dragging = False
while not done:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
dragging = True
elif event.type == pygame.MOUSEBUTTONUP:
if event.button == 1:
dragging = False
elif event.type == pygame.MOUSEMOTION:
if dragging:
a += event.rel[0]
b += event.rel[1]
# [...]

pygame-How to replace an image with an other one

I have this sort of code:
width = 100
height = 50
gameDisplay.blit(button, (width, height))
pygame.display.update()
while True:
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONUP and event.button == 1:
# replace the button's image with another
Is there any function or something that would let me replace the image with another?
You cannot 'replace' anything you've drawn. What you do is that you draw new images over the existing ones. Usually, you clear the screen and redraw your images every loop. Here's pseudo-code illustrating what a typical game loop looks like. I'll use screen as variable name instead of gameDisplay, because gameDisplay goes against PEP-8 naming convention.
while True:
handle_time() # Make sure your programs run at constant FPS.
handle_events() # Handle user interactions.
handle_game_logic() # Use the time and interactions to update game objects and such.
screen.fill(background_color) # Clear the screen / Fill the screen with a background color.
screen.blit(image, rect) # Blit an image on some destination. Usually you blit more than one image using pygame.sprite.Group.
pygame.display.update() # Or 'pygame.display.flip()'.
For your code you probably should do something like this:
rect = pygame.Rect((x_position, y_position), (button_width, button_height))
while True:
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONUP and event.button == 1:
button = new_button # Both should be a pygame.Surface.
gameDisplay.fill(background_color, rect) # Clear the screen.
gameDisplay.blit(button, rect)
pygame.display.update()
If you only want to update the area where your image is you could pass rect inside the update method, pygame.display.update(rect).
To show changes on screen use pygame.display.update().
Your code should look like this
width = 100
height = 50
gameDisplay.blit(button, (width, height))
while True:
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONUP and event.button == 1:
gameDisplay.blit(new_button, (width, height))
pygame.display.update()

Python/Pygame; MOUSEBUTTONDOWN event

Okay I'm pretty new to using Pygame, I'm really just playing around with some of the methods and events. So far i pretty much have an image that moves around the pygame frame and bounces off any of the edges of the frame when it hits it. if the image touches the top of the frame it will increase a count variable by 1 which will be displayed on the screen. I then wanted to add a feature whereby if I clicked the image which was moving it would also add one onto the count variable. When i added this code in however (I think because the function operates on a loop), depending how long you hold the mouse down, count increases by a multiple of 8. I want to make it so that no matter how long i hold the mouse down for, the event stored inside the MOUSEBUTTONDOWN handler will only fire once. what am I doing wrong?
import pygame, sys
from pygame.locals import *
pygame.init()
DISPLAYSURF = pygame.display.set_mode((400, 300))
pygame.display.set_caption('Hello World!')
screen =pygame.display.set_mode((600,400))
ball = pygame.image.load("homers.png")
ball = pygame.transform.scale(ball,(225,200))
x=200
y=100
left = True
up = True
color = 67,143,218
def text_objects(text,font):
text_surface = font.render(text,True, color)
return text_surface,text_surface.get_rect()
def message_display(text,x,y,z):
largeText = pygame.font.Font('freesansbold.ttf',z)
TextSurf,TextRect = text_objects(text,largeText)
TextRect.center = (x,y)
screen.blit(TextSurf,TextRect)
def hi(x,y,p,z):
message_display(x,y,p,z)
count = 0
message_count = str(count)
while True: # main game loop
screen.fill((180,0,0))
screen.blit(ball,(x,y))
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
hi(message_count,x,y,140)
hi("How Many Times Has Homer Hit His Head?",300,200,20)
if event.type == pygame.MOUSEBUTTONDOWN:
# Set the x, y postions of the mouse click
if ball.get_rect().collidepoint(x, y):
count = count+1
if event.type == pygame.MOUSEBUTTONUP:
0
if left == True:
x=x-10
if x == -100:
left =False
if left == False:
x=x+10
if x == 450:
left = True
if up == True:
y=y-10
if y == -20:
up =False
count = count+1
message_count = str(count)
hi(message_count,x,y,140)
if up == False:
y=y+10
if y== 230:
up =True
pygame.display.update()
You have to fix the indentation of your code:
while True: # main game loop
screen.fill((180,0,0))
screen.blit(ball,(x,y))
hi(message_count,x,y,140)
hi("How Many Times Has Homer Hit His Head?",300,200,20)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
# this has to be part of the for loop
if event.type == pygame.MOUSEBUTTONDOWN:
if ball.get_rect().collidepoint(x, y):
count = count+1
...

Categories

Resources