Browse Source

Fixes to make compatible with new panda

Mark Mine 24 years ago
parent
commit
ce32c8c748

+ 26 - 16
direct/src/cluster/ClusterClient.py

@@ -1,14 +1,10 @@
 """ClusterClient: Master for mutlipiping or PC clusters.  """
 
 from PandaModules import *
-from TaskManagerGlobal import *
-from ShowBaseGlobal import *
-import Task
+from ClusterMsgs import *
 import DirectNotifyGlobal
-import Datagram
 import DirectObject
-from ClusterMsgs import *
-import time
+import Task
 
 class ClusterConfigItem:
     def __init__(self, serverFunction, serverName, port):
@@ -79,10 +75,12 @@ class DisplayConnection:
 
     # the following should only be called by a synchronized cluster manger
     def getSwapReady(self):
-        datagram = self.msgHandler.blockingRead(self.qcr)
-        (type,dgi) = self.msgHandler.readHeader(datagram)
-        if type != CLUSTER_SWAP_READY:
-            self.notify.warning( ('was expecting SWAP_READY, got %d' % type) )
+        while 1:
+            (datagram, dgi,type) = self.msgHandler.blockingRead(self.qcr)
+            if type == CLUSTER_SWAP_READY:
+                break
+            else:
+                self.notify.warning('was expecting SWAP_READY, got %d' % type)
 
     # the following should only be called by a synchronized cluster manger
     def sendSwapNow(self):
@@ -97,6 +95,13 @@ class DisplayConnection:
         datagram = self.msgHandler.makeCommandStringDatagram(commandString)
         self.cw.send(datagram, self.tcpConn)
 
+    def sendExit(self):
+        ClusterManager.notify.debug( 
+            "display connect send exit, packet %d" %
+            self.msgHandler.packetNumber)
+        datagram = self.msgHandler.makeExitDatagram()
+        self.cw.send(datagram, self.tcpConn)
+
 class ClusterManager(DirectObject.DirectObject):
     notify = DirectNotifyGlobal.directNotify.newCategory("ClusterClient")
     MGR_NUM = 1000000
@@ -127,9 +132,7 @@ class ClusterManager(DirectObject.DirectObject):
             server.sendMoveCam(xyz,hpr)
 
     def startMoveCamTask(self):
-        task = Task.Task(self.moveCameraTask,49)
-        taskMgr.add(task, "moveCamTask")
-        return None        
+        taskMgr.add(self.moveCameraTask, "moveCamTask", 49)
 
     def moveCameraTask(self,task):
         self.moveCamera(
@@ -142,7 +145,15 @@ class ClusterManager(DirectObject.DirectObject):
         for server in self.serverList:
             server.sendCommandString(commandString)
         # Execute locally
-        exec( commandString, globals() )
+        exec( commandString, globals())
+
+    def exit(self):
+        # Execute remotely
+        for server in self.serverList:
+            server.sendExit()
+        # Execute locally
+        import sys
+        sys.exit()
 
 class ClusterManagerSync(ClusterManager):
 
@@ -154,8 +165,7 @@ class ClusterManagerSync(ClusterManager):
         self.startSwapCoordinatorTask()
 
     def startSwapCoordinatorTask(self):
-        task = Task.Task(self.swapCoordinator,51)
-        taskMgr.add(task, "clientSwapCoordinator")
+        taskMgr.add(self.swapCoordinator, "clientSwapCoordinator", 51)
         return None
 
     def swapCoordinator(self,task):

+ 3 - 3
direct/src/cluster/ClusterConfig.py

@@ -23,7 +23,7 @@ ClientConfigs = {
                              {'pos' : Vec3(0),
                               'hpr' : Vec3(60,0,0)}
                              ],
-    'cavetest'            : [{'pos' : Vec3(-0.105, -0.020, 5.000),
+    'cavetest-old'            : [{'pos' : Vec3(-0.105, -0.020, 5.000),
                               'hpr' : Vec3(51.213, 0.000, 0.000),
                               'focal length' : 0.809,
                               'film size' : (1.000, 0.831),
@@ -48,7 +48,7 @@ ClientConfigs = {
                               'film offset' : (0.000, 0.173),
                               },
                              ],
-    'cavetest-all'        : [{'pos' : Vec3(-0.105, -0.020, 5.000),
+    'cavetest'        : [{'pos' : Vec3(-0.105, -0.020, 5.000),
                               'hpr' : Vec3(51.213, 0.000, 0.000),
                               'focal length' : 0.809,
                               'film size' : (1.000, 0.831),
@@ -100,7 +100,7 @@ def createClusterManager():
     displayConfigs = []
     configList = ClientConfigs[clusterConfig]
     numConfigs = len(configList)
-    for i in range(numConfigs):
+    for i in range(1,numConfigs):
         configData = configList[i]
         serverConfigName = 'display%d' % i
         serverString = base.config.GetString(serverConfigName, '')

+ 55 - 41
direct/src/cluster/ClusterMsgs.py

@@ -7,70 +7,77 @@
 #    recieved are handled outside of here, after the header (message type
 #    and number) are read here.
 
+from PandaModules import *
+import Datagram
+import time
+
 #these are the types of messages that are currently supported.
-CLUSTER_NOTHING    = -1
+CLUSTER_NONE    = 0
 CLUSTER_CAM_OFFSET = 1
 CLUSTER_CAM_FRUSTUM = 2
-CLUSTER_POS_UPDATE = 3
+CLUSTER_CAM_MOVEMENT = 3
 CLUSTER_SWAP_READY = 4
 CLUSTER_SWAP_NOW   = 5
 CLUSTER_COMMAND_STRING = 6
+CLUSTER_EXIT = 100
 
 #Port number for cluster rendering
 CLUSTER_PORT = 1970
 
-from ShowBaseGlobal import *
-from PandaModules import *
-from TaskManagerGlobal import *
-import Task
-import DirectNotifyGlobal
-import Datagram
-import time
-
 class MsgHandler:
     """MsgHandler: wrapper for PC clusters/multi-piping networking"""
     def __init__(self,packetStart, notify):
-        #packetStart can be used to distinguish which MsgHandler sends a
-        #given packet.
+        # packetStart can be used to distinguish which MsgHandler sends a
+        # given packet.
         self.packetNumber = packetStart
         self.notify = notify
 
     def nonBlockingRead(self,qcr):
-        availGetVal = qcr.dataAvailable()
-        if availGetVal:
+        """
+        Return a datagram iterator and type if data is available on the
+        queued connection reader
+        """
+        if qcr.dataAvailable():
             datagram = NetDatagram()
-            readRetVal = qcr.getData(datagram)
-            if readRetVal:
-                dgi = DatagramIterator(datagram)
-                number = dgi.getUint32()
-                type = dgi.getUint8()
-                self.notify.debug( ("Packet %d type %d recieved" % (number,type)) )    
+            if qcr.getData(datagram):
+                (dgi, type) = self.readHeader(datagram)
             else:
+                dgi = None
+                type = CLUSTER_NONE
                 self.notify.warning("getData returned false")
         else:
-            type = CLUSTER_NOTHING
             dgi = None
-        return (type,dgi)
-
-    def readHeader(self,datagram):
-        dgi = DatagramIterator(datagram)
-        number = dgi.getUint32()
-        type = dgi.getUint8()
-        self.notify.debug( ("Packet %d type %d recieved" % (number,type)) )
-        return (type,dgi)        
+            type = CLUSTER_NONE
+        # Note, return datagram to keep a handle on the data
+        return (datagram, dgi,type)
 
     def blockingRead(self,qcr):
+        """
+        Block until data is available on the queued connection reader.
+        Returns a datagram iterator and type
+        """
         while not qcr.dataAvailable():
             # The following may not be necessary.
             # I just wanted some
             # time given to the operating system while
             # busy waiting.
             time.sleep(0.002)
+        # Data is available, create a datagram iterator
         datagram = NetDatagram()
-        readRetVal = qcr.getData(datagram)
-        if not readRetVal:
+        if qcr.getData(datagram):
+            (dgi, type) = self.readHeader(datagram)
+        else:
+            (dgi, type) = (None, CLUSTER_NONE)
             self.notify.warning("getData returned false")
-        return datagram
+        # Note, return datagram to keep a handle on the data
+        return (datagram, dgi, type)
+
+    def readHeader(self,datagram):
+        dgi = DatagramIterator(datagram)
+        number = dgi.getUint32()
+        type = dgi.getUint8()
+        self.notify.debug("Packet %d type %d recieved" % (number,type))
+        return (dgi,type)        
 
     def makeCamOffsetDatagram(self,xyz,hpr):
         datagram = Datagram.Datagram()
@@ -85,14 +92,6 @@ class MsgHandler:
         datagram.addFloat32(hpr[2])
         return datagram
 
-    def makeCommandStringDatagram(self, commandString):
-        datagram = Datagram.Datagram()
-        datagram.addUint32(self.packetNumber)
-        self.packetNumber = self.packetNumber + 1
-        datagram.addUint8(CLUSTER_COMMAND_STRING)
-        datagram.addString(commandString)
-        return datagram
-
     def makeCamFrustumDatagram(self,focalLength, filmSize, filmOffset):
         datagram = Datagram.Datagram()
         datagram.addUint32(self.packetNumber)
@@ -109,7 +108,7 @@ class MsgHandler:
         datagram = Datagram.Datagram()
         datagram.addUint32(self.packetNumber)
         self.packetNumber = self.packetNumber + 1
-        datagram.addUint8(CLUSTER_POS_UPDATE)
+        datagram.addUint8(CLUSTER_CAM_MOVEMENT)
         datagram.addFloat32(xyz[0])
         datagram.addFloat32(xyz[1])
         datagram.addFloat32(xyz[2])
@@ -118,6 +117,14 @@ class MsgHandler:
         datagram.addFloat32(hpr[2])
         return datagram
 
+    def makeCommandStringDatagram(self, commandString):
+        datagram = Datagram.Datagram()
+        datagram.addUint32(self.packetNumber)
+        self.packetNumber = self.packetNumber + 1
+        datagram.addUint8(CLUSTER_COMMAND_STRING)
+        datagram.addString(commandString)
+        return datagram
+
     def makeSwapNowDatagram(self):
         datagram = Datagram.Datagram()
         datagram.addUint32(self.packetNumber)
@@ -131,6 +138,13 @@ class MsgHandler:
         self.packetNumber = self.packetNumber + 1
         datagram.addUint8(CLUSTER_SWAP_READY)
         return datagram
+
+    def makeExitDatagram(self):
+        datagram = Datagram.Datagram()
+        datagram.addUint32(self.packetNumber)
+        self.packetNumber = self.packetNumber + 1
+        datagram.addUint8(CLUSTER_EXIT)
+        return datagram
         
 
 

+ 34 - 41
direct/src/cluster/ClusterServer.py

@@ -1,18 +1,11 @@
 """ServerRepository module: contains the ServerRepository class"""
 
-from ShowBaseGlobal import *
-from ClusterMsgs import *
-import DirectObject
-import Datagram
-#import DatagramIterator
-#import NetDatagram
-import __builtin__
-import time
 from PandaModules import *
-from TaskManagerGlobal import *
+from ClusterMsgs import *
 from MsgTypes import *
-import Task
 import DirectNotifyGlobal
+import DirectObject
+import Task
 
 # Cam offset handling is a little sloppy.  The problem is that there is a
 # single arc used for both movement of the camera, and the offset of the
@@ -76,32 +69,32 @@ class ClusterServer(DirectObject.DirectObject):
         # Run this task just after the listener poll task and dataloop
         taskMgr.add(self.readerPollTask, "serverReaderPollTask", -39)
 
-    def readerPollTask(self):
-        while self.qcr.dataAvailable():
-            datagram = NetDatagram()
-            readRetVal = self.qcr.getData(datagram)
-            if readRetVal:
-                self.handleDatagram(datagram)
+    def readerPollTask(self, state):
+        # Process all available datagrams
+        while 1:
+            (datagram, dgi,type) = self.msgHandler.nonBlockingRead(self.qcr)
+            if type is CLUSTER_NONE:
+                break
             else:
-                self.notify.warning("getData returned false")
+                handleDatagram(dgi, type)
         return Task.cont
 
-    def handleDatagram(self, datagram):
-        (type, dgi) = self.msgHandler.nonBlockingRead(self.qcr)
-        if type==CLUSTER_CAM_OFFSET:
+    def handleDatagram(self, dgi, type):
+        if type == CLUSTER_NONE:
+            pass
+        elif type == CLUSTER_EXIT:
+            import sys
+            sys.exit()
+        elif type == CLUSTER_CAM_OFFSET:
             self.handleCamOffset(dgi)
-        elif type==CLUSTER_CAM_FRUSTUM:
+        elif type == CLUSTER_CAM_FRUSTUM:
             self.handleCamFrustum(dgi)
-        elif type==CLUSTER_POS_UPDATE:
+        elif type == CLUSTER_CAM_MOVEMENT:
             self.handleCamMovement(dgi)
-        elif type==CLUSTER_SWAP_READY:
-            pass
-        elif type==CLUSTER_SWAP_NOW:
-            pass
-        elif type==CLUSTER_COMMAND_STRING:
+        elif type == CLUSTER_COMMAND_STRING:
             self.handleCommandString(dgi)
         else:
-            self.notify.warning("recieved unknown packet")
+            self.notify.warning("Recieved unknown packet type:" % type)
         return type
     
     def handleCamOffset(self,dgi):
@@ -147,7 +140,7 @@ class ClusterServer(DirectObject.DirectObject):
 
     def handleCommandString(self, dgi):
         command = dgi.getString()
-        exec( command, globals() )
+        exec( command, globals())
         
 class ClusterServerSync(ClusterServer):
 
@@ -163,11 +156,10 @@ class ClusterServerSync(ClusterServer):
             pass
         elif self.qcr.isConnectionOk(self.lastConnection):
             # Process datagrams till you get a postion update
-            type = CLUSTER_NOTHING
-            while type != CLUSTER_POS_UPDATE:
-                datagram = self.msgHandler.blockingRead(self.qcr)
-                (type,dgi) = self.msgHandler.readHeader(datagram)
-                if type == CLUSTER_POS_UPDATE:
+            type = CLUSTER_NONE
+            while type != CLUSTER_CAM_MOVEMENT:
+                (datagram,dgi,type) = self.msgHandler.blockingRead(self.qcr)
+                if type == CLUSTER_CAM_MOVEMENT:
                     # Move camera
                     self.handleCamMovement(dgi)
                     # Set flag for swap coordinator
@@ -194,13 +186,14 @@ class ClusterServerSync(ClusterServer):
         if self.posRecieved:
             self.posRecieved = 0
             self.sendSwapReady()
-            datagram = self.msgHandler.blockingRead(self.qcr)
-            (type,dgi) = self.msgHandler.readHeader(datagram)
-            if type == CLUSTER_SWAP_NOW:
-                self.notify.debug('swapping')
-                base.win.swap()
-            else:
-                self.notify.warning("did not get expected swap now")
+            while 1:
+                (datagram,dgi,type) = self.msgHandler.blockingRead(self.qcr)
+                if type == CLUSTER_SWAP_NOW:
+                    self.notify.debug('swapping')
+                    base.win.swap()
+                    break
+                else:
+                    self.handleDatagram(dgi,type)
         return Task.cont
 
 

+ 14 - 0
direct/src/directtools/DirectCameraControl.py

@@ -260,11 +260,23 @@ class DirectCameraControl(PandaObject):
         angle = getCrankAngle(state.coaCenter)
         deltaAngle = angle - state.lastAngle
         state.lastAngle = angle
+        if deltaAngle != 0.0:
+            print deltaAngle
+        print 'cam Manip before'
+        print self.camManipRef.getMat()
         if base.config.GetBool('temp-hpr-fix',0):
             self.camManipRef.setHpr(self.camManipRef, 0, 0, deltaAngle)
         else:
             self.camManipRef.setHpr(self.camManipRef, 0, 0, -deltaAngle)
+        print 'cam Manip after'
+        print self.camManipRef.getMat()
+        print 'direct camera before'
+        print direct.camera.getMat()
         direct.camera.setMat(self.camManipRef, wrtMat)
+        print 'direct camera after'
+        print direct.camera.getMat()
+        print
+        print
         return Task.cont
 
     def lockCOA(self):
@@ -352,6 +364,8 @@ class DirectCameraControl(PandaObject):
             # ref = base.cam
             ref = direct.drList.getCurrentDr().cam
         self.coaMarker.setPos(ref, self.coa)
+        pos = self.coaMarker.getPos()
+        self.coaMarker.setPosHprScale(pos, Vec3(0), Vec3(1))
         # Resize it
         self.updateCoaMarkerSize(coaDist)
         # Record marker pos in render space

+ 2 - 2
direct/src/directtools/DirectManipulation.py

@@ -98,7 +98,7 @@ class DirectManipulationControl(PandaObject):
                 self.hitPt.assign(hitPt)
                 self.hitPtDist = hitPtDist
                 # Find the node path from the node found above
-                nodePath = render.findPathDownTo(node)
+                nodePath = render.findAllPathsTo(node)[0]
                 # Select it
                 direct.select(nodePath, direct.fShift)
             else:
@@ -223,7 +223,7 @@ class DirectManipulationControl(PandaObject):
                 self.objectHandles.transferObjectHandlesScale()
                 self.fScaling = 0
             # Alt key switches to a scaling mode
-            if direct.fAlt:
+            if direct.fControl:
                 self.fScaling = 1
                 self.scale3D(state)
             # Otherwise, manip mode depends on where you started

+ 16 - 35
direct/src/directtools/DirectSelection.py

@@ -22,7 +22,7 @@ class DirectNodePath(NodePath):
         # and its center of action (COA)
         self.mCoa2Dnp = Mat4()
         self.mCoa2Dnp.assign(Mat4.identMat())
-        # self.mCoa2Dnp.setRow(3, Vec4(center[0], center[1], center[2], 1))
+        self.mCoa2Dnp.setRow(3, Vec4(center[0], center[1], center[2], 1))
         # Transform from nodePath to widget
         self.mDnp2Widget = Mat4()
         self.mDnp2Widget.assign(Mat4.identMat())
@@ -127,18 +127,7 @@ class SelectedNodePaths(PandaObject):
         """
         dnp = self.selectedDict.get(id, None)
         if dnp:
-            # Found item in selected Dictionary, is it still valid?
-            if dnp.verifyConnectivity():
-                # Yes
-                return dnp
-            else:
-                # Not valid anymore, try to repair
-                if dnp.repairConnectivity(render):
-                    # Fixed, return node path
-                    return dnp
-                else:
-                    del(self.selectedDict[id])
-                    return None
+            return dnp
         else:
             # Not in selected dictionary
             return None
@@ -152,18 +141,8 @@ class SelectedNodePaths(PandaObject):
         """
         dnp = self.deselectedDict.get(id, None)
         if dnp:
-            # Found item in deselected Dictionary, is it still valid?
-            if dnp.verifyConnectivity():
-                # Yes
-                return dnp
-            else:
-                # Not valid anymore, try to repair
-                if dnp.repairConnectivity(render):
-                    # Fixed, return node path
-                    return dnp
-                else:
-                    del(self.deselectedDict[id])
-                    return None
+            # Yes
+            return dnp
         else:
             # Not in deselected dictionary
             return None
@@ -344,10 +323,11 @@ class DirectBoundingBox:
 
     def getBounds(self):
         # Get a node path's bounds
-        nodeBounds = self.nodePath.node().getBound()
+        nodeBounds = BoundingSphere()
+        nodeBounds.extendBy(self.nodePath.node().getInternalBound())
         for child in self.nodePath.getChildrenAsList():
-            nodeBounds.extendBy(child.arc().getBound())
-            return nodeBounds.makeCopy()
+            nodeBounds.extendBy(child.getBounds())
+        return nodeBounds.makeCopy()
 
     def show(self):
         self.lines.reparentTo(self.nodePath)
@@ -428,11 +408,11 @@ class SelectionRay:
         for i in range(0,self.numEntries):
             entry = self.cq.getEntry(i)
             node = entry.getIntoNode()
-            nodePath = render.findPathDownTo(node)
+            nodePath = render.findAllPathsTo(node)[0]
             # Don't pick hidden nodes
-            if node.isHidden():
-                pass
-            elif fIgnoreCamera and (direct.camera in nodePath.getAncestry()):
+            #if node.isHidden():
+            #pass
+            if fIgnoreCamera and (direct.camera in nodePath.getAncestry()):
                 # This avoids things parented to a camera.  Good idea?
                 pass
             # Can pick unpickable, use the first visible node
@@ -520,10 +500,11 @@ class SelectionRay:
             entry = self.cq.getEntry(i)
             node = entry.getIntoNode()
             # Don't pick hidden nodes
-            if node.isHidden():
-                pass
+            # MRM: Doesn't work in new panda for GeomNodes
+            #if node.isHidden():
+            #pass
             # Can pick unpickable, use the first visible node
-            elif fIntersectUnpickable:
+            if fIntersectUnpickable:
                 self.cqIndex = i
                 break
             # Is it a named node?, If so, see if it has a name

+ 11 - 16
direct/src/showbase/qpShowBase.py

@@ -96,12 +96,7 @@ class ShowBase:
         self.camList = []
         self.camNode = None
         self.camLens = None
-
-        # base.camera is a little different; rather than referring to
-        # base.cameraList[0], it is instead the parent node of all
-        # cameras in base.cameraList.  That way, multiple cameras can
-        # easily be dragged around by moving the one node.
-        self.camera = self.render.attachNewNode('camera')
+        self.camera = None
         self.cameraList = []
         self.groupList = []
         self.camera2d = self.render2d.attachNewNode('camera2d')
@@ -383,8 +378,8 @@ class ShowBase:
         
 
     def getCameras(self, chanConfig):
-        """getCameras(self, chanConfig)
-
+        """
+        getCameras(self, chanConfig)
         Extracts the camera(s) out of the ChanConfig record, parents
         them all to base.camera, and adds them to base.cameraList.
         """
@@ -393,7 +388,7 @@ class ShowBase:
         # be more than one display region/camera node beneath each
         # one.
         for i in range(chanConfig.getNumGroups()):
-            camera = self.camera.attachNewNode(chanConfig.getGroupNode(i))
+            camera = self.render.attachNewNode(chanConfig.getGroupNode(i))
             cam = camera.find('**/+Camera')
             lens = cam.node().getLens()
 
@@ -409,19 +404,18 @@ class ShowBase:
         for i in range(chanConfig.getNumDrs()):
             self.groupList.append(chanConfig.getGroupMembership(i))
 
-        # Set the default camera
+        # Set the default camera and cam
+        if self.camera == None:
+            self.camera = self.cameraList[0]
         if self.cam == None:
             self.cam = self.camList[0]
-
             # If you need to get a handle to the camera node itself,
             # use self.camNode.
             self.camNode = self.cam.node()
-
             # If you need to adjust camera parameters, like fov or
             # near/far clipping planes, use self.camLens.
             self.camLens = self.camNode.getLens()
 
-
     def getAlt(self):
         return base.mouseWatcherNode.getModifierButtons().isDown(
             KeyboardButton.alt())
@@ -576,12 +570,13 @@ class ShowBase:
             self.backfaceCullingOn()
 
     def backfaceCullingOn(self):
-        self.render.setTwoSided(self.wireframeEnabled)
+        if not self.backfaceCullingEnabled:
+            self.render.setTwoSided(0)
         self.backfaceCullingEnabled = 1
 
     def backfaceCullingOff(self):
-        if not self.wireframeEnabled:
-            self.render.setTwoSided(0)
+        if self.backfaceCullingEnabled:
+            self.render.setTwoSided(1)
         self.backfaceCullingEnabled = 0
 
     def toggleTexture(self):

+ 21 - 0
direct/src/task/Task.py

@@ -5,6 +5,7 @@ from MessengerGlobal import *
 import time
 import fnmatch
 import string
+import signal
 
 # MRM: Need to make internal task variables like time, name, index
 # more unique (less likely to have name clashes)
@@ -230,6 +231,8 @@ class TaskManager:
             TaskManager.notify = directNotify.newCategory("TaskManager")
         self.taskTimerVerbose = 0
         self.extendedExceptions = 0
+        self.fKeyboardInterrupt = 0
+        self.interruptCount = 0
         self.pStatsTasks = 0
         self.resumeFunc = None
         self.fVerbose = 0
@@ -241,6 +244,14 @@ class TaskManager:
         self.fVerbose = value
         messenger.send('TaskManager-setVerbose', sentArgs = [value])
 
+    def keyboardInterruptHandler(self, signalNumber, stackFrame):
+        self.fKeyboardInterrupt = 1
+        self.interruptCount += 1
+        if self.interruptCount == 2:
+            # The user must really want to interrupt this process
+            # Next time around use the default interrupt handler
+            signal.signal(signal.SIGINT, signal.default_int_handler)
+
     def add(self, funcOrTask, name, priority = 0):
         """
         Add a new task to the taskMgr.
@@ -370,6 +381,12 @@ class TaskManager:
         if TaskManager.notify.getDebug():
             TaskManager.notify.debug('step')
         self.currentTime, self.currentFrame = self.__getTimeFrame()
+        # Replace keyboard interrupt handler during task list processing
+        # so we catch the keyboard interrupt but don't handle it until
+        # after task list processing is complete.
+        self.fKeyboardInterrupt = 0
+        self.interruptCount = 0
+        signal.signal(signal.SIGINT, self.keyboardInterruptHandler)
         for task in self.taskList:
             task.setCurrentTimeFrame(self.currentTime, self.currentFrame)
 
@@ -410,6 +427,10 @@ class TaskManager:
                 self.__removeTask(task)
             else:
                 raise StandardError, "Task named %s did not return cont, exit, or done" % task.name
+        # Restore default interrupt handler
+        signal.signal(signal.SIGINT, signal.default_int_handler)
+        if self.fKeyboardInterrupt:
+            raise KeyboardInterrupt
         return len(self.taskList)
 
     def run(self):

+ 1 - 1
direct/src/tkpanels/DirectSessionPanel.py

@@ -695,7 +695,7 @@ class DirectSessionPanel(AppShell):
             dictName = name
         else:
             # Generate a unique name for the dict
-            dictName = name + '-' + `nodePath.id().this`
+            dictName = name + '-' + `nodePath.id()`
         if not dict.has_key(dictName):
             # Update combo box to include new item
             names.append(dictName)

+ 5 - 5
direct/src/tkpanels/MopathRecorder.py

@@ -292,8 +292,7 @@ class MopathRecorder(AppShell, PandaObject):
             resolution = 0.01, command = self.playbackGoTo, side = TOP)
         widget.component('hull')['relief'] = RIDGE
         # Kill playback task if drag slider
-        widget.component('scale').bind(
-            '<ButtonPress-1>', lambda e = None, s = self: s.stopPlayback())
+        widget['preCallback'] = self.stopPlayback
         # Jam duration entry into entry scale
         self.createLabeledEntry(widget.labelFrame, 'Resample', 'Path Duration',
                                 'Set total curve duration',
@@ -371,7 +370,7 @@ class MopathRecorder(AppShell, PandaObject):
             'Number of samples in resampled curve',
             resolution = 1, min = 2, max = 1000, command = self.setNumSamples)
         widget.component('hull')['relief'] = RIDGE
-        widget['preCallback'] = widget['postCallback'] = self.sampleCurve
+        widget['postCallback'] = self.sampleCurve
 
         frame = Frame(resampleFrame)
         self.createButton(
@@ -785,7 +784,9 @@ class MopathRecorder(AppShell, PandaObject):
     def extractPointSetFromCurveCollection(self):
         # Use curve to compute new point set
         # Record maxT
+        print 'before', self.maxT
         self.maxT = self.curveCollection.getMaxT()
+        print 'after', self.maxT
         # Determine num samples
         # Limit point set to 1000 points and samples per second to 30
         samplesPerSegment = min(30.0, 1000.0/self.curveCollection.getMaxT())
@@ -1232,7 +1233,7 @@ class MopathRecorder(AppShell, PandaObject):
             dictName = name
         else:
             # Generate a unique name for the dict
-            dictName = name + '-' + `nodePath.id().this`
+            dictName = name + '-' + `nodePath.id()`
         if not dict.has_key(dictName):
             # Update combo box to include new item
             names.append(dictName)
@@ -1248,7 +1249,6 @@ class MopathRecorder(AppShell, PandaObject):
     def playbackGoTo(self, time):
         if self.curveCollection == None:
             return
-        print time
         self.playbackTime = CLAMP(time, 0.0, self.maxT)
         if self.curveCollection != None:
             pos = Point3(0)

+ 1 - 1
direct/src/tkpanels/Placer.py

@@ -493,7 +493,7 @@ class Placer(AppShell):
             dictName = name
         else:
             # Generate a unique name for the dict
-            dictName = name + '-' + `nodePath.id().this`
+            dictName = name + '-' + `nodePath.id()`
         if not dict.has_key(dictName):
             # Update combo box to include new item
             names.append(dictName)

+ 7 - 0
direct/src/tkwidgets/Slider.py

@@ -374,6 +374,8 @@ class SliderWidget(Pmw.MegaWidget):
         if fInside:
             self._fPressInside = 1
             self._fUpdate = 1
+            if self['preCallback']:
+                apply(self['preCallback'], self['callbackData'])
             self._updateValue(event)
         else:
             self._fPressInside = 0
@@ -385,11 +387,16 @@ class SliderWidget(Pmw.MegaWidget):
                 event.y_root - self._widget.winfo_rooty())
             if canvasY > 0:
                 self._fUpdate = 1
+                if self['preCallback']:
+                    apply(self['preCallback'], self['callbackData'])
                 self._unpostOnNextRelease()
         elif self._fUpdate:
             self._updateValue(event)
 
     def _widgetBtnRelease(self, event):
+        # Do post callback if any
+        if self._fUpdate and self['postCallback']:
+            apply(self['postCallback'], self['callbackData'])
         if (self._fUnpost or
             (not (self._firstPress or self._fPressInside))):
             self._unpostSlider()