Paepoi

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

Objective-c からの変換

最終更新日 2024.08.25

import に注意
#from AppKit import *
import AppKit, objc
PyObjC で検索すると上部の from を使ったコードばかり見つかると思います。
2024 年現在その手段を使うと初期化に十倍以上の時間が掛かるようになりました。
面倒でも AppKit のプリフィクスから関数呼び出しするように書きましょう。
objc も当然見つからなくなるため import するように。

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_('タイトル') # setter
print(button.title()) # getter
他言語のプロパティと違い Objective-c 同様、具体的には以下のように。

セッターは setProperty_ と set の後先頭を大文字にしてアンダーバー。
ゲッターは property() と関数のように呼び出す。
読み取り専用の bool 値の場合 isProperty() になっている場合もあり。
NSCollectionViewItem の selected なんかがそうなっています。

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

import AppKit, objc

class TestClass(AppKit.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
こんな感じ。

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

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

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

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

import AppKit, objc

class Counter(AppKit.NSObject):
    def init(self):
        objc.super(Counter, self).init()
        self.count = 0
        self.start('Start!')
        self.timer = AppKit.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__')
            AppKit.NSApp.terminate_(self)

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

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

@objc.IBAction, @objc.selector 等はコードのみなら書かなくてもいい。
詳しく調べていませんが nib を利用する場合の判別用のようです。
単純に引数の数と関数名のアンダーバーの数をチェックしているだけのようです。
デリゲートを自身に指定した場合等に注意しましょう。

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

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