I'm making a little GUI with python, using cocos2d and pyglet modules. The GUI should play a sound while the "h" is pressed and stop when it is released. The problem here is that I can't find a solution to this. After searching this site I've found this question - How to play music continuously in pyglet, the problem with this one is that I can't get the sound to stop after it starts.
EDIT: I found a way to play the sound until keyrelease, but ran into another problem
Right now the code that is supposed to play the music looks like this:
class Heartbeat (cocos.layer.Layer):
is_event_handler=True
def __init__ (self):
super(Heartbeat, self).__init__()
global loop, music, player
music = pyglet.media.load('long_beep.wav')
loop=pyglet.media.SourceGroup(music.audio_format, None)
player=pyglet.media.Player()
loop.queue(music)
player.queue(loop)
def on_key_press(self, key, modifiers):
if chr(key)=='h':
loop.loop=True
player.play()
def on_key_release (self, key, modifiers):
if chr(key)=="h":
loop.loop=False
This code works when the "h" key is pressed and held down for the first time, it doesn't work in subsequent attempts. Python doesn't raise an exception, it just seems to ignore "h" key presses that occur after the first release.
NOTE: The statement - if chr(key)=="h" may not be the best solution to keypress handling, but I'm relitively new to using cocos2d and pyglet modules.
Nevermind, I have figured this out, all I had to do was move the line player.queue(loop) from the initialization function to the function that handles keypresses. The updated code looks like this:
class Heartbeat (cocos.layer.Layer):
is_event_handler=True
def __init__ (self):
super(Heartbeat, self).__init__()
global loop, music, player
music = pyglet.media.load('long_beep.wav')
loop=pyglet.media.SourceGroup(music.audio_format, None)
player=pyglet.media.Player()
loop.queue(music)
def on_key_press(self, key, modifiers):
if chr(key)=='h':
loop.loop=True
player.queue(loop) #This is the line that had to be moved
player.play()
def on_key_release (self, key, modifiers):
if chr(key)=="h":
loop.loop=False
NOTE: I ommited statements such as import and others, used for initialization as they are not relevant to this issue.
Related
I started coding snake in python 3. As GUI i use Tkinter.
I got a timer which waits for a second an then calls the method again. Well now my question is how to i stop the self.wait?
I know i could work around this pretty easily, but i had this problem already somewhere else, so it would be nice to know how i can stop this.
This is the method which moves the snake around. (Only the timer is critical). The timer is here so it moves every second.
def move_snake(self):
self.after(1000, self.move_snake)
# code goes on
Now if i change the direction (by pressing a button) i do following:
def change_direction(self, event):
self.pressed = event.keysym
self.move_snake()
If i do this this way the "old" timer still is active and therefore the method gets called multiple times (it adds an additional timer when you press an button).
It would be nice that just the latest timer is activated.
Do you need more information?
Assuming that move_snake uses self.pressed, you don't need to call move_snake inside of change_direction.
However, if you really want to stop the old loop and start a new loop, you can save the id that is returned from after and give that to after_cancel:
def move_snake(self):
self.after_id = self.after(1000, self.move_snake)
# code goes on
def change_direction(self, event):
self.pressed = event.keysym
# cancel the old loop
self.after_cancel(self.after_id)
# start a new loop
self.move_snake()
This question is NOT a duplicate of this question: Why doesn't the .bind() method work with a frame widget in Tkinter?
As you can see, I set the focus to the current frame in my game_frame() method.
I'm writing a Chip-8 emulator in Python and using Tkinter for my GUI. The emulator is running, but I can't get Tkinter to recognize keypresses. Here is my code:
def game_frame(self):
self.screen = Frame(self.emulator_frame, width=640, height=320)
self.screen.focus_set()
self.canvas = Canvas(self.screen, width=640, height=320, bg="black")
self._root.bind("<KeyPress-A>", self.hello)
for key in self.CPU.KEY_MAP.keys():
print(key)
self.screen.bind(key, self.hello)
self.screen.pack()
self.canvas.pack()
def hello(self, event):
if event.keysym in self.CPU.KEY_MAP.keys():
self.CPU.keypad[self.CPU.KEY_MAP[event.keysym]] = 1
self.CPU.key_pressed = True
self.CPU.pc += 2
sys.exit()
def run_game(self, event):
self.game_frame()
self.CPU.load_rom("TANK")
while True:
self._root.update()
self.after(0, self.CPU.emulate_cycle)
Could you please help me figure out what's going wrong? I think it might have something to do with my game loop interfering with the key bindings, but I'm not sure. The hello method never gets called when I run the game because the program continues to run in an infinite loop and never exits, regardless of what key is pressed. Thank you!
The problem could be due to two things. Without seeing all your code it's impossible to say for sure.
For one, you are binding to a capital "A" rather than a lowercase "a" -- have you testing that the binding works or not when you press a capital A?
Also, you are using after and update incorrectly. You may be starving the event loop, preventing it from processing key presses. The right way to run a function periodically is to have a function that (re)schedules itself.
class CPU_Class():
...
def run_cycle(self):
self.emulate_cycle()
self._root.after(1, self.run_cycle)
Two things to note:
don't use after(0, ...) -- you need to give tkinter at least a ms or so to process other events.
the run_cycle function is responsible for running one cycle, and then scheduling the next cycle to run in the future.
Once you do that, you no longer need your while loop. You can simply call run_cycle once, and that will start the CPU running.
I am new to programming with Python. I have been working through a tutorial book that I found, and got the game up and running, but then decided I wanted to have a "Play Again?" option at the end. I can get the game to quit out with a press of the "n" key, but cannot work out how to get the game to restart.
Here is the code I think is giving the trouble:
#player reaches treasure
if player_rectangle.colliderect(treasure_rectangle):
#display text
screen.blit(text,(screen_width/2-195,screen_height/2-25))
if event.type==pygame.KEYDOWN:
if event.key==pygame.K_n:
exit()
elif event.key==pygame.K_y:
pygame.display.update()
I know something needs to go after the elif event, and I have tried all that I can think of. I tried to define the whole program, and call it but that stopped the whole thing running. I have looked around internet sites, but just cannot seem to come up with a answer.
Can some one help out in easy terms how to get the game to restart to the starting position when the y key is pressed? I know it has something to do with a loop, I just cannot place my finger on what.
Many thanks.
It's not entirely clear how your code is organized, so I'll be very general. Usually games are implemented with a "main loop" that handles all of the action. In "pseudo"-python:
def main_loop():
while True:
handle_next_action()
draw_screen()
if game_is_over():
break
Before you start the loop, you usually do some setup to get the game state how you want it:
def main():
setup()
main_loop()
shut_down()
Given those parts, you can reset the game by having the main loop code call setup again (it may need to be specifically designed to be runable more than once):
def main_loop():
while True:
handle_events()
draw_screen()
if game_is_over():
if play_again(): # new code here!
setup()
else:
break
You might want to split the setup code into two parts, one which only needs to be run when the program begins (to read configuration files and set up things like the window system), and a second that gets repeated for each new game (setting up the game's state).
To restart a game you normally need to reset all variables to their initial value (e.g. number of lives, score, initial position, ...).
The best way is to put all initialisations inside a procedure:
def init():
global score,lives # use these global variables
score=0
lives=3
init() # call init() to define and reset all values
while 1: # main loop
...
elif event.key==pygame.K_y:
init() # restart the game
I wanted to create a simple gui with a play and stop button to play an mp3 file in python. I created a very simple gui using Tkinter that consists of 2 buttons (stop and play).
I created a function that does the following:
def playsound () :
sound = pyglet.media.load('music.mp3')
sound.play()
pyglet.app.run()
I added that function as a command to the button play. I also made a different function to stop music:
def stopsound ():
pyglet.app.exit
I added this function as a command to the second button. But the problem is that when I hit play, python and the gui freeze. I can try to close the window but it does not close, and the stop button is not responsive. I understand that this is because the pyglet.app.run() is executing till the song is over but how exactly do I prevent this? I want the gui to stop the music when I click on the button. Any ideas on where I can find a solution to this?
You are mixing two UI libraries together - that is not intrinsically bad, but there are some problems. Notably, both of them need a main loop of their own to process their events. TKinter uses it to communicate with the desktop and user-generated events, and in this case, pyglet uses it to play your music.
Each of these loops prevents a normal "top down" program flow, as we are used to when we learn non-GUI programming, and the program should proceed basically with callbacks from the main loops. In this case, in the middle of a Tkinter callback, you put the pyglet mainloop (calling pyglet.app.run) in motion, and the control never returns to the Tkinter library.
Sometimes loops of different libraries can coexist on the same process, with no conflicts -- but of course you will be either running one of them or the other. If so, it may be possible to run each library's mainloop in a different Python thread.
If they can not exist together, you will have to deal with each library in a different process.
So, one way to make the music player to start in another thread could be:
from threading import Thread
def real_playsound () :
sound = pyglet.media.load('music.mp3')
sound.play()
pyglet.app.run()
def playsound():
global player_thread
player_thread = Thread(target=real_playsound)
player_thread.start()
If Tkinter and pyglet can coexist, that should be enough to get your music to start.
To be able to control it, however, you will need to implement a couple more things. My suggestion is to have a callback on the pyglet thread that is called by pyglet every second or so -- this callback checks the state of some global variables, and based on them chooses to stop the music, change the file being played, and so on.
I would do something like:
import pyglet
from pyglet.gl import *
class main (pyglet.window.Window):
def __init__ (self):
super(main, self).__init__(800, 600, fullscreen = False)
self.button_texture = pyglet.image.load('button.png')
self.button = pyglet.sprite.Sprite(self.button_texture)
self.sound = pyglet.media.load('music.mp3')
self.sound.play()
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 x > self.button.x and x < (self.button.x + self.button_texture.width):
if y > self.button.y and y < (self.button.y + self.button_texture.height):
self.alive = 0
def on_key_press(self, symbol, modifiers):
if symbol == 65307: # [ESC]
self.alive = 0
def render(self):
self.clear()
self.button.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 = main()
x.run()
This solution is the easiest one:
import pyglet
foo=pyglet.media.load("/data/Me/Music/Goo Goo Dolls/[1998] Dizzy Up The Girl/11 - Iris.mp3")
foo.play()
def exiter(dt):
pyglet.app.exit()
print "Song length is: %f" % foo.duration
# foo.duration is the song length
pyglet.clock.schedule_once(exiter, foo.duration)
pyglet.app.run()
source: http://ubuntuforums.org/showthread.php?t=1651906
There is a media player implementation in the pyglet documentation:
http://www.pyglet.org/doc/programming_guide/playing_sounds_and_music.html
The script you should look at is media_player.py
Hopefully this will get you started
Can anybody recommend how to switch between scenes in pyglet.
I.e.
menu > game
game > menu
menu > help
ect
The only way that i can think to do it off the top of my head is by using different windows, which i am quite sure would be the complete wrong way to do it. Or by overloading all of the window's event functions.
Sorry if i haven't made myself clear but any help would be appreciated
The cocos2d.org framework is built on pyglet and includes scene management.
Here is a rough schematic of a class structure that might work for you:
class Game(object):
"This class contains the Scene which is the current scene the user is look ing at."
def __init__(self):
self.current_level = 0
self.current_screen = MainMenu(self)
def load(self):
"Load progress from disk"
pass
def save(self):
"Save progress to disk"
pass
def clearCurrentScreen(self):
self.current_screen.clear()
self.window.remove_handlers()
def startCurrentScreen(self):
self.window.set_handler("on_key_press", self.current_screen.on_key_press)
# etc
self.current_screen.start()
def gotoNextLevel(self):
"called from within LevelPlayer when the player beats the level"
self.clearCurrentScreen()
self.current_level += 1
self.current_screen = LevelPlayer(self, game, self.current_level)
self.startCurrentScreen()
def startPlaying(self):
"called by the main menu when the user selects an option"
self.clearCurrentScreen()
self.current_screen = LevelPlayer(self, game, self.current_level)
self.startCurrentScreen()
def execute(self):
self.window = pyglet.window.Window()
self.startCurrentScene()
pyglet.app.run()
class Screen(object):
def __init__(self):
pass
def start():
pass
def clear():
"delete all graphical objects on screen, batches, groups, etc. Clear all state in pyglet."
pass
def on_key_press(self, key, etc):
pass
def on_draw(self):
pass
# etc
class LevelPlayer(Screen):
"This class contains all your game logic. This is the class that enables the user to play through a level."
def __init__(self, game, level_to_play):
pass
# be sure to implement methods from Screen
class MainMenu(Screen):
"This class presents the title screen and options for new game or continue."
def __init__(self, game):
self.game = game
def handleNewGame(self):
self.game.startPlaying()
def handleContinue(self):
self.game.load()
self.game.startPlaying()
# be sure to implement methods from Screen
game = Game()
game.execute()
So you have a Game class who owns the window and that manages which Screen is displayed to the user. Here I use "Screen" to mean what the user is interacting with, for example a MainMenu, or a LevelPlayer. The key here is the clear() method of Screen, which you should implement to delete all the sprites, media, groups, and batches that you were displaying. You also have to remove the window handlers on clear and set them on start.
You can see this solution in action here: https://github.com/superjoe30/lemming/tree/master/lemming
I'm not very experienced, but for what it's worth, the method I use is as follows. It does not recognise explicit 'states' or 'scenes' as such, but rather it relies on the adding (and removal) of discrete items into my game world. Each such item may have its own key handlers, and knows how and when to create other such items.
A specific example:
GameItem is a subclass for all items that can be put into the world. It is a simple collection of attributes, with no behaviour. It is subclassed by items in the game world like Bush, Player, etc, and also by HUD items like ScoreDisplay and MainMenu.
World is just a collection of GameItems.
My 'update' function iterates through all items in the world, calling their update method if they have one.
My 'draw' function similarly iterates through all items in the world, drawing each of them. (many of them might be drawn en-masse by simply calling something like pyglet's Batch.draw)
Right at application start-up, one of the first items I add to the world is a MainMenu object. It has an on_key_down method.
World.add looks at the item being added. If an item has an on_key_down method, then it adds this handler to the pyglet key handlers stack. (Similarly, it undoes this in World.remove) (Actually, on reflection, I guess world.add raises an event when it adds an item. If the application's keyboard handler module is interested, then on recieving one of these events it then does the adding of the item's key hander, but this is kinda incidental to the question)
The MainMenu.on_key_handler method looks at the pressed key, and if it's the key to start the game, then it makes a series of calls:
# Do everything needed to start the game
# For dramatic pacing, any of these might be scheduled to be
# called in a second or so, using pyglet.clock.schedule_once
world.add(player)
camera.to_follow(player)
world.add(scoredisplay)
# Finally, remove the main menu from the world
# This will stop showing it on screen
# and it will remove its keyboard event handler
world.remove_item(self)
This is just a simple example, but hopefully you can see how, instead of starting the game, the mainmenu could instead display secondary menus by adding them to the world, or any such thing.
Once in the game, you can change 'scenes' by simply calling 'world.remove' on all items that you no longer want visible, and calling 'world.add' on all the items in the new scene.
An example of a game that uses this technique is previous pyweek entry SinisterDucks:
http://code.google.com/p/brokenspell/
(although, to be fair, it does not feature distinct 'scenes' during gameplay. It merely usesthis technique to manage menus, game over screen, etc)
The solution i ended up using is by giving the main window a stack and having it pass all events out to items on the top of the stack.
i.e.
class GameWindow(pyglet.window.Window):
def __init__(self,*args, **kwargs):
pyglet.window.Window.__init__(self, *args, **kwargs)
self.states = [PlayLevel(self)] ## Starting State
def on_draw(self):
if hasattr(self.states[-1],"on_draw"):
self.states[-1].on_draw()
def on_mouse_press(self,*args):
if hasattr(self.states[-1],"on_mouse_press"):
self.states[-1].on_mouse_press(*args)
ect. For all of the other events that i use
i am currently in the process of writing up some functions to go in GameWindow that will manage pushing and popping scenes from the stack