how to play sound that will continue changing-Pygame - audio

i am creating a program for my little brother on pygame where numbers 1-20 will come up in order and each number will be pronounced by mp3 files which i have already created.what i am stuck on now is how to make it so that the sound being played for each number will change as the next button is pressed. i didn't want do it one by one as it will take too long. is there a way to make like a list of all the mp3 sounds together so that the sounds played matches the number showing on the screen.
Sorry it was a long question. This is my first time asking a question and i couldn't find a way to shorten it.

If I understand you correctly, you just need to put the sounds into a list and use the number as the index.
import pygame as pg
pg.init()
SOUNDS = [
pg.mixer.Sound('1.mp3'),
pg.mixer.Sound('2.mp3'),
pg.mixer.Sound('3.mp3'),
]
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
number = 0
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.KEYDOWN:
# Just use the number as the index.
SOUNDS[number].play()
# Increment the number and keep it in the range.
number += 1
number %= len(SOUND_LIST)
screen.fill((30, 30, 30))
print(number)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()

Related

How generators can work inside event loops like pyinput

For example, this code is not working:
from pynput import keyboard
def on_press(key):
global play
if str(key) == "'x'":
play = 'Play'
play = ''
with keyboard.Listener(on_press = on_press, suppress=True) as listener:
while listener.is_alive(): # infinite loop which runs all time
def pump():
num = 0
while True:
yield num # generator
num += 1
if play == 'Play':
next(gen)
listener.join()
No matter how I put the generator function or using a global variable, I always have a StopIteration: error or others.
I want to control the generator, so I can "play" and "pause" with keyboard keys.
While this code works:
def pump():
num = 0
while True:
yield num
num += 1
gen = pump()
next(gen) #play
next(gen)
I'm so bewildered...
I don't use Jupyter notebooks but I tried the VS Code extension and it seems to work in them as well.
Here is some sample code to prove the generator is working.
from pynput import keyboard
import sys
def on_press(key):
global play
if str(key) == "'x'":
play = 'Play'
# added to provide a way of escaping the while loop
elif str(key) == "'q'":
sys.exit(1)
def pump():
num = 0
while True:
# added as proof your generator is working
print(num)
yield num
num += 1
gen = pump()
play = ''
with keyboard.Listener(on_press=on_press, suppress=True) as listener:
while listener.is_alive():
if play == 'Play':
next(gen)
listener.join()
This will infinitely call next(gen) since there is no way to unset play from "Play" in the code. Once it's set, it will continue infinitely looping and if play == 'Play': will always be True.
Now, onto your example.
This line:
next(gen)
will error.
gen is never defined in your code.
I assume this was just a typo and you have gen = pump() somewhere.
Additionally, defining pump in your loop will almost certainly have unintended consequences.
Suppose you do have gen = pump() immediately after the definition.
play will be set to "Play" when you press the x key. You will continuously call next(gen).
However, this time after each loop pump will be redefined and num will be set to 0. It will yield the 0 and continue to do so infinitely.
So what did you mean to do?
Probably something like this:
from pynput import keyboard
import sys
def on_press(key):
global play
if str(key) == "'x'":
play = 'Play'
elif str(key) == "'q'":
sys.exit(1)
def pump():
global play
num = 0
while True:
play = ''
print(num)
yield num
num += 1
gen = pump()
play = ''
with keyboard.Listener(on_press = on_press, suppress=True) as listener:
while listener.is_alive():
if play == 'Play':
next(gen)
listener.join()
Personally, I would avoid the use of globals.
However this, calls the next(gen) once with the x key press. The generator sets play back to "" avoiding subsequent calls of next(gen) until x is pressed again.

how to get cards to show up in pygame as they are played and not have the screen freeze if clicked on?

I'm trying to make a blackjack game in Pygame. I've got to the point where if I take a new card the cards start showing up. However, there are some issues:
The screen is blank until the first card is drawn at which point you see 3 cards. so you never see the first two on their own.
How would I go about making it so that it can be played and then followed by dealers cards showing as he plays(i have the dealers logic done already.
The GUI screen is always not responding when you click it, how to mitigate that.
The code:
from random import shuffle
import pygame
class card:
def __init__(self,suit,rank):
self.suit = suit
self.rank = rank
if type(self.rank) == int:
self.value = rank
elif self.rank == 'ACE':
self.value = 11
else:
self.value = 10
def showval(self):
print('{} of {}'.format(self.rank, self.suit))
print('card value is: ', self.value)
class deck:
def __init__(self):
ranks = [i for i in range(2, 11)] + ['JACK', 'QUEEN', 'KING',
'ACE']
suits = ['SPADE', 'HEART', 'DIAMOND', 'CLUB']
self.cards = []
for i in suits:
for j in ranks:
self.cards.append(card(i,j))
self.shuffle()
def __len__(self):
return len(self.cards)
def shuffle(self):
shuffle(self.cards)
class hand:
def __init__(self,name=None):
self.contents = []
self.deal(2,theDeck)
self.show()
def deal(self,x,theDeck):
if len(theDeck) < x:
theDeck = deck()
for j in range (x):
self.contents.append(theDeck.cards.pop(0))
self.total = sum(i.value for i in self.contents)
def show(self):
print('your cards are:')
for i in self.contents:
print(i.rank,'of',i.suit)
def makedecision(self):
hitorstand = ''
while hitorstand not in ('h' , 'H' , 's' , 'S'):
self.show()
hitorstand = input('enter "H" to hit or "S" to stand: ')
return hitorstand
def play(self):
decision = self.makedecision()
if decision in('h', 'H') :
self.deal(1,theDeck)
elif decision in ('s', 'S'):
print('ok you have chosen to stand.')
self.show()
### end of classes
theDeck = deck()
pygame.init()
cardpngs={'CLUB':{'ACE':pygame.image.load('pcp\\CLUb
\\1.png'),2:pygame.image.load('pcp\\CLUB
\\2.png'),3:pygame.image.load('pcp\\CLUB
\\3.png'),4:pygame.image.load('pcp\\CLUB},'HEART':
{'ACE':pygame.image.load('pcp\\HEART\\1.png').............}}
display_width = 800
display_height = 600
black = (0, 0 ,0)
white =(255,255, 255)
red = (255, 0, 0)
back_ground = pygame.image.load('tablesmall.png')
myhand=hand()
def start_game():
gameDisplay = pygame.display.set_mode((display_width,display_height))
pygame.display.set_caption('BlackJack')
gameDisplay.blit(back_ground,(1,1))
gameExit = False
while not gameExit:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
exit()
gameExit=True
card_xy = 1
myhand.play()
for i in myhand.contents:
gameDisplay.blit((cardpngs[i.suit][i.rank]),
(card_xy,card_xy))
card_xy+=30
pygame.display.update()
if __name__ == '__main__':
start_game()
The escape button doesn't seem to work to exit as the (if event.key == pygame.K_ESCAPE) would suggest. the plan is to also add some buttons to facilitate laying the game.
You asked 3 separate questions, unrelated each other. Each one should be a post on stack overflow.
I'm going to answer the first one, which is simpler to solve, and make only a couple of comments on the other to try to point what I think is the right way to go.
The screen is blank until the first card is drawn at which point you see 3 cards. so you never see the first two on their own. I'm going to answer the first which is more simple, and make only few comments on the others.
This is because when you call myhand=hand() you give the first two cards to the player. Then in the main loop you call myhand.play() which asks the user to hit a card or stay. You blit the cards on the screen and call pygame.display.update() for the first time after the player choice, so if the player chooses to hit, 3 cards are blit. The easiest solution is to move the call to myhand.play() after the call to pygame.display.update(). So first the cards are blit, and then the player can make a choice.
How would I go about making it so that it can be played and then followed by dealers cards showing as he plays.
You can consider the dealer as another player. Make a new class Dealer, which inherits from hand, and override the makedecision method. Instead of asking the player, put here the dealer choice.
Of course you also need to replicate this portion of code:
card_xy = 1
for i in myhand.contents:
gameDisplay.blit((cardpngs[i.suit][i.rank]),(card_xy,card_xy))
card_xy+=30
for the dealer to blit the delaer's cards, at a different place on the screen.
The GUI screen is always not responding when you click it, how to mitigate that.
This is really to broad to answer here, since your code does not show any attempt on this aspect.
The general answer is that you need to catch the MOUSEBUTTONUP and / or MOUSEBUTTONDOWN events in the event loop, obtaining the mouse position (to identify for example the card clicked by the user) and associate a proper action.

How to make the global value reset each time?

def playAgain():
b = input('Do you want to play again? y/n')
if b == ('y'):
def startGame():
startGame()
else:
print('Goodbye!')
time.sleep(1)
sys.exit()
import random
import time
import sys
global shots
shots = 0
while shots <=5:
chanceofDeath =random.randint(1,6)
input('press enter to play Russian roulette.')
if chanceofDeath ==1:
shots = shots + 1
print (shots)
print('You died.')
time.sleep(1)
playAgain()
else:
shots = shots + 1
print (shots)
print ('click')
if shots == 5:
print('You won without dying!')
time.sleep(1)
playAgain()
When I run the program, when it asks to play again or not, if you choose yes it works, but continues from the last shot. For example, if you died on the second shot and played again, instead of restarting, it starts right off at 3. How do I make the shots reset everytime?
The reason why it continues from the last shot is because you never actually set the 'shots' back to 0 this code:
import random
import time
import sys
global shots
shots = 0
is ran only once, meaning shots is never assigned back to 0.
What you want is if the user chooses to play again, the 'shots' variable should be set back to 0. You could edit your playAgain() function to return True if the user wanted to play again. For example:
def playAgain():
b = input('Do you want to play again? y/n')
if b == ('y'):
return True
else:
print('Goodbye!')
time.sleep(1)
sys.exit()
This allows you to check for whether the user wanted to play again in your main while loop and set 'shots' to 0 like this:
if playAgain():
shots = 0
Also as shots is declared outside of any functions and the while loop is the only thing using it, it does not need to be defined as a global variable.
Revised Program
def playAgain():
b = input('Do you want to play again? y/n')
if b == ('y'):
return True
else:
print('Goodbye!')
time.sleep(1)
sys.exit()
import random
import time
import sys
shots = 0
while shots <=5:
chanceofDeath =random.randint(1,6)
input('press enter to play Russian roulette.')
if chanceofDeath ==1:
shots = shots + 1
print (shots)
print('You died.')
time.sleep(1)
if playAgain():
shots = 0
else:
shots = shots + 1
print (shots)
print ('click')
if shots == 5:
print('You won without dying!')
time.sleep(1)
if playAgain():
shots = 0
Also I am unsure of what you wanted your code to do with the following:
def startGame():
startGame()
Hope this helps

Using shell commands at the same time as a while loop.

I'm making a game that is for teaching people python, so I'm controlling it by assigning variables, calling functions etc. screeps.com is a similar concept, except it's Javascript, and this is Python. The problem I'm having though, is that I have to use a while loop to refresh the screen, but commands given through the shell can't be executed while the while loop is running. For example, I want to be able to input x += 5, and have the player move to the right. So what I need is a way to refresh the screen at the same time as executing shell commands. Also, I'm using Python 3.4. Thanks in advance.
import pygame
pygame.init()
display_width = 800
display_height = 600
gameDisplay = pygame.display.set_mode((display_width,display_height))
pygame.display.set_caption('My game')
black = (0,0,0)
white = (255,255,255)
clock = pygame.time.Clock()
crashed = False
pImg = pygame.image.load('player.png')
def car(x,y):
gameDisplay.blit(pImg, (x,y))
x = (display_width * 0.45)
y = (display_height * 0.8)
x_change = 0
car_speed = 0
while not crashed:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
gameDisplay.fill(black)
car(x,y)
pygame.display.update()
clock.tick(60)
pygame.quit()
quit()
Why don't you use two threads
1) Thread - Update the display periodically based on inputs
2) Thread - Process inputs and update the shared variables
Threads are the right way to do this. Make sure you import thread at the top.
import thread
consoling = False
def threadedConsole():
global consoling
consoling = True
exec(raw_input(">>> "))
consoling = False
while True:
#Game loop
if not consoling:
thread.start_new_thread(threadedConsole,())

wxPython Panel Text Not Rendering

The problem I'm having is that when I set the static text inside a panel (see #Case 2) it only renders the first letter. The code below is a super stripped down version of my actual code but it produces an identical result:
import wx
import time
class TestInterface(wx.Frame):
testStatusFlag = 0
def __init__(self, *args, **kw):
super(TestInterface, self).__init__(*args, **kw)
self.pnl = wx.Panel(self)
self.SetSize((450, 225))
self.SetTitle('example')
self.Centre()
self.Show(True)
self.indicatorFullTest = None
self.buttonFullTest = None
self.setTestStatus(status=1)
def runTest(self, ev=None):
self.setTestStatus(status=2)
#the test is a bunch of functions that take a bunch of time to run
#they're all located in separate files but all access a single piece of hardware
#so multithreading is effectively impossible (I don't want to spend days re-writing stuff to accomodate it)
time.sleep(10)
self.setTestStatus(status=3)
return 0
def setTestStatus(self, ev=None, status=None):
#Handle the optional status argument
if (status in [1,2,3]):
self.testStatusFlag = status
#Remove any old stuff since if we're calling this function they have to get removed
if (self.indicatorFullTest != None):
self.indicatorFullTest.Hide()
if (self.buttonFullTest != None):
self.buttonFullTest.Hide()
#Case 1
if (self.testStatusFlag == 1):
self.buttonFullTest = wx.Button( self.pnl, label='Run Test', pos=(125, 100), size=(250, 50))
self.buttonFullTest.Bind(wx.EVT_BUTTON, self.runTest)
#Case 2
elif (self.testStatusFlag == 2):
self.indicatorFullTest = wx.Panel( self.pnl, pos=(125, 100), size=(250, 50))
wx.StaticText(self.indicatorFullTest, wx.ID_ANY, "Full-Board Test now in progress\nAllow up to 6 min to finish...",
style=wx.ALIGN_CENTRE_HORIZONTAL, pos=(18,7))
self.indicatorFullTest.SetBackgroundColour( 'Tan' )
self.Update()
#Case 3
elif (self.testStatusFlag == 3):
self.buttonFullTest = wx.Button( self.pnl, label='Test Complete\nPress to reset GUI',
pos=(125, 100), size=(250, 50) )
self.buttonFullTest.SetBackgroundColour( (130,255,130) )
self.buttonFullTest.Bind(wx.EVT_BUTTON, self.resetGUI)
#Resets the GUI after a test is complete
def resetGUI(self, ev=None):
self.setTestStatus(status=1) #Reset the fullTest button/indicator thing
if __name__ == '__main__':
ex = wx.App()
gui = TestInterface(None)
ex.MainLoop()
Basically, how do I make the UI fully render the text? I imagine it has something to do with not going back to wx's main loop after changing that indicator, but I feel like calling self.Update() should make that unnecessary. It may also have something to do with how I'm switching between using a button and using a panel (which is probably bad but I'm not sure how else to do it). I know that I could solve this by making my test function run in a separate thread but the problem is that the test function calls separate libraries which I literally do not have the time to re-write.
Thanks
This is a bit easier when you use wxPython's sizers because then you can just Show and Hide widgets. You can see a good example in this tutorial. I would recommend learning sizers just to make your UI more dynamic when you resize the frame.
But regardless, the answer to this conundrum is pretty simple. Instead of calling self.Update() in Case #2, you need to call wx.Yield(). I swapped that one line change in and it worked for me on Linux.

Resources