Iterate



  • On 01/06/2017 at 05:45, xxxxxxxx wrote:

    Hi. I have some Python code that resets user data in a group. What I have written looks to be very cumbersome and it is very slow when I have a lot of resets. Here is what I have:

    import c4d

    def main() :
        
        ID_G_Vegas_RESET = 486
        ID_G_Motel_RESET = 485
        ID_G_Spitn_Kitten_RESET = 489
        ID_G_Vacancy_RESET = 490
        ID_G_Open_RESET = 491
        ID_G_Sports_Bar_RESET = 492
        ID_G_Heinek_RESET = 508
        ID_G_Claude_RESET = 509
        ID_G_Argon_RESET = 510
        ID_G_Xenon_RESET = 511

    RESET G ------------------------------------------------------------

    #RESET G_Vegas
        if obj[c4d.ID_USERDATA, ID_G_Vegas_RESET]:
            obj[c4d.ID_USERDATA, ID_G_Vegas_RESET] = False

    for id, bc in obj.GetUserDataContainer() :    
                if bc[c4d.DESC_PARENTGROUP][-1].id == 284: # G_Vegas Group
                    try:
                        obj[id] = bc[c4d.DESC_DEFAULT]
                    except TypeError:
                        pass
            
              
        #RESET G_Motel
        if obj[c4d.ID_USERDATA, ID_G_Motel_RESET]:
            obj[c4d.ID_USERDATA, ID_G_Motel_RESET] = False

    for id, bc in obj.GetUserDataContainer() :    
                if bc[c4d.DESC_PARENTGROUP][-1].id == 285: # G_Motel Group
                    try:
                        obj[id] = bc[c4d.DESC_DEFAULT]
                    except TypeError:
                        pass
                        
                        
        #RESET G_The Spit'n Kitten'
        if obj[c4d.ID_USERDATA, ID_G_Spitn_Kitten_RESET]:
            obj[c4d.ID_USERDATA, ID_G_Spitn_Kitten_RESET] = False

    for id, bc in obj.GetUserDataContainer() :    
                if bc[c4d.DESC_PARENTGROUP][-1].id == 287: # G_The Spit'n Kitten Group
                    try:
                        obj[id] = bc[c4d.DESC_DEFAULT]
                    except TypeError:
                        pass
                        
                        
        #RESET G_Vacancy
        if obj[c4d.ID_USERDATA, ID_G_Vacancy_RESET]:
            obj[c4d.ID_USERDATA, ID_G_Vacancy_RESET] = False

    for id, bc in obj.GetUserDataContainer() :    
                if bc[c4d.DESC_PARENTGROUP][-1].id == 289: # G_Vacancy Group
                    try:
                        obj[id] = bc[c4d.DESC_DEFAULT]
                    except TypeError:
                        pass
                        
                        
        #RESET G_Open
        if obj[c4d.ID_USERDATA, ID_G_Open_RESET]:
            obj[c4d.ID_USERDATA, ID_G_Open_RESET] = False

    for id, bc in obj.GetUserDataContainer() :    
                if bc[c4d.DESC_PARENTGROUP][-1].id == 291: # G_Open Group
                    try:
                        obj[id] = bc[c4d.DESC_DEFAULT]
                    except TypeError:
                        pass
                        
                        
        #RESET G_Sports Bar
        if obj[c4d.ID_USERDATA, ID_G_Sports_Bar_RESET]:
            obj[c4d.ID_USERDATA, ID_G_Sports_Bar_RESET] = False

    for id, bc in obj.GetUserDataContainer() :    
                if bc[c4d.DESC_PARENTGROUP][-1].id == 293: # G_Sports Bar
                    try:
                        obj[id] = bc[c4d.DESC_DEFAULT]
                    except TypeError:
                        pass
                        
                        
        #RESET G_Heinek
        if obj[c4d.ID_USERDATA, ID_G_Heinek_RESET]:
            obj[c4d.ID_USERDATA, ID_G_Heinek_RESET] = False

    for id, bc in obj.GetUserDataContainer() :    
                if bc[c4d.DESC_PARENTGROUP][-1].id == 295: # G_Vegas Group
                    try:
                        obj[id] = bc[c4d.DESC_DEFAULT]
                    except TypeError:
                        pass
                        
                        
        #RESET G_Claude
        if obj[c4d.ID_USERDATA, ID_G_Claude_RESET]:
            obj[c4d.ID_USERDATA, ID_G_Claude_RESET] = False

    for id, bc in obj.GetUserDataContainer() :    
                if bc[c4d.DESC_PARENTGROUP][-1].id == 297: # G_Claude Group
                    try:
                        obj[id] = bc[c4d.DESC_DEFAULT]
                    except TypeError:
                        pass

    #RESET G_Argon
        if obj[c4d.ID_USERDATA, ID_G_Argon_RESET]:
            obj[c4d.ID_USERDATA, ID_G_Argon_RESET] = False

    for id, bc in obj.GetUserDataContainer() :    
                if bc[c4d.DESC_PARENTGROUP][-1].id == 299: # G_Argon Group
                    try:
                        obj[id] = bc[c4d.DESC_DEFAULT]
                    except TypeError:
                        pass
                        
                        
        #RESET G_Xenon
        if obj[c4d.ID_USERDATA, ID_G_Xenon_RESET]:
            obj[c4d.ID_USERDATA, ID_G_Xenon_RESET] = False

    for id, bc in obj.GetUserDataContainer() :    
                if bc[c4d.DESC_PARENTGROUP][-1].id == 301: # G_Xenon Group
                    try:
                        obj[id] = bc[c4d.DESC_DEFAULT]
                    except TypeError:
                        pass

    c4d.EventAdd()

    I know there is a better way to do this. Can anyone offer a suggestion? Thanks.



  • On 02/06/2017 at 01:45, xxxxxxxx wrote:

    Hi Swinn,

    optimizing code is really not our main area of expertise here in MAXON's SDK Team. So just a few notes from our end.

    I'd say the answer highly depends on a two factors:
    - the number of reset check boxes set on an average run
    - the amount of user data or rather number of entries in the user data container

    Depending on these it might be faster to have just one loop outside, iterating the container only once and then do the checks/switches inside. Having a nice map "reset ID" to "group ID" could make the code a bit more generic.

    In the end I'm pretty sure one of our Python gurus in the community will have much better ideas on this.



  • On 02/06/2017 at 02:37, xxxxxxxx wrote:

    Thanks for taking a look, Andreas. Hopefully one of the Python gurus can point me in the right direction. I have over 100 of these and user data approaching 3K.



  • On 02/06/2017 at 03:48, xxxxxxxx wrote:

    I have made a quick reply into c4dcafee.
    As said try to avoid same question on multiple forum. I guess this one will be better for continue this discussion since support team can give their advices too ;)

    https://www.c4dcafe.com/ipb/forums/topic/98566-easier-iteration/



  • On 03/06/2017 at 02:59, xxxxxxxx wrote:

    Is there a way for getting the bc of individual user data without hte need to loop?

    Like something liek that but without the loop wich is really not efficiency at all.

    import c4d
      
    def main() :
      if op is None: return
      
      desc_id, bc = get_bc(4)
      op[desc_id] = bc[c4d.DESC_DEFAULT]
      c4d.EventAdd()
      
    def get_bc(my_id) :
        for id, bc in op.GetUserDataContainer() :
            if id[-1].id == my_id:
                return id, bc
      
    if __name__=='__main__':
      main()
    


  • On 03/06/2017 at 04:26, xxxxxxxx wrote:

    I posted a file which may help.
    https://www.dropbox.com/s/s264ofllrr7pq1d/reset test.c4d?dl=0



  • On 03/06/2017 at 08:38, xxxxxxxx wrote:

    GetUserDataContainer() over and over again is pretty slow. Call it once and reuse the result.

    ud = op.GetUserDataContainer()
    desc_id, bc = get_bc(ud, 4)
    

    To optimize the loop to find the matching entry, you can build a dictionary first.

    ud = {}
    for id, bc in op.GetUserDataContainer() :
      ud[id[-1].id] = (id, bc)
      
    desc_id, bc = ud[4]
    


  • On 03/06/2017 at 08:57, xxxxxxxx wrote:

    Yes it's what I did but I fel not optimized.
    I should not post on c4dcafe but only here.
    So to the op I will stop the answerd on the c4d cafee ;)

    https://www.c4dcafe.com/ipb/forums/topic/98566-easier-iteration/#comment-652009



  • On 03/06/2017 at 11:35, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    Is there a way for getting the bc of individual user data without hte need to loop?

    Yes.
    You can get the data for a specific UD item by either it's ID#. Or by it's position within the UD stack.
    Here's an example of getting a specific UD item by it's ID# in the UD manager:

    import c4d  
    def main() :  
        
      if op is None: return  
        
      index = 1  #<---Change the target UD item as desired  
        
      ud = op.GetUserDataContainer()  
      item = ud[index-1]    
      bc = item[1]  
      name = bc.GetString(c4d.DESC_NAME)   
      udType = bc.GetLong(c4d.DESC_CUSTOMGUI)   
      units = bc.GetLong(c4d.DESC_UNIT)   
        
      #If the UD item is not a Group...Get it's value  
      if udType != 0:   
        item_value = op[c4d.ID_USERDATA, index]  
        print item_value  
      
      c4d.EventAdd()  
      
    if __name__=='__main__':  
      main()
    

    -ScottA



  • On 03/06/2017 at 12:10, xxxxxxxx wrote:

    Thanks you scotta ! I didn't thinked about the -1 and was baited with this ! :D I learned something thanks you.

    Anyway here I guess is the most optimized thing we can do.
    Code to be into the Tag object.

    #Scotta win the optimization contest ! :p
    #https://plugincafe.maxon.net/topic/483/13629_iterate
    import c4d
      
    def reset(obj, id_reset, list_id, ud_bc) :
        #Only if reset is True
        if obj[c4d.ID_USERDATA, id_reset]:
            obj[c4d.ID_USERDATA, id_reset] = False
            
            #We list all our id who are into our list_of_id_to_reset
            for id in list_id:
                if id == id_reset:
                    continue
                
                #Get the data corresponding to the user data bc
                item = ud_bc[id_reset-1]
                descid = item[0]
                bc = item[1]
                try:
                    obj[descid] = bc[c4d.DESC_DEFAULT]
                except:
                    pass
                                            
                
    def main() :
        #Get the object attached ot the tag
        obj = op.GetObject()
        
        #Get the userData Object
        ud_bc = obj.GetUserDataContainer()
        
        #list who store all our data
        #Reset_id, List_of_id_to_reset, list_of_descid_bc
        datas = list()
      
        #Data exemple for G_Vegas
        #prefer tuples since they are speeder than list
        tuple_G_vegas = (486, 15, 17, 4, 44, 42, 27, 28, 512, 36, 29, 393, 38, 39, 40, 19, 389)
        datas.append([486, tuple_G_vegas])
      
        #Loop for each data then reset them
        for data in datas:
            reset(obj, data[0], data[1], ud_bc)
            
        #Trigger c4d update
        c4d.EventAdd()
      
      
    if __name__ == '__main__':
        main()
    

    And the code for generate tuple of all id under parent

    import c4d
      
    def get_all_children_ud(obj, list_all_children, parent_id) :
        ignore_list = [c4d.DTYPE_GROUP,
                       c4d.DTYPE_SEPARATOR]
                       
        for id, bc in obj.GetUserDataContainer() :
            if bc[c4d.DESC_PARENTGROUP][-1].id == parent_id:
                desc_level = id[-1]
                if not desc_level.dtype in ignore_list:
                    list_all_children.append(desc_level.id)
      
    def main() :
        obj = doc.GetActiveObject()
        if not obj:
            return
        
        list_all_children = list()
        parent_id = 284
        
        get_all_children_ud(obj, list_all_children, parent_id)
        print tuple(list_all_children)
        
      
    if __name__=='__main__':
        main()
    

    EDIT: Hooww didn't work in the op exemple since the user id stack is not build from scratch and got some missing id :/ So I guess my previous method was correct.

    import c4d
      
    #dict who gonna store all our bc and descid. Like that we iterate only 1 time.
    #dict["user_data_id"] = [descid, bc]
    #I use global vairable liek that we only have to do build it one time.
    #take care if you add/remove ud you need to update it, look the main function
    global dict_descid_bc
    dict_descid_bc = None
      
    def reset(obj, id_reset, list_id) :
        global dict_descid_bc
        ud_bc = obj.GetUserDataContainer()
        
        #Only if reset is True
        if obj[c4d.ID_USERDATA, id_reset]:
            obj[c4d.ID_USERDATA, id_reset] = False
            #We list all our id who are into our list_of_id_to_reset
            for id in list_id:
                if id == id_reset:
                    continue
                
                descid = dict_descid_bc[str(id)][0]
                bc = dict_descid_bc[str(id)][1]
                try:
                    obj[descid] = bc[c4d.DESC_DEFAULT]
                except:
                    pass
                        
    def get_descid_bc(obj, dict) :
        for descid, bc in obj.GetUserDataContainer() :
            dict[str(descid[-1].id)] = [descid, bc]
                        
                
    def main() :
        #Get the object attached ot the tag
        obj = op.GetObject()
        
        #Tell to python we use the global variable
        global dict_descid_bc
        
        #Only update our dict if not build yet.
        if not dict_descid_bc:
            dict_descid_bc = dict()
            get_descid_bc(obj, dict_descid_bc)
        
        #list who store all our data
        #Reset_id, List_of_id_to_reset, list_of_descid_bc
        datas = list()
      
        #Data exemple for G_Vegas
        tupe_G_vegas = (486, 15, 17, 4, 44, 42, 27, 28, 512, 36, 29, 393, 38, 39, 40, 19, 389)
        datas.append([486, tupe_G_vegas ])
      
        #Loop for each data then reset them
        for data in datas:
            reset(obj, data[0], data[1])
            
        #Trigger c4d update
        c4d.EventAdd()
      
      
    if __name__ == '__main__':
        main()
    


  • On 03/06/2017 at 14:29, xxxxxxxx wrote:

    Tested in the scene file and the code above does not work. The code previous to that will throw the reset switch but none of the user data is reset to default.
    Here is a link to the file with all the code on separate tags, only the latest enabled.

    https://www.dropbox.com/s/s264ofllrr7pq1d/reset test.c4d?dl=0



  • On 03/06/2017 at 16:39, xxxxxxxx wrote:

    @ScottA that's not always going to work since the IDs are not garuanteed to be continous (delete a userdata) or in orde (re-order parameters). Hence my proposal with the dictionary.



  • On 03/06/2017 at 18:26, xxxxxxxx wrote:

    I never said that it was the proper solution to use Niklas.
    I merely answered his question on "if there was a way to target a specific UD item without using a loop".
    I never told him to use it. Nor did I tell him it was a better solution than yours.

    -ScottA



  • On 04/06/2017 at 00:45, xxxxxxxx wrote:

    import c4d
      
    #dict who gonna store all our bc and descid. Like that we iterate only 1 time.
    #dict["user_data_id"] = [descid, bc]
    #I use global vairable liek that we only have to do build it one time.
    #take care if you add/remove ud you need to update it, look the main function
    global dict_descid_bc
    dict_descid_bc = None
      
    def reset(obj, id_reset, list_id) :
        global dict_descid_bc
        ud_bc = obj.GetUserDataContainer()
        
        #Only if reset is True
        if obj[c4d.ID_USERDATA, id_reset]:
            obj[c4d.ID_USERDATA, id_reset] = False
            #We list all our id who are into our list_of_id_to_reset
            for id in list_id:
                if id == id_reset:
                    continue
                
                descid = dict_descid_bc[id][0]
                bc = dict_descid_bc[id][1]
                try:
                    obj[c4d.ID_USERDATA, id] = bc[c4d.DESC_DEFAULT]
                except:
                    pass
                        
    def get_descid_bc(obj, dict) :
        for descid, bc in obj.GetUserDataContainer() :
            dict[descid[-1].id] = [descid, bc]
                        
                
    def main() :
        #Get the object attached ot the tag
        obj = op.GetObject()
        
        #Tell to python we use the global variable
        global dict_descid_bc
        
        #Only update our dict if not build yet.
        if not dict_descid_bc:
            dict_descid_bc = dict()
            get_descid_bc(obj, dict_descid_bc)
        
        #list who store all our data
        #Reset_id, List_of_id_to_reset, list_of_descid_bc
        datas = list()
      
        #Data exemple for G_Vegas
        tupe_G_vegas = (486, 15, 17, 4, 44, 42, 27, 28, 512, 36, 29, 393, 38, 39, 40, 19, 389)
        datas.append([486, tupe_G_vegas ])
      
        #Loop for each data then reset them
        for data in datas:
            reset(obj, data[0], data[1])
            
        #Trigger c4d update
        c4d.EventAdd()
      
      
    if __name__ == '__main__':
        main()
    

    Tested and working fine in your exemple file.



  • On 04/06/2017 at 04:37, xxxxxxxx wrote:

    Yes! This works! I have a question, though. The code below will reset everything within the group without hardcoding individual userdatas. I would like to avoid that as there are so many. Can this be done?

    for id, bc in obj.GetUserDataContainer() :    
                if bc[c4d.DESC_PARENTGROUP][-1].id == 284: # G_Vegas Group
                    try:
                        obj[id] = bc[c4d.DESC_DEFAULT]
                    except TypeError:
                        pass
    


  • On 04/06/2017 at 04:42, xxxxxxxx wrote:

    There are 16 user data variables for each of 10 presets and there are 10 sets of presets so I am looking at hardcoding 1600 userdatas. I would like to avoid that if possible and just use the Group user data id so that everything within the group is reset. Also, this way if I add to the group, I don't have to worry about the code missing that parameter because I didn't reference its specific ID.



  • On 04/06/2017 at 08:52, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    I never said that it was the proper solution to use Niklas.
    I merely answered his question on "if there was a way to target a specific UD item without using a loop".
    I never told him to use it. Nor did I tell him it was a better solution than yours.

    -ScottA

    You posted your answer without any information on potential
    issues or limitations, thus I must assume that you either do not
    know them or forgot to include them in your answer.

    Either way, I feel obliged to point out that your answer only
    works in a special case, namely when you create the userdata
    sequentially and do not modify it afterwards.

    If you answer a question without the intend of someone using
    the information from your answer, why answer it at all.

    -Niklas



  • On 04/06/2017 at 12:00, xxxxxxxx wrote:

    Who the hell do you think you are Niklas?
    And where do you get off talking to me in such a manner?
    He asked a question. I even quoted it for clarity. Then I gave him the answer to that question.
    He asked a specific question. And I gave him a specific answer. That's SOP.
    It's not your place to give your almighty approval on how to answer someone's question.

    I've managed hundreds of people and several business for over three decades. And some 20 something yr old snot nosed punk on the internet with the ego the size of 747 who thinks he's God's gift to coding is going to tell me how to properly reply to a question?
    Oh hell no.
    You weren't even an idea when I was managing people for a living.
    Seriously. Who the hell do you think you are talking to me in that way?
    That was way out of line.

    -ScottA



  • On 04/06/2017 at 16:36, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    Who the hell do you think you are Niklas?
    And where do you get off talking to me in such a manner?
    He asked a question. I even quoted it for clarity. Then I gave him the answer to that question.
    He asked a specific question. And I gave him a specific answer. That's SOP.
    It's not your place to give your almighty approval on how to answer someone's question.

    I've managed hundreds of people and several business for over three decades. And some 20 something yr old snot nosed punk on the internet with the ego the size of 747 who thinks he's God's gift to coding is going to tell me how to properly reply to a question?
    Oh hell no.
    You weren't even an idea when I was managing people for a living.
    Seriously. Who the hell do you think you are talking to me in that way?
    That was way out of line.

    -ScottA

    Dear Scott, I am sorry if you feel personally assaulted. My intentions are solely directed at improving
    accuracy of information in this thread. I do not want to further derail this thread, so if you feel like
    continuing this discussion, I invite you to contact me via PM.

    Regards,
    -Niklas



  • On 04/06/2017 at 17:46, xxxxxxxx wrote:

    Is there a way instead of using the tuple to just use the group user ID and some code that refers to what's under it instead of hardcoding what's under it? That would solve the problem.


Log in to reply