Paepoi

Paepoi » PyObjC Tips » NSView

NSView

最終更新日 2019.04.21

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

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

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

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

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

#!/usr/bin/env python3

from AppKit import *

PIC = '五条川.jpg' # 手持ち画像に書き換えしてください
RECT = ((0, 0), (320, 240))
wins = []

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

    def drawRect_(self, rect):
        # 背景の塗りつぶし
        NSColor.darkGrayColor().set()
        NSRectFill(rect)
        # 背景から 5px オフセットして枠っぽく
        NSColor.brownColor().set()
        rect1 = NSInsetRect(rect, 5, 5)
        NSRectFill(rect1)
        # 線
        NSColor.whiteColor().set()
        path = NSBezierPath.bezierPath()
        path.setLineWidth_(10)
        path.moveToPoint_((50, 100))
        path.lineToPoint_((200, 200))
        path.stroke()
        # 文字列
        bg_color = NSColor.colorWithSRGBRed_green_blue_alpha_(0.4, 1.0, 1.0, 0.5)
        text = NSAttributedString.alloc().initWithString_attributes_(
            '描写原点は左下です',
            {
                NSForegroundColorAttributeName: NSColor.whiteColor(),
                NSBackgroundColorAttributeName: bg_color
            })
        NSColor.blueColor().set()
        text.drawAtPoint_((15, 15))
        # 画像
        image = NSImage.alloc().initWithContentsOfFile_(PIC)
        image.drawInRect_(((200, 50), (90, 120)))

    #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_('NSView Test')
        self.setDelegate_(self)
        # NSView
        self.canvas = MyView.alloc().initWithFrame_(RECT)
        self.contentView().addSubview_(self.canvas)
        #
        return self

    def windowDidResize_(self, sender):
        # GTK+ や WPF のように追従してくれないので
        self.canvas.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/nsview.png

もっと詳しい描写に関しては以下がすごく参考になる。
Cocoa Dev Central: Cocoa Graphics with Quartz
Cocoa Dev Central: Cocoa Graphics with Quartz II
日本人のページはどれもイマイチ、細切ればかり。

CoreGraphics を使う
PyObjC からでも CoreGraphics を直接使えます。
CGContext というものがあるので他の API を知っているなら解りやすいかも。
C 言語と同じだから面倒だけど。
from AppKit import *
from Quartz.CoreGraphics import * # 必須

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

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

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