如何在Mac上用Python监听鼠标事件?

11 投票
3 回答
2542 浏览
提问于 2025-04-15 19:44

我需要在我的Mac上监听全局的鼠标事件(不是绑定到某个应用程序的),这个应用是用Python写的。

我在用PyObjC,但我不知道该怎么做。如果有简单的ObjC例子或者其他Python的方法也可以。

到目前为止我的代码是:

from Quartz import *
def MyFunction(proxy, type, event):
    print event

CGEventTapCreate(kCGHIDEventTap, kCGTailAppendEventTap, kCGEventTapOptionListenOnly, kCGEventLeftMouseDown, MyFunction)

== 出现了段错误

我知道我需要把它添加到事件源中,但我得先让这个工作起来。

[更新]

使用Macports中的PyObjC解决了段错误,所以我现在写了这个:

from Quartz import *

def MyFunction(p, t, e, c):
    print e

tap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, kCGEventTapOptionListenOnly, kCGEventLeftMouseDown, MyFunction, None)

runLoopSource = CFMachPortCreateRunLoopSource(None, tap, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode);
CGEventTapEnable(tap, True);

CFRunLoopRun();

但是这个代码一直在运行,没法响应鼠标事件,问题出在哪里呢?

3 个回答

-1

首先,CGEventTapCreateCGEventTapCreateForPSN 这两个函数在被调用时会泄露一些内存。这是为了避免内存管理上的问题。因此,建议不要频繁调用这些函数,或者至少尽量少调用它们。

接下来,鼠标事件的工作原理大致是这样的:

evt = CGEventCreateMouseEvent(None, kCGEventLeftMouseDown, (80, 90), kCGMouseButtonLeft)
self.failUnlessIsInstance(evt, CGEventRef)
1

关于CGEventTapCreate的说明(你可以在这里找到详细信息:http://developer.apple.com/mac/library/documentation/Carbon/Reference/QuartzEventServicesRef/Reference/reference.html#//apple_ref/c/func/CGEventTapCreate),它提到要使用kCGHIDEventTap这个功能,你需要有管理员权限。你运行你的脚本时是以管理员身份吗?(可以用sudo命令来实现)

如果是的话,你还应该检查一下tap是否为None;这能帮助你更好地找出问题所在。文档中列出了几种错误情况,这些情况可能导致CGEventTapCreate返回NULL,在Python中这会显示为None。

3

CGEventTapCreate的第四个参数是 CGEventMask eventsOfInterest,你给它传入了 kCGEventLeftMouseDown,这是一个类型为 _CGEventType 的枚举值。其实,你需要在位掩码中翻转相应的位,而不是直接使用这个整数常量。你可以使用 CGEventMaskBit 来做到这一点。

所以,不应该这样写:

tap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap,
    kCGEventTapOptionListenOnly, kCGEventLeftMouseDown, MyFunction, None)

我们可以这样写:

tap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap,
    kCGEventTapOptionListenOnly, CGEventMaskBit(kCGEventLeftMouseDown),
    MyFunction, None)

或者等价于:

tap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap,
    kCGEventTapOptionListenOnly, (1 << kCGEventLeftMouseDown),
    MyFunction, None)

撰写回答