PyDoc: use complete sentences in comments for templates & examples

This commit is contained in:
Campbell Barton
2025-04-02 23:46:58 +00:00
parent d001e143a9
commit b2c57fd877
58 changed files with 181 additions and 179 deletions

View File

@@ -8,15 +8,15 @@ This script shows how to use the classes: :class:`Device`, :class:`Sound` and
import aud import aud
device = aud.Device() device = aud.Device()
# load sound file (it can be a video file with audio) # Load sound file (it can be a video file with audio).
sound = aud.Sound('music.ogg') sound = aud.Sound('music.ogg')
# play the audio, this return a handle to control play/pause # Play the audio, this return a handle to control play/pause.
handle = device.play(sound) handle = device.play(sound)
# if the audio is not too big and will be used often you can buffer it # If the audio is not too big and will be used often you can buffer it.
sound_buffered = aud.Sound.cache(sound) sound_buffered = aud.Sound.cache(sound)
handle_buffered = device.play(sound_buffered) handle_buffered = device.play(sound_buffered)
# stop the sounds (otherwise they play until their ends) # Stop the sounds (otherwise they play until their ends).
handle.stop() handle.stop()
handle_buffered.stop() handle_buffered.stop()

View File

@@ -5,7 +5,7 @@ Hello World Text Example
Example of using the blf module. For this module to work we Example of using the blf module. For this module to work we
need to use the GPU module :mod:`gpu` as well. need to use the GPU module :mod:`gpu` as well.
""" """
# import stand alone modules # Import stand alone modules.
import blf import blf
import bpy import bpy
@@ -20,21 +20,21 @@ def init():
import os import os
# Create a new font object, use external TTF file. # Create a new font object, use external TTF file.
font_path = bpy.path.abspath('//Zeyada.ttf') font_path = bpy.path.abspath('//Zeyada.ttf')
# Store the font indice - to use later. # Store the font index - to use later.
if os.path.exists(font_path): if os.path.exists(font_path):
font_info["font_id"] = blf.load(font_path) font_info["font_id"] = blf.load(font_path)
else: else:
# Default font. # Default font.
font_info["font_id"] = 0 font_info["font_id"] = 0
# set the font drawing routine to run every frame # Set the font drawing routine to run every frame.
font_info["handler"] = bpy.types.SpaceView3D.draw_handler_add( font_info["handler"] = bpy.types.SpaceView3D.draw_handler_add(
draw_callback_px, (None, None), 'WINDOW', 'POST_PIXEL') draw_callback_px, (None, None), 'WINDOW', 'POST_PIXEL')
def draw_callback_px(self, context): def draw_callback_px(self, context):
"""Draw on the viewports""" """Draw on the viewports"""
# BLF drawing routine # BLF drawing routine.
font_id = font_info["font_id"] font_id = font_info["font_id"]
blf.position(font_id, 2, 80, 0) blf.position(font_id, 2, 80, 0)
blf.size(font_id, 50.0) blf.size(font_id, 50.0)

View File

@@ -14,7 +14,7 @@ Below is an example of a mesh that is altered from a handler:
def frame_change_pre(scene): def frame_change_pre(scene):
# A triangle that shifts in the z direction # A triangle that shifts in the z direction.
zshift = scene.frame_current * 0.1 zshift = scene.frame_current * 0.1
vertices = [(-1, -1, zshift), (1, -1, zshift), (0, 1, zshift)] vertices = [(-1, -1, zshift), (1, -1, zshift), (0, 1, zshift)]
triangles = [(0, 1, 2)] triangles = [(0, 1, 2)]

View File

@@ -1,23 +1,23 @@
import bpy import bpy
# print all objects # Print all objects.
for obj in bpy.data.objects: for obj in bpy.data.objects:
print(obj.name) print(obj.name)
# print all scene names in a list # Print all scene names in a list.
print(bpy.data.scenes.keys()) print(bpy.data.scenes.keys())
# remove mesh Cube # Remove mesh Cube.
if "Cube" in bpy.data.meshes: if "Cube" in bpy.data.meshes:
mesh = bpy.data.meshes["Cube"] mesh = bpy.data.meshes["Cube"]
print("removing mesh", mesh) print("removing mesh", mesh)
bpy.data.meshes.remove(mesh) bpy.data.meshes.remove(mesh)
# write images into a file next to the blend # Write images into a file next to the blend.
import os import os
with open(os.path.splitext(bpy.data.filepath)[0] + ".txt", 'w') as fs: with open(os.path.splitext(bpy.data.filepath)[0] + ".txt", 'w') as fs:
for image in bpy.data.images: for image in bpy.data.images:

View File

@@ -29,6 +29,6 @@ The execution context is one of:
- ``EXEC_SCREEN`` - ``EXEC_SCREEN``
""" """
# collection add popup # Collection add popup.
import bpy import bpy
bpy.ops.object.collection_instance_add('INVOKE_DEFAULT') bpy.ops.object.collection_instance_add('INVOKE_DEFAULT')

View File

@@ -45,10 +45,10 @@ Each of these arguments is optional, but must be given in the order above.
""" """
import bpy import bpy
# calling an operator # Calling an operator.
bpy.ops.mesh.subdivide(number_cuts=3, smoothness=0.5) bpy.ops.mesh.subdivide(number_cuts=3, smoothness=0.5)
# check poll() to avoid exception. # Check poll() to avoid exception.
if bpy.ops.object.mode_set.poll(): if bpy.ops.object.mode_set.poll():
bpy.ops.object.mode_set(mode='EDIT') bpy.ops.object.mode_set(mode='EDIT')

View File

@@ -19,7 +19,7 @@ bpy.utils.register_class(MaterialSettings)
bpy.types.Material.my_settings = bpy.props.PointerProperty(type=MaterialSettings) bpy.types.Material.my_settings = bpy.props.PointerProperty(type=MaterialSettings)
# test the new settings work # Test the new settings work.
material = bpy.data.materials[0] material = bpy.data.materials[0]
material.my_settings.my_int = 5 material.my_settings.my_int = 5

View File

@@ -28,7 +28,7 @@ def set_float(self, value):
bpy.types.Scene.test_float = bpy.props.FloatProperty(get=get_float, set=set_float) bpy.types.Scene.test_float = bpy.props.FloatProperty(get=get_float, set=set_float)
# Read-only string property, returns the current date # Read-only string property, returns the current date.
def get_date(self): def get_date(self):
import datetime import datetime
return str(datetime.datetime.now()) return str(datetime.datetime.now())
@@ -37,9 +37,10 @@ def get_date(self):
bpy.types.Scene.test_date = bpy.props.StringProperty(get=get_date) bpy.types.Scene.test_date = bpy.props.StringProperty(get=get_date)
# Boolean array. Set function stores a single boolean value, returned as the second component. # Boolean array.
# Array getters must return a list or tuple # - Set function stores a single boolean value, returned as the second component.
# Array size must match the property vector size exactly # - Array getters must return a list or tuple.
# - Array size must match the property vector size exactly.
def get_array(self): def get_array(self):
return (True, self["somebool"]) return (True, self["somebool"])
@@ -82,7 +83,7 @@ print('test_float:', scene.test_float)
scene.test_array = (True, False) scene.test_array = (True, False)
print('test_array:', tuple(scene.test_array)) print('test_array:', tuple(scene.test_array))
# scene.test_date = "blah" # this would fail, property is read-only # scene.test_date = "blah" # This would fail, property is read-only.
print('test_date:', scene.test_date) print('test_date:', scene.test_date)
scene.test_enum = 'BLUE' scene.test_enum = 'BLUE'

View File

@@ -2,33 +2,33 @@ import bpy
filepath = "//link_library.blend" filepath = "//link_library.blend"
# load a single scene we know the name of. # Load a single scene we know the name of.
with bpy.data.libraries.load(filepath) as (data_from, data_to): with bpy.data.libraries.load(filepath) as (data_from, data_to):
data_to.scenes = ["Scene"] data_to.scenes = ["Scene"]
# load all meshes # Load all meshes.
with bpy.data.libraries.load(filepath) as (data_from, data_to): with bpy.data.libraries.load(filepath) as (data_from, data_to):
data_to.meshes = data_from.meshes data_to.meshes = data_from.meshes
# link all objects starting with 'A' # Link all objects starting with "A".
with bpy.data.libraries.load(filepath, link=True) as (data_from, data_to): with bpy.data.libraries.load(filepath, link=True) as (data_from, data_to):
data_to.objects = [name for name in data_from.objects if name.startswith("A")] data_to.objects = [name for name in data_from.objects if name.startswith("A")]
# append everything # Append everything.
with bpy.data.libraries.load(filepath) as (data_from, data_to): with bpy.data.libraries.load(filepath) as (data_from, data_to):
for attr in dir(data_to): for attr in dir(data_to):
setattr(data_to, attr, getattr(data_from, attr)) setattr(data_to, attr, getattr(data_from, attr))
# the loaded objects can be accessed from 'data_to' outside of the context # The loaded objects can be accessed from 'data_to' outside of the context
# since loading the data replaces the strings for the datablocks or None # since loading the data replaces the strings for the data-blocks or None
# if the datablock could not be loaded. # if the data-block could not be loaded.
with bpy.data.libraries.load(filepath) as (data_from, data_to): with bpy.data.libraries.load(filepath) as (data_from, data_to):
data_to.meshes = data_from.meshes data_to.meshes = data_from.meshes
# now operate directly on the loaded data # Now operate directly on the loaded data.
for mesh in data_to.meshes: for mesh in data_to.meshes:
if mesh is not None: if mesh is not None:
print(mesh.name) print(mesh.name)

View File

@@ -2,17 +2,17 @@ import bpy
filepath = "//new_library.blend" filepath = "//new_library.blend"
# write selected objects and their data to a blend file # Write selected objects and their data to a blend file.
data_blocks = set(bpy.context.selected_objects) data_blocks = set(bpy.context.selected_objects)
bpy.data.libraries.write(filepath, data_blocks) bpy.data.libraries.write(filepath, data_blocks)
# write all meshes starting with a capital letter and # Write all meshes starting with a capital letter and
# set them with fake-user enabled so they aren't lost on re-saving # set them with fake-user enabled so they aren't lost on re-saving.
data_blocks = {mesh for mesh in bpy.data.meshes if mesh.name[:1].isupper()} data_blocks = {mesh for mesh in bpy.data.meshes if mesh.name[:1].isupper()}
bpy.data.libraries.write(filepath, data_blocks, fake_user=True) bpy.data.libraries.write(filepath, data_blocks, fake_user=True)
# write all materials, textures and node groups to a library # Write all materials, textures and node groups to a library.
data_blocks = {*bpy.data.materials, *bpy.data.textures, *bpy.data.node_groups} data_blocks = {*bpy.data.materials, *bpy.data.textures, *bpy.data.node_groups}
bpy.data.libraries.write(filepath, data_blocks) bpy.data.libraries.write(filepath, data_blocks)

View File

@@ -16,7 +16,7 @@ def set_pose_matrices(obj, matrix_map):
# pbone.matrix = matrix # pbone.matrix = matrix
# bpy.context.view_layer.update() # bpy.context.view_layer.update()
# Compute and assign local matrix, using the new parent matrix # Compute and assign local matrix, using the new parent matrix.
if pbone.parent: if pbone.parent:
pbone.matrix_basis = pbone.bone.convert_local_to_pose( pbone.matrix_basis = pbone.bone.convert_local_to_pose(
matrix, matrix,
@@ -32,7 +32,7 @@ def set_pose_matrices(obj, matrix_map):
invert=True invert=True
) )
else: else:
# Compute the updated pose matrix from local and new parent matrix # Compute the updated pose matrix from local and new parent matrix.
if pbone.parent: if pbone.parent:
matrix = pbone.bone.convert_local_to_pose( matrix = pbone.bone.convert_local_to_pose(
pbone.matrix_basis, pbone.matrix_basis,
@@ -46,11 +46,11 @@ def set_pose_matrices(obj, matrix_map):
pbone.bone.matrix_local, pbone.bone.matrix_local,
) )
# Recursively process children, passing the new matrix through # Recursively process children, passing the new matrix through.
for child in pbone.children: for child in pbone.children:
rec(child, matrix) rec(child, matrix)
# Scan all bone trees from their roots # Scan all bone trees from their roots.
for pbone in obj.pose.bones: for pbone in obj.pose.bones:
if not pbone.parent: if not pbone.parent:
rec(pbone, None) rec(pbone, None)

View File

@@ -19,7 +19,7 @@ class OBJECT_OT_simple_exporter(bpy.types.Operator):
depsgraph = context.evaluated_depsgraph_get() depsgraph = context.evaluated_depsgraph_get()
for object_instance in depsgraph.object_instances: for object_instance in depsgraph.object_instances:
if not self.is_object_instance_from_selected(object_instance): if not self.is_object_instance_from_selected(object_instance):
# We only export selected objects # We only export selected objects.
continue continue
# NOTE: This will create a mesh for every instance, which is not ideal at all. In # NOTE: This will create a mesh for every instance, which is not ideal at all. In
# reality destination format will support some sort of instancing mechanism, so the # reality destination format will support some sort of instancing mechanism, so the

View File

@@ -64,7 +64,7 @@ class ShaderScriptImport(bpy.types.Operator, ImportHelper):
text_node.filepath = filepath text_node.filepath = filepath
text_node.location = Vector((x, y)) text_node.location = Vector((x, y))
# Set the node's title to the file name # Set the node's title to the file name.
if self.set_label: if self.set_label:
text_node.label = file.name text_node.label = file.name

View File

@@ -41,14 +41,14 @@ class CustomHydraRenderEngine(bpy.types.HydraRenderEngine):
# by implementing the methods like this. # by implementing the methods like this.
def update(self, data, depsgraph): def update(self, data, depsgraph):
super().update(data, depsgraph) super().update(data, depsgraph)
# Do extra work here # Do extra work here.
def update_render_passes(self, scene, render_layer): def update_render_passes(self, scene, render_layer):
if render_layer.use_pass_z: if render_layer.use_pass_z:
self.register_pass(scene, render_layer, 'Depth', 1, 'Z', 'VALUE') self.register_pass(scene, render_layer, 'Depth', 1, 'Z', 'VALUE')
# Registration # Registration.
def register(): def register():
bpy.utils.register_class(CustomHydraRenderEngine) bpy.utils.register_class(CustomHydraRenderEngine)

View File

@@ -6,12 +6,11 @@ count is used to prevent data being removed when it is used.
# This example shows what _not_ to do, and will crash Blender. # This example shows what _not_ to do, and will crash Blender.
import bpy import bpy
# object which is in the scene. # Object which is in the scene.
obj = bpy.data.objects["Cube"] obj = bpy.data.objects["Cube"]
# without this, removal would raise an error. # Without this, removal would raise an error.
obj.user_clear() obj.user_clear()
# runs without an exception # Runs without an exception but will crash on redraw.
# but will crash on redraw.
bpy.data.objects.remove(obj) bpy.data.objects.remove(obj)

View File

@@ -34,5 +34,5 @@ class SubMenu(bpy.types.Menu):
bpy.utils.register_class(SubMenu) bpy.utils.register_class(SubMenu)
# test call to display immediately. # Test call to display immediately.
bpy.ops.wm.call_menu(name="OBJECT_MT_select_submenu") bpy.ops.wm.call_menu(name="OBJECT_MT_select_submenu")

View File

@@ -29,12 +29,12 @@ class AddPresetObjectDisplay(AddPresetBase, Operator):
bl_label = "Add Object Display Preset" bl_label = "Add Object Display Preset"
preset_menu = "OBJECT_MT_display_presets" preset_menu = "OBJECT_MT_display_presets"
# variable used for all preset values # Variable used for all preset values.
preset_defines = [ preset_defines = [
"obj = bpy.context.object" "obj = bpy.context.object"
] ]
# properties to store in the preset # Properties to store in the preset.
preset_values = [ preset_values = [
"obj.display_type", "obj.display_type",
"obj.show_bounds", "obj.show_bounds",
@@ -44,11 +44,11 @@ class AddPresetObjectDisplay(AddPresetBase, Operator):
"obj.show_wire", "obj.show_wire",
] ]
# where to store the preset # Where to store the preset.
preset_subdir = "object/display" preset_subdir = "object/display"
# Display into an existing panel # Display into an existing panel.
def panel_func(self, context): def panel_func(self, context):
layout = self.layout layout = self.layout

View File

@@ -38,5 +38,5 @@ class BasicMenu(bpy.types.Menu):
bpy.utils.register_class(BasicMenu) bpy.utils.register_class(BasicMenu)
# test call to display immediately. # Test call to display immediately.
bpy.ops.wm.call_menu(name="OBJECT_MT_select_test") bpy.ops.wm.call_menu(name="OBJECT_MT_select_test")

View File

@@ -34,7 +34,7 @@ uv_layer = me.uv_layers.active.data
for poly in me.polygons: for poly in me.polygons:
print("Polygon index: {:d}, length: {:d}".format(poly.index, poly.loop_total)) print("Polygon index: {:d}, length: {:d}".format(poly.index, poly.loop_total))
# range is used here to show how the polygons reference loops, # Range is used here to show how the polygons reference loops,
# for convenience 'poly.loop_indices' can be used instead. # for convenience 'poly.loop_indices' can be used instead.
for loop_index in range(poly.loop_start, poly.loop_start + poly.loop_total): for loop_index in range(poly.loop_start, poly.loop_start + poly.loop_total):
print(" Vertex: {:d}".format(me.loops[loop_index].vertex_index)) print(" Vertex: {:d}".format(me.loops[loop_index].vertex_index))

View File

@@ -10,10 +10,10 @@ import bpy
view_layer = bpy.context.view_layer view_layer = bpy.context.view_layer
# Create new light datablock. # Create new light data-block.
light_data = bpy.data.lights.new(name="New Light", type='POINT') light_data = bpy.data.lights.new(name="New Light", type='POINT')
# Create new object with our light datablock. # Create new object with our light data-block.
light_object = bpy.data.objects.new(name="New Light", object_data=light_data) light_object = bpy.data.objects.new(name="New Light", object_data=light_data)
# Link light object to the active collection of current view layer, # Link light object to the active collection of current view layer,

View File

@@ -39,8 +39,8 @@ class SimpleMouseOperator(bpy.types.Operator):
y: bpy.props.IntProperty() y: bpy.props.IntProperty()
def execute(self, context): def execute(self, context):
# rather than printing, use the report function, # Rather than printing, use the report function,
# this way the message appears in the header, # this way the message appears in the header.
self.report({'INFO'}, "Mouse coords are {:d} {:d}".format(self.x, self.y)) self.report({'INFO'}, "Mouse coords are {:d} {:d}".format(self.x, self.y))
return {'FINISHED'} return {'FINISHED'}
@@ -55,13 +55,13 @@ def menu_func(self, context):
self.layout.operator(SimpleMouseOperator.bl_idname, text="Simple Mouse Operator") self.layout.operator(SimpleMouseOperator.bl_idname, text="Simple Mouse Operator")
# Register and add to the view menu (required to also use F3 search "Simple Mouse Operator" for quick access) # Register and add to the view menu (required to also use F3 search "Simple Mouse Operator" for quick access).
bpy.utils.register_class(SimpleMouseOperator) bpy.utils.register_class(SimpleMouseOperator)
bpy.types.VIEW3D_MT_view.append(menu_func) bpy.types.VIEW3D_MT_view.append(menu_func)
# Test call to the newly defined operator. # Test call to the newly defined operator.
# Here we call the operator and invoke it, meaning that the settings are taken # Here we call the operator and invoke it,
# from the mouse. # meaning that the settings are taken from the mouse.
bpy.ops.wm.mouse_position('INVOKE_DEFAULT') bpy.ops.wm.mouse_position('INVOKE_DEFAULT')
# Another test call, this time call execute() directly with pre-defined settings. # Another test call, this time call execute() directly with pre-defined settings.

View File

@@ -43,10 +43,10 @@ def menu_func(self, context):
self.layout.operator(ExportSomeData.bl_idname, text="Text Export Operator") self.layout.operator(ExportSomeData.bl_idname, text="Text Export Operator")
# Register and add to the file selector (required to also use F3 search "Text Export Operator" for quick access) # Register and add to the file selector (required to also use F3 search "Text Export Operator" for quick access).
bpy.utils.register_class(ExportSomeData) bpy.utils.register_class(ExportSomeData)
bpy.types.TOPBAR_MT_file_export.append(menu_func) bpy.types.TOPBAR_MT_file_export.append(menu_func)
# test call # Test call.
bpy.ops.export.some_data('INVOKE_DEFAULT') bpy.ops.export.some_data('INVOKE_DEFAULT')

View File

@@ -32,7 +32,7 @@ def menu_func(self, context):
self.layout.operator(DialogOperator.bl_idname, text="Dialog Operator") self.layout.operator(DialogOperator.bl_idname, text="Dialog Operator")
# Register and add to the object menu (required to also use F3 search "Dialog Operator" for quick access) # Register and add to the object menu (required to also use F3 search "Dialog Operator" for quick access).
bpy.utils.register_class(DialogOperator) bpy.utils.register_class(DialogOperator)
bpy.types.VIEW3D_MT_object.append(menu_func) bpy.types.VIEW3D_MT_object.append(menu_func)

View File

@@ -51,5 +51,5 @@ def menu_func(self, context):
bpy.utils.register_class(CustomDrawOperator) bpy.utils.register_class(CustomDrawOperator)
bpy.types.VIEW3D_MT_object.append(menu_func) bpy.types.VIEW3D_MT_object.append(menu_func)
# test call # Test call.
bpy.ops.object.custom_draw('INVOKE_DEFAULT') bpy.ops.object.custom_draw('INVOKE_DEFAULT')

View File

@@ -43,12 +43,12 @@ class ModalOperator(bpy.types.Operator):
return {'FINISHED'} return {'FINISHED'}
def modal(self, context, event): def modal(self, context, event):
if event.type == 'MOUSEMOVE': # Apply if event.type == 'MOUSEMOVE': # Apply.
self.value = event.mouse_x self.value = event.mouse_x
self.execute(context) self.execute(context)
elif event.type == 'LEFTMOUSE': # Confirm elif event.type == 'LEFTMOUSE': # Confirm.
return {'FINISHED'} return {'FINISHED'}
elif event.type in {'RIGHTMOUSE', 'ESC'}: # Cancel elif event.type in {'RIGHTMOUSE', 'ESC'}: # Cancel.
# Revert all changes that have been made # Revert all changes that have been made
context.object.location.x = self.init_loc_x context.object.location.x = self.init_loc_x
return {'CANCELLED'} return {'CANCELLED'}
@@ -73,5 +73,5 @@ def menu_func(self, context):
bpy.utils.register_class(ModalOperator) bpy.utils.register_class(ModalOperator)
bpy.types.VIEW3D_MT_object.append(menu_func) bpy.types.VIEW3D_MT_object.append(menu_func)
# test call # Test call.
bpy.ops.object.modal_operator('INVOKE_DEFAULT') bpy.ops.object.modal_operator('INVOKE_DEFAULT')

View File

@@ -37,9 +37,9 @@ def menu_func(self, context):
self.layout.operator(SearchEnumOperator.bl_idname, text="Search Enum Operator") self.layout.operator(SearchEnumOperator.bl_idname, text="Search Enum Operator")
# Register and add to the object menu (required to also use F3 search "Search Enum Operator" for quick access) # Register and add to the object menu (required to also use F3 search "Search Enum Operator" for quick access).
bpy.utils.register_class(SearchEnumOperator) bpy.utils.register_class(SearchEnumOperator)
bpy.types.VIEW3D_MT_object.append(menu_func) bpy.types.VIEW3D_MT_object.append(menu_func)
# test call # Test call.
bpy.ops.object.search_enum_operator('INVOKE_DEFAULT') bpy.ops.object.search_enum_operator('INVOKE_DEFAULT')

View File

@@ -12,7 +12,7 @@ def bbone_deform_matrix(pose_bone, point):
pose1 = pose_bone.bbone_segment_matrix(index, rest=False) pose1 = pose_bone.bbone_segment_matrix(index, rest=False)
deform1 = pose1 @ rest1.inverted() deform1 = pose1 @ rest1.inverted()
# bbone_segment_index ensures that index + 1 is always valid # `bbone_segment_index` ensures that index + 1 is always valid
rest2 = pose_bone.bbone_segment_matrix(index + 1, rest=True) rest2 = pose_bone.bbone_segment_matrix(index + 1, rest=True)
pose2 = pose_bone.bbone_segment_matrix(index + 1, rest=False) pose2 = pose_bone.bbone_segment_matrix(index + 1, rest=False)
deform2 = pose2 @ rest2.inverted() deform2 = pose2 @ rest2.inverted()

View File

@@ -37,5 +37,5 @@ bpy.utils.register_class(MyPropertyGroup)
bpy.types.Object.my_prop_grp = bpy.props.PointerProperty(type=MyPropertyGroup) bpy.types.Object.my_prop_grp = bpy.props.PointerProperty(type=MyPropertyGroup)
# test this worked # Test this worked.
bpy.data.objects[0].my_prop_grp.custom_1 = 22.0 bpy.data.objects[0].my_prop_grp.custom_1 = 22.0

View File

@@ -125,14 +125,14 @@ class CustomDrawData:
def __init__(self, dimensions): def __init__(self, dimensions):
import gpu import gpu
# Generate dummy float image buffer # Generate dummy float image buffer.
self.dimensions = dimensions self.dimensions = dimensions
width, height = dimensions width, height = dimensions
pixels = width * height * array.array('f', [0.1, 0.2, 0.1, 1.0]) pixels = width * height * array.array('f', [0.1, 0.2, 0.1, 1.0])
pixels = gpu.types.Buffer('FLOAT', width * height * 4, pixels) pixels = gpu.types.Buffer('FLOAT', width * height * 4, pixels)
# Generate texture # Generate texture.
self.texture = gpu.types.GPUTexture((width, height), format='RGBA16F', data=pixels) self.texture = gpu.types.GPUTexture((width, height), format='RGBA16F', data=pixels)
# Note: This is just a didactic example. # Note: This is just a didactic example.
@@ -167,7 +167,7 @@ def get_panels():
def register(): def register():
# Register the RenderEngine # Register the RenderEngine.
bpy.utils.register_class(CustomRenderEngine) bpy.utils.register_class(CustomRenderEngine)
for panel in get_panels(): for panel in get_panels():

View File

@@ -60,7 +60,7 @@ class UIListPanelExample1(bpy.types.Panel):
obj = context.object obj = context.object
# template_list now takes two new args. # `template_list` now takes two new arguments.
# The first one is the identifier of the registered UIList to use (if you want only the default list, # The first one is the identifier of the registered UIList to use (if you want only the default list,
# with no custom draw code, use "UI_UL_list"). # with no custom draw code, use "UI_UL_list").
layout.template_list("MATERIAL_UL_matslots_example", "", obj, "material_slots", obj, "active_material_index") layout.template_list("MATERIAL_UL_matslots_example", "", obj, "material_slots", obj, "active_material_index")

View File

@@ -10,11 +10,11 @@ import bpy
class MESH_UL_vgroups_slow(bpy.types.UIList): class MESH_UL_vgroups_slow(bpy.types.UIList):
# Constants (flags) # Constants (flags).
# Be careful not to shadow FILTER_ITEM! # Be careful not to shadow FILTER_ITEM!
VGROUP_EMPTY = 1 << 0 VGROUP_EMPTY = 1 << 0
# Custom properties, saved with .blend file. # Custom properties, saved with `.blend` file.
use_filter_empty: bpy.props.BoolProperty( use_filter_empty: bpy.props.BoolProperty(
name="Filter Empty", name="Filter Empty",
default=False, default=False,
@@ -151,10 +151,10 @@ class MESH_UL_vgroups_slow(bpy.types.UIList):
flt_flags = [] flt_flags = []
flt_neworder = [] flt_neworder = []
# Pre-compute of vgroups data, CPU-intensive. :/ # Pre-compute of vertex-groups data, unfortunately this is CPU-intensive.
vgroups_empty = self.filter_items_empty_vgroups(context, vgroups) vgroups_empty = self.filter_items_empty_vgroups(context, vgroups)
# Filtering by name # Filtering by name.
if self.filter_name: if self.filter_name:
flt_flags = helper_funcs.filter_items_by_name(self.filter_name, self.bitflag_filter_item, vgroups, "name", flt_flags = helper_funcs.filter_items_by_name(self.filter_name, self.bitflag_filter_item, vgroups, "name",
reverse=self.use_filter_name_reverse) reverse=self.use_filter_name_reverse)
@@ -193,7 +193,7 @@ class UIListPanelExample2(bpy.types.Panel):
layout = self.layout layout = self.layout
obj = context.object obj = context.object
# template_list now takes two new args. # `template_list` now takes two new arguments.
# The first one is the identifier of the registered UIList to use (if you want only the default list, # The first one is the identifier of the registered UIList to use (if you want only the default list,
# with no custom draw code, use "UI_UL_list"). # with no custom draw code, use "UI_UL_list").
layout.template_list("MESH_UL_vgroups_slow", "", obj, "vertex_groups", obj.vertex_groups, "active_index") layout.template_list("MESH_UL_vgroups_slow", "", obj, "vertex_groups", obj.vertex_groups, "active_index")

View File

@@ -5,6 +5,6 @@ Multi-dimensional arrays (like array of vectors) will be flattened into seq.
collection.foreach_get(attr, some_seq) collection.foreach_get(attr, some_seq)
# Python equivalent # Python equivalent.
for i in range(len(seq)): for i in range(len(seq)):
some_seq[i] = getattr(collection[i], attr) some_seq[i] = getattr(collection[i], attr)

View File

@@ -5,6 +5,6 @@ seq must be uni-dimensional, multi-dimensional arrays (like array of vectors) wi
collection.foreach_set(attr, some_seq) collection.foreach_set(attr, some_seq)
# Python equivalent # Python equivalent.
for i in range(len(some_seq)): for i in range(len(some_seq)):
setattr(collection[i], attr, some_seq[i]) setattr(collection[i], attr, some_seq[i])

View File

@@ -6,6 +6,6 @@ import bpy
obj = bpy.context.object obj = bpy.context.object
# set the keyframe at frame 1 # Set the keyframe at frame 1.
obj.location = (3.0, 4.0, 10.0) obj.location = (3.0, 4.0, 10.0)
obj.keyframe_insert(data_path="location", frame=1) obj.keyframe_insert(data_path="location", frame=1)

View File

@@ -2,8 +2,10 @@
Custom compute shader (using image store) and vertex/fragment shader Custom compute shader (using image store) and vertex/fragment shader
-------------------------------------------------------------------- --------------------------------------------------------------------
This is an example of how to use a custom compute shader to write to a texture and then use that texture in a vertex/fragment shader. This is an example of how to use a custom compute shader
The expected result is a 2x2 plane (size of the default cube), which changes color from a green-black gradient to a green-red gradient, to write to a texture and then use that texture in a vertex/fragment shader.
The expected result is a 2x2 plane (size of the default cube),
which changes color from a green-black gradient to a green-red gradient,
based on current time. based on current time.
""" """
import bpy import bpy
@@ -17,7 +19,7 @@ start_time = time.time()
size = 128 size = 128
texture = gpu.types.GPUTexture((size, size), format='RGBA32F') texture = gpu.types.GPUTexture((size, size), format='RGBA32F')
# Create the compute shader to write to the texture # Create the compute shader to write to the texture.
compute_shader_info = gpu.types.GPUShaderCreateInfo() compute_shader_info = gpu.types.GPUShaderCreateInfo()
compute_shader_info.image(0, 'RGBA32F', "FLOAT_2D", "img_output", qualifiers={"WRITE"}) compute_shader_info.image(0, 'RGBA32F', "FLOAT_2D", "img_output", qualifiers={"WRITE"})
compute_shader_info.compute_source(''' compute_shader_info.compute_source('''
@@ -35,7 +37,7 @@ compute_shader_info.push_constant('FLOAT', "time")
compute_shader_info.local_group_size(1, 1) compute_shader_info.local_group_size(1, 1)
compute_shader = gpu.shader.create_from_info(compute_shader_info) compute_shader = gpu.shader.create_from_info(compute_shader_info)
# Create the shader to draw the texture # Create the shader to draw the texture.
vert_out = gpu.types.GPUStageInterfaceInfo("my_interface") vert_out = gpu.types.GPUStageInterfaceInfo("my_interface")
vert_out.smooth('VEC2', "uvInterp") vert_out.smooth('VEC2', "uvInterp")
shader_info = gpu.types.GPUShaderCreateInfo() shader_info = gpu.types.GPUShaderCreateInfo()

View File

@@ -22,7 +22,7 @@ with offscreen.bind():
fb = gpu.state.active_framebuffer_get() fb = gpu.state.active_framebuffer_get()
fb.clear(color=(0.0, 0.0, 0.0, 0.0)) fb.clear(color=(0.0, 0.0, 0.0, 0.0))
with gpu.matrix.push_pop(): with gpu.matrix.push_pop():
# reset matrices -> use normalized device coordinates [-1, 1] # Reset matrices -> use normalized device coordinates [-1, 1].
gpu.matrix.load_matrix(Matrix.Identity(4)) gpu.matrix.load_matrix(Matrix.Identity(4))
gpu.matrix.load_projection_matrix(Matrix.Identity(4)) gpu.matrix.load_projection_matrix(Matrix.Identity(4))

View File

@@ -27,7 +27,7 @@ with offscreen.bind():
fb = gpu.state.active_framebuffer_get() fb = gpu.state.active_framebuffer_get()
fb.clear(color=(0.0, 0.0, 0.0, 0.0)) fb.clear(color=(0.0, 0.0, 0.0, 0.0))
with gpu.matrix.push_pop(): with gpu.matrix.push_pop():
# reset matrices -> use normalized device coordinates [-1, 1] # Reset matrices -> use normalized device coordinates [-1, 1].
gpu.matrix.load_matrix(Matrix.Identity(4)) gpu.matrix.load_matrix(Matrix.Identity(4))
gpu.matrix.load_projection_matrix(Matrix.Identity(4)) gpu.matrix.load_projection_matrix(Matrix.Identity(4))

View File

@@ -1,30 +1,30 @@
import mathutils import mathutils
# color values are represented as RGB values from 0 - 1, this is blue # Color values are represented as RGB values from 0 - 1, this is blue.
col = mathutils.Color((0.0, 0.0, 1.0)) col = mathutils.Color((0.0, 0.0, 1.0))
# as well as r/g/b attribute access you can adjust them by h/s/v # As well as r/g/b attribute access you can adjust them by h/s/v.
col.s *= 0.5 col.s *= 0.5
# you can access its components by attribute or index # You can access its components by attribute or index.
print("Color R:", col.r) print("Color R:", col.r)
print("Color G:", col[1]) print("Color G:", col[1])
print("Color B:", col[-1]) print("Color B:", col[-1])
print("Color HSV: {:.2f}, {:.2f}, {:.2f}".format(*col)) print("Color HSV: {:.2f}, {:.2f}, {:.2f}".format(*col))
# components of an existing color can be set # Components of an existing color can be set.
col[:] = 0.0, 0.5, 1.0 col[:] = 0.0, 0.5, 1.0
# components of an existing color can use slice notation to get a tuple # Components of an existing color can use slice notation to get a tuple.
print("Values: {:f}, {:f}, {:f}".format(*col)) print("Values: {:f}, {:f}, {:f}".format(*col))
# colors can be added and subtracted # Colors can be added and subtracted.
col += mathutils.Color((0.25, 0.0, 0.0)) col += mathutils.Color((0.25, 0.0, 0.0))
# Color can be multiplied, in this example color is scaled to 0-255 # Color can be multiplied, in this example color is scaled to 0-255
# can printed as integers # can printed as integers.
print("Color: {:d}, {:d}, {:d}".format(*(int(c) for c in (col * 255.0)))) print("Color: {:d}, {:d}, {:d}".format(*(int(c) for c in (col * 255.0))))
# This example prints the color as hexadecimal # This example prints the color as hexadecimal.
print("Hexadecimal: {:02x}{:02x}{:02x}".format(int(col.r * 255), int(col.g * 255), int(col.b * 255))) print("Hexadecimal: {:02x}{:02x}{:02x}".format(int(col.r * 255), int(col.g * 255), int(col.b * 255)))

View File

@@ -1,32 +1,32 @@
import mathutils import mathutils
import math import math
# create a new euler with default axis rotation order # Create a new euler with default axis rotation order.
eul = mathutils.Euler((0.0, math.radians(45.0), 0.0), 'XYZ') eul = mathutils.Euler((0.0, math.radians(45.0), 0.0), 'XYZ')
# rotate the euler # Rotate the euler.
eul.rotate_axis('Z', math.radians(10.0)) eul.rotate_axis('Z', math.radians(10.0))
# you can access its components by attribute or index # You can access its components by attribute or index.
print("Euler X", eul.x) print("Euler X", eul.x)
print("Euler Y", eul[1]) print("Euler Y", eul[1])
print("Euler Z", eul[-1]) print("Euler Z", eul[-1])
# components of an existing euler can be set # Components of an existing euler can be set.
eul[:] = 1.0, 2.0, 3.0 eul[:] = 1.0, 2.0, 3.0
# components of an existing euler can use slice notation to get a tuple # Components of an existing euler can use slice notation to get a tuple.
print("Values: {:f}, {:f}, {:f}".format(*eul)) print("Values: {:f}, {:f}, {:f}".format(*eul))
# the order can be set at any time too # The order can be set at any time too.
eul.order = 'ZYX' eul.order = 'ZYX'
# eulers can be used to rotate vectors # Eulers can be used to rotate vectors.
vec = mathutils.Vector((0.0, 0.0, 1.0)) vec = mathutils.Vector((0.0, 0.0, 1.0))
vec.rotate(eul) vec.rotate(eul)
# often its useful to convert the euler into a matrix so it can be used as # Often its useful to convert the euler into a matrix so it can be used as
# transformations with more flexibility # transformations with more flexibility.
mat_rot = eul.to_matrix() mat_rot = eul.to_matrix()
mat_loc = mathutils.Matrix.Translation((2.0, 3.0, 4.0)) mat_loc = mathutils.Matrix.Translation((2.0, 3.0, 4.0))
mat = mat_loc @ mat_rot.to_4x4() mat = mat_loc @ mat_rot.to_4x4()

View File

@@ -1,27 +1,27 @@
import mathutils import mathutils
import math import math
# a new rotation 90 degrees about the Y axis # A new rotation 90 degrees about the Y axis.
quat_a = mathutils.Quaternion((0.7071068, 0.0, 0.7071068, 0.0)) quat_a = mathutils.Quaternion((0.7071068, 0.0, 0.7071068, 0.0))
# passing values to Quaternion's directly can be confusing so axis, angle # Passing values to Quaternion's directly can be confusing so axis, angle
# is supported for initializing too # is supported for initializing too.
quat_b = mathutils.Quaternion((0.0, 1.0, 0.0), math.radians(90.0)) quat_b = mathutils.Quaternion((0.0, 1.0, 0.0), math.radians(90.0))
print("Check quaternions match", quat_a == quat_b) print("Check quaternions match", quat_a == quat_b)
# like matrices, quaternions can be multiplied to accumulate rotational values # Like matrices, quaternions can be multiplied to accumulate rotational values.
quat_a = mathutils.Quaternion((0.0, 1.0, 0.0), math.radians(90.0)) quat_a = mathutils.Quaternion((0.0, 1.0, 0.0), math.radians(90.0))
quat_b = mathutils.Quaternion((0.0, 0.0, 1.0), math.radians(45.0)) quat_b = mathutils.Quaternion((0.0, 0.0, 1.0), math.radians(45.0))
quat_out = quat_a @ quat_b quat_out = quat_a @ quat_b
# print the quat, euler degrees for mere mortals and (axis, angle) # Print the quaternion, euler degrees for mere mortals and (axis, angle).
print("Final Rotation:") print("Final Rotation:")
print(quat_out) print(quat_out)
print("{:.2f}, {:.2f}, {:.2f}".format(*(math.degrees(a) for a in quat_out.to_euler()))) print("{:.2f}, {:.2f}, {:.2f}".format(*(math.degrees(a) for a in quat_out.to_euler())))
print("({:.2f}, {:.2f}, {:.2f}), {:.2f}".format(*quat_out.axis, math.degrees(quat_out.angle))) print("({:.2f}, {:.2f}, {:.2f}), {:.2f}".format(*quat_out.axis, math.degrees(quat_out.angle)))
# multiple rotations can be interpolated using the exponential map # Multiple rotations can be interpolated using the exponential map.
quat_c = mathutils.Quaternion((1.0, 0.0, 0.0), math.radians(15.0)) quat_c = mathutils.Quaternion((1.0, 0.0, 0.0), math.radians(15.0))
exp_avg = (quat_a.to_exponential_map() + exp_avg = (quat_a.to_exponential_map() +
quat_b.to_exponential_map() + quat_b.to_exponential_map() +

View File

@@ -1,9 +1,9 @@
import mathutils import mathutils
# zero length vector # Zero length vector.
vec = mathutils.Vector((0.0, 0.0, 1.0)) vec = mathutils.Vector((0.0, 0.0, 1.0))
# unit length vector # Unit length vector.
vec_a = vec.normalized() vec_a = vec.normalized()
vec_b = mathutils.Vector((0.0, 1.0, 2.0)) vec_b = mathutils.Vector((0.0, 1.0, 2.0))
@@ -29,7 +29,7 @@ vec_a < vec_b
vec_a <= vec_b vec_a <= vec_b
# Math can be performed on Vector classes # Math can be performed on Vector classes.
vec_a + vec_b vec_a + vec_b
vec_a - vec_b vec_a - vec_b
vec_a @ vec_b vec_a @ vec_b
@@ -39,7 +39,7 @@ quat @ vec_a
-vec_a -vec_a
# You can access a vector object like a sequence # You can access a vector object like a sequence.
x = vec_a[0] x = vec_a[0]
len(vec) len(vec)
vec_a[:] = vec_b vec_a[:] = vec_b
@@ -47,7 +47,7 @@ vec_a[:] = 1.0, 2.0, 3.0
vec2d[:] = vec3d[:2] vec2d[:] = vec3d[:2]
# Vectors support 'swizzle' operations # Vectors support 'swizzle' operations.
# See https://en.wikipedia.org/wiki/Swizzling_(computer_graphics) # See https://en.wikipedia.org/wiki/Swizzling_(computer_graphics)
vec.xyz = vec.zyx vec.xyz = vec.zyx
vec.xy = vec4d.zw vec.xy = vec4d.zw

View File

@@ -1,6 +1,6 @@
import mathutils import mathutils
# create a kd-tree from a mesh # Create a KD-tree from a mesh.
from bpy import context from bpy import context
obj = context.object obj = context.object
@@ -14,21 +14,21 @@ for i, v in enumerate(mesh.vertices):
kd.balance() kd.balance()
# Find the closest point to the center # Find the closest point to the center.
co_find = (0.0, 0.0, 0.0) co_find = (0.0, 0.0, 0.0)
co, index, dist = kd.find(co_find) co, index, dist = kd.find(co_find)
print("Close to center:", co, index, dist) print("Close to center:", co, index, dist)
# 3d cursor relative to the object data # 3D cursor relative to the object data.
co_find = obj.matrix_world.inverted() @ context.scene.cursor.location co_find = obj.matrix_world.inverted() @ context.scene.cursor.location
# Find the closest 10 points to the 3d cursor # Find the closest 10 points to the 3D cursor.
print("Close 10 points") print("Close 10 points")
for (co, index, dist) in kd.find_n(co_find, 10): for (co, index, dist) in kd.find_n(co_find, 10):
print(" ", co, index, dist) print(" ", co, index, dist)
# Find points within a radius of the 3d cursor # Find points within a radius of the 3D cursor.
print("Close points within 0.5 distance") print("Close points within 0.5 distance")
for (co, index, dist) in kd.find_range(co_find, 0.5): for (co, index, dist) in kd.find_range(co_find, 0.5):
print(" ", co, index, dist) print(" ", co, index, dist)

View File

@@ -902,12 +902,12 @@ including advanced features.
import bgl import bgl
xval, yval= 100, 40 xval, yval= 100, 40
# Get the scale of the view matrix # Get the scale of the view matrix.
view_matrix = bgl.Buffer(bgl.GL_FLOAT, 16) view_matrix = bgl.Buffer(bgl.GL_FLOAT, 16)
bgl.glGetFloatv(bgl.GL_MODELVIEW_MATRIX, view_matrix) bgl.glGetFloatv(bgl.GL_MODELVIEW_MATRIX, view_matrix)
f = 1.0 / view_matrix[0] f = 1.0 / view_matrix[0]
# Instead of the usual glRasterPos2i(xval, yval) # Instead of the usual `glRasterPos2i(xval, yval)`.
bgl.glRasterPos2f(xval * f, yval * f) bgl.glRasterPos2f(xval * f, yval * f)

View File

@@ -104,11 +104,11 @@ Here are some examples:
.. code-block:: python .. code-block:: python
# in this example the active vertex group index is used, # In this example the active vertex group index is used,
# this is stored in the object, not the BMesh # this is stored in the object, not the `BMesh`.
group_index = obj.vertex_groups.active_index group_index = obj.vertex_groups.active_index
# only ever one deform weight layer # Only ever one deform weight layer.
dvert_lay = bm.verts.layers.deform.active dvert_lay = bm.verts.layers.deform.active
for vert in bm.verts: for vert in bm.verts:

View File

@@ -149,13 +149,13 @@ Rather than:
.. code-block:: python .. code-block:: python
polygons = mesh.polygons[:] # make a list copy of the meshes polygons polygons = mesh.polygons[:] # Make a list copy of the meshes polygons.
p_idx = len(polygons) # Loop backwards p_idx = len(polygons) # Loop backwards
while p_idx: # while the value is not 0 while p_idx: # While the value is not 0.
p_idx -= 1 p_idx -= 1
if len(polygons[p_idx].vertices) == 3: if len(polygons[p_idx].vertices) == 3:
polygons.pop(p_idx) # remove the triangle polygons.pop(p_idx) # Remove the triangle.
It's faster to build a new list with list comprehension: It's faster to build a new list with list comprehension:
@@ -227,10 +227,10 @@ This works by swapping two list items, so the item you remove is always last:
pop_index = 5 pop_index = 5
# swap so the pop_index is last. # Swap so the pop_index is last.
my_list[-1], my_list[pop_index] = my_list[pop_index], my_list[-1] my_list[-1], my_list[pop_index] = my_list[pop_index], my_list[-1]
# remove last item (pop_index) # Remove last item (pop_index).
my_list.pop() my_list.pop()
@@ -357,6 +357,6 @@ While developing a script it is good to time it to be aware of any changes in pe
import time import time
time_start = time.time() time_start = time.time()
# do something... # Do something...
print("My Script Finished: %.4f sec" % (time.time() - time_start)) print("My Script Finished: %.4f sec" % (time.time() - time_start))

View File

@@ -67,10 +67,10 @@ Examples using :class:`bpy.types.PoseBone` in Object or Pose-Mode:
.. code-block:: python .. code-block:: python
# Gets the name of the first constraint (if it exists) # Gets the name of the first constraint (if it exists).
bpy.context.object.pose.bones["Bone"].constraints[0].name bpy.context.object.pose.bones["Bone"].constraints[0].name
# Gets the last selected pose bone (Pose-Mode only) # Gets the last selected pose bone (Pose-Mode only).
bpy.context.active_pose_bone bpy.context.active_pose_bone

View File

@@ -19,7 +19,7 @@ Here is an example of threading supported by Blender:
def prod(): def prod():
print(threading.current_thread().name, "Starting") print(threading.current_thread().name, "Starting")
# do something vaguely useful # Do something vaguely useful.
import bpy import bpy
from mathutils import Vector from mathutils import Vector
from random import random from random import random
@@ -28,7 +28,7 @@ Here is an example of threading supported by Blender:
print("Prodding", prod_vec) print("Prodding", prod_vec)
bpy.data.objects["Cube"].location += prod_vec bpy.data.objects["Cube"].location += prod_vec
time.sleep(random() + 1.0) time.sleep(random() + 1.0)
# finish # Finish.
print(threading.current_thread().name, "Exiting") print(threading.current_thread().name, "Exiting")
@@ -256,7 +256,7 @@ Only the reference to the data itself can be re-accessed, the following example
bpy.ops.object.mode_set(mode='EDIT') bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='OBJECT')
# this will crash # This will crash!
print(polygons) print(polygons)
@@ -270,7 +270,7 @@ the following example shows how to avoid the crash above.
bpy.ops.object.mode_set(mode='EDIT') bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='OBJECT')
# polygons have been re-allocated # Polygons have been re-allocated.
polygons = mesh.polygons polygons = mesh.polygons
print(polygons) print(polygons)
@@ -291,7 +291,7 @@ internally the array which stores this data is re-allocated.
point = bpy.context.object.data.splines[0].bezier_points[0] point = bpy.context.object.data.splines[0].bezier_points[0]
bpy.context.object.data.splines[0].bezier_points.add() bpy.context.object.data.splines[0].bezier_points.add()
# this will crash! # This will crash!
point.co = 1.0, 2.0, 3.0 point.co = 1.0, 2.0, 3.0
This can be avoided by re-assigning the point variables after adding the new one or by storing This can be avoided by re-assigning the point variables after adding the new one or by storing
@@ -315,9 +315,9 @@ The following example shows how this precaution works:
.. code-block:: python .. code-block:: python
mesh = bpy.data.meshes.new(name="MyMesh") mesh = bpy.data.meshes.new(name="MyMesh")
# normally the script would use the mesh here... # Normally the script would use the mesh here.
bpy.data.meshes.remove(mesh) bpy.data.meshes.remove(mesh)
print(mesh.name) # <- give an exception rather than crashing: print(mesh.name) # <- Give an exception rather than crashing:
# ReferenceError: StructRNA of type Mesh has been removed # ReferenceError: StructRNA of type Mesh has been removed
@@ -330,7 +330,7 @@ the next example will still crash:
mesh = bpy.data.meshes.new(name="MyMesh") mesh = bpy.data.meshes.new(name="MyMesh")
vertices = mesh.vertices vertices = mesh.vertices
bpy.data.meshes.remove(mesh) bpy.data.meshes.remove(mesh)
print(vertices) # <- this may crash print(vertices) # <- This may crash.
Unfortunate Corner Cases Unfortunate Corner Cases

View File

@@ -42,7 +42,7 @@ This can cause bugs when you add data (normally imported) then reference it late
bpy.data.meshes.new(name=meshid) bpy.data.meshes.new(name=meshid)
# normally some code, function calls... # Normally some code, function calls, etc.
bpy.data.meshes[meshid] bpy.data.meshes[meshid]
@@ -52,7 +52,7 @@ Or with name assignment:
obj.name = objname obj.name = objname
# normally some code, function calls... # Normally some code, function calls, etc.
obj = bpy.data.meshes[objname] obj = bpy.data.meshes[objname]
@@ -69,15 +69,15 @@ this way you don't run this risk of referencing existing data from the blend-fil
.. code-block:: python .. code-block:: python
# typically declared in the main body of the function. # Typically declared in the main body of the function.
mesh_name_mapping = {} mesh_name_mapping = {}
mesh = bpy.data.meshes.new(name=meshid) mesh = bpy.data.meshes.new(name=meshid)
mesh_name_mapping[meshid] = mesh mesh_name_mapping[meshid] = mesh
# normally some code, or function calls... # Normally some code, or function calls, etc.
# use own dictionary rather than bpy.data # Use own dictionary rather than `bpy.data`.
mesh = mesh_name_mapping[meshid] mesh = mesh_name_mapping[meshid]
@@ -96,18 +96,18 @@ If you need to select between local and library data, there is a feature in ``bp
.. code-block:: python .. code-block:: python
# typical name lookup, could be local or library. # Typical name lookup, could be local or library.
obj = bpy.data.objects["my_obj"] obj = bpy.data.objects["my_obj"]
# library object name look up using a pair # Library object name lookup using a pair,
# where the second argument is the library path matching bpy.types.Library.filepath # where the second argument is the library path matching bpy.types.Library.filepath.
obj = bpy.data.objects["my_obj", "//my_lib.blend"] obj = bpy.data.objects["my_obj", "//my_lib.blend"]
# local object name look up using a pair # Local object name look up using a pair,
# where the second argument excludes library data from being returned. # where the second argument excludes library data from being returned.
obj = bpy.data.objects["my_obj", None] obj = bpy.data.objects["my_obj", None]
# both the examples above also works for 'get' # Both the examples above also works for `get`.
obj = bpy.data.objects.get(("my_obj", None)) obj = bpy.data.objects.get(("my_obj", None))

View File

@@ -362,7 +362,7 @@ For example, if you want to store material settings for a custom engine:
.. code-block:: python .. code-block:: python
# Create new property # Create new property:
# bpy.data.materials[0].my_custom_props.my_float # bpy.data.materials[0].my_custom_props.my_float
import bpy import bpy
@@ -389,7 +389,7 @@ For example, if you want to store material settings for a custom engine:
.. code-block:: python .. code-block:: python
# Create new property group with a sub property # Create new property group with a sub property:
# bpy.data.materials[0].my_custom_props.sub_group.my_float # bpy.data.materials[0].my_custom_props.sub_group.my_float
import bpy import bpy
@@ -428,9 +428,9 @@ For example:
.. code-block:: python .. code-block:: python
# add a new property to an existing type # Add a new property to an existing type.
bpy.types.Object.my_float: bpy.props.FloatProperty() bpy.types.Object.my_float: bpy.props.FloatProperty()
# remove # Remove it.
del bpy.types.Object.my_float del bpy.types.Object.my_float
This works just as well for ``PropertyGroup`` subclasses you define yourself. This works just as well for ``PropertyGroup`` subclasses you define yourself.

View File

@@ -182,7 +182,7 @@ This data is saved with the blend-file and copied with objects, for example:
# which can have a fallback value. # which can have a fallback value.
value = bpy.data.scenes["Scene"].get("test_prop", "fallback value") value = bpy.data.scenes["Scene"].get("test_prop", "fallback value")
# dictionaries can be assigned as long as they only use basic types. # Dictionaries can be assigned as long as they only use basic types.
collection = bpy.data.collections.new("MyTestCollection") collection = bpy.data.collections.new("MyTestCollection")
collection["MySettings"] = {"foo": 10, "bar": "spam", "baz": {}} collection["MySettings"] = {"foo": 10, "bar": "spam", "baz": {}}
@@ -363,10 +363,10 @@ so these are accessed as normal Python types.
.. code-block:: python .. code-block:: python
# setting multiple snap targets # Setting multiple snap targets.
bpy.context.scene.tool_settings.snap_elements_base = {'VERTEX', 'EDGE'} bpy.context.scene.tool_settings.snap_elements_base = {'VERTEX', 'EDGE'}
# passing as an operator argument for report types # Passing as an operator argument for report types.
self.report({'WARNING', 'INFO'}, "Some message!") self.report({'WARNING', 'INFO'}, "Some message!")
@@ -411,10 +411,10 @@ Example of a matrix, vector multiplication:
.. code-block:: python .. code-block:: python
# modifies the Z axis in place. # Modifies the Z axis in place.
bpy.context.object.location.z += 2.0 bpy.context.object.location.z += 2.0
# location variable holds a reference to the object too. # Location variable holds a reference to the object too.
location = bpy.context.object.location location = bpy.context.object.location
location *= 2.0 location *= 2.0

View File

@@ -19,7 +19,7 @@ from bpy.types import Operator
class ExportSomeData(Operator, ExportHelper): class ExportSomeData(Operator, ExportHelper):
"""This appears in the tooltip of the operator and in the generated docs""" """This appears in the tooltip of the operator and in the generated docs"""
bl_idname = "export_test.some_data" # important since its how bpy.ops.import_test.some_data is constructed bl_idname = "export_test.some_data" # Important since its how bpy.ops.import_test.some_data is constructed.
bl_label = "Export Some Data" bl_label = "Export Some Data"
# ExportHelper mix-in class uses this. # ExportHelper mix-in class uses this.
@@ -72,5 +72,5 @@ def unregister():
if __name__ == "__main__": if __name__ == "__main__":
register() register()
# test call # Test call.
bpy.ops.export_test.some_data('INVOKE_DEFAULT') bpy.ops.export_test.some_data('INVOKE_DEFAULT')

View File

@@ -7,7 +7,7 @@ def read_some_data(context, filepath, use_some_setting):
data = f.read() data = f.read()
f.close() f.close()
# would normally load the data here # Would normally load the data here.
print(data) print(data)
return {'FINISHED'} return {'FINISHED'}
@@ -75,5 +75,5 @@ def unregister():
if __name__ == "__main__": if __name__ == "__main__":
register() register()
# test call # Test call.
bpy.ops.import_test.some_data('INVOKE_DEFAULT') bpy.ops.import_test.some_data('INVOKE_DEFAULT')

View File

@@ -33,7 +33,7 @@ def add_box(width, height, depth):
(4, 0, 3, 7), (4, 0, 3, 7),
] ]
# apply size # Apply size.
for i, v in enumerate(verts): for i, v in enumerate(verts):
verts[i] = v[0] * width, v[1] * depth, v[2] * height verts[i] = v[0] * width, v[1] * depth, v[2] * height
@@ -87,7 +87,7 @@ class AddBox(bpy.types.Operator, AddObjectHelper):
bm.to_mesh(mesh) bm.to_mesh(mesh)
mesh.update() mesh.update()
# add the mesh as an object into the scene with this utility module # Add the mesh as an object into the scene with this utility module.
from bpy_extras import object_utils from bpy_extras import object_utils
object_utils.object_data_add(context, mesh, operator=self) object_utils.object_data_add(context, mesh, operator=self)
@@ -112,5 +112,5 @@ def unregister():
if __name__ == "__main__": if __name__ == "__main__":
register() register()
# test call # Test call.
bpy.ops.mesh.primitive_box_add() bpy.ops.mesh.primitive_box_add()

View File

@@ -9,11 +9,11 @@ def main(context):
uv_layer = bm.loops.layers.uv.verify() uv_layer = bm.loops.layers.uv.verify()
# adjust uv coordinates # Adjust UV coordinates.
for face in bm.faces: for face in bm.faces:
for loop in face.loops: for loop in face.loops:
loop_uv = loop[uv_layer] loop_uv = loop[uv_layer]
# use xy position of the vertex as a uv coordinate # Use XY position of the vertex as a uv coordinate.
loop_uv.uv = loop.vert.co.xy loop_uv.uv = loop.vert.co.xy
bmesh.update_edit_mesh(me) bmesh.update_edit_mesh(me)
@@ -52,5 +52,5 @@ def unregister():
if __name__ == "__main__": if __name__ == "__main__":
register() register()
# test call # Test call.
bpy.ops.uv.simple_operator() bpy.ops.uv.simple_operator()

View File

@@ -54,5 +54,5 @@ def unregister():
if __name__ == "__main__": if __name__ == "__main__":
register() register()
# test call # Test call.
bpy.ops.object.modal_operator('INVOKE_DEFAULT') bpy.ops.object.modal_operator('INVOKE_DEFAULT')

View File

@@ -14,7 +14,7 @@ class ModalTimerOperator(bpy.types.Operator):
return {'CANCELLED'} return {'CANCELLED'}
if event.type == 'TIMER': if event.type == 'TIMER':
# change theme color, silly! # Change theme color, silly!
color = context.preferences.themes[0].view_3d.space.gradients.high_gradient color = context.preferences.themes[0].view_3d.space.gradients.high_gradient
color.s = 1.0 color.s = 1.0
color.h += 0.01 color.h += 0.01
@@ -50,5 +50,5 @@ def unregister():
if __name__ == "__main__": if __name__ == "__main__":
register() register()
# test call # Test call.
bpy.ops.wm.modal_timer_operator() bpy.ops.wm.modal_timer_operator()

View File

@@ -38,5 +38,5 @@ def unregister():
if __name__ == "__main__": if __name__ == "__main__":
register() register()
# test call # Test call.
bpy.ops.object.simple_operator() bpy.ops.object.simple_operator()

View File

@@ -48,7 +48,7 @@ class UIListPanelExample(bpy.types.Panel):
obj = context.object obj = context.object
# template_list now takes two new args. # `template_list` now takes two new arguments.
# The first one is the identifier of the registered UIList to use (if you want only the default list, # The first one is the identifier of the registered UIList to use (if you want only the default list,
# with no custom draw code, use "UI_UL_list"). # with no custom draw code, use "UI_UL_list").
layout.template_list("MATERIAL_UL_matslots_example", "", obj, "material_slots", obj, "active_material_index") layout.template_list("MATERIAL_UL_matslots_example", "", obj, "material_slots", obj, "active_material_index")