Line drawing in pygame - python

I am trying to draw a line in pygame that has a rounded cap unlike the image below. Is there a way I can do this. Ideally I would want a smooth line with no breaks and a rounded edge as opposed to the flat edge it currently has. My current code for drawing a line is as follows
pygame.draw.line(window, (0, 0, 0), start, end, 50)

The best I can think of is to use pygame.draw.lines() and draw additional circles (pygame.draw.circle) at the joints:
points = [(100, 100), (300, 150), (250, 300)]
pygame.draw.lines(window, (0, 0, 0), False, points, 50)
for p in points:
pygame.draw.circle(window, (0, 0, 0), p, 25)
See also Paint
Minimal example:
repl.it/#Rabbid76/PyGame-PaintFreeThickLine
import pygame
pygame.init()
window = pygame.display.set_mode((500, 500))
clock = pygame.time.Clock()
lines = []
draw = False
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.MOUSEBUTTONDOWN:
draw = not draw
if draw:
lines.append([event.pos])
if event.type == pygame.MOUSEMOTION and draw:
lines[-1].append(event.pos)
window.fill((255, 255, 255))
for points in lines:
if len(points) > 1:
pygame.draw.lines(window, (0, 0, 0), False, points, 50)
for p in points:
pygame.draw.circle(window, (0, 0, 0), p, 25)
pygame.display.flip()
pygame.quit()
exit()

Related

How to draw circle count down in pygame? [duplicate]

How can i make this kind of countdown in Pygame? (i'm looking for how could i make the circle's perimeter decrease, that's the issue, because displaying the time isn't hard )
Keep in mind that how long the perimeter of the circle is and the displayed time should be in proportion with each other.
Just use pygame.draw.arc and specify the stop_angle argument depending on the counter:
percentage = counter/100
end_angle = 2 * math.pi * percentage
pygame.draw.arc(window, (255, 0, 0), arc_rect, 0, end_angle, 10)
Minimal example:
import pygame
import math
pygame.init()
window = pygame.display.set_mode((200, 200))
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 100)
counter = 100
text = font.render(str(counter), True, (0, 128, 0))
timer_event = pygame.USEREVENT+1
pygame.time.set_timer(timer_event, 1000)
def drawArc(surf, color, center, radius, width, end_angle):
arc_rect = pygame.Rect(0, 0, radius*2, radius*2)
arc_rect.center = center
pygame.draw.arc(surf, color, arc_rect, 0, end_angle, width)
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == timer_event:
counter -= 1
text = font.render(str(counter), True, (0, 128, 0))
if counter == 0:
pygame.time.set_timer(timer_event, 0)
window.fill((255, 255, 255))
text_rect = text.get_rect(center = window.get_rect().center)
window.blit(text, text_rect)
drawArc(window, (255, 0, 0), (100, 100), 90, 10, 2*math.pi*counter/100)
pygame.display.flip()
pygame.quit()
exit()
Sadly the quality of pygame.draw.arc with a width > 1 is poor. However this can be improved, using cv2 and cv2.ellipse:
import pygame
import cv2
import numpy
pygame.init()
window = pygame.display.set_mode((200, 200))
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 100)
counter = 100
text = font.render(str(counter), True, (0, 128, 0))
timer_event = pygame.USEREVENT+1
pygame.time.set_timer(timer_event, 1000)
def drawArcCv2(surf, color, center, radius, width, end_angle):
circle_image = numpy.zeros((radius*2+4, radius*2+4, 4), dtype = numpy.uint8)
circle_image = cv2.ellipse(circle_image, (radius+2, radius+2),
(radius-width//2, radius-width//2), 0, 0, end_angle, (*color, 255), width, lineType=cv2.LINE_AA)
circle_surface = pygame.image.frombuffer(circle_image.flatten(), (radius*2+4, radius*2+4), 'RGBA')
surf.blit(circle_surface, circle_surface.get_rect(center = center))
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == timer_event:
counter -= 1
text = font.render(str(counter), True, (0, 128, 0))
if counter == 0:
pygame.time.set_timer(timer_event, 0)
window.fill((255, 255, 255))
text_rect = text.get_rect(center = window.get_rect().center)
window.blit(text, text_rect)
drawArcCv2(window, (255, 0, 0), (100, 100), 90, 10, 360*counter/100)
pygame.display.flip()
pygame.quit()
exit()

Pygame: display doesn't update

I am having an issue where the display doesn't update for some reason. I have confirmed that x goes up but the display doesn't show it for some reason.
Code:
import pygame
SIZE = (500, 500)
CENTER = (SIZE[0] / 2, SIZE[1] / 2)
FPS = 60
COLORS = {
"white": (255, 255, 255),
"red": (255, 0, 0),
"green": (0, 255, 0),
"blue": (0, 0, 255),
"black": (0, 0, 0)
}
WIN = pygame.display.set_mode(SIZE)
pygame.display.set_caption("what da dog doing?")
def main():
clock = pygame.time.Clock()
run = True
player = pygame.draw.rect(WIN, COLORS.get(
"white"), (CENTER[0] - 400 / 2, CENTER[1] - 70 / 2, 400, 70), border_radius=10)
while run:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
player.x += 1
pygame.display.update()
pygame.quit()
if __name__ == "__main__":
main()
Any help is appreciated.
you need to place stuff that is supposed to be updated in the main loop, also you need to fill the screen with some color (or blit an image that is large enough to cover the area you need) to draw over the previously drawn stuff
def main():
clock = pygame.time.Clock()
run = True
player = pygame.draw.rect(WIN, COLORS.get(
"white"), (CENTER[0] - 400 / 2, CENTER[1] - 70 / 2, 400, 70), border_radius=10)
while run:
clock.tick(FPS)
WIN.fill((0, 0, 0))
pygame.draw.rect(WIN, COLORS.get('white'), player, border_radius=10)
player.x += 1
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.display.update()
pygame.quit()

Questions regarding the pygame.mask function

So I read the documentation of pygame but I could not understand it clearly. I recently asked a question about bitmap fonts and I got some code as my answer; here is the code:
import pygame
pygame.init()
win = pygame.display.set_mode((800, 600))
font = pygame.font.Font("freesansbold.ttf", 32)
i = 0
text = "hello how are you?"
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
letter = text[i]
text_1 = font.render(letter, True, (255, 255, 255))
bw, bh = font.size(letter)
glyph_rect = pygame.mask.from_surface(text_1).get_bounding_rects()
# print(glyph_rect)
if glyph_rect:
gh = glyph_rect[0].height
print(f'letter {letter} bitmap height: {bh} glyph height: {gh}')
win.fill((0, 0, 0))
win.blit(text_1, (0, 0))
pygame.display.update()
i += 1
run = i < len(text)
pygame.quit()
So, my questions are on the line glyph_rect = pygame.mask.from_surface(text_1).get_bounding_rects().
What does the pygame.mask.from_surface() function do?
What does the line glyph_rect = pygame.mask.from_surface(text_1).get_bounding_rects() do?
What arguments does the variable glyph_rect return, and what is the meaning of those arguments?
pygame.mask.from_surface creates a pygame.mask.Mask object form a pygame.Surface.
A Surface is bitmap. A Mask is an 2 dimensional array with Boolean values. The Mask created is the size of the _Surface. A field is True if the corresponding pixel in the surface is not transparent, and False if it is transparent.
pygame.mask.Mask.get_bounding_rects creates a list of pygame.Rect objects. Each rectangle describes a bounding area of connected pixles.
If the Surface contains exactly 1 connected image, you will get exactly 1 rectangle surrounding the image.
See the example. The black rectangle is the Surface rectangle and the red rectangle is the bound rectangle of the connected component:
repl.it/#Rabbid76/ImageHitbox
import pygame
def getMaskRect(surf, top = 0, left = 0):
surf_mask = pygame.mask.from_surface(surf)
rect_list = surf_mask.get_bounding_rects()
surf_mask_rect = rect_list[0].unionall(rect_list)
surf_mask_rect.move_ip(top, left)
return surf_mask_rect
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
try:
my_image = pygame.image.load('Bomb-256.png')
except:
my_image = pygame.Surface((200, 200), pygame.SRCALPHA)
pygame.draw.circle(my_image, (0, 128, 0), (60, 60), 40)
pygame.draw.circle(my_image, (0, 0, 128), (100, 150), 40)
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pos = window.get_rect().center
my_image_rect = my_image.get_rect(center = pos)
my_image_mask_rect = getMaskRect(my_image, *my_image_rect.topleft)
window.fill((255, 255, 255))
window.blit(my_image, my_image_rect)
pygame.draw.rect(window, (0, 0, 0), my_image_rect, 3)
pygame.draw.rect(window, (255, 0, 0), my_image_mask_rect, 3)
pygame.display.flip()
pygame.quit()
exit()

PyGame polygon, using a tuple as a parameter

I want to draw a polygon on screen, like so:
coords = (653,333),(680,444),(680,444),(653,445)
pygame.draw.polygon(screen, (0, 0, 255), coords, 0)
however this does not draw to the screen. Any help appreciated.
ANSWER: coords should be a list of tuples, so for my example the correct answer would be:
coords = [(653,333), (680,444), (680,444), (653,445)]
pygame.draw.polygon(screen, (0, 0, 255), coords, 0)
The code that you gave seems to be correct. I've tested it and it displays a triangle. As suggested by skrx in the comment your screen size is too small and you are not able to see it.
import pygame
pygame.init()
BLUE = (0, 0, 255)
size = [1920, 1080]
screen = pygame.display.set_mode(size)
done = False
clock = pygame.time.Clock()
while not done:
clock.tick(10)
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
temp = (653, 333), (680, 444), (680, 444), (653, 445)
pygame.draw.polygon(screen, BLUE, temp, 0)
pygame.display.flip()
pygame.quit()

Pygame - rect appears and immediately disappears

I am rewriting my question
Pygame - rect disappears before I reach it because I think I have not described it clearly.
Full code:
#! /usr/bin/python
import pygame, time, sys, random, os
from pygame.locals import *
from time import gmtime, strftime
pygame.init()
w = 640
h = 400
screen = pygame.display.set_mode((w, h),RESIZABLE)
clock = pygame.time.Clock()
x = y = 100
def starting():
basicfont = pygame.font.SysFont(None, 48)
text = basicfont.render('Starting...', True, (255, 255, 255), (0, 0, 255))
textrect = text.get_rect()
textrect.centerx = screen.get_rect().centerx
textrect.centery = screen.get_rect().centery
screen.fill((0, 0, 255))
screen.blit(text, textrect)
pygame.display.update()
def taskbar():
basicfont = pygame.font.SysFont(None, 24)
pygame.draw.rect(screen, ((0, 255, 0)), (0, h-40, w, 40), 0)
text = basicfont.render(strftime("%Y-%m-%d", gmtime()), True, (0, 0, 0))
text2 = basicfont.render(strftime("%H:%M:%S", gmtime()), True, (0, 0, 0))
screen.blit(text, (w - 100, h - 37))
screen.blit(text2, (w - 100, h - 17))
logoimage = pygame.image.load("C:\Users\chef\Desktop\logo.png").convert()
logoimage = pygame.transform.scale(logoimage, (40, 40))
screen.blit(logoimage, (0, h-40),)
def start():
button1, button2, button3 = pygame.mouse.get_pressed()
mousex, mousey = pygame.mouse.get_pos()
if 0 < mousex < 40 and h-40 < mousey < h:
if button1:
pygame.draw.rect(screen, ((255, 0, 0)), (0, int(h/2), int(w/6), int(h/2)-40), 0)
pygame.display.update()
starting()
#time.sleep(2)
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type==VIDEORESIZE:
w = event.dict['size'][0]
h = event.dict['size'][1]
screen=pygame.display.set_mode(event.dict['size'],RESIZABLE)
screen.fill((255, 255, 255))
taskbar()
start()
pygame.display.update()
clock.tick(60)
Just like my original question, when I press logoimage.png and the red rectangle pops up, it shows for a second then immediately disappears. How can I make it so that the rect will stay until I press outside of it?
You should move the screen.fill((255, 255, 255)) out of the while loop. This is causing problems, because each time the program loops, it colors the display white, writing over whatever you had there before. One way to work around this is to move the screen.fill((255, 255, 255)) out of the loop and place it before the pygame.draw.rect function. This will color the screen before you place the rect, and will not continue to fill it. For instance:
if button1:
screen.fill((255, 255, 255))
pygame.draw.rect(screen, ((255, 0, 0)), (0, int(h/2), int(w/6), int(h/2)-40), 0)
You could also move it elsewhere or come up with a way to draw rect every loop after you fill the window.
Hope this helped.
You need to flag a boolean that it's been clicked. Something like:
btnClicked = false
def start():
button1, button2, button3 = pygame.mouse.get_pressed()
mousex, mousey = pygame.mouse.get_pos()
if button1:
if 0 < mousex < 40 and h-40 < mousey < h:
btnClicked = true
else:
btnClicked = false
if btnClicked:
pygame.draw.rect(screen, ((255, 0, 0)), (0, int(h/2), int(w/6), int(h/2)-40), 0)
pygame.display.update()

Categories