import pygame from collections import deque class InputHandler: """ Class that is implemented into surfaces in order to control the pressing of buttons on both the real hardware and on a keyboard. Attributes: clock (pygame.time.Clock): Pygame clock used for input time calculations. last_input_tick (int): The tick that the last input was registered on. """ def __init__(self, pygame_clock, tick_check=True): """ Create a InputHandler object using a given Pygame clock. Args: pygame_clock (:obj:`pygame.time.Clock`): A pygame clock to use as the clock for input time calculations. tick_check (bool, optional): Bool to ignore inputs that happen to quickly after another. Defaults to True. """ self.clock = pygame_clock self.tick_check = tick_check self.last_input_tick = 0 self.dev_check = deque() self.dev_code = deque() for button in [pygame.K_DOWN, pygame.K_DOWN, pygame.K_UP, pygame.K_UP, pygame.K_DOWN, pygame.K_LEFT, pygame.K_RIGHT, pygame.K_LEFT, pygame.K_a]: self.dev_code.append(button) self.dev_found = False def create_event(self, pressed_button): """ Create a pygame event given a GPIO code and post it to the pygame event handler. Args: pressed_button (int): The GPIO code to be registered and pressed. """ # Register a button click so long as the last button click happened no less than two frames ago if self.tick_check: if pygame.time.get_ticks() - self.last_input_tick > self.clock.get_time() * 2: pygame.event.post(pygame.event.Event(pygame.KEYDOWN, {'key': pressed_button})) pygame.event.post(pygame.event.Event(pygame.KEYUP, {'key': pressed_button})) self.dev_check.append(pressed_button) if len(self.dev_check) > len(self.dev_code): self.dev_check.popleft() else: pygame.event.post(pygame.event.Event(pygame.KEYDOWN, {'key': pressed_button})) pygame.event.post(pygame.event.Event(pygame.KEYUP, {'key': pressed_button})) self.last_input_tick = pygame.time.get_ticks() def handle_keyboard(self): """Handle keyboard presses and generate corresponding GPIO codes to create events.""" # Checks if a corresponding keyboard key has been pressed. If it has, emulate a button press. for keyboard_event in pygame.event.get(): if keyboard_event.type == pygame.QUIT: running = False if keyboard_event.type == pygame.KEYDOWN: if keyboard_event.key == pygame.K_a: self.create_event(pygame.K_a) if keyboard_event.key == pygame.K_b: self.create_event(pygame.K_b) if keyboard_event.key == pygame.K_KP_ENTER: self.create_event(pygame.K_KP_ENTER) if keyboard_event.key == pygame.K_RIGHT: self.create_event(pygame.K_RIGHT) if keyboard_event.key == pygame.K_LEFT: self.create_event(pygame.K_LEFT) if keyboard_event.key == pygame.K_DOWN: self.create_event(pygame.K_DOWN) if keyboard_event.key == pygame.K_UP: self.create_event(pygame.K_UP) if keyboard_event.key == pygame.K_ESCAPE: running = False def update(self): """Run either the GPIO handler or the keyboard handler to check for input and create events.""" self.handle_keyboard() if self.dev_code == self.dev_check: self.dev_found = True