Replacing logic for 'hide_unused_objects' code block due to unrecognized keyword #5

Open
opened 2025-12-06 01:25:03 +00:00 by lock7244 · 3 comments

First off, I'm so glad this project is finally alive again! I was checking the last youtube video every few days for an update, and I'm so happy that the project is back up!

Anyway, using Blender 5.0, the new beta for KKBP, and on MMDTools 4.5.2, I exported a default Chika with Variations and All Outfits, and I got this error block during import:

Traceback (most recent call last):
  File "/home/key/.config/blender/5.0/extensions/user_default/kkbp/importing/importbuttons.py", line 93, in execute
    function()
  File "/home/key/.config/blender/5.0/extensions/user_default/kkbp/importing/importbuttons.py", line 85, in <lambda>
    lambda:bpy.ops.kkbp.postoperations('INVOKE_DEFAULT'),
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/key/AppData/Blender/blender-5.0.0/5.0/scripts/modules/bpy/ops.py", line 107, in __call__
    ret = _op_call(self.idname_py(), kw, C_exec, C_undo)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
RuntimeError: Error: Traceback (most recent call last):
  File "/home/key/.config/blender/5.0/extensions/user_default/kkbp/importing/postoperations.py", line 34, in execute
    self.hide_unused_objects()
  File "/home/key/.config/blender/5.0/extensions/user_default/kkbp/importing/postoperations.py", line 85, in hide_unused_objects
    bpy.ops.object.move_to_collection(collection_index = (index+i) + 1 + (character_collection_index + 1), is_new = True, new_collection_name = new_collection_name)
  File "/home/key/AppData/Blender/blender-5.0.0/5.0/scripts/modules/bpy/ops.py", line 109, in __call__
    ret = _op_call(self.idname_py(), kw)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: Converting py args to operator properties:: keyword "collection_index" unrecognized

Following that trace to line 85, I saw this:

#extremely confusing move to under the clothes collection. Index is the outfit index + outfit collection index (starts at 1) + Scene collection (1) +  + character collection index (usually 0) + 1
bpy.ops.object.move_to_collection(collection_index = (index+i) + 1 + (character_collection_index + 1), is_new = True, new_collection_name = new_collection_name)

Extremely confusing indeed. And after going to ChatGPT for some assistance, it told me that bpy.ops.object.move_to_collection() does not support collection_index, which is odd since it's on the project for a reason. This is a source it gave me.

After fixing THAT issue, I got another error for the last line in the for index, id in enumerate(outfit_ids): block:

Traceback (most recent call last):
  File "/home/key/.config/blender/5.0/extensions/user_default/kkbp/importing/importbuttons.py", line 93, in execute
    function()
  File "/home/key/.config/blender/5.0/extensions/user_default/kkbp/importing/importbuttons.py", line 85, in <lambda>
    lambda:bpy.ops.kkbp.postoperations('INVOKE_DEFAULT'),
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/key/AppData/Blender/blender-5.0.0/5.0/scripts/modules/bpy/ops.py", line 107, in __call__
    ret = _op_call(self.idname_py(), kw, C_exec, C_undo)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
RuntimeError: Error: Traceback (most recent call last):
  File "/home/key/.config/blender/5.0/extensions/user_default/kkbp/importing/postoperations.py", line 34, in execute
    self.hide_unused_objects()
  File "/home/key/.config/blender/5.0/extensions/user_default/kkbp/importing/postoperations.py", line 107, in hide_unused_objects
    child.children[0].exclude = True
    ~~~~~~~~~~~~~~^^^
IndexError: bpy_prop_collection[index]: index 0 out of range, size 0

Looking at the project after the error, it completely failed to create the extra Alt collections other than the 00 and move it inside the Outfit collection, while the meshes were just sitting inside the rig.

So after some back and forth, GPT ended up giving me this replacement for the entire for index, id in enumerate(outfit_ids): block:

        for index, id in enumerate(outfit_ids):

            # Convert outfit ID to two digits
            id_str = str(id).zfill(2)

            # Find all objects for this alt group
            clothes_in_this_id = [c for c in c.get_alts() if c.get('id') == id_str]
            c.switch(clothes_in_this_id[0], 'OBJECT')

            # 1. FIND CHARACTER LAYER COLLECTION
            character_lc = bpy.context.view_layer.layer_collection.children[-1]  # last child = character
            character_name = c.get_name()

            # 2. FIND THE OUTFIT LAYER COLLECTION UNDER CHARACTER
            outfit_lc = None
            outfit_name = f"Outfit {id_str} {character_name}"

            for lc in character_lc.children:
                if lc.name == outfit_name:
                    outfit_lc = lc
                    break

            if outfit_lc is None:
                print(f"ERROR: Could not find outfit layer collection '{outfit_name}'")
                continue

            # 3. CREATE / FIND NEW ALTS COLLECTION (bpy.data)
            new_collection_name = f"Alts {id_str} {character_name}"

            if new_collection_name not in bpy.data.collections:
                new_col = bpy.data.collections.new(new_collection_name)
            else:
                new_col = bpy.data.collections[new_collection_name]

            # 4. LINK NEW COLLECTION UNDER OUTFIT COLLECTION
            if new_col.name not in outfit_lc.collection.children:
                outfit_lc.collection.children.link(new_col)

            # 5. MOVE ALL OBJECTS INTO THE NEW COLLECTION
            for ob in clothes_in_this_id:
                ob.select_set(True)
                bpy.context.view_layer.objects.active = ob

                # unlink from all current collections
                for col in ob.users_collection:
                    col.objects.unlink(ob)

                # link into target
                new_col.objects.link(ob)

            # 6. FIND THE *LAYER COLLECTION* VERSION OF NEW_COL AND HIDE IT
            alts_lc = None
            for lc in outfit_lc.children:
                if lc.name == new_collection_name:
                    alts_lc = lc
                    break

            if alts_lc:
                alts_lc.exclude = True
            else:
                print(f"WARNING: Unable to hide layer collection '{new_collection_name}'.")

I tried it, and it looks like it worked well. No error was raised this time, and every Alt collection for each outfit went into their respective Outfit collection, and it all was properly disabled except for Outfit 00.

Not sure how much credit I can take, since I simply asked GPT for help on those error messages and asked for corrections, but I hope this is a fair replacement.

First off, I'm so glad this project is finally alive again! I was checking the last youtube video every few days for an update, and I'm so happy that the project is back up! Anyway, using Blender 5.0, the new beta for KKBP, and on MMDTools 4.5.2, I exported a default Chika with Variations and All Outfits, and I got this error block during import: ```sh Traceback (most recent call last): File "/home/key/.config/blender/5.0/extensions/user_default/kkbp/importing/importbuttons.py", line 93, in execute function() File "/home/key/.config/blender/5.0/extensions/user_default/kkbp/importing/importbuttons.py", line 85, in <lambda> lambda:bpy.ops.kkbp.postoperations('INVOKE_DEFAULT'), ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/key/AppData/Blender/blender-5.0.0/5.0/scripts/modules/bpy/ops.py", line 107, in __call__ ret = _op_call(self.idname_py(), kw, C_exec, C_undo) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RuntimeError: Error: Traceback (most recent call last): File "/home/key/.config/blender/5.0/extensions/user_default/kkbp/importing/postoperations.py", line 34, in execute self.hide_unused_objects() File "/home/key/.config/blender/5.0/extensions/user_default/kkbp/importing/postoperations.py", line 85, in hide_unused_objects bpy.ops.object.move_to_collection(collection_index = (index+i) + 1 + (character_collection_index + 1), is_new = True, new_collection_name = new_collection_name) File "/home/key/AppData/Blender/blender-5.0.0/5.0/scripts/modules/bpy/ops.py", line 109, in __call__ ret = _op_call(self.idname_py(), kw) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TypeError: Converting py args to operator properties:: keyword "collection_index" unrecognized ``` Following that trace to line 85, I saw this: ```python #extremely confusing move to under the clothes collection. Index is the outfit index + outfit collection index (starts at 1) + Scene collection (1) + + character collection index (usually 0) + 1 bpy.ops.object.move_to_collection(collection_index = (index+i) + 1 + (character_collection_index + 1), is_new = True, new_collection_name = new_collection_name) ``` Extremely confusing indeed. And after going to ChatGPT for some assistance, it told me that `bpy.ops.object.move_to_collection()` does not support `collection_index`, which is odd since it's on the project for a reason. [This is a source it gave me](https://devtalk.blender.org/t/where-to-find-collection-index-for-moving-an-object/3289/2). After fixing THAT issue, I got another error for the last line in the `for index, id in enumerate(outfit_ids):` block: ```sh Traceback (most recent call last): File "/home/key/.config/blender/5.0/extensions/user_default/kkbp/importing/importbuttons.py", line 93, in execute function() File "/home/key/.config/blender/5.0/extensions/user_default/kkbp/importing/importbuttons.py", line 85, in <lambda> lambda:bpy.ops.kkbp.postoperations('INVOKE_DEFAULT'), ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/key/AppData/Blender/blender-5.0.0/5.0/scripts/modules/bpy/ops.py", line 107, in __call__ ret = _op_call(self.idname_py(), kw, C_exec, C_undo) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RuntimeError: Error: Traceback (most recent call last): File "/home/key/.config/blender/5.0/extensions/user_default/kkbp/importing/postoperations.py", line 34, in execute self.hide_unused_objects() File "/home/key/.config/blender/5.0/extensions/user_default/kkbp/importing/postoperations.py", line 107, in hide_unused_objects child.children[0].exclude = True ~~~~~~~~~~~~~~^^^ IndexError: bpy_prop_collection[index]: index 0 out of range, size 0 ``` Looking at the project after the error, it completely failed to create the extra Alt collections other than the 00 and move it inside the Outfit collection, while the meshes were just sitting inside the rig. So after some back and forth, GPT ended up giving me this replacement for the entire `for index, id in enumerate(outfit_ids):` block: ```python for index, id in enumerate(outfit_ids): # Convert outfit ID to two digits id_str = str(id).zfill(2) # Find all objects for this alt group clothes_in_this_id = [c for c in c.get_alts() if c.get('id') == id_str] c.switch(clothes_in_this_id[0], 'OBJECT') # 1. FIND CHARACTER LAYER COLLECTION character_lc = bpy.context.view_layer.layer_collection.children[-1] # last child = character character_name = c.get_name() # 2. FIND THE OUTFIT LAYER COLLECTION UNDER CHARACTER outfit_lc = None outfit_name = f"Outfit {id_str} {character_name}" for lc in character_lc.children: if lc.name == outfit_name: outfit_lc = lc break if outfit_lc is None: print(f"ERROR: Could not find outfit layer collection '{outfit_name}'") continue # 3. CREATE / FIND NEW ALTS COLLECTION (bpy.data) new_collection_name = f"Alts {id_str} {character_name}" if new_collection_name not in bpy.data.collections: new_col = bpy.data.collections.new(new_collection_name) else: new_col = bpy.data.collections[new_collection_name] # 4. LINK NEW COLLECTION UNDER OUTFIT COLLECTION if new_col.name not in outfit_lc.collection.children: outfit_lc.collection.children.link(new_col) # 5. MOVE ALL OBJECTS INTO THE NEW COLLECTION for ob in clothes_in_this_id: ob.select_set(True) bpy.context.view_layer.objects.active = ob # unlink from all current collections for col in ob.users_collection: col.objects.unlink(ob) # link into target new_col.objects.link(ob) # 6. FIND THE *LAYER COLLECTION* VERSION OF NEW_COL AND HIDE IT alts_lc = None for lc in outfit_lc.children: if lc.name == new_collection_name: alts_lc = lc break if alts_lc: alts_lc.exclude = True else: print(f"WARNING: Unable to hide layer collection '{new_collection_name}'.") ``` I tried it, and it looks like it worked well. No error was raised this time, and every Alt collection for each outfit went into their respective Outfit collection, and it all was properly disabled except for Outfit 00. Not sure how much credit I can take, since I simply asked GPT for help on those error messages and asked for corrections, but I hope this is a fair replacement.

Oh I've also noticed this issue several days ago, and I tried to fix it myself by editing L65~88 to

        for id in outfit_ids:
            print('Processing outfit ID: ' + str(id).zfill(2))
            clothes_in_this_id = [c for c in clothes_and_hair if c.get('id') == str(id).zfill(2)]
            clothes_in_this_id.extend([c for c in c.get_alts() if c.get('id') == str(id).zfill(2)])
            print("Clothing item in outfit ID " + str(id).zfill(2) + ": ")
            for o in clothes_in_this_id:
                print(o.name)
            c.move_and_hide_collection(clothes_in_this_id, 'Outfit ' + str(id).zfill(2) + ' ' + c.get_name(), hide = (id != min(outfit_ids) and not bpy.context.scene.kkbp.separate_clothes))

        #put any clothes variations into their own collection
        outfit_ids = (int(c['id']) for c in c.get_alts() if c.get('id'))
        outfit_ids = list(set(outfit_ids))
        for index, id in enumerate(outfit_ids):
            print('Processing alts for outfit ID: ' + str(id).zfill(2))
            clothes_in_this_id = [c for c in c.get_alts() if c.get('id') == str(id).zfill(2)]
            c.switch(clothes_in_this_id[0], 'OBJECT')
            #find the character index
            character_collection_index = len(bpy.context.view_layer.layer_collection.children)-1
            #find the index of the outfit collection
            for i, child in enumerate(bpy.context.view_layer.layer_collection.children[character_collection_index].children):
                if child.name == 'Outfit ' + str(id).zfill(2) + ' ' + c.get_name():
                    des_coll_name = child.name
                    print('Found outfit collection at index: ' + str(i))
                    break
            for ob in clothes_in_this_id:
                print(ob.name)
                ob.select_set(True)
                bpy.context.view_layer.objects.active=ob
            new_collection_name = 'Alts ' + str(id).zfill(2) + ' ' + c.get_name()

            new_coll = bpy.data.collections.new(new_collection_name)
            des_coll = bpy.data.collections[des_coll_name]
            if des_coll and new_coll:
                parent_coll = None
                for coll in bpy.data.collections:
                    for child_coll in coll.children:                     
                        if new_coll.name == child_coll.name:
                            parent_coll = coll
                            break
                    if parent_coll:
                        parent_coll.children.unlink(new_coll)
                        break
                
                des_coll.children.link(new_coll)
                for ob in clothes_in_this_id:
                    new_coll.objects.link(ob)
                    des_coll.objects.unlink(ob)
            #then hide the alts
            child.children[0].exclude = True

It works on exporting just one outfit, however I've not test it on multiple outfits.

Oh I've also noticed this issue several days ago, and I tried to fix it myself by editing L65~88 to ```python for id in outfit_ids: print('Processing outfit ID: ' + str(id).zfill(2)) clothes_in_this_id = [c for c in clothes_and_hair if c.get('id') == str(id).zfill(2)] clothes_in_this_id.extend([c for c in c.get_alts() if c.get('id') == str(id).zfill(2)]) print("Clothing item in outfit ID " + str(id).zfill(2) + ": ") for o in clothes_in_this_id: print(o.name) c.move_and_hide_collection(clothes_in_this_id, 'Outfit ' + str(id).zfill(2) + ' ' + c.get_name(), hide = (id != min(outfit_ids) and not bpy.context.scene.kkbp.separate_clothes)) #put any clothes variations into their own collection outfit_ids = (int(c['id']) for c in c.get_alts() if c.get('id')) outfit_ids = list(set(outfit_ids)) for index, id in enumerate(outfit_ids): print('Processing alts for outfit ID: ' + str(id).zfill(2)) clothes_in_this_id = [c for c in c.get_alts() if c.get('id') == str(id).zfill(2)] c.switch(clothes_in_this_id[0], 'OBJECT') #find the character index character_collection_index = len(bpy.context.view_layer.layer_collection.children)-1 #find the index of the outfit collection for i, child in enumerate(bpy.context.view_layer.layer_collection.children[character_collection_index].children): if child.name == 'Outfit ' + str(id).zfill(2) + ' ' + c.get_name(): des_coll_name = child.name print('Found outfit collection at index: ' + str(i)) break for ob in clothes_in_this_id: print(ob.name) ob.select_set(True) bpy.context.view_layer.objects.active=ob new_collection_name = 'Alts ' + str(id).zfill(2) + ' ' + c.get_name() new_coll = bpy.data.collections.new(new_collection_name) des_coll = bpy.data.collections[des_coll_name] if des_coll and new_coll: parent_coll = None for coll in bpy.data.collections: for child_coll in coll.children: if new_coll.name == child_coll.name: parent_coll = coll break if parent_coll: parent_coll.children.unlink(new_coll) break des_coll.children.link(new_coll) for ob in clothes_in_this_id: new_coll.objects.link(ob) des_coll.objects.unlink(ob) #then hide the alts child.children[0].exclude = True ``` It works on exporting just one outfit, however I've not test it on multiple outfits.
Author

@Windy_D
I've only tested the one I got with multiple outfits, and it settles onto Outfit 00 with the Alt 00 collection inside it, same with Outfit 01 - > Alt 01, Outfit 02 - > Alt 02, and so on. I haven't tested it with just one outfit or without alts.

@Windy_D I've only tested the one I got with multiple outfits, and it settles onto Outfit 00 with the Alt 00 collection inside it, same with Outfit 01 - > Alt 01, Outfit 02 - > Alt 02, and so on. I haven't tested it with just one outfit or without alts.
Owner

Thanks guys, I decided to just replace the line with "c.move_and_hide_collection(...". It should be fixed in the latest beta release. I know it puts the alts on the outside of the outfit collections, but I think I like it better that way anyway because you don't have to have the clothes collections expanded to show / hide the alts.

Thanks guys, I decided to just replace the line with "c.move_and_hide_collection(...". It should be fixed in the latest beta release. I know it puts the alts on the outside of the outfit collections, but I think I like it better that way anyway because you don't have to have the clothes collections expanded to show / hide the alts.
Sign in to join this conversation.
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
kkbp-dev/KKBP_Importer#5
No description provided.