PyObjC SceneKit Framework
PyObjC is the bridge between Python and the Objective-C programming language, used to develop applications for macOS. This specific package, `pyobjc-framework-scenekit` (currently at version 12.1), provides Python wrappers for Apple's SceneKit framework, enabling 3D graphics and game development. PyObjC generally follows the macOS SDK release cycle for updates.
Warnings
- breaking Python 3.9 support was dropped in PyObjC 12.0. Ensure your Python environment is 3.10 or later.
- breaking Python 3.8 support was dropped in PyObjC 11.0. Ensure your Python environment is 3.9 or later.
- gotcha PyObjC 11.1 changed how it models initializer methods ('init' family) to align with Clang's Automatic Reference Counting (ARC) documentation. Methods in the 'init' family now correctly steal a reference to `self` and return a new one.
- gotcha In PyObjC 10.3, support for calling `__init__` on Python classes that implement `__new__` was temporarily broken, then partially reintroduced in 10.3.1. If a class or its superclass has a user-implemented `__new__`, `__init__` can be used. Otherwise, if relying on PyObjC's `__new__`, `__init__` cannot be used.
- gotcha PyObjC 12.1 automatically disables Key-Value Observing (KVO) usage for subclasses of `NSProxy` defined in Python to fix `SystemError` issues.
- gotcha SceneKit methods that expect or return `vector_float3` or `vector_float4` arguments are not fully available from Python due to limitations in PyObjC's core bridge. This includes `SCNVector3ToFloat3` and `SCNVector3FromFloat3`.
- gotcha PyObjC framework wrappers, including SceneKit, do not include their own documentation. Refer to Apple's official SceneKit documentation for API details and usage.
Install
-
pip install pyobjc-framework-scenekit -
pip install 'pyobjc[allbindings]'
Imports
- SceneKit
import SceneKit
- Cocoa
from Cocoa import NSApplication, NSWindow, NSView
- AppHelper
from PyObjCTools import AppHelper
Quickstart
import SceneKit
from Cocoa import NSApplication, NSWindow, NSView, NSRect, NSMakeRect
from PyObjCTools import AppHelper
import objc
class AppDelegate(objc.NSObject):
def applicationDidFinishLaunching_(self, aNotification):
print("SceneKit app launched!")
# Create a window
rect = NSMakeRect(100, 100, 640, 480)
self.window = NSWindow.alloc().initWithContentRect_styleMask_backing_defer_(
rect,
(1 << 0) | (1 << 1) | (1 << 2), # NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable
2, # NSBackingStoreBuffered
False
)
self.window.setTitle_("PyObjC SceneKit Cube")
# Create an SCNView (SceneKit view)
self.scnView = SceneKit.SCNView.alloc().initWithFrame_(rect)
self.window.setContentView_(self.scnView)
# Create a scene
scene = SceneKit.SCNScene.scene()
self.scnView.setScene_(scene)
# Create a camera and add it to a node
camera = SceneKit.SCNCamera.camera()
cameraNode = SceneKit.SCNNode.node()
cameraNode.setCamera_(camera)
cameraNode.setPosition_(SceneKit.SCNVector3(0, 0, 15))
scene.rootNode().addChildNode_(cameraNode)
self.scnView.setPointOfView_(cameraNode)
# Create a light and add it to a node
light = SceneKit.SCNLight.light()
light.setType_(SceneKit.SCNLightTypeOmni)
lightNode = SceneKit.SCNNode.node()
lightNode.setLight_(light)
lightNode.setPosition_(SceneKit.SCNVector3(0, 10, 10))
scene.rootNode().addChildNode_(lightNode)
# Create an ambient light
ambientLight = SceneKit.SCNLight.light()
ambientLight.setType_(SceneKit.SCNLightTypeAmbient)
ambientLight.setColor_(SceneKit.SCNColor.colorWithRed_green_blue_alpha_(0.1, 0.1, 0.1, 1.0))
ambientLightNode = SceneKit.SCNNode.node()
ambientLightNode.setLight_(ambientLight)
scene.rootNode().addChildNode_(ambientLightNode)
# Create a cube geometry
box = SceneKit.SCNBox.boxWithWidth_height_length_chamferRadius_(5.0, 5.0, 5.0, 0.5)
boxNode = SceneKit.SCNNode.nodeWithGeometry_(box)
scene.rootNode().addChildNode_(boxNode)
# Allow the user to manipulate the camera
self.scnView.setAllowsCameraControl_(True)
self.window.makeKeyAndOrderFront_(None)
def main():
app = NSApplication.sharedApplication()
delegate = AppDelegate.alloc().init()
app.setDelegate_(delegate)
AppHelper.runEventLoop()
if __name__ == '__main__':
main()