Paepoi

Paepoi » PyObjC Tips » NSView

NSView

最終更新日 2024.08.25

NSView 概要
NSView は配置や描写を行う基底クラスです。
単体で使う場合は主に線や図形、又は画像の描写を行う場合に利用します。

ところで、AppKit の API はものすごく古臭いです。
レイアウタはありません、絶対位置と絶対サイズで配置する必要がある。
文字列のはみ出しに注意したり NSWindow のリサイズに合わせたりする必要がある。

NSWindow の contentView プロパティは NSView ですが配置しかできません。
この contentView に subView として NSView を配置していく。

nib を使う場合は NSViewController を使ってロードします。
ここでは使わないので直接配置しています。

drawRect
drawRect というハンドラで描写しますが、他の API と何か違いますよね。
グラフィック(デバイス)コンテキストの指定がありません。
AppKit での描写はフォーカスをロックして描写という流れなので不要なだけです。
そんなこんなで色々描写してみます。
#!/usr/bin/env python3

import AppKit, objc

PIC = 'umejiro3.jpg' # 手持ち画像に書き換えしてください

class MyView(AppKit.NSView):
    def initWithFrame_(self, rect):
        objc.super(MyView, self).initWithFrame_(rect)
        return self

    def drawRect_(self, rect):
        # 背景の塗りつぶし
        AppKit.NSColor.brownColor().set()
        AppKit.NSRectFill(rect)
        # 背景から 5px オフセットして枠っぽく画像
        rect_img = AppKit.NSInsetRect(rect, 5, 5)
        image = AppKit.NSImage.alloc().initWithContentsOfFile_(PIC)
        image.drawInRect_(rect_img)
        # 線
        AppKit.NSColor.whiteColor().set()
        path = AppKit.NSBezierPath.bezierPath()
        path.setLineWidth_(10)
        path.moveToPoint_((50, 100))
        path.lineToPoint_((200, 200))
        path.stroke()
        # 文字列
        bg_color = AppKit.NSColor.colorWithSRGBRed_green_blue_alpha_(0.4, 1.0, 1.0, 0.5)
        text = AppKit.NSAttributedString.alloc().initWithString_attributes_(
            'ウメジロー',
            {
                AppKit.NSForegroundColorAttributeName: AppKit.NSColor.whiteColor(),
                AppKit.NSBackgroundColorAttributeName: bg_color
            })
        AppKit.NSColor.blueColor().set()
        text.drawAtPoint_((15, 15))

    #def isFlipped(self):
    #    # 描写原点は左下です、コレを有効にすると左上が原点になる
    #    return True

class MyWindow(AppKit.NSWindow):
    def init(self):
        rect = AppKit.NSMakeRect(0, 0, 320, 240)
        objc.super(MyWindow, self).initWithContentRect_styleMask_backing_defer_(
            rect,
            AppKit.NSTitledWindowMask |
            AppKit.NSClosableWindowMask |
            AppKit.NSResizableWindowMask |
            AppKit.NSMiniaturizableWindowMask,
            AppKit.NSBackingStoreBuffered, False)
        # NSView
        self.canvas = MyView.alloc().initWithFrame_(rect)
        self.contentView().addSubview_(self.canvas)
        # etc
        self.setTitle_('ウメジロー')
        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()
図形は省いた。
だって普通画像を使うよね。

umejiro.webp

CoreGraphics を使う
PyObjC からでも CoreGraphics を直接使えます、C 言語と同じだから面倒だけど。
CGContext というものがあるので他の API を知っているなら解りやすいかも。
プリフィクスが長くなるので as を使って省略するといいでしょう。
import AppKit, objc
from Quartz import CoreGraphics as cg

class MyView(AppKit.NSView):
    def initWithFrame_(self, rect):
        objc.super(MyView, self).initWithFrame_(rect)
        return self

    def drawRect_(self, rect):
        # CoreGraphics 直描写
        ctx = cg.NSGraphicsContext.currentContext().CGContext()
        # 背景の塗りつぶし
        rgb = cg.CGColorCreateGenericRGB(0.5, 1.0, 0.7, 1)
        cg.CGContextSetFillColorWithColor(ctx, rgb)
        cg.CGContextFillRect(ctx, rect)
        # moveto, lineto
        cg.CGContextSetRGBStrokeColor(ctx, 1.0, 0, 0, 1)
        cg.CGContextSetLineWidth(ctx, 10)
        cg.CGContextMoveToPoint(ctx, 100.0, 100.0)
        cg.CGContextAddLineToPoint(ctx, 200, 200)
        # まとめることもできる
        cg.CGContextAddLines(ctx, [(50, 100), (50, 200)], 2)
        # 描写
        cg.CGContextDrawPath(ctx, cg.kCGPathStroke)

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