pyglet on_draw event stops until mouse moves - python

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.

Related

Pyglet not calling on_draw

I'm trying to make a simple game but I'm having issues
this is my code:
from myvector import myVector
from car import Car
import pyglet
width = 1000
height = 600
agent = None
agent = Car(int(width/2), int(height/2))
window = pyglet.window.Window()
window.set_size(width,height)
#window.event
def on_key_press(symbol, modifiers):
if symbol == 119: # w
agent.applyForce(myVector(-1, 0))
if symbol == 115: # s
agent.applyForce(myVector(1, 0))
if symbol == 97: # a
agent.applyForce(myVector(0, -1))
if symbol == 100: # d
agent.applyForce(myVector(0, 1))
#window.event
def on_draw():
window.clear()
agent.update()
agent.sprite.draw()
print(1)
if __name__ == "__main__":
pyglet.app.run()
problem is on_draw event is only called when I input something on keyboard
I'm using python 3.6 and latest pyglet package
I found nothing on internet
why is this happening?
Pyglet invokes on_draw only if an event occurs. Use pyglet.clock.schedule_interval to continuously invoke a function by a timer invent. That causes that on_draw is triggered, too:
#window.event
def on_draw():
window.clear()
agent.update()
agent.sprite.draw()
print(1)
def update(dt):
# update objects
# [...]
pass
if __name__ == "__main__":
pyglet.clock.schedule_interval(update, 1/60) # schedule 60 times per second
pyglet.app.run()
It could be an issue with the decorator function.
Instead of decorating the on_draw, replace the window object's on_draw function with your own declaration of that function:
See this example on on_mouse_press, which is replced with its own declaration.
#window.event
def on_mouse_press(x, y, button, modifiers):
global state, image
if button == pyglet.window.mouse.LEFT:
print('mouse press')
if state:
state = False
else:
state = True
Replaced to
import pyglet
image = pyglet.resource.image('test.png')
image.anchor_x = image.width // 2
image.anchor_y = image.height // 2
state = True
def on_draw():
print('on_draw() called')
window.clear()
if state:
image.blit(window.width // 2, window.height // 2)
def on_mouse_press(x, y, button, modifiers):
global state
print('mouse pressed')
if state:
state = False
else:
state = True
window = pyglet.window.Window()
window.on_draw = on_draw
window.on_mouse_press = on_mouse_press
pyglet.app.run()

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.
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.

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.

Update method in Pyglet

I am new to pyglet and I have a question about the image on the screen update. Necessary each time the mouse pressed to change the count, but I have all the time is zero, where did I go wrong and how can I fix it?
from pyglet.window import mouse
from pyglet import *
import pyglet
window = pyglet.window.Window(800, 600)
n = 0
label = pyglet.text.Label(str(n), x = window.width/2, y = window.width/2 )
#window.event
def on_mouse_press(x, y, button, modifiers):
if button == mouse.LEFT:
global n
n += 1
#window.event
def on_draw():
window.clear()
label.draw()
def update():
pass
pyglet.app.run()
pyglet.clock.schedule_interval(update, 1/30.0)
Thank you!
You need to create again the label in order to change its value (or better modify the label.text attribute)! Actually you didn't pass n to pyglet.text.Label but a string created "on the fly" containing that value.
#window.event
def on_mouse_press(x, y, button, modifiers):
if button == mouse.LEFT:
global n
n += 1
label.text = str(n)
Hope it helps.

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.

Categories

Resources