I was hoping someone might have some insight on how to stop a script from continuing to repeat if a button is held (or in my case pressed longer than a second)?
Basically i've a button setup on the breadboard, and I have it coded to play an audio file when the button is pressed. This works, however if the button isn't very quickly tapped, then the audio will repeat itself until button is fully released. Also if the button is pressed and held, the audio file will just repeat indefinitely.
I've recorded a quick recording to demonstrate the issue if its helpful, here: https://streamable.com/esvoy6
I should also note that I am very new to python (coding in general actually), so its most likely something simple that I just haven't been able to find yet. I am using gpiozero for my library.
Any help or insight is greatly appreciated!
Here is what my code looks like right now:
from gpiozero import LED, Button
import vlc
import time
import sys
def sleep_minute(minutes):
sleep(minutes * 60)
# GPIO Pins of Green LED
greenLight = LED(17)
greenButton = Button(27)
# Green Button Pressed Definition
def green_btn_pressed():
print("Green Button Pressed")
greenButton.when_pressed = greenLight.on
greenButton.when_released = greenLight.on
# Executed Script
while True:
if greenButton.is_pressed:
green_btn_pressed()
time.sleep(.1)
print("Game Audio Start")
p = vlc.MediaPlayer("/home/pi/Desktop/10 Second Countdown.mp3")
p.play()
So from a brief look at it, it seems that 'time.sleep(.1)' is not doing what you are expecting. Ie. it is obviously interrupted by button presses. This is not abnormal behaviour as button presses on Ardiuno and raspPi (guessing here) would be processed as interrupts.
The script itself does not contain any prevention from double pressing or press and hold etc.
Have you put in any debug lines to see what is executing when you press the button?
I would start there and make adjustments based on what you are seeing.
I am not familiar with this gpiozero, so I can't give any insight about what it may be doing, but looking at the code and given the issue you are having, I would start with some debug lines in both functions to confirm what is happening.
Thinking about it for a minute though, could you not just change the check to 'if greenButton.is_released:'? As then you know the button has already been pressed, and the amount of time it is held in for becomes irrelevant. May also want to put in a check for if the file is already playing to stop it and start it again, or ignore and continue playing (if that is the desired behaviour).
Further suggestions:
For this section of code:
# Executed Script
while True:
if greenButton.is_pressed:
green_btn_pressed()
time.sleep(.1)
print("Game Audio Start")
p = vlc.MediaPlayer("/home/pi/Desktop/10 Second Countdown.mp3")
p.play()
You want to change this to something along these lines:
alreadyPlaying = 0
# Executed Script
while True:
if greenButton.is_pressed:
green_btn_pressed()
#Check if already playing file.
if alreadyPlaying == 1:
# Do check to see if file is still playing (google this, not sure off the top of head how to do this easiest).
# If file still playing do nothing,
#else set 'alreadyPlaying' back to '0'
break
#Check if already playing file.
if alreadyPlaying == 0:
time.sleep(.1)
print("Game Audio Start")
p = vlc.MediaPlayer("/home/pi/Desktop/10 Second Countdown.mp3")
p.play()
alreadyPlaying = 1
Hopefully you get the idea of what I am saying. Best of luck!
i think you have to write something like this in your loop:
import time, vlc
def Sound(sound):
vlc_instance = vlc.Instance()
player = vlc_instance.media_player_new()
media = vlc_instance.media_new(sound)
player.set_media(media)
player.play()
time.sleep(1.5)
duration = player.get_length() / 1000
time.sleep(duration)
I have some code that is kinda funky. Basically, the variable gameStart is in a variable that uses the function, cursorOver, which finds and detects where and if the mouse button is and if it is pressed. I have 3 buttons and I want each button to become larger when the cursor is over the button. The first button implementation works. However, if I try to add another button, the button becomes enlarged, however, the button starts flickering.
window.blit(background,(0,0))
window.blit(title,(175,200))
pygame.draw.rect(window,PURPLE,(50,400,200,100),0)
pygame.draw.rect(window,PURPLE,(300,400,200,100),0)
pygame.draw.rect(window,PURPLE,(550,400,200,100),0)
close()
mouseX,mouseY = pygame.mouse.get_pos()
mouseClick = pygame.mouse.get_pressed()[0]
gameStartC = cursorOver(50,400,200,100,mouseX,mouseY)
instructionStartC = cursorOver(300,400,200,100,mouseX,mouseY)
objectiveStartC = cursorOver(550,400,200,100,mouseX,mouseY)
nextStartC = cursorOver(580,250,175,100,mouseX,mouseY)
if gameStartC == True:
while True:
pygame.draw.rect(window,PURPLE,(25,375,250,150),0)
pygame.display.update()
break
else:
pygame.draw.rect(window,PURPLE,(50,400,200,100),0)
pygame.display.update()
#this is the part where the code becomes glitchy
if instructionStartC == True:
while True:
pygame.draw.rect(window,PURPLE,(275,375,250,150),0)
pygame.display.update()
break
else:
pygame.draw.rect(window,PURPLE,(300,400,200,100),0)
pygame.display.update()
It's simply because you call pygame.display.update() multiple times.
You should create a standard game loop that typically does these three things:
handle input
update state
draw to screen
and then repeats.
In the 'draw to screen'-step, you draw all your sprites/rects/whatever to the screen surface, and then eventually call pygame.display.update() once at the end.
Calling pygame.display.update() multiple times, not clearing the screen between iterations of the loop and creating multiple unnecessary event loops are common beginner mistakes that lead to those kind of glitches IMHO.
So in your case, the code should probably look more like this:
if gameStartC:
pygame.draw.rect(window,PURPLE,(25,375,250,150),0)
else:
pygame.draw.rect(window,PURPLE,(50,400,200,100),0)
if instructionStartC:
pygame.draw.rect(window,PURPLE,(275,375,250,150),0)
else:
pygame.draw.rect(window,PURPLE,(300,400,200,100),0)
pygame.display.update()
I don't know what you expected the while-loops to do, and maybe you should use pygames Rect and Sprite classes. It will make your life easier.
I have been trying to blit an image to my screen using the mouse position. This is my code.
#Placing the farm
if farm:
if pygame.mouse.get_pressed()[0]:
hi = True
if hi:
screen.blit(farm_image,mouse_pos)
So basically I want it to blit an image to screen only once at the mouse_position when the mouse is clicked., however it displays the image not once but moves with the mouse_pos. What would I do to make the image only be displayed once at the mouse_position. I do have a main loop before this.
What I have tried is setting hi to False after the image is blit, but that erases the image latter. Thank you for any answers you can provide!
Building on PythonMaster's answer something like that might work and ensure the farm remains blitted at every frame in case you refresh all your screen with a background every time:
#Placing the farm
if farm:
if pygame.mouse.get_pressed()[0] == False: #resets the first_click to False everytime the button is released
first_click = False
if pygame.mouse.get_pressed()[0]:
if first_click == False:
first_click = True
else:
pass
if first_click == True: #set's the farm position it will keep updating the position of the farm as you drag the mouse, if you do not want that set first_click to False here.
farm_pos = mouse_pos
try: #using try here, prevents errors when farm_pos is not yet defined
screen.blit(farm_image,farm_pos)
except:
print 'farm position not defined yet, click to place'
You could try using another variable to check to see if it was your first click or not:
if pygame.mouse.get_pressed()[0]:
if first_click == False:
hi = true
first_click = True
else:
pass
Make sure to define first_click before you use it. This code uses first_click to see if you clicked once already. Its starting value is False and changes to True once the mouse has clicked. This also changes hi into True while disallowing a second click as first_click is no longer False. This leads to the else and pass statements, which do practically nothing.
You can also use event checking instead of state checking when you only want a single click. This is especially useful because it means you won't miss a click (which might happen if the user clicks and releases the mouse at a moment when your code isn't checking for user input). Pygame has the events MOUSEBUTTONDOWN and MOUSEBUTTONUP which both give you the position of the mouse and the button that was clicked.
How your incorporate this into your code depends on the rest of your code, e.g. if you use the pygame event queue somewhere else already. Small example:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
if farm:
screen.blit(farm_image,(event.pos[0], event.pos[1]))
You may use a variable to keep track of the state once the farm has been placed as suggested by PythonMaster and Sorade, or better yet, store the position and visible state of the farm in a farm object (Sprite) and simply blit it to its position whenever its state is set to visible.
I'm writing a Pygame program that takes user input to write music. It waits for a specific event (ie keyboard or mouse input) and immediately analyzes the input without the user pressing enter with each entry.
while (running == 1):
for event in pygame.event.get():
# If event is a keypress
if(event.type == pygame.KEYDOWN):
key = pygame.key.get_pressed() # get_pressed returns an boolean array
# of every key showing pressed or not
# Find which key is pressed
for x in range(len(key)):
if(key[x] == 1):
break
# If a number key is pressed (0-9)
if(x >= 48 and x <=57):
# Set the octave to keypress
gui.setOctave(x-48) # gui is an instance of a class
# that controlls pygame display
The only way I know to do this is using the infinite while loop. This, however, takes up nearly all CPU power to run. Is there another efficient way to go about this without using the loop?
You could use event = pygame.event.wait() to wait for an event.
I know that this is a vague question, but I was hoping to get some help. I know VBA pretty well, and have been able to accomplish some simple tasks in python as well as the statistical programming language in R.
What I am looking to do is create a simple application that lets me capture data, some of which is captured from the keyboard. Every time there is a keystroke, I wanted to create a new record in my dataset.
For some context, think about creating a simple interface that lets me track the location (and duration) of the puck in an NHL hockey game.
I am not really a programmer, but know just enough to get in trouble and am not really sure where to get started. I simply am looking for some thoughts on a very basic (non-commercial) solution.
Many thanks in advance.
EDIT: I want to capture how long the puck is each zone. I plan on using the directional keys left/right to "follow" the puck from zone to each. Each time the puck changes to a zone, I want to "close" the active record and start a new one. The start and end times will let me calculate how long the puck was in the zone. I also need a way to stop the creation of a new record for things like faceoffs, tv time outs, and end of period. I was planning on using the spacebar. My thought is that if I do this correctly, when I follow along, the times recorded should match up with what is posted on the game clock found on tv. Yes, this is a crazy idea.
If you choose to program in Python:
You could use the pygame package to easily capture keyboard events. The library was built to write games, but would probably give you the functionality that you are looking for with keydown/keyup events. It also handles mouse events and (since it is intended for games) has the ability to do graphics/text. The documentation is really good and it is cross platform. A possible downside is that you have to have a "screen" and it has to have focus. Here is a small example:
import pygame
def main():
"""
Pygame Example
"""
pygame.init()
screen = pygame.display.set_mode((200, 200))
app_running = True
while app_running:
# Get all key/mouse events from system.
events = pygame.event.get()
# Loop thru each event...
for e in events:
# Handle when the program is killed.
if e.type == pygame.QUIT:
app_running = False
break
# Handle key events.
elif e.type == pygame.KEYDOWN:
# Exit if escape is pressed.
if e.key == pygame.K_ESCAPE:
app_running = False
# Do something when the right arrow
# is pressed.
elif e.key == pygame.K_RIGHT:
print "right arrow pressed"
# Do something when the left arrow
# is pressed.
elif e.key == pygame.K_LEFT:
print "left arrow pressed"
# and so on ...
# Fill the screen to blank it.
#screen.fill(mycolor)
# Write someting to the screen to display.
#screen.blit(some_image, some_position)
# Flip to display.
#screen.flip()
pygame.quit()
if __name__ == '__main__':
main()
If you are using a version of Windows you could use the msvcrt library but the event handling is not as nice as pygame: instead of events, you have to deal with raw keyboard output and it is a little less intuitive. Here is a small code snippet from Robert Gillies on ActiveState:
import msvcrt
def funkeypress():
"""
Waits for the user to press any key including function keys. Returns
the ascii code for the key or the scancode for the function key.
"""
while 1:
if msvcrt.kbhit(): # Key pressed?
a = ord(msvcrt.getch()) # get first byte of keyscan code
if a == 0 or a == 224: # is it a function key?
b = ord(msvcrt.getch()) # get next byte of key scan code
x = a + (b*256) # cook it.
return x # return cooked scancode
else:
return a # else return ascii code
Look at scan() for keyboard input in R. And you didn't ask about mouse input, but consider locator() for that.
Put it a loop if you want the output immediately.
Is it necessary that you program it yourself? There is a free program called jwatcher Designed for scoring animal behavior in ethological studies. It seems like that would be well-suited to your task.