From 753b1c623da8cbd36f3f6537d5ca8f25a7330b8b Mon Sep 17 00:00:00 2001 From: rdb Date: Sun, 15 Mar 2015 13:06:49 +0100 Subject: [PATCH] Deprecate DirectStart and run(), allow clean ShowBase teardown, docstrings for ShowBase members --- direct/src/directbase/DirectStart.py | 3 +- direct/src/directnotify/DirectNotifyGlobal.py | 2 + direct/src/showbase/EventManager.py | 5 + direct/src/showbase/ShowBase.py | 197 ++++++++++-------- 4 files changed, 123 insertions(+), 84 deletions(-) diff --git a/direct/src/directbase/DirectStart.py b/direct/src/directbase/DirectStart.py index 61b9c20651..d33c9c5fde 100644 --- a/direct/src/directbase/DirectStart.py +++ b/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() diff --git a/direct/src/directnotify/DirectNotifyGlobal.py b/direct/src/directnotify/DirectNotifyGlobal.py index 7159b066af..353b3d48f7 100644 --- a/direct/src/directnotify/DirectNotifyGlobal.py +++ b/direct/src/directnotify/DirectNotifyGlobal.py @@ -1,5 +1,7 @@ """instantiate global DirectNotify used in Direct""" +__all__ = ['directNotify', 'giveNotify'] + import DirectNotify directNotify = DirectNotify.DirectNotify() diff --git a/direct/src/showbase/EventManager.py b/direct/src/showbase/EventManager.py index c29cb0b05b..367d24c051 100644 --- a/direct/src/showbase/EventManager.py +++ b/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() diff --git a/direct/src/showbase/ShowBase.py b/direct/src/showbase/ShowBase.py index bb6ad5a2ce..03a0d66a86 100644 --- a/direct/src/showbase/ShowBase.py +++ b/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() + +@atexit.register +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()