本文檔的目的是解釋Python和Blender如何組合在一塊兒,涵蓋了一些在閱讀API參考和示例腳本時可能不明顯的功能。python
Blender有一個嵌入式Python解釋器,它在Blender啓動時加載,並在Blender運行時保持活動狀態。該解釋器運行腳原本繪製用戶界面,並用於Blender的一些內部工具。
Blender的嵌入式解釋器提供了典型的Python環境,所以關於如何編寫Python腳本的教程中的代碼也可使用Blender的解釋器運行。Blender爲嵌入式解釋器提供了Python模塊,例如bpy和mathutils,所以能夠將它們導入到腳本中,並能夠訪問Blender的數據,類和函數。處理Blender數據的腳本須要導入模塊才能工做。
這是一個簡單的示例,它移動附加到名爲Cube的對象的頂點:api
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中直接運行腳本的一些方法。工具
blender --python /home/me/my_script.py
要做爲模塊運行:佈局
一些Blenders功能最好保持可選,除了在啓動時加載的腳本咱們有附加組件,這些附加組件保存在它們本身的目錄中scripts/addons,而且只有在從用戶首選項中選擇時纔會在啓動時加載。
附加組件和內置Python模塊之間的惟一區別是附加組件必須包含bl_info Blender用於讀取元數據的變量,例如名稱,做者,類別和URL。
用戶首選項加載項列表使用bl_info顯示有關每一個加載項的信息。
有關bl_info字典的 詳細信息,請參閱加載項。測試
在文本編輯器中運行Python腳本對於測試頗有用,可是您須要擴展Blender以使工具能夠像其餘內置功能同樣訪問。
Blender Python api容許集成:spa
這是故意限制的。目前,對於更高級的功能,例如網格修改器,對象類型或着色器節點,必須使用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示例:code
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_module Blender,能夠註冊該模塊及其子模塊建立的全部類。
在自定義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(): del bpy.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(): del bpy.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 del bpy.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着色器定義中,例如,將它們定義爲類型並在運行中刪除它們可能頗有用。
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)
注意
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'}