🐍

【TouchDesigner】tdPyenvManagerでPythonパッケージを管理する

に公開

TouchDesigner 2025がリリースされ、新機能の一つとしてtdPyenvManagerというPythonの外部パッケージを管理する機能が追加されました。tdPyEnvMangerではvenvまたはCondaでパッケージ管理をすることができます。

https://derivative.ca/community-post/2025-official-update/73153
https://docs.derivative.ca/Palette:tdPyEnvManager

ちなみに、以前は外部でインストールしたパッケージをPreferencesでの設定やsys.path.appendでパッケージ探索パスに追加してTouchDesignerで利用できるようにしていました。詳しい方法は以下の記事にまとまっています。
https://satoruhiga.com/post/extending-touchdesigner/

使い方

以下の記事を参考にtdPyenvManagerでvenvを使用して外部パッケージを管理してみます。

https://derivative.ca/community-post/introducing-touchdesigner-python-environment-manager-tdpyenvmanager/72024

まず、PaletteからTools > tdPyEnvManagerをNetwork Editorにドラッグ&ドロップしてtdPyenvManager COMPを作成します。

パラメータダイアログでActiveをオンにして有効化し、Create vEnvのPulseを押してvenv仮想環境を作成します。作成が完了すると、{親ディレクトリ名}_vEnv(例えば/Foo/Bar.toeの場合Foo_vEnv)というディレクトリが作成されます。Macの場合はこれに加えてTDPyEnvManagerContext.jsonというファイルも作成されます。

Open CLIのPulseを押すと仮想環境が有効になっている状態でターミナルが起動するので、ここでpipコマンドで外部パッケージをインストールします。

pip install zmq

TouchDesignerのTextportを開き、以下のコマンドでパッケージがインストールできているか確認します。

import zmq

TouchDesignerプロジェクトをGitなどのバージョン管理システムで管理する場合、外部パッケージ本体は管理対象に含めず、インストールしたパッケージの情報だけをrequirements.txtに出力して管理します。tdPyEnvManagerではパラメータダイアログのExport requirements.txtでrequirements.txtを作成することができます。バージョン管理システムから新たにプロジェクトを作成した場合、パラメータダイアログのCreate From requirements.txtでvenv環境を作成して必要なパッケージをインストールできます。

Create vEnvで作成された*_vEnvディレクトリ内には外部パッケージ本体はGit管理しないようにする.gitignoreファイルがあらかじめ含まれています。Macの場合はTDPyEnvManagerContext.jsonもバージョン管理対象外にして問題なさそうです。

使用例

例として以前検証したQRコード作成をtdPyEnvManagerを使って試してみます。

https://zenn.dev/aadebdeb/articles/touchdesigner-create-qrcode

このときはvenvで仮想環境を作成してpyqrcodepypngをインストールし、venvのパッケージインストールパスをsys.path.appendでパッケージ探索パスに追加して利用できるようにしていました。

今回は先ほど紹介した手順でtdPyEnvManagerをセットアップして、ターミナルで以下のようにpyqrcodepypngをインストールします。

pip install pyqrcode pypng

Script TOPではsys.path.apppendは不要になるので、以下のスクリプトでQRコードを作成できることができました。

import pyqrcode
import numpy as np
import cv2
import io

def onSetupParameters(scriptOp):
	page = scriptOp.appendCustomPage('QR Code')
	p = page.appendInt('Scale', label='QR Code Scale')
	p[0].normMin = 1
	p[0].normMax = 20
	p[0].default = 5
	p = page.appendStr('Data', label='QR Code Data')
	p[0].default = 'Hello, World!'

def onPulse(par):
	return

def onCook(scriptOp):
	scale = scriptOp.par.Scale.val
	data = scriptOp.par.Data.val
	qrcode = pyqrcode.create(data)
	buffer = io.BytesIO()
	qrcode.png(buffer, scale=scale)
	array = np.frombuffer(buffer.getvalue(), dtype=np.uint8)
	image = cv2.imdecode(array, cv2.IMREAD_COLOR)
	image = np.flipud(image).copy() # copyがないとTDがクラッシュする
	scriptOp.copyNumpyArray(image)

Discussion