1
$\begingroup$

I have a custom list to show some Groups to the user like this:

class RenderGroups(bpy.types.PropertyGroup):
    image = StringProperty(
            name="Image",
            description="The image-texture of this group")

    def __setattr__(self, attr, value):
        _name = "name"
        print("Hi", attr == _name)
        super().__setattr__(attr, new_value)

class RenderGroupUIList(bpy.types.UIList):
    def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
        if self.layout_type in {'DEFAULT', 'COMPACT'}:
            row = layout.row()
            if not item:
                row.label(text="", translate=False, icon_value=icon)
                return
            row.prop(item, "name", text="", emboss=False, icon_value=icon)
            if item.image not in bpy.data.images:
                row.label(icon="ERROR")
        # 'GRID' layout type should be as compact as possible (typically a single icon!).
        elif self.layout_type in {'GRID'}:
            layout.alignment = 'CENTER'
            layout.label(text="", icon_value=icon)

The problem now is the following:

  • When I access an element of the list (C.object.test_list) with C.object.test_list[0].name = "New name" it prints "Hi"
  • When I set the name of an element of the list via double clicking on the item in the Pane I show the list in, the Info-Header tells me bpy.context.object.test_list[1].name = "New Name" but "Hi" never shows up in the console (Yes, the System Console in this context)

EDIT: closely connected to this question. I need to check for updates of prop.name, not prop.image.

$\endgroup$
1
$\begingroup$

Your custom __setattr__ method is called if you add or change a collection item via Python (e.g. Python Console). It is not called if you change an entry via the UI.

The reason is probably that bpy properties are no regular python objects / properties. If you access it directly via Python, it is handled like a regular Python object. The UI however is handled in Blender's native C code (so are change events in Blender in general), and it obviously disregards custom __setattr__ handlers.

The proper way to check for property changes is to supply a callback function to the update attribute on property registration, or specify a getter and a setter function.

import bpy
from bpy.props import *

def update_cb(self, context):
    print("image =", self.image)

class RenderGroups(bpy.types.PropertyGroup):
    image = StringProperty(
            name="Image",
            description="The image-texture of this group",
            update=update_cb)


class RenderGroupUIList(bpy.types.UIList):
    def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
        if self.layout_type in {'DEFAULT', 'COMPACT'}:
            row = layout.row()
            if not item:
                row.label(text="", translate=False, icon_value=icon)
                return
            row.prop(item, "name", text="", emboss=False, icon_value=icon)
            row.prop(item, "image", text="", emboss=False)
            if item.image not in bpy.data.images:
                row.label(icon="ERROR")
        # 'GRID' layout type should be as compact as possible (typically a single icon!).
        elif self.layout_type in {'GRID'}:
            layout.alignment = 'CENTER'
            layout.label(text="", icon_value=icon)


class HelloWorldPanel(bpy.types.Panel):
    """Creates a Panel in the Object properties window"""
    bl_label = "Hello World Panel"
    bl_idname = "OBJECT_PT_hello"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "object"

    def draw(self, context):
        layout = self.layout
        layout.template_list("RenderGroupUIList", "", context.object, "coll", context.object, "idx")


def register():
    bpy.utils.register_module(__name__)
    bpy.types.Object.coll = bpy.props.CollectionProperty(type=bpy.types.RenderGroups)
    bpy.types.Object.idx = bpy.props.IntProperty()


def unregister():
    bpy.utils.unregister_module(__name__)
    del bpy.types.Object.coll
    del bpy.types.Object.idx


if __name__ == "__main__":
    register()

Be careful with writes to the property inside a callback, there is no protection against infinite loops - you need to handle that yourself.

http://www.blender.org/api/blender_python_api_2_73_release/bpy.props.html#get-set-example

$\endgroup$
  • $\begingroup$ I want to check for updates of name as you might already see from attr == "name" and I don't see how that is possible with an update function (that's the reason I use __setattr__ in the first place) $\endgroup$ – WorldSEnder Feb 10 '15 at 15:07
  • $\begingroup$ update_cb() is called whenever you change the image property in my example, could be any property you register beforehand. Why doesn't that work for you? The fact that bpy.types.PropertyGroup comes with a StringProperty called name doesn't matter, just re-define it in your property group. $\endgroup$ – CoDEmanX Feb 11 '15 at 18:18
  • $\begingroup$ sadly shadowing the name property will not override the name from PropertyGroup that is used by the collection. Re-defining it-- I guess all of my scripts ... may work, but when someone's iterating the collection they may not get what they expect name to do. $\endgroup$ – WorldSEnder Feb 11 '15 at 18:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.