Pygame Sprite Button

Sprite Button

from pygame import Vector2, Surface, Color, Rect
from pygame.sprite import Sprite

class Button(Sprite):
    def __init__(self, caption, font, rect, callback, user_data=None,
            text_color=Color('white'),
            button_color=Color('slateblue'),
            hover_color=Color('dodgerblue')):

        Sprite.__init__(self)
        self.rect = Rect(rect)
        self.hover = False
        self.callback = callback
        self.user_data = user_data

        # Build are text and center it
        image_caption = font.render(caption, 1, text_color)
        image_rect = image_caption.get_rect()
        image_rect.center = Vector2(self.rect.center) - self.rect.topleft

        # Build are normal image
        self.image_normal = Surface(self.rect.size)
        self.image_normal.fill(button_color)
        self.image_normal.blit(image_caption, image_rect)

        # Build are hover image
        self.image_hover =  Surface(self.rect.size)
        self.image_hover.fill(hover_color)
        self.image_hover.blit(image_caption, image_rect)

    # Override image to control the draw method
    @property
    def image(self):
        if self.hover:
            return self.image_hover
        else:
            return self.image_normal

    def on_mousemotion(self, event):
        self.hover = self.rect.collidepoint(event.pos)

    def on_clicked(self, event):
        if event.button == 1:
            if self.rect.collidepoint(event.pos):
                self.callback(self)

Example

import pygame
from statemachine import State
from sprite_button import Button
from pygame.sprite import Group

class Scene(State):
    def __init__(self):
        self.font = pygame.font.Font(None, 32)
        self.buttons = Group()
        self.add_buttons()
        self.text = None
        self.text_pos = (300, 50)

    def add_buttons(self):
        for i in range(1, 6):
            rect = (50, i * 50, 200, 40)
            caption = 'Button {}'.format(i)
            button = Button(caption, self.font, rect, self.button_push, i)
            button.add(self.buttons)

    def button_push(self, button):
        self.text = self.font.render('Button {} Pushed'.format(button.user_data),
                                     1, pygame.Color('navy'))

    def on_draw(self, surface):
        surface.fill(pygame.Color('skyblue'))
        self.buttons.draw(surface)

        if self.text:
            surface.blit(self.text, self.text_pos)

    def on_event(self, event):
        if event.type == pygame.MOUSEMOTION:
            for sprite in self.buttons:
                sprite.on_mousemotion(event)
        elif event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 1: # left mouse click
                for sprite in self.buttons:
                    sprite.on_clicked(event)
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                State.machine.running = False
        elif event.type == pygame.QUIT:
            State.machine.running = False

if __name__ == '__main__':
    pygame.init()
    State.machine_setup("Sprite Button Example", 800, 600, True)
    State.machine.flip(Scene())
    State.machine.mainloop()