I do not understand completely how to use pyglet's event system. What I need to do is to get each event for each frame. In Pygame I can do it in the following way:
import sys, pygame
pygame.init()
size = width, height = 320, 240
screen = pygame.display.set_mode(size)
while 1:
for event in pygame.event.get():
print event
if event.type == pygame.QUIT: sys.exit()
screen.fill((0,0,0))
pygame.display.flip()
I accomplish something similar with pyglet, using 'pyglet.window.event.WindowEventLogger()' but I do not how to acces to the information that the WindowEventLogger prints, and it prints the same event more than once.
How could I obtain a pyglet version of the pygame code above?
Here's a basic example of a working class that dispatches events.
#!/usr/bin/python
import pyglet
from time import time, sleep
class Window(pyglet.window.Window):
def __init__(self, refreshrate):
super(Window, self).__init__(vsync = False)
self.frames = 0
self.framerate = pyglet.text.Label(text='Unknown', font_name='Verdana', font_size=8, x=10, y=10, color=(255,255,255,255))
self.last = time()
self.alive = 1
self.refreshrate = refreshrate
def on_draw(self):
self.render()
def render(self):
self.clear()
if time() - self.last >= 1:
self.framerate.text = str(self.frames)
self.frames = 0
self.last = time()
else:
self.frames += 1
self.framerate.draw()
self.flip()
def on_close(self):
self.alive = 0
def run(self):
while self.alive:
self.render()
event = self.dispatch_events() # <-- This is the event queue
sleep(1.0/self.refreshrate)
win = Window(23) # set the fps
win.run()
However note that normally I never encounter any events at all in this event queue. Most likely because Pyglet works with function decorators/hooks.
So for instance, if you want to catch keyboard events, you would simply add the following code to this awesome class:
def on_key_release(self, symbol, modkey):
print(symbol, modkey)
There's also on_mouse_motion etc :)
Related
Currently, I am loading all images at the start of the program. Is there a better solution for this? As some images may not be used at all throughout the entire game (I'm currently making a main menu).
Main.py
import pygame as pg
from Menu import Menu
pg.init()
"""Displays screen"""
winWidth = 1920
winHeight = 1080
size = (winWidth, winHeight)
win = pg.display.set_mode(size)
"""Load images"""
bg_img = pg.image.load('Pic/td-gui/PNG/menu/bg.png')
title_img = pg.image.load('transtitle.png')
settings_img = pg.image.load('Pic/td-gui/PNG/menu/button_settings.png')
play_img = pg.image.load('Pic/td-gui/PNG/menu/button_play.png')
reg_img = pg.image.load('Pic/td-gui/PNG/menu/button_registration.png')
quit_img = pg.image.load('Custom/Menu/Quit.png')
rope_img = pg.image.load('Pic/td-gui/PNG/menu/rope_small.png')
changeDiff_img = pg.image.load('Custom/Menu/Change_Difficulty.png')
score_img = pg.image.load('Custom/Menu/Scoreboard.png')
selectmap_img = pg.image.load('Custom/Menu/Select_map.png')
tutorial_img = pg.image.load('Custom/Menu/Tutorial.png')
"""Puts loaded images into the correct size"""
bg_img = pg.transform.scale(bg_img,(winWidth, winHeight))
title_img = pg.transform.scale(title_img, (750, 75))
play_img = pg.transform.scale(play_img, (250, 250))
rope_img = pg.transform.scale(rope_img, (27, 290))
clock = pg.time.Clock()
win.blit(bg_img, (0, 0))
mainMenu = True
settingsMenu = False
run = True
running = True
while run:
clock.tick(30)
pg.display.update()
for event in pg.event.get():
if event.type == pg.QUIT:
run = False
if not run:
pg.quit()
"""Places correct images in specified location"""
title = Menu(winWidth // 2 - 375, winHeight // 2 - 500, title_img)
if mainMenu:
reg_button = Menu(winWidth // 2 - 150, winHeight // 2 - 100, reg_img)
ropeR = Menu(winWidth // 2 - 100, 200, rope_img)
ropeL = Menu(winWidth // 2 + 70, 200, rope_img)
scoreboard_button = Menu(winWidth//2 - 330, winHeight//2 + 275, score_img)
play_button = Menu(winWidth // 2 - 125, winHeight // 2 - 375, play_img)
reg_button = Menu(winWidth // 2 - 161, winHeight // 2 - 100, reg_img)
quit_button = Menu(winWidth//2 + 30, winHeight//2 + 275, quit_img)
changeDiff_button = Menu(winWidth//2 - 330, winHeight//2 + 80, changeDiff_img)
selectmap_button = Menu(winWidth//2 + 30, winHeight//2 + 80, selectmap_img)
settings_button = Menu(winWidth - 200, winHeight - 200, settings_img)
"""Draws images in specified location and makes buttons if specified"""
title.draw_img(win)
if reg_button.draw_button(win):
print('Reg button clicked')
ropeL.draw_img(win)
ropeR.draw_img(win)
if scoreboard_button.draw_button(win):
print("options Clicked")
if play_button.draw_button(win):
print("Play Clicked")
if quit_button.draw_button(win):
pg.quit()
if changeDiff_button.draw_button(win):
print('Change Difficulty button pressed')
if selectmap_button.draw_button(win):
print('Select Map button preseed')
if settings_button.draw_button(win):
print('Settings button pressed')
mainMenu = False
settingsMenu = True
if settingsMenu:
win.blit(bg_img, (0, 0))
title.draw_img(win)
Menu.py
import pygame as pg
pg.init()
class Menu():
def __init__(self, x, y, img):
self.img = img
self.rect = self.img.get_rect()
self.rect.y = y
self.rect.x = x
self.clicked = False
def draw_button(self, win):
action = False
#get mouse position
pos = pg.mouse.get_pos()
#check mouseover and clicked conditions
if self.rect.collidepoint(pos):
if pg.mouse.get_pressed()[0] == 1 and not self.clicked:
action = True
self.clicked = True
if pg.mouse.get_pressed()[0] == 0:
action = False
#draw image
win.blit(self.img, (self.rect.x, self.rect.y))
return action
def draw_img(self, win):
win.blit(self.img, (self.rect.x, self.rect.y))
Say I'm making a music button, which is accessible through the settings button, I would currently load the music button image at the start of the program. Is there a way to make it so the music image would only be loaded once the settings button has been pressed?
To give a quick and simple overview of what you could do, here's an example. You define an abstract Scene class. Then, every "scene" in your game inherits from it. Each scene has a setup method that loads everything the scene needs (or might need).
import pygame
class Scene:
def __init__(self):
""" Create all attributes """
def setup(self):
""" Load everything in and initialize attributes """
def handle_events(self, events):
""" Handle the events for this scene """
def update(self, dt):
""" Run logic """
def render(self, screen):
""" Draw to the screen """
class Menu(Scene):
def __init__(self):
""" Create all attributes """
self.image1 = None
self.image2 = None
# ... and so on ...
def setup(self):
""" Load everything in and initialize attributes """
self.image1 = pygame.image.load("my_image1.png")
self.image2 = pygame.image.load("my_image2.png")
# ... and so on ...
def handle_events(self, events):
""" Handle the events for this scene """
def update(self, dt):
""" Run logic """
def render(self, screen):
""" Draw to the screen """
screen.blit(self.image1, (0, 0))
# ... and so on ...
class Game(Scene):
def __init__(self):
""" Create all attributes """
self.player = None
self.enemies = None
# ... and so on ...
def setup(self):
""" Load everything in and initialize attributes """
self.player = Player(position=(100, 100), image=pygame.image.load("player.png"))
self.enemies = [
Enemy(position=(200, 400), pygame.image.load("enemy1.png")),
Enemy(position=(200, 500), pygame.image.load("enemy2.png")),
]
# ... and so on ...
def handle_events(self, events):
""" Handle the events for this scene """
for event in events:
if event.type == pygame.KEY_DOWN:
print('PAM!')
def update(self, dt):
""" Run logic """
self.player.update(dt)
for enemy in self.enemies:
enemy.update()
def render(self, screen):
""" Draw to the screen """
screen.blit(self.player.image, self.player.position)
for enemy in self.enemies:
screen.blit(enemy.image, enemy.position)
# ... and so on ...
Here's roughly how a game loop would look like.
def main():
screen = pygame.display.set_mode((1024, 1024))
clock = pygame.time.Clock()
running = True
scene = Menu()
scene.setup()
while running:
dt = clock.tick(60)
events = list(pygame.event.get())
scene.handle_events(events)
scene.update(dt)
scene.render(screen)
# Have some way of determining when to switch scene.
if should_switch_to_game:
scene = Game()
scene.setup()
elif should_switch_to_menu:
scene = Menu()
scene.setup()
# ... and so on ...
This is a basic starting point, but there are other ways to handle the situation, like having an asset manager that dynamically load and unload assets based on usage. However, these are quite complicated and will make development and debugging much harder. It's always best to keep things simple when you can. If you can load all images at the beginning of the program, then I'd suggest doing so.
Also, remember that there's nothing that's "best" or "optimized" as all you're doing is tradeoffs. If you're trying to optimize for memory, then you're often making your program slower. If you're optimizing for speed, then you're often making your program use more memory.
If you are loading things just when you need them, you're going to have a loading time switching between different views in your menu. This is often not something you want as the user expects it to be fast. Switching from the game to the menu, it's more forgiving to have a couple of extra ms. But if you feel you want to go the other way then different views in the menu could be considered different scenes.
In general, no it's not a problem to load images that are never used, as long as the images are small. It's better to load them all at once so that the interface never has to wait for loading.
However, if you have a very large amount of images or your images are very big, it might be worth loading some images only when you need them. You could do something like this when you start your music field:
def start_music_menu():
global music_icon # Sorry about the global
if music_icon is None:
music_icon = pygame.image.load('...')
In this case your interface will freeze while loading the extra image. You can also use threading or asyncio to keep your interface responsive while a image is loading.
Properly use OOP concept make separate class for various loading image and then use them accordingly .
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.
couple of days ago, I made a code that plays a video in pygame window.
Code works just fine, just as I originally intended. However, when I print debug statement to see its fps, it's somewhere around 30fps. If I were to increase fps, what should I do?
Here is the code I used.
import sys
from color import *
import pyglet
pygame.init()
running = True
gameDisplay= pygame.display.set_mode((800,600))
window = pyglet.window.Window(visible=False)
background_vid = pyglet.media.Player()
background_vid.queue(pyglet.media.load(".\\music_folder\\music_vid/servant_of_evil_converted.mp4"))
background_vid.play()
def hellow():
print "hellow bloody world"
def on_draw():
#We have to convert the Pyglet media player's image to a Pygame surface
rawimage = background_vid.get_texture().get_image_data()
print "rawimage "+str(rawimage)
pixels = rawimage.get_data('RGBA', rawimage.width *8)
video = pygame.image.frombuffer(pixels, (rawimage.width*2,rawimage.height), 'RGBA')
#Blit the image to the screen
gameDisplay.blit(video, (0, 0))
circle_x=300
while True:
pyglet.clock.tick()
on_draw()
print "fps: "+str(pyglet.clock.get_fps())
for event in pygame.event.get():
if(event.type == pygame.QUIT):
sys.exit()
pygame.quit()
pygame.draw.rect(gameDisplay, red, (circle_x, 300, 300, 300), 5)
circle_x+=1
pygame.display.update()
So what #pydude said is not completely wrong.
However, in order to actually messure FPS I'd put a custom counter in the on_draw function, that will give better accuracy.
Further more, the only real problem with your code is that you don't insert vsync=False into your Window() decorator.
I've reworked your code to make it a little bit more modular, I've also removed potential bottle-necks and added my own custom FPS counter (via GL and not console), here - have a go and see if it works better for you.
(Note: Pressing Escape will exit the application)
import sys
from color import *
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.background_vid = pyglet.media.Player()
self.background_vid.queue(pyglet.media.load(".\\music_folder\\music_vid/servant_of_evil_converted.mp4"))
self.background_vid.play()
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
def on_draw(self):
self.render()
#We have to convert the Pyglet media player's image to a Pygame surface
def render(self):
self.clear()
rawimage = background_vid.get_texture().get_image_data()
pixels = rawimage.get_data('RGBA', rawimage.width *8)
video = pygame.image.frombuffer(pixels, (rawimage.width*2,rawimage.height), 'RGBA')
#Blit the image to the screen
self.blit(video, (0, 0))
# 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()
Try toggling vsync = True to vsync = False and watch the difference in the FPS counter.
With python, printing is very slow. Try just printing every once and a while.
Example:(requires import random):
if random.random()>0.09:print "fps: "+str(pyglet.clock.get_fps())
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.
I'm new to working with Pyglet and I've written a small program which moves a ball around the screen. Right now I'm having difficulty establishing a steady frame rate of 60 fps. While Pyglet is supposed to sync with my monitor's refresh rate of 60Hz, Pyglet is setting my fps to half of my refresh rate (ex. when 60Hz, 30 fps). Is there something wrong in my code that is causing this?
import pyglet
import physicalobject
import random
from pyglet.window import mouse
pyglet.resource.path = ['./resources']
pyglet.resource.reindex()
ball_image = pyglet.resource.image("ball2.png")
#sets clock format
fps_display = pyglet.clock.ClockDisplay(format='%(fps).2f fps')
def center_image(image):
image.anchor_x = image.width/2
image.anchor_y = image.height/2
center_image(ball_image)
ball = physicalobject.PhysicalObject(img=ball_image, x = 400, y = 300)
ball.scale = .2
ball.velocity_x = random.randint(-4,4)*150
ball.velocity_y = random.randint(-4,4)*150
#Calls update function to set new ball position based on velocity
def update(dt):
ball.update(dt)
#window.event
def on_mouse_drag(x, y, dx, dy, button, modifiers):
ball.x = x
ball.y = y
ball.velocity_x = dx * 20
ball.velocity_y = dy * 20
#window.event
def on_draw():
window.clear()
ball.draw()
fps_display.draw()
def main():
pyglet.clock.schedule_interval(update, 1/120.0)
pyglet.app.run()
if __name__ == '__main__':
main()
Pyglet simply doesn't handle it correctly on some systems, you have to disable your application window's vsync in order to get it to work. Here's an example script you can run to get a feel for how it works:
import pyglet
# Show FPS
fps = pyglet.clock.ClockDisplay()
# The game window
class Window(pyglet.window.Window):
def __init__(self):
super(Window, self).__init__(vsync = False)
# Run "self.update" 128 frames a second and set FPS limit to 128.
pyglet.clock.schedule_interval(self.update, 1.0/128.0)
pyglet.clock.set_fps_limit(128)
# You need the dt argument there to prevent errors,
# it does nothing as far as I know.
def update(self, dt):
pass
def on_draw(self):
pyglet.clock.tick() # Make sure you tick the clock!
self.clear()
fps.draw()
# Create a window and run
win = Window()
pyglet.app.run()
import pyglet
from time import time, sleep
class Window(pyglet.window.Window):
def __init__(self, refreshrate):
super(Window, self).__init__(vsync = False)
self.frames = 0
self.framerate = pyglet.text.Label(text='Unknown', font_name='Verdana', font_size=8, x=10, y=10, color=(255,255,255,255))
self.last = time()
self.alive = 1
self.refreshrate = refreshrate
def on_draw(self):
self.render()
def render(self):
self.clear()
if time() - self.last >= 1:
self.framerate.text = str(self.frames)
self.frames = 0
self.last = time()
else:
self.frames += 1
self.framerate.draw()
self.flip()
def on_close(self):
self.alive = 0
def run(self):
while self.alive:
self.render()
event = self.dispatch_events()
sleep(1.0/self.refreshrate)
win = Window(23) # set the fps
win.run()
Note the lack of the clock feature.
Also, try setting vsync = True and removing the sleep(1.0/self.refreshrate), this will lock the refresh rate to your monitor.
Also, note that i don't use pyglet.app.run() to lock the rendering process, i call self.dispatch_events() instead.. it doesn't really do anything except let the graphic "poll" and move on, without it.. pyglet waits for a poll to occur, which pyglet.app.run() normally does.