orangedietc
orangedietc

Reputation: 152

How to detect both left and right mouse click at the same time in Pygame

I use python-2.7 and pygame to build a minesweeper. It's almost done, only one important function left to implement. When both left and right mouse buttons are pressed, it'll scan the blocks around and do something. But I couldn't figure out how to detect both clicks at the same time. On pygame's website, there is

Note, that on X11 some XServers use middle button emulation. When you click both buttons 1 and 3 at the same time a 2 button event can be emitted.

But it doesn't work out for me.

Upvotes: 2

Views: 3624

Answers (3)

The4thIceman
The4thIceman

Reputation: 3899

you can code for the scenario that is outlined in the docs (a button 2 middle mouse gets triggered on button 1 and 3 press).

mouse_pressed = pygame.mouse.get_pressed()
if (mouse_pressed[0] and mouse_pressed[2]) or mouse_pressed[1]:
    print("left and right clicked")

keep in mind, using pygame.mouse.get_pressed() will often trigger very fast, meaning any code that you have in the if statement will happen a few times before you can release the mouse key. This is because get_pressed() returns the state of all mouse buttons at all times. This method is good if you want to track buttons that are being held down.

I would code the solution into pygame.event.get(). This will only trigger when an event happens, and therefore only once:

left_mouse_down = False
right_mouse_down = False
while True:
    for event in pygame.event.get():
        if event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 1:
                left_mouse_down = True
                if right_mouse_down:
                    print("perform action")
                else:
                    print("action for single left click")
            if event.button == 3:
                right_mouse_down = True
                if left_mouse_down:
                    print("perform action")
                else:
                    print("action for single right click")
        elif event.type == pygame.MOUSEBUTTONUP:
            if event.button == 1:
                left_mouse_down = False
            if event.button == 3:
                 right_mouse_down = False

UPDATE - Simulate a third mouse click on a two button mouse

the above code works but only if you accept the fact that the single right or left action will trigger as well, but before the combo(left/right) action. If this works for your game design, have at it. If this is not acceptable, then the following will work (although a little more in depth):

left_mouse_down = False
right_mouse_down = False
left_click_frame = 0  # keeps track of how long the left button has been down
right_click_frame = 0  # keeps track of how long the right button has been down
left_right_action_once = True  # perform the combo action only once
left_action_once = True  # perform the left action only once
right_action_once = True  # perform the right action only once
max_frame = 2  # The frames to wait to see if the other mouse button is pressed (can be tinkered with)
performing_left_right_action = False  # prevents the off chance one of the single button actions running on top of the combo action
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

    mouse_pressed = pygame.mouse.get_pressed()
    if mouse_pressed[0]:  # left click
        left_click_frame += 1
        left_mouse_down = True
    else:
        left_action_once = True
        left_mouse_down = False
        left_click_frame = 0

    if mouse_pressed[2]:  # right click
        right_click_frame += 1
        right_mouse_down = True
    else:
        right_action_once = True
        right_mouse_down = False
        right_click_frame = 0

    if not mouse_pressed[0] and not mouse_pressed[2]:
        left_right_action_once = True
        performing_left_right_action = False

    if left_mouse_down and right_mouse_down and abs(left_click_frame - right_click_frame) <= max_frame:
        if left_right_action_once:
            print("performing right/left action")
            left_right_action_once = False
            performing_left_right_action = True
    elif left_mouse_down and left_click_frame > max_frame and not performing_left_right_action and not right_mouse_down:
        if left_action_once:
            print("perform single left click action")
            left_action_once = False
    elif right_mouse_down and right_click_frame > max_frame and not performing_left_right_action and not left_mouse_down:
        if right_action_once:
            print("perform single right click action")
            right_action_once = False

The hard part about this problem is only one event can happen at a time so you have to have a way of knowing the left click was pressed, not evaluate it until you are sure right click will also not be pressed. You can accomplish this by keeping track of the frames and if the left and right happen within a certain number of frames (max_frames), then you have a right/left click. Otherwise you have either a left or right.

the action_once variables are important because since pygame.mouse.get_pressed() checks the state of each mouse button every time through the game loop, you need a flag to prevent your actions from running more than once while the button is held down (even during the short duration of a click). The game loop is faster than a mouse click.

So all three scenarios are covered: left single, right single, left right combo. In other words it simulates a "third" mouse click on a two button mouse....Although the solution is rather clunky

Upvotes: 4

PurpleJo
PurpleJo

Reputation: 327

You should test values of pygame.mouse.get_pressed()[0] (left mouse button) and pygame.mouse.get_pressed()[2] (rigth mouse button) at the same time.

http://www.pygame.org/docs/ref/mouse.html#pygame.mouse.get_pressed

This test should be at the same level of the pygame.event.get() for loop and not included in this loop. You can test this script to see what I mean:

import pygame
from pygame.locals import *
pygame.init()
screen = pygame.display.set_mode((800, 500), RESIZABLE)
running = True
while(running):
    mousebuttons = pygame.mouse.get_pressed()
    if mousebuttons[0] == 1 and mousebuttons[2] == 1:
        print("Both left and rigth mouse buttons are pressed.")
    for event in pygame.event.get():
        if event.type == QUIT:
            running = False

Upvotes: 0

Nick Jarvis
Nick Jarvis

Reputation: 131

If you only want your code to execute once after pressing both buttons just set right_mouse_down and left_mouse_down to False after you run your code.

left_mouse_down = False
right_mouse_down = False
while True:
    for event in pygame.event.get():
        if event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 1:
                left_mouse_down = True
                if right_mouse_down:
                    print("perform action")
                    right_mouse_down = False
                    left_mouse_down = True
            if event.button == 3:
                right_mouse_down = False
                if left_mouse_down:
                    print("perform action")
                    right_mouse_down = False
                    left_mouse_down = False
        elif event.type == pygame.MOUSEBUTTONUP:
            if event.button == 1:
                left_mouse_down = False
            if event.button == 3:
                 right_mouse_down = False

Now this code will never run again until you remove and reclick both mouse buttons. I would recommend using pygame.mouse.get_pressed() to check the state of the buttons at any point, so that if you only want the code to happen the first time you can set a flag.

done_the_thing = False
while not done: # Main game loop
    buttons = pygame.mouse.get_pressed()

    if buttons[0] and buttons[2] and not done_the_thing:
        do_the_thing() # This is just your function
        done_the_thing = True

    if not buttons[0] and not buttons[2]:
        done_the_thing = False

This will allow more flexibility so if you want to add in more mouse events that do occur every frame or change what you are currently doing, you can keep all of them together and clean.

Upvotes: 0

Related Questions