Plugin Cafe Homepage
Forum Home Forum Home > Plugin Cafe > PYTHON Development
  New Posts New Posts
  FAQ FAQ  Forum Search

Selected Keys? Move Keys?

Page  12>
Author
Message
  Topic Search Topic Search
deepshade View Drop Down
Member
Member


Joined: 2010 Dec 21
Online Status: Offline
Posts: 190
Direct Link To This Post Topic: Selected Keys? Move Keys?
    Posted: 2011 Feb 21 at 2:04am
Trying to find a way to identify selected timeline keys and move them

ie

3 items
3 tracks
select a range of  keys for all three items
move keys on the tracks by differing amounts

essentially running c4d.CallCommand(465001216) - the Move Scale Selected Keyframe command
on each track
with different MOVE values

any help appreciated

(thanks to Scott A so far) 

Back to Top
NiklasR View Drop Down
Member
Member


Joined: 2010 Dec 13
Location: Germany
Online Status: Offline
Posts: 2575
Direct Link To This Post Posted: 2011 Feb 22 at 11:23pm
Well, to move them should not be a problem. But to find the selected ones would be ..
I couldn't find a function doing so .. :/

//edit:
I think I found it out, but I couldn't test it, yet. Try this:

 

def GetSelectedKeys(curve):
    # Returns the selected Keys in a Curve and their count
    cnt = curve.GetKeyCount()
    keylist = list()
    key_i = 0
    for i in xrange(cnt):
        k = curve.GetKey(i)
        if k.GetBit(BIT_ACTIVE) is True:
             l[key_i] = k
             key_i = key_i+1
    return keylist, key_i
 


If the syntax for the list obejct is wrong, please correct me. I don't understand much of them yet. :/

Cheers, nux


Edited by nux95 - 2011 Feb 22 at 11:42pm
Back to Top
deepshade View Drop Down
Member
Member


Joined: 2010 Dec 21
Online Status: Offline
Posts: 190
Direct Link To This Post Posted: 2011 Feb 23 at 12:17am
much appreciate the help
Back to Top
ScottA View Drop Down
Member
Member


Joined: 2011 Jan 07
Online Status: Offline
Posts: 2288
Direct Link To This Post Posted: 2011 Feb 23 at 12:07pm
Nux.
I have all the pieces to make this key mover script work. But half of it is in Coffee form and needs to be converted to python. And I'm horrible with python loops. So maybe you (or anyone else) can take all the parts and put them together in python?

The goal is to move only selected keys in the timeline by an offset value.

Coffee does not have NBit abilities. So AFAIK it can't find selected keys.
So here is how to move all the keys on all of the tracks on an object using Coffee:

//This script moves keys in a positive direction

var offset = 10;// change this value as desired

var trk = op->GetFirstCTrack();// Get the first track
if(!trk) // Error handling if object has no tracks
 {
 TextDialog("No tracks found on object!\n" + "Please create one", DLG_OK + DLG_ICONEXCLAMATION);
 return;
 }
while(trk)
  {
  var curve = trk->GetCurve(CCURVE_CURVE, FALSE); // R12 version
  var i;
  for(i=curve->GetKeyCount()-1; i>=0 ; --i) // Get all of the keys
    {
    var keys = curve->GetKey(i);
    var keytime = keys->GetTime(); // Get's the key's baseTime value
    var keyframe = keytime->GetFrame(30);//Get the frame the key is on based on a project setting of 30fps
    var newframe = doc->GetTime();
    newframe->SetFrame(keyframe + offset, 30); //Move the key by offset value from where it currently is
    keys->SetTime(curve,newframe); // Move the key to the value of newframe
    EventAdd(EVMSG_FCURVECHANGE);//update the F-Curve timeline changes
    }
 trk = trk->GetNext(); // Get the next track
  }




Here is an example of getting only the selected keys on the first track using GetNBit() and python:
#This code checks if any keys in the first track are selected

import c4d
from c4d import gui

def main():
    obj = doc.GetActiveObject()
    trk = obj.GetFirstCTrack()
    curve = trk.GetCurve()
    cnt = curve.GetKeyCount()
    for i in xrange(cnt):
     keys = curve.GetKey(i)
     if keys.GetNBit(5) is 1: # 5 is the int id for NBIT_TL1_SELECT           
      print keys.GetValue() #prints the keys value   
   

if __name__=='__main__':
    main()



What needs to be done is the Coffee code example needs to be converted to Python. And then combined with the GetNBit() functionality so that it loops through all of the tracks on an object. And only moves the ones that are selected by the offset value.

I'm just not good enough with python loops yet to do this myself.

-ScottA
Back to Top
NiklasR View Drop Down
Member
Member


Joined: 2010 Dec 13
Location: Germany
Online Status: Offline
Posts: 2575
Direct Link To This Post Posted: 2011 Feb 23 at 1:58pm
Yea, I'll try to write this in Python now. Smile I didn't know where to start, because I'm absolutely not familiar with Animation stuff and so on. .. @_@ But now I have a reference of the COFFEE Version.
Seems like we're a good Team LOL

I can only be on a mobile device for today, but I'll write the Raw Code on it ow and the search Bugs on  my computer tomorrow.
I'll be back with new Infos then. :)

Cheers, nux


Edited by nux95 - 2011 Feb 23 at 2:02pm
Back to Top
deepshade View Drop Down
Member
Member


Joined: 2010 Dec 21
Online Status: Offline
Posts: 190
Direct Link To This Post Posted: 2011 Feb 23 at 2:33pm
Trying my best to follow this. Not sitting idly by.
So far I can only get key values on one track.
And the selection of key seems to depend on the object selection, rather than just a key selection
Confused - but learning as I go. Thanks again to all

Back to Top
ScottA View Drop Down
Member
Member


Joined: 2011 Jan 07
Online Status: Offline
Posts: 2288
Direct Link To This Post Posted: 2011 Feb 23 at 3:34pm
Originally posted by deepshade

So far I can only get key values on one track.


Yes. The python example I posted only loops though all the keys on a single track using a for loop.
In order to get the keys on the other tracks. That for loop would need to be inside of a while loop to move on to the next track. which isn't that difficult to do.
The hard part is that you have to get the last key first as you loop through all of this stuff. Because if you move the first keys in order. They will end up on top of eachother. And the loop will fail.
Think of a train pulling it's cargo cars.

The code in Coffee to do that looks like this: for(i=curve->GetKeyCount()-1; i>=0 ; --i)
Which means. Start from the last key. Then work your way back every time the loop runs.
I don't know how to write that kind of loop in python yet.


Originally posted by deepshade


And the selection of key seems to depend on the object selection, rather than just a key selection
Confused - but learning as I go. Thanks again to all


Sort of. It's a case of Parent ->Child relationships.
The document is the parent of all objects. So to get at objects in python you write doc.GetActiveObject(). Where doc is the parent, and the selected object is the child.

The same rule goes for things attached to objects.
-Tracks are the children of objects.
--Curves are the children of tracks
---Keys are the children of curves.

So you almost never grab things directly in C4D. You get the parent. Then drill down into it's children until you reach the item you want to manipulate.
Hope that makes sense.

-ScottA


Edit* - I just found this with Google. Turns out it's not as hard to do a revese loop in python as I thought. In fact it's pretty darned simple:
    cnt = curve.GetKeyCount()
    for i in reversed(xrange(cnt)):
     keys = curve.GetKey(i)
     if keys.GetNBit(5) is 1: # If a key is selected          
      print keys.GetValue() #print the key's value


We'll get it working eventually deepshade. Smile



Edited by ScottA - 2011 Feb 23 at 4:27pm
Back to Top
NiklasR View Drop Down
Member
Member


Joined: 2010 Dec 13
Location: Germany
Online Status: Offline
Posts: 2575
Direct Link To This Post Posted: 2011 Feb 23 at 9:09pm
Uhm. just by the way: Why do we need a reversed loop ?
Back to Top
NiklasR View Drop Down
Member
Member


Joined: 2010 Dec 13
Location: Germany
Online Status: Offline
Posts: 2575
Direct Link To This Post Posted: 2011 Feb 23 at 9:55pm
Now, i got it all work. Only thing missing is the GetNBit, cause I have not enough Time now. I'll post the full code later on. Smile
But a look in the SDK makes me think that CKeys doesn't have the function GetNBit because it's a method of the GeListNode Class.. ?

PS: Still don't understand why you used an Invers Iteration.

//edit:

So, here's the complete function: Smile
Even with Undo-Functionality ;-)

def MoveSelectedKeys(op,offset,timeln = 1):    # offset in frames
    import c4d

    if (not op) or (not offset): return False

    if timeln == 1: timeln = c4d.NBIT_TL1_SELECT
    elif timeln == 2: timeln = c4d.NBIT_TL2_SELECT
    elif timeln == 3: timeln = c4d.NBIT_TL3_SELECT
    elif timeln == 4: timeln = c4d.NBIT_TL4_SELECT
    else:
        import math
        timeln = math.fmod(timeln,4)
        MoveSelectedKeys(op,offset,timeln)

    doc = op.GetDocument()
    doc.StartUndo()

    track = op.GetFirstCTrack()
    if not track: return False

    fps = float(doc.GetFps())

    while track:
        curve = track.GetCurve()
        cnt = curve.GetKeyCount()
        for i in xrange(cnt):
            key = curve.GetKey(i)
            if key.GetNBit(int(timeln)) == True:
                tme = key.GetTime()
                doc.AddUndo(c4d.UNDOTYPE_CHANGE,op)
                key.SetTime(curve,c4d.BaseTime(offset/fps+tme.Get()))
        track = track.GetNext()
    doc.EndUndo()
    c4d.EventAdd(c4d.MSG_UPDATE)

    return True


Is there a bit for keys in the "usual" timeline ? This one does only affect the selected keys in the Manager .. Confused

Cheers, nux


Edited by nux95 - 2011 Feb 24 at 5:28am
Back to Top
ScottA View Drop Down
Member
Member


Joined: 2011 Jan 07
Online Status: Offline
Posts: 2288
Direct Link To This Post Posted: 2011 Feb 24 at 7:56am
Good job nux.
I like how you implemented the other timeline windows.

I needed to use a reverse loop in Coffee because if I had for example: keys ten frames apart and an offset value of ten. The first key would get moved on top of the last key. Then when the loop came around a second time. The keys were all on the same frame and it couldn't figure out what to move.
I didn't know of any other way to do it.


Deepshade,
Just in case you don't know this. What nux posted is a method.
So if you want to use this in the python script manager. You paste it above the def main section.
Then call to the method like this:

def main():
    obj = doc.GetActiveObject()
    offset = 15 #change this value as needed
    MoveSelectedKeys(obj,offset,timeln = 1)

if __name__=='__main__':
    main()


-ScottA



Back to Top
deepshade View Drop Down
Member
Member


Joined: 2010 Dec 21
Online Status: Offline
Posts: 190
Direct Link To This Post Posted: 2011 Feb 24 at 8:34am
OMG Smile - just dropped in for a look. Middle of some exhibition visual at present. Back this evening. Will have a good read later.

cheers all
Back to Top
NiklasR View Drop Down
Member
Member


Joined: 2010 Dec 13
Location: Germany
Online Status: Offline
Posts: 2575
Direct Link To This Post Posted: 2011 Feb 24 at 8:34am
Let me add, that in the SkriptManager op is automatically the selected Object. :)
And you don't need to overload the timeln since you don't want timeline 2,3 or 4.
So this works too:


import c4d
def main():
    offset = 15 #change this value as needed
    MoveSelectedKeys(op,offset)

if __name__=='__main__':
    main()
Back to Top
deepshade View Drop Down
Member
Member


Joined: 2010 Dec 21
Online Status: Offline
Posts: 190
Direct Link To This Post Posted: 2011 Feb 25 at 3:47am
Hi All

Sorry for lack of contribution - yesterdays work over ran
try to get back to this as soon as job is out of the door
Back to Top
deepshade View Drop Down
Member
Member


Joined: 2010 Dec 21
Online Status: Offline
Posts: 190
Direct Link To This Post Posted: 2011 Feb 26 at 2:07am
Hi all

Finally escaped from the last job.

A couple of things

1
Confusion
Just to confirm - the original concept was for staggered keys on multiple objects
Don't get me wrong - not complaining - just need to be clear what I'm seeing 

The code is there in the while loop looks like it iterates through all the tracks of a single selected object.
If I select more than one object - nothing happens? (no provision in script so far)

2
I'd assume (if you want to modify keys differently for different objects) it would be better to write a small function 
and modify the Key value before running Key.SetTime(...etc 
dependent on the number of selected tracks
Sadly I cant see a way to return the count of selected tracks (yes Scott I now see the route) - so I guess 
the whole things needs to run in an outer loop - iterating through the selected objects?

Another issue  - if you modify the offset inside the While track loop
ie after
key.SetTime..
with something as simple as offset = offset + 10
Assuming you have a POS ROT SCALE tracks as well
Selecting all tracks is fine
Selecting just one track ie POS - inserts two extra sets of keyframes
as the while loop count is based on the total number of tracks
Cant see a simple solution


3
can't find reference to
C4d.NBIT_TL1_SELECT in the SDK - I take it these refer to the 4 timelines
Where did you get that info pls?



def MoveSelectedKeys(op,offset,timeln = 1):    # offset in frames
    import c4d

    if (not op) or (not offset): return False

    if timeln == 1: timeln = c4d.NBIT_TL1_SELECT
    elif timeln == 2: timeln = c4d.NBIT_TL2_SELECT
    elif timeln == 3: timeln = c4d.NBIT_TL3_SELECT
    elif timeln == 4: timeln = c4d.NBIT_TL4_SELECT
    else:
        import math
        timeln = math.fmod(timeln,4)
        MoveSelectedKeys(op,offset,timeln)

    doc = op.GetDocument()
    doc.StartUndo()

    track = op.GetFirstCTrack()
    if not track: return False

    fps = float(doc.GetFps())

    while track:
        curve = track.GetCurve()
        cnt = curve.GetKeyCount()
        for i in xrange(cnt):
            key = curve.GetKey(i)
            if key.GetNBit(int(timeln)) == True:
                tme = key.GetTime()
                doc.AddUndo(c4d.UNDOTYPE_CHANGE,op)
                key.SetTime(curve,c4d.BaseTime(offset/fps+tme.Get()))
        track = track.GetNext()
    doc.EndUndo()
    c4d.EventAdd(c4d.MSG_UPDATE)

    return True


import c4d
def main():
    offset = 50 #change this value as needed
    MoveSelectedKeys(op,offset) 

if __name__=='__main__':
    main()



Edited by deepshade - 2011 Feb 26 at 2:34am
Back to Top
NiklasR View Drop Down
Member
Member


Joined: 2010 Dec 13
Location: Germany
Online Status: Offline
Posts: 2575
Direct Link To This Post Posted: 2011 Feb 26 at 3:06am
1:
You can simply use the function in an iteration through the selected objects.

What means staggered ? Don't know the word ^^

2:
Why modifieng the
key value ? I thought it's about moving keys ?
And what do you want to achieve with offset = offset + 10

Don't select a track, select the keys.

3:
It's in the GetNBits() function. Search for it without the c4d. in front.

I'll can answer your questions better later when im at home in about 2 hours.
Till then,

cheers
Back to Top
deepshade View Drop Down
Member
Member


Joined: 2010 Dec 21
Online Status: Offline
Posts: 190
Direct Link To This Post Posted: 2011 Feb 26 at 4:32am
What means staggered ? Don't know the word ^^ 

All explained here
http://forums.cgsociety.org/showthread.php?f=182&t=959094
with sample file (first post)

file shows example of before and after stagger

hth



Back to Top
NiklasR View Drop Down
Member
Member


Joined: 2010 Dec 13
Location: Germany
Online Status: Offline
Posts: 2575
Direct Link To This Post Posted: 2011 Feb 26 at 5:23am
Ah, you mean the 2nd Cube should start moving when the 1st one finished ?
That shouldn't be a problem.
I'll be back in a minute with a function doing this.

cheers

PS: Do still only the selected keys need to be moved ?

//So, this is a bit more complex than i thought. LOL
But it works fine :)

import c4d
from c4d import documents

overlap = 20    ## Change this to the Frame you want to let the keys beeing overlapped

def MoveSelectedKeys(op,offset,timeln = 1):    # offset in frames
    import c4d

    if (not op) or (not offset): return False

    if timeln == 1: timeln = c4d.NBIT_TL1_SELECT
    elif timeln == 2: timeln = c4d.NBIT_TL2_SELECT
    elif timeln == 3: timeln = c4d.NBIT_TL3_SELECT
    elif timeln == 4: timeln = c4d.NBIT_TL4_SELECT
    else:
        import math
        timeln = math.fmod(timeln,4)
        MoveSelectedKeys(op,offset,timeln)

    doc = op.GetDocument()

    track = op.GetFirstCTrack()
    if not track: return False

    fps = float(doc.GetFps())

    while track:
        curve = track.GetCurve()
        cnt = curve.GetKeyCount()
        for i in xrange(cnt):
            key = curve.GetKey(i)
            if key.GetNBit(int(timeln)) == True:
                tme = key.GetTime()
                key.SetTime(curve,c4d.BaseTime(offset/fps+tme.Get()))
        track = track.GetNext()
    c4d.EventAdd(c4d.MSG_UPDATE)

    return True

def GetLastSelectedKey(track, timeln = 1):
    import c4d

    if timeln == 1: timeln = c4d.NBIT_TL1_SELECT
    elif timeln == 2: timeln = c4d.NBIT_TL2_SELECT
    elif timeln == 3: timeln = c4d.NBIT_TL3_SELECT
    elif timeln == 4: timeln = c4d.NBIT_TL4_SELECT
    else:
        import math
        timeln = math.fmod(timeln,4)
        MoveSelectedKeys(op,offset,timeln)

    curve = track.GetCurve()
    cnt = curve.GetKeyCount()

    for i in xrange(cnt):
        key = curve.GetKey(cnt-i-1)
        if key.GetNBit(int(timeln)) == True:
            return key

def GetHNext(op):
    if not op: return
    if op.GetDown(): return op.GetDown()
    while not op.GetNext() and op.GetUp():
        op = op.GetUp()
    return op.GetNext()

def GetActiveObjects(doc):
    import c4d
    lst = list()
    op = doc.GetFirstObject()
    while op:
        if op.GetBit(c4d.BIT_ACTIVE) == True: lst.append(op)
        op = GetHNext(op)
    return lst


def main():
    doc = documents.GetActiveDocument()
    doc.StartUndo()

    lst = GetActiveObjects(doc)
    op = lst[0]
    if not op: return False

    key = GetLastSelectedKey(op.GetFirstCTrack())
    if not key: offset = 0
    else: offset = key.GetTime().GetFrame(doc.GetFps())-overlap


    for i in xrange(len(lst)):
        doc.AddUndo(c4d.UNDOTYPE_CHANGE,op)
        MoveSelectedKeys(op,offset)
        key = GetLastSelectedKey(op.GetFirstCTrack())
        if not key: offset = 0
        else: offset = key.GetTime().GetFrame(doc.GetFps())-overlap
        print op.GetName()
        op = lst[i+1]
    doc.EndUndo()



if __name__=='__main__':
    main()



Here a tiny Screencast to show you how to use it:

"Stagger Keys" Cinema 4D Python Script




Edited by nux95 - 2011 Feb 26 at 6:37am
Back to Top
deepshade View Drop Down
Member
Member


Joined: 2010 Dec 21
Online Status: Offline
Posts: 190
Direct Link To This Post Posted: 2011 Feb 26 at 12:08pm
Hey Nux95

This is just great

And there's so much useful stuff here for reference
It's really appreciated  - many thanks

line 103 
op=list[i+1]
is throwing an error
index out of range

I guess this is just incrementing one object too many? at the end of the selected objects



Not wanting to take anything away from what you've done here - far beyond the call of duty :)
I think it would be nice to have the 'processing' part of the script possibly
in a function

ie release a collection of key manipulating scripts - by just changing the function

stagger - by offset - as we have now
stagger  - by total time - total time over which to stagger all frames
stagger exponentially large to small
stagger exponentially small to large
etc etc

cheers again


Back to Top
NiklasR View Drop Down
Member
Member


Joined: 2010 Dec 13
Location: Germany
Online Status: Offline
Posts: 2575
Direct Link To This Post Posted: 2011 Feb 26 at 1:22pm
Originally posted by deepshade

line 103 
op=list[i+1]
is throwing an error
index out of range

Do you recieve that message every time ? It didn't appear when I was using the Script.
I guess this is just incrementing one object too many? at the end of the selected objects

Shouldnt. 'op' is already the object with index 0, and 'i' starts with '0', so the next object for 'op' in the first iteration should be 1 => 'i+1'
But .. Hm, very bizarre. I didn't recieve that error, but as I think about it, that error MUST occure at the end of the loop.
Try it like this and tell me if the problen still exists:

op = lst[i+1]
if not op: break # Hopefully there is a 'break' expression for 'for'-loops. If not try something that stops the loop


stagger  - by total time - total time over which to stagger all frames
stagger exponentially large to small
stagger exponentially small to large
etc etc

Why not writing a Plugin ? ;-)

cheers, nux


Edited by nux95 - 2011 Feb 26 at 1:23pm
Back to Top
deepshade View Drop Down
Member
Member


Joined: 2010 Dec 21
Online Status: Offline
Posts: 190
Direct Link To This Post Posted: 2011 Feb 27 at 3:10am
Using

if i+1 < len(lst):
op = lst[i+1]

got rid of the index error

As far as writing a plugin. A bit beyond me at present, probably best to learn from your foundation work here Nux.

Back to Top
Page  12>

Forum Jump Forum Permissions View Drop Down

Bulletin Board Software by Web Wiz Forums® version 9.61 [Free Express Edition]
Copyright ©2001-2009 Web Wiz

This page was generated in 0.125 seconds.