Основы Python API для Blender
Приветствую всех блендерщиков, не блендерщиков и других, кто решил сюда заглянуть. Сегодня я попробую доступным образом объяснить, как писать простенькие скрипты для блендера и не только
Язык, который мы будем использовать - Python. Он простенький, с необычным синтаксисом лично для меня, человека, который начал и в течении двух лет учил С++. В этой статье я не буду делать урок по питону, она рассчитана на то, что вы хотя бы поверхностно знаете его. Этого можно легко добиться, посмотрев пару уроков на ютубе или прочитав в интернете. Если возникнет какая-то проблема, то пишите в комментарии
Кстати, если заметили ошибку, знаете решение оптимальнее, то не стесняйтесь писать об этом (естественно, адекватно, а без наездов в стиле "автор - ламер-гумманитарий" и т.п.). Я бы не назвал себя прям таким мидл питон программистом, или человеком, который очень глубоко знает API для блендера, но моих знаний хватает, чтобы писать скрипты, которые очень могут облегчить жизнь, или те же интерфейсы. Ну чтож, мы что-то отвлеклись на отступление, пожалуй, начнем!
Подготовка
Мы будем писать код прямо в блендере, так как это будет удобнее. Чтобы использовать модули API вне блендера, требуются определенные махинации, которые в том числе включают компиляцию блендера из исходников. Однако в 3.4 в этой области ожидается улучшение, я пока что не тестировал, да и урок будет в 3.3
Для начала стоит изменить настройки. Переходим в Edit->Preferences->Interface. Ставим галочки у пунктов Developer Extras и Python Tooltips
Первое откроет оператор Edit source при правом клике на определенные элементы интерфейса. Он открывает файл в текст едиторе по пути <Папка_с_блендером_/номер_версии/scripts/startup>, который содержит код, определяющий этот элемент. Почти весь интерфейс блендера написан на питоне, и лежит в папках по пути выше. Так как python - интерпретируемый язык, то текст лежит в .py файлах (однако стоит заметить, что код можно скомпилировать, тогда его не получится отредактировать, но такая возможность не используется в угоду легкой редактируемости). Можно легко залезть в каталоги, и что-то поправить в UI. Но и тут есть подвох: по какой-то причине часть элементов реализована в ядре блендера, то есть написана на С/С++, закомпилирована в бинарник (т.е. в blender.exe или же просто blender для Линукса, исполняемые файлы не носят расширения там), и отредактировать их без перекомпиляции блендера не получится. Если вы наткнетесь на такое, то оператор известит вас, что исходники недоступны
Второй оператор добавляет Python подсказки, когда вы оставляете курсор неподвижным на элементе. Увы, мой скриншотер сбивает фокус курсора, поэтому сплывашка изчезает, и заскринить ее не удалось:(
После этого переходим в такую непримечательную рабочую область в самом конце под названием Scripting. если у вас ее нет, но нажимаете на плюсик в конце, потом оттуда выбираем General->Scripting
Там практически все необходимое для того, чтоб писать скрипты. Ну чтож, пора познакомится с нашими инструментами
Окно по середине - это Text Editor, редактор, в котором будет производится основное действие, и который был упомянут ранее. По сути - это обычный блокнот с подсветкой синтаксиса, у которого есть кнопка запуска скрипта, и всякие опции, типа автоматического запуска скрипта при открытии файла
Второй по значимости редактор - это Python Console. Это встроенная консоль с питоном и определенными преднастройками, которая позволяет быстро запускать функции без написания скрипта, или просматривать содержимое классов/модулей
Третий, не особо часто используемый, но определенно полезный редактор - Info. Просто список операторов, которые подразумевались под выполнением каких-то действий пользователем (нажатие хоткея, кнопки и т.п.). По сути - аналог майской строки внизу, где указывается MEL команда, когда что-то происходит, ни больше, ни меньше
остальные редакторы - это все уже знакомое, вьюпорт, панель свойств, оутлайнер в обычном режиме и режиме отображения структуры текущего blend файла
Мы будем работать с консолью, так как это легче в начале. Для открытия консоли в Windows надо нажать Window->Toggle System Console, для Линукса необходимо запускать блендер через консоль
На этом необходимый сетап выполнен, можно приступать к кодингу!
Использование инструментов
Для начала напишем hello world, чтобы понять, как работать с инструментами
Напишем во внутренней консоли
print("hello world")
И получим ожидаемый ответ
Как уже было ранее сказано, консоль внутри блендера идентична консоли, в которой был запущен Python. Однако в системной консоли вывода от принта не будет, так как он ушел в виртуальную консоль
Теперь напишем код, который будет выводить hello world уже в системную консоль. Создаем новый текстовый файл в текст едиторе, пишем идентичный код, нажимаем на кнопку запуска, и смотрим в консоль
Стоит заметить, что инфо редактор отреагировал на нажатие кнопки запуска, выдав оператор, который скрывается за кнопкой
Помимо исполнения кода, внутренняя консоль может отображать документацию к функциям и члены модуля/класса
Чтобы так сделать, надо написать точку/скобку (в зависимости от того, что из себя представляет сущность), и потом нажать Tab. В консоли появится лаймовый текст с документацией или членами сущности. Tab - это на самом деле функция автодополнения, так что помимо быстрого доступа к документации этот хоткей можно использовать по его прямому назначению
Существует опция Register в пункте Text заголовка. Этот флажок говорит о том, что необходимо запускать скрипт, когда загружается blend файл. Она может очень часто использоваться, а почему - мы поговорим позже
Теперь, когда мы научились пользоваться инструментами, пора уже перейти к самому API
Собственно API
Для того, чтоб как-то взаимодействовать с блендером, нам надо импортировать модуль под названием bpy. Он идет вместе с блендером. Чтобы начать работать с ним, необходимо написать в начале скрипта:
import bpy
Теперь нам доступны все элементы bpy, которых не так уж и много. Их можно посмотреть через Tab в консоли
Мы будем разбирать их в порядке важности/частоты использования
Контекст (bpy.context)
Это один из важнейших классов API. Он содержит в себе различные объекты и коллекции, которые отображают то, что сейчас происходит в блендере. именно через него можно получить активный объект, или все выделенные например. Список того, что можно получить через него - довольно огромен. Мы можете посмотреть сами, используя выше описанный трюк с автодополнением
Практически все элементы контекста - read only, то есть вы не можете их изменять напрямую. Однако это возможно путем изменения данных объектов или других функций API
Часть контекста напрямую зависит от того, где запускается код. В документации расписано, какие элементы подчиняются этому правилу Лишь малая часть контекста независима, и везде будет одинакова
В пример приведу коллекцию, относящуюся к анимации - editable_fcurves. Она содержит в себе редактируемые анимационные кривые. Если вы запустите такой код в консоли (кстати, вот и та самая изначальная настройка консоли - заранее импортирована часть модулей + для сокращения времени печатания есть парочку переменных, например С -это bpy.context в консоли)
print(C.editable_fcurves)
То вы неожиданно обнаружите, что коллекция имеет тип None, т.е. там ничего нету, даже если в редакторе графов будет хоть одна кривая. Как же так?! - спросите вы. Это и есть пример зависимости контекста от того, где запускается код. Но, если вы обернете функцию в оператор (о чем пониже), и запустите в граф едиторе через F3, то вместо None будет напечатана коллекция, все будет нормально
Операторы (bpy.ops)
Я очень часто упоминаю их, так что пора бы рассказать, да и тем более это очень важная часть программной экосистемы блендера
Оператор - это по сути функция, которая что-то делает, но оформлена в виде класса, из-за того что для оператора приходится хранить кучу дополнительных данных
bpy.types.Operator
Каждый оператор - это класс, который наследуется от класса выше. Код обязательно должен быть обернут в класс оператора, если надо, чтобы он запускался из интерфейса (будь то кнопка, или пункт в меню), а не только вызывался другим кодом
Пример того, как должен быть реализован оператор, есть в Templates-Python->Operator Simple
Данный оператор может быть сложен для первого раза, поэтому упрощу код, убрав все лишнее
Для начала импортируем модуль bpy, все как обычно. Потом создаем класс, и в качестве родителя указываем bpy.types.Operator
Текст в тройных кавычках - то, что будет отображаться во встроенной консоли в качестве документации
bl_idname - это встроенное поле. Оно должно быть обязательно указано. Это по сути ID оператора, то, как к нему будет обращаться остальная система (включая вас и остальных скриптеров/прогеров). Оно включает имя подмодуля в bpy.ops и само имя оператора. Если имя подмодуля уже существует, то оператор станет членом этого модуля, иначе - создастся новый подмодуль
bl_label - это имя оператора уже в UI, то есть то, что будет отображаться в том же меню
метод poll - это способ ограничить применение оператора, если существует риск появления контекста, где оператор завершится с ошибкой, либо бесполезен по самой логике. True - оператор будет доступен, False - нет. В нашем примере оператор будет запускаться, только если есть активный объект. Можно создать любое условие, например запуск в определенном режиме объекта, или если выделены определенные элементы, все. что вам позволяет делать контекст
execute - это собственно метод, где находится то, что делает оператор. Метод обязательно должен вернуть словарь (т.е. фигурные скобки, "{ }") со строкой, которая означает статус завершения работы оператора. Обычно используют два значения - 'FINISHED', что значит, что все хорошо, и 'CANCELLED' - значит работа оператора была прекращена по какой-то причине
def execute(self, context): .... return {'FINISHED'}
Естественно, это не все, что можно сказать по операторам, это очень обширная тема. Например, существуют метод invoke, который позволяет проинициализировать оператор перед запуском, модальные операторы (их пример - это GRS операторы. Подробнее о модальных операторах можно почитать тут) и многое много другое. Все это рассказывать очень долго, но есть то, что хотелось бы мне добавить
У операторов существует огромное количество разных свойств, помимо bl_idname и bl_label. Они все помогают настроить поведение оператора, но есть одно очень важное, которое почему-то не было вынесено в шаблон оператора, и без которого оператор даже не сможет отменяться - это bl_options. Оно принимает словарь из ключевых слов, которые указывают определенные опции оператору. Например 'UNDO' - оператор может отменяться, или 'REGISTER' - оператор будет иметь панель повтора снизу. В основном это самые популярные используемые опции, их стоит запомнить
bl_options = {'UNDO', 'REGISTER'}
Чтобы передавать оператору параметры (если он используется в качестве функции) или поставить какие-то определенные (если он например оформлен в виде кнопки, и значения уже определены), то необходимо использовать свойства из bpy.props. Об этом будет ниже
Регистрация классов
Однако после написания класса блендер не будет видеть оператор. Для того, чтобы это исправить, необходимо зарегистрировать его. В начале своего запуска блендер вообще ничего не знает ни о операторах (кроме тех, что реализованы в ядре), ни о различных панельках, менюшках, которые реализованы на питоне. Каждый такой элемент должен быть зарегистрирован при помощи функции bpy.utils.register_class(). Практически каждый оператор, каждая менюшка, которая реализована в <номер_версии/scripts/startup>, так или иначе проходит процедуру регистрации через register_class
В нашем примере есть код, который регистрирует класс. Он расположен в функции register. далее идет код, который значит, что при запуске скрипта будет выполнена функция register(). Также есть функционал для отмены регистрации класса, это соответственно bpy.utils.unregister_class(). Она нужна например аддонам, чтобы когда ты выключал аддон, от них ничего не оставалось
Стоит заметить, что порядок регистрации имеет очень важную роль. Если сначала зарегистрировать панельку, которая использует оператор, а только потом сам оператор, то будет ошибка
Чтобы наконец-то увидеть результат нашего оператора, нажимаем кнопку запуска, и вуаля - в системной консоли отобразится информация о классе объекта, а через F3 появится возможность запускать оператор. Галочка Register у текста нужна именно за этим - чтобы каждый раз самому не запускать скрипт, тем самым запуская регистрацию
UI (bpy.types.UILayout и другие)
Думаю, многие этого ждали
В блендере очень много UI элементов - панельки, менюшки, кнопки, и т.п. и т.п. С каждым можно так или иначе работать через API
Начнем пожалуй с панелек. думаю, каждый бы хотел что-то прикрутить справа во вьюпорте. Для этого нам надо Создать класс, производный от bpy.types.Panel. Я возьму пример от моего проекта
У класса панели имя желательно должно строится образом - имя_пространства_PT_свой_текст
bl_space_type - пространство или редактор, в котором будет отображаться панель
bl_region_type - тип региона, в котором будет отображаться панель. У окна есть несколько регионов, мы указываем 'UI'
bl_category - категория, в которой будет панелька. Если указываем существующее, то панель будет там же вместе с остальными панелями, которые указали такой регион, если такой категории не будет - то она создастся. Ну и bl_label - это само название панели
У панели также есть метод poll, чтобы избегать запуска в неправильном контексте, аналогично оператору
Вместо метода execute у панели и другого UI есть метод draw - это метод, отвечающий за рисование внутри сущности. В блендере UI описывается функциональным подходом - то есть хотим поместить оператор - вызываем функцию, которая создаст оператор, потом настраиваем элемент, передавая функции соответствующие аргументы
Модернизируем наш код, чтобы теперь он отображал панель в 3д вьюпорте
Пока что она пустая, надо бы ее наполнить элементами
Для того, чтобы начать работать с UI - берем у self поле layout
layout = self.layout
У лайяута ОЧЕНЬ много методов и всяких полей, всех их можно прочитать тут. Я же перечислю часто используемые
row = layout.row()
создает ряд с элементами, которые идут по горизонтали
column = layout.column()
Это собрат row(), но уже по вертикали. Это колонка элементов
Значение, которое возвращает метод, необходимо принять в переменную, иначе не получится получить доступ к ним
Хочу заметить, что у каждого вышеописанного метода возвращаемое значение имеет тип UILayout, т.е. все, что было и будет сказано для лайяута, применимо ко всем перечисленным элементам: вам ничто не мешает сделать ряд, а в нем столбцы
Очень важные методы - это layout.prop и layout.operator
layout.operator('module_name.op_name')
Дает доступ к оператору из UI. Может быть и кнопкой с текстом, и с иконкой
Используем полученные знания, чтобы наполнить нашу панельку
Теперь у нас появилась кнопка с нашим оператором. Если при вызове row.operator() задать параметр text, то можно задавать кастомный текст
Для того, чтобы оставить только иконку, необходимо выставить text как '', а также указать ID иконки. Айдишник можно взять пи помощи встроенного аддона Icon Viewer, что мы и сделаем
Если использовать несколько row.operator() подряд, то можно заметить, чем row отличается от column
Аналогично можно сделать и с column
Теперь следующий наиболее важный метод
layout.prop(object, 'path_to_data')
Позволяет вынести уже свойство объекта в UI. Сначала пишется то, с чего берется свойство, потом путь до свойства в виде строки относительно объекта. Чтобы узнать путь, можно просто навестись на свойство в UI. Как я понял, блендер не воспринимает вложенные объекты. Т.е. если у класса Scene есть поле под класс SceneEEVEE, который хранит настройки eevee, то для того, чтобы допустим вынести количество семплов, надо задавать объектом scene.eevee, а путь - просто 'taa_samples'. В UI в зависимости от настроек свойства будет отображаться соответствующий виджет
И последнее, что хотелось бы сказать по UI - это меню. Они делаются как и все UI классы, но чтобы прицепить это меню к существующим, необходимо использовать коллбек - функцию, которая передается в параметр другой функции, которая потом где-то вызывает первую
В нашем случае мы пишем сначала класс менюшки
class VIEW3D_MT_test(bpy.types.Menu): bl_label = "test" def draw(self, context): layout = self.layout layout.operator(...)
А потом коллбек функцию, подобную draw(self, context). Единственное условие - параметры должны совпадать, на этом - все. Теперь берем и пишем код, используя метод layout.menu(). Думаю, вы догадались, что оно сделает
def draw_func(self, context): layout = self.layout layout.menu(menu_name) .....
После написания функции ее надо прицепить туда, куда нам надо. Вот тут и пригодится оператор Edit Source. Выясняете, в каком месте рисуется меню, и тем самым находите класс, который представляет меню. Например, для менюшек в заголовке вьюпорта это будет
bpy.types.VIEW3D_MT_editor_menus
Далее необходимо вызвать у класса метод append()
bpy.types.VIEW3D_MT_editor_menus.append(draw_func)
Кстати, не забываем про регистрацию. И меню, и панели надо сначала регистрировать, а только потом использовать
Свойства (bpy.props)
Еще одна очень важная тема, чтобы полноценно писать то же UI для ригов
bpy.props - это по сути обертка для более низкоуровневой и сложной системы - RNA
Свойства есть так и у объектов, так и у операторов
Есть немало разных свойств, но я перечислю самые часто используемые: IntProperty (целое число), FloatProperty (число с плавающей запятой), BoolProperty (булево значение)
Существует также StringProperty, EnumProperty и куча других. Обо всех свойствах можно прочитать тута
Синтаксис для операторов и обычных обычных объектов различается
Для операторов
var : Property(....)
Для объектов
bpy.types.Type.new_prop = bpy.props.Property(....)
Для операторов свойства можно использовать через self
...... prop : IntProperty(name='rbgrb') .... def execute(self, context): .... a = self.prop
Их можно настраивать через layout.operator()
layout.operator('object.my_op').prop = 1
В нашем случае оператор запустится со значением prop в виде 1
Можно прокинуть свойство в оператор, повесив его на объект
bpy.types.Scene.prop = bpy.props.StringProperty(name='sas') .... def execute(self, context): ... a = context.scene.prop
В скобках при создании свойства указываются его параметры. Именно так например можно сделать слайдер
У свойств огромная куча комбинаций использования. Какой метод будет лучше - зависит от задачи
Остальные Модули
- bpy.data - все данные текущего blend файла
- bpy.app - модуль позволяет получить информацию о самом блендере, дает доступ к обработчикам, таймерам, и прочим штукам для чего-то сложнее панельки или оператора
- bpy.types - содержит все когда-либо зарегистрированные классы
- bpy.utils - содержит различные вспомогательные функции, вроде register_class
- bpy.path - необходим при работе с путями
- bpy.msgbus - модуль, который нужен, когда необходимо отслеживать изменения данных
Помимо bpy есть и другие модули. Их необходимо импортировать отдельно
- mathutils - математика: вектора, матрицы, все, что пожелаете
- aud - модуль работы со звуком
- bgl - устаревший модуль для низкоуровневого взаимодействия с GPU. Не надо его использовать
- bl_math - если вам было мало mathutils, то вот вам еще)
- blf - для низкоуровневой работы со шрифтами
- bmesh - модуль для работы с мешем. Перед применением лучше прочитать мануал
- bpy_extras - различные дополнительные функции
- freestyle - модуль для работы с фристайл рендером
- gpu - как bgl, но лучше, и не будет выпилен
- gpu_extras - небольшой модуль со всякими доп штуками для GPU
- idprop.types - крайне мало документации. Что-то связанное с данными
- imbuf - модуль для работы с изображениями
Послесловие
Естественно, я не могу описать все на свете. Вы сами видели, сколько одних только модулей, и насколько может рознится сфера их применения. Для того, чтобы нормально писать скрипты, достаточно bpy. А дальше изучаете в зависимости от сферы, в которой работают ваши скрипты
Я оставлю ссылки на различные материалы, которые могут вам помочь
- официальная документация
- Канал в блендер чате, где могут помочь
- На форуме разработчиков тоже могут подсобить
- Плейлист Андрея Соколова, откуда можно подчеркнуть информацию, которую я мог не рассказать, а также небольшой курс по питону
Спасибо всем за прочтение!