浏览代码

direct: General cleanup of Python code

rdb 2 年之前
父节点
当前提交
8b1126ba3e
共有 79 个文件被更改,包括 536 次插入541 次删除
  1. 2 2
      direct/src/controls/GravityWalker.py
  2. 3 3
      direct/src/controls/PhysicsWalker.py
  3. 13 13
      direct/src/directnotify/Notifier.py
  4. 3 2
      direct/src/directtools/DirectCameraControl.py
  5. 7 5
      direct/src/directtools/DirectSession.py
  6. 3 1
      direct/src/directutil/LargeBlobSenderConsts.py
  7. 1 1
      direct/src/directutil/Mopath.py
  8. 2 0
      direct/src/directutil/Verify.py
  9. 4 3
      direct/src/dist/_android.py
  10. 4 3
      direct/src/dist/commands.py
  11. 2 2
      direct/src/dist/installers.py
  12. 1 1
      direct/src/dist/pefile.py
  13. 1 1
      direct/src/distributed/AsyncRequest.py
  14. 1 1
      direct/src/distributed/ClientRepositoryBase.py
  15. 2 2
      direct/src/distributed/ConnectionRepository.py
  16. 2 0
      direct/src/distributed/DistributedCartesianGrid.py
  17. 2 1
      direct/src/distributed/DoInterestManager.py
  18. 1 1
      direct/src/distributed/GridChild.py
  19. 3 3
      direct/src/distributed/NetMessenger.py
  20. 1 0
      direct/src/distributed/TimeManager.py
  21. 58 58
      direct/src/extensions_native/CInterval_extensions.py
  22. 21 21
      direct/src/extensions_native/HTTPChannel_extensions.py
  23. 147 146
      direct/src/extensions_native/NodePath_extensions.py
  24. 2 2
      direct/src/filter/FilterManager.py
  25. 3 5
      direct/src/fsm/ClassicFSM.py
  26. 2 3
      direct/src/fsm/FSM.py
  27. 3 0
      direct/src/fsm/FourStateAI.py
  28. 12 12
      direct/src/fsm/StatePush.py
  29. 5 5
      direct/src/gui/DirectEntry.py
  30. 1 1
      direct/src/gui/DirectEntryScroll.py
  31. 0 2
      direct/src/gui/DirectGuiGlobals.py
  32. 1 0
      direct/src/gui/DirectScrolledList.py
  33. 1 1
      direct/src/interval/AnimControlInterval.py
  34. 2 2
      direct/src/interval/Interval.py
  35. 1 0
      direct/src/interval/IntervalTest.py
  36. 46 46
      direct/src/leveleditor/GraphEditorUI.py
  37. 3 3
      direct/src/leveleditor/LevelEditorBase.py
  38. 1 0
      direct/src/leveleditor/LevelEditorUIBase.py
  39. 1 1
      direct/src/leveleditor/LevelLoader.py
  40. 0 2
      direct/src/leveleditor/ObjectMgrBase.py
  41. 56 56
      direct/src/leveleditor/PaletteTreeCtrl.py
  42. 1 1
      direct/src/leveleditor/ProtoObjsUI.py
  43. 2 2
      direct/src/motiontrail/MotionTrail.py
  44. 1 1
      direct/src/particles/ParticleFloorTest.py
  45. 2 2
      direct/src/particles/ParticleTest.py
  46. 2 2
      direct/src/physics/FallTest.py
  47. 2 2
      direct/src/physics/RotationTest.py
  48. 2 1
      direct/src/showbase/ContainerLeakDetector.py
  49. 1 0
      direct/src/showbase/DirectObject.py
  50. 1 0
      direct/src/showbase/EventManager.py
  51. 0 1
      direct/src/showbase/GarbageReport.py
  52. 2 0
      direct/src/showbase/GarbageReportScheduler.py
  53. 8 8
      direct/src/showbase/PhasedObject.py
  54. 3 3
      direct/src/showbase/ProfileSession.py
  55. 12 12
      direct/src/showbase/PythonUtil.py
  56. 1 1
      direct/src/showbase/ShowBaseGlobal.py
  57. 7 20
      direct/src/showutil/Effects.py
  58. 5 6
      direct/src/showutil/TexMemWatcher.py
  59. 0 1
      direct/src/stdpy/file.py
  60. 3 3
      direct/src/stdpy/threading2.py
  61. 0 3
      direct/src/task/MiniTask.py
  62. 1 1
      direct/src/task/Task.py
  63. 1 1
      direct/src/task/TaskTester.py
  64. 1 0
      direct/src/tkpanels/DirectSessionPanel.py
  65. 1 0
      direct/src/tkpanels/MopathRecorder.py
  66. 13 12
      direct/src/tkpanels/NotifyPanel.py
  67. 0 1
      direct/src/tkpanels/ParticlePanel.py
  68. 3 4
      direct/src/tkpanels/Placer.py
  69. 0 2
      direct/src/tkpanels/TaskManagerPanel.py
  70. 12 12
      direct/src/tkwidgets/AppShell.py
  71. 1 0
      direct/src/tkwidgets/Dial.py
  72. 7 14
      direct/src/tkwidgets/EntryScale.py
  73. 1 0
      direct/src/tkwidgets/Floater.py
  74. 2 2
      direct/src/tkwidgets/MemoryExplorer.py
  75. 1 0
      direct/src/tkwidgets/SceneGraphExplorer.py
  76. 2 8
      direct/src/tkwidgets/Tree.py
  77. 2 0
      direct/src/tkwidgets/Valuator.py
  78. 8 4
      direct/src/tkwidgets/VectorWidgets.py
  79. 1 0
      direct/src/wxwidgets/WxPandaShell.py

+ 2 - 2
direct/src/controls/GravityWalker.py

@@ -22,8 +22,8 @@ from direct.showbase.InputStateGlobal import inputState
 from direct.showbase.MessengerGlobal import messenger
 from direct.task.Task import Task
 from direct.task.TaskManagerGlobal import taskMgr
-from direct.extensions_native import VBase3_extensions
-from direct.extensions_native import VBase4_extensions
+from direct.extensions_native import VBase3_extensions # pylint: disable=unused-import
+from direct.extensions_native import VBase4_extensions # pylint: disable=unused-import
 from panda3d.core import (
     BitMask32,
     ClockObject,

+ 3 - 3
direct/src/controls/PhysicsWalker.py

@@ -23,9 +23,9 @@ from direct.showbase.InputStateGlobal import inputState
 from direct.showbase.MessengerGlobal import messenger
 from direct.task.Task import Task
 from direct.task.TaskManagerGlobal import taskMgr
-from direct.extensions_native import Mat3_extensions
-from direct.extensions_native import VBase3_extensions
-from direct.extensions_native import VBase4_extensions
+from direct.extensions_native import Mat3_extensions # pylint: disable=unused-import
+from direct.extensions_native import VBase3_extensions # pylint: disable=unused-import
+from direct.extensions_native import VBase4_extensions # pylint: disable=unused-import
 from panda3d.core import (
     BitMask32,
     ClockObject,

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

@@ -126,10 +126,10 @@ class Notifier:
         Exception: error
         """
         message = str(errorString)
-        if Notifier.showTime.getValue():
-            string = (self.getTime() + str(exception) + ": " + self.__name + "(error): " + message)
+        if Notifier.showTime:
+            string = f'{self.getTime()}{exception!s}: {self.__name}(error): {message}'
         else:
-            string = (str(exception) + ": " + self.__name + "(error): " + message)
+            string = f'{exception!s}: {self.__name}(error): {message}'
         self.__log(string)
         raise exception(errorString)
 
@@ -140,10 +140,10 @@ class Notifier:
         """
         if self.__warning:
             message = str(warningString)
-            if Notifier.showTime.getValue():
-                string = (self.getTime() + self.__name + '(warning): ' + message)
+            if Notifier.showTime:
+                string = f'{self.getTime()}{self.__name}(warning): {message}'
             else:
-                string = (":" + self.__name + '(warning): ' + message)
+                string = f':{self.__name}(warning): {message}'
             self.__log(string)
             self.__print(string)
         return 1 # to allow assert myNotify.warning("blah")
@@ -167,10 +167,10 @@ class Notifier:
         """
         if self.__debug:
             message = str(debugString)
-            if Notifier.showTime.getValue():
-                string = (self.getTime() + self.__name + '(debug): ' + message)
+            if Notifier.showTime:
+                string = f'{self.getTime()}{self.__name}(debug): {message}'
             else:
-                string = (':' + self.__name + '(debug): ' + message)
+                string = f':{self.__name}(debug): {message}'
             self.__log(string)
             self.__print(string)
         return 1 # to allow assert myNotify.debug("blah")
@@ -194,10 +194,10 @@ class Notifier:
         """
         if self.__info:
             message = str(infoString)
-            if Notifier.showTime.getValue():
-                string = (self.getTime() + self.__name + ': ' + message)
+            if Notifier.showTime:
+                string = f'{self.getTime()}{self.__name}: {message}'
             else:
-                string = (':' + self.__name + ': ' + message)
+                string = f':{self.__name}: {message}'
             self.__log(string)
             self.__print(string)
         return 1 # to allow assert myNotify.info("blah")
@@ -271,7 +271,7 @@ class Notifier:
                         state = "%s, %s"%(state, stateObj.getName())
 
                 if hasattr(obj, 'doId'):
-                    doId = " doId:%s"%(obj.doId,)
+                    doId = f" doId:{obj.doId}"
             #if type(obj) == types.ClassType:
             #    name = "%s."%(obj.__class__.__name__,)
             string = ":%s:%s [%-7s] id(%s)%s %s"%(

+ 3 - 2
direct/src/directtools/DirectCameraControl.py

@@ -308,7 +308,7 @@ class DirectCameraControl(DirectObject):
 
     def XZTranslateTask(self, state):
         coaDist = Vec3(self.coaMarker.getPos(base.direct.camera)).length()
-        xlateSF = (coaDist / base.direct.dr.near)
+        xlateSF = coaDist / base.direct.dr.near
         base.direct.camera.setPos(base.direct.camera,
                              (-0.5 * base.direct.dr.mouseDeltaX *
                               base.direct.dr.nearWidth *
@@ -822,7 +822,8 @@ class DirectCameraControl(DirectObject):
         try:
             self.camManipRef.setPos(base.direct.camera, deltaMove)
         except Exception:
-            self.notify.debug
+            #self.notify.debug
+            pass
 
         parent = base.direct.camera.getParent()
         base.direct.camera.wrtReparentTo(self.camManipRef)

+ 7 - 5
direct/src/directtools/DirectSession.py

@@ -36,6 +36,8 @@ from direct.cluster.ClusterServer import ClusterServer
 from direct.gui import OnscreenText
 from direct.interval.IntervalGlobal import Func, Sequence
 from direct.task.TaskManagerGlobal import taskMgr
+from direct.showbase.MessengerGlobal import messenger
+
 
 class DirectSession(DirectObject):
 
@@ -1218,12 +1220,13 @@ class DisplayRegionContext(DirectObject):
                          self.near,
                          (self.nearHeight*0.5) * self.mouseY)
 
+
 class DisplayRegionList(DirectObject):
     def __init__(self):
         self.displayRegionList = []
         i = 0
         # Things are funky if we are oobe
-        if (hasattr(base, 'oobeMode') and base.oobeMode):
+        if getattr(base, 'oobeMode', False):
             # assume we only have one cam at this point
             drc = DisplayRegionContext(base.cam)
             self.displayRegionList.append(drc)
@@ -1239,10 +1242,9 @@ class DisplayRegionList(DirectObject):
             # a display region for each real display region, and then
             # keep track of which are currently active (e.g. use a flag)
             # processing only them.
-            for camIndex in range(len(base.camList)):
-                cam = base.camList[camIndex]
-                if cam.getName()=='<noname>':
-                    cam.setName('Camera%d' % camIndex)
+            for camIndex, cam in enumerate(base.camList):
+                if cam.name == '<noname>':
+                    cam.name = f'Camera{camIndex}'
                 drc = DisplayRegionContext(cam)
                 self.displayRegionList.append(drc)
 

+ 3 - 1
direct/src/directutil/LargeBlobSenderConsts.py

@@ -7,6 +7,8 @@ ChunkSize = 100
 FilePattern = 'largeBlob.%s'
 
 def getLargeBlobPath():
+    from panda3d.core import ConfigVariableString, ConfigFlags
+
     # this folder needs to be accessible by everyone that is going to level edit
     # an area as a group
-    return config.GetString('large-blob-path', 'i:\\toontown_in_game_editor_temp')
+    return ConfigVariableString('large-blob-path', 'i:\\toontown_in_game_editor_temp', 'DConfig', ConfigFlags.F_dconfig).value

+ 1 - 1
direct/src/directutil/Mopath.py

@@ -61,7 +61,7 @@ class Mopath(DirectObject):
             self.reset()
 
         self.__extractCurves(nodePath)
-        if self.tNurbsCurve != []:
+        if self.tNurbsCurve:
             self.maxT = self.tNurbsCurve[-1].getMaxT()
         elif self.xyzNurbsCurve is not None:
             self.maxT = self.xyzNurbsCurve.getMaxT()

+ 2 - 0
direct/src/directutil/Verify.py

@@ -45,6 +45,8 @@ Please use assert (properly) and do proper error handling; and use
 where it helps you resist using assert for error handling.
 """
 
+__all__ = ["verify"]
+
 from panda3d.core import ConfigVariableBool
 
 # Set to true to load pdb on failure.

+ 4 - 3
direct/src/dist/_android.py

@@ -3,9 +3,10 @@
 import xml.etree.ElementTree as ET
 
 from ._proto.targeting_pb2 import Abi
-from ._proto.config_pb2 import BundleConfig
-from ._proto.files_pb2 import NativeLibraries
-from ._proto.Resources_pb2 import XmlNode, ResourceTable
+from ._proto.config_pb2 import BundleConfig # pylint: disable=unused-import
+from ._proto.files_pb2 import NativeLibraries # pylint: disable=unused-import
+from ._proto.Resources_pb2 import ResourceTable # pylint: disable=unused-import
+from ._proto.Resources_pb2 import XmlNode
 
 
 AbiAlias = Abi.AbiAlias

+ 4 - 3
direct/src/dist/commands.py

@@ -125,7 +125,7 @@ PACKAGE_LIB_DIRS = {
     'PyQt5':  [('PyQt5/Qt5/bin', 'PyQt5_Qt5')],
 }
 
-SITE_PY = u"""
+SITE_PY = """
 import sys
 from _frozen_importlib import _imp, FrozenImporter
 
@@ -592,7 +592,7 @@ class build_apps(setuptools.Command):
 
         whlcache = os.path.join(self.build_base, '__whl_cache__')
 
-        pip_version = int(pip.__version__.split('.')[0])
+        pip_version = int(pip.__version__.split('.', 1)[0])
         if pip_version < 9:
             raise RuntimeError("pip 9.0 or greater is required, but found {}".format(pip.__version__))
 
@@ -1494,7 +1494,8 @@ class build_apps(setuptools.Command):
         for idx in string_tables.keys():
             elf.seek(shoff + idx * shentsize)
             type, offset, size, link, entsize = struct.unpack_from(section_struct, elf.read(shentsize))
-            if type != 3: continue
+            if type != 3:
+                continue
             elf.seek(offset)
             string_tables[idx] = elf.read(size)
 

+ 2 - 2
direct/src/dist/installers.py

@@ -271,7 +271,7 @@ def create_aab(command, basename, build_dir):
     bundle.add_subfile('base/manifest/AndroidManifest.xml', p3d.StringStream(axml.dumps()), 9)
 
     # Add the classes.dex.
-    bundle.add_subfile(f'base/dex/classes.dex', build_dir_fn / 'classes.dex', 9)
+    bundle.add_subfile('base/dex/classes.dex', build_dir_fn / 'classes.dex', 9)
 
     # Add libraries, compressed.
     for abi in os.listdir(os.path.join(build_dir, 'lib')):
@@ -302,7 +302,7 @@ def create_aab(command, basename, build_dir):
             # It appears to be encrypted, and we don't have a passphrase, so we
             # must request it on the command-line.
             from getpass import getpass
-            password = getpass(f'Enter pass phrase for private key: ')
+            password = getpass('Enter pass phrase for private key: ')
 
         if not bundle.add_jar_signature(
                 p3d.Filename.from_os_specific(command.signing_certificate),

+ 1 - 1
direct/src/dist/pefile.py

@@ -302,7 +302,7 @@ class VersionInfoResource(object):
             # It contains a value.
             if type:
                 # It's a wchar array value.
-                value = u""
+                value = ""
                 c, = unpack('<H', data[offset:offset+2])
                 offset += 2
                 while c:

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

@@ -267,4 +267,4 @@ def cleanupAsyncRequests():
     """
     for asyncRequest in AsyncRequest._asyncRequests:
         asyncRequest.delete()
-    assert AsyncRequest._asyncRequests == {}
+    assert not AsyncRequest._asyncRequests

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

@@ -110,7 +110,7 @@ class ClientRepositoryBase(ConnectionRepository):
         ## self.flush()
 
     def specialName(self, label):
-        name = ("SpecialName %s %s" % (self.specialNameNumber, label))
+        name = f"SpecialName {self.specialNameNumber} {label}"
         self.specialNameNumber += 1
         return name
 

+ 2 - 2
direct/src/distributed/ConnectionRepository.py

@@ -382,7 +382,7 @@ class ConnectionRepository(
             # in the DC file.
             for i in range(dcFile.getNumClasses()):
                 dclass = dcFile.getClass(i)
-                if (dclass.getName()+ownerDcSuffix) in ownerImportSymbols:
+                if dclass.getName() + ownerDcSuffix in ownerImportSymbols:
                     number = dclass.getNumber()
                     className = dclass.getName() + ownerDcSuffix
 
@@ -466,7 +466,7 @@ class ConnectionRepository(
         hasProxy = 0
         if self.checkHttp():
             proxies = self.http.getProxiesForUrl(serverList[0])
-            hasProxy = (proxies != 'DIRECT')
+            hasProxy = proxies != 'DIRECT'
 
         if hasProxy:
             self.notify.info("Connecting to gameserver via proxy list: %s" % (proxies))

+ 2 - 0
direct/src/distributed/DistributedCartesianGrid.py

@@ -3,9 +3,11 @@ from direct.directnotify.DirectNotifyGlobal import directNotify
 
 from direct.distributed.DistributedNode import DistributedNode
 from direct.task import Task
+from direct.task.TaskManagerGlobal import taskMgr
 from direct.gui import DirectGuiGlobals
 from direct.showbase.EventGroup import EventGroup
 from direct.showbase.PythonUtil import report
+from direct.showbase.MessengerGlobal import messenger
 from direct.distributed.GridParent import GridParent
 
 if __debug__:

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

@@ -633,6 +633,7 @@ class DoInterestManager(DirectObject.DirectObject):
 
 if __debug__:
     import unittest
+    import time
 
     class AsyncTestCase(unittest.TestCase):
         def setCompleted(self):
@@ -647,7 +648,7 @@ if __debug__:
         suiteClass = AsyncTestSuite
 
     class AsyncTextTestRunner(unittest.TextTestRunner):
-        def run(self, testCase):
+        def run(self, test):
             result = self._makeResult()
             startTime = time.time()
             test(result)

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

@@ -1,6 +1,6 @@
 from direct.distributed.DistributedSmoothNodeBase import DistributedSmoothNodeBase
 from direct.distributed.GridParent import GridParent
-from direct.showbase.PythonUtil import report
+from direct.showbase.PythonUtil import report, getBase
 
 
 class GridChild:

+ 3 - 3
direct/src/distributed/NetMessenger.py

@@ -20,9 +20,9 @@ MESSAGE_TYPES=(
 
 # This is the reverse look up for the recipient of the
 # datagram:
-MESSAGE_STRINGS={}
-for i in zip(MESSAGE_TYPES, range(1, len(MESSAGE_TYPES)+1)):
-    MESSAGE_STRINGS[i[0]]=i[1]
+MESSAGE_STRINGS = {}
+for i in zip(MESSAGE_TYPES, range(1, len(MESSAGE_TYPES) + 1)):
+    MESSAGE_STRINGS[i[0]] = i[1]
 
 
 class NetMessenger(Messenger):

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

@@ -4,6 +4,7 @@ from direct.task.TaskManagerGlobal import taskMgr
 from direct.distributed import DistributedObject
 from direct.directnotify import DirectNotifyGlobal
 from direct.distributed.ClockDelta import globalClockDelta
+from direct.showbase.MessengerGlobal import messenger
 
 
 class TimeManager(DistributedObject.DistributedObject):

+ 58 - 58
direct/src/extensions_native/CInterval_extensions.py

@@ -61,66 +61,66 @@ del privPostEvent
 #####################################################################
 
 def popupControls(self, tl = None):
-        """
-        Popup control panel for interval.
-        """
-        import math
-        # Don't use a regular import, to prevent ModuleFinder from picking
-        # it up as a dependency when building a .p3d package.
-        import importlib
-        EntryScale = importlib.import_module('direct.tkwidgets.EntryScale')
-        tkinter = importlib.import_module('tkinter')
+    """
+    Popup control panel for interval.
+    """
+    import math
+    # Don't use a regular import, to prevent ModuleFinder from picking
+    # it up as a dependency when building a .p3d package.
+    import importlib
+    EntryScale = importlib.import_module('direct.tkwidgets.EntryScale')
+    tkinter = importlib.import_module('tkinter')
 
-        if tl is None:
-            tl = tkinter.Toplevel()
-            tl.title('Interval Controls')
-        outerFrame = tkinter.Frame(tl)
-        def entryScaleCommand(t, s=self):
-            s.setT(t)
-            s.pause()
-        self.es = es = EntryScale.EntryScale(
-            outerFrame, text = self.getName(),
-            min = 0, max = math.floor(self.getDuration() * 100) / 100,
-            command = entryScaleCommand)
-        es.set(self.getT(), fCommand = 0)
-        es.pack(expand = 1, fill = tkinter.X)
-        bf = tkinter.Frame(outerFrame)
-        # Jump to start and end
-        def toStart(s=self, es=es):
-            s.setT(0.0)
-            s.pause()
-        def toEnd(s=self):
-            s.setT(s.getDuration())
-            s.pause()
-        jumpToStart = tkinter.Button(bf, text = '<<', command = toStart)
-        # Stop/play buttons
-        def doPlay(s=self, es=es):
-            s.resume(es.get())
+    if tl is None:
+        tl = tkinter.Toplevel()
+        tl.title('Interval Controls')
+    outerFrame = tkinter.Frame(tl)
+    def entryScaleCommand(t, s=self):
+        s.setT(t)
+        s.pause()
+    self.es = es = EntryScale.EntryScale(
+        outerFrame, text = self.getName(),
+        min = 0, max = math.floor(self.getDuration() * 100) / 100,
+        command = entryScaleCommand)
+    es.set(self.getT(), fCommand = 0)
+    es.pack(expand = 1, fill = tkinter.X)
+    bf = tkinter.Frame(outerFrame)
+    # Jump to start and end
+    def toStart(s=self, es=es):
+        s.setT(0.0)
+        s.pause()
+    def toEnd(s=self):
+        s.setT(s.getDuration())
+        s.pause()
+    jumpToStart = tkinter.Button(bf, text = '<<', command = toStart)
+    # Stop/play buttons
+    def doPlay(s=self, es=es):
+        s.resume(es.get())
 
-        stop = tkinter.Button(bf, text = 'Stop',
-                      command = lambda s=self: s.pause())
-        play = tkinter.Button(
-            bf, text = 'Play',
-            command = doPlay)
-        jumpToEnd = tkinter.Button(bf, text = '>>', command = toEnd)
-        jumpToStart.pack(side = tkinter.LEFT, expand = 1, fill = tkinter.X)
-        play.pack(side = tkinter.LEFT, expand = 1, fill = tkinter.X)
-        stop.pack(side = tkinter.LEFT, expand = 1, fill = tkinter.X)
-        jumpToEnd.pack(side = tkinter.LEFT, expand = 1, fill = tkinter.X)
-        bf.pack(expand = 1, fill = tkinter.X)
-        outerFrame.pack(expand = 1, fill = tkinter.X)
-        # Add function to update slider during setT calls
-        def update(t, es=es):
-            es.set(t, fCommand = 0)
-        if not hasattr(self, "setTHooks"):
-            self.setTHooks = []
-        self.setTHooks.append(update)
-        self.setWantsTCallback(1)
-        # Clear out function on destroy
-        def onDestroy(e, s=self, u=update):
-            if u in s.setTHooks:
-                s.setTHooks.remove(u)
-        tl.bind('<Destroy>', onDestroy)
+    stop = tkinter.Button(bf, text = 'Stop',
+                  command = lambda s=self: s.pause())
+    play = tkinter.Button(
+        bf, text = 'Play',
+        command = doPlay)
+    jumpToEnd = tkinter.Button(bf, text = '>>', command = toEnd)
+    jumpToStart.pack(side = tkinter.LEFT, expand = 1, fill = tkinter.X)
+    play.pack(side = tkinter.LEFT, expand = 1, fill = tkinter.X)
+    stop.pack(side = tkinter.LEFT, expand = 1, fill = tkinter.X)
+    jumpToEnd.pack(side = tkinter.LEFT, expand = 1, fill = tkinter.X)
+    bf.pack(expand = 1, fill = tkinter.X)
+    outerFrame.pack(expand = 1, fill = tkinter.X)
+    # Add function to update slider during setT calls
+    def update(t, es=es):
+        es.set(t, fCommand = 0)
+    if not hasattr(self, "setTHooks"):
+        self.setTHooks = []
+    self.setTHooks.append(update)
+    self.setWantsTCallback(1)
+    # Clear out function on destroy
+    def onDestroy(e, s=self, u=update):
+        if u in s.setTHooks:
+            s.setTHooks.remove(u)
+    tl.bind('<Destroy>', onDestroy)
 
 Dtool_funcToMethod(popupControls, CInterval)
 del popupControls

+ 21 - 21
direct/src/extensions_native/HTTPChannel_extensions.py

@@ -8,22 +8,22 @@ from .extension_native_helpers import Dtool_funcToMethod
 
 
 def spawnTask(self, name = None, callback = None, extraArgs = []):
-        """Spawns a task to service the download recently requested
-        via beginGetDocument(), etc., and/or downloadToFile() or
-        downloadToRam().  If a callback is specified, that function is
-        called when the download is complete, passing in the extraArgs
-        given.
+    """Spawns a task to service the download recently requested
+    via beginGetDocument(), etc., and/or downloadToFile() or
+    downloadToRam().  If a callback is specified, that function is
+    called when the download is complete, passing in the extraArgs
+    given.
 
-        Returns the newly-spawned task.
-        """
-        if not name:
-            name = str(self.getUrl())
-        from direct.task import Task
-        from direct.task.TaskManagerGlobal import taskMgr
-        task = Task.Task(self.doTask)
-        task.callback = callback
-        task.callbackArgs = extraArgs
-        return taskMgr.add(task, name)
+    Returns the newly-spawned task.
+    """
+    if not name:
+        name = str(self.getUrl())
+    from direct.task import Task
+    from direct.task.TaskManagerGlobal import taskMgr
+    task = Task.Task(self.doTask)
+    task.callback = callback
+    task.callbackArgs = extraArgs
+    return taskMgr.add(task, name)
 
 if hasattr(core, 'HTTPChannel'):
     Dtool_funcToMethod(spawnTask, core.HTTPChannel)
@@ -31,12 +31,12 @@ del spawnTask
 #####################################################################
 
 def doTask(self, task):
-        from direct.task import Task
-        if self.run():
-            return Task.cont
-        if task.callback:
-            task.callback(*task.callbackArgs)
-        return Task.done
+    from direct.task import Task
+    if self.run():
+        return Task.cont
+    if task.callback:
+        task.callback(*task.callbackArgs)
+    return Task.done
 
 if hasattr(core, 'HTTPChannel'):
     Dtool_funcToMethod(doTask, core.HTTPChannel)

+ 147 - 146
direct/src/extensions_native/NodePath_extensions.py

@@ -116,6 +116,7 @@ def remove(self):
         warnings.warn("NodePath.remove() is deprecated.  Use remove_node() instead.", DeprecationWarning, stacklevel=2)
     # Send message in case anyone needs to do something
     # before node is deleted
+    from direct.showbase.MessengerGlobal import messenger
     messenger.send('preRemoveNodePath', [self])
     # Remove nodePath
     self.removeNode()
@@ -493,7 +494,7 @@ def showCS(self, mask = None):
     npc = self.findAllMatches('**/+CollisionNode')
     for p in range(0, npc.getNumPaths()):
         np = npc[p]
-        if (mask == None or (np.node().getIntoCollideMask() & mask).getWord()):
+        if mask is None or (np.node().getIntoCollideMask() & mask).getWord():
             np.show()
 
 Dtool_funcToMethod(showCS, NodePath)
@@ -512,7 +513,7 @@ def hideCS(self, mask = None):
     npc = self.findAllMatches('**/+CollisionNode')
     for p in range(0, npc.getNumPaths()):
         np = npc[p]
-        if (mask == None or (np.node().getIntoCollideMask() & mask).getWord()):
+        if mask is None or (np.node().getIntoCollideMask() & mask).getWord():
             np.hide()
 
 Dtool_funcToMethod(hideCS, NodePath)
@@ -667,40 +668,40 @@ del attachCollisionRay
 #####################################################################
 def flattenMultitex(self, stateFrom = None, target = None,
                         useGeom = 0, allowTexMat = 0, win = None):
-        from panda3d.core import MultitexReducer
-        mr = MultitexReducer()
-        if target is not None:
-            mr.setTarget(target)
-        mr.setUseGeom(useGeom)
-        mr.setAllowTexMat(allowTexMat)
-
-        if win is None:
-            win = base.win
-
-        if stateFrom is None:
-            mr.scan(self)
-        else:
-            mr.scan(self, stateFrom)
-        mr.flatten(win)
+    from panda3d.core import MultitexReducer
+    mr = MultitexReducer()
+    if target is not None:
+        mr.setTarget(target)
+    mr.setUseGeom(useGeom)
+    mr.setAllowTexMat(allowTexMat)
+
+    if win is None:
+        win = base.win
+
+    if stateFrom is None:
+        mr.scan(self)
+    else:
+        mr.scan(self, stateFrom)
+    mr.flatten(win)
 Dtool_funcToMethod(flattenMultitex, NodePath)
 del flattenMultitex
 #####################################################################
 def getNumDescendants(self):
-        return len(self.findAllMatches('**')) - 1
+    return len(self.findAllMatches('**')) - 1
 Dtool_funcToMethod(getNumDescendants, NodePath)
 del getNumDescendants
 #####################################################################
 def removeNonCollisions(self):
-        # remove anything that is not collision-related
-        print("NodePath.removeNonCollisions() is deprecated")
-        stack = [self]
-        while len(stack) > 0:
-                np = stack.pop()
-                # if there are no CollisionNodes under this node, remove it
-                if np.find('**/+CollisionNode').isEmpty():
-                        np.detachNode()
-                else:
-                        stack.extend(np.getChildren())
+    # remove anything that is not collision-related
+    print("NodePath.removeNonCollisions() is deprecated")
+    stack = [self]
+    while len(stack) > 0:
+        np = stack.pop()
+        # if there are no CollisionNodes under this node, remove it
+        if np.find('**/+CollisionNode').isEmpty():
+            np.detachNode()
+        else:
+            stack.extend(np.getChildren())
 Dtool_funcToMethod(removeNonCollisions, NodePath)
 del removeNonCollisions
 #####################################################################
@@ -729,130 +730,130 @@ def subdivideCollisions(self, numSolidsInLeaves):
         colNp.stash()
 
 def r_subdivideCollisions(self, solids, numSolidsInLeaves):
-        # takes a list of solids, returns a list containing some number of lists,
-        # with the solids evenly distributed between them (recursively nested until
-        # the lists at the leaves contain no more than numSolidsInLeaves)
-        # if solids is already small enough, returns solids unchanged
-        if len(solids) <= numSolidsInLeaves:
-            return solids
-        origins = []
-        avgX = 0
-        avgY = 0
-        avgZ = 0
-        minX = None
-        minY = None
-        minZ = None
-        maxX = None
-        maxY = None
-        maxZ = None
-        for solid in solids:
-            origin = solid.getCollisionOrigin()
-            origins.append(origin)
-            x = origin.getX()
-            y = origin.getY()
-            z = origin.getZ()
-            avgX += x
-            avgY += y
-            avgZ += z
-            if minX is None:
-                minX = x
-                minY = y
-                minZ = z
-                maxX = x
-                maxY = y
-                maxZ = z
+    # takes a list of solids, returns a list containing some number of lists,
+    # with the solids evenly distributed between them (recursively nested until
+    # the lists at the leaves contain no more than numSolidsInLeaves)
+    # if solids is already small enough, returns solids unchanged
+    if len(solids) <= numSolidsInLeaves:
+        return solids
+    origins = []
+    avgX = 0
+    avgY = 0
+    avgZ = 0
+    minX = None
+    minY = None
+    minZ = None
+    maxX = None
+    maxY = None
+    maxZ = None
+    for solid in solids:
+        origin = solid.getCollisionOrigin()
+        origins.append(origin)
+        x = origin.getX()
+        y = origin.getY()
+        z = origin.getZ()
+        avgX += x
+        avgY += y
+        avgZ += z
+        if minX is None:
+            minX = x
+            minY = y
+            minZ = z
+            maxX = x
+            maxY = y
+            maxZ = z
+        else:
+            minX = min(x, minX)
+            minY = min(y, minY)
+            minZ = min(z, minZ)
+            maxX = max(x, maxX)
+            maxY = max(y, maxY)
+            maxZ = max(z, maxZ)
+    avgX /= len(solids)
+    avgY /= len(solids)
+    avgZ /= len(solids)
+    extentX = maxX - minX
+    extentY = maxY - minY
+    extentZ = maxZ - minZ
+    maxExtent = max(extentX, extentY, extentZ)
+    # sparse octree
+    xyzSolids = []
+    XyzSolids = []
+    xYzSolids = []
+    XYzSolids = []
+    xyZSolids = []
+    XyZSolids = []
+    xYZSolids = []
+    XYZSolids = []
+    midX = avgX
+    midY = avgY
+    midZ = avgZ
+    # throw out axes that are not close to the max axis extent; try and keep
+    # the divisions square/spherical
+    if extentX < (maxExtent * .75) or extentX > (maxExtent * 1.25):
+        midX += maxExtent
+    if extentY < (maxExtent * .75) or extentY > (maxExtent * 1.25):
+        midY += maxExtent
+    if extentZ < (maxExtent * .75) or extentZ > (maxExtent * 1.25):
+        midZ += maxExtent
+    for i, solid in enumerate(solids):
+        origin = origins[i]
+        x = origin.getX()
+        y = origin.getY()
+        z = origin.getZ()
+        if x < midX:
+            if y < midY:
+                if z < midZ:
+                    xyzSolids.append(solids[i])
+                else:
+                    xyZSolids.append(solids[i])
             else:
-                minX = min(x, minX)
-                minY = min(y, minY)
-                minZ = min(z, minZ)
-                maxX = max(x, maxX)
-                maxY = max(y, maxY)
-                maxZ = max(z, maxZ)
-        avgX /= len(solids)
-        avgY /= len(solids)
-        avgZ /= len(solids)
-        extentX = maxX - minX
-        extentY = maxY - minY
-        extentZ = maxZ - minZ
-        maxExtent = max(max(extentX, extentY), extentZ)
-        # sparse octree
-        xyzSolids = []
-        XyzSolids = []
-        xYzSolids = []
-        XYzSolids = []
-        xyZSolids = []
-        XyZSolids = []
-        xYZSolids = []
-        XYZSolids = []
-        midX = avgX
-        midY = avgY
-        midZ = avgZ
-        # throw out axes that are not close to the max axis extent; try and keep
-        # the divisions square/spherical
-        if extentX < (maxExtent * .75) or extentX > (maxExtent * 1.25):
-            midX += maxExtent
-        if extentY < (maxExtent * .75) or extentY > (maxExtent * 1.25):
-            midY += maxExtent
-        if extentZ < (maxExtent * .75) or extentZ > (maxExtent * 1.25):
-            midZ += maxExtent
-        for i, solid in enumerate(solids):
-            origin = origins[i]
-            x = origin.getX()
-            y = origin.getY()
-            z = origin.getZ()
-            if x < midX:
-                if y < midY:
-                    if z < midZ:
-                        xyzSolids.append(solids[i])
-                    else:
-                        xyZSolids.append(solids[i])
+                if z < midZ:
+                    xYzSolids.append(solids[i])
                 else:
-                    if z < midZ:
-                        xYzSolids.append(solids[i])
-                    else:
-                        xYZSolids.append(solids[i])
+                    xYZSolids.append(solids[i])
+        else:
+            if y < midY:
+                if z < midZ:
+                    XyzSolids.append(solids[i])
+                else:
+                    XyZSolids.append(solids[i])
             else:
-                if y < midY:
-                    if z < midZ:
-                        XyzSolids.append(solids[i])
-                    else:
-                        XyZSolids.append(solids[i])
+                if z < midZ:
+                    XYzSolids.append(solids[i])
                 else:
-                    if z < midZ:
-                        XYzSolids.append(solids[i])
-                    else:
-                        XYZSolids.append(solids[i])
-        newSolids = []
-        if len(xyzSolids) > 0:
-            newSolids.append(self.r_subdivideCollisions(xyzSolids, numSolidsInLeaves))
-        if len(XyzSolids) > 0:
-            newSolids.append(self.r_subdivideCollisions(XyzSolids, numSolidsInLeaves))
-        if len(xYzSolids) > 0:
-            newSolids.append(self.r_subdivideCollisions(xYzSolids, numSolidsInLeaves))
-        if len(XYzSolids) > 0:
-            newSolids.append(self.r_subdivideCollisions(XYzSolids, numSolidsInLeaves))
-        if len(xyZSolids) > 0:
-            newSolids.append(self.r_subdivideCollisions(xyZSolids, numSolidsInLeaves))
-        if len(XyZSolids) > 0:
-            newSolids.append(self.r_subdivideCollisions(XyZSolids, numSolidsInLeaves))
-        if len(xYZSolids) > 0:
-            newSolids.append(self.r_subdivideCollisions(xYZSolids, numSolidsInLeaves))
-        if len(XYZSolids) > 0:
-            newSolids.append(self.r_subdivideCollisions(XYZSolids, numSolidsInLeaves))
-        #import pdb;pdb.set_trace()
-        return newSolids
+                    XYZSolids.append(solids[i])
+    newSolids = []
+    if len(xyzSolids) > 0:
+        newSolids.append(self.r_subdivideCollisions(xyzSolids, numSolidsInLeaves))
+    if len(XyzSolids) > 0:
+        newSolids.append(self.r_subdivideCollisions(XyzSolids, numSolidsInLeaves))
+    if len(xYzSolids) > 0:
+        newSolids.append(self.r_subdivideCollisions(xYzSolids, numSolidsInLeaves))
+    if len(XYzSolids) > 0:
+        newSolids.append(self.r_subdivideCollisions(XYzSolids, numSolidsInLeaves))
+    if len(xyZSolids) > 0:
+        newSolids.append(self.r_subdivideCollisions(xyZSolids, numSolidsInLeaves))
+    if len(XyZSolids) > 0:
+        newSolids.append(self.r_subdivideCollisions(XyZSolids, numSolidsInLeaves))
+    if len(xYZSolids) > 0:
+        newSolids.append(self.r_subdivideCollisions(xYZSolids, numSolidsInLeaves))
+    if len(XYZSolids) > 0:
+        newSolids.append(self.r_subdivideCollisions(XYZSolids, numSolidsInLeaves))
+    #import pdb;pdb.set_trace()
+    return newSolids
 
 def r_constructCollisionTree(self, solidTree, parentNode, colName):
-        from panda3d.core import CollisionNode
-        for item in solidTree:
-            if isinstance(item[0], list):
-                newNode = parentNode.attachNewNode('%s-branch' % colName)
-                self.r_constructCollisionTree(item, newNode, colName)
-            else:
-                cn = CollisionNode('%s-leaf' % colName)
-                for solid in item:
-                    cn.addSolid(solid)
-                parentNode.attachNewNode(cn)
+    from panda3d.core import CollisionNode
+    for item in solidTree:
+        if isinstance(item[0], list):
+            newNode = parentNode.attachNewNode(f'{colName}-branch')
+            self.r_constructCollisionTree(item, newNode, colName)
+        else:
+            cn = CollisionNode(f'{colName}-leaf')
+            for solid in item:
+                cn.addSolid(solid)
+            parentNode.attachNewNode(cn)
 
 Dtool_funcToMethod(subdivideCollisions, NodePath)
 Dtool_funcToMethod(r_subdivideCollisions, NodePath)

+ 2 - 2
direct/src/filter/FilterManager.py

@@ -348,10 +348,10 @@ class FilterManager(DirectObject):
 
     def resizeBuffers(self):
         """ Resize all buffers to match the size of the window. """
-        for i in range(len(self.buffers)):
+        for i, buffer in enumerate(self.buffers):
             (mul, div, align) = self.sizes[i]
             (xsize, ysize) = self.getScaledSize(mul, div, align)
-            self.buffers[i].setSize(xsize, ysize)
+            buffer.setSize(xsize, ysize)
 
     def cleanup(self):
         """ Restore everything to its original state, deleting any

+ 3 - 5
direct/src/fsm/ClassicFSM.py

@@ -16,7 +16,6 @@ if __debug__:
     _debugFsms = {}
 
     def printDebugFsmList():
-        global _debugFsms
         for k in sorted(_debugFsms.keys()):
             print("%s %s" % (k, _debugFsms[k]()))
     __builtins__['debugFsmList'] = printDebugFsmList
@@ -83,7 +82,6 @@ class ClassicFSM(DirectObject):
         # doing this.
         self.__internalStateInFlux = 0
         if __debug__:
-            global _debugFsms
             _debugFsms[name] = weakref.ref(self)
 
     # I know this isn't how __repr__ is supposed to be used, but it
@@ -95,12 +93,12 @@ class ClassicFSM(DirectObject):
         """
         Print out something useful about the fsm
         """
+        name = self.getName()
         currentState = self.getCurrentState()
         if currentState:
-            str = ("ClassicFSM " + self.getName() + ' in state "' +
-                   currentState.getName() + '"')
+            str = f'ClassicFSM {name} in state "{currentState.getName()}"'
         else:
-            str = ("ClassicFSM " + self.getName() + ' not in any state')
+            str = f'ClassicFSM {name} not in any state'
         return str
 
     def enterInitialState(self, argList=[]):

+ 2 - 3
direct/src/fsm/FSM.py

@@ -615,9 +615,8 @@ class FSM(DirectObject):
         try:
             className = self.__class__.__name__
             if self.state:
-                str = ('%s FSM:%s in state "%s"' % (className, self._name, self.state))
+                return f'{className} FSM:{self._name} in state "{self.state}"'
             else:
-                str = ('%s FSM:%s in transition from \'%s\' to \'%s\'' % (className, self._name, self.oldState, self.newState))
-            return str
+                return f'{className} FSM:{self._name} in transition from \'{self.oldState}\' to \'{self.newState}\''
         finally:
             self.fsmLock.release()

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

@@ -145,6 +145,9 @@ class FourStateAI:
         assert self.__debugPrint("getState() returning %s"%(self.stateIndex,))
         return [self.stateIndex]
 
+    def sendUpdate(self, fieldName, args = [], sendToId = None):
+        raise NotImplementedError
+
     def sendState(self):
         assert self.__debugPrint("sendState()")
         self.sendUpdate('setState', self.getState())

+ 12 - 12
direct/src/fsm/StatePush.py

@@ -251,12 +251,12 @@ class FunctionCall(ReceivesMultipleStateChanges, PushesStateChanges):
 
 if __debug__:
     l = []
-    def handler(value, l=l):
+    def handler1(value, l=l):
         l.append(value)
-    assert l == []
+    assert not l
     sv = StateVar(0)
-    fc = FunctionCall(handler, sv)
-    assert l == []
+    fc = FunctionCall(handler1, sv)
+    assert not l
     fc.pushCurrentState()
     assert l == [0,]
     sv.set(1)
@@ -267,17 +267,17 @@ if __debug__:
     sv.destroy()
     del fc
     del sv
-    del handler
+    del handler1
     del l
 
     l = []
-    def handler(value, kDummy=None, kValue=None, l=l):
+    def handler2(value, kDummy=None, kValue=None, l=l):
         l.append((value, kValue))
-    assert l == []
+    assert not l
     sv = StateVar(0)
     ksv = StateVar('a')
-    fc = FunctionCall(handler, sv, kValue=ksv)
-    assert l == []
+    fc = FunctionCall(handler2, sv, kValue=ksv)
+    assert not l
     fc.pushCurrentState()
     assert l == [(0,'a',),]
     sv.set(1)
@@ -288,7 +288,7 @@ if __debug__:
     sv.destroy()
     del fc
     del sv
-    del handler
+    del handler2
     del l
 
 class EnterExit(StateChangeNode):
@@ -323,7 +323,7 @@ if __debug__:
     sv = StateVar(0)
     ee = EnterExit(sv, enter, exit)
     sv.set(0)
-    assert l == []
+    assert not l
     sv.set(1)
     assert l == [1,]
     sv.set(2)
@@ -357,7 +357,7 @@ if __debug__:
         l.append(value)
     p = Pulse()
     fc = FunctionCall(handler, p)
-    assert l == []
+    assert not l
     fc.pushCurrentState()
     assert l == [False, ]
     p.sendPulse()

+ 5 - 5
direct/src/gui/DirectEntry.py

@@ -13,7 +13,7 @@ from . import DirectGuiGlobals as DGG
 from .DirectFrame import DirectFrame
 from .OnscreenText import OnscreenText
 # import this to make sure it gets pulled into the publish
-import encodings.utf_8
+import encodings.utf_8 # pylint: disable=unused-import
 from direct.showbase.DirectObject import DirectObject
 
 # DirectEntry States:
@@ -221,9 +221,9 @@ class DirectEntry(DirectFrame):
     def _autoCapitalize(self):
         name = self.guiItem.getWtext()
         # capitalize each word, allowing for things like McMutton
-        capName = u''
+        capName = ''
         # track each individual word to detect prefixes like Mc
-        wordSoFar = u''
+        wordSoFar = ''
         # track whether the previous character was part of a word or not
         wasNonWordChar = True
         for i, character in enumerate(name):
@@ -232,9 +232,9 @@ class DirectEntry(DirectFrame):
             #   This assumes that string.lower and string.upper will return different
             #   values for all unicode letters.
             # - Don't count apostrophes as a break between words
-            if character.lower() == character.upper() and character != u"'":
+            if character.lower() == character.upper() and character != "'":
                 # we are between words
-                wordSoFar = u''
+                wordSoFar = ''
                 wasNonWordChar = True
             else:
                 capitalize = False

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

@@ -3,7 +3,7 @@ __all__ = ['DirectEntryScroll']
 from panda3d.core import NodePath, OmniBoundingVolume, PGVirtualFrame
 from . import DirectGuiGlobals as DGG
 from .DirectFrame import DirectFrame
-from .DirectEntry import DirectEntry
+
 
 class DirectEntryScroll(DirectFrame):
     def __init__(self, entry, parent = None, **kw):

+ 0 - 2
direct/src/gui/DirectGuiGlobals.py

@@ -146,11 +146,9 @@ def setDefaultFontFunc(newFontFunc):
     defaultFontFunc = newFontFunc
 
 def getDefaultDialogGeom():
-    global defaultDialogGeom
     return defaultDialogGeom
 
 def getDefaultDialogRelief():
-    global defaultDialogRelief
     return defaultDialogRelief
 
 def setDefaultDialogGeom(newDialogGeom, relief=None):

+ 1 - 0
direct/src/gui/DirectScrolledList.py

@@ -8,6 +8,7 @@ __all__ = ['DirectScrolledListItem', 'DirectScrolledList']
 
 from panda3d.core import TextNode
 from direct.showbase import ShowBaseGlobal
+from direct.showbase.MessengerGlobal import messenger
 from . import DirectGuiGlobals as DGG
 from direct.directnotify import DirectNotifyGlobal
 from direct.task.Task import Task

+ 1 - 1
direct/src/interval/AnimControlInterval.py

@@ -97,7 +97,7 @@ class AnimControlInterval(Interval.Interval):
         # Must we play the animation backwards?  We play backwards if
         # either (or both) of the following is true: the playRate is
         # negative, or endFrame is before startFrame.
-        self.reverse = (playRate < 0)
+        self.reverse = playRate < 0
         if self.endFrame < self.startFrame:
             self.reverse = 1
             t = self.endFrame

+ 2 - 2
direct/src/interval/Interval.py

@@ -9,8 +9,8 @@ from direct.showbase.DirectObject import DirectObject
 from direct.showbase.MessengerGlobal import messenger
 from direct.task.Task import Task, TaskManager
 from direct.task.TaskManagerGlobal import taskMgr
-from direct.extensions_native import CInterval_extensions
-from direct.extensions_native import NodePath_extensions
+from direct.extensions_native import CInterval_extensions # pylint: disable=unused-import
+from direct.extensions_native import NodePath_extensions # pylint: disable=unused-import
 import math
 
 

+ 1 - 0
direct/src/interval/IntervalTest.py

@@ -9,6 +9,7 @@ if __name__ == "__main__":
     from direct.showbase.ShowBase import ShowBase
     from direct.actor.Actor import Actor
     from direct.directutil import Mopath
+    from direct.showbase.MessengerGlobal import messenger
     from .ActorInterval import ActorInterval
     from .FunctionInterval import (
         AcceptInterval,

+ 46 - 46
direct/src/leveleditor/GraphEditorUI.py

@@ -365,19 +365,19 @@ class GraphEditorWindow(wx.Window):
 
     def DrawCurve(self, dc):
         if self.property == self._mainDialog.namestr:
-           self.drawX(dc)
-           self.drawY(dc)
-           self.drawZ(dc)
-           return
+            self.drawX(dc)
+            self.drawY(dc)
+            self.drawZ(dc)
+            return
         if self.property == property[AG.X]:
-           self.drawX(dc)
-           return
+            self.drawX(dc)
+            return
         if self.property == property[AG.Y]:
-           self.drawY(dc)
-           return
+            self.drawY(dc)
+            return
         if self.property == property[AG.Z]:
-           self.drawZ(dc)
-           return
+            self.drawZ(dc)
+            return
 
     def drawSingleCurve(self, list, dc):
         if len(list) == 1:
@@ -459,7 +459,7 @@ class GraphEditorWindow(wx.Window):
             if list[i][AG.KEYFRAME][AG.SELECT] == 1:
                 X1 = list[i][AG.KEYFRAME][AG.LOCAL_VALUE][0]
                 Y1 = list[i][AG.KEYFRAME][AG.LOCAL_VALUE][1]
-                if self._OneTangent == True:
+                if self._OneTangent is True:
                     for j in range(3,5):
                         X = list[i][j][AG.LOCAL_VALUE][0]
                         Y = list[i][j][AG.LOCAL_VALUE][1]
@@ -479,7 +479,7 @@ class GraphEditorWindow(wx.Window):
                             dc.SetPen(wx.Pen("brown",1))
                             dc.DrawLine(X1, Y1, X, Y)
 
-                if self._OneTangent == False:
+                if self._OneTangent is False:
                     if list[i][AG.IN_TANGENT][AG.SELECT] == 1:
                         X = list[i][AG.IN_TANGENT][AG.LOCAL_VALUE][0]
                         Y = list[i][AG.IN_TANGENT][AG.LOCAL_VALUE][1]
@@ -521,7 +521,7 @@ class GraphEditorWindow(wx.Window):
                         dc.DrawLine(X1, Y1, X, Y)
 
     def DrawSelectRec(self, dc):
-        if self._selectRec == True:
+        if self._selectRec is True:
             dc.SetPen(wx.Pen("navy", 1))
             dc.SetBrush(wx.Brush("navy"))
             ## dc.SetLogicalFunction(wx.AND)
@@ -607,19 +607,19 @@ class GraphEditorWindow(wx.Window):
         for i in range(len(list)):
             if list[i][AG.KEYFRAME][AG.SELECT] == 1:
                 inside = self.inside(self.pos, self.newPos, (list[i][AG.KEYFRAME][AG.LOCAL_VALUE][0], list[i][AG.KEYFRAME][AG.LOCAL_VALUE][1]))
-                if inside == True:
+                if inside is True:
                     list[i][AG.KEYFRAME][AG.SELECT] = 0
-                if inside == False:
+                if inside is False:
                     find = False
                     for j in range(3,5):
                         inside = self.inside(self.pos, self.newPos, (list[i][j][AG.LOCAL_VALUE][0], list[i][j][AG.LOCAL_VALUE][1]))
-                        if inside == False:
+                        if inside is False:
                             list[i][j][AG.SELECT] = 0
-                        if inside == True:
+                        if inside is True:
                             list[i][j][AG.SELECT] = 1
                             find = True
                             flag = True
-                    if find == False:
+                    if find is False:
                         list[i][AG.KEYFRAME][AG.SELECT] == 0
 
         return flag
@@ -627,30 +627,30 @@ class GraphEditorWindow(wx.Window):
     def setNewKey(self, list):
         for i in range(len(list)):
             inside = self.inside(self.pos, self.newPos, (list[i][2][0][0], list[i][2][0][1]))
-            if inside == True:
+            if inside is True:
                 list[i][AG.KEYFRAME][AG.SELECT] = 1
-            if inside == False:
+            if inside is False:
                 list[i][AG.KEYFRAME][AG.SELECT] = 0
 
     def setSelection(self):
         if self.property == self._mainDialog.namestr:
-           self.setSelectionBase(self.X)
-           self.setSelectionBase(self.Y)
-           self.setSelectionBase(self.Z)
-           return
+            self.setSelectionBase(self.X)
+            self.setSelectionBase(self.Y)
+            self.setSelectionBase(self.Z)
+            return
         if self.property == property[AG.X]:
-           self.setSelectionBase(self.X)
-           return
+            self.setSelectionBase(self.X)
+            return
         if self.property == property[AG.Y]:
-           self.setSelectionBase(self.Y)
-           return
+            self.setSelectionBase(self.Y)
+            return
         if self.property == property[AG.Z]:
-           self.setSelectionBase(self.Z)
-           return
+            self.setSelectionBase(self.Z)
+            return
 
     def setSelectionBase(self, list):
         self.setExistKey(list)
-        if self.setExistKey(list) == True:
+        if self.setExistKey(list) is True:
             return
         else:
             self.setNewKey(list)
@@ -681,19 +681,19 @@ class GraphEditorWindow(wx.Window):
 
     def recalculateSlope(self):
         if self.property == self._mainDialog.namestr:
-           self.recalculateSlopeBase(self.X)
-           self.recalculateSlopeBase(self.Y)
-           self.recalculateSlopeBase(self.Z)
-           return
+            self.recalculateSlopeBase(self.X)
+            self.recalculateSlopeBase(self.Y)
+            self.recalculateSlopeBase(self.Z)
+            return
         if self.property == property[AG.X]:
-           self.recalculateSlopeBase(self.X)
-           return
+            self.recalculateSlopeBase(self.X)
+            return
         if self.property == property[AG.Y]:
-           self.recalculateSlopeBase(self.Y)
-           return
+            self.recalculateSlopeBase(self.Y)
+            return
         if self.property == property[AG.Z]:
-           self.recalculateSlopeBase(self.Z)
-           return
+            self.recalculateSlopeBase(self.Z)
+            return
 
     def recalculateSlopeBase(self, list):
         #recalculate the tangent slope
@@ -718,12 +718,12 @@ class GraphEditorWindow(wx.Window):
                         self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.INSLOPE][1] = temp1
                         return
                     if handler[1][0][0] < list[i][AG.KEYFRAME][AG.LOCAL_VALUE][0]:
-                        if self._OneTangent == False:
+                        if self._OneTangent is False:
                             list[i][AG.IN_TANGENT][0] = handler[1][0]
                             list[i][AG.IN_SLOPE][0] = handler[3][0]
                             list[i][AG.IN_SLOPE][1] = handler[3][1]
 
-                        if self._OneTangent == True:
+                        if self._OneTangent is True:
                             self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.OUTSLOPE][0] = newSlope[0]/self.unitWidth
                             self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.OUTSLOPE][1] = newSlope[1]/self.unitHeight
                             handler = self.generateHandler(self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]])
@@ -753,12 +753,12 @@ class GraphEditorWindow(wx.Window):
                         self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.OUTSLOPE][1] = temp1
                         return
                     if handler[2][0][0] > list[i][AG.KEYFRAME][AG.LOCAL_VALUE][0]:
-                        if self._OneTangent == False:
+                        if self._OneTangent is False:
                             list[i][AG.OUT_TANGENT][0] = handler[2][0]
                             list[i][AG.OUT_SLOPE][0] = handler[4][0]
                             list[i][AG.OUT_SLOPE][1] = handler[4][1]
 
-                        if self._OneTangent == True:
+                        if self._OneTangent is True:
                             self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.INSLOPE][0] = newSlope[0]/self.unitWidth
                             self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.INSLOPE][1] = newSlope[1]/self.unitHeight
                             handler = self.generateHandler(self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]])
@@ -862,7 +862,7 @@ class GraphEditorUI(wx.Dialog):
 
     def AddTreeNodes(self, parentItem, items):
         for item in items:
-            if type(item) == str:
+            if isinstance(item, str):
                 self.tree.AppendItem(parentItem, item)
 
     def OnSelChanged(self, evt):

+ 3 - 3
direct/src/leveleditor/LevelEditorBase.py

@@ -105,7 +105,7 @@ class LevelEditorBase(DirectObject):
         useDirectRenderStyle(self.statusReadout)
         self.statusReadout.reparentTo(hidden)
         self.statusLines = []
-        taskMgr.doMethodLater(5, self.updateStatusReadoutTimeouts, 'updateStatus')
+        base.taskMgr.doMethodLater(5, self.updateStatusReadoutTimeouts, 'updateStatus')
 
         self.loadSettings()
         self.reset()
@@ -236,7 +236,7 @@ class LevelEditorBase(DirectObject):
         self.ui.buildContextMenu(nodePath)
 
         if self.mode == self.EDIT_CURVE_MODE:
-            taskMgr.add(self.curveEditor.editCurve, "modify")
+            base.taskMgr.add(self.curveEditor.editCurve, "modify")
             self.curveEditor.accept("DIRECT-enter", self.curveEditor.onBaseMode)
 
     def deselectAll(self, np=None):
@@ -400,7 +400,7 @@ class LevelEditorBase(DirectObject):
                 self.statusLines.append([time,status,color])
 
         # update display of new status lines
-        self.statusReadout.reparentTo(aspect2d)
+        self.statusReadout.reparentTo(base.aspect2d)
         statusText = ""
         lastColor = None
         for currLine in self.statusLines:

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

@@ -6,6 +6,7 @@ from panda3d.core import BitMask32, Mat4, NodePath, Vec3
 from direct.wxwidgets.WxPandaShell import WxPandaShell
 from direct.wxwidgets.WxSlider import WxSlider
 from direct.directtools.DirectSelection import SelectionRay
+from direct.showbase.MessengerGlobal import messenger
 
 #from ViewPort import *
 from . import ObjectGlobals as OG

+ 1 - 1
direct/src/leveleditor/LevelLoader.py

@@ -15,10 +15,10 @@ import os
 from direct.leveleditor.LevelLoaderBase import LevelLoaderBase
 from direct.leveleditor.ObjectMgr import ObjectMgr
 from direct.leveleditor.ProtoPalette import ProtoPalette
-from direct.leveleditor import ObjectGlobals as OG
 from .ObjectHandler import ObjectHandler
 from .ObjectPalette import ObjectPalette
 
+
 class LevelLoader(LevelLoaderBase):
     def __init__(self):
         LevelLoaderBase.__init__(self)

+ 0 - 2
direct/src/leveleditor/ObjectMgrBase.py

@@ -931,5 +931,3 @@ class ObjectMgrBase:
                     self.Nodes.append(obj)
 
                 self.findActors(child)
-
-

+ 56 - 56
direct/src/leveleditor/PaletteTreeCtrl.py

@@ -27,18 +27,18 @@ class PaletteTreeCtrl(wx.TreeCtrl):
         self.SortChildren(parent)
         item, cookie = self.GetFirstChild(parent)
         while item:
-              if self.ItemHasChildren(item):
-                 self.SortTreeNodes(item)
+            if self.ItemHasChildren(item):
+                self.SortTreeNodes(item)
 
-              # continue iteration to the next child
-              item, cookie = self.GetNextChild(parent, cookie)
+            # continue iteration to the next child
+            item, cookie = self.GetNextChild(parent, cookie)
 
     def addTreeNodes(self, parentItem, parentItemName, items, itemKeys):
         roots = []
         rootItems = []
         for key in itemKeys:
             if parentItemName == items[key]:
-               roots.append(key)
+                roots.append(key)
         for root in roots:
             newItem = self.AppendItem(parentItem, root)
             self.SetItemData(newItem, root)
@@ -49,22 +49,22 @@ class PaletteTreeCtrl(wx.TreeCtrl):
 
     def traverse(self, parent, itemText):
         if itemText == self.GetItemText(parent):
-           return parent
+            return parent
         item, cookie = self.GetFirstChild(parent)
         while item:
-              # if the item was found - return it
-              if itemText == self.GetItemText(item):
-                 return item
-
-              # the tem was not found - checking if it has children
-              if self.ItemHasChildren(item):
-                 # item has children - delving into it
-                 child = self.traverse(item, itemText)
-                 if child is not None:
+            # if the item was found - return it
+            if itemText == self.GetItemText(item):
+                return item
+
+            # the tem was not found - checking if it has children
+            if self.ItemHasChildren(item):
+                # item has children - delving into it
+                child = self.traverse(item, itemText)
+                if child is not None:
                     return child
 
-              # continue iteration to the next child
-              item, cookie = self.GetNextChild(parent, cookie)
+            # continue iteration to the next child
+            item, cookie = self.GetNextChild(parent, cookie)
         return None
 
     def AddGroup(self):
@@ -74,20 +74,20 @@ class PaletteTreeCtrl(wx.TreeCtrl):
             parent = self.GetRootItem()
 
         i = 1
-        namestr = "Group%s"%(i)
+        namestr = f"Group{i}"
         found = self.traverse(self.GetRootItem(), namestr)
         while found:
-              i = i + 1
-              namestr = "Group%s"%(i)
-              found = self.traverse(self.GetRootItem(), namestr)
+            i = i + 1
+            namestr = f"Group{i}"
+            found = self.traverse(self.GetRootItem(), namestr)
 
         newItem = self.AppendItem(parent, namestr)
         itemData = ObjectGen(name=namestr)
         parentName = self.GetItemText(parent)
         if parentName == self.rootName:
-           self.paletteUI.palette.add(itemData)
+            self.paletteUI.palette.add(itemData)
         else:
-           self.paletteUI.palette.add(itemData, parentName)
+            self.paletteUI.palette.add(itemData, parentName)
         self.SetItemPyData(newItem, itemData)
 
         self.Expand(self.GetRootItem())
@@ -96,8 +96,8 @@ class PaletteTreeCtrl(wx.TreeCtrl):
     def DeleteItem(self, item):
         itemText = self.GetItemText(item)
         if item and itemText != self.rootName:
-           self.Delete(item)
-           self.paletteUI.palette.delete(itemText)
+            self.Delete(item)
+            self.paletteUI.palette.delete(itemText)
 
     def DeleteSelected(self):
         item = self.GetSelection()
@@ -107,48 +107,48 @@ class PaletteTreeCtrl(wx.TreeCtrl):
         # main loop - iterating over item's children
         item, cookie = self.GetFirstChild(parent)
         while item:
-           itemName = self.GetItemText(item)
-           itemData = self.GetItemData(item)
+            itemName = self.GetItemText(item)
+            itemData = self.GetItemData(item)
 
-           newItem = self.AppendItem(newParent, itemName)
-           self.SetItemPyData(newItem, itemData)
+            newItem = self.AppendItem(newParent, itemName)
+            self.SetItemPyData(newItem, itemData)
 
-           # if an item had children, we need to re-parent them as well
-           if self.ItemHasChildren(item):
-              # recursing...
-              self.ReParent(item, newItem, )
+            # if an item had children, we need to re-parent them as well
+            if self.ItemHasChildren(item):
+                # recursing...
+                self.ReParent(item, newItem, )
 
-           # continue iteration to the next child
-           item, cookie = self.GetNextChild(parent, cookie)
+            # continue iteration to the next child
+            item, cookie = self.GetNextChild(parent, cookie)
 
     def ChangeHierarchy(self, itemName, x, y):
         parent = self.GetRootItem()
         item = self.traverse(parent, itemName)
         if item is None:
-           return
+            return
 
         dragToItem, flags = self.HitTest(wx.Point(x, y))
         if dragToItem.IsOk():
-           # prevent draging into itself
-           if  dragToItem == item:
-               return
-           dragToItemName = self.GetItemText(dragToItem)
-           if isinstance(self.paletteUI.palette.findItem(dragToItemName), ObjectBase):
-              # this is a file node, bailing out
-              return
-
-           newItem = self.AppendItem(dragToItem, itemName)
-
-           itemObj = self.paletteUI.palette.findItem(itemName)
-           if itemObj is not None:
-              # reparenting the data objects...
-              if dragToItemName == self.rootName:
-                 self.paletteUI.palette.add(itemObj)
-              else:
-                 self.paletteUI.palette.add(itemObj, dragToItemName)
-
-           self.ReParent(item, newItem)
-           self.Delete(item)
+            # prevent draging into itself
+            if dragToItem == item:
+                return
+            dragToItemName = self.GetItemText(dragToItem)
+            if isinstance(self.paletteUI.palette.findItem(dragToItemName), ObjectBase):
+                # this is a file node, bailing out
+                return
+
+            newItem = self.AppendItem(dragToItem, itemName)
+
+            itemObj = self.paletteUI.palette.findItem(itemName)
+            if itemObj is not None:
+                # reparenting the data objects...
+                if dragToItemName == self.rootName:
+                    self.paletteUI.palette.add(itemObj)
+                else:
+                    self.paletteUI.palette.add(itemObj, dragToItemName)
+
+            self.ReParent(item, newItem)
+            self.Delete(item)
 
     def onBeginDrag(self, event):
         item = event.GetItem()

+ 1 - 1
direct/src/leveleditor/ProtoObjsUI.py

@@ -124,7 +124,7 @@ class ProtoObjsUI(wx.Panel):
                 break
 
     def addNewItem(self, result):
-       ProtoObjsUI.AquireFile(self, result[1])
+        ProtoObjsUI.AquireFile(self, result[1])
 
     def AquireFile(self, filename):
         label = self.findLabel(filename)

+ 2 - 2
direct/src/motiontrail/MotionTrail.py

@@ -673,8 +673,8 @@ class MotionTrail(NodePath, DirectObject):
                             end_nurbs_start_t = end_nurbs_curve_result.getStartT()
                             end_nurbs_end_t = end_nurbs_curve_result.getEndT()
 
-                            start_delta_t = (start_nurbs_end_t - start_nurbs_start_t)
-                            end_delta_t = (end_nurbs_end_t - end_nurbs_start_t)
+                            start_delta_t = start_nurbs_end_t - start_nurbs_start_t
+                            end_delta_t = end_nurbs_end_t - end_nurbs_start_t
 
                             start_nurbs_curve_result.evalPoint(start_nurbs_start_t + (start_delta_t * st), v0)
                             end_nurbs_curve_result.evalPoint(end_nurbs_start_t + (end_delta_t * st), v1)

+ 1 - 1
direct/src/particles/ParticleFloorTest.py

@@ -50,7 +50,7 @@ class ParticleFloorTest(NodePath):
 
 
 if __name__ == "__main__":
-    from direct.directbase.TestStart import *
+    from direct.directbase.TestStart import base
     pt = ParticleFloorTest()
     pt.reparentTo(base.render)
     pt.start()

+ 2 - 2
direct/src/particles/ParticleTest.py

@@ -3,7 +3,7 @@ if __name__ == "__main__":
     from panda3d.core import Vec3
     from panda3d.physics import LinearVectorForce
 
-    from direct.directbase.TestStart import *
+    from direct.directbase.TestStart import base
     from direct.tkpanels import ParticlePanel
 
     from . import ParticleEffect
@@ -23,7 +23,7 @@ if __name__ == "__main__":
 
     # Particle effect
     pe = ParticleEffect.ParticleEffect('particle-fx')
-    pe.reparentTo(render)
+    pe.reparentTo(base.render)
     #pe.setPos(0.0, 5.0, 4.0)
     pe.addForceGroup(fg)
     pe.addParticles(p)

+ 2 - 2
direct/src/physics/FallTest.py

@@ -87,8 +87,8 @@ class FallTest(NodePath):
         #self.actorNode.updateTransform()
 
 if __name__ == "__main__":
-    from direct.directbase.ThreeUpStart import *
-    test=FallTest()
+    from direct.directbase.ThreeUpStart import base
+    test = FallTest()
     test.reparentTo(base.render)
     test.setup()
     base.camera.setY(-10.0)

+ 2 - 2
direct/src/physics/RotationTest.py

@@ -98,8 +98,8 @@ class RotationTest(NodePath):
         #self.actorNode.updateTransform()
 
 if __name__ == "__main__":
-    from direct.directbase.ThreeUpStart import *
-    test=RotationTest()
+    from direct.directbase.ThreeUpStart import base
+    test = RotationTest()
     test.reparentTo(base.render)
     test.setup()
     base.camera.setY(-10.0)

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

@@ -1,7 +1,8 @@
 from direct.directnotify.DirectNotifyGlobal import directNotify
 import direct.showbase.DConfig as config
-from direct.showbase.PythonUtil import makeFlywheelGen
+from direct.showbase.PythonUtil import makeFlywheelGen, flywheel
 from direct.showbase.PythonUtil import itype, serialNum, safeRepr, fastRepr
+from direct.showbase.PythonUtil import getBase, uniqueName, ScratchPad, nullGen
 from direct.showbase.Job import Job
 from direct.showbase.JobManagerGlobal import jobMgr
 from direct.showbase.MessengerGlobal import messenger

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

@@ -92,6 +92,7 @@ class DirectObject:
         if hasattr(self, '_taskList'):
             tasks = [task.name for task in self._taskList.values()]
         if len(events) != 0 or len(tasks) != 0:
+            from direct.showbase.PythonUtil import getRepository
             estr = ('listening to events: %s' % events if len(events) != 0 else '')
             andStr = (' and ' if len(events) != 0 and len(tasks) != 0 else '')
             tstr = ('%srunning tasks: %s' % (andStr, tasks) if len(tasks) != 0 else '')

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

@@ -6,6 +6,7 @@ __all__ = ['EventManager']
 
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.task.TaskManagerGlobal import taskMgr
+from direct.showbase.MessengerGlobal import messenger
 from panda3d.core import PStatCollector, EventQueue, EventHandler
 from panda3d.core import ConfigVariableBool
 

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

@@ -307,7 +307,6 @@ class GarbageReport(Job):
                 yield None
                 digits += 1
                 n = n // 10
-            digits = digits
             format = '%0' + '%s' % digits + 'i:%s \t%s'
 
             for i in range(numGarbage):

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

@@ -1,4 +1,6 @@
 from direct.showbase.GarbageReport import GarbageReport
+from direct.showbase.PythonUtil import serialNum
+from direct.task.TaskManagerGlobal import taskMgr
 
 
 class GarbageReportScheduler:

+ 8 - 8
direct/src/showbase/PhasedObject.py

@@ -118,19 +118,19 @@ class PhasedObject:
             self.setPhase(-1)
 
     def __loadPhase(self, phase):
-        aPhase = self.phaseAliasMap.get(phase,phase)
-        getattr(self, 'loadPhase%s' % aPhase,
-                lambda: self.__phaseNotFound('load',aPhase))()
+        aPhase = self.phaseAliasMap.get(phase, phase)
+        getattr(self, f'loadPhase{aPhase}',
+                lambda: self.__phaseNotFound('load', aPhase))()
         self.phase = phase
 
     def __unloadPhase(self, phase):
-        aPhase = self.phaseAliasMap.get(phase,phase)
-        getattr(self, 'unloadPhase%s' % aPhase,
-                lambda: self.__phaseNotFound('unload',aPhase))()
-        self.phase = (phase - 1)
+        aPhase = self.phaseAliasMap.get(phase, phase)
+        getattr(self, f'unloadPhase{aPhase}',
+                lambda: self.__phaseNotFound('unload', aPhase))()
+        self.phase = phase - 1
 
     def __phaseNotFound(self, mode, aPhase):
-        assert self.notify.debug('%s%s() not found!\n' % (mode,aPhase))
+        assert self.notify.debug(f'{mode}{aPhase}() not found!\n')
 
 if __debug__:
     class AnfaPhasedObject(PhasedObject):

+ 3 - 3
direct/src/showbase/ProfileSession.py

@@ -207,9 +207,9 @@ class ProfileSession:
             _removeProfileCustomFuncs(filename)
 
             # clean up the globals
-            result = globalProfileSessionResult[0]
-            del builtins.__dict__['globalProfileSessionFunc']
-            del builtins.__dict__['globalProfileSessionResult']
+            result = builtins.globalProfileSessionResult[0]
+            del builtins.globalProfileSessionFunc
+            del builtins.globalProfileSessionResult
 
             self._successfulProfiles += 1
 

+ 12 - 12
direct/src/showbase/PythonUtil.py

@@ -43,7 +43,8 @@ import functools
 
 __report_indent = 3
 
-from panda3d.core import ConfigVariableBool, ClockObject
+from panda3d.core import ConfigVariableBool, ConfigVariableString, ConfigFlags
+from panda3d.core import ClockObject
 
 
 ## with one integer positional arg, this uses about 4/5 of the memory of the Functor class below
@@ -578,7 +579,6 @@ if __debug__:
         # if you called profile with 'log' not set to True,
         # you can call this function to get the results as
         # a string
-        global _ProfileResultStr
         return _ProfileResultStr
 
     def profileFunc(callback, name, terse, log=True):
@@ -603,9 +603,9 @@ if __debug__:
             print(suffix)
         else:
             _ProfileResultStr = '%s\n%s\n%s' % (prefix, _ProfileResultStr, suffix)
-        result = globalProfileResult[0]
-        del builtins.__dict__['globalProfileFunc']
-        del builtins.__dict__['globalProfileResult']
+        result = builtins.globalProfileResult[0]
+        del builtins.globalProfileFunc
+        del builtins.globalProfileResult
         return result
 
     def profiled(category=None, terse=False):
@@ -1216,12 +1216,12 @@ class SerialMaskedGen(SerialNumGen):
     __next__ = next
 
 _serialGen = SerialNumGen()
+
 def serialNum():
-    global _serialGen
     return _serialGen.next()
+
 def uniqueName(name):
-    global _serialGen
-    return '%s-%s' % (name, _serialGen.next())
+    return f'{name}-{serialNum()}'
 
 
 ############################################################
@@ -1664,7 +1664,7 @@ class DelayedCall:
         self._removeDoLater()
     def finish(self):
         if not self._finished:
-            self._doCallback()
+            self._doCallback(None)
         self.destroy()
     def _addDoLater(self):
         taskMgr.doMethodLater(self._delay, self._doCallback, self._taskName)
@@ -1916,7 +1916,7 @@ def report(types = [], prefix = '', xform = None, notifyFunc = None, dConfigPara
             prefixes = set()
 
         for param in dConfigParamList:
-            prefix = config.GetString('prefix-%s-report' % (param,), '')
+            prefix = ConfigVariableString(f"prefix-{param}-report", "", "DConfig", ConfigFlags.F_dconfig).value
             if prefix:
                 prefixes.add(prefix)
 
@@ -2166,7 +2166,7 @@ if __debug__:
                     s = clock.getRealTime() - st
                     print("Function %s.%s took %s seconds"%(f.__module__, f.__name__,s))
                 else:
-                    import profile as prof, pstats
+                    import profile as prof
 
                     #detailed profile, stored in base.stats under (
                     if not hasattr(base, "stats"):
@@ -2441,7 +2441,7 @@ def configIsToday(configName):
     # TODO: replace usage of strptime with something else
     # returns true if config string is a valid representation of today's date
     today = time.localtime()
-    confStr = config.GetString(configName, '')
+    confStr = ConfigVariableString(configName, "", "DConfig", ConfigFlags.F_dconfig).value
     for format in ('%m/%d/%Y', '%m-%d-%Y', '%m.%d.%Y'):
         try:
             confDate = time.strptime(confStr, format)

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

@@ -18,7 +18,7 @@ from direct.directnotify.DirectNotifyGlobal import directNotify, giveNotify # py
 from panda3d.core import VirtualFileSystem, Notify, ClockObject, PandaSystem
 from panda3d.core import ConfigPageManager, ConfigVariableManager, ConfigVariableBool
 from panda3d.core import NodePath, PGTop
-from . import DConfig as config
+from . import DConfig as config # pylint: disable=unused-import
 import warnings
 
 __dev__ = ConfigVariableBool('want-dev', __debug__).value

+ 7 - 20
direct/src/showutil/Effects.py

@@ -65,36 +65,26 @@ def createBounce(nodeObj, numBounces, startValues, totalTime, amplitude,
     given nodePath bounce a given number of times over a give total time.
     """
     if not nodeObj or numBounces < 1 or totalTime == 0:
-        self.notify.warning(
-            "createBounceIvals called with invalid parameter")
-        return
+        raise ValueError("createBounce called with invalid parameter")
 
     result = Sequence()
 
     # calculate how long, in seconds, each bounce should last
-    bounceTime = totalTime/float(numBounces)
+    bounceTime = totalTime / numBounces
     currTime = bounceTime
     currAmplitude = amplitude
 
     # determine the how much of a change in value the first bounce
     # will produce based on the given start value and amplitude
     #
-    if ((bounceType == SX_BOUNCE) or (bounceType == TX_BOUNCE) or
-        (bounceType == H_BOUNCE)):
-        index = 0
-    elif ((bounceType == SY_BOUNCE) or (bounceType == TY_BOUNCE) or
-          (bounceType == P_BOUNCE)):
-        index = 1
-    elif ((bounceType == SZ_BOUNCE) or (bounceType == TZ_BOUNCE) or
-          (bounceType == R_BOUNCE)):
-        index = 2
+    index = bounceType % 3
     currBounceVal = startValues[index]
 
     # create a lerp interval for each bounce, making sure to
     # figure out the new value, which progressively gets closer
     # to our start value
     #
-    for bounceNum in range(numBounces*2):
+    for bounceNum in range(numBounces * 2):
         # determine the direction that this value should go,
         # alternating for each lerp interval to simulate
         # a spring effect
@@ -112,16 +102,13 @@ def createBounce(nodeObj, numBounces, startValues, totalTime, amplitude,
         newVec3.setCell(index, currBounceVal)
 
         # create the right type of lerp
-        if ((bounceType == SX_BOUNCE) or (bounceType == SY_BOUNCE) or
-            (bounceType == SZ_BOUNCE)):
+        if bounceType >= SX_BOUNCE and bounceType <= SZ_BOUNCE:
             result.append(LerpScaleInterval(
                 nodeObj, currTime, newVec3, blendType=blend))
-        elif ((bounceType == TX_BOUNCE) or (bounceType == TY_BOUNCE) or
-              (bounceType == TZ_BOUNCE)):
+        elif bounceType >= TX_BOUNCE and bounceType <= TZ_BOUNCE:
             result.append(LerpPosInterval(
                 nodeObj, currTime, newVec3, blendType=blend))
-        elif ((bounceType == H_BOUNCE) or (bounceType == P_BOUNCE) or
-              (bounceType == R_BOUNCE)):
+        elif bounceType >= H_BOUNCE and bounceType <= R_BOUNCE:
             result.append(LerpHprInterval(
                 nodeObj, currTime, newVec3, blendType=blend))
 

+ 5 - 6
direct/src/showutil/TexMemWatcher.py

@@ -1002,8 +1002,8 @@ class TexMemWatcher(DirectObject):
                 while t < self.h and (self.bitmasks[t] & mask).isZero():
                     t += 1
 
-                tpw = (r - l)
-                tph = (t - b)
+                tpw = r - l
+                tph = t - b
                 tarea = tpw * tph
                 assert tarea > 0
                 if tarea >= area:
@@ -1231,10 +1231,9 @@ class TexRecord:
         self.root = root
 
         # Also, make one or more clickable MouseWatcherRegions.
-        assert self.regions == []
-        for pi in range(len(self.placements)):
-            p = self.placements[pi]
-            r = MouseWatcherRegion('%s:%s' % (self.key, pi), *p.p)
+        assert not self.regions
+        for pi, p in enumerate(self.placements):
+            r = MouseWatcherRegion(f'{self.key}:{pi}', *p.p)
             tmw.mw.addRegion(r)
             self.regions.append(r)
 

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

@@ -13,7 +13,6 @@ __all__ = [
 from panda3d import core
 import os
 import io
-import encodings
 from posixpath import join
 
 _vfs = core.VirtualFileSystem.getGlobalPtr()

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

@@ -202,13 +202,13 @@ class _Condition(_Verbose):
     def __repr__(self):
         return "<Condition(%s, %d)>" % (self.__lock, len(self.__waiters))
 
-    def _release_save(self):
+    def _release_save(self): # pylint: disable=method-hidden
         self.__lock.release()           # No state to save
 
-    def _acquire_restore(self, x):
+    def _acquire_restore(self, x): # pylint: disable=method-hidden
         self.__lock.acquire()           # Ignore saved state
 
-    def _is_owned(self):
+    def _is_owned(self): # pylint: disable=method-hidden
         # Return True if lock is owned by currentThread.
         # This method is called only if __lock doesn't have _is_owned().
         if self.__lock.acquire(0):

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

@@ -33,9 +33,6 @@ class MiniTaskManager:
         except ValueError:
             pass
 
-    def __executeTask(self, task):
-        return task(task)
-
     def step(self):
         i = 0
         while i < len(self.taskList):

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

@@ -40,7 +40,7 @@ from panda3d.core import (
     PythonTask,
     Thread,
 )
-from direct.extensions_native import HTTPChannel_extensions
+from direct.extensions_native import HTTPChannel_extensions # pylint: disable=unused-import
 
 
 def print_exc_plus():

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

@@ -18,7 +18,7 @@ def spawnNewTask():
 
 def taskCallback(task):
     randNum = int(round(random.random() * 1000))
-    n = ("taskTester-%s" % randNum)
+    n = f"taskTester-{randNum}"
     taskMgr.remove(n)
     spawnNewTask()
     spawnNewTask()

+ 1 - 0
direct/src/tkpanels/DirectSessionPanel.py

@@ -19,6 +19,7 @@ from direct.tkwidgets import Slider
 from direct.tkwidgets import VectorWidgets
 from direct.tkwidgets import SceneGraphExplorer
 from direct.tkwidgets import MemoryExplorer
+from direct.task.TaskManagerGlobal import taskMgr
 from .TaskManagerPanel import TaskManagerWidget
 import Pmw
 import tkinter as tk

+ 1 - 0
direct/src/tkpanels/MopathRecorder.py

@@ -26,6 +26,7 @@ from direct.directtools.DirectUtil import CLAMP, useDirectRenderStyle
 from direct.directtools.DirectGeometry import LineNodePath, qSlerp
 from direct.directtools.DirectSelection import SelectionRay
 from direct.task import Task
+from direct.task.TaskManagerGlobal import taskMgr
 from direct.tkwidgets import Dial
 from direct.tkwidgets import Floater
 from direct.tkwidgets import Slider

+ 13 - 12
direct/src/tkpanels/NotifyPanel.py

@@ -13,8 +13,9 @@ class NotifyPanel:
         notify levels for all available DIRECT and PANDA notify categories
         """
         # Make sure TK mainloop is running
-        from direct.showbase.TkGlobal import Pmw, Toplevel, Frame, Label, Radiobutton
-        from direct.showbase.TkGlobal import HORIZONTAL, X, W, NW, BOTH, LEFT, RIGHT, IntVar
+        from direct.showbase.TkGlobal import Pmw
+        from tkinter import Toplevel, Frame, Label, Radiobutton, IntVar
+        from direct.showbase.TkGlobal import HORIZONTAL, X, W, NW, BOTH, LEFT, RIGHT
         # To get severity levels
         from panda3d.core import NSFatal, NSError, NSWarning, NSInfo, NSDebug, NSSpam
 
@@ -50,12 +51,12 @@ class NotifyPanel:
         # Create a listbox
         self.categoryList = Pmw.ScrolledListBox(
             categoryFrame,
-            labelpos = 'nw', label_text = 'Categories:',
+            labelpos = NW, label_text = 'Categories:',
             label_font=('MSSansSerif', 10, 'bold'),
             listbox_takefocus = 1,
             items = categoryNames,
             selectioncommand = self.setActivePandaCategory)
-        self.categoryList.pack(expand = 1, fill = 'both')
+        self.categoryList.pack(expand = 1, fill = BOTH)
 
         # Severity frame
         Label(severityFrame, text = 'Severity:',
@@ -64,44 +65,44 @@ class NotifyPanel:
         self.severity = IntVar()
         self.severity.set(0)
         self.fatalSeverity = Radiobutton(severityFrame, text = 'Fatal',
-                                         justify = 'left', anchor = 'w',
+                                         justify = LEFT, anchor = W,
                                          value = NSFatal,
                                          variable = self.severity,
                                          command = self.setActiveSeverity)
         self.fatalSeverity.pack(fill = X)
         self.errorSeverity = Radiobutton(severityFrame, text = 'Error',
-                                         justify = 'left', anchor = 'w',
+                                         justify = LEFT, anchor = W,
                                          value = NSError,
                                          variable = self.severity,
                                          command = self.setActiveSeverity)
         self.errorSeverity.pack(fill = X)
         self.warningSeverity = Radiobutton(severityFrame, text = 'Warning',
-                                           justify = 'left', anchor = 'w',
+                                           justify = LEFT, anchor = W,
                                            value = NSWarning,
                                            variable = self.severity,
                                            command = self.setActiveSeverity)
         self.warningSeverity.pack(fill = X)
         self.infoSeverity = Radiobutton(severityFrame, text = 'Info',
-                                        justify = 'left', anchor = 'w',
+                                        justify = LEFT, anchor = W,
                                         value = NSInfo,
                                         variable = self.severity,
                                         command = self.setActiveSeverity)
         self.infoSeverity.pack(fill = X)
         self.debugSeverity = Radiobutton(severityFrame, text = 'Debug',
-                                         justify = 'left', anchor = 'w',
+                                         justify = LEFT, anchor = W,
                                          value = NSDebug,
                                          variable = self.severity,
                                          command = self.setActiveSeverity)
         self.debugSeverity.pack(fill = X)
         self.spamSeverity = Radiobutton(severityFrame, text = 'Spam',
-                                        justify = 'left', anchor = 'w',
+                                        justify = LEFT, anchor = W,
                                         value = NSSpam,
                                         variable = self.severity,
                                         command = self.setActiveSeverity)
         self.spamSeverity.pack(fill = X)
         # Pack frames
-        framePane.pack(expand = 1, fill = 'both')
-        mainFrame.pack(expand = 1, fill = 'both')
+        framePane.pack(expand = 1, fill = BOTH)
+        mainFrame.pack(expand = 1, fill = BOTH)
         # Get listbox
         listbox = self.categoryList.component('listbox')
         # Bind updates to arrow buttons

+ 0 - 1
direct/src/tkpanels/ParticlePanel.py

@@ -2363,7 +2363,6 @@ class ParticlePanel(AppShell):
 
     def createSpriteAnimationFrame(self, parent, anim, animName):
         ren = self.particles.getRenderer()
-        pass
         frame = tk.Frame(parent, relief = tk.RAISED, borderwidth = 2)
         frame.pack(pady = 1, fill = tk.X, expand = 0)
 

+ 3 - 4
direct/src/tkpanels/Placer.py

@@ -8,13 +8,12 @@ from direct.tkwidgets.AppShell import AppShell
 from direct.tkwidgets import Dial
 from direct.tkwidgets import Floater
 from direct.directtools.DirectGlobals import ZERO_VEC, UNIT_VEC
+from direct.showbase.MessengerGlobal import messenger
+from direct.task.TaskManagerGlobal import taskMgr
 import Pmw
 import tkinter as tk
 
-"""
-TODO:
-Task to monitor pose
-"""
+#TODO: Task to monitor pose
 
 class Placer(AppShell):
     # Override class variables here

+ 0 - 2
direct/src/tkpanels/TaskManagerPanel.py

@@ -53,8 +53,6 @@ class TaskManagerWidget(DirectObject):
         TaskManagerWidget class pops up a control panel to view/delete
         tasks managed by the taskManager.
         """
-        # Make sure TK mainloop is running
-        from direct.showbase import TkGlobal
         # Record parent (used by ok cancel dialog boxes)
         self.parent = parent
         # Record taskManager

+ 12 - 12
direct/src/tkwidgets/AppShell.py

@@ -16,23 +16,23 @@ from . import VectorWidgets
 from . import ProgressBar
 import Pmw
 import tkinter as tk
+import builtins
 
 
 # Create toplevel widget dictionary
-try:
-    __builtins__["widgetDict"]
-except KeyError:
-    __builtins__["widgetDict"] = {}
+if not hasattr(builtins, "widgetDict"):
+    builtins.widgetDict = {}
+
 # Create toplevel variable dictionary
-try:
-    __builtins__["variableDict"]
-except KeyError:
-    __builtins__["variableDict"] = {}
+if not hasattr(builtins, "variableDict"):
+    builtins.variableDict = {}
 
 def resetWidgetDict():
-    __builtins__["widgetDict"] = {}
+    builtins.widgetDict = {}
+
 def resetVariableDict():
-    __builtins__["variableDict"] = {}
+    builtins.variableDict = {}
+
 
 # Inherit from MegaWidget instead of Toplevel so you can pass in a toplevel
 # to use as a container if you wish.  If no toplevel passed in, create one
@@ -79,9 +79,9 @@ class AppShell(Pmw.MegaWidget, DirectObject):
         AppShell.panelCount += 1
         self.id = self.appname + '-' + repr(AppShell.panelCount)
         # Create a dictionary in the widgetDict to hold this panel's widgets
-        self.widgetDict = widgetDict[self.id] = {}
+        self.widgetDict = builtins.widgetDict[self.id] = {}
         # And one to hold this panel's variables
-        self.variableDict = variableDict[self.id] = {}
+        self.variableDict = builtins.variableDict[self.id] = {}
         # Get handle to the toplevels hull
         self._hull = self.component('hull')
         # Initialize the application

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

@@ -7,6 +7,7 @@ __all__ = ['Dial', 'AngleDial', 'DialWidget']
 
 from .Valuator import Valuator, VALUATOR_MINI, VALUATOR_FULL
 from direct.task import Task
+from direct.task.TaskManagerGlobal import taskMgr
 from panda3d.core import ClockObject
 import math
 import operator

+ 7 - 14
direct/src/tkwidgets/EntryScale.py

@@ -126,13 +126,6 @@ class EntryScale(Pmw.MegaWidget):
         # Check keywords and initialise options based on input values.
         self.initialiseoptions(EntryScale)
 
-    def label(self):
-        return self.label
-    def scale(self):
-        return self.scale
-    def entry(self):
-        return self.entry
-
     def askForLabel(self, event = None):
         newLabel = askstring(title = self['text'],
                              prompt = 'New label:',
@@ -202,13 +195,12 @@ class EntryScale(Pmw.MegaWidget):
             return
         # convert scale val to float
         self.set(float(strVal))
-        """
+
         # Update entry to reflect formatted value
-        self.entryValue.set(self.entryFormat % self.value)
-        self.entry.checkentry()
-        if self['command']:
-            self['command'](self.value)
-        """
+        #self.entryValue.set(self.entryFormat % self.value)
+        #self.entry.checkentry()
+        #if self['command']:
+        #    self['command'](self.value)
 
     def _entryCommand(self, event = None):
         try:
@@ -414,7 +406,6 @@ class EntryScaleGroup(Pmw.MegaToplevel):
 
     def onReturn(self, *args):
         """ User redefinable callback executed on button press """
-        pass
 
     def __onReturnRelease(self, esg):
         # Execute onReturnRelease callback
@@ -441,6 +432,8 @@ class EntryScaleGroup(Pmw.MegaToplevel):
 
 
 def rgbPanel(nodePath, callback = None):
+    from direct.showbase.MessengerGlobal import messenger
+
     def setNodePathColor(color, np = nodePath, cb = callback):
         np.setColor(color[0]/255.0, color[1]/255.0,
                     color[2]/255.0, color[3]/255.0)

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

@@ -7,6 +7,7 @@ __all__ = ['Floater', 'FloaterWidget', 'FloaterGroup']
 
 from .Valuator import Valuator, VALUATOR_MINI, VALUATOR_FULL
 from direct.task import Task
+from direct.task.TaskManagerGlobal import taskMgr
 from panda3d.core import ClockObject
 import math
 import Pmw

+ 2 - 2
direct/src/tkwidgets/MemoryExplorer.py

@@ -232,10 +232,10 @@ class MemoryExplorer(Pmw.MegaWidget, DirectObject):
     # List & Analyze
     #--------------------------------------------------------------------------
     def makeList(self):
-        self.renderItem = MemoryExplorerItem(None, render)
+        self.renderItem = MemoryExplorerItem(None, base.render)
         self.buildList(self.renderItem)
 
-        self.render2dItem = MemoryExplorerItem(None, render2d)
+        self.render2dItem = MemoryExplorerItem(None, base.render2d)
         self.buildList(self.render2dItem)
 
     def buildList(self, parentItem):

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

@@ -6,6 +6,7 @@ Requires Pmw."""
 __all__ = ['SceneGraphExplorer', 'SceneGraphExplorerItem', 'explore']
 
 from direct.showbase.DirectObject import DirectObject
+from direct.showbase.MessengerGlobal import messenger
 from .Tree import TreeItem, TreeNode
 import Pmw
 import tkinter as tk

+ 2 - 8
direct/src/tkwidgets/Tree.py

@@ -19,7 +19,6 @@ __all__ = ['TreeNode', 'TreeItem']
 # - keep track of object ids to allow more careful cleaning
 # - optimize tree redraw after expand of subnode
 
-import os, sys
 from panda3d.core import Filename, getModelPath
 import tkinter as tk
 
@@ -119,8 +118,7 @@ class TreeNode:
     def createPopupMenu(self):
         if self.menuList:
             self._popupMenu = tk.Menu(self.canvas, tearoff = 0)
-            for i in range(len(self.menuList)):
-                item = self.menuList[i]
+            for i, item in enumerate(self.menuList):
                 if item == 'Separator':
                     self._popupMenu.add_separator()
                 else:
@@ -493,8 +491,7 @@ class TreeItem:
         """Do not override!  Called by TreeNode."""
         if not self.IsExpandable():
             return []
-        sublist = self.GetSubList()
-        return sublist
+        return self.GetSubList()
 
     def IsEditable(self):
         """Return whether the item's text may be edited."""
@@ -516,6 +513,3 @@ class TreeItem:
 
     def OnSelect(self):
         """Called when item selected."""
-
-
-

+ 2 - 0
direct/src/tkwidgets/Valuator.py

@@ -597,6 +597,8 @@ Pmw.forwardmethods(ValuatorGroupPanel, ValuatorGroup, 'valuatorGroup')
 
 
 def rgbPanel(nodePath, callback = None, style = 'mini'):
+    from direct.showbase.MessengerGlobal import messenger
+
     def onRelease(r, g, b, a, nodePath = nodePath):
         messenger.send('RGBPanel_setColor', [nodePath, r, g, b, a])
 

+ 8 - 4
direct/src/tkwidgets/VectorWidgets.py

@@ -337,7 +337,11 @@ if __name__ == '__main__':
     root = tk.Toplevel()
     root.title('Vector Widget demo')
 
-    ve = VectorEntry(root); ve.pack()
-    v3e = Vector3Entry(root); v3e.pack()
-    v4e = Vector4Entry(root); v4e.pack()
-    ce = ColorEntry(root); ce.pack()
+    ve = VectorEntry(root)
+    ve.pack()
+    v3e = Vector3Entry(root)
+    v3e.pack()
+    v4e = Vector4Entry(root)
+    v4e.pack()
+    ce = ColorEntry(root)
+    ce.pack()

+ 1 - 0
direct/src/wxwidgets/WxPandaShell.py

@@ -4,6 +4,7 @@ from wx.lib.agw import fourwaysplitter as FWS
 from direct.showbase.ShowBase import ShowBase
 from direct.showbase import ShowBaseGlobal
 from direct.directtools.DirectGlobals import SKIP_UNPICKABLE
+from direct.task.TaskManagerGlobal import taskMgr
 
 from .WxAppShell import WxAppShell
 from .ViewPort import Viewport, ViewportManager