properties and inheritance

Hi,
i’m trying to make a bigger program and have a few problems with the pointer property and inheritance. lets say i have a class Tool, who inherits from Property group. and then i have a class Joint that inherits for Tool.
oke know i want to show the property Joint in lets say bpy.context.scene.joint

if i run the code underneth this… i get a property joint with attributes name and type but the name string is empty…

class Tool(bpy.types.PropertyGroup):
    name = bpy.props.StringProperty(default = 'tool')
    
class Joint(Tool):
    type = bpy.props.StringProperty(default = 'type')

    
bpy.utils.register_class(Tool) 
bpy.utils.register_class(Joint)
bpy.types.Scene.joint = bpy.props.PointerProperty(type = Joint)

an other question i have is, can i add init functions to my classes and how do i call for a knew object because something like this doesent work

bpy.types.Scene.joint = bpy.props.PointerProperty(type = Joint)
bpy.context.scene.joint = Joint(parms)

init classes are a no-go. blender needs the init. Most of the rest, however, do-able.

the joint class and the tool class are fine. the joint class has a name and a type. python will find the type on the joint instance and look up name in the tool class. so far we have one class descending from a propertygroup directly, and the other one descending from a propertygroup indirectly. the place we attach at, and make a connection from blender’s to our own logic, is the joint property of the Scene. A pointer to a property that will be an instance of a Joint which will be a subclass of a tool. so far so good, really.

the problem lies in how you are a) accessing the joint property on the last line of your example, and b) assigning using the normal, intuitive pythonic way, Joint(params) is not the blender way. Even though it is the python way.

The joint property is just a pointer. you can access bpy.context.scene.joint.type or name through the joint property, but it is merely an instance of the idea of a pointer, and what is important is the things it points to.

If you make the Joint class be the PropertyGroup, descending from the Tool as necessary, then bpy.utils.register_module(name) will register the classes that need to be registered. Right now what you have is a vice-versa, and you’re registering a Tool class that you won’t actually use directly.

You should prefer Blender props from python object ONLY if you fall under those 2 scenarios

a) you are making a gui in which case you need blender props

b) you want to store data inside your blend file

In any of the above case using Python object is a very good idea. Blender props are a clumsy way to manipulate data. Yes you can use init functions.

You should be aware of the diffirence between class variables and instance variables, init() allows you to create instance variables , while what you create in your examples is class variables. What that essentially mean is that if you create an instance out of your class, the class variable will be also in that instance, however all instance variables are shared references . What that means ? Well simply if you try to change the value of the instance class variable then all your other instances will also reflect the same change in their class variable, you could say that class variables share their values across instances. On the other hand instance variables carry unique values for each instance of the same class.

Pick your poison

The thing i’m confused about is by doing bpy.types.Scene.joint = bpy.props.PointerProperty(type = Joint) does it create an instance of the class Joint?

Instantiating a class, in python:



class A:
    name = 'foo'

a = A()

The a variable now holds an instance of an A class. As per intended.

Now let’s extrapolate this into the domain of blender’s python.



class B(bpy.types.PropertyGroup):
    name = bpy.props.StringProperty(default='bar')

If we expected to do b = B() and have it work, well, then we would have a lot of expectations.

First off, subclasses of bpy.types.PropertyGroup need to be registered, before they are useful.

Next, look at the ‘name’ attribute for both A and B. With A, there are no restrictions on the type of ‘name’. It is a string, ‘foo’, but there is no logic that says it can’t be something else. If you assign an int value to name, then name will be an int. With class B, the name attribute has an intermediary between it and its value. The fact that this is a StringProperty means that when assigning to the name property, wrong types will not be assigned.

What happens in the first example where it says a = A()?

Class A’s constructor method is called to return an initialized instance of an A …
and we’re going to keep it in a variable
and we’re going to call it “a”.

In the second example, doing that to B would circumvent blender’s ability to keep the instance of B that is returned. So even if the construction of the class went ok, a returned instance in the ‘b’ variable would be … floating out in space as far as blender is concerned.

During the registration phase, various things need to happen so that blender can translate some of the informations about the class into useful informations for itself. bpy.utils.register_class is called to do this, and once called, not even yet is class instantiated. If you do lots of classes, with register_module(name) then there is even a search, by blender, for subclasses of things it knows what to do with, and the classes can even be defined in other scripts and from . imported… whatever is visibly-named at the top level where you are doing the register_module, blender will register_class for each class in subclasses() of each registrable class.

BUT once all that is done, hopefully it is only to be done once. It doesn’t keep happening over and over unless something bad is happening.

Asking python for to make there to be a ‘b’ variable which is a usable instance of class B, to be directly returned upon instance-making, how then is blender to know about this? Short of altering the inner-workings of blender’s python interpreter to always tell blender about all of the classes that get instantiated, which would be all the things, all the time, mostly none of which blender needs to know about.

So, hopefully I have told you at least two things, which should be:

Blender is not going out of its way to search through all of the class instances everywhere more than once.

You can’t have init constructors for the various PropertyGroups, Panels and Operators you’re going to create, because blender needs them for itself.

You can, however create a method on a PropertyGroup, attach that to the scene via PointerProperty, and then call that method just after your registration phase, to provide the functionality of an ‘init’.

If all you wanted was creation parameters, then perhaps a from_params method on the class might do the trick.

Also, as a general rule, I only make the classes that blender will register descend from something that blender will register. Thus, classes that will be used as mix-ins, I don’t make descend from PointerProperty,Operator,Panel etcetera.

Also also, you’re running into the standard confusions about class dictionaries vs instance dictionaries. Consider the order that they are searched for attribute names, and that when an attribute name is found, that where it is found, it also stays. Setting a value in the class dict of one of your mix-ins is … exactly that.

These pointer properties, they are tricky. Consider it like the StringProperty, how the attribute was going to be managed. The ‘name’ property is always going to be a string, it has a default, but you can access it by referring to it and it will return you its value, which will ALWAYS be a string. The PointerProperty, it is the same way. It will always BE a PointerProperty. Attached to an object, it will be a property of that object, so that when you access it as a property of the object, the method that is called to “get” it will return to you – and here we have at last – an instance of the type of class it points to. If bpy.context.scene.joint = Joint(params) were actually to take place, the scene’s ‘joint’ property would no longer be a PointerProperty to an instance of a Joint, it would be the actual instance, minus all of blender’s abilities to interact with it.

I’m not saying it can’t be done. I just don’t think there is a setattr on the PointerProperty that is going to know how to handle being set to an instance. Maybe you can try and it will work under controlled conditions, who knows?

I have a similar question, so rather than starting a new topic, I hope no one minds if I “addon” to this one.

I am part of a team working on a fairly complex Blender addon and we’d like to use inheritance in our design. Here’s one of the simplest “regular python” inheritance examples:

########################################################

class Base(object):
        s = "ABC"

class Child(Base):
        pass

class App:

        b = Base()
        c = Child()

        def print_self(self):
              print ( "App Members:" )
              print ( " b.s = " + self.b.s )
              print ( " c.s = " + self.c.s )

########################################################

if __name__ == "__main__":
        app = App()
        app.print_self()

This works fine and prints out:

App Members:b.s = ABC
c.s = ABC

In other words, the “Child” class inherits the string “s” from its parent (“Base”) as expected.

Here’s an attempt to do the same thing using Blender Properties (full addon included below):

########################################################

class Base(bpy.types.PropertyGroup):
        s = StringProperty(name="s",default="ABC")

class Child(Base):
        pass

class AppPropertyGroup(bpy.types.PropertyGroup):

       b = PointerProperty(name="b", type=Base)
       c = PointerProperty(name="c", type=Child)

       def print_self (self, context):
              print ( "App Members:" )
              print ( " b.s = " + self.b.s ) # OK
              print ( " c.s = " + self.c.s ) # Fails

########################################################

As the comments indicates, the reference to self.b.s works, but the reference to self.c.s fails. Here’s the output:

App Members:b.s = ABC
Traceback (most recent call last):
File “/home/user/.config/blender/2.68/scripts/addons/InheritBlender_forum.py”, line 45, in execute
context.scene.app.print_self(context)
File “/home/user/.config/blender/2.68/scripts/addons/InheritBlender_forum.py”, line 34, in print_self
print ( " c.s = " + self.c.s ) # Fails
TypeError: Can’t convert ‘tuple’ object to str implicitly

If I wrap self.c.s in the print statement with a string conversion ( str(self.c.s) ), it prints out this:

App Members:b.s = ABC
c.s = (<built-in function StringProperty>, {‘attr’: ‘s’, ‘default’: ‘ABC’, ‘name’: ‘s’})

It appears that using inheritance has somehow removed Blender’s ability to treat a StringProperty as a string (lost it’s “RNA” wrapper?). Is this a bug?

Finally, I apologize in advance that I’m relatively new to both Blender and Python, so please forgive any beginner misconceptions in my post … but please feel free to point them out!!

Full Addon Example:

"""
This program demonstrates a potential bug
in Blender's Property inheritance.
"""

bl_info = {
       "version": "0.1",
       "name": "Inherit Blender",
       "author": "BlenderHawk",
       "location": "Properties &gt; Scene",
       "category": "Blender Experiments"
   }

import bpy
from bpy.props import *

########################################################

class Base(bpy.types.PropertyGroup):
       s = StringProperty(name="s",default="ABC")

class Child(Base):
       pass

class AppPropertyGroup(bpy.types.PropertyGroup):

       b = PointerProperty(name="b", type=Base)
       c = PointerProperty(name="c", type=Child)

       def print_self (self, context):
              print ( "App Members:" )
              print ( " b.s = " + self.b.s ) # OK
              print ( " c.s = " + self.c.s ) # Fails

########################################################

class APP_OT_print_operator(bpy.types.Operator):
       bl_idname = "app.run_example"
       bl_label = "Run Example"
       bl_description = "Run Example"
       bl_options = {'REGISTER'}

       def execute(self, context):
             context.scene.app.print_self(context)
             return {'FINISHED'}

class APP_PT_Inheritance(bpy.types.Panel):
       bl_label = "Property Inheritance"
       bl_space_type = "PROPERTIES"
       bl_region_type = "WINDOW"
       bl_context = "scene"
       def draw(self, context):
              row = self.layout.row()
              row.operator("app.run_example", text="Run Example")

def register():
       print ("Registering ", __name__)
       bpy.utils.register_module(__name__)
       bpy.types.Scene.app = bpy.props.PointerProperty(type=AppPropertyGroup)

def unregister():
       print ("Unregistering ", __name__)
       del bpy.types.Scene.app
       bpy.utils.unregister_module(__name__)

if __name__ == "__main__":
       register()

Thanks in advance for any help.