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.
Related
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()
I've created a simple script that shows a square moving to the left:
import pyglet
window = pyglet.window.Window(800, 600)
x = 0
y = 250
width = 100
height = 100
def update(time):
global x
window.clear()
pyglet.graphics.draw(4, pyglet.gl.GL_QUADS, ('v2f', [x, y, x, y+height, x+width, y+height, x+width, y]))
x += 10
pyglet.clock.schedule_interval(update, 1/10)
pyglet.app.run()
The square is rendered correctly when there is no keyboard input, but whenever I press a key, it seems like the window is cleared and the previous frame gets rendered again. I solved the problem by splitting the update function and by using the on_key_press() function:
#window.event
def on_key_press(symbol, modifiers):
draw()
def update(time):
global x
x += 10
draw()
def draw():
window.clear()
pyglet.graphics.draw(4, pyglet.gl.GL_QUADS, ('v2f', [x, y, x, y + height, x + width, y + height, x + width, y]))
But I don't think is the right way to solve the problem, because I'm rendering the square two times in one frame. You can see what the problem is over here: https://youtu.be/cIwuLYuLWtY.
In pyglet the window should be redraw in the on_draw(). See also Writing a pyglet application.
After a event occurs, the on_draw event is executed and the window is updated.
When no on_draw event is implemented, then it seems that in a double buffered context a buffer swap is triggered after the key event. Since the scene is not updated (because there is nor on_draw), the previous frame is shown.
I recommend to implement a on_draw event to redraw the window:
import pyglet
window = pyglet.window.Window(800, 600)
x, y, width, height = 0, 250, 100, 100
def update(time):
global x
x += 10
#window.event
def on_draw():
window.clear()
pyglet.graphics.draw(4, pyglet.gl.GL_QUADS, ('v2f', [x, y, x, y+height, x+width, y+height, x+width, y]))
pyglet.clock.schedule_interval(update, 1/10)
pyglet.app.run()
I'm trying to make a simple application with pyglet. My main problem so far is that I can't seem to blit an image with alpha - all of the transparent pixels are converted into black pixels. I'm not sure whether the problem is with the loading of the image or the blitting. Here is a very basic overview of how I'm trying to render the image:
import pyglet
import pyglet.clock
window = pyglet.window.Window()
window.config.alpha_size = 8
#fancy text
text = pyglet.resource.image("text.png")
#background image
bg = pyglet.resource.image("bg.png")
bg.blit(0, 0)
text.blit(100, 100)
pyglet.app.run()
Any help is appreciated. Thanks in advance.
You most likely just need to enable GL ALPHA blends.
from pyglet.gl import *
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
But first of all, your code is not able to run.
Mostly because you don't declare a window.event function to handle the on_draw where you normally render things.
Secondly, you never clear your window (which will cause a mess).
Here's a minimal working example of your code:
import pyglet
import pyglet.clock
window = pyglet.window.Window()
window.config.alpha_size = 8
#fancy text
text = pyglet.resource.image("text.png")
#background image
bg = pyglet.resource.image("bg.png")
#window.event
def on_draw():
window.clear()
bg.blit(0, 0)
text.blit(100, 100)
pyglet.app.run()
Now this generates this:
And here's a working example of how you use the GL_BLEND feature:
import pyglet
import pyglet.clock
from pyglet.gl import *
window = pyglet.window.Window()
window.config.alpha_size = 8
#fancy text
text = pyglet.resource.image("text.png")
#background image
bg = pyglet.resource.image("bg.png")
#window.event
def on_draw():
window.clear()
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
bg.blit(0, 0)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
text.blit(100, 100)
pyglet.app.run()
This yields a result like so:
However, this code will quickly become messy.
So there's two things you can do. You can first, put your images into sprite objects. Secondly, make this a bit more object oriented.
First, we'll use sprites.
self.fancy_background = pyglet.sprite.Sprite(pyglet.image.load('bg.png'))
self.fancy_background.draw() # not blit!
Sprites automatically uses transparency, which makes your life (and code) a lot easier.
Secondly, we'll put these into a batch.
Batches are made to bunch A LOT of sprites so you can call .draw() on the batch, and all sprites in that batch gets insta-rendered.
self.background = pyglet.graphics.Batch()
self.fancy_background = pyglet.sprite.Sprite(pyglet.image.load('bg.png'), batch=self.background)
self.background.draw() # background, not fancy_background! And also not blit!!
Last and most certainly not least.
We'll put this into a class so we can do cool stuff later on.
import pyglet
import pyglet.clock
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.background = pyglet.graphics.Batch()
self.texts = pyglet.graphics.Batch()
self.fancy_background = pyglet.sprite.Sprite(pyglet.image.load('bg.png'), batch=self.background)
self.fancy_text = pyglet.sprite.Sprite(pyglet.image.load('text.png'), batch=self.texts)
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_press(self, x, y, button, modifiers):
if button == 1: # Left click
pass
def on_key_press(self, symbol, modifiers):
if symbol == key.ESCAPE: # [ESC]
self.alive = 0
def render(self):
self.clear()
self.background.draw()
self.texts.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()
BAM.
This code will enable you to create custom functions and custom "player objects" later on for instance. Also you can do collision detection easier and the code just looks a lot more structured (I threw in a little bonus features such as keyboard and mouse events).
Note tho, that the position of the sprites will default to x=0, y=0 as shown in the last picture. You can set the position with x=100 either on the variable/handle or when creating the sprite.
I am trying to have a circle that, when clicked, moves somewhere else on the screen. However, when I click the circle, nothing happens.
#IMPORT STUFF
import pyglet as pg
from random import randint
mouse = pg.window.mouse
#VARS
window = pg.window.Window(width = 640, height = 480)
score = 0
circleImg = pg.image.load("circle.png")
circle = pg.sprite.Sprite(circleImg, randint(1, window.width), randint(1, window.height))
text = pg.text.Label("Click red!", font_name = "Times New Roman", font_size = 18, x = 260, y = 10)
#DETECT MOUSE PRESS ON CIRCLE
#window.event
def on_mouse_press(x, y, button, modifiers):
if x == circle.x and y == circle.y:
circle.x = randint(1, window.width)
circle.y = randint(1, window.height)
#window.event
def on_draw():
window.clear()
text.draw()
circle.draw()
pg.app.run()
import pyglet
from pyglet.gl import *
from random import randint
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
class Circle(pyglet.sprite.Sprite):
def __init__(self, radiance=5, x=0, y=0):
self.texture = pyglet.image.load('circle.png')
super(Circle, self).__init__(self.texture)
def click(self, x, y):
if x >= self.x and y >= self.y:
if x <= self.x + self.texture.width and y <= self.y + self.texture.height:
return self
mouse = pyglet.window.mouse
#VARS
window = pyglet.window.Window(width = 640, height = 480)
score = 0
#circleImg = pyglet.image.load("circle.png")
#circle = pyglet.sprite.Sprite(circleImg, randint(1, window.width), randint(1, window.height))
circle = Circle(x=50, y=50)
text = pyglet.text.Label("Click red!", font_name = "Times New Roman", font_size = 18, x = 260, y = 10)
#DETECT MOUSE PRESS ON CIRCLE
#window.event
def on_mouse_press(x, y, button, modifiers):
if circle.click(x, y):
print('Clicked in circle')
circle.x = randint(0, window.width - 10)
circle.y = randint(0, window.height - 10)
#window.event
def on_draw():
window.clear()
text.draw()
circle.draw()
pyglet.app.run()
A short description of what this does is it creates a custom class called Circle that inherits the Sprite class. It loads the circle.png as a texture with a alpha channel that gets blended by the GL library.
We add a custom function called click that checks if the lowest x,y coordinates are higher than the circles lowest x,y, then we check if the cursor is below x+width and same for y of the image region.
If that's the case, we return the circle sprite class as a True value in case we want to use the sprite.
Future enhancements:
You should draw the circle using gl functions, hence why I've defined radiance in the class definitions. However radiance here is never used, it's a placeholder for the future.
This is so you can use math to defined if you actually clicked within the circle, but this is beyond my scope of quick answers.. I would have to do a lot of debugging myself in order to get the math to add up (it's not my strong side).
What makes it work now is that we use the image width, height, x and y data to crudely check if we're within the image, aka "the circle".
trying to draw over sprite or change picture pyglet
As a bonus, I'll add this answer to the list of enhancements because it contains some stuff that might be useful. One would be to replace 90% of your code with a custom pyglet.window.Window class to replace global variables and decorators and stuff.
And it would look something like this:
import pyglet
from pyglet.gl import *
from random import randint
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
key = pyglet.window.key
class Circle(pyglet.sprite.Sprite):
def __init__(self, radiance=5, x=0, y=0):
self.texture = pyglet.image.load('circle.png')
super(Circle, self).__init__(self.texture)
def click(self, x, y):
if x >= self.x and y >= self.y:
if x <= self.x + self.texture.width and y <= self.y + self.texture.height:
return self
class MainScreen(pyglet.window.Window):
def __init__ (self):
super(MainScreen, self).__init__(800, 600, fullscreen = False)
self.x, self.y = 0, 0
self.bg = pyglet.sprite.Sprite(pyglet.image.load('background.jpg'))
self.sprites = {}
self.sprites['circle'] = Circle(x=50, y=50)
self.sprites['label'] = pyglet.text.Label("Click red!", font_name = "Times New Roman", font_size = 18, x = 260, y = 10)
self.alive = 1
def on_draw(self):
self.render()
def on_close(self):
self.alive = 0
def on_mouse_press(self, x, y, button, modifiers):
if self.sprites['circle'].click(x, y):
print('Clicked in circle')
self.sprites['circle'].x = randint(0, self.width - 10)
self.sprites['circle'].y = randint(0, self.height - 10)
def on_key_press(self, symbol, modifiers):
if symbol == key.ESCAPE: # [ESC]
self.alive = 0
def render(self):
self.clear()
self.bg.draw()
for sprite_name, sprite_obj in self.sprites.items():
sprite_obj.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()
x = MainScreen()
x.run()
I'm not familiar with pyglet, but I'm guessing the problem is that you're checking whether x == circle.x etc, which means it only moves when you click the single pixel at the exact centre of the circle. Try some kind of maximum distance from the centre (e.g. a hypotenuse math.sqrt( (x-circle.x)**2 + (y-circle.y)**2) < circle.radius
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.