Browse Source

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

WMOkiishi 1 year ago
parent
commit
f5b0b1e1d6

+ 3 - 0
direct/src/directbase/TestStart.py

@@ -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)

+ 3 - 0
direct/src/directbase/ThreeUpStart.py

@@ -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)

+ 82 - 74
direct/src/showbase/ShowBase.py

@@ -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()`.