Extensions: классы у компонента
Класс Python на компоненте: op('scene').Next() вместо цепочки скриптов. Как подключить, почему имена с большой буквы и что делать с инициализацией.
Extension — класс Python, привязанный к компоненту, и набор узлов
превращается в объект с методами: op('scene').Next() вместо цепочки
скриптов.
Быстрее всего подключать через RMB → Customize Component, раздел
Extension Code: вписали имя, нажали Add — и TouchDesigner сам создаёт
Text DAT с заготовкой класса и прописывает его. Называют расширение
с большой буквы и суффиксом Ext, например SceneExt. Вручную то же
делается на странице Extensions: Text DAT SceneExt внутри компонента,
в Object строка op('./SceneExt').module.SceneExt(me) и включённый
Promote, чтобы методы были видны прямо на компоненте.
Пересоздаётся расширение само, когда меняется код; руками — кнопкой
Re-Init Extensions, а Init Extensions On Start делает это при запуске.
class SceneExt:
def __init__(self, ownerComp):
self.ownerComp = ownerComp
self.index = 0
self.Count = ownerComp.par.Count.eval()
def Next(self):
self.index = (self.index + 1) % self.Count
self.Apply()
def Goto(self, i):
self.index = max(0, min(int(i), self.Count - 1))
self.Apply()
def Apply(self):
self.ownerComp.op('switch1').par.index = self.index
op('scene').Next()
op('scene').Goto(3)
Большая буква
Публичное пишут с большой буквы, внутреннее с маленькой, и это не вкус:
Promote выносит наружу только имена с заглавной. Без Promote
до расширения добираются через .ext — op('scene').ext.SceneExt.Next().
Заодно видно, где интерфейс компонента, а где внутренняя кухня.
@property
def Active(self):
return bool(self.ownerComp.par.Active.eval())
@Active.setter
def Active(self, v):
self.ownerComp.par.Active = 1 if v else 0
Так компонент становится чёрным ящиком: Next() вызывает кто угодно, не заходя
внутрь. Логика собрана в одном месте, а не в пяти колбэках, код в Text DAT с
внешним .py хранится в git (Файл проекта), и
компонент целиком переносится в другой проект.
Когда он создаётся
__init__ вызывается при каждом создании расширения — загрузка файла,
вырезка и вставка, Re-Init Extensions, правка кода, — и всё в self при
этом пропадает. То, что должно пережить это и сохранение, держат
в хранилище через StorageManager — Состояние и хранилище.
Тяжёлого в __init__ не делают и на загруженную сеть не рассчитывают.
Для этого есть встроенные методы:
def onInitTD(self):
# вызывается в конце кадра, в котором расширение инициализировано
self.Setup()
def onDestroyTD(self):
# вызывается перед тем, как расширение будет уничтожено
pass
Если параметр обращается к расширению раньше времени, вылезает
«Cannot use an extension during its initialization», и выражение
защищают проверкой parent().extensionsReady.
Не скомпилировался класс — у компонента просто нет методов, а вызовы падают с невнятным сообщением. После правки сразу смотрите Textport.
«Главный класс проекта» быстро становится файлом, который не разобрать. Пять компонентов с пятью маленькими расширениями проще поддерживать.
Нашли в статье ошибку, устаревшее или непонятное место?