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

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.

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.

Related

Pyglet Into Class

Given that so far I have always used Pygame, I wanted to start using Pyglet, to understand a little how it works.
import pyglet, os
class runGame():
widthDisplay = 1024
heightDiplay = 576
title = "Pokémon Life and Death: Esploratori del proprio Destino"
wra = pyglet.image.load("wra.png")
wrb = pyglet.image.load("wrb.png")
def __init__(self):
platform = pyglet.window.get_platform()
display = platform.get_default_display()
screen = display.get_default_screen()
self.widthScreen = screen.width
self.heightScreen = screen.height
self.xDisplay = int(self.widthScreen / 2 - self.widthDisplay / 2)
self.yDisplay = int(self.heightScreen / 2 - self.heightDiplay / 2)
self.Display = pyglet.window.Window(width=self.widthDisplay, height=self.heightDiplay, caption=self.title, resizable=False)
self.Display.set_location(self.xDisplay, self.yDisplay)
pyglet.app.run()
game = runGame()
Up to here everything is fine and everything works correctly. But I was wrong, in the sense, now that I have to draw something, how should I do? In the sense, can Pyglet stay in a class like Pygame or not?
The marked solution might solve the problem.
However, in my opinion it doesn't really add more functionality than you can already achieve.
If you truly want to make Pyglet into a class, you actually gotta inherit something, or make use of the possibility of programming in a OOP way i recon.
Here's my two cents:
import pyglet
from pyglet.gl import *
from collections import OrderedDict
from time import time
key = pyglet.window.key
class main(pyglet.window.Window):
def __init__ (self, width=1024, height=576, caption="Pokémon Life and Death: Esploratori del proprio Destino", fps=True, *args, **kwargs):
super(main, self).__init__(width, height, *args, **kwargs)
platform = pyglet.window.get_platform()
display = platform.get_default_display()
screen = display.get_default_screen()
self.xDisplay = int(screen.width / 2 - self.width / 2)
self.yDisplay = int(screen.height / 2 - self.height / 2)
self.set_location(self.xDisplay, self.yDisplay)
self.sprites = OrderedDict()
if fps:
self.sprites['fps_label'] = pyglet.text.Label('0 fps', x=10, y=10)
self.last_update = time()
self.fps_count = 0
self.keys = OrderedDict()
self.mouse_x = 0
self.mouse_y = 0
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
self.mouse_y = y
def on_mouse_release(self, x, y, button, modifiers):
print('Released mouse at {}x{}'.format(x, y))
def on_mouse_press(self, x, y, button, modifiers):
if button == 1:
print('Pressed mouse at {}x{}'.format(x, y))
def on_mouse_drag(self, x, y, dx, dy, button, modifiers):
self.drag = True
print('Dragging mouse at {}x{}'.format(x, y))
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 pre_render(self):
pass
def render(self):
self.clear()
# FPS stuff (if you want to)
self.fps_count += 1
if time() - self.last_update > 1: # 1 sec passed
self.sprites['fps_label'].text = str(self.fps_count)
self.fps_count = 0
self.last_update = time()
#self.bg.draw()
self.pre_render()
for sprite in self.sprites:
self.sprites[sprite].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()
This way, you can actually interact with your class as if it were the pyglet class. For instance, you got on_key_press without having to use decorators etc.
You should make more functions for each thing you are doing. For example if you want to make a line you might have
import pyglet, os
class runGame():
widthDisplay = 1024
heightDiplay = 576
title = "Pokémon Life and Death: Esploratori del proprio Destino"
wra = pyglet.image.load("wra.png")
wrb = pyglet.image.load("wrb.png")
def __init__(self):
platform = pyglet.window.get_platform()
display = platform.get_default_display()
screen = display.get_default_screen()
self.widthScreen = screen.width
self.heightScreen = screen.height
self.xDisplay = int(self.widthScreen / 2 - self.widthDisplay / 2)
self.yDisplay = int(self.heightScreen / 2 - self.heightDiplay / 2)
self.Display = pyglet.window.Window(width=self.widthDisplay, height=self.heightDiplay, caption=self.title, resizable=False)
self.Display.set_location(self.xDisplay, self.yDisplay)
pyglet.app.run()
def drawLine(x, y):
drawLine(x, y)
You will have to make the functions based on what you want to do, but if you aren't going to use the runGame class multiple times before you kill the program, you shouldn't use OOP programming and use Procedural Programming.
You can create a custom pyglet window, by inheriting from the pyglet.window.Window class. After that in your init method call the super classes init (this will call the the init inside pyglet.window.Window class )Then overwrite the inherited methods (on_draw, on_key_press, on_key_release etc.). After that create an update method, which will be called by the pyglet.clock.schedule_interval 60 times in a second.Lastly just call the pyglet.app.run() method.
import pyglet
class GameWindow(pyglet.window.Window):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def on_draw(self):
self.clear()
def on_mouse_press(self, x, y, button, modifiers):
pass
def on_mouse_release(self, x, y, button, modifiers):
pass
def on_mouse_motion(self, x, y, dx, dy):
pass
def update(self, dt):
pass
if __name__ == "__main__":
window = GameWindow(1280, 720, "My Window", resizable=False)
pyglet.clock.schedule_interval(window.update, 1/60.0)
pyglet.app.run()

How do I run the pyglet.window.clear function through a on_button_press event?

Long story short I'm making a text based game in python 3 using Pyglet and I'm having trouble clearing the window. I want to clear out everything so I can put new words and new pictures in. I was hoping to set it up like this:
#window.event
def on_key_press(key.escape,modifier)
window.clear
Or something along those lines but it doesn't seem to work. Does anyone have any suggestions, ideas? the only other idea would be to convert the words to black and any images to black and layer it up but then the window will still be loading that stuff and I could see as the game goes on it starts eating up more and more ram to run the window with it loading all the previous pictures and text.
I'd prefer to avoid that and just clear it out, also then I don't have to basically copy the same code over and over again making it black.
Thanks,
Ben
import pyglet
from pyglet.gl import *
from time import time # Used for FPS calc
key = pyglet.window.key
class main(pyglet.window.Window):
def __init__ (self):
super(main, self).__init__(800, 800, fullscreen = False, vsync = True)
self.running = True
self.fps_counter = 0
self.last_fps = time()
self.fps_text = pyglet.text.Label(str(self.fps_counter), font_size=12, x=10, y=10)
def on_key_press(self, symbol, modifiers):
if symbol == key.ESCAPE: # [ESC]
self.running = False
else:
self.clear() # However, this is done in the render() logic anyway.
def on_draw(self):
self.render()
def render(self):
self.clear()
# And flip the GL buffer
self.fps_counter += 1
if time() - self.last_fps > 1:
self.fps_text.text = str(self.fps_counter)
self.fps_counter = 0
self.last_fps = time()
self.fps_text.draw()
self.flip()
def run(self):
while self.running is True:
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 event and event.type == pygame.QUIT:
self.running = False
x = main()
x.run()
This is a quick and dirty copy-and-paste logic of a FPS counter against a black screen.
Every key-press will force a clear of the screen.
In your example, you're probably store pyglet.window.Window() as myWindow = py... or something similar. And on that global variable you'd call myWindow.clear() from your decorator.
import pyglet
myWindow = pyglet.window.Window()
#myWindow.event
def on_key_press(symbol, modifier):
myWindow.clear()
pyglet.all.run()
(You lacked a : at the end, key.escape should just be a variable and clear is a function not a variable.)
However, if you're going to continue writing large graphical applications, i'd strongly suggest you classify your code and make it "OOP" right off the bat, graphical libraries tend to get messy rather quick otherwise.

pyglet on_draw event stops until mouse moves

I'm trying to write my first pyglet animation and I ran into a problem.
I have an update function called from the on_draw function. It does what it should do, but it stops at random places down the loop.
If I start moving the mouse it continues going down the loop.
I saw there is a question made here in 2011 about the same problem but with no relevant answer: (pyglet on_draw event occurs only when mouse moves)
To work I need to keep calling the update function inside the on_draw.
This is the code for both functions:
def update(zd):
stripe.y += zd[0]
stripe._set_rotation(zd[0])
#window.event
def on_draw():
window.clear()
window.clear()
batch.draw()
try:
update(next(calc))
except:
pass
I get the zd to the update from a big loop with a lot of calculations in the calc function.
Here, try this code instead:
import pyglet
from pyglet.gl import *
from math import radians, cos, sin, degrees, atan2
from time import time
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glEnable(GL_LINE_SMOOTH)
glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE)
pyglet.options['audio'] = ('alsa', 'openal', 'silent')
key = pyglet.window.key
class GUI(pyglet.window.Window):
def __init__(self):
super(GUI, self).__init__(640,340, caption='My app')
self.alive = True
self.keys_down = {}
imgTexture = pyglet.image.load('/path/to/image.png')
self.myImage = pyglet.sprite.Sprite(imgTexture)
self.myImage.x, self.myImage.y = 10, 50 # x,y from bottom left corner
def render(self, *args):
pyglet.gl.glClearColor(1, 1, 1, 1)
self.clear()
# .. This is where you draw your objects, for instance
self.myImage.draw()
self.flip()
def on_draw(self):
self.render()
def on_close(self):
self.alive = False
def on_key_press(self, symbol, modkey):
self.keys_down[symbol] = time()
def on_key_release(self, symbol, modkey):
if symbol in self.keys_down:
del(self.keys_down[symbol])
def on_mouse_release(self, x, y, button, modifiers):
pass
def on_mouse_press(self, x, y, button, modifiers):
print(button,'pressed',(x,y))
def on_mouse_motion(self, x, y, dx, dy):
pass
def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers):
pass
def run(self):
while self.alive:
event = self.dispatch_events()
for symbol in self.keys_down:
if symbol == key.ESCAPE:
self.alive = None
break
elif symbol == key.LEFT:
pass #Arrowkey Left
elif symbol == key.RIGHT:
pass #Arrowkey Right
elif symbol == key.UP:
pass #Arrowkey Up
elif symbol == key.DOWN:
pass #Arrowkey Down
elif symbol == 65515:
pass # Win key
else:
print(symbol)
self.render()
if __name__ == '__main__':
x = GUI()
pyglet.clock.set_fps_limit(120)
x.run()
Note that on_draw() is not really called ever in this code.
In the traditional pyglet code on_draw() is only called whenever a event exists, normally you trigger these with a timer or otherwise scheduled event.. Moving the mouse is one such event.
This is a manual override of the traditional app.run() that you're used to.
So the two main functions here are:
event = self.dispatch_events()
self.render()
The first fetches any events from pyglet effectively releasing any locks because even empty events must be fetched.
The second is the rendering functions which is what on_draw() normally does, except we can now call it all the time (or whenever we want).
I'm no expert but this works for 99% of any GUI making you'll ever encounter, as long as you're not going to do massive 3D games, this will do the job for you.

Pyglet unstable frame rate

I am trying to create a simple program in Pyglet that shows an animation and retrieves some mouse input and saves it to a text file. The code I have shows a significant unstable frame rate even when I am not using the mouse.
I have also looked at similar questions and the answers suggest to use a subclassed window, or to call the function on_draw with schedule_interval. I do not know, however, how to use a subclassed window to display my animation and, when I try to call on_draw with schedule_interval I get the error that on_draw does not receives any argument.
This is part of the code I am using:
fps = pyglet.clock.ClockDisplay()# Show FPS
#mywindow.event
def on_mouse_press(x, y, button, modifiers):
global timeStart, file, count
timeNow = time.clock() - timeStart
if button == mouse.LEFT:
print('left click press in {}').format(timeNow)
with open(out_file_name, 'a') as file:
file.write(str(count) +'\t'+ str(timeNow) +'\t'+ '-1\n')
#file.write('' + count + timeNow + 'left click press\n')
count += 1
def update_frames(dt):
global x
x=x+1
#mywindow.event
def on_draw():
pyglet.gl.glClearColor(0,0,0,0)
mywindow.clear()
glColor4f(1,0,0,1)
drawSquare(x,y)#this draws an opengl square
fps.draw()# Show FPS
dt = 1/10.0
pyglet.clock.schedule_interval(update_frames,dt)
pyglet.app.run()
What can I add to the code in order to obtain an stable frame rate?
I'd use something like this instead:
import pyglet
from pyglet.gl import *
from collections import OrderedDict
from time import time
from os.path import abspath
class GUI(pyglet.window.Window):
def __init__(self):
super(GUI, self).__init__(640,340, caption='Test')
pyglet.gl.glClearColor(1, 1, 1, 1)
self.alive = True
self.batches = OrderedDict()
self.batches['apples'] = pyglet.graphics.Batch()
self.framerate = 0, time()
self.count = 0
def render(self, *args):
self.clear()
#glColor4f(1,0,0,1)
#drawSquare(x,y)
if time() - self.framerate[1] > 1:
print('fps:',self.framerate[0])
self.framerate = 0, time()
else:
# Not an optimal way to do it, but it will work.
self.framerate = self.framerate[0]+1, self.framerate[1]
self.flip()
def on_draw(self):
self.render()
def on_close(self):
self.alive = False
def on_key_press(self, symbol, modkey):
pass
def on_key_release(self, symbol, modkey):
pass
def on_mouse_release(self, x, y, button, modifiers):
pass
def on_mouse_press(self, x, y, button, modifiers):
self.count += 1
with open('debug.log', 'w') as fh:
fh.write(str(count))
def on_mouse_motion(self, x, y, dx, dy):
pass
def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers):
pass
def run(self):
while self.alive:
event = self.dispatch_events()
if event:
print(event)
self.render()
if __name__ == '__main__':
x = GUI()
pyglet.clock.set_fps_limit(60)
x.run()
For one, this code actually produces 60 FPS,
You get way better control over your application and your main loop.
Secondly, the coding style is perhaps a personal preference but throwing stuff into class objects rather than doing a huge list of functions which you attach to say #window.update etc is more to my liking.. The code looks cleaner.
Give it a go, see if it works.
Note: Key here is event = self.dispatch_events() which must be called for each iteration, it's what replaces app.run().
Tie this together with objects and rendering them
class House(pyglet.sprite.Sprite):
def __init__(self):
self.texture = pyglet.image.load(abspath('./image.png'))
super(House, self).__init__(self.texture)
self.x = 0
self.y = 0
self.rotation = 0
self.name = 'house'
self.anchor = 'center'
def swap_image(self, image):
self.texture = pyglet.image.load(abspath(image))
self.image = self.texture
def rotate(self, deg):
self.image.anchor_x = self.image.width / 2
self.image.anchor_y = self.image.height / 2
self.rotation = self.rotation+deg
if self.anchor != 'center':
self.image.anchor_x = 0
self.image.anchor_y = 0
return True
def click(self):
print('Clicked:',self.name)
def work(self, *args):
pass
def click_check(self, x, y):
if x > self.x and x < (self.x + self.width):
if y > self.y and y < (self.y + self.height):
return self
def move(self, x, y):
if self.moveable:
self.x += x
self.y += y
def _draw(self):
self.draw()
in GUI() you'll do:
class GUI(pyglet.window.Window):
def __init__(self):
super(GUI, self).__init__(640,340, caption='Test')
...
self.house = House()
def render(self, *args):
self.house.rotate(1)
self.house._draw()
This should create a "house" (or whatever) and rotate the picture 1 degrees for each render occations, meaning you'll be rotating it 60 degrees a second with a nice flow.
There's more stuff to this than just this, but it's a simplification of what i usually use to squeeze out FPS while still maintaining readable code.. Because graphics get messy quite quickly.

Is there an issue with my use of pygame.rect.move?

I am using pygame.rect.move() to move a button to a certain position on instantiation. Then from the list of objects to blit that exists the menu button is called and all functions that need to be called each frame. The button does NOT move.
The rect should perfectly fit the button.
Is my use of pygame.rect.move() incorrect?
Here's the class:
class menuButton(mainMenu):
def setpos(self):
self.r = self.r.move(self.p)
def __init__(self,i,e,s,p, ai, ao,sa):
self.t = "Menu Button"
self.i = pygame.image.load(i)
self.r = self.i.get_rect()
self.e = e
self.ai = ai
self.ao = ao
self.p = p
self.a = 0
self.i.set_alpha(sa)
pygame.transform.scale(self.i,s)
objects.append(self)
print "%s has been instantiated." % (self.t)
def logic(self):
pass
def animations(self):
if self.ai == "FADE_IN":
if(self.i.get_alpha() < 255):
self.i.set_alpha(self.i.get_alpha() + 1)
def update(self):
self.r = self.i.get_rect()
r = True
for obj in objects:
if isinstance(self,type(obj)) == False:
r = False
if r == True:
if self.a == 0:
self.setpos()
print self.r
self.a += 1
self.logic()
self.animations()
screen.blit(self.i,self.r)
pygame.display.flip()
I'm not using .convert() at the end of an image import because it breaks the images.
I'm not using sprites because I want more control.
I have also written test programs and it seems to have worked, and I've re-written the main menu button class 3 times. Same issue.
m not using .convert() at the end of an image import because it breaks the images.
In what way? You probably want the alpha version http://www.pygame.org/docs/ref/surface.html#pygame.Surface.convert_alpha
I'm not using sprites because I want more control.
You can derive from Sprite , then you can add anything you want, yet still use sprite.Groups
A side note on the names, a Menu would contain Buttons. But buttons deriving from menu doesn't make sense.
move() returns a new rect that has moved, move_ip() modifies the existing rect.
Or you can use the rect properties
def setpos(self):
self.r.topleft = self.p
What are you doing with this code?
r = True
for obj in objects:
if isinstance(self,type(obj)) == False:
r = False
if r == True:
if self.a == 0:
self.setpos()
print self.r
self.a += 1

Categories

Resources