How to stop the execution of the function in pygame - python

I am trying to make an Algorithm Visualizer with pygame and I started by implementing Linear Search. The problem I am facing is that, in the visualization I am changing colour of the rectangle but that animation is repeating itself as it is inside the while loop. Here is the code:
I want that the animation should stop so that the viewer can actually see what happened. Further I am planning to add more algorithms.
ARRAY = [2, 10, 5, 8, 7, 3, 43, 54, 23, 1]
def main():
global SCREEN, CLOCK
pygame.init()
SCREEN = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
CLOCK = pygame.time.Clock()
num_font = pygame.font.SysFont("roboto", NUM_FONT_SIZE)
x = 3
y = 10
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
if event.type == pygame.KEYUP and event.key == pygame.K_ESCAPE:
pygame.quit()
showArray(x, y, num_font)
linearSearch(x, y)
pygame.display.update()
CLOCK.tick(60)
def linearSearch(x, y):
num = 23
box = pygame.Rect(x, y, BOX_SIZE+5, BOX_SIZE)
for i in ARRAY:
if i == num:
pygame.draw.rect(SCREEN, RED, box, 1)
pygame.draw.rect(SCREEN, GREEN, box, 1)
else:
box.x += BOX_SIZE + 5
CLOCK.tick_busy_loop(10)
pygame.draw.rect(SCREEN, RED, box, 1)
pygame.display.update()
def showArray(x, y, num_font):
box = pygame.Rect(x, y, BOX_SIZE+5, BOX_SIZE)
for i in ARRAY:
box.x += BOX_SIZE + 5
pygame.draw.rect(SCREEN, WHITE, box, 1)
nums = num_font.render(str(i), True, WHITE)
SCREEN.blit(nums, (box.x + 5, box.y + 5))
Here is the image of the output

You have an application loop, so use it. Change the function linearSearch. The list index has to be an argument to the function, but remove the for loop has to be removed form the function:
def linearSearch(x, y, list_i):
i = ARRAY[list_i]
num = 23
box = pygame.Rect(x + (BOX_SIZE+5)*(list_i+1), y, (BOX_SIZE+5), BOX_SIZE)
color = GREEN if i == num else RED
pygame.draw.rect(SCREEN, color, box, 1)
Iterate through the list in the main application loop:
def main():
# [...]
list_i = 0
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
if event.type == pygame.KEYUP and event.key == pygame.K_ESCAPE:
pygame.quit()
showArray(x, y, num_font)
if list_i < len(ARRAY):
linearSearch(x, y, list_i)
list_i += 1
pygame.display.update()
CLOCK.tick(60)

Related

How can I create multiple rect objects?

I am trying to make a block spawner like thingy in pygame. I've used lists to store x and y variables, but I can't make rects with these without using variables like rect1, rect2 ... How can I use lists or something else to have infinite amounts of rects?
My code is looking like that:
import pygame
from pygame.locals import *
screen = pygame.display.set_mode((800,600))
kares = []
karx = [0]
kary = [0]
karposx = 0
karposy = 0
k = 0
running = True
while running:
# screen.fill((0,0,0))
if k == 1:
karx[0] = pygame.mouse.get_pos()[0]
for i in range(len(karx)):
a = [karx[i], kary[i]]
kares.append(a)
for i in kares:
karposx = i[0]
karposy = i[1]
print(kares)
k+=1
rect1 = pygame.Rect(karposx,200,64,64)
# for i in kares:
# kares[i][1] -= 1
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == KEYDOWN:
if event.key == K_RIGHT:
k += 1
if k >= 2:
k = 0
if k == 1:
pygame.draw.rect(screen, (255,0,0), rect1)
pygame.display.update()
Output
You have to create a list of rectangles. Create rectangles and add them to the list:
rect = pygame.Rect(x, 200, 64, 64)
rects.append(rect)
Draw the rectangles in a loop:
for rect in rects:
pygame.draw.rect(screen, (255,0,0), rect)
Minimal example based on your code:
import pygame
from pygame.locals import *
screen = pygame.display.set_mode((800,600))
clock = pygame.time.Clock()
rects = []
running = True
while running:
clock.tick(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == MOUSEBUTTONDOWN:
if event.button == 3:
x, _ = event.pos
x = (x // 64) * 64
if not any(r for r in rects if r.x == x):
rect = pygame.Rect(x, 200, 64, 64)
rects.append(rect)
print(len(rects))
screen.fill((0,0,0))
for rect in rects:
pygame.draw.rect(screen, (255,0,0), rect)
pygame.display.update()
pygame.quit()

Is Pygame Drawing a Line and I Just Can't See It?

I'm new to OOP and trying to get the gist of using Classes and Methods. In this particular case, I've used a class to create two red nodes and managed to use MOUSEBUTTONDOWN with my class.
However, when I try to use a MOUSEBUTTONDOWN event to draw a line, nothing seems to happen. I've
used test print statements in multiple places to ensure that I'm "reaching" my class and that
the method is executing. Nothing, however, can seem to make my red line appear.
I've also moved the draw statement out of the method to near the end of my game loop and it
appears correctly.
What am I misunderstanding about classes and methods?
import pygame
class Rnode():
def __init__(self, x, y, image_rednode):
self.x = x
self.y = y
self.image_rednode = image_rednode
self.rect = self.image_rednode.get_rect()
self.rect.topleft = (x, y)
self.clicked = False
self.wired = False
# draw node line
def put(self):
screen.blit(self.image_rednode, (self.x, self.y))
#get mouse position
pos = pygame.mouse.get_pos()
#check mouseover and clicked
if self.rect.collidepoint(pos):
if pygame.mouse.get_pressed()[0] == 1 and self.clicked == False:
self.clicked = True
print('gotcha' + str(self))
self.wired = True
if pygame.mouse.get_pressed()[0] == 0:
self.clicked = False
def draw_line(self):
if pygame.mouse.get_pressed()[0]:
self.pos = pygame.mouse.get_pos()
pygame.draw.line(screen,red,(self.x + 15, self.y + 15),(self.pos), 3)
# these are the colors
green = (48, 141, 70)
grey = (211, 211, 211)
lime = (201, 223, 202)
purplish = (116,137,192)
orange = (234,168,0)
brown = (59,47,47)
blue = (0,91,150)
red = (255,8,0)
screen = pygame.display.set_mode((1280, 720))
pygame.display.set_caption('Classy, Baby!')
running = 1
xb = pygame.image.load(r'xb7white.png').convert_alpha()
rednode = pygame.image.load('redhole.svg').convert_alpha()
rednode = pygame.transform.scale(rednode, (100, 100))
# make node instances
r1 = Rnode(300, 300, rednode)
r2 = Rnode(500, 300, rednode)
while running:
screen.fill((0, 0, 0))
event = pygame.event.poll()
if event.type == pygame.QUIT:
running = 0
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit()
# if event.type == pygame.MOUSEBUTTONDOWN: # if the user pressed a mouse button
# pos = pygame.mouse.get_pos() # get the mouse pos
# if g1.rect.collidepoint(pos):
r1.put()
r2.put()
if r1.wired:
r1.draw_line()
pygame.display.flip()
pygame.mouse.get_pressed() is not an event, but gives the current state of the mouse buttons. Rnode represents a node and should not draw a line or handle the events. Handle the event in an event loop and add the lines to a list:
import pygame
class Rnode():
def __init__(self, x, y, image_rednode):
self.image_rednode = image_rednode
self.rect = self.image_rednode.get_rect(center = (x, y))
def put(self):
screen.blit(self.image_rednode, self.rect)
class Line():
def __init__(self, nodeFrom, nodeTo):
self.form = nodeFrom
self.to = nodeTo
def draw(self):
p1 = self.form.rect.center
p2 = self.to.rect.center
pygame.draw.line(screen, "yellow", p1, p2, 3)
screen = pygame.display.set_mode((300, 300))
pygame.display.set_caption('Classy, Baby!')
clock = pygame.time.Clock()
#rednode = pygame.image.load('redhole.svg').convert_alpha()
#rednode = pygame.transform.scale(rednode, (100, 100))
rednode = pygame.Surface((40, 40), pygame.SRCALPHA)
pygame.draw.circle(rednode, "red", (20, 20), 20)
nodes = [
Rnode(100, 100, rednode), Rnode(200, 100, rednode),
Rnode(100, 200, rednode), Rnode(200, 200, rednode)]
lines = []
start = None
running = 1
while running:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = 0
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = 0
if event.type == pygame.MOUSEBUTTONDOWN:
for node in nodes:
if node.rect.collidepoint(event.pos):
if start and start != node:
lines.append(Line(start, node))
start = None
else:
start = node
break
screen.fill((0, 0, 0))
for line in lines:
line.draw()
if start:
pygame.draw.line(screen, "yellow", start.rect.center, pygame.mouse.get_pos(), 3)
for node in nodes:
node.put()
pygame.display.flip()
pygame.quit()

how do I cover a window with scalable rects in pygame

how would I go about making the rects be evenly placed throughout the grid while still being scalable. I want to cover the whole window with them and increase how many there are as you scale them down, my intention is to make it look like its zooming out.
import pygame
import sys
width, height = 750, 750
window = pygame.display.set_mode((width, height))
scale = 1
scroll_scale = .05
while True:
graph_size = 50 * scale
graph_count = width / graph_size
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 4:
scale += scroll_scale
elif event.button == 5 and scale > 0:
scale -= scroll_scale
window.fill((255,255,255))
pygame.draw.rect(window, (0,255,0),(0, 0, graph_size, graph_size))
pygame.draw.rect(window, (0,255,0),(0 + graph_size, 0, graph_size, graph_size))
pygame.draw.rect(window, (0,255,0),(0 + graph_size * 2, 0, graph_size, graph_size))
pygame.display.update()
I know there must be a way to do this without drawing all of the rects separately but I just cant seem to find it
Use 2 nested loops and the built-in range function:
import pygame
import sys
width, height = 750, 750
window = pygame.display.set_mode((width, height))
scale = 1
scroll_scale = .05
while True:
graph_size = 50 * scale
graph_count = width / graph_size
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 4:
scale += scroll_scale
elif event.button == 5 and scale > 0:
scale -= scroll_scale
window.fill((255,255,255))
size = round(graph_size)
for x in range(0, width + size, size):
for y in range(0, height + size, size):
c = (0,255,0) if (((x + y) // size) % 2) == 0 else (127, 127, 127)
pygame.draw.rect(window, c, (x, y, size, size))
pygame.display.update()

Other ways to create line animation

I'm trying to create a animation that runs around a shape. The method I came up with is similar to the snake game, drawing the desired part and refresh it with white screen.
For example, if I want to draw a linear line animation, with the max animated length of 3:
lst = [1, 2, 3]
dummy_lst = [1, 1, 1, 2, 3, 3, 3]
colored_position = [[1,1,1], [1,1,2], [1,2,3], [2,3,3], [3,3,3]] # if the numbers overlapped, it draws the same place which is fine
and then I iterate the colored_position to get the animation done.
The real code:
import pygame
import sys
SCREEN_WIDTH = 800
CELLSIZE = 10
x, y = 150, 200
position = [[x+CELLSIZE*i, y] for i in range(50)] +\
[[x+CELLSIZE*50, y+CELLSIZE*i] for i in range(50)] +\
[[x+CELLSIZE*50-CELLSIZE*i, y+CELLSIZE*50]for i in range(50)] +\
[[x, y+CELLSIZE*50-CELLSIZE*i]for i in range(50)]
# creates fake firsts and lasts
def dummy_list(list, length):
return [list[0]]*(length-1) + list + [list[-1]]*(length-1)
def main():
pygame.init()
clock = pygame.time.Clock()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_WIDTH))
count = 0
while True:
clock.tick(10)
screen.fill((255, 255, 255))
dummy = dummy_list(position, 50)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
count = 0
while count < 1: # I could chaget this to see many times it rotates
for i in range(len(dummy)-50+1):
for j in range(9): # change this to adjust how wide the colored part is
pygame.draw.rect(screen, (255, 0, 0), pygame.Rect(
dummy[i+j][0], dummy[i+j][1], CELLSIZE, CELLSIZE), 0)
pygame.display.update()
screen.fill((255, 255, 255))
count += 1
pygame.display.update()
But it seems very hard-coding. since I have to know each part of the desired position's Xs and Ys. What's the smarter way to complete the task?
You want to move the object along a path. I don't see any problem in defining the path through a list of points.
However you can write a function that generates the list of points from a pygame.Rect object:
def generate_positions(rect, step):
return [[x, rect.top] for x in range(rect.left, rect.right, step)] +\
[[rect.right, y] for y in range(rect.top, rect.bottom, step)] +\
[[x, rect.bottom] for x in range(rect.right, rect.left, -step)] +\
[[rect.left, y] for y in range(rect.bottom, rect.top, -step)]
CELLSIZE = 10
x, y = 150, 200
position = generate_positions(pygame.Rect(x, y, 50*CELLSIZE, 50*CELLSIZE), CELLSIZE)
I recommend to use the application loop to draw the snake:
import pygame
import sys
SCREEN_WIDTH = 800
def generate_positions(rect, step):
return [[x, rect.top] for x in range(rect.left, rect.right, step)] +\
[[rect.right, y] for y in range(rect.top, rect.bottom, step)] +\
[[x, rect.bottom] for x in range(rect.right, rect.left, -step)] +\
[[rect.left, y] for y in range(rect.bottom, rect.top, -step)]
CELLSIZE = 10
x, y = 150, 200
position = generate_positions(pygame.Rect(x, y, 10*CELLSIZE, 10*CELLSIZE), CELLSIZE)
def main():
pygame.init()
clock = pygame.time.Clock()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_WIDTH))
current_point = None
snake_len = 9
while True:
clock.tick(10)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
current_point = -snake_len
screen.fill((255, 255, 255))
if current_point != None:
for i in range(max(0, current_point), min(len(position), current_point+snake_len)):
pygame.draw.rect(screen, (255, 0, 0), pygame.Rect(*position[i], CELLSIZE, CELLSIZE), 0)
current_point += 1
if current_point >= len(position):
current_point = None
pygame.display.update()
main()

How do give every circle a random color in pygame?

In this program, I want a bigger movable circle and many multi-colored smaller circles. However, when I run the program all of the smaller circles are all the same color and I cannot figure out how to randomly give each of them a different color. How do I fix this?
import pygame as pg
import random as rd
pg.init()
screen = pg.display.set_mode((800, 600))
p_1_x = 200
p_1_y = 200
p_1_change_x = 0
def p_1(x, y):
player_1 = pg.draw.circle(screen, (2, 2, 0), (x, y), 15)
locations = []
small_color = []
for i in range(50):
red = rd.randint(0, 220)
blue = rd.randint(0, 220)
green = rd.randint(0, 220)
x = rd.randint(100, 700)
y = rd.randint(100, 500)
locations.append((x, y))
small_color.append((red, blue, green))
while True:
screen.fill((255, 255, 255))
for event in pg.event.get():
if event.type == pg.QUIT:
pg.quit()
if event.type == pg.KEYDOWN:
if event.key == pg.K_RIGHT:
p_1_change_x = 1
if event.key == pg.K_LEFT:
p_1_change_x = -1
if event.type == pg.KEYUP:
if event.key == pg.K_RIGHT or pg.K_LEFT:
p_1_change_x = 0
p_1_x += p_1_change_x
p_1(p_1_x, p_1_y)
for locate in locations:
pg.draw.circle(screen, (small_color[i]), locate, 5)
pg.display.update()
In the main loop, you're using i which never changes. Use enumerate to return an index while looping through the locations collection.
Try this code:
for i,locate in enumerate(locations):
pg.draw.circle(screen, (small_color[i]), locate, 5)
be sure you import random library at the first line:
import random as rd
Here is a simple function, which generate random color:
def random_color():
r = rd.randint(0, 255)
g = rd.randint(0, 255)
b = rd.randint(0, 255)
return (r, g, b)
so you can call it when ever you need to:
color = random_color()
draw.circle(screen, color, (x, y), SIZE)
Github repo
I make a repo in the GitHub and did some changes,
Dots are colorful, new dot gets random color and the ball gets bigger whenever eats a dot.
https://github.com/peymanmajidi/Ball-And-Dots-Game__Pygame

Categories