Integer
Float
Integer Vector
Float Vector
Toggle
Channel Mask
Button
Menu
String
File
Picture File
Inputs are usually grouped into tabs, like this:
The Info tab is standard to all nodes, and appears by default - you don't have to define it. Any other tabs need to be added before the parameters that appear in them. There must be at least one tab. So the minimum code for a node that has only one parameter would be:
{ add a tab add the parameter }To do multiple tabs, the code would be structured as:
{ add tab 1 add parameter 1 add parameter 2 add parameter 3 add tab 2 add parameters add tab 3 add parameters }The routine used to define the node UI is
CPIDSOEXPORT void upiCreateParameters(void) { }This routine is called whenever a node is created and placed on the work area. The routines used to add the given parameters are as follows:
Usage: cpiAddInteger(char *label, int default, int min_val, int max_val, int flags);
Example: cpiAddInteger("Integer", 1, 0, 10, P_ANIMATE);
Result:
Usage: cpiAddFloat(char *label, float default, float min_val, float max_val, int flags);
Example: cpiAddFloat("Float", 1.0, 0.0, 10.0, P_ANIMATE);
Result:
Usage: cpiAddIntVector(char *label, int size, int *default, int min_val, int max_val, int flags);
Example: int idef[] = {1, 150}; cpiAddIntVector("Int Vector", 2, idef, 1, 100, 0);
Result:
Usage: cpiAddFloatVector(char *label, int size, float *default, float min_val, float max_val, int flags);
Example: float fdef[] = {-0.5, 3.1415}; cpiAddFloatVector("Float Vector", 2, fdef, 0.0, 1.0, 0);
Result:
Usage: cpiAddToggle(char *label, int default, int flags );
Example: cpiAddToggle( "Toggle", 0, 0);
Result:
Usage: cpiAddMenu(char *label, int default, char *menu[], int flags);
Example: char *mdef[] = {"a choice", "another choice", "default choice", NULL}; cpiAddMenu( "Menu", 2, mdef, 0);
Result:
Usage: cpiAddChannelMask(char *label, int default, int flags );
Example: cpiAddChannelMask("Channel Mask", 0x0002, 0);
Result:
Usage: cpiAddButton(char *label);
Example: cpiAddButton("Button");
Result:
Usage: cpiAddString(char *label, char *default, int flags );
Example: cpiAddString("String", "default value", 0);
Result:
Usage: cpiAddFile(char *label, char *default, int flags );
Example: cpiAddFile("File", "default file", 0);
Result:
Usage: cpiAddPicFile(char *label, char *default, int flags );
Example: char sdef[] = {"/a/picture/file.pic"}; cpiAddPicFile("Picture File", sdef, 0);
Result:
cpiAddTab( "Window" ); cpiAddInteger( "Center", 1, 0, 150, P_MIN | P_ANIMATE ); cpiAddInteger( "Width", 5, 0, 150, P_MIN | P_ANIMATE );Then when you want to get the value of the "Center" field, you will do this
cpiGetInteger( "Center", result->time, ¢er )This tells the software to get the value associated with the field you called "Center" at the current time. We will cover this in depth in a moment.
Note:
The following words are reserved by Chalice
for the Info tab of all nodes, and should not be used by your plugin:
framerange
imageresolution
raster depth
channels
cache size
preview cache hold
comment
One way around this is to add a blank space to the end of your
chosen name - for example, if you want to have a field called "channels"
in your dialog, you can name it "channels " (trailing space, no quotes)
and the interface will work properly.
The default parameter is just that, a value to initialize the gadget
with when it first appears.
min_val and max_val are values to put at the ends
of the slider associated with the gadget. If the appropriate flag is
set (see below), then these values are enforced - if not, they just
appear as a guide to the user of a reasonable range of values.
flags may be any of the following:
P_MIN enforce the minimum value as a hard limit P_MAX enforce the maximum value as a hard limit P_MINMAX enforce both min and max values as hard limits P_ANIMATE this parameter may be animated. If this flag is not set, the grey STED box will not appear next to this parameter, and its value will apply across all frames. P_HIDDEN this parameter will not appear in the node interface, but it will be available internally to the node, and its value(s) will be saved to the .grail file when it is written. This can be usefull for allocating extra parameters which users will not be changing; the track node, for example, has hidden flags set up for internal use, which you can see if you allocate a track node and then open up the STED on its parameters.Flags may be combined with a logical 'OR' (|) to set more than one flag.
Type Routine Integer int cpiGetInteger( char *param, float time, int *value ); Float int cpiGetFloat( char *param, float time, float *value ); Integer Vector int cpiGetIntVector( char *param, float time, unsigned int element, int *value ); Float Vector int cpiGetFloatVector( char *param, float time, unsigned int element, float *value ); Toggle use cpiGetInteger() - a return value of 0 means the toggle is not set, 1 means it is. Menu use cpiGetInteger() - the result is the index into the menu array that was passed when the menu was defined. Indices begin at 0. Channel Mask use cpiGetInteger() - bits are set depending on whether the corresponding channel button was depressed. Bits start at the low end, so (value & 0x01) = red (value & 0x02) = green (value & 0x04) = blue (value & 0x08) = alpha etc Button Buttons don't return a value, they cause upiParameterChanged() to be executed, with the name of the button passed in as an argument. String int cpiGetString( char *param, float time, char *buffer ); File use cpiGetString() Picture File use cpiGetString()
#include <stdlib.h> #include <stdio.h> #include <limits.h> #include <memory.h> #include <string.h> #include "cpi.h" // some constants to make the code more readable #define FIRST_ELEM 0 #define SECND_ELEM 1 #define MAX_STRING 256 // define the user interface tabs and gadgets CPIDSOEXPORT void upiCreateParameters( void ) { int idef[] = {1, 150}; float fdef[] = {-0.5, 3.14159}; char *mdef[] = {"a choice", "another choice", "default choice", NULL}; char sdef[] = {"/a/picture/file.pic"}; // define first tab cpiAddTab( "some stuff" ); cpiAddInteger( "Integer", 1, 0, 10, P_ANIMATE ); cpiAddFloat( "Float", 1.0F, 0.0F, 10.0F, P_ANIMATE ); cpiAddIntVector( "Int Vector", 2, idef, 1, 100, 0); cpiAddFloatVector( "Float Vector", 2, fdef, 0.0, 1.0, 0); cpiAddToggle( "Toggle", 0, 0); cpiAddButton( "Button" ); // define second tab cpiAddTab( "more stuff" ); cpiAddString( "String", "default value", 0); cpiAddMenu( "Menu", 2, mdef, 0); cpiAddChannelMask( "Channel Mask", 0x0002, 0); cpiAddFile( "File", "default file", 0); cpiAddPicFile( "Picture File", sdef, 0); } // this routine is called by Chalice whenever a node parameter is // changed - it comes in with the name of the parameter that changed CPIDSOEXPORT void upiParameterChanged(char *name) { int ival, ivec[2]; float fval, fvec[2]; char string[MAX_STRING]; // you might just get all the values from the node, or you can // check which was changed and only get that/those if (!strcmp(name, "Integer")) cpiGetInteger("Integer", 0, &ival); else if (!strcmp(name, "Float")) cpiGetFloat("Float", 0, &fval); else if (!strcmp(name, "Int Vector")) { cpiGetIntVector("Int Vector", 0, FIRST_ELEM, &ivec[0]); cpiGetIntVector("Int Vector", 0, SECND_ELEM, &ivec[1]); } else if (!strcmp(name, "Float Vector")) { cpiGetFloatVector("Float Vector", 0, FIRST_ELEM, &fvec[0]); cpiGetFloatVector("Float Vector", 0, SECND_ELEM, &fvec[1]); } else if (!strcmp(name, "Toggle")) cpiGetInteger("Toggle", 0, &ival); // ival == 1 if toggle set else if (!strcmp(name, "String")) cpiGetString("String", 0, string); else if (!strcmp(name, "Menu")) // return an index into the menu array, starting at 0 cpiGetInteger("Menu", 0, &ival); else if (!strcmp(name, "File")) cpiGetString("File", 0, string); else if (!strcmp(name, "Picture File")) cpiGetString("Picture File", 0, string); else if (!strcmp(name, "Button")) do_button_function(); // get here if button pushed }The result of the above is the following dialog in Chalice:
void cpiWarning(char *format, ...) void cpiError(char *format, ...)cpiWarning() returns from the call and continues to process the image; cpiError() does not return. A warning turns the info button on the node to yellow; an error turns it to red.
cpiError("Must have alpha channel to proceed"); cpiWarning("Negative value (%d) not allowed", ival); cpiError("Can't open file %s", filename);cpiWarning() and cpiError() can only be called during the cooking of an image. Thus if you want to issue an error for bad input values, you will have to store the error and retrieve it during cook time, or postpone checking inputs until the image is cooked.
Example 1: Say you want to ask the user for a lookup table. Then you might have the following code fragments:
// user routine to load a lookup table void loadfile(char *filename) { FILE *fp; if (!strcmp(Curfile, filename)) /* if different, load new table */ return; if ((fp = fopen(filename, "r")) == NULL) { lookuptable.error = E_NOFILE; /* file missing - save error */ return; } if (readheader(fp)) { lookuptable.error = E_BADHEAD; /* bad header - save error */ return; } if (readluts(fp)) { lookuptable.error = E_BADLUT; /* bad table - save error */ return; } fclose(fp); strcpy(Curfile, filename); lookuptable.error = E_NONE; /* everything went fine */ } // define the UI to ask for a lookup table CPIDSOEXPORT void upiCreateParameters( void ) { char def[] = {"/usr/grail/chalice/support/luts/cineview.dlut"}; cpiAddTab( "Lookup Table" ); cpiAddFile( "Use File", def, 0); } // when input changes, try to load the lookup table - if we fail, save // that fact for later CPIDSOEXPORT void upiInputChanged( void ) { char filename[MAXSTR]; cpiGetString("Use File", 0, filename); loadfile(filename); } // now we are going to process the image, but first, check to see if // there are any errors to report. Another way of doing this would be // to wait until now to get the file name, and try to read it now. In // that case, cpiError() could be called directly from loadfile() CPIDSOEXPORT int upiProcessImage( CPI_Image *result ) { if (lookuptable.error == E_NOFILE) cpiError("Could not open lookup table"); else if (lookuptable.error == E_BADHEAD) cpiError("Bad header in lookup table"); else if (lookuptable.error == E_BADLUT) cpiError("Bad or missing table data"); ...process image... }Example 2: toggle the warning/error behavior - this example is provided as UIfeedback.C
#include <stdlib.h> #include <stdio.h> #include <limits.h> #include <memory.h> #include <string.h> #include "cpi.h" // some constants to make the code more readable #define TAB_NAME "Parameter Area" #define HOW_MANY "How Many" #define SEE_ERR "Show Error" #define LANGUAGE "Language" #define MAX_STRING 256 char *menu[] = { "Dutch", "English", "French", "German", NULL}; // define the user interface tabs and gadgets CPIDSOEXPORT void upiCreateParameters( void ) { // define parameters cpiAddTab( TAB_NAME ); cpiAddInteger( HOW_MANY, 1, 0, 10, P_ANIMATE ); cpiAddMenu( LANGUAGE, 1, menu, 0); cpiAddToggle( SEE_ERR, 0, 0); } // warning/error messages can only be sent during cooking, so // we check for errors in upiProcessImage() CPIDSOEXPORT int upiProcessImage(CPI_Image *result) { int count, choice, state; // get the parameter values cpiGetInteger(HOW_MANY, result->info.time, &count); cpiGetInteger(SEE_ERR, result->info.time, &state); cpiGetInteger(LANGUAGE, result->info.time, &choice); if (state) cpiError("Language is %s", menu[choice]); else cpiWarning("%d Sample(s)", count); return 0; }