Навчальник про Додатки – 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 add-on 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

Клацніть на кнопці «Прогнати Скрипт» – 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.

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

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

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

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

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

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

To install the Blender text as an add-on, you will first have to save it on drive. Take care to obey the naming restrictions that apply to Python modules and end with a .py extension.

Once the file is on drive, you can install it as you would for an add-on downloaded online.

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

Now the add-on will be listed and you can enable it by pressing the checkbox, if you want it to be enabled on restart, press Save as Default. The operator can be run in the same way as described in the previous section.

When the add-on is enabled, Blender executes the code and runs the register() function. When the add-on is disabled, Blender runs the unregister() function.

Примітка

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

import addon_utils
print(addon_utils.paths())

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

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

For our second add-on, we will focus on object instancing – this is – to make linked copies of an object in a similar way to what you may have seen with the Array modifier.

Написання Скрипту – 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()

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

The two of the most obvious missing things are – having the total fixed at 10, and having to access the operator with Пошук Оператора – Operator Search is not very convenient.

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

Властивість Оператора – 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’і.

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