So: I’m making great progress thanks to the help on this forum. Of course, one problem solved and another pops up.
I’ve taken out an offending piece of code from the main part and pasted it below. First, don’t laugh but I prefer seeing i = i + 1; sorry guys.
The problem I have found can be summarised easily as:
cbpy.ops.object.move_to_collection(collection_index=1) refers to Collection[0]
I found that collection_index=0 refers to the Scene Collection. I don’t understand why collection_index=0 doesn’t refer to Collection[0] which seems nice and neat. I don’t want to have to come back at a later date to change the code because of an error that has arisen from a lack of understanding. In the main part of the code I use lines such as 'bpy.data.collections[0].objects[i].location[0]=‘some function of i’ to arrange objects using a loop - here collections[0] refers to the first collection and not the scene collection.
It’s like bpy.ops has a different indexing to bpy.data.
If that’s just the way it is I can work with it but it seems odd. And I’ve spent a lot of time messing about to get here: good way to learn I guess.
#Create two collections and move 6 objects into the first collection
import bpy
number_of_ob = 6
#Create two Collections
bpy.context.area.ui_type = 'OUTLINER'
bpy.ops.outliner.item_activate(deselect_all=True)
bpy.ops.outliner.collection_new(nested=True) #add Collection[0]
bpy.ops.outliner.collection_new(nested=True) #add Collection[1]
bpy.context.area.ui_type = 'TEXT_EDITOR'
#Add objects for first collection
for i in range(number_of_ob):
#ICOSPHERE
bpy.ops.mesh.primitive_ico_sphere_add(radius=1, enter_editmode=False, align='WORLD', location=(0, 0, 0), scale=(1, 1, 1))
i = i + 1
#
#Move objects to Collection
j = 0
for i in range(number_of_ob):
bpy.data.objects[j].select_set(True) #selects the object 'j'
bpy.ops.object.move_to_collection(collection_index=1) #PROBLEM: collection_index=1 refers to Collection[0]
j = j + 1
#
The outliner does not reference collections in the same manner as bpy.data.collections it references collections based on view_layer. bpy.data.collections references all collections in a file. Consider the potential of working with multiple scenes. the file still needs to store all collections, but only the collections of the scene you are currently working in need to be available in the outliner.
Each scene must have a top layer collection that cannot be removed you may see this referred to as “Scene Collection” (the name in the outliner) or as master collection. This collection does not exist in bpy.data.collections
an alternative method for making a collection shown below:
by storing a reference to the collection when it is created you can verify the correct collection is used without needing an index number
import bpy
# create a collection in data
new_coll = bpy.data.collections.new("My Collection")
# link to specific scene's master collection
bpy.data.scenes['Scene'].collection.children.link(new_coll)
Since we are working directly with the file data we do not need to override the context which you are essentially doing with:
Also to link an object to a collection (assuming you just called bpy.ops.mesh.primitive_ico_sphere_add()) :
new_obj = bpy.context.active_object # Get a handle on new object
new_obj.users_collection[0].objects.unlink(new_obj) # Unlink from active collection
new_coll.objects.link(new_obj) # Link to new collection
I’m not laughing. But what?? Some basic stuff (independent of the collection context):
for i in range(number_of_ob):
do_anything_with_i
i = i + i
In the usual for-loops you have to increment your counter but in the modern foreach-loops this is automatically done and fideling with it isn’t good practice and it may not works as expected:
>>> for i in range(9):
... print(i)
...
0
1
2
3
4
5
6
7
8
and your example…
>>> for i in range(9):
... print(i)
... i = i + 1
...
0
1
2
3
4
5
6
7
8
or do you want this (? with step 2):
>>> for i in range(0, 9, 2):
... print(i)
...
0
2
4
6
8
And in you last example: you are counting (with j) what told the for loop to do by range(number…) … so j will be == number…
It’s just I prefer it to i+=1, mainly because I think my eyes are going and it’s easier to see i = i +1.
But you’re right to point out that it is totally redundant in that bit of code - in removing all the pieces of code not relevant to the question I didn’t delete that line.
I think it’s fine to include some redundancy in your code, but I would just advise you to add a comment when you do eg : i = i + 1 # I know this doesn't do anything, but it helps me visualize what happens in the loop
that way if you share it or when you come back to it in a few months and you don’t need this help anymore you won’t be stumped as to why this was placed there.
After all the main purpose of programming languages is to help humans read and write programs, this line is most likely removed altogether by the compiler when translated to machine code.