Search code examples
pythonkeyboardlogicpygletkeyrelease

Make a "Key Released" In Python With No Library?


I have been trying to use some "key released" functions from libraries like pynput and so, but I ran in to the problem that if I try using them together with a library called pyglet, that is a library for window-based apps, it won't let me and the program would crash.

I was wondering if there is any way detect key releases without libraries.

P.S: I've tried using the on_key_release function from pyglet but it was buggy for me and even though I wrote something for it to upon key release it usually didn't do it. I have checked my code a thousand times and it's not a problem on my part.

Pressing = False # var to indicate when the user is doing a long press and when he is not
@window.event
def on_key_release(symbol, modifiers):
    global Pressing
    if((symbol == key.A) | (symbol == key.S) | (symbol == key.W) | (symbol == key.D)):
        Pressing = False
    pass

and that code causes my player to freeze after i start moving him, and it does this even if i do nothing and just leach the whole on_key_release dunction empty. really weird.


Solution

  • So, your issue is most likely that you're doing Pressing = False if any key is released. Forcing your player object to freeze due to Pressing being False as soon as you release any of your keys.

    To work around this, you should store the states of your keys in a variable (I'll call it self.keys), and just before you render stuff, update/check the keys and update your player object accordingly.

    Here's a working example of movement on a player object (a red square in this case):

    from pyglet import *
    from pyglet.gl import *
    
    key = pyglet.window.key
    
    class main(pyglet.window.Window):
        def __init__ (self, width=800, height=600, fps=False, *args, **kwargs):
            super(main, self).__init__(width, height, *args, **kwargs)
            self.x, self.y = 0, 0
    
            self.keys = {}
    
            self.mouse_x = 0
            self.mouse_y = 0
    
            square = pyglet.image.SolidColorImagePattern((255, 0, 0, 255)).create_image(40, 40)
            self.player_object = pyglet.sprite.Sprite(square, x=self.width/2, y=self.height/2)
    
            self.alive = 1
    
        def on_draw(self):
            self.render()
    
        def on_close(self):
            self.alive = 0
    
        def on_mouse_motion(self, x, y, dx, dy):
            self.mouse_x = x
    
        def on_key_release(self, symbol, modifiers):
            try:
                del self.keys[symbol]
            except:
                pass
    
        def on_key_press(self, symbol, modifiers):
            if symbol == key.ESCAPE: # [ESC]
                self.alive = 0
    
            self.keys[symbol] = True
    
        def render(self):
            self.clear()
    
            ## Movement logic,
            #  check if key.W is in self.keys (updated via key_press and key_release)
            if key.W in self.keys:
                self.player_object.y += 1
            if key.S in self.keys:
                self.player_object.y -= 1
            if key.A in self.keys:
                self.player_object.x -= 1
            if key.D in self.keys:
                self.player_object.x += 1
    
            self.player_object.draw()
    
            self.flip()
    
        def run(self):
            while self.alive == 1:
                self.render()
    
                # -----------> This is key <----------
                # This is what replaces pyglet.app.run()
                # but is required for the GUI to not freeze
                #
                event = self.dispatch_events()
    
    if __name__ == '__main__':
        x = main()
        x.run()
    

    I've moved away from @window.event because it's easier for me to just copy paste this class/inheritance example since I had it laying around. But you could apply this logic any way you want. Just make sure you don't defined a solid state of pressing or not pressing, check individual keys and update accordingly before you render.