Blender 之 入门
KAlO2
随笔 - 24 文章 - 2 评论 - 23 阅读 - 73554
Blender 之 Splash 代码分析
注:以下内容基于 Blender 2.7x 版本工程,其它低版本可能有改动。
Blender启动完成时,会出现一个画面,英文叫Splash。默认是打开的,可以在设置里关闭。在文件菜单里点击用户首选项(快捷键Ctrl + Alt + U),在弹出的窗口第一个Tab页面,也就是界面(Interface)的右下角,有一个选项 Show Splash,默认打了勾,关闭然后最下一行的 Save User Settings。这样,下次启动的时候就不会出现Splash。
假设你已经下载好了blender工程代码,下面通过Splash讲解C语言Operator的实现,前一篇是关于Python的Operator。
datatoc
Splash资源所在的文件夹是 blender/release/datafiles/ ,一起的还有各种图标、字体、SVG矢量图、画笔以及matcap效果图。
source/blender/editors/datafiles/CMakeLists.txt 会调用 data_to_c_simple 函数从图片格式转换成C代码(其实就是长度加二进制数据),blender 需要在源代码路径外编译(out of source build) ,对应生成在 blender-build/release/datafiles/ 。
data_to_c_simple(../../../../release/datafiles/splash.png SRC)
data_to_c_simple 函数(cmake的宏)定义在 build_files/cmake/macros.cmake
data_to_c 需要提供file_from、file_to参数,data_to_c_simple 则仅提供 file_from 参数,file_to 的值为 ${file_from}.c。
调用的命令是 datatoc 其工程在 source/blender/datatoc/ 。
该 CMakeLists.txt 的最后一句指明生成 bf_editor_datafiles.lib
blender_add_lib(bf_editor_datafiles "${SRC}" "${INC}" "${INC_SYS}")
bf_editor_datafiles.vcxproj -> blender-build/lib/Debug/bf_editor_datafiles.lib
blender/source/blenderplayer/CMakeLists.txt
bf_editor_datafiles 被包含在 BLENDER_SORTED_LIBS 里,blender.exe blenderplayer.exe 会依赖这些工程库。
target_link_libraries(blenderplayer ${BLENDER_SORTED_LIBS})
target_link_libraries(blender ${BLENDER_SORTED_LIBS})
这些图片资源最终转换成C数组数据链接到.exe文件里。
好处是:如果缺少相关资源(写错名字、用了中文字符、或者图片格式误写),编译期就会报错;如果仅仅是替换掉一张图片的话,错误只能在运行时被发现,导致出错。
坏处是:不仅仅是换掉图片资源就好了的,还需要重新编译。这让一些想把splash换成自己作品的艺术家望而却步。
后话:Google Summer Of Code 2016 列出了可配置的Splash想法(Configuration Splash Screen)。
main函数
Blender.sln 解决方案包含多个工程,cmake工具会额外生成 ALL_BUILD / INSTALL / PACKAGE / RUN_TESTS / ZERO_CHECK 等几个工程。其中,ALL_BUILD与INSTALL相当于makefile里面的make与install命令。
编译完成,在运行的时候,需要将启动工程从 ALL_BUILD 修改成 blender,否则会提示 Unable to start program 'build\x64\Debug\ALL_BUILD' 拒绝访问。(在blender工程上鼠标右键后,选择 Set as StartUp Project 即可)
一切的一切,要从main函数开始……
main函数在 blender 工程里的 source/creator/creator.c ,初始化子系统(图像、修改器、画笔、Python、节点、材质等)、处理命令行参数、进入消息循环WM_main()或者退出循环在后台运行(-b参数可以指定在后台渲染)。
BLI_argsAdd(ba, 1, "-b", "--background", "\n\tRun in background (often used for UI-less rendering)", background_mode, NULL);
所有有UI的应用程序会有消息循环机制。Blender 的是在 WM_main(C);
WM_main 的实现在 source/blender/windowmanager/intern/wm.c ,很简单的几行代码。
void WM_main(bContext *C)
{
while (1) {
/* get events from ghost, handle window events, add to window queues */
wm_window_process_events(C);
/* per window, all events to the window, screen, area and region handlers */
wm_event_do_handlers(C);
/* events have left notes about changes, we handle and cache it */
wm_event_do_notifiers(C);
/* execute cached changes draw */
wm_draw_update(C);
}
}
在 WM_main(C); 一行的上面是关于splash的,
if(!G.file_loaded)
WM_init_splash(C);
Blender 有两个重要的数据结构 Global G; 和 UserDef U; ,见名知意。
Global 的字段 file_loaded 是 bool 类型的,但是字符串全局搜索到的是赋值为整形1,其实就是true。
source/blender/windowmanager/intern/wm_operators.c: G.file_loaded = 1; /* prevents splash to show */
file_loaded 为 true 则阻止加载splash,否则启动时加载splash。
Splash
关于 splash 的代码,就在 WM_init_splash(C); 这一行了。
WM是WindowManager的缩写,C语言缺少 C++ 的 namespace 概念,这样附带模块命名以防冲突。
来看看代码中有关splash的地方,Blender一个很常见的概念是Operator。
source/blender/windowmanager/intern/wm_init_exit.c
void WM_init_splash(bContext *C)
{
if ((U.uiflag & USER_SPLASH_DISABLE) == 0) {
wmWindowManager *wm = CTX_wm_manager(C);
wmWindow *prevwin = CTX_wm_window(C);
if (wm->windows.first) {
CTX_wm_window_set(C, wm->windows.first);
WM_operator_name_call(C, "WM_OT_splash", WM_OP_INVOKE_DEFAULT, NULL);
CTX_wm_window_set(C, prevwin);
}
}
}
U.uiflag的比特位差不多用光了,又开了uiflag2。其中,USER_SPLASH_DISABLE 对应上面用户偏好里的 Show Splash。
bContext 是很重要的数据结构,通过指针传递,函数定义在 source/blender/blenkernel/intern/context.c,很多地方都引用了它,应该定义在 .h 文件里的。
给变量取好名字,更方便读代码。WM_operator_name_call 见名知意,根据Operator的名字来调用函数,比如上面就是想调用 "WM_OT_splash" 函数。
int WM_operator_name_call(bContext *C, const char *opstring, short context, PointerRNA *properties)
{
wmOperatorType *ot = WM_operatortype_find(opstring, 0);
if (ot) {
return WM_operator_name_call_ptr(C, ot, context, properties);
}
return 0;
}
在 blender/source/blender/windowmanager/intern/wm_operators.c 你可以看到关于 Operator 的各种操作,通过函数 WM_operatortype_append 可以添加新的 Operator,由于有很多 Operator,Blender 用自己的哈希表 GHash *global_ops_hash 管理,WM_OT_splash 是在 WM_init() 初始化时添加的。
/* called on initialize WM_init() */void wm_operatortype_init(void)
{
/* reserve size is set based on blender default setup */
global_ops_hash = BLI_ghash_str_new_ex("wm_operatortype_init gh", 2048);
...
WM_operatortype_append(WM_OT_splash);
...
}
调用的栈是这样的
int main(int argc, const char **argv)
WM_init(C, argc, (const char **)argv);
wm_operatortype_init();
WM_operatortype_append(WM_OT_splash);
WM_OT_splash,WM是Operator的种类,_OT_ 是 Operator Type,构成Operator ID名称的一部分,该函数填充 wmOperatorType 里的字段。
static void WM_OT_splash(wmOperatorType *ot)
{
ot->name = "Splash Screen";
ot->idname = "WM_OT_splash";
ot->description = "Open the splash screen with release info";
ot->invoke = wm_splash_invoke;
ot->poll = WM_operator_winactive;
}
前一篇讲到过 name、idname、description。struct wmOperatorType 也给了注释。name 会在UI上显示出来的。idname 名字应该和函数名字一致,这里可以用 __func__ 宏的。description 是提示信息,鼠标放在上面会显示出来。
poll回调函数用来测试该Operator是否可以在当前环境(context)执行。我发现有些人在读代码的时候,对变量或函数的取名不在意。poll是轮询的意思,通过名称就能猜到要完成的功能。有关函数WM_operator_poll
invoke 就是函数调用了。
因为是static函数,所以只需要在本文件内搜索调用处,其他文件是不能调用的。这也算是一种搜索技巧。
static int wm_splash_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event))
{
UI_popup_block_invoke(C, wm_block_create_splash, NULL);
return OPERATOR_FINISHED;
}
C返回 OPERATOR_FINISHED 的状态,对应 Python 返回 {"FINISHED"}。
接着就是 wm_block_create_splash 函数了,画出Splash的地方,Splash上面的跳转链接也写在这个函数里。
// blender/source/blender/windowmanager/intern/wm_operators.c
splash.png
WITH_HEADLESS 名字看不懂,CMakeLists.txt 里解释说是不带图形界面的编译(渲染农场,服务端模式),默认是关闭的。
这里引用到了 splash.png 的图,工程datatoc用来将splash.png图片资源转换成长度datatoc_splash_png_size 和二进制的dump datatoc_splash_png,上面讲过。
有关函数wm_operator_invoke,在 source/blender/windowmanager/intern/wm_event_system.c,调用栈是这样的:
int main(int argc, const char **argv)
WM_main(C);
WM_init_splash(C);
WM_operator_name_call(C, "WM_OT_splash", WM_OP_INVOKE_DEFAULT, NULL);
WM_operator_name_call_ptr(C, ot, context, properties);
wm_operator_call_internal(C, ot, properties, NULL, context, false);
wm_operator_invoke(C, ot, event, properties, reports, poll_only);
wm_operator_invoke
invoke和exec是函数指针,要二选一填充,否则就是非法的Operator调用。上面的Splash Operator用的是invoke,它们的函数原型是:
int (*exec)(struct bContext *, struct wmOperator *);
int (*invoke)(struct bContext *, struct wmOperator *, const struct wmEvent *);
invoke 比 exec 多出一个 struct wmEvent* 参数,返回类型是int,其实是下面的无名enum类型。
调用exec或invoke后,都会 OPERATOR_RETVAL_CHECK(ret) 测试一下返回值。
invoke比exec多出的一句是:wm_region_mouse_co(C, event);
OPERATOR_RETVAL_CHECK
Blender的用户偏好设置是可以保存在.blend文件里的,这是关于Splash的。
source/blender/makesrna/intern/rna_userdef.c
prop = RNA_def_property(srna, "show_splash", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "uiflag", USER_SPLASH_DISABLE);
RNA_def_property_ui_text(prop, "Show Splash", "Display splash screen on startup");
source/blender/makesdna/DNA_userdef_types.h
USER_SPLASH_DISABLE = (1 << 27),
参考:
Context + Operator = Action!
blender工程_blender插件开发入门
金门走狗 2020-12-27 03:33:07 825 收藏 5
文章标签: blender工程
版权
身为游戏开发者,不想只用blender建模,想写插件提升工作效率?
一、介绍
我希望这篇文章可以让你快速入门,早知道插件的套路,少走弯路,这篇文章将先直接快速演示一遍如何创建插件工程,从为blender添加一个简单实用小功能开始,开始带大家在接下来的时间逐渐熟悉blender插件开发,然后才是回过头来,介绍必要的常识资料。
(我想大家在blender画贴图后,一定会一遍一遍不厌其烦的手动保存贴图-UV/Image_Editor-Image-Save All Images,因为blender文件保存时不会保存贴图等数据,一旦什么时候忘记保存贴图,在心安理得保存完blender文件后关闭blender猛然想起贴图没了,可谓欲吐血。这次要添加的简单又极其实用的功能便是:保存blender文件时自动保存所有已修改图片)
建议跟随本文章演示做一遍,本篇文章创建的基础工程将在未来不断填充扩展功能。
本文章使用2.79版本,建议插件入门不要使用2.8版本。
二、创建第一个插件工程
1、进入blender的用户插件文件夹,创建工程文件
上两图贴出了blender的两个插件路径。
第一个路径是用户目录插件,目录:userAppDataRoamingBlender FoundationBlenderscriptsaddons,代表非系统原生插件,blender所有安装的外置插件最终都会被解压放置到这个文件夹下。安装插件可以在blender的addon界面直接选择zip文件安装,也可以把zip文件中文件夹直接拖入此目录完成安装。。
第二个是系统插件路径,不建议将自己写的插件放入此地,此地不少系统插件的代码可在以后做参考用,值得了解。
请在userAppDataRoamingBlender FoundationBlenderscriptsaddons文件夹下新建工程文件夹HelloAddon,并在HelloAddon文件夹下再次新建三个文件夹“command_operators”"model_data"、"view_panels"。
工程文件布局仿造mvc,在前期,我们会经常与command_operators与view panels打交道,组合blender自身的命令与编写相关界面,至于数据层自然也都是blender内置的各种数据了,基本不需要自定义数据层,暂不关注model_data文件夹。
我使用的文本编辑器为sublime3,仅有方便打开工程文件与python语法高亮功能:
2、新建基础脚本
a.HelloAddon文件夹下新建入口脚本__init__.py
bl_info = {
"name": "HelloAddon",
"author": "作者",
"version": (1, 0),
"blender": (2, 79, 0),
"location": "View3D > Tool Shelf >HelloAddon Panel",
"description": "插件描述",
"warning": "",
"wiki_url": "",
"category": "3D View",
}
import bpy
import logging
from . import model_data
from . import command_operators
from . import view_panels
def register():
bpy.utils.register_module(__name__)
command_operators.register()
view_panels.register()
def unregister():
bpy.utils.unregister_module(__name__)
command_operators.unregister()
view_panels.unregister()
if __name__ == "__main__":
register()
以上代码就是套路,不需要理解,主体为bpy.utils.register_module(__name_),作用是注册import进来的所有模块。至于command_operators.register() 与 view_panels.register()则代表其他非模块相关的注册。这里的代码以后要新建工程直接ctrl c即可。
view_panels文件夹底新建脚本_init__.py
import bpy
def register():
pass
def unregister():
pass
3、开始添加自动保存图片功能
command_operators文件夹下新建脚本save_dirty_images.py
import bpy
from bpy.app.handlers import persistent
from bpy.types import Operator
@persistent
def save_dirty_images(dummy):
bpy.ops.image.save_dirty()
print('save image')
保存文件时自动保存图片的其中一句关键代码就在这里,即使用了系统命令bpy.ops.image.savedirty()。
扩展:可以在blender的系统脚本中找到此命令的类SaveDirty(Operator) 查看实现path:blender/release/scripts/startup/bl_operators/image.py:
这个类的参数Operator,代表此类为blender可调用的操作类,其他函数中可根据bl_idname中的值直接调用执行此类的execute方法,bl_label为blender界面中直接调用执行此方法的命令名称,可在blender2.79中空格键键入SaveAllDirtyImages直接执行此类中的execute中的函数功能。
我们的脚本中首先使用了一个空函数save_dirty_images(dummy)调用命令bpy.ops.image.save_dirty(),即执行系统类SaveDirty(operator)的execute方法,这样包装起来是为了后续要将此函数传入blender的保存文件回调方法中做参数,该方法接受一个函数而不是方法,且@persistent与(dummy)是必须的参数。此外我们在save_dirty_images函数底下 print('save image')输出语句,以便待会能在控制台看到执行成功的消息。
这里虽然创建了可直接执行的命令,但要使得保存blender时自动保存修改过的图片,还差了一步:找到blender保存文件的回调函数,并将此类附加上去。
commandoperators文件夹下新建_init__.py脚本
import bpy
from . import(
save_dirty_images,
)
def register():
#bpy.app.handlers.save_pre.append为blender保存文件时的回调
bpy.app.handlers.save_pre.append(save_dirty_images.save_dirty_images)
def unregister():
bpy.app.handlers.save_pre.append(save_dirty_images.save_dirty_images)
好了,我们的目的已经达到了,接下来打开blender,开始看效果!
4、打开blender,启用插件,测试功能
ctrl+alt+U打开配置界面,启用HelloAddon,并点击左下按钮SaveUserSettings保存设置。
Window/Toggle System Console打开控制台,待会可看到命令执行成功我们要输出的语句。
回到blender主界面,空格键后输入SaveAllDirtyImages,可以看到我们刚才写的函数:
回车键执行,可看到控制台输出了“save image”
也可以直接ctrl-s保存blender文件,也会在控制台输出save image,代表确实执行了保存图片命令,不过我们目前还未创建图片,没能看到效果,创建图片的步骤留给读者自行尝试,在image editor创建图片后切换到paint模式抹两笔,先手动将图片保存到自己要的目录,然后就可以开始测试了,继续往图片上抹,然后ctrl-s保存blender文件,将能观察到图片也自动保存更新了。
保存blender文件时自动保存所有更改图片,从此再无忘记保存图片的烦恼,各位读者用起来!
5、功能升级
上述虽然实现了保存文件自动保存图片功能,但唯一的缺点是其只对已有保存路径的图片而言有效,若是新建图片还未保存指定保存路径,那么此方法会将其直接忽视掉,原因见前面的系统脚本实现。
所以我们现在来改写系统的方法,当然不是直接更改系统文件,我们将其实现copy出来,加上一个提示功能:若有新建图片未指定保存路径,则自动将图片保存到blender文件的根目录下。
代码如下:
import bpy
import os
from bpy.app.handlers import persistent
from bpy.types import Operator
@persistent
def save_dirty_images(dummy):
unique_paths = set()
for image in bpy.data.images:
if image.is_dirty:
if image.packed_file:
if image.library:
self.report({'WARNING'},
"Packed library image: %r from library %r"
" can't be re-packed" %
(image.name, image.library.filepath))
else:
image.pack(as_png=True)
else:
# 若blender与图片均无路径,则忽略此图片自动保存
# 若blender有路径而图片无路径,根目录上自动保存
if image.filepath == "":
if not bpy.data.filepath == "":
filepath = CreateUniquePath(os.path.split(bpy.data.filepath)[0] + "", image.name, ".png")
image.filepath = filepath
else:
filepath = bpy.path.abspath(image.filepath,
library=image.library)
if "" not in filepath and "/" not in filepath:
print("Invalid path: " + filepath)
elif filepath in unique_paths:
print("Path used by more than one image: %r" %
filepath)
else:
unique_paths.add(filepath)
image.save()
print('save image')
def CreateUniquePath(base_path, file_name, extension):
path = base_path + file_name + extension
while os.path.isfile(path):
file_name += ".001"
path = base_path + file_name + extension
return path
class SaveDirty(Operator):
"""Save all modified textures"""
bl_idname = "image.save_dirty_images"
bl_label = "SaveAllDirtyImages"
def execute(self, context):
save_dirty_images(None)
return {'FINISHED'}
将代码更改后,现在能保存新建未指定路径的图片,路径图片名称将与blender文件内的图片名称相同,若根目录下已有相同名称图片,则保存名称往后叠加.001。
到这里为止,一个完整而实用的小功能就完成了!
6、为下一篇文章做准备
只有一个看不见摸不着的命令怎么行,本着全都要的原则,下面我们来实现一个UI的hello world!下面先来写一个界面函数。
view_panels文件夹底下新建脚本hello_panel.py,并修改viewpanels/_init__.py,添加语句
from . import(hello_panel)
view_panels文件夹底下新建脚本hello_panel.py
import bpy
from bpy.types import Panel, Menu, UIList, PropertyGroup
from HelloAddon import command_operators
class HelloWorld(Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'TOOLS'
bl_category = 'HelloAddon'
bl_idname = 'hello_world'
bl_label = 'HelloWorld'
# bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
layout.label("你好UI")
layout.operator("image.save_dirty_images", text="保存图片")
重新打开blender(注意blender插件更改后需要重启blender):
可以看到T面板已经出现HelloAddon面板,包含HelloWorld标题(由bl_label决定)与Label“你好UI”以及按钮“保存图片”。
三、blender脚本插件入门心得
跟着上面做完一个插件功能,现在开始介绍如何自行深入学习blender脚本:
途径1:打开blender的text editor,很多模板文件可供复读(制):
途径2:活用python console中的.后代码提示快捷键自动补全查看方法
默认的智能提示快捷键未ctrl+space,对中国人而言实在不友好,我改为了ctrl+alt+/,建议读者也改为此键位。
途径3:关注Outliner的Data-Blocks面板,极其方便,新手时对blender数据块摸不着头脑时务必打开此面板,直接http://bpy.data.xxx点出来,如bpy.data.images['myimage']....、bpy.data.objects['myobj'].data...,此面板下要找什么数据一路沿着+号点下去即可看到,可以说是可视化的代码自动补全!
途径4:查询api(实用性不高)
途径5:blender python 全球最大爱好者论坛:Stack Exchange
途径6:查看各类开源插件
blender插件那么多,随便拿起一个找到想学的功能就可以开始copy了,不需要自己从零走,何乐不为
本篇文章到此结束,有什么想法欢迎提出,下一篇文章内容将根据评论区而定,当然这第一篇文章很可能根本没什么人看到,那我就按照自己想法继续走啦。
相关资源:blender:自行编写或修改的Blender插件-源码_blender插件开发...
————————————————
版权声明:本文为CSDN博主「金门走狗」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_32768455/article/details/112126216
Blender插件初始化范例
目标
- [x] 总结Blender插件初始化范例
总结
插件模板
Blender内部插件实现方式模板功能总结如下:
- 定义了子模块重加载方式
- 定义了批量加载子模块的方式
- 插件注册函数
- 插件注销函数
模块总体结构如下:
# 支持子模块重加载(support reloading sub-modules)if "bpy" in locals():
from importlib import reload
_modules_loaded[:] = [reload(val) for val in _modules_loaded]
del reload
# 定义要加载的模块
_modules = [
"add_mesh_torus",
...
]
import bpy
# 模块加载, __import__()相当于 from __name__ import _modules__import__(name=__name__, fromlist=_modules)
_namespace = globals()
_modules_loaded = [_namespace[name] for name in _modules]del _namespace
def register():
from bpy.utils import register_class
for mod in _modules_loaded:
for cls in mod.classes:
register_class(cls)
def unregister():
from bpy.utils import unregister_class
for mod in reversed(_modules_loaded):
for cls in reversed(mod.classes):
if cls.is_registered:
unregister_class(cls)
范例
Blender Foundation\Blender\2.79\scripts\startup\bl_operators\__init__.py
# ##### BEGIN GPL LICENSE BLOCK ####### This program is free software; you can redistribute it and/or# modify it under the terms of the GNU General Public License# as published by the Free Software Foundation; either version 2# of the License, or (at your option) any later version.## This program is distributed in the hope that it will be useful,# but WITHOUT ANY WARRANTY; without even the implied warranty of# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the# GNU General Public License for more details.## You should have received a copy of the GNU General Public License# along with this program; if not, write to the Free Software Foundation,# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.## ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
# support reloading sub-modulesif "bpy" in locals():
from importlib import reload
_modules_loaded[:] = [reload(val) for val in _modules_loaded]
del reload
_modules = [
"add_mesh_torus",
"anim",
"clip",
"console",
"file",
"image",
"mask",
"mesh",
"node",
"object_align",
"object",
"object_randomize_transform",
"object_quick_effects",
"presets",
"rigidbody",
"screen_play_rendered_anim",
"sequencer",
"uvcalc_follow_active",
"uvcalc_lightmap",
"uvcalc_smart_project",
"vertexpaint_dirt",
"view3d",
"wm",
]
import bpy
if bpy.app.build_options.freestyle:
_modules.append("freestyle")
__import__(name=__name__, fromlist=_modules)
_namespace = globals()
_modules_loaded = [_namespace[name] for name in _modules]del _namespace
def register():
from bpy.utils import register_class
for mod in _modules_loaded:
for cls in mod.classes:
register_class(cls)
def unregister():
from bpy.utils import unregister_class
for mod in reversed(_modules_loaded):
for cls in reversed(mod.classes):
if cls.is_registered:
unregister_class(cls)
Blender插件编写指南
前言
Blender插件是Blender的利器, 用户可以使用各种插件扩充Blender的功能.
Blender Python插件以bpy.props, bpy.types.Operator, bpy.types.Panel, bpy.types.UILayout, (...)为基础, 通过用户自定义包来实现.
插件要点
- 定义操作器
- 定义操作器控制面板(或菜单)
- 注册/注销操作器和面板
简单实例
bl_info = {
"name": "Move X Axis",
"category": "Object",
}
import bpy
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.
def execute(self, context): # execute() is called by blender when running the operator.
# 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()
参考
- Blender插件之操作器(Operator)实战 1
- Blender之UILayout 2
- Blender插件之Panel 3
- Blender之Property 4
- Addon Tutorial 5