diff --git a/direct/src/actor/Actor.py b/direct/src/actor/Actor.py index 902c6830d7..f99a18bbdf 100644 --- a/direct/src/actor/Actor.py +++ b/direct/src/actor/Actor.py @@ -104,45 +104,43 @@ class Actor(DirectObject, NodePath): lodNode = None, flattenable = True, setFinal = False, mergeLODBundles = None, allowAsyncBind = 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 method acts like a copy constructor. Single part actors are 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"}) - This could be displayed and animated as such: + This could be displayed and animated as such:: a.reparentTo(render) a.loop("walk") a.stop() 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( # 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 - {"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 a meaningful fashion: + in a meaningful fashion:: a.attach("head", "torso", "joint-head") a.attach("torso", "legs", "joint-hips") @@ -151,7 +149,7 @@ class Actor(DirectObject, NodePath): # ADD LOD COMMENT HERE! # - Other useful Actor class functions: + Other useful Actor class functions:: #fix actor eye rendering a.drawInFront("joint-pupil?", "eyes*") @@ -1135,7 +1133,7 @@ class Actor(DirectObject, NodePath): def getJoints(self, partName = None, jointName = '*', lodName = None): """ Returns the list of all joints, from the named part or from all parts, that match the indicated jointName. The - jointName may include pattern characters like *. """ + jointName may include pattern characters like \\*. """ joints=[] pattern = GlobPattern(jointName) @@ -2439,15 +2437,15 @@ class Actor(DirectObject, NodePath): return ActorInterval.ActorInterval(self, *args, **kw) def getAnimBlends(self, animName=None, partName=None, lodName=None): - """ Returns a list of the form: + """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), ...]), - ...]), - ... ] + [ (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 partName within a particular animation and LOD. """ diff --git a/direct/src/cluster/ClusterClient.py b/direct/src/cluster/ClusterClient.py index 873b03d4ea..969ba9e860 100644 --- a/direct/src/cluster/ClusterClient.py +++ b/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 .ClusterMsgs import * @@ -8,6 +8,7 @@ from direct.showbase import DirectObject from direct.task import Task import os + class ClusterClient(DirectObject.DirectObject): notify = DirectNotifyGlobal.directNotify.newCategory("ClusterClient") MGR_NUM = 1000000 diff --git a/direct/src/cluster/ClusterConfig.py b/direct/src/cluster/ClusterConfig.py index 73e4ebf2b6..a22e98b7db 100644 --- a/direct/src/cluster/ClusterConfig.py +++ b/direct/src/cluster/ClusterConfig.py @@ -1,23 +1,26 @@ 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 = { 'single-server': [{'display name': 'display0', 'display mode': 'client', diff --git a/direct/src/controls/ControlManager.py b/direct/src/controls/ControlManager.py index bc3aad3ff2..6c11f619f6 100755 --- a/direct/src/controls/ControlManager.py +++ b/direct/src/controls/ControlManager.py @@ -15,7 +15,9 @@ from direct.directnotify import DirectNotifyGlobal from direct.task import Task 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: notify = DirectNotifyGlobal.directNotify.newCategory("ControlManager") @@ -52,14 +54,14 @@ class ControlManager: return 'ControlManager: using \'%s\'' % self.currentControlsName 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() 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() call). - See also: use(). + See also: :meth:`use()`. """ assert self.notify.debugCall(id(self)) assert controls is not None @@ -77,15 +79,14 @@ class ControlManager: return self.controls.get(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(, ) 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(, ) call). - See also: add(). + See also: :meth:`add()`. """ assert self.notify.debugCall(id(self)) oldControls = self.controls.pop(name,None) @@ -108,7 +109,7 @@ class ControlManager: Use a previously added control system. - See also: add(). + See also: :meth:`add()`. """ assert self.notify.debugCall(id(self)) if __debug__ and hasattr(self, "ignoreUse"): diff --git a/direct/src/controls/DevWalker.py b/direct/src/controls/DevWalker.py index 03f2f0b0ff..460b6c83f6 100755 --- a/direct/src/controls/DevWalker.py +++ b/direct/src/controls/DevWalker.py @@ -2,15 +2,17 @@ DevWalker.py is for avatars. 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: - - 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. """ diff --git a/direct/src/controls/GhostWalker.py b/direct/src/controls/GhostWalker.py index 4badbf7258..26f4ad8a9f 100755 --- a/direct/src/controls/GhostWalker.py +++ b/direct/src/controls/GhostWalker.py @@ -2,15 +2,17 @@ GhostWalker.py is for avatars. 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: - - 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. """ diff --git a/direct/src/controls/GravityWalker.py b/direct/src/controls/GravityWalker.py index bf3ea8a9bd..28d047d6f0 100755 --- a/direct/src/controls/GravityWalker.py +++ b/direct/src/controls/GravityWalker.py @@ -2,15 +2,17 @@ GravityWalker.py is for avatars. 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: - - 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. """ from direct.directnotify.DirectNotifyGlobal import directNotify diff --git a/direct/src/controls/InputState.py b/direct/src/controls/InputState.py index 68608e9bea..1be5d6fbfe 100755 --- a/direct/src/controls/InputState.py +++ b/direct/src/controls/InputState.py @@ -1,7 +1,6 @@ - - from direct.directnotify import DirectNotifyGlobal from direct.showbase import DirectObject +from direct.showbase.PythonUtil import SerialNumGen # internal class, don't create these on your own class InputStateToken: @@ -136,14 +135,16 @@ class InputState(DirectObject.DirectObject): 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. + 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() + Example:: + + # set up + token = inputState.watch('forward', 'w', 'w-up', inputSource=inputState.WASD) + ... + # tear down + token.release() """ assert self.debugPrint( "watch(name=%s, eventOn=%s, eventOff=%s, startState=%s)"%( @@ -192,15 +193,16 @@ class InputState(DirectObject.DirectObject): """ 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: - # set up - token=inputState.force('forward', True, inputSource='myForwardForcer') - ... - # tear down - token.release() + Example:: + + # set up + token = inputState.force('forward', True, inputSource='myForwardForcer') + ... + # tear down + token.release() """ token = InputStateForceToken(self) self._token2forceInfo[token] = (name, inputSource) diff --git a/direct/src/controls/NonPhysicsWalker.py b/direct/src/controls/NonPhysicsWalker.py index de5a6bfb00..0b9c67f5e6 100755 --- a/direct/src/controls/NonPhysicsWalker.py +++ b/direct/src/controls/NonPhysicsWalker.py @@ -2,15 +2,17 @@ NonPhysicsWalker.py is for avatars. 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: - - 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. """ diff --git a/direct/src/controls/ObserverWalker.py b/direct/src/controls/ObserverWalker.py index f93e0e3325..3fbdaeaed6 100755 --- a/direct/src/controls/ObserverWalker.py +++ b/direct/src/controls/ObserverWalker.py @@ -2,15 +2,17 @@ ObserverWalker.py is for avatars. 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: - - 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. """ diff --git a/direct/src/controls/PhysicsRoller.py b/direct/src/controls/PhysicsRoller.py deleted file mode 100755 index c0510dfc69..0000000000 --- a/direct/src/controls/PhysicsRoller.py +++ /dev/null @@ -1,2 +0,0 @@ -"""PhysicsRoller is for wheels, soccer balls, billiard balls, and other things that roll.""" - diff --git a/direct/src/controls/PhysicsWalker.py b/direct/src/controls/PhysicsWalker.py index de6ebcefa8..3743f25ac9 100755 --- a/direct/src/controls/PhysicsWalker.py +++ b/direct/src/controls/PhysicsWalker.py @@ -2,15 +2,17 @@ PhysicsWalker.py is for avatars. 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: - - 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. """ diff --git a/direct/src/controls/TwoDWalker.py b/direct/src/controls/TwoDWalker.py index b99f6db11b..58fa913959 100644 --- a/direct/src/controls/TwoDWalker.py +++ b/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 * diff --git a/direct/src/directdevices/DirectDeviceManager.py b/direct/src/directdevices/DirectDeviceManager.py index 76206f6c40..1ee3be6507 100644 --- a/direct/src/directdevices/DirectDeviceManager.py +++ b/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 panda3d.core import * diff --git a/direct/src/directdevices/DirectFastrak.py b/direct/src/directdevices/DirectFastrak.py index c6e30bd8fc..adc275ca0b 100644 --- a/direct/src/directdevices/DirectFastrak.py +++ b/direct/src/directdevices/DirectFastrak.py @@ -20,9 +20,9 @@ class DirectFastrak(DirectObject): fastrakCount = 0 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 - if base.direct.deviceManager == None: + if base.direct.deviceManager is None: base.direct.deviceManager = DirectDeviceManager() # Set name diff --git a/direct/src/directnotify/DirectNotifyGlobal.py b/direct/src/directnotify/DirectNotifyGlobal.py index e25ddb11ad..96b10224ac 100644 --- a/direct/src/directnotify/DirectNotifyGlobal.py +++ b/direct/src/directnotify/DirectNotifyGlobal.py @@ -1,8 +1,12 @@ -"""instantiate global DirectNotify used in Direct""" +"""Instantiates global DirectNotify used in Direct.""" __all__ = ['directNotify', 'giveNotify'] from . import DirectNotify +#: The global :class:`~.DirectNotify.DirectNotify` object. directNotify = DirectNotify.DirectNotify() + +#: Shorthand function for adding a DirectNotify category to a given class +#: object. Alias of `.DirectNotify.DirectNotify.giveNotify`. giveNotify = directNotify.giveNotify diff --git a/direct/src/directnotify/LoggerGlobal.py b/direct/src/directnotify/LoggerGlobal.py index 610a009a8f..9fe05b3957 100644 --- a/direct/src/directnotify/LoggerGlobal.py +++ b/direct/src/directnotify/LoggerGlobal.py @@ -1,5 +1,6 @@ -"""instantiate global Logger object""" +"""Instantiates a global :class:`~.Logger.Logger` object.""" from . import Logger +#: Contains a global :class:`~.Logger.Logger` object. defaultLogger = Logger.Logger() diff --git a/direct/src/directnotify/Notifier.py b/direct/src/directnotify/Notifier.py index 35a916f8b3..571d7f43b0 100644 --- a/direct/src/directnotify/Notifier.py +++ b/direct/src/directnotify/Notifier.py @@ -8,6 +8,7 @@ from panda3d.core import ConfigVariableBool, NotifyCategory, StreamWriter, Notif import time import sys + class Notifier: serverDelta = 0 @@ -23,12 +24,11 @@ class Notifier: 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 diff --git a/direct/src/directnotify/RotatingLog.py b/direct/src/directnotify/RotatingLog.py index e663da67ac..5eb5706c95 100755 --- a/direct/src/directnotify/RotatingLog.py +++ b/direct/src/directnotify/RotatingLog.py @@ -1,8 +1,7 @@ - - import os import time + class RotatingLog: """ 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): """ - 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: - 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: - self.sizeLimit=megabyteLimit*1024*1024 + self.sizeLimit = megabyteLimit*1024*1024 def __del__(self): self.close() diff --git a/direct/src/directutil/Verify.py b/direct/src/directutil/Verify.py index ce23b1daa9..20a450ea92 100755 --- a/direct/src/directutil/Verify.py +++ b/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. +You can use :func:`verify()` just like assert, with these small differences: -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 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). 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 (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 -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): @@ -54,7 +59,7 @@ def verify(assertion): if not assertion: print("\n\nverify failed:") import sys - print(" File \"%s\", line %d"%( + print(" File \"%s\", line %d" % ( sys._getframe(1).f_code.co_filename, sys._getframe(1).f_lineno)) if wantVerifyPdb: @@ -62,5 +67,6 @@ def verify(assertion): pdb.set_trace() raise AssertionError + if not hasattr(__builtins__, "verify"): __builtins__["verify"] = verify diff --git a/direct/src/dist/__init__.py b/direct/src/dist/__init__.py index e69de29bb2..882eaf97c9 100644 --- a/direct/src/dist/__init__.py +++ b/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. +""" diff --git a/direct/src/dist/commands.py b/direct/src/dist/commands.py index b6a6617fad..25e22e52fb 100644 --- a/direct/src/dist/commands.py +++ b/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 import collections diff --git a/direct/src/distributed/AsyncRequest.py b/direct/src/distributed/AsyncRequest.py index 1d30259a6e..10e00f3765 100755 --- a/direct/src/distributed/AsyncRequest.py +++ b/direct/src/distributed/AsyncRequest.py @@ -15,7 +15,7 @@ if __debug__: 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 read or created, or if you only have one object that you need you can diff --git a/direct/src/distributed/DistributedObject.py b/direct/src/distributed/DistributedObject.py index 64fd3ecc20..84870b890c 100644 --- a/direct/src/distributed/DistributedObject.py +++ b/direct/src/distributed/DistributedObject.py @@ -26,11 +26,12 @@ ESNum2Str = { ESGenerated: 'ESGenerated', } + class DistributedObject(DistributedObjectBase): """ The Distributed Object class is the base class for all network based (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") diff --git a/direct/src/distributed/DistributedObjectBase.py b/direct/src/distributed/DistributedObjectBase.py index 3d9b525067..066466df1d 100755 --- a/direct/src/distributed/DistributedObjectBase.py +++ b/direct/src/distributed/DistributedObjectBase.py @@ -1,11 +1,12 @@ from direct.showbase.DirectObject import DirectObject from direct.directnotify.DirectNotifyGlobal import directNotify + class DistributedObjectBase(DirectObject): """ The Distributed Object class is the base class for all network based (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") diff --git a/direct/src/distributed/DoCollectionManager.py b/direct/src/distributed/DoCollectionManager.py index fcff96e218..9dbf794dce 100755 --- a/direct/src/distributed/DoCollectionManager.py +++ b/direct/src/distributed/DoCollectionManager.py @@ -5,6 +5,7 @@ import re BAD_DO_ID = BAD_ZONE_ID = 0 # 0xFFFFFFFF BAD_CHANNEL_ID = 0 # 0xFFFFFFFFFFFFFFFF + class DoCollectionManager: def __init__(self): # Dict of {DistributedObject ids: DistributedObjects} @@ -186,7 +187,6 @@ class DoCollectionManager: strToReturn = '%s%s' % (strToReturn, self._returnObjects(self.getDoTable(ownerView=False))) return strToReturn - def printObjectCount(self): # print object counts by distributed object type print('==== OBJECT COUNT ====') @@ -199,13 +199,14 @@ class DoCollectionManager: 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; otherwise the list is filtered to only include objects of that type. diff --git a/direct/src/distributed/DoHierarchy.py b/direct/src/distributed/DoHierarchy.py index e06572ab2e..c7283f4ac2 100755 --- a/direct/src/distributed/DoHierarchy.py +++ b/direct/src/distributed/DoHierarchy.py @@ -26,15 +26,14 @@ class DoHierarchy: 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; otherwise the list is filtered to only include objects of that type. diff --git a/direct/src/distributed/PyDatagram.py b/direct/src/distributed/PyDatagram.py index f45c3c52c1..1f1f56ad16 100755 --- a/direct/src/distributed/PyDatagram.py +++ b/direct/src/distributed/PyDatagram.py @@ -9,6 +9,7 @@ from panda3d.direct import * from direct.distributed.MsgTypes import * + class PyDatagram(Datagram): # This is a little helper Dict to replace the huge statement @@ -29,8 +30,6 @@ class PyDatagram(Datagram): STBlob32: (Datagram.addBlob32, None), } - #def addChannel(self, channelId): - # ... addChannel = Datagram.addUint64 def addServerHeader(self, channel, sender, code): @@ -39,14 +38,12 @@ class PyDatagram(Datagram): self.addChannel(sender) self.addUint16(code) - def addOldServerHeader(self, channel, sender, code): self.addChannel(channel) self.addChannel(sender) self.addChannel('A') self.addUint16(code) - def addServerControlHeader(self, code): self.addInt8(1) self.addChannel(CONTROL_CHANNEL) diff --git a/direct/src/distributed/PyDatagramIterator.py b/direct/src/distributed/PyDatagramIterator.py index 6ce96e77e4..bd27469c41 100755 --- a/direct/src/distributed/PyDatagramIterator.py +++ b/direct/src/distributed/PyDatagramIterator.py @@ -7,6 +7,7 @@ from panda3d.core import * from panda3d.direct import * # Import the type numbers + class PyDatagramIterator(DatagramIterator): # This is a little helper Dict to replace the huge statement diff --git a/direct/src/distributed/ServerRepository.py b/direct/src/distributed/ServerRepository.py index 894dfb1349..966d22923b 100644 --- a/direct/src/distributed/ServerRepository.py +++ b/direct/src/distributed/ServerRepository.py @@ -137,7 +137,7 @@ class ServerRepository: # An allocator object that assigns the next doIdBase to each # client. - self.idAllocator = UniqueIdAllocator(0, 0xffffffff / self.doIdRange) + self.idAllocator = UniqueIdAllocator(0, 0xffffffff // self.doIdRange) self.dcFile = DCFile() self.dcSuffix = '' diff --git a/direct/src/fsm/ClassicFSM.py b/direct/src/fsm/ClassicFSM.py index c954718e4d..18c0bd6a4d 100644 --- a/direct/src/fsm/ClassicFSM.py +++ b/direct/src/fsm/ClassicFSM.py @@ -1,9 +1,8 @@ """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'] @@ -14,12 +13,14 @@ import weakref if __debug__: _debugFsms = {} + def printDebugFsmList(): global _debugFsms for k in sorted(_debugFsms.keys()): print("%s %s" % (k, _debugFsms[k]())) __builtins__['debugFsmList'] = printDebugFsmList + class ClassicFSM(DirectObject): """ Finite State Machine class. @@ -45,14 +46,14 @@ class ClassicFSM(DirectObject): """__init__(self, string, State[], string, string, int) ClassicFSM constructor: takes name, list of states, initial state and - final state as: + 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') + 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, is optional; if left out (or explicitly specified to be diff --git a/direct/src/fsm/FSM.py b/direct/src/fsm/FSM.py index 6af064e3d7..eb67f55c0a 100644 --- a/direct/src/fsm/FSM.py +++ b/direct/src/fsm/FSM.py @@ -1,5 +1,8 @@ """The new Finite State Machine module. This replaces the module 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'] @@ -14,12 +17,15 @@ from direct.stdpy.threading import RLock class FSMException(Exception): pass + class AlreadyInTransition(FSMException): pass + class RequestDenied(FSMException): pass + class FSM(DirectObject): """ 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 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 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 is often empty), and the return value should either be None to do 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 omitted, the defaultFilter() method is called instead. A standard @@ -111,7 +117,7 @@ class FSM(DirectObject): at construction time; it is simply in Off already by convention. If you need to call code in enterOff() to initialize your FSM properly, call it explicitly in the constructor. Similarly, when - cleanup() is called or the FSM is destructed, the FSM transitions + `cleanup()` is called or the FSM is destructed, the FSM transitions back to 'Off' by convention. (It does call enterOff() at this point, but does not call exitOff().) @@ -255,9 +261,9 @@ class FSM(DirectObject): def demand(self, request, *args): """Requests a state transition, by code that does not expect the request to be denied. If the request is denied, raises a - RequestDenied exception. + `RequestDenied` exception. - Unlike request(), this method allows a new request to be made + Unlike `request()`, this method allows a new request to be made while the FSM is currently in transition. In this case, the request is queued up and will be executed when the current transition finishes. Multiple requests will queue up in @@ -284,7 +290,7 @@ class FSM(DirectObject): """Requests a state transition (or other behavior). The request may be denied by the FSM's filter function. If it is denied, the filter function may either raise an exception - (RequestDenied), or it may simply return None, without + (`RequestDenied`), or it may simply return None, without changing the FSM's state. The request parameter should be a string. The request, along @@ -299,7 +305,7 @@ class FSM(DirectObject): If the FSM is currently in transition (i.e. in the middle of executing an enterState or exitState function), an - AlreadyInTransition exception is raised (but see demand(), + `AlreadyInTransition` exception is raised (but see `demand()`, which will queue these requests up and apply when the transition is complete).""" @@ -395,7 +401,6 @@ class FSM(DirectObject): return (request,) + args return self.defaultFilter(request, args) - def setStateArray(self, stateArray): """array of unique states to iterate through""" self.fsmLock.acquire() @@ -404,7 +409,6 @@ class FSM(DirectObject): finally: self.fsmLock.release() - def requestNext(self, *args): """Request the 'next' state in the predefined state array.""" self.fsmLock.acquire() diff --git a/direct/src/fsm/FourState.py b/direct/src/fsm/FourState.py index 5188284baf..65a5b5b807 100755 --- a/direct/src/fsm/FourState.py +++ b/direct/src/fsm/FourState.py @@ -44,25 +44,22 @@ class FourState: 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',] - e.g. 2: ['off', 'locking', 'locked', 'unlocking', 'unlocked',] - e.g. 3: ['off', 'deactivating', 'deactive', 'activating', 'activated',] durations is a list of time values (floats) or None values. Each list must have five entries. - More Details + .. rubric:: More Details Here is a diagram showing the where the names from the list - are used: + are used:: +---------+ | 0 (off) |----> (any other state and vice versa). diff --git a/direct/src/fsm/FourStateAI.py b/direct/src/fsm/FourStateAI.py index a17b9e4c62..09c83e7486 100755 --- a/direct/src/fsm/FourStateAI.py +++ b/direct/src/fsm/FourStateAI.py @@ -45,27 +45,25 @@ class FourStateAI: 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. - 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 - are used: + are used:: +---------+ | 0 (off) |----> (any other state and vice versa). diff --git a/direct/src/fsm/__init__.py b/direct/src/fsm/__init__.py index 365e539c4e..83f9f7414e 100644 --- a/direct/src/fsm/__init__.py +++ b/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 between several defined states. These are useful for a range of logic programming tasks. + +For more information on FSMs, consult the :ref:`finite-state-machines` section +of the programming manual. """ diff --git a/direct/src/gui/DirectButton.py b/direct/src/gui/DirectButton.py index e6111af710..5cf8a91e9e 100644 --- a/direct/src/gui/DirectButton.py +++ b/direct/src/gui/DirectButton.py @@ -1,4 +1,8 @@ -"""This module contains the DirectButton class.""" +"""This module contains the DirectButton class. + +See the :ref:`directbutton` page in the programming manual for a more +in-depth explanation and an example of how to use this class. +""" __all__ = ['DirectButton'] diff --git a/direct/src/gui/DirectCheckButton.py b/direct/src/gui/DirectCheckButton.py index f353f3d989..ed74c3eef7 100644 --- a/direct/src/gui/DirectCheckButton.py +++ b/direct/src/gui/DirectCheckButton.py @@ -1,6 +1,10 @@ """A DirectCheckButton is a type of button that toggles between two states when clicked. It also has a separate indicator that can be modified -separately.""" +separately. + +See the :ref:`directcheckbutton` page in the programming manual for a more +in-depth explanation and an example of how to use this class. +""" __all__ = ['DirectCheckButton'] diff --git a/direct/src/gui/DirectDialog.py b/direct/src/gui/DirectDialog.py index ba56fdbfa0..b6401c7a7a 100644 --- a/direct/src/gui/DirectDialog.py +++ b/direct/src/gui/DirectDialog.py @@ -1,6 +1,13 @@ -"""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'] +See the :ref:`directdialog` page in the programming manual for a more +in-depth explanation and an example of how to use this class. +""" + +__all__ = [ + 'findDialog', 'cleanupDialog', 'DirectDialog', 'OkDialog', + 'OkCancelDialog', 'YesNoDialog', 'YesNoCancelDialog', 'RetryCancelDialog', +] from panda3d.core import * from direct.showbase import ShowBaseGlobal @@ -9,9 +16,9 @@ from .DirectFrame import * from .DirectButton import * import types -def findDialog(uniqueName): - """findPanel(string uniqueName) +def findDialog(uniqueName): + """ Returns the panel whose uniqueName is given. This is mainly useful for debugging, to get a pointer to the current onscreen panel of a particular type. @@ -20,6 +27,7 @@ def findDialog(uniqueName): return DirectDialog.AllDialogs[uniqueName] return None + def cleanupDialog(uniqueName): """cleanupPanel(string uniqueName) @@ -35,52 +43,48 @@ def cleanupDialog(uniqueName): # self.cleanup() directly DirectDialog.AllDialogs[uniqueName].cleanup() + class DirectDialog(DirectFrame): AllDialogs = {} 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: - 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 diff --git a/direct/src/gui/DirectEntry.py b/direct/src/gui/DirectEntry.py index d614a3825a..d870bf0c7e 100644 --- a/direct/src/gui/DirectEntry.py +++ b/direct/src/gui/DirectEntry.py @@ -1,5 +1,9 @@ """Contains the DirectEntry class, a type of DirectGUI widget that accepts -text entered using the keyboard.""" +text entered using the keyboard. + +See the :ref:`directentry` page in the programming manual for a more in-depth +explanation and an example of how to use this class. +""" __all__ = ['DirectEntry'] diff --git a/direct/src/gui/DirectFrame.py b/direct/src/gui/DirectFrame.py index 684fda005c..19e8717ba6 100644 --- a/direct/src/gui/DirectFrame.py +++ b/direct/src/gui/DirectFrame.py @@ -11,6 +11,9 @@ A DirectFrame can have: Each of these has 1 or more states. The same object can be used for all states or each state can have a different text/geom/image (for radio button and check button indicators, for example). + +See the :ref:`directframe` page in the programming manual for a more in-depth +explanation and an example of how to use this class. """ __all__ = ['DirectFrame'] diff --git a/direct/src/gui/DirectGui.py b/direct/src/gui/DirectGui.py index e0a59abb82..4b6d62b9db 100644 --- a/direct/src/gui/DirectGui.py +++ b/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 .OnscreenText import * diff --git a/direct/src/gui/DirectGuiBase.py b/direct/src/gui/DirectGuiBase.py index 3b47ac1973..fecfea608a 100644 --- a/direct/src/gui/DirectGuiBase.py +++ b/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. -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) - 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] } - 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 + 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() + 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:: -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] } + 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 self._constructorKeywords dictionary (for use by component constructors), 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 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 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. 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: - '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 _constructorKeywords (those not transferred to _optionInfo by @@ -71,9 +79,9 @@ Code Overview: component. If any constructor keywords remain at the end of component 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'] diff --git a/direct/src/gui/DirectGuiGlobals.py b/direct/src/gui/DirectGuiGlobals.py index 96519fd9fc..59fd474900 100644 --- a/direct/src/gui/DirectGuiGlobals.py +++ b/direct/src/gui/DirectGuiGlobals.py @@ -17,8 +17,9 @@ drawOrder = 100 panel = None # 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'] # Mouse buttons @@ -158,7 +159,3 @@ def getDefaultPanel(): def setDefaultPanel(newPanel): global panel panel = newPanel - -#from OnscreenText import * -#from OnscreenGeom import * -#from OnscreenImage import * diff --git a/direct/src/gui/DirectLabel.py b/direct/src/gui/DirectLabel.py index bc1328ded7..aa965bbe61 100644 --- a/direct/src/gui/DirectLabel.py +++ b/direct/src/gui/DirectLabel.py @@ -1,4 +1,8 @@ -"""Contains the DirectLabel class.""" +"""Contains the DirectLabel class. + +See the :ref:`directlabel` page in the programming manual for a more in-depth +explanation and an example of how to use this class. +""" __all__ = ['DirectLabel'] diff --git a/direct/src/gui/DirectOptionMenu.py b/direct/src/gui/DirectOptionMenu.py index cbf3a2974e..fe417cfd9f 100644 --- a/direct/src/gui/DirectOptionMenu.py +++ b/direct/src/gui/DirectOptionMenu.py @@ -1,13 +1,19 @@ -"""Implements a pop-up menu containing multiple clickable options.""" +"""Implements a pop-up menu containing multiple clickable options. + +See the :ref:`directoptionmenu` page in the programming manual for a more +in-depth explanation and an example of how to use this class. +""" __all__ = ['DirectOptionMenu'] from panda3d.core import * +from direct.showbase import ShowBaseGlobal from . import DirectGuiGlobals as DGG from .DirectButton import * from .DirectLabel import * from .DirectFrame import * + class DirectOptionMenu(DirectButton): """ DirectOptionMenu(parent) - Create a DirectButton which pops up a @@ -68,6 +74,10 @@ class DirectOptionMenu(DirectButton): self.popupMenu = None self.selectedIndex = None self.highlightedIndex = None + if 'item_text_scale' in kw: + self._prevItemTextScale = kw['item_text_scale'] + else: + self._prevItemTextScale = (1,1) # A big screen encompassing frame to catch the cancel clicks self.cancelFrame = self.createcomponent( 'cancelframe', (), None, @@ -214,27 +224,27 @@ class DirectOptionMenu(DirectButton): self.popupMenu.setZ( self, self.minZ + (self.selectedIndex + 1)*self.maxHeight) # Make sure the whole popup menu is visible - pos = self.popupMenu.getPos(render2d) - scale = self.popupMenu.getScale(render2d) + pos = self.popupMenu.getPos(ShowBaseGlobal.render2d) + scale = self.popupMenu.getScale(ShowBaseGlobal.render2d) # How are we doing relative to the right side of the screen maxX = pos[0] + fb[1] * scale[0] if maxX > 1.0: # Need to move menu to the left - self.popupMenu.setX(render2d, pos[0] + (1.0 - maxX)) + self.popupMenu.setX(ShowBaseGlobal.render2d, pos[0] + (1.0 - maxX)) # How about up and down? minZ = pos[2] + fb[2] * scale[2] maxZ = pos[2] + fb[3] * scale[2] if minZ < -1.0: # Menu too low, move it up - self.popupMenu.setZ(render2d, pos[2] + (-1.0 - minZ)) + self.popupMenu.setZ(ShowBaseGlobal.render2d, pos[2] + (-1.0 - minZ)) elif maxZ > 1.0: # Menu too high, move it down - self.popupMenu.setZ(render2d, pos[2] + (1.0 - maxZ)) + self.popupMenu.setZ(ShowBaseGlobal.render2d, pos[2] + (1.0 - maxZ)) # Also display cancel frame to catch clicks outside of the popup self.cancelFrame.show() # Position and scale cancel frame to fill entire window - self.cancelFrame.setPos(render2d, 0, 0, 0) - self.cancelFrame.setScale(render2d, 1, 1, 1) + self.cancelFrame.setPos(ShowBaseGlobal.render2d, 0, 0, 0) + self.cancelFrame.setScale(ShowBaseGlobal.render2d, 1, 1, 1) def hidePopupMenu(self, event = None): """ Put away popup and cancel frame """ @@ -243,6 +253,7 @@ class DirectOptionMenu(DirectButton): def _highlightItem(self, item, index): """ Set frame color of highlighted item, record index """ + self._prevItemTextScale = item['text_scale'] item['frameColor'] = self['highlightColor'] item['frameSize'] = (self['highlightScale'][0]*self.minX, self['highlightScale'][0]*self.maxX, self['highlightScale'][1]*self.minZ, self['highlightScale'][1]*self.maxZ) item['text_scale'] = self['highlightScale'] @@ -252,7 +263,7 @@ class DirectOptionMenu(DirectButton): """ Clear frame color, clear highlightedIndex """ item['frameColor'] = frameColor item['frameSize'] = (self.minX, self.maxX, self.minZ, self.maxZ) - item['text_scale'] = (1,1) + item['text_scale'] = self._prevItemTextScale self.highlightedIndex = None def selectHighlightedIndex(self, event = None): diff --git a/direct/src/gui/DirectRadioButton.py b/direct/src/gui/DirectRadioButton.py index f6543d1e8f..c679eff334 100755 --- a/direct/src/gui/DirectRadioButton.py +++ b/direct/src/gui/DirectRadioButton.py @@ -1,7 +1,11 @@ """A DirectRadioButton is a type of button that, similar to a DirectCheckButton, has a separate indicator and can be toggled between two states. However, only one DirectRadioButton in a group can be enabled -at a particular time.""" +at a particular time. + +See the :ref:`directradiobutton` page in the programming manual for a more +in-depth explanation and an example of how to use this class. +""" __all__ = ['DirectRadioButton'] diff --git a/direct/src/gui/DirectScrollBar.py b/direct/src/gui/DirectScrollBar.py index d56385163b..a873924456 100644 --- a/direct/src/gui/DirectScrollBar.py +++ b/direct/src/gui/DirectScrollBar.py @@ -1,4 +1,8 @@ -"""Defines the DirectScrollBar class.""" +"""Defines the DirectScrollBar class. + +See the :ref:`directscrollbar` page in the programming manual for a more +in-depth explanation and an example of how to use this class. +""" __all__ = ['DirectScrollBar'] diff --git a/direct/src/gui/DirectScrolledFrame.py b/direct/src/gui/DirectScrolledFrame.py index 2c60ed950f..7b30ba499e 100644 --- a/direct/src/gui/DirectScrolledFrame.py +++ b/direct/src/gui/DirectScrolledFrame.py @@ -1,4 +1,8 @@ -"""Contains the DirectScrolledFrame class.""" +"""Contains the DirectScrolledFrame class. + +See the :ref:`directscrolledframe` page in the programming manual for a more +in-depth explanation and an example of how to use this class. +""" __all__ = ['DirectScrolledFrame'] @@ -82,6 +86,9 @@ class DirectScrolledFrame(DirectFrame): self.guiItem.setVirtualFrame(f[0], f[1], f[2], f[3]) def getCanvas(self): + """Returns the NodePath of the virtual canvas. Nodes parented to this + canvas will show inside the scrolled area. + """ return self.canvas def setManageScrollBars(self): diff --git a/direct/src/gui/DirectScrolledList.py b/direct/src/gui/DirectScrolledList.py index ca7551d2c9..5bade6caf9 100644 --- a/direct/src/gui/DirectScrolledList.py +++ b/direct/src/gui/DirectScrolledList.py @@ -1,4 +1,8 @@ -"""Contains the DirectScrolledList class.""" +"""Contains the DirectScrolledList class. + +See the :ref:`directscrolledlist` page in the programming manual for a more +in-depth explanation and an example of how to use this class. +""" __all__ = ['DirectScrolledListItem', 'DirectScrolledList'] diff --git a/direct/src/gui/DirectSlider.py b/direct/src/gui/DirectSlider.py index 507c13b5a8..ddcd35d05d 100644 --- a/direct/src/gui/DirectSlider.py +++ b/direct/src/gui/DirectSlider.py @@ -1,4 +1,8 @@ -"""Defines the DirectSlider class.""" +"""Defines the DirectSlider class. + +See the :ref:`directslider` page in the programming manual for a more +in-depth explanation and an example of how to use this class. +""" __all__ = ['DirectSlider'] diff --git a/direct/src/gui/DirectWaitBar.py b/direct/src/gui/DirectWaitBar.py index ff3f554fee..55785152cf 100644 --- a/direct/src/gui/DirectWaitBar.py +++ b/direct/src/gui/DirectWaitBar.py @@ -1,4 +1,8 @@ -"""Contains the DirectWaitBar class, a progress bar widget.""" +"""Contains the DirectWaitBar class, a progress bar widget. + +See the :ref:`directwaitbar` page in the programming manual for a more +in-depth explanation and an example of how to use this class. +""" __all__ = ['DirectWaitBar'] diff --git a/direct/src/gui/OnscreenImage.py b/direct/src/gui/OnscreenImage.py index 026076fc69..3d8a9b0633 100644 --- a/direct/src/gui/OnscreenImage.py +++ b/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'] @@ -21,14 +25,15 @@ class OnscreenImage(DirectObject, NodePath): parent = None, 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. - 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. This maybe a 3-tuple of floats or a vector. diff --git a/direct/src/gui/OnscreenText.py b/direct/src/gui/OnscreenText.py index c3accf191d..4491c918e9 100644 --- a/direct/src/gui/OnscreenText.py +++ b/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'] @@ -41,7 +45,7 @@ class OnscreenText(NodePath): Make a text node from string, put it into the 2d sg and set it up with all the indicated parameters. - The parameters are as follows: + Parameters: text: the actual text to display. This may be omitted and specified later via setText() if you don't have it diff --git a/direct/src/gui/__init__.py b/direct/src/gui/__init__.py index 5cc1895b62..4e55aa3655 100644 --- a/direct/src/gui/__init__.py +++ b/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. It is based on the lower-level PGui system, which is implemented in diff --git a/direct/src/interval/ActorInterval.py b/direct/src/interval/ActorInterval.py index 6ac026497b..8d5044c2b5 100644 --- a/direct/src/interval/ActorInterval.py +++ b/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'] diff --git a/direct/src/interval/ParticleInterval.py b/direct/src/interval/ParticleInterval.py index b5a3b09b73..de1f533b63 100644 --- a/direct/src/interval/ParticleInterval.py +++ b/direct/src/interval/ParticleInterval.py @@ -34,26 +34,22 @@ class ParticleInterval(Interval): cleanup = False, 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 diff --git a/direct/src/interval/__init__.py b/direct/src/interval/__init__.py index 5f3e8e4332..46dc3f0c2a 100644 --- a/direct/src/interval/__init__.py +++ b/direct/src/interval/__init__.py @@ -9,4 +9,6 @@ All interval types can be conveniently imported from the :mod:`.IntervalGlobal` module:: from direct.interval.IntervalGlobal import * + +For more information about intervals, see the :ref:`intervals` manual page. """ diff --git a/direct/src/leveleditor/ActionMgr.py b/direct/src/leveleditor/ActionMgr.py index dcc5b7ff91..6ccda3e51e 100755 --- a/direct/src/leveleditor/ActionMgr.py +++ b/direct/src/leveleditor/ActionMgr.py @@ -1,4 +1,5 @@ from panda3d.core import * +from direct.showbase.PythonUtil import Functor from . import ObjectGlobals as OG class ActionMgr: diff --git a/direct/src/particles/Particles.py b/direct/src/particles/Particles.py index d05700c3b1..adbfc8bf3f 100644 --- a/direct/src/particles/Particles.py +++ b/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.physics import PhysicalNode @@ -29,6 +35,7 @@ from . import SpriteParticleRendererExt from direct.directnotify.DirectNotifyGlobal import directNotify import sys + class Particles(ParticleSystem): notify = directNotify.newCategory('Particles') 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)])) - def accelerate(self,time,stepCount = 1,stepTime=0.0): if time > 0.0: if stepTime == 0.0: diff --git a/direct/src/particles/__init__.py b/direct/src/particles/__init__.py index d3525ee464..f739d016c1 100644 --- a/direct/src/particles/__init__.py +++ b/direct/src/particles/__init__.py @@ -4,4 +4,7 @@ system. Also see the :mod:`panda3d.physics` module, which contains the C++ implementation of the particle system. + +For more information about the particle system, see the :ref:`particle-effects` +page in the manual. """ diff --git a/direct/src/showbase/AppRunnerGlobal.py b/direct/src/showbase/AppRunnerGlobal.py index f5663b5ef5..81f56c9562 100644 --- a/direct/src/showbase/AppRunnerGlobal.py +++ b/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 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__: 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 diff --git a/direct/src/showbase/BufferViewer.py b/direct/src/showbase/BufferViewer.py index aced71061e..0c43063bc8 100644 --- a/direct/src/showbase/BufferViewer.py +++ b/direct/src/showbase/BufferViewer.py @@ -1,6 +1,16 @@ """Contains the BufferViewer class, which is used as a debugging aid 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'] @@ -11,6 +21,7 @@ from direct.directnotify.DirectNotifyGlobal import * from direct.showbase.DirectObject import DirectObject import math + class BufferViewer(DirectObject): notify = directNotify.newCategory('BufferViewer') @@ -100,11 +111,13 @@ class BufferViewer(DirectObject): def setPosition(self, pos): """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'.""" valid=["llcorner","lrcorner","ulcorner","urcorner","window"] if (valid.count(pos)==0): @@ -119,11 +132,13 @@ class BufferViewer(DirectObject): def setLayout(self, lay): """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'.""" valid=["vline","hline","vgrid","hgrid","cycle"] if (valid.count(lay)==0): diff --git a/direct/src/showbase/BulletinBoardGlobal.py b/direct/src/showbase/BulletinBoardGlobal.py index dd750d9a22..1dd41e9a24 100755 --- a/direct/src/showbase/BulletinBoardGlobal.py +++ b/direct/src/showbase/BulletinBoardGlobal.py @@ -1,7 +1,8 @@ -"""instantiate global BulletinBoard object""" +"""Instantiates the global :class:`~.BulletinBoard.BulletinBoard` object.""" __all__ = ['bulletinBoard'] from . import BulletinBoard +#: The global :class:`~.BulletinBoard.BulletinBoard` object. bulletinBoard = BulletinBoard.BulletinBoard() diff --git a/direct/src/showbase/DistancePhasedNode.py b/direct/src/showbase/DistancePhasedNode.py index a1bd879af0..385f94ae70 100755 --- a/direct/src/showbase/DistancePhasedNode.py +++ b/direct/src/showbase/DistancePhasedNode.py @@ -3,16 +3,18 @@ from direct.directnotify.DirectNotifyGlobal import directNotify from panda3d.core import * from .PhasedObject import PhasedObject + 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 scene graph, or even inherit from this class to get its functionality. What you will need to define to use this class: + - The distances at which you want the phases to load/unload - Whether you want the object to clean itself up or not when 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 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 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 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") @@ -118,7 +121,6 @@ class DistancePhasedNode(PhasedObject, DirectObject, NodePath): def __str__(self): return '%s in phase \'%s\'' % (NodePath.__str__(self), self.getPhase()) - def cleanup(self): """ Disables all collisions. @@ -262,9 +264,9 @@ class BufferedDistancePhasedNode(DistancePhasedNode): border. 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") diff --git a/direct/src/showbase/ExceptionVarDump.py b/direct/src/showbase/ExceptionVarDump.py index 42aca9b7c9..90fa052a0c 100755 --- a/direct/src/showbase/ExceptionVarDump.py +++ b/direct/src/showbase/ExceptionVarDump.py @@ -179,6 +179,7 @@ def _excepthookDumpVars(eType, eValue, tb): oldExcepthook(eType, eValue, origTb) def install(log, upload): + """Installs the exception hook.""" global oldExcepthook global wantStackDumpLog global wantStackDumpUpload diff --git a/direct/src/showbase/GarbageReportScheduler.py b/direct/src/showbase/GarbageReportScheduler.py index 4705cd3344..e26da9ccc5 100755 --- a/direct/src/showbase/GarbageReportScheduler.py +++ b/direct/src/showbase/GarbageReportScheduler.py @@ -1,7 +1,9 @@ from direct.showbase.GarbageReport import GarbageReport + 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): # waitBetween is in seconds # waitScale is a multiplier for the waitBetween every time around @@ -30,6 +32,7 @@ class GarbageReportScheduler: self._taskName) # and increase the delay every time around self._waitBetween = self._waitBetween * self._waitScale + def _runGarbageReport(self, task): # 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 diff --git a/direct/src/showbase/InputStateGlobal.py b/direct/src/showbase/InputStateGlobal.py index ff04825133..802e77e089 100644 --- a/direct/src/showbase/InputStateGlobal.py +++ b/direct/src/showbase/InputStateGlobal.py @@ -1,4 +1,4 @@ -"""instantiate global InputState object""" +"""Instantiates the global :class:`~.InputState.InputState` object.""" __all__ = ['inputState'] @@ -7,4 +7,5 @@ __all__ = ['inputState'] from direct.controls import InputState +#: The global :class:`~.InputState.InputState` object. inputState = InputState.InputState() diff --git a/direct/src/showbase/Job.py b/direct/src/showbase/Job.py index 02407b29c4..01b7a0c8b7 100755 --- a/direct/src/showbase/Job.py +++ b/direct/src/showbase/Job.py @@ -3,14 +3,22 @@ from direct.showbase.DirectObject import DirectObject if __debug__: from panda3d.core import PStatCollector -class Job(DirectObject): - # Base class for cpu-intensive or non-time-critical operations that - # are run through the JobManager. - # values to yield from your run() generator method +class Job(DirectObject): + """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. + """ + + #: Yielded from the `run()` generator method when the job is done. 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 # 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 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") def getPriority(self): @@ -57,23 +66,22 @@ class Job(DirectObject): self._printing = False 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): - # 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): self._finished = True diff --git a/direct/src/showbase/JobManagerGlobal.py b/direct/src/showbase/JobManagerGlobal.py index 767f4ce342..4952a31462 100755 --- a/direct/src/showbase/JobManagerGlobal.py +++ b/direct/src/showbase/JobManagerGlobal.py @@ -2,4 +2,5 @@ __all__ = ['jobMgr'] from . import JobManager +#: Contains the global :class:`~.JobManager.JobManager` object. jobMgr = JobManager.JobManager() diff --git a/direct/src/showbase/LeakDetectors.py b/direct/src/showbase/LeakDetectors.py index 38d45d225b..b2a17b5617 100755 --- a/direct/src/showbase/LeakDetectors.py +++ b/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 direct.showbase.DirectObject import DirectObject diff --git a/direct/src/showbase/Loader.py b/direct/src/showbase/Loader.py index 988982ab28..f0afbc06ec 100644 --- a/direct/src/showbase/Loader.py +++ b/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'] diff --git a/direct/src/showbase/Messenger.py b/direct/src/showbase/Messenger.py index f1b7bcc1a7..c8d72cb6b4 100644 --- a/direct/src/showbase/Messenger.py +++ b/direct/src/showbase/Messenger.py @@ -1,5 +1,6 @@ """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'] @@ -10,26 +11,28 @@ import types from direct.stdpy.threading import Lock + class Messenger: notify = DirectNotifyGlobal.directNotify.newCategory("Messenger") 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], object2: [method, extraArgs, persistent]}, event2: {object1: [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 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]}} """ # eventName->objMsgrId->callbackInfo @@ -281,20 +284,20 @@ class Messenger: """ 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 + 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. + 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): assert Messenger.notify.debug( @@ -485,7 +488,7 @@ class Messenger: This is intended for debugging use only. This function is not defined if python is ran with -O (optimize). - See Also: unwatch + See Also: `unwatch` """ if not self.__watching.get(needle): self.__isWatching += 1 @@ -499,7 +502,7 @@ class Messenger: This is intended for debugging use only. This function is not defined if python is ran with -O (optimize). - See Also: watch + See Also: `watch` """ if self.__watching.get(needle): self.__isWatching -= 1 @@ -514,7 +517,7 @@ class Messenger: This is intended for debugging use only. This function is not defined if python is ran with -O (optimize). - See Also: unquiet + See Also: `unquiet` """ if not self.quieting.get(message): self.quieting[message]=1 @@ -528,7 +531,7 @@ class Messenger: This is intended for debugging use only. This function is not defined if python is ran with -O (optimize). - See Also: quiet + See Also: `quiet` """ if self.quieting.get(message): del self.quieting[message] @@ -658,4 +661,3 @@ class Messenger: detailed_repr = detailedRepr get_all_accepting = getAllAccepting toggle_verbose = toggleVerbose - diff --git a/direct/src/showbase/MessengerGlobal.py b/direct/src/showbase/MessengerGlobal.py index f5a534c0d1..ba516ab38d 100644 --- a/direct/src/showbase/MessengerGlobal.py +++ b/direct/src/showbase/MessengerGlobal.py @@ -1,7 +1,8 @@ -"""instantiate global Messenger object""" +"""Instantiates the global :class:`~.Messenger.Messenger` object.""" __all__ = ['messenger'] from . import Messenger +#: Contains the global :class:`~.Messenger.Messenger` instance. messenger = Messenger.Messenger() diff --git a/direct/src/showbase/MirrorDemo.py b/direct/src/showbase/MirrorDemo.py index 0c5090cbc4..1e43f7621b 100755 --- a/direct/src/showbase/MirrorDemo.py +++ b/direct/src/showbase/MirrorDemo.py @@ -1,5 +1,5 @@ """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. The approach taken here is to create an offscreen buffer with its own diff --git a/direct/src/showbase/PhysicsManagerGlobal.py b/direct/src/showbase/PhysicsManagerGlobal.py index 9be21b6060..f1c3d51dc6 100644 --- a/direct/src/showbase/PhysicsManagerGlobal.py +++ b/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'] from panda3d.physics import PhysicsManager +#: Contains the global :class:`~panda3d.physics.PhysicsManager` instance. physicsMgr = PhysicsManager() diff --git a/direct/src/showbase/Pool.py b/direct/src/showbase/Pool.py index 5c1782d629..9c11979c6a 100755 --- a/direct/src/showbase/Pool.py +++ b/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. -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 + class Pool: notify = DirectNotifyGlobal.directNotify.newCategory("Pool") diff --git a/direct/src/showbase/PythonUtil.py b/direct/src/showbase/PythonUtil.py index fbef9ecc70..4c9afa634b 100644 --- a/direct/src/showbase/PythonUtil.py +++ b/direct/src/showbase/PythonUtil.py @@ -1,36 +1,35 @@ """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__: - __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 math @@ -652,13 +651,15 @@ if __debug__: """ decorator for profiling functions 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" @@ -1171,7 +1172,7 @@ def normalDistrib(a, b, gauss=random.gauss): uniformly onto the curve inside [a, b] ------------------------------------------------------------------------ - http://www-stat.stanford.edu/~naras/jsm/NormalDensity/NormalDensity.html + https://statweb.stanford.edu/~naras/jsm/NormalDensity/NormalDensity.html The 68-95-99.7% Rule ==================== @@ -1195,13 +1196,14 @@ def normalDistrib(a, b, gauss=random.gauss): 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.:: - -Weights need not add up to any particular value. - -The actual selection will be returned. + {"Choice 1": 10, + "Choice 2": 30, + "bear": 100} + + - Weights need not add up to any particular value. + - The actual selection will be returned. """ selections = list(valDict.keys()) 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, 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. + 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. + - *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. + 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. + 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. + 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. + 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. """ diff --git a/direct/src/showbase/ShowBase.py b/direct/src/showbase/ShowBase.py index f9506074a9..6c514647af 100644 --- a/direct/src/showbase/ShowBase.py +++ b/direct/src/showbase/ShowBase.py @@ -60,7 +60,19 @@ class ShowBase(DirectObject.DirectObject): config = DConfig 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__) builtins.__dev__ = self.__dev__ @@ -531,6 +543,8 @@ class ShowBase(DirectObject.DirectObject): del ShowBaseGlobal.base self.aspect2d.node().removeAllChildren() + self.render2d.node().removeAllChildren() + self.aspect2d.reparent_to(self.render2d) # [gjeon] restore sticky key settings if self.config.GetBool('disable-sticky-keys', 0): @@ -1095,8 +1109,12 @@ class ShowBase(DirectObject.DirectObject): 2-d objects and gui elements that are superimposed over the 3-d geometry in the window. """ + # We've already created aspect2d in ShowBaseGlobal, for the + # benefit of creating DirectGui elements before ShowBase. + from . import ShowBaseGlobal + ## This is the root of the 2-D scene graph. - self.render2d = NodePath('render2d') + self.render2d = ShowBaseGlobal.render2d # Set up some overrides to turn off certain properties which # we probably won't need for 2-d objects. @@ -1127,7 +1145,6 @@ class ShowBase(DirectObject.DirectObject): ## aspect2d, which scales things back to the right aspect ## ratio along the X axis (Z is still from -1 to 1) self.aspect2d = ShowBaseGlobal.aspect2d - self.aspect2d.reparentTo(self.render2d) aspectRatio = self.getAspectRatio() self.aspect2d.setScale(1.0 / aspectRatio, 1.0, 1.0) @@ -1853,6 +1870,7 @@ class ShowBase(DirectObject.DirectObject): self.notify.debug("Disabling music") def SetAllSfxEnables(self, bEnabled): + """Calls ``setActive(bEnabled)`` on all valid SFX managers.""" for i in range(len(self.sfxManagerList)): if (self.sfxManagerIsValidList[i]): self.sfxManagerList[i].setActive(bEnabled) @@ -2586,9 +2604,9 @@ class ShowBase(DirectObject.DirectObject): 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. Unlike screenshot(), source may only be a GraphicsWindow, @@ -2650,19 +2668,19 @@ class ShowBase(DirectObject.DirectObject): cameraMask = PandaNode.getAllCameraMask(), 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: source = self.win @@ -2739,17 +2757,22 @@ class ShowBase(DirectObject.DirectObject): format = 'png', sd = 4, source = None): """ 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.setDt(1.0/float(fps)) @@ -3112,11 +3135,12 @@ class ShowBase(DirectObject.DirectObject): self.startDirect(fWantDirect = fDirect, fWantTk = fTk, fWantWx = fWx) def run(self): - """ This method runs the TaskManager when self.appRunner is - None, which is to say, when we are not running from within a - p3d file. When we *are* within a p3d file, the Panda - runtime has to be responsible for running the main loop, so - we can't allow the application to do it. """ + """This method runs the :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 \ (self.appRunner.interactiveConsole and not self.appRunner.initialAppImport): diff --git a/direct/src/showbase/ShowBaseGlobal.py b/direct/src/showbase/ShowBaseGlobal.py index dc5f4596fc..0fdbf0104e 100644 --- a/direct/src/showbase/ShowBaseGlobal.py +++ b/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 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__) +#: The global instance of the :class:`panda3d.core.VirtualFileSystem`. vfs = VirtualFileSystem.getGlobalPtr() ostream = Notify.out() globalClock = ClockObject.getGlobalClock() @@ -24,23 +26,30 @@ cvMgr = ConfigVariableManager.getGlobalPtr() pandaSystem = PandaSystem.getGlobalPtr() # This is defined here so GUI elements can be instantiated before ShowBase. -aspect2d = NodePath(PGTop("aspect2d")) +render2d = NodePath("render2d") +aspect2d = render2d.attachNewNode(PGTop("aspect2d")) hidden = NodePath("hidden") # Set direct notify categories now that we have config directNotify.setDconfigLevels() + def run(): + """Deprecated alias for :meth:`base.run() <.ShowBase.run>`.""" assert ShowBase.notify.warning("run() is deprecated, use base.run() instead") base.run() + 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 # it up as a dependency when building a .p3d package. import importlib Inspector = importlib.import_module('direct.tkpanels.Inspector') return Inspector.inspect(anObject) + import sys if sys.version_info >= (3, 0): import builtins diff --git a/direct/src/showbase/TkGlobal.py b/direct/src/showbase/TkGlobal.py index e6cf9b7f84..512bdb3583 100644 --- a/direct/src/showbase/TkGlobal.py +++ b/direct/src/showbase/TkGlobal.py @@ -39,4 +39,5 @@ del bordercolors def spawnTkLoop(): + """Alias for :meth:`base.spawnTkLoop() <.ShowBase.spawnTkLoop>`.""" base.spawnTkLoop() diff --git a/direct/src/showbase/VFSImporter.py b/direct/src/showbase/VFSImporter.py index e6849fd503..b59965ae3c 100644 --- a/direct/src/showbase/VFSImporter.py +++ b/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', 'reloadSharedPackage', 'reloadSharedPackages'] @@ -8,19 +15,18 @@ import marshal import imp 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 = {} vfs = VirtualFileSystem.getGlobalPtr() @@ -31,6 +37,7 @@ if not __debug__: # We implement that by reversing the extension names. compiledExtensions = [ 'pyo', 'pyc' ] + class VFSImporter: """ This class serves as a Python importer to support loading Python .py and .pyc/.pyo files from Panda's Virtual File System, @@ -326,6 +333,7 @@ class VFSLoader: return code + class VFSSharedImporter: """ 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 @@ -418,6 +426,7 @@ class VFSSharedImporter: # Couldn't figure it out. return None + class VFSSharedLoader: """ The second part of VFSSharedImporter, this imports a list of packages and combines them. """ @@ -470,6 +479,7 @@ class VFSSharedLoader: return mod + _registered = False def register(): """ Register the VFSImporter on the path_hooks, if it has not @@ -488,6 +498,7 @@ def register(): # folders that previously were loaded directly. sys.path_importer_cache = {} + def reloadSharedPackage(mod): """ Reloads the specific module as a shared package, adding any new directories that might have appeared on the search path. """ @@ -515,6 +526,7 @@ def reloadSharedPackage(mod): sharedPackages[childname] = True reloadSharedPackage(child) + def reloadSharedPackages(): """ Walks through the sharedPackages list, and forces a reload of any modules on that list that have already been loaded. This @@ -530,4 +542,3 @@ def reloadSharedPackages(): continue reloadSharedPackage(mod) - diff --git a/direct/src/showbase/WxGlobal.py b/direct/src/showbase/WxGlobal.py index ae2e618102..dd43c673b5 100755 --- a/direct/src/showbase/WxGlobal.py +++ b/direct/src/showbase/WxGlobal.py @@ -1,4 +1,6 @@ """ This module is now vestigial. """ + def spawnWxLoop(): + """Alias for :meth:`base.spawnWxLoop() <.ShowBase.spawnWxLoop>`.""" base.spawnWxLoop() diff --git a/direct/src/stdpy/file.py b/direct/src/stdpy/file.py index 02cec1050d..458406a1e0 100644 --- a/direct/src/stdpy/file.py +++ b/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): + """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): # Python 3 is much stricter than Python 2, which lets # unknown flags fall through. diff --git a/direct/src/stdpy/pickle.py b/direct/src/stdpy/pickle.py index 027888e281..fefdeae9ff 100644 --- a/direct/src/stdpy/pickle.py +++ b/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 knowing about the other NodePaths that will also be writing to the 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 support extensions of this nature. """ diff --git a/direct/src/task/Task.py b/direct/src/task/Task.py index 031c403644..9143055d53 100644 --- a/direct/src/task/Task.py +++ b/direct/src/task/Task.py @@ -1,6 +1,10 @@ """ 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', 'cont', 'done', 'again', 'pickup', 'exit', @@ -68,7 +72,7 @@ again = AsyncTask.DSAgain pickup = AsyncTask.DSPickup exit = AsyncTask.DSExit -# Alias PythonTask to Task for historical purposes. +#: Task aliases to :class:`panda3d.core.PythonTask` for historical purposes. Task = PythonTask # Copy the module-level enums above into the class level. This funny @@ -123,7 +127,7 @@ class TaskManager: self.fKeyboardInterrupt = False self.interruptCount = 0 - self._frameProfileQueue = Queue() + self._frameProfileQueue = [] # this will be set when it's safe to import StateVar self._profileFrames = None @@ -274,7 +278,7 @@ class TaskManager: def getTasksMatching(self, taskPattern): """Returns a list of all tasks, active or sleeping, with a 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))) @@ -302,7 +306,7 @@ class TaskManager: uponDeath = None, appendTask = False, owner = None): """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 that the task will not begin executing until at least the indicated delayTime (in seconds) has elapsed. @@ -326,59 +330,61 @@ class TaskManager: def add(self, funcOrTask, name = None, sort = None, extraArgs = None, priority = None, uponDeath = None, appendTask = False, taskChain = None, owner = None): - """ Add a new task to the taskMgr. The task will begin executing immediately, or next frame if its sort value has already passed this frame. - The parameters are: + 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. - 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. - name - 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. - extraArgs - 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. - 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 (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. - 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 (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".) - 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 (bool): a function to call when the task + terminates, either because it has run to completion, or + because it has been explicitly removed. - uponDeath - 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. - 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. + 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) @@ -455,8 +461,8 @@ class TaskManager: def removeTasksMatching(self, taskPattern): """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. """ @@ -515,7 +521,7 @@ class TaskManager: while self.running: try: if len(self._frameProfileQueue): - numFrames, session, callback = self._frameProfileQueue.pop() + numFrames, session, callback = self._frameProfileQueue.pop(0) def _profileFunc(numFrames=numFrames): self._doProfiledFrames(numFrames) session.setFunc(_profileFunc) @@ -623,7 +629,7 @@ class TaskManager: session = self.getProfileSession() # make sure the profile session doesn't get destroyed before we're done with it session.acquire() - self._frameProfileQueue.push((num, session, callback)) + self._frameProfileQueue.append((num, session, callback)) def _doProfiledFrames(self, numFrames): for i in range(numFrames): diff --git a/direct/src/task/TaskManagerGlobal.py b/direct/src/task/TaskManagerGlobal.py index 792938cfc4..e760565db1 100644 --- a/direct/src/task/TaskManagerGlobal.py +++ b/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'] diff --git a/direct/src/task/__init__.py b/direct/src/task/__init__.py index 40a5246131..6a75e7b904 100644 --- a/direct/src/task/__init__.py +++ b/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:: 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. """ diff --git a/direct/src/tkpanels/FSMInspector.py b/direct/src/tkpanels/FSMInspector.py index 14b307b300..42ab82a37d 100644 --- a/direct/src/tkpanels/FSMInspector.py +++ b/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'] @@ -14,6 +116,7 @@ else: DELTA = (5.0 / 360.) * 2.0 * math.pi + class FSMInspector(AppShell): # Override class variables appname = 'ClassicFSM Inspector' @@ -445,104 +548,3 @@ class StateInspector(Pmw.MegaArchetype): def exitedState(self): 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()) - -""" - diff --git a/direct/src/tkpanels/__init__.py b/direct/src/tkpanels/__init__.py index e69de29bb2..af339608b6 100644 --- a/direct/src/tkpanels/__init__.py +++ b/direct/src/tkpanels/__init__.py @@ -0,0 +1,3 @@ +"""This package provides various GUI panels useful during Panda3D development +written using the Tkinter framework. +""" diff --git a/direct/src/tkwidgets/__init__.py b/direct/src/tkwidgets/__init__.py index e69de29bb2..18c32074c6 100644 --- a/direct/src/tkwidgets/__init__.py +++ b/direct/src/tkwidgets/__init__.py @@ -0,0 +1 @@ +"""This package provides various Tkinter widgets.""" diff --git a/dtool/src/dtoolutil/pandaSystem.cxx b/dtool/src/dtoolutil/pandaSystem.cxx index c3a6fda015..79a8e5206b 100644 --- a/dtool/src/dtoolutil/pandaSystem.cxx +++ b/dtool/src/dtoolutil/pandaSystem.cxx @@ -61,6 +61,12 @@ PandaSystem() : #else set_system_tag("system", "malloc", "malloc"); #endif + +#ifdef _LIBCPP_VERSION + set_system_tag("system", "stdlib", "libc++"); +#elif defined(__GLIBCXX__) + set_system_tag("system", "stdlib", "libstdc++"); +#endif } /** diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index 5e8f76e9a5..2a4dd7fa68 100644 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -2874,7 +2874,7 @@ write_module_class(ostream &out, Object *obj) { // const char *tp_name; out << " \"" << _def->module_name << "." << export_class_name << "\",\n"; // Py_ssize_t tp_basicsize; - out << " sizeof(Dtool_PyInstDef),\n"; + out << " 0, // tp_basicsize\n"; // inherited from tp_base // Py_ssize_t tp_itemsize; out << " 0, // tp_itemsize\n"; @@ -3137,9 +3137,8 @@ write_module_class(ostream &out, Object *obj) { } out << " Dtool_" << ClassName << "._PyType.tp_bases = PyTuple_Pack(" << bases.size() << baseargs << ");\n"; - } else { - out << " Dtool_" << ClassName << "._PyType.tp_base = (PyTypeObject *)Dtool_GetSuperBase();\n"; } + out << " Dtool_" << ClassName << "._PyType.tp_base = (PyTypeObject *)Dtool_GetSuperBase();\n"; int num_nested = obj->_itype.number_of_nested_types(); int num_dict_items = 1; diff --git a/dtool/src/interrogatedb/py_panda.cxx b/dtool/src/interrogatedb/py_panda.cxx index 5d17d8851f..725e9bdedd 100644 --- a/dtool/src/interrogatedb/py_panda.cxx +++ b/dtool/src/interrogatedb/py_panda.cxx @@ -156,8 +156,7 @@ PyObject *Dtool_Raise_AssertionError() { #else PyObject *message = PyString_FromString(notify->get_assert_error_message().c_str()); #endif - Py_INCREF(PyExc_AssertionError); - PyErr_Restore(PyExc_AssertionError, message, nullptr); + PyErr_SetObject(PyExc_AssertionError, message); notify->clear_assert_failed(); return nullptr; } @@ -166,14 +165,7 @@ PyObject *Dtool_Raise_AssertionError() { * Raises a TypeError with the given message, and returns NULL. */ PyObject *Dtool_Raise_TypeError(const char *message) { - // PyErr_Restore is what PyErr_SetString would have ended up calling - // eventually anyway, so we might as well just get to the point. - Py_INCREF(PyExc_TypeError); -#if PY_MAJOR_VERSION >= 3 - PyErr_Restore(PyExc_TypeError, PyUnicode_FromString(message), nullptr); -#else - PyErr_Restore(PyExc_TypeError, PyString_FromString(message), nullptr); -#endif + PyErr_SetString(PyExc_TypeError, message); return nullptr; } @@ -194,8 +186,7 @@ PyObject *Dtool_Raise_ArgTypeError(PyObject *obj, int param, const char *functio function_name, param, type_name, Py_TYPE(obj)->tp_name); - Py_INCREF(PyExc_TypeError); - PyErr_Restore(PyExc_TypeError, message, nullptr); + PyErr_SetObject(PyExc_TypeError, message); return nullptr; } @@ -214,8 +205,7 @@ PyObject *Dtool_Raise_AttributeError(PyObject *obj, const char *attribute) { "'%.100s' object has no attribute '%.200s'", Py_TYPE(obj)->tp_name, attribute); - Py_INCREF(PyExc_AttributeError); - PyErr_Restore(PyExc_AttributeError, message, nullptr); + PyErr_SetObject(PyExc_AttributeError, message); return nullptr; } diff --git a/dtool/src/interrogatedb/py_wrappers.cxx b/dtool/src/interrogatedb/py_wrappers.cxx index 6aed3d2bbb..0b1bb59c66 100644 --- a/dtool/src/interrogatedb/py_wrappers.cxx +++ b/dtool/src/interrogatedb/py_wrappers.cxx @@ -75,7 +75,7 @@ static PyObject *Dtool_SequenceWrapper_repr(PyObject *self) { } if (len < 0) { - PyErr_Restore(nullptr, nullptr, nullptr); + PyErr_Clear(); return Dtool_WrapperBase_repr(self); } @@ -422,7 +422,7 @@ static int Dtool_MappingWrapper_contains(PyObject *self, PyObject *key) { return 1; } else if (_PyErr_OCCURRED() == PyExc_KeyError || _PyErr_OCCURRED() == PyExc_TypeError) { - PyErr_Restore(nullptr, nullptr, nullptr); + PyErr_Clear(); return 0; } else { return -1; @@ -480,7 +480,7 @@ static PyObject *Dtool_MappingWrapper_get(PyObject *self, PyObject *args) { if (value != nullptr) { return value; } else if (_PyErr_OCCURRED() == PyExc_KeyError) { - PyErr_Restore(nullptr, nullptr, nullptr); + PyErr_Clear(); Py_INCREF(defvalue); return defvalue; } else { @@ -944,7 +944,7 @@ static PyObject *Dtool_MutableMappingWrapper_pop(PyObject *self, PyObject *args) return nullptr; } } else if (_PyErr_OCCURRED() == PyExc_KeyError) { - PyErr_Restore(nullptr, nullptr, nullptr); + PyErr_Clear(); Py_INCREF(defvalue); return defvalue; } else { @@ -1044,7 +1044,7 @@ static PyObject *Dtool_MutableMappingWrapper_setdefault(PyObject *self, PyObject if (value != nullptr) { return value; } else if (_PyErr_OCCURRED() == PyExc_KeyError) { - PyErr_Restore(nullptr, nullptr, nullptr); + PyErr_Clear(); if (wrap->_setitem_func(wrap->_base._self, key, defvalue) == 0) { Py_INCREF(defvalue); return defvalue; diff --git a/makepanda/makepanda.bat b/makepanda/makepanda.bat index 3908b4f69a..ec843cc0ae 100644 --- a/makepanda/makepanda.bat +++ b/makepanda/makepanda.bat @@ -8,14 +8,20 @@ REM If we can find both, then run 'makepanda'. REM if %PROCESSOR_ARCHITECTURE% == AMD64 ( - set pythondir=win-python-x64 + set suffix=-x64 ) else ( - set pythondir=win-python + set suffix= ) set thirdparty=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 %thirdparty%\%pythondir%\python.exe goto :missing2 %thirdparty%\%pythondir%\python.exe makepanda\makepanda.py %* diff --git a/makepanda/makepandacore.py b/makepanda/makepandacore.py index 506e232715..5e125e6fa0 100644 --- a/makepanda/makepandacore.py +++ b/makepanda/makepandacore.py @@ -2321,7 +2321,6 @@ def SdkLocateWindows(version = '7.1'): if not os.path.isdir(os.path.join(platsdk, 'Lib', verstring, 'um')): continue - print(verstring) vertuple = tuple(map(int, verstring.split('.'))) if vertuple > max_version: version = verstring @@ -3166,7 +3165,7 @@ def CopyPythonTree(dstdir, srcdir, lib2to3_fixers=[], threads=0): if (NeedsBuild([dstpth], [srcpth])): WriteBinaryFile(dstpth, ReadBinaryFile(srcpth)) - if ext == '.py' and not entry.endswith('-extensions.py'): + if ext == '.py' and not entry.endswith('-extensions.py') and lib2to3 is not None: refactor.append((dstpth, srcpth)) lib2to3_args.append(dstpth) else: diff --git a/makepanda/makewheel.py b/makepanda/makewheel.py index e230ae4049..2c0d0530c8 100644 --- a/makepanda/makewheel.py +++ b/makepanda/makewheel.py @@ -678,6 +678,13 @@ if __debug__: if file.endswith('.py'): 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. entry_points = '[console_scripts]\n' entry_points += 'eggcacher = direct.directscripts.eggcacher:main\n' diff --git a/panda/src/audio/audioSound.h b/panda/src/audio/audioSound.h index c7eb3653da..4f7116c080 100644 --- a/panda/src/audio/audioSound.h +++ b/panda/src/audio/audioSound.h @@ -42,17 +42,21 @@ PUBLISHED: virtual void set_loop_count(unsigned long loop_count=1) = 0; virtual unsigned long get_loop_count() const = 0; -/* - * Control time position within the sound. This is similar (in concept) to - * the seek position within a file. time in seconds: 0 = beginning; length() - * = end. inits to 0.0. - The current time position will not change while the - * sound is playing; you must call play() again to effect the change. To play - * the same sound from a time offset a second time, explicitly set the time - * position again. When looping, the second and later loops will start from - * the beginning of the sound. - If a sound is playing, calling get_time() - * repeatedly will return different results over time. e.g.: PN_stdfloat - * percent_complete = s.get_time() s.length(); - */ + /** + * Control time position within the sound, in seconds. This is similar (in + * concept) to the seek position within a file. The value starts at 0.0 (the + * default) and ends at the value given by the length() method. + * + * In the past, this call did nothing if the sound was currently playing, and + * it was necessary to call play() to effect the change. This is no longer + * the case; the time change takes effect immediately. + * + * If a sound is playing, calling get_time() repeatedly will return different + * results over time. e.g. + * @code + * PN_stdfloat percent_complete = s.get_time() / s.length(); + * @endcode + */ virtual void set_time(PN_stdfloat start_time=0.0) = 0; virtual PN_stdfloat get_time() const = 0; diff --git a/panda/src/audiotraits/openalAudioSound.cxx b/panda/src/audiotraits/openalAudioSound.cxx index 5676e63f80..ff7d187a3d 100644 --- a/panda/src/audiotraits/openalAudioSound.cxx +++ b/panda/src/audiotraits/openalAudioSound.cxx @@ -564,13 +564,18 @@ push_fresh_buffers() { } /** - * The next time you call play, the sound will start from the specified - * offset. + * Sets the offset within the sound. If the sound is currently playing, its + * position is updated immediately. */ void OpenALAudioSound:: set_time(PN_stdfloat time) { ReMutexHolder holder(OpenALAudioManager::_lock); _start_time = time; + + if (is_playing()) { + // Ensure that the position is updated immediately. + play(); + } } /** diff --git a/panda/src/collide/collisionTraverser.cxx b/panda/src/collide/collisionTraverser.cxx index a09d7b645a..46c06b8829 100644 --- a/panda/src/collide/collisionTraverser.cxx +++ b/panda/src/collide/collisionTraverser.cxx @@ -399,7 +399,7 @@ set_recorder(CollisionRecorder *recorder) { * should be any node in the scene graph; typically, the top node (e.g. * render). The CollisionVisualizer will be attached to this node. */ -CollisionVisualizer *CollisionTraverser:: +PandaNode *CollisionTraverser:: show_collisions(const NodePath &root) { #ifdef DO_COLLISION_RECORDING hide_collisions(); diff --git a/panda/src/collide/collisionTraverser.h b/panda/src/collide/collisionTraverser.h index cb12d6d4e9..b1be66d49f 100644 --- a/panda/src/collide/collisionTraverser.h +++ b/panda/src/collide/collisionTraverser.h @@ -72,7 +72,7 @@ PUBLISHED: MAKE_PROPERTY2(recorder, has_recorder, get_recorder, set_recorder, clear_recorder); - CollisionVisualizer *show_collisions(const NodePath &root); + PandaNode *show_collisions(const NodePath &root); void hide_collisions(); #endif // DO_COLLISION_RECORDING diff --git a/panda/src/downloader/httpClient.I b/panda/src/downloader/httpClient.I index 33b9432f3d..419083309c 100644 --- a/panda/src/downloader/httpClient.I +++ b/panda/src/downloader/httpClient.I @@ -111,7 +111,7 @@ get_verify_ssl() const { * Specifies the set of ciphers that are to be made available for SSL * connections. This is a string as described in the ciphers(1) man page of * the OpenSSL documentation (or see - * http://www.openssl.org/docs/apps/ciphers.html ). If this is not specified, + * https://www.openssl.org/docs/apps/ciphers.html ). If this isn't specified, * the default is provided by the Config file. You may also specify "DEFAULT" * to use the built-in OpenSSL default value. */ diff --git a/panda/src/event/asyncFuture_ext.cxx b/panda/src/event/asyncFuture_ext.cxx index 5bf7cb3c94..c4cc4b5302 100644 --- a/panda/src/event/asyncFuture_ext.cxx +++ b/panda/src/event/asyncFuture_ext.cxx @@ -102,7 +102,7 @@ static PyObject *get_done_result(const AsyncFuture *future) { if (value != nullptr) { return value; } - PyErr_Restore(nullptr, nullptr, nullptr); + PyErr_Clear(); Py_DECREF(wrap); } } @@ -132,8 +132,7 @@ static PyObject *get_done_result(const AsyncFuture *future) { nullptr, nullptr); } } - Py_INCREF(exc_type); - PyErr_Restore(exc_type, nullptr, nullptr); + PyErr_SetNone(exc_type); return nullptr; } } @@ -154,8 +153,7 @@ static PyObject *gen_next(PyObject *self) { } else { PyObject *result = get_done_result(future); if (result != nullptr) { - Py_INCREF(PyExc_StopIteration); - PyErr_Restore(PyExc_StopIteration, result, nullptr); + PyErr_SetObject(PyExc_StopIteration, result); } return nullptr; } @@ -225,8 +223,7 @@ result(PyObject *timeout) const { nullptr, nullptr); } } - Py_INCREF(exc_type); - PyErr_Restore(exc_type, nullptr, nullptr); + PyErr_SetNone(exc_type); return nullptr; } } diff --git a/panda/src/event/pythonTask.cxx b/panda/src/event/pythonTask.cxx index fdf6be440c..86c05b33d7 100644 --- a/panda/src/event/pythonTask.cxx +++ b/panda/src/event/pythonTask.cxx @@ -539,7 +539,7 @@ do_python_task() { result = Py_None; Py_INCREF(result); #endif - PyErr_Restore(nullptr, nullptr, nullptr); + PyErr_Clear(); // If we passed a coroutine into the task, eg. something like: // taskMgr.add(my_async_function()) diff --git a/panda/src/glstuff/glGraphicsBuffer_src.cxx b/panda/src/glstuff/glGraphicsBuffer_src.cxx index d5252944f5..d484a174e4 100644 --- a/panda/src/glstuff/glGraphicsBuffer_src.cxx +++ b/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()) { gl_format = GL_SRGB8_ALPHA8; } 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 { gl_format = GL_RGBA; } diff --git a/panda/src/gobj/geomPrimitive.cxx b/panda/src/gobj/geomPrimitive.cxx index 73aaf77c40..e55ea41ec8 100644 --- a/panda/src/gobj/geomPrimitive.cxx +++ b/panda/src/gobj/geomPrimitive.cxx @@ -470,6 +470,9 @@ offset_vertices(int offset) { * primitive. Unlike the other version of offset_vertices, this makes the * 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 * away other changes you might have recently made in an upstream thread. */ diff --git a/panda/src/gobj/preparedGraphicsObjects.cxx b/panda/src/gobj/preparedGraphicsObjects.cxx index d4a70d6054..800d4ee1f6 100644 --- a/panda/src/gobj/preparedGraphicsObjects.cxx +++ b/panda/src/gobj/preparedGraphicsObjects.cxx @@ -1343,6 +1343,8 @@ release_all_shader_buffers() { ++bci) { BufferContext *bc = (BufferContext *)(*bci); + ((ShaderBuffer *)bc->_object)->clear_prepared(this); + bc->_object = nullptr; _released_shader_buffers.push_back(bc); } diff --git a/panda/src/grutil/geoMipTerrain.h b/panda/src/grutil/geoMipTerrain.h index 0d0ce94da0..1075e865de 100644 --- a/panda/src/grutil/geoMipTerrain.h +++ b/panda/src/grutil/geoMipTerrain.h @@ -31,7 +31,7 @@ * GeoMipMapping algorithm, or Geometrical MipMapping, based on the LOD (Level * of Detail) algorithm. For more information about the GeoMipMapping * algoritm, see this paper, written by Willem H. de Boer: - * http://flipcode.com/articles/article_geomipmaps.pdf + * https://flipcode.com/articles/article_geomipmaps.pdf */ class EXPCL_PANDA_GRUTIL GeoMipTerrain : public TypedObject { PUBLISHED: diff --git a/panda/src/mathutil/perlinNoise2.h b/panda/src/mathutil/perlinNoise2.h index d10555e38b..33e46c7b4e 100644 --- a/panda/src/mathutil/perlinNoise2.h +++ b/panda/src/mathutil/perlinNoise2.h @@ -20,7 +20,7 @@ /** * This class provides an implementation of Perlin noise for 2 variables. * This code is loosely based on the reference implementation at - * http://mrl.nyu.edu/~perlin/noise/ . + * https://mrl.nyu.edu/~perlin/noise/ . */ class EXPCL_PANDA_MATHUTIL PerlinNoise2 : public PerlinNoise { PUBLISHED: diff --git a/panda/src/physx/physxContactPair.cxx b/panda/src/physx/physxContactPair.cxx index fdaf22889c..9374709418 100644 --- a/panda/src/physx/physxContactPair.cxx +++ b/panda/src/physx/physxContactPair.cxx @@ -73,8 +73,8 @@ is_deleted_b() const { * You should set the ContactPairFlag CPF_notify_forces in order to receive * this value. * - * @see PhysxScene::set_actor_pair_flag @see - * PhysxScene::set_actor_group_pair_flag + * @see PhysxScene::set_actor_pair_flag + * @see PhysxScene::set_actor_group_pair_flag */ LVector3f PhysxContactPair:: get_sum_normal_force() const { @@ -88,8 +88,8 @@ get_sum_normal_force() const { * You should set the ContactPairFlag CPF_notify_forces in order to receive * this value. * - * @see PhysxScene::set_actor_pair_flag @see - * PhysxScene::set_actor_group_pair_flag + * @see PhysxScene::set_actor_pair_flag + * @see PhysxScene::set_actor_group_pair_flag */ LVector3f PhysxContactPair:: get_sum_friction_force() const { diff --git a/panda/src/pnmimage/pnm-image-filter-core.cxx b/panda/src/pnmimage/pnm-image-filter-core.cxx index 273a4b0713..882c4c41e1 100644 --- a/panda/src/pnmimage/pnm-image-filter-core.cxx +++ b/panda/src/pnmimage/pnm-image-filter-core.cxx @@ -43,8 +43,9 @@ FUNCTION_NAME(IMAGETYPE &dest, const IMAGETYPE &source, WorkType *filter; float filter_width; + int actual_width; - make_filter(scale, width, filter, filter_width); + make_filter(scale, width, filter, filter_width, actual_width); for (b = 0; b < source.BSIZE(); b++) { for (a = 0; a < source.ASIZE(); a++) { @@ -54,7 +55,7 @@ FUNCTION_NAME(IMAGETYPE &dest, const IMAGETYPE &source, filter_row(temp_dest, dest.ASIZE(), temp_source, source.ASIZE(), scale, - filter, filter_width); + filter, filter_width, actual_width); for (a = 0; a < dest.ASIZE(); a++) { matrix[a][b] = temp_dest[a]; @@ -69,13 +70,13 @@ FUNCTION_NAME(IMAGETYPE &dest, const IMAGETYPE &source, scale = (float)dest.BSIZE() / (float)source.BSIZE(); temp_dest = (StoreType *)PANDA_MALLOC_ARRAY(dest.BSIZE() * sizeof(StoreType)); - make_filter(scale, width, filter, filter_width); + make_filter(scale, width, filter, filter_width, actual_width); for (a = 0; a < dest.ASIZE(); a++) { filter_row(temp_dest, dest.BSIZE(), matrix[a], source.BSIZE(), scale, - filter, filter_width); + filter, filter_width, actual_width); for (b = 0; b < dest.BSIZE(); b++) { dest.SETVAL(a, b, channel, (float)temp_dest[b]/(float)source_max); diff --git a/panda/src/pnmimage/pnm-image-filter-sparse-core.cxx b/panda/src/pnmimage/pnm-image-filter-sparse-core.cxx index 4edf632ee4..abfa2017cd 100644 --- a/panda/src/pnmimage/pnm-image-filter-sparse-core.cxx +++ b/panda/src/pnmimage/pnm-image-filter-sparse-core.cxx @@ -49,10 +49,12 @@ FUNCTION_NAME(IMAGETYPE &dest, const IMAGETYPE &source, WorkType *filter; float filter_width; + int actual_width; - make_filter(scale, width, filter, filter_width); + make_filter(scale, width, filter, filter_width, actual_width); for (b = 0; b < source.BSIZE(); b++) { + memset(temp_source, 0, source.ASIZE() * sizeof(StoreType)); memset(temp_source_weight, 0, source.ASIZE() * sizeof(StoreType)); for (a = 0; a < source.ASIZE(); a++) { if (source.HASVAL(a, b)) { @@ -64,7 +66,7 @@ FUNCTION_NAME(IMAGETYPE &dest, const IMAGETYPE &source, filter_sparse_row(temp_dest, temp_dest_weight, dest.ASIZE(), temp_source, temp_source_weight, source.ASIZE(), scale, - filter, filter_width); + filter, filter_width, actual_width); for (a = 0; a < dest.ASIZE(); a++) { matrix[a][b] = temp_dest[a]; @@ -83,16 +85,18 @@ FUNCTION_NAME(IMAGETYPE &dest, const IMAGETYPE &source, temp_dest = (StoreType *)PANDA_MALLOC_ARRAY(dest.BSIZE() * sizeof(StoreType)); temp_dest_weight = (StoreType *)PANDA_MALLOC_ARRAY(dest.BSIZE() * sizeof(StoreType)); - make_filter(scale, width, filter, filter_width); + make_filter(scale, width, filter, filter_width, actual_width); for (a = 0; a < dest.ASIZE(); a++) { filter_sparse_row(temp_dest, temp_dest_weight, dest.BSIZE(), matrix[a], matrix_weight[a], source.BSIZE(), scale, - filter, filter_width); + filter, filter_width, actual_width); for (b = 0; b < dest.BSIZE(); b++) { if (temp_dest_weight[b] != 0) { + // The temp_dest array has already been scaled by + // temp_dest_weight; we don't scale it again here. dest.SETVAL(a, b, channel, (float)temp_dest[b]/(float)source_max); } } diff --git a/panda/src/pnmimage/pnm-image-filter.cxx b/panda/src/pnmimage/pnm-image-filter.cxx index 6ca6a145d6..a19ee75f5c 100644 --- a/panda/src/pnmimage/pnm-image-filter.cxx +++ b/panda/src/pnmimage/pnm-image-filter.cxx @@ -110,7 +110,8 @@ filter_row(StoreType dest[], int dest_len, const StoreType source[], int source_len, float scale, // == dest_len / source_len const WorkType filter[], - float filter_width) { + float filter_width, + int actual_width) { // If we are expanding the row (scale > 1.0), we need to look at a // fractional granularity. Hence, we scale our filter index by scale. If // we are compressing (scale < 1.0), we don't need to fiddle with the filter @@ -147,13 +148,15 @@ filter_row(StoreType dest[], int dest_len, // of center--so we don't have to incur the overhead of calling fabs() // each time through the loop. for (source_x = left; source_x < right_center; source_x++) { - index = (int)(iscale * (center - source_x) + 0.5f); + index = (int)cfloor(iscale * (center - source_x) + 0.5f); + nassertv(index >= 0 && index < actual_width); net_value += filter[index] * source[source_x]; net_weight += filter[index]; } for (; source_x <= right; source_x++) { - index = (int)(iscale * (source_x - center) + 0.5f); + index = (int)cfloor(iscale * (source_x - center) + 0.5f); + nassertv(index >= 0 && index < actual_width); net_value += filter[index] * source[source_x]; net_weight += filter[index]; } @@ -174,15 +177,16 @@ filter_sparse_row(StoreType dest[], StoreType dest_weight[], int dest_len, const StoreType source[], const StoreType source_weight[], int source_len, float scale, // == dest_len / source_len const WorkType filter[], - float filter_width) { + float filter_width, + int actual_width) { // If we are expanding the row (scale > 1.0), we need to look at a // fractional granularity. Hence, we scale our filter index by scale. If // we are compressing (scale < 1.0), we don't need to fiddle with the filter // index, so we leave it at one. float iscale; - if (scale < 1.0) { - iscale = 1.0; + if (scale < 1.0f) { + iscale = 1.0f; filter_width /= scale; } else { iscale = scale; @@ -211,13 +215,15 @@ filter_sparse_row(StoreType dest[], StoreType dest_weight[], int dest_len, // of center--so we don't have to incur the overhead of calling fabs() // each time through the loop. for (source_x = left; source_x < right_center; source_x++) { - index = (int)(iscale * (center - source_x) + 0.5f); + index = (int)cfloor(iscale * (center - source_x) + 0.5f); + nassertv(index >= 0 && index < actual_width); net_value += filter[index] * source[source_x] * source_weight[source_x]; net_weight += filter[index] * source_weight[source_x]; } for (; source_x <= right; source_x++) { - index = (int)(iscale * (source_x - center) + 0.5f); + index = (int)cfloor(iscale * (source_x - center) + 0.5f); + nassertv(index >= 0 && index < actual_width); net_value += filter[index] * source[source_x] * source_weight[source_x]; net_weight += filter[index] * source_weight[source_x]; } @@ -244,11 +250,12 @@ filter_sparse_row(StoreType dest[], StoreType dest_weight[], int dest_len, // corresponding to values in the range -filter_width to filter_width. typedef void FilterFunction(float scale, float width, - WorkType *&filter, float &filter_width); + WorkType *&filter, float &filter_width, int &actual_width); static void box_filter_impl(float scale, float width, - WorkType *&filter, float &filter_width) { + WorkType *&filter, float &filter_width, + int &actual_width) { float fscale; if (scale < 1.0) { // If we are compressing the image, we want to expand the range of the @@ -263,7 +270,11 @@ box_filter_impl(float scale, float width, fscale = scale; } filter_width = width; - int actual_width = (int)cceil((filter_width + 1) * fscale) + 1; + + // It seems we need a buffer of two extra values in the filter array + // to allow room for all calculations (especially including the 1/2 + // pixel offset). + actual_width = (int)cceil((filter_width + 1) * fscale) + 2; filter = (WorkType *)PANDA_MALLOC_ARRAY(actual_width * sizeof(WorkType)); @@ -274,7 +285,8 @@ box_filter_impl(float scale, float width, static void gaussian_filter_impl(float scale, float width, - WorkType *&filter, float &filter_width) { + WorkType *&filter, float &filter_width, + int &actual_width) { float fscale; if (scale < 1.0) { // If we are compressing the image, we want to expand the range of the @@ -291,7 +303,11 @@ gaussian_filter_impl(float scale, float width, float sigma = width/2; filter_width = 3.0 * sigma; - int actual_width = (int)cceil((filter_width + 1) * fscale); + + // It seems we need a buffer of two extra values in the filter array + // to allow room for all calculations (especially including the 1/2 + // pixel offset). + actual_width = (int)cceil((filter_width + 1) * fscale) + 2; // G(x, y) = (1(2 pi sigma^2)) * exp( - (x^2 + y^2) (2 sigma^2)) diff --git a/panda/src/pstatclient/pStatClient.cxx b/panda/src/pstatclient/pStatClient.cxx index 9e2d9c1082..dd57612107 100644 --- a/panda/src/pstatclient/pStatClient.cxx +++ b/panda/src/pstatclient/pStatClient.cxx @@ -1264,6 +1264,7 @@ get_thread(int index) const { double PStatClient:: get_real_time() const { + return 0.0; } PStatThread PStatClient:: diff --git a/panda/src/putil/bamEnums.h b/panda/src/putil/bamEnums.h index 1f5b481c9a..ec395148de 100644 --- a/panda/src/putil/bamEnums.h +++ b/panda/src/putil/bamEnums.h @@ -22,12 +22,13 @@ */ class EXPCL_PANDA_PUTIL BamEnums { PUBLISHED: - - // This defines an enumerated type used to represent the endianness of - // certain numeric values stored in a Bam file. It really has only two - // possible values, either BE_bigendian or BE_littleendian; but through a - // preprocessor trick we also add BE_native, which is the same numerically - // as whichever value the hardware supports natively. + /** + * This defines an enumerated type used to represent the endianness of + * certain numeric values stored in a Bam file. It really has only two + * possible values, either BE_bigendian or BE_littleendian; but through a + * preprocessor trick we also add BE_native, which is the same numerically + * as whichever value the hardware supports natively. + */ enum BamEndian { BE_bigendian = 0, BE_littleendian = 1, @@ -38,21 +39,25 @@ PUBLISHED: #endif }; -/* - * This is the code written along with each object. It is used to control - * object scoping. A BOC_push includes an object definition, and will always - * be eventually paired with a BOC_pop (which does not). A BOC_adjunct - * includes an object definition but does not push the level; it is associated - * with the current level. BOC_remove lists object ID's that have been - * deallocated on the sender end. BOC_file_data may appear at any level and - * indicates the following datagram contains auxiliary file data that may be - * referenced by a later object. - */ + /** + * This is the code written along with each object. It is used to control + * object scoping. + */ enum BamObjectCode { + // Indicates an object definition, and will always be eventually paired + // with a BOC_pop (which does not). BOC_push, BOC_pop, + + // Includes an object definition but does not push the level; it is + // associated with the current level. BOC_adjunct, + + // Lists object IDs that have been deallocated on the sender end. BOC_remove, + + // May appear at any level and indicates the following datagram contains + // auxiliary file data that may be referenced by a later object. BOC_file_data, }; diff --git a/panda/src/x11display/x11GraphicsWindow.cxx b/panda/src/x11display/x11GraphicsWindow.cxx index 0a5d969bdf..de58cd1028 100644 --- a/panda/src/x11display/x11GraphicsWindow.cxx +++ b/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); } } else { - x11display_cat.info() + x11display_cat.warning() << "XF86DGA extension not available, cannot enable relative mouse mode\n"; _dga_mouse_enabled = false; } diff --git a/pandatool/src/deploy-stub/deploy-stub.c b/pandatool/src/deploy-stub/deploy-stub.c index 707694a8ca..e30482654e 100644 --- a/pandatool/src/deploy-stub/deploy-stub.c +++ b/pandatool/src/deploy-stub/deploy-stub.c @@ -650,6 +650,15 @@ int main(int argc, char *argv[]) { void *blob = 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_size: %d\n", (int)blobinfo.blob_size); diff --git a/tests/gui/test_DirectOptionMenu.py b/tests/gui/test_DirectOptionMenu.py new file mode 100644 index 0000000000..f83ada1352 --- /dev/null +++ b/tests/gui/test_DirectOptionMenu.py @@ -0,0 +1,73 @@ +from direct.gui.DirectOptionMenu import DirectOptionMenu +import pytest + + +def test_menu_destroy(): + menu = DirectOptionMenu(items=["item1", "item2"]) + menu.destroy() + + +def test_showPopupMenu(): + menu = DirectOptionMenu() + + # Showing an option menu without items will raise an exception + with pytest.raises(Exception): + menu.showPopupMenu() + + menu["items"] = ["item1", "item2"] + menu.showPopupMenu() + assert not menu.popupMenu.isHidden() + assert not menu.cancelFrame.isHidden() + + menu.hidePopupMenu() + assert menu.popupMenu.isHidden() + assert menu.cancelFrame.isHidden() + + +def test_index(): + menu = DirectOptionMenu(items=["item1", "item2"]) + assert menu.index("item1") == 0 + assert menu.index("item2") == 1 + + +def test_set_get(): + menu = DirectOptionMenu(items=["item1", "item2"]) + menu.set(1, False) + assert menu.selectedIndex == 1 + assert menu.get() == "item2" + assert menu["text"] == "item2" + + +def test_initialitem(): + # initialitem by string + menuByStr = DirectOptionMenu(items=["item1", "item2"], initialitem="item2") + assert menuByStr.get() == "item2" + assert menuByStr["text"] == "item2" + + # initialitem by Index + menuByIdx = DirectOptionMenu(items=["item1", "item2"], initialitem=1) + assert menuByIdx.get() == "item2" + assert menuByIdx["text"] == "item2" + + +def test_item_text_scale(): + highlightScale = (2, 2) + unhighlightScale = (0.5, 0.5) + menu = DirectOptionMenu( + items=["item1", "item2"], + item_text_scale=unhighlightScale, + highlightScale=highlightScale) + + # initial scale + item = menu.component("item0") + + item_text_scale = 0.8 + assert item["text_scale"] == unhighlightScale + + # highlight scale + menu._highlightItem(item, 0) + assert item["text_scale"] == highlightScale + + # back to initial scale + menu._unhighlightItem(item, item["frameColor"]) + assert item["text_scale"] == unhighlightScale diff --git a/tests/test_imports.py b/tests/test_imports.py index 262685f8b7..bcc3c180e8 100644 --- a/tests/test_imports.py +++ b/tests/test_imports.py @@ -45,7 +45,6 @@ def test_imports_direct(): import direct.controls.InputState import direct.controls.NonPhysicsWalker import direct.controls.ObserverWalker - import direct.controls.PhysicsRoller import direct.controls.PhysicsWalker import direct.controls.SwimWalker import direct.controls.TwoDWalker