Paepoi

Paepoi » PyObjC Tips » Objective-c からの変換

Objective-c からの変換

最終更新日 2019.04.18
Foundation は基本いらない
PyObjC では Foundation の NS*** 型と Python の型は同様に扱われる。
FoundationPython
NSString文字列
NSArrayリスト
NSDictionary辞書
NSPointタプル
NSSizeタプル
NSRectタプルのタプル

NSDate は datetime、NSTask は subprocess などで賄える。

JXA では JavaScript の型と相互変換が必要でしたが PyObjC では不要です。
NSURL や NSNotification 等が必要無い場合インポートは AppKit だけでいい。

メソッドの変換
- (instancetype)initWithContentRect:(NSRect)contentRect 
                          styleMask:(NSWindowStyleMask)style 
                            backing:(NSBackingStoreType)backingStoreType 
                              defer:(BOOL)flag;

initWithContentRect_styleMask_backing_defer_(
    contentRect, styleMask, backing, defer)
と変換します。

Objective-c は書き方が少し違うだけで C 言語とそんなに代わりません。
引数をメソッドの中間に書くという認識でいいです。
PyObjC ではコロンをアンダーバーに変換して合体させるだけです。

プロパティ
button = NSButton.alloc().initWithFrame_(NSMakeRect(10, 10, 60, 36))
button.setTitle_('タイトル')
print(button.title())
何故かこんな感じ。

セッターは setProperty_ と set の後先頭を大文字にしてアンダーバー。
ゲッターは property() と関数のように呼び出す。

一部例外があって読み取り専用の bool 値の場合 isProperty() になっている場合もあり。
NSCollectionViewItem の selected なんかがそうなっています。

継承(サブクラス)
#!/usr/bin/env python3

from AppKit import *

class TestClass(NSObject):
    def __init__(self):
        print('ココにはこない')
    def init(self):
        objc.super(TestClass, self).init()
        print('init')
        return self
    def method(self):
        print('Instance Method')

#t = TestClass.alloc().init()
#は以下に省略できる
t = TestClass.new()
#=> init
t.method()
#=> Instance Method
こんな感じ。

AppKit に objc が含まれているので import objc はいりません。
TestClass() という呼び出しではないので __init__ は無視されます。
Objective-c の alloc() にてインスタンス化しないと継承されません。

alloc().init() は new() に省略できます。
alloc().initWith*** の場合もあります、この場合 alloc() を忘れずに。
init() 内ではこの記法で親クラスの init() を呼び出す必要があります。

インスタンスメソッドを使う場合は init() の最後で自身を戻すのを忘れずに。
これでインスタンスが呼び出し元の変数に入ります。

Python の普通なインスタンス化ではないので注意する部分があります。
以下の場合はデコレーターが必要になります。
#!/usr/bin/env python3

from AppKit import *

class Counter(NSObject):
    def init(self):
        objc.super(Counter, self).init()
        self.count = 0
        self.start('Start!')
        self.timer = NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
            0.5, self, self.onTimer_, None,True)

    @objc.python_method
    def start(self, text):
        print(text)

    def putEnd_(self, text):
        print(text)

    def onTimer_(self, timer):
        self.count += 1
        print(self.count)
        if self.count == 5:
            self.putEnd_('__done__')
            NSApp.terminate_(self)

NSApplication.sharedApplication()
Counter.new()
NSApp.run()

PyObjC の継承で普通に Python のメソッドを定義する時は
@objc.python_method というデコレーターが必須になります。
引数の数と関数名のアンダーバーの数が揃っていないと容赦無くエラーになります。
self はカウントされません。

@objc.IBAction, @objc.selector 等は書かなくてもいい。
単純に引数の数と関数名のアンダーバーの数をチェックしているだけのようです。
デリゲートを自身に指定した場合等に注意しましょう。

全部アンダーバーの数を揃えるならデコレーターは不要となります。
筆者はオーバーライドと自分で定義したメソッドを見分けるために利用しています。
だって Python だもの、なるべく小文字にしたいよね。

Copyright(C) sasakima-nao All rights reserved 2002 --- 2020.