Навчальник про Додатки – Add-on Tutorial

Цільова Аудиторія – Intended Audience

Цей навчальник розроблено, щоб допомогти технічним митцям або розробникам дізнатися, як розширювати Blender. Розуміння основ Python очікується від тих, хто працює за цим навчальником.

Передумови – Prerequisites

Перед приступанням до продовження цього навчальника ви повинні…

  • Бути знайомими з основами роботи у Blender’і.

  • Знати, як виконувати скрипт у редакторі Тексту Blender’а.

  • Мати розуміння примітивних типів Python (integer (цілочислове), Boolean (булеве), string (рядкове), list (список), tuple (кортеж), dictionary (словник) та set (множина, набір)).

  • Бути знайомими з концепцією модулів Python.

  • Мати базове розуміння класів (орієнтація об’єкта) у Python.

Рекомендується прочитати перед починанням цього навчальника.

Щоб найкраще усунути несправності будь-якого повідомлення про помилку, що Python виводить при написанні скриптів, ви запускайте Blender із термінала. Дивіться Use The Terminal.

Що таке Додаток? – What is an Add-on?

Додаток – add-on – це простий модуль 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

є словником, що містить метадані додатка, такі як назва, версія та автор, які показуються в Уподобаннях – Preferences списку додатків. Він також визначає мінімальну версію Blender’а, необхідну для виконання цього скрипту; старіші версії не будуть показувати додаток у списку.

register

це функція, яка проганяється тільки, коли вмикається додаток, що означає, що модуль може бути завантажений у пам’ять без активування додатку.

unregister

це функція розвантаження з пам’яті будь-чого, що завантажене функцією register, вона викликається, коли додаток вимикається.

Зауважте, що цей додаток нічого не робить відносно Blender’а, (модуль bpy не імпортується, наприклад).

Це вигаданий приклад додатку, що слугує для ілюстрації тог, що базові умови додатку, є простими.

Додаток типово буде реєструвати оператори, панелі, записи меню тощо, але варто зазначити, що будь-який скрипт може робити це ж саме, коли виконується з Редактора Тексту – Text Editor або навіть з інтерактивної консолі – немає нічого за своєю суттю відрізняльного у додатку, що дозволяє його інтегрувати з Blender’ом, дана функціональність просто забезпечується модулем bpy для будь-якого скрипту, щоб він мав доступ.

Звідси, додаток – це просто засіб інкапсуляції модуля Python у спосіб, щоб користувач міг легко його використовувати.

Примітка

Виконування цього скрипту у редакторі тексту Text Editor не буде виводити нічого, щоб бачити вивід, він має бути інстальований через уподобання Preferences. Повідомлення будуть виводитися при вмиканні та вимиканні.

Ваш Перший Додаток – Your First Add-on

Вищенаведений найпростіший можливий додаток є корисним як приклад, але не більш того. Наступний додаток є також простим, але показує як інтегрувати скрипт у Blender, використовуючи Operator, який є типовим шляхом визначити засіб, доступний за допомогою меню, кнопок та клавіатурних скорочень.

Для першого прикладу ми зробимо скрипт, що просто переміщує усі об’єкти у сцені.

Написання Скрипту – Write the Script

Додайте наступний скрипт у Редактор Тексту – Text Editor Blender’а:

import bpy

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

Клацніть на кнопці «Прогнати Скрипт» – Run Script, усі об’єкти в активній сцені перемістяться на одну 1.0 одиницю.

Написання Додатку (Просто) – Write the Add-on (Simple)

Цей додаток бере тіло вищеописаного скрипту та додає таке у функцію оператора 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 register():
    bpy.utils.register_class(ObjectMoveX)


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 передавався операторам.

Для тестування цього скрипту ви можете скопіювати та вставити його у редактор тексту Text Editor Blender’а та виконати його. Це виконає скрипт безпосередньо та відразу викличе реєстр.

Проте, виконування цього скрипту не буде переміщувати жоден з об’єктів. Для цього вам потрібно виконати щойно зареєстрований оператор.

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

Меню Пошук Оператора – Operator Search.

Open the Operator Search menu and type in «Move X by One» (the bl_label), then Return.

Об’єкти повинні переміститися, як і у попередньому випадку.

Утримуйте цей додаток відкритим у Blender’і для наступного кроку - Інсталювання.

Інсталювання Додатку – Install the Add-on

Після того, як у вас є додаток у Редакторі тексту – Text Editor Blender’а, ви схочете змогти інсталювати його, щоб його можна було увімкнути в Уподобаннях – Preferences для завантаження при запуску програми.

Навіть хоча цей додаток є всього лише тестом, давайте пройдемо усі потрібні кроки, щоб ви знали, як це робити на майбутнє.

Щоб інсталювати текст у Blender’і як додаток ви спершу маєте зберегти його на диску. Подбайте, щоб дотримуватися обмежень іменування, що застосовуються до модулів Python та закінчуються розширенням .py.

Після збереження файлу на диску ви можете інсталювати його так само, як і додаток, завантажений онлайн.

Відкрийте «Уподобання > Додатки > Інсталювання…» – Preferences ‣ Add-ons ‣ Install… та виберіть цей файл.

Тепер додаток буде показуватися у списку і ви зможете увімкнути його, натиснувши стяг, а якщо ви хочете, щоб він вмикався автоматично при запуску програми, то натисніть «Зберегти як Стандарт» – Save as Default. Оператор може виконуватися так само, як описано в previous section.

Коли додаток вмикається, Blender виконує код та запускає функцію register(). Коли додаток вимикається, Blender виконує функцію unregister().

Примітка

Місце призначення додатка залежить від вашої конфігурації Blender’а. При інсталюванні додатку шляхи джерела і призначення виводяться у консолі. Ви можете також знайти шлях до локацій додатку, запустивши це у консолі Python Console:

import addon_utils
print(addon_utils.paths())

Більше написано про цю тему тут: Directory Layout.

Ваш Другий Додаток – Your Second Add-on

Для нашого другого додатка ми зосередимось на примірникуванні об’єкта – тобто – на виробленні пов’язаних копій об’єкта подібно до тих, які ви могли бачити з модифікатором Масив – Array.

Написання Скрипту – Write the Script

Як і раніше, спершу почнемо зі скрипту, розробимо його, а потім конвертуємо його у додаток.

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 та виконати його на стандартному кубі Cube. Переконайтеся, що ви клацнули в іншому місці для переміщення 3D курсора перед виконуванням, щоб дублікат показувався в іншій локації курсора.

Після запуску, зауважте, що коли ви переходите у режим редагування Edit Mode для зміни куба Cube – всі з копій будуть змінюватися. У Blender’і, це відоме як пов’язані дублікати Linked Duplicates.

Наступне, ми будемо робити це у циклі, щоб зробити масив об’єктів між активним об’єктом та курсором.

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, зручний клас було забезпечено модулем mathutils, який дозволяє векторам множитися на числа та матриці.

Якщо ви зацікавилися цією областю, читайте про клас векторів – mathutils.Vector – там є багато зручних функцій, таких як отримання кута між векторами, векторний добуток, скалярний добуток, а також багато просунутих геометричних функцій у mathutils.geometry, таких як інтерполяція сплайну Безьє та перетин променя з трикутником.

Далі ми зосередимося на перетворенні цього скрипту у додаток, але добре знати, що цей математичний 3D модуль доступний і може допомогти вам у майбутньому отримувати просунуту функціональність.

Написання Додатку – Write the Add-on

Першим кроком конвертуємо цей скрипт «як є» у додаток:

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-и елементів та наявність доступу до цього оператора через меню Search не є дуже зручними.

Обидва ці аспекти пояснюються далі, нижче.

Властивість Оператора – Operator Property

Існують різні типи властивостей, що використовуються для устав засобів, загальні типи властивостей включають: int (цілочислове), float (дійсночислове), vector (вектор), color (колір), Boolean (булеве) and string (рядкове).

Ці властивості обробляються по-різному у типових атрибутах класів Python, оскільки Blender потребує показувати їх в інтерфейсі, зберігати їх установки у розкладках клавіш та зберігати ці установки для повторного використання.

При вказуванні їх у стилі Python, майте на увазі, що ви фактично визначаєте установки засобу, які будуть завантажуватися у Blender’і та будуть доступні іншими частинами Blender’а, поза Python.

Щоб позбутися буквальної 10 для total, ми використаємо властивість оператора. Властивості оператора визначаються через модуль bpy.props, це додається до тіла класу:

# 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

Цей документ не вдається у деталі щодо використання інших типів властивостей. Проте, за цим вищенаведеним посиланням включено приклади про більш просунуте використання властивостей.

Розкладка клавіш – Keymap

У 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, ніж стандартно задана оператором, це дозволяє вам мати одночасно кілька варіантів клавіш для доступу до того ж самого оператора з різними уставами.

Примітка

Хоча Shift-Ctrl-T не є стандартним клавіатурним скороченням Blender’а, важко бути впевненим, що додатки не будуть перезаписувати розкладки клавіш один одного. Таким чином, принаймні, подбайте, при призначанні клавіш, що вони не конфліктують з важливою функціональністю Blender’а (дивіться також Is Key Free).

Для функій, згаданих вище, дивіться наступну API документацію:

Зведення Всього В Одне – Bringing It All Together

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

У меню.

Проженіть цей скрипт (або збережіть його та додайте через Уподобання – Preferences, як і вище) і він з’явиться у меню Об’єкт – Object.

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

Властивість Оператора.

Після вибору його у меню ви можете вказати, скільки примірників куба ви хочете створити.

Примітка

Пряме виконування цього скрипту кілька разів буде додавати кожного разу меню. Хоча це не корисна поведінка, турбуватися немає про що, оскільки додатки не будуть реєструватися більше одного разу при їх увімкненні через уподобання Preferences.

Висновки – Conclusions

Додатки можуть інкапсулювати акуратно певну функціональність при написанні засобів для покращення вашого потоку роботи або при написанні утиліт для використання іншими користувачами.

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

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

Додаткові матеріали – Further Reading

Blender постачається з коментованими шаблонами, які доступні із заголовка редактора тексту Text editor. Якщо ви хочете переглянути приклад коду для певних областей, то це добре місце, щоб розпочати.

Ось деякі із сайтів, які можливо ви схочете переглянути після завершення цього навчальника.