This tutorial covers how to use Python callbacks for event-driven programming in TouchDesigner, allowing you to execute code based on specific events from operators.
What Are Callbacks?
Callbacks are Python functions that run automatically based on events from a docked operator. When you add certain operators like a Timer CHOP to your network, you’ll notice a small purple square at the bottom right corner. Clicking on this reveals a docked DAT containing callback functions.
These callbacks include functions like:
onInitialize- Runs when the timer is initializedonReady- Runs when the timer is ready to startonStart- Runs when the timer startsonCycle- Runs each time the timer completes a cycle
Whatever code you place inside these functions will execute when the corresponding event occurs.
Setting Up the Example
Creating the Network
- Add a Timer CHOP - Use the Tab key to open the OP Create dialog and add a Timer CHOP
- Add a Text TOP - Navigate to TOPs and add a Text TOP
- Add a Noise TOP - Connect the Text TOP to a Noise TOP
- Configure the Noise TOP - Turn off monochrome and set the period to 10 for broad color swaths
Editing Callbacks in a Text Port
To edit callbacks more conveniently without zooming into your network:
- Split your view and change one pane to a Text Port
- Drag the callback DAT (the docked operator) to the Text Port
- Drop it where it says “textport” and select “Open the DAT”
Now you can edit all callbacks without making the operator viewer active.
Configuring the Timer
- Turn on Cycle mode
- Turn off Cycle Limit
- Set the Length to about 2 seconds
- Initialize and start the timer to verify it restarts every 2 seconds
Writing Callback Code
The onCycle Callback
Every time the timer completes a cycle, we can update operators in our network:
def onCycle(timerOp, cycle, interrupt):
# Update the text display
op('text1').par.text = 'The current cycle is ' + str(cycle)
# Change the noise seed
op('noise1').par.seed = cycle
Key points:
- Use
op('operatorName')to reference operators in your network - Access parameters with
.par.parameterName - The
cyclevariable is an integer, so usestr()to convert it for string concatenation
The onReady Callback
Set up the initial state when the timer is ready:
def onReady(timerOp):
op('text1').par.text = 'Ready'
The onStart Callback
Execute code when the timer starts:
def onStart(timerOp):
op('text1').par.text = 'Start'
The onInitialize Callback
Reset everything to a clean state when initializing:
def onInitialize(timerOp):
op('text1').par.text = 'Ready'
op('noise1').par.seed = 0
This ensures consistent state every time you initialize the timer.
Why Use Callbacks Instead of Channel Outputs?
While you could use the Timer CHOP’s channel outputs directly, callbacks offer advantages:
- Event-driven logic - Code runs exactly when events occur, not continuously
- Cleaner state management - Set up initialization and reset conditions clearly
- Better organization - Separate code for different events (start, cycle, etc.)
- Consistent state - Ensure your project starts in a known state every time
Summary
Callbacks provide a powerful way to implement event-driven programming in TouchDesigner:
| Callback | When It Runs |
|---|---|
onInitialize | When the timer is initialized |
onReady | When the timer is ready to start |
onStart | When the timer begins running |
onCycle | Each time the timer completes a cycle |
This approach helps you think about setting up conditions for startup and cycling, ensuring your project’s state is consistent every time you run an operation. This is just the tip of the iceberg for what you can accomplish with callbacks in TouchDesigner.
本教程介绍如何在TouchDesigner中使用Python回调进行事件驱动编程,让你能够根据操作符的特定事件执行代码。
什么是回调?
回调是基于停靠操作符的事件自动运行的Python函数。当你向网络中添加某些操作符(如Timer CHOP)时,你会注意到右下角有一个小紫色方块。点击它会显示一个包含回调函数的停靠DAT。
这些回调包括以下函数:
onInitialize- 当计时器初始化时运行onReady- 当计时器准备好启动时运行onStart- 当计时器启动时运行onCycle- 每次计时器完成一个周期时运行
你放在这些函数中的任何代码都会在相应事件发生时执行。
设置示例
创建网络
- 添加Timer CHOP - 使用Tab键打开OP创建对话框并添加Timer CHOP
- 添加Text TOP - 导航到TOPs并添加Text TOP
- 添加Noise TOP - 将Text TOP连接到Noise TOP
- 配置Noise TOP - 关闭单色模式并将周期设置为10以获得宽广的色彩范围
在文本端口中编辑回调
为了更方便地编辑回调而无需放大网络:
- 分割视图并将一个面板更改为文本端口
- 将回调DAT(停靠的操作符)拖到文本端口
- 将其放在显示”textport”的位置并选择”打开DAT”
现在你可以编辑所有回调而无需激活操作符查看器。
配置计时器
- 打开循环模式
- 关闭循环限制
- 将长度设置为约2秒
- 初始化并启动计时器以验证它每2秒重新启动
编写回调代码
onCycle回调
每次计时器完成一个周期时,我们可以更新网络中的操作符:
def onCycle(timerOp, cycle, interrupt):
# 更新文本显示
op('text1').par.text = 'The current cycle is ' + str(cycle)
# 更改噪声种子
op('noise1').par.seed = cycle
关键点:
- 使用
op('operatorName')引用网络中的操作符 - 使用
.par.parameterName访问参数 cycle变量是整数,所以使用str()将其转换为字符串以进行连接
onReady回调
当计时器准备好时设置初始状态:
def onReady(timerOp):
op('text1').par.text = 'Ready'
onStart回调
当计时器启动时执行代码:
def onStart(timerOp):
op('text1').par.text = 'Start'
onInitialize回调
初始化时将所有内容重置为干净状态:
def onInitialize(timerOp):
op('text1').par.text = 'Ready'
op('noise1').par.seed = 0
这确保每次初始化计时器时状态一致。
为什么使用回调而不是通道输出?
虽然你可以直接使用Timer CHOP的通道输出,但回调提供了以下优势:
- 事件驱动逻辑 - 代码在事件发生时精确运行,而不是持续运行
- 更清晰的状态管理 - 清晰地设置初始化和重置条件
- 更好的组织 - 为不同事件(启动、循环等)分离代码
- 一致的状态 - 确保项目每次都从已知状态启动
总结
回调提供了在TouchDesigner中实现事件驱动编程的强大方式:
| 回调 | 运行时机 |
|---|---|
onInitialize | 当计时器初始化时 |
onReady | 当计时器准备好启动时 |
onStart | 当计时器开始运行时 |
onCycle | 每次计时器完成一个周期时 |
这种方法帮助你思考如何设置启动和循环的条件,确保每次运行操作时项目状态一致。这只是TouchDesigner中回调功能的冰山一角。