Main Loop
A typical form of a Panda program might look like:
from direct.showbase.DirectObject import DirectObject # To listen for Events
class World(DirectObject):
def __init__(self):
#initialize instance self. variables here
def method1():
# Panda source goes here
w = World()
base.run() # main loop
run()
is a function that never
returns. It is the main loop.
For an alternative, run()
could not be called at all. Panda doesn’t really
need to own the main loop. Instead, taskMgr.step()
can be called
intermittently, which will run through one iteration of Panda’s loop. In fact,
run()
is basically just an infinite loop that calls taskMgr.step()
repeatedly.
taskMgr.step()
must be called quickly enough after the previous call to
taskMgr.step()
. This must be done quick enough to be faster than the frame
rate.
This may useful when an imported third party python module that also has its own event loop wants and wants to be in control of program flow. A third party example may be Twisted, the event-driven networking framework.
The solution to this problem is to let Panda3D’s loop be controlled entirely by
twisted’s event loop. You will need to use the LoopingCall method to add Panda’s
taskMgr.step()
method to twisted’s event loop. Then, you need to call
reactor.run()
instead of Panda3D’s run()
method to run twisted’s event
loop. Here’s an example on how this will work:
from twisted.internet.task import LoopingCall
from twisted.internet import reactor
LoopingCall(taskMgr.step).start(1 / Desired_FPS)
reactor.run()
You will need to replace Desired_FPS by the desired framerate, that is, how many
times you want Panda3D to redraw the frame per second. Please note that
reactor.run()
is blocking, just like Panda’s run() method.
Another third party example is wxPython GUI, that is a blending of the wxWidgets
C++ class library with the Python programming language. Panda’s run()
function, and wx’s app.MainLoop()
method, both are designed to handle all
events and never return. They are each supposed to serve as the one main loop of
the application. Two main loops can not effectively run an application.
wxPython also supplies a method that can be called occasionally, instead of a
function that never returns. In wx’s case, it’s app.Dispatch()
.
A choice can be made whether or not to make wx handle the main loop, and call
taskMgr.step()
intermittently, or whether or not to make Panda handle the
main loop, and call app.Dispatch()
intermittently. The better performance
choice is to have Panda handle the main loop.
In the case that Panda handles the main loop, a task needs to be started to call
app.Dispatch()
every frame, if needed. Instead of calling wxPython’s
app.MainLoop()
, do something like the following:
app = wx.App(0)
def handleWxEvents(task):
while app.Pending():
app.Dispatch()
return Task.cont
taskMgr.add(handleWxEvents, 'handleWxEvents')
base.run() # Panda handles the main loop
In the case that wxPython handles the main loop using app.MainLoop()
, to
keep the framerate quick and reduce the CPU, add sleep(0.001)
in the body of
the program. This will yield to Panda. After the sleep is over, control will
return to wxPython. wxPython can then check for user events. wxPython’s user
generated callback events are generally generated only at infrequent intervals
(based on when the user is interacting with the window). This is appropriate for
a 2-D application that is completely response-driven, but not very useful for a
3-D application that continues to be active even when a user is not interacting
with it.