# Select-Drag-Drop

Hi. I am new to Panda. I wish to achieve the select-drag-drop action for my project involving Chemical Reactions. The User should be able to drag an atom around the screen and drop it where he/she wishes to.

Referring to the Chessboard tutorial that comes along with Panda, Ive written a piece of code (thats a basic modification of the tutorial), but it has obvious logical gaps that I cant find my way around. I would be grateful if someone can help me with this.

``````import direct.directbase.DirectStart
from pandac.PandaModules import CollisionTraverser,CollisionNode
from pandac.PandaModules import CollisionHandlerQueue,CollisionRay
from pandac.PandaModules import AmbientLight,DirectionalLight,LightAttrib
from pandac.PandaModules import TextNode
from direct.gui.OnscreenText import OnscreenText
from direct.showbase.DirectObject import DirectObject
import sys

#First we define some contants for the colors
BLACK = Vec4(0,0,0,1)
WHITE = Vec4(1,1,1,1)
HIGHLIGHT = Vec4(0,1,1,1)
PIECEBLACK = Vec4(.15, .15, .15, 1)

#This function, given a line (vector plus origin point) and a desired z value,
#will give us the point on the line where the desired z value is what we want.
#This is how we know where to position an object in 3D space based on a 2D mouse
#position. It also assumes that we are dragging in the XY plane.
#
#This is derived from the mathmatical of a plane, solved for a given point
def PointAtZ(z, point, vec):
return point + vec * ((z-point.getZ()) / vec.getZ())

#A handy little function for getting the proper position for a point in space to place the atom at
def SquarePos(i):
return Point3((i%8) - 3.5, int(i/8) - 3.5, 0)

class World(DirectObject):
def __init__(self):
#This code puts the standard title and instruction text on screen
self.title = OnscreenText(text="Atom Drag Drop Test",
style=1, fg=(1,1,1,1), font = font,
pos=(0.8,-0.95), scale = .07)
self.escapeEvent = OnscreenText(
text="ESC: Quit", font = font,
style=1, fg=(1,1,1,1), pos=(-1.3, 0.95),
align=TextNode.ALeft, scale = .05)
self.mouse1Event = OnscreenText(
text="Left-click and drag: Pick up and drag piece",
style=1, fg=(1,1,1,1), pos=(-1.3, 0.90), font = font,
align=TextNode.ALeft, scale = .05)

self.accept('escape', sys.exit)              #Escape quits
base.disableMouse()                          #Disble mouse camera control
camera.setPosHpr(0, -13.75, 6, 0, -25, 0)    #Set the camera
self.setupLights()                           #Setup default lighting

#Since we are using collision detection to do picking, we set it up like
#any other collision detection system with a traverser and a handler
self.picker = CollisionTraverser()            #Make a traverser
self.pq     = CollisionHandlerQueue()         #Make a handler

#Make a collision node for our picker ray
self.pickerNode = CollisionNode('mouseRay')

#Attach that node to the camera since the ray will need to be positioned
#relative to it
self.pickerNP = camera.attachNewNode(self.pickerNode)

#Everything to be picked will use bit 1. This way if we were doing other
#collision we could seperate it
self.pickerRay = CollisionRay()               #Make our ray

#Register the ray as something that can cause collisions
#self.picker.showCollisions(render)

#Start the task that handles the picking
self.accept("mouse1", self.grabPiece)       #left-click grabs a piece
self.accept("mouse1-up", self.releasePiece) #releasing places it

#Check to see if we can access the mouse. We need it to do anything else
if base.mouseWatcherNode.hasMouse():
#get the mouse position
mpos = base.mouseWatcherNode.getMouse()

#Set the position of the ray based on the mouse position
self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())

#If we are dragging something, set the position of the object
#to be at the appropriate point over the plane of the board
if self.dragging is not False:
#Gets the point described by pickerRay.getOrigin(), which is relative to
nearPoint = render.getRelativePoint(camera, self.pickerRay.getOrigin())
#Same thing with the direction of the ray
nearVec = render.getRelativeVector(camera, self.pickerRay.getDirection())
self.pieces[self.dragging].obj.setPos(
PointAtZ(.5, nearPoint, nearVec))

#Do the actual collision pass
self.picker.traverse(self.object)
if self.pq.getNumEntries() > 0:
#if we have hit something, sort the hits so that the closest is first
self.pq.sortEntries()
i = int(self.pq.getEntry(0).getIntoNode())
flag=TRUE

def grabPiece(self):
#If the flag set is true,set it to dragging mode
if (flag is not False):
self.dragging = TRUE

def releasePiece(self):
#Letting go of a piece.
if self.dragging is not False:          #Make sure we really are dragging something
self.pieces[self.dragging].obj.setPos(Pos(self.dragging))

#We are no longer dragging anything
self.dragging = False

def setupLights(self):    #This function sets up some default lighting
lAttrib = LightAttrib.makeAllOff()
ambientLight = AmbientLight( "ambientLight" )
ambientLight.setColor( Vec4(.8, .8, .8, 1) )
directionalLight = DirectionalLight( "directionalLight" )
directionalLight.setDirection( Vec3( 0, 45, -45 ) )
directionalLight.setColor( Vec4( 0.2, 0.2, 0.2, 1 ) )
render.attachNewNode( directionalLight.upcastToPandaNode() )
render.attachNewNode( ambientLight.upcastToPandaNode() )
render.node().setAttrib( lAttrib )

class Object:
def __init__(self, position):
self.obj.reparentTo(render)
self.obj.setColor(color)

#Do the main initialization and start 3D rendering
w = World()
run()

``````

Thanks

here there is a working simple sample that shows you how to perform easy drag and dropping:

``````#snatched time ago from chombee's code
#
import direct.directbase.DirectStart
from pandac.PandaModules import *
import sys,random

# Collision mask worn by all draggable objects.
# Collision mask worn by all droppable objects.

highlight = VBase4(.3,.3,.3,1)

class objectMangerClass:
def __init__( self ):
self.objectIdCounter = 0
self.objectDict = dict()

def tag( self, objectNp, objectClass ):
self.objectIdCounter += 1
objectTag = str(self.objectIdCounter)
objectNp.setTag( 'objectId', objectTag )
self.objectDict[objectTag] = objectClass

def get( self, objectTag ):
if objectTag in self.objectDict:
return self.objectDict[objectTag]
return None
objectManger = objectMangerClass()

class dragDropObjectClass:
def __init__( self, np ):
self.model = np
self.previousParent = None
objectManger.tag( self.model, self )

def onPress( self, mouseNp ):
self.previousParent = self.model.getParent()
self.model.wrtReparentTo( mouseNp )

def onRelease( self ):
self.model.wrtReparentTo( self.previousParent )

def onCombine( self, otherObj ):
self.model.setPos( otherObj.model.getPos() )

class mouseCollisionClass:
def __init__(self):
base.accept("escape",sys.exit)
base.accept('mouse1',self.onPress)
base.accept('mouse1-up',self.onRelease)
self.draggedObj = None
self.setupCollision()

def setupCollision( self ):
# Initialise the collision ray that is used to detect which draggable
# object the mouse pointer is over.
cn = CollisionNode('')
self.cnp = aspect2d.attachNewNode(cn)
self.ctrav = CollisionTraverser()
self.queue = CollisionHandlerQueue()
self.cnp.show()

if base.mouseWatcherNode.hasMouse():
mpos = base.mouseWatcherNode.getMouse()
self.cnp.setPos(render2d,mpos[0],0,mpos[1])

def collisionCheck( self ):
self.ctrav.traverse(aspect2d)
self.queue.sortEntries()
if self.queue.getNumEntries():
np = self.queue.getEntry( self.queue.getNumEntries()-1 ).getIntoNodePath()
objectId = np.getTag( 'objectId' )
if objectId is None:
objectId = np.findNetTag( 'objectId' )
if objectId is not None:
object = objectManger.get( objectId )
return object
return None

def onPress( self ):
obj = self.collisionCheck()
if obj is not None:
self.draggedObj = obj
obj.onPress( self.cnp )

def onRelease( self ):
obj = self.collisionCheck()
self.draggedObj.onRelease() # self.cnp )
if obj is not None:
self.draggedObj.onCombine( obj )

if __name__ == '__main__':
cm = CardMaker('cm')
left,right,bottom,top = 0,2,0,-2
width = right - left
height = top - bottom
cm.setFrame(left,right,bottom,top)
node = aspect2d.attachNewNode('')
node.setPos(-1.2,0,0.9)
cards = []

for i in range(3):
for j in range(3):
card = node.attachNewNode(cm.generate())
card.setScale(.2)
card.setPos(i/2.0,0,-j/2.0)
card.setColor(random.random(),random.random(),random.random())
draggable = dragDropObjectClass(card)
cards.append(card)

mouseCollision = mouseCollisionClass()
run()``````

hope it helps

Thanks a lot astelix…I ran the code in the Python Shell…Gave me the following error:

``````Traceback (most recent call last):
File "<pyshell#0>", line 3, in <module>
import direct.directbase.DirectStart
ImportError: No module named direct.directbase.DirectStart``````

Probably a very trivial error but since Im an absolute beginner please bear.

Try changing to the directory that contains the file, then use:

ppython filename.py

the ppython command invokes panda’s python interpreter, which I find works better with Panda code, probably just because it knows where to look for the library files.

edit:
WOO! 100 posts!

[quote=“The Ideator”]
Thanks a lot astelix…I ran the code in the Python Shell…Gave me the following error:
[cut]

wow never faced that error so I guess you got something wrong in your panda engine install
FWIK ppython is not actually used anymore so if you can’t run it with normal python call I doubt ppython will work but try and see however
did you managed to run successfully any official panda3d sample provided with the install package?

@astelix:
Works fine with the ppython command that Tutunkommon suggested. I can run all the tutorials with the install package, but when I run it in the Python 2.6.2 IDLE GUI, it gives me the error. Where do you run your codes?

well I’m on kubuntu linux and use a little editor that have an embedded console - the IDLE I honestly ain’t never used nor had the need to use it. If you’re on windows you may find useful komodo edit but anyhow you need to open a console to test your code out.
I recall time ago a discussion here above this topic - try to search the forum and find it cos I guess could be interesting for you.

Ppython works fine. The code executes, but I need help understanding it. Any suggestions where should I start? I then need to replace the cards with meshes modelled in Blender which need to be dragged and dropped. The meshes have been exported using Chicken. Need help with that too.

No replies. So here are specific queries regarding the code. Referring to the manual, I gleaned that a ray traverser is setup from the mouse pointer to the objects and collisions are checked for. Looking at the code I didnt get the following:

``````self.objectIdCounter = 0
self.objectDict = dict()``````

What purpose do these serve?

``````def tag( self, objectNp, objectClass ):
self.objectIdCounter += 1
objectTag = str(self.objectIdCounter)
objectNp.setTag( 'objectId', objectTag )
self.objectDict[objectTag] = objectClass``````

Didnt get what this function does. What is NP? Its repeated later for MouseNP etc.

`````` def onPress( self, mouseNp ):
self.previousParent = self.model.getParent()
self.model.wrtReparentTo( mouseNp )