Навчальник про Додатки – 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 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’а та прогнати його. Це виконає скрипт безпосередньо та відразу викличе реєстр.
Проте, проганяння цього скрипту не перемістить жоден з об’єктів. Для цього вам потрібно виконати ново зареєстрований оператор.
Зробіть це, натиснувши 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.
Відкрийте «Уподобання > Додатки > Інсталювання…» –
та виберіть цей файл.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 документацію:
bpy.types.KeyMaps.new
,bpy.types.KeyMapItems.new
,
Зведення Всього В Одне – 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.