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 --- 2025.