The following line of code, I have borrowed as an example to use. What it does is it takes a string and prints it out in pygame, and the line will follow wherever the mouse goes. I simply was wondering how to change the string line for a draw.circle, I keep getting an error. Cheers! Code Below, the string in question is the one saying, "the last button pressed is".
from pygame import *
init()
size = width, height = 800, 600
screen = display.set_mode(size)
button = 0
BLACK = (0, 0, 0)
RED = (255, 255, 255)
font = font.SysFont("Times New Roman",30)
def drawScene(screen, mx, my, button):
draw.rect(screen, BLACK, (0, 0, width, height))
# Draw circle if the left mouse button is down.
string = "The last button pressed is " + str(button) + "."
text = font.render(string, 1, RED)
size = font.size(string)
screen.blit(text, Rect(mx, my, size[0], size[1]))
display.flip()
running = True
myClock = time.Clock()
mx = my = 0
# Game Loop
while running:
for evnt in event.get(): # checks all events that happen
if evnt.type == QUIT:
running = False
if evnt.type == MOUSEBUTTONDOWN:
mx, my = evnt.pos
button = evnt.button
if evnt.type == MOUSEMOTION:
mx, my = evnt.pos
drawScene(screen, mx, my, button)
myClock.tick(60) # waits long enough to have 60 fps
quit()
This will draw a 5 pixel radius circle at mx, my:
pygame.draw.circle(screen, BLACK, (mx, my), 5)
Related
I don't know how to detect when my mouse is on 2 rect and choose (if two rect has an action) the action. For example, in Windows, when two windows are one over the other, it's the first window that will be selected. I want to do exactly the same thing.
import pygame
class Rectangle(pygame.sprite.Sprite):
def __init__(self, screen, rect, x, y, color, name):
super().__init__()
self.screen = screen
self.name = name
self.screen_str = str(screen)
self.rect = rect
self.color = color
self.x, self.y = x, y
self.r = pygame.Surface((self.rect[2], self.rect[3]), pygame.SRCALPHA)
self.rect = self.r.get_rect()
self.rect.x, self.rect.y = x, y
self.r.fill(self.color)
pygame.init()
screen = pygame.display.set_mode((1280, 720))
pygame.display.set_caption("PyStoneTest")
width, height = screen.get_size()
background_default = "image\Settings\Wallpaper\default_1.jpg"
D = {}
D["Rect2"] = Rectangle(screen, (0, 200, width, 70), 0,
50, (255, 255, 0), "Rect2")
D["Rect1"] = Rectangle(screen, (0, 100, width-200, 200), 0,
100, (255, 0, 255), "Rect1")
Programme = ["Rect1", "Rect2"]
while True:
background = pygame.image.load(background_default).convert()
background = pygame.transform.scale(background, (width, height))
for event in pygame.event.get():
x,y = pygame.mouse.get_pos()
if event.type == pygame.QUIT:
pygame.quit()
for element in Programme:
if D[element].rect.collidepoint(x,y) and event.type == pygame.MOUSEBUTTONDOWN:
del Programme[Programme.index(D[element].name)]
Programme.append(D[element].name)
print(Programme)
screen.blit(background, (0, 0))
for element in Programme:
screen.blit(D[element].r, D[element].rect)
pygame.display.update()
You should first run for-loop to check all windows and use last one which collides with mouse.
elif event.type == pygame.MOUSEBUTTONDOWN:
last = None
for element in Programme:
if D[element].rect.collidepoint(event.pos):
last = element
if last:
Programme.remove(last)
Programme.append(last)
print(Programme)
Or you would have to check in reverse order - from last to first - and break loop on first matching rectangle.
elif event.type == pygame.MOUSEBUTTONDOWN:
last = None
for element in reversed(Programme):
if D[element].rect.collidepoint(event.pos):
last = element
break
if last:
Programme.remove(last)
Programme.append(last)
print(Programme)
Minimal working code with other changes
import pygame
# --- classes ---
class Rectangle(pygame.sprite.Sprite):
# I skip `x,y` because I have it in `rect`
def __init__(self, screen, rect, color, name):
super().__init__()
self.screen = screen
self.color = color
self.name = name
self.rect = pygame.Rect(rect)
self.image = pygame.Surface(self.rect.size, pygame.SRCALPHA)
self.image.fill(self.color)
def draw(self):
self.screen.blit(self.image, self.rect)
# --- functions ---
# empty
# --- main ---
pygame.init()
screen = pygame.display.set_mode((1280, 720))
screen_rect = screen.get_rect() # it can be useful to center elements on screen - `d[name].rect.center = screen_rect.center`
pygame.display.set_caption("PyStoneTest")
# raw string
background_default = r"image\Settings\Wallpaper\default_1.jpg"
# load and rescale before `while`-loop
background = pygame.image.load(background_default).convert()
background = pygame.transform.scale(background, screen_rect.size)
d = {} # PEP8: `lower_case_names` for variable
d["Rect2"] = Rectangle(screen, (0, 0, screen_rect.width-100, 70), (255, 255, 0), "Rect2")
d["Rect2"].rect.center = screen_rect.center
d["Rect1"] = Rectangle(screen, (0, 0, 70, screen_rect.height-100), (255, 0, 255), "Rect1")
d["Rect1"].rect.center = screen_rect.center
programme = ["Rect1", "Rect2"] # PEP8: `lower_case_names` for variable
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit() # to skip rest of while-loop
elif event.type == pygame.MOUSEBUTTONDOWN:
selected = None
for name in programme:
if d[name].rect.collidepoint(event.pos):
selected = name
if selected and selected != programme[-1]:
programme.remove(selected)
programme.append(selected)
print('after replace:', programme)
screen.blit(background, (0, 0))
for name in programme:
d[name].draw()
pygame.display.update()
I cannot help with your code since I cannot understand what you are doing but I can offer my own solution. Since pygame renders thigs that are drawn later on the top, you can change the rendering order of your rectangles by checking which rectangle is being clicked and swapping it with the last rectangle in your list.
Here is an example. The colors in my example change weirdly but that's because I am generating them on the fly just to be able to tell the different between the different rects. You shouldn't have this problem.
import pygame
pygame.init()
screen = pygame.display.set_mode((1280, 720))
pygame.display.set_caption("PyStoneTest")
rects = []
for i in range(10):
rects.append(pygame.Rect(i * 25, 100, 30, 30))
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
screen.fill((0, 0, 0))
c = 1
for rect in rects:
pygame.draw.rect(screen, (c, 100, 100), rect)
c += 20
clicked = pygame.mouse.get_pressed()
x,y = pygame.mouse.get_pos()
for rect in rects:
if (rect.collidepoint(x, y) and clicked[0]):
#swap it
rects[-1][:], rect[:] = rect[:], rects[-1][:]
pygame.display.update()
I've been having a little issue with my code. I am trying to make a minesweeper game. as you know, minesweeper has a reset button and a timer at the top, so I've made my list start 50 from the top, to be able to add them down the line, however, this also means when I click on a square, the square a couple of rows down gets clicked instead.
How can I avoid this? is there a way to make it start lower without the mouse click also being lower? or which values will I need to change for it to accommodate it?
Thank you
import pygame
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
WIDTH = 20
HEIGHT = 20
MARGIN = 5
grid = []
for row in range(10):
grid.append([])
for column in range(10):
grid[row].append(0)
print(grid)
pygame.init()
WINDOW_SIZE = [255, 265]
screen = pygame.display.set_mode(WINDOW_SIZE)
pygame.display.set_caption("Array Backed Grid")
done = False
clock = pygame.time.Clock()
# -------- Main Program Loop -----------
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
pos = pygame.mouse.get_pos()
column = pos[0] // (WIDTH + MARGIN)
row = pos[1] // (HEIGHT + MARGIN)
grid[row][column] = 1
print("Click ", pos, "Grid coordinates: ", row, column)
screen.fill(BLACK)
for row in range(10):
for column in range(10):
color = WHITE
if grid[row][column] == 1:
color = GREEN
pygame.draw.rect(screen,
color,
[(MARGIN + WIDTH) * (column) + MARGIN,
50+(MARGIN + HEIGHT) * row + MARGIN,
WIDTH,
HEIGHT])
clock.tick(60)
pygame.display.flip()
pygame.quit()
Your grid Y starts at 50, so subtract 50 from the mouse position when determining the row.
row = (pos[1]-50) // (HEIGHT + MARGIN)
Also add 50 to your window size to see the entire grid.
WINDOW_SIZE = [255, 315]
I know that there are several similar questions online, but none of them really helped me. I simply want to draw a grid and give the user the option to click into those grid cells. Every time the user clicks, the color/fill of the cell should change from black to white.
What I'm doing at the moment is the following:
BLACK = (0, 0, 0)
WHITE = (200, 200, 200)
def drawGrid(h, w, blocksize):
for x in range(w):
for y in range(h):
rect = pygame.Rect(x*blocksize, y*blocksize,
blocksize, blocksize)
pygame.draw.rect(SCREEN, WHITE, rect, 1)
def handle_events():
col = WHITE
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == MOUSEBUTTONDOWN:
col = WHITE
# determine mouse position
mpos_x, mpos_y = event.pos
# determine cell number
coord = mpos_x // blocksize, mpos_y // blocksize
rect = pygame.Rect(coord[0]*blocksize, coord[1]*blocksize,
blocksize, blocksize)
pygame.draw.rect(SCREEN, col, rect, 1)
pygame.display.update()
def main():
global SCREEN, CLOCK, blocksize
w = int( sys.argv[1] )
h = int( sys.argv[2] )
blocksize = int( sys.argv[3] )
pygame.init()
SCREEN = pygame.display.set_mode((h, w))
CLOCK = pygame.time.Clock()
SCREEN.fill(BLACK)
drawGrid(h,w,blocksize)
handle_events()
if __name__ == "__main__":
main()
The program is printing the grid. However, when I click somewhere nothing happens. I know this is not the best code, so I would appreciate for any suggestion.
I changed the code a little and it worked properly, pygame.draw.rect(SCREEN, col, rect, 1) you draw same thing and you can't see the change. You should use pygame.draw.rect(SCREEN, col, rect):
import pygame
import sys
BLACK = (0, 0, 0)
WHITE = (200, 200, 200)
# WINDOW_HEIGHT = 400
# WINDOW_WIDTH = 400
def drawGrid(h, w, blocksize):
#blockSize = 20 #Set the size of the grid block
for x in range(w):
for y in range(h):
rect = pygame.Rect(x*blocksize, y*blocksize,
blocksize, blocksize)
pygame.draw.rect(SCREEN, WHITE, rect, 1)
def handle_events():
#coords_list = []
col = WHITE
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
col = WHITE
# determine mouse position
mpos_x, mpos_y = event.pos
# determine cell number
coord = mpos_x // 32, mpos_y // 32
rect = pygame.Rect(coord[0]*32, coord[1]*32,
32, 32)
pygame.draw.rect(SCREEN, col, rect)
#coords_list.append(coord)
pygame.display.update()
#return coords_list
def main():
global SCREEN, CLOCK, blocksize
pygame.init()
SCREEN = pygame.display.set_mode((480, 640))
CLOCK = pygame.time.Clock()
SCREEN.fill(BLACK)
drawGrid(480,640,32)
handle_events()
if __name__ == "__main__":
main()
As a suggestion, I think you should use sprite for every cell. For example like this:
class Cell(pygame.sprite.Sprite):
def __init__(self, sprite_group, x, y, cell_dimension, color=BLACK):
self.groups = sprite_group
pygame.sprite.Sprite.__init__(self, self.groups)
self.image = pygame.Surface((cell_dimension, cell_dimension))
self.image.fill(color)
self.rect = self.image.get_rect()
self.rect.x = x * cell_dimension
self.rect.y = y * cell_dimension
self.clicked = False
def update(self):
if self.clicked:
self.image.fill(WHITE)
I'm making a constellation that starts from (0,0). The previous line has to disappear after a two second delay and when the left is clicked within the two second delay, white circles should appear. I don't know why my timer isn't working and I'm not sure how to make the line disappear. Also the circles don't appear. This is my code
from pygame import *
import random
init()
size = width, height = 700, 700
screen = display.set_mode(size)
button = 0
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
BLUE = (0,0,255)
WHITE = (255,255,255)
colors = (RED,GREEN,BLUE)
time.set_timer(USEREVENT, 2000)
mx = 0
my = 0
def drawScene(screen, button):
if button == 1:
draw.circle(screen,RED,(mx,my), 5)
draw.line(screen,RED,(mx,my),(lx,ly),2)
draw.circle(screen,RED,(lx,ly), 5)
display.flip()
if button == 3:
draw.line(screen,random.choice(colors),(mx,my),(lx,ly),2)
draw.circle(screen,random.choice(colors),(lx,ly), 5)
display.flip()
running = True
myClock = time.Clock()
start = time.get_ticks()
# Game Loop
while running:
lx = mx
ly = my
for evnt in event.get(): # checks all events that happen
if evnt.type == QUIT:
running = False
if evnt.type == MOUSEBUTTONDOWN:
mx,my = evnt.pos
button = evnt.button
cx,cy = mouse.get_pos()
draw.circle(screen,WHITE,(cx,cy),5)
if evnt.type == USEREVENT:
my_event = event.Event(USEREVENT)
time.set_timer(my_event , 2000)
drawScene(screen, button)
myClock.tick(60) # waits long enough to have 60 fps
quit()
I hope this is what you were wanting and the comments are clear.
from pygame import *
import random
init()
size = width, height = 700, 700
screen = display.set_mode(size)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
WHITE = (255, 255, 255)
colors = (RED, GREEN, BLUE)
time.set_timer(USEREVENT, 2000)
mx = 0
my = 0
def drawScene(screen, button, prev_point, new_point):
if button == 1:
draw.circle(screen, RED, prev_point, 5)
draw.circle(screen, WHITE, new_point, 5)
draw.line (screen, RED, prev_point, new_point, 2)
display.flip()
if button == 3:
draw.line (screen, random.choice(colors), prev_point, new_point,2)
draw.circle(screen, random.choice(colors), new_point, 5)
display.flip()
running = True
myClock = time.Clock()
prev_render = time.get_ticks() #time in ticks that last render occured
prev_point = (mx, my) #where previous line ended
#function for when to re draw the scene
#ternary if. if override == True, then return true. Else return if time since last update > 2secs
rerender = lambda time_since_update, override: (time_since_update>2000, True)[override]
# Game Loop
while running:
override = False
for evnt in event.get(): # checks all events that happen
if evnt.type == QUIT:
running = False
if evnt.type == MOUSEBUTTONDOWN:
override = True #force window to rerender
new_point = evnt.pos #(mx, my)
button = evnt.button
#rerender window only if necessary. (personal preference)
updated_at = time.get_ticks()
dt = updated_at-prev_render #time difference
if(rerender(dt, override)):
screen.fill(BLACK)
drawScene(screen, button, prev_point, new_point)
prev_point = new_point
prev_render = updated_at
display.update()
myClock.tick(60) # waits long enough to have 60 fps
quit()
This is not a complete solution, but I'll give you a general idea how how to start
Create a list of times and a list of lines:
lines = []
Get the current time in the main application loop:
current_time = pygame.time.get_ticks()
When the mouse is clicked then compute the time when the line has to disappear (current_time + 2000) and compute a random color.
Add a Dictionary with the start point, the end point, the time when the line has to disappear and the color to the list of lines.
If the list of lines is empty, then the start point of the line is (0, 0) else the start point is the end point of the last line in the list:
if event.type == pygame.MOUSEBUTTONDOWN:
disappear_time = current_time + 2000
line_color = random.choice(colors)
prev_pt = (0, 0) if len(lines) == 0 else lines[-1]['end']
lines.append({'start': prev_pt, 'end': event.pos, 'time': disappear_time, 'color': line_color})
When the current time exceeds the time which is stored in time, then remove a point and a time form the lists of lines:
if len(times) > 0 and current_time > times[0]:
del lines[0]
Draw the lines by and circles in a loop:
screen.fill(BLACK)
for li in lines:
pygame.draw.line(screen, li['color'], li['start'], li['end'], 2)
pygame.draw.circle(screen, li['color'], li['start'], 5)
pygame.draw.circle(screen, li['color'], li['end'], 5)
See the example:
import pygame
import random
pygame.init()
size = width, height = 700, 700
screen = pygame.display.set_mode(size)
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
BLUE = (0,0,255)
WHITE = (255,255,255)
colors = (RED,GREEN,BLUE)
lines = []
running = True
myClock = pygame.time.Clock()
while running:
current_time = pygame.time.get_ticks()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.MOUSEBUTTONDOWN:
disappear_time = current_time + 2000
line_color = random.choice(colors)
prev_pt = (0, 0) if len(lines) == 0 else lines[-1]['end']
lines.append({'start': prev_pt, 'end': event.pos, 'time': disappear_time, 'color': line_color})
if len(lines) > 0 and current_time > lines[0]['time']:
del lines[0]
screen.fill(BLACK)
for li in lines:
pygame.draw.line(screen, li['color'], li['start'], li['end'], 2)
pygame.draw.circle(screen, li['color'], li['start'], 5)
pygame.draw.circle(screen, li['color'], li['end'], 5)
pygame.display.flip()
myClock.tick(60)
quit()
I created a window with a width and height of 800 pixels using pygame then drew rectangles with size 32 to make the window a 25x25 grid. What I want to do is change the color of the rectangle I click to change.
My Code:
def createGrid():
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 800
BLOCK_SIZE = 32
WHITE = (255,255,255)
pygame.init()
frame = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("PathFinder")
frame.fill(WHITE)
for y in range(SCREEN_HEIGHT):
for x in range(SCREEN_WIDTH):
rect = pygame.Rect(x*BLOCK_SIZE, y*BLOCK_SIZE, BLOCK_SIZE - 1, BLOCK_SIZE - 1)
pygame.draw.rect(frame, (0,250,0), rect)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
pygame.display.update()
First you should create list with all rects and they colors.
Next you should draw all rect inside while loop.
And finally you have to use event.MOUSEBUTTONDOWN to get mouse click and compare mouse position with every rect on list and change color on list.
import pygame
import sys
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 800
BLOCK_SIZE = 32
WHITE = (255,255,255)
pygame.init()
frame = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("PathFinder")
# create list with all rects
all_rects = []
for y in range(0, SCREEN_HEIGHT, BLOCK_SIZE):
row = []
for x in range(0, SCREEN_WIDTH, BLOCK_SIZE):
rect = pygame.Rect(x, y, BLOCK_SIZE-1, BLOCK_SIZE-1)
row.append([rect, (0, 255, 0)])
all_rects.append(row)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.MOUSEBUTTONDOWN:
# check which rect was clicked and change its color on list
for row in all_rects:
for item in row:
rect, color = item
if rect.collidepoint(event.pos):
if color == (0, 255, 0):
item[1] = (255, 0, 0)
else:
item[1] = (0, 255, 0)
# draw all in every loop
frame.fill(WHITE)
for row in all_rects:
for item in row:
rect, color = item
pygame.draw.rect(frame, color, rect)
pygame.display.flip()
Eventually you could draw all rects before loop and use event to draw new rect on clicked rect. But you still need list with all rects to check which rect was click and what is its color.
It depends on what your code is intended to do? This example shows how to paint to the primary display surface but doesn't keep track of which colour is where.
import pygame
white = (255, 255, 255)
red = (255, 0, 0)
size = 32
pygame.init()
s = pygame.display.set_mode((800, 800))
s.fill(white)
# press escape to exit example
while True:
e = pygame.event.get()
if pygame.key.get_pressed()[pygame.K_ESCAPE]: break
x = int(pygame.mouse.get_pos()[0] / size) * size
y = int(pygame.mouse.get_pos()[1] / size) * size
if pygame.mouse.get_pressed()[0]:
pygame.draw.rect(s, red, (x, y, size, size), 0)
pygame.display.update()
pygame.time.Clock().tick(60)
pygame.quit()