Inheritance, registration and init

Hi all,

I want to create a class of object with handles, which inherits from blender objects. I draw 3d plans of bikes before construction and the handles will contain some extra information useful for the construction ( length,angle, visibility attribute…).

First method to achieve this. Works but dirty.

class objectWithHandles()
def init(self):
self.withouthandles=defineTheUsualObjectHere
self.myHandles=addTheExtraInformationHere

This is not nice because I cannot access to the object location directly for instance. If o is in object, I need to type o.withoutHandles.location instead of the simple o.location and similarly for all propoerties.

It is not possible to directly use inheritance in Python since the init function complains about the number of arguments.
I saw in the doc that it is possible to define a new type which inherits a given type directly in Blender, not in Python. We have to register the new class in blender. However, it is not possible to use an init function for that new type. I have seen no indication on how to initialize such an object.

Thus my question : how to define an object o and initialize it so that I can access to its properties using o.location and o.myHandles.extraProperty ?

Thank you for your help.
Laurent.

Blender doesn’t really do inheritance and classes, at least not in a way you’d expect. For all intents and purposes, there is only a single Object type for scene objects, and a handful of datablock types (Mesh,Lamp,Camera…). Any specialization is done through the datablock type. It’s not possible to add your own datablock type from Python.

Also, you cannot instantiate objects from Python. What you need to do is create objects through the libraries that contain them:

This creates an ‘EMPTY’ type object:


my_ob = bpy.data.objects.new('my_ob',data=None)
my_ob.type # 'EMPTY'

If instead you want to create a Mesh object, you need to also supply a mesh:


my_mesh = bpy.data.meshes.new('my_mesh') # a new empty mesh
my_ob = bpy.data.objects.new('my_ob',data=my_mesh)
my_ob.type # 'MESH'

Note that the ‘data’ argument defines the object type, the data for a ‘MESH’ is created through the respective datablock collection (bpy.data.meshes), similar for ‘LAMP’ (bpy.data.lamps), ‘CAMERA’ (bpy.data.cameras) and so on.

It’s also important to mention that the references you get from .new() are just python wrappers. You should not store them, because any undo/redo can make them invalid and accessing them afterwards can crash Blender. You should get your reference from the datablock collection instead. This is where the .name property becomes important.


my_ob = bpy.data.object.new('some_name',data=None)
my_ob.name # may or may not be 'some_name'!

Since there could already be an object called ‘some_name’, the name might be generated like this ‘some_name.001’. If you have the name, you can get a reference through the datablock collection:


my_ob = bpy.data.objects['some_name']

Another complication is that datablocks can be renamed without you being notified. Together with the inability to store references, that means it is impossible to reliably track objects across multiple script invocations in a reasonable manner.

Blender makes it really really really cumbersome to have something like your own scene object type, what you can do however is add custom properties to all objects using bpy.props:


bpy.types.Object.my_number = bpy.props.IntProperty(default=1)

Now all objects have a my_number property that you can use as you please. It will be serialized in the .blend file, too, and you can access it when drawing your own UI Panel.

I haven’t answered your original question, because as you hopefully can see now, what you want to do doesn’t really make sense for Blender. I hope my diatribe scared you enough to convince you that your youth (or whatever remains of it) is better spent on other things (such as sex and drugs) than this outlandish API. Consider yourself warned.

Also: If you want to write code examples, put them into “code” tags, so that the formatting isn’t lost.

Update: The above code examples don’t make the objects visible. For that, you need to link it to the scene, like this:


bpy.data.scene['Scene'].objects.link(my_ob)

Thank you BeerBaron for your very instructive message.

OK. So, clearly, what I intended to do is not possible.

It’s also important to mention that the references you get from .new() are just python wrappers. You should not store them, because any undo/redo can make them invalid and accessing them afterwards can crash Blender. You should get your reference from the datablock collection instead. This is where the .name property becomes important.

Another complication is that datablocks can be renamed without you being notified. Together with the inability to store references, that means it is impossible to reliably track objects across multiple script invocations in a reasonable manner.

Interisting.

I haven’t answered your original question, because as you hopefully can see now, what you want to do doesn’t really make sense for Blender. I hope my diatribe scared you enough to convince you that your youth (or whatever remains of it) is better spent on other things (such as sex and drugs) than this outlandish API. Consider yourself warned.

Yes, you convinced me not to hope too much on this API. I should stick to simple things without trying to extend Blender. I understand that large projects should be scripted at a lower level.

Nevertheless, I still think that the API is very useful for small to medium projects. In my case, there are around 30 pieces in my bike, and it not sensible to draw each piece individually in the UI, it would take weels. With the API, I can draw the elements using some math computations, and then with some loops, I can draw the pieces individually and the different steps of the construction. It is very useful in the workshop to avoid mistakes. I have looked around, and found no solution besides the blender api.

Thank you BeerBaron for your informative answer.

OK. Then it means that what I had in mind is not possible.

It’s also important to mention that the references you get from .new() are just python wrappers. You should not store them, because any undo/redo can make them invalid and accessing them afterwards can crash Blender. You should get your reference from the datablock collection instead. This is where the .name property becomes important.

Another complication is that datablocks can be renamed without you being notified. Together with the inability to store
references, that means it is impossible to reliably track objects across multiple script invocations in a reasonable manner.

Interisting.

I hope my diatribe scared you enough to convince you that your youth (or whatever remains of it) is better spent on other things (such as sex and drugs) than this outlandish API. Consider yourself warned.

Yes, You convinced me not to hope too much from the Blender api. I understand that I should stick to some small projects and that large projects should use a lower level approach.

However, i still think the api is useful for small projects. In my case, my bike has around 30 pieces, and I want to draw every step before going to the workshop. With the UI it is not feasible. With Python, I can compute with maths, and then draw every step using a loop in the Python script. Since this is faster with Python, I can spend time on sex and drugs :evilgrin:

this outlandish API

The more I dive in the API, the more I understand that it is not an API in the usual meaning. The last example I met: when sce is my scene, the instruction sce.layers[3]=True changes the value of sce.active_layer to 3. It is not the expected behaviour of an API where we expect to control the variables individually.

I think we have to understand that the API is a python access to the graphical interface more than an API in the usual meaning. Now I am used to it. But it was quite destabilizing at the beginning.

In a perfect world, some words about this could be included in the doc so that people know what they can expect from the API.

I can’t reproduce this behavior

I think we have to understand that the API is a python access to the graphical interface more than an API in the usual meaning. Now I am used to it. But it was quite destabilizing at the beginning.

There’s truth to that. Besides binding to the UI, the API mostly gives you access to data, but not functionality. A lot of functionality is locked behind the dreaded operators, which can be extremely painful to use. Uncountable hours must’ve been wasted on an incorrect context.

In a perfect world, some words about this could be included in the doc so that people know what they can expect from the API.

No kind words can express what expects those who dare to use the bpy API. That’s why those words remain unspoken. 'Tis true, the API shouldn’t lure in the innocent souls. “Abandon all hope, ye who enter here” should be written on the frontpage of the API docs.

I can’t reproduce this behavior

With Blender 2.69, adding a visible layer changes the active layer
as illustrated with the following code:


import bpy
sce = bpy.context.scene
print("active layer",sce.active_layer) #-> prints 0 here
sce.layers[1]=True # should not change the active layer but:  
print("active layer",sce.active_layer) #->prints 1 here

!!! IGNORE THIS POST !!!

You’re right, I wasn’t playing close enough attention. Looks like it’s a consequence of Blender cleverly mapping a bitflag to a Python collection of 20 bools:

<i>layers=(False</i>, <i>False</i>, <i>False</i>, <i>False</i>, <i>False</i>, <i>False</i>, <i>False</i>, <i>False</i>, <i>False</i>, <i>False</i>, <i>False</i>, <i>False</i>, <i>False</i>, <i>False</i>, <i>False</i>, <i>False</i>, <i>False</i>, <i>False</i>, <i>False</i>, <i>False)</i>

Setting any of the values in the collection just calls a function that sets the bitflag (and as an undocumented side-effect, sets the active layer, too).

You really should be happy about that behavior though, since active_layer is read-only…

Yes, I hope I will have the oportunity to use it now that I have spent some time in a debugging party :smiley: my objects were located on a bad layer because of this undocumented behaviour).

By the way, an other thing that could be documented is the fact that at least one layer for an object needs to be true. A consequence is that several instructions like myObject.layers[2]=False are completly ignored, without warnings. This has the strange consequence that the two following sequences of code are mathematically equivalent but have different behaviours in blender:

myObject.layers[2]=False
myObject.layers[3]=True
myObject.layers[3]=True
myObject.layers[2]=False

The second code is OK. In the first piece of code, if myObject is visible only on layer2, the first instruction is ignored and myObject ends up being visible on layers 2 and 3.

Debugging is not so easy because this is very an unexpected behaviour from an API that affecting two variables is not a commutative operation. I think this is a major difficulty in Blender: we have to find out the definitions, the integrity conditions and the side effects by ourselves. Nevertheles I am happy with the Blender API : incredibly more productive than the UI once we understand it :yes: