Paepoi

Paepoi » PyObjC Tips » NSButton

NSButton

最終更新日 2024.08.25

ボタンスタイル
スタイル指定でどんな形のボタンになるかを決定します。
どんなスタイルがあるのかは作って確認したほうが早いので以下に。
#!/usr/bin/env python3

# 左上基準のほうが配置が楽なので継承した NSView の上に置いています
# title プロパティには 'Button' という文字列が最初から入っています

import AppKit, objc

STYLES = dict(
    Rounded = AppKit.NSBezelStyleRounded, #1
    RegularSquare = AppKit.NSBezelStyleRegularSquare, #2
    ShadowlessSquare = AppKit.NSBezelStyleShadowlessSquare, #6
    TexturedSquare = AppKit.NSBezelStyleTexturedSquare, #8
    SmallSquare = AppKit.NSBezelStyleSmallSquare, #10
    TexturedRounded = AppKit.NSBezelStyleTexturedRounded, #11
    RoundRect = AppKit.NSBezelStyleRoundRect, #12
    Recessed = AppKit.NSBezelStyleRecessed, #13
    Inline = AppKit.NSBezelStyleInline #15
)
IMGSTYLES = [
    AppKit.NSBezelStyleDisclosure, #5
    AppKit.NSBezelStyleCircular, #7
    AppKit.NSBezelStyleHelpButton, #9
    AppKit.NSBezelStyleRoundedDisclosure, #14
]

class ButtonView(AppKit.NSView):
    def initWithFrame_(self, rect):
        objc.super(ButtonView, self).initWithFrame_(rect)
        #
        y = 10
        for key, style in STYLES.items():
            button = AppKit.NSButton.alloc().initWithFrame_(((10, y), (200, 36)))
            button.setTitle_(key)
            button.setBezelStyle_(style)
            self.addSubview_(button)
            y += 40
        for style in IMGSTYLES:
            button = AppKit.NSButton.alloc().initWithFrame_(((10, y), (200, 36)))
            button.setTitle_('') # Button という文字列が最初に入っているので消す
            button.setBezelStyle_(style)
            self.addSubview_(button)
            y += 40
        #
        return self

    def isFlipped(self):
        # 左上を原点にする
        return True

class MyWindow(AppKit.NSWindow):
    def init(self):
        rect = AppKit.NSMakeRect(0, 0, 220, 550)
        objc.super(MyWindow, self).initWithContentRect_styleMask_backing_defer_(
            rect,
            AppKit.NSTitledWindowMask |
            AppKit.NSClosableWindowMask |
            AppKit.NSResizableWindowMask |
            AppKit.NSMiniaturizableWindowMask,
            AppKit.NSBackingStoreBuffered, False)
        # NSView
        self.canvas = ButtonView.alloc().initWithFrame_(rect)
        self.contentView().addSubview_(self.canvas)
        # etc
        self.setTitle_('NSButton Styles')
        self.setDelegate_(self)
        return self

    def windowDidResize_(self, sender):
        # GTK+ や WPF のように追従してくれないので
        self.canvas.setFrameSize_(self.contentView().frame().size)

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

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)
        # 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()

webp/bs.webp

ボタンタイプ
ON|OFF ボタンやラジオボタンもすべて NSButton の type で指定して作ります。
どんなタイプがあるか、やはり作って確認したほうが早いので以下に。
#!/usr/bin/env python3

import AppKit, objc

STYLES = dict(
    # 普通のボタン、違いはわからない(昔は違いがあった?)
    MomentaryLight = AppKit.NSButtonTypeMomentaryLight, #0
    MomentaryPushIn = AppKit.NSButtonTypeMomentaryPushIn, #7
    # 押す毎に ON|OFF を切り替え、GtkToggleButton と同じ、違いはわからない
    PushOnPushOff = AppKit.NSButtonTypePushOnPushOff, #1
    OnOff = AppKit.NSButtonTypeOnOff, #6
    # ON 状態の時に違うテキストをタイトルにしたい場合に、GtkToggleButton とは違う
    Toggle = AppKit.NSButtonTypeToggle, #2
    # GTK+ でいう GtkCheckButton、紛らわしい
    Switch = AppKit.NSButtonTypeSwitch, #3
    # ラジオボタン、NSMatrix と組み合わせで使う
    Radio = AppKit.NSButtonTypeRadio, #4
    # ON 状態の時に違う画像をタイトルにしたい場合
    MomentaryChange = AppKit.NSButtonTypeMomentaryChange, #5
    # 感圧トラックパッドの圧力を得る時に使う
    Accelerator = AppKit.NSButtonTypeAccelerator, #8
    MultiLevelAccelerator = AppKit.NSButtonTypeMultiLevelAccelerator #9
)

class ButtonView(AppKit.NSView):
    def initWithFrame_(self, rect):
        objc.super(ButtonView, self).initWithFrame_(rect)
        #
        y = 10
        for key, button_type in STYLES.items():
            # メッセージハンドラは self のメソッドにする、コロンとアンダーバーに注意
            button = AppKit.NSButton.buttonWithTitle_target_action_(key, self, 'onButtonClick:')
            button.setFrame_(((10, y), (200, 36)))
            button.setBezelStyle_(AppKit.NSBezelStyleRounded)
            button.setButtonType_(button_type)
            # 代替タイトル、Toggle 指定の時だけ切り替わる
            button.setAlternateTitle_('ON!!!')
            # 代替画像、MomentaryChange 指定の時だけ入れ替わる
            #button.setAlternateImage_(image)
            self.addSubview_(button)
            y += 40
        #
        return self

    def onButtonClick_(self, sender):
        # ボタンクリックのハンドラ
        if sender.state() == AppKit.NSControlStateValueOn:
            print(sender.title())
        # 感圧トラックパッドで Accelerator ボタン押し込み時のみ反応する
        tp = sender.doubleValue()
        if 1.0 < tp:
            print(tp)

    def isFlipped(self):
        # 左上を原点にする
        return True

class MyWindow(AppKit.NSWindow):
    def init(self):
        rect = AppKit.NSMakeRect(0, 0, 220, 420)
        objc.super(MyWindow, self).initWithContentRect_styleMask_backing_defer_(
            rect,
            AppKit.NSTitledWindowMask |
            AppKit.NSClosableWindowMask |
            AppKit.NSResizableWindowMask |
            AppKit.NSMiniaturizableWindowMask,
            AppKit.NSBackingStoreBuffered, False)
        # NSView
        self.canvas = ButtonView.alloc().initWithFrame_(rect)
        self.contentView().addSubview_(self.canvas)
        # etc
        self.setTitle_('NSButton Types')
        self.setDelegate_(self)
        return self

    def windowDidResize_(self, sender):
        # GTK+ や WPF のように追従してくれないので
        self.canvas.setFrameSize_(self.contentView().frame().size)


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

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)
        # 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()

webp/bt.webp

ラジオボタン (NSMatrix)
ラジオボタンは複数の選択肢から一つを選択させるのに利用します。
NSMatrix と組み合わせて使います、以下サンプル。
#!/usr/bin/env python3

import AppKit, objc

CAMERAS = ['OM SYSTEM', 'Leica', 'NIKON', 'FUJIFILM']

class ButtonView(AppKit.NSView):
    def initWithFrame_(self, rect):
        objc.super(ButtonView, self).initWithFrame_(rect)
        #
        # Radio Buttons
        #
        # セルを作る
        cell = AppKit.NSButtonCell.new()
        cell.setButtonType_(AppKit.NSButtonTypeRadio)
        # 全体サイズでマトリクス作成
        self.matrix = AppKit.NSMatrix.alloc().initWithFrame_mode_prototype_numberOfRows_numberOfColumns_(
            ((10, 10), (200, 20*4)), AppKit.NSRadioModeMatrix, cell, 4, 1)
        for n, camera in enumerate(CAMERAS):
            self.matrix.setCellSize_((200, 20))
            self.matrix.cells()[n].setTitle_(camera)
        # 選択は setState 引数にゼロ以外を指定
        self.matrix.setState_atRow_column_(1, 2, 0)
        self.addSubview_(self.matrix)
        #
        return self

    def isFlipped(self):
        # 左上を原点にする
        return True

class MyWindow(AppKit.NSWindow):
    def init(self):
        rect = AppKit.NSMakeRect(0, 0, 220, 200)
        objc.super(MyWindow, self).initWithContentRect_styleMask_backing_defer_(
            rect,
            AppKit.NSTitledWindowMask |
            AppKit.NSClosableWindowMask |
            AppKit.NSResizableWindowMask |
            AppKit.NSMiniaturizableWindowMask,
            AppKit.NSBackingStoreBuffered, False)
        # NSView
        self.canvas = ButtonView.alloc().initWithFrame_(rect)
        self.contentView().addSubview_(self.canvas)
        # etc
        self.setTitle_('NSButton Matrix')
        self.setDelegate_(self)
        return self

    def windowDidResize_(self, sender):
        # GTK+ や WPF のように追従してくれないので
        self.canvas.setFrameSize_(self.contentView().frame().size)

    def windowWillClose_(self, sender):
        # 選択値はゼロベースで得る
        row = self.canvas.matrix.selectedRow()
        print(f'選択されたのは {CAMERAS[row]} です')

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

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)
        # 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()

webp/mx.webp

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