User Tools

Site Tools


Node Property Sliders

Summary

Below is an example demonstrating how you can dynamically create sliders for specific properties associated with a given node (presented in the context of the node within the interface).

API Areas of Interest

Example

Node_Property_Sliders.dsa
// Define an anonymous function;
// serves as our main loop,
// limits the scope of variables
(function( bDebug ){
 
	// Define a 'static' variable that holds whether we can use Function.scriptConnect()
	var s_bScriptConnect = (typeof( Function.scriptConnect ) == "function");
	// Define a 'static' variable that holds whether we can use Global::connect() to
	// define 'this' within a function; i.e., 4.15.0.18 or newer
	var s_bGlobalMemberFuncConn = s_bScriptConnect ? App.version64 >= 0x0004000f00000012 : false;
 
	// Define a 'static' map of property-slider pairs
	var s_oSliderMap = {};
 
	/*********************************************************************/
	// String : A function for retrieving a translation if one exists
	function text( sText )
	{
		// If the version of the application supports qsTr()
		if( typeof( qsTr ) != "undefined" ){
			// Return the translated (if any) text
			return qsTr( sText );
		}
 
		// Return the original text
		return sText;
	};
 
	/*********************************************************************/
	// Array<DzProperty> : A function for getting a list of the properties in a group
	function getGroupProperties( oGroup, bTraverse, bRecurse )
	{
		// Declare an array to hold properties
		var aProperties = [];
 
		// If a group is not passed in
		if( !oGroup ){
			// We are done, return an empty array
			return aProperties;
		}
 
		// Get the number of proeprties in the group
		var nProperties = oGroup.getNumProperties();
		// Pre-size the properties array
		aProperties = new Array( nProperties );
		// Iterate over the properties, setting each element in the array
		for( var i = 0; i < nProperties; i += 1 ){
			// Assign the property to the position in the array
			aProperties[ i ] = oGroup.getProperty( i );
		}
 
		// If we are recursing
		if( bRecurse ){
			// Concatenate the properties array from child groups
			aProperties = aProperties.concat(
				getGroupProperties( oGroup.getFirstChild(), true, bRecurse ) );
		}
 
		// If we are traversing
		if( bTraverse ){
			// Concatenate the properties array from sibling groups
			aProperties = aProperties.concat(
				getGroupProperties( oGroup.getNextSibling(), bTraverse, bRecurse ) );
		}
 
		// Return the array of properties
		return aProperties;
	};
 
	/*********************************************************************/
	// Array<DzProperty> : A function for getting the list properties for an element
	function getElementProperties( oElement, bTraverse, bRecurse )
	{
		// Get the property group tree for the element
		var oPropertyGroupTree = oElement.getPropertyGroups();
 
		// If the application version is 4.9.4.101 or newer and we want all properties
		if( App.version64 >= 0x0004000900040065 && bTraverse && bRecurse ){
			// Return the properties for the element
			return oPropertyGroupTree.getAllProperties();
		}
 
		// Get the first group in the tree
		var oPropertyGroup = oPropertyGroupTree.getFirstChild();
		// Return the properties for the element
		return getGroupProperties( oPropertyGroup, bTraverse, bRecurse );
	};
 
	/*********************************************************************/
	// DzProperty : A function for finding a property associated with an element
	function findElementProperty( oElement, sProperty, bUseLabel )
	{
		// Whether or not to use optimizations; 4.7.1.44 or newer
		var bUseOptimization = (App.version64 >= 0x000400070001002c);
 
		// Declare a working variable
		var oProperty;
 
		// If the application version is 4.7.1.44 or newer and we are not using
		// the label to find, or the application version is 4.11.0.166 or newer
		if( (bUseOptimization && !bUseLabel) ||
			App.version64 >= 0x0004000b000000a6 ){
			// Get the property group tree for the element
			var oPropertyGroupTree = oElement.getPropertyGroups();
 
			// If we are using the label
			if( bUseLabel ){
				// Attempt to find the property
				oProperty = oPropertyGroupTree.findPropertyByLabel( sProperty );
			// If we are not using the label
			} else {
				// Attempt to find the property
				oProperty = oPropertyGroupTree.findProperty( sProperty );
			}
 
			// If we found a property
			if( oProperty ){
				// We are done, return it
				return oProperty;
			}
		// Otherwise
		} else {
			// Get the properties of the element
			var aProperties = getElementProperties( oElement, true, true );
			// Iterate over the properties
			for( var i = 0; i < aProperties.length; i += 1 ){
				// Get the 'current' property
				oProperty = aProperties[i];
 
				// If we are using the label
				if( bUseLabel ){
					// If the label of the property is the one we are looking for
					if( oProperty.getLabel() == sProperty ){
						// We are done, return it
						return oProperty;
					}
				// If we are not using the label
				} else {
					// If the name of the property is the one we are looking for
					if( oProperty.name == sProperty ){
						// We are done, return it
						return oProperty;
					}
				}
			}
		}
 
		// Declare working variables
		var oChild;
 
		// Iterate over the child elements
		for( var i = 0, nChildren = oElement.getNumElementChildren(); i < nChildren; i += 1 ){
			// Get the 'current' child element
			oChild = oElement.getElementChild( i );
			// Get the property from the child
			oProperty = findElementProperty( oChild, sProperty, bUseLabel );
 
			// If we are using a version with optimizations
			if( bUseOptimization ){
				// If we have a property
				if( oProperty ){
					// We are done, return it
					return oProperty;
				}
			// Otherwise
			} else {
				// If we are using the label
				if( bUseLabel ){
					// If the label of the property is the one we are looking for
					if( oProperty.getLabel() == sProperty ){
						// We are done, return it
						return oProperty;
					}
				// If we are not using the label
				} else {
					// If the name of the property is the one we are looking for
					if( oProperty.name == sProperty ){
						// We are done, return it
						return oProperty;
					}
				}
			}
		}
 
		return null;
	};
 
	/*********************************************************************/
	// void : A function for handling the editEnd() signal from a slider
	// 'this' is expected to be an instance of DzFloatSlider, or DzIntSlider
	function handleSliderEditEnd()
	{
		// If the 'this' object doesn't have a 'name' member
		if( this["name"] == undefined ){
			// Record the error
			print( "'this' does not have a 'name' member!" );
			// We are done...
			return;
		}
 
		// If the 'this' object doesn't have a 'value' member
		if( this["value"] == undefined ){
			// Record the error
			print( this.name, "does not have a 'value' member!" );
			// We are done...
			return;
		}
 
		// Get the id that was encoded into the name of the slider
		var sId = this.name.split( "__" )[ 0 ];
 
		// Get the node and property members
		var oMapItem = s_oSliderMap[ sId ];
		var oNode = oMapItem[ "node" ];
		var oProperty = oMapItem[ "property" ];
 
		// Do something with the values of the member(s) we verified to exist
		print( "Adjust", oProperty.getLabel(), "on", oNode.getLabel(), "by",
			this.value - oProperty.getValue() );
	};
 
	/*********************************************************************/
	// void : A function for initializing a slider with common settings
	function initSlider( wSlider, sId, sName, sLabel )
	{
		// Configure common settings
		wSlider.labelVisible = true;
		wSlider.textEditable = true;
		wSlider.name = String("%1__%2").arg( sId ).arg( sName );
		wSlider.label = sLabel;
 
		// Connect the 'editEnd' signal on the slider to the
		// 'handleSliderEditEnd' function in a way that sets the
		// 'this' object in the function to the slider
 
		// If we can use Global::connect( sender, "signal", thisObject, slot ) syntax
		if( s_bGlobalMemberFuncConn ){
			connect( wSlider, "editEnd()", wSlider, handleSliderEditEnd );
		// Otherwise, if we can use the sender.signal.scriptConnect( thisObject, slot ) syntax
		} else if( s_bScriptConnect ){
			wSlider.editEnd.scriptConnect( wSlider, handleSliderEditEnd );
		// Otherwise, fall back to the deprecated syntax
		} else {
			// The first argument provides the 'this' object within
			// the function, the second argument is the function
			wSlider.editEnd.connect( wSlider, handleSliderEditEnd );
		}
	};
 
	/*********************************************************************/
	// DzFloatSlider : A function for creating a float slider
	function createFloatSlider( wParent, nId, sName, sLabel )
	{
		// Create a float slider widget
		var wSlider = new DzFloatSlider( wParent );
		// Initialize the slider
		initSlider( wSlider, nId, sName, sLabel );
 
		// Return the slider
		return wSlider;
	};
 
	/*********************************************************************/
	// DzFloatSlider : A function for creating a float slider for a property
	function createFloatPropertySlider( wParent, nId, oProperty )
	{
		// Get the name and label of the property
		var sName = oProperty.name;
		var sLabel = oProperty.getLabel();
 
		// Create the float slider
		var wSlider = createFloatSlider( wParent, nId, sName, sLabel );
 
		// Configure the slider to match the property
		wSlider.min = oProperty.getMin();
		wSlider.max = oProperty.getMax();
		wSlider.clamped = oProperty.isClamped();
		wSlider.sensitivity = oProperty.getSensitivity();
		wSlider.displayAsPercent = oProperty.getDisplayAsPercent();
		wSlider.value = oProperty.getValue();
 
		// Return the slider
		return wSlider;
	};
 
	/*********************************************************************/
	// void : A function for creating float sliders for properties on a node
	function createNodePropertySliders( wParent, oNode, aPropertyNames )
	{
		// Declare local working variables
		var sId;
		var oProperty;
		var wSlider;
 
		// Iterate over the property names
		for( var i = 0, nNames = aPropertyNames.length; i < nNames; i +=1 ){
			// Find the property on the node
			oProperty = findElementProperty( oNode, aPropertyNames[ i ], false );
			// If the property was not found
			if( !oProperty ){
				// Next!!
				continue;
			}
 
			// Create a unique (reproducible) id
			sId = App.createDigest( [ oNode.getLabel(), oProperty.getLabel() ] );
 
			// Create the slider for the property
			wSlider = createFloatPropertySlider( wParent, sId, oProperty );
 
			// Update the map
			s_oSliderMap[ sId ] = {
				"node" : oNode,
				"property" : oProperty,
				"slider" : wSlider
			};
		}
	};
 
	/*********************************************************************/
	// DzFloatSlider : A function for creating a float slider for a property
	function applyChanges()
	{
		// Declare local working variables
		var sId;
		var oMapItem, oProperty;
		var wSlider;
 
		// Get the IDs for the property-slider map
		var aIds = Object.keys( s_oSliderMap );
 
		// Start collecting changes
		beginUndo();
		// Iterate over the IDs
		for( var i = 0, nIds = aIds.length; i < nIds; i += 1 ){
			sId = aIds[ i ];
			oMapItem = s_oSliderMap[ sId ];
			oProperty = oMapItem[ "property" ];
			wSlider = oMapItem[ "slider" ];
 
			oProperty.setValue( wSlider.value );
		}
 
		// Finish collecting changes and add the undo to the stack
		acceptUndo( text( "Node Property Slider Test" ) );
	};
 
	/*********************************************************************/	
	// Declare working variables
	var wSlider, wGroupBox;
	var oNode, oProperty;
 
	// Get the list of selected nodes
	var aNodes = Scene.getSelectedNodeList();
	// Get the number of selected nodes
	var nNodes = aNodes.length;
 
	// If no nodes are selected
	if( nNodes < 1 ){
		// Inform the user
		MessageBox.information( text( "A node must be selected to continue." ),
			text( "Selection Error" ), text( "&OK" ) );
 
		// We are done...
		return;
	}
 
	// Define the list of property names to create slider for
	var aPropertyNames = [ "XRotate", "YRotate", "ZRotate" ];
 
	// Create a basic dialog
	var oDlg = new DzBasicDialog();
 
	// Iterate over the nodes
	for( var i = 0; i < nNodes; i +=1 ){
		// Get the 'current' node
		oNode = aNodes[ i ];
 
		// Create a group box with a vertical box layout for the node
		wGroupBox = new DzVGroupBox( oDlg );
		wGroupBox.title = oNode.getLabel();
 
		// Create the sliders for the named properties; if they exist
		createNodePropertySliders( wGroupBox, oNode, aPropertyNames );
 
		// Add the group box to the dialog
		oDlg.addWidget( wGroupBox );
	}
 
	// Launch the dialog, and if the user cancels
	if( !oDlg.exec() ){
		// We are done...
		return;
	}
 
	// Apply the changes from the map
	applyChanges();
 
// Finalize the function and invoke
})( true );