菜鸟学bpy(三)添加面板(Panel)

在进入正文之前,先唠叨一下我最近学习bpy的一些心得:

  1. Python基础知识 对python基础知识有所了解,就跟学习英语一样,一些常用的语法要知道。
  2. 学会看别人的代码 如果遇到不会的可以去参考别人写的bpy代码,文本编辑器里就有很多现成的模板cookbookBlender 3D: Noob to ProAPI文档 里都有很多教程和例子。
    Template.png
  3. 学会查API文档 遇到自己看不懂的代码或者不知道怎么写,可以根据关键字搜索文档,查找相关用法。
    API_Search.png
  4. 学会排错 我们在自己写脚本的时候经常会出现各种各样的错误,这些 错误提示信息 通常出现会在系统控制台信息窗口 。根据这些错误提示信息,我们可以逐步找到并排除代码中的错误。
    Info_and_System_Console.png

好了,下面进入正题。


1. 添加面板(Panel)

参考API文档中的例子,我们先定义一个最简单的面板 :

import bpy      #这个必须有

class AddPyramidPanel(bpy.types.Panel):
    bl_idname = 'OBJECT_PT_Pyramid'      #ID名称
    bl_space_type = 'VIEW_3D'            #面板所在窗口类型
    bl_region_type = 'TOOLS'             #面板所在区域
    bl_category = 'Create'               #面板所属分类
    bl_context = 'objectmode'            #面板作用情境
    bl_label = 'Add Pyramid'             #标签,也就是面板显示的标题
    
    #定义面板元素
    def draw(self, context):      
         pass       #pass表示什么都没有,空面板

#注册面板    
bpy.utils.register_class(AddPyramidPanel)

将上面的代码输入到文本编辑器,并运行脚本(Run Script) ,就可以在3D视图 窗口的工具栏下的 Create 分类找到新建的面板了。

Simple_Panel.png

写完这个面板之后,我试着把一些面板属性的代码 注释(#) 掉,发现也可以正常运行,在窗口中生成面板。
就像把bl_category = 'Create'这一行注释掉,不定义面板分类,面板就自动跑到 Misc (杂项) 分类下去了。

import bpy      #这个必须有

class AddPyramidPanel(bpy.types.Panel):
#    bl_idname = 'OBJECT_PT_Pyramid'      #ID名称
    bl_space_type = 'VIEW_3D'            #面板所在窗口类型
    bl_region_type = 'TOOLS'             #面板所在区域
#    bl_category = 'Create'               #面板所属分类
#    bl_context = 'objectmode'            #面板作用情境
    bl_label = 'Add Pyramid'             #标签,也就是面板显示的标题
    
#定义面板元素
def draw(self, context):      
    pass       #pass表示什么都没有,空面板

#注册面板    
bpy.utils.register_class(AddPyramidPanel)
Another_Simple_Panel.png

2. 在面板上添加按钮

我们在draw方法中添加 标签按钮 如下:

import bpy      #这个必须有

class AddPyramidPanel(bpy.types.Panel):
    bl_idname = 'OBJECT_PT_Pyramid'      #ID名称
    bl_space_type = 'VIEW_3D'            #面板所在窗口类型
    bl_region_type = 'TOOLS'             #面板所在区域
    bl_category = 'Create'               #面板所属分类
    bl_context = 'objectmode'            #面板作用情境
    bl_label = 'Add Pyramid'             #标签,也就是面板显示的标题
    
    #定义面板元素
    def draw(self, context):      
        layout = self.layout
        layout.label(text = 'Height:')      #添加标签
        layout.operator(AddPyramid.bl_idname)    #添加按钮

#注册面板    
bpy.utils.register_class(AddPyramidPanel)

运行之后我们发现 信息窗口 报错了:

Info_Error.png

根据错误信息的指示,我们知道问题出在第16行,因为我们的AddPyramid 这个operator 并没有定义。

注:关于layout的用法可以参考文档中的 UILayout(bpy_struct)User Interface Layout,界面元素比较多,你可以慢慢研究。

所以我们把上面的代码复制到上次写的添加四棱锥operator 代码里,并把无关代码行注释(#)掉:

import bpy    #加载bpy,这个是必须有的

#定义添加网格的方法
def Add_Pyramid(height = 2):
    h = height      #四棱锥高度

    #顶点
    verts = [(1,1,0),
             (-1,1,0),
             (-1,-1,0),
             (1,-1,0),
             (0,0,h)]

    #边
    edges = [(0,1),
             (1,2),
             (2,3),
             (3,0),
             (0,4),
             (1,4),
             (2,4),
             (3,4)]

    #面
    faces = [(0,1,4),
             (1,2,4),
             (2,3,4),
             (3,0,4),
             (0,1,2,3)]

    mesh = bpy.data.meshes.new('Pyramid_Mesh') #新建网格
    mesh.from_pydata(verts, edges, faces)      #载入网格数据
    mesh.update()                              #更新网格数据

    pyramid=bpy.data.objects.new('Pyramid', mesh) #新建物体“Pyramid”,并使用“mesh”网格数据
    scene=bpy.context.scene
    scene.objects.link(pyramid) #将物体链接至场景


#添加一个Operator类AddPyramid
class AddPyramid(bpy.types.Operator):
    bl_idname = 'mesh.pyramid_add'    #定义ID名称
    bl_label= 'Pyramid'               #定义显示的标签名
    bl_options  = {'REGISTER', 'UNDO'}
    

    def execute(self, context):
        Add_Pyramid()           #调用Add_Pyramid()方法
        return {'FINISHED'}     #执行结束后返回值
    
#定义面板
class AddPyramidPanel(bpy.types.Panel):
    bl_idname = 'OBJECT_PT_Pyramid'      #ID名称
    bl_space_type = 'VIEW_3D'            #面板所在窗口类型
    bl_region_type = 'TOOLS'             #面板所在区域
    bl_category = 'Create'               #面板所属分类
    bl_context = 'objectmode'            #面板作用情境
    bl_label = 'Add Pyramid'             #标签,也就是面板显示的标题
    
    #定义面板元素
    def draw(self, context):      
        layout = self.layout
        layout.label(text = 'Height:')      #添加标签
        layout.operator(AddPyramid.bl_idname)    #添加按钮

#定义添加菜单方法
#def menu_func(self, context):
#    self.layout.operator(AddPyramid.bl_idname, icon = 'MESH_CONE')


#定义注册类方法    
def register():
   bpy.utils.register_class(AddPyramid) 
   #注册面板    
   bpy.utils.register_class(AddPyramidPanel)
#   bpy.types.INFO_MT_mesh_add.append(menu_func)   #添加菜单


#定义取消注册类方法       
def unregister():
   bpy.utils.unregister_class(AddPyramid)    
   #取消注册面板 
   bpy.utils.unregister_class(AddPyramidPanel)
#   bpy.types.INFO_MT_mesh_add.remove(menu_func) #移除菜单

#直接执行py文件时,注册Operator   
if __name__ == '__main__':
    register()

再次点击 运行脚本(Run Script),在工具栏 创建(Create) 分类下找到 Pyramid 按钮,单击运行就得到下面的结果了,我们成功了。

Button_Added.png

3. 添加属性

还记得之前定义四棱锥 operator 里定义了一个 height(高度) 参数吗?
能不能把这个参数变成添加 四棱锥 时的可调节参数呢?

首先我们就得用到另一个bpy的模块——Property,还可以参考这里。这玩意就是我界面上那些输入框和各种参数调整用到的东西。

这里我们要添加的 height 应该是属于 FloatProperty(浮点数,也就是小数) 的,所以我们要添加的代码就有:

from bpy.props import FloatProperty    #加载FloatProperty

operator的代码也得做一些添加和修改了:

class AddPyramid(bpy.types.Operator):
    bl_idname = 'mesh.pyramid_add'    #定义ID名称
    bl_label= 'Pyramid'               #定义显示的标签名
    bl_options  = {'REGISTER', 'UNDO'}
    
    height = FloatProperty(
             name = 'Height',
             description = 'Pyramid Height',
             min = 0.0, 
             default = 2.0)

    def execute(self, context):
        Add_Pyramid(self.height)           #调用Add_Pyramid()方法
        return {'FINISHED'}     #执行结束后返回值

完整代码如下:

import bpy    #加载bpy,这个是必须有的
from bpy.props import FloatProperty    #加载FloatProperty


#定义添加网格的方法
def Add_Pyramid(height = 2):
    h = height      #四棱锥高度

    #顶点
    verts = [(1,1,0),
             (-1,1,0),
             (-1,-1,0),
             (1,-1,0),
             (0,0,h)]

    #边
    edges = [(0,1),
             (1,2),
             (2,3),
             (3,0),
             (0,4),
             (1,4),
             (2,4),
             (3,4)]

    #面
    faces = [(0,1,4),
             (1,2,4),
             (2,3,4),
             (3,0,4),
             (0,1,2,3)]

    mesh = bpy.data.meshes.new('Pyramid_Mesh') #新建网格
    mesh.from_pydata(verts, edges, faces)      #载入网格数据
    mesh.update()                              #更新网格数据

    pyramid=bpy.data.objects.new('Pyramid', mesh) #新建物体“Pyramid”,并使用“mesh”网格数据
    scene=bpy.context.scene
    scene.objects.link(pyramid) #将物体链接至场景


#添加一个Operator类AddPyramid
class AddPyramid(bpy.types.Operator):
    bl_idname = 'mesh.pyramid_add'    #定义ID名称
    bl_label= 'Pyramid'               #定义显示的标签名
    bl_options  = {'REGISTER', 'UNDO'}
    
    height = FloatProperty(
             name = 'Height',
             description = 'Pyramid Height',
             min = 0.0, 
             default = 2.0)

    def execute(self, context):
        Add_Pyramid(self.height)           #调用Add_Pyramid()方法
        return {'FINISHED'}     #执行结束后返回值
    
class AddPyramidPanel(bpy.types.Panel):
    bl_idname = 'OBJECT_PT_Pyramid'      #ID名称
    bl_space_type = 'VIEW_3D'            #面板所在窗口类型
    bl_region_type = 'TOOLS'             #面板所在区域
    bl_category = 'Create'               #面板所属分类
    bl_context = 'objectmode'            #面板作用情境
    bl_label = 'Add Pyramid'             #标签,也就是面板显示的标题
    
    #定义面板元素
    def draw(self, context):      
        layout = self.layout
        layout.label(text = 'Height:')      #添加标签
        layout.operator(AddPyramid.bl_idname)    #添加按钮

#定义添加菜单方法
#def menu_func(self, context):
#    self.layout.operator(AddPyramid.bl_idname, icon = 'MESH_CONE')


#定义注册类方法    
def register():
   bpy.utils.register_class(AddPyramid) 
   #注册面板    
   bpy.utils.register_class(AddPyramidPanel)
#   bpy.types.INFO_MT_mesh_add.append(menu_func)   #添加菜单


#定义取消注册类方法       
def unregister():
   bpy.utils.unregister_class(AddPyramid)     
   bpy.utils.unregister_class(AddPyramidPanel)
#   bpy.types.INFO_MT_mesh_add.remove(menu_func) #移除菜单

#直接执行py文件时,注册Operator   
if __name__ == '__main__':
    register()

运行后添加四棱锥效果如下:


Property_Adjust.gif

4. 添加变换调节

正常我们添加网格物体,除了一些基本的属性调节之外,还应该有对齐到屏幕位移旋转的变换调节。

Transformation_Adjustment.png

那我们要怎么在自己的脚本里实现这一点呢?

首先,对齐到屏幕 要用到 BoolProperty,也就是布尔值;
位移旋转 则要用到 FloatVectorProperty,也就是向量。

然后就是前面我们新建网格物体用的是bpy.data.objects.new() 方法,这里不能用了,我们得用 object_utils.object_data_add()方法。区别在于后者可以使用operator作为参数,并使用自身的位移和旋转作为物体的变换参数。

objects.new.png
object_data_add.png

完整代码如下:

import bpy    #加载bpy,这个是必须有的
from bpy.props import FloatProperty, FloatVectorProperty, BoolProperty


#定义添加网格的方法
def Add_Pyramid(self, context, height = 2):
    h = height      #四棱锥高度

    #顶点
    verts = [(1,1,0),
             (-1,1,0),
             (-1,-1,0),
             (1,-1,0),
             (0,0,h)]

    #边
    edges = [(0,1),
             (1,2),
             (2,3),
             (3,0),
             (0,4),
             (1,4),
             (2,4),
             (3,4)]

    #面
    faces = [(0,1,4),
             (1,2,4),
             (2,3,4),
             (3,0,4),
             (0,1,2,3)]

    mesh = bpy.data.meshes.new('Pyramid_Mesh') #新建网格
    mesh.from_pydata(verts, edges, faces)      #载入网格数据
    mesh.update()                              #更新网格数据

    from bpy_extras import object_utils
    object_utils.object_data_add(context, mesh, operator=self)    
#    pyramid=bpy.data.objects.new('Pyramid', mesh) #新建物体“Pyramid”,并使用“mesh”网格数据
#    scene=bpy.context.scene
#    scene.objects.link(pyramid) #将物体链接至场景


#添加一个Operator类AddPyramid
class AddPyramid(bpy.types.Operator):
    bl_idname = 'mesh.primitive_pyramid_add'    #定义ID名称
    bl_label= 'Pyramid'               #定义显示的标签名
    bl_options  = {'REGISTER', 'UNDO'}
    
    view_align = BoolProperty(
            name="Align to View",
            default=False,
            )    
        
    height = FloatProperty(
             name = 'Height',
             description = 'Pyramid Height',
             min = 0.0, 
             default = 2.0)
    
    location = FloatVectorProperty(
             name = 'Location',
             subtype = 'TRANSLATION')

    rotation = FloatVectorProperty(
            name="Rotation",
            subtype='EULER',
            )   

    def execute(self, context):
        Add_Pyramid(self, context, self.height)           #调用Add_Pyramid()方法
        return {'FINISHED'}     #执行结束后返回值
    
class AddPyramidPanel(bpy.types.Panel):
    bl_idname = 'OBJECT_PT_Pyramid'   
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'TOOLS'
    bl_category = 'Create'
    bl_context = 'objectmode'
    bl_label = 'Add Pyramid'

    def draw(self, context):      
         layout = self.layout
         
         layout.label(text = 'Height:')
         layout.operator(AddPyramid.bl_idname)

#定义添加菜单方法
#def menu_func(self, context):
#    self.layout.operator(AddPyramid.bl_idname, icon = 'MESH_CONE')


#定义注册类方法    
def register():
   bpy.utils.register_class(AddPyramid) 
   #注册面板    
   bpy.utils.register_class(AddPyramidPanel)
#   bpy.types.INFO_MT_mesh_add.append(menu_func)   #添加菜单


#定义取消注册类方法       
def unregister():
   bpy.utils.unregister_class(AddPyramid)
   bpy.utils.register_class(AddPyramidPanel)       
#   bpy.types.INFO_MT_mesh_add.remove(menu_func) #移除菜单

#直接执行py文件时,注册Operator   
if __name__ == '__main__':
    register()

运行后添加四棱锥效果如下:


Property_Adjust_1.gif

这基本就是我想要的结果了。


好了,终于说完了。
前面三个部分的内容都是非常基础的,我也尽量保证连贯。后面的东西我也没有什么规划,很可能就是想到哪就写到哪了。
最后还想说的就是,我写这个的目的是分享一些我自己学bpy的经验,同时也希望能够帮到大家,让更多的人加入到学习bpy的行列中来。所以,我也希望大家看完之后能够把自己的学习经验分享出来,大家一起进步。
我新建了一个菜鸟学bpy的专题,这个是接受投稿的。大家可以在简书上写下自己的收获,并投稿这个专题。
当然,我还希望社区那些会bpy的大神也能来写几篇教程,指点一下大家。-

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

推荐阅读更多精彩内容