#!/usr/bin/env python # -*- coding: utf-8 -*- import maya.cmds as cmds import maya.mel as mel import math import sys import inspect import importlib # Crucial for fixing namespace issues: # Dynamically gets the module name (e.g., 'animation_tools.ikfk_switch') MODULE_NAME = __name__ # ============================================================================== # UI Creation # ============================================================================== def IKFK_Switch_UI(): """ Creates the main IK/FK switch Maya window interface. """ WINDOW_NAME = "IKFK_Switch_UI" WINDOW_TITLE = "IK/FK Switch V2.9 (Final Button Fix)" WINDOW_WIDTH = 300 # Check if window exists and delete it if cmds.window(WINDOW_NAME, exists=True): cmds.deleteUI(WINDOW_NAME, window=True) # Create window cmds.window(WINDOW_NAME, width=WINDOW_WIDTH, title=WINDOW_TITLE) # Main Layout cmds.columnLayout(adjustableColumn=True, rowSpacing=1) # --- Helper function for button commands --- # CORE FIX: Removed the 'python("...")' wrapper. # The function now returns a pure Python command string for button execution. def py_cmd(func_name, args=""): # Format: 'import module; module.function("args")' (Pure Python string) if args: # Use double quotes to avoid escaping issues return 'import {0}; {0}.{1}("{2}")'.format(MODULE_NAME, func_name, args) else: return 'import {0}; {0}.{1}()'.format(MODULE_NAME, func_name) # --- Edit Section --- cmds.frameLayout(label="Edit", collapse=True, collapsable=True, collapseCommand="cmds.window('{}', edit=True, height=578)".format(WINDOW_NAME), expandCommand="cmds.window('{}', edit=True, height=578)".format(WINDOW_NAME)) cmds.columnLayout(adjustableColumn=True, rowSpacing=2) cmds.button(label="<<< ADV Build >>>", command=py_cmd("sg_ADV_Build")) cmds.button(label="<<< Empty >>>", command=py_cmd("sg_Empty")) cmds.setParent("..") cmds.setParent("..") # --- Load FK Ctrl Section --- cmds.frameLayout(label="Load FK Ctrl") cmds.columnLayout(adjustableColumn=True, rowSpacing=2) # FK Joint Root cmds.rowLayout(numberOfColumns=2, columnWidth2=(45, 120), adjustableColumn=1) cmds.textField("target_01", text="", placeholderText="FK Joint Root") cmds.button(label="< FK Joint Root", command=py_cmd("sg_setTextField", "target_01")) cmds.setParent("..") # FK Joint Mid cmds.rowLayout(numberOfColumns=2, columnWidth2=(45, 120), adjustableColumn=1) cmds.textField("target_02", text="", placeholderText="FK Joint Mid") cmds.button(label="< FK Joint Mid", command=py_cmd("sg_setTextField", "target_02")) cmds.setParent("..") # FK Joint End cmds.rowLayout(numberOfColumns=2, columnWidth2=(45, 120), adjustableColumn=1) cmds.textField("target_03", text="", placeholderText="FK Joint End") cmds.button(label="< FK Joint End", command=py_cmd("sg_setTextField", "target_03")) cmds.setParent("..") cmds.separator(style="in", height=5) # FK Ctrl Root cmds.rowLayout(numberOfColumns=2, columnWidth2=(45, 120), adjustableColumn=1) cmds.textField("target_04", text="", placeholderText="FK Ctrl Root") cmds.button(label="< FK Ctrl Root", command=py_cmd("sg_setTextField", "target_04")) cmds.setParent("..") # FK Ctrl Mid cmds.rowLayout(numberOfColumns=2, columnWidth2=(45, 120), adjustableColumn=1) cmds.textField("target_05", text="", placeholderText="FK Ctrl Mid") cmds.button(label="< FK Ctrl Mid", command=py_cmd("sg_setTextField", "target_05")) cmds.setParent("..") # FK Ctrl End cmds.rowLayout(numberOfColumns=2, columnWidth2=(45, 120), adjustableColumn=1) cmds.textField("target_06", text="", placeholderText="FK Ctrl End") cmds.button(label="< FK Ctrl End", command=py_cmd("sg_setTextField", "target_06")) cmds.setParent("..") cmds.setParent("..") cmds.setParent("..") # --- Load IK Ctrl Section --- cmds.frameLayout(label="Load IK Ctrl") cmds.columnLayout(adjustableColumn=True, rowSpacing=2) # IK Joint Root cmds.rowLayout(numberOfColumns=2, columnWidth2=(45, 120), adjustableColumn=1) cmds.textField("target_07", text="", placeholderText="IK Joint Root") cmds.button(label="< IK Joint Root", command=py_cmd("sg_setTextField", "target_07")) cmds.setParent("..") # IK Joint Mid cmds.rowLayout(numberOfColumns=2, columnWidth2=(45, 120), adjustableColumn=1) cmds.textField("target_08", text="", placeholderText="IK Joint Mid") cmds.button(label="< IK Joint Mid", command=py_cmd("sg_setTextField", "target_08")) cmds.setParent("..") # IK Joint End cmds.rowLayout(numberOfColumns=2, columnWidth2=(45, 120), adjustableColumn=1) cmds.textField("target_09", text="", placeholderText="IK Joint End") cmds.button(label="< IK Joint End", command=py_cmd("sg_setTextField", "target_09")) cmds.setParent("..") cmds.separator(style="in", height=5) # IK Ctrl Root cmds.rowLayout(numberOfColumns=2, columnWidth2=(45, 120), adjustableColumn=1) cmds.textField("target_10", text="", placeholderText="IK Ctrl Root") cmds.button(label="< IK Ctrl Root", command=py_cmd("sg_setTextField", "target_10")) cmds.setParent("..") # IK Ctrl Pole cmds.rowLayout(numberOfColumns=2, columnWidth2=(45, 120), adjustableColumn=1) cmds.textField("target_11", text="", placeholderText="IK Ctrl Pole") cmds.button(label="< IK Ctrl Pole", command=py_cmd("sg_setTextField", "target_11")) cmds.setParent("..") cmds.setParent("..") cmds.setParent("..") # --- Load Switch Ctrl Section --- cmds.frameLayout(label="Load Switch Ctrl") cmds.columnLayout(adjustableColumn=True, rowSpacing=2) # Switch Ctrl cmds.rowLayout(numberOfColumns=2, columnWidth2=(45, 120), adjustableColumn=1) cmds.textField("target_12", text="", placeholderText="Switch Ctrl") cmds.button(label="< Switch Ctrl", command=py_cmd("sg_setTextField", "target_12")) cmds.setParent("..") # Switch Attr cmds.rowLayout(numberOfColumns=2, columnWidth2=(45, 120), adjustableColumn=1) cmds.textField("target_13", text="", placeholderText="Switch Attr Name") cmds.button(label="< Switch Attr", command=py_cmd("sg_setLoadAttr")) cmds.setParent("..") cmds.setParent("..") cmds.setParent("..") cmds.separator(style="in", height=5) # Build Button cmds.button(height=32, label="<<< Build Seamless Switching >>>", command=py_cmd("sg_Execute")) cmds.setParent("..") # Adjust window height and show cmds.window(WINDOW_NAME, edit=True, height=578) cmds.showWindow(WINDOW_NAME) def sg_setTextField(target): """ Sets the name of the currently selected object into the specified text field. """ selection = cmds.ls(selection=True, long=False) if selection: selected_object = selection[0] cmds.textField(target, edit=True, text=selected_object) else: cmds.warning("Please select an object.") def sg_setLoadAttr(): """ Sets the name of the selected attribute in the Channel Box into the target_13 text field. """ # Query selected attributes in the Channel Box attrs = cmds.channelBox("mainChannelBox", query=True, selectedMainAttributes=True) if not attrs: cmds.error("Select an attribute in the Channel Box...") return # Take only the first selected attribute attribute_name = attrs[0] cmds.textField("target_13", edit=True, text=attribute_name) def sg_Execute(): """ Reads all values from UI controls and calls the sg_SeamlessSwitching function. """ # Get values from UI controls FK_Joint_Root = cmds.textField("target_01", query=True, text=True) FK_Joint_Mid = cmds.textField("target_02", query=True, text=True) FK_Joint_End = cmds.textField("target_03", query=True, text=True) FK_Ctrl_Root = cmds.textField("target_04", query=True, text=True) FK_Ctrl_Mid = cmds.textField("target_05", query=True, text=True) FK_Ctrl_End = cmds.textField("target_06", query=True, text=True) IK_Joint_Root = cmds.textField("target_07", query=True, text=True) IK_Joint_Mid = cmds.textField("target_08", query=True, text=True) IK_Joint_End = cmds.textField("target_09", query=True, text=True) IK_Ctrl_Root = cmds.textField("target_10", query=True, text=True) IK_Ctrl_Pole = cmds.textField("target_11", query=True, text=True) Switch_Ctrl = cmds.textField("target_12", query=True, text=True) Switch_Attr = cmds.textField("target_13", query=True, text=True) # Check for empty values all_targets = [FK_Joint_Root, FK_Joint_Mid, FK_Joint_End, FK_Ctrl_Root, FK_Ctrl_Mid, FK_Ctrl_End, IK_Joint_Root, IK_Joint_Mid, IK_Joint_End, IK_Ctrl_Root, IK_Ctrl_Pole, Switch_Ctrl, Switch_Attr] for target in all_targets: if not target: cmds.error("All fields must be filled before building the switch.") return # Call core build function sg_SeamlessSwitching( FK_Joint_Root, FK_Joint_Mid, FK_Joint_End, FK_Ctrl_Root, FK_Ctrl_Mid, FK_Ctrl_End, IK_Joint_Root, IK_Joint_Mid, IK_Joint_End, IK_Ctrl_Root, IK_Ctrl_Pole, Switch_Ctrl, Switch_Attr ) def sg_ADV_Build(): """ Builds IK/FK switching for sample limbs using preset naming conventions. NOTE: Modify names here to match your Rig naming convention. """ # Right Arm sg_SeamlessSwitching( "FKXShoulder_R", "FKXElbow_R", "FKXWrist_R", "FKShoulder_R", "FKElbow_R", "FKWrist_R", "IKXShoulder_R", "IKXElbow_R", "IKXWrist_R", "IKArm_R", "PoleArm_R", "FKIKArm_R", "fkik" ) # Left Arm sg_SeamlessSwitching( "FKXShoulder_L", "FKXElbow_L", "FKXWrist_L", "FKShoulder_L", "FKElbow_L", "FKWrist_L", "IKXShoulder_L", "IKXElbow_L", "IKXWrist_L", "IKArm_L", "PoleArm_L", "FKIKArm_L", "fkik" ) # Right Leg sg_SeamlessSwitching( "FKXHip_R", "FKXKnee_R", "FKXAnkle_R", "FKHip_R", "FKKnee_R", "FKAnkle_R", "IKXHip_R", "IKXKnee_R", "IKXAnkle_R", "IKLeg_R", "PoleLeg_R", "FKIKLeg_R", "fkik" ) # Left Leg sg_SeamlessSwitching( "FKXHip_L", "FKXKnee_L", "FKXAnkle_L", "FKHip_L", "FKKnee_L", "FKAnkle_L", "IKXHip_L", "IKXKnee_L", "IKXAnkle_L", "IKLeg_L", "PoleLeg_L", "FKIKLeg_L", "fkik" ) def sg_Empty(): """ Clears all text fields in the UI. """ for i in range(1, 14): target_name = "target_{:02d}".format(i) if cmds.textField(target_name, exists=True): cmds.textField(target_name, edit=True, text="") # ============================================================================== # Core IK/FK Switching Logic # ============================================================================== def sg_switching(locator_name): """ Executes the core IK/FK switching logic (seamless match). This function is called at runtime by the scriptJob. Args: locator_name (str): The name of the Locator storing connection info. """ # Get the Switch Control and attribute name (stored as string attributes) try: switch_ctrl = cmds.getAttr("{}.Switch_Ctrl".format(locator_name)) switch_attr_name = cmds.getAttr("{}.Switch_Attr".format(locator_name)) except: cmds.error("Locator {} does not have required attributes.".format(locator_name)) return # State check: Use optionVar to store the last state, preventing redundant execution option_var_name = locator_name current_state = cmds.getAttr("{}.IKFK_Seamless".format(switch_ctrl)) try: last_state = cmds.optionVar(query=option_var_name) except RuntimeError: last_state = -1 if last_state == current_state: # State has not changed, exit return # Store current selection selection = cmds.ls(selection=True, long=False) # Get all object names from the Locator (stored as string attributes) try: FK_Joint_Root = cmds.getAttr("{}.FK_Joint_Root".format(locator_name)) FK_Joint_Mid = cmds.getAttr("{}.FK_Joint_Mid".format(locator_name)) FK_Joint_End = cmds.getAttr("{}.FK_Joint_End".format(locator_name)) FK_Ctrl_Root = cmds.getAttr("{}.FK_Ctrl_Root".format(locator_name)) FK_Ctrl_Mid = cmds.getAttr("{}.FK_Ctrl_Mid".format(locator_name)) FK_Ctrl_End = cmds.getAttr("{}.FK_Ctrl_End".format(locator_name)) IK_Joint_Root = cmds.getAttr("{}.IK_Joint_Root".format(locator_name)) IK_Joint_Mid = cmds.getAttr("{}.IK_Joint_Mid".format(locator_name)) IK_Joint_End = cmds.getAttr("{}.IK_Joint_End".format(locator_name)) IK_Ctrl_Root = cmds.getAttr("{}.IK_Ctrl_Root".format(locator_name)) IK_Ctrl_Pole = cmds.getAttr("{}.IK_Ctrl_Pole".format(locator_name)) except Exception as e: cmds.error("Failed to retrieve object names from Locator {}: {}".format(locator_name, e)) return # Get Min/Max values for the blend attribute try: attr_min = cmds.attributeQuery(switch_attr_name, node=switch_ctrl, minimum=True) attr_max = cmds.attributeQuery(switch_attr_name, node=switch_ctrl, maximum=True) min_val = attr_min[0] if attr_min else 0.0 max_val = attr_max[0] if attr_max else 1.0 except Exception: cmds.warning("Could not query min/max values for blend attribute. Defaulting to 0 and 1.") min_val = 0.0 max_val = 1.0 current_time = cmds.currentTime(query=True) # Set keyframes on all controls before switching (MEL lines 504-508) cmds.setKeyframe(FK_Ctrl_Root) cmds.setKeyframe(FK_Ctrl_Mid) cmds.setKeyframe(FK_Ctrl_End) cmds.setKeyframe(IK_Ctrl_Root) cmds.setKeyframe(IK_Ctrl_Pole) # **MATCHING LOGIC** if current_state == 0: # Switch to IK (IKFK_Seamless: IK) # 1. Match IK Ctrl Root (End Controller) - Using temp groups to preserve rotation offset # This matches the MEL version's approach (lines 510-519) tempGroup_A = cmds.group(empty=True) tempGroup_B = cmds.group(empty=True) # Step 1: Match both temp groups to IK_Joint_End cmds.delete(cmds.parentConstraint(IK_Joint_End, tempGroup_A, weight=1)) cmds.delete(cmds.parentConstraint(IK_Joint_End, tempGroup_B, weight=1)) # Step 2: Apply IK_Ctrl_Root's current orientation to tempGroup_B (preserve rotation offset) cmds.delete(cmds.orientConstraint(IK_Ctrl_Root, tempGroup_B, offset=(0, 0, 0), weight=1)) # Step 3: Parent tempGroup_B under tempGroup_A to create hierarchy cmds.parent(tempGroup_B, tempGroup_A) # Step 4: Match tempGroup_A to FK_Joint_End (this moves the hierarchy) cmds.delete(cmds.parentConstraint(FK_Joint_End, tempGroup_A, weight=1)) # Step 5: Apply tempGroup_B's final transform to IK_Ctrl_Root con_A = cmds.parentConstraint(tempGroup_B, IK_Ctrl_Root, weight=1) cmds.setKeyframe(IK_Ctrl_Root) cmds.delete(con_A) # 2. Calculate Pole Vector position based on FK chain bend direction # Get world space positions of FK joints pos_root = cmds.xform(FK_Joint_Root, query=True, worldSpace=True, translation=True) pos_mid = cmds.xform(FK_Joint_Mid, query=True, worldSpace=True, translation=True) pos_end = cmds.xform(FK_Joint_End, query=True, worldSpace=True, translation=True) # Calculate the bend direction using vector projection # Vector from root to end (main chain direction) vec_RE = [pos_end[i] - pos_root[i] for i in range(3)] # Vector from root to mid vec_RM = [pos_mid[i] - pos_root[i] for i in range(3)] # Length of main chain vector len_RE = math.sqrt(sum(vec_RE[i]**2 for i in range(3))) if len_RE > 0.0001: # Avoid division by zero # Normalize the main chain vector norm_RE = [vec_RE[i] / len_RE for i in range(3)] # Project vec_RM onto vec_RE to find the projection point proj_scalar = sum(vec_RM[i] * norm_RE[i] for i in range(3)) proj_vec = [proj_scalar * norm_RE[i] for i in range(3)] # Perpendicular vector (from projection point to mid joint) vec_perp = [vec_RM[i] - proj_vec[i] for i in range(3)] len_perp = math.sqrt(sum(vec_perp[i]**2 for i in range(3))) if len_perp > 0.0001: # Chain has a bend # Normalize perpendicular vector norm_perp = [vec_perp[i] / len_perp for i in range(3)] # Extension distance: use chain length as reference # Multiply by a factor to place pole vector at a reasonable distance extend_dist = len_RE * 0.5 # Pole Vector position = mid joint + perpendicular direction * extension distance pole_pos = [pos_mid[i] + norm_perp[i] * extend_dist for i in range(3)] else: # Chain is straight, use a default offset # If straight, offset along Z-axis (or another appropriate axis) pole_pos = [pos_mid[0], pos_mid[1], pos_mid[2] + len_RE * 0.5] else: # Fallback: keep current position if chain length is zero pole_pos = cmds.xform(IK_Ctrl_Pole, query=True, worldSpace=True, translation=True) # Apply calculated position to Pole Vector cmds.xform(IK_Ctrl_Pole, worldSpace=True, translation=pole_pos) cmds.setKeyframe(IK_Ctrl_Pole) # Clean up temp groups cmds.delete(tempGroup_A) # 3. Switch attribute: from FK (min_val) to IK (max_val) cmds.setAttr("{}.{}".format(switch_ctrl, switch_attr_name), min_val) cmds.setKeyframe(switch_ctrl, attribute=switch_attr_name, time=current_time - 1) cmds.setAttr("{}.{}".format(switch_ctrl, switch_attr_name), max_val) cmds.setKeyframe(switch_ctrl, attribute=switch_attr_name, time=current_time) elif current_state == 1: # Switch to FK (IKFK_Seamless: FK) # Match FK controls to IK joints (MEL lines 530-538) # Use weight=1 instead of maintainOffset to match MEL behavior # 1. Match FK Ctrl Root to IK Joint Root con_A = cmds.parentConstraint(IK_Joint_Root, FK_Ctrl_Root, weight=1) cmds.setKeyframe(FK_Ctrl_Root) cmds.delete(con_A) # 2. Match FK Ctrl Mid to IK Joint Mid con_B = cmds.parentConstraint(IK_Joint_Mid, FK_Ctrl_Mid, weight=1) cmds.setKeyframe(FK_Ctrl_Mid) cmds.delete(con_B) # 3. Match FK Ctrl End to IK Joint End con_C = cmds.parentConstraint(IK_Joint_End, FK_Ctrl_End, weight=1) cmds.setKeyframe(FK_Ctrl_End) cmds.delete(con_C) # 4. Switch attribute: from IK (max_val) to FK (min_val) cmds.setAttr("{}.{}".format(switch_ctrl, switch_attr_name), max_val) cmds.setKeyframe(switch_ctrl, attribute=switch_attr_name, time=current_time - 1) cmds.setAttr("{}.{}".format(switch_ctrl, switch_attr_name), min_val) cmds.setKeyframe(switch_ctrl, attribute=switch_attr_name, time=current_time) # Update optionVar cmds.optionVar(intValue=(option_var_name, current_state)) # Restore selection if selection: cmds.select(selection, replace=True) def sg_SeamlessSwitching( FK_Joint_Root, FK_Joint_Mid, FK_Joint_End, FK_Ctrl_Root, FK_Ctrl_Mid, FK_Ctrl_End, IK_Joint_Root, IK_Joint_Mid, IK_Joint_End, IK_Ctrl_Root, IK_Ctrl_Pole, Switch_Ctrl, Switch_Attr ): """ Sets up the necessary nodes, attributes, and the scriptJob for IK/FK switching. """ # 1. Ensure all objects exist all_objects = [FK_Joint_Root, FK_Joint_Mid, FK_Joint_End, FK_Ctrl_Root, FK_Ctrl_Mid, FK_Ctrl_End, IK_Joint_Root, IK_Joint_Mid, IK_Joint_End, IK_Ctrl_Root, IK_Ctrl_Pole, Switch_Ctrl] for obj in all_objects: if not cmds.objExists(obj): cmds.error("Object not found: '{}'. Please verify all joint and controller names.".format(obj)) return # 2. Cleanup and Create Locator LOCATOR_NAME = "{}_Switch_Locator".format(Switch_Ctrl) matching_locators = cmds.ls(LOCATOR_NAME + "*", type='transform') final_locators_to_delete = [] for loc in matching_locators: if loc.startswith(LOCATOR_NAME) and cmds.listRelatives(loc, shapes=True, type='locator'): final_locators_to_delete.append(loc) if final_locators_to_delete: cmds.warning("Found and deleting {} existing switch locators for {}: {}".format( len(final_locators_to_delete), Switch_Ctrl, final_locators_to_delete)) try: cmds.delete(final_locators_to_delete) except Exception as e: cmds.warning("Failed to delete old locators: {}".format(e)) # Recreate Locator locator_shape = cmds.createNode("locator", name=LOCATOR_NAME + "Shape") locator = cmds.listRelatives(locator_shape, parent=True)[0] locator = cmds.rename(locator, LOCATOR_NAME) cmds.parent(locator, Switch_Ctrl) # Unlock and clear transform attributes for attr in ['tx', 'ty', 'tz', 'rx', 'ry', 'rz', 'sx', 'sy', 'sz', 'v']: full_attr = "{}.{}".format(locator, attr) cmds.setAttr(full_attr, lock=False) connections = cmds.listConnections(full_attr, plugs=True, destination=False, source=True) if connections: cmds.disconnectAttr(connections[0], full_attr) # Reset and hide Locator cmds.setAttr("{}.t".format(locator), 0, 0, 0) cmds.setAttr("{}.r".format(locator), 0, 0, 0) cmds.setAttr("{}.s".format(locator), 1, 1, 1) cmds.setAttr("{}.visibility".format(locator), 0) # 3. Add IKFK_Seamless attribute to Switch Ctrl if not cmds.attributeQuery("IKFK_Seamless", node=Switch_Ctrl, exists=True): cmds.addAttr(Switch_Ctrl, longName="IKFK_Seamless", attributeType="enum", enumName="IK:FK:", keyable=False) cmds.setAttr("{}.IKFK_Seamless".format(Switch_Ctrl), channelBox=True) # 4. Add config attributes to Locator and connect config_map = { "IKFK_Seamless_Switching": LOCATOR_NAME, "FK_Joint_Root": FK_Joint_Root, "FK_Joint_Mid": FK_Joint_Mid, "FK_Joint_End": FK_Joint_End, "FK_Ctrl_Root": FK_Ctrl_Root, "FK_Ctrl_Mid": FK_Ctrl_Mid, "FK_Ctrl_End": FK_Ctrl_End, "IK_Joint_Root": IK_Joint_Root, "IK_Joint_Mid": IK_Joint_Mid, "IK_Joint_End": IK_Joint_End, "IK_Ctrl_Root": IK_Ctrl_Root, "IK_Ctrl_Pole": IK_Ctrl_Pole, "Switch_Ctrl": Switch_Ctrl, "Switch_Attr": Switch_Attr, } for attr_name, value in config_map.items(): attr_full_name = "{}.{}".format(locator, attr_name) if not cmds.attributeQuery(attr_name, node=locator, exists=True): cmds.addAttr(locator, longName=attr_name, dataType="string", keyable=True) cmds.setAttr(attr_full_name, value, type="string") # 6. Set up scriptJob (Uses the full module path) # Note: Old scriptJobs will be automatically replaced when the same attribute is monitored # The command string for scriptJob - pure Python code # Format: 'import module; module.sg_switching("locator_name")' command = 'import {0}; {0}.sg_switching("{1}")'.format(MODULE_NAME, LOCATOR_NAME) try: cmds.scriptJob(attributeChange=["{}.IKFK_Seamless".format(Switch_Ctrl), command], killWithScene=True) cmds.inViewMessage(message='IK/FK Seamless Switching Built for: {}'.format(Switch_Ctrl), position='topCenter', fade=True) except Exception as e: cmds.error("Failed to set up scriptJob: {}. Command was: {}".format(e, command)) return if __name__ == "__main__": IKFK_Switch_UI()