Drag and Drop in Pygame


pandorasbox

Still Fresh
Joined
Jan 15, 2013
Messages
3
Hi! I am trying to figure out how to click and drag and drop any item on pygame. I was testing with a simple circle and this is what I have:

from pygame import *
screen=display.set_mode((600,400))
running=True
while running:
    for evnt in event.get():
        if evnt.type == QUIT:
            running=False
    x,y=mouse.get_pos()
    b=mouse.get_pressed()
    if evnt.type == MOUSEBUTTONDOWN:
        if evnt.button == 1:
            screencopy=screen.copy()
    if b[0] == 1:
        screen.blit(screencopy,(0,0))
        draw.circle(screen,(255,0,0),(x,y),20)
    display.flip()       
quit()

I have a couple problems. Firstly although it does drag and drop, when your mouse is first pressed down another circle will also appear where you first clicked so now you have one permanent circle and another one that is being dragged. My second problem is that once you let go and the circle is "dropped", and you click again with in a certain time frame (I think that's the problem), the old circle disappears and your mouse begins to drag a new one.

Could I get some help please? I would really like it if you could use what I have here and tweak it just a little? Because I really would like the code to be really basic as it is now since I am a very very new beginner and barely understand any of the other code I've seen online.

The second problem is not as big a deal, though. I'd be happy with just the first one solved.

Thanks!
 
When you hold the MOUSEBUTTONDOWN, you are still taking copies of the screen.  It is highly unlikely that it is down for only one frame, therefore you are drawing a circle to a screen that you are about to copy again.  That's why there are two circles.

I cannot repeat the second effect.

What, exactly, are you trying to do?
 
How do I ensure it only copies once? Would just remove the MOUSEBUTTONDOWN? And what would I replace it with? Sorry I'm not very good at this.

I think if I solve the first problem the second one will also be solved, it might be a similar problem. I'm not too sure how to describe it clearly, but you can run the program and test it if you like.

Thanks so much for your help!
 
You need to know the difference between the first frame the mouse is held down, and the other frames it's down.  Copy only on the first frame, before you draw the circle:


from pygame import *

screen=display.set_mode((600,400))
running=True
FirstFrameDown=True # Initialization - we haven't entered second frames, so this is true
while running:
for evnt in event.get():
if evnt.type == QUIT:
running=False
x,y=mouse.get_pos()
b=mouse.get_pressed()
if evnt.type == MOUSEBUTTONDOWN:
if evnt.button == 1:
if FirstFrameDown == True: # Is this the first frame?
screencopy=screen.copy()
FirstFrameDown=False # Later frames are disqualified
if evnt.type == MOUSEBUTTONUP: # Did we lift the mouse button?
FirstFrameDown=True # Reset the flag
if b[0] == 1:
screen.blit(screencopy,(0,0))
draw.circle(screen,(255,0,0),(x,y),20)
display.flip()
quit()

The problem with what you're doing here is that it is not very extensible - it's barely able to look like it's doing what you want - it's not really dragging an object, so much as redrawing a new circle every frame where the mouse is.  This is how I would do it:


import pygame

class Disk: # Something we can create and manipulate
def __init__(self,color,pos,size): # initialze the properties of the object
self.color=color
self.pos=pos
self.size=size

def Render(self,screen):
pygame.draw.circle(screen,self.color,self.pos,self.size)

def main(): # Where we start
screen=pygame.display.set_mode((600,400))
running=True
RenderList=[] # list of objects
MousePressed=False # Pressed down THIS FRAME
MouseDown=False # mouse is held down
MouseReleased=False # Released THIS FRAME
Target=None # target of Drag/Drop
while running:
screen.fill((0,0,0)) # clear screen
pos=pygame.mouse.get_pos()
for Event in pygame.event.get():
if Event.type == pygame.QUIT:
running=False
break # get out now

if Event.type == pygame.MOUSEBUTTONDOWN:
MousePressed=True
MouseDown=True

if Event.type == pygame.MOUSEBUTTONUP:
MouseReleased=True
MouseDown=False

if MousePressed==True:
for item in RenderList: # search all items
if (pos[0]>=(item.pos[0]-item.size) and
pos[0]<=(item.pos[0]+item.size) and
pos[1]>=(item.pos[1]-item.size) and
pos[1]<=(item.pos[1]+item.size) ): # inside the bounding box
Target=item # "pick up" item

if Target is None: # didn't find any?
Target=Disk((0,0,255),pos,10) # create a new one
RenderList.append(Target) # add to list of things to draw

if MouseDown and Target is not None: # if we are dragging something
Target.pos=pos # move the target with us

if MouseReleased:
Target=None # Drop item, if we have any

for item in RenderList:
item.Render(screen) # Draw all items

MousePressed=False # Reset these to False
MouseReleased=False # Ditto
pygame.display.flip()
return # End of function

if __name__ == '__main__': # Are we RUNNING from this module?
main() # Execute our main function

Granted, this is a bit much for a drag/drop demo, but this can be extended - any object with a position, color, and size can be manipulated with this loop.  This does not use a lot of memory saving the whole display, nor is it using a lot of time saving and re-blitting it.

I highly recommend learning more about the basics of programming before you get into more advanced topics like user interfaces or games.  Fortunately Python is great for this.  I'd start with python's own tutorial, then move onto tutorials on Object-Oriented Programming and Event-Driven Programming. Finally, I'd peek at design patterns to see how powerful programs can be.
 
Back
Top