7
\$\begingroup\$

I've built a simple decorator that wraps a function in a general Pygame game loop. It also allows for the programmer to set the tick rate.

import pygame
import sys
pygame.init()
def game_loop(tick_rate=60):
 """
 This simple decorator wraps functions
 with a pygame game loop. Here's an
 example of usage:
 @game_loop(tick_rate=70)
 def main():
 ...
 """
 def game_loop_decorator(function):
 def wrapper(*args, **kwargs):
 clock = pygame.time.Clock()
 while True:
 clock.tick(tick_rate)
 function(*args, **kwargs)
 for event in pygame.event.get():
 if event.type == pygame.QUIT:
 pygame.quit()
 sys.exit(0)
 pygame.display.flip()
 return wrapper
 return game_loop_decorator

Here's some example usage:

screen = pygame.display.set_mode((640, 480))
pygame.display.set_caption("Test game!")
@game_loop(tick_rate=100)
def main():
 font = pygame.font.SysFont("monospace", 15)
 text = font.render("Look! A game loop!", 1, (255, 255, 255))
 screen.blit(text, (50, 50))
if __name__ == "__main__":
 main()
asked Jul 15, 2015 at 19:08
\$\endgroup\$
2
  • \$\begingroup\$ Do you really need the sys.exit(0) there? It's not a good practice to exit in the middle of execution. Can't you simply return? \$\endgroup\$ Commented Jul 15, 2015 at 19:51
  • \$\begingroup\$ @janos I use sys.exit(0) because it seems to exit more cleanly when I'm using Python. \$\endgroup\$ Commented Jul 15, 2015 at 20:10

1 Answer 1

2
\$\begingroup\$

style


PEP8

  • There is one too many blank line after your imports.

  • It is debatable if there is too many blank lines in your decorator too. I personally think it's good without the blank lines.
    If you ignore the docstring then 17.6% of the function is blank lines.

    Use blank lines in functions, sparingly, to indicate logical sections.

You may feel the white space is needed, and so this section can be happily ignored.


PEP257

  • Multi-line docstrings consist of a summary line just like a one-line docstring, followed by a blank line, followed by a more elaborate description.

    Yours has this short summary with the elaborate description.
    I would be thrown off if your summery came up in my IDE,as I would be confused where this example is.

  • The docstring is a phrase ending in a period. It prescribes the function or method's effect as a command ("Do this", "Return that"), not as a description; e.g. don't write "Returns the pathname ..."

    Your summary line is more of a description. I'm not good with words, but the summary could be changed to, "Decorate a function inside a pygame loop, at the specified tick-rate.".

  • The docstring for a function or method should summarize its behavior and document its arguments, return value(s), side effects, exceptions raised, and restrictions on when it can be called (all if applicable). Optional arguments should be indicated. It should be documented whether keyword arguments are part of the interface.

    Yours does not explain what tick_rate is. A good way to do this is also explained in the PEP.

    """Form a complex number.
    Keyword arguments:
    real -- the real part (default 0.0)
    imag -- the imaginary part (default 0.0)
    """

I was thrown off a bit to see the event handler below the main of the code. I think there is no difference, however it's probably best to keep to the norm.


Overall there's almost no problems with your style. Your docstrings could follow PEP257 more, as the biggest problem would be the summarized line at the beginning.


code


You should add the ability to pass a function to handle pygame events. This is as the programmer currently would have to use a hack to get user input, or not use the decorator. Both bad options.

for a rudimentary example of what the handler could look like:

def even_handler(event):
 if event.type == pygame.QUIT:
 raise SystemExit

You could however use pygame.quit();sys.exit(0), but then if you exit multiple times you would need to make a function to exit, and it would remove a little added abstraction.

you would then need to add it to the decorator. The nicest way would probably be:

def game_loop(event_function, tick_rate=60):
 # replace the current event handler.
 for event in pygame.event.get():
 try:
 event_function(event)
 except SystemExit:
 pygame.quit()
 sys.exit(0)

This has the downside of having to pass an event-handler, unless you pass a default one, like the one above. However it allows the programmer to handle user input, a must in most pygame applications.

answered Jul 16, 2015 at 0:55
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.