「Extensions:2.6/Py/API Changes」の版間の差分
細 (1版 をインポートしました) |
|
(相違点なし)
|
2018年6月29日 (金) 05:53時点における最新版
目次
API Changes
This page shall give addon developers advice on how to update their scripts to make them work again after major changes to the Blender Python API.
If your specific problem isn't covered here, feel free to inform the community about it in the forum.
You may also have a look at the auto-generated API change log for minor things that changed - but keep in mind that it may be incomplete!
2.66
Template List (r53355)
UI's template_list()
was refactored to be much more flexible and give real control over items' drawing. A new RNA type was introduced for this, bpy.types.UIList
, making lists independent from Panels. This UIList has a draw_item()
callback which allows to customize items' drawing from python, that all addons can now use.
If your addon uses the old API for a simple list, it's easy to update:
# OLD
template_list(bpy.context.scene, "your_collection", bpy.context.scene, "your_collection_idx")
# NEW
template_list('UI_UL_list', 'your_collection_id', bpy.context.scene, "your_collection", bpy.context.scene, "your_collection_idx")
For simple lists, it isn't necessary to derive a custom class from UIList type. Instead, simply put 'UI_UL_list' (for a default drawing method for the list items) as first argument and a list ID string as second. Chose something unique for the ID, preferably a brief description of what your list contains. If another script used the default 'UI_UL_list' and the same ID (maybe a blank string), Blender would not be able to distinguish between both lists!
For in-depth explanation, e.g. how to create custom draw_item callbacks, see the API docs: UIList, UILayout.template_list.
Region Draw Callbacks (r53207)
Region drawing callbacks were changed to work much closer to how blender manages them internally. The change was necessary to fix bug T30740.
To update old code, you need to replace the Region.callback_add()
and Region.callback_remove()
calls. The new way of adding/removing a draw callback is to call a Space type's draw_handler_add()
and draw_handler_remove()
functions. A common Space type is the 3D View, bpy.types.SpaceView3D
. For a list of Space types, see subclasses in API docs - bpy.types.Space.
The deprecated callback_add()
expected 3 arguments, whereas draw_handler_add()
requires 4. You now need to specify the Space's region type (string) as 3rd parameter. It is also required on callback removal:
# OLD
self._handle = context.region.callback_add(draw_callback_px, (self, context), 'POST_PIXEL')
context.region.callback_remove(self._handle)
# NEW
CLASSNAME._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, (self, context), 'WINDOW', 'POST_PIXEL')
bpy.types.SpaceView3D.draw_handler_remove(CLASSNAME._handle, 'WINDOW')
# To store the handle, the class should be used rather than the class instance (you may use self.__class__),
# so we can access the variable from outside, e.g. to remove the drawback on unregister()
Below you find a comparison of the old and the new API usage:
class ScreencastKeysStatus(bpy.types.Operator):
bl_idname = "view3d.screencast_keys"
bl_label = "Screencast Keys"
bl_description = "Display keys pressed in the 3D View"
_handle = None
_timer = None
def modal(self, context, event):
# ...
if not context.window_manager.screencast_keys_keys:
# stop script
context.window_manager.event_timer_remove(self._timer)
context.region.callback_remove(self._handle)
return {'CANCELLED'}
return {'PASS_THROUGH'}
def cancel(self, context):
if context.window_manager.screencast_keys_keys:
context.window_manager.event_timer_remove(self._timer)
context.region.callback_remove(self._handle)
context.window_manager.screencast_keys_keys = False
return {'CANCELLED'}
def invoke(self, context, event):
# ...
self._handle = context.region.callback_add(draw_callback_px,
(self, context), 'POST_PIXEL')
self._timer = context.window_manager.event_timer_add(0.075,
context.window)
# ...
def unregister():
# ...
class ScreencastKeysStatus(bpy.types.Operator):
bl_idname = "view3d.screencast_keys"
bl_label = "Screencast Keys"
bl_description = "Display keys pressed in the 3D View"
_handle = None
_timer = None
@staticmethod
def handle_add(self, context):
ScreencastKeysStatus._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, (self, context), 'WINDOW', 'POST_PIXEL')
ScreencastKeysStatus._timer = context.window_manager.event_timer_add(0.075, context.window)
@staticmethod
def handle_remove(context):
if ScreencastKeysStatus._handle is not None:
context.window_manager.event_timer_remove(ScreencastKeysStatus._timer)
bpy.types.SpaceView3D.draw_handler_remove(ScreencastKeysStatus._handle, 'WINDOW')
ScreencastKeysStatus._handle = None
ScreencastKeysStatus._timer = None
def modal(self, context, event):
# ...
if not context.window_manager.screencast_keys_keys:
# stop script
ScreencastKeysStatus.handle_remove(context)
return {'CANCELLED'}
return {'PASS_THROUGH'}
def cancel(self, context):
if context.window_manager.screencast_keys_keys:
ScreencastKeysStatus.handle_remove(context)
context.window_manager.screencast_keys_keys = False
return {'CANCELLED'}
def invoke(self, context, event):
# ...
ScreencastKeysStatus.handle_add(self, context)
# ...
def unregister():
ScreencastKeysStatus.handle_remove(bpy.context)
# ...
Note: If you don't use an event timer, remove the argument "context" from handle_remove()
, in staticmethod definition and in the call inside unregister()
. Or omit the static methods and call the draw_handler_* functions directly, as seen in the Operator Modal Draw template.
Restricted Context
To avoid errors on accessing blend-file data while addons register()
/ unregister()
, Blender now restricts access to bpy.context and bpy.data.
This is done because there is no assurance that the data loaded when the addon is registered will be active or even exist when the user accesses operators the addon defines.
If you see an exception like this, then the addon needs to be updated to access the context during execution rather then on registration:
AttributeError: '_RestrictContext' object has no attribute 'scene'
Bad code example:
def register():
bpy.types.Scene.my_prop = bpy.props.StringProperty()
bpy.context.scene.my_prop = "foobar" # can't access bpy.context here!
Such initialization needs to be done when an operator runs the first time. It's also possible to provide a special init operator, shown as button in a panel.
Init operator example:
import bpy
class HelloWorldPanel(bpy.types.Panel):
"""Creates a Panel in the Object properties window"""
bl_label = "Hello World Panel"
bl_idname = "OBJECT_PT_hello"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "scene"
def draw(self, context):
layout = self.layout
# If Scene.my_prop wasn't created in register() or removed, draw a note
if not hasattr(context.scene, "my_prop"):
layout.label("Scene does not have a property 'my_prop'")
# If it has no longer the default property value, draw a label with icon
elif context.scene.my_prop != 'default value':
layout.label("my_prop = " + context.scene.my_prop, icon="FILE_TICK")
# It has the default property value, draw a label with no icon
else:
layout.label("my_prop = " + context.scene.my_prop)
layout.operator(InitMyPropOperator.bl_idname, text=InitMyPropOperator.bl_label)
class InitMyPropOperator(bpy.types.Operator):
"""Tooltip"""
bl_idname = "scene.init_my_prop"
bl_label = "Init my_prop"
@classmethod
def poll(cls, context):
return context.active_object is not None
def execute(self, context):
if context.scene.my_prop != "initialized":
context.scene.my_prop = "initialized"
self.__class__.bl_label = "Change my_prop"
else:
context.scene.my_prop = "foobar"
self.__class__.bl_label = self.bl_label
return {'FINISHED'}
def register():
bpy.utils.register_module(__name__)
bpy.types.Scene.my_prop = bpy.props.StringProperty(default="default value")
def unregister():
bpy.utils.unregister_module(__name__)
del bpy.types.Scene.my_prop
if __name__ == "__main__":
register()
Also see: mailing list thread
2.67
Node Identifiers
The type
enum property of nodes is no longer the primary way of identifying node types. Instead nodes now have a bl_idname
property (like other registerable types such as operators, menus, etc.). Also see: Python Nodes - API Changes
The bl_idname
must be passed to the nodetree.nodes.new
method in particular for identifying the node type. The old type
property still exists, but should be avoided as much as possible. Scripters are encouraged to convert their use of explicit identifiers to the new system. The full list of which enum type maps to which bl_idname can be found below (most names are pretty obvious though). A quick way of getting the correct identifier is typing
node.bl_idname
in the Blender console.
Shader Nodes
type | bl_idname |
ADD_SHADER | ShaderNodeAddShader |
AMBIENT_OCCLUSION | ShaderNodeAmbientOcclusion |
ATTRIBUTE | ShaderNodeAttribute |
BACKGROUND | ShaderNodeBackground |
BRIGHTCONTRAST | ShaderNodeBrightContrast |
BSDF_ANISOTROPIC | ShaderNodeBsdfAnisotropic |
BSDF_DIFFUSE | ShaderNodeBsdfDiffuse |
BSDF_GLASS | ShaderNodeBsdfGlass |
BSDF_GLOSSY | ShaderNodeBsdfGlossy |
BSDF_REFRACTION | ShaderNodeBsdfRefraction |
BSDF_TRANSLUCENT | ShaderNodeBsdfTranslucent |
BSDF_TRANSPARENT | ShaderNodeBsdfTransparent |
BSDF_VELVET | ShaderNodeBsdfVelvet |
BUMP | ShaderNodeBump |
CAMERA | ShaderNodeCameraData |
COMBRGB | ShaderNodeCombineRGB |
CURVE_RGB | ShaderNodeRGBCurve |
CURVE_VEC | ShaderNodeVectorCurve |
EMISSION | ShaderNodeEmission |
FRESNEL | ShaderNodeFresnel |
GAMMA | ShaderNodeGamma |
GEOMETRY | ShaderNodeGeometry |
GROUP | ShaderNodeGroup |
HAIR_INFO | ShaderNodeHairInfo |
HOLDOUT | ShaderNodeHoldout |
HUE_SAT | ShaderNodeHueSaturation |
INVERT | ShaderNodeInvert |
LAYER_WEIGHT | ShaderNodeLayerWeight |
LIGHT_FALLOFF | ShaderNodeLightFalloff |
LIGHT_PATH | ShaderNodeLightPath |
MAPPING | ShaderNodeMapping |
MATERIAL | ShaderNodeMaterial |
MATERIAL_EXT | ShaderNodeExtendedMaterial |
MATH | ShaderNodeMath |
MIX_RGB | ShaderNodeMixRGB |
MIX_SHADER | ShaderNodeMixShader |
NEW_GEOMETRY | ShaderNodeNewGeometry |
NORMAL | ShaderNodeNormal |
NORMAL_MAP | ShaderNodeNormalMap |
OBJECT_INFO | ShaderNodeObjectInfo |
OUTPUT | ShaderNodeOutput |
OUTPUT_LAMP | ShaderNodeOutputLamp |
OUTPUT_MATERIAL | ShaderNodeOutputMaterial |
OUTPUT_WORLD | ShaderNodeOutputWorld |
PARTICLE_INFO | ShaderNodeParticleInfo |
RGB | ShaderNodeRGB |
RGBTOBW | ShaderNodeRGBToBW |
SCRIPT | ShaderNodeScript |
SEPRGB | ShaderNodeSeparateRGB |
SQUEEZE | ShaderNodeSqueeze |
SUBSURFACE_SCATTERING | ShaderNodeSubsurfaceScattering |
TANGENT | ShaderNodeTangent |
TEXTURE | ShaderNodeTexture |
TEX_BRICK | ShaderNodeTexBrick |
TEX_CHECKER | ShaderNodeTexChecker |
TEX_COORD | ShaderNodeTexCoord |
TEX_ENVIRONMENT | ShaderNodeTexEnvironment |
TEX_GRADIENT | ShaderNodeTexGradient |
TEX_IMAGE | ShaderNodeTexImage |
TEX_MAGIC | ShaderNodeTexMagic |
TEX_MUSGRAVE | ShaderNodeTexMusgrave |
TEX_NOISE | ShaderNodeTexNoise |
TEX_SKY | ShaderNodeTexSky |
TEX_VORONOI | ShaderNodeTexVoronoi |
TEX_WAVE | ShaderNodeTexWave |
VALTORGB | ShaderNodeValToRGB |
VALUE | ShaderNodeValue |
VECT_MATH | ShaderNodeVectorMath |
Compositor Nodes
type | bl_idname |
ALPHAOVER | CompositorNodeAlphaOver |
BILATERALBLUR | CompositorNodeBilateralblur |
BLUR | CompositorNodeBlur |
BOKEHBLUR | CompositorNodeBokehBlur |
BOKEHIMAGE | CompositorNodeBokehImage |
BOXMASK | CompositorNodeBoxMask |
BRIGHTCONTRAST | CompositorNodeBrightContrast |
CHANNEL_MATTE | CompositorNodeChannelMatte |
CHROMA_MATTE | CompositorNodeChromaMatte |
COLORBALANCE | CompositorNodeColorBalance |
COLORCORRECTION | CompositorNodeColorCorrection |
COLOR_MATTE | CompositorNodeColorMatte |
COLOR_SPILL | CompositorNodeColorSpill |
COMBHSVA | CompositorNodeCombHSVA |
COMBRGBA | CompositorNodeCombRGBA |
COMBYCCA | CompositorNodeCombYCCA |
COMBYUVA | CompositorNodeCombYUVA |
COMPOSITE | CompositorNodeComposite |
CROP | CompositorNodeCrop |
CURVE_RGB | CompositorNodeCurveRGB |
CURVE_VEC | CompositorNodeCurveVec |
DBLUR | CompositorNodeDBlur |
DEFOCUS | CompositorNodeDefocus |
DESPECKLE | CompositorNodeDespeckle |
DIFF_MATTE | CompositorNodeDiffMatte |
DILATEERODE | CompositorNodeDilateErode |
DISPLACE | CompositorNodeDisplace |
DISTANCE_MATTE | CompositorNodeDistanceMatte |
DOUBLEEDGEMASK | CompositorNodeDoubleEdgeMask |
ELLIPSEMASK | CompositorNodeEllipseMask |
FILTER | CompositorNodeFilter |
FLIP | CompositorNodeFlip |
GAMMA | CompositorNodeGamma |
GLARE | CompositorNodeGlare |
GROUP | CompositorNodeGroup |
HUECORRECT | CompositorNodeHueCorrect |
HUE_SAT | CompositorNodeHueSat |
ID_MASK | CompositorNodeIDMask |
IMAGE | CompositorNodeImage |
INPAINT | CompositorNodeInpaint |
INVERT | CompositorNodeInvert |
KEYING | CompositorNodeKeying |
KEYINGSCREEN | CompositorNodeKeyingScreen |
LENSDIST | CompositorNodeLensdist |
LEVELS | CompositorNodeLevels |
LUMA_MATTE | CompositorNodeLumaMatte |
MAP_RANGE | CompositorNodeMapRange |
MAP_UV | CompositorNodeMapUV |
MAP_VALUE | CompositorNodeMapValue |
MASK | CompositorNodeMask |
MATH | CompositorNodeMath |
MIX_RGB | CompositorNodeMixRGB |
MOVIECLIP | CompositorNodeMovieClip |
MOVIEDISTORTION | CompositorNodeMovieDistortion |
NORMAL | CompositorNodeNormal |
NORMALIZE | CompositorNodeNormalize |
OUTPUT_FILE | CompositorNodeOutputFile |
PIXELATE | CompositorNodePixelate |
PREMULKEY | CompositorNodePremulKey |
RGB | CompositorNodeRGB |
RGBTOBW | CompositorNodeRGBToBW |
ROTATE | CompositorNodeRotate |
SCALE | CompositorNodeScale |
SEPHSVA | CompositorNodeSepHSVA |
SEPRGBA | CompositorNodeSepRGBA |
SEPYCCA | CompositorNodeSepYCCA |
SEPYUVA | CompositorNodeSepYUVA |
SETALPHA | CompositorNodeSetAlpha |
SPLITVIEWER | CompositorNodeSplitViewer |
STABILIZE2D | CompositorNodeStabilize |
SWITCH | CompositorNodeSwitch |
TEXTURE | CompositorNodeTexture |
TIME | CompositorNodeTime |
TONEMAP | CompositorNodeTonemap |
TRACKPOS | CompositorNodeTrackPos |
TRANSFORM | CompositorNodeTransform |
TRANSLATE | CompositorNodeTranslate |
VALTORGB | CompositorNodeValToRGB |
VALUE | CompositorNodeValue |
VECBLUR | CompositorNodeVecBlur |
VIEWER | CompositorNodeViewer |
ZCOMBINE | CompositorNodeZcombine |
Texture Nodes
type | bl_idname |
AT | TextureNodeAt |
BRICKS | TextureNodeBricks |
CHECKER | TextureNodeChecker |
COMPOSE | TextureNodeCompose |
COORD | TextureNodeCoordinates |
CURVE_RGB | TextureNodeCurveRGB |
CURVE_TIME | TextureNodeCurveTime |
DECOMPOSE | TextureNodeDecompose |
DISTANCE | TextureNodeDistance |
GROUP | TextureNodeGroup |
HUE_SAT | TextureNodeHueSaturation |
IMAGE | TextureNodeImage |
INVERT | TextureNodeInvert |
MATH | TextureNodeMath |
MIX_RGB | TextureNodeMixRGB |
OUTPUT | TextureNodeOutput |
RGBTOBW | TextureNodeRGBToBW |
ROTATE | TextureNodeRotate |
SCALE | TextureNodeScale |
TEXTURE | TextureNodeTexture |
TEX_BLEND | TextureNodeTexBlend |
TEX_CLOUDS | TextureNodeTexClouds |
TEX_DISTNOISE | TextureNodeTexDistNoise |
TEX_MAGIC | TextureNodeTexMagic |
TEX_MARBLE | TextureNodeTexMarble |
TEX_MUSGRAVE | TextureNodeTexMusgrave |
TEX_NOISE | TextureNodeTexNoise |
TEX_STUCCI | TextureNodeTexStucci |
TEX_VORONOI | TextureNodeTexVoronoi |
TEX_WOOD | TextureNodeTexWood |
TRANSLATE | TextureNodeTranslate |
VALTONOR | TextureNodeValToNor |
VALTORGB | TextureNodeValToRGB |
VIEWER | TextureNodeViewer |
The tables above were generated with a python script. This may need to be rerun in case nodes are added and missing in these lists:
import bpy
trees = [
('ShaderNodeTree', 'ShaderNode', 'Shader Nodes'),
('CompositorNodeTree', 'CompositorNode', 'Compositor Nodes'),
('TextureNodeTree', 'TextureNode', 'Texture Nodes'),
]
for tree_idname, node_idname, type_category in trees:
treetype = getattr(bpy.types, tree_idname)
nodetype = getattr(bpy.types, node_idname)
# print(treetype, nodetype)
if not treetype or not nodetype:
continue
tree = bpy.data.node_groups.new("test", tree_idname)
node_map = {}
for c in nodetype.__subclasses__():
# print(c)
if hasattr(c, "bl_rna"):
try:
node = tree.nodes.new(c.bl_rna.identifier)
node_map[node.type] = node.bl_idname
# print("%s -> %s" % (node.type, c.bl_rna.identifier))
shader_tree.nodes.remove(node)
except:
pass
sorted_node_map = sorted(node_map.items(), key=lambda item: item[0])
table = "{| {{Css/prettytable|75%%|talign=left}}\n| '''type''' || '''bl_idname'''\n|-\n%s|}" \
% "|-\n".join("| %s || %s\n" % (key, value) for key, value in sorted_node_map)
print("\n\n=====%s=====\n" % type_category)
print(table)
2.70
Node Socket in_out (r60660)
This was an enum property with 2 modes 'IN' and 'OUT'. Now there is a boolean property is_output instead that can be used in the same way:
old:
socket.in_out == 'IN' # is it an input socket?
socket.in_out == 'OUT' # is it an output socket?
new:
not socket.is_output # is it an input socket?
socket.is_output # is it an output socket?
Python Node Output Drawing (r60661)
Node output sockets now use the same button drawing function as input sockets, instead of always displaying just the label. This means pynodes scripters have to make a small adjustment in order to draw sockets in the established way, i.e. only showing labels for outputs:
def draw(self, context, layout, node, text):
- if self.is_linked:
+ if self.is_output or self.is_linked:
layout.label(text)
else:
# draw full property buttons here