2

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.

orangedietc
  • 142
  • 3
  • 12
  • You detect a right click while the left button is still down and the other way arround. You will have to save the state of the button on every down / up to do that. – Klaus D. Mar 16 '17 at 05:36
  • Is it a single click of both right and left? or do you hold them down? – The4thIceman Mar 16 '17 at 08:53

3 Answers3

4

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

The4thIceman
  • 3,709
  • 2
  • 29
  • 36
  • Thank you a lot. I want to detect a single click of both left and right instead of holding down. It seems your code could work perfectly to detect this scenario. But I also hope to perform other actions while there is only left or right click. I feel these can't be told apart from both click scenario right? – orangedietc Mar 16 '17 at 15:05
  • are you referring to something like a keystroke? or just a single left click? – The4thIceman Mar 16 '17 at 15:06
  • @orangedietc I edited my code to account for single left and single right click actions. for reading keystrokes you would just do: elif event.type == pygame.KEYDOWN – The4thIceman Mar 16 '17 at 15:11
  • I'm referring to mouse clicks. I don't think this would work. If I click both left and right at the same time, 'for event in pygame.event.get():' only accept one event at a time(say, left click first), and it will need to enter the next loop to detect a right click and perform a both click action, but what it'll really do is trigger a single left click action, right? – orangedietc Mar 16 '17 at 15:28
  • yes, i see what you are saying now. The code does not know your intent to press the other mouse button so will default to the single click – The4thIceman Mar 16 '17 at 15:30
  • @orangedietc I wrote up a new solution, check the update – The4thIceman Mar 16 '17 at 17:38
  • Thanks! I'm a bit confused about the role of `action_once` but I have to go to class right now. I'll think about it later. Thank you again. – orangedietc Mar 16 '17 at 18:40
  • @orangedietc I will update my answer to explain that – The4thIceman Mar 16 '17 at 18:43
0

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
PurpleJo
  • 187
  • 1
  • 13
  • I use 'pygame.event.get()' to accept one event at a time because if not, I can't make it work that one right click places a flag and another cancels the flag. I assume in this case, there is always only one boolean value would be true in 'pygame.mouse.get_pressed()' ? – orangedietc Mar 16 '17 at 15:14
  • You should place the `pygame.mouse.get_pressed()` part outside of the `pygame.event.get()` for loop... I am working at my office for the moment, let give me some time this evening and I will show you some simple code to do what you want by editing my answer above ;) – PurpleJo Mar 16 '17 at 16:09
  • Here I am with more details. The script works perfectly at home. Do not forget to validate this answer if i make your day. Enjoy ! – PurpleJo Mar 16 '17 at 18:18
0

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.

Nick Jarvis
  • 131
  • 5