How can I detect if the user has double-clicked in pygame? - python

I know I can check if there was a left click
event.type == pygame.MOUSEBUTTONDOWN and event.button == LEFT
but how can I check if they double clicked? Also is there any way to check if the user moved the scroll wheel forward or backwards?

I'd just use the delta time value that clock.tick returns to increase a timer. In this example you have 0.5 seconds to double click otherwise the timer is reset.
import sys
import pygame as pg
pg.init()
screen = pg.display.set_mode((640, 480))
BLACK = pg.Color('black')
FONT = pg.font.Font(None, 32)
def game():
clock = pg.time.Clock()
timer = 0
dt = 0
running = True
while running:
for event in pg.event.get():
if event.type == pg.QUIT:
running = False
if event.type == pg.MOUSEBUTTONDOWN:
if event.button == 1:
if timer == 0: # First mouse click.
timer = 0.001 # Start the timer.
# Click again before 0.5 seconds to double click.
elif timer < 0.5:
print('double click')
timer = 0
# Increase timer after mouse was pressed the first time.
if timer != 0:
timer += dt
# Reset after 0.5 seconds.
if timer >= 0.5:
print('too late')
timer = 0
screen.fill(BLACK)
txt = FONT.render(str(round(timer, 2)), True, (180, 190, 40))
screen.blit(txt, (40, 40))
pg.display.flip()
# dt == time in seconds since last tick.
# / 1000 to convert milliseconds to seconds.
dt = clock.tick(30) / 1000
if __name__ == '__main__':
game()
pg.quit()
sys.exit()

I've never used pygame - but:
Detecting double clicks: at a guess, instead of processing each click immediately, apply a 50ms delay and see if you get another click event in that time. The user probably won't notice the 50ms delay.
Distinguishing between scrollwheel up/down: see the comments on this documentation page. Apparently there are five buttons defined - left, middle, right, scrollwheel-up and scrollwheel-down. That is, you can capture scrollwheel events the same way you're capturing left clicks - you just need to look for SCROLL_UP or similar instead of LEFT.
Look up the documentation to find out exactly what SCROLL_UP is called.

A very simple solution is to define a pygame.time.Clock() to keep track of the time between two consecutive MOUSEBUTTONDOWN event.
Before the main loop define:
dbclock = pygame.time.Clock()
and in the event loop controller:
if event.type == pygame.MOUSEBUTTONDOWN:
if dbclock.tick() < DOUBLECLICKTIME:
print("double click detected!")
where DOUBLECLICKTIME is the maximum time allowed (in milliseconds) between two clicks for them being considered a double click. Define it before the mainloop. For example, to allow a maximum delay of half a second between the two clicks: DOUBLECLICKTIME = 500.
In pygame is possible to create as many pygame.time.Clock() objects are needed. dbclock must be used only for this purpose (I mean, no other calls to dbclock.tick() anywhere in the main loop) or it will mess with the tracking of the time between the two clicks.
For the sake of completeness, let me add also the answer about the scroll wheel, even if other answers already covered it.
The scroll wheel emits MOUSEBUTTONDOWN and MOUSEBUTTONUP events (it's considered a button). I can be identified by the event.button parameter, which is 4 when the wheel is rolled up, and 5 when the wheel is rolled down.

Set a timer when the mouse is pressed the first time to place a userevent on the pygame event queue, and set a variable to 1 to indicate a click. When the second click occurs, check the variable and set the timer event object to off. Check if the userevent comes up on the queue as this means the timer has timed out. see this beautiful answer for more information: Move an object every few seconds in Pygame
Here is the code, replace the double_click() call with your own function call:
def run():
global clock, double_click_event, timer
double_click_event = pygame.USEREVENT + 1
timer = 0
while True:
clock.tick(60)
check_events()
frame.update()
screen.blit(frame, (0,0))
pygame.display.flip()
def check_events():
global dispatcher, double_click_event, timer
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
if timer == 0:
pygame.time.set_timer(double_click_event, 500)
timerset = True
else:
if timer == 1:
pygame.time.set_timer(double_click_event, 0)
double_click()
timerset =False
if timerset:
timer = 1
return
else:
timer = 0
return
elif event.type == double_click_event:
# timer timed out
pygame.time.set_timer(double_click_event, 0)
timer = 0
print "evt = dble click"

There doesn't appear to be a native double-click event. I'll guess you'd need to check the time between consecutive MOUSEBUTTONDOWN events.
The mouse wheel will generate pygame.MOUSEBUTTONDOWN events when rolled. The button will be set to 4 when the wheel is rolled up, and to button 5 when the wheel is rolled down

Related

Why does pygame.time.set_timer() stop working when I give it a parameter higher than 16 miliseconds?

I have a custom user event that I have assigned to the variable enemy_fire, and have it set to activate every 100 ms with pygame.time.set_timer(enemy_fire, 100). However, this does not work. I tried setting it to 10 ms: pygame.time.set_timer(enemy_fire, 10) and it worked, and I kept playing with the time parameter until I found the threshold in which it stopped working, and have found that anything higher than 16 ms does not work. I've been reading the documentation, and I'm pretty sure I'm using this function correctly. This seems like odd behavior to me, I'm not really sure why this is happening.
Here is the main part of my code:
import pygame
import gametools
import players
import enemies
from sys import exit
# initilaization
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption('Bullet hell')
clock = pygame.time.Clock()
# Player
player = players.Player()
enemy = enemies.Enemy()
# Background
back_surf = pygame.image.load('sprites/backgrounds/background.png').convert()
enemy_fire = pygame.USEREVENT + 1
# main game loop
while True:
pygame.time.set_timer(enemy_fire, 100)
# event loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
if event.type == enemy_fire:
print('fire')
enemy.attack(screen)
# display background
screen.blit(back_surf, (0, 0))
player.draw(screen)
player.update(screen)
enemy.draw(screen)
enemy.update()
enemy.collision(enemy.rect, player.bullets)
pygame.display.update()
clock.tick(60)
You have to call pygame.time.set_timer(enemy_fire, 100) before the application loop, but not in the loop. When you call pygame.time.set_timer() in the loop, it will continuously restart and never fire unless you set an interval time faster than the application loop interval. Since the application loop is limited by clock.tick(60), that's about 16 milliseconds (1000/60).
pygame.time.set_timer(enemy_fire, 100) # <-- INSERT
# main game loop
while True:
# pygame.time.set_timer(enemy_fire, 100) <-- DELETE
# event loop
for event in pygame.event.get():
# [...]

Pygame action when no joystick event

So i'm building an application that controls a mini drone and to control the drone i use DragonRise Inc. PC TWIN SHOCK Gamepad USB joystick in Pygame. What i want it to do, is go through a while loop every 70ms and check if there is a Pygame joystick event. If so than execute some code. If not, then execute some other piece of code.
What I've read online is that is done by checking if the length of the pygame.event.get() list is greater than 0. If so, there is motion, if not (else) no motion. But unfortunately this doesn't work well.
The situation is when i push the joystick axis up, it executes not only the block of code associated with pygame.JOYAXISMOTION event but also the else block of code.
Anyone know how to fix this, or have a better solution ?
import time
import pygame
done = False
deadzone = 0.1
pygame.init()
pygame.joystick.init()
joystick = pygame.joystick.Joystick(0)
joystick.init()
while not done:
events = pygame.event.get()
if len(events) > 0:
for event in events:
if event.type == pygame.JOYAXISMOTION:
if joystick.get_axis(1) < -1 * deadzone:
print('Throttle Up')
if joystick.get_axis(1) > deadzone:
print('Throttle Down')
else:
print('No Event')
time.sleep(0.07)
You don't have to evaluate the JOYAXISMOTION at all. pygame.joystick.Joystick.get_axis gets the current position of an axis. Just evaluate the position with a 70ms interval. Use pygame.time.Clock.tick instead of time.sleep:
clock = pygame.time.Clock()
while not done:
clock.tick(70)
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
axis_pos = joystick.get_axis(1)
if axis_pos < -1 * deadzone:
print('Throttle Up')
elif axis_pos > deadzone:
print('Throttle Down')
else:
print('No Event')

Python, pygame mouse position and which button is pressed

I have been trying to get my code collecting which mouse button is pressed and its position yet whenever I run the below code the pygame window freezes and the shell/code keeps outputting the starting position of the mouse. Does anybody know why this happens and more importantly how to fix it?
(For the code below I used this website https://www.pygame.org/docs/ref/mouse.html and other stack overflow answers yet they were not specific enough for my problem.)
clock = pygame.time.Clock()
# Set the height and width of the screen
screen = pygame.display.set_mode([700,400])
pygame.display.set_caption("Operation Crustacean")
while True:
clock.tick(1)
screen.fill(background_colour)
click=pygame.mouse.get_pressed()
mousex,mousey=pygame.mouse.get_pos()
print(click)
print(mousex,mousey)
pygame.display.flip()
You have to call one of the pygame.event functions regularly (for example pygame.event.pump or for event in pygame.event.get():), otherwise pygame.mouse.get_pressed (and some joystick functions) won't work correctly and the pygame window will become unresponsive after a while.
Here's a runnable example:
import pygame
pygame.init()
screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()
BG_COLOR = pygame.Color('gray12')
done = False
while not done:
# This event loop empties the event queue each frame.
for event in pygame.event.get():
# Quit by pressing the X button of the window.
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
# MOUSEBUTTONDOWN events have a pos and a button attribute
# which you can use as well. This will be printed once per
# event / mouse click.
print('In the event loop:', event.pos, event.button)
# Instead of the event loop above you could also call pygame.event.pump
# each frame to prevent the window from freezing. Comment it out to check it.
# pygame.event.pump()
click = pygame.mouse.get_pressed()
mousex, mousey = pygame.mouse.get_pos()
print(click, mousex, mousey)
screen.fill(BG_COLOR)
pygame.display.flip()
clock.tick(60) # Limit the frame rate to 60 FPS.

Create a button to control background music on and off in pygame

I want to create a button in my game that can control the background music on and off. The first click will stop the background music, and the second click can bring back the music. Now my button can control the music on and off, but I need to click multiple times to make it work, it seems that the click event is not captured everytime, here is my code:
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
if 20 + 50 > mouse_position[0] > 20 and 20 + 20 > mouse_position[1] > 20:
play_music = not play_music
if play_music:
pygame.mixer.music.unpause()
else:
pygame.mixer.music.pause()
pygame.display.flip()
clock = pygame.time.Clock()
clock.tick(15)
J0hn's answer is correct. Define a boolean variable (e.g. music_paused = False), toggle it when the user clicks on the button and call pygame.mixer.music.pause to stop the music and pygame.mixer.music.unpause to resume the playback of the music stream.
I also recommend doing the collision check in the event loop (for event in pygame.event.get():), because the button should be clicked only once per pygame.MOUSEBUTTONDOWN event. pygame.mouse.get_pressed() will keep clicking the music button as long as the mouse button is down.
import pygame as pg
pg.init()
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
BG_COLOR = pg.Color('gray12')
BLUE = pg.Color('dodgerblue1')
pg.mixer.music.load('your_music_file.ogg')
pg.mixer.music.play(-1)
button = pg.Rect(100, 150, 90, 30)
music_paused = False
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.MOUSEBUTTONDOWN:
if button.collidepoint(event.pos):
# Toggle the boolean variable.
music_paused = not music_paused
if music_paused:
pg.mixer.music.pause()
else:
pg.mixer.music.unpause()
screen.fill(BG_COLOR)
pg.draw.rect(screen, BLUE, button)
pg.display.flip()
clock.tick(60)
pg.quit()
It looks like you have a pygame.mixer.music.pause() but nothing from resume. I'm not sure how pygame's music mixer works but I'd recommend using a flag that is updated on button click
music = 0 by default, if clicked, set music = 1 then have a check if music == 1: pygame.mixer.music.pause() and if music == 0: pygame.mixer.music.unpause(). Make that check and flag change every time the button is clicked.
ya guys don't need a boolean variable only a small function
def toggleMusic():
if pygame.mixer.music.get_busy():
pygame.mixer.music.pause()
else:
pygame.mixer.music.unpause()
pygame.mixer.music.get_busy() is the boolean you need because this checks if music is playing. if music is playing it will return True and vice versa

MOUSEBUTTON events in pygame

I have made a sort of cookie clicker like game in pygame, however when I click the mouse once loads of points are added to the score. I assume this is because of the game loop, however I would like to know how to make this stop, and make it add 1 to the score for every click, no matter how long the mouse button is held down.
Here's an example that only increments the score on mouse button down events:
import pygame
pygame.init()
pygame.font.init()
clock = pygame.time.Clock()
screen = pygame.display.set_mode([320,240])
sys_font = pygame.font.SysFont(pygame.font.get_default_font(), 18)
pygame.display.set_caption("Clicker")
clicks = 0 # initialise the score counter
done = False
while not done:
#Events
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
clicks += 1
#Graphics
screen.fill(pygame.color.Color("white"))
score_txt = sys_font.render(f"Clicks: {clicks}", True, pygame.color.Color("blue"))
screen.blit(score_txt, (20, 220))
#Frame Change
pygame.display.update()
clock.tick(60)
pygame.quit()
Use 2 variables like is_mouse_clicked and was_mouse_clicked_previously.
In the initialization of the game (even before the first loop), assign False to both of them.
At the beginning of the game loop assign value of is_mouse_clicked to was_mouse_clicked_previously
Then, load the information whether the mouse button is being pressed to is_mouse_clicked variable
Then, add the point if the values of is_mouse_clicked and was_mouse_clicked_previously differ.
Option 1: Adding points inside if is_mouse_clicked and not was_mouse_clicked_previously: will increase the score right away (the moment you start pressing the button)
Option 2: Adding points inside if not is_mouse_clicked and was_mouse_clicked_previously: will increase the score a bit later (the moment you release the button)

Categories