본문 바로가기
블렌더

3D블렌더2.83+파이썬+뷰어UI 만들기

첫 게시글 가기 : https://itadventure.tistory.com/319

 

3D 블렌더 2.83 + 파이썬 스크립트와의 만남

3D 블렌더 프로그램에는 파이썬 스크립트 엔진이 내장되어 있는데요. 관련 스크립트를 통해 재미난 것들을 할 수가 있지요. 앞으로 이걸로 어디까지 할 수 있을지 알아 보는 시간을 가져보도록 �

itadventure.tistory.com

3D 블렌더 2.83 버전에서의 3D뷰어 창에는 단축키 N 키를 누르면 나오는 UI 창이 있습니다.
보통 물체의 좌표를 조정하거나 특정 조작등을 할 수도 있고 블렌더 키트와 같은 애드온을 설치하면 새로운 탭이 등장하기도 하지요.

오늘의 파이썬 스크립트는 이 UI 탭을 추가하는 방법을 다뤄보려고 합니다.
지난번 팝업창보다는 조금 사용방법이 복잡하지만 역시 반복하시다 보면 충분히 익히실 수 있을 겁니다 :)


우선 역시 스크립트를 먼저 공개합니다. 지난번보다 약간 내용이 길어졌습니다.
블렌더를 실행하여 스크립트 창에 입력 후 실행해주세요.

import bpy

def print(*datas):
    window=bpy.context.window_manager.windows[0]
    screen = window.screen
    for area in screen.areas:
        if area.type == 'CONSOLE':
            for data in datas:
                bpy.ops.console.scrollback_append(
                    {'window': window, 'screen': screen, 'area': area},
                    text=str(data))

class CRAYSPIN_PROPERTY(bpy.types.PropertyGroup):
    x_repeat: bpy.props.IntProperty(name="X반복", default=0)
    y_repeat: bpy.props.IntProperty(name="Y반복", default=0)

class CRAYSPIN_PT_panel(bpy.types.Panel):
    bl_label = "크레이 스핀 도구창"
    bl_category = "크레이"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"

    def draw(self, context):
        row = self.layout.row()
        row.label(text="선택 오브젝트 : ", icon='OBJECT_DATA')
        box = self.layout.box()
        obj = context.object
        if obj is not None:
          box.label(text=obj.name, icon='KEYFRAME')
          
        row = self.layout.row()
        row.prop(context.scene.crayspin_property, "x_repeat")
        row = self.layout.row()
        row.prop(context.scene.crayspin_property, "y_repeat")
        
        row = self.layout.row()
        row.operator("cray.spinoperator", text="복사")
    
class CRAYSPIN_Operator(bpy.types.Operator):
    bl_idname = 'cray.spinoperator'
    bl_label = 'cray.spinoperator'

    def execute(self, context):        
        print(context.scene.crayspin_property.x_repeat, context.scene.crayspin_property.y_repeat)
        
        selected_objects=bpy.context.selected_objects
        if len(selected_objects) == 0:
            self.report({'ERROR'}, "오브젝트를 선택하세요")
            return {'FINISHED'}

        org_name=selected_objects[0].name;
                
        for x in range(0, context.scene.crayspin_property.x_repeat + 1):
            for y in range(0, context.scene.crayspin_property.y_repeat + 1):
                if x==0 and y==0:        continue
                bpy.data.objects[org_name].select_set(True)
                bpy.ops.object.duplicate_move(
                    OBJECT_OT_duplicate={"mode":'TRANSLATION'},
                    TRANSFORM_OT_translate={"value":(x * 4, y * 4, 0)})
                bpy.ops.object.select_all(action='DESELECT')

        self.report({'INFO'}, "오브젝트가 복사되었습니다.")
        
        return {'FINISHED'}
    
bpy.utils.register_class(CRAYSPIN_PROPERTY)
bpy.types.Scene.crayspin_property = bpy.props.PointerProperty(type=CRAYSPIN_PROPERTY)
bpy.utils.register_class(CRAYSPIN_Operator)
bpy.utils.register_class(CRAYSPIN_PT_panel)

그리고 레이아웃창으로 이동해서 단축키 N 키를 눌러 보세요.
그러면 화면 오른쪽에 물체의 좌표를 지정할 수 있는 창이 등장할 겁니다.

그런데 잘 살펴보시면 탭 아래쪽에 못 보던 탭이 하나 추가된걸 보실 수 있는데요.

이 탭을 한번 눌러 보시면 새로운 UI 가 하나 구성된 걸 보실 수 있습니다.

우선 기본 큐브를 선택하고, X반복을 3, Y반복을 4를 입력하고 나서, 복사 버튼을 눌러보실까요?

그러면, 이런 일이 일어납니다.

지난번 '돌리고 돌리고'편의 UI 버전인 셈입니다. 이제 이런 기능이 있으면 사용자도 한번에 배열 복사가 가능할 겁니다.

https://itadventure.tistory.com/320?category=715917

 

3D 블렌더 2.83 + 파이썬 스크립트 / 돌리고돌리고~ for 반복문

첫 게시글 보기 : https://itadventure.tistory.com/319 3D 블렌더 2.83 + 파이썬 스크립트와의 만남 3D 블렌더 프로그램에는 파이썬 스크립트 엔진이 내장되어 있는데요. 관련 스크립트를 통해 재미난 것들�

itadventure.tistory.com

사실 기능은 이게 전부입니다만,
3D뷰어에서 이 기능을 만들기 위해서는 신경써야 할 부분이 꽤 있습니다.
하나씩 소스를 확인해볼까요?


서두에 등장하는 import bpy 와 print() 함수는 이미 설명드린바 있으니 건너 뛰도록 하겠습니다.

이번 소스에서는 클래스가 무려 3개나 등장합니다.
클래스는 설계도와 같다고 말씀드린바 있었지요?

각각의 클래스는 아래와 같습니다.
- 오퍼레이터 클래스 1개 : 선택된 오브젝트를 가로, 세로로 몇개씩 복사하는 기능
- 프로퍼티 ( 속성 ) 클래스 1개 : 가로, 세로 몇개를 입력할지 UI 요소를 속성으로 정의하는 속성 클래스
- 패널 클래스 : 실제적인 유저 인터페이스

지난 시간에 다뤄본 팝업창 방식은 제작은 가장 간단하긴 하지만 제약이 있습니다.
바로 파일 선택을 할 수 없다는 것이 첫번째이고, 두번째는 OK 버튼을 누르면 무조건 창이 닫힙니다.
그래서 간단한 1회성 조작으로는 유용하나 자주 쓰는 도구로 항상 옆에 있어주는 툴바같은 기능으로는 작동할 수가 없다는 것이지요.

그래서 이번에는 3D뷰어 창 내의 툴바와 같은 패널창을 다루어 보려는 것입니다.
그런데 이 3D뷰어 패널 창은 팝업창과는 다르게 한가지 제약을 갖습니다.
팝업창에서처럼 실시간으로 입력UI를 기술할 수 없다는 것입니다.

어떤 의미냐 하면, 지난 팝업창 방식에서는 오퍼레이터에 실시간으로 x_repeat 라는 정수 입력 속성을 지정하고
draw 함수에서는 x_repeat 항목을 입력받을 수 있는 화면을 구성했다가,
execute 함수에서는 이 x_repeat  항목을 사용하여 특정 기능을 수행합니다.
그런데 이런 방식을 사용할 수가 없습니다. 아마도 단일 오퍼레이터라 순간 동작 방식으로 작동하기 때문에 그런 것이 아닐까 추정됩니다.

class CustomArrayOperator(bpy.types.Operator):
        :
    # 속성 정의
    x_repeat = bpy.props.IntProperty(name="갯수")

        :
    def draw(self, context):
        layout = self.layout
        
        # 행 추가
        row = layout.row()
        # 입력 항목 - 속성 매칭
        row.prop(self, "x_repeat")

하여간, 3D뷰의 인터페이스 방식은 위와 같이는 사용할 수는 없으며 화면에 보여줄 입력항목들에 해당하는 속성그룹을 담을 클래스를 미리 정의해 주어야 합니다. 아래처럼 말이지요.

class CRAYSPIN_PROPERTY(bpy.types.PropertyGroup):
    x_repeat: bpy.props.IntProperty(name="X반복", default=0)
    y_repeat: bpy.props.IntProperty(name="Y반복", default=0)

CRAYSPIN_PROPERTY 는 직접 이름을 지어준 것인데요.
UI 가 여러가지인 경우 각각 다른 속성 그룹이 필요할 겁니다.
어떤 UI는 X, Y값을 입력한다면, 어떤 UI는 회전값과, 몇바퀴를 입력받을 수도 있갰지요,
그러니 UI 종류마다 속성이름을 따로 주어야 합니다. 크레이는 UI 이름과 비슷하게 지어주었습니다.

프로퍼티 클래스는 bpy.types.PropertyGroup 라는 클래스를 상속받아야 합니다.
괄호안의 내용은 부모 클래스를 의미하며,

(bpy.types.PropertyGroup)

그 다음으로 속성을 정의합니다.
x_repeat 와 y_repeat 속성을 각각 정의하는데요.
정수 속성 (IntProperty)으로 정의하고 표시될 이름(name="...") 정의, 그리고 기본값은 0으로 정해줍니다. (default=0)

x_repeat: bpy.props.IntProperty(name="X반복", default=0)
y_repeat: bpy.props.IntProperty(name="Y반복", default=0)

그러면 나중에 화면에 이렇게 표시될 겁니다.

그리고 이 속성 클래스를 블렌더에 등록, 이어서 속성입력값을 담을 오브젝트를 선언합니다.
bpy.types.Scene.crayspin_property 라는 오브젝트를 선언하는데요.
블렌더 파이썬의 특징적인 사용 방법입니다. 앞으로 속성의 저장은 실제 오브젝트인 crayspin_property 에 보관할 것입니다.

 

bpy.utils.register_class(CRAYSPIN_PROPERTY)
bpy.types.Scene.crayspin_property = bpy.props.PointerProperty(type=CRAYSPIN_PROPERTY)

다음으로 UI 화면에 대한 클래스가 등장합니다.
bpy.types.Panel 클래스를 상속받아야 하며 이름은 임의로 CRAYSPIN_PT_panel 로 지어주었지요.

class CRAYSPIN_PT_panel(bpy.types.Panel):
    bl_label = "크레이 스핀 도구창"
    bl_category = "크레이"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"

bl_label은 이 부분에 등장하는 제목입니다.

bl_category 는 오른쪽의 탭을 의미하는데요.

기존 탭의 명칭을 사용할 경우 새로운 탭이 아닌 기존 탭에 내용이 추가됩니다.
아래 화면은 category 를 'Edit'로 하였을 경우 새로운 탭이 아닌 편집 탭에 추가된 모습니다.

참고로 각 탭의 명칭은 환경설정에서 언어 설정을 한글이 아닌 영문으로 하면 번역되기 전 단어를 확인하실 수 있습니다. 이 명칭을 사용하면 해당 탭에 내용이 추가됩니다.

bl_space_type = "VIEW_3D" 는 3차원 뷰에 표시하겠다는 의미이며,
bl_region_type = "UI"인데 대체적으로 해당값을 사용하는 것으로 보입니다.

다음으로 draw 함수가 등장합니다. 이 부분부터가 실제 UI를 그려주는 부분인데요.

    def draw(self, context):

우선 처음에는 '선택 오브젝트' 글자와 아이콘을 뿌려줍니다.
self.layout.row() 가 한 행을 추가하는 부분이고,
row.label 은 글자 내용을 결정해주는 것이지요.

        row = self.layout.row()
        row.label(text="선택 오브젝트 : ", icon='OBJECT_DATA')

그 다음으로는 상자를 하나 그려주고 상자에 현재 선택된 오브젝트의 이름을 표기해 줍니다.
context.object 가 선택된 오브젝트이며,
if obj is not None: 문은 선택된 오브젝트가 없지 않을 경우, 즉 있을 경우라는 의미입니다.

        box = self.layout.box()
        obj = context.object
        if obj is not None:
          box.label(text=obj.name, icon='KEYFRAME')

그 다음으로 복사할 가로 세로 횟수 입력상자를 표시합니다.
앞에서 정의한 속성 프로퍼티를 사용하는데요,
context.scene.crayspin_property 항목이 나왔습니다.
앞에서 정의할 떄는 bpy.types.Scene.crayspin_property 를 사용하나
draw 에서 사용할 떄는 파라미터인 context 변수를 기반으로 사용해야 하므로 이 명칭으로 사용해야 작동됩니다.

        row = self.layout.row()
        row.prop(context.scene.crayspin_property, "x_repeat")
        row = self.layout.row()
        row.prop(context.scene.crayspin_property, "y_repeat")

그리고 오퍼레이터를 작동해줄 버튼을 표시해줍니다.
이 버튼을 누르면 bl_label 값이 "cray.spinoperator"인 오퍼레이터를 실행해주는 것이지요,

        row = self.layout.row()
        row.operator("cray.spinoperator", text="복사")

오퍼레이터는 사실 지난 스크립트와 유사하기 떄문에 분석은 독자님의 몪으로 남기겠습니다 :)
다만 UI를 사용하지 않기 때문에 execute 함수밖에는 존재하지 않지요.
내부적으로 가로값과 세로값의 갯수를 판단하는 부분은
각각 context.scene.crayspin_property.x_repeat,
context.scene.crayspin_property.y_repeat 값을 사용한다는 점에 주목해 주세요..

class CRAYSPIN_Operator(bpy.types.Operator):
    bl_idname = 'cray.spinoperator'
    bl_label = 'cray.spinoperator'

    def execute(self, context):        
        selected_objects=bpy.context.selected_objects
        if len(selected_objects) == 0:
            self.report({'ERROR'}, "오브젝트를 선택하세요")
            return {'FINISHED'}

        org_name=selected_objects[0].name;
                
        for x in range(0, context.scene.crayspin_property.x_repeat + 1):
            for y in range(0, context.scene.crayspin_property.y_repeat + 1):
                if x==0 and y==0:        continue
                bpy.data.objects[org_name].select_set(True)
                bpy.ops.object.duplicate_move(
                    OBJECT_OT_duplicate={"mode":'TRANSLATION'},
                    TRANSFORM_OT_translate={"value":(x * 4, y * 4, 0)})
                bpy.ops.object.select_all(action='DESELECT')

        self.report({'INFO'}, "오브젝트가 복사되었습니다.")
        
        return {'FINISHED'}

마지막으로 아직 등록안된 2개의 클래스 ( 패널, 오퍼레이터 ) 를 등록하고 종료합니다.

bpy.utils.register_class(CRAYSPIN_Operator)
bpy.utils.register_class(CRAYSPIN_PT_panel)

여기까지가 패널의 작동원리에 대한 모든 설명입니다.
그러면 결국 아래처럼 작동하는 것이지요.

오늘도 필요하신 분께 도움이 되셨는지 모르겠습니다 :)
코로나 사태에 건강 유의하시고
하나님의 돌보심이 함께 하시길 소원합니다.


하나님의 말씀을 마음에 담아두면 환난 날에 도움이 됩니다.
바로 예수님에 대한 말씀이지요 :)

우리에게 있는 대제사장은 
우리 연약함을 체휼하지 아니하는 자가 아니요 
모든 일에 우리와 한결같이 시험을 받은 자로되 
죄는 없으시니라

- 히브리서 4장 15절 말씀 -

 

다음 게시글 : https://itadventure.tistory.com/332

 

3D블렌더 2.83+애드온(add-on) 만들기+파이썬

첫 게시글 보기 : https://itadventure.tistory.com/319 3D 블렌더 2.83 + 파이썬 스크립트와의 만남 3D 블렌더 프로그램에는 파이썬 스크립트 엔진이 내장되어 있는데요. 관련 스크립트를 통해 재미난 것들을

itadventure.tistory.com