Python, Queue, Pygame and the GLib mainloop
27 April 2007
The GLib mainloop is what you're probably running in your Python program when you are using PyGTK or GStreamer.
Sometimes you want to integrate other event sources to it. The documentation for this is a bit thin; all I've seen is GLib's page on the main event loop, GSourceFuncs section and test-source.py in pygobject. Here I'm presenting two snippets displaying how to do it with two systems I've needed to work with. No guarantees on their correctness, but they do seem to work. Both are parts of larger modules, so they aren't usable by themselves and aren't really tidied up to look like proper examples. However, they should give you the general idea.
Here's how to do it with Python's Queue:
class QueueSource(gobject.Source):
def __init__(self, queue):
gobject.Source.__init__(self)
self._queue = queue
self.logger = loghelper.get_logger(self)
def prepare(self):
return False
def check(self):
return not self._queue.empty()
def dispatch(self, callback, args):
self.logger.debug("dispatch called, callback: " + str(callback))
if callback is not None:
self.logger.debug("dispatch calling callback: " + str(callback))
v = callback(self._queue.get(), *args)
self.logger.debug("dispatch returned " + str(v))
return True
def finalize(self):
self.logger.debug("finalize called")
m = QueueSource(self._queue)
def my_callback(command, *args):
self.logger.debug("my_callback: args: " + str(command) + ", " + str(args))
self.logger.debug("my_callback: thread: " + str(threading.currentThread()))
if command == self.PLAY:
self._player.play()
elif command == self.STOP:
self._player.stop()
elif command == self.SKIP:
self._player.skip()
m.set_callback(my_callback, mainloop)
m.attach()
And here's Pygame:
class PygameEventSource(gobject.Source):
def __init__(self):
gobject.Source.__init__(self)
self.logger = loghelper.get_logger(self)
def prepare(self):
return False
def check(self):
try:
pygame.event.pump()
p = pygame.event.peek(
[KEYUP, KEYDOWN, VIDEOEXPOSE, VIDEORESIZE, NUMEVENTS, QUIT,
ACTIVEEVENT, USEREVENT])
return p
except Exception, ex:
self.logger.error("Caught an exception while checking for events",
exc_info=True)
def dispatch(self, callback, args):
if callback is not None:
try:
e = pygame.event.poll()
v = callback(e, *args)
return v
except (KeyboardInterrupt, SystemExit):
self.logger.warn(
"Caught a terminating exception while dispatching to the "
"callback, re-raising", exc_info=True)
pygame.display.quit()
raise
except Exception, ex:
self.logger.error(
"Caught an exception while dispatching to the callback",
exc_info=True)
return True
def finalize(self):
pass
def my_callback(self, event, *args):
if event.type == QUIT:
sys.exit()
if event.type == KEYDOWN:
self.logger.debug("my_callback: keydown: event.key: " + str(event.key))
if event.key in (K_q,):
sys.exit()
elif event.key in (K_n, K_SPACE):
self._player.skip()
elif event.key in (K_p,):
if self._player.playing:
self._player.stop()
else:
self._player.play()
return True
def start_mainloop(self, mainloop):
self.logger.debug("Starting mainloop")
if not self._started:
s = PygameEventSource()
s.set_callback(self.my_callback, mainloop)
s.attach()
return