A typical form of a Panda program may my the following:
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 create two program threads, one for Panda and one for Twisted. Two queue structures, an input queue and an output queue are created to exist, to pass messages between the two threads.
In the Panda3D area of the code, create a task which runs once per frame, which checks the incoming Queue for messages from the server and updates world objects in Panda as needed. Player inputs call event handlers which place messages on the outgoing queue. In the Twisted area of the code, create a loop which periodically checks the outgoing queue for messages, and processes them as needed. Processed messages are then placed on the incoming queue and sent to the Panda task as they happen. See http://twistedmatrix.com/trac/
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.
|