Search code examples
pythonkeyboardemulationpygletchip-8

Pyglet not registering key presses unless I import keyboard module in my Python Chip-8 emulator even though it's not being used


Background

I'm working on a Python chip-8 emulator Chipy. And I'm using Pyglet to display sprites and handle keypresses.

The way I implemented the screen and keyboard functions are in separate files but in the same module, as follow.

screen.py

from pyglet.window import Window

window = Window(args, kwargs)

"""
All the functions related to displaying things on screen
"""

keyboard.py

from .screen import window

@window.event
def on_key_press():
  # Handled key presses

@window.event
def on_key_release():
 # Handled key releases

Issue

The actual opcodes are being handled in a different file, which uses the screen.py module for some functionalities but not the keyboard.py module. But if I do not import the keyboard along with the screen, pyglet won't register keypresses.

control_unit.py

from chipy.peripherals import screen, keyboard

# Handles all the opcodes
# Uses screen
# keyboard is an unused import

Everything works fine with above file, but If I were to remove the unused import keyboard, pyglet will stop registering keypresses. I don't know what is causing this behaviour or is there a work around to make the code more organized?

I skimmed through the keyboard events in the Pyglet documentation but didn't find anything that could help understand this behaviour.


Solution

  • There may be a little confusion, keyboard.py is not unused per-se. It may be unused in control_unit, but from what I can tell it's being used by your application to register the pyglet keyboard events.

    @window.event is essentially a wrapper that registers that function to your window so it can receive the keyboard input. If you are not importing the module, then that wrapper is not going to run and register it. So you should be registering any events on the application start after your Window, so importing keyboard.py when screen.py is imported is an easy fix.

    As far as making the code more organized there are a few ways to do it. Mainly with the event system. You could use a class to manage your window and events this way:

    For example:

    class MyApplication:
        def __init__(self):
            self.window = pyglet.window.Window(width, height)
            
            self.window.push_handlers(self)
            
        def on_key_press(self, symbol, modifiers):
            print("I got pressed!")
    

    This means that the MyApplication instance is acting as a handler for the events, so it will automatically receive any events the Window gets.

    You can do this for another class as well if you want it even more organized. You could even have multiple kinds of these handlers to have the events travel down the stack.

    class ApplicationInputHandler:
        def __init__(self, window):
            window.push_handlers(self)
            
        def on_key_press(self, symbol, modifiers):
            print("I got pressed!")
    

    I would visit the docs here for more information: https://pyglet.readthedocs.io/en/latest/programming_guide/events.html

    You can also visit the Pyglet Discord to discuss it openly with others for additional suggestions.