2013-08-02 88 views
0

函數CGEventKeyboardSetUnicodeString使用UniCharCount和const UniChar unicodeString []。我無法弄清楚如何使用pyobjc從python調用它。理想情況下,我希望能夠傳遞一個python unicode字符串。有沒有辦法修改這個函數的元數據,所以我可以做到這一點?另外,有沒有辦法讓我把python unicode字符串轉換成一個UNICHAR數組和一個使用pyobjc的長度?如何從python調用CGEventKeyboardSetUnicodeString?

澄清:

我使用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' 
+0

OK,它看起來像PyObjC 2.5.1(你必須手動安裝,因爲蘋果最新版本是2.3.2a0)有我在談論修復......顯然這不是正確的解決辦法。讓我看看它。 – abarnert

+0

當我使用Python 3.3.0或2.7.5在2.4.0或頂級的PyObjC中測試這個時,我得到了與參數不同且正確的元數據。類型是'n^T',它有'c_array_length_in_arg = 1'。並將它傳遞給一個Python'unicode'對象工作正常。你正在使用'Quartz'而不是'CoreGraphics'的功能,對吧? – abarnert

+0

再看看這個......它看起來像PyObjC 2.4.0修復了這些函數,2.5.0可能再次打破了它們(以不同的方式),而2.6.0可能再次修復它們......這很難確定,安裝我可以測試的所有版本,當我知道時我會報告回來。如果我是對的,最好的解決方案可能會或可能不會將2.6橋接支持文件複製到2.5.1 ...但是,一旦我做到了,我會讓你知道。 – abarnert

回答

2

功能,採取UniChar *字符串從來沒有很容易調用,但噸這是一個解決方法。不幸的是,對於2.5,即使該解決方法不起作用,除非您首先解決另一個問題。首先,我將解釋早期版本的解決方法,然後我會回到2.5。

在早期版本的PyObjC中,函數採用UniChar *的參數類型聲明爲unsigned short *(即元數據中的'^S');在更高版本中,它正確UniChar *(即'^T')。但無論哪種方式,PyObjC都不會將其視爲字符串類型,而是將其視爲從0到65535之間的整數序列。所以,這是你必須通過它。


如果你已經得到了所有的unicode,其字符是在BMP,*這只是字符串中的每個字符的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字符? A UniChar *是一系列UTF-16代碼點,而不是Unicode字符序列,因此您需要編碼爲UTF-16。你可以明確地做到這一點(然後struct.unpack得到一個H值列表),但有一個更簡單的方法:一個CFStringNSString 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行的字符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對象,但不這樣做。

+0

@abarnet感謝您的回覆。我嘗試了上述所有選項,但不幸的是,它們不適用於我正在使用的pyobjc版本。當我傳遞一個短褲列表甚至是一個NSString時,我得到: 'ValueError:depythonifying'pointer','list''或'ValueError:depythonifying'pointer','objc.pyobjc_unicode'' 結束你建議改變類型爲'^ T',但它似乎已經是這樣。 –

+0

爲第三個參數中的元數據是: '{ 'already_cfretained':虛假, 'already_retained':虛假, 'null_accepted':真, '類型': '^ T'}' –

+0

這些註釋具有非常有限空間的數量,所以我已經更新了上面的問題。 –

0

我已經知道我可以使用ctypes進行調用,並且仍然可以很好地與pyobjc一起工作。但是,如果有人知道如何完全用p​​yobjc做到這一點,那麼我會很樂意接受這個答案。

我的解決辦法是這樣的:

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) 
+0

除了這個_不應該是必要的(如果由於pyobjc的缺陷實際上是必要的,這是不相關的......),這實際上並不正確。 OS X'wchar_t'是一個4字節的類型,其中'UniChar'是一個2字節的類型。實際上,您將轉換爲UTF-32,然後將其傳遞給需要UTF-16的代碼。當然,對於這個測試用例來說,這很好,因爲它會看到'a',後面跟着'\ u0000',但對於多字符字符串(或者不在BMP中的單個字符)則不會。 – abarnert

+0

謝謝!我注意到它不適用於BMP以外的字符,但我不知道爲什麼。我更新了這個例子。 –

+0

好的,我想我已經發現了這個問題,如果你願意修改函數元數據,並且我已經提交了一個bug,我已經有了一個解決方法。根據過去的歷史,Ronald Oussoren很可能會很快得到解決,或者像往常一樣,解釋我錯了什麼,爲什麼不需要修復,這對我來說也是足夠好的。但是,如果您需要使用2.5.x,則需要採取某種解決方法。 – abarnert