Paepoi

Paepoi » PyObjC Tips » NSButton

NSButton

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

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

from AppKit import *

RECT = ((0, 0), (220, 550))
wins = []

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

class ButtonView(NSView):
    def initWithFrame_(self, rect):
        objc.super(ButtonView, self).initWithFrame_(rect)
        #
        y = 10
        for key, style in STYLES.items():
            button = NSButton.alloc().initWithFrame_(((10, y), (200, 36)))
            button.setTitle_(key)
            button.setBezelStyle_(style)
            self.addSubview_(button)
            y += 40
        for style in IMGSTYLES:
            button = 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(NSWindow):
    def init(self):
        objc.super(MyWindow, self).initWithContentRect_styleMask_backing_defer_(
            RECT,
            NSTitledWindowMask | NSClosableWindowMask |
            NSResizableWindowMask | NSMiniaturizableWindowMask,
            NSBackingStoreBuffered, False)
        self.center()
        self.setTitle_('NSButton Styles')
        self.setDelegate_(self)
        # ButtonView
        self.button_view = ButtonView.alloc().initWithFrame_(RECT)
        self.contentView().addSubview_(self.button_view)
        #
        return self

    def windowDidResize_(self, sender):
        self.button_view.setFrameSize_(self.contentView().frame().size)

class AppDelegate(NSObject):
    def applicationDidFinishLaunching_(self, notification):
        window = MyWindow.new()
        window.makeKeyAndOrderFront_(window)
        wins.append(window)

class AppMenu(NSMenu):
    def init(self):
        objc.super(AppMenu, self).init()
        item_app  = NSMenuItem.new()
        self.addItem_(item_app)
        menu_app = NSMenu.new()
        item_app.setSubmenu_(menu_app)
        # quit menu
        item_quit = NSMenuItem.new()
        item_quit.initWithTitle_action_keyEquivalent_('Quit App', 'terminate:', 'q')
        menu_app.addItem_(item_quit)
        return self

NSApplication.sharedApplication()
NSApp.setMainMenu_(AppMenu.new())
NSApp.setDelegate_(AppDelegate.new())
NSApp.activateIgnoringOtherApps_(True)
NSApp.run()

img/nsbutton_style.png

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

from AppKit import *

RECT = ((0, 0), (220, 420))
wins = []

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

class ButtonView(NSView):
    def initWithFrame_(self, rect):
        objc.super(ButtonView, self).initWithFrame_(rect)
        #
        y = 10
        for key, button_type in STYLES.items():
            # メッセージハンドラは self のメソッドにする、コロンとアンダーバーに注意
            button = NSButton.buttonWithTitle_target_action_(key, self, 'onButtonClick:')
            button.setFrame_(((10, y), (200, 36)))
            button.setBezelStyle_(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() == NSControlStateValueOn:
            print(sender.title())
        # 感圧トラックパッドで Accelerator ボタン押し込み時のみ反応する
        tp = sender.doubleValue()
        if 1.0 < tp:
            print(tp)

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

class MyWindow(NSWindow):
    def init(self):
        objc.super(MyWindow, self).initWithContentRect_styleMask_backing_defer_(
            RECT,
            NSTitledWindowMask | NSClosableWindowMask |
            NSResizableWindowMask | NSMiniaturizableWindowMask,
            NSBackingStoreBuffered, False)
        self.center()
        self.setTitle_('NSButton Type')
        self.setDelegate_(self)
        # ButtonView
        self.button_view = ButtonView.alloc().initWithFrame_(RECT)
        self.contentView().addSubview_(self.button_view)
        #
        return self

    def windowDidResize_(self, sender):
        self.button_view.setFrameSize_(self.contentView().frame().size)

class AppDelegate(NSObject):
    def applicationDidFinishLaunching_(self, notification):
        window = MyWindow.new()
        window.makeKeyAndOrderFront_(window)
        wins.append(window)

class AppMenu(NSMenu):
    def init(self):
        objc.super(AppMenu, self).init()
        item_app  = NSMenuItem.new()
        self.addItem_(item_app)
        menu_app = NSMenu.new()
        item_app.setSubmenu_(menu_app)
        # quit menu
        item_quit = NSMenuItem.new()
        item_quit.initWithTitle_action_keyEquivalent_('Quit App', 'terminate:', 'q')
        menu_app.addItem_(item_quit)
        return self

NSApplication.sharedApplication()
NSApp.setMainMenu_(AppMenu.new())
NSApp.setDelegate_(AppDelegate.new())
NSApp.activateIgnoringOtherApps_(True)
NSApp.run()

img/nsbutton_type.png

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

from AppKit import *

RECT = ((0, 0), (220, 200))
wins = []
MOTS = ['HONDA', 'YAMAHA', 'SUZUKI', 'KAWASAKI']

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

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

class MyWindow(NSWindow):
    def init(self):
        objc.super(MyWindow, self).initWithContentRect_styleMask_backing_defer_(
            RECT,
            NSTitledWindowMask | NSClosableWindowMask |
            NSResizableWindowMask | NSMiniaturizableWindowMask,
            NSBackingStoreBuffered, False)
        self.center()
        self.setTitle_('RadioButton Sample')
        self.setDelegate_(self)
        # ButtonView
        self.button_view = ButtonView.alloc().initWithFrame_(RECT)
        self.contentView().addSubview_(self.button_view)
        #
        return self

    def windowDidResize_(self, sender):
        self.button_view.setFrameSize_(self.contentView().frame().size)

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

class AppDelegate(NSObject):
    def applicationDidFinishLaunching_(self, notification):
        window = MyWindow.new()
        window.makeKeyAndOrderFront_(window)
        wins.append(window)

class AppMenu(NSMenu):
    def init(self):
        objc.super(AppMenu, self).init()
        item_app  = NSMenuItem.new()
        self.addItem_(item_app)
        menu_app = NSMenu.new()
        item_app.setSubmenu_(menu_app)
        # quit menu
        item_quit = NSMenuItem.new()
        item_quit.initWithTitle_action_keyEquivalent_('Quit App', 'terminate:', 'q')
        menu_app.addItem_(item_quit)
        return self

NSApplication.sharedApplication()
NSApp.setMainMenu_(AppMenu.new())
NSApp.setDelegate_(AppDelegate.new())
NSApp.activateIgnoringOtherApps_(True)
NSApp.run()

img/nsradiobutton.png

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