I am writing a Maya/Python script that uses a button callback to select a control, run a script on the selection, and then advance to the next keyframe and repeat.
However, selecting a control in the viewport and selecting a control via script don’t seem to behave the same.
If I select a control in the viewport and then run a script that advances to the next keyframe, it works as I would expect.
But if the selection is cleared, and I select the same control within the script and then advance to the next keyframe, the selection gets updated but it doesn’t advance to the next keyframe.
I’ve tried forcing a refresh or force updating the viewport as part of the script but that doesn’t seem to help.
Does cmds.select() not select keyframe data? Or is the selection not fully updating until the script has finished running?
Any ideas on what I’m missing would be much appreciated?
Below is a simple UI that illustrates what I’m trying to do. It requires a single object named “body_anim” with some keyframes set on it.
If something is already selected in the viewport and I press the button it works as expected, but if I clear the selection in the viewport and then run the script it doesn’t advance to the next keyframe.
In the grand tradition of answering a question with a question, is there a reason you are not getting the keyframe data from the object you are selecting, rather than the time control?
For example, replacing your frameStep() func with:
def frameStep(*args):
sel = cmds.ls(sl=True)
frame = cmds.findKeyframe(sel[0], which = "next")
cmds.currentTime(frame, update = True, edit = True)
I haven’t looked at when the time control actually gets updated, but I know this code snippet seems to behave how you want.
With nothing selected, if I click the button twice, it behaves correctly.
Here’s some horrible code that makes it work with one click (but will fail if you’re trying to go from the last keyframe to the first keyframe, and require two clicks…).
import maya.cmds as cmds
def FrameStepUI():
windowName = "FrameStep"
if cmds.window(windowName, exists=True):
cmds.deleteUI(windowName)
window = cmds.window(windowName, title=windowName, w=100, h=100, mnb=False, mxb=False, sizeable = False)
mainLayout = cmds.columnLayout(w=100, h=100)
selectNextFrameBtn = cmds.button(label = "Select + Next Frame", c = selectFrameStep)
cmds.showWindow(window)
def frameStep(*args):
current_time = cmds.currentTime(query = True)
flagged = selectCtrl()
if flagged:
next = cmds.findKeyframe('body_anim', time = (current_time, current_time), which = 'next')
cmds.currentTime(next, edit = True, update = True)
cmds.currentTime((cmds.findKeyframe(timeSlider = True, which = "next")), update = True, edit = True)
def selectCtrl():
sel = cmds.ls(selection = True)
flagged = False
if len(sel) == 0:
flagged = True
cmds.select("body_anim", replace = True)
return flagged
def selectFrameStep(*args):
frameStep()
FrameStepUI()
You’re right Phil, it looks like there is no need to get the key data from the time control and that I can just get it from the object directly. I thought for some reason I could only get the keyframe info from the time control. Oops.
So thanks for answering my question with a question. :): I’m still learning this stuff, and the question helped me think about the way I was approaching it.
It looks like my problem was in using the timeSlider flag. The timeslider doesn’t seem to get updated until after the script has finished running and forceRefreshing the time control is unnecessary if I can get the data from the object directly like you said.