Browse Source

Merge branch 'release/1.10.x'

rdb 6 years ago
parent
commit
a72be90f17
86 changed files with 979 additions and 764 deletions
  1. 27 29
      direct/src/actor/Actor.py
  2. 2 1
      direct/src/cluster/ClusterClient.py
  3. 20 17
      direct/src/cluster/ClusterConfig.py
  4. 16 15
      direct/src/controls/ControlManager.py
  5. 8 6
      direct/src/controls/DevWalker.py
  6. 8 6
      direct/src/controls/GhostWalker.py
  7. 8 6
      direct/src/controls/GravityWalker.py
  8. 20 18
      direct/src/controls/InputState.py
  9. 8 6
      direct/src/controls/NonPhysicsWalker.py
  10. 8 6
      direct/src/controls/ObserverWalker.py
  11. 8 6
      direct/src/controls/PhysicsWalker.py
  12. 1 1
      direct/src/controls/TwoDWalker.py
  13. 1 1
      direct/src/directdevices/DirectDeviceManager.py
  14. 2 2
      direct/src/directdevices/DirectFastrak.py
  15. 5 1
      direct/src/directnotify/DirectNotifyGlobal.py
  16. 2 1
      direct/src/directnotify/LoggerGlobal.py
  17. 6 6
      direct/src/directnotify/Notifier.py
  18. 15 15
      direct/src/directnotify/RotatingLog.py
  19. 38 32
      direct/src/directutil/Verify.py
  20. 4 0
      direct/src/dist/__init__.py
  21. 6 0
      direct/src/dist/commands.py
  22. 1 1
      direct/src/distributed/AsyncRequest.py
  23. 2 1
      direct/src/distributed/DistributedObject.py
  24. 2 1
      direct/src/distributed/DistributedObjectBase.py
  25. 9 8
      direct/src/distributed/DoCollectionManager.py
  26. 8 9
      direct/src/distributed/DoHierarchy.py
  27. 1 4
      direct/src/distributed/PyDatagram.py
  28. 1 0
      direct/src/distributed/PyDatagramIterator.py
  29. 1 1
      direct/src/distributed/ServerRepository.py
  30. 13 12
      direct/src/fsm/ClassicFSM.py
  31. 32 26
      direct/src/fsm/FSM.py
  32. 3 6
      direct/src/fsm/FourState.py
  33. 12 14
      direct/src/fsm/FourStateAI.py
  34. 3 0
      direct/src/fsm/__init__.py
  35. 43 43
      direct/src/gui/DirectDialog.py
  36. 1 1
      direct/src/gui/DirectGui.py
  37. 44 36
      direct/src/gui/DirectGuiBase.py
  38. 3 6
      direct/src/gui/DirectGuiGlobals.py
  39. 11 6
      direct/src/gui/OnscreenImage.py
  40. 6 2
      direct/src/gui/OnscreenText.py
  41. 1 1
      direct/src/gui/__init__.py
  42. 5 1
      direct/src/interval/ActorInterval.py
  43. 16 20
      direct/src/interval/ParticleInterval.py
  44. 2 0
      direct/src/interval/__init__.py
  45. 1 0
      direct/src/leveleditor/ActionMgr.py
  46. 7 1
      direct/src/particles/Particles.py
  47. 3 0
      direct/src/particles/__init__.py
  48. 8 3
      direct/src/showbase/AppRunnerGlobal.py
  49. 26 11
      direct/src/showbase/BufferViewer.py
  50. 2 1
      direct/src/showbase/BulletinBoardGlobal.py
  51. 23 21
      direct/src/showbase/DistancePhasedNode.py
  52. 1 0
      direct/src/showbase/ExceptionVarDump.py
  53. 4 1
      direct/src/showbase/GarbageReportScheduler.py
  54. 2 1
      direct/src/showbase/InputStateGlobal.py
  55. 35 27
      direct/src/showbase/Job.py
  56. 1 0
      direct/src/showbase/JobManagerGlobal.py
  57. 3 1
      direct/src/showbase/LeakDetectors.py
  58. 3 1
      direct/src/showbase/Loader.py
  59. 25 23
      direct/src/showbase/Messenger.py
  60. 2 1
      direct/src/showbase/MessengerGlobal.py
  61. 1 1
      direct/src/showbase/MirrorDemo.py
  62. 2 1
      direct/src/showbase/PhysicsManagerGlobal.py
  63. 7 4
      direct/src/showbase/Pool.py
  64. 78 76
      direct/src/showbase/PythonUtil.py
  65. 50 31
      direct/src/showbase/ShowBase.py
  66. 10 2
      direct/src/showbase/ShowBaseGlobal.py
  67. 1 0
      direct/src/showbase/TkGlobal.py
  68. 25 14
      direct/src/showbase/VFSImporter.py
  69. 2 0
      direct/src/showbase/WxGlobal.py
  70. 5 0
      direct/src/stdpy/file.py
  71. 3 3
      direct/src/stdpy/pickle.py
  72. 64 58
      direct/src/task/Task.py
  73. 1 1
      direct/src/task/TaskManagerGlobal.py
  74. 3 0
      direct/src/task/__init__.py
  75. 104 102
      direct/src/tkpanels/FSMInspector.py
  76. 3 0
      direct/src/tkpanels/__init__.py
  77. 1 0
      direct/src/tkwidgets/__init__.py
  78. 6 0
      dtool/src/dtoolutil/pandaSystem.cxx
  79. 8 2
      makepanda/makepanda.bat
  80. 0 1
      makepanda/makepandacore.py
  81. 7 0
      makepanda/makewheel.py
  82. 5 1
      panda/src/glstuff/glGraphicsBuffer_src.cxx
  83. 3 0
      panda/src/gobj/geomPrimitive.cxx
  84. 1 0
      panda/src/pstatclient/pStatClient.cxx
  85. 1 1
      panda/src/x11display/x11GraphicsWindow.cxx
  86. 9 0
      pandatool/src/deploy-stub/deploy-stub.c

+ 27 - 29
direct/src/actor/Actor.py

@@ -104,45 +104,43 @@ class Actor(DirectObject, NodePath):
                  lodNode = None, flattenable = True, setFinal = False,
                  lodNode = None, flattenable = True, setFinal = False,
                  mergeLODBundles = None, allowAsyncBind = None,
                  mergeLODBundles = None, allowAsyncBind = None,
                  okMissing = None):
                  okMissing = None):
-        """__init__(self, string | string:string{}, string:string{} |
-        string:(string:string{}){}, Actor=None)
-        Actor constructor: can be used to create single or multipart
+        """Actor constructor: can be used to create single or multipart
         actors. If another Actor is supplied as an argument this
         actors. If another Actor is supplied as an argument this
         method acts like a copy constructor. Single part actors are
         method acts like a copy constructor. Single part actors are
         created by calling with a model and animation dictionary
         created by calling with a model and animation dictionary
-        (animName:animPath{}) as follows:
+        ``(animName:animPath{})`` as follows::
 
 
-           a = Actor("panda-3k.egg", {"walk":"panda-walk.egg" \
+           a = Actor("panda-3k.egg", {"walk":"panda-walk.egg",
                                       "run":"panda-run.egg"})
                                       "run":"panda-run.egg"})
 
 
-        This could be displayed and animated as such:
+        This could be displayed and animated as such::
 
 
            a.reparentTo(render)
            a.reparentTo(render)
            a.loop("walk")
            a.loop("walk")
            a.stop()
            a.stop()
 
 
         Multipart actors expect a dictionary of parts and a dictionary
         Multipart actors expect a dictionary of parts and a dictionary
-        of animation dictionaries (partName:(animName:animPath{}){}) as
-        below:
+        of animation dictionaries ``(partName:(animName:animPath{}){})``
+        as below::
 
 
             a = Actor(
             a = Actor(
 
 
                 # part dictionary
                 # part dictionary
-                {"head":"char/dogMM/dogMM_Shorts-head-mod", \
-                 "torso":"char/dogMM/dogMM_Shorts-torso-mod", \
-                 "legs":"char/dogMM/dogMM_Shorts-legs-mod"}, \
+                {"head": "char/dogMM/dogMM_Shorts-head-mod",
+                 "torso": "char/dogMM/dogMM_Shorts-torso-mod",
+                 "legs": "char/dogMM/dogMM_Shorts-legs-mod"},
 
 
                 # dictionary of anim dictionaries
                 # dictionary of anim dictionaries
-                {"head":{"walk":"char/dogMM/dogMM_Shorts-head-walk", \
-                         "run":"char/dogMM/dogMM_Shorts-head-run"}, \
-                 "torso":{"walk":"char/dogMM/dogMM_Shorts-torso-walk", \
-                          "run":"char/dogMM/dogMM_Shorts-torso-run"}, \
-                 "legs":{"walk":"char/dogMM/dogMM_Shorts-legs-walk", \
-                         "run":"char/dogMM/dogMM_Shorts-legs-run"} \
+                {"head":{"walk": "char/dogMM/dogMM_Shorts-head-walk",
+                         "run": "char/dogMM/dogMM_Shorts-head-run"},
+                 "torso":{"walk": "char/dogMM/dogMM_Shorts-torso-walk",
+                          "run": "char/dogMM/dogMM_Shorts-torso-run"},
+                 "legs":{"walk": "char/dogMM/dogMM_Shorts-legs-walk",
+                         "run": "char/dogMM/dogMM_Shorts-legs-run"}
                  })
                  })
 
 
         In addition multipart actor parts need to be connected together
         In addition multipart actor parts need to be connected together
-        in a meaningful fashion:
+        in a meaningful fashion::
 
 
             a.attach("head", "torso", "joint-head")
             a.attach("head", "torso", "joint-head")
             a.attach("torso", "legs", "joint-hips")
             a.attach("torso", "legs", "joint-hips")
@@ -151,7 +149,7 @@ class Actor(DirectObject, NodePath):
         # ADD LOD COMMENT HERE!
         # ADD LOD COMMENT HERE!
         #
         #
 
 
-        Other useful Actor class functions:
+        Other useful Actor class functions::
 
 
             #fix actor eye rendering
             #fix actor eye rendering
             a.drawInFront("joint-pupil?", "eyes*")
             a.drawInFront("joint-pupil?", "eyes*")
@@ -1135,7 +1133,7 @@ class Actor(DirectObject, NodePath):
     def getJoints(self, partName = None, jointName = '*', lodName = None):
     def getJoints(self, partName = None, jointName = '*', lodName = None):
         """ Returns the list of all joints, from the named part or
         """ Returns the list of all joints, from the named part or
         from all parts, that match the indicated jointName.  The
         from all parts, that match the indicated jointName.  The
-        jointName may include pattern characters like *. """
+        jointName may include pattern characters like \\*. """
 
 
         joints=[]
         joints=[]
         pattern = GlobPattern(jointName)
         pattern = GlobPattern(jointName)
@@ -2439,15 +2437,15 @@ class Actor(DirectObject, NodePath):
         return ActorInterval.ActorInterval(self, *args, **kw)
         return ActorInterval.ActorInterval(self, *args, **kw)
 
 
     def getAnimBlends(self, animName=None, partName=None, lodName=None):
     def getAnimBlends(self, animName=None, partName=None, lodName=None):
-        """ Returns a list of the form:
-
-        [ (lodName, [(animName, [(partName, effect), (partName, effect), ...]),
-                     (animName, [(partName, effect), (partName, effect), ...]),
-                     ...]),
-          (lodName, [(animName, [(partName, effect), (partName, effect), ...]),
-                     (animName, [(partName, effect), (partName, effect), ...]),
-                     ...]),
-           ... ]
+        """Returns a list of the form::
+
+           [ (lodName, [(animName, [(partName, effect), (partName, effect), ...]),
+                        (animName, [(partName, effect), (partName, effect), ...]),
+                        ...]),
+             (lodName, [(animName, [(partName, effect), (partName, effect), ...]),
+                        (animName, [(partName, effect), (partName, effect), ...]),
+                        ...]),
+              ... ]
 
 
         This list reports the non-zero control effects for each
         This list reports the non-zero control effects for each
         partName within a particular animation and LOD. """
         partName within a particular animation and LOD. """

+ 2 - 1
direct/src/cluster/ClusterClient.py

@@ -1,4 +1,4 @@
-"""ClusterClient: Master for mutli-piping or PC clusters.  """
+"""ClusterClient: Master for multi-piping or PC clusters."""
 
 
 from panda3d.core import *
 from panda3d.core import *
 from .ClusterMsgs import *
 from .ClusterMsgs import *
@@ -8,6 +8,7 @@ from direct.showbase import DirectObject
 from direct.task import Task
 from direct.task import Task
 import os
 import os
 
 
+
 class ClusterClient(DirectObject.DirectObject):
 class ClusterClient(DirectObject.DirectObject):
     notify = DirectNotifyGlobal.directNotify.newCategory("ClusterClient")
     notify = DirectNotifyGlobal.directNotify.newCategory("ClusterClient")
     MGR_NUM = 1000000
     MGR_NUM = 1000000

+ 20 - 17
direct/src/cluster/ClusterConfig.py

@@ -1,23 +1,26 @@
 
 
 from .ClusterClient import *
 from .ClusterClient import *
 
 
-# A dictionary of information for various cluster configurations.
-# Dictionary is keyed on cluster-config string
-# Each dictionary contains a list of display configurations, one for
-# each display in the cluster
-# Information that can be specified for each display:
-#      display name: Name of display (used in Configrc to specify server)
-#      display type: Used to flag client vs. server
-#      pos:   positional offset of display's camera from main cluster group
-#      hpr:   orientation offset of display's camera from main cluster group
-#      focal length: display's focal length (in mm)
-#      film size: display's film size (in inches)
-#      film offset: offset of film back (in inches)
-# Note: Note, this overrides offsets specified in DirectCamConfig.py
-# For now we only specify frustum for first display region of configuration
-# TODO: Need to handle multiple display regions per cluster node and to
-# generalize to non cluster situations
-
+#: A dictionary of information for various cluster configurations.
+#: Dictionary is keyed on cluster-config string
+#: Each dictionary contains a list of display configurations, one for
+#: each display in the cluster
+#:
+#: Information that can be specified for each display:
+#:
+#: - display name: Name of display (used in Configrc to specify server)
+#: - display type: Used to flag client vs. server
+#: - pos: positional offset of display's camera from main cluster group
+#: - hpr: orientation offset of display's camera from main cluster group
+#: - focal length: display's focal length (in mm)
+#: - film size: display's film size (in inches)
+#: - film offset: offset of film back (in inches)
+#:
+#: Note: this overrides offsets specified in DirectCamConfig.py
+#: For now we only specify frustum for first display region of configuration
+#:
+#: TODO: Need to handle multiple display regions per cluster node and to
+#: generalize to non cluster situations
 ClientConfigs = {
 ClientConfigs = {
     'single-server':       [{'display name': 'display0',
     'single-server':       [{'display name': 'display0',
                               'display mode': 'client',
                               'display mode': 'client',

+ 16 - 15
direct/src/controls/ControlManager.py

@@ -15,7 +15,9 @@ from direct.directnotify import DirectNotifyGlobal
 from direct.task import Task
 from direct.task import Task
 from panda3d.core import ConfigVariableBool
 from panda3d.core import ConfigVariableBool
 
 
-CollisionHandlerRayStart = 4000.0 # This is a hack, it may be better to use a line instead of a ray.
+# This is a hack, it may be better to use a line instead of a ray.
+CollisionHandlerRayStart = 4000.0
+
 
 
 class ControlManager:
 class ControlManager:
     notify = DirectNotifyGlobal.directNotify.newCategory("ControlManager")
     notify = DirectNotifyGlobal.directNotify.newCategory("ControlManager")
@@ -52,14 +54,14 @@ class ControlManager:
         return 'ControlManager: using \'%s\'' % self.currentControlsName
         return 'ControlManager: using \'%s\'' % self.currentControlsName
 
 
     def add(self, controls, name="basic"):
     def add(self, controls, name="basic"):
-        """
-        controls is an avatar control system.
-        name is any key that you want to use to refer to the
-            the controls later (e.g. using the use(<name>) call).
+        """Add a control instance to the list of available control systems.
 
 
-        Add a control instance to the list of available control systems.
+        Args:
+            controls: an avatar control system.
+            name (str): any key that you want to use to refer to the controls
+                later (e.g. using the use(<name>) call).
 
 
-        See also: use().
+        See also: :meth:`use()`.
         """
         """
         assert self.notify.debugCall(id(self))
         assert self.notify.debugCall(id(self))
         assert controls is not None
         assert controls is not None
@@ -77,15 +79,14 @@ class ControlManager:
         return self.controls.get(name)
         return self.controls.get(name)
 
 
     def remove(self, name):
     def remove(self, name):
-        """
-        name is any key that was used to refer to the
-            the controls when they were added (e.g.
-            using the add(<controls>, <name>) call).
+        """Remove a control instance from the list of available control
+        systems.
 
 
-        Remove a control instance from the list of
-        available control systems.
+        Args:
+            name: any key that was used to refer to the controls when they were
+                added (e.g. using the add(<controls>, <name>) call).
 
 
-        See also: add().
+        See also: :meth:`add()`.
         """
         """
         assert self.notify.debugCall(id(self))
         assert self.notify.debugCall(id(self))
         oldControls = self.controls.pop(name,None)
         oldControls = self.controls.pop(name,None)
@@ -108,7 +109,7 @@ class ControlManager:
 
 
         Use a previously added control system.
         Use a previously added control system.
 
 
-        See also: add().
+        See also: :meth:`add()`.
         """
         """
         assert self.notify.debugCall(id(self))
         assert self.notify.debugCall(id(self))
         if __debug__ and hasattr(self, "ignoreUse"):
         if __debug__ and hasattr(self, "ignoreUse"):

+ 8 - 6
direct/src/controls/DevWalker.py

@@ -2,15 +2,17 @@
 DevWalker.py is for avatars.
 DevWalker.py is for avatars.
 
 
 A walker control such as this one provides:
 A walker control such as this one provides:
-    - creation of the collision nodes
-    - handling the keyboard and mouse input for avatar movement
-    - moving the avatar
+
+- creation of the collision nodes
+- handling the keyboard and mouse input for avatar movement
+- moving the avatar
 
 
 it does not:
 it does not:
-    - play sounds
-    - play animations
 
 
-although it does send messeges that allow a listener to play sounds or
+- play sounds
+- play animations
+
+although it does send messages that allow a listener to play sounds or
 animations based on walker events.
 animations based on walker events.
 """
 """
 
 

+ 8 - 6
direct/src/controls/GhostWalker.py

@@ -2,15 +2,17 @@
 GhostWalker.py is for avatars.
 GhostWalker.py is for avatars.
 
 
 A walker control such as this one provides:
 A walker control such as this one provides:
-    - creation of the collision nodes
-    - handling the keyboard and mouse input for avatar movement
-    - moving the avatar
+
+- creation of the collision nodes
+- handling the keyboard and mouse input for avatar movement
+- moving the avatar
 
 
 it does not:
 it does not:
-    - play sounds
-    - play animations
 
 
-although it does send messeges that allow a listener to play sounds or
+- play sounds
+- play animations
+
+although it does send messages that allow a listener to play sounds or
 animations based on walker events.
 animations based on walker events.
 """
 """
 
 

+ 8 - 6
direct/src/controls/GravityWalker.py

@@ -2,15 +2,17 @@
 GravityWalker.py is for avatars.
 GravityWalker.py is for avatars.
 
 
 A walker control such as this one provides:
 A walker control such as this one provides:
-    - creation of the collision nodes
-    - handling the keyboard and mouse input for avatar movement
-    - moving the avatar
+
+- creation of the collision nodes
+- handling the keyboard and mouse input for avatar movement
+- moving the avatar
 
 
 it does not:
 it does not:
-    - play sounds
-    - play animations
 
 
-although it does send messeges that allow a listener to play sounds or
+- play sounds
+- play animations
+
+although it does send messages that allow a listener to play sounds or
 animations based on walker events.
 animations based on walker events.
 """
 """
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.directnotify.DirectNotifyGlobal import directNotify

+ 20 - 18
direct/src/controls/InputState.py

@@ -1,7 +1,6 @@
-
-
 from direct.directnotify import DirectNotifyGlobal
 from direct.directnotify import DirectNotifyGlobal
 from direct.showbase import DirectObject
 from direct.showbase import DirectObject
+from direct.showbase.PythonUtil import SerialNumGen
 
 
 # internal class, don't create these on your own
 # internal class, don't create these on your own
 class InputStateToken:
 class InputStateToken:
@@ -136,14 +135,16 @@ class InputState(DirectObject.DirectObject):
 
 
     def watch(self, name, eventOn, eventOff, startState=False, inputSource=None):
     def watch(self, name, eventOn, eventOff, startState=False, inputSource=None):
         """
         """
-        This returns a token; hold onto the token and call token.release() when you
-        no longer want to watch for these events.
-
-        # set up
-        token = inputState.watch('forward', 'w', 'w-up', inputSource=inputState.WASD)
-         ...
-        # tear down
-        token.release()
+        This returns a token; hold onto the token and call token.release() when
+        you no longer want to watch for these events.
+
+        Example::
+
+            # set up
+            token = inputState.watch('forward', 'w', 'w-up', inputSource=inputState.WASD)
+            ...
+            # tear down
+            token.release()
         """
         """
         assert self.debugPrint(
         assert self.debugPrint(
             "watch(name=%s, eventOn=%s, eventOff=%s, startState=%s)"%(
             "watch(name=%s, eventOn=%s, eventOff=%s, startState=%s)"%(
@@ -192,15 +193,16 @@ class InputState(DirectObject.DirectObject):
         """
         """
         Force isSet(name) to return 'value'.
         Force isSet(name) to return 'value'.
 
 
-        This returns a token; hold onto the token and call token.release() when you
-        no longer want to force the state.
+        This returns a token; hold onto the token and call token.release() when
+        you no longer want to force the state.
+
+        Example::
 
 
-        example:
-        # set up
-        token=inputState.force('forward', True, inputSource='myForwardForcer')
-         ...
-        # tear down
-        token.release()
+            # set up
+            token = inputState.force('forward', True, inputSource='myForwardForcer')
+            ...
+            # tear down
+            token.release()
         """
         """
         token = InputStateForceToken(self)
         token = InputStateForceToken(self)
         self._token2forceInfo[token] = (name, inputSource)
         self._token2forceInfo[token] = (name, inputSource)

+ 8 - 6
direct/src/controls/NonPhysicsWalker.py

@@ -2,15 +2,17 @@
 NonPhysicsWalker.py is for avatars.
 NonPhysicsWalker.py is for avatars.
 
 
 A walker control such as this one provides:
 A walker control such as this one provides:
-    - creation of the collision nodes
-    - handling the keyboard and mouse input for avatar movement
-    - moving the avatar
+
+- creation of the collision nodes
+- handling the keyboard and mouse input for avatar movement
+- moving the avatar
 
 
 it does not:
 it does not:
-    - play sounds
-    - play animations
 
 
-although it does send messeges that allow a listener to play sounds or
+- play sounds
+- play animations
+
+although it does send messages that allow a listener to play sounds or
 animations based on walker events.
 animations based on walker events.
 """
 """
 
 

+ 8 - 6
direct/src/controls/ObserverWalker.py

@@ -2,15 +2,17 @@
 ObserverWalker.py is for avatars.
 ObserverWalker.py is for avatars.
 
 
 A walker control such as this one provides:
 A walker control such as this one provides:
-    - creation of the collision nodes
-    - handling the keyboard and mouse input for avatar movement
-    - moving the avatar
+
+- creation of the collision nodes
+- handling the keyboard and mouse input for avatar movement
+- moving the avatar
 
 
 it does not:
 it does not:
-    - play sounds
-    - play animations
 
 
-although it does send messeges that allow a listener to play sounds or
+- play sounds
+- play animations
+
+although it does send messages that allow a listener to play sounds or
 animations based on walker events.
 animations based on walker events.
 """
 """
 
 

+ 8 - 6
direct/src/controls/PhysicsWalker.py

@@ -2,15 +2,17 @@
 PhysicsWalker.py is for avatars.
 PhysicsWalker.py is for avatars.
 
 
 A walker control such as this one provides:
 A walker control such as this one provides:
-    - creation of the collision nodes
-    - handling the keyboard and mouse input for avatar movement
-    - moving the avatar
+
+- creation of the collision nodes
+- handling the keyboard and mouse input for avatar movement
+- moving the avatar
 
 
 it does not:
 it does not:
-    - play sounds
-    - play animations
 
 
-although it does send messeges that allow a listener to play sounds or
+- play sounds
+- play animations
+
+although it does send messages that allow a listener to play sounds or
 animations based on walker events.
 animations based on walker events.
 """
 """
 
 

+ 1 - 1
direct/src/controls/TwoDWalker.py

@@ -1,5 +1,5 @@
 """
 """
-TwoDWalker.py is for controling the avatars in a 2D Scroller game environment.
+TwoDWalker.py is for controlling the avatars in a 2D scroller game environment.
 """
 """
 
 
 from .GravityWalker import *
 from .GravityWalker import *

+ 1 - 1
direct/src/directdevices/DirectDeviceManager.py

@@ -1,4 +1,4 @@
-""" Class used to create and control vrpn devices """
+"""Class used to create and control VRPN devices."""
 
 
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.DirectObject import DirectObject
 from panda3d.core import *
 from panda3d.core import *

+ 2 - 2
direct/src/directdevices/DirectFastrak.py

@@ -20,9 +20,9 @@ class DirectFastrak(DirectObject):
     fastrakCount = 0
     fastrakCount = 0
     notify = DirectNotifyGlobal.directNotify.newCategory('DirectFastrak')
     notify = DirectNotifyGlobal.directNotify.newCategory('DirectFastrak')
 
 
-    def __init__(self, device = 'Tracker0', nodePath = base.direct.camera):
+    def __init__(self, device = 'Tracker0', nodePath = None):
         # See if device manager has been initialized
         # See if device manager has been initialized
-        if base.direct.deviceManager == None:
+        if base.direct.deviceManager is None:
             base.direct.deviceManager = DirectDeviceManager()
             base.direct.deviceManager = DirectDeviceManager()
 
 
         # Set name
         # Set name

+ 5 - 1
direct/src/directnotify/DirectNotifyGlobal.py

@@ -1,8 +1,12 @@
-"""instantiate global DirectNotify used in Direct"""
+"""Instantiates global DirectNotify used in Direct."""
 
 
 __all__ = ['directNotify', 'giveNotify']
 __all__ = ['directNotify', 'giveNotify']
 
 
 from . import DirectNotify
 from . import DirectNotify
 
 
+#: The global :class:`~.DirectNotify.DirectNotify` object.
 directNotify = DirectNotify.DirectNotify()
 directNotify = DirectNotify.DirectNotify()
+
+#: Shorthand function for adding a DirectNotify category to a given class
+#: object.  Alias of `.DirectNotify.DirectNotify.giveNotify`.
 giveNotify = directNotify.giveNotify
 giveNotify = directNotify.giveNotify

+ 2 - 1
direct/src/directnotify/LoggerGlobal.py

@@ -1,5 +1,6 @@
-"""instantiate global Logger object"""
+"""Instantiates a global :class:`~.Logger.Logger` object."""
 
 
 from . import Logger
 from . import Logger
 
 
+#: Contains a global :class:`~.Logger.Logger` object.
 defaultLogger = Logger.Logger()
 defaultLogger = Logger.Logger()

+ 6 - 6
direct/src/directnotify/Notifier.py

@@ -8,6 +8,7 @@ from panda3d.core import ConfigVariableBool, NotifyCategory, StreamWriter, Notif
 import time
 import time
 import sys
 import sys
 
 
+
 class Notifier:
 class Notifier:
     serverDelta = 0
     serverDelta = 0
 
 
@@ -23,12 +24,11 @@ class Notifier:
 
 
     def __init__(self, name, logger=None):
     def __init__(self, name, logger=None):
         """
         """
-        name is a string
-        logger is a Logger
-
-        Create a new instance of the Notifier class with a given name
-        and an optional Logger class for piping output to. If no logger
-        specified, use the global default
+        Parameters:
+            name (str): a string name given to this Notifier instance.
+            logger (Logger, optional): an optional Logger object for
+                piping output to.  If none is specified, the global
+                :data:`~.LoggerGlobal.defaultLogger` is used.
         """
         """
         self.__name = name
         self.__name = name
 
 

+ 15 - 15
direct/src/directnotify/RotatingLog.py

@@ -1,8 +1,7 @@
-
-
 import os
 import os
 import time
 import time
 
 
+
 class RotatingLog:
 class RotatingLog:
     """
     """
     A file() (or open()) replacement that will automatically open and write
     A file() (or open()) replacement that will automatically open and write
@@ -11,22 +10,23 @@ class RotatingLog:
 
 
     def __init__(self, path="./log_file", hourInterval=24, megabyteLimit=1024):
     def __init__(self, path="./log_file", hourInterval=24, megabyteLimit=1024):
         """
         """
-        path is a full or partial path with file name.
-        hourInterval is the number of hours at which to rotate the file.
-        megabyteLimit is the number of megabytes of file size the log
-            may grow to, after which the log is rotated.  Note: The log
-            file may get a bit larger than limit do to writing out whole
-            lines (last line may exceed megabyteLimit or "megabyteGuidline").
+        Args:
+            path: a full or partial path with file name.
+            hourInterval: the number of hours at which to rotate the file.
+            megabyteLimit: the number of megabytes of file size the log may
+                grow to, after which the log is rotated.  Note: The log file
+                may get a bit larger than limit do to writing out whole lines
+                (last line may exceed megabyteLimit or "megabyteGuidline").
         """
         """
-        self.path=path
-        self.timeInterval=None
-        self.timeLimit=None
-        self.sizeLimit=None
+        self.path = path
+        self.timeInterval = None
+        self.timeLimit = None
+        self.sizeLimit = None
         if hourInterval is not None:
         if hourInterval is not None:
-            self.timeInterval=hourInterval*60*60
-            self.timeLimit=time.time()+self.timeInterval
+            self.timeInterval = hourInterval*60*60
+            self.timeLimit = time.time()+self.timeInterval
         if megabyteLimit is not None:
         if megabyteLimit is not None:
-            self.sizeLimit=megabyteLimit*1024*1024
+            self.sizeLimit = megabyteLimit*1024*1024
 
 
     def __del__(self):
     def __del__(self):
         self.close()
         self.close()

+ 38 - 32
direct/src/directutil/Verify.py

@@ -1,27 +1,31 @@
 """
 """
-You can use verify() just like assert, with these small differences:
-    - you may need to "import Verify", if someone hasn't done it
-      for you.
-    - unlike assert where using parenthises are optional, verify()
-      requires them.
-      e.g.:
-        assert foo  # OK
-        verify foo  # Error
-        assert foo  # Not Recomended (may be interpreted as a tuple)
-        verify(foo) # OK
-    - verify() will print something like the following before raising
-      an exception:
-        verify failed:
-            File "direct/src/showbase/ShowBase.py", line 60
-    - verify() will optionally start pdb for you (this is currently
-      false by default).  You can either edit Verify.py to set
-      wantVerifyPdb = 1 or if you are using ShowBase you can set
-      want-verify-pdb 1 in your Configrc to start pdb automatically.
-    - verify() will still function in the release build.  It will
-      not be removed by -O like assert will.
-
-verify() will also throw an AssertionError, but you can ignore that if you
-like (I don't suggest trying to catch it, it's just doing it so that it can
+You can use :func:`verify()` just like assert, with these small differences:
+
+- you may need to ``import Verify``, if someone hasn't done it for you.
+
+- unlike assert where using parentheses are optional, :func:`verify()`
+  requires them, e.g.::
+
+    assert foo  # OK
+    verify foo  # Error
+    assert foo  # Not Recomended (may be interpreted as a tuple)
+    verify(foo) # OK
+
+- :func:`verify()` will print something like this before raising an exception::
+
+    verify failed:
+        File "direct/src/showbase/ShowBase.py", line 60
+
+- :func:`verify()` will optionally start pdb for you (this is currently false
+  by default).  You can either edit Verify.py to set ``wantVerifyPdb = 1`` or
+  if you are using ShowBase you can set ``want-verify-pdb 1`` in your
+  Config.prc file to start pdb automatically.
+
+- :func:`verify()` will still function in the release build.  It will not be
+  removed by -O like assert will.
+
+:func:`verify()` will also throw an AssertionError, but you can ignore that if
+you like (I don't suggest trying to catch it, it's just doing it so that it can
 replace assert more fully).
 replace assert more fully).
 
 
 Please do not use assert for things that you want run on release builds.
 Please do not use assert for things that you want run on release builds.
@@ -31,19 +35,20 @@ an exception can get it mistaken for an error handler.  If your code
 needs to handle an error or throw an exception, you should do that
 needs to handle an error or throw an exception, you should do that
 (and not just assert for it).
 (and not just assert for it).
 
 
-If you want to be a super keen software engineer then avoid using verify().
-If you want to be, or already are, a super keen software engineer, but
-you don't always have the time to write proper error handling, go ahead
-and use verify() -- that's what it's for.
+If you want to be a super keen software engineer then avoid using
+:func:`verify()`.  If you want to be, or already are, a super keen software
+engineer, but you don't always have the time to write proper error handling,
+go ahead and use :func:`verify()` -- that's what it's for.
 
 
-Please use assert (properly) and do proper error handling; and use verify()
-only when debugging (i.e. when it won't be checked-in) or where it helps
-you resist using assert for error handling.
+Please use assert (properly) and do proper error handling; and use
+:func:`verify()` only when debugging (i.e. when it won't be checked-in) or
+where it helps you resist using assert for error handling.
 """
 """
 
 
 from panda3d.core import ConfigVariableBool
 from panda3d.core import ConfigVariableBool
 
 
-wantVerifyPdb = ConfigVariableBool('want-verify-pdb', False) # Set to true to load pdb on failure.
+# Set to true to load pdb on failure.
+wantVerifyPdb = ConfigVariableBool('want-verify-pdb', False)
 
 
 
 
 def verify(assertion):
 def verify(assertion):
@@ -54,7 +59,7 @@ def verify(assertion):
     if not assertion:
     if not assertion:
         print("\n\nverify failed:")
         print("\n\nverify failed:")
         import sys
         import sys
-        print("    File \"%s\", line %d"%(
+        print("    File \"%s\", line %d" % (
                 sys._getframe(1).f_code.co_filename,
                 sys._getframe(1).f_code.co_filename,
                 sys._getframe(1).f_lineno))
                 sys._getframe(1).f_lineno))
         if wantVerifyPdb:
         if wantVerifyPdb:
@@ -62,5 +67,6 @@ def verify(assertion):
             pdb.set_trace()
             pdb.set_trace()
         raise AssertionError
         raise AssertionError
 
 
+
 if not hasattr(__builtins__, "verify"):
 if not hasattr(__builtins__, "verify"):
     __builtins__["verify"] = verify
     __builtins__["verify"] = verify

+ 4 - 0
direct/src/dist/__init__.py

@@ -0,0 +1,4 @@
+"""This package contains tools to help with distributing Panda3D
+applications.  See the :ref:`distribution` section in the programming
+manual for further details.
+"""

+ 6 - 0
direct/src/dist/commands.py

@@ -1,3 +1,9 @@
+"""Extends setuptools with the ``build_apps`` and ``bdist_apps`` commands.
+
+See the :ref:`distribution` section of the programming manual for information
+on how to use these commands.
+"""
+
 from __future__ import print_function
 from __future__ import print_function
 
 
 import collections
 import collections

+ 1 - 1
direct/src/distributed/AsyncRequest.py

@@ -15,7 +15,7 @@ if __debug__:
 
 
 class AsyncRequest(DirectObject):
 class AsyncRequest(DirectObject):
     """
     """
-    This class is used to make asynchronos reads and creates to a database.
+    This class is used to make asynchronous reads and creates to a database.
 
 
     You can create a list of self.neededObjects and then ask for each to be
     You can create a list of self.neededObjects and then ask for each to be
     read or created, or if you only have one object that you need you can
     read or created, or if you only have one object that you need you can

+ 2 - 1
direct/src/distributed/DistributedObject.py

@@ -26,11 +26,12 @@ ESNum2Str = {
     ESGenerated: 'ESGenerated',
     ESGenerated: 'ESGenerated',
     }
     }
 
 
+
 class DistributedObject(DistributedObjectBase):
 class DistributedObject(DistributedObjectBase):
     """
     """
     The Distributed Object class is the base class for all network based
     The Distributed Object class is the base class for all network based
     (i.e. distributed) objects.  These will usually (always?) have a
     (i.e. distributed) objects.  These will usually (always?) have a
-    dclass entry in a *.dc file.
+    dclass entry in a \\*.dc file.
     """
     """
     notify = directNotify.newCategory("DistributedObject")
     notify = directNotify.newCategory("DistributedObject")
 
 

+ 2 - 1
direct/src/distributed/DistributedObjectBase.py

@@ -1,11 +1,12 @@
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.DirectObject import DirectObject
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.directnotify.DirectNotifyGlobal import directNotify
 
 
+
 class DistributedObjectBase(DirectObject):
 class DistributedObjectBase(DirectObject):
     """
     """
     The Distributed Object class is the base class for all network based
     The Distributed Object class is the base class for all network based
     (i.e. distributed) objects.  These will usually (always?) have a
     (i.e. distributed) objects.  These will usually (always?) have a
-    dclass entry in a *.dc file.
+    dclass entry in a \\*.dc file.
     """
     """
     notify = directNotify.newCategory("DistributedObjectBase")
     notify = directNotify.newCategory("DistributedObjectBase")
 
 

+ 9 - 8
direct/src/distributed/DoCollectionManager.py

@@ -5,6 +5,7 @@ import re
 BAD_DO_ID = BAD_ZONE_ID = 0 # 0xFFFFFFFF
 BAD_DO_ID = BAD_ZONE_ID = 0 # 0xFFFFFFFF
 BAD_CHANNEL_ID = 0 # 0xFFFFFFFFFFFFFFFF
 BAD_CHANNEL_ID = 0 # 0xFFFFFFFFFFFFFFFF
 
 
+
 class DoCollectionManager:
 class DoCollectionManager:
     def __init__(self):
     def __init__(self):
         # Dict of {DistributedObject ids: DistributedObjects}
         # Dict of {DistributedObject ids: DistributedObjects}
@@ -186,7 +187,6 @@ class DoCollectionManager:
             strToReturn = '%s%s' % (strToReturn, self._returnObjects(self.getDoTable(ownerView=False)))
             strToReturn = '%s%s' % (strToReturn, self._returnObjects(self.getDoTable(ownerView=False)))
         return strToReturn
         return strToReturn
 
 
-
     def printObjectCount(self):
     def printObjectCount(self):
         # print object counts by distributed object type
         # print object counts by distributed object type
         print('==== OBJECT COUNT ====')
         print('==== OBJECT COUNT ====')
@@ -199,13 +199,14 @@ class DoCollectionManager:
 
 
     def getDoList(self, parentId, zoneId=None, classType=None):
     def getDoList(self, parentId, zoneId=None, classType=None):
         """
         """
-        parentId is any distributed object id.
-        zoneId is a uint32, defaults to None (all zones).  Try zone 2 if
-            you're not sure which zone to use (0 is a bad/null zone and
-            1 has had reserved use in the past as a no messages zone, while
-            2 has traditionally been a global, uber, misc stuff zone).
-        dclassType is a distributed class type filter, defaults
-            to None (no filter).
+        Args:
+            parentId: any distributed object id.
+            zoneId: a uint32, defaults to None (all zones).  Try zone 2 if
+                you're not sure which zone to use (0 is a bad/null zone and
+                1 has had reserved use in the past as a no messages zone, while
+                2 has traditionally been a global, uber, misc stuff zone).
+            dclassType: a distributed class type filter, defaults to None
+                (no filter).
 
 
         If dclassName is None then all objects in the zone are returned;
         If dclassName is None then all objects in the zone are returned;
         otherwise the list is filtered to only include objects of that type.
         otherwise the list is filtered to only include objects of that type.

+ 8 - 9
direct/src/distributed/DoHierarchy.py

@@ -26,15 +26,14 @@ class DoHierarchy:
 
 
     def getDoIds(self, getDo, parentId, zoneId=None, classType=None):
     def getDoIds(self, getDo, parentId, zoneId=None, classType=None):
         """
         """
-        Moved from DoCollectionManager
-        ==============================
-        parentId is any distributed object id.
-        zoneId is a uint32, defaults to None (all zones).  Try zone 2 if
-            you're not sure which zone to use (0 is a bad/null zone and
-            1 has had reserved use in the past as a no messages zone, while
-            2 has traditionally been a global, uber, misc stuff zone).
-        dclassType is a distributed class type filter, defaults
-            to None (no filter).
+        Args:
+            parentId: any distributed object id.
+            zoneId: a uint32, defaults to None (all zones).  Try zone 2 if
+                you're not sure which zone to use (0 is a bad/null zone and
+                1 has had reserved use in the past as a no messages zone, while
+                2 has traditionally been a global, uber, misc stuff zone).
+            dclassType: a distributed class type filter, defaults to None
+                (no filter).
 
 
         If dclassName is None then all objects in the zone are returned;
         If dclassName is None then all objects in the zone are returned;
         otherwise the list is filtered to only include objects of that type.
         otherwise the list is filtered to only include objects of that type.

+ 1 - 4
direct/src/distributed/PyDatagram.py

@@ -9,6 +9,7 @@ from panda3d.direct import *
 
 
 from direct.distributed.MsgTypes import *
 from direct.distributed.MsgTypes import *
 
 
+
 class PyDatagram(Datagram):
 class PyDatagram(Datagram):
 
 
     # This is a little helper Dict to replace the huge <if> statement
     # This is a little helper Dict to replace the huge <if> statement
@@ -29,8 +30,6 @@ class PyDatagram(Datagram):
         STBlob32: (Datagram.addBlob32, None),
         STBlob32: (Datagram.addBlob32, None),
         }
         }
 
 
-    #def addChannel(self, channelId):
-    #    ...
     addChannel = Datagram.addUint64
     addChannel = Datagram.addUint64
 
 
     def addServerHeader(self, channel, sender, code):
     def addServerHeader(self, channel, sender, code):
@@ -39,14 +38,12 @@ class PyDatagram(Datagram):
         self.addChannel(sender)
         self.addChannel(sender)
         self.addUint16(code)
         self.addUint16(code)
 
 
-
     def addOldServerHeader(self, channel, sender, code):
     def addOldServerHeader(self, channel, sender, code):
         self.addChannel(channel)
         self.addChannel(channel)
         self.addChannel(sender)
         self.addChannel(sender)
         self.addChannel('A')
         self.addChannel('A')
         self.addUint16(code)
         self.addUint16(code)
 
 
-
     def addServerControlHeader(self, code):
     def addServerControlHeader(self, code):
         self.addInt8(1)
         self.addInt8(1)
         self.addChannel(CONTROL_CHANNEL)
         self.addChannel(CONTROL_CHANNEL)

+ 1 - 0
direct/src/distributed/PyDatagramIterator.py

@@ -7,6 +7,7 @@ from panda3d.core import *
 from panda3d.direct import *
 from panda3d.direct import *
 # Import the type numbers
 # Import the type numbers
 
 
+
 class PyDatagramIterator(DatagramIterator):
 class PyDatagramIterator(DatagramIterator):
 
 
     # This is a little helper Dict to replace the huge <if> statement
     # This is a little helper Dict to replace the huge <if> statement

+ 1 - 1
direct/src/distributed/ServerRepository.py

@@ -137,7 +137,7 @@ class ServerRepository:
 
 
         # An allocator object that assigns the next doIdBase to each
         # An allocator object that assigns the next doIdBase to each
         # client.
         # client.
-        self.idAllocator = UniqueIdAllocator(0, 0xffffffff / self.doIdRange)
+        self.idAllocator = UniqueIdAllocator(0, 0xffffffff // self.doIdRange)
 
 
         self.dcFile = DCFile()
         self.dcFile = DCFile()
         self.dcSuffix = ''
         self.dcSuffix = ''

+ 13 - 12
direct/src/fsm/ClassicFSM.py

@@ -1,9 +1,8 @@
 """Finite State Machine module: contains the ClassicFSM class.
 """Finite State Machine module: contains the ClassicFSM class.
 
 
-.. note::
-
-   This module and class exist only for backward compatibility with
-   existing code.  New code should use the :mod:`.FSM` module instead.
+Note:
+    This module and class exist only for backward compatibility with
+    existing code.  New code should use the :mod:`.FSM` module instead.
 """
 """
 
 
 __all__ = ['ClassicFSM']
 __all__ = ['ClassicFSM']
@@ -14,12 +13,14 @@ import weakref
 
 
 if __debug__:
 if __debug__:
     _debugFsms = {}
     _debugFsms = {}
+
     def printDebugFsmList():
     def printDebugFsmList():
         global _debugFsms
         global _debugFsms
         for k in sorted(_debugFsms.keys()):
         for k in sorted(_debugFsms.keys()):
             print("%s %s" % (k, _debugFsms[k]()))
             print("%s %s" % (k, _debugFsms[k]()))
     __builtins__['debugFsmList'] = printDebugFsmList
     __builtins__['debugFsmList'] = printDebugFsmList
 
 
+
 class ClassicFSM(DirectObject):
 class ClassicFSM(DirectObject):
     """
     """
     Finite State Machine class.
     Finite State Machine class.
@@ -45,14 +46,14 @@ class ClassicFSM(DirectObject):
         """__init__(self, string, State[], string, string, int)
         """__init__(self, string, State[], string, string, int)
 
 
         ClassicFSM constructor: takes name, list of states, initial state and
         ClassicFSM constructor: takes name, list of states, initial state and
-        final state as:
-
-        fsm = ClassicFSM.ClassicFSM('stopLight',
-          [State.State('red', enterRed, exitRed, ['green']),
-            State.State('yellow', enterYellow, exitYellow, ['red']),
-            State.State('green', enterGreen, exitGreen, ['yellow'])],
-          'red',
-          'red')
+        final state as::
+
+            fsm = ClassicFSM.ClassicFSM('stopLight',
+              [State.State('red', enterRed, exitRed, ['green']),
+                State.State('yellow', enterYellow, exitYellow, ['red']),
+                State.State('green', enterGreen, exitGreen, ['yellow'])],
+              'red',
+              'red')
 
 
         each state's last argument, a list of allowed state transitions,
         each state's last argument, a list of allowed state transitions,
         is optional; if left out (or explicitly specified to be
         is optional; if left out (or explicitly specified to be

+ 32 - 26
direct/src/fsm/FSM.py

@@ -1,5 +1,8 @@
 """The new Finite State Machine module. This replaces the module
 """The new Finite State Machine module. This replaces the module
 previously called FSM (now called :mod:`.ClassicFSM`).
 previously called FSM (now called :mod:`.ClassicFSM`).
+
+For more information on FSMs, consult the :ref:`finite-state-machines` section
+of the programming manual.
 """
 """
 
 
 __all__ = ['FSMException', 'FSM']
 __all__ = ['FSMException', 'FSM']
@@ -14,12 +17,15 @@ from direct.stdpy.threading import RLock
 class FSMException(Exception):
 class FSMException(Exception):
     pass
     pass
 
 
+
 class AlreadyInTransition(FSMException):
 class AlreadyInTransition(FSMException):
     pass
     pass
 
 
+
 class RequestDenied(FSMException):
 class RequestDenied(FSMException):
     pass
     pass
 
 
+
 class FSM(DirectObject):
 class FSM(DirectObject):
     """
     """
     A Finite State Machine.  This is intended to be the base class
     A Finite State Machine.  This is intended to be the base class
@@ -34,25 +40,25 @@ class FSM(DirectObject):
 
 
     To define specialized behavior when entering or exiting a
     To define specialized behavior when entering or exiting a
     particular state, define a method named enterState() and/or
     particular state, define a method named enterState() and/or
-    exitState(), where "State" is the name of the state, e.g.:
+    exitState(), where "State" is the name of the state, e.g.::
 
 
-    def enterRed(self):
-        ... do stuff ...
+        def enterRed(self):
+            ... do stuff ...
 
 
-    def exitRed(self):
-        ... cleanup stuff ...
+        def exitRed(self):
+            ... cleanup stuff ...
 
 
-    def enterYellow(self):
-        ... do stuff ...
+        def enterYellow(self):
+            ... do stuff ...
 
 
-    def exitYellow(self):
-        ... cleanup stuff ...
+        def exitYellow(self):
+            ... cleanup stuff ...
 
 
-    def enterGreen(self):
-        ... do stuff ...
+        def enterGreen(self):
+            ... do stuff ...
 
 
-    def exitGreen(self):
-        ... cleanup stuff ...
+        def exitGreen(self):
+            ... cleanup stuff ...
 
 
     Both functions can access the previous state name as
     Both functions can access the previous state name as
     self.oldState, and the new state name we are transitioning to as
     self.oldState, and the new state name we are transitioning to as
@@ -70,22 +76,22 @@ class FSM(DirectObject):
     input is always a string and a tuple of optional parameters (which
     input is always a string and a tuple of optional parameters (which
     is often empty), and the return value should either be None to do
     is often empty), and the return value should either be None to do
     nothing, or the name of the state to transition into.  For
     nothing, or the name of the state to transition into.  For
-    example:
+    example::
 
 
-    def filterRed(self, request, args):
-        if request in ['Green']:
-            return (request,) + args
-        return None
+        def filterRed(self, request, args):
+            if request in ['Green']:
+                return (request,) + args
+            return None
 
 
-    def filterYellow(self, request, args):
-        if request in ['Red']:
-            return (request,) + args
-        return None
+        def filterYellow(self, request, args):
+            if request in ['Red']:
+                return (request,) + args
+            return None
 
 
-    def filterGreen(self, request, args):
-        if request in ['Yellow']:
-            return (request,) + args
-        return None
+        def filterGreen(self, request, args):
+            if request in ['Yellow']:
+                return (request,) + args
+            return None
 
 
     As above, the filterState() functions are optional.  If any is
     As above, the filterState() functions are optional.  If any is
     omitted, the defaultFilter() method is called instead.  A standard
     omitted, the defaultFilter() method is called instead.  A standard

+ 3 - 6
direct/src/fsm/FourState.py

@@ -44,25 +44,22 @@ class FourState:
 
 
     def __init__(self, names, durations = [0, 1, None, 1, 1]):
     def __init__(self, names, durations = [0, 1, None, 1, 1]):
         """
         """
-        names is a list of state names
+        Names is a list of state names.  Some examples are::
 
 
-        E.g.
             ['off', 'opening', 'open', 'closing', 'closed',]
             ['off', 'opening', 'open', 'closing', 'closed',]
 
 
-        e.g. 2:
             ['off', 'locking', 'locked', 'unlocking', 'unlocked',]
             ['off', 'locking', 'locked', 'unlocking', 'unlocked',]
 
 
-        e.g. 3:
             ['off', 'deactivating', 'deactive', 'activating', 'activated',]
             ['off', 'deactivating', 'deactive', 'activating', 'activated',]
 
 
         durations is a list of time values (floats) or None values.
         durations is a list of time values (floats) or None values.
 
 
         Each list must have five entries.
         Each list must have five entries.
 
 
-        More Details
+        .. rubric:: More Details
 
 
         Here is a diagram showing the where the names from the list
         Here is a diagram showing the where the names from the list
-        are used:
+        are used::
 
 
             +---------+
             +---------+
             | 0 (off) |----> (any other state and vice versa).
             | 0 (off) |----> (any other state and vice versa).

+ 12 - 14
direct/src/fsm/FourStateAI.py

@@ -45,27 +45,25 @@ class FourStateAI:
 
 
     def __init__(self, names, durations = [0, 1, None, 1, 1]):
     def __init__(self, names, durations = [0, 1, None, 1, 1]):
         """
         """
-        names is a list of state names
-            E.g.
-                ['off', 'opening', 'open', 'closing', 'closed',]
+        Names is a list of state names.  Some examples are::
 
 
-            e.g. 2:
-                ['off', 'locking', 'locked', 'unlocking', 'unlocked',]
+            ['off', 'opening', 'open', 'closing', 'closed',]
 
 
-            e.g. 3:
-                ['off', 'deactivating', 'deactive', 'activating', 'activated',]
+            ['off', 'locking', 'locked', 'unlocking', 'unlocked',]
+
+            ['off', 'deactivating', 'deactive', 'activating', 'activated',]
 
 
         durations is a list of durations in seconds or None values.
         durations is a list of durations in seconds or None values.
-            The list of duration values should be the same length
-            as the list of state names and the lists correspond.
-            For each state, after n seconds, the ClassicFSM will move to
-            the next state.  That does not happen for any duration
-            values of None.
+        The list of duration values should be the same length
+        as the list of state names and the lists correspond.
+        For each state, after n seconds, the ClassicFSM will move to
+        the next state.  That does not happen for any duration
+        values of None.
 
 
-        More Details
+        .. rubric:: More Details
 
 
         Here is a diagram showing the where the names from the list
         Here is a diagram showing the where the names from the list
-        are used:
+        are used::
 
 
             +---------+
             +---------+
             | 0 (off) |----> (any other state and vice versa).
             | 0 (off) |----> (any other state and vice versa).

+ 3 - 0
direct/src/fsm/__init__.py

@@ -3,4 +3,7 @@ This package contains implementations of a Finite State Machine, an
 abstract construct that holds a particular state and can transition
 abstract construct that holds a particular state and can transition
 between several defined states.  These are useful for a range of logic
 between several defined states.  These are useful for a range of logic
 programming tasks.
 programming tasks.
+
+For more information on FSMs, consult the :ref:`finite-state-machines` section
+of the programming manual.
 """
 """

+ 43 - 43
direct/src/gui/DirectDialog.py

@@ -1,6 +1,9 @@
 """This module defines various dialog windows for the DirectGUI system."""
 """This module defines various dialog windows for the DirectGUI system."""
 
 
-__all__ = ['findDialog', 'cleanupDialog', 'DirectDialog', 'OkDialog', 'OkCancelDialog', 'YesNoDialog', 'YesNoCancelDialog', 'RetryCancelDialog']
+__all__ = [
+    'findDialog', 'cleanupDialog', 'DirectDialog', 'OkDialog',
+    'OkCancelDialog', 'YesNoDialog', 'YesNoCancelDialog', 'RetryCancelDialog',
+]
 
 
 from panda3d.core import *
 from panda3d.core import *
 from direct.showbase import ShowBaseGlobal
 from direct.showbase import ShowBaseGlobal
@@ -9,9 +12,9 @@ from .DirectFrame import *
 from .DirectButton import *
 from .DirectButton import *
 import types
 import types
 
 
-def findDialog(uniqueName):
-    """findPanel(string uniqueName)
 
 
+def findDialog(uniqueName):
+    """
     Returns the panel whose uniqueName is given.  This is mainly
     Returns the panel whose uniqueName is given.  This is mainly
     useful for debugging, to get a pointer to the current onscreen
     useful for debugging, to get a pointer to the current onscreen
     panel of a particular type.
     panel of a particular type.
@@ -20,6 +23,7 @@ def findDialog(uniqueName):
         return DirectDialog.AllDialogs[uniqueName]
         return DirectDialog.AllDialogs[uniqueName]
     return None
     return None
 
 
+
 def cleanupDialog(uniqueName):
 def cleanupDialog(uniqueName):
     """cleanupPanel(string uniqueName)
     """cleanupPanel(string uniqueName)
 
 
@@ -35,52 +39,48 @@ def cleanupDialog(uniqueName):
         # self.cleanup() directly
         # self.cleanup() directly
         DirectDialog.AllDialogs[uniqueName].cleanup()
         DirectDialog.AllDialogs[uniqueName].cleanup()
 
 
+
 class DirectDialog(DirectFrame):
 class DirectDialog(DirectFrame):
 
 
     AllDialogs = {}
     AllDialogs = {}
     PanelIndex = 0
     PanelIndex = 0
 
 
-    def __init__(self, parent = None, **kw):
-        """
-        DirectDialog(kw)
-
-        Creates a popup dialog to alert and/or interact with user.
+    def __init__(self, parent=None, **kw):
+        """Creates a popup dialog to alert and/or interact with user.
         Some of the main keywords that can be used to customize the dialog:
         Some of the main keywords that can be used to customize the dialog:
-            Keyword              Definition
-            -------              ----------
-            text                 Text message/query displayed to user
-            geom                 Geometry to be displayed in dialog
-            buttonTextList       List of text to show on each button
-            buttonGeomList       List of geometry to show on each button
-            buttonImageList      List of images to show on each button
-            buttonValueList      List of values sent to dialog command for
-                                 each button.  If value is [] then the
-                                 ordinal rank of the button is used as
-                                 its value
-            buttonHotKeyList     List of hotkeys to bind to each button.
-                                 Typing hotkey is equivalent to pressing
-                                 the corresponding button.
-            suppressKeys         Set to true if you wish to suppress keys
-                                 (i.e. Dialog eats key event), false if
-                                 you wish Dialog to pass along key event
-            buttonSize           4-tuple used to specify custom size for
-                                 each button (to make bigger then geom/text
-                                 for example)
-            pad                  Space between border and interior graphics
-            topPad               Extra space added above text/geom/image
-            midPad               Extra space added between text/buttons
-            sidePad              Extra space added to either side of
-                                 text/buttons
-            buttonPadSF          Scale factor used to expand/contract
-                                 button horizontal spacing
-            command              Callback command used when a button is
-                                 pressed.  Value supplied to command
-                                 depends on values in buttonValueList
-
-         Note: Number of buttons on the dialog depends upon the maximum
-               length of any button[Text|Geom|Image|Value]List specified.
-               Values of None are substituted for lists that are shorter
-               than the max length
+
+        Parameters:
+            text (str): Text message/query displayed to user
+            geom: Geometry to be displayed in dialog
+            buttonTextList: List of text to show on each button
+            buttonGeomList: List of geometry to show on each button
+            buttonImageList: List of images to show on each button
+            buttonValueList: List of values sent to dialog command for
+                each button.  If value is [] then the ordinal rank of
+                the button is used as its value.
+            buttonHotKeyList: List of hotkeys to bind to each button.
+                Typing the hotkey is equivalent to pressing the
+                corresponding button.
+            suppressKeys: Set to true if you wish to suppress keys
+                (i.e. Dialog eats key event), false if you wish Dialog
+                to pass along key event.
+            buttonSize: 4-tuple used to specify custom size for each
+                button (to make bigger then geom/text for example)
+            pad: Space between border and interior graphics
+            topPad: Extra space added above text/geom/image
+            midPad: Extra space added between text/buttons
+            sidePad: Extra space added to either side of text/buttons
+            buttonPadSF: Scale factor used to expand/contract button
+                horizontal spacing
+            command: Callback command used when a button is pressed.
+                Value supplied to command depends on values in
+                buttonValueList.
+
+        Note:
+            The number of buttons on the dialog depends on the maximum
+            length of any button[Text|Geom|Image|Value]List specified.
+            Values of None are substituted for lists that are shorter
+            than the max length
          """
          """
 
 
         # Inherits from DirectFrame
         # Inherits from DirectFrame

+ 1 - 1
direct/src/gui/DirectGui.py

@@ -1,4 +1,4 @@
-""" Imports all of the DirectGUI classes. """
+"""Imports all of the :ref:`directgui` classes."""
 
 
 from . import DirectGuiGlobals as DGG
 from . import DirectGuiGlobals as DGG
 from .OnscreenText import *
 from .OnscreenText import *

+ 44 - 36
direct/src/gui/DirectGuiBase.py

@@ -1,49 +1,57 @@
 """
 """
-Base class for all Direct Gui items.  Handles composite widgets and
+Base class for all DirectGui items.  Handles composite widgets and
 command line argument parsing.
 command line argument parsing.
 
 
-Code Overview:
+Code overview:
 
 
-1   Each widget defines a set of options (optiondefs) as a list of tuples
-    of the form ('name', defaultValue, handler).
+1)  Each widget defines a set of options (optiondefs) as a list of tuples
+    of the form ``('name', defaultValue, handler)``.
     'name' is the name of the option (used during construction of configure)
     'name' is the name of the option (used during construction of configure)
-    handler can be: None, method, or INITOPT.  If a method is specified,
-    it will be called during widget construction (via initialiseoptions),
-    if the Handler is specified as an INITOPT, this is an option that can
-    only be set during widget construction.
+    handler can be: None, method, or INITOPT.  If a method is specified, it
+    will be called during widget construction (via initialiseoptions), if the
+    Handler is specified as an INITOPT, this is an option that can only be set
+    during widget construction.
 
 
-2)  DirectGuiBase.defineoptions is called.  defineoption creates:
+2)  :func:`~DirectGuiBase.defineoptions` is called.  defineoption creates:
 
 
     self._constructorKeywords = { keyword: [value, useFlag] }
     self._constructorKeywords = { keyword: [value, useFlag] }
-    a dictionary of the keyword options specified as part of the constructor
-    keywords can be of the form 'component_option', where component is
-    the name of a widget's component, a component group or a component alias
-
-    self._dynamicGroups, a list of group names for which it is permissible
-    to specify options before components of that group are created.
-    If a widget is a derived class the order of execution would be:
-    foo.optiondefs = {}
-    foo.defineoptions()
-      fooParent()
-         fooParent.optiondefs = {}
-         fooParent.defineoptions()
-
-3)  addoptions is called.  This combines options specified as keywords to
-    the widget constructor (stored in self._constuctorKeywords)
-    with the default options (stored in optiondefs).  Results are stored in
-    self._optionInfo = { keyword: [default, current, handler] }
+        A dictionary of the keyword options specified as part of the
+        constructor keywords can be of the form 'component_option', where
+        component is the name of a widget's component, a component group or a
+        component alias.
+
+    self._dynamicGroups
+        A list of group names for which it is permissible to specify options
+        before components of that group are created.
+        If a widget is a derived class the order of execution would be::
+
+          foo.optiondefs = {}
+          foo.defineoptions()
+            fooParent()
+               fooParent.optiondefs = {}
+               fooParent.defineoptions()
+
+3)  :func:`~DirectGuiBase.addoptions` is called.  This combines options
+    specified as keywords to the widget constructor (stored in
+    self._constructorKeywords) with the default options (stored in optiondefs).
+    Results are stored in
+    ``self._optionInfo = { keyword: [default, current, handler] }``.
     If a keyword is of the form 'component_option' it is left in the
     If a keyword is of the form 'component_option' it is left in the
     self._constructorKeywords dictionary (for use by component constructors),
     self._constructorKeywords dictionary (for use by component constructors),
     otherwise it is 'used', and deleted from self._constructorKeywords.
     otherwise it is 'used', and deleted from self._constructorKeywords.
-    Notes: - constructor keywords override the defaults.
-           - derived class default values override parent class defaults
-           - derived class handler functions override parent class functions
+
+    Notes:
+
+    - constructor keywords override the defaults.
+    - derived class default values override parent class defaults
+    - derived class handler functions override parent class functions
 
 
 4)  Superclass initialization methods are called (resulting in nested calls
 4)  Superclass initialization methods are called (resulting in nested calls
     to define options (see 2 above)
     to define options (see 2 above)
 
 
-5)  Widget components are created via calls to self.createcomponent.
-    User can specify aliases and groups for each component created.
+5)  Widget components are created via calls to
+    :func:`~DirectGuiBase.createcomponent`.  User can specify aliases and groups
+    for each component created.
 
 
     Aliases are alternate names for components, e.g. a widget may have a
     Aliases are alternate names for components, e.g. a widget may have a
     component with a name 'entryField', which itself may have a component
     component with a name 'entryField', which itself may have a component
@@ -55,8 +63,8 @@ Code Overview:
     Groups allow option specifications that apply to all members of the group.
     Groups allow option specifications that apply to all members of the group.
     If a widget has components: 'text1', 'text2', and 'text3' which all belong
     If a widget has components: 'text1', 'text2', and 'text3' which all belong
     to the 'text' group, they can be all configured with keywords of the form:
     to the 'text' group, they can be all configured with keywords of the form:
-    'text_keyword' (e.g. text_font = 'comic.rgb').  A component's group
-    is stored as the fourth element of its entry in self.__componentInfo
+    'text_keyword' (e.g. ``text_font='comic.rgb'``).  A component's group
+    is stored as the fourth element of its entry in self.__componentInfo.
 
 
     Note: the widget constructors have access to all remaining keywords in
     Note: the widget constructors have access to all remaining keywords in
     _constructorKeywords (those not transferred to _optionInfo by
     _constructorKeywords (those not transferred to _optionInfo by
@@ -71,9 +79,9 @@ Code Overview:
     component.  If any constructor keywords remain at the end of component
     component.  If any constructor keywords remain at the end of component
     construction (and initialisation), an error is raised.
     construction (and initialisation), an error is raised.
 
 
-5)  initialiseoptions is called.  This method calls any option handlers to
-    respond to any keyword/default values, then checks to see if any keywords
-    are left unused.  If so, an error is raised.
+5)  :func:`~DirectGuiBase.initialiseoptions` is called.  This method calls any
+    option handlers to respond to any keyword/default values, then checks to
+    see if any keywords are left unused.  If so, an error is raised.
 """
 """
 
 
 __all__ = ['DirectGuiBase', 'DirectGuiWidget']
 __all__ = ['DirectGuiBase', 'DirectGuiWidget']

+ 3 - 6
direct/src/gui/DirectGuiGlobals.py

@@ -17,8 +17,9 @@ drawOrder = 100
 panel = None
 panel = None
 
 
 # USEFUL GUI CONSTANTS
 # USEFUL GUI CONSTANTS
-# Constant used to indicate that an option can only be set by a call
-# to the constructor.
+
+#: Constant used to indicate that an option can only be set by a call
+#: to the constructor.
 INITOPT = ['initopt']
 INITOPT = ['initopt']
 
 
 # Mouse buttons
 # Mouse buttons
@@ -158,7 +159,3 @@ def getDefaultPanel():
 def setDefaultPanel(newPanel):
 def setDefaultPanel(newPanel):
     global panel
     global panel
     panel = newPanel
     panel = newPanel
-
-#from OnscreenText import *
-#from OnscreenGeom import *
-#from OnscreenImage import *

+ 11 - 6
direct/src/gui/OnscreenImage.py

@@ -1,4 +1,8 @@
-"""OnscreenImage module: contains the OnscreenImage class"""
+"""OnscreenImage module: contains the OnscreenImage class.
+
+See the :ref:`onscreenimage` page in the programming manual for explanation of
+this class.
+"""
 
 
 __all__ = ['OnscreenImage']
 __all__ = ['OnscreenImage']
 
 
@@ -21,14 +25,15 @@ class OnscreenImage(DirectObject, NodePath):
                  parent = None,
                  parent = None,
                  sort = 0):
                  sort = 0):
         """
         """
-        Make a image node from string or a node path,
-        put it into the 2d sg and set it up with all the indicated parameters.
+        Make a image node from string or a `~panda3d.core.NodePath`, put
+        it into the 2-D scene graph and set it up with all the indicated
+        parameters.
 
 
-        The parameters are as follows:
+        Parameters:
 
 
           image: the actual geometry to display or a file name.
           image: the actual geometry to display or a file name.
-                This may be omitted and specified later via setImage()
-                if you don't have it available.
+                 This may be omitted and specified later via setImage()
+                 if you don't have it available.
 
 
           pos: the x, y, z position of the geometry on the screen.
           pos: the x, y, z position of the geometry on the screen.
                This maybe a 3-tuple of floats or a vector.
                This maybe a 3-tuple of floats or a vector.

+ 6 - 2
direct/src/gui/OnscreenText.py

@@ -1,4 +1,8 @@
-"""OnscreenText module: contains the OnscreenText class"""
+"""OnscreenText module: contains the OnscreenText class.
+
+See the :ref:`onscreentext` page in the programming manual for explanation of
+this class.
+"""
 
 
 __all__ = ['OnscreenText', 'Plain', 'ScreenTitle', 'ScreenPrompt', 'NameConfirm', 'BlackOnWhite']
 __all__ = ['OnscreenText', 'Plain', 'ScreenTitle', 'ScreenPrompt', 'NameConfirm', 'BlackOnWhite']
 
 
@@ -41,7 +45,7 @@ class OnscreenText(NodePath):
         Make a text node from string, put it into the 2d sg and set it
         Make a text node from string, put it into the 2d sg and set it
         up with all the indicated parameters.
         up with all the indicated parameters.
 
 
-        The parameters are as follows:
+        Parameters:
 
 
           text: the actual text to display.  This may be omitted and
           text: the actual text to display.  This may be omitted and
               specified later via setText() if you don't have it
               specified later via setText() if you don't have it

+ 1 - 1
direct/src/gui/__init__.py

@@ -1,5 +1,5 @@
 """
 """
-This package contains the DirectGui system, a set of classes
+This package contains the :ref:`directgui` system, a set of classes
 responsible for drawing graphical widgets to the 2-D scene graph.
 responsible for drawing graphical widgets to the 2-D scene graph.
 
 
 It is based on the lower-level PGui system, which is implemented in
 It is based on the lower-level PGui system, which is implemented in

+ 5 - 1
direct/src/interval/ActorInterval.py

@@ -1,4 +1,8 @@
-"""ActorInterval module: contains the ActorInterval class"""
+"""ActorInterval module: contains the ActorInterval class.
+
+See the :ref:`actor-intervals` page in the programming manual for explanation
+of this class.
+"""
 
 
 __all__ = ['ActorInterval', 'LerpAnimInterval']
 __all__ = ['ActorInterval', 'LerpAnimInterval']
 
 

+ 16 - 20
direct/src/interval/ParticleInterval.py

@@ -34,26 +34,22 @@ class ParticleInterval(Interval):
                  cleanup = False,
                  cleanup = False,
                  name = None):
                  name = None):
         """
         """
-        particleEffect is a ParticleEffect
-        parent is a NodePath: this is where the effect will be
-                              parented in the scenegraph
-        worldRelative is a boolean: this will override 'renderParent'
-                                    with render
-        renderParent is a NodePath: this is where the particles will
-                                    be rendered in the scenegraph
-        duration is a float: for the time
-        softStopT is a float: no effect if 0.0,
-                              a positive value will count from the
-                              start of the interval,
-                              a negative value will count from the
-                              end of the interval
-        cleanup is a boolean: if True the effect will be destroyed
-                              and removed from the scenegraph upon
-                              interval completion
-                              set to False if planning on reusing
-                              the interval
-        name is a string: use this for unique intervals so that
-                          they can be easily found in the taskMgr
+        Args:
+            particleEffect (ParticleEffect): a particle effect
+            parent (NodePath): this is where the effect will be parented in the
+                scene graph
+            worldRelative (bool): this will override 'renderParent' with render
+            renderParent (NodePath): this is where the particles will be
+                rendered in the scenegraph
+            duration (float): for the time
+            softStopT (float): no effect if 0.0, a positive value will count
+                from the start of the interval, a negative value will count
+                from the end of the interval
+            cleanup (boolean): if True the effect will be destroyed and removed
+                from the scenegraph upon interval completion.  Set to False if
+                planning on reusing the interval.
+            name (string): use this for unique intervals so that they can be
+                easily found in the taskMgr.
         """
         """
 
 
         # Generate unique name
         # Generate unique name

+ 2 - 0
direct/src/interval/__init__.py

@@ -9,4 +9,6 @@ All interval types can be conveniently imported from the
 :mod:`.IntervalGlobal` module::
 :mod:`.IntervalGlobal` module::
 
 
    from direct.interval.IntervalGlobal import *
    from direct.interval.IntervalGlobal import *
+
+For more information about intervals, see the :ref:`intervals` manual page.
 """
 """

+ 1 - 0
direct/src/leveleditor/ActionMgr.py

@@ -1,4 +1,5 @@
 from panda3d.core import *
 from panda3d.core import *
+from direct.showbase.PythonUtil import Functor
 from . import ObjectGlobals as OG
 from . import ObjectGlobals as OG
 
 
 class ActionMgr:
 class ActionMgr:

+ 7 - 1
direct/src/particles/Particles.py

@@ -1,3 +1,9 @@
+"""The Python specialization of the particle system.
+
+See the :ref:`particle-effects` section in the manual for an explanation
+of the particle system.
+"""
+
 from panda3d.core import *
 from panda3d.core import *
 
 
 from panda3d.physics import PhysicalNode
 from panda3d.physics import PhysicalNode
@@ -29,6 +35,7 @@ from . import SpriteParticleRendererExt
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.directnotify.DirectNotifyGlobal import directNotify
 import sys
 import sys
 
 
+
 class Particles(ParticleSystem):
 class Particles(ParticleSystem):
     notify = directNotify.newCategory('Particles')
     notify = directNotify.newCategory('Particles')
     id = 1
     id = 1
@@ -575,7 +582,6 @@ class Particles(ParticleSystem):
 
 
         return dict(zip(('min','median','max'),[l*s/b for l,s,b in zip(litterRange,lifespanRange,birthRateRange)]))
         return dict(zip(('min','median','max'),[l*s/b for l,s,b in zip(litterRange,lifespanRange,birthRateRange)]))
 
 
-
     def accelerate(self,time,stepCount = 1,stepTime=0.0):
     def accelerate(self,time,stepCount = 1,stepTime=0.0):
         if time > 0.0:
         if time > 0.0:
             if stepTime == 0.0:
             if stepTime == 0.0:

+ 3 - 0
direct/src/particles/__init__.py

@@ -4,4 +4,7 @@ system.
 
 
 Also see the :mod:`panda3d.physics` module, which contains the C++
 Also see the :mod:`panda3d.physics` module, which contains the C++
 implementation of the particle system.
 implementation of the particle system.
+
+For more information about the particle system, see the :ref:`particle-effects`
+page in the manual.
 """
 """

+ 8 - 3
direct/src/showbase/AppRunnerGlobal.py

@@ -4,11 +4,16 @@ runp3d.py or via the Panda3D plugin or standalone executable.
 
 
 This is needed for apps that start themselves by importing
 This is needed for apps that start themselves by importing
 DirectStart; it provides a place for these apps to look for
 DirectStart; it provides a place for these apps to look for
-the AppRunner at startup. """
+the AppRunner at startup.
+
+.. deprecated:: 1.10.0
+   The p3d packaging system has been replaced with the new setuptools-based
+   system.  See the :ref:`distribution` manual section.
+"""
 
 
 if __debug__:
 if __debug__:
     print('AppRunner has been removed and AppRunnerGlobal has been deprecated')
     print('AppRunner has been removed and AppRunnerGlobal has been deprecated')
 
 
-#: Contains the global AppRunner instance, or None if this application
-#: was not run from the runtime environment.
+#: Contains the global :class:`~.AppRunner.AppRunner` instance, or None
+#: if this application was not run from the runtime environment.
 appRunner = None
 appRunner = None

+ 26 - 11
direct/src/showbase/BufferViewer.py

@@ -1,6 +1,16 @@
 """Contains the BufferViewer class, which is used as a debugging aid
 """Contains the BufferViewer class, which is used as a debugging aid
 when debugging render-to-texture effects.  It shows different views at
 when debugging render-to-texture effects.  It shows different views at
-the bottom of the screen showing the various render targets."""
+the bottom of the screen showing the various render targets.
+
+When using ShowBase, the normal way to enable the BufferViewer is using the
+following code::
+
+    base.bufferViewer.toggleEnable()
+
+Or, you can enable the following variable in your Config.prc::
+
+    show-buffers true
+"""
 
 
 __all__ = ['BufferViewer']
 __all__ = ['BufferViewer']
 
 
@@ -11,6 +21,7 @@ from direct.directnotify.DirectNotifyGlobal import *
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.DirectObject import DirectObject
 import math
 import math
 
 
+
 class BufferViewer(DirectObject):
 class BufferViewer(DirectObject):
     notify = directNotify.newCategory('BufferViewer')
     notify = directNotify.newCategory('BufferViewer')
 
 
@@ -100,11 +111,13 @@ class BufferViewer(DirectObject):
 
 
     def setPosition(self, pos):
     def setPosition(self, pos):
         """Set the position of the cards.  The valid values are:
         """Set the position of the cards.  The valid values are:
-        * llcorner - put them in the lower-left  corner of the window
-        * lrcorner - put them in the lower-right corner of the window
-        * ulcorner - put them in the upper-left  corner of the window
-        * urcorner - put them in the upper-right corner of the window
-        * window   - put them in a separate window
+
+        - *llcorner* - put them in the lower-left  corner of the window
+        - *lrcorner* - put them in the lower-right corner of the window
+        - *ulcorner* - put them in the upper-left  corner of the window
+        - *urcorner* - put them in the upper-right corner of the window
+        - *window* - put them in a separate window
+
         The initial value is 'lrcorner'."""
         The initial value is 'lrcorner'."""
         valid=["llcorner","lrcorner","ulcorner","urcorner","window"]
         valid=["llcorner","lrcorner","ulcorner","urcorner","window"]
         if (valid.count(pos)==0):
         if (valid.count(pos)==0):
@@ -119,11 +132,13 @@ class BufferViewer(DirectObject):
 
 
     def setLayout(self, lay):
     def setLayout(self, lay):
         """Set the layout of the cards.  The valid values are:
         """Set the layout of the cards.  The valid values are:
-        * vline - display them in a vertical line
-        * hline - display them in a horizontal line
-        * vgrid - display them in a vertical grid
-        * hgrid - display them in a horizontal grid
-        * cycle - display one card at a time, using selectCard/advanceCard
+
+        - *vline* - display them in a vertical line
+        - *hline* - display them in a horizontal line
+        - *vgrid* - display them in a vertical grid
+        - *hgrid* - display them in a horizontal grid
+        - *cycle* - display one card at a time, using selectCard/advanceCard
+
         The default value is 'hline'."""
         The default value is 'hline'."""
         valid=["vline","hline","vgrid","hgrid","cycle"]
         valid=["vline","hline","vgrid","hgrid","cycle"]
         if (valid.count(lay)==0):
         if (valid.count(lay)==0):

+ 2 - 1
direct/src/showbase/BulletinBoardGlobal.py

@@ -1,7 +1,8 @@
-"""instantiate global BulletinBoard object"""
+"""Instantiates the global :class:`~.BulletinBoard.BulletinBoard` object."""
 
 
 __all__ = ['bulletinBoard']
 __all__ = ['bulletinBoard']
 
 
 from . import BulletinBoard
 from . import BulletinBoard
 
 
+#: The global :class:`~.BulletinBoard.BulletinBoard` object.
 bulletinBoard = BulletinBoard.BulletinBoard()
 bulletinBoard = BulletinBoard.BulletinBoard()

+ 23 - 21
direct/src/showbase/DistancePhasedNode.py

@@ -3,16 +3,18 @@ from direct.directnotify.DirectNotifyGlobal import directNotify
 from panda3d.core import *
 from panda3d.core import *
 from .PhasedObject import PhasedObject
 from .PhasedObject import PhasedObject
 
 
+
 class DistancePhasedNode(PhasedObject, DirectObject, NodePath):
 class DistancePhasedNode(PhasedObject, DirectObject, NodePath):
     """
     """
-    This class defines a PhasedObject,NodePath object that will handle the phasing
-    of an object in the scene graph according to its distance from some
-    other collider object(such as an avatar).
+    This class defines a PhasedObject,NodePath object that will handle
+    the phasing of an object in the scene graph according to its
+    distance from some other collider object(such as an avatar).
 
 
     Since it's a NodePath, you can parent it to another object in the
     Since it's a NodePath, you can parent it to another object in the
     scene graph, or even inherit from this class to get its functionality.
     scene graph, or even inherit from this class to get its functionality.
 
 
     What you will need to define to use this class:
     What you will need to define to use this class:
+
      - The distances at which you want the phases to load/unload
      - The distances at which you want the phases to load/unload
      - Whether you want the object to clean itself up or not when
      - Whether you want the object to clean itself up or not when
        exitting the largest distance sphere
        exitting the largest distance sphere
@@ -22,14 +24,14 @@ class DistancePhasedNode(PhasedObject, DirectObject, NodePath):
      - (Optional) A 'from' collision node to collide into our 'into' spheres
      - (Optional) A 'from' collision node to collide into our 'into' spheres
 
 
     You specify the distances and function names by the phaseParamMap
     You specify the distances and function names by the phaseParamMap
-    parameter to __init__().  For example:
+    parameter to `__init__()`.  For example::
 
 
-    phaseParamMap = {'Alias': distance, ...}
-    ...
-    def loadPhaseAlias(self):
-        pass
-    def unloadPhaseAlias(self):
-        pass
+        phaseParamMap = {'Alias': distance, ...}
+        ...
+        def loadPhaseAlias(self):
+            pass
+        def unloadPhaseAlias(self):
+            pass
 
 
     If the 'fromCollideNode' is supplied, we will set up our own
     If the 'fromCollideNode' is supplied, we will set up our own
     traverser and only traverse below this node.  It will send out
     traverser and only traverse below this node.  It will send out
@@ -40,14 +42,15 @@ class DistancePhasedNode(PhasedObject, DirectObject, NodePath):
     Most of the time, it will be reacting to events from the main
     Most of the time, it will be reacting to events from the main
     collision traverser.
     collision traverser.
 
 
-    IMPORTANT!: The following only applies when autoCleanup == True:
-                If you unload the last phase, by either calling
-                cleanup() or by exitting the last phase's distance,
-                you will need to explicitly call reset() to get the
-                distance phasing to work again. This was done so if
-                either this node or the collider is removed from the
-                scene graph(eg. avatar teleport), the phased object
-                will clean itself up automatically.
+    IMPORTANT:
+
+        The following only applies when ``autoCleanup is True``:
+        If you unload the last phase, by either calling `cleanup()` or
+        by exiting the last phase's distance, you will need to
+        explicitly call `reset()` to get the distance phasing to work
+        again. This was done so if either this node or the collider is
+        removed from the scene graph (e.g. avatar teleport), the phased
+        object will clean itself up automatically.
     """
     """
 
 
     notify = directNotify.newCategory("DistancePhasedObject")
     notify = directNotify.newCategory("DistancePhasedObject")
@@ -118,7 +121,6 @@ class DistancePhasedNode(PhasedObject, DirectObject, NodePath):
     def __str__(self):
     def __str__(self):
         return '%s in phase \'%s\'' % (NodePath.__str__(self), self.getPhase())
         return '%s in phase \'%s\'' % (NodePath.__str__(self), self.getPhase())
 
 
-
     def cleanup(self):
     def cleanup(self):
         """
         """
         Disables all collisions.
         Disables all collisions.
@@ -262,9 +264,9 @@ class BufferedDistancePhasedNode(DistancePhasedNode):
     border.
     border.
 
 
     You specify the buffer amount in the bufferParamMap parameter
     You specify the buffer amount in the bufferParamMap parameter
-    to __init__().  It has this format:
+    to :meth:`__init__()`.  It has this format::
 
 
-    bufferParamMap = {'alias':(distance, bufferAmount), ...}
+        bufferParamMap = {'alias':(distance, bufferAmount), ...}
     """
     """
     notify = directNotify.newCategory("BufferedDistancePhasedObject")
     notify = directNotify.newCategory("BufferedDistancePhasedObject")
 
 

+ 1 - 0
direct/src/showbase/ExceptionVarDump.py

@@ -179,6 +179,7 @@ def _excepthookDumpVars(eType, eValue, tb):
     oldExcepthook(eType, eValue, origTb)
     oldExcepthook(eType, eValue, origTb)
 
 
 def install(log, upload):
 def install(log, upload):
+    """Installs the exception hook."""
     global oldExcepthook
     global oldExcepthook
     global wantStackDumpLog
     global wantStackDumpLog
     global wantStackDumpUpload
     global wantStackDumpUpload

+ 4 - 1
direct/src/showbase/GarbageReportScheduler.py

@@ -1,7 +1,9 @@
 from direct.showbase.GarbageReport import GarbageReport
 from direct.showbase.GarbageReport import GarbageReport
 
 
+
 class GarbageReportScheduler:
 class GarbageReportScheduler:
-    # runs a garbage report every once in a while and logs the results
+    """Runs a garbage report every once in a while and logs the results."""
+
     def __init__(self, waitBetween=None, waitScale=None):
     def __init__(self, waitBetween=None, waitScale=None):
         # waitBetween is in seconds
         # waitBetween is in seconds
         # waitScale is a multiplier for the waitBetween every time around
         # waitScale is a multiplier for the waitBetween every time around
@@ -30,6 +32,7 @@ class GarbageReportScheduler:
                               self._taskName)
                               self._taskName)
         # and increase the delay every time around
         # and increase the delay every time around
         self._waitBetween = self._waitBetween * self._waitScale
         self._waitBetween = self._waitBetween * self._waitScale
+
     def _runGarbageReport(self, task):
     def _runGarbageReport(self, task):
         # run a garbage report and schedule the next one after this one finishes
         # run a garbage report and schedule the next one after this one finishes
         # give this job 3 times as many timeslices as normal-priority jobs
         # give this job 3 times as many timeslices as normal-priority jobs

+ 2 - 1
direct/src/showbase/InputStateGlobal.py

@@ -1,4 +1,4 @@
-"""instantiate global InputState object"""
+"""Instantiates the global :class:`~.InputState.InputState` object."""
 
 
 __all__ = ['inputState']
 __all__ = ['inputState']
 
 
@@ -7,4 +7,5 @@ __all__ = ['inputState']
 
 
 from direct.controls import InputState
 from direct.controls import InputState
 
 
+#: The global :class:`~.InputState.InputState` object.
 inputState = InputState.InputState()
 inputState = InputState.InputState()

+ 35 - 27
direct/src/showbase/Job.py

@@ -3,14 +3,22 @@ from direct.showbase.DirectObject import DirectObject
 if __debug__:
 if __debug__:
     from panda3d.core import PStatCollector
     from panda3d.core import PStatCollector
 
 
+
 class Job(DirectObject):
 class Job(DirectObject):
-    # Base class for cpu-intensive or non-time-critical operations that
-    # are run through the JobManager.
+    """Base class for cpu-intensive or non-time-critical operations that
+    are run through the :class:`.JobManager`.
+
+    To use, subclass and override the `run()` method.
+    """
 
 
-    # values to yield from your run() generator method
+    #: Yielded from the `run()` generator method when the job is done.
     Done = object()
     Done = object()
-    Continue = None # 'yield None' is acceptable in place of 'yield Job.Continue'
-    Sleep = object() # yield any remaining time for this job until next frame
+
+    #: ``yield None`` is acceptable in place of ``yield Job.Continue``
+    Continue = None
+
+    #: Yield any remaining time for this job until next frame.
+    Sleep = object()
 
 
     # These priorities determine how many timeslices a job gets relative to other
     # These priorities determine how many timeslices a job gets relative to other
     # jobs. A job with priority of 1000 will run 10 times more often than a job
     # jobs. A job with priority of 1000 will run 10 times more often than a job
@@ -37,13 +45,14 @@ class Job(DirectObject):
         return 'job-finished-%s' % self._id
         return 'job-finished-%s' % self._id
 
 
     def run(self):
     def run(self):
-        # this is a generator
-        # override and do your processing
-        # yield Job.Continue when possible/reasonable
-        # try not to run longer than the JobManager's timeslice between yields
-        #
-        # when done, yield Job.Done
-        #
+        """This should be overridden with a generator that does the
+        needful processing.
+
+        yield `Job.Continue` when possible/reasonable, and try not to run
+        longer than the JobManager's timeslice between yields.
+
+        When done, yield `Job.Done`.
+        """
         raise NotImplementedError("don't call down")
         raise NotImplementedError("don't call down")
 
 
     def getPriority(self):
     def getPriority(self):
@@ -57,23 +66,22 @@ class Job(DirectObject):
         self._printing = False
         self._printing = False
 
 
     def resume(self):
     def resume(self):
-        # called every time JobManager is going to start running this job
-        """
-        if self._printing:
-            # we may be suspended/resumed multiple times per frame, that gets spammy
-            # if we need to pick out the output of a job, put a prefix onto each line
-            # of the output
-            print 'JOB:%s:RESUME' % self._name
-            """
-        pass
+        """Called every time JobManager is going to start running this job."""
+        #if self._printing:
+        #    # we may be suspended/resumed multiple times per frame, that gets spammy
+        #    # if we need to pick out the output of a job, put a prefix onto each line
+        #    # of the output
+        #    print('JOB:%s:RESUME' % self._name)
+
     def suspend(self):
     def suspend(self):
-        # called when JobManager is going to stop running this job for a while
+        """Called when JobManager is going to stop running this job for a
+        while.
         """
         """
-        if self._printing:
-            #print 'JOB:%s:SUSPEND' % self._name
-            pass
-            """
-        pass
+
+        #if self._printing:
+        #    #print('JOB:%s:SUSPEND' % self._name)
+        #    pass
+        #    """
 
 
     def _setFinished(self):
     def _setFinished(self):
         self._finished = True
         self._finished = True

+ 1 - 0
direct/src/showbase/JobManagerGlobal.py

@@ -2,4 +2,5 @@ __all__ = ['jobMgr']
 
 
 from . import JobManager
 from . import JobManager
 
 
+#: Contains the global :class:`~.JobManager.JobManager` object.
 jobMgr = JobManager.JobManager()
 jobMgr = JobManager.JobManager()

+ 3 - 1
direct/src/showbase/LeakDetectors.py

@@ -1,4 +1,6 @@
-# objects that report different types of leaks to the ContainerLeakDetector
+"""Contains objects that report different types of leaks to the
+ContainerLeakDetector.
+"""
 
 
 from panda3d.core import *
 from panda3d.core import *
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.DirectObject import DirectObject

+ 3 - 1
direct/src/showbase/Loader.py

@@ -1,4 +1,6 @@
-"""Loader module: contains the Loader class"""
+"""This module contains a high-level interface for loading models, textures,
+sound, music, shaders and fonts from disk.
+"""
 
 
 __all__ = ['Loader']
 __all__ = ['Loader']
 
 

+ 25 - 23
direct/src/showbase/Messenger.py

@@ -1,5 +1,6 @@
 """This defines the Messenger class, which is responsible for most of the
 """This defines the Messenger class, which is responsible for most of the
-event handling that happens on the Python side."""
+event handling that happens on the Python side.
+"""
 
 
 __all__ = ['Messenger']
 __all__ = ['Messenger']
 
 
@@ -10,26 +11,28 @@ import types
 
 
 from direct.stdpy.threading import Lock
 from direct.stdpy.threading import Lock
 
 
+
 class Messenger:
 class Messenger:
 
 
     notify = DirectNotifyGlobal.directNotify.newCategory("Messenger")
     notify = DirectNotifyGlobal.directNotify.newCategory("Messenger")
 
 
     def __init__(self):
     def __init__(self):
         """
         """
-        One is keyed off the event name. It has the following structure:
+        One is keyed off the event name. It has the following structure::
+
             {event1: {object1: [method, extraArgs, persistent],
             {event1: {object1: [method, extraArgs, persistent],
                        object2: [method, extraArgs, persistent]},
                        object2: [method, extraArgs, persistent]},
              event2: {object1: [method, extraArgs, persistent],
              event2: {object1: [method, extraArgs, persistent],
                        object2: [method, extraArgs, persistent]}}
                        object2: [method, extraArgs, persistent]}}
 
 
-        This dictionary allow for efficient callbacks when the messenger
-        hears an event.
+        This dictionary allows for efficient callbacks when the
+        messenger hears an event.
 
 
         A second dictionary remembers which objects are accepting which
         A second dictionary remembers which objects are accepting which
         events. This allows for efficient ignoreAll commands.
         events. This allows for efficient ignoreAll commands.
 
 
+        Or, for an example with more real data::
 
 
-        Or, for an example with more real data:
             {'mouseDown': {avatar: [avatar.jump, [2.0], 1]}}
             {'mouseDown': {avatar: [avatar.jump, [2.0], 1]}}
         """
         """
         # eventName->objMsgrId->callbackInfo
         # eventName->objMsgrId->callbackInfo
@@ -281,20 +284,20 @@ class Messenger:
         """
         """
         return (not self.isAccepting(event, object))
         return (not self.isAccepting(event, object))
 
 
-    def send(self, event, sentArgs=[], taskChain = None):
+    def send(self, event, sentArgs=[], taskChain=None):
         """
         """
-        Send this event, optionally passing in arguments
-
-        event is usually a string.
-        sentArgs is a list of any data that you want passed along to the
-            handlers listening to this event.
-
-        If taskChain is not None, it is the name of the task chain
-        which should receive the event.  If taskChain is None, the
-        event is handled immediately.  Setting a non-None taskChain
-        will defer the event (possibly till next frame or even later)
-        and create a new, temporary task within the named taskChain,
-        but this is the only way to send an event across threads.
+        Send this event, optionally passing in arguments.
+
+        Args:
+            event (str): The name of the event.
+            sentArgs (list): A list of arguments to be passed along to the
+                handlers listening to this event.
+            taskChain (str, optional): If not None, the name of the task chain
+                which should receive the event.  If None, then the event is
+                handled immediately. Setting a non-None taskChain will defer
+                the event (possibly till next frame or even later) and create a
+                new, temporary task within the named taskChain, but this is the
+                only way to send an event across threads.
         """
         """
         if Messenger.notify.getDebug() and not self.quieting.get(event):
         if Messenger.notify.getDebug() and not self.quieting.get(event):
             assert Messenger.notify.debug(
             assert Messenger.notify.debug(
@@ -485,7 +488,7 @@ class Messenger:
             This is intended for debugging use only.
             This is intended for debugging use only.
             This function is not defined if python is ran with -O (optimize).
             This function is not defined if python is ran with -O (optimize).
 
 
-            See Also: unwatch
+            See Also: `unwatch`
             """
             """
             if not self.__watching.get(needle):
             if not self.__watching.get(needle):
                 self.__isWatching += 1
                 self.__isWatching += 1
@@ -499,7 +502,7 @@ class Messenger:
             This is intended for debugging use only.
             This is intended for debugging use only.
             This function is not defined if python is ran with -O (optimize).
             This function is not defined if python is ran with -O (optimize).
 
 
-            See Also: watch
+            See Also: `watch`
             """
             """
             if self.__watching.get(needle):
             if self.__watching.get(needle):
                 self.__isWatching -= 1
                 self.__isWatching -= 1
@@ -514,7 +517,7 @@ class Messenger:
             This is intended for debugging use only.
             This is intended for debugging use only.
             This function is not defined if python is ran with -O (optimize).
             This function is not defined if python is ran with -O (optimize).
 
 
-            See Also: unquiet
+            See Also: `unquiet`
             """
             """
             if not self.quieting.get(message):
             if not self.quieting.get(message):
                 self.quieting[message]=1
                 self.quieting[message]=1
@@ -528,7 +531,7 @@ class Messenger:
             This is intended for debugging use only.
             This is intended for debugging use only.
             This function is not defined if python is ran with -O (optimize).
             This function is not defined if python is ran with -O (optimize).
 
 
-            See Also: quiet
+            See Also: `quiet`
             """
             """
             if self.quieting.get(message):
             if self.quieting.get(message):
                 del self.quieting[message]
                 del self.quieting[message]
@@ -658,4 +661,3 @@ class Messenger:
     detailed_repr = detailedRepr
     detailed_repr = detailedRepr
     get_all_accepting = getAllAccepting
     get_all_accepting = getAllAccepting
     toggle_verbose = toggleVerbose
     toggle_verbose = toggleVerbose
-

+ 2 - 1
direct/src/showbase/MessengerGlobal.py

@@ -1,7 +1,8 @@
-"""instantiate global Messenger object"""
+"""Instantiates the global :class:`~.Messenger.Messenger` object."""
 
 
 __all__ = ['messenger']
 __all__ = ['messenger']
 
 
 from . import Messenger
 from . import Messenger
 
 
+#: Contains the global :class:`~.Messenger.Messenger` instance.
 messenger = Messenger.Messenger()
 messenger = Messenger.Messenger()

+ 1 - 1
direct/src/showbase/MirrorDemo.py

@@ -1,5 +1,5 @@
 """This file demonstrates one way to create a mirror effect in Panda.
 """This file demonstrates one way to create a mirror effect in Panda.
-Call setupMirror() to create a mirror in the world that reflects
+Call :func:`setupMirror()` to create a mirror in the world that reflects
 everything in front of it.
 everything in front of it.
 
 
 The approach taken here is to create an offscreen buffer with its own
 The approach taken here is to create an offscreen buffer with its own

+ 2 - 1
direct/src/showbase/PhysicsManagerGlobal.py

@@ -1,7 +1,8 @@
-"""PhysicsManagerGlobal module: contains the global physics manager"""
+"""Instantiates the global :class:`~panda3d.physics.PhysicsManager` object."""
 
 
 __all__ = ['physicsMgr']
 __all__ = ['physicsMgr']
 
 
 from panda3d.physics import PhysicsManager
 from panda3d.physics import PhysicsManager
 
 
+#: Contains the global :class:`~panda3d.physics.PhysicsManager` instance.
 physicsMgr = PhysicsManager()
 physicsMgr = PhysicsManager()

+ 7 - 4
direct/src/showbase/Pool.py

@@ -7,11 +7,13 @@ or be the same type.
 
 
 Internally the pool is implemented with 2 lists, free items and used items.
 Internally the pool is implemented with 2 lists, free items and used items.
 
 
-Example::
+Example:
 
 
-   p = Pool([1, 2, 3, 4, 5])
-   x = p.checkout()
-   p.checkin(x)
+    .. code-block:: python
+
+        p = Pool([1, 2, 3, 4, 5])
+        x = p.checkout()
+        p.checkin(x)
 
 
 """
 """
 
 
@@ -20,6 +22,7 @@ __all__ = ['Pool']
 
 
 from direct.directnotify import DirectNotifyGlobal
 from direct.directnotify import DirectNotifyGlobal
 
 
+
 class Pool:
 class Pool:
 
 
     notify = DirectNotifyGlobal.directNotify.newCategory("Pool")
     notify = DirectNotifyGlobal.directNotify.newCategory("Pool")

+ 78 - 76
direct/src/showbase/PythonUtil.py

@@ -1,36 +1,35 @@
 """Contains miscellaneous utility functions and classes."""
 """Contains miscellaneous utility functions and classes."""
 
 
-__all__ = ['indent',
-'doc', 'adjust', 'difference', 'intersection', 'union',
-'sameElements', 'makeList', 'makeTuple', 'list2dict', 'invertDict',
-'invertDictLossless', 'uniqueElements', 'disjoint', 'contains',
-'replace', 'reduceAngle', 'fitSrcAngle2Dest', 'fitDestAngle2Src',
-'closestDestAngle2', 'closestDestAngle', 'getSetterName',
-'getSetter', 'Functor', 'Stack', 'Queue',
-'bound', 'clamp', 'lerp', 'average', 'addListsByValue',
-'boolEqual', 'lineupPos', 'formatElapsedSeconds', 'solveQuadratic',
-'findPythonModule', 'mostDerivedLast',
-'clampScalar', 'weightedChoice', 'randFloat', 'normalDistrib',
-'weightedRand', 'randUint31', 'randInt32',
-'SerialNumGen', 'serialNum', 'uniqueName', 'Enum', 'Singleton',
-'SingletonError', 'printListEnum', 'safeRepr',
-'fastRepr', 'isDefaultValue',
-'ScratchPad', 'Sync', 'itype', 'getNumberedTypedString',
-'getNumberedTypedSortedString',
-'printNumberedTyped', 'DelayedCall', 'DelayedFunctor',
-'FrameDelayedCall', 'SubframeCall', 'getBase', 'GoldenRatio',
-'GoldenRectangle', 'rad90', 'rad180', 'rad270', 'rad360',
-'nullGen', 'loopGen', 'makeFlywheelGen', 'flywheel',
-'listToIndex2item', 'listToItem2index',
-'formatTimeCompact','deeptype','StdoutCapture','StdoutPassthrough',
-'Averager', 'getRepository', 'formatTimeExact', 'startSuperLog', 'endSuperLog',
-'typeName', 'safeTypeName', 'histogramDict', 'unescapeHtmlString']
+__all__ = [
+
+    'indent', 'doc', 'adjust', 'difference', 'intersection', 'union',
+    'sameElements', 'makeList', 'makeTuple', 'list2dict', 'invertDict',
+    'invertDictLossless', 'uniqueElements', 'disjoint', 'contains', 'replace',
+    'reduceAngle', 'fitSrcAngle2Dest', 'fitDestAngle2Src', 'closestDestAngle2',
+    'closestDestAngle', 'getSetterName', 'getSetter', 'Functor', 'Stack',
+    'Queue', 'bound', 'clamp', 'lerp', 'average', 'addListsByValue',
+    'boolEqual', 'lineupPos', 'formatElapsedSeconds', 'solveQuadratic',
+    'findPythonModule', 'mostDerivedLast', 'clampScalar', 'weightedChoice',
+    'randFloat', 'normalDistrib', 'weightedRand', 'randUint31', 'randInt32',
+    'SerialNumGen', 'serialNum', 'uniqueName', 'Enum', 'Singleton',
+    'SingletonError', 'printListEnum', 'safeRepr', 'fastRepr',
+    'isDefaultValue', 'ScratchPad', 'Sync', 'itype', 'getNumberedTypedString',
+    'getNumberedTypedSortedString', 'printNumberedTyped', 'DelayedCall',
+    'DelayedFunctor', 'FrameDelayedCall', 'SubframeCall', 'getBase',
+    'GoldenRatio', 'GoldenRectangle', 'rad90', 'rad180', 'rad270', 'rad360',
+    'nullGen', 'loopGen', 'makeFlywheelGen', 'flywheel', 'listToIndex2item',
+    'listToItem2index', 'formatTimeCompact', 'deeptype', 'StdoutCapture',
+    'StdoutPassthrough', 'Averager', 'getRepository', 'formatTimeExact',
+    'startSuperLog', 'endSuperLog', 'typeName', 'safeTypeName',
+    'histogramDict', 'unescapeHtmlString',
+]
 
 
 if __debug__:
 if __debug__:
-    __all__ += ['StackTrace', 'traceFunctionCall', 'traceParentCall', 'printThisCall',
-                'stackEntryInfo', 'lineInfo', 'callerInfo', 'lineTag',
-                'profileFunc', 'profiled', 'startProfile', 'printProfile',
-                'getProfileResultString', 'printStack', 'printReverseStack']
+    __all__ += ['StackTrace', 'traceFunctionCall', 'traceParentCall',
+                'printThisCall', 'stackEntryInfo', 'lineInfo', 'callerInfo',
+                'lineTag', 'profileFunc', 'profiled', 'startProfile',
+                'printProfile', 'getProfileResultString', 'printStack',
+                'printReverseStack']
 
 
 import types
 import types
 import math
 import math
@@ -652,13 +651,15 @@ if __debug__:
         """ decorator for profiling functions
         """ decorator for profiling functions
         turn categories on and off via "want-profile-categoryName 1"
         turn categories on and off via "want-profile-categoryName 1"
 
 
-        e.g.
+        e.g.::
+
+            @profiled('particles')
+            def loadParticles():
+                ...
 
 
-        @profiled('particles')
-        def loadParticles():
-            ...
+        ::
 
 
-        want-profile-particles 1
+            want-profile-particles 1
         """
         """
         assert type(category) in (str, type(None)), "must provide a category name for @profiled"
         assert type(category) in (str, type(None)), "must provide a category name for @profiled"
 
 
@@ -1195,13 +1196,14 @@ def normalDistrib(a, b, gauss=random.gauss):
 
 
 def weightedRand(valDict, rng=random.random):
 def weightedRand(valDict, rng=random.random):
     """
     """
-    pass in a dictionary with a selection -> weight mapping.  Eg.
-    {"Choice 1": 10,
-     "Choice 2": 30,
-     "bear":     100}
+    pass in a dictionary with a selection -> weight mapping.  E.g.::
+
+        {"Choice 1": 10,
+         "Choice 2": 30,
+         "bear":     100}
 
 
-    -Weights need not add up to any particular value.
-    -The actual selection will be returned.
+    - Weights need not add up to any particular value.
+    - The actual selection will be returned.
     """
     """
     selections = list(valDict.keys())
     selections = list(valDict.keys())
     weights = list(valDict.values())
     weights = list(valDict.values())
@@ -1997,42 +1999,42 @@ def report(types = [], prefix = '', xform = None, notifyFunc = None, dConfigPara
     has no effect and no wrapping/transform occurs.  So in production,
     has no effect and no wrapping/transform occurs.  So in production,
     it's as if the report has been asserted out.
     it's as if the report has been asserted out.
 
 
-    Parameters::
-    types : A subset list of ['timeStamp', 'frameCount', 'avLocation']
-            This allows you to specify certain useful bits of info.
-
-            module:     Prints the module that this report statement
-                        can be found in.
-            args:       Prints the arguments as they were passed to
-                        this function.
-            timeStamp:  Adds the current frame time to the output.
-            deltaStamp: Adds the current AI synched frame time to
-                        the output
-            frameCount: Adds the current frame count to the output.
-                        Usually cleaner than the timeStamp output.
-            avLocation: Adds the localAvatar's network location
-                        to the output.  Useful for interest debugging.
-            interests:  Prints the current interest state after the
-                        report.
-            stackTrace: Prints a stack trace after the report.
-
-    prefix: Optional string to prepend to output, just before the function.
-            Allows for easy grepping and is useful when merging AI/Client
-            reports into a single file.
-
-    xform:  Optional callback that accepts a single parameter: argument 0 to
-            the decorated function. (assumed to be 'self')
-            It should return a value to be inserted into the report output string.
-
-    notifyFunc: A notify function such as info, debug, warning, etc.
-                By default the report will be printed to stdout. This
-                will allow you send the report to a designated 'notify'
-                output.
-
-    dConfigParam: A list of Config.prc string variables.
-                  By default the report will always print.  If you
-                  specify this param, it will only print if one of the
-                  specified config strings resolve to True.
+    Parameters:
+        types: A subset list of ['timeStamp', 'frameCount', 'avLocation']
+            This allows you to specify certain useful bits of info:
+
+              - *module*: Prints the module that this report statement
+                can be found in.
+              - *args*: Prints the arguments as they were passed to this
+                function.
+              - *timeStamp*: Adds the current frame time to the output.
+              - *deltaStamp*: Adds the current AI synched frame time to
+                the output
+              - *frameCount*: Adds the current frame count to the output.
+                Usually cleaner than the timeStamp output.
+              - *avLocation*: Adds the localAvatar's network location to
+                the output.  Useful for interest debugging.
+              - *interests*: Prints the current interest state after the
+                report.
+              - *stackTrace*: Prints a stack trace after the report.
+
+        prefix: Optional string to prepend to output, just before the
+            function.  Allows for easy grepping and is useful when
+            merging AI/Client reports into a single file.
+
+        xform:  Optional callback that accepts a single parameter:
+            argument 0 to the decorated function. (assumed to be 'self')
+            It should return a value to be inserted into the report
+            output string.
+
+        notifyFunc: A notify function such as info, debug, warning, etc.
+            By default the report will be printed to stdout. This will
+            allow you send the report to a designated 'notify' output.
+
+        dConfigParam: A list of Config.prc string variables.
+            By default the report will always print.  If you specify
+            this param, it will only print if one of the specified
+            config strings resolve to True.
     """
     """
 
 
 
 

+ 50 - 31
direct/src/showbase/ShowBase.py

@@ -60,7 +60,19 @@ class ShowBase(DirectObject.DirectObject):
     config = DConfig
     config = DConfig
     notify = directNotify.newCategory("ShowBase")
     notify = directNotify.newCategory("ShowBase")
 
 
-    def __init__(self, fStartDirect = True, windowType = None):
+    def __init__(self, fStartDirect=True, windowType=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.
+
+        To prevent a window from being opened, set windowType to the string
+        'none' (or 'offscreen' to create an offscreen buffer).  If this is not
+        specified, the default value is taken from the 'window-type'
+        configuration variable.
+
+        This constructor will add various things to the Python builtins scope,
+        including this instance itself (under the name ``base``).
+        """
+
         self.__dev__ = self.config.GetBool('want-dev', __debug__)
         self.__dev__ = self.config.GetBool('want-dev', __debug__)
         builtins.__dev__ = self.__dev__
         builtins.__dev__ = self.__dev__
 
 
@@ -1853,6 +1865,7 @@ class ShowBase(DirectObject.DirectObject):
             self.notify.debug("Disabling music")
             self.notify.debug("Disabling music")
 
 
     def SetAllSfxEnables(self, bEnabled):
     def SetAllSfxEnables(self, bEnabled):
+        """Calls ``setActive(bEnabled)`` on all valid SFX managers."""
         for i in range(len(self.sfxManagerList)):
         for i in range(len(self.sfxManagerList)):
             if (self.sfxManagerIsValidList[i]):
             if (self.sfxManagerIsValidList[i]):
                 self.sfxManagerList[i].setActive(bEnabled)
                 self.sfxManagerList[i].setActive(bEnabled)
@@ -2586,9 +2599,9 @@ class ShowBase(DirectObject.DirectObject):
                     sourceLens = None):
                     sourceLens = None):
 
 
         """
         """
-        Similar to screenshot(), this sets up a temporary cube map
-        Texture which it uses to take a series of six snapshots of the
-        current scene, one in each of the six cube map directions.
+        Similar to :meth:`screenshot()`, this sets up a temporary cube
+        map Texture which it uses to take a series of six snapshots of
+        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,
@@ -2650,19 +2663,19 @@ class ShowBase(DirectObject.DirectObject):
                       cameraMask = PandaNode.getAllCameraMask(),
                       cameraMask = PandaNode.getAllCameraMask(),
                       numVertices = 1000, sourceLens = None):
                       numVertices = 1000, sourceLens = None):
         """
         """
-        This works much like saveCubeMap(), and uses the graphics
-        API's hardware cube-mapping ability to get a 360-degree view
-        of the world.  But then it converts the six cube map faces
-        into a single fisheye texture, suitable for applying as a
-        static environment map (sphere map).
+        This works much like :meth:`saveCubeMap()`, and uses the
+        graphics API's hardware cube-mapping ability to get a 360-degree
+        view of the world.  But then it converts the six cube map faces
+        into a single fisheye texture, suitable for applying as a static
+        environment map (sphere map).
 
 
-        For eye-relative static environment maps, sphere maps are
-        often preferable to cube maps because they require only a
-        single texture and because they are supported on a broader
-        range of hardware.
+        For eye-relative static environment maps, sphere maps are often
+        preferable to cube maps because they require only a single
+        texture and because they are supported on a broader range of
+        hardware.
 
 
-        The return value is the filename if successful, or None if
-        there is a problem.
+        The return value is the filename if successful, or None if there
+        is a problem.
         """
         """
         if source == None:
         if source == None:
             source = self.win
             source = self.win
@@ -2739,17 +2752,22 @@ class ShowBase(DirectObject.DirectObject):
               format = 'png', sd = 4, source = None):
               format = 'png', sd = 4, source = None):
         """
         """
         Spawn a task to capture a movie using the screenshot function.
         Spawn a task to capture a movie using the screenshot function.
-        - namePrefix will be used to form output file names (can include
-          path information (e.g. '/i/beta/frames/myMovie')
-        - duration is the length of the movie in seconds
-        - fps is the frame rate of the resulting movie
-        - format specifies output file format (e.g. png, bmp)
-        - sd specifies number of significant digits for frame count in the
-          output file name (e.g. if sd = 4, movie_0001.png)
-        - source is the Window, Buffer, DisplayRegion, or Texture from which
-          to save the resulting images.  The default is the main window.
-
-        The task is returned, so that it can be awaited.
+
+        Args:
+            namePrefix (str): used to form output file names (can
+                include path information (e.g. '/i/beta/frames/myMovie')
+            duration (float): the length of the movie in seconds
+            fps (float): the frame rate of the resulting movie
+            format (str): specifies output file format (e.g. png, bmp)
+            sd (int): specifies number of significant digits for frame
+                count in the output file name (e.g. if sd = 4, the name
+                will be something like movie_0001.png)
+            source: the Window, Buffer, DisplayRegion, or Texture from
+                which to save the resulting images.  The default is the
+                main window.
+
+        Returns:
+            A `~direct.task.Task` that can be awaited.
         """
         """
         globalClock.setMode(ClockObject.MNonRealTime)
         globalClock.setMode(ClockObject.MNonRealTime)
         globalClock.setDt(1.0/float(fps))
         globalClock.setDt(1.0/float(fps))
@@ -3112,11 +3130,12 @@ class ShowBase(DirectObject.DirectObject):
         self.startDirect(fWantDirect = fDirect, fWantTk = fTk, fWantWx = fWx)
         self.startDirect(fWantDirect = fDirect, fWantTk = fTk, fWantWx = fWx)
 
 
     def run(self):
     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 :class:`~direct.task.Task.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 Panda3D 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 \
         if self.appRunner is None or self.appRunner.dummy or \
            (self.appRunner.interactiveConsole and not self.appRunner.initialAppImport):
            (self.appRunner.interactiveConsole and not self.appRunner.initialAppImport):

+ 10 - 2
direct/src/showbase/ShowBaseGlobal.py

@@ -1,5 +1,6 @@
-"""This module serves as a container to hold the global ShowBase instance, as
-an alternative to using the builtin scope.
+"""This module serves as a container to hold the global
+:class:`~.ShowBase.ShowBase` instance, as an alternative to using the builtin
+scope.
 
 
 Note that you cannot directly import `base` from this module since ShowBase
 Note that you cannot directly import `base` from this module since ShowBase
 may not have been created yet; instead, ShowBase dynamically adds itself to
 may not have been created yet; instead, ShowBase dynamically adds itself to
@@ -16,6 +17,7 @@ from . import DConfig as config
 
 
 __dev__ = config.GetBool('want-dev', __debug__)
 __dev__ = config.GetBool('want-dev', __debug__)
 
 
+#: The global instance of the :class:`panda3d.core.VirtualFileSystem`.
 vfs = VirtualFileSystem.getGlobalPtr()
 vfs = VirtualFileSystem.getGlobalPtr()
 ostream = Notify.out()
 ostream = Notify.out()
 globalClock = ClockObject.getGlobalClock()
 globalClock = ClockObject.getGlobalClock()
@@ -30,17 +32,23 @@ hidden = NodePath("hidden")
 # Set direct notify categories now that we have config
 # Set direct notify categories now that we have config
 directNotify.setDconfigLevels()
 directNotify.setDconfigLevels()
 
 
+
 def run():
 def run():
+    """Deprecated alias for :meth:`base.run() <.ShowBase.run>`."""
     assert ShowBase.notify.warning("run() is deprecated, use base.run() instead")
     assert ShowBase.notify.warning("run() is deprecated, use base.run() instead")
     base.run()
     base.run()
 
 
+
 def inspect(anObject):
 def inspect(anObject):
+    """Opens up a :mod:`direct.tkpanels.Inspector` GUI panel for inspecting an
+    object."""
     # Don't use a regular import, to prevent ModuleFinder from picking
     # Don't use a regular import, to prevent ModuleFinder from picking
     # it up as a dependency when building a .p3d package.
     # it up as a dependency when building a .p3d package.
     import importlib
     import importlib
     Inspector = importlib.import_module('direct.tkpanels.Inspector')
     Inspector = importlib.import_module('direct.tkpanels.Inspector')
     return Inspector.inspect(anObject)
     return Inspector.inspect(anObject)
 
 
+
 import sys
 import sys
 if sys.version_info >= (3, 0):
 if sys.version_info >= (3, 0):
     import builtins
     import builtins

+ 1 - 0
direct/src/showbase/TkGlobal.py

@@ -39,4 +39,5 @@ del bordercolors
 
 
 
 
 def spawnTkLoop():
 def spawnTkLoop():
+    """Alias for :meth:`base.spawnTkLoop() <.ShowBase.spawnTkLoop>`."""
     base.spawnTkLoop()
     base.spawnTkLoop()

+ 25 - 14
direct/src/showbase/VFSImporter.py

@@ -1,3 +1,10 @@
+"""The VFS importer allows importing Python modules from Panda3D's virtual
+file system, through Python's standard import mechanism.
+
+Calling the :func:`register()` function to register the import hooks should be
+sufficient to enable this functionality.
+"""
+
 __all__ = ['register', 'sharedPackages',
 __all__ = ['register', 'sharedPackages',
            'reloadSharedPackage', 'reloadSharedPackages']
            'reloadSharedPackage', 'reloadSharedPackages']
 
 
@@ -8,19 +15,18 @@ import marshal
 import imp
 import imp
 import types
 import types
 
 
-# The sharedPackages dictionary lists all of the "shared packages",
-# special Python packages that automatically span multiple directories
-# via magic in the VFSImporter.  You can make a package "shared"
-# simply by adding its name into this dictionary (and then calling
-# reloadSharedPackages() if it's already been imported).
-
-# When a package name is in this dictionary at import time, *all*
-# instances of the package are located along sys.path, and merged into
-# a single Python module with a __path__ setting that represents the
-# union.  Thus, you can have a direct.showbase.foo in your own
-# application, and loading it won't shadow the system
-# direct.showbase.ShowBase which is in a different directory on disk.
-
+#: The sharedPackages dictionary lists all of the "shared packages",
+#: special Python packages that automatically span multiple directories
+#: via magic in the VFSImporter.  You can make a package "shared"
+#: simply by adding its name into this dictionary (and then calling
+#: reloadSharedPackages() if it's already been imported).
+#:
+#: When a package name is in this dictionary at import time, *all*
+#: instances of the package are located along sys.path, and merged into
+#: a single Python module with a __path__ setting that represents the
+#: union.  Thus, you can have a direct.showbase.foo in your own
+#: application, and loading it won't shadow the system
+#: direct.showbase.ShowBase which is in a different directory on disk.
 sharedPackages = {}
 sharedPackages = {}
 
 
 vfs = VirtualFileSystem.getGlobalPtr()
 vfs = VirtualFileSystem.getGlobalPtr()
@@ -31,6 +37,7 @@ if not __debug__:
     # We implement that by reversing the extension names.
     # We implement that by reversing the extension names.
     compiledExtensions = [ 'pyo', 'pyc' ]
     compiledExtensions = [ 'pyo', 'pyc' ]
 
 
+
 class VFSImporter:
 class VFSImporter:
     """ This class serves as a Python importer to support loading
     """ This class serves as a Python importer to support loading
     Python .py and .pyc/.pyo files from Panda's Virtual File System,
     Python .py and .pyc/.pyo files from Panda's Virtual File System,
@@ -326,6 +333,7 @@ class VFSLoader:
 
 
         return code
         return code
 
 
+
 class VFSSharedImporter:
 class VFSSharedImporter:
     """ This is a special importer that is added onto the meta_path
     """ This is a special importer that is added onto the meta_path
     list, so that it is called before sys.path is traversed.  It uses
     list, so that it is called before sys.path is traversed.  It uses
@@ -418,6 +426,7 @@ class VFSSharedImporter:
         # Couldn't figure it out.
         # Couldn't figure it out.
         return None
         return None
 
 
+
 class VFSSharedLoader:
 class VFSSharedLoader:
     """ The second part of VFSSharedImporter, this imports a list of
     """ The second part of VFSSharedImporter, this imports a list of
     packages and combines them. """
     packages and combines them. """
@@ -470,6 +479,7 @@ class VFSSharedLoader:
 
 
         return mod
         return mod
 
 
+
 _registered = False
 _registered = False
 def register():
 def register():
     """ Register the VFSImporter on the path_hooks, if it has not
     """ Register the VFSImporter on the path_hooks, if it has not
@@ -488,6 +498,7 @@ def register():
         # folders that previously were loaded directly.
         # folders that previously were loaded directly.
         sys.path_importer_cache = {}
         sys.path_importer_cache = {}
 
 
+
 def reloadSharedPackage(mod):
 def reloadSharedPackage(mod):
     """ Reloads the specific module as a shared package, adding any
     """ Reloads the specific module as a shared package, adding any
     new directories that might have appeared on the search path. """
     new directories that might have appeared on the search path. """
@@ -515,6 +526,7 @@ def reloadSharedPackage(mod):
                 sharedPackages[childname] = True
                 sharedPackages[childname] = True
                 reloadSharedPackage(child)
                 reloadSharedPackage(child)
 
 
+
 def reloadSharedPackages():
 def reloadSharedPackages():
     """ Walks through the sharedPackages list, and forces a reload of
     """ Walks through the sharedPackages list, and forces a reload of
     any modules on that list that have already been loaded.  This
     any modules on that list that have already been loaded.  This
@@ -530,4 +542,3 @@ def reloadSharedPackages():
             continue
             continue
 
 
         reloadSharedPackage(mod)
         reloadSharedPackage(mod)
-

+ 2 - 0
direct/src/showbase/WxGlobal.py

@@ -1,4 +1,6 @@
 """ This module is now vestigial.  """
 """ This module is now vestigial.  """
 
 
+
 def spawnWxLoop():
 def spawnWxLoop():
+    """Alias for :meth:`base.spawnWxLoop() <.ShowBase.spawnWxLoop>`."""
     base.spawnWxLoop()
     base.spawnWxLoop()

+ 5 - 0
direct/src/stdpy/file.py

@@ -34,6 +34,11 @@ else:
 
 
 
 
 def open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True):
 def open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True):
+    """This function emulates the built-in Python open() function, additionally
+    providing support for Panda's virtual file system.  It takes the same
+    arguments as Python's built-in open() function.
+    """
+
     if sys.version_info >= (3, 0):
     if sys.version_info >= (3, 0):
         # Python 3 is much stricter than Python 2, which lets
         # Python 3 is much stricter than Python 2, which lets
         # unknown flags fall through.
         # unknown flags fall through.

+ 3 - 3
direct/src/stdpy/pickle.py

@@ -14,9 +14,9 @@ mechanism for sharing context between different objects written to the
 same pickle stream, so each NodePath has to write itself without
 same pickle stream, so each NodePath has to write itself without
 knowing about the other NodePaths that will also be writing to the
 knowing about the other NodePaths that will also be writing to the
 same stream.  This replacement module solves this problem by defining
 same stream.  This replacement module solves this problem by defining
-a __reduce_persist__() replacement method for __reduce__(), which
-accepts a pointer to the Pickler object itself, allowing for shared
-context between all objects written by that Pickler.
+a ``__reduce_persist__()`` replacement method for ``__reduce__()``,
+which accepts a pointer to the Pickler object itself, allowing for
+shared context between all objects written by that Pickler.
 
 
 Unfortunately, cPickle cannot be supported, because it does not
 Unfortunately, cPickle cannot be supported, because it does not
 support extensions of this nature. """
 support extensions of this nature. """

+ 64 - 58
direct/src/task/Task.py

@@ -1,6 +1,10 @@
 """ This module defines a Python-level wrapper around the C++
 """ This module defines a Python-level wrapper around the C++
-AsyncTaskManager interface.  It replaces the old full-Python
-implementation of the Task system. """
+:class:`~panda3d.core.AsyncTaskManager` interface.  It replaces the old
+full-Python implementation of the Task system.
+
+For more information about the task system, consult the
+:ref:`tasks-and-event-handling` page in the programming manual.
+"""
 
 
 __all__ = ['Task', 'TaskManager',
 __all__ = ['Task', 'TaskManager',
            'cont', 'done', 'again', 'pickup', 'exit',
            'cont', 'done', 'again', 'pickup', 'exit',
@@ -68,7 +72,7 @@ again = AsyncTask.DSAgain
 pickup = AsyncTask.DSPickup
 pickup = AsyncTask.DSPickup
 exit = AsyncTask.DSExit
 exit = AsyncTask.DSExit
 
 
-# Alias PythonTask to Task for historical purposes.
+#: Task aliases to :class:`panda3d.core.PythonTask` for historical purposes.
 Task = PythonTask
 Task = PythonTask
 
 
 # Copy the module-level enums above into the class level.  This funny
 # Copy the module-level enums above into the class level.  This funny
@@ -123,7 +127,7 @@ class TaskManager:
         self.fKeyboardInterrupt = False
         self.fKeyboardInterrupt = False
         self.interruptCount = 0
         self.interruptCount = 0
 
 
-        self._frameProfileQueue = Queue()
+        self._frameProfileQueue = []
 
 
         # this will be set when it's safe to import StateVar
         # this will be set when it's safe to import StateVar
         self._profileFrames = None
         self._profileFrames = None
@@ -274,7 +278,7 @@ class TaskManager:
     def getTasksMatching(self, taskPattern):
     def getTasksMatching(self, taskPattern):
         """Returns a list of all tasks, active or sleeping, with a
         """Returns a list of all tasks, active or sleeping, with a
         name that matches the pattern, which can include standard
         name that matches the pattern, which can include standard
-        shell globbing characters like *, ?, and []. """
+        shell globbing characters like \\*, ?, and []. """
 
 
         return self.__makeTaskList(self.mgr.findTasksMatching(GlobPattern(taskPattern)))
         return self.__makeTaskList(self.mgr.findTasksMatching(GlobPattern(taskPattern)))
 
 
@@ -302,7 +306,7 @@ class TaskManager:
                       uponDeath = None, appendTask = False, owner = None):
                       uponDeath = None, appendTask = False, owner = None):
 
 
         """Adds a task to be performed at some time in the future.
         """Adds a task to be performed at some time in the future.
-        This is identical to add(), except that the specified
+        This is identical to `add()`, except that the specified
         delayTime is applied to the Task object first, which means
         delayTime is applied to the Task object first, which means
         that the task will not begin executing until at least the
         that the task will not begin executing until at least the
         indicated delayTime (in seconds) has elapsed.
         indicated delayTime (in seconds) has elapsed.
@@ -326,59 +330,61 @@ class TaskManager:
     def add(self, funcOrTask, name = None, sort = None, extraArgs = None,
     def add(self, funcOrTask, name = None, sort = None, extraArgs = None,
             priority = None, uponDeath = None, appendTask = False,
             priority = None, uponDeath = None, appendTask = False,
             taskChain = None, owner = None):
             taskChain = None, owner = None):
-
         """
         """
         Add a new task to the taskMgr.  The task will begin executing
         Add a new task to the taskMgr.  The task will begin executing
         immediately, or next frame if its sort value has already
         immediately, or next frame if its sort value has already
         passed this frame.
         passed this frame.
 
 
-        The parameters are:
-
-        funcOrTask - either an existing Task object (not already added
-        to the task manager), or a callable function object.  If this
-        is a function, a new Task object will be created and returned.
-        You may also pass in a coroutine object.
-
-        name - the name to assign to the Task.  Required, unless you
-        are passing in a Task object that already has a name.
-
-        extraArgs - the list of arguments to pass to the task
-        function.  If this is omitted, the list is just the task
-        object itself.
-
-        appendTask - a boolean flag.  If this is true, then the task
-        object itself will be appended to the end of the extraArgs
-        list before calling the function.
-
-        sort - the sort value to assign the task.  The default sort is
-        0.  Within a particular task chain, it is guaranteed that the
-        tasks with a lower sort value will all run before tasks with a
-        higher sort value run.
-
-        priority - the priority at which to run the task.  The default
-        priority is 0.  Higher priority tasks are run sooner, and/or
-        more often.  For historical purposes, if you specify a
-        priority without also specifying a sort, the priority value is
-        understood to actually be a sort value.  (Previously, there
-        was no priority value, only a sort value, and it was called
-        "priority".)
-
-        uponDeath - a function to call when the task terminates,
-        either because it has run to completion, or because it has
-        been explicitly removed.
-
-        taskChain - the name of the task chain to assign the task to.
-
-        owner - an optional Python object that is declared as the
-        "owner" of this task for maintenance purposes.  The owner must
-        have two methods: owner._addTask(self, task), which is called
-        when the task begins, and owner._clearTask(self, task), which
-        is called when the task terminates.  This is all the owner
-        means.
-
-        The return value of add() is the new Task object that has been
-        added, or the original Task object that was passed in.
-
+        Parameters:
+            funcOrTask: either an existing Task object (not already
+                added to the task manager), or a callable function
+                object. If this is a function, a new Task object will be
+                created and returned. You may also pass in a coroutine
+                object.
+
+            name (str): the name to assign to the Task.  Required,
+                unless you are passing in a Task object that already has
+                a name.
+
+            extraArgs (list): the list of arguments to pass to the task
+                function.  If this is omitted, the list is just the task
+                object itself.
+
+            appendTask (bool): If this is true, then the task object
+                itself will be appended to the end of the extraArgs list
+                before calling the function.
+
+            sort (int): the sort value to assign the task.  The default
+                sort is 0.  Within a particular task chain, it is
+                guaranteed that the tasks with a lower sort value will
+                all run before tasks with a higher sort value run.
+
+            priority (int): the priority at which to run the task.  The
+                default priority is 0.  Higher priority tasks are run
+                sooner, and/or more often.  For historical purposes, if
+                you specify a priority without also specifying a sort,
+                the priority value is understood to actually be a sort
+                value. (Previously, there was no priority value, only a
+                sort value, and it was called "priority".)
+
+            uponDeath (bool): a function to call when the task
+                terminates, either because it has run to completion, or
+                because it has been explicitly removed.
+
+            taskChain (str): the name of the task chain to assign the
+                task to.
+
+            owner: an optional Python object that is declared as the
+                "owner" of this task for maintenance purposes.  The
+                owner must have two methods:
+                ``owner._addTask(self, task)``, which is called when the
+                task begins, and ``owner._clearTask(self, task)``, which
+                is called when the task terminates.  This is all the
+                ownermeans.
+
+        Returns:
+            The new Task object that has been added, or the original
+            Task object that was passed in.
         """
         """
 
 
         task = self.__setupTask(funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath)
         task = self.__setupTask(funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath)
@@ -455,8 +461,8 @@ class TaskManager:
 
 
     def removeTasksMatching(self, taskPattern):
     def removeTasksMatching(self, taskPattern):
         """Removes all tasks whose names match the pattern, which can
         """Removes all tasks whose names match the pattern, which can
-        include standard shell globbing characters like *, ?, and [].
-        See also remove().
+        include standard shell globbing characters like \\*, ?, and [].
+        See also :meth:`remove()`.
 
 
         Returns the number of tasks removed.
         Returns the number of tasks removed.
         """
         """
@@ -515,7 +521,7 @@ class TaskManager:
             while self.running:
             while self.running:
                 try:
                 try:
                     if len(self._frameProfileQueue):
                     if len(self._frameProfileQueue):
-                        numFrames, session, callback = self._frameProfileQueue.pop()
+                        numFrames, session, callback = self._frameProfileQueue.pop(0)
                         def _profileFunc(numFrames=numFrames):
                         def _profileFunc(numFrames=numFrames):
                             self._doProfiledFrames(numFrames)
                             self._doProfiledFrames(numFrames)
                         session.setFunc(_profileFunc)
                         session.setFunc(_profileFunc)
@@ -623,7 +629,7 @@ class TaskManager:
             session = self.getProfileSession()
             session = self.getProfileSession()
         # make sure the profile session doesn't get destroyed before we're done with it
         # make sure the profile session doesn't get destroyed before we're done with it
         session.acquire()
         session.acquire()
-        self._frameProfileQueue.push((num, session, callback))
+        self._frameProfileQueue.append((num, session, callback))
 
 
     def _doProfiledFrames(self, numFrames):
     def _doProfiledFrames(self, numFrames):
         for i in range(numFrames):
         for i in range(numFrames):

+ 1 - 1
direct/src/task/TaskManagerGlobal.py

@@ -1,4 +1,4 @@
-"""TaskManagerGlobal module: contains the global task manager"""
+"""Contains the global :class:`~.Task.TaskManager` object."""
 
 
 __all__ = ['taskMgr']
 __all__ = ['taskMgr']
 
 

+ 3 - 0
direct/src/task/__init__.py

@@ -5,4 +5,7 @@ manages scheduled functions that are executed at designated intervals.
 The global task manager object can be imported as a singleton::
 The global task manager object can be imported as a singleton::
 
 
    from direct.task.TaskManagerGlobal import taskMgr
    from direct.task.TaskManagerGlobal import taskMgr
+
+For more information about the task system, consult the
+:ref:`tasks-and-event-handling` page in the programming manual.
 """
 """

+ 104 - 102
direct/src/tkpanels/FSMInspector.py

@@ -1,4 +1,106 @@
-""" Finite State Machine Inspector module """
+"""Defines the `FSMInspector` class, which opens a Tkinter window for
+inspecting :ref:`finite-state-machines`.
+
+Using the Finite State Inspector
+--------------------------------
+
+1) In your Config.prc add::
+
+    want-tk #t
+
+2) Start up the show and create a Finite State Machine::
+
+    from direct.showbase.ShowBaseGlobal import *
+
+    from direct.fsm import ClassicFSM
+    from direct.fsm import State
+
+    def enterState():
+        print('enterState')
+
+    def exitState():
+        print 'exitState'
+
+    fsm = ClassicFSM.ClassicFSM('stopLight',
+              [State.State('red', enterState, exitState, ['green']),
+                State.State('yellow', enterState, exitState, ['red']),
+                State.State('green', enterState, exitState, ['yellow'])],
+              'red',
+              'red')
+
+    import FSMInspector
+
+    inspector = FSMInspector.FSMInspector(fsm, title = fsm.getName())
+
+    # Note, the inspectorPos argument is optional, the inspector will
+    # automagically position states on startup
+    fsm = ClassicFSM.ClassicFSM('stopLight', [
+        State.State('yellow',
+                    enterState,
+                    exitState,
+                    ['red'],
+                    inspectorPos = [95.9, 48.0]),
+        State.State('red',
+                    enterState,
+                    exitState,
+                    ['green'],
+                    inspectorPos = [0.0, 0.0]),
+        State.State('green',
+                    enterState,
+                    exitState,
+                    ['yellow'],
+                    inspectorPos = [0.0, 95.9])],
+            'red',
+            'red')
+
+3) Pop open a viewer::
+
+    import FSMInspector
+    insp = FSMInspector.FSMInspector(fsm)
+
+or if you wish to be fancy::
+
+    insp = FSMInspector.FSMInspector(fsm, title = fsm.getName())
+
+Features:
+
+  - Right mouse button over a state pops up a menu allowing you to
+    request a transition to that state
+  - Middle mouse button will grab the canvas and slide things around if
+    your state machine is bigger than the viewing area
+  - There are some self explanatory menu options up at the top, the most
+    useful being: "print ClassicFSM layout" which will print out Python
+    code which will create an ClassicFSM augmented with layout
+    information for the viewer so everything shows up in the same place
+    the next time you inspect the state machine
+
+Caveat
+------
+
+There is an unexplained problem with using Tk and emacs right now which
+occasionally results in everything locking up.  This procedure seems to
+avoid the problem for me::
+
+   # Start up the show
+   from direct.showbase.ShowBaseGlobal import *
+
+   # You will see the window and a Tk panel pop open
+
+   # Type a number at the emacs prompt
+   >>> 123
+
+   # At this point everything will lock up and you won't get your prompt back
+
+   # Hit a bunch of Control-C's in rapid succession, in most cases
+   # this will break you out of whatever badness you were in and
+   # from that point on everything will behave normally
+
+
+   # This is how you pop up an inspector
+   import FSMInspector
+   inspector = FSMInspector.FSMInspector(fsm, title = fsm.getName())
+
+"""
 
 
 __all__ = ['FSMInspector', 'StateInspector']
 __all__ = ['FSMInspector', 'StateInspector']
 
 
@@ -14,6 +116,7 @@ else:
 
 
 DELTA = (5.0 / 360.) * 2.0 * math.pi
 DELTA = (5.0 / 360.) * 2.0 * math.pi
 
 
+
 class FSMInspector(AppShell):
 class FSMInspector(AppShell):
     # Override class variables
     # Override class variables
     appname = 'ClassicFSM Inspector'
     appname = 'ClassicFSM Inspector'
@@ -445,104 +548,3 @@ class StateInspector(Pmw.MegaArchetype):
 
 
     def exitedState(self):
     def exitedState(self):
         self._canvas.itemconfigure(self.marker, fill = 'CornflowerBlue')
         self._canvas.itemconfigure(self.marker, fill = 'CornflowerBlue')
-
-
-"""
-# USING FINITE STATE INSPECTOR
-
-1)      in your Configrc add:
-
-want-tk #t
-
-2)      start up the show and create a Finite State Machine
-
-from direct.showbase.ShowBaseGlobal import *
-
-from direct.fsm import ClassicFSM
-from direct.fsm import State
-
-def enterState():
-    print 'enterState'
-
-def exitState():
-    print 'exitState'
-
-fsm = ClassicFSM.ClassicFSM('stopLight',
-          [State.State('red', enterState, exitState, ['green']),
-            State.State('yellow', enterState, exitState, ['red']),
-            State.State('green', enterState, exitState, ['yellow'])],
-          'red',
-          'red')
-
-import FSMInspector
-
-inspector = FSMInspector.FSMInspector(fsm, title = fsm.getName())
-
-# Note, the inspectorPos argument is optional, the inspector will
-# automagically position states on startup
-fsm = ClassicFSM.ClassicFSM('stopLight', [
-    State.State('yellow',
-                enterState,
-                exitState,
-                ['red'],
-                inspectorPos = [95.9, 48.0]),
-    State.State('red',
-                enterState,
-                exitState,
-                ['green'],
-                inspectorPos = [0.0, 0.0]),
-    State.State('green',
-                enterState,
-                exitState,
-                ['yellow'],
-                inspectorPos = [0.0, 95.9])],
-        'red',
-        'red')
-
-3)      Pop open a viewer
-
-import FSMInspector
-insp = FSMInspector.FSMInspector(fsm)
-
-or if you wish to be fancy:
-
-insp = FSMInspector.FSMInspector(fsm, title = fsm.getName())
-
-Features:
-   -  Right mouse button over a state pops up a menu allowing you to
-        request a transition to that state
-   -  Middle mouse button will grab the canvas and slide things around
-        if your state machine is bigger than the viewing area
-   -  There are some self explanatory menu options up at the top, the most
-        useful being: "print ClassicFSM layout" which will print out python code
-        which will create an ClassicFSM augmented with layout information for the
-        viewer so everything shows up in the same place the next time you
-        inspect the state machine
-
-CAVEAT:
-
-There is some unexplained problems with using TK and emacs right now which
-occasionally results in everything locking up.  This procedure seems to
-avoid the problem for me:
-
-# Start up the show
-from direct.showbase.ShowBaseGlobal import *
-
-# You will see the window and a Tk panel pop open
-
-# Type a number at the emacs prompt
->>> 123
-
-# At this point everything will lock up and you won't get your prompt back
-
-# Hit a bunch of Control-C's in rapid succession, in most cases
-# this will break you out of whatever badness you were in and
-# from that point on everything will behave normally
-
-
-# This is how you pop up an inspector
-import FSMInspector
-inspector = FSMInspector.FSMInspector(fsm, title = fsm.getName())
-
-"""
-

+ 3 - 0
direct/src/tkpanels/__init__.py

@@ -0,0 +1,3 @@
+"""This package provides various GUI panels useful during Panda3D development
+written using the Tkinter framework.
+"""

+ 1 - 0
direct/src/tkwidgets/__init__.py

@@ -0,0 +1 @@
+"""This package provides various Tkinter widgets."""

+ 6 - 0
dtool/src/dtoolutil/pandaSystem.cxx

@@ -61,6 +61,12 @@ PandaSystem() :
 #else
 #else
   set_system_tag("system", "malloc", "malloc");
   set_system_tag("system", "malloc", "malloc");
 #endif
 #endif
+
+#ifdef _LIBCPP_VERSION
+  set_system_tag("system", "stdlib", "libc++");
+#elif defined(__GLIBCXX__)
+  set_system_tag("system", "stdlib", "libstdc++");
+#endif
 }
 }
 
 
 /**
 /**

+ 8 - 2
makepanda/makepanda.bat

@@ -8,14 +8,20 @@ REM If we can find both, then run 'makepanda'.
 REM
 REM
 
 
 if %PROCESSOR_ARCHITECTURE% == AMD64 (
 if %PROCESSOR_ARCHITECTURE% == AMD64 (
-  set pythondir=win-python-x64
+  set suffix=-x64
 ) else (
 ) else (
-  set pythondir=win-python
+  set suffix=
 )
 )
 
 
 set thirdparty=thirdparty
 set thirdparty=thirdparty
 if defined MAKEPANDA_THIRDPARTY set thirdparty=%MAKEPANDA_THIRDPARTY%
 if defined MAKEPANDA_THIRDPARTY set thirdparty=%MAKEPANDA_THIRDPARTY%
 
 
+if exist %thirdparty%\win-python3.7%suffix%\python.exe (
+  set pythondir=win-python3.7%suffix%
+) else (
+  set pythondir=win-python%suffix%
+)
+
 if not exist makepanda\makepanda.py goto :missing1
 if not exist makepanda\makepanda.py goto :missing1
 if not exist %thirdparty%\%pythondir%\python.exe goto :missing2
 if not exist %thirdparty%\%pythondir%\python.exe goto :missing2
 %thirdparty%\%pythondir%\python.exe makepanda\makepanda.py %*
 %thirdparty%\%pythondir%\python.exe makepanda\makepanda.py %*

+ 0 - 1
makepanda/makepandacore.py

@@ -2321,7 +2321,6 @@ def SdkLocateWindows(version = '7.1'):
                 if not os.path.isdir(os.path.join(platsdk, 'Lib', verstring, 'um')):
                 if not os.path.isdir(os.path.join(platsdk, 'Lib', verstring, 'um')):
                     continue
                     continue
 
 
-                print(verstring)
                 vertuple = tuple(map(int, verstring.split('.')))
                 vertuple = tuple(map(int, verstring.split('.')))
                 if vertuple > max_version:
                 if vertuple > max_version:
                     version = verstring
                     version = verstring

+ 7 - 0
makepanda/makewheel.py

@@ -678,6 +678,13 @@ if __debug__:
         if file.endswith('.py'):
         if file.endswith('.py'):
             whl.write_file('pandac/' + file, os.path.join(pandac_dir, file))
             whl.write_file('pandac/' + file, os.path.join(pandac_dir, file))
 
 
+    # Let's also add the interrogate databases.
+    input_dir = os.path.join(pandac_dir, 'input')
+    if os.path.isdir(input_dir):
+        for file in os.listdir(input_dir):
+            if file.endswith('.in'):
+                whl.write_file('pandac/input/' + file, os.path.join(input_dir, file))
+
     # Add a panda3d-tools directory containing the executables.
     # Add a panda3d-tools directory containing the executables.
     entry_points = '[console_scripts]\n'
     entry_points = '[console_scripts]\n'
     entry_points += 'eggcacher = direct.directscripts.eggcacher:main\n'
     entry_points += 'eggcacher = direct.directscripts.eggcacher:main\n'

+ 5 - 1
panda/src/glstuff/glGraphicsBuffer_src.cxx

@@ -1198,7 +1198,11 @@ bind_slot_multisample(bool rb_resize, Texture **attach, RenderTexturePlane slot,
         if (_fb_properties.get_srgb_color()) {
         if (_fb_properties.get_srgb_color()) {
           gl_format = GL_SRGB8_ALPHA8;
           gl_format = GL_SRGB8_ALPHA8;
         } else if (_fb_properties.get_float_color()) {
         } else if (_fb_properties.get_float_color()) {
-          gl_format = GL_RGBA32F_ARB;
+          if (_fb_properties.get_color_bits() > 16 * 3) {
+            gl_format = GL_RGBA32F_ARB;
+          } else {
+            gl_format = GL_RGBA16F_ARB;
+          }
         } else {
         } else {
           gl_format = GL_RGBA;
           gl_format = GL_RGBA;
         }
         }

+ 3 - 0
panda/src/gobj/geomPrimitive.cxx

@@ -470,6 +470,9 @@ offset_vertices(int offset) {
  * primitive.  Unlike the other version of offset_vertices, this makes the
  * primitive.  Unlike the other version of offset_vertices, this makes the
  * geometry indexed if it isn't already.
  * geometry indexed if it isn't already.
  *
  *
+ * Note that end_row indicates one past the last row that should be offset.
+ * In other words, the number of vertices touched is (end_row - begin_row).
+ *
  * Don't call this in a downstream thread unless you don't mind it blowing
  * Don't call this in a downstream thread unless you don't mind it blowing
  * away other changes you might have recently made in an upstream thread.
  * away other changes you might have recently made in an upstream thread.
  */
  */

+ 1 - 0
panda/src/pstatclient/pStatClient.cxx

@@ -1264,6 +1264,7 @@ get_thread(int index) const {
 
 
 double PStatClient::
 double PStatClient::
 get_real_time() const {
 get_real_time() const {
+  return 0.0;
 }
 }
 
 
 PStatThread PStatClient::
 PStatThread PStatClient::

+ 1 - 1
panda/src/x11display/x11GraphicsWindow.cxx

@@ -886,7 +886,7 @@ set_properties_now(WindowProperties &properties) {
             _input->set_pointer_in_window(event.xbutton.x, event.xbutton.y);
             _input->set_pointer_in_window(event.xbutton.x, event.xbutton.y);
           }
           }
         } else {
         } else {
-          x11display_cat.info()
+          x11display_cat.warning()
             << "XF86DGA extension not available, cannot enable relative mouse mode\n";
             << "XF86DGA extension not available, cannot enable relative mouse mode\n";
           _dga_mouse_enabled = false;
           _dga_mouse_enabled = false;
         }
         }

+ 9 - 0
pandatool/src/deploy-stub/deploy-stub.c

@@ -650,6 +650,15 @@ int main(int argc, char *argv[]) {
   void *blob = NULL;
   void *blob = NULL;
   log_filename = NULL;
   log_filename = NULL;
 
 
+#ifdef __APPLE__
+  // Strip a -psn_xxx argument passed in by macOS when run from an .app bundle.
+  if (argc > 1 && strncmp(argv[1], "-psn_", 5) == 0) {
+    argv[1] = argv[0];
+    ++argv;
+    --argc;
+  }
+#endif
+
   /*
   /*
   printf("blob_offset: %d\n", (int)blobinfo.blob_offset);
   printf("blob_offset: %d\n", (int)blobinfo.blob_offset);
   printf("blob_size: %d\n", (int)blobinfo.blob_size);
   printf("blob_size: %d\n", (int)blobinfo.blob_size);