Paepoi » PyObjC Tips » NSApplication
NSApplication
最終更新日 2024.08.25
を呼び出すと AppKit.NSApp というグローバル変数に NSApplication のインスタンスが入ります。
.NET や GtkApplication 又は TApplication 等を知っている人はアレッと思うかも。
つまり Cocoa ではウインドウが無いアプリケーションのインスタンスが作れます。
というか既存の Cocoa アプリも全部そうなっています、そういう文化です。
NSApplication は NSWindow を参照カウンタで管理なんかしません。
同じインスタンスで動いている NSWindow を keyWindow 等で見つけてくれるだけです。
でも終了時には全部まとめて破棄してくれる頼もしい存在です。
閉じるボタンで終了するサンプルコードばかり見つかるので最初にコレを書いてみました。
macOS アプリは command+Q で終了しないとおかしいですよね。
メソッド名はアンダーバーを使いますがメニューを作る時はコロンで指定なので注意。
デリゲートは NSApplicationDelegate を継承する必要は無い、ただの class でいい。
command+N で新規ウインドウ、command+W でウインドウを閉じる。
という macOS 標準ショートカットはこんな感じで実装します。
terminate は予約されているので上書きしないように注意しよう。
NSApp.keyWindow() で現在キー操作を受け付けるウインドウが得られます。
NSWindow を作ったらどこかに保存する。
先ほど書いたように NSApp は NSWindow を参照カウンタで管理なんかしません。
つまり自力で保持しないと Python のガベージコレクションに破棄されてしまいます。
NSWindow は close() メソッドを呼ぶ、又は閉じるボタンで自動的に破棄される。
その時リストで保持した NSWindow は何もしなくていい。
ポインタアドレスが残るだけなので無視する、へたに破棄すると二重破棄でエラー。
継承した NSWindow はこんな感じで作ります。
必要に応じて NSRect と **mask を指定、他の引数は現在固定値です。
Interface Builder の nib から作る方法もあるけどココでは解説しません。
ズラして配置はどうせ必要になるから書いたけど説明は省きます。
NSWindowDelegate は自身を指定する。
別の class を作ってもいいけど無駄に class が多くなるより管理しやすい。
windowWillClose_ は閉じるボタンや command+W で届くメッセージのハンドラです。
stdout に Close! が表示されるのを確認ください。
applicationSupportsSecureRestorableState は書かないと警告が出ます。
素直に True を戻すコードを入れておきましょう。
NSApp を作る
AppKit.NSApplication.sharedApplication()を呼び出すと AppKit.NSApp というグローバル変数に NSApplication のインスタンスが入ります。
#!/usr/bin/env python3 import AppKit, objc class AppMenu(AppKit.NSMenu): def init(self): objc.super(AppMenu, self).init() item_app = AppKit.NSMenuItem.new() self.addItem_(item_app) menu_app = AppKit.NSMenu.new() item_app.setSubmenu_(menu_app) # command+Q で閉じるメニュー item_quit = AppKit.NSMenuItem.new() item_quit.initWithTitle_action_keyEquivalent_('Quit App', 'terminate:', 'q') menu_app.addItem_(item_quit) return self # NSApp を作る AppKit.NSApplication.sharedApplication() # command+Q で終了するメニューを入れる AppKit.NSApp.setMainMenu_(AppMenu.new()) # メインループを回す AppKit.NSApp.run()ウインドウも何も無いアプリケーション。
.NET や GtkApplication 又は TApplication 等を知っている人はアレッと思うかも。
つまり Cocoa ではウインドウが無いアプリケーションのインスタンスが作れます。
というか既存の Cocoa アプリも全部そうなっています、そういう文化です。
NSApplication は NSWindow を参照カウンタで管理なんかしません。
同じインスタンスで動いている NSWindow を keyWindow 等で見つけてくれるだけです。
でも終了時には全部まとめて破棄してくれる頼もしい存在です。
閉じるボタンで終了するサンプルコードばかり見つかるので最初にコレを書いてみました。
macOS アプリは command+Q で終了しないとおかしいですよね。
NSWindow を作る
AppKit の一般的な動作をするウインドウを書いてみます。#!/usr/bin/env python3 import AppKit, objc class MyWindow(AppKit.NSWindow): def init(self): objc.super(MyWindow, self).initWithContentRect_styleMask_backing_defer_( AppKit.NSMakeRect(0, 0, 320, 240), AppKit.NSTitledWindowMask | AppKit.NSClosableWindowMask | AppKit.NSResizableWindowMask | AppKit.NSMiniaturizableWindowMask, AppKit.NSBackingStoreBuffered, False) # すでに Window がある場合はズラして配置 if len(AppKit.NSApp.windows()) == 1: self.center() else: point = AppKit.NSApp.keyWindow().cascadeTopLeftFromPoint_(AppKit.NSZeroPoint) self.setFrameTopLeftPoint_(point) # title はプロパティなので self.setTitle_('日本語') # 自身をデリゲートに self.setDelegate_(self) # 自分を戻す return self def windowWillClose_(self, sender): # オーバーライド、閉じるアクションでココに来る print('Close!') class AppDelegate(AppKit.NSObject): # ガベージコレクション回避用 wins = [] def applicationDidFinishLaunching_(self, notification): ''' オーバーライド、ウインドウは基本ココで作る ''' window = MyWindow.new() window.makeKeyAndOrderFront_(window) # ガベージコレクションされないようにどこかに保存する self.wins.append(window) # アクティブ化 AppKit.NSApp.activateIgnoringOtherApps_(True) def applicationSupportsSecureRestorableState_(self, app): ''' セキュリティのためにこれを書けと怒られるので ''' return True def newWindow_(self, notification): ''' 自作メソッドの場合でもメニューではアンダーバーをコロンにする ''' window = MyWindow.new() window.makeKeyAndOrderFront_(window) self.wins.append(window) def closeWindow_(self, notification): ''' command+w ''' AppKit.NSApp.keyWindow().close() class AppMenu(AppKit.NSMenu): def init(self): objc.super(AppMenu, self).init() item_app = AppKit.NSMenuItem.new() self.addItem_(item_app) menu_app = AppKit.NSMenu.new() item_app.setSubmenu_(menu_app) # new menu item_new = AppKit.NSMenuItem.new() item_new.initWithTitle_action_keyEquivalent_('New Window', 'newWindow:', 'n') menu_app.addItem_(item_new) # close menu item_close = AppKit.NSMenuItem.new() item_close.initWithTitle_action_keyEquivalent_('Close Window', 'closeWindow:', 'w') menu_app.addItem_(item_close) # separator menu_app.addItem_(AppKit.NSMenuItem.separatorItem()) # quit menu item_quit = AppKit.NSMenuItem.new() item_quit.initWithTitle_action_keyEquivalent_('Quit App', 'terminate:', 'q') menu_app.addItem_(item_quit) return self AppKit.NSApplication.sharedApplication() AppKit.NSApp.setMainMenu_(AppMenu.new()) AppKit.NSApp.setDelegate_(AppDelegate.new()) AppKit.NSApp.run()先ほどの NSApp にデリゲートを追加してウインドウを作るメニューを追加。
メソッド名はアンダーバーを使いますがメニューを作る時はコロンで指定なので注意。
デリゲートは NSApplicationDelegate を継承する必要は無い、ただの class でいい。
command+N で新規ウインドウ、command+W でウインドウを閉じる。
という macOS 標準ショートカットはこんな感じで実装します。
terminate は予約されているので上書きしないように注意しよう。
NSApp.keyWindow() で現在キー操作を受け付けるウインドウが得られます。
NSWindow を作ったらどこかに保存する。
先ほど書いたように NSApp は NSWindow を参照カウンタで管理なんかしません。
つまり自力で保持しないと Python のガベージコレクションに破棄されてしまいます。
NSWindow は close() メソッドを呼ぶ、又は閉じるボタンで自動的に破棄される。
その時リストで保持した NSWindow は何もしなくていい。
ポインタアドレスが残るだけなので無視する、へたに破棄すると二重破棄でエラー。
継承した NSWindow はこんな感じで作ります。
必要に応じて NSRect と **mask を指定、他の引数は現在固定値です。
Interface Builder の nib から作る方法もあるけどココでは解説しません。
ズラして配置はどうせ必要になるから書いたけど説明は省きます。
NSWindowDelegate は自身を指定する。
別の class を作ってもいいけど無駄に class が多くなるより管理しやすい。
windowWillClose_ は閉じるボタンや command+W で届くメッセージのハンドラです。
stdout に Close! が表示されるのを確認ください。
applicationSupportsSecureRestorableState は書かないと警告が出ます。
素直に True を戻すコードを入れておきましょう。
Copyright(C) sasakima-nao All rights reserved 2002 --- 2024.