Blender插件教程
什么是插件?
插件只是一个带有一些附加要求的Python模块,因此Blender可以在包含有用信息的列表中显示它。
举个例子,这是最简单的插件:
bl_info = {"name": "My Test Add-on", "category": "Object"}
def register():
print("Hello World")
def unregister():
print("Goodbye World")
bl_info
是一个包含附加元数据的字典,例如要在“用户首选项”附加列表中显示的标题,版本和作者。
register
是一个仅在启用加载项时运行的函数,这意味着可以在不激活加载项的情况下加载模块。
unregister
是一个卸载任何设置的函数register,当加载项被禁用时调用。
请注意,此附加组件不会执行与Blender相关的任何操作(bpy例如,未导入模块)。
这是一个附加组件的设计示例,用于说明附加组件的基本要求很简单。
附加组件通常会注册操作符,面板,菜单项等,但值得注意的是,当从文本编辑器甚至交互式控制台执行时,任何脚本都可以执行此操作 – 附加组件没有任何本质上不同的内容允许它与Blender集成,这些功能只是由bpy模块提供,可供任何脚本访问。
因此,附加组件只是以用户可以轻松利用的方式封装Python模块的一种方式。
注意
在文本编辑器中运行此脚本将不会打印任何内容,以查看必须通过“用户首选项”安装的输出。启用和禁用时将打印消息。
你的第一个加载项
上面最简单的附加组件作为示例很有用,但不是很多。下一个附加组件很简单,但展示了如何使用一个脚本将脚本集成到Blender中,Operator 这是定义从菜单,按钮和键盘快捷键访问的工具的典型方法。
对于第一个示例,我们将创建一个简单地移动场景中所有对象的脚本。
写剧本
将以下脚本添加到Blender中的文本编辑器:
import bpy
scene = bpy.context.scene
for obj in scene.objects:
obj.location.x += 1.0
单击“ 运行脚本”按钮,活动场景中的所有对象都将由1.0 Blender单元移动。
写加载项(简单)
此附加组件使用上面脚本的主体,并将其添加到操作员的execute()功能中。
bl_info = {
"name": "Move X Axis",
"category": "Object",
}
import bpy
class ObjectMoveX(bpy.types.Operator):
"""My Object Moving Script""" # Use this as a tooltip for menu items and buttons.
bl_idname = "object.move_x" # Unique identifier for buttons and menu items to reference.
bl_label = "Move X by One" # Display name in the interface.
bl_options = {'REGISTER', 'UNDO'} # Enable undo for the operator.
def execute(self, context): # execute() is called when running the operator.
# The original script
scene = context.scene
for obj in scene.objects:
obj.location.x += 1.0
return {'FINISHED'} # Lets Blender know the operator finished successfully.
def register():
bpy.utils.register_class(ObjectMoveX)
def unregister():
bpy.utils.unregister_class(ObjectMoveX)
# This allows you to run the script directly from Blender's Text editor
# to test the add-on without having to install it.
if __name__ == "__main__":
register()
注意
bl_info 分为多行,这只是一种用于更轻松添加项目的样式约定。
注意
bpy.context.scene我们使用context.scene传递给的参数而不是使用execute()。在大多数情况下,这些都是相同的。但是在某些情况下,运算符将被传递给自定义上下文,因此脚本作者应该更喜欢context传递给运算符的参数。
要测试脚本,您可以将其复制并粘贴到Blender的文本编辑器中并运行它。这将直接执行脚本并立即调用寄存器。
但是,运行脚本不会移动任何对象。为此,您需要执行新注册的运算符。
../../_images/advanced_scripting_addon-tutorial_operator-search-menu.png
操作员搜索菜单。
通过按下Spacebar以调出操作员搜索菜单并输入“Move X by One”(the bl_label),然后执行此操作Return。
对象应该像以前一样移动。
在Blender中打开此附加组件以进行下一步 – 安装。
安装附加组件
在Blender的文本编辑器中添加了加载项之后,您将希望能够安装它,以便可以在用户首选项中启用它以在启动时加载。
即使上面的附加组件是一个测试,但是我们仍然要完成这些步骤,以便您知道如何在以后执行此操作。
要将Blender文本作为加载项安装,首先必须将其保存到磁盘。请注意遵守适用于Python模块的命名限制,并以.py扩展名结束。
一旦文件在磁盘上,您就可以像在线下载的加载项一样安装它。
打开用户文件‣用户首选项,选择加载项选项卡,按安装加载项…并选择文件。
现在将列出加载项,您可以通过按复选框启用加载项,如果要在重新启动时启用加载项,请按“ 另存为默认值”。
注意
附加组件的目标取决于您的Blender配置。安装附加组件时,将在控制台中打印源和目标路径。您还可以通过在Python控制台中运行此命令来查找加载项路径位置。
import addon_utils
print(addon_utils.paths())
这里有关于此主题的更多内容: 目录布局。
你的第二个加载项
对于我们的第二个附加组件,我们将专注于对象实例化 – 这是 – 以与您可能在数组修饰符中看到的方式类似的方式创建对象的链接副本。
写剧本
和以前一样,首先我们将从脚本开始,开发它,然后将其转换为附加组件。
import bpy
from bpy import context
# Get the current scene
scene = context.scene
# Get the 3D cursor
cursor = scene.cursor_location
# Get the active object (assume we have one)
obj = scene.objects.active
# Now make a copy of the object
obj_new = obj.copy()
# The object won't automatically get into a new scene
scene.objects.link(obj_new)
# Now we can place the object
obj_new.location = cursor
现在尝试将此脚本复制到Blender并在默认多维数据集上运行它。确保在运行之前单击以移动3D光标,因为副本将出现在光标的位置。
运行后,请注意当您进入编辑模式以更改多维数据集时 – 所有副本都会更改。在Blender中,这称为Linked Duplicates。
接下来,我们将在循环中执行此操作,以在活动对象和游标之间创建对象数组。
import bpy
from bpy import context
scene = context.scene
cursor = scene.cursor_location
obj = scene.objects.active
# Use a fixed value for now, eventually make this user adjustable
total = 10
# Add 'total' objects into the scene
for i in range(total):
obj_new = obj.copy()
scene.objects.link(obj_new)
# Now place the object in between the cursor
# and the active object based on 'i'
factor = i / total
obj_new.location = (obj.location * factor) + (cursor * (1.0 - factor))
尝试使用活动对象运行此脚本,并将光标间隔开以查看结果。
使用这个脚本,您会注意到我们正在使用对象位置和光标进行一些数学计算,这是有效的,因为它们都是3D 实例,模块提供了一个方便的类,它允许向量乘以数字和矩阵。mathutils. Vectormathutils
如果您对这个领域感兴趣,请阅读mathutils.Vector – 有许多方便的实用功能,例如获取矢量,交叉积,点积之间的角度以及mathutils.geometry Bézier样条插值和光线三角交叉等更高级的函数。
目前我们将专注于使这个脚本成为一个附加组件,但很高兴知道这个3D数学模块可用,并可以在以后帮助您使用更高级的功能。
写加载项
第一步是将脚本按原样转换为附加组件:
bl_info = {
"name": "Cursor Array",
"category": "Object",
}
import bpy
class ObjectCursorArray(bpy.types.Operator):
"""Object Cursor Array"""
bl_idname = "object.cursor_array"
bl_label = "Cursor Array"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
scene = context.scene
cursor = scene.cursor_location
obj = scene.objects.active
total = 10
for i in range(total):
obj_new = obj.copy()
scene.objects.link(obj_new)
factor = i / total
obj_new.location = (obj.location * factor) + (cursor * (1.0 - factor))
return {'FINISHED'}
def register():
bpy.utils.register_class(ObjectCursorArray)
def unregister():
bpy.utils.unregister_class(ObjectCursorArray)
if __name__ == "__main__":
register()
这里的所有内容都已在前面的步骤中介绍过,您可能还想尝试运行附加组件,并考虑可以采取哪些措施使其更有用。
两个最明显的缺失是 – 总固定为10,并且不得不从太空杆访问操作员不是很方便。
接下来将解释这些添加内容,然后是最终脚本。
运算符属性
有许多属性类型用于工具设置,常见的属性类型包括:int,float,vector,color,boolean和string。
这些属性的处理方式与典型的Python类属性不同,因为Blender需要在界面中显示它们,将它们的设置存储在键映射中并保留设置以便重用。
虽然这是以相当Pythonic的方式处理的,但请注意,您实际上定义的是加载到Blender中的工具设置,并且在Python之外由Blender的其他部分访问。
为了摆脱文字10 total,我们将使用运算符属性。运算符属性是通过bpy.props模块定义的,它被添加到类主体中:
# moved assignment from execute() to the body of the class...
total = bpy.props.IntProperty(name="Steps", default=2, min=1, max=100)
# and this is accessed on the class
# instance within the execute() function as...
self.total
这些属性bpy.props由Blender在注册类时专门处理,因此它们在用户界面中显示为按钮。您可以将许多参数传递给属性以设置限制,更改默认值并显示工具提示。
也可以看看
bpy.props.IntProperty
本文档不会详细介绍如何使用其他属性类型。但是,上面的链接包含更高级的属性使用示例。
菜单项
附加组件可以添加到Python中定义的现有面板,标题和菜单的用户界面。
对于此示例,我们将添加到现有菜单。
../../_images/advanced_scripting_addon-tutorial_menu-id.png
菜单标识符。
要查找菜单的标识符,可以将鼠标悬停在菜单项上,并显示标识符。
用于添加菜单项的方法是将绘图函数附加到现有类中:
def menu_func(self, context):
self.layout.operator(ObjectCursorArray.bl_idname)
def register():
bpy.types.VIEW3D_MT_object.append(menu_func)
有关扩展菜单的文档,请参阅:菜单(bpy_struct)。
键图
在Blender中,附加组件有自己的键盘映射,以免干扰Blender的内置键映射。
在下面的示例中,bpy.types.KeyMap添加了一个新的对象模式,然后将a bpy.types.KeyMapItem添加到引用我们新添加的运算符的键映射中,使用它Shift-Ctrl-Spacebar作为激活它的键快捷键。
# store keymaps here to access after registration
addon_keymaps = []
def register():
# handle the keymap
wm = bpy.context.window_manager
km = wm.keyconfigs.addon.keymaps.new(name='Object Mode', space_type='EMPTY')
kmi = km.keymap_items.new(ObjectCursorArray.bl_idname, 'SPACE', 'PRESS', ctrl=True, shift=True)
kmi.properties.total = 4
addon_keymaps.append((km, kmi))
def unregister():
# handle the keymap
for km, kmi in addon_keymaps:
km.keymap_items.remove(kmi)
addon_keymaps.clear()
请注意,键盘映射项的total设置不同于操作员设置的默认设置,这允许您使用不同设置访问同一运算符的多个键。
注意
虽然Shift-Ctrl-Spacebar不是默认的Blender键快捷键,但很难确保加载项不会覆盖彼此的键盘图,至少在分配键时不要与Blender中的重要功能冲突。
有关上面列出的函数的API文档,请参阅:
- bpy.types.KeyMaps.new,
- bpy.types.KeyMap,
- bpy.types.KeyMapItems.new,
- bpy.types.KeyMapItem。
把它们一起
bl_info = {
"name": "Cursor Array",
"category": "Object",
}
import bpy
class ObjectCursorArray(bpy.types.Operator):
"""Object Cursor Array"""
bl_idname = "object.cursor_array"
bl_label = "Cursor Array"
bl_options = {'REGISTER', 'UNDO'}
total = bpy.props.IntProperty(name="Steps", default=2, min=1, max=100)
def execute(self, context):
scene = context.scene
cursor = scene.cursor_location
obj = scene.objects.active
for i in range(self.total):
obj_new = obj.copy()
scene.objects.link(obj_new)
factor = i / self.total
obj_new.location = (obj.location * factor) + (cursor * (1.0 - factor))
return {'FINISHED'}
def menu_func(self, context):
self.layout.operator(ObjectCursorArray.bl_idname)
# store keymaps here to access after registration
addon_keymaps = []
def register():
bpy.utils.register_class(ObjectCursorArray)
bpy.types.VIEW3D_MT_object.append(menu_func)
# handle the keymap
wm = bpy.context.window_manager
# Note that in background mode (no GUI available), keyconfigs are not available either,
# so we have to check this to avoid nasty errors in background case.
kc = wm.keyconfigs.addon
if kc:
km = wm.keyconfigs.addon.keymaps.new(name='Object Mode', space_type='EMPTY')
kmi = km.keymap_items.new(ObjectCursorArray.bl_idname, 'SPACE', 'PRESS', ctrl=True, shift=True)
kmi.properties.total = 4
addon_keymaps.append((km, kmi))
def unregister():
# Note: when unregistering, it's usually good practice to do it in reverse order you registered.
# Can avoid strange issues like keymap still referring to operators already unregistered...
# handle the keymap
for km, kmi in addon_keymaps:
km.keymap_items.remove(kmi)
addon_keymaps.clear()
bpy.utils.unregister_class(ObjectCursorArray)
bpy.types.VIEW3D_MT_object.remove(menu_func)
if __name__ == "__main__":
register()
../../_images/advanced_scripting_addon-tutorial_in-menu.png
在菜单中。
运行脚本(或保存它并通过之前的用户首选项添加它),它将出现在菜单中。
../../_images/advanced_scripting_addon-tutorial_op-prop.png
运营商财产。
从菜单中选择它后,您可以选择要创建的多维数据集的实例数。
注意
多次直接执行脚本也会每次添加菜单。虽然没有用处,但没有什么可担心的,因为当通过用户首选项启用时,加载项不会多次注册。
结论
附加组件可以巧妙地封装某些功能,以便编写工具来改进工作流程或编写实用程序以供其他人使用。
虽然Python在Blender中可以做的事情有限,但是在不必深入研究Blender的C / C ++代码的情况下肯定可以实现很多。
本教程中给出的示例是有限的,但显示了用于常见任务的Blender API,您可以将其扩展为编写自己的工具。
进一步阅读
Blender带有注释模板,可以从文本编辑器的标题中访问。如果您想要查看示例代码的特定区域,这是一个很好的起点