-
Notifications
You must be signed in to change notification settings - Fork 200
Add on_exit() hook #166
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add on_exit() hook #166
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,16 +15,15 @@ | |
DISPLAY_FLAGS = 0 | ||
|
||
|
||
def exit(): | ||
"""Wait for up to a second for all sounds to play out | ||
and then exit | ||
def exit(exit_status=0): | ||
"""Cleanly exits pgzero and pygame. | ||
|
||
Args: | ||
exit_status (int): Exit status. The default value of 0 indicates | ||
a successful termination. | ||
|
||
""" | ||
t0 = time.time() | ||
while pygame.mixer.get_busy(): | ||
time.sleep(0.1) | ||
if time.time() - t0 > 1.0: | ||
break | ||
sys.exit() | ||
sys.exit(exit_status) | ||
|
||
|
||
def positional_parameters(handler): | ||
|
@@ -213,6 +212,55 @@ def get_draw_func(self): | |
) | ||
return draw | ||
|
||
def _call_users_on_enter_func(self): | ||
# Calls the user's on_enter function if defined. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think an Would you like me to remove it? |
||
try: | ||
on_enter = self.mod.on_enter | ||
except AttributeError: | ||
# No func defined, so nothing to do. | ||
return | ||
|
||
if 0 != on_enter.__code__.co_argcount: | ||
# Put exception string on its own line for a cleaner traceback. | ||
raise TypeError( | ||
'on_enter() must not take any arguments' | ||
) | ||
|
||
on_enter() | ||
|
||
def _call_users_on_exit_func(self, exit_status): | ||
# Calls the user's on_exit function if defined. | ||
# Supports calling functions with zero or one argument (exit_status). | ||
try: | ||
on_exit = self.mod.on_exit | ||
except AttributeError: | ||
# No func defined, so nothing to do. | ||
return | ||
|
||
if 0 == on_exit.__code__.co_argcount: | ||
on_exit() | ||
elif 1 == on_exit.__code__.co_argcount: | ||
on_exit(exit_status) | ||
else: | ||
# Put exception string on its own line for a cleaner traceback. | ||
raise TypeError( | ||
'on_exit() can only take up to one argument' | ||
) | ||
|
||
def _on_exit(self, exit_status): | ||
# Exit clean up done here. | ||
# User's on_exit func is called right after exiting the game loop. | ||
self._call_users_on_exit_func(exit_status) | ||
|
||
# Wait (up to a second) for all sounds to play out. | ||
t0 = time.time() | ||
while pygame.mixer.get_busy(): | ||
time.sleep(0.1) | ||
if time.time() - t0 > 1.0: | ||
break | ||
|
||
pygame.quit() # Uninitialize any initialized pygame modules. | ||
|
||
def run(self): | ||
"""Invoke the main loop, and then clean up.""" | ||
loop = asyncio.get_event_loop() | ||
|
@@ -224,13 +272,20 @@ def run(self): | |
@asyncio.coroutine | ||
def run_as_coroutine(self): | ||
self.running = True | ||
exit_status = 0 | ||
|
||
try: | ||
yield from self.mainloop() | ||
except SystemExit as e: | ||
exit_status = e.code | ||
finally: | ||
pygame.display.quit() | ||
pygame.mixer.quit() | ||
self._on_exit(exit_status) | ||
self.running = False | ||
|
||
# This needs to be outside the finally clause to allow all other | ||
# exceptions to be passed up. | ||
sys.exit(exit_status) | ||
|
||
@asyncio.coroutine | ||
def mainloop(self): | ||
"""Run the main loop of Pygame Zero.""" | ||
|
@@ -242,8 +297,11 @@ def mainloop(self): | |
self.load_handlers() | ||
|
||
pgzclock = pgzero.clock.clock | ||
|
||
self.need_redraw = True | ||
|
||
# User's on_enter func is called right before entering the game loop. | ||
self._call_users_on_enter_func() | ||
|
||
while True: | ||
# TODO: Use asyncio.sleep() for frame delay if accurate enough | ||
yield from asyncio.sleep(0) | ||
|
@@ -253,9 +311,9 @@ def mainloop(self): | |
if event.type == pygame.QUIT: | ||
return | ||
if event.type == pygame.KEYDOWN: | ||
if event.key == pygame.K_q and \ | ||
event.mod & (pygame.KMOD_CTRL | pygame.KMOD_META): | ||
sys.exit(0) | ||
if (event.key == pygame.K_q and | ||
event.mod & (pygame.KMOD_CTRL | pygame.KMOD_META)): | ||
return | ||
self.keyboard._press(event.key) | ||
elif event.type == pygame.KEYUP: | ||
self.keyboard._release(event.key) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should bother with exit statuses. What you've done makes perfect sense for a general app development framework, but for Pygame Zero I think this adds unnecessary complexity.
We pay a cost for any additional complexity. It makes the documentation longer and adds terms that beginners may not be familiar with (and are unlikely to need). Here I suspect they will not know what an exit status is, and there are very few cases where it would be useful for them.
There is a very slightly stronger case that you can pair up
exit(n)
withon_exit(n)
, so you can signal different exit conditions. But there are other ways to deal with this, eg. setting global flags.I guess our expectations differ. I expect
exit()
to be very rarely used, because most games don't need to manage their own quitting. Meanwhileon_exit()
would be used rarely, eg to save scores or game state.