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):
__init__(self):
#initialize instance self. variables here
method1():
# Panda source goes here
w = World()
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
Task.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')
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.