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

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

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

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

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

  • Бути знайомими з основами роботи у Blender’і.
  • Знати, як виконувати скрипт у редакторі Тексту Blender’а.
  • Мати розуміння примітивних типів у Python (int (цілочислове), 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
is a dictionary containing add-on metadata such as the title, version and author to be displayed in the Preferences add-on list. It also specifies the minimum Blender version required to run the script; older versions won’t display the addon in the list.
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

Click the Run Script button, all objects in the active scene are moved by 1.0 unit.

Написання Додатку (Просто) – 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.

Зробіть це, натиснувши F3 для виклику меню пошуку оператора та уведіть у ньому «Move X by One» (bl_label), а потім натисніть Return.

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

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

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

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

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

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

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

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

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

Примітка

Місце знаходження додатку залежить від вашої конфігурації Blender’а. При інсталюванні додатку шляхи джерела і призначення виводяться у консолі. Ви можете також знайти шлях до локації додатку, виконавши це у Консолі Python – 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 для зміни Куба – всі з копій будуть змінюватися. У 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-и елементів та наявність доступу до цього оператора через меню Пробілу – є не дуже зручними.

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

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

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

These properties are handled differently to typical Python class attributes because Blender needs to display them in the interface, store their settings in keymaps and keep settings for reuse.

При вказуванні їх у стилі 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

In Blender, add-ons have their own keymaps so as not to interfere with Blender’s built-in keymaps.

In the example below, a new object mode bpy.types.KeyMap is added, then a bpy.types.KeyMapItem is added to the keymap which references our newly added operator, using Shift-Ctrl-T as the key shortcut to activate it.

# 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’і.

Для функій, згаданих вище, дивіться наступну 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. Якщо ви хочете переглянути приклад коду для певних областей, то це добре місце, щоб розпочати.

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

  • Blender/Python API OverviewДля більш основоположних деталей про інтеграцію Blender/Python.
  • How to Think Like a Computer ScientistБагато інфо для тих, хто тільки почав вивчати Python.
  • Blender Development (Wiki)Розроблення Blender, загальна інформація та корисні посилання.
  • DevTalkФорум, де люди задаються питання щодо розроблення на Python.