pygame rect is slipping out of the mouse curser - python

I have a method which moves rects around when you drag them
everything is working good except that when I move the mouse fast it ends up outside the rect therefore making the rect stop moving
even though I am setting the rect center to the mouse pos
so, How can I make the rect never slip out of the mouse curser?
def play_card(self, events):
global mousebutton
mousebutton = pg.mouse.get_pressed()
for index, card in enumerate(self.cards_rects):
posx,posy = pg.mouse.get_pos()
if mousebutton[0] and card.collidepoint(posx, posy):
print("clicked")
self.clicked = True
else:
self.clicked = False
if self.clicked:
x,y = pg.mouse.get_pos()
self.cards_rects[index].center= x,y

I suggest using the MOUSEBUTTONDOWN, MOUSEBUTTONUP and MOUSEMOTION event for dragging. Test if the mouse collides with the object, set an attribute indicating that the dragging has started and calculate the position of the mouse relative to the object in MOUSEBUTTONDOWN:
if event.type == pygame.MOUSEBUTTONDOWN:
self.dragging = self.rect.collidepoint(event.pos)
self.rel_pos = event.pos[0] - self.rect.x, event.pos[1] - self.rect.y
Set the new position in 'MOUSEMOTION' depending on the new mouse position when in drag mode:
if event.type == pygame.MOUSEMOTION and self.dragging:
self.rect.topleft = event.pos[0] - self.rel_pos[0], event.pos[1] - self.rel_pos[1]
End dragging in MOUSEBUTTONUP:
if event.type == pygame.MOUSEBUTTONUP:
self.dragging = False
Minimal example:
import pygame
class DragOperator:
def __init__(self, rect):
self.rect = rect
self.dragging = False
self.rel_pos = (0, 0)
def update(self, event_list):
for event in event_list:
if event.type == pygame.MOUSEBUTTONDOWN:
self.dragging = self.rect.collidepoint(event.pos)
self.rel_pos = event.pos[0] - self.rect.x, event.pos[1] - self.rect.y
if event.type == pygame.MOUSEBUTTONUP:
self.dragging = False
if event.type == pygame.MOUSEMOTION and self.dragging:
self.rect.topleft = event.pos[0] - self.rel_pos[0], event.pos[1] - self.rel_pos[1]
pygame.init()
window = pygame.display.set_mode((500, 500))
clock = pygame.time.Clock()
rectangle = pygame.Rect(0, 0, 40, 40)
rectangle.center = window.get_rect().center
drag_rectangle = DragOperator(rectangle)
run = True
while run:
clock.tick(60)
event_list = pygame.event.get()
for event in event_list:
if event.type == pygame.QUIT:
run = False
drag_rectangle.update(event_list)
rectangle_color = (0, 255, 0) if drag_rectangle.dragging else (255, 0, 0)
window.fill(0)
pygame.draw.rect(window, rectangle_color, rectangle)
pygame.display.flip()
pygame.quit()
exit()

In this part of the code:
if mousebutton[0] and card.collidepoint(posx, posy):
print("clicked")
self.clicked = True
else:
self.clicked = False
The else means that self.clicked will be set False whenever either the mouse is released (not mousebutton[0]) or the mouse escapes from the card by a quick movement (not card.collidepoint(posx, posy)). So the quick movement will cause the card to be dropped.
If the mouse is outside of the card, there are two possibilities:
We were not already dragging the card, and we are still not dragging the card. So self.clicked should be False, but it was already False, so we don't need to do anything.
We were dragging the card, and we will still be dragging the card. So again, we don't need to do anything; self.clicked was True, and should still be True.
We only want to actually set the value back to False if the mouse button is released, so we can e.g. replace the else with elif:
if mousebutton[0] and card.collidepoint(posx, posy):
print("clicked")
self.clicked = True
elif not mousebutton[0]:
self.clicked = False
Another way to do this logic:
if not mousebutton[0]:
self.clicked = False
elif card.collidepoint(posx, posy):
self.clicked = True
That way avoids repeating the mention of mousebutton. If we get to the elif, we already know that mousebutton[0] is set, since otherwise the if branch would have been taken.

Related

How to make image disappear or how to hide image from the screen? [duplicate]

This question already has answers here:
how to make image/images disappear in pygame?
(1 answer)
How to delete one object from a Surface instance?
(1 answer)
How to clean up sprites without covering other things?
(1 answer)
Closed 4 months ago.
I'm making my first pygame game, Plane Delivery game. I'm stuck at the menu. I created menu with my custom background image. Also, I created START button which is used to start the game. When player clicks the START button, I want to hide main menu background, and show the game's background image, world map. Thank you for help!
There's code:
import pygame
pygame.init()
window = pygame.display.set_mode((1920, 1080))
pygame.display.set_caption('Plane Delivery')
POZADINA = (254, 0, 60) # BOJA POZADINE
window.fill(POZADINA)
clock = pygame.time.Clock()
menu_img = pygame.image.load('Plane_Delivery2.jpg')
menu_img = pygame.transform.scale(menu_img, (1920, 1080))
bg_img = pygame.image.load('background.jpg')
bg_img = pygame.transform.scale(bg_img,(1920, 1080))
plane_sprite = pygame.image.load('avion3.png')
x2 = 960
y2 = 540
# -- Main Menu --
#button class
class Button():
def __init__(self, x, y, image, scale):
width = image.get_width()
height = image.get_height()
self.image = pygame.transform.scale(image, (int(width * scale), int(height * scale)))
self.rect = self.image.get_rect()
self.rect.topleft = (x, y)
self.clicked = False
def draw(self, surface):
action = False
#get mouse position
pos = pygame.mouse.get_pos()
#check mouseover and clicked conditions
if self.rect.collidepoint(pos):
if pygame.mouse.get_pressed()[0] == 1 and self.clicked == False:
self.clicked = True
action = True
if pygame.mouse.get_pressed()[0] == 0:
self.clicked = False
#draw button on screen
surface.blit(self.image, (self.rect.x, self.rect.y))
return action
start_img = pygame.image.load('start2.png').convert_alpha()
exit_img = pygame.image.load('exit2.png').convert_alpha()
start_button = Button(30, 400, start_img, 0.8)
exit_button = Button(200, 360, exit_img, 0.4)
run = True
while run:
clock.tick(40)
window.blit(menu_img, (0, 0))
if start_button.draw(window):
window.fill((255, 255, 0))
window.blit(bg_img, (0, 0))
pygame.display.flip()
window.blit(plane_sprite, (x2, y2))
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
while 1:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_w:
y2 += 20
if event.key == pygame.K_s:
y2 -= 20
if event.key == pygame.K_a:
x2 -= 20
if event.key == pygame.K_d:
x2 += 20
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if exit_button.draw(window):
pygame.quit()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.display.update()
Thank you for help! :D

the player is shaking when walking [duplicate]

So I run the code and it just starts glitching out. I am new to pygame.
Here is the code:
import pygame
pygame.init()
# Screen (Pixels by Pixels (X and Y (X = right and left Y = up and down)))
screen = pygame.display.set_mode((1000, 1000))
running = True
# Title and Icon
pygame.display.set_caption("Space Invaders")
icon = pygame.image.load('Icon.png')
pygame.display.set_icon(icon)
# Player Icon/Image
playerimg = pygame.image.load('Player.png')
playerX = 370
playerY = 480
def player(x, y):
# Blit means Draw
screen.blit(playerimg, (x, y))
# Game loop (Put all code for pygame in this loop)
while running:
screen.fill((225, 0, 0))
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# if keystroke is pressed check whether is right or left
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
print("Left arrow is pressed")
if event.key == pygame.K_RIGHT:
print("Right key has been pressed")
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
print("kEYSTROKE RELEASED")
# RGB (screen.fill) = red green blue
player(playerX, playerY)
pygame.display.update()
The image is not the glitching one as I was not able to post a video but it is what my code does
The problem is caused by multiple calls to pygame.display.update(). An update of the display at the end of the application loop is sufficient. Multiple calls to pygame.display.update() or pygame.display.flip() cause flickering.
Remove all calls to pygame.display.update() from your code, but call it once at the end of the application loop:
while running:
screen.fill((225, 0, 0))
# pygame.display.update() <---- DELETE
# [...]
player(playerX, playerY)
pygame.display.update()
If you update the display after screen.fill(), the display will be shown filled with the background color for a short moment. Then the player is drawn (blit) and the display is shown with the player on top of the background.

Why does my sprite reset to the top left of the screen when I try to rotate it with pygame.transform.rotate? [duplicate]

This question already has answers here:
How do I rotate an image around its center using Pygame?
(6 answers)
Closed 1 year ago.
I'm trying to rotate a sprite 90 degrees whenever the player presses the A and D keys, and I've got that working, but the sprite is always sent to the top left corner of the screen whenever this happens. Can someone shed some light on this? As well as that, I've tried to get a shooting system working with shooting asteroids, by creating an asteroid and appending it to a list, but it seems to become a tuple instead of a pygame rectangle when I try to change the y of it, can someone help with that as well?
import pygame, sys, random
from pygame.locals import *
#Imports pygame, system and random as modules to use later in the code.
#Also imports extra stuff from pygame that contains useful variables.
pygame.init()
mainClock = pygame.time.Clock()
FONT = pygame.font.SysFont(None, 48)
#Initialises pygame, sets up the clock to stop the program running too fast
#And also makes the font (must happen after pygame initialises)
WINDOWWIDTH = 1000
WINDOWHEIGHT = 1000
BACKGROUNDCOLOUR = (255, 255, 255)
TEXTCOLOUR = (0, 0, 0)
FPS = 60
PLAYERSPEED = 5
PLAYERIMAGE = pygame.image.load("images/P1.png")
PLAYERRECT = PLAYERIMAGE.get_rect()
ASTEROIDIMAGE = pygame.image.load("images/asteroid.png")
ASTEROIDRECT = ASTEROIDIMAGE.get_rect()
ASTEROIDMINSIZE = 3
ASTEROIDMAXSIZE = 5
ASTEROIDSPEED = 5
ASTEROIDS = []
#Defining Variables and setting up player sprite
def terminate():
pygame.quit()
sys.exit()
def pendingKey():
while True:
for event in pygame.event.get():
if event.type == QUIT:
terminate()
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
terminate()
return
def drawText(text, font, surface, x, y):
textobj = font.render(text, 1, TEXTCOLOUR)
textrect = textobj.get_rect()
textrect.topleft = (x, y)
surface.blit(textobj, textrect)
#Defining functions, to quit pygame and the system.
#And to wait for the escape key to be pressed to start the terminate function.
#And to wait for the quit event (such as at the end of the game).
#And a function to create text on the screen, such as for the title.
WINDOWSURFACE = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
pygame.display.set_caption('Space Penguin Remastered')
pygame.mouse.set_visible(False)
#Creates the games window, sets the name of the window, and makes the mouse invisible
WINDOWSURFACE.fill(BACKGROUNDCOLOUR)
drawText('Space Penguin Remastered', FONT, WINDOWSURFACE, (WINDOWWIDTH / 3), (WINDOWHEIGHT / 3))
drawText('Press a key to start!', FONT, WINDOWSURFACE, (WINDOWWIDTH / 3) - 30, (WINDOWHEIGHT / 3) + 50)
pygame.display.update()
pendingKey()
#Sets the colour of the background and draws the title and some basic instructions
#And updates the display and activates the terminate function
while True:
LEFT = RIGHT = UP = DOWN = SHOOT = LEFTROTATE = RIGHTROTATE = False
#Sets the players start position to half through the screen, and 50 pixels down
#And sets the movement variables to false
while True:
for event in pygame.event.get():
if event.type == QUIT:
terminate()
#Checks for events, first event being the game quitting
if event.type == KEYDOWN:
if event.key == K_LEFT:
RIGHT = False
LEFT = True
if event.key == K_RIGHT:
LEFT = False
RIGHT = True
if event.key == K_UP:
DOWN = False
UP = True
if event.key == K_DOWN:
UP = False
DOWN = True
if event.key == K_SPACE:
SHOOT = True
if event.key == K_a:
RIGHTROTATE = False
LEFTROTATE = True
if event.key == K_d:
LEFTROTATE = False
RIGHTROTATE = True
#Checks for keys being pressed, corresponding to movement.
if event.key == K_ESCAPE:
terminate()
#Checks for escape being pressed, which quits the game.
if event.type == KEYUP:
if event.key == K_ESCAPE:
terminate()
if event.key == K_LEFT:
LEFT = False
if event.key == K_RIGHT:
RIGHT = False
if event.key == K_UP:
UP = False
if event.key == K_DOWN:
DOWN = False
if event.key == K_SPACE:
SHOOT = False
if event.key == K_a:
RIGHTROTATE = False
LEFTROTATE = False
if event.key == K_d:
LEFTROTATE = False
RIGHTROTATE = False
#Checks whether keys have been let go of.
if LEFT and PLAYERRECT.left > 0:
PLAYERRECT.move_ip(-1 * PLAYERSPEED, 0)
if RIGHT and PLAYERRECT.right < WINDOWWIDTH:
PLAYERRECT.move_ip(PLAYERSPEED, 0)
if UP and PLAYERRECT.top > 0:
PLAYERRECT.move_ip(0, -1 * PLAYERSPEED)
if DOWN and PLAYERRECT.bottom < WINDOWHEIGHT:
PLAYERRECT.move_ip(0, PLAYERSPEED)
if SHOOT:
ASTEROIDS.append(ASTEROIDIMAGE.get_rect(center=PLAYERRECT.midtop))
if LEFTROTATE:
PLAYERIMAGE = pygame.transform.rotate(PLAYERIMAGE, 90)
PLAYERRECT = PLAYERIMAGE.get_rect()
if RIGHTROTATE:
PLAYERIMAGE = pygame.transform.rotate(PLAYERIMAGE, -90)
PLAYERRECT = PLAYERIMAGE.get_rect()
for asteroid in ASTEROIDS:
asteroid.y -= 4
for asteroid in ASTEROIDS:
WINDOWSURFACE.blit(ASTEROIDIMAGE, asteroid)
#Moves the player a certain number of pixels in a direction and shoots asteroids
WINDOWSURFACE.fill(BACKGROUNDCOLOUR)
WINDOWSURFACE.blit(PLAYERIMAGE, PLAYERRECT)
pygame.display.update()
mainClock.tick(FPS)
#Fills the background, draws the players image on the rectangle
#And updates the screen and selects how many frames per second the game should tick by
Thanks in advance
Note that you should only call pygame.event.get() once in your application.
When you rotate, you set your player rect as such:
PLAYERRECT = PLAYERIMAGE.get_rect()
You have never specified the value of PLAYERIMAGE.get_rect() and it is (0, 0) by default, so if the player is transported to the top left of the screen. To fix that, simply remove it, it serves no purpose.
Also, your movement code can be simplified.
keys = pygame.key.get_pressed()
PLAYERRECT.move_ip
(
(keys[K_RIGHT] - keys[K_LEFT]) * PLAYERSPEED,
(keys[K_DOWN] - keys[K_UP]) * PLAYERSPEED
)
Thats it.
Code to handle rotation can be simplified as well. Make a function that gets the players rotation angle:
def getPlayerRotation(keys):
if keys[K_a]: return 90
elif keys[K_d]: return -90
return 0
Then use that to rotate your image and draw it.
#https://stackoverflow.com/questions/4183208/how-do-i-rotate-an-image-around-its-center-using-pygame
rotated_image = pygame.transform.rotate(PLAYERIMAGE, getPlayerRotation(keys))
new_rect = rotated_image.get_rect(center = PLAYERIMAGE.get_rect(topleft = PLAYERRECT.topleft).center)
WINDOWSURFACE.blit(rotated_image, new_rect)
Your asteroid isn't drawing because you are trying to draw it before you clear the screen, which draws over the asteroids.
Also you cannot do
ASTEROIDS.append(ASTEROIDIMAGE.get_rect(center=PLAYERRECT.midtop))
because there is only one asteroid.rect object, so what you are appending is a reference to the same object over and over. You need to create a new rect if your want to use a rect, but I got around the problem by using a list.
ASTEROIDS.append(list(PLAYERRECT.center))
and then later:
for asteroid in ASTEROIDS:
asteroid[1] -= 4
Lastly I changed pending key function:
def pendingKey(events):
for event in events:
if event.type == QUIT:
terminate()
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
terminate()
It takes events as argument to avoid calling pygame.event.get() in the function. I also removed the return statement at the end of it since it was causing the function to exit before it got to to through all the events.
Here is the new code:
import pygame, sys, random
from pygame.locals import *
WINDOWWIDTH = 1000
WINDOWHEIGHT = 1000
pygame.init()
mainClock = pygame.time.Clock()
FONT = pygame.font.SysFont(None, 48)
BACKGROUNDCOLOUR = (255, 255, 255)
TEXTCOLOUR = (0, 0, 0)
FPS = 60
PLAYERSPEED = 5
PLAYERIMAGE = pygame.image.load("images/P1.png")
PLAYERRECT = PLAYERIMAGE.get_rect()
ASTEROIDIMAGE = pygame.image.load("images/asteroid.png")
ASTEROIDRECT = ASTEROIDIMAGE.get_rect()
ASTEROIDMINSIZE = 3
ASTEROIDMAXSIZE = 5
ASTEROIDSPEED = 5
ASTEROIDS = []
#Defining Variables and setting up player sprite
def terminate():
pygame.quit()
sys.exit()
def pendingKey(events):
for event in events:
if event.type == QUIT:
terminate()
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
terminate()
def drawText(text, font, surface, x, y):
textobj = font.render(text, 1, TEXTCOLOUR)
textrect = textobj.get_rect()
textrect.topleft = (x, y)
surface.blit(textobj, textrect)
WINDOWSURFACE = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
pygame.display.set_caption('Space Penguin Remastered')
pygame.mouse.set_visible(False)
WINDOWSURFACE.fill(BACKGROUNDCOLOUR)
drawText('Space Penguin Remastered', FONT, WINDOWSURFACE, (WINDOWWIDTH / 3), (WINDOWHEIGHT / 3))
drawText('Press a key to start!', FONT, WINDOWSURFACE, (WINDOWWIDTH / 3) - 30, (WINDOWHEIGHT / 3) + 50)
pygame.display.update()
def getPlayerRotation(keys):
if keys[K_a]: return 90
elif keys[K_d]: return -90
return 0
while True:
events = pygame.event.get()
pendingKey(events)
keys = pygame.key.get_pressed()
PLAYERRECT.move_ip((keys[K_RIGHT] - keys[K_LEFT]) * PLAYERSPEED, (keys[K_DOWN] - keys[K_UP]) * PLAYERSPEED)
#https://stackoverflow.com/questions/4183208/how-do-i-rotate-an-image-around-its-center-using-pygame
rotated_image = pygame.transform.rotate(PLAYERIMAGE, getPlayerRotation(keys))
new_rect = rotated_image.get_rect(center = PLAYERIMAGE.get_rect(topleft = PLAYERRECT.topleft).center)
for event in events:
if event.type == KEYDOWN and event.key == K_SPACE:
ASTEROIDS.append(list(PLAYERRECT.center))
for asteroid in ASTEROIDS:
asteroid[1] -= 4
WINDOWSURFACE.fill(BACKGROUNDCOLOUR)
WINDOWSURFACE.blit(rotated_image, new_rect)
for asteroid in ASTEROIDS:
WINDOWSURFACE.blit(ASTEROIDIMAGE, asteroid)
pygame.display.update()
mainClock.tick(FPS)

pygame: drawing a selection rectangle with the mouse

I have written a simple breakout game in pygame and am writing a level editor. Everything was working until I tried to add a selecting rectangle that has the transparent look (like the one on my desktop background). I can get a rectangle (sorta) but everything else vanishes, and it isn't semi-transparent.
code:
pygame.init()
screen = pygame.display.set_mode(size)
mousescreen = pygame.Surface((screen.get_size())).convert_alpha()
...
in the designing loop:
global xpos, ypos, theBricks, clock, mousedrag, mouseRect
global designing, titles
global theLevels, level, cur_max_level, max_level
mousedrag = False
mouseRect = None
mouseDown = False
while designing:
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.MOUSEBUTTONDOWN:
mouseDown = True
mpos = pygame.mouse.get_pos()
x_position = mpos[0]
y_position = mpos[1]
xpos = ((x_position-left)/BRW) * BRW + left
ypos = ((y_position-top)/BRH) * BRH + top
elif event.type == MOUSEMOTION:
if mouseDown:
newx_pos = mpos[0]
newy_pos = mpos[1]
mousedrag = True
if mousedrag:
mouseRect = Rect(newx_pos, newy_pos, xpos, ypos)
elif event.type == MOUSEBUTTONUP:
if mousedrag:
mousedrag = False
else:
if is_a_brick(xpos, ypos):
del_brick(xpos, ypos)
else:
make_brick(xpos, ypos)
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
designing = False
titles = True
...
in the update screen function:
for bricks in theBricks:
pygame.draw.rect(screen, GREEN, bricks.rect)
if mousedrag:
pygame.draw.rect(mousescreen, RED, mouseRect, 50)
screen.blit(mousescreen, (0,0))
pygame.draw.rect(screen, WHITE, (xpos, ypos, BRW, BRH))
pygame.display.update()
The rectangle is not transparent and everything else vanishes off the screen? Where am I going wrong?
I'm not sure if .convert_alpha() is creating a transparent screen like you think. Try setting the alpha level on mousescreen explicitly:
mousescreen = pygame.Surface((screen.get_size()))
mousescreen.set_alpha(100) # this value doesn't have to be 100
Another way to achieve the same effect is to draw your rect as 4 lines directly onto the screen which means you don't have to have mousescreen at all. In your update screen function:
if mousedrag:
mouseRectCorners = [mouseRect.topleft,
mouseRect.topright,
mouseRect.bottomright,
mouseRect.bottomleft]
pygame.draw.lines(screen, RED, True, mouseRectCorners, 50)
Just make sure you draws these lines after any other objects or they might get hidden. I'm not sure if this option is really considered best practice but it's always good to have options.

Pygame - blitting for a preview picture

First off, this is a school assignment so I want to be upfront about that. Second I'm just asking for advice on the approach, possible help with the code. I'm working on a MSPAINT style clone using some pre-existing code from our book. The code already has the use of the draw.line when pressing mouse button 1. Teacher wants us to add ability to make circles or rectangles. I'm working on the circle part and I have figured out (thanks to the forums on here) how to implement what I wanted to do with the MOUSEBUTTONDOWN and MOUSEBUTTONUP events.. This has brought me to a new Question.. How would I blit then erase then blit a preview of the circle until it is the size the user wants and they release the MOUSEBUTTON and view the final blit...
while keepGoing:
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.QUIT:
keepGoing = False
elif event.type == pygame.MOUSEMOTION:
lineEnd = pygame.mouse.get_pos()
if pygame.mouse.get_pressed() == (1,0,0):
pygame.draw.line(background, drawColor, lineStart, lineEnd, lineWidth)
lineStart = lineEnd
elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 3:
circleStart = pygame.mouse.get_pos()
elif event.type == pygame.MOUSEBUTTONUP and event.button == 3:
circleEnd = pygame.mouse.get_pos()
size = (circleEnd[0] - circleStart[0])
pygame.draw.circle(background, drawColor, circleStart, size, lineWidth)
elif event.type == pygame.KEYDOWN:
myData = (event, background, drawColor, lineWidth, keepGoing)
myData = checkKeys(myData)
event, background, drawColor, lineWidth, keepGoing) = myData
Thanks so much
-Ben
So after some thinking this is the best solution I came up with using pygame. Tell me what you think and if it has helped you.
import pygame,sys,math #---- Import modules we will need
pygame.init() #---- Initialize the module
def get_rad(origin_x,origin_y,x,y): #----- Returns the appropriate radius
return math.sqrt((origin_x - x)**2 + (origin_y - y)**2) #----- Distance between 2
#----- points
screen = pygame.display.set_mode((400,400)) #----- Sets up the screen
clock = pygame.time.Clock() #------- Sets up the clock
mouse_button = 0 #--------- This variable is used to determine whether a mouse button
#--------- has been pressed
draw_final_circle = False #---------- This variable lets us know that we should draw the
#---------- final circle
while True: #------ main loop
clock.tick(60) #------ Limit the Fps
mouse_button0 = mouse_button #-------- This variable holds the previous value of
#-------- mouse_button(it will be useful later)
mouse_x,mouse_y = pygame.mouse.get_pos() #----- Get the mosue coordinates
for e in pygame.event.get(): #---- Cycle through events
if e.type == pygame.QUIT: pygame.quit();sys.exit() #--Quit when window is closed
if e.type == pygame.MOUSEBUTTONDOWN: #---- If the mouse button is pressed
if mouse_button == 0: #---- if the mouse button is released
mouse_button = 1 #----- set it to pressed basically
originx,originy = mouse_x,mouse_y #---- keep the mouse_x,mouse_y pos
if e.type == pygame.MOUSEBUTTONUP: #---- if the mouse button is released
if mouse_button == 1: #-------- if it is pressed
mouse_button = 0 #--------- set it to released
screen.fill((255,255,255)) #---- clear the screen
#-------- If a mouse button is pressed and a circle can be drawn (rad>width) ------#
if mouse_button == 1 and get_rad(originx,originy,mouse_x,mouse_y) > 1:
rad = int(get_rad(originx,originy,mouse_x,mouse_y)) #---- get the radius(as int)
pos = mouse_x,mouse_y
pygame.draw.circle(screen,(0,0,0),pos,rad,1) #--- draw the circle
#----------------------------------------------------------------------------------#
#---------- if the button is released but in the previous loop it was pressed -----#
if mouse_button == 0 and mouse_button0 == 1:
draw_final_circle = True #----- set the final circle boolean to True
if draw_final_circle: #----- if the final circle is decided
pygame.draw.circle(screen,(0,0,0),pos,rad,1) #---- keep drawing it
pygame.display.flip() #----- flip the buffer
I suggest you implement the different modes of your drawing program into different classes that represent the current mode and its state. This way, implementing different modes become very easy.
As for a circle drawing mode, you want to take a copy the screen surface when the user presses the mouse button, and blit that copy to the screen every frame.
Then draw your circle on that copy. This way, you basically "erase" the temporary circles.
Here's a simple example. Press SPACE to cycle between the different modes (draw, circle, rect) and TAB for different colors:
import pygame
from math import hypot
from itertools import cycle
from operator import itemgetter
pygame.init()
screen = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
colors = cycle(sorted(pygame.color.THECOLORS.iteritems(), key=itemgetter(0)))
color = next(colors)[1]
class DrawMode(object):
def __init__(self):
self.last = None
def handle(self, e):
if e.type == pygame.MOUSEBUTTONDOWN and e.button == 1:
self.last = pygame.mouse.get_pos()
if e.type == pygame.MOUSEBUTTONUP and e.button == 1:
self.last = None
def draw(self, screen):
pos = pygame.mouse.get_pos()
if self.last:
pygame.draw.line(screen, color, self.last, pos)
self.last = pos
class ClickReleaseMode(object):
def __init__(self):
self.tmp = None
self.start = None
def handle(self, e):
if e.type == pygame.MOUSEBUTTONDOWN and e.button == 1:
self.start = pygame.mouse.get_pos()
if e.type == pygame.MOUSEBUTTONUP and e.button == 1:
self.start = self.tmp = None
def draw(self, screen):
if not self.tmp:
self.tmp = screen.copy()
pos = pygame.mouse.get_pos()
screen.blit(self.tmp, (0,0))
if self.start:
self.do_draw(screen, pos)
class CircleMode(ClickReleaseMode):
def __init__(self):
super(CircleMode, self).__init__()
def do_draw(self, screen, pos):
r = hypot(pos[0] - self.start[0], pos[1] - self.start[1])
if r >= 2:
pygame.draw.circle(screen, color, self.start, int(r), 2)
class RectMode(ClickReleaseMode):#
def __init__(self):
super(RectMode, self).__init__()
def do_draw(self, screen, pos):
p = pos[0] - self.start[0], pos[1] - self.start[1]
pygame.draw.rect(screen, color, pygame.Rect(self.start, p), 2)
quit = False
modes = cycle((DrawMode, CircleMode, RectMode))
mode = next(modes)()
while not quit:
quit = pygame.event.get(pygame.QUIT)
for e in pygame.event.get():
if e.type == pygame.KEYDOWN:
if e.key == pygame.K_SPACE:
mode = next(modes)()
print 'enter', mode.__class__.__name__
if e.key == pygame.K_TAB:
name, color = next(colors)
print 'changing color to', name, color
mode.handle(e)
mode.draw(screen)
pygame.display.flip()
clock.tick(60)

Categories