К содержанию
fohbook/ TouchDesigner
профи

Extensions: классы у компонента

Как дать компоненту методы и свойства, чтобы наружу торчал понятный интерфейс, а не двадцать узлов.

Extension — класс Python, привязанный к компоненту. Он превращает набор узлов в объект с методами: op('scene').Next() вместо цепочки скриптов.

Как подключить

  1. Внутри компонента создайте Text DAT, например SceneExt.
  2. На компоненте, страница Extensions: Extension 1 = op('SceneExt'), имя объекта — например Scene, и включите Promote Extension.
  3. Правый клик по компоненту → Re-Init Extensions после правки кода.

Минимальный класс

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)

Соглашение об именах

В TouchDesigner принято: публичные методы и свойства с большой буквы, внутренние — с маленькой. Причина техническая: с Promote Extension наружу отдаются только имена с заглавной. Это же отделяет интерфейс компонента от его кишок.

Свойства

    @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. Не делайте там тяжёлых вещей и не полагайтесь на то, что вся сеть уже загружена: при старте проекта порядок инициализации не гарантирован.

Для отложенного старта:

    def __init__(self, ownerComp):
        self.ownerComp = ownerComp
        run("args[0].Setup()", self, delayFrames=1)
Ошибка в extension ломает компонент молча

Если класс не компилируется, у компонента просто не будет методов, а вызовы упадут с непонятным сообщением. Смотрите Textport сразу после Re-Init Extensions.

Один компонент — одно расширение

Соблазн сделать «главный класс проекта», который управляет всем, велик. Он же превращает проект в один большой файл, который нельзя разобрать. Лучше пять компонентов с пятью маленькими расширениями.

рядом по смыслу