Browse Source

Deprecate DirectStart and run(), allow clean ShowBase teardown, docstrings for ShowBase members

rdb 10 years ago
parent
commit
753b1c623d

+ 2 - 1
direct/src/directbase/DirectStart.py

@@ -1,4 +1,5 @@
-print 'DirectStart: Starting the game.'
+__all__ = []
+print('Using deprecated DirectStart interface.')
 
 from direct.showbase import ShowBase
 base = ShowBase.ShowBase()

+ 2 - 0
direct/src/directnotify/DirectNotifyGlobal.py

@@ -1,5 +1,7 @@
 """instantiate global DirectNotify used in Direct"""
 
+__all__ = ['directNotify', 'giveNotify']
+
 import DirectNotify
 
 directNotify = DirectNotify.DirectNotify()

+ 5 - 0
direct/src/showbase/EventManager.py

@@ -187,3 +187,8 @@ class EventManager:
 
     def shutdown(self):
         taskMgr.remove('eventManager')
+
+        # Flush the event queue.  We do this after removing the task
+        # since the task removal itself might also fire off an event.
+        if self.eventQueue is not None:
+            self.eventQueue.clear()

+ 114 - 83
direct/src/showbase/ShowBase.py

@@ -14,12 +14,12 @@ from panda3d.direct import get_config_showbase, throw_new_frame, init_app_for_gu
 import __builtin__
 __builtin__.config = get_config_showbase()
 
-from direct.directnotify.DirectNotifyGlobal import *
-from MessengerGlobal import *
-from BulletinBoardGlobal import *
-from direct.task.TaskManagerGlobal import *
-from JobManagerGlobal import *
-from EventManagerGlobal import *
+from direct.directnotify.DirectNotifyGlobal import directNotify, giveNotify
+from MessengerGlobal import messenger
+from BulletinBoardGlobal import bulletinBoard
+from direct.task.TaskManagerGlobal import taskMgr
+from JobManagerGlobal import jobMgr
+from EventManagerGlobal import eventMgr
 from PythonUtil import *
 from direct.showbase import PythonUtil
 #from direct.interval.IntervalManager import ivalMgr
@@ -29,11 +29,11 @@ from direct.showbase.BufferViewer import BufferViewer
 from direct.task import Task
 from direct.directutil import Verify
 from direct.showbase import GarbageReport
-import EventManager
 import math,sys,os
 import Loader
 import time
 import gc
+import atexit
 from direct.fsm import ClassicFSM
 from direct.fsm import State
 from direct.showbase import ExceptionVarDump
@@ -47,6 +47,14 @@ import AppRunnerGlobal
 __builtin__.FADE_SORT_INDEX = 1000
 __builtin__.NO_FADE_SORT_INDEX = 2000
 
+def legacyRun():
+    __builtin__.base.notify.warning("run() is deprecated, use base.run() instead")
+    __builtin__.base.run()
+
[email protected]
+def exitfunc():
+    if getattr(__builtin__, 'base', None) is not None:
+        __builtin__.base.destroy()
 
 # Now ShowBase is a DirectObject.  We need this so ShowBase can hang
 # hooks on messages, particularly on window-event.  This doesn't
@@ -65,11 +73,12 @@ class ShowBase(DirectObject.DirectObject):
         if logStackDump or uploadStackDump:
             ExceptionVarDump.install(logStackDump, uploadStackDump)
 
-        # Locate the directory containing the main program
+        ## The directory containing the main Python file of this application.
         self.mainDir = ExecutionEnvironment.getEnvironmentVariable("MAIN_DIR")
 
-        # The appRunner should have been created by the time ShowBase
-        # has been.
+        ## This contains the global appRunner instance, as imported from
+        ## AppRunnerGlobal.  This will be None if we are not running in the
+        ## runtime environment (ie. from a .p3d file).
         self.appRunner = AppRunnerGlobal.appRunner
 
         #debug running multiplier
@@ -108,13 +117,13 @@ class ShowBase(DirectObject.DirectObject):
         self.wantTk = False
         self.wantWx = False
 
-        # Fill this in with a function to invoke when the user "exits"
-        # the program by closing the main window.
+        ## Fill this in with a function to invoke when the user "exits"
+        ## the program by closing the main window.
         self.exitFunc = None
 
-        # Add final-exit callbacks to this list.  These will be called
-        # when sys.exit() is called, after Panda has unloaded, and
-        # just before Python is about to shut down.
+        ## Add final-exit callbacks to this list.  These will be called
+        ## when sys.exit() is called, after Panda has unloaded, and
+        ## just before Python is about to shut down.
         self.finalExitCallbacks = []
 
         Task.TaskManager.taskTimerVerbose = self.config.GetBool('task-timer-verbose', 0)
@@ -139,13 +148,15 @@ class ShowBase(DirectObject.DirectObject):
         # we get a window-event.
         self.__oldAspectRatio = None
 
+        ## This is set to the value of the window-type config variable, but may
+        ## optionally be overridden in the Showbase constructor.  Should either be
+        ## 'onscreen' (the default), 'offscreen' or 'none'.
         self.windowType = windowType
         if self.windowType is None:
             self.windowType = self.config.GetString('window-type', 'onscreen')
         self.requireWindow = self.config.GetBool('require-window', 1)
 
-        # base.win is the main, or only window; base.winList is a list of
-        # *all* windows.  Similarly with base.camList.
+        ## This is the main, or only window; see winList for a list of *all* windows.
         self.win = None
         self.frameRateMeter = None
         self.sceneGraphAnalyzerMeter = None
@@ -165,17 +176,29 @@ class ShowBase(DirectObject.DirectObject):
         self.trackball = None
         self.texmem = None
         self.showVertices = None
+
+        ## This is a NodePath pointing to the Camera object set up for the 3D scene.
+        ## This is usually a child of self.camera.
         self.cam = None
         self.cam2d = None
         self.cam2dp = None
+
+        ## This is the NodePath that should be used to manipulate the camera.  This
+        ## is the node to which the default camera is attached.
         self.camera = None
         self.camera2d = None
         self.camera2dp = None
+
+        ## This is a list of all cameras created with makeCamera, including base.cam.
         self.camList = []
+        ## Convenience accessor for base.cam.node()
         self.camNode = None
+        ## Convenience accessor for base.camNode.get_lens()
         self.camLens = None
         self.camFrustumVis = None
         self.direct = None
+        ## This is used to store the wx.Application object used when want-wx is
+        ## set or base.startWx() is called.
         self.wxApp = None
         self.tkRoot = None
 
@@ -189,6 +212,7 @@ class ShowBase(DirectObject.DirectObject):
 
         self.hidden = NodePath('hidden')
 
+        ## The global graphics engine, ie. GraphicsEngine.getGlobalPtr()
         self.graphicsEngine = GraphicsEngine.getGlobalPtr()
         self.setupRender()
         self.setupRender2d()
@@ -198,11 +222,11 @@ class ShowBase(DirectObject.DirectObject):
             self.setupRender2dp()
 
 
-        # This is a placeholder for a CollisionTraverser.  If someone
-        # stores a CollisionTraverser pointer here, we'll traverse it
-        # in the collisionLoop task.
-        self.shadowTrav = 0
+        ## This is a placeholder for a CollisionTraverser.  If someone
+        ## stores a CollisionTraverser pointer here, we'll traverse it
+        ## in the collisionLoop task.
         self.cTrav = 0
+        self.shadowTrav = 0
         self.cTravStack = Stack()
         # Ditto for an AppTraverser.
         self.appTrav = 0
@@ -233,10 +257,6 @@ class ShowBase(DirectObject.DirectObject):
             random.seed(seed)
             #whrandom.seed(seed & 0xff, (seed >> 8) & 0xff, (seed >> 16) & 0xff)
 
-        # Now that we've set up the window structures, assign an exitfunc.
-        self.oldexitfunc = getattr(sys, 'exitfunc', None)
-        sys.exitfunc = self.exitfunc
-
         # Open the default rendering window.
         if self.windowType != 'none':
             props = WindowProperties.getDefault()
@@ -254,17 +274,22 @@ class ShowBase(DirectObject.DirectObject):
         self.loader = Loader.Loader(self)
         self.graphicsEngine.setDefaultLoader(self.loader.loader)
 
+        ## The global event manager, as imported from EventManagerGlobal.
         self.eventMgr = eventMgr
+        ## The global messenger, as imported from MessengerGlobal.
         self.messenger = messenger
+        ## The global bulletin board, as imported from BulletinBoardGlobal.
         self.bboard = bulletinBoard
+        ## The global task manager, as imported from TaskManagerGlobal.
         self.taskMgr = taskMgr
+        ## The global job manager, as imported from JobManagerGlobal.
         self.jobMgr = jobMgr
 
-        # Particle manager
+        ## Particle manager
         self.particleMgr = None
         self.particleMgrEnabled = 0
 
-        # Physics manager
+        ## Physics manager
         self.physicsMgr = None
         self.physicsMgrEnabled = 0
         self.physicsMgrAngular = 0
@@ -316,6 +341,8 @@ class ShowBase(DirectObject.DirectObject):
         if 'base' in __builtin__.__dict__:
             raise StandardError, "Attempt to spawn multiple ShowBase instances!"
 
+        # DO NOT ADD TO THIS LIST.  We're trying to phase out the use of
+        # built-in variables by ShowBase.  Use a Global module if necessary.
         __builtin__.base = self
         __builtin__.render2d = self.render2d
         __builtin__.aspect2d = self.aspect2d
@@ -331,7 +358,7 @@ class ShowBase(DirectObject.DirectObject):
         __builtin__.bboard = self.bboard
         # Config needs to be defined before ShowBase is constructed
         #__builtin__.config = self.config
-        __builtin__.run = self.run
+        __builtin__.run = legacyRun
         __builtin__.ostream = Notify.out()
         __builtin__.directNotify = directNotify
         __builtin__.giveNotify = giveNotify
@@ -426,7 +453,7 @@ class ShowBase(DirectObject.DirectObject):
         self.cTrav = self.cTravStack.pop()
 
     def __setupProfile(self):
-        """ Sets up the Python profiler, if avaialable, according to
+        """ Sets up the Python profiler, if available, according to
         some Panda config settings. """
 
         try:
@@ -445,8 +472,7 @@ class ShowBase(DirectObject.DirectObject):
         return 0
 
     def printEnvDebugInfo(self):
-        """
-        Print some information about the environment that we are running
+        """Print some information about the environment that we are running
         in.  Stuff like the model paths and other paths.  Feel free to
         add stuff to this.
         """
@@ -470,11 +496,18 @@ class ShowBase(DirectObject.DirectObject):
         for cb in self.finalExitCallbacks[:]:
             cb()
 
+        # Remove the built-in base reference
+        if getattr(__builtin__, 'base', None) is self:
+            del __builtin__.base
+            del __builtin__.loader
+            del __builtin__.taskMgr
+
         # [gjeon] restore sticky key settings
         if self.config.GetBool('disable-sticky-keys', 0):
             allowAccessibilityShortcutKeys(True)
 
-        taskMgr.destroy()
+        self.ignoreAll()
+        self.shutdown()
 
         if getattr(self, 'musicManager', None):
             self.musicManager.shutdown()
@@ -501,19 +534,6 @@ class ShowBase(DirectObject.DirectObject):
         vfs = VirtualFileSystem.getGlobalPtr()
         vfs.unmountAll()
 
-
-    def exitfunc(self):
-        """
-        This should be assigned to sys.exitfunc to be called just
-        before Python shutdown.  It guarantees that the Panda window
-        is closed cleanly, so that we free system resources, restore
-        the desktop and keyboard functionality, etc.
-        """
-        self.destroy()
-
-        if self.oldexitfunc:
-            self.oldexitfunc()
-
     def makeDefaultPipe(self, printPipeTypes = True):
         """
         Creates the default GraphicsPipe, which will be used to make
@@ -1022,6 +1042,7 @@ class ShowBase(DirectObject.DirectObject):
         Creates the render scene graph, the primary scene graph for
         rendering 3-d geometry.
         """
+        ## This is the root of the 3-D scene graph.
         self.render = NodePath('render')
         self.render.setAttrib(RescaleNormalAttrib.makeDefault())
 
@@ -1037,6 +1058,7 @@ class ShowBase(DirectObject.DirectObject):
         2-d objects and gui elements that are superimposed over the
         3-d geometry in the window.
         """
+        ## This is the root of the 2-D scene graph.
         self.render2d = NodePath('render2d')
 
         # Set up some overrides to turn off certain properties which
@@ -1057,22 +1079,26 @@ class ShowBase(DirectObject.DirectObject):
         self.render2d.setMaterialOff(1)
         self.render2d.setTwoSided(1)
 
-        # The normal 2-d DisplayRegion has an aspect ratio that
-        # matches the window, but its coordinate system is square.
-        # This means anything we parent to render2d gets stretched.
-        # For things where that makes a difference, we set up
-        # aspect2d, which scales things back to the right aspect
-        # ratio.
-        aspectRatio = self.getAspectRatio()
+        ## The normal 2-d DisplayRegion has an aspect ratio that
+        ## matches the window, but its coordinate system is square.
+        ## This means anything we parent to render2d gets stretched.
+        ## For things where that makes a difference, we set up
+        ## aspect2d, which scales things back to the right aspect
+        ## ratio along the X axis (Z is still from -1 to 1)
         self.aspect2d = self.render2d.attachNewNode(PGTop("aspect2d"))
+
+        aspectRatio = self.getAspectRatio()
         self.aspect2d.setScale(1.0 / aspectRatio, 1.0, 1.0)
 
         self.a2dBackground = self.aspect2d.attachNewNode("a2dBackground")
 
-        # It's important to know the bounds of the aspect2d screen.
+        ## The Z position of the top border of the aspect2d screen.
         self.a2dTop = 1.0
+        ## The Z position of the bottom border of the aspect2d screen.
         self.a2dBottom = -1.0
+        ## The X position of the left border of the aspect2d screen.
         self.a2dLeft = -aspectRatio
+        ## The X position of the right border of the aspect2d screen.
         self.a2dRight = aspectRatio
 
         self.a2dTopCenter = self.aspect2d.attachNewNode("a2dTopCenter")
@@ -1112,12 +1138,12 @@ class ShowBase(DirectObject.DirectObject):
         self.a2dBottomRight.setPos(self.a2dRight, 0, self.a2dBottom)
         self.a2dBottomRightNs.setPos(self.a2dRight, 0, self.a2dBottom)
 
-        # This special root, pixel2d, uses units in pixels that are relative
-        # to the window. The upperleft corner of the window is (0, 0),
-        # the lowerleft corner is (xsize, -ysize), in this coordinate system.
-        xsize, ysize = self.getSize()
+        ## This special root, pixel2d, uses units in pixels that are relative
+        ## to the window. The upperleft corner of the window is (0, 0),
+        ## the lowerleft corner is (xsize, -ysize), in this coordinate system.
         self.pixel2d = self.render2d.attachNewNode(PGTop("pixel2d"))
         self.pixel2d.setPos(-1, 0, 1)
+        xsize, ysize = self.getSize()
         if xsize > 0 and ysize > 0:
             self.pixel2d.setScale(2.0 / xsize, 1.0, 2.0 / ysize)
 
@@ -1144,25 +1170,27 @@ class ShowBase(DirectObject.DirectObject):
         self.render2dp.setMaterialOff(1)
         self.render2dp.setTwoSided(1)
 
-        # The normal 2-d DisplayRegion has an aspect ratio that
-        # matches the window, but its coordinate system is square.
-        # This means anything we parent to render2d gets stretched.
-        # For things where that makes a difference, we set up
-        # aspect2d, which scales things back to the right aspect
-        # ratio.
-
-        aspectRatio = self.getAspectRatio()
+        ## The normal 2-d DisplayRegion has an aspect ratio that
+        ## matches the window, but its coordinate system is square.
+        ## This means anything we parent to render2dp gets stretched.
+        ## For things where that makes a difference, we set up
+        ## aspect2dp, which scales things back to the right aspect
+        ## ratio along the X axis (Z is still from -1 to 1)
         self.aspect2dp = self.render2dp.attachNewNode(PGTop("aspect2dp"))
         self.aspect2dp.node().setStartSort(16384)
+
+        aspectRatio = self.getAspectRatio()
         self.aspect2dp.setScale(1.0 / aspectRatio, 1.0, 1.0)
 
-        # It's important to know the bounds of the aspect2d screen.
+        ## The Z position of the top border of the aspect2dp screen.
         self.a2dpTop = 1.0
+        ## The Z position of the bottom border of the aspect2dp screen.
         self.a2dpBottom = -1.0
+        ## The X position of the left border of the aspect2dp screen.
         self.a2dpLeft = -aspectRatio
+        ## The X position of the right border of the aspect2dp screen.
         self.a2dpRight = aspectRatio
 
-
         self.a2dpTopCenter = self.aspect2dp.attachNewNode("a2dpTopCenter")
         self.a2dpBottomCenter = self.aspect2dp.attachNewNode("a2dpBottomCenter")
         self.a2dpLeftCenter = self.aspect2dp.attachNewNode("a2dpLeftCenter")
@@ -1184,13 +1212,13 @@ class ShowBase(DirectObject.DirectObject):
         self.a2dpBottomLeft.setPos(self.a2dpLeft, 0, self.a2dpBottom)
         self.a2dpBottomRight.setPos(self.a2dpRight, 0, self.a2dpBottom)
 
-        # This special root, pixel2d, uses units in pixels that are relative
-        # to the window. The upperleft corner of the window is (0, 0),
-        # the lowerleft corner is (xsize, -ysize), in this coordinate system.
-        xsize, ysize = self.getSize()
+        ## This special root, pixel2d, uses units in pixels that are relative
+        ## to the window. The upperleft corner of the window is (0, 0),
+        ## the lowerleft corner is (xsize, -ysize), in this coordinate system.
         self.pixel2dp = self.render2dp.attachNewNode(PGTop("pixel2dp"))
         self.pixel2dp.node().setStartSort(16384)
         self.pixel2dp.setPos(-1, 0, 1)
+        xsize, ysize = self.getSize()
         if xsize > 0 and ysize > 0:
             self.pixel2dp.setScale(2.0 / xsize, 1.0, 2.0 / ysize)
 
@@ -1502,12 +1530,14 @@ class ShowBase(DirectObject.DirectObject):
             np = mw.getParent().attachNewNode(mouseRecorder)
             mw.reparentTo(np)
 
-        # A special ButtonThrower to generate keyboard events and
-        # include the time from the OS.  This is separate only to
-        # support legacy code that did not expect a time parameter; it
-        # will eventually be folded into the normal ButtonThrower,
-        # above.
+
         mw = self.buttonThrowers[0].getParent()
+
+        ## A special ButtonThrower to generate keyboard events and
+        ## include the time from the OS.  This is separate only to
+        ## support legacy code that did not expect a time parameter; it
+        ## will eventually be folded into the normal ButtonThrower,
+        ## above.
         self.timeButtonThrower = mw.attachNewNode(ButtonThrower('timeButtons'))
         self.timeButtonThrower.node().setPrefix('time-')
         self.timeButtonThrower.node().setTimeFlag(1)
@@ -1931,7 +1961,7 @@ class ShowBase(DirectObject.DirectObject):
         throw_new_frame()
         return Task.cont
 
-    def restart(self,clusterSync=False,cluster=None):
+    def restart(self, clusterSync=False, cluster=None):
         self.shutdown()
         # __resetPrevTransform goes at the very beginning of the frame.
         self.taskMgr.add(
@@ -1970,7 +2000,7 @@ class ShowBase(DirectObject.DirectObject):
         self.taskMgr.remove('dataLoop')
         self.taskMgr.remove('resetPrevTransform')
         self.taskMgr.remove('ivalLoop')
-        self.taskMgr.remove('garbage_collect')
+        self.taskMgr.remove('garbageCollectStates')
         self.eventMgr.shutdown()
 
     def getBackgroundColor(self, win = None):
@@ -2931,11 +2961,12 @@ class ShowBase(DirectObject.DirectObject):
         self.startDirect(fWantDirect = fDirect, fWantTk = fTk, fWantWx = fWx)
 
     def run(self):
-        # This method runs the TaskManager when self.appRunner is
-        # None, which is to say, when we are not running from within a
-        # p3d file.  When we *are* within a p3d file, the Panda
-        # runtime has to be responsible for running the main loop, so
-        # we can't allow the application to do it.
+        """ This method runs the TaskManager when self.appRunner is
+        None, which is to say, when we are not running from within a
+        p3d file.  When we *are* within a p3d file, the Panda
+        runtime has to be responsible for running the main loop, so
+        we can't allow the application to do it. """
+
         if self.appRunner is None or self.appRunner.dummy or \
            (self.appRunner.interactiveConsole and not self.appRunner.initialAppImport):
             self.taskMgr.run()