Blender的插件開發-Operator操做器(算子)

        毫無疑問,Operator是Blender中最爲核心的一個對象,並且裏面一應俱全(不只能夠操做幾何對象,全部的菜單均可以控制,由於Blender其實就是一個三維圖形的超級命令解釋器嘛!)。python

        咱們先定義一個簡單的Operator。把下面的代碼複製到文本編輯中,點擊「執行腳本」。
shell

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)

        在三維視窗中,鼠標點擊,按下空格鍵,輸入「tool」,將會列出這個「Tool Name」的Operator工具。app

        繼續,選中「Tool Name」項,並無出現上面的execute中定義的「Hello World」,怎麼回事!?ide

        由於Blender把輸出信息定義到標準輸出了,若是從控制檯窗口啓動,將從控制檯上看到輸出的信息。Blender並無把信息輸出到信息窗口和python控制檯,這是一個讓圖形界面使用者有點困惑的地方,先按下不表,後面再想辦法解決。工具

        如今,換一種方式來定義Operator:
測試

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)

        這裏用到了python的類的繼承特性。這是官方文檔的例子,但我以爲更符合對象的定義應該是:this

import bpy

class BaseOperator:
    bl_idname = "object.simple_operator"
    bl_label = "Tool subclass."

class SimpleOperator(BaseOperator,bpy.types.Operator):
    def execute(self, context):
        print("Hello World,i am subclass.")
        return {'FINISHED'}

bpy.utils.register_class(SimpleOperator)

        酸菜蘿蔔,各有所愛。一種是行爲優先,符合腳本化思路;第二種是標識優先,符合OO的習慣。測試了下,兩種方式卻是都能運行。spa

        上面直接把Operator的執行類註冊到系統了,下面採用更通用的作法:code

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()

        一個更綜合一點的例子:對象

# 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():
    del bpy.types.Material.my_custom_props
    bpy.utils.unregister_class(MyMaterialGroupProps)
    bpy.utils.unregister_class(MyMaterialSubProps)

if __name__ == "__main__":
    register()

        由於「一切皆對象」,Python腳本還能夠建立動態類型。以下:

# add a new property to an existing type
bpy.types.Object.my_float = bpy.props.FloatProperty()

# remove
del bpy.types.Object.my_float

        對類對象一樣適用:

class MyPropGroup(bpy.types.PropertyGroup):
    pass
MyPropGroup.my_float = bpy.props.FloatProperty()

#等價於下面的語法:
class MyPropGroup(bpy.types.PropertyGroup):
    my_float = bpy.props.FloatProperty()

        動態定義的對象:

for i in 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)

    有點頭大,很少解釋了。運行下看:

>>> bpy.ops.object.operator_1()
Hello World OBJECT_OT_operator_1
{'FINISHED'}

>>> bpy.ops.object.operator_2()
Hello World OBJECT_OT_operator_2
{'FINISHED'}

    如今,咱們建立一個操做幾何對象的Addon:

import bpy

bl_info = {
    "name": "Move X Axis",
    "category": "Object",
    }

class ObjectMoveX(bpy.types.Operator):
    """My Object Moving Script"""      
    # blender will 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.

    # execute() is called by blender when running the operator.
    def execute(self, context):     # The original script
        scene = context.scene
        for obj in scene.objects:
            obj.location.x += 1.0
        return {'FINISHED'}      #this 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 blenders text editor
# to test the addon without having to install it.
if __name__ == "__main__":
    register()

    一個擴展了菜單和快捷鍵映射的例子:

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()

複製到文本編輯框,點擊「運行腳本」運行下看:

In the menu

運行這個菜單項,結果以下:

Operator Property

當選中之後,能夠選擇一次建立多少個Cube對象。

注意:直接運行這個腳本,將會致使菜單項屢次加載。可是放到Addon就沒有這個問題了。

相關文章
相關標籤/搜索