ShowBase: significant improvements to ShowBase API documentation

Fixes #198
This commit is contained in:
rdb 2020-07-25 12:48:54 +02:00
parent 9d91274739
commit 43b5345588

View File

@ -85,6 +85,7 @@ def exitfunc():
# *seem* to cause anyone any problems. # *seem* to cause anyone any problems.
class ShowBase(DirectObject.DirectObject): class ShowBase(DirectObject.DirectObject):
#: The deprecated `.DConfig` interface for accessing config variables.
config = DConfig config = DConfig
notify = directNotify.newCategory("ShowBase") notify = directNotify.newCategory("ShowBase")
@ -101,6 +102,8 @@ class ShowBase(DirectObject.DirectObject):
including this instance itself (under the name ``base``). including this instance itself (under the name ``base``).
""" """
#: Set if the want-dev Config.prc variable is enabled. By default, it
#: is set to True except when using Python with the -O flag.
self.__dev__ = self.config.GetBool('want-dev', __debug__) self.__dev__ = self.config.GetBool('want-dev', __debug__)
builtins.__dev__ = self.__dev__ builtins.__dev__ = self.__dev__
@ -113,13 +116,13 @@ class ShowBase(DirectObject.DirectObject):
if __debug__: if __debug__:
self.__autoGarbageLogging = self.__dev__ and self.config.GetBool('auto-garbage-logging', False) self.__autoGarbageLogging = self.__dev__ and self.config.GetBool('auto-garbage-logging', False)
## The directory containing the main Python file of this application. #: The directory containing the main Python file of this application.
self.mainDir = ExecutionEnvironment.getEnvironmentVariable("MAIN_DIR") self.mainDir = ExecutionEnvironment.getEnvironmentVariable("MAIN_DIR")
self.main_dir = self.mainDir self.main_dir = self.mainDir
## This contains the global appRunner instance, as imported from #: This contains the global appRunner instance, as imported from
## AppRunnerGlobal. This will be None if we are not running in the #: `.AppRunnerGlobal`. This will be None if we are not running in the
## runtime environment (ie. from a .p3d file). #: runtime environment (ie. from a .p3d file). Deprecated.
self.appRunner = AppRunnerGlobal.appRunner self.appRunner = AppRunnerGlobal.appRunner
self.app_runner = self.appRunner self.app_runner = self.appRunner
@ -154,13 +157,13 @@ class ShowBase(DirectObject.DirectObject):
self.wantTk = False self.wantTk = False
self.wantWx = False self.wantWx = False
## Fill this in with a function to invoke when the user "exits" #: Fill this in with a function to invoke when the user "exits"
## the program by closing the main window. #: the program by closing the main window.
self.exitFunc = None self.exitFunc = None
## Add final-exit callbacks to this list. These will be called #: Add final-exit callbacks to this list. These will be called
## when sys.exit() is called, after Panda has unloaded, and #: when sys.exit() is called, after Panda has unloaded, and
## just before Python is about to shut down. #: just before Python is about to shut down.
self.finalExitCallbacks = [] self.finalExitCallbacks = []
# Set up the TaskManager to reset the PStats clock back # Set up the TaskManager to reset the PStats clock back
@ -181,27 +184,34 @@ class ShowBase(DirectObject.DirectObject):
# we get a window-event. # we get a window-event.
self.__oldAspectRatio = None self.__oldAspectRatio = None
## This is set to the value of the window-type config variable, but may #: This is set to the value of the window-type config variable, but may
## optionally be overridden in the Showbase constructor. Should either be #: optionally be overridden in the Showbase constructor. Should either
## 'onscreen' (the default), 'offscreen' or 'none'. #: be 'onscreen' (the default), 'offscreen' or 'none'.
self.windowType = windowType self.windowType = windowType
if self.windowType is None: if self.windowType is None:
self.windowType = self.config.GetString('window-type', 'onscreen') self.windowType = self.config.GetString('window-type', 'onscreen')
self.requireWindow = self.config.GetBool('require-window', 1) self.requireWindow = self.config.GetBool('require-window', 1)
## This is the main, or only window; see winList for a list of *all* windows. #: This is the main, or only window; see `winList` for a list of *all* windows.
self.win = None self.win = None
self.frameRateMeter = None self.frameRateMeter = None
self.sceneGraphAnalyzerMeter = None self.sceneGraphAnalyzerMeter = None
#: A list of all windows opened via `openWindow()`.
self.winList = [] self.winList = []
self.winControls = [] self.winControls = []
self.mainWinMinimized = 0 self.mainWinMinimized = 0
self.mainWinForeground = 0 self.mainWinForeground = 0
#: Contains the :class:`~panda3d.core.GraphicsPipe` object created by
#: `makeDefaultPipe()`.
self.pipe = None self.pipe = None
#: The full list of :class:`~panda3d.core.GraphicsPipe` objects,
#: including any auxiliary pipes. Filled by `makeAllPipes()`.
self.pipeList = [] self.pipeList = []
self.mouse2cam = None self.mouse2cam = None
self.buttonThrowers = None self.buttonThrowers = None
self.mouseWatcher = None self.mouseWatcher = None
#: The :class:`~panda3d.core.MouseWatcher` object, created by
#: `setupMouse()`.
self.mouseWatcherNode = None self.mouseWatcherNode = None
self.pointerWatcherNodes = None self.pointerWatcherNodes = None
self.mouseInterface = None self.mouseInterface = None
@ -211,28 +221,37 @@ class ShowBase(DirectObject.DirectObject):
self.showVertices = None self.showVertices = None
self.deviceButtonThrowers = [] self.deviceButtonThrowers = []
## This is a NodePath pointing to the Camera object set up for the 3D scene. #: This is a :class:`~panda3d.core.NodePath` pointing to the
## This is usually a child of self.camera. #: :class:`~panda3d.core.Camera` object set up for the 3D scene.
#: Usually a child of `camera`.
self.cam = None self.cam = None
#: Same as `cam`, but for the 2D scene graph.
self.cam2d = None self.cam2d = None
#: Same as `cam2d`, but for the 2D overlay scene graph.
self.cam2dp = None self.cam2dp = None
## This is the NodePath that should be used to manipulate the camera. This #: This is the :class:`~panda3d.core.NodePath` that should be used to
## is the node to which the default camera is attached. #: manipulate the camera. It points at the node to which the default
#: camera (`cam`, `camNode`) is attached.
self.camera = None self.camera = None
#: Same as `camera`, but for the 2D scene graph. Parent of `cam2d`.
self.camera2d = None self.camera2d = None
#: Same as `camera2d`, but for the 2D overlay scene graph. Parent of
#: `cam2dp`.
self.camera2dp = None self.camera2dp = None
## This is a list of all cameras created with makeCamera, including base.cam. #: A list of all cameras created with `makeCamera()`, including `cam`.
self.camList = [] self.camList = []
## Convenience accessor for base.cam.node() #: Convenience accessor for base.cam.node(), containing a
#: :class:`~panda3d.core.Camera` object.
self.camNode = None self.camNode = None
## Convenience accessor for base.camNode.get_lens() #: Convenience accessor for base.camNode.get_lens(), containing a
#: :class:`~panda3d.core.Lens` object.
self.camLens = None self.camLens = None
self.camFrustumVis = None self.camFrustumVis = None
self.direct = None self.direct = None
## This is used to store the wx.Application object used when want-wx is #: This is used to store the wx.Application object used when want-wx is
## set or base.startWx() is called. #: set or `startWx()` is called.
self.wxApp = None self.wxApp = None
self.wxAppCreated = False self.wxAppCreated = False
self.tkRoot = None self.tkRoot = None
@ -251,7 +270,8 @@ class ShowBase(DirectObject.DirectObject):
from . import ShowBaseGlobal from . import ShowBaseGlobal
self.hidden = ShowBaseGlobal.hidden self.hidden = ShowBaseGlobal.hidden
## The global graphics engine, ie. GraphicsEngine.getGlobalPtr() #: The global :class:`~panda3d.core.GraphicsEngine`, as returned by
#: GraphicsEngine.getGlobalPtr()
self.graphicsEngine = GraphicsEngine.getGlobalPtr() self.graphicsEngine = GraphicsEngine.getGlobalPtr()
self.graphics_engine = self.graphicsEngine self.graphics_engine = self.graphicsEngine
self.setupRender() self.setupRender()
@ -261,10 +281,11 @@ class ShowBase(DirectObject.DirectObject):
if self.wantRender2dp: if self.wantRender2dp:
self.setupRender2dp() self.setupRender2dp()
#: A placeholder for a :class:`~panda3d.core.CollisionTraverser`. If
## This is a placeholder for a CollisionTraverser. If someone #: someone stores a CollisionTraverser pointer here, ShowBase will
## stores a CollisionTraverser pointer here, we'll traverse it #: traverse it automatically in the collisionLoop task, so you won't
## in the collisionLoop task. #: need to call :meth:`~panda3d.core.CollisionTraverser.traverse()`
#: yourself every frame.
self.cTrav = 0 self.cTrav = 0
self.shadowTrav = 0 self.shadowTrav = 0
self.cTravStack = Stack() self.cTravStack = Stack()
@ -322,33 +343,35 @@ class ShowBase(DirectObject.DirectObject):
self.mouseInterface = self.trackball self.mouseInterface = self.trackball
self.useTrackball() self.useTrackball()
#: `.Loader.Loader` object.
self.loader = Loader.Loader(self) self.loader = Loader.Loader(self)
self.graphicsEngine.setDefaultLoader(self.loader.loader) self.graphicsEngine.setDefaultLoader(self.loader.loader)
## The global event manager, as imported from EventManagerGlobal. #: The global event manager, as imported from `.EventManagerGlobal`.
self.eventMgr = eventMgr self.eventMgr = eventMgr
## The global messenger, as imported from MessengerGlobal. #: The global messenger, as imported from `.MessengerGlobal`.
self.messenger = messenger self.messenger = messenger
## The global bulletin board, as imported from BulletinBoardGlobal. #: The global bulletin board, as imported from `.BulletinBoardGlobal`.
self.bboard = bulletinBoard self.bboard = bulletinBoard
## The global task manager, as imported from TaskManagerGlobal. #: The global task manager, as imported from `.TaskManagerGlobal`.
self.taskMgr = taskMgr self.taskMgr = taskMgr
self.task_mgr = taskMgr self.task_mgr = taskMgr
## The global job manager, as imported from JobManagerGlobal. #: The global job manager, as imported from `.JobManagerGlobal`.
self.jobMgr = jobMgr self.jobMgr = jobMgr
#: If `enableParticles()` has been called, this is the particle manager
## Particle manager #: as imported from :mod:`direct.particles.ParticleManagerGlobal`.
self.particleMgr = None self.particleMgr = None
self.particleMgrEnabled = 0 self.particleMgrEnabled = 0
## Physics manager #: If `enableParticles()` has been called, this is the physics manager
#: as imported from :mod:`direct.showbase.PhysicsManagerGlobal`.
self.physicsMgr = None self.physicsMgr = None
self.physicsMgrEnabled = 0 self.physicsMgrEnabled = 0
self.physicsMgrAngular = 0 self.physicsMgrAngular = 0
## This is the global input device manager, which keeps track of #: This is the global :class:`~panda3d.core.InputDeviceManager`, which
## connected input devices. #: keeps track of connected input devices.
self.devices = InputDeviceManager.getGlobalPtr() self.devices = InputDeviceManager.getGlobalPtr()
self.__inputDeviceNodes = {} self.__inputDeviceNodes = {}
@ -466,6 +489,8 @@ class ShowBase(DirectObject.DirectObject):
# Transition effects (fade, iris, etc) # Transition effects (fade, iris, etc)
from . import Transitions from . import Transitions
#: `.Transitions.Transitions` object.
self.transitions = Transitions.Transitions(self.loader) self.transitions = Transitions.Transitions(self.loader)
if self.win: if self.win:
@ -486,12 +511,8 @@ class ShowBase(DirectObject.DirectObject):
else: else:
self.multiClientSleep = 0 self.multiClientSleep = 0
# Offscreen buffer viewing utility. #: Utility for viewing offscreen buffers, see :mod:`.BufferViewer`.
# This needs to be allocated even if the viewer is off. self.bufferViewer = BufferViewer(self.win, self.render2dp if self.wantRender2dp else self.render2d)
if self.wantRender2dp:
self.bufferViewer = BufferViewer(self.win, self.render2dp)
else:
self.bufferViewer = BufferViewer(self.win, self.render2d)
if self.windowType != 'none': if self.windowType != 'none':
if fStartDirect: # [gjeon] if this is False let them start direct manually if fStartDirect: # [gjeon] if this is False let them start direct manually
@ -635,6 +656,8 @@ class ShowBase(DirectObject.DirectObject):
Returns a GraphicsPipe from the indicated module, Returns a GraphicsPipe from the indicated module,
e.g. 'pandagl' or 'pandadx9'. Does not affect base.pipe or e.g. 'pandagl' or 'pandadx9'. Does not affect base.pipe or
base.pipeList. base.pipeList.
:rtype: panda3d.core.GraphicsPipe
""" """
selection = GraphicsPipeSelection.getGlobalPtr() selection = GraphicsPipeSelection.getGlobalPtr()
@ -643,7 +666,7 @@ class ShowBase(DirectObject.DirectObject):
def makeAllPipes(self): def makeAllPipes(self):
""" """
Creates all GraphicsPipes that the system knows about and fill up Creates all GraphicsPipes that the system knows about and fill up
self.pipeList with them. `pipeList` with them.
""" """
selection = GraphicsPipeSelection.getGlobalPtr() selection = GraphicsPipeSelection.getGlobalPtr()
selection.loadAuxModules() selection.loadAuxModules()
@ -685,27 +708,34 @@ class ShowBase(DirectObject.DirectObject):
Creates a window and adds it to the list of windows that are Creates a window and adds it to the list of windows that are
to be updated every frame. to be updated every frame.
props is the WindowProperties that describes the window. :param props: the :class:`~panda3d.core.WindowProperties` that
describes the window.
type is either 'onscreen', 'offscreen', or 'none'. :param fbprops: the :class:`~panda3d.core.FrameBufferProperties`
indicating the requested framebuffer properties.
If keepCamera is true, the existing base.cam is set up to :param type: Either 'onscreen', 'offscreen', or 'none'.
render into the new window.
If keepCamera is false but makeCamera is true, a new camera is :param keepCamera: If True, the existing base.cam is set up to
set up to render into the new window. render into the new window.
If unexposedDraw is not None, it specifies the initial value :param makeCamera: If True (and keepCamera is False), a new camera is
of GraphicsWindow.setUnexposedDraw(). set up to render into the new window.
If callbackWindowDict is not None, a CallbackGraphicWindow is :param unexposedDraw: If not None, it specifies the initial value
created instead, which allows the caller to create the actual of :meth:`~panda3d.core.GraphicsWindow.setUnexposedDraw()`.
window with its own OpenGL context, and direct Panda's
rendering into that window.
If requireWindow is true, it means that the function should :param callbackWindowDict: If not None, a
raise an exception if the window fails to open correctly. :class:`~panda3d.core.CallbackGraphicWindow`
is created instead, which allows the caller
to create the actual window with its own
OpenGL context, and direct Panda's rendering
into that window.
:param requireWindow: If True, the function should raise an exception
if the window fails to open correctly.
:rtype: panda3d.core.GraphicsWindow
""" """
# Save this lambda here for convenience; we'll use it to call # Save this lambda here for convenience; we'll use it to call
@ -968,16 +998,19 @@ class ShowBase(DirectObject.DirectObject):
self.graphicsEngine.renderFrame() self.graphicsEngine.renderFrame()
def openDefaultWindow(self, *args, **kw): def openDefaultWindow(self, *args, **kw):
# Creates the main window for the first time, without being """
# too particular about the kind of graphics API that is Creates the main window for the first time, without being too
# chosen. The suggested window type from the load-display particular about the kind of graphics API that is chosen.
# config variable is tried first; if that fails, the first The suggested window type from the load-display config variable is
# window type that can be successfully opened at all is tried first; if that fails, the first window type that can be
# accepted. Returns true on success, false otherwise. successfully opened at all is accepted.
#
# This is intended to be called only once, at application This is intended to be called only once, at application startup.
# startup. It is normally called automatically unless It is normally called automatically unless window-type is configured
# window-type is configured to 'none'. to 'none'.
:returns: True on success, False on failure.
"""
startDirect = kw.get('startDirect', True) startDirect = kw.get('startDirect', True)
if 'startDirect' in kw: if 'startDirect' in kw:
@ -998,9 +1031,8 @@ class ShowBase(DirectObject.DirectObject):
previous main window and open a new one, preserving the lens previous main window and open a new one, preserving the lens
properties in base.camLens. properties in base.camLens.
The return value is true on success, or false on failure (in :returns: True on success, or False on failure (in which case base.win
which case base.win may be either None, or the previous, may be either None, or the previous, closed window).
closed window).
""" """
keepCamera = kw.get('keepCamera', False) keepCamera = kw.get('keepCamera', False)
@ -1130,7 +1162,6 @@ class ShowBase(DirectObject.DirectObject):
self.textureEnabled = 1 self.textureEnabled = 1
self.wireframeEnabled = 0 self.wireframeEnabled = 0
def setupRender2d(self): def setupRender2d(self):
""" """
Creates the render2d scene graph, the primary scene graph for Creates the render2d scene graph, the primary scene graph for
@ -1344,9 +1375,10 @@ class ShowBase(DirectObject.DirectObject):
return aspectRatio return aspectRatio
def getSize(self, win = None): def getSize(self, win = None):
# Returns the actual size of the indicated (or main """
# window), or the default size if there is not yet a Returns the actual size of the indicated (or main window), or the
# main window. default size if there is not yet a main window.
"""
if win == None: if win == None:
win = self.win win = self.win
@ -1380,6 +1412,8 @@ class ShowBase(DirectObject.DirectObject):
If useCamera is not None, it is a NodePath to be used as the If useCamera is not None, it is a NodePath to be used as the
camera to apply to the window, rather than creating a new camera to apply to the window, rather than creating a new
camera. camera.
:rtype: panda3d.core.NodePath
""" """
# self.camera is the parent node of all cameras: a node that # self.camera is the parent node of all cameras: a node that
# we can move around to move all cameras as a group. # we can move around to move all cameras as a group.
@ -1462,6 +1496,8 @@ class ShowBase(DirectObject.DirectObject):
""" """
Makes a new camera2d associated with the indicated window, and Makes a new camera2d associated with the indicated window, and
assigns it to render the indicated subrectangle of render2d. assigns it to render the indicated subrectangle of render2d.
:rtype: panda3d.core.NodePath
""" """
dr = win.makeMonoDisplayRegion(*displayRegion) dr = win.makeMonoDisplayRegion(*displayRegion)
dr.setSort(sort) dr.setSort(sort)
@ -1507,6 +1543,8 @@ class ShowBase(DirectObject.DirectObject):
""" """
Makes a new camera2dp associated with the indicated window, and Makes a new camera2dp associated with the indicated window, and
assigns it to render the indicated subrectangle of render2dp. assigns it to render the indicated subrectangle of render2dp.
:rtype: panda3d.core.NodePath
""" """
dr = win.makeMonoDisplayRegion(*displayRegion) dr = win.makeMonoDisplayRegion(*displayRegion)
dr.setSort(sort) dr.setSort(sort)
@ -1569,14 +1607,13 @@ class ShowBase(DirectObject.DirectObject):
using the indicated window. If the mouse has already been set using the indicated window. If the mouse has already been set
up for a different window, those structures are deleted first. up for a different window, those structures are deleted first.
The return value is the ButtonThrower NodePath created for :param fMultiWin: If True, then the previous mouse structures are not
this window. deleted; instead, multiple windows are allowed to
monitor the mouse input. However, in this case, the
trackball controls are not set up, and must be set up
by hand if desired.
If fMultiWin is true, then the previous mouse structures are :returns: The ButtonThrower NodePath created for this window.
not deleted; instead, multiple windows are allowed to monitor
the mouse input. However, in this case, the trackball
controls are not set up, and must be set up by hand if
desired.
""" """
if not fMultiWin and self.buttonThrowers != None: if not fMultiWin and self.buttonThrowers != None:
for bt in self.buttonThrowers: for bt in self.buttonThrowers:
@ -1707,18 +1744,30 @@ class ShowBase(DirectObject.DirectObject):
self.mouseWatcherNode.setGeometry(mouseViz.node()) self.mouseWatcherNode.setGeometry(mouseViz.node())
def getAlt(self): def getAlt(self):
"""
Returns True if the alt key is currently held down.
"""
return self.mouseWatcherNode.getModifierButtons().isDown( return self.mouseWatcherNode.getModifierButtons().isDown(
KeyboardButton.alt()) KeyboardButton.alt())
def getShift(self): def getShift(self):
"""
Returns True if the shift key is currently held down.
"""
return self.mouseWatcherNode.getModifierButtons().isDown( return self.mouseWatcherNode.getModifierButtons().isDown(
KeyboardButton.shift()) KeyboardButton.shift())
def getControl(self): def getControl(self):
"""
Returns True if the control key is currently held down.
"""
return self.mouseWatcherNode.getModifierButtons().isDown( return self.mouseWatcherNode.getModifierButtons().isDown(
KeyboardButton.control()) KeyboardButton.control())
def getMeta(self): def getMeta(self):
"""
Returns True if the meta key is currently held down.
"""
return self.mouseWatcherNode.getModifierButtons().isDown( return self.mouseWatcherNode.getModifierButtons().isDown(
KeyboardButton.meta()) KeyboardButton.meta())
@ -1784,6 +1833,11 @@ class ShowBase(DirectObject.DirectObject):
del self.__inputDeviceNodes[device] del self.__inputDeviceNodes[device]
def addAngularIntegrator(self): def addAngularIntegrator(self):
"""
Adds a :class:`~panda3d.physics.AngularEulerIntegrator` to the default
physics manager. By default, only a
:class:`~panda3d.physics.LinearEulerIntegrator` is attached.
"""
if not self.physicsMgrAngular: if not self.physicsMgrAngular:
physics = importlib.import_module('panda3d.physics') physics = importlib.import_module('panda3d.physics')
self.physicsMgrAngular = 1 self.physicsMgrAngular = 1
@ -1791,6 +1845,15 @@ class ShowBase(DirectObject.DirectObject):
self.physicsMgr.attachAngularIntegrator(integrator) self.physicsMgr.attachAngularIntegrator(integrator)
def enableParticles(self): def enableParticles(self):
"""
Enables the particle and physics managers, which are stored in
`particleMgr` and `physicsMgr` members, respectively. Also starts a
task to periodically update these managers.
By default, only a :class:`~panda3d.physics.LinearEulerIntegrator` is
attached to the physics manager. To attach an angular integrator,
follow this up with a call to `addAngularIntegrator()`.
"""
if not self.particleMgrEnabled: if not self.particleMgrEnabled:
# Use importlib to prevent this import from being picked up # Use importlib to prevent this import from being picked up
# by modulefinder when packaging an application. # by modulefinder when packaging an application.
@ -1813,21 +1876,34 @@ class ShowBase(DirectObject.DirectObject):
self.taskMgr.add(self.updateManagers, 'manager-update') self.taskMgr.add(self.updateManagers, 'manager-update')
def disableParticles(self): def disableParticles(self):
"""
The opposite of `enableParticles()`.
"""
if self.particleMgrEnabled: if self.particleMgrEnabled:
self.particleMgrEnabled = 0 self.particleMgrEnabled = 0
self.physicsMgrEnabled = 0 self.physicsMgrEnabled = 0
self.taskMgr.remove('manager-update') self.taskMgr.remove('manager-update')
def toggleParticles(self): def toggleParticles(self):
"""
Calls `enableParticles()` or `disableParticles()` depending on the
current state.
"""
if self.particleMgrEnabled == 0: if self.particleMgrEnabled == 0:
self.enableParticles() self.enableParticles()
else: else:
self.disableParticles() self.disableParticles()
def isParticleMgrEnabled(self): def isParticleMgrEnabled(self):
"""
Returns True if `enableParticles()` has been called.
"""
return self.particleMgrEnabled return self.particleMgrEnabled
def isPhysicsMgrEnabled(self): def isPhysicsMgrEnabled(self):
"""
Returns True if `enableParticles()` has been called.
"""
return self.physicsMgrEnabled return self.physicsMgrEnabled
def updateManagers(self, state): def updateManagers(self, state):
@ -1839,6 +1915,11 @@ class ShowBase(DirectObject.DirectObject):
return Task.cont return Task.cont
def createStats(self, hostname=None, port=None): def createStats(self, hostname=None, port=None):
"""
If want-pstats is set in Config.prc, or the `wantStats` member is
otherwise set to True, connects to the PStats server.
This is normally called automatically from the ShowBase constructor.
"""
# You can specify pstats-host in your Config.prc or use ~pstats/~aipstats # You can specify pstats-host in your Config.prc or use ~pstats/~aipstats
# The default is localhost # The default is localhost
if not self.wantStats: if not self.wantStats:
@ -1854,8 +1935,11 @@ class ShowBase(DirectObject.DirectObject):
PStatClient.connect(hostname, port) PStatClient.connect(hostname, port)
return PStatClient.isConnected() return PStatClient.isConnected()
def addSfxManager(self, extraSfxManager): def addSfxManager(self, extraSfxManager):
"""
Adds an additional SFX audio manager to `sfxManagerList`, the list of
managers managed by ShowBase.
"""
# keep a list of sfx manager objects to apply settings to, # keep a list of sfx manager objects to apply settings to,
# since there may be others in addition to the one we create here # since there may be others in addition to the one we create here
self.sfxManagerList.append(extraSfxManager) self.sfxManagerList.append(extraSfxManager)
@ -1865,6 +1949,10 @@ class ShowBase(DirectObject.DirectObject):
extraSfxManager.setActive(self.sfxActive) extraSfxManager.setActive(self.sfxActive)
def createBaseAudioManagers(self): def createBaseAudioManagers(self):
"""
Creates the default SFX and music manager. Called automatically from
the ShowBase constructor.
"""
self.sfxPlayer = SfxPlayer.SfxPlayer() self.sfxPlayer = SfxPlayer.SfxPlayer()
sfxManager = AudioManager.createAudioManager() sfxManager = AudioManager.createAudioManager()
self.addSfxManager(sfxManager) self.addSfxManager(sfxManager)
@ -1881,6 +1969,9 @@ class ShowBase(DirectObject.DirectObject):
# to a user request so sfxActive/musicActive represent how things # to a user request so sfxActive/musicActive represent how things
# *should* be, regardless of App/OS/HW state # *should* be, regardless of App/OS/HW state
def enableMusic(self, bEnableMusic): def enableMusic(self, bEnableMusic):
"""
Enables or disables the music manager.
"""
# don't setActive(1) if no audiofocus # don't setActive(1) if no audiofocus
if self.AppHasAudioFocus and self.musicManagerIsValid: if self.AppHasAudioFocus and self.musicManagerIsValid:
self.musicManager.setActive(bEnableMusic) self.musicManager.setActive(bEnableMusic)
@ -1900,6 +1991,9 @@ class ShowBase(DirectObject.DirectObject):
self.sfxManagerList[i].setActive(bEnabled) self.sfxManagerList[i].setActive(bEnabled)
def enableSoundEffects(self, bEnableSoundEffects): def enableSoundEffects(self, bEnableSoundEffects):
"""
Enables or disables SFX managers.
"""
# don't setActive(1) if no audiofocus # don't setActive(1) if no audiofocus
if self.AppHasAudioFocus or (bEnableSoundEffects==0): if self.AppHasAudioFocus or (bEnableSoundEffects==0):
self.SetAllSfxEnables(bEnableSoundEffects) self.SetAllSfxEnables(bEnableSoundEffects)
@ -1916,6 +2010,10 @@ class ShowBase(DirectObject.DirectObject):
# come back ok when the app is switched back to # come back ok when the app is switched back to
def disableAllAudio(self): def disableAllAudio(self):
"""
Disables all SFX and music managers, meant to be called when the app
loses audio focus.
"""
self.AppHasAudioFocus = 0 self.AppHasAudioFocus = 0
self.SetAllSfxEnables(0) self.SetAllSfxEnables(0)
if self.musicManagerIsValid: if self.musicManagerIsValid:
@ -1923,6 +2021,11 @@ class ShowBase(DirectObject.DirectObject):
self.notify.debug("Disabling audio") self.notify.debug("Disabling audio")
def enableAllAudio(self): def enableAllAudio(self):
"""
Reenables the SFX and music managers that were active at the time
`disableAllAudio() was called. Meant to be called when the app regains
audio focus.
"""
self.AppHasAudioFocus = 1 self.AppHasAudioFocus = 1
self.SetAllSfxEnables(self.sfxActive) self.SetAllSfxEnables(self.sfxActive)
if self.musicManagerIsValid: if self.musicManagerIsValid:
@ -1933,6 +2036,9 @@ class ShowBase(DirectObject.DirectObject):
# backwards compatibility. Please do not add code here, add # backwards compatibility. Please do not add code here, add
# it to the loader. # it to the loader.
def loadSfx(self, name): def loadSfx(self, name):
"""
:deprecated: Use `.Loader.Loader.loadSfx()` instead.
"""
assert self.notify.warning("base.loadSfx is deprecated, use base.loader.loadSfx instead.") assert self.notify.warning("base.loadSfx is deprecated, use base.loader.loadSfx instead.")
return self.loader.loadSfx(name) return self.loader.loadSfx(name)
@ -1940,6 +2046,9 @@ class ShowBase(DirectObject.DirectObject):
# backwards compatibility. Please do not add code here, add # backwards compatibility. Please do not add code here, add
# it to the loader. # it to the loader.
def loadMusic(self, name): def loadMusic(self, name):
"""
:deprecated: Use `.Loader.Loader.loadMusic()` instead.
"""
assert self.notify.warning("base.loadMusic is deprecated, use base.loader.loadMusic instead.") assert self.notify.warning("base.loadMusic is deprecated, use base.loader.loadMusic instead.")
return self.loader.loadMusic(name) return self.loader.loadMusic(name)
@ -2070,7 +2179,6 @@ class ShowBase(DirectObject.DirectObject):
throw_new_frame() throw_new_frame()
return Task.cont return Task.cont
def __igLoopSync(self, state): def __igLoopSync(self, state):
if __debug__: if __debug__:
# We render the watch variables for the onScreenDebug as soon # We render the watch variables for the onScreenDebug as soon
@ -2164,6 +2272,8 @@ class ShowBase(DirectObject.DirectObject):
Returns the current window background color. This assumes Returns the current window background color. This assumes
the window is set up to clear the color each frame (this is the window is set up to clear the color each frame (this is
the normal setting). the normal setting).
:rtype: panda3d.core.VBase4
""" """
if win == None: if win == None:
win = self.win win = self.win
@ -2195,47 +2305,74 @@ class ShowBase(DirectObject.DirectObject):
win.setClearColor(color) win.setClearColor(color)
def toggleBackface(self): def toggleBackface(self):
"""
Toggles between `backfaceCullingOn()` and `backfaceCullingOff()`.
"""
if self.backfaceCullingEnabled: if self.backfaceCullingEnabled:
self.backfaceCullingOff() self.backfaceCullingOff()
else: else:
self.backfaceCullingOn() self.backfaceCullingOn()
def backfaceCullingOn(self): def backfaceCullingOn(self):
"""
Disables two-sided rendering on the entire 3D scene graph.
"""
if not self.backfaceCullingEnabled: if not self.backfaceCullingEnabled:
self.render.setTwoSided(0) self.render.setTwoSided(0)
self.backfaceCullingEnabled = 1 self.backfaceCullingEnabled = 1
def backfaceCullingOff(self): def backfaceCullingOff(self):
"""
Enables two-sided rendering on the entire 3D scene graph.
"""
if self.backfaceCullingEnabled: if self.backfaceCullingEnabled:
self.render.setTwoSided(1) self.render.setTwoSided(1)
self.backfaceCullingEnabled = 0 self.backfaceCullingEnabled = 0
def toggleTexture(self): def toggleTexture(self):
"""
Toggles between `textureOn()` and `textureOff()`.
"""
if self.textureEnabled: if self.textureEnabled:
self.textureOff() self.textureOff()
else: else:
self.textureOn() self.textureOn()
def textureOn(self): def textureOn(self):
"""
Undoes the effects of a previous call to `textureOff()`.
"""
self.render.clearTexture() self.render.clearTexture()
self.textureEnabled = 1 self.textureEnabled = 1
def textureOff(self): def textureOff(self):
"""
Disables texturing on the entire 3D scene graph.
"""
self.render.setTextureOff(100) self.render.setTextureOff(100)
self.textureEnabled = 0 self.textureEnabled = 0
def toggleWireframe(self): def toggleWireframe(self):
"""
Toggles between `wireframeOn()` and `wireframeOff()`.
"""
if self.wireframeEnabled: if self.wireframeEnabled:
self.wireframeOff() self.wireframeOff()
else: else:
self.wireframeOn() self.wireframeOn()
def wireframeOn(self): def wireframeOn(self):
"""
Enables wireframe rendering on the entire 3D scene graph.
"""
self.render.setRenderModeWireframe(100) self.render.setRenderModeWireframe(100)
self.render.setTwoSided(1) self.render.setTwoSided(1)
self.wireframeEnabled = 1 self.wireframeEnabled = 1
def wireframeOff(self): def wireframeOff(self):
"""
Undoes the effects of a previous call to `wireframeOn()`.
"""
self.render.clearRenderMode() self.render.clearRenderMode()
render.setTwoSided(not self.backfaceCullingEnabled) render.setTwoSided(not self.backfaceCullingEnabled)
self.wireframeEnabled = 0 self.wireframeEnabled = 0
@ -2257,8 +2394,8 @@ class ShowBase(DirectObject.DirectObject):
def enableMouse(self): def enableMouse(self):
""" """
Reverse the effect of a previous call to disableMouse(). Reverse the effect of a previous call to `disableMouse()`.
useDrive() also implicitly enables the mouse. `useDrive()` also implicitly enables the mouse.
""" """
if self.mouse2cam: if self.mouse2cam:
self.mouse2cam.reparentTo(self.mouseInterface) self.mouse2cam.reparentTo(self.mouseInterface)
@ -2266,14 +2403,14 @@ class ShowBase(DirectObject.DirectObject):
def silenceInput(self): def silenceInput(self):
""" """
This is a heavy-handed way of temporarily turning off This is a heavy-handed way of temporarily turning off
all inputs. Bring them back with reviveInput(). all inputs. Bring them back with `reviveInput()`.
""" """
if not self.__deadInputs: if not self.__deadInputs:
self.__deadInputs = taskMgr.remove('dataLoop') self.__deadInputs = taskMgr.remove('dataLoop')
def reviveInput(self): def reviveInput(self):
""" """
Restores inputs after a previous call to silenceInput. Restores inputs after a previous call to `silenceInput()`.
""" """
if self.__deadInputs: if self.__deadInputs:
self.eventMgr.doEvents() self.eventMgr.doEvents()
@ -2288,7 +2425,7 @@ class ShowBase(DirectObject.DirectObject):
def changeMouseInterface(self, changeTo): def changeMouseInterface(self, changeTo):
""" """
Switch mouse action Change the mouse interface used to control the camera.
""" """
# Get rid of the prior interface: # Get rid of the prior interface:
self.mouseInterface.detachNode() self.mouseInterface.detachNode()
@ -2303,7 +2440,7 @@ class ShowBase(DirectObject.DirectObject):
def useDrive(self): def useDrive(self):
""" """
Switch mouse action to drive mode Changes the mouse interface used for camera control to drive mode.
""" """
if self.drive: if self.drive:
self.changeMouseInterface(self.drive) self.changeMouseInterface(self.drive)
@ -2313,14 +2450,16 @@ class ShowBase(DirectObject.DirectObject):
def useTrackball(self): def useTrackball(self):
""" """
Switch mouse action to trackball mode Changes the mouse interface used for camera control to trackball mode.
""" """
if self.trackball: if self.trackball:
self.changeMouseInterface(self.trackball) self.changeMouseInterface(self.trackball)
def toggleTexMem(self): def toggleTexMem(self):
""" Toggles a handy texture memory watcher. See TexMemWatcher """
for more information. """ Toggles a handy texture memory watcher utility.
See :mod:`direct.showutil.TexMemWatcher` for more information.
"""
if self.texmem and not self.texmem.cleanedUp: if self.texmem and not self.texmem.cleanedUp:
self.texmem.cleanup() self.texmem.cleanup()
@ -2386,7 +2525,6 @@ class ShowBase(DirectObject.DirectObject):
self.showVertices = self.cam.attachNewNode(cam) self.showVertices = self.cam.attachNewNode(cam)
dr.setCamera(self.showVertices) dr.setCamera(self.showVertices)
def oobe(self, cam = None): def oobe(self, cam = None):
""" """
Enable a special "out-of-body experience" mouse-interface Enable a special "out-of-body experience" mouse-interface
@ -2397,12 +2535,12 @@ class ShowBase(DirectObject.DirectObject):
mouse events, if needed, may be sent to the normal node by mouse events, if needed, may be sent to the normal node by
holding down the Control key. holding down the Control key.
This is different than useTrackball(), which simply changes This is different than `useTrackball()`, which simply changes
the existing mouse action to a trackball interface. In fact, the existing mouse action to a trackball interface. In fact,
OOBE mode doesn't care whether useDrive() or useTrackball() is OOBE mode doesn't care whether `useDrive()` or `useTrackball()` is
in effect; it just temporarily layers a new trackball in effect; it just temporarily layers a new trackball
interface on top of whatever the basic interface is. You can interface on top of whatever the basic interface is. You can
even switch between useDrive() and useTrackball() while OOBE even switch between `useDrive()` and `useTrackball()` while OOBE
mode is in effect. mode is in effect.
This is a toggle; the second time this function is called, it This is a toggle; the second time this function is called, it
@ -2594,8 +2732,7 @@ class ShowBase(DirectObject.DirectObject):
generated by makeCubeMap(), namePrefix should contain the hash generated by makeCubeMap(), namePrefix should contain the hash
mark ('#') character. mark ('#') character.
The return value is the filename if successful, or None if :returns: The filename if successful, or None if there is a problem.
there is a problem.
""" """
if source == None: if source == None:
@ -2633,15 +2770,14 @@ class ShowBase(DirectObject.DirectObject):
the current scene, one in each of the six cube map directions. the current scene, one in each of the six cube map directions.
This requires rendering a new frame. This requires rendering a new frame.
Unlike screenshot(), source may only be a GraphicsWindow, Unlike `screenshot()`, source may only be a GraphicsWindow,
GraphicsBuffer, or DisplayRegion; it may not be a Texture. GraphicsBuffer, or DisplayRegion; it may not be a Texture.
camera should be the node to which the cubemap cameras will be camera should be the node to which the cubemap cameras will be
parented. The default is the camera associated with source, parented. The default is the camera associated with source,
if source is a DisplayRegion, or base.camera otherwise. if source is a DisplayRegion, or base.camera otherwise.
The return value is the filename if successful, or None if :returns: The filename if successful, or None if there is a problem.
there is a problem.
""" """
if source == None: if source == None:
@ -2703,8 +2839,7 @@ class ShowBase(DirectObject.DirectObject):
texture and because they are supported on a broader range of texture and because they are supported on a broader range of
hardware. hardware.
The return value is the filename if successful, or None if there :returns: The filename if successful, or None if there is a problem.
is a problem.
""" """
if source == None: if source == None:
source = self.win source = self.win
@ -2875,7 +3010,7 @@ class ShowBase(DirectObject.DirectObject):
def adjustWindowAspectRatio(self, aspectRatio): def adjustWindowAspectRatio(self, aspectRatio):
""" This function is normally called internally by """ This function is normally called internally by
windowEvent(), but it may also be called to explicitly adjust `windowEvent()`, but it may also be called to explicitly adjust
the aspect ratio of the render/render2d DisplayRegion, by a the aspect ratio of the render/render2d DisplayRegion, by a
class that has redefined these. """ class that has redefined these. """
@ -2959,6 +3094,10 @@ class ShowBase(DirectObject.DirectObject):
self.finalizeExit() self.finalizeExit()
def finalizeExit(self): def finalizeExit(self):
"""
Called by `userExit()` to quit the application. The default
implementation just calls `sys.exit()`.
"""
sys.exit() sys.exit()
# [gjeon] start wxPython # [gjeon] start wxPython
@ -3033,9 +3172,9 @@ class ShowBase(DirectObject.DirectObject):
self.taskMgr.step() self.taskMgr.step()
def wxRun(self): def wxRun(self):
""" This method replaces base.run() after we have called """ This method replaces `run()` after we have called `spawnWxLoop()`.
spawnWxLoop(). Since at this point wxPython now owns the main Since at this point wxPython now owns the main loop, this method is a
loop, this method is a call to wxApp.MainLoop(). """ call to wxApp.MainLoop(). """
if Thread.getCurrentThread().getCurrentTask(): if Thread.getCurrentThread().getCurrentTask():
# This happens in the p3d environment during startup. # This happens in the p3d environment during startup.
@ -3117,9 +3256,9 @@ class ShowBase(DirectObject.DirectObject):
self.tkRoot.after(self.tkDelay, self.__tkTimerCallback) self.tkRoot.after(self.tkDelay, self.__tkTimerCallback)
def tkRun(self): def tkRun(self):
""" This method replaces base.run() after we have called """ This method replaces `run()` after we have called `spawnTkLoop()`.
spawnTkLoop(). Since at this point Tkinter now owns the main Since at this point Tkinter now owns the main loop, this method is a
loop, this method is a call to tkRoot.mainloop(). """ call to tkRoot.mainloop(). """
if Thread.getCurrentThread().getCurrentTask(): if Thread.getCurrentThread().getCurrentTask():
# This happens in the p3d environment during startup. # This happens in the p3d environment during startup.
@ -3146,6 +3285,11 @@ class ShowBase(DirectObject.DirectObject):
return None return None
def getAxes(self): def getAxes(self):
"""
Loads and returns the ``models/misc/xyzAxis.bam`` model.
:rtype: panda3d.core.NodePath
"""
return loader.loadModel("models/misc/xyzAxis.bam") return loader.loadModel("models/misc/xyzAxis.bam")
def __doStartDirect(self): def __doStartDirect(self):