Yes this is all possible. But it is an illustration of how often you can do something amazing with a single line of code (in this case, get real-time interactive animation of a stimulus) but the complexity of the code grows rapidly as you need to add more constraints.
I can’t write this code for you, as this really requires interactive testing which only you can do, but I can point you in the right direction. To figure this out, it is often useful to sketch out a flow chart, thinking of each action that the participant takes, and what information you need to track and what actions to take in response.
In this case, you not only need to track when the mouse button is pressed, but you also need to notice when it first becomes no longer pressed, but was pressed on the immediately preceding check. You also need to track what the last-clicked stimulus was, so when the mouse button is released, you then either bounce that stimulus back to its original location (which also needs to be stored), or if it is overlapping a destination, you snap it to align with it exactly. Some code needs to run at the start of the routine:
# Begin routine code:
square_locations = {} # an empty dictionary
for square in [square01, square02, square03, etc]:
# associate each square with its original location:
square_locations[square] = square.pos
clicked_square = None # initial status
and this is a sketch of the sort of structure you would need to call on each frame:
# each frame code
mouse_down = False
for square in [square01, square02, square03, etc]:
if mouse.isPressedIn(square):
square.pos = mouse.getPos() # animate the square
clicked_square = square # remember which one is being clicked
mouse_down = True
# exit the loop so only drag one stimulus at a time:
break
if not mouse_down and clicked_square is not None:
# the mouse is no longer clicked but it previously was
# so check if clicked_square overlaps each destination square.
# if so, set its position to the exact position of that destination,
# so it "snaps" to it.
# shapeStims have a "contains" method, so you can check if the
# pos attribute of the square is contained within the boundary of
# each destination:
# https://www.psychopy.org/api/visual/shapestim.html
# if not, then look up its original location in the square_locations
# dictionary and send it back there
# Also, store the name of the square and the name of the
# associated destination in the data file, eg something like:
thisExp.addData(square.name, destination.name)
# lastly, don't forget this, so this check doesn't happen again
# until the next time the mouse is released:
clicked_square = None