Руководство по дополнению (Add-on Tutorial)

Целевая аудитория

Это руководство создано, чтобы помочь техническим художникам и разработчикам научиться расширять Blender. От тех, кто работает с этим руководством, ожидается понимание основ Python.

Предварительные условия

Прежде чем пройти обучение, вам следует…

  • Хорошо знать основы работы в Blender.

  • Знать, как запустить скрипт в текстовом редакторе Blender.

  • Иметь представление о примитивных типах Python (целое число, логическое значение, строка, список, кортеж, словарь и набор ((integer, Boolean, string, list, tuple, dictionary, and set)).

  • Знать концепцию модулей Python.

  • Иметь базовое представление о классах (объектной ориентации) в Python.

Рекомендуем прочитать перед началом работы с этим руководством.

Чтобы наилучшим образом устранить любые сообщения об ошибках, которые Python печатает во время написания скриптов, вы запускаете Blender из терминала. См. Использование терминала.

Совет

You can enable Developer Extras in the preferences to enable features that make developing add-ons easier.

Что такое «Аддон»?

Аддон (дополнение) – это просто модуль Python с некоторыми дополнительными требованиями, поэтому Blender может отображать его в списке с полезной информацией.

В качестве примера приведём простейшее дополнение:

bl_info = {
    "name": "My Test Add-on",
    "blender": (2, 80, 0),
    "category": "Object",
}
def register():
    print("Hello World")
def unregister():
    print("Goodbye World")
bl_info

– это словарь, содержащий метаданные аддона, такие как название, версия и автор, которые будут отображаться в списке аддонов в Настройках. Он также определяет минимальную версию Blender, необходимую для запуска скрипта; более старые версии не будут отображать в списке этот аддон.

register

– это функция, которая запускается только при включении аддона. Это означает, что модуль можно загрузить без активации аддона.

unregister

– это функция для выгрузки (unload) всего, что установлено с помощью register, она вызывается, когда аддон отключен.

Обратите внимание, что этот аддон не делает ничего, связанного с Blender (например, модуль blender_api:bpy не импортируется).

Это надуманный пример аддона, который служит для иллюстрации того, что базовые требования к аддону просты.

Аддон обычно регистрирует операторов, панели, пункты меню и т. д., но стоит отметить, что любой скрипт может сделать это при запуске из текстового редактора или даже интерактивной консоли – в аддоне нет ничего особенного, что позволяет ему интегрироваться с Blender, такая функциональность предоставляется модулем blender_api:bpy для доступа любого скрипта.

Таким образом, аддон – это всего лишь способ инкапсулировать модуль Python таким образом, чтобы пользователь мог легко его использовать.

Примечание

Запуск этого скрипта в Текстовом редакторе ничего не отобразит (print), чтобы увидеть выходные данные, его необходимо установить через Настройки. Сообщения будут отображаться при включении и отключении.

Ваш первый Аддон

Простейший аддон, описанный выше, полезен в качестве примера, но не более того. Следующий – прост, но показывает, как интегрировать скрипт в Blender с помощью Operator'а, который является типичным способом определения инструмента, доступ к которому осуществляется через меню, кнопки и сочетания клавиш.

Для первого примера мы создадим скрипт, который просто перемещает все объекты в сцене.

Написание скрипта

Добавьте следующий скрипт в текстовый редактор Blender:

import bpy

scene = bpy.context.scene
for obj in scene.objects:
    obj.location.x += 1.0

Нажмите кнопку Выполнить сценарий, все объекты в активной сцене перемещаются на 1,0 единицу (unit).

Написание аддона (простой)

Этот аддон берёт тело (body) приведённого выше скрипта и добавляет его в функцию оператора execute().

bl_info = {
    "name": "Move X Axis",
    "blender": (2, 80, 0),
    "category": "Object",
}

import bpy


class ObjectMoveX(bpy.types.Operator):
    """My Object Moving Script"""      # 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 when running the operator.

        # The original script
        scene = context.scene
        for obj in scene.objects:
            obj.location.x += 1.0

        return {'FINISHED'}            # Lets Blender know the operator finished successfully.

def menu_func(self, context):
    self.layout.operator(ObjectMoveX.bl_idname)

def register():
    bpy.utils.register_class(ObjectMoveX)
    bpy.types.VIEW3D_MT_object.append(menu_func)  # Adds the new operator to an existing menu.

def unregister():
    bpy.utils.unregister_class(ObjectMoveX)


# This allows you to run the script directly from Blender's Text editor
# to test the add-on without having to install it.
if __name__ == "__main__":
    register()

Примечание

bl_info разбит на несколько строк, это просто соглашение о стиле, используемое для упрощения добавления элементов.

Примечание

Вместо использования bpy.context.scene мы используем аргумент context.scene, передаваемый в execute(). В большинстве случаев они будут одинаковыми. Однако в некоторых случаях операторам будет передан собственный контекст, поэтому авторам скриптов следует предпочесть аргумент context, передаваемый операторам.

Чтобы протестировать скрипт, вы можете скопировать и вставить его в Текстовый редактор Blender и запустить. Это приведёт к непосредственному выполнению скрипта и немедленному вызову регистра.

Однако запуск скрипта не приведёт к перемещению каких-либо объектов. Для этого вам необходимо выполнить вновь зарегистрированный оператор.

../../_images/advanced_scripting_addon-tutorial_operator-search-menu.png

Меню поиска оператора.

Откройте меню поиска оператора и введите «Move X by One (Переместить X на один)» (bl_label), затем Return.

Объекты должны двигаться, как и раньше.

Оставьте этот аддон открытым в Blender для следующего шага — установки.

Установка аддона

Как только у вас появится аддон в Текстовом редакторе Blender, вы захотите установить его, чтобы его можно было включить в Настройках для загрузки при запуске.

Несмотря на то, что вышеописанное дополнение (аддон) является тестовым, давайте всё равно пройдемся по шагам, чтобы вы знали, как это сделать позже.

Чтобы установить текст Blender как дополнение (аддон), вам сначала придётся сохранить его на диске. Позаботьтесь о том, чтобы соблюдать ограничения по именам, которые применяются к модулям Python и заканчиваются расширением .py.

Как только файл окажется на диске, вы сможете установить его так же, как и аддон, загруженный из интернета.

Откройте Настройки ‣ Аддоны ‣ Установить… и выберите файл.

Теперь аддон появится в списке, и вы сможете включить его, установив флажок. Если вы хотите, чтобы он включался при перезагрузке, нажмите Save as Default (Сохранить как «По умолчанию»). Оператор можно запустить так же, как описано в предыдущем разделе.

Когда аддон включен, Blender выполняет код и запускает функцию register(). Когда аддон отключен, Blender запускает функцию unregister().

Примечание

Пункт назначения (destination) аддона зависит от вашей конфигурации Blender. При установке дополнения – пути к источнику и назначению выводятся в консоли. Вы также можете найти местоположения дополнительных путей, запустив это в консоли Python:

import addon_utils
print(addon_utils.paths())

Подробнее на эту тему написано здесь: Макет каталога (Directory Layout).

Ваш второй аддон

В нашем втором дополнении мы сосредоточимся на создании экземпляров объектов (object instancing), то есть на создании связанных копий объекта аналогично тому, что вы, возможно, видели с модификатором Array.

Написание скрипта

Как и раньше, сначала мы начнём со скрипта, разработаем его, затем преобразуем в аддон.

import bpy
from bpy import context

# Get the current scene
scene = context.scene

# Get the 3D cursor location
cursor = scene.cursor.location

# Get the active object (assume we have one)
obj = context.active_object

# Now make a copy of the object
obj_new = obj.copy()

# The new object has to be added to a collection in the scene
scene.collection.objects.link(obj_new)

# Now we can place the object
obj_new.location = cursor

Теперь попробуйте скопировать этот скрипт в Blender и запустить его для стандартного куба. Перед запуском убедитесь, что вы кликнули, чтобы переместить 3D-курсор, поскольку дубликат появится в месте расположения курсора.

После запуска обратите внимание: когда вы переходите в режим Редактирования для изменения куба – изменяются все копии. В Blender это называется Связанные дубликаты.

Далее мы собираемся сделать это в цикле (loop), чтобы создать массив (array) объектов между активным объектом и курсором.

import bpy
from bpy import context

scene = context.scene
cursor = scene.cursor.location
obj = context.active_object

# Use a fixed value for now, eventually make this user adjustable
total = 10

# Add 'total' objects into the scene
for i in range(total):
    obj_new = obj.copy()
    scene.collection.objects.link(obj_new)

    # Now place the object in between the cursor
    # and the active object based on 'i'
    factor = i / total
    obj_new.location = (obj.location * factor) + (cursor * (1.0 - factor))

Попробуйте запустить этот скрипт с активным объектом и отодвинутым курсором, чтобы увидеть результат.

Вы заметите, что с помощью этого скрипта мы выполняем некоторые математические вычисления с местоположением объекта и курсором, это работает, потому что оба являются 3D-экземплярами mathutils.Vector, удобным классом, предоставляемым blender_api: mathutils модуль, который позволяет умножать векторы на числа и матрицы.

Если вас интересует эта область, прочтите mathutils.Vector – есть много удобных служебных функций, таких как получение угла между векторами, векторное произведение, скалярное произведение, а также более продвинутые функции в mathutils.geometry, такие как сплайн-интерполяция Безье и пересечение лучей и треугольников.

На данный момент мы сосредоточимся на том, чтобы сделать этот скрипт аддоном, но приятно знать, что этот модуль трехмерной математики доступен и может помочь вам в дальнейшем с более расширенными функциями.

Написание аддона

Первый шаг – преобразовать скрипт «как есть» в аддон:

bl_info = {
    "name": "Cursor Array",
    "blender": (2, 80, 0),
    "category": "Object",
}

import bpy


class ObjectCursorArray(bpy.types.Operator):
    """Object Cursor Array"""
    bl_idname = "object.cursor_array"
    bl_label = "Cursor Array"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        scene = context.scene
        cursor = scene.cursor.location
        obj = context.active_object

        total = 10

        for i in range(total):
            obj_new = obj.copy()
            scene.collection.objects.link(obj_new)

            factor = i / total
            obj_new.location = (obj.location * factor) + (cursor * (1.0 - factor))

        return {'FINISHED'}

def register():
    bpy.utils.register_class(ObjectCursorArray)


def unregister():
    bpy.utils.unregister_class(ObjectCursorArray)


if __name__ == "__main__":
    register()

Всё здесь было рассмотрено на предыдущих шагах, возможно, вы захотите ещё попробовать запустить аддон и подумать, что можно сделать, чтобы сделать его более полезным.

Две наиболее очевидные недостающие вещи – фиксированное общее число, равное 10, и необходимость доступа к оператору с помощью меню «Поиск» не очень удобно.

Оба этих дополнения (additions) объясняются далее, а затем — окончательный скрипт.

Свойство оператора

Существует множество типов свойств, которые используются для настроек инструмента. К общим типам свойств относятся: int, float, vector, color, Boolean и string.

Эти свойства обрабатываются иначе, чем типичные атрибуты класса Python’а, поскольку Blender’у необходимо отображать их в интерфейсе, сохранять их настройки в раскладках клавиш (keymaps) и сохранять настройки для повторного использования.

Хотя это реализовано довольно Python’ическим способом, имейте в виду, что вы фактически определяете настройки инструмента, которые загружаются в Blender и доступны из других частей Blender, за пределами Python.

Чтобы избавиться от литерала 10, обозначающего total, мы воспользуемся свойством оператора. Свойства оператора определяются через модуль bpy.props, он добавляется в тело класса (class body):

# moved assignment from execute() to the body of the class...
total: bpy.props.IntProperty(name="Steps", default=2, min=1, max=100)

# and this is accessed on the class
# instance within the execute() function as...
self.total

Эти свойства из bpy.props специально обрабатываются Blender’ом при регистрации класса, поэтому они отображаются в виде кнопок в пользовательском интерфейсе. Существует множество аргументов, которые вы можете передать свойствам, чтобы установить ограничения, изменить значение по умолчанию и отобразить всплывающую подсказку.

См.также

bpy.props.IntProperty()

В этом документе не рассматриваются подробности использования других типов свойств. Однако ссылка выше содержит примеры более продвинутого использования свойств.

Горячие клавиши

В Blender аддоны имеют свои собственные раскладки клавиш, чтобы не мешать встроенным раскладкам клавиш Blender.

В приведённом ниже примере добавляется новый объектный режим bpy.types.KeyMap, затем к раскладке ключей добавляется bpy.types.KeyMapItem, который ссылается на наш недавно добавленный оператор, используя Shift-Ctrl-T в качестве сочетания клавиш для его активации.

# store keymaps here to access after registration
addon_keymaps = []

def register():

    # handle the keymap
    wm = bpy.context.window_manager
    km = wm.keyconfigs.addon.keymaps.new(name='Object Mode', space_type='EMPTY')

    kmi = km.keymap_items.new(ObjectCursorArray.bl_idname, 'T', 'PRESS', ctrl=True, shift=True)
    kmi.properties.total = 4

    addon_keymaps.append((km, kmi))


def unregister():

    # handle the keymap
    for km, kmi in addon_keymaps:
        km.keymap_items.remove(kmi)
    addon_keymaps.clear()

Обратите внимание, что элемент раскладки клавиатуры может иметь настройку total, отличную от настройки по умолчанию, установленной оператором. Это позволяет вам иметь несколько клавиш, обращающихся к одному и тому же оператору с разными настройками.

Примечание

While Shift-Ctrl-T is not a default Blender key shortcut, it is hard to make sure add-ons will not overwrite each other’s keymaps. Thus at least take care when assigning keys that they do not conflict with important functionality of Blender (see also Is Key Free).

Документацию по API для перечисленных выше функций см. в разделах:

Собираем всё это вместе

bl_info = {
    "name": "Cursor Array",
    "blender": (2, 80, 0),
    "category": "Object",
}

import bpy


class ObjectCursorArray(bpy.types.Operator):
    """Object Cursor Array"""
    bl_idname = "object.cursor_array"
    bl_label = "Cursor Array"
    bl_options = {'REGISTER', 'UNDO'}

    total: bpy.props.IntProperty(name="Steps", default=2, min=1, max=100)

    def execute(self, context):
        scene = context.scene
        cursor = scene.cursor.location
        obj = context.active_object

        for i in range(self.total):
            obj_new = obj.copy()
            scene.collection.objects.link(obj_new)

            factor = i / self.total
            obj_new.location = (obj.location * factor) + (cursor * (1.0 - factor))

        return {'FINISHED'}


def menu_func(self, context):
    self.layout.operator(ObjectCursorArray.bl_idname)

# store keymaps here to access after registration
addon_keymaps = []


def register():
    bpy.utils.register_class(ObjectCursorArray)
    bpy.types.VIEW3D_MT_object.append(menu_func)

    # handle the keymap
    wm = bpy.context.window_manager
    # Note that in background mode (no GUI available), keyconfigs are not available either,
    # so we have to check this to avoid nasty errors in background case.
    kc = wm.keyconfigs.addon
    if kc:
        km = wm.keyconfigs.addon.keymaps.new(name='Object Mode', space_type='EMPTY')
        kmi = km.keymap_items.new(ObjectCursorArray.bl_idname, 'T', 'PRESS', ctrl=True, shift=True)
        kmi.properties.total = 4
        addon_keymaps.append((km, kmi))

def unregister():
    # Note: when unregistering, it's usually good practice to do it in reverse order you registered.
    # Can avoid strange issues like keymap still referring to operators already unregistered...
    # handle the keymap
    for km, kmi in addon_keymaps:
        km.keymap_items.remove(kmi)
    addon_keymaps.clear()

    bpy.utils.unregister_class(ObjectCursorArray)
    bpy.types.VIEW3D_MT_object.remove(menu_func)


if __name__ == "__main__":
    register()
../../_images/advanced_scripting_addon-tutorial_in-menu.png

В меню.

Запустите скрипт (или сохраните его и добавьте через Настройки, как раньше), и он появится в меню Объект.

../../_images/advanced_scripting_addon-tutorial_op-prop.png

Свойство оператора.

Выбрав его в меню, вы можете выбрать, сколько экземпляров куба вы хотите создать.

Примечание

Непосредственное выполнение скрипта несколько раз также каждый раз будет добавлять меню. Хотя это и бесполезное поведение, беспокоиться не о чем, поскольку аддоны не будут регистрироваться несколько раз, если они включены в Настройках.

Выводы

Аддоны могут аккуратно инкапсулировать определенные функции для написания инструментов для улучшения вашего рабочего процесса или для написания утилит для использования другими.

Хотя существуют ограничения на то, что Python может делать в Blender, безусловно, многого можно достичь, не погружаясь в код Blender на C/C++.

Пример, приведенный в руководстве, ограничен, но показывает Blender API, используемый для общих задач, которые вы можете расширить, чтобы написать свои собственные инструменты.

Дальнейшее чтение

Blender поставляется с шаблонами с комментариями, которые доступны из заголовка Текстового редактора. Если у вас есть конкретные области (areas), для которых вы хотите увидеть примеры кода, это хорошее место для начала.

Вот несколько сайтов, которые вы, возможно, захотите проверить после завершения этого руководства.