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()
Related
So I try to make a "game" with PyGlet:
This is my code:
import pyglet
from pyglet import shapes
window = pyglet.window.Window(800, 600, "PyGlet Window")
circle = shapes.Circle(x = 100, y = 100, radius = 13, color=(255, 255, 255))
def callback(dt):
pass
pyglet.clock.schedule_interval(callback, 0.5)
#window.event
def on_draw():
window.clear()
circle.draw()
pyglet.app.run()
How to make the circle follow the mouse? Thanks!
Implement the on_mouse_motion event (see Working with the mouse) and change the position of the shape (see pyglet.shapes):
#window.event
def on_mouse_motion(x, y, dx, dy):
circle.x = x
circle.y = y
I copied this function from one of my older projects, on which it works perfectly, but it doesn't work anymore. The button is supposed to detect when the cursor is above it, and redraw itself in a lighter colour, and then when the cursor moves off, it redraws itself in the usual darker colour. But now when the cursor is above it, it doesn't change. It doesn't respond to clicking either. Here is the code
def button(text, x, y, width, height, inactive_color, active_color, action = None):
cur = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if x + width > cur[0] > x and y + height > cur[1] > y:
pygame.draw.rect(gameDisplay, active_color, (x, y, width, height))
pygame.display.update()
if click[0] == 1 and action != None:
if action == 'correct':
print('correct!')
else:
pygame.draw.rect(gameDisplay, inactive_color, (x, y, width, height))
text_to_button(text, black, x, y, width, height)
pygame.display.update()
button('test', 100, 100, 100, 50, darkGreen, green, action = 'correct')
You are calling the button function once. It runs through the code of the function and terminates, and does not respond further to any inputs -- because it only ran once (almost instantly).
Perhaps if you called this method every time a mouse movement event occurs in the Pygame evnet queue it may work.
Alternatively, consider using an object instead of a function for this, such as:
class Button():
def __init__(self, x, y, width, height, text, action):
self.x = x
self.y = y
self.width = width
self.height = height
self.text = text
self.action = action
self.label = myfont.render(self.text, 1, (0,0,0))
def render(self, window):
if self.visible == True:
pygame.draw.rect(window, (255,255,255), [self.x, self.y, self.width, self.height])
window.blit(self.label, (self.x, self.y))
def pressed(self):
self.action(self.arguments)
def hover(self):
#Here you can implement your code that handles when the mouse hovers over the button. This method can be called by checking mouse movement events in your main loop and seeing if they lie within the coordinates, width, and height of the button.
I am using Pyglet to create a main menu for my python game. I want to draw text on top of a box that would act as its container. Whenever I render the text, instead of it having a transparent background, it draws whatever the glClearColor is set to. This also happens whenever I try and draw an image that has no background.
I am currently using these two lines for my textbox and the text.
self.text_box = pyglet.sprite.Sprite(pyglet.image.load('../Resources/Textures/Menu/text_box.png'),640-150,360-25, batch=self.menuBatch,group=boxGroup)
self.play_text = pyglet.text.Label("Play", font_name="Arial", font_size=32, x=640, y=360, anchor_x='center', anchor_y='center', color=(255,255,255,255), batch=self.menuBatch,group=textGroup)
Then I just call self.menuBatch.draw(). A picture of the problem I am having is:
For transparency effects to work, 'blend' should be enabled in OpenGL.
To enable blend:
glEnable(GL_BLEND) # transparency
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) # transparency
This complete snippet is working for me:
import pyglet
from pyglet.gl import *
# Pyglet Window stuff ---------------------------------------------------------
batch = pyglet.graphics.Batch() # holds all graphics
config = Config(sample_buffers=1, samples=4,depth_size=16, double_buffer=True, mouse_visible=False)
window = pyglet.window.Window(fullscreen=False, config=config)
glClearColor( 0, 100, 0, 255) # background color
glEnable(GL_LINE_SMOOTH)
glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE)
glEnable(GL_BLEND) # transparency
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) # transparency
play_text = pyglet.text.Label("Play", font_name="Arial", font_size=32, x=240, y=140, anchor_x='center', anchor_y='center', color=(255,0,255,255), batch=batch,group=None)
text_box = pyglet.sprite.Sprite(pyglet.image.load('text_box.png'),240-150,160-25, batch=batch,group=None)
#window.event
def draw(dt):
window.clear()
batch.draw()
if __name__ == "__main__":
pyglet.clock.schedule_interval(draw, 1.0/60)
pyglet.app.run()
I have done something very similar to this before when making the GUI for one of my 3D programs. To make things very simple, I firstly turned off GL_DEPTH_TEST before the draw() call and then I re-enabled GL_DEPTH_TEST after the draw() call. I believe that the reason why the previous answer was working for them is because they didn't have GL_DEPTH_TEST enabled in the first place. I made a button class that you could maybe use as well (It's important to note that the argument "pos" is for the (x, y) coordinate of the lower left corner of the button and the argument "dim" is for the (width, height) dimensions of the button. The "texture_coords" argument refers to the texture coordinates of the background image of the button. If you want the full image, just leave it at the default. The rest is pretty self explanatory). So here's the code:
class Button:
def __init__(self, btn_img_file, text = "Button", pos = (0, 0), dim = (10, 10), text_color = (255, 255, 255), texture_coords = (0, 0, 1, 0, 1, 1, 0, 1)):
self.x, self.y = pos
self.w, self.h = dim
self.text = text
self.img = btn_img_file
self.tex = get_tex(self.img)
self.coords = texture_coords
self.color = text_color
self.batch = pyglet.graphics.Batch()
def on_click(self, mouse_x, mouse_y, function, *args, **kwargs):
x, y = self.x, self.y
X, Y = x + self.w, y + self.h
if mouse_x > x and mouse_x < X and mouse_y > y and mouse_y < Y:
function(*args, **kwargs)
def draw(self):
x, y = self.x, self.y
X, Y = x + self.w, y + self.h
font_size = (self.w // len(self.text)) - 5
label = pyglet.text.Label(self.text,
font_name = 'Times New Roman',
font_size = font_size,
x = x + (self.w // 2), y = y + (self.h // 2),
anchor_x = 'center', anchor_y = 'center',
color = (*self.color, 255))
self.batch.add(4, GL_QUADS, self.tex, ('v2f', (x, y, X, y, X, Y, x, Y)), ('t2f', self.coords))
glDisable(GL_DEPTH_TEST)
self.batch.draw()
label.draw()
glEnable(GL_DEPTH_TEST)
I hope this was helpful!
~ Rick Bowen
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 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.