Building Control Panels
This tutorial will give an introduction to building control panels in TouchDesigner. It will cover the panel building toolset and go through some examples of applying these tools. A video tutorial on building control panels can also be found on the forum.
NOTE: The language here is TScript. The python equivalents are forthcoming.
The Panel Toolset
There are 5 Panel Components used in the construction of TouchDesigner control panels. They are found under the 'COMP' section of the OP Create Menu. Press <Tab> and select 'COMP', you will see the Panel Components at the top of the list in dark gray. They are: Button, Slider, Container, Field, Select.
All control panel gadgets (the Panel Component types) have a list of states represented by what are called "Panel Values". See the Panel Value page for the full list of the panel values.
It is the user's interaction with the gadgets that sets all the panel values.
The panel values can be accessed in several places:
- a Panel CHOP, which you can put inside any gadget, where you can also see the list of possible panel values for that gadget. Put a Panel CHOP in a Button component, display the viewer of the button component and watch the panel values in the Panel CHOP change.
panel()expression, where you can query the state of a variable.
- Middle-click on a panel component shows the names and values of all the panel values.
- Panel Execute DAT allows you to trigger a script based on the changes or states of any panel value. Put a Panel Execute DAT in a Button COMP and look in the Panel Value parameter menu to see the list of values. Select a Panel Value to use as a trigger to execute the script.
The Panel CHOP works in conjunction with Panel Components by grabbing all the Panel Values and outputs them as CHOP channels. Exporting these channels to parameters is the most straightforward way to connect custom UI panels to various operator parameters in the project.
The Panel CHOP only has 3 parameters:
Component - specifies the component that the Panel CHOP is retrieving values from. You can drag and drop any panel component onto this parameter. If this path is left blank, it defaults to the Parent component.
Select - selects which values to output. The default is '*', meaning 'all values'. You can select a subset by using the drop-down menu on the right, or by typing in the names of the values you are interested in. Pattern Matching can also be used to select multiple values.
Rename - Allows you to rename selected channels.
Panel Component Parameters
All Panel Components have 3 common parameter pages used to create a control panel. They are the Layout page, the Panel page, and the Color page. Additionally, the Button, Field, and Slider components have an addition parameter pages (of the respective name) to contain parameters specific to that panel type.
Building UI Gadgets
Momentary and Toggle Buttons
Buttons are the simplest form of UI gadget and a good place to start. The Button component is used to build momentary, toggle and radio buttons. Let's start with a simple momentary button.
- Create a Button component from the OP Create Menu. Press <tab> to open the OP Create Menu and select COMP>Button. A Button component called button1 will be created.
- Look at button1's parameters by pressing 'p' or right-clicking on it and selecting 'Parameters...' from the menu. The default width (W) and height (H) are 100. To look at the button panel, right-click on button1 and select Open Control Panel... The panel will open in a floating window that is 100x100 pixels in resolution.
- The background color of the button can be set on the Color parameter page. Set Background Color to 0.125, 0.125, 0.125, and the Background Alpha parameter to 1.0. You can also give the button a border here. Set the Border A Color 0.275, 0.275, 0.275, and the Border A Alpha to 1.0. Then set the 1st column of the top, bottom, left, and right border parameters to Border A, this assigns the color defined in Border A to the outside edges.
- Any image in TOPs can set as the background of the button using the Background TOP parameter. Temporarily place a default Movie File In TOP here. Create a Movie File In TOP by pressing <tab> and select TOP>Movie File In from the create OP menu. Now Drag and Drop the new TOP onto the Background TOP parameter of button1. The image from moviefilein1 will appear in button1's background.
- Now to make the button change color when clicked on. Go inside button1's network. Do this by selecting the button1 and pressing <enter> or < i >.
- Create a Text TOP (press <tab> and select TOP>Text). The Text TOP is useful for making UI gadgets as it can be used to add color, borders, and text to elements. To define the button "off" and "on" looks, two TOPs are required. Rename the first Text TOP created to buttonoff, then create a second and rename it to buttonon.
- Let's make the button 100x25 pixels in size. Since these TOPs will define the background of the button, the TOPs should be 100x25 pixels as well. Go to the Common page of parameters in each TOP and set Custom Res to 100,25. The button panel itself is still 100x100 pixels. To change this go back up a level by pressing < u >, then change button1's Height parameter to 25. Jump back into button1's network by selecting it and pressing <enter> or < i >.
- Setting the look of buttonoff. Go to the Color page, then set Font Color to 0.625,0.625,0.625 and Font Alpha to 1.0. Now enter the word "Off" in the Text parameter on the Text page.
- Setting the look of buttonon. On buttonon's Color page, set Font Color to 0.625,0.625,0.625 and Font Alpha to 1.0. Then set Background Color to 0.1,0.1,0.1 and Background Alpha to 0.1. Enter the word "On" in the Text parameter.
- To make the button panel switch between "off" and "on", we need to switch between buttonoff and buttonon using a Switch TOP. Create a Switch TOP called switch1 (press <tab> and select TOP>Switch), connect buttonoff to its first input and buttonon to its second input.
- Set the background of button1 to use these new TOPs. First append a Null TOP to the output of switch1, and rename it to bg. Now return to button1's Panel page of parameters and set the Background TOP parameter to "./bg". The ./ is a relative path meaning "inside this component".
To get interaction from button1, a Panel CHOP is needed to get the panel's values as CHOP channels. While inside the button1 network, use the OP Create menu and select CHOP>Panel. By default, the Panel CHOP (called panel1) will refer to its Parent Component, so in this case it is already referring to button1.
View panel1s CHOP channels. Click on the Node Viewer flag on panel1. This will turn the node's icon into a small CHOP viewer. panel1 may need to be resized larger to view all the channels. Grab the CHOP's upper-right corner (a resize cursor will appear) and drag it until the viewer is big enough to read all the channel names.
The channels will update when you move, drag and click the cursor over button1's open control panel. NOTE: TouchDesigner must be playing forward.
Clicking on the button, you will notice that the select and state channels go to 1 with each click. Using one of these channels, but button look can be switch between the buttonoff and buttonon looks whenever the button is clicked.
Inside button1 create a Switch TOP by using the OP Create menu and selecting TOP>Switch. Connect the buttonoff TOP to switch1's first input, and buttonon to it's second input.
Connect button1's state value to switch1.
When clicking on the button panel, notice the state channel in panel1 go to 1 each time the mouse is clicked. To export panel1s state channel to switch1, append a Null CHOP to panel1.
Right-click on null1 and select CHOP Exporter... from the menu. The CHOP Exporter dialog will open. Drag state from the CHOP Exporter onto switch1s Index parameter. When the channel is dropped onto the parameter a pop-up box will appear, select Export CHOP.
The exported channel can be confirmed by the dotted line now extending from null1 CHOP to switch1 TOP. The Input parameter in switch1 will also be highlighted blue. Clicking the button1 panel now changes switch1s Input parameter to 1 and the TOP image to its second input, which is buttonon.
Notice that the button1 open control panel does not update yet. This is because we need to specify the switching output as the Background TOP. Append a Null TOP to switch1, rename the Null TOP to bg.
Go back up to the button1 Component (press < u >). Set the Background TOP parameter to ./switch1 (you can also Drag and Drop switch1 here).
The button will now update it's 'look' each time you click on it. Below we have the button off (left) and on (right).
Getting the channels out of the Component.
Inside button1s network, append an Select CHOP to panel1. Since only the state channel for button1 is needed, select state from the Channel Names parameter menu (or simply type state into the field). Now in the Rename To field, enter $OPN. $OPN is a variable that holds the parent's name. By using $OPN here, the channel name is renamed to the parent component's name, button1. This is handy when duplicating buttons, as each button will have a distinct channel name. Append an Out CHOP to the Select CHOP.
Now go back up to the button1 Component (press < u >). There will now be a CHOP output on the right side of the button1. Append a Null CHOP to this output and turn on its Node Viewer. Use this to connect button1s state to any parameter you like. Using an output like this keeps the button encapsulated nicely as a re-usable component, without the need to go inside and grab channels out. Components were designed for encapsulation and re-usability.
Changing the button from momentary to toggle.
This button is currently a momentary button, but it is easy to make it a toggle. Go to the Button page of parameters for button1. Change the Button Type menu from Button Momentary to Button Toggle Down. Button1 is now a toggle button!
NOTE: Button Toggle Down activates on pressing the button down. Button Toggle Up activates on releasing the button up.
Sliders are another commonly used UI gadget. Use the Slider Component type for building sliders and knobs, this example will build a basic slider.
- Create a Slider Component. Press < tab > to open the OP Create menu and select COMP>Slider. A Slider Component called slider1 will be created.
- This slider example will be a vertical slider. On slider1s Layout page of parameters, set the width and height to W = 25 and H = 100. Then on the Slider page, set Slider Type to Slider V.
Start by defining the background of the slider. On the Color page of parameters, set Background Color to 0.2,0.2,0.2, and Background Alpha to 1.0.
To create an outline, set Border A color to 0.6,0.6,0.6 and Border A Alpha to 1.0. To use Border A color on the outside border, set the first menu of the Left, Right, Bottom, and Top parameters to Border A.
Now to create the part of the slider that moves with the value. Go inside slider1 and create a Text TOP. Remove the default text ("derivative") from the Text parameter on the Text page.
Set the Custom Res parameter on the Common page to 25,100.
On the Color page of text1, set the Background Color to 0.4,0.4,0.4 and he Background Alpha to 1.0. Also set Border A color to 0.6,0.6,0.6 and Border A Alpha to 1.0, and set the first menu of the Top parameter to Border A.
To get the slider value, the cursor's vertical position when it is over the slider panel is needed. Open the slider1 control panel by right-clicking on slider1 and selecting Open Control Panel...
Now go back inside the component and create a Panel CHOP. Set it's Select parameter to v. Turn on the Panel CHOP's (called panel1) Node Viewer and the panel value v will be displayed. Now click and drag the cursor vertically over the slider1 control panel and watch the v channel update.
Animating the slider's level. The image in text1 must move with the panel value v. Append a Transform TOP to text1.
Now append a Null CHOP to panel1. null1 will be used to export the CHOP channels. Open the CHOP Exporter by right-clicking on null1 and selecting CHOP Exporter... from the menu.
Drag and Drop the v channel from the CHOP Exporter to transform1s Translate Y (ty) parameter. Select Export CHOP from the menu that pops up when you drop the channel onto the parameter.
Adjusting the movement. Dragging the cursor over the slider1 panel will drag the level up and down. Notice that the bottom of the text1 TOP lines up with the cursor instead of the top part of text1s image. As the cursor goes from bottom to top of the slider, the ty value in transform1 goes from 0 to 1. To line up the top part of text1 with the cursor, we need to offset this by -1.0, so the ty value goes from -1 to 0.
Insert a Math CHOP in between panel1 and null1 by right-clicking on the output of panel1 and selecting CHOP>Math from the OP Create menu. Go to the Range parameter page of the new math1 CHOP and set the To Range to -1,0. Now the slider properly follows the cursor's location.
Getting the channel out of the component. To access the slider's value without going inside the component, a CHOP output needs to be added.
Inside slider1s network, middle-click on the output of panel1 and create a new Out CHOP. Notice that by using middle-click, the added OP starts a new branch from panel1. Using right-click would have placed the OP inline between panel1 and math1. This is very helpful for creating new branches in your networks.
Set panel1s Rename parameter to $OPN. This simply renames the channel from v to the parent component's name, slider1. Go back up to the slider1 component (press < u >). You will see the component now has a CHOP output that has the slider's value in it. You can connect this wherever you need slider1s value.
The Container Component
The Container Panel Component can be used for layout and grouping of UI gadgets. Starting with some simple UI gadgets, a full control panel can be build inside a Container COMP. This example will create a panel of control gadgets with 2 sliders and 1 button.
- Create a Container Component from the OP Create Dialog. Press < tab > and select COMP>Container. A Container Component called container1 will be created, rename it to controls.
- Copy slider1 component created in the Slider section above and paste it inside the controls network (or get a vertical slider from the Palette under Derivative/UI/Sliders/SliderV.tox). Now open controls control panel by right-clicking on the controls node and selecting Open Control Panel... The slider will appear in the control panel's lower left corner.
- For this panel, make slider1 twice the size. On the Layout parameter page, set Width = 50 and Height = 200. Now copy & paste slider1 to create a second identical slider called slider2.
- Move the slider's position. The second slider will have the same position as the first, so it will not be visible in the control panel yet. Move slider2 over 50 pixels by setting its X parameter to 50. Both sliders will now be beside each other in the control panel.
- Copy button1 component created in the Button section above (or get a momentary button from the Palette under Derivative/UI/Buttons/ButtonMomentary.tox). Again, the button will be behind the sliders, so move it up in Y until it is visible. Set button1s Y parameter to 200.
- Modify the button make it play/stop the Timeline. First change the label of button1. Go inside button1 and change the Text parameter for buttonoff to "Play", and change the Text parameter of buttonon to "Stop". Also make sure that
button1is set to be a toggle button by going to the Button page of parameters and setting Button Type to Toggle Down.
Add the Play script. Create a Text DAT from the OP Create menu by pressing < tab > and selecting DAT>Text. Rename the DAT to play. On the Execute parameter page, change the Execute parameter menu to On Panel Change. This tells the Text DAT to run the script it contains when the Panel Value changes. The Panel Value state is being monitored as specified in the Panel Value parameter, and the script will run when that value goes Off to On (0 to 1), as specified by the checkboxes. Leave these defaults as is. Add the script to play by double-clicking in the node viewer and typing. The script required to starting the Timeline moving forward is:
Test this by pressing the button in the control panel.
Now add the Stop script. Copy and paste the DAT called play and rename it to stop. On the Execute parameter page, uncheck the Off to On box and turn on the On to Off box. This script will now execute when the button turns off (1 to 0). Edit the script to stop by double-clicking in the node viewer and typing. The script required to stop the Timeline is:
Resize controls to trim the unused space. There is unused space above out 3 gadgets which is not needed. The horizontal width of the slider is 100 pixels (slider = 50 *2). Set controls Width parameter to 100. The vertical height of the 3 gadgets is 225 pixels (slider = 200 + button = 25). Set controls Height parameter to 225. Re-open the control panel. This control panel can be used as part of a larger panel or standalone by itself. You can open a floating window of controls using the script:
controlpanel -o controls
Finishing up. To make the controls panel easier to use and connect to networks, we will create a CHOP output which gives access to the values for the sliders and button.
Enter the controls network. Create a Merge CHOP and connects the outputs from each UI gadget to merge1s input. Now look at the operator info for merge1 (middle mouse click on the CHOP), you can see the 3 channels called button1, slider1, and slider2. Append an Out CHOP after merge1. Go back up to the top level (press < u >). There is now a CHOP output on the right side of the controls component. Add a Null CHOP to this output and turn on it's node viewer. You can watch the 3 channels update when you interact with the button and sliders.
Creating a Viewer
The background of any control panel can originate from a TOP image using the panel component's Background TOP parameter. This makes it easy to add a viewer to any panel.
- Create a Container COMP by pressing < tab > and selecting COMP>Container. Rename the component to viewer1.
- This viewer will have an aspect ratio of 4:3. Using the current height (H=225) of our control panel built in the previous examples, we need a width of 300 (300:225 = 4:3). Set viewer1s W parameter to 300, and it's H parameter to 225.
- For this example, a quicktime movie will be used to fill the main viewer. This can be changed to a 3D render, or any other TOP image. Create a Movie File In TOP, it will be called moviein1. Load a 4:3 movie of your choice into the File parameter. In this example I used the small movie available here: QuickTime-Kalevad-Small. This movie's resolution is 240x180.
- Go inside the viewer1 network. Create an In TOP (in1) by pressing < tab > and selecting TOP>In. Append a Null TOP to in1 and rename it to bg (short for background). This TOP will be assigned to be the background image of viewer1. Go back up a level and set viewer1s Background TOP parameter to "./bg".
- Now plug moviein1 into the TOP input of viewer1. Whatever TOP is connected to the input will be displayed in the viewer panel.
- To create an outline, set Border A color to 0.6,0.6,0.6 and Border A Alpha to 1.0. Then set the first menu of the Left, Right, Bottom, and Top parameters to Border A.
There are a number of variables and expressions that are useful when building control panels. These can be used in parameters and expressions to rename, reposition, or resize UI gadgets.
$ON - operator name > if opname = movieIn1 then $ON = movieIn1 $OD - operator digits > if opname = movieIn1 then $OD = 1 $OB - operator base > if opname = movieIn1 then $OB = movieIn $OPN - operator parent name > if parent opname = container1 then $OPN = container1 $OPD - operator parent digits > if parent opname = container1 then $OPD = 1 $OPB - operator parent base > if parent opname = container1 then $OPB = container $C - expands to $TOUCH/C
opdigits (opname) - This function will return the numeric value of the last set of consecutive digits in a node's name. It is used when building several similar networks. This is similar to $OD and $OPD above, but with opdigits you can reference any operator by path.
Usage - float opdigits (string name)
opdigits("/obj/geo1") = 1 opdigits(".") = same as $OD, to get digits of current OP opdigits("..") = same as $OPD, to get digits of parent opdigits("../..") = to get digits of parent's parent
opname (opname) - This function will print the name of the node given. This is similar to $ON above, but with opname you can reference any operator by path.
Usage - string opname (string name)
opname("/container1/geo1") = geo1 opname(".") = name of operater that the opname(".") expression is in. opname("..") = name of the parent node of the operator that the opname("..") expression is in.
The Override CHOP
Every panel-type component has a Override CHOP parameter on its Panel page. Putting a CHOP path here (Drag and Drop it or type in the path) allows you to override a gadget's Panel CHOP channels. This Override CHOP's value directly affects the state, ustate, and vstate channels.
The Override CHOP only overrides when its value changes. It does not override while its value is constant. You can hook this up to devices like the mouse or MIDI.
The Help DAT Parameter
Every panel-type component also has a Help DAT parameter on its Panel page. You can drag and drop any Text DAT onto this parameter and it will provide a help message when the cursor sits over this panel for 3 seconds.
Advanced Tip Examples
When creating an array of buttons or sliders, individual placement of each UI gadget can be very repetitive and time consuming. Using $OD, this can be automated. From the slider example created above, notice that it's width is W = 25. Therefore each successive slider needs to be moved over in X another 25 pixels.
In slider1s X parameter, enter
This will evaluate as 1*25 = 25 for slider1, 2*25 = 50 for slider2, etc.
Since this is the first slider, we want it at X = 0, so subtract 25 in the expression giving $OD*25-25. This will give X = 0 for slider1, X = 25 for slider2, X = 50 for slider3, and so on...
Alternatively, slider0 could be the name of your first slider and leave the expression as $OD*25.
Instead of using $OD in this example, `opdigits(".")` could also be used.
Assume you have a button component called button1 and would like the label on the button to be the same as the name of the component. Using $OPN, the button can be duplicated (using Copy & Paste) and the new buttons will be automatically renamed.
From the button example above, have look at the Text TOPs inside that define the buttonon and buttonoff look. Enter $OPN into the Text parameter for both buttonon and buttonoff (Set your font size and color to your liking).
Using $OPN here will set the Text TOP's label to the name of their parent, which is the button name, button1. Each time this button is duplicated, (by Copy & Pasting) the name is incremented and the Text TOP's text acquires this new name. This automates having to manually enter simple names when making a strip of buttons.
Instead of $OPN in this example, `opname("..")` could also be used.
Building an array of buttons using $OD and $OPN
This example will step through building a 4-button panel using the two tips mentioned above.
- Create a Container Component, rename it to buttonpanel. Open the control panel by right clicking on the component and selecting Open Control Panel...>Native Size...
- Copy a button1 component from the button example above and paste it inside buttonpanel (alternatively grab a button from the Palette). Rename this button to button0. You should now see the button0 in buttonpanels floating panel window.
- Go inside the button0s network. Open the buttonoff TOP's Text page of parameters and enter $OPN in the Text parameter. Then on the Font page, set Display Method to Bitmap and Font Size X = 14. Now do the same for the buttonon TOP.
- Go back up one level to buttonpanels network. Open button0s parameters. Notice that button0 is 100 pixels wide (set by the W parameter). Therefore, each successive button should be translated over 100 pixels in X by the expression. Use $OD for this by creating the following expression in the X parameter: $OD*100 Since the first button is renamed to button0, $OD*100 = 0*100 = 0.
- Duplicate the button. Copy and paste the button 3 times, creating the new buttons button1, button2, and button3.
- Create a Merge CHOP and connect all 4 button component's outputs to it's input. Then append an Out CHOP to the Merge CHOP. Go up a level to look at the buttonpanel component. Append a Null CHOP to the CHOP output on the right side of the component and the 4 channels from the buttons will be visible and ready to use.
Putting it all together
Now combine the controls and the viewer together in to a complete control panel.
- Create a new Container COMP and rename it to controlpanel. Inside controlpanel, add the controls and viewer1 components build in the sections above. Also copy the moviein1 TOP input for viewer1 and then null1 CHOP output for controls.
- Adjust controlpanels H parameter to 225 to properly fit the gadgets inside. Notice viewer1 is sitting on top of controls. Move viewer1 over by the width of controls by setting viewer1s X parameter to 100.