2021年4月6日 星期二

在 Blender 開發 Addon 時載入自己的 Module

 在 Blender 開發 Addon 時載入自己的 Module

前言

  由於需要從 Blender 匯出自製遊戲引擎的格式,所以就需要開發 Addon ,但由於要支援的規格越來越多,所以程式碼就越來越多,全部都塞在一個檔案寫起來並不是很舒服,之前不拆成其它 Module 來載入是因為找不到方法來載入,官方並沒有提供載入 Module 的範例,但最近在 Valve 的 Source 引擎提供給 Blender 的 Addon 裡發現它可以載入自己的 Module ,所以就來研究是如何做的,在此做個紀錄。


內容

  Valve 的 Source 引擎提供給 Blender 的 Addon 可以在 [ steamreview.org ] Blender Source Tools 下載。在研究中發現以前從沒想過把 Addon 裝起來跟直接在 Blender 的文字介面有何不同,所以總是在文字介面裡做實驗,所以 Import 自己的 Module 總是會失敗,但我看了 Valve 提供的 Addon ,裡面遽然可以直接 Import 自己的 Module ,本以為事情解決了,但發現一些小毛病!如果在安裝的環境下開發,當程式被修改時, Blender 並不會去 Reload 它,這裡我找到兩種方法來 Reload ,一種是直接用 Blender 的操作介面來 Reload ,如下圖

Reload Addon 在 Blender 的介面

這個方法很簡潔,但不知為什麼每次 Reload 時會小小的頓一下,所以我個人比較建議第二種方法。第二種方法是利用 Addon 的啟動按鍵來 Reload ,如下圖

利用 Addon 的起用來 Reload

這個方法有個麻煩, 只是單純的利用關閉後再啟用會發現其實它不會被 Relaod ,但如果主程式(__init__.py)的修改日期有變動時,它就會 Reload,這種 Reload 操作會負責一點,但可以確定只 Reload 這個 Addon。解決了不會 Reload 後又發現當自己的 Module 被修改時,也不會 Reload! Reload 似乎只針對主程式(__init__.py),所以需要在主程式增加 Reload 自己的 Module ,這段程式碼我在 Valve 的 Addon 裡找到了如下


 # Python doesn't reload package sub-modules at the same time as __init__.py!
import importlib, sys
for filename in [ f for f in os.listdir(os.path.dirname(os.path.realpath(__file__))) if f.endswith(".py") ]:
	if filename == os.path.basename(__file__): continue
	module = sys.modules.get("{}.{}".format(__name__,filename[:-3]))
	if module: importlib.reload(module)

# clear out any scene update funcs hanging around, e.g. after a script reload
from bpy.app.handlers import depsgraph_update_pre, depsgraph_update_post
for func in depsgraph_update_post:
	if func.__module__.startswith(__name__):
		depsgraph_update_post.remove(func)
 

要在主程式裡加這段才可以 Reload 自己的 Module 。完整的範例程式碼如下

__init__.py

bl_info = {
	"name": "My export addon",
	"author": "Hosee",
	"version": (1, 0, 0),
	"blender": (2, 80, 0),
	"category": "Import-Export",
	"location": "",
	"wiki_url": "",
	"tracker_url": "",
	"description": "My custom export addon."
}
#
import bpy, os
from bpy_extras.io_utils import ExportHelper
from bpy.props import StringProperty, BoolProperty, EnumProperty
from bpy.types import Operator

#follow code from Valve
# Python doesn't reload package sub-modules at the same time as __init__.py!
import importlib, sys
for filename in [ f for f in os.listdir(os.path.dirname(os.path.realpath(__file__))) if f.endswith(".py") ]:
	if filename == os.path.basename(__file__): continue
	module = sys.modules.get("{}.{}".format(__name__,filename[:-3]))
	if module: importlib.reload(module)

# clear out any scene update funcs hanging around, e.g. after a script reload
from bpy.app.handlers import depsgraph_update_pre, depsgraph_update_post
for func in depsgraph_update_post:
	if func.__module__.startswith(__name__):
		depsgraph_update_post.remove(func)
#
#import my module
from . import utility
#
class ExportSomeData(Operator, ExportHelper):
	bl_idname = "export_test.something" 
	bl_label = "Export something"

	filename_ext = ".txt"

	filter_glob: StringProperty(
		default="*.txt",
		options={'HIDDEN'},
		maxlen=255,  # Max internal buffer length, longer would be clamped.
	)

	use_setting: BoolProperty(
		name="Example Boolean",
		description="Example Tooltip",
		default=True,
	)

	type: EnumProperty(
		name="Example Enum",
		description="Choose between two items",
		items=(
			('OPT_A', "First Option", "Description one"),
			('OPT_B', "Second Option", "Description two"),
		),
		default='OPT_A',
	)

	def execute(self, context):
		utility.do_something()
		return {"FINISHED"}


# Only needed if you want to add into a dynamic menu
def menu_func_export(self, context):
	self.layout.operator(ExportSomeData.bl_idname, text="Text Export Operator")


def register():
	bpy.utils.register_class(ExportSomeData)
	bpy.types.TOPBAR_MT_file_export.append(menu_func_export)


def unregister():
	bpy.utils.unregister_class(ExportSomeData)
	bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)

if __name__ == "__main__":
		register()


utility.py

def do_something():
	print('Do something')


完整的程式碼可以在 [ Gitlab ] basic_export_addon 下載。

 

  雖然照著上述的做法就可以載入自己的 Module ,但還是有些麻煩,如修改後要測試要經過 Reload 才能看到結果,或是修改後如果要搭配版本管理(Version control)時,要把修改的那一份(Blender 安裝的資料夾)複製到版本管理(Version control)才能 Commit ,這些麻煩目前還找不到方法解決,如果解決了會再寫一篇。


參考資料

[ steamreview.org ] Blender Source Tools


相關資料

[ Gitlab ] basic_export_addon


沒有留言:

張貼留言