Make loop run only when holding right click - python

I'm trying to make a program that randomly moves the mouse only when the right mouse button is held to prank a friend.
I found some code online to detect right-clicks with Win32 API. When I added my own while loop, it didn't stop after it started. I tried adding an if statement with break but nothing changed.
import win32api
import pyautogui
import random
state = win32api.GetKeyState(0x02) # Right button down = 1. Button up = -128
while True:
pressed = win32api.GetKeyState(0x02)
if pressed != state: # Button state changed
state = pressed
print(pressed)
if pressed < 0:
print('Right Button Pressed')
while pressed < 0: # If the right mouse button is pressed, move the mouse randomly.
pyautogui.moveRel(random.randint(-10, 10), random.randint(-10, 10))
if (pressed > 0):
break
else:
print('Right Button Released')

You can try replacing your main while loop with this one that uses the assignment (walrus) operator := to continuously check the button state
has_right_clicked = False # flag if the button has been pressed yet
while state := win32api.GetKeyState(0x02) # Right button down = 1. Button up = -128:
if state == 1: # If the right mouse button is pressed, move the mouse randomly.
has_right_clicked = True # user has right-clicked, set the flag
pyautogui.moveRel(random.randint(-10, 10), random.randint(-10, 10))
if has_right_clicked: # this will be ignored until the flag is set
break # if the user lets go of the button
The loop will start automatically because both 1 and 128 evaluate to True, which is why we need the extra has_right_clicked flag to break the loop! The caveat here, however, is that this loop will only run once. You should probably wrap this in a function that's bound to right-click so it can be called whenever the user right-clicks the mouse.

Waiting for input with GetKeyState is never the correct solution for anything.
To capture global mouse events you should probably use a low-level mouse hook in this case. I don't know if it is possible in Python but I assume there is a way...

Related

Pygame seems to skip an if statement on whether or not I click again

In making my chess game function for moving pieces, I've created a system that should:
Look for a click
See if a piece was clicked on
Determine what piece was clicked on
Click on a square to move to
Move said piece to said square
I've written the follow code for this:
if event.type == pygame.MOUSEBUTTONDOWN and moving == False:
mousepos = pygame.mouse.get_pos()
roundedmouse1 = rounddown80(mousepos[0]) #function to found out what square was clicked
roundedmouse2 = rounddown80(mousepos[1]) #function to found out what square was clicked
mousecoords = [roundedmouse1,roundedmouse2]
for key,value in piecepositionsdict.items():
if int(value[0]) == int(mousecoords[0]) and int(value[1]) == int(mousecoords[1]):
x = coordstosquaredict[str(mousecoords)]
print("You have clicked",whatpiece(x),"on square",x)
print("Click a square to move the piece to:")
moving = True
time.sleep(0.5)
#this should be where the program stops until it gets another click
if event.type == pygame.MOUSEBUTTONDOWN and moving == True:
mousepos2 = pygame.mouse.get_pos()
roundedmouse21 = rounddown80(mousepos2[0])
roundedmouse22 = rounddown80(mousepos2[1])
mousecoords2 = [roundedmouse21, roundedmouse22]
print(mousecoords2)
print(piecepositionsdict[whatpiece(x)+"pos"])
piecepositionsdict[whatpiece(x)+"pos"] = mousecoords2
print(piecepositionsdict[whatpiece(x) + "pos"])
However, the program goes right past the second click check, as shown by how it prints out mousecoords 2 etc. when I have only clicked once.
What have I done wrong to cause this error?
Games are event driven applications. In event driven application you have a main loop checking for all events, and code executed when an event is detected. The starting point of your logic is always the event.
In your case the event is the clicking of the button. You have to check for the click only once in your code (in the main loop) and then determine the action the code has to do. If different actions may be triggered by the same event, you need some additional logic or flags to determine which action should be executed.
In your case you have an event (mouse click) and two possible actions, check what pieces has been clicked, or move the piece.
So the code should be designed like this:
def check_and_grab(position):
# code here
def move_piece(position):
# code here
piece_grabbed = None
while True:
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTODOWN:
if piece_grabbed is None:
check_and_grab(event.pos)
else:
move_piece(event.pos)
piece_grabbed is a variable storing the piece grabbed: if is None, no piece has been grabbed.
check_and_grab(position) should check if there is a piece at the clicked position and, if so, set piece_grabbed to that piece. It implements your points 2 and 3.
move_piece(position) should move the grabbed piece at the new position and after that, set again grabbed_piece = None. It implements your point 5.
Your code will run in a single frame and the code will only register one click during that frame. You need to have a function thats gets called everytime you click on the screen.
See my sort of example bellow:
#Holder for the piece you have selected
currentPiece = piece
##Mouse click event/function##
if currentPiece == None:
getPiece()
else:
movePiece()
##Get piece function##
def getPiece():
#Your function here which sets
#currentPiece to what you selected.
##Move function##
def movePiece():
#Moves the currentPieece to the selected spot

Python check if Left Mouse Button is being held?

I'm pretty new to Python and I'd like to make a kind of an Autoclicker, which keeps clicking every 0.1 seconds when my left mouse button is held down.
My Problem is, that when I run my script, my mouse instantly starts clicking. What should I do?:
import win32api
import time
from pynput.mouse import Button, Controller
mouse = Controller()
while True:
if win32api.GetAsyncKeyState(0x01):
mouse.click(Button.left, 1)
time.sleep(0.1)
else:
pass
Thanks
My Problem is, that when I run my script, my mouse instantly starts clicking. What should I do?
If the function succeeds, the return value specifies whether the key
was pressed since the last call to GetAsyncKeyState, and whether the
key is currently up or down. If the most significant bit is set, the
key is down, and if the least significant bit is set, the key was
pressed after the previous call to GetAsyncKeyState.
You should use win32api.GetAsyncKeyState(0x01)&0x8000 instead.
Now, the only thing it does is click once, which makes my click a double click.
Because GetAsyncKeyState detects the key state of the left mouse button. When you press the left mouse button, the click function is called, click will realize the two actions of the left mouse button press and release. Then in the place of the while loop, GetAsyncKeyState will detect the release action, which is why it stops after double-clicking.
I suggest you set the left mouse button to start and the right mouse button to stop.
Code Sample:
import win32api
import time
from pynput.mouse import Button, Controller
mouse = Controller()
while True:
if (win32api.GetAsyncKeyState(0x01)&0x8000 > 0):
flag = True
while flag == True:
mouse.click(Button.left, 1)
time.sleep(0.1)
if (win32api.GetAsyncKeyState(0x02)&0x8000 > 0):
flag = False
else:
pass
Check if win32api.GetAsyncKeyState(0x01) < 0 in the if condition.
I made it work with mouse5!
import win32api
import win32con
import time
from pynput.mouse import Button, Controller
mouse = Controller()
def ac():
if keystate < 0:
mouse.click(Button.left, 1)
time.sleep(0.1)
else:
pass
while True:
keystate = win32api.GetAsyncKeyState(0x06)
ac()

Mouse Listener on_move event

import pynput
import time
def poschange(x,y):
#print(f"{x},{y}")
print("poschange called")
pynput.mouse.Controller().position = (0,0)
def stop(key):
#print(key)
try:
if key.char == 'a':
print("stopped")
keyli.stop()
mouli.stop()
exit()
except:
pass
keyli = pynput.keyboard.Listener(on_press = stop)
keyli.start()
mouli = pynput.mouse.Listener(on_move = poschange)
mouli.start()
keyli.join()
mouli.join()
I just want to lock the mouse position when of the the mouse to (0,0) until I press the 'a' key on the keyboard and then the program terminates and i get control my mouse again.
To my knowledge in this code when ever i move the mouse the poschange() method is called and the mouse position must be set back to (0,0) and repeat again until the mouse listener thread is terminated, but it just works twice, the print statement in the poschange() function is printed twice in the console and then the mouse becomes sluggish and moves slowly, when I press 'a' the listeners must stop and program must terminate but it doesn't I have to manually do it and only the the mouse becomes fast and normal again. But when I remove the line pynput.mouse.Controller().position = (0,0) from the code the thing works as i intend, it prints the print statement "poschange called" every time I move the mouse and the program terminates as expected when I press the 'a' key.
can someone please tell me why and tell me whats wrong with this. Thanks in advance.
You've created a recursion with that call to .position as you move the cursor position in a callback triggered when a cursor changes its position.
I suppose you may get expected behavior by calling that
pynput.mouse.Controller().position = (0,0)
upon application start and by preventing the event propagation with:
mouli = pynput.mouse.Listener(on_move=poschange, suppress=True)

Unable To Break Out Of While Loop When Using Pynput.mouse

I have this relatively simple program that listens for mouse clicks and when the mouse button is held down prints "1"s. Unfortunately when I let go of the mouse it just keeps printing "1"s, even though there is an if statement which checks if the mouse is clicked and should stop the loop if it is not. I am using the pynput.mouse module for the mouse interaction.
Here is my code:
import time
from pynput.mouse import Listener
def clicked(x, y, button, pressed):
if pressed == True:
while button == button.left:
print("1")
time.sleep(1)
if pressed == False:
break
with Listener(on_click=clicked) as listener:
listener.join()
My theory is that once the loops starts it stops listening for mouse clicks, so it can never stop the loop. Would it be necessary to create a new thread for the loop? If yes, how would I do that?
Thanks!
Your current logic makes it impossible to get out of the loop, since pressed doesn't change within the loop. There is not a statement that checks if the mouse is clicked: your only if statements check whether the mouse was clicked when you entered the function. pressed doesn't change within the function.
Look at the critical logiic:
if pressed == True:
while ...
...
if pressed == False:
break
There is nothing in here to change the value of pressed; the first if guarantees that it's True anywhere within the loop.
Yes, you need to set up another listener that operates within the loop. You already know the building blocks: create a new one within the function and bind it to another operation that breaks the loop. For instance, you might "cheat" and have it reset pressed as a global variable.
You could also research how to do this in other answers, if you want an overall handler solution. keypress and keyrelease have been done often enough.
import pyautogui, random, time
import pynput
keys = ['w', 's', 'a', 'd']
def on_press(key):
p = True
if key == pynput.keyboard.Key.esc:
return False
else:
while p == True:
press = keys[random.randint(0,3)]
pyautogui.keyDown(press)
pyautogui.keyUp(press)
p = False
with pynput.keyboard.Listener(on_press=on_press) as L:
L.join()
a code like this will work instead of what you did.
BTW, that is just an example; feel free to visit my GitHub page: github.com/ironnicko

How to know if the left mouse click is pressed

I am using PyAutoGUI library. How can I know if the left mouse button is pressed?
This is what I want to do:
if(leftmousebuttonpressed):
print("left")
else:
print("nothing")
(I'm the author of PyAutoGUI.) I can confirm that currently PyAutoGUI can't read/record clicks or keystrokes. These features are on the roadmap, but there aren't any resources or timelines dedicated to them currently.
How to know if left mouse click is pressed?
A simple version of the code inspired from the original documentation:
from pynput import mouse
def on_click(x, y, button, pressed):
if button == mouse.Button.left:
print('{} at {}'.format('Pressed Left Click' if pressed else 'Released Left Click', (x, y)))
return False # Returning False if you need to stop the program when Left clicked.
else:
print('{} at {}'.format('Pressed Right Click' if pressed else 'Released Right Click', (x, y)))
listener = mouse.Listener(on_click=on_click)
listener.start()
listener.join()
Like Sir Al Sweigart mentioned in the comment above, I looked for pynput module which works perfect. See documentation and PyPi instructions at:
https://pynput.readthedocs.io/
https://pypi.org/project/pynput/
Install the library using pip: pip install pynput
Monitoring other events (e.g. Mouse move, click, scroll)
See the code under Monitoring the mouse heading from original documentation.
https://pynput.readthedocs.io/en/latest/mouse.html#monitoring-the-mouse
I don't think you can use PyAutoGui to listen for mouseclick.
Instead try Pyhook (from their source page):
import pythoncom, pyHook
def OnMouseEvent(event):
# called when mouse events are received
print 'MessageName:',event.MessageName
print 'Message:',event.Message
print 'Time:',event.Time
print 'Window:',event.Window
print 'WindowName:',event.WindowName
print 'Position:',event.Position
print 'Wheel:',event.Wheel
print 'Injected:',event.Injected
print '---'
# return True to pass the event to other handlers
return True
# create a hook manager
hm = pyHook.HookManager()
# watch for all mouse events
hm.MouseAll = OnMouseEvent
# set the hook
hm.HookMouse()
# wait forever
pythoncom.PumpMessages()
I believe you can you do this:
import pyHook, pythoncom
def left_down():
print("left down")
def right_down():
print("right down")
hm = pyHook.HookManager()
hm.SubscribeMouseLeftDown(left_down)
hm.SubscribeMouseRightDown(right_down)
hm.HookMouse()
pythoncom.PumpMessages()
hm.UnhookMouse()
They also do keyboards events, just go look their api up.
Edit:
Here's their mini tutorial: https://sourceforge.net/p/pyhook/wiki/PyHook_Tutorial/
Also PyHook is only for windows (thanks to John Doe for pointing it out)
you can use win32api which is a built-in library as in the following example:
import win32api
if win32api.GetKeyState(0x01)<0: #if mouse left button is pressed
#do something
else: #if mouse left button is not pressed
#do something
as for why that code functions like that here's the explanation:
you do "<0" because the GetKeyState function returns either a negative number or 1 or 0, 1 or 0 are for when the button is released and it changes each time u let go and the negative number is for when the button is pressed, as an example if the mouse button is now returning a 0 using the GetKeyState function and you press it it will return a negative number until u let go and then it will return 1, and if it was a 1 and u press the button it will return a negative number while your holding it down and then once u let go it will go back to being 0 (you can print(GetKeyState(0x01) and see for yourself for a better understanding), as for "0x01" thats the virtual key code for the left mouse button and you can find all the virtual keycodes of each and every button on the mouse and the keyboard using this site provided by microsoft themselves https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes

Categories

Resources