Drag Selecting multiple nodes

I solved the off-window issue so we gotta a final release I guess

''' 
by: mavasher
revisers: crossedgun, ynjh_jo, pro_rsoft, astelix
'''
from pandac.PandaModules import *
from direct.directbase.DirectStart import *
from direct.showbase.DirectObject import DirectObject
from direct.task import Task
import random

class World(DirectObject):

  def __init__(self):
    for i in range(10):
      ball = loader.loadModel("smiley")
      ball.reparentTo(render)
      ball.setTag('click', '1')
      ball.setPos(random.randint(-4,4),random.randint(-4,4),random.randint(-4,4))
      cs = CollisionSphere(0, 0, 0, 1)
      cnodePath = ball.attachNewNode(CollisionNode('cnode'))
      cnodePath.node().addSolid(cs)
      cnodePath.node().setIntoCollideMask(BitMask32.allOff())
    self.accept("mouse1", self.click)
  #-----------------------------------------
  def click(self):
    if not base.mouseWatcherNode.hasMouse():
      return
    self.click_start = Point2(base.mouseWatcherNode.getMouseX(),base.mouseWatcherNode.getMouseY())
    cmfg = CardMaker('fg')
    cmfg.setFrame(0, 1, -1, 0)
    self.bar = render2d.attachNewNode(cmfg.generate())
    self.bar.setPos(self.click_start[0],1,self.click_start[1])
    self.bar.setColor(0,1,0,.5)
    self.bar.setTransparency(1)
    taskMgr.add(w.update_rect, "update_rect")
    self.acceptOnce("mouse1-up", self.release_click)
  #-----------------------------------------
  def update_rect(self, task):
    if not base.mouseWatcherNode.hasMouse():  #check for mouse first, in case the mouse is outside the Panda window
      return Task.cont
    sx = base.mouseWatcherNode.getMouseX() - self.click_start[0]
    sy = self.click_start[1] - base.mouseWatcherNode.getMouseY()
    self.bar.setScale(sx if sx else .0001, 1, sy if sy else .0001)
    return Task.cont
  #-----------------------------------------
  def release_click(self):
    taskMgr.remove("update_rect")
    
    click_start=Point2(self.bar.getTightBounds()[0][0], self.bar.getTightBounds()[0][2])
    click_end=Point2(self.bar.getTightBounds()[1][0], self.bar.getTightBounds()[1][2])
    self.bar.removeNode()
    
    if click_end == click_start:  #Fudge the numbers a bit to avoid the degenerate case of no rectangle
      click_end = Point2(click_end[0]+.00001, click_end[1]+.00001)
      #I would have used the simpler PickerRay method, but the collision masks on the objects are reversed
    pt = Point3()
    junk_pt = Point3()
    base.camLens.extrude(click_start, junk_pt, pt)  #calculate a point in 3d space from the mouse position
    click_start3 = pt  #Use the point farthest away from the camera for the best precision
    click_end3 = Point3()
    base.camLens.extrude(click_end, junk_pt, click_end3)
    left = CollisionPlane(Plane(camera.getPos(),Point3(click_start3[0],1000,click_end3[2]), click_start3 )) #Create the 4 sides with planes
    right=CollisionPlane(Plane(camera.getPos(),Point3(click_end3[0],1000,click_start3[2]),click_end3 )) #They should all 'face' inward: i.e.
    bot = CollisionPlane(Plane(camera.getPos(), click_end3, Point3(click_start3[0],1000,click_end3[2])))  #Collisions are inside
    top = CollisionPlane(Plane(camera.getPos(), click_start3, Point3(click_end3[0],1000,click_start3[2])))
    pyramid = render.attachNewNode(CollisionNode('pyramid'))
    pyramid.node().addSolid(left)
    pyramid.node().addSolid(top)
    pyramid.node().addSolid(right)
    pyramid.node().addSolid(bot)
    click_traverser = CollisionTraverser('click_enabler')
    click_handler = CollisionHandlerQueue()
    for ci in render.findAllMatches("=click").asList():
      click_traverser.addCollider(ci.find("+CollisionNode"), click_handler)
    click_traverser.traverse(render)
    hits = []
    for i in range(click_handler.getNumEntries()):
      hits.append(click_handler.getEntry(i).getFromNodePath().getParent())  #this actually includes all clickable items since the planes cover everything.

    # If the object is within the rectangle, it collides with all 4 planes
    # Use set to remove 3 duplicates of each selected object
    sel=set(filter(lambda i: hits.count(i) == 4, hits))
    print len(sel),'selected'

    all=render.findAllMatches("**/smiley*")
    for i in range(all.getNumPaths()): all[i].clearColor()

    for i in sel:
      i.setColor(random.random(),random.random(),random.random())
    click_traverser.clearColliders()
    click_handler.clearEntries()
    pyramid.removeNode()

camera.setPos(0, -25, 5)
base.disableMouse()
w = World()
run()