Для того, чтобы создать несколько одинаковых панелей UI с одним и тем же набором полей и кнопок в разных окнах в Blender, проще всего скопировать код панели, заменив в нем параметры, определяющие, в каком именно окне панель будет показана.
Основной недостаток такого метода заключается в избыточном дублировании кода, что может создать сложности в дальнейшем – при редактировании панелей, изменения необходимо вносить в каждую копию кода, вместо того, чтобы поменять его разом для всех панелей.
Логичным решением этой проблемы видится использование наследования – мы пишем класс для одной панели, а затем для всех остальных панелей просто наследуем код от этого класса.
Однако API Blender не позволяет сделать это напрямую.
Определим класс для простейшей панели с одной кнопкой, вызывающей стандартный оператор добавления в сцену дефолтного куба:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import bpy from bpy.types import Panel from bpy.utils import register_class class TEST_PT_panel_viewport(Panel): bl_idname = 'TEST_PT_panel_viewport' bl_space_type = 'VIEW_3D' bl_label = 'SAME PANEL' bl_region_type = 'UI' bl_category = 'SAME PANEL' def draw(self, context): self.layout.operator( operator='mesh.primitive_cube_add', icon='CUBE' ) def register(): register_class(TEST_PT_panel_viewport) |
С этим нет никаких проблем, мы определили класс панели, зарегистрировали его для отображения в окне 3D вьюпорта.
Если мы теперь попытаемся наследовать от него класс для новой панели в окне, например, Shader Editor:
1 2 3 4 5 6 |
class TEST_PT_panel_shader_editor(TEST_PT_panel_viewport): bl_idname = 'TEST_PT_panel_shader_editor' bl_space_type = 'NODE_EDITOR' def register(): register_class(TEST_PT_panel_shader_editor) |
Blender выдаст ошибку:
WARN (bpy.rna): bpy_class_call: unable to get Python class for RNA struct ‘TEST_PT_panel_viewport’
и не сможет корректно отобразить обе панели в нужных окнах.
Кажется, что наследование не решает проблему дублирования кода, однако, мы все же можем ее решить, подойдя к определению классов панелей немного по другому.
Для начала определим общий класс, не наследуя его ни от какого другого класса. Определим в нем точно такие же атрибуты и точно такую же функцию “draw”, как и в классах панелей UI:
1 2 3 4 5 6 7 8 9 10 |
class TEST_PT_panel_common: bl_label = 'SAME PANEL' bl_region_type = 'UI' bl_category = 'SAME PANEL' def draw(self, context): self.layout.operator( operator='mesh.primitive_cube_add', icon='CUBE' ) |
Это будет базовый класс, в функции “draw” которого мы опишем всю структуру панели UI, которая нам нужна. Создание всех необходимых на панели полей и кнопок мы поместим в эту функцию.
После этого определим два класса, один для панели в 3D вьюпорте, а второй – для панели в окне Shader Editor и наследуем их не только от системного класса bpy.types.Panel, но и от созданного нами базового класса “TEST_PT_panel_common” с описанием панели.
1 2 3 4 5 6 7 |
class TEST_PT_panel_viewport(Panel, TEST_PT_panel_common): bl_idname = 'TEST_PT_panel_viewport' bl_space_type = 'VIEW_3D' class TEST_PT_panel_shader_editor(Panel, TEST_PT_panel_common): bl_idname = 'TEST_PT_panel_shader_editor' bl_space_type = 'NODE_EDITOR' |
В этих производных классах мы переопределили параметры, отвечающие за окно для отображения панели так, чтобы каждая из них отображалась в нужном окне.
Регистрировать в API Blender необходимо только эти два производных класса. Базовый класс регистрировать не нужно.
1 2 3 |
def register(): register_class(TEST_PT_panel_viewport) register_class(TEST_PT_panel_shader_editor) |
При таком подходе Blender отображает обе панели в нужных нам окнах без ошибок.
Если же нам нужно что-то изменить во внешнем виде обоих панелей, мы будем редактировать только код в функции “draw” в одном месте – в базовом классе.
Финальный код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
import bpy from bpy.types import Panel from bpy.utils import register_class, unregister_class class TEST_PT_panel_common: bl_label = 'SAME PANEL' bl_region_type = 'UI' bl_category = 'SAME PANEL' def draw(self, context): self.layout.operator( operator='mesh.primitive_cube_add', icon='CUBE' ) class TEST_PT_panel_viewport(Panel, TEST_PT_panel_common): bl_idname = 'TEST_PT_panel_viewport' bl_space_type = 'VIEW_3D' class TEST_PT_panel_shader_editor(Panel, TEST_PT_panel_common): bl_idname = 'TEST_PT_panel_shader_editor' bl_space_type = 'NODE_EDITOR' def register(): register_class(TEST_PT_panel_viewport) register_class(TEST_PT_panel_shader_editor) register() |