Introduction

Knobs are the fundamentals of all user interface elements available to NUKE Ops. They provide mechanisms for user interaction in both the Viewer and the Properties panels, along with storing and accessing data in the NUKE script file.

In previous sections, we’ve come across various subsets of the knobs on offer and their capabilities. In this section we’ll take a look at the full range of built in knobs offered, their usage and customisation options, and then move on to building custom knobs from scratch.

Knob Classifications

Knobs fall into three broad categories:

  • Those that use data storage initialized in the operator itself (for example the standard set of data types such as Int_knob)

  • Those that use no data storage (for example Tab_knob and other layout knobs)

  • More complex and recent additions which manage their own internal data storage (for example Table_knob)

Using the first type involves declaring appropriate data storage as a member variable of the Op, initialising it in the Op constructor and then passing it by address on knob creation. The member variable is subsequently synchronised with the interface value before multithreaded data access calls, such as engine(). At other times the knob object itself can be accessed using knob(“<name>”), though bear in mind this should be done only from calls in the main thread. Note that the knob does not directly employ the variable passed for internal storage, rather it is used during the knob’s call to set the default value and for synchronization.

The second type is obviously pretty simple - call the appropriate knob constructor and you’re good to go.

Working with the final type is generally fairly specific to the knob in question, but normally follows the same basic form. All of these knobs have a ‘<knobname>I.h’ header file associated with them which provides the various methods that particular knob offers. Each of these uses a knob pointer dependent on class, which is obtained from the knob pointer using a knob-><knobname> call. Most of these knobs need to be configured on creation, so you’ll often need to use a snippet similar to the following:

Knob* tableKnob = Table_knob(f, "Table_knob");
if (f.makeKnobs()) {
 Table_KnobI* tableKnobI = tableKnob->tableKnob();
 tableKnobI->addColumn("col1", "col1", Table_KnobI::FloatColumn, true);
 tableKnobI->addColumn("col1", "col1", Table_KnobI::FloatColumn, true);
}

Here the Knob_Callback (named f) is used to determine if the knobs call is in creation mode, and then only when in this mode is the Table_knob object manipulated.

Knob Naming

All knobs, with the exception of one or two layout types, take a name char* on construction. This means the knob can be:

  • Accessed later on via knob(<name>) calls

  • Used to identify the values written to and read from a NUKE script

  • Used as the name NUKE’s various scripting languages access the knob by

An additional ‘label’ char* can be provided so that the interface panel can use a different string to the full name (generally for brevity). In the NUKE interface, the name is obtained by hovering over a control - the first entry on a tooltip is always a bold formatted name for that knob.

Good practice when creating knob names includes:

  • The knob name must be unique. Naming multiple knobs the same thing on an Op results in all sorts of weirdness.

  • Spaces in knob names may cause delimiting issues when addressed using scripting.

  • Most of NUKE’s other controls are named using lowercase with an underscore word delimiter, so it’s generally worth following this convention to provide the easiest and most accessible interface to users.

  • Interface labels also generally follow the lowercase convention, although with actual spaces where required.