showbase: Annotate basic `ShowBase` setup/shutdown methods (#1558)

This commit is contained in:
WMOkiishi 2024-10-24 07:57:36 -06:00 committed by GitHub
parent 583f7366db
commit f5b0b1e1d6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 88 additions and 74 deletions

View File

@ -8,6 +8,9 @@ base = ShowBase.ShowBase()
# Put an axis in the world:
base.loader.loadModel("models/misc/xyzAxis").reparentTo(base.render)
assert base.camera is not None
assert base.camLens is not None
base.camera.setPosHpr(0, -10.0, 0, 0, 0, 0)
base.camLens.setFov(52.0)
base.camLens.setNearFar(1.0, 10000.0)

View File

@ -10,6 +10,9 @@ base = ThreeUpShow.ThreeUpShow()
# Put an axis in the world:
base.loader.loadModel("models/misc/xyzAxis").reparentTo(base.render)
assert base.camera is not None
assert base.camLens is not None
base.camera.setPosHpr(0, -10.0, 0, 0, 0, 0)
base.camLens.setFov(52.0)
base.camLens.setNearFar(1.0, 10000.0)

View File

@ -32,6 +32,8 @@ built-in scope.
"""
from __future__ import annotations
__all__ = ['ShowBase', 'WindowControls']
# This module redefines the builtin import function with one
@ -71,9 +73,11 @@ from panda3d.core import (
GraphicsPipe,
GraphicsPipeSelection,
GraphicsWindow,
InputDevice,
InputDeviceManager,
InputDeviceNode,
KeyboardButton,
Lens,
LensNode,
Mat4,
ModelNode,
@ -118,9 +122,11 @@ from . import DConfig
from direct.extensions_native import NodePath_extensions # pylint: disable=unused-import
# This needs to be available early for DirectGUI imports
from typing import Any
builtins: Any # Tell mypy not to worry about us setting attributes on builtins
import sys
import builtins
builtins.config = DConfig # type: ignore[attr-defined]
builtins.config = DConfig
from direct.directnotify.DirectNotifyGlobal import directNotify, giveNotify
from direct.directnotify.Notifier import Notifier
@ -141,7 +147,7 @@ import importlib
from direct.showbase import ExceptionVarDump
from . import DirectObject
from . import SfxPlayer
from typing import ClassVar, Optional
from typing import Callable, ClassVar, Literal, NoReturn
if __debug__:
from direct.showbase import GarbageReport
from direct.directutil import DeltaProfiler
@ -150,9 +156,10 @@ if __debug__:
@atexit.register
def exitfunc():
if getattr(builtins, 'base', None) is not None:
builtins.base.destroy()
def exitfunc() -> None:
base = getattr(builtins, 'base', None)
if base is not None:
base.destroy()
# Now ShowBase is a DirectObject. We need this so ShowBase can hang
# hooks on messages, particularly on window-event. This doesn't
@ -170,7 +177,7 @@ class ShowBase(DirectObject.DirectObject):
aspect2d: NodePath
pixel2d: NodePath
def __init__(self, fStartDirect=True, windowType=None):
def __init__(self, fStartDirect: bool = True, windowType: str | None = None) -> None:
"""Opens a window, sets up a 3-D and several 2-D scene graphs, and
everything else needed to render the scene graph to the window.
@ -233,10 +240,10 @@ class ShowBase(DirectObject.DirectObject):
self.wantRender2dp = ConfigVariableBool('want-render2dp', True).value
self.screenshotExtension = ConfigVariableString('screenshot-extension', 'jpg').value
self.musicManager = None
self.musicManagerIsValid = None
self.sfxManagerList = []
self.sfxManagerIsValidList = []
self.musicManager: AudioManager | None = None
self.musicManagerIsValid: bool | None = None
self.sfxManagerList: list[AudioManager] = []
self.sfxManagerIsValidList: list[bool] = []
self.wantStats = ConfigVariableBool('want-pstats', False).value
self.wantTk = False
@ -245,12 +252,12 @@ class ShowBase(DirectObject.DirectObject):
#: Fill this in with a function to invoke when the user "exits"
#: the program by closing the main window.
self.exitFunc = None
self.exitFunc: Callable[[], object] | None = 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.
self.finalExitCallbacks = []
self.finalExitCallbacks: list[Callable[[], object]] = []
# Set up the TaskManager to reset the PStats clock back
# whenever we resume from a pause. This callback function is
@ -268,7 +275,7 @@ class ShowBase(DirectObject.DirectObject):
self.__configAspectRatio = ConfigVariableDouble('aspect-ratio', 0).value
# This variable is used to see if the aspect ratio has changed when
# we get a window-event.
self.__oldAspectRatio = None
self.__oldAspectRatio: float | None = None
#: This is set to the value of the window-type config variable, but may
#: optionally be overridden in the Showbase constructor. Should either
@ -279,68 +286,68 @@ class ShowBase(DirectObject.DirectObject):
self.requireWindow = ConfigVariableBool('require-window', True).value
#: This is the main, or only window; see `winList` for a list of *all* windows.
self.win = None
self.frameRateMeter = None
self.sceneGraphAnalyzerMeter = None
self.win: GraphicsOutput | None = None
self.frameRateMeter: FrameRateMeter | None = None
self.sceneGraphAnalyzerMeter: SceneGraphAnalyzerMeter | None = None
#: A list of all windows opened via `openWindow()`.
self.winList = []
self.winControls = []
self.mainWinMinimized = 0
self.mainWinForeground = 0
self.winList: list[GraphicsEngine] = []
self.winControls: list[WindowControls] = []
self.mainWinMinimized = False
self.mainWinForeground = False
#: Contains the :class:`~panda3d.core.GraphicsPipe` object created by
#: `makeDefaultPipe()`.
self.pipe = None
self.pipe: GraphicsPipe | None = None
#: The full list of :class:`~panda3d.core.GraphicsPipe` objects,
#: including any auxiliary pipes. Filled by `makeAllPipes()`.
self.pipeList = []
self.mouse2cam = None
self.buttonThrowers = None
self.mouseWatcher = None
self.pipeList: list[GraphicsPipe] = []
self.mouse2cam: NodePath | None = None
self.buttonThrowers: list[ButtonThrower] | None = None
self.mouseWatcher: NodePath | None = None
#: The :class:`~panda3d.core.MouseWatcher` object, created by
#: `setupMouse()`.
self.mouseWatcherNode = None
self.pointerWatcherNodes = None
self.mouseInterface = None
self.drive = None
self.trackball = None
self.texmem = None
self.showVertices = None
self.deviceButtonThrowers = []
self.mouseWatcherNode: MouseWatcher | None = None
self.pointerWatcherNodes: list[MouseWatcher] | None = None
self.mouseInterface: NodePath | None = None
self.drive: NodePath | None = None
self.trackball: NodePath | None = None
self.texmem: Any | None = None
self.showVertices: NodePath | None = None
self.deviceButtonThrowers: list[NodePath] = []
#: This is a :class:`~panda3d.core.NodePath` pointing to the
#: :class:`~panda3d.core.Camera` object set up for the 3D scene.
#: Usually a child of `camera`.
self.cam = None
self.cam: NodePath | None = None
#: Same as `cam`, but for the 2D scene graph.
self.cam2d = None
self.cam2d: NodePath | None = None
#: Same as `cam2d`, but for the 2D overlay scene graph.
self.cam2dp = None
self.cam2dp: NodePath | None = None
#: This is the :class:`~panda3d.core.NodePath` that should be used to
#: manipulate the camera. It points at the node to which the default
#: camera (`cam`, `camNode`) is attached.
self.camera = None
self.camera: NodePath | None = None
#: Same as `camera`, but for the 2D scene graph. Parent of `cam2d`.
self.camera2d = None
self.camera2d: NodePath | None = None
#: Same as `camera2d`, but for the 2D overlay scene graph. Parent of
#: `cam2dp`.
self.camera2dp = None
self.camera2dp: NodePath | None = None
#: A list of all cameras created with `makeCamera()`, including `cam`.
self.camList = []
self.camList: list[NodePath] = []
#: Convenience accessor for base.cam.node(), containing a
#: :class:`~panda3d.core.Camera` object.
self.camNode = None
self.camNode: Camera | None = None
#: Convenience accessor for base.camNode.get_lens(), containing a
#: :class:`~panda3d.core.Lens` object.
self.camLens = None
self.camFrustumVis = None
self.camLens: Lens | None = None
self.camFrustumVis: NodePath | None = None
self.direct = None
#: This is used to store the wx.Application object used when want-wx is
#: set or `startWx()` is called.
self.wxApp = None
self.wxApp: Any | None = None
self.wxAppCreated = False
self.tkRoot = None
self.tkRoot: Any | None = None
self.tkRootCreated = False
# This is used for syncing multiple PCs in a distributed cluster
@ -371,11 +378,11 @@ class ShowBase(DirectObject.DirectObject):
#: traverse it automatically in the collisionLoop task, so you won't
#: need to call :meth:`~panda3d.core.CollisionTraverser.traverse()`
#: yourself every frame.
self.cTrav = 0
self.shadowTrav = 0
self.cTrav: CollisionTraverser | Literal[0] = 0
self.shadowTrav: CollisionTraverser | Literal[0] = 0
self.cTravStack = Stack()
# Ditto for an AppTraverser.
self.appTrav = 0
self.appTrav: Any | Literal[0] = 0
# This is the DataGraph traverser, which we might as well
# create now.
@ -383,7 +390,7 @@ class ShowBase(DirectObject.DirectObject):
# Maybe create a RecorderController to record and/or play back
# the user session.
self.recorder = None
self.recorder: RecorderController | None = None
playbackSession = ConfigVariableFilename('playback-session', '')
recordSession = ConfigVariableFilename('record-session', '')
if not playbackSession.empty():
@ -448,22 +455,22 @@ class ShowBase(DirectObject.DirectObject):
#: If `enableParticles()` has been called, this is the particle manager
#: as imported from :mod:`direct.particles.ParticleManagerGlobal`.
self.particleMgr = None
self.particleMgrEnabled = 0
self.particleMgrEnabled = False
#: If `enableParticles()` has been called, this is the physics manager
#: as imported from :mod:`direct.showbase.PhysicsManagerGlobal`.
self.physicsMgr = None
self.physicsMgrEnabled = 0
self.physicsMgrAngular = 0
self.physicsMgrEnabled = False
self.physicsMgrAngular = False
#: This is the global :class:`~panda3d.core.InputDeviceManager`, which
#: keeps track of connected input devices.
self.devices = InputDeviceManager.getGlobalPtr()
self.__inputDeviceNodes = {}
self.__inputDeviceNodes: dict[InputDevice, NodePath] = {}
self.createStats()
self.AppHasAudioFocus = 1
self.AppHasAudioFocus = True
# Get a pointer to Panda's global ClockObject, used for
# synchronizing events between Python and C.
@ -502,7 +509,7 @@ class ShowBase(DirectObject.DirectObject):
affinity = ConfigVariableInt('client-cpu-affinity', -1).value
if (affinity in (None, -1)) and autoAffinity:
affinity = 0
if affinity not in (None, -1):
if affinity is not None and affinity != -1:
# Windows XP supports a 32-bit affinity mask
TrueClock.getGlobalPtr().setCpuAffinity(1 << (affinity % 32))
@ -575,7 +582,7 @@ class ShowBase(DirectObject.DirectObject):
# Now hang a hook on the window-event from Panda. This allows
# us to detect when the user resizes, minimizes, or closes the
# main window.
self.__prevWindowProperties = None
self.__prevWindowProperties: WindowProperties | None = None
self.__directObject.accept('window-event', self.windowEvent)
# Transition effects (fade, iris, etc)
@ -658,7 +665,7 @@ class ShowBase(DirectObject.DirectObject):
#print getDnaPath()
print("}")
def destroy(self):
def destroy(self) -> None:
""" Call this function to destroy the ShowBase and stop all
its tasks, freeing all of the Panda resources. Normally, you
should not need to call it explicitly, as it is bound to the
@ -704,6 +711,7 @@ class ShowBase(DirectObject.DirectObject):
self.shutdown()
if getattr(self, 'musicManager', None):
assert self.musicManager is not None
self.musicManager.shutdown()
self.musicManager = None
for sfxManager in self.sfxManagerList:
@ -711,12 +719,12 @@ class ShowBase(DirectObject.DirectObject):
self.sfxManagerList = []
if getattr(self, 'loader', None):
self.loader.destroy()
self.loader = None
del self.loader
if getattr(self, 'graphicsEngine', None):
self.graphicsEngine.removeAllWindows()
try:
self.direct.panel.destroy()
self.direct.panel.destroy() # type: ignore[attr-defined]
except Exception:
pass
@ -1185,7 +1193,7 @@ class ShowBase(DirectObject.DirectObject):
self.setSceneGraphAnalyzerMeter(flag.value)
return success
def setSleep(self, amount):
def setSleep(self, amount: float) -> None:
"""
Sets up a task that calls python 'sleep' every frame. This is a simple
way to reduce the CPU usage (and frame rate) of a panda program.
@ -1205,7 +1213,7 @@ class ShowBase(DirectObject.DirectObject):
#time.sleep(self.clientSleep)
return Task.cont
def setFrameRateMeter(self, flag):
def setFrameRateMeter(self, flag: bool) -> None:
"""
Turns on or off (according to flag) a standard frame rate
meter in the upper-right corner of the main window.
@ -1219,7 +1227,7 @@ class ShowBase(DirectObject.DirectObject):
self.frameRateMeter.clearWindow()
self.frameRateMeter = None
def setSceneGraphAnalyzerMeter(self, flag):
def setSceneGraphAnalyzerMeter(self, flag: bool) -> None:
"""
Turns on or off (according to flag) a standard frame rate
meter in the upper-right corner of the main window.
@ -1242,7 +1250,7 @@ class ShowBase(DirectObject.DirectObject):
mouseKeyboard = self.dataRoot.find("**/*"))
self.winControls.append(winCtrl)
def setupRender(self):
def setupRender(self) -> None:
"""
Creates the render scene graph, the primary scene graph for
rendering 3-d geometry.
@ -1252,11 +1260,11 @@ class ShowBase(DirectObject.DirectObject):
self.render.setAttrib(RescaleNormalAttrib.makeDefault())
self.render.setTwoSided(0)
self.backfaceCullingEnabled = 1
self.textureEnabled = 1
self.wireframeEnabled = 0
self.backfaceCullingEnabled = True
self.textureEnabled = True
self.wireframeEnabled = False
def setupRender2d(self):
def setupRender2d(self) -> None:
"""
Creates the render2d scene graph, the primary scene graph for
2-d objects and gui elements that are superimposed over the
@ -1355,7 +1363,7 @@ class ShowBase(DirectObject.DirectObject):
if xsize > 0 and ysize > 0:
self.pixel2d.setScale(2.0 / xsize, 1.0, 2.0 / ysize)
def setupRender2dp(self):
def setupRender2dp(self) -> None:
"""
Creates a render2d scene graph, the secondary scene graph for
2-d objects and gui elements that are superimposed over the
@ -1677,7 +1685,7 @@ class ShowBase(DirectObject.DirectObject):
return camera2dp
def setupDataGraph(self):
def setupDataGraph(self) -> None:
"""
Creates the data graph and populates it with the basic input
devices.
@ -2006,7 +2014,7 @@ class ShowBase(DirectObject.DirectObject):
self.physicsMgr.doPhysics(dt)
return Task.cont
def createStats(self, hostname=None, port=None):
def createStats(self, hostname: str | None = None, port: int | None = None) -> bool:
"""
If want-pstats is set in Config.prc, or the `wantStats` member is
otherwise set to True, connects to the PStats server.
@ -2323,7 +2331,7 @@ class ShowBase(DirectObject.DirectObject):
throw_new_frame()
return Task.cont
def restart(self, clusterSync=False, cluster=None):
def restart(self, clusterSync: bool = False, cluster=None) -> None:
self.shutdown()
# __resetPrevTransform goes at the very beginning of the frame.
self.taskMgr.add(
@ -2354,7 +2362,7 @@ class ShowBase(DirectObject.DirectObject):
self.taskMgr.add(self.__audioLoop, 'audioLoop', sort = 60)
self.eventMgr.restart()
def shutdown(self):
def shutdown(self) -> None:
self.taskMgr.remove('audioLoop')
self.taskMgr.remove('igLoop')
self.taskMgr.remove('shadowCollisionLoop')
@ -3195,14 +3203,14 @@ class ShowBase(DirectObject.DirectObject):
# If anybody needs to update their GUI, put a callback on this event
messenger.send("aspectRatioChanged")
def userExit(self):
def userExit(self) -> NoReturn:
# The user has requested we exit the program. Deal with this.
if self.exitFunc:
self.exitFunc()
self.notify.info("Exiting ShowBase.")
self.finalizeExit()
def finalizeExit(self):
def finalizeExit(self) -> NoReturn:
"""
Called by `userExit()` to quit the application. The default
implementation just calls `sys.exit()`.