python win32api MOUSEEVENTF_MOVE not working as expected
Asked Answered
P

1

0

Here is the code

# set mouse position to absolute position 674, 499
win32api.SetCursorPos((674, 499))
# add 100 to current x position which will be 674 + 100 = 774
# will not change y position it will be 499 
win32api.mouse_event(win32con.MOUSEEVENTF_MOVE, 100 ,0)
# print the position
print(f'The final position is {win32api.GetCursorPos()}')

I ran the code in a loop for a while and Here is the output:

The final position is (864, 499)
The final position is (983, 499)
The final position is (984, 499)
The final position is (983, 499)
The final position is (984, 499)
The final position is (983, 499)
The final position is (983, 499)
The final position is (984, 499)
The final position is (983, 499)
The final position is (984, 499)
The final position is (983, 499)
The final position is (983, 499)
The final position is (984, 499)
The final position is (983, 499)
The final position is (984, 499)
The final position is (983, 499)
The final position is (864, 499)
The final position is (864, 499)
The final position is (882, 499)
The final position is (864, 499)
The final position is (984, 499)

In the running of the script, I did not move my mouse(my hand is not holding my mouse during the script is running)

In my understanding, the output will always be (774, 499), but it gives me a surprise without a warning, it is because of my wrong understanding of the method or it`s related to something else ? please help. Thank you all.

Pendent answered 20/8, 2022 at 16:29 Comment(0)
B
1

[MS.Docs]: mouse_event function (winuser.h) which states:

Note This function has been superseded. Use SendInput instead.

also contains details on how to pass those values in the Remarks section.

Options (to move the mouse relative to current position):

  1. Since you're already using GetCursorPos / SetCursorPos, you could:

    • Get current position

    • Add deltas

    • Set the new position

    This makes most sense from what I am concerned

  2. Use SendInput. But PyWin32 doesn't wrap it, so you'd need to call it via CTypes, but that requires some deeper knowledge (C, Python internals, WinAPI), and also requires lots of boilerplate code. [SO]: How to wrap the SendInput function to python using ctypes is exactly about that

  3. Use other 3rd-party modules. With [PyPI]: pynput you can accomplish this in a couple lines of code. Probably PyWinAuto is another candidate

  4. Continue digging in mouse_event direction (although I consider it pointless)

code00.py

#!/usr/bin/env python

import msvcrt
import random
import sys
import time

import win32con as wcon
import win32api as wapi
import win32gui as wgui


def transform_relative(val, tres0, tres1, acc, speed):
    ret = val
    val = abs(val)
    if val > tres0 and acc:
        ret *= 2
    if val > tres1 and acc == 2:
        ret *= 2
    return round(ret * speed / 10)


def set_pos_mouse_event(x, y, absolute_coordinates=True):
    flags = wcon.MOUSEEVENTF_MOVE
    if absolute_coordinates: 
        flags |= wcon.MOUSEEVENTF_ABSOLUTE
        normx = round(x * 0xFFFF / (wapi.GetSystemMetrics(wcon.SM_CXSCREEN) - 1))
        normy = round(y * 0xFFFF / (wapi.GetSystemMetrics(wcon.SM_CYSCREEN) - 1))
    else:  # @TODO - cfati: Not working yet!!!
        tres0, tres1, acc = wgui.SystemParametersInfo(wcon.SPI_GETMOUSE)
        speed = wgui.SystemParametersInfo(wcon.SPI_GETMOUSESPEED)
        #print(tres0, tres1, acc, speed)
        normx = transform_relative(x, tres0, tres1, acc, speed)
        normy = transform_relative(y, tres0, tres1, acc, speed)
    print(f"Move with: ({x}, {y}) ({normx}, {normy})")
    wapi.mouse_event(flags, normx, normy)


def set_pos_cursor_pos(x, y, absolute_coordinates=True):
    print(f"Move with: ({x}, {y})")
    if absolute_coordinates:
        wapi.SetCursorPos((x, y))
    else:
        curx, cury = wapi.GetCursorPos()
        wapi.SetCursorPos((curx + x, cury + y))


def main(*argv):
    maxx = wapi.GetSystemMetrics(wcon.SM_CXSCREEN) - 1
    maxy = wapi.GetSystemMetrics(wcon.SM_CYSCREEN) - 1
    #print(maxx, maxy)
    abs_coords = 0
    set_pos = set_pos_cursor_pos
    #set_pos = set_pos_mouse_event
    print(f"Using function: {set_pos.__name__}")
    print(f"Absolute coordinates: {'True' if abs_coords else 'False'}")
    print(f"Start position: {wapi.GetCursorPos()}")
    while not msvcrt.kbhit():
        print()
        #set_pos_mouse_event(100, 100, 1)
        #print(f"{wapi.GetCursorPos()}")
        x = random.randint(0, maxx)
        y = random.randint(0, maxy)
        if not abs_coords:
            curx, cury = wapi.GetCursorPos()
            x -= curx
            y -= cury
        set_pos(x, y, absolute_coordinates=abs_coords)
        print(f"Final position: {wapi.GetCursorPos()}")
        time.sleep(0.5)


if __name__ == "__main__":
    print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
                                                   64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    rc = main(*sys.argv[1:])
    print("\nDone.")
    sys.exit(rc)

Notes:

  • I chose #1. (set_pos_cursor_pos). It works fine

  • I also attempted #4. (set_pos_mouse_event)

    • I was only able to complete half (MOUSEEVENTF_ABSOLUTE). I tried to complete the other half (that you are interested in) by implementing the steps from the URL (in transform_relative) but it doesn't work
  • I chose concept clarity over common sense (performance). Some APIs (GetSystemMetrics for example) are called from within functions that are executed lots of times. That makes no sense, for production code they should be only called once (at the beginning) and the returned values passed as arguments to the function that needs them

Output (2 variants that work):

[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q073428381]> "e:\Work\Dev\VEnvs\py_pc064_03.09_test0\Scripts\python.exe" ./code00.py
Python 3.9.9 (tags/v3.9.9:ccb0e6a, Nov 15 2021, 18:08:50) [MSC v.1929 64 bit (AMD64)] 064bit on win32

Using function: set_pos_cursor_pos
Absolute coordinates: False
Start position: (583, 610)

Move with: (-461, 103)
Final position: (122, 713)

Move with: (44, -324)
Final position: (166, 389)

Move with: (1153, 418)
Final position: (1319, 807)

Move with: (390, 118)
Final position: (1709, 925)

Move with: (-860, 127)
Final position: (849, 1052)

Move with: (127, -202)
Final position: (976, 850)

Move with: (-84, -605)
Final position: (892, 245)

Move with: (86, 330)
Final position: (978, 575)

Move with: (-167, 170)
Final position: (811, 745)

Move with: (-63, -233)
Final position: (748, 512)

Done.

[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q073428381]> "e:\Work\Dev\VEnvs\py_pc064_03.09_test0\Scripts\python.exe" ./code00.py
Python 3.9.9 (tags/v3.9.9:ccb0e6a, Nov 15 2021, 18:08:50) [MSC v.1929 64 bit (AMD64)] 064bit on win32

Using function: set_pos_mouse_event
Absolute coordinates: True
Start position: (748, 512)

Move with: (1076, 803) (36746, 48772)
Final position: (1076, 803)

Move with: (1435, 572) (49006, 34741)
Final position: (1435, 572)

Move with: (197, 820) (6728, 49804)
Final position: (197, 820)

Move with: (1153, 522) (39376, 31705)
Final position: (197, 820)

Move with: (12, 29) (410, 1761)
Final position: (12, 29)

Move with: (150, 176) (5123, 10690)
Final position: (150, 176)

Move with: (882, 37) (30121, 2247)
Final position: (882, 37)

Move with: (582, 195) (19876, 11844)
Final position: (582, 195)

Move with: (1066, 810) (36405, 49197)
Final position: (1066, 810)

Move with: (1888, 487) (64476, 29579)
Final position: (1888, 487)

Done.
Baal answered 21/8, 2022 at 23:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.