I want to use Python to read raw data from the mouse (e.g. left signal, right signal) independent of the mouse pointer. I don't want to read the position of the cursor, I want to read the raw data, akin to how a game would. How can I do that? I can't find any libraries that support it.
I'm using Windows.
Edit: To clarify, when I said "left signal and right signal", I meant mouse movement, not mouse clicks,
Edit 2: I believe the terminology is that I want the "mouse delta." This is how people did it in autohotkey - https://www.autohotkey.com/boards/viewtopic.php?t=10159 - but I want it in Python.
Edit 3: I should mention that this is in a game that constantly resets pointer position, so I can't use the difference in pointer position. That's why I want a lower level API.
Get the mouse position and compare two events. If the x-axis value increases, the mouse moved right, if the x-axis value decreases, the mouse moved left:
from pynput.mouse import Listener
last_position = None
def on_move(x, y):
global last_position
if last_position:
if x > last_position:
print('mouse moved right')
elif x < last_position:
print('mouse moved left')
last_position = x
with Listener(on_move=on_move) as listener:
listener.join()
You can always compare the position constantly that would have easily given you a delta. Any lower level API you will have to code a mouse driver yourself, which is what game developers do to prevent people from scripting, which isn't what python made for.
import pyautogui
import time
while True:
prev_x, prev_y = pyautogui.position()
time.sleep(0.3)
curr_x, curr_y = pyautogui.position()
if (curr_x - prev_x) > 0:
print("move right")
else:
print("move left")
Related
(EDIT #1)
I am new to coding and tried to create an infinite grid and a way to drag/move the grid using the mouse when any mouse button is pressed.
What I am trying to do is take the coordinates of the mouse right before any mouse button is pressed.
Then calculate the difference in coordinates while mouse is being moved in real time from that starting position (without releasing the button).
Then I want to save that difference and use it to move the grid (after mouse button is released).
After that, when I release any mouse button and the grid was successfully moved, I want to then be able to drag it again without resetting the already existing coordinates of the grid.
The problem with all of this is: when any mouse button is held down and is not being released, if moved from its starting point and then stopped at another location than the starting, the variable "CPD" representing the difference between starting and current mouse position adds itself, yet I do not see why that is supposed to be happening. It is an "=" sign and not "+=" nor is itself specified when measuring the difference and the "if" is not triggered at all, only the "else" is!
SP - Starting position of the mouse (right before left mouse button is pressed). CP - Current position of the mouse. PPD - The previous difference in starting and current mouse position. CPD - Current difference in starting and current mouse position.
This is the continuation of the previous picture (couldn't fit everything in one)
import time
import mouse
CPD = [0, 0] # Current difference in the starting and curruent mouse position
PPD = [0, 0] # Previous difference in the starting and curruent mouse position
CP = [0, 0] # Current position of the mouse
SP = [0, 0] # Position of the mouse right before any mouse button is pressed
# the loop (obviously) so it can check again and again so that the difference would be seen in real time.
while True:
# simply a way to slow down the code to see the difference step by step
time.sleep(1)
# Saves the position of the mouse [x, y] and the previous difference in starting and current mouse position until any mouse button is pressed
if not mouse.is_pressed():
PPD = CPD
SP = list(mouse.get_position())
# Then, if any mouse button is pressed: it finds current mouse's position
# Then it calculates the difference in starting and current mouse positions for x first and then y after which it stores the result
else:
CP = list(mouse.get_position())
for position in range(2):
CPD[position] = PPD[position] + CP[position] - SP[position]
# all the variable results after math
print(f"Mouse is pressed: {mouse.is_pressed()}\nSP: {SP}\nCP: {CP}\nPPD: {PPD}\nCPD: {CPD}\n")
I have tried to figure it out for a very long time and neither could my friend who is decent at coding (I believe). Any ideas?
The confusion here for you is - I think - coming from the interpretation of mouse.is_pressed(). This function returns you the current state, not a change of state. So throughout the whole time you have the button pressed, it returns True. It is not that, when you first time access this method, it is somehow reset to False again. Only when you release the mouse button again.
That also explains, why it is being added again and again: You have a loop and check for it every 0.1 seconds.
is_pressed() gives True all time when you keep pressed button.
You would have to remeber previous value and execute code only when previous state is False and current state is True
Somethink like this
import time
import mouse
Current_Position_Difference = [0, 0]
Previous_Position_Difference = [0, 0]
Start_Position = [0, 0]
previous_state = False
while True:
time.sleep(0.1)
current_state = mouse.is_pressed()
if current_state is False:
Previous_Position_Difference = Current_Position_Difference
Start_Position = list(mouse.get_position())
else:
if previous_state is False:
Current_Position = list(mouse.get_position())
for position in range(2):
Current_Position_Difference[position] = Previous_Position_Difference[position] + Current_Position[position] - Start_Position[position]
previous_state = current_state
print(Current_Position_Difference)
But mouse has also mouse.on_click(callback) to run function callback() only once when you press button. And on_click doesn't need to run in while-loop.
I don't know what you try to do in your code so I don't know if this code works the same
import time
import mouse
# --- function ---
def my_function():
print('clicked', mouse.get_position())
Current_Position = list(mouse.get_position())
for position in range(2):
Current_Position_Difference[position] = Previous_Position_Difference[position] + Current_Position[position] - Start_Position[position]
# --- main ---
Current_Position_Difference = [0, 0]
Previous_Position_Difference = [0, 0]
Start_Position = [0, 0]
mouse.on_click(my_function)
while True:
time.sleep(0.01)
Previous_Position_Difference = Current_Position_Difference
Start_Position = mouse.get_position()
print(Current_Position_Difference)
Other modules may have also on_move(callback) to run function only when mouse change position.
I have a script that strip-mines in MineCraft and occasionally places a torch at its feet. I am using PyAutoGUI to move the mouse from in front of the player to below the player, place a torch, move it back and continue mining. The problem is that it won't move the mouse down unless it is already moving (e.g. if I am wiggling the mouse a bit, then it will work). Otherwise, it just stays still.
Code (I have commented the bits that aren't to do with placing a torch):
def mine():
count = 0
while not found_diamonds:
# if keyboard.is_pressed('q'):
# break
# pyautogui.keyDown('w')
# pyautogui.mouseDown()
count += 1
if count >= 20:
count = 0
pyautogui.mouseUp()
es.wait(0.01)
pyautogui.moveRel(00, 500)
es.wait(0.01)
pyautogui.rightClick()
es.wait(0.01)
pyautogui.moveRel(00, -400)
time.sleep(0.01)
Thanks in advance.
I included the whole loop, but the part I'm talking about is the third paragraph. I have this loop, where it clicks on something, and then it moves the mouse back to wherever it was, to not interrupt the user while this thing is running. However, the pyautogui.MoveTo() call seems to be getting skipped sometimes?
while unlim or intFTC > 0:
# switch to program, do thing, and switch back to current window
current_win = gw.getActiveWindow().title
win = gw.getWindowsWithTitle('World of Warcraft')[0]
win.activate()
pyautogui.press(input_key)
win = gw.getWindowsWithTitle(current_win)[0]
win.activate()
# find and click thing
bx, by = thing_finder(x, y, x1, x2, y1, y2)
time.sleep(random.uniform(0.2, 0.6))
current_x, current_y = pyautogui.position()
current_win = gw.getActiveWindow().title
pyautogui.moveTo(bx, by)
pyautogui.rightClick()
# send mouse back to original position
while pyautogui.position() != (current_x, current_y):
pyautogui.moveTo(current_x, current_y)
print('::', pyautogui.position())
# return to og window
win = gw.getWindowsWithTitle(current_win)[0]
win.activate()
window.refresh()
stop()
time.sleep(random.uniform(1.5, 2))
runs += 1
So in the third chunk commented "send mouse back to original position," it doesn't always work. The current_x, and current_y are always correct, but it won't move the mouse to those coordinates. I thought the while loop would fix it, but it still doesn't always work which boggles my mind, because how does it get out of the while loop without moving the mouse? Thank you for reading.
Is it simply a case of not enough pause time? I have the time between pyautogui calls set to 0.001, but I figured it would slow down if it had to, not skip commands.
The loop does not make sense, you should send the command pyautogui.moveTo(current_x, current_y) and it should work. That condition can lead to some errors.
I am using PsychoPy and I would like to print the position of my mouse when it has been clicked.
The actual printing of the position needs to be placed inside a while loop. Using the code below, when I click I get more than one output lines, which print the same positions. I would like to have only one output printing for each click.
This is the code I am using:
#!/usr/bin/env python2
from psychopy import visual, core, event
from pyglet.gl import *
width = 600
height = 600
myWin = visual.Window([width,height], color='white',units='pix',monitor='testMonitor')
#This will set the windows units (pixels) to GL units
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(0, width, 0, height, -1, 1)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glEnable(GL_BLEND)
glBlendFunc(GL_ZERO, GL_SRC_COLOR)
myMouse = event.Mouse() # will use myWin by default
while True:
#Triangle left
glColor3f(1.0, 0.0, 0.0)
glBegin(GL_TRIANGLES)
glVertex3f(150, 550, 1)
glVertex3f(50, 350, 1)
glVertex3f(250, 350, -1)
glEnd()
if myMouse.getPressed()[0]:
myMouse.clickReset()
print myMouse.getPos()
myWin.flip()
core.quit()
Is there something wrong I am doing? Should the 'frame rate' of the while loop be changed?
I've not used the module, but it seems like mouse events are thrown for mouse raises as well.
You'll need to
Store the mouse state for next time
Each iteration, test whether the mouse state for button 0 has gone up → down
The mouse state is returned by myMouse.getPressed.
So something like:
oldMouseIsDown = False
while True:
...
mouseIsDown = myMouse.getPressed()[0]
myMouse.clickReset()
if mouseIsDown and not oldMouseIsDown:
print myMouse.getPos()
oldMouseIsDown = mouseIsDown
Veedrac's answer is correct. Your code is using the typical PsychoPy pattern of checking for events once every time the window is redrawn. This will typically be happening at least at 60 Hz. So unless you manage to press the mouse button for less than 16.7 ms (or less for a faster screen), you will detect it multiple times as being pressed, as each time you check on successive window redraws, the mouse button remains down. Even though it was pushed only once, the duration of the push is not instantaneous.
As Veedrac suggests, you therefore need to maintain the previous state of the mouse button in a variable so that you can choose to only print the position once. Mouseup events are not relevant here: you are purely testing for whether the button is currently pressed.
I had a very similar problem and fixed it in a slightly different way from the accepted answer. The advantage of this one is that you can set how long you want to 'desensitize' the mouse after a click (i.e. to prevent longer clicks from triggering your if loop multiple times). Depending on your mouse or the user's click release speed you can change minFramesAfterClick:
minFramesAfterClick = 10 # to prevent re-entering the if loop too early
myMouse.clickReset()
timeAfterClick = 0
while True:
timeAfterClick += 1
if myMouse.getPressed()[0] and timeAfterClick >= minFramesAfterClick:
print myMouse.getPos()
myMouse.clickReset()
timeAfterClick = 0
myWin.flip()
By the way, the reason why the OP couldn't get Veedrac's answer work is because line oldMouseIsDown = mouseIsDown should be placed it inside the if loop rather than after it (not enough reputation to comment there).
I am attempting to apply the getMouse() function to a specific part of the window, instead of the whole thing. I need the block that is clicked in to change color if it is 'rect1'. However if any other block is clicked, nothing should happen. I have attached the portion of my code which I feel relates to this in case anyone can be of any assistance.
#draw the grid
for j in range (6):
for i in range(6):
sq_i = Rectangle(Point(20 + (40*i), 20 + (40*j)),
Point(60 + (40*i),60 + (40*j)))
sq_i.draw(window)
sq_i.setFill('white')
sq_i.setOutline('grey')
#wait for a click
window.getMouse ()
#turn the alotted region red
rect1 = Rectangle(Point(20 + (40*1), 20 + (40*1)),
Point(60 + (40*1), 60 + (40*1)))
rect1.setOutline('black')
rect1.draw(window)
rect1.setFill('brown')
#if the mouse is clicked in rect1, change the block color to black
while window.getMouse() in rect1:
rect1.setFill('black')
First off, you need to understand what window.getMouse() in rect1 does. Python's in operator works by turning a in b into the method call b.__contains__(a). Unfortunately, the Rectangle class doesn't have a __contains__ method. This your immediate problem.
So, you need to use a different test. I suggest using Python's chained comparison operators to do the bounds checking yourself (there doesn't seem to be any library support for this in the graphics module):
mouse = window.getMouse()
if rect1.p1.x < mouse.x < rect1.p2.x and rect1.p1.y < mouse.y < rect1.p2.y
rect1.setFill("black")
Note that I've changed the while loop for an if statement. If you want to repeatedly test mouse clicks until one hits the rectangle, you probably want to loop on while True and then break if the tested condition is true:
while True:
mouse = window.getMouse()
if rect1.p1.x < mouse.x < rect1.p2.x and rect1.p1.y < mouse.y < rect1.p2.y
rect1.setFill("black")
break