Project 11.4: Scripted Panel Creation


The design for the blenderPanel will be slightly modified from the blender window, as seen in Figure 11.26.

click to expand
Figure 11.26: The new blend shape editing panel.

Notice that the design incorporates a vertical scroll, to account for the user placing the panel in a pane that is horizontally aligned, such as the Outliner is in Figure 11.27.Should the user align the panel in a vertically oriented pane, as the Hypergraph is in Figure 11.28, the panel will do its best to fit. However, should the user size the panel to an orientation too thin, it will lose its functionality. This loss of functionality is unavoidable, but follows the paradigm set by many of the editors in Maya that lose any real functionality when sized too thin, such as the Graph Editor.

click to expand
Figure 11.27: The Outliner, designed to be a vertically oriented editor.
click to expand
Figure 11.28: Maya does not prevent the user from resizing panels into unusable sizes.

We will also eliminate the modelingPanel , instead letting the users use the standard modeling panels. This will allow them more control over the layout of their workspace, and it simplifies our task as scripters.

We will also add the capability to the blender UI to effectively hold onto the blendshape node, allowing anything to be selected and still retaining control over the blendshape. This will also require us to add a Load Blendshape Node button to the interface, allowing the user to explicitly load the blendshape attributes for the selected node.

The first step is to create a valid script, and issue the statement defining the scriptedPanelType , seen in Example 11.27. One difficult aspect of creating scripted panels is that all the callback procedures need to be in place and have at least a minimum of their intended functionality before the script can even be tested .

Example 11.27: Begin the procedure by defining our scripted panel.

 global proc blendPanelDefinition ()     {  // define the scripted panel type for the blender   // panel  scriptedPanelType         -createCallback blenderCreateCallback         -initCallback blenderInitCallback         -addCallback blenderAddCallback         -removeCallback blenderRemoveCallback         -deleteCallback blenderDeleteCallback         -saveStateCallback blenderSaveStateCallback         -unique true         blenderScriptedPanelType;     } 

Next, we add the procedures for each of the callbacks. The first procedure, blenderCreateCallback is very simple, as we have no editors to create. This is shown in Example 11.28.

Example 11.28: All the procedures in a scripted panel definition have to exist, even if empty.

 global proc blenderCreateCallback(string $panelName)     {  // Empty panel creation procedure  } 

Next, we add the initialization procedure. Note that although we are writing these scripts in the order called from the scriptEditorType command, there is no requirement that we must do so. The scripts are simply presented here in this order for convenience. Likewise, after script construction is complete, it is good practice to organize the procedures into this order to allow for easy editing after initial construction. In Example 11.29, we see the code for the initialization procedure.

Example 11.29: Once again, the empty required procedure.

 global proc blenderInitCallback (string $panelName)     {  // Empty panel initialization procedure  } 

The addCallback procedure is where we begin to see our panel take shape. This code is adapted from the blenderUI code from Project 11.3. Note that the initialization section of the blenderUI , along with the variable arrays that result, has been replaced with the simple declaration of global variables , which are then used in the construction of the UI controls. See Example 11.30.

Example 11.30: Adapted code from the blenderUI.

 global proc blenderAddCallback (string $panelName)     {  // declaration of global variables  global string $blndPanbShape;     global string $blndPanCon[];     global float $blndPanWts[];     string $blenderForm = `formLayout                                 -numberOfDivisions 100                                 blndPanFrm`;     string $scrollLay = `scrollLayout                             -horizontalScrollBarThickness 2                             -verticalScrollBarThickness 5                             -childResizable true                             blndPanScrl`;     string $blenderColumn = `columnLayout                                 -columnAttach "both" 0                                 -adjustableColumn true                                 blndPanCol`;     for ($i = 0; $i < `size $blndPanWts`; $i++)         {             rowLayout                 -numberOfColumns 2                 -columnWidth2 30 50                 -adjustableColumn 1                 -columnAlign 1 "center"                 -columnAlign 2 "center"                 -columnAttach 1 "both" 0                 -columnAttach 2 "both" 0                 ("blndPanRow_" + $i)                 ;                 attrFieldSliderGrp                             -min 0.0                             -max 1.0                             -at ($blndPanbShape                                 + "."                                 + $blndPanCon[$i])                             -label $blndPanCon[$i]                             -adjustableColumn 3                             -columnAlign 1 right                             -columnWidth 1 90                             -columnWidth 4 1                             ;                 button                     -label "Key"                     -align center                     -command ("setKeyframe "                                 + $blndPanbShape                                 + "."                                 + $blndPanCon[$i]);                 setParent..;                 }             setParent..;         setParent..;     string $getButt = `button                             -label "Update Panel"                             -command "blenderPanelGetVars;                                     blenderUpdatePanel;"`;     formLayout         -edit         -attachForm $scrollLay "top" 2         -attachForm $scrollLay left 2         -attachControl $scrollLay "bottom" 2 $getButt         -attachForm $scrollLay right 2         -attachForm $getButt left 2         -attachForm $getButt "bottom" 2         -attachForm $getButt right 2         $blenderForm;     } 

We now need to construct a procedure, seen in Example 11.31, which evaluates the selected object and builds the global variables needed to build the user interface controls. This is one of the two commands issued by the button created in the addCallback procedure. This procedure is not called from the scriptPanelType command, so we will have to later figure out a way to bring this procedure into the construction of the panel.

Example 11.31: The procedure to assign values to the global variables.

 global proc int blenderPanelGetVars ()     {  // declaration of global variables  global string $blndPanbShape;     global string $blndPanCon[];     global float $blndPanWts[];  // Find All the info on the blend node   // on current object  string $sel[] = `ls -selection`;     string $hist[], $attribs[], $blendShapeNode;     if (`size $sel` == 1)         {         $hist = `listHistory -il 2`;         for ($each in $hist)             if (`nodeType $each` == "blendShape")                 $blendShapeNode = $each;         string $contrib[] = `listAttr                             -multi ($blendShapeNode                                     + ".weight")`;         float $conWts[] = `getAttr ($blendShapeNode                                     + ".weight")`;  // assign found values to variables  $blndPanbShape = $blendShapeNode;         $blndPanCon = $contrib;         $blndPanWts = $conWts;         }     else         warning "Please select a single blendshape object";     if (`size $blendShapeNode` == 0)         {         warning "Selected object is not a blendshape";         return 0;         }     else         return 1;     } 

The return value is there as a way of easily checking within MEL scripts whether the user has a blendshape object selected. Constructing the script with this return value check allows us to rebuild the panel if and only if the user has a blendshape object selected.

We now have to construct a procedure that will update the user interface when the user presses the Update button. This first requires some modification of the procedure that constructs the panel, as well as the addition of a global variable. We begin in Example 11.32 by assigning the name of the column layout holding the child row layout to a variable. We then assign this to a global variable, allowing us to access this layout by simply using the setParent command to explicitly declare the active layout to the scripted panel, letting us delete the appropriate user controls.

Example 11.32: MEL code to update the UI.

 global proc blenderAddCallback (string $panelName)     {  // declaration of global variables  global string $blndPanbShape;     global string $blndPanCon[];     global float $blndPanWts[];     global string $blndPanName;     string $blenderForm = `formLayout                                 -numberOfDivisions 100                                 blndPanFrm`;     string $scrollLay = `scrollLayout                             -horizontalScrollBarThickness 2                             -verticalScrollBarThickness 5                             -childResizable true                             blndPanScrl`;     string $blenderColumn = `columnLayout                                 -columnAttach "both" 0                                 -adjustableColumn true                                 blndPanCol`;     string $panLayout =  `setParent -query`;     $blndPanName = $panLayout;     . . . 

We can now create a procedure to update the controls held in the panel, seen in Example 11.33.

Example 11.33: The procedure to update the controls of the UI.

 global proc blenderUpdatePanel ()     {  // declaration of global variables  global string $blndPanbShape;     global string $blndPanCon[];     global float $blndPanWts[];     global string $blndPanName;          int $numRowLayouts;          if (`columnLayout -exists $blndPanName`)         {              $numRowLayouts = `columnLayout                                 -query                                 -numberOfChildren                                 $blndPanName`;             if ($numRowLayouts > 0)                 for ($i = 0; $i < $numRowLayouts; $i++)                     if (`rowLayout exists                                ("blndPanRow_" + $i)`)                         deleteUI ("blndPanRow_" + $i);         }  // Explicitly state the path to place the rowLayouts in  if (`columnLayout -exists $blndPanName`)         setParent $blndPanName;          for ($i = 0; $i < `size $blndPanWts`; $i++)         {            rowLayout                 -numberOfColumns 2                 -columnWidth2 30 50                 -adjustableColumn 1                 -columnAlign 1 "center"                 -columnAlign 2 "center"                 -columnAttach 1 "both" 0                 -columnAttach 2 "both" 0                 ("blndPanRow_" + $i)                 ;                 attrFieldSliderGrp                             -min 0.0                             -max 1.0                             -at ($blndPanbShape                                 + "."                                 + $blndPanCon[$i])                             -label $blndPanCon[$i]                             -adjustableColumn 3                             -columnAlign 1 right                             -columnWidth 1 90                             -columnWidth 4 1                             ;                 button                     -label "Key"                     -align center                     -command ("setKeyframe "                                 + $blndPanbShape                                 + "."                                 + $blndPanCon[$i]);                 setParent..;         }     } 

Before continuing on to the other callback procedures, we should modify the creation and initialization procedures to include the declaration of the global variables in both, and the update procedure should be called from the initialization procedure after resetting the global procedure values. This process is seen in Example 11.34.

Example 11.34: Updating the previously created procedures to include the global variables.

 global proc blenderCreateCallback(string $panelName)     {  // panel creation procedure   // just declare global variables  global string $blndPanbShape;     global string $blndPanCon[];     global float $blndPanWts[];     global string $blndPanName;     } global proc blenderInitCallback (string $panelName)     {  // panel initialization procedure   // declaration of global variables  global string $blndPanbShape;     global string $blndPanCon[];     global float $blndPanWts[];     global string $blndPanName;  // reset global variable values  $blndPanbShape = "";     clear $blndPanCon;     clear $blndPanWts[];  // update the panel  blenderUpdatePanel;     } 

Now that all of our existing procedures have been brought up to date, we can add procedures for the remaining callbacks.

In Example 11.35, we see the remove callback, which normally calls commands to store the state of the controls in the panel. Because the controls in our layout are linked to the attributes of the blendshape node, we have no need to store their values. When the controls are redrawn, their values will automatically be updated. Note that not only is this where we would store the values of any sliders, but also the collapse state of any frameLayouts in the panel, which tab is active in a tabLayout , or even which item is selected in a textScrollList control.

Example 11.35: The procedure normally called upon to store the state of a UI.

 global proc blenderRemoveCallback (string $panelName)     {  // panel initialization procedure   // declaration of global variables  global string $blndPanbShape;     global string $blndPanCon[];     global float $blndPanWts[];     global string $blndPanName;     } 

Next is the callback for the deletion of the UI elements. Normally, this is just used for the deletion of any editors that are created unparented, such as an outlinerPanel . Here, however, the formLayout , and all of the child controls, are deleted with the panel. See Example 11.36.

Example 11.36: The procedure issued upon deletion of a UI.

 global proc blenderDeleteCallback (string $panelName)     {  // panel delete procedure   // declaration of global variables  global string $blndPanbShape;     global string $blndPanCon[];     global float $blndPanWts[];     global string $blndPanName;     } 

Finally, we use the saveStateCallback procedure, seen in Example 11.37, to do nothing. If we wanted to have the panel automatically take into account the user s current selection when the panel is redrawn, we could have the saveStateCallback return a string that would evaluate the blenderPanelGetVars procedure, which would update the global variables and therefore change the sliders which are drawn in the blenderAddCallback procedure. The declaration of global variables even in procedures that have no real functionality is for consistency and to aid in any future additions to the panel definition.

Example 11.37: The procedure to update the controls UI, although here it is unused.

 global proc string blenderSaveStateCallback                             (string $panelName)     {  // panel save state procedure   // declaration of global variables  global string $blndPanbShape;     global string $blndPanCon[];     global float $blndPanWts[];     return "";     } 

Now that the scripted panel is defined, we can actually create the panel, using the scriptedPanel command to create an unparented panel, as seen in Example 11.38.

Example 11.38: The actual creation of the panel.

 scriptedPanel         -unParent         -type blenderScriptedPanelType         -label "BlendShape Plus"         blenderScriptedPanel; 

After executing this, in the Maya viewports, under the menu Panels>Panels, we see an entry for BlendShape Plus, as seen in Figure 11.29. On selecting this view, and updating the data with some sample blendshape data, we see a view similar to that in Figure 11.30.

click to expand
Figure 11.29: Panel entry for our new panel.
click to expand
Figure 11.30: The Blendshape Plus panel in the Maya interface.

In addition to the panel, we also have the ability to tear off the panel, effectively turning it into a window, as seen in Figure 11.31.

click to expand
Figure 11.31: Tearing off the panel gives us a free window with the creation of our scripted panel.

Now that our panel is functional, we can place all the procedures in a single script,  blendPanelDefinition.mel , and source this file from our userSetup.mel file. We also can create the unparented panel in the userSetup file, effectively adding it to our default Maya interface.

On the CD  

The text for these scripts can be found on the companion CD-ROM under Chapter_11/project_UI07/ as /blendPanelDefinition.mel and  userSetup.addition .

Project Conclusion and Review

As we have seen, scripted panels are a useful and powerful way of integrating created interfaces into the Maya workspace. It also is a way of accessing and redesigning such Maya windows as the Hypergraph and Graph Editor. Such endeavors should not be undertaken lightly, as the scripts that define these editors are quite involved, and the editors are part of the core functionality of Maya itself. Altering or removing properties from these editors can confuse users.




The MEL Companion
The MEL Companion: Maya Scripting for 3D Artists (Charles River Media Graphics)
ISBN: 1584502754
EAN: 2147483647
Year: 2003
Pages: 101

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net