-1

So I have been looking for multiple ways to perform a "click" without actually moving the mouse. After hours of searching, I came upon these two pages: ctypes mouse_events and https://schurpf.com/python-mouse-control/ where there's some code that can apparently perform a click without moving the mouse. The code seems to come from the same person, but https://schurpf.com/python-mouse-control/ seems more up to date with the "Added functions". I tried fixing it for a while but didn't get anywhere and am stuck with the following script

import win32gui, win32api, win32con, ctypes

class Mouse:
    """It simulates the mouse"""
    MOUSEEVENTF_MOVE = 0x0001 # mouse move
    MOUSEEVENTF_LEFTDOWN = 0x0002 # left button down
    MOUSEEVENTF_LEFTUP = 0x0004 # left button up
    MOUSEEVENTF_RIGHTDOWN = 0x0008 # right button down
    MOUSEEVENTF_RIGHTUP = 0x0010 # right button up
    MOUSEEVENTF_MIDDLEDOWN = 0x0020 # middle button down
    MOUSEEVENTF_MIDDLEUP = 0x0040 # middle button up
    MOUSEEVENTF_WHEEL = 0x0800 # wheel button rolled
    MOUSEEVENTF_ABSOLUTE = 0x8000 # absolute move
    SM_CXSCREEN = 0
    SM_CYSCREEN = 1

    def _do_event(self, flags, x_pos, y_pos, data, extra_info):
        """generate a mouse event"""
        x_calc = 65536 * x_pos / ctypes.windll.user32.GetSystemMetrics(self.SM_CXSCREEN) + 1
        y_calc = 65536 * y_pos / ctypes.windll.user32.GetSystemMetrics(self.SM_CYSCREEN) + 1
        return ctypes.windll.user32.mouse_event(flags, x_calc, y_calc, data, extra_info)

    def _get_button_value(self, button_name, button_up=False):
        """convert the name of the button into the corresponding value"""
        buttons = 0
        if button_name.find("right") >= 0:
            buttons = self.MOUSEEVENTF_RIGHTDOWN
        if button_name.find("left") >= 0:
            buttons = buttons + self.MOUSEEVENTF_LEFTDOWN
        if button_name.find("middle") >= 0:
            buttons = buttons + self.MOUSEEVENTF_MIDDLEDOWN
        if button_up:
            buttons = buttons << 1
        return buttons

    def move_mouse(self, pos):
        """move the mouse to the specified coordinates"""
        (x, y) = pos
        old_pos = self.get_position()
        x =  x if (x != -1) else old_pos[0]
        y =  y if (y != -1) else old_pos[1]
        self._do_event(self.MOUSEEVENTF_MOVE + self.MOUSEEVENTF_ABSOLUTE, x, y, 0, 0)

    def press_button(self, pos=(-1, -1), button_name="left", button_up=False):
        """push a button of the mouse"""
        self.move_mouse(pos)
        self._do_event(self.get_button_value(button_name, button_up), 0, 0, 0, 0)

    def click(self, pos=(-1, -1), button_name= "left"):
        """Click at the specified placed"""
##        self.move_mouse(pos)
        self._do_event(self._get_button_value(button_name, False)+self._get_button_value(button_name, True), 0, 0, 0, 0)

    def double_click (self, pos=(-1, -1), button_name="left"):
        """Double click at the specifed placed"""
        for i in xrange(2):
            self.click(pos, button_name)

    def get_position(self):
        """get mouse position"""
        return win32api.GetCursorPos()
#-----------------------------------------------------------------------------------------
#Added functions
#-----------------------------------------------------------------------------------------
    def invisible_click(self,pos=(-1, -1), button_name="left"):
        """Click in specified place without moving mouse"""
        xcur,ycur = win32gui.GetCursorPos()
        ctypes.windll.user32.SetCursorPos(pos[0],pos[1])
        self.click(pos,button_name)
        ctypes.windll.user32.SetCursorPos(xcur,ycur)

    def invisible_click_rel(self,handle,pos, button_name="left"):
        """Click in window coordinates without moving mouse"""
        #get window info
        xleft, ytop, xright, ybottom = win32gui.GetWindowRect(handle)

        xcur,ycur = win32gui.GetCursorPos()

        ctypes.windll.user32.SetCursorPos(pos[0]+xleft,pos[1]+ytop)
        self.click((pos[0]+xleft,pos[1]+ytop),button_name)
        ctypes.windll.user32.SetCursorPos(xcur,ycur)

if __name__ == '__main__':
    p = (500,500)
    print (win32gui.GetForegroundWindow())
    mouse = Mouse()
    mouse.invisible_click(p)

What I am trying to do is trigger the invisible_click function of the Mouse class so that it performs an "invisible click" at position 500,500.

The error I am getting with the code above is

File "C:\Users\MyName\Desktop\test1del\Future.py", line 21, in _do_event
    return ctypes.windll.user32.mouse_event(flags, x_calc, y_calc, data, extra_info)
ctypes.ArgumentError: argument 2: <class 'TypeError'>: Don't know how to convert parameter 2

Sorry if it's something obvious that I am missing, I would really appreciate the help! Thanks for reading

Kian
  • 25
  • 1
  • 7
  • For that to work, the intended target application needs to be in the foreground. While you may accomplish the goal of not actually moving the mouse cursor, it is still a disruptive operation to bring the target window into the foreground. The correct way to automate a UI is to use [UI Automation](https://learn.microsoft.com/en-us/windows/win32/winauto/entry-uiauto-win32). – IInspectable May 19 '20 at 09:32

1 Answers1

0

File "C:\Users\MyName\Desktop\test1del\Future.py", line 21, in _do_event return ctypes.windll.user32.mouse_event(flags, x_calc, y_calc, data, extra_info) ctypes.ArgumentError: argument 2: : Don't know how to convert parameter 2

This error message indicate that parameters x_calc and y_calc passed in function mouse_event have wrong type. They are float but unsigned int required .

Try the following code:

import math
#...
    x_calc = math.floor(65536 * x_pos / ctypes.windll.user32.GetSystemMetrics(self.SM_CXSCREEN) + 1)
    y_calc = math.floor(65536 * y_pos / ctypes.windll.user32.GetSystemMetrics(self.SM_CYSCREEN) + 1)

Note: mouse_event function has been superseded. Use SendInput instead.

Rita Han
  • 9,574
  • 1
  • 11
  • 24