Навчальник про Додатки – Add-on Tutorial¶
Цільова Аудиторія – Intended Audience¶
Цей навчальник розроблено, щоб допомогти технічним митцям або розробникам дізнатися, як розширювати Blender. Припускається розуміння основ Python, хто хоче працювати з цим навчальником.
Передумови – Prerequisites¶
Перед приступанням до продовження цього навчальника ви повинні…
- Бути знайомими з основами роботи у Blender’і.
- Знати, як виконувати скрипт у редакторі Тексту Blender’а.
- Мати розуміння примітивних типів у Python (int (цілочислове), boolean (булеве), string (рядкове), list (список), tuple (кортеж), dictionary (словник) та set (множина, набір))
- Бути знайомими з концепцією модулів Python.
- Мати базове розуміння класів (орієнтація об’єкта) у Python.
Рекомендується прочитати перед приступанням до цього навчальника.
- Dive Into Python секції (1, 2, 3, 4 і 7).
- Blender API Quickstart, щоб допомогти ознайомитися з основами Blender/Python.
Щоб найкраще усунути несправності, будь-яке повідомлення про помилку Python виводить при написанні скриптів, коли ви запускаєте Blender із терміналу. Дивіться про термінал тут – Use The Terminal.
Посилання на Документацію – Documentation Links¶
При проходженні цього навчальника ви можливо захочете глянути на нашу орієнтувальну документацію.
- Blender API Overview: Цей документ досить детальний, але і корисний, якщо ви хочете знати більше про цю тему.
bpy.context
API reference – Зручно мати список доступних елементів, якими може оперувати ваш скрипт.bpy.types.Operator
– Додатки, про які піде мова, визначають оператори, ці документи дають деталі та більше прикладів операторів.
Що таке Додаток? – 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’а та прогнати його. Це виконає скрипт безпосередньо та відразу викличе реєстр.
Проте, проганяння цього скрипту не перемістить жоден з об’єктів. Для цього вам потрібно виконати ново зареєстрований оператор.
Зробіть це, натиснувши F3 для виклику меню пошуку оператора та уведіть у ньому «Move X by One» (bl_label
), а потім натисніть Return.
Об’єкти повинні переміститися, як і у попередньому випадку.
Утримуйте цей додаток відкритим у Blender’і для наступного кроку - Інсталювання.
Інсталювання Додатку – Install the Add-on¶
Після того, як у вас є додаток у Редакторі тексту – Text Editor Blender’а, ви схочете змогти інсталювати його, щоб його можна було увімкнути в Уподобаннях – Preferences для завантаження при запуску програми.
Навіть хоча цей додаток є всього лише тестом, давайте пройдемо усі потрібні кроки, щоб ви знали, як це робити на майбутнє.
Для інсталювання тексту у Blender’і як додатка ви спершу маєте зберегти його на диск. Подбайте, щоб дотримуватися обмежень іменування, що застосовуються до модулів Python та закінчуються розширенням .py
.
Після збереження файлу на диск ви можете інсталювати його так само, як і додаток, звантажений з Інтернету.
Відкрийте «Уподобання > Додатки > Інсталювання…» –
та виберіть цей файл.Тепер додаток буде показуватися у списку і ви зможете увімкнути його, поставивши стяг біля його запису, а якщо ви хочете, щоб він вмикався автоматично при запуску програми, то клацніть на кнопці «Зберегти як Стандарт» – 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 документацію:
bpy.types.KeyMaps.new
,bpy.types.KeyMap
,bpy.types.KeyMapItems.new
,bpy.types.KeyMapItem
.
Зведення Всього В Одне – 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()
Проженіть цей скрипт (або збережіть його та додайте через Уподобання – Preferences, як і вище) і він з’явиться у меню Об’єкт – Object.
Після вибору його у меню ви можете вказати, скільки примірників куба ви хочете створити.
Примітка
Пряме виконування цього скрипту кілька разів буде додавати відповідну кількість пунктів у меню. Хоча це не корисна поведінка, тут немає нічого страшного, оскільки додатки не реєструються більше одного разу при їх увімкненні через Уподобання – 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.