Blender Python API概述

Python API概述

本文档的目的是解释Python和Blender如何组合在一起,涵盖了一些在阅读API参考和示例脚本时可能不明显的功能。

Python in Blender

Blender有一个嵌入式Python解释器,它在Blender启动时加载,并在Blender运行时保持活动状态。该解释器运行脚本来绘制用户界面,并用于Blender的一些内部工具。

Blender的嵌入式解释器提供了典型的Python环境,因此关于如何编写Python脚本的教程中的代码也可以使用Blender的解释器运行。Blender为嵌入式解释器提供了Python模块,例如bpymathutils,因此可以将它们导入到脚本中,并可以访问Blender的数据,类和函数。处理Blender数据的脚本需要导入模块才能工作。

这是一个简单的示例,它移动附加到名为Cube的对象的顶点:

import bpy

bpy.data.objects["Cube"].data.vertices[0].co.x += 1.0

这会直接修改Blender的内部数据。在交互式控制台中运行此操作时,您将看到3D视口更新。

默认环境

在开发自己的脚本时,了解Blender如何设置其Python环境可能会有所帮助。许多Python脚本与Blender捆绑在一起,可以用作参考,因为它们使用脚本作者编写工具的相同API。脚本的典型用法包括:用户界面,导入/导出,场景操作,自动化,定义自己的工具集和定制。

在启动时,Blender会扫描scripts/startup/目录中的Python模块并导入它们。此目录的确切位置取决于您的安装。请参阅目录布局文档

脚本加载

这看起来很明显,但重要的是要注意直接执行脚本和将脚本作为模块导入之间的区别。

通过直接执行脚本来扩展Blender意味着脚本完成执行后脚本定义的类在Blender中保持可用。与将脚本作为模块导入相比,以这种方式使用脚本使得将来访问其类(例如取消注册它们)变得更加困难。将脚本作为模块导入时,其类实例将保留在模块中,稍后可以通过再次导入该模块来访问。

因此,最好避免直接执行通过注册类来扩展Blender的脚本。

以下是在Blender中直接运行脚本的一些方法。

    在文本编辑器中加载并按Run Script

    键入或粘贴到交互式控制台中。

    使用Blender从命令行执行Python文件,例如:

blender --python /home/me/my_script.py

要作为模块运行:

    - 显而易见的方法,来自文本窗口或交互式控制台的命令。import some_module

    - 打开文本块并勾选“注册”选项,这将加载混合文件。

    - 复制到其中一个目录中scripts/startup,它们将在启动时自动导入。

    - 定义为加载项,启用加载项将其加载为Python模块。

附加组件

一些Blenders功能最好保持可选,除了在启动时加载的脚本我们有附加组件,这些附加组件保存在它们自己的目录中scripts/addons,并且只有在从用户首选项中选择时才会在启动时加载。

附加组件和内置Python模块之间的唯一区别是附加组件必须包含bl_info Blender用于读取元数据的变量,例如名称,作者,类别和URL。

用户首选项加载项列表使用bl_info显示有关每个加载项的信息。

有关bl_info字典的 详细信息,请参阅加载项

通过类集成

在文本编辑器中运行Python脚本对于测试很有用,但是您需要扩展Blender以使工具可以像其他内置功能一样访问。

Blender Python api允许集成:

bpy.types.Panel

bpy.types.Menu

bpy.types.Operator

bpy.types.PropertyGroup

bpy.types.KeyingSet

bpy.types.RenderEngine

这是故意限制的。目前,对于更高级的功能,例如网格修改器,对象类型或着色器节点,必须使用C / C ++。

对于Python集成,Blender定义了所有类型共有的方法。这可以通过创建Blender类的Python子类来实现,该类包含由父类指定的变量和函数,这些变量和函数是预定义为与Blender接口的。

例如:

import bpy

class SimpleOperator(bpy.types.Operator):

    bl_idname= "object.simple_operator"

    bl_label= "Tool Name"


    def execute(self,context):

        print("Hello World")

        return{'FINISHED'}


bpy.utils.register_class(SimpleOperator)

首先请注意,我们将其成员子类化bpy.types,这对于可以与Blender集成并使用的所有类都是通用的,因此我们知道这是一个运算符而不是注册时的Panel。

两个类属性都以bl_前缀开头。这是一个用于区分Blender属性和您自己添加的属性的约定。

接下来看到execute函数,它接受运算符的实例和当前上下文。公共前缀不用于函数。

最后调用寄存器函数,这将获取类并将其加载到Blender中。请参阅班级注册

关于继承,Blender不对所使用的类继承施加限制,注册检查将使用父类中定义的属性和函数。

class mix-in示例:

import bpy

class BaseOperator:

    def execute(self,context):

        print("Hello World BaseClass")

        return{'FINISHED'}


class SimpleOperator(bpy.types.Operator,BaseOperator):

    bl_idname= "object.simple_operator"

    bl_label= "Tool Name"


bpy.utils.register_class(SimpleOperator)

请注意,这些类没有定义__init__(self)函数。而__init__()和__del__()如果定义了将被调用,在类实例寿命仅跨越执行。因此,例如一个面板将为每次重绘都有一个新实例,因此很少有理由在面板实例中存储变量。相反,持久变量应存储在Blenders ata中,以便在重新启动Blender时可以恢复状态。

注意

模态运算符是一个例外,它们的实例变量保持为Blender运行,请参见模态运算符模板。

因此,一旦类在Blender中注册,实例化类并调用函数就由Blender完成。实际上,您无法像在大多数Python API中所期望的那样从脚本中实例化这些类。

要运行运算符,可以通过运算符api调用它们,例如:

import bpy

bpy.ops.object.simple_operator()

用户界面类给出了绘制,按钮窗口,文件头,工具栏等的上下文,然后在显示该区域时绘制它们,因此Python脚本不会直接调用它们。

注册

模块注册

启动时加载的Blender模块需要register()和unregister()功能。这些是Blender从您的代码调用的唯一函数,否则它是常规的Python模块。

一个简单的Blender / Python模块可能如下所示:

import bpy


class SimpleOperator(bpy.types.Operator):

    """ See example above """


def register():

    bpy.utils.register_class(SimpleOperator)


def unregister():

    bpy.utils.unregister_class(SimpleOperator)


if __name__ == "__main__":

    register()

这些函数通常出现在包含类注册的脚本的底部,有时会添加菜单项。您也可以将它们用于内部目的,为您自己的工具设置数据,但要小心,因为加载新的混合文件时寄存器不会重新运行。

使用了注册/取消注册调用,因此可以在Blender运行时切换加载项和重新加载脚本。如果寄存器调用放在脚本的主体中,则会在导入时调用注册,这意味着导入模块或将其类加载到Blender之间没有区别。

当脚本从另一个模块导入类时,这会成为问题,因为很难管理正在加载哪些类以及何时加载。

最后两行仅用于测试:

if __name__ == "__main__":

    register()

这允许脚本直接在文本编辑器中运行以测试更改。register()将脚本作为模块导入时,此调用将不会运行,因为__main__保留用于直接执行。

类注册

使用Blender注册类会导致类定义被加载到Blender中,并在现有功能的同时可用。

加载此类后,您可以bpy.types使用bl_idname而不是类原始名称来访问它。

加载类时,Blender执行完整性检查,确保找到所有必需的属性和函数,属性具有正确的类型,并且函数具有正确数量的参数。

大多数情况下,你不需要担心这个问题,但是如果类定义有问题,它将在注册时引发:

使用函数参数,将引发异常:def execute(self, context, spam)

ValueError: expected Operator, SimpleOperator class "execute" function to have 2 args, found 3

使用将提高。bl_idname = 1

TypeError: validating class error: Operator.bl_idname expected a string type, not int

多类

上面描述了将类加载到Blender中,对于简单的情况,调用bpy.utils.register_class(SomeClass)就足够了,但是当有很多类或子模块子模块有自己的类时,将它们全部列入注册可能会很繁琐。

为了更方便的加载/卸载bpy.utils.register_module(模块)和bpy.utils.unregister_module(模块)功能存在。

一个脚本,它定义了许多自己的操作符,面板菜单等,你只需要编写:

def register():

    bpy.utils.register_module(__name__)


def unregister():

    bpy.utils.unregister_module(__name__)

内部Blender在可注册类型上收集子类,通过定义它们的模块存储它们。通过将模块名称传递给bpy.utils.register_moduleBlender,可以注册该模块及其子模块创建的所有类。

类间依赖性

在自定义Blender时,您可能希望将自己的设置组合在一起,毕竟,它们可能必须与其他脚本共存。要对这些属性进行分组,需要定义类,对于组内的组或组内的集合,您可以发现自己必须处理注册/取消注册的顺序。

自定义属性组本身就是需要注册的类。

假设您要存储自定义引擎的材质设置。

# Create new property

# bpy.data.materials[0].my_custom_props.my_float

import bpy


class MyMaterialProps(bpy.types.PropertyGroup):

    my_float=bpy.props.FloatProperty()


def register():

    bpy.utils.register_class(MyMaterialProps)

    bpy.types.Material.my_custom_props =bpy.props.PointerProperty(type=MyMaterialProps)


def unregister():

    delbpy.types.Material.my_custom_props

    bpy.utils.unregister_class(MyMaterialProps)


if __name__ == "__main__":

    register()

注意

该类必须在用于属性之前注册,否则将引发错误:

ValueError: bpy_struct "Material" registration error: my_custom_props could not register

# Create new property group with a sub property

# bpy.data.materials[0].my_custom_props.sub_group.my_float

import bpy


class MyMaterialSubProps(bpy.types.PropertyGroup):

    my_float=bpy.props.FloatProperty()


class MyMaterialGroupProps(bpy.types.PropertyGroup):

    sub_group=bpy.props.PointerProperty(type=MyMaterialSubProps)


def register():

    bpy.utils.register_class(MyMaterialSubProps)

    bpy.utils.register_class(MyMaterialGroupProps)

    bpy.types.Material.my_custom_props =bpy.props.PointerProperty(type=MyMaterialGroupProps)


def unregister():

    delbpy.types.Material.my_custom_props

    bpy.utils.unregister_class(MyMaterialGroupProps)

    bpy.utils.unregister_class(MyMaterialSubProps)


if __name__ == "__main__":

    register()

注意

最低级别需要首先注册,而unregister()是register()的镜像

操纵类

可以在Blender运行时添加和删除属性,通常在注册或取消注册时发生,但对于某些特殊情况,在脚本运行时修改类型可能很有用。

例如:

# add a new property to an existing type

bpy.types.Object.my_float =bpy.props.FloatProperty()

# remove

delbpy.types.Object.my_float

这适用于您自己定义的PropertyGroup子类。

class MyPropGroup(bpy.types.PropertyGroup):

    pass

MyPropGroup.my_float =bpy.props.FloatProperty()

......这相当于:

class MyPropGroup(bpy.types.PropertyGroup):

    my_float=bpy.props.FloatProperty()

动态定义类(高级)

在某些情况下,数据的说明符可能不在Blender,renderman着色器定义中,例如,将它们定义为类型并在运行中删除它们可能很有用。

foriin range(10):

    idname= "object.operator_%d" %i


    def func(self,context):

        print("Hello World", self.bl_idname)

        return{'FINISHED'}


    opclass= type("DynOp%d" %i,

                   (bpy.types.Operator,),

                   {"bl_idname":idname,"bl_label": "Test", "execute":func},

                   )

    bpy.utils.register_class(opclass)

注意

type()被调用来定义类。这是Python中类创建的替代语法,更适合动态构造类。

要从上一个示例中调用运算符:

>>> bpy.ops.object.operator_1()

Hello World OBJECT_OT_operator_1

{'FINISHED'}

>>> bpy.ops.object.operator_2()

Hello World OBJECT_OT_operator_2

{'FINISHED'}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容