この記事はTouchDesigner Advent Calendar 2022 17日目の記事です。
今年は、TouchDesigner使いの人間が抱く3大欲求の一つである、
TouchDesignerで作った絵をTouchDesignerから印刷するやり方をご紹介したいと思います。
※Window用です。
今回のプロジェクトファイルは下記にアップしています。
システム構成
本来であれば venv作って必要なライブラリインストールしてTouchDesignerの中から印刷命令! と行きたいところなのですが、印刷に使うPythonライブラリである 「pywin32」が仮想環境から使えないらしい ということがわかりました。
ということでsubprocessで外部pythonを実行しています。
※pythonはグローバルのpythonを使います。
それと出力ファイル名をバリデートしている箇所がありますが、フォルダ監視で間違って変なファイルを検出して印刷してしまわないようなシステム構成にしています。
プログラム
そして実際のプログラムです。
TouchDesignerのオペレーターはそれほど多くありませんね。
以下流れ
- 画像出力先のフォルダ(今回はimagesフォルダ)を外部のPythonで監視開始(後述)
- Button 押下でnull1のCookが走る
- CHOP EXECUTE DAT(createFileName) でファイル名を生成してText DAT(fileName) にタイムスタンプのファイル名を出力し、moviefileout1のrecordを実行
- 監視プログラムが画像ファイルを検出しそのファイル名をOSCで通知、udpin1に検出したファイル名を送信
- 4のタイミングでudpin1_callbacksが走り、受信したファイル名と、3で生成したファイル名を比較
- 5のバリデーションで問題なければprintを実行。※本当はここでプリント処理を記述したかったのですが、subprocessで外部pythonを実行しています。(後述)
- 外部の印刷用pythonが走り、印刷が完了するとudpin2に成功の通知が来る
※今回はエラーハンドリングは行わず成功が返るだけになっています。
フォルダ監視プログラム
これはpythonの Watchdog をpipでグローバルにインストールして使っています。
コマンドプロンプトで実行してシステムに常駐させます。
import os
import time
import sys
import socket
from argparse import ArgumentParser
from watchdog.events import FileSystemEventHandler
from watchdog.observers import Observer
class MyWatchHandler(FileSystemEventHandler):
def __init__(self):
super().__init__()
def on_any_event(self, event):
pass
def on_created(self, event):
#ファイルが生成されたときにUDPでファイル名を送信
sendUDP(os.path.basename(event.src_path))
def msg_to_bytes(msg):
return str(msg).encode('utf-8')
def sendUDP(basename):
upd_ip = "127.0.0.1" #TouchDesigner側(udpin1)と合わせる
udp_port = int(7000) #TouchDesigner側(udpin1)と合わせる
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(msg_to_bytes(basename), (upd_ip, udp_port))
def monitor():
event_handler = MyWatchHandler()
observer = Observer()
observer.schedule(event_handler, '../images/', recursive=True)#監視するフォルダを指定する
observer.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
observer.stop()
observer.join()
def main():
monitor()
if __name__ == "__main__":
main()
subprocess部分
ここは去年のアドカレでも少し記述しました。同じように記述します。
import os
import subprocess
#オペレーターの名前は適宜変更
port = str(op('udpin2').par.port.val)
file = '{}/images/'.format(project.folder) + str(op('fileName').text)
#自分の環境のPythonを指定
cmd_python = 'C:/Users/[user]/AppData/Local/Programs/Python/Python39/python.exe'
#実行するpythonファイルを指定
cmd_python_script = '{}/scripts/print.py'.format(project.folder)
python_args = [cmd_python, cmd_python_script]
#OSCに使うポート番号と、印刷するためのファイルパスを引数で渡す指定
script_args = ['-p', port, '-f', file]
cmd_args = python_args + script_args
op('udpin2').par.clear.pulse()
#ここでプロンプトのウィンドウを非表示にする設定
si = subprocess.STARTUPINFO()
si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
#コマンド実行
subprocess.Popen(cmd_args, startupinfo=si)
印刷のプログラム
下記の記事等を参考にpythonから印刷するコードを書いて実行するだけです。
その際にport番号やOSCの送受信の記述も入れています。
実行するとwindowsのシステムで既定になっているプリンターのキューに入るようになっています。
以上で終わりです。
よいお年をー😀