scalist
"""
#######################################################################
scale_keys v1.5
by Eric Luhta
Last update: 12/20/2016
Makes scaling curves in the graph editor vastly more powerful and simple.
INSTALL:
- Place the scale_keys.py script in your maya scripts folder
- Place the scale_keys.png and the scale_keys_icon.png files in your ../icons folder
TO RUN:
Save the following as a python shelf button and add the scalist_icon for maximum performance.
import scale_keys
reload(scale_keys)
scale_keys
#######################################################################
"""
import pymel.core as pm
__author__ = 'Eric Luhta'
########################################################################
# Scalist Class
########################################################################
class Scalist(object):
def __init__(self, pivot, user_scale, scale_type):
self.pivot = pivot
self.scale = user_scale.getValue()
self.curves = pm.keyframe(query=True, selected=True, name=True)
self.key_values = pm.keyframe(query=True, selected=True, valueChange=True, absolute=True)
self.key_times = pm.keyframe(query=True, selected=True, timeChange=True, absolute=True)
self.scale_type = scale_type
#####################################################################
# Pivot functions
def get_pivot(self):
"""Returns the right function to determine the pivot based on a dictionary argument"""
switcher = {'pivot_zero_value': self.pivot_zero_value,
'pivot_highest_value': self.pivot_highest_value,
'pivot_lowest_value': self.pivot_lowest_value,
'pivot_middle_value': self.pivot_middle_value,
'pivot_last_selected_value': self.pivot_last_selected_value,
'pivot_first_time': self.pivot_first_time,
'pivot_last_time': self.pivot_last_time,
'pivot_current_time': self.pivot_current_time,
'pivot_last_selected_time': self.pivot_last_selected_time,
'pivot_flip_curve_value': self.pivot_flip_curve_value,
'pivot_first_value': self.pivot_first_value,
'pivot_ramped_value': self.pivot_ramped_value}
# Get the function from the switcher dictionary
func = switcher.get(self.pivot)
return func()
def pivot_zero_value(self):
"""Returns 0 for using it as a pivot point"""
return 0
def pivot_highest_value(self):
"""Returns the value of the highest keyframe in the active selection"""
return max(self.key_values)
def pivot_lowest_value(self):
"""Returns the value of the lowest keyframe in the active selection"""
return min(self.key_values)
def pivot_middle_value(self):
"""Returns the middle value of the current active selection"""
return ((max(self.key_values) + min(self.key_values)) / 2)
def pivot_last_selected_value(self):
"""Returns the value of the last selected key"""
pivot = pm.keyframe(query=True, lastSelected=True, valueChange=True)
# Pymel returns a set with lastSelected flag, so make sure it only sends the value
return pivot[0]
def pivot_first_time(self):
"""Returns the first key time of the active selection"""
return min(self.key_times)
def pivot_last_time(self):
"""Returns the last key time of the active selection"""
return max(self.key_times)
def pivot_current_time(self):
"""Returns the current frame"""
return pm.currentTime(query=True)
def pivot_last_selected_time(self):
"""Returns the time of the last selected keyframe"""
pivot = pm.keyframe(query=True, lastSelected=True, timeChange=True)
# Pymel returns a set with lastSelected flag, so make sure it only sends the value
return pivot[0]
def pivot_first_value(self):
"""Returns the value of the first key in each curve"""
return self.key_values[0]
def pivot_flip_curve_value(self):
"""Returns -1 to invert selected curves"""
self.scale = -1
return self.pivot_middle_value()
def pivot_ramped_value(self):
"""Scales gradually over the selected range"""
pass
#############################################################################
# Scale functions
def scale_keys_value(self):
"""Scales all selected keys in value from a single pivot for all"""
pm.scaleKey(valuePivot=self.get_pivot(), valueScale=self.scale)
def scale_keys_value_multi(self):
"""Scales each selected curve's keys independently on their own pivots"""
# Go through each curve, finding the times and values for each and apply the pivot for that curve
for curve in self.curves:
self.key_values = self.get_key_values(curve)
self.key_times = self.get_key_times(curve)
pivot = self.get_pivot()
for i in xrange(len(self.key_values)):
pm.scaleKey(curve, valuePivot=pivot, valueScale=self.scale, time=(self.key_times[i], self.key_times[i]))
def scale_keys_time(self):
"""Scales all selected keys in time from a single pivot for all"""
pm.scaleKey(timePivot=(self.get_pivot()), timeScale=self.scale)
# Snap all keys so there are no subframe keys
pm.snapKey(tm=1.0)
def scale_keys_time_multi(self):
"""Scales each selected curve's keys independently in time on their own pivots"""
for curve in self.curves:
self.key_times = self.get_key_times(curve)
pm.scaleKey(curve, timePivot=(self.get_pivot()), timeScale=self.scale)
pm.snapKey(tm=1.0)
##########################################################################
# Helper functions
def get_scale_type(self):
"""For the UI, a switcher to pick the appropriate type of scaling based on a passed value"""
scale_operations = {'scale_keys_value': self.scale_keys_value,
'scale_keys_value_multi': self.scale_keys_value_multi,
'scale_keys_time': self.scale_keys_time,
'scale_keys_time_multi': self.scale_keys_time_multi}
func = scale_operations.get(self.scale_type)
return func()
def get_curves(self):
"""Get a list of the names of any selected curves"""
return pm.keyframe(query=True, selected=True, name=True)
def get_key_values(self, curve):
"""Get list of values for an individual curve"""
return pm.keyframe(curve, query=True, selected=True, valueChange=True, absolute=True)
def get_key_times(self, curve):
"""Get list of times for an individual curve"""
return pm.keyframe(curve, query=True, selected=True, timeChange=True, absolute=True)
#############################################################################
# Scaling tool functions
def check_for_selected_keys():
""" Makes sure there are keys selected in the graph editor """
selected_keys = pm.keyframe(q=True, sl=True)
if len(selected_keys) >= 2:
return True
pm.warning('[scalist.py] Please select at least 2 keyframes.'),
return False
def do_scale(pivot, user_scale, scale_type):
"""If selection is ok, create an instance and do the scaling"""
if check_for_selected_keys():
scalist = Scalist(pivot, user_scale, scale_type)
scalist.get_scale_type()
# UI
class Window_UI:
def __init__(self):
if pm.window('scale_keys', exists=True):
pm.deleteUI('scale_keys')
self.window_id = 'scale_keys'
def update_slider(self, ctrl, val):
"""updates the slider when a scale amount preset button is clicked"""
pm.floatSliderGrp(ctrl, edit=True, v=val)
def rgb(self, values):
"""converts rgb values to 0.0-1.0 for maya flags"""
converted = []
for val in values:
converted.append(round(val / 255.0, 3))
return converted
def build_ui(self):
"""builds the ui window"""
tool_window = pm.window(self.window_id, title="Scale Keys", width=368, height=295, mnb=False, mxb=False,
sizeable=True)
main_layout = pm.rowColumnLayout(w=368, h=295)
# get the header image from the user's prefs
imagePath = pm.internalVar(upd=True) + "icons/scale_keys.png"
pm.image(w=225, h=75, image=imagePath)
# scale amount slider
user_scale = pm.floatSliderGrp(label='Amount', field=True, precision=2, width=363, minValue=-2.0, maxValue=5.0,
v=1.0, fieldMinValue=-10.0, fieldMaxValue=10.0)
# scale preset buttons
btn_layout = pm.rowColumnLayout(nc=11)
btn_1 = pm.button(label='-1', w=33, bgc=self.rgb([231, 205, 59]),
c=pm.Callback(self.update_slider, user_scale, -1))
btn_2 = pm.button(label='.25', w=33, c=pm.Callback(self.update_slider, user_scale, 0.25))
btn_3 = pm.button(label='.50', w=33, c=pm.Callback(self.update_slider, user_scale, 0.5))
btn_4 = pm.button(label='.75', w=33, c=pm.Callback(self.update_slider, user_scale, 0.75))
btn_5 = pm.button(label='.90', w=33, c=pm.Callback(self.update_slider, user_scale, 0.9))
btn_6 = pm.button(label='reset', w=33, bgc=self.rgb([231, 205, 59]),
c=pm.Callback(self.update_slider, user_scale, 1.0))
btn_7 = pm.button(label='1.1', w=33, bgc=self.rgb([215, 215, 215]),
c=pm.Callback(self.update_slider, user_scale, 1.1))
btn_8 = pm.button(label='1.25', w=33, bgc=self.rgb([215, 215, 215]),
c=pm.Callback(self.update_slider, user_scale, 1.25))
btn_9 = pm.button(label='1.5', w=33, bgc=self.rgb([215, 215, 215]),
c=pm.Callback(self.update_slider, user_scale, 1.5))
btn_10 = pm.button(label='1.75', w=33, bgc=self.rgb([215, 215, 215]),
c=pm.Callback(self.update_slider, user_scale, 1.75))
btn_11 = pm.button(label='x2', w=33, bgc=self.rgb([231, 205, 59]),
c=pm.Callback(self.update_slider, user_scale, 2.0))
# headers
pm.setParent(main_layout)
pm.separator(style='none', h=5)
categories = pm.rowColumnLayout(nc=3)
pm.text(label='Value', w=177, font='boldLabelFont', bgc=self.rgb([231, 205, 59]))
pm.separator(style='none', w=10)
pm.text(label='Time', w=179, font='boldLabelFont', bgc=self.rgb([20, 20, 20]))
pm.separator(style='none', h=5)
pm.separator(style='none', w=10)
pm.separator(style='none', h=5)
# pivot buttons
pm.setParent('..')
pivot_buttons = pm.rowColumnLayout(nc=5)
pb1 = pm.button(label='Mid', w=88, annotation='Scaled from midpoint value of curve',
bgc=self.rgb([215, 215, 215]),
command=pm.Callback(do_scale, 'pivot_middle_value', user_scale, 'scale_keys_value'))
pb2 = pm.button(label='Multi', w=89, annotation='Each curve scaled from its own midpoint',
bgc=self.rgb([45, 45, 45]),
command=pm.Callback(do_scale, 'pivot_middle_value', user_scale, 'scale_keys_value_multi'))
pm.separator(style='none', w=10)
pb3 = pm.button(label='First', w=87, annotation='Scaled from first frame of selection',
bgc=self.rgb([120, 120, 120]),
command=pm.Callback(do_scale, 'pivot_first_time', user_scale, 'scale_keys_time'))
pb4 = pm.button(label='Multi', w=88, annotation='Each curve scaled from its first frame',
bgc=self.rgb([45, 45, 45]),
command=pm.Callback(do_scale, 'pivot_first_time', user_scale, 'scale_keys_time_multi'))
pb5 = pm.button(label='Highest', w=87, annotation='Scaled from the highest key value selected',
bgc=self.rgb([215, 215, 215]),
command=pm.Callback(do_scale, 'pivot_highest_value', user_scale, 'scale_keys_value'))
pb6 = pm.button(label='Multi', w=88, annotation='Each curve scaled from its highest selected key',
bgc=self.rgb([45, 45, 45]),
command=pm.Callback(do_scale, 'pivot_highest_value', user_scale, 'scale_keys_value_multi'))
pm.separator(style='none', w=10)
pb7 = pm.button(label='Last', w=89, annotation='Scaled from last frame of selection',
bgc=self.rgb([120, 120, 120]),
command=pm.Callback(do_scale, 'pivot_last_time', user_scale, 'scale_keys_time'))
pb8 = pm.button(label='Multi', w=88, annotation='Each curve scaled from its last frame',
bgc=self.rgb([45, 45, 45]),
command=pm.Callback(do_scale, 'pivot_last_time', user_scale, 'scale_keys_time_multi'))
pb9 = pm.button(label='Lowest', w=87, annotation='Scaled from the lowest key value selected',
bgc=self.rgb([215, 215, 215]),
command=pm.Callback(do_scale, 'pivot_lowest_value', user_scale, 'scale_keys_value'))
pb10 = pm.button(label='Multi', w=88, annotation='Each curve scaled from its lowest selected key',
bgc=self.rgb([45, 45, 45]),
command=pm.Callback(do_scale, 'pivot_lowest_value', user_scale, 'scale_keys_value_multi'))
pm.separator(style='none', w=10)
pb11 = pm.button(label='Current', w=89, annotation='Scaled from the current frame in timerange',
bgc=self.rgb([120, 120, 120]),
command=pm.Callback(do_scale, 'pivot_current_time', user_scale, 'scale_keys_time'))
pm.separator(style='none')
pb12 = pm.button(label='0', w=87, annotation='Scaled from 0', bgc=self.rgb([215, 215, 215]),
command=pm.Callback(do_scale, 'pivot_zero_value', user_scale, 'scale_keys_value'))
pm.separator(style='none')
pm.separator(style='none', w=10)
pb13 = pm.button(label='Last Selected', w=89, annotation='Scaled in time from the last selected key frame',
bgc=self.rgb([120, 120, 120]),
command=pm.Callback(do_scale, 'pivot_last_selected_time', user_scale, 'scale_keys_time'))
pm.separator(style='none')
pb14 = pm.button(label='Last Selected', w=87, annotation='Scaled in value from the last selected key',
bgc=self.rgb([215, 215, 215]),
command=pm.Callback(do_scale, 'pivot_last_selected_value', user_scale, 'scale_keys_value'))
pm.separator(style='none')
pm.separator(style='none', w=10)
pm.separator(style='in')
pm.separator(style='in')
pb15 = pm.button(label='First', w=87, annotation='Each curve selected from its earliest selected key',
bgc=self.rgb([215, 215, 215]),
command=pm.Callback(do_scale, 'pivot_first_value', user_scale, 'scale_keys_value_multi'))
pm.separator(style='none')
pm.separator(style='none', w=10)
pb16 = pm.button(label='Flip', w=77, annotation='Flip each selected curve along its midpoint value',
bgc=self.rgb([231, 205, 59]),
command=pm.Callback(do_scale, 'pivot_flip_curve_value', user_scale, 'scale_keys_value_multi'))
pm.setParent('..')
pm.separator(h=10, style='in')
pm.showWindow(tool_window)
w = Window_UI()
w.build_ui()