Here's what I ended up doing:
I extended pyglet.window.Window so it contains a list of "GameModes", starting with the initial one I specify:
class GameWindow( pyglet.window.Window ):
def __init__( self, gamemode ):
self._modes = [gamemode]
pyglet.window.Window.__init__( self )
I override the GameWindow's on_draw handler so that it clears the screen only once, goes from the "bottom" (left) of the stack to the top (right), and calls a draw() method from each game mode.
def on_draw( self ):
self.clear()
for m in self._modes:
m.draw()
The GameMode objects are derived from this one. Any graphics that are created by the mode are added to a pyglet.graphics.Batch so they can be drawn all at once, and the draw() method can be altered to do updating, etc.
class GameMode():
def __init__(self):
self.batch = pyglet.graphics.Batch()
def draw(self):
self.batch.draw()
Specific GameModes are also defined by other handlers (e.g. on_key_press(), on_text() etc) and these are added to the GameWindow using push_handlers() so they run from top to bottom unlike the draw() methods.
It's not quite as elegant as finding a way to reverse the built-in event handler stack, but it works.