PyObjC: MetalKit Framework

12.1 · active · verified Tue Apr 14

PyObjC provides Pythonic bindings to macOS Cocoa frameworks, enabling Python applications to interact with native macOS APIs. `pyobjc-framework-metalkit` specifically offers wrappers for the MetalKit framework, simplifying the use of Apple's low-overhead 3D graphics API (Metal) from Python. The current version is 12.1, and PyObjC maintains a regular release cadence, often aligning with macOS SDK updates and Python version support.

Warnings

Install

Imports

Quickstart

This quickstart demonstrates how to initialize a Metal device, create an `MTKView`, and set up a basic rendering delegate using PyObjC. It showcases the integration of MetalKit with a minimal Cocoa application structure. Run this on macOS to see a window displaying a continuously cleared MetalKIt view. Remember to close the window to terminate the Python script.

import objc
from AppKit import NSApplication, NSWindow, NSView, NSApp, NSMakeRect, NSBackingStoreBuffered, NSWindowStyleMaskTitled, NSWindowStyleMaskClosable
from MetalKit import MTKView
from Metal import MTLCreateSystemDefaultDevice, MTLPixelFormatBGRA8Unorm
from PyObjCTools import AppHelper

# Define an MTKViewDelegate for basic rendering
class PyMetalViewDelegate(objc.NSObject):
    def drawInMTKView_(self, view):
        # In a real app, you'd perform complex Metal rendering here.
        # This example simply clears the view to a solid color.
        commandQueue = view.device().newCommandQueue()
        commandBuffer = commandQueue.commandBuffer()
        renderPassDescriptor = view.currentRenderPassDescriptor()
        
        if renderPassDescriptor:
            # Set clear color and load action
            renderPassDescriptor.colorAttachments().objectAtIndex_(0).setClearColor_(
                objc.runtime.struct(red=0.1, green=0.3, blue=0.5, alpha=1.0, _pyobjc_structs_type_name="MTLClearColor")
            )
            renderPassDescriptor.colorAttachments().objectAtIndex_(0).setLoadAction_(0) # MTLLoadActionClear
            
            renderEncoder = commandBuffer.renderCommandEncoderWithDescriptor_(renderPassDescriptor)
            renderEncoder.endEncoding()
            commandBuffer.presentDrawable_(view.currentDrawable())
            commandBuffer.commit()

    def mtkView_drawableSizeWillChange_(self, view, size):
        # Respond to view size changes if needed for Metal resources
        pass

class PyMetalAppDelegate(objc.NSObject):
    def applicationDidFinishLaunching_(self, notification):
        device = MTLCreateSystemDefaultDevice()
        if device is None:
            print("Metal is not supported on this device.")
            NSApp().terminate_(self)
            return

        window_rect = NSMakeRect(0, 0, 640, 480)
        window = NSWindow.alloc().initWithContentRect_styleMask_backing_defer_(
            window_rect,
            NSWindowStyleMaskTitled | NSWindowStyleMaskClosable,
            NSBackingStoreBuffered,
            False
        )
        window.setTitle_("PyObjC MetalKit Quickstart")
        window.makeKeyAndOrderFront_(None)

        mtk_view = MTKView.alloc().initWithFrame_device_(window_rect, device)
        mtk_view.setColorPixelFormat_(MTLPixelFormatBGRA8Unorm) # Using enum value
        mtk_view.setClearColor_(objc.runtime.struct(red=0.2, green=0.4, blue=0.6, alpha=1.0, _pyobjc_structs_type_name="MTLClearColor"))
        mtk_view.setPaused_(False) # Ensure view is not paused
        mtk_view.setEnableSetNeedsDisplay_(False) # Allow continuous drawing

        # Set the delegate for rendering
        self.metal_delegate = PyMetalViewDelegate.alloc().init()
        mtk_view.setDelegate_(self.metal_delegate)

        window.contentView().addSubview_(mtk_view)
        self.window = window
        self.mtk_view = mtk_view
        print(f"MTKView created with device: {device.name()}")
        print("Close the window to terminate the application.")

if __name__ == '__main__':
    # AppHelper.runEventLoop simplifies running a Cocoa application by managing NSApplication and its lifecycle.
    AppHelper.runEventLoop(mainLoop=PyMetalAppDelegate.alloc().init())

view raw JSON →