如何从python中调用CGEventKeyboardSetUnicodeString?

时间:2013-08-02 22:58:37

标签: python pyobjc

函数CGEventKeyboardSetUnicodeString采用UniCharCount和const UniChar unicodeString []。我无法弄清楚如何使用pyobjc从python中调用它。理想情况下,我希望能够传入一个python unicode字符串。有没有办法修改此功能的元数据,所以我可以这样做?或者,有没有办法让我使用pyobjc将python unicode字符串转换为UNICHAR数组和长度?

澄清:

我正在使用pyobjc的2.5.1版

CGEventKeyboardSetUnicodeString的元数据:

>>> pprint(CGEventKeyboardSetUnicodeString.__metadata__())
{'arguments': ({'already_cfretained': False,
            'already_retained': False,
            'null_accepted': True,
            'type': '^{__CGEvent=}'},
           {'already_cfretained': False,
            'already_retained': False,
            'type': 'L'},
           {'already_cfretained': False,
            'already_retained': False,
            'null_accepted': True,
            'type': '^T'}),
'retval': {'already_cfretained': False,
        'already_retained': False,
        'type': 'v'},
'variadic': False}

我尝试了以下内容:

>>> CGEventKeyboardSetUnicodeString(event, 1, 'a')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: depythonifying 'pointer', got 'str'

>>> CGEventKeyboardSetUnicodeString(event, 1, u'a')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: depythonifying 'pointer', got 'unicode'

>>> CGEventKeyboardSetUnicodeString(event, 1, [ord('a')])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: depythonifying 'pointer', got 'list'

>>> Quartz.CGEventKeyboardSetUnicodeString(event, 1, struct.unpack('>{}H'.format(1), u'a'.encode('utf-16-le')))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: depythonifying 'pointer', got 'tuple'

2 个答案:

答案 0 :(得分:2)

使用UniChar *字符串的函数从未如此容易调用,但有一种解决方法。不幸的是,对于2.5,除非您首先解决另一个问题,否则即使该解决方法也不起作用。首先,我将解释早期版本的解决方法,然后我将回到2.5。

在早期版本的PyObjC中,使用UniChar *的函数将其param类型声明为unsigned short *(元数据中为'^S');在以后的版本中,它是正确的UniChar *(即'^T')。但无论哪种方式,PyObjC都不会将其视为字符串类型,它将其视为0-65535之间的整数序列。所以,这就是你必须通过它。


如果你的所有角色都在BMP中unicode,那就是字符串中每个字符的ord。这很容易:

>>> s = u'abc'
>>> e = Quartz.CGEventCreateKeyboardEvent(None, 0, True)
>>> Quartz.CGEventKeyboardSetUnicodeString(e, len(s), map(ord, s))
>>> slen, swords = Quartz.CGEventKeyboardGetUnicodeString(e, 100, None, None)
>>> slen
3
>>> u''.join(map(unichr, swords))
u'abc'

请注意,Get函数还向我们返回了一系列短整数,而不是unicode


但是,如果你有或可能有一些非BMP角色怎么办? UniChar *是一系列UTF-16代码点,而不是Unicode字符序列,因此您需要编码为UTF-16。您可以明确地执行此操作(然后struct.unpack获取H值列表),但有一种更简单的方法:CFStringNSString 也< / em>一系列UTF-16代码点。因此,如果您只使用原始代码,但使用NSString代替unicode,则可以使用:

>>> s = u'abc\U00010000d'
>>> nss = Foundation.NSString.stringWithString_(s)
>>> len(s), len(nss)
(5, 6, 6)
>>> Quartz.CGEventKeyboardSetUnicodeString(e, len(nss), map(ord, nss))

如果你发现自己做了很多,你可能想写一个简单的包装器:

def to_unichr(s):
    nss = Foundation.NSStringWithString_(s)
    return len(nss), map(ord, nss)

所以你可以这样做:

Quartz.CGEventKeyboardSetUnicodeString(e, *to_unichr(s))

以上所有内容适用于Python 2.x,但在3.x中应该相同,只要删除多余的u前缀(对于3.2及更早版本)并使用list(map(…))当你想要打印出来的东西时,而不是map(…)


与此同时,PyObjC 2.5改变了声明桥支持元数据的方式,并改变了大部分元数据的细节。据我所知,用2.5.0或2.5.1调用此函数实际上是不可能的。有关详细信息,请参阅issue #64

问题似乎是_C_IN'n')类型修饰符已被删除。您可以将序列传递给期望输入参数指针的函数,PyObjC会自动适当地转换它,但出于显而易见的原因,它不适用于输出,输入输出或未指定的指针。

您可以通过自行修改元数据来解决此问题。在2.5.1中,您可以在文件Quartz/CoreGraphics/_metadata.py中找到它,在第29行的char 1351(或者更简单地说,搜索CGEventKeyboardSetUnicodeString)。您必须向元组添加两个值。而不是:

'CGEventKeyboardSetUnicodeString': (sel32or64(b'v^{__CGEvent=}L^T', b'v^{__CGEvent=}Q^T'),)

你需要这个:

'CGEventKeyboardSetUnicodeString': (sel32or64(b'v^{__CGEvent=}L^T', b'v^{__CGEvent=}Q^T'), '', {'arguments': {2: {'type_modifier': 'n'}}})

通过此更改,上面的解决方法适用于2.5.0或2.5.1。


*它也适用于只有ASCII字符的str / bytes对象,但不能这样做。

答案 1 :(得分:0)

我已经发现我可以使用ctypes来进行调用,并且仍然能够很好地使用pyobjc。但如果有人知道怎么用pyobjc完全做到这一点,那么我很乐意接受这个答案。

我的解决方案如下:

import ctypes
import ctypes.util
import objc
import Quartz
import sys

event = Quartz.CGEventCreateKeyboardEvent(None, 0, True)
cf = ctypes.cdll.LoadLibrary(ctypes.util.find_library('ApplicationServices'))
cf.CGEventKeyboardSetUnicodeString.restype = None
event_id = objc.pyobjc_id(event)
s = u'\U0001F600'
utf16_native = 'utf-16-le' if sys.byteorder == 'little' else 'utf-16-be'
buf = s.encode(utf16_native)
cf.CGEventKeyboardSetUnicodeString(event_id, len(buf) / 2, buf)
Quartz.CGEventPost(Quartz.kCGSessionEventTap, event)