Detecting both mouse buttons held down - python

I am new to python and I found a code that detects mouse buttons when held down and released, but i want "x" to turn True when both of the buttons held down, how can I do this
# This function will be called when any key of mouse is pressed
def on_click(*args):
# see what argument is passed.
print(args)
if args[-1]:
# Do something when the mouse key is pressed.
print('The "{}" mouse key has held down'.format(args[-2].name))
elif not args[-1]:
# Do something when the mouse key is released.
print('The "{}" mouse key is released'.format(args[-2].name))
# Open Listener for mouse key presses
with Listener(on_click=on_click) as listener:
# Listen to the mouse key presses
listener.join()

To detect if both buttons are held down simultaneously, three variables will be required (initialise these at the start of your program, outside of the on_click function):
global leftPressed, rightPressed, bothPressed
leftPressed = False
rightPressed = False
bothPressed = False
*note the variables are global here, as multiple versions of on_click will be accessing and modifying the variables
Then, in the first if statement (when a mouse button has been pressed):
if args[-2].name == "left":
leftPressed = True
elif args[-2].name == "right":
rightPressed = True
if leftPressed and rightPressed:
# if both left and right are pressed
bothPressed = True
And in the second if statement (when a mouse button has been released)
if args[-2].name == "left":
leftPressed = False
elif args[-2].name == "right":
rightPressed = False
# as one key has been released, both are no longer pressed
bothPressed = False
print(bothPressed)
Finally, to access the global variables from within the function on_click, place this line at the start of the function:
global leftPressed, rightPressed, bothPressed
See a full version of the code here:
https://pastebin.com/nv9ddqMM

Related

Tkinter mouse and key press at the same time

I am trying to convert from pygame to tkinter as it seems to be much better for what I want to do, although I have hit a bit of a wall. I need to be able to call a function when both a certain key and mouse button are pressed. In pygame it was as simple as the following.
while not done:
for event in pygame.event.get():
keys = pygame.key.get_pressed()
mouse = pygame.mouse.get_pressed()
if event.type == pygame.QUIT:
done = True
if mouse[0]:
if keys[pygame.K_s]:
pos = pygame.mouse.get_pos()
// function
I know in tkinter you can do c.bind("<Button-1>", function) to register mouse clicks and c.bind("e", function) to register key presses but I'm not sure how to get both at the same time as the button event does not pass through key presses
Not sure if there is an official method to bind both Button-1 and Key, but maybe you can work around it.
import tkinter as tk
root = tk.Tk()
tk.Label(root,text="Hi I'm a label").pack()
class Bindings:
def __init__(self):
self.but_1 = False
root.bind("<Button-1>", self.mouse_clicked)
root.bind("<e>", self.e_clicked)
root.bind("<ButtonRelease-1>",self.mouse_release)
def mouse_release(self,event):
self.but_1 = False
def mouse_clicked(self,event):
self.but_1 = True
print ("Mouse button 1 clicked")
def e_clicked(self,event):
if self.but_1:
print ("Both keys clicked")
self.but_1 = False
else:
print ("Key E pressed")
Bindings()
root.mainloop()

how do you check if two keys are pressed consecutively in tkinter?

I am trying to create a tkinter app that checks if two keys are pressed consecutively. For example, if the user presses ":" and then the enter key right after. Is there a way to do this using key events in tkinter?
I have tried doing the following, but it does not work.
if key_pressed == ':':
if key_pressed == '<Enter>':
print("ok")
One way is to save a state for each keypress which stores the last key pressed:
from tkinter import *
root = Tk()
last_key = None
def keypress_handler(event):
global last_key
key_pressed = event.keysym
print(key_pressed)
if key_pressed == 'b':
if last_key == 'a':
print('Pressed first "a" and then "b"')
last_key = key_pressed
root.bind('<KeyPress>', keypress_handler)
root.mainloop()
If you are going to use modifiers like shift, ctrl or alt you'll have to filter them out because they also generate keypress events.

Making multiple 'game screens' using Pygame

How would you be able to create multiple screens using Pygame and events performed by the user?
For example, if I had a menu screen with 2 buttons ('Start' and 'Exit') and the user clicked on 'Start', a new screen would appear in the same window with whatever is next in the game. From that screen, a user could click on another button and move on to another screen/ return to the menu, etc.
Make classes for each one, where each class is a subclass of pygame.Surface, of the same size as the display. Then you could have 3 variable TITLESCREEN, PLAYING, HIGHSCORES and change them on key press. Then you would be able to blit the correct screen to the display.
It is same as with any program/language, you simply trigger current loop.
Consider this example:
FPS = 25
MainLoop = True
Loop1 = True # Start app in Loop1
Loop2 = False # Loop2 is idle
while MainLoop :
clock.tick(FPS)
pygame.event.pump()
keypress = pygame.key.get_pressed()
keypress_dn = tuple(x > y for x,y in zip(keypress, keypress_old))
keypress_old = keypress
if Loop1:
if keypress_dn [pygame.K_ESCAPE] :
Loop1 = False
MainLoop = False
if keypress_dn [pygame.K_2] : # goto Loop2
Loop1 = False
Loop2 = True
...
if Loop2:
if keypress_dn [pygame.K_ESCAPE] :
Loop2 = False
MainLoop = False
if keypress_dn [pygame.K_1] : # goto Loop1
Loop2 = False
Loop1 = True
...
pygame.display.flip( )
pygame.quit( )
So you add your rendering and check keypresses in corresponding loop. Here you press "2" to go to loop2 and press "1" to go to loop 1. "Escape" will quit both loops and main loop.
What I did for this was have a literal main loop that checks to see what "Screen" the player wants to go to. Like if they press the exit button, it returns where to go next. The main loop then runs a the new script for the new screen.

pygame.key.set_repeat for Joystick

I'm building a menu using pygame and I want to make it navigable using a specific gamepad. Ideally I want to be able to press and hold *down" on the D-pad repeatedly, or get something like on a keyboard where the first button press has a delay before repeatedly entering the same character (seemingly).
I'm trying to emulate the pygame.key.set_repeat(...) function for a Joystick. my approach so far has been
pygame.time.set_timer(pygame.USEREVENT, 10)
DELAY_TIME = 0.250 #ms
y_delay = True
while not done:
for event in pygame.event.get():
y_axis = gamepad.get_axis(1)
if y_axis > 0.5: # pushing down
main_menu.move_down()
redraw() #redraw everything on the surface before sleeping
if y_delay:
time.sleep(DELAY_TIME)
y_delay = False #don't delay the next time the y axis is used
elif y_axis < -0.5: #pushing up
# repetitive I know, but I'm still working on it
main_menu.move_up()
redraw()
if y_delay:
time.sleep(DELAY_TIME)
y_delay = False
else:
y_delay = True # delay the next time
my issue is if someone taps up or down faster than DELAY_TIME they are limited to the DELAY_TIME before they can move again. Also if someone releases and depresses the up/down button within the time.sleep interval, python never sees that it was released at all and doesn't allow for a delay.
Maybe there's a way to do this using events or mapping the joystick to keys somehow? qjoypad doesn't cut it for me, and joy2keys is trash. I would need to do the mapping within the python program.
Sleep causes the program to halt execution, so it's not a viable option. You can also do this without using set_timer and events. I did it using a couple of flags and pygame.time's get_ticks.
import pygame
from pygame.locals import *
def main():
pygame.init()
pygame.display.set_mode((480, 360))
gamepad = pygame.joystick.Joystick(0)
gamepad.init()
delay = 1000
neutral = True
pressed = 0
last_update = pygame.time.get_ticks()
while True:
for event in pygame.event.get():
if event.type == QUIT:
return
move = False
if gamepad.get_axis(1) == 0:
neutral = True
pressed = 0
else:
if neutral:
move = True
neutral = False
else:
pressed += pygame.time.get_ticks() - last_update
if pressed > delay:
move = True
pressed -= delay
if move:
print "move"
last_update = pygame.time.get_ticks()
if __name__ == "__main__":
main()
pygame.quit()
When get_axis indicates no motion, the neutral flag is set, and the pressed timer is reset, causing the move flag to remain unset. When the neutral flag is unset, if it's newly set, the move flag is set. If it's not newly set, the pressed timer increases, and move is set only if the pressed timer is greater than delay.

Menu problem with PyGame MOUSEBUTTONDOWN event

Yes, that title wasn't worded very properly at all.
Ok, here's what we've got - a Python program using the pyGame library, and we're making a game. We start in a menu environment main.py. When the user clicks on one of the menu buttons, an action is performed. The program checks for clicks on menu items using the following code:
if event.type == pygame.MOUSEBUTTONDOWN:
mousePos = pygame.mouse.get_pos()
for item in buttons: # For each button
X = item.getXPos() # Check if the mouse click was...
Y = item.getYPos() # ...inside the button
if X[0] < mousePos[0] < X[1] and Y[0] < mousePos[1] < Y [1]:
# If it was
item.action(screen) # Do something
When the user clicks on the "Play Game" button, it opens a sub-module, playGame.py. In this sub-module is another pyGame loop etc.
Part of the game is to hold the left mouse button to 'grow' circles out of the current position (It's a puzzle game, and it makes sense in context). Here is my code for doing this:
mouseIsDown == False
r = 10
circleCentre = (0,0)
[...other code...]
if mouseIsDown == True:
# This grown the circle's radius by 1 each frame, and redraws the circle
pygame.draw.circle(screen, setColour(currentColourID), circleCentre, r, 2)
r += 1
for event in pygame.event.get():
if event.type == pygame.QUIT:
runningLevel = False
elif event.type == pygame.MOUSEBUTTONDOWN:
# User has pressed mouse button, wants to draw new circle
circleCentre = pygame.mouse.get_pos()
mouseIsDown = True
elif event.type == pygame.MOUSEBUTTONUP:
# Stop drawing the circle and store it in the circles list
mouseIsDown = False
newCircle = Circle(circleCentre, r, currentColourID)
circles.append(newCircle)
circleCount += 1
r = 10 # Reset radius
The problem I have is that the user's left mouse click from the main menu is persisting into the playGame.py module, and causing it to create and store a new circle, of radius 10 and at position (0,0). Both of these are the default values.
This only happens for the one frame after the menu.
Is there any way to prevent this, or is it a flaw in my code?
All help greatly appreciated, as always. If you need more code or explanation of these snippets, let me know.
If you'd like the full code, it's on GitHub.
You could use MOUSEBUTTONUP instead of MOUSBUTTONDOWN in the menu.
Does adding pygame.event.clear() to the top of Play fix it?

Categories