pyautogui在mac系统上,双击无效,打不开文件

前言

之前看了水哥的自动化视频,觉得非常牛逼,准备自己上手写点自动化的东西,但奈何pyautogui最后一次更新的时间是Jul 8, 2021,这就很尴尬了,现在mac的版本都12,13了,虽然大部分的功能都能适配,但是仍然存在某些小问题。

问题

import pyautogui
pyautogui.doubleClick(1324,315)

正常的双击,我们可以发现,他无法打开文件

def doubleClick(x=None, y=None, interval=0.0, button=LEFT, duration=0.0, tween=linear, logScreenshot=None, _pause=True):
    if sys.platform == "darwin":
        x, y = _normalizeXYArgs(x, y)
        _mouseMoveDrag("move", x, y, 0, 0, duration=0, tween=None)
        x, y = platformModule._position()
        platformModule._multiClick(x, y, button, 2)
        _logScreenshot(logScreenshot, 'click', '%s,2,%s,%s' % (button, x, y), folder='.')
    else:
        # Click for Windows or Linux:
        click(x, y, 2, interval, button, duration, tween, logScreenshot, _pause=False)

def _multiClick(x, y, button, num, interval=0.0):
    btn    = None
    down   = None
    up     = None

    if button == LEFT:
        btn  = Quartz.kCGMouseButtonLeft
        down = Quartz.kCGEventLeftMouseDown
        up   = Quartz.kCGEventLeftMouseUp
    elif button == MIDDLE:
        btn  = Quartz.kCGMouseButtonCenter
        down = Quartz.kCGEventOtherMouseDown
        up   = Quartz.kCGEventOtherMouseUp
    elif button == RIGHT:
        btn  = Quartz.kCGMouseButtonRight
        down = Quartz.kCGEventRightMouseDown
        up   = Quartz.kCGEventRightMouseUp
    else:
        assert False, "button argument not in ('left', 'middle', 'right')"
        return

    for i in range(num):
        _click(x, y, button)
        time.sleep(interval)
        
def _click(x, y, button):
    if button == LEFT:
        _sendMouseEvent(Quartz.kCGEventLeftMouseDown, x, y, Quartz.kCGMouseButtonLeft)
        _sendMouseEvent(Quartz.kCGEventLeftMouseUp, x, y, Quartz.kCGMouseButtonLeft)
    elif button == MIDDLE:
        _sendMouseEvent(Quartz.kCGEventOtherMouseDown, x, y, Quartz.kCGMouseButtonCenter)
        _sendMouseEvent(Quartz.kCGEventOtherMouseUp, x, y, Quartz.kCGMouseButtonCenter)
    elif button == RIGHT:
        _sendMouseEvent(Quartz.kCGEventRightMouseDown, x, y, Quartz.kCGMouseButtonRight)
        _sendMouseEvent(Quartz.kCGEventRightMouseUp, x, y, Quartz.kCGMouseButtonRight)
    else:
        assert False, "button argument not in ('left', 'middle', 'right')"

通过查看源码,我们可以发现,作为darwin的mac他调用双击的事件与正常pyobjc提供的双击事件不同。

from Quartz.CoreGraphics import *

def mouseDoubleClick(posx, posy):
    '''perfrom a double left click'''
    theEvent = CGEventCreateMouseEvent(None, kCGEventLeftMouseDown, (posx, posy), kCGMouseButtonLeft);
    CGEventPost(kCGHIDEventTap, theEvent);

    CGEventSetType(theEvent, kCGEventLeftMouseUp);
    CGEventPost(kCGHIDEventTap, theEvent);

    CGEventSetIntegerValueField(theEvent, kCGMouseEventClickState, 2);

    CGEventSetType(theEvent, kCGEventLeftMouseDown);
    CGEventPost(kCGHIDEventTap, theEvent);

    CGEventSetType(theEvent, kCGEventLeftMouseUp);
    CGEventPost(kCGHIDEventTap, theEvent);

通过比对发现,pyautogui缺少CGEventSetIntegerValueField这个函数,导致虽然完成双击事件,但无法双击打开文件

pynput

这里说一下,这个插件,比较好,是22年一直在维护的一个库,可以满足双击打开文件的需求

from pynput.mouse import Button, Controller as c_mouse
import sys
print(sys.platform)
mouse= c_mouse()
# mouse.move(1324,315)
mouse.position = (1324,315)
mousePosition=mouse.position
mouse.click(Button.left, 2)

我们也来看看他的源码

        def click(self, button, count=1):
        """Emits a button click event at the current position.

        The default implementation sends a series of press and release events.

        :param Button button: The button to click.

        :param int count: The number of clicks to send.
        """
        with self as controller:
            for _ in range(count):
                controller.press(button)
                controller.release(button)

    def _press(self, button):
        (press, _, _), mouse_button = button.value
        event = Quartz.CGEventCreateMouseEvent(
            None,
            press,
            self.position,
            mouse_button)

        # If we are performing a click, we need to set this state flag
        if self._click is not None:
            self._click += 1
            Quartz.CGEventSetIntegerValueField(
                event,
                Quartz.kCGMouseEventClickState,
                self._click)

        Quartz.CGEventPost(Quartz.kCGHIDEventTap, event)

        # Store the button to enable dragging
        self._drag_button = button

    def _release(self, button):
        (_, release, _), mouse_button = button.value
        event = Quartz.CGEventCreateMouseEvent(
            None,
            release,
            self.position,
            mouse_button)

        # If we are performing a click, we need to set this state flag
        if self._click is not None:
            Quartz.CGEventSetIntegerValueField(
                event,
                Quartz.kCGMouseEventClickState,
                self._click)

        Quartz.CGEventPost(Quartz.kCGHIDEventTap, event)

        if button == self._drag_button:
            self._drag_button = None

通过对比我们发现,在这个库里面使用了CGEventSetIntegerValueField这个函数,可以完成任务。

解决办法

可以用pynupt

from pynput.mouse import Button, Controller as c_mouse
mouse= c_mouse()
mouse.position = (1324,315)
mousePosition=mouse.position
mouse.click(Button.left, 2)

解决办法改源码(危险系数大,不过很好玩)

先改这个文件_pyautogui_osx.py

def _click(x, y, button, num):
    if button == LEFT:
        _sendMouseEvent_2(Quartz.kCGEventLeftMouseDown, x, y, Quartz.kCGMouseButtonLeft, num)
        _sendMouseEvent_2(Quartz.kCGEventLeftMouseUp, x, y, Quartz.kCGMouseButtonLeft, num)
    elif button == MIDDLE:
        _sendMouseEvent_2(Quartz.kCGEventOtherMouseDown, x, y, Quartz.kCGMouseButtonCenter, num)
        _sendMouseEvent_2(Quartz.kCGEventOtherMouseUp, x, y, Quartz.kCGMouseButtonCenter, num)
    elif button == RIGHT:
        _sendMouseEvent_2(Quartz.kCGEventRightMouseDown, x, y, Quartz.kCGMouseButtonRight, num)
        _sendMouseEvent_2(Quartz.kCGEventRightMouseUp, x, y, Quartz.kCGMouseButtonRight, num)
    else:
        assert False, "button argument not in ('left', 'middle', 'right')"

def _multiClick(x, y, button, num, interval=0.0):
    btn    = None
    down   = None
    up     = None

    if button == LEFT:
        btn  = Quartz.kCGMouseButtonLeft
        down = Quartz.kCGEventLeftMouseDown
        up   = Quartz.kCGEventLeftMouseUp
    elif button == MIDDLE:
        btn  = Quartz.kCGMouseButtonCenter
        down = Quartz.kCGEventOtherMouseDown
        up   = Quartz.kCGEventOtherMouseUp
    elif button == RIGHT:
        btn  = Quartz.kCGMouseButtonRight
        down = Quartz.kCGEventRightMouseDown
        up   = Quartz.kCGEventRightMouseUp
    else:
        assert False, "button argument not in ('left', 'middle', 'right')"
        return

    for i in range(num):
        _click(x, y, button, i+1)
        time.sleep(interval)

def _sendMouseEvent_2(ev, x, y, button, num):
    mouseEvent = Quartz.CGEventCreateMouseEvent(None, ev, (x, y), button)
    Quartz.CGEventSetIntegerValueField(mouseEvent, Quartz.kCGMouseEventClickState, num)
    Quartz.CGEventPost(Quartz.kCGHIDEventTap, mouseEvent)

改__init__.py文件

@_genericPyAutoGUIChecks
def click(
    x=None, y=None, clicks=1, interval=0.0, button=PRIMARY, duration=0.0, tween=linear, logScreenshot=None, _pause=True
):
    button = _normalizeButton(button)
    x, y = _normalizeXYArgs(x, y)

    # Move the mouse cursor to the x, y coordinate:
    _mouseMoveDrag("move", x, y, 0, 0, duration, tween)

    _logScreenshot(logScreenshot, "click", "%s,%s,%s,%s" % (button, clicks, x, y), folder=".")

    if sys.platform == 'darwin':
        failSafeCheck()
        if button in (LEFT, MIDDLE, RIGHT):
            platformModule._multiClick(x, y, button, clicks, interval)
    else:
        for i in range(clicks):
            failSafeCheck()
            if button in (LEFT, MIDDLE, RIGHT):
                platformModule._click(x, y, button)

            time.sleep(interval)
        
@_genericPyAutoGUIChecks
def doubleClick(x=None, y=None, interval=0.0, button=LEFT, duration=0.0, tween=linear, logScreenshot=None, _pause=True):

    # Multiple clicks work different in OSX
    if sys.platform == "darwin":
        x, y = _normalizeXYArgs(x, y)
        _mouseMoveDrag("move", x, y, 0, 0, duration=0, tween=None)
        x, y = platformModule._position()
        platformModule._multiClick(x, y, button, 2)
        _logScreenshot(logScreenshot, 'click', '%s,2,%s,%s' % (button, x, y), folder='.')
    else:
        # Click for Windows or Linux:
        click(x, y, 2, interval, button, duration, tween, logScreenshot, _pause=False)

备注

哦对,如果你是以下几种方法想模拟双击都会因为缺少CGEventSetIntegerValueField而导致无法打开文件

mouse.click(Button.left, 2)
#或者
mouse.press(Button.left)
mouse.release(Button.left)
mouse.press(Button.left)
mouse.release(Button.left)

你可能感兴趣的:(mac,自动化,源码修改,macos,python,自动化)