6
$\begingroup$

I try to define five variables (called weights) with Propertygroups and a function to be notified if the Floatporperty-sliders have been moved:

def weightUpdate():
  print(f"update----------------------{time.time()}" )

class My_Settings(bpy.types.PropertyGroup):
  wgt1: bpy.props.FloatProperty(
     name='wgt1',
     default=0.0,
     update = weightUpdate()
  )
  wgt2: bpy.props.FloatProperty(
     name='wgt2',
     default=0.0,
     update = weightUpdate()
  )
  wgt3: bpy.props.FloatProperty(
     name='wgt3',
     default=0.0,
     update = weightUpdate()
  )
  wgt4: bpy.props.FloatProperty(
     name='wgt4',
     default=0.0,
     update = weightUpdate()
  )
  wgt5: bpy.props.FloatProperty(
     name='wgt5',
     default=0.0,
     update = weightUpdate()
  )

The registration seems to work:

 def register():
    bpy.utils.register_class(My_Settings)
    bpy.types.Scene.my_tool = bpy.props.PointerProperty(type=My_Settings)

And in the draw function of my main addon-class I also could define the slider, even in a loop for all five sliders:

def draw(self, context):
...
 for i in range(0,N):
      row = layout.row()
      wTxt = f"wgt{i+1}"
      row.label(text=wTxt)
      row = layout.row()
      row.prop(my_tool, wTxt)

The problem:

The function weightUpdate is only called when the script is started for the first time. Although any slider change is printed into the Python console in Blender, the function is not called again.

Also, is there a way to pass parameters to the update-function, like update = weightUpdate(3) for the third wgt-slider to identify which slider was changed?

$\endgroup$

1 Answer 1

10
$\begingroup$

When you initialize your bpy.props.Property attributes, you assign what's on the right side of the equal sign to the attribute that's on the left side.

If you assign update to weightUpdate(), you're not assigning the function weightUpdate, but the result of this function, which is None, the default implicit return value of a function.

That's why the print is done once on registering, and never again. The function is called only once and the result is stored in the update callback, but since it's None, it might as well not exist.

What you want to assign update to is the function :

update=weightUpdate

But even then the code will throw an error, because according to the docs :

This function must take 2 values (self, context) and return None

The two parameters will be automatically provided by Blender at runtime when the update is fired. Here, self will be the object that's holding the property, so the instance of My_Settings that's tied to your scene. context will be the context at the time the update is fired.

So a stripped down soluton would be :

def weightUpdate(self, context):
  print(self)
  print(context)
  print(f"update----------------------{time.time()}" )
  
class My_Settings(bpy.types.PropertyGroup):
  wgt1: bpy.props.FloatProperty(
     name='wgt1',        
     default=0.0,
     update = weightUpdate
  )
# etc.

Now let's say you want to use only one update function, but you want it to behave differently whether you update value1, or value2, etc.

You can use a lambda expression to set up additional parameters.

def weightUpdate(self, context, index):
  print(f"Weight {index} was updated")
  print(f"update----------------------{time.time()}" )
  
class My_Settings(bpy.types.PropertyGroup):
  wgt1: bpy.props.FloatProperty(
     name='wgt1',        
     default=0.0,
     update= lambda self, context: weightUpdate(self, context, 1)
  )
  wgt2: bpy.props.FloatProperty(
     name='wgt2',
     default=0.0,
     update= lambda self, context: weightUpdate(self, context, 2)
  )
# etc.

In this case the lambda is a new object that's not evaluated directly on creation. It can be called and evaluated at runtime, that's why you can call weightUpdate(self, context, index).

A minimal working example :

import bpy

def weightUpdate(self, context, index):
  print(f"Weight {index} was updated")
  
class My_Settings(bpy.types.PropertyGroup):
  wgt1: bpy.props.FloatProperty(
     name='wgt1',        
     default=0.0,
     update= lambda self, context: weightUpdate(self, context, 1)
  )
  wgt2: bpy.props.FloatProperty(
     name='wgt2',
     default=0.0,
     update= lambda self, context: weightUpdate(self, context, 2)
  )
  
class HelloWorldPanel(bpy.types.Panel):
    bl_label = "Hello World Panel"
    bl_idname = "OBJECT_PT_hello"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "scene"

    def draw(self, context):
        layout = self.layout
        my_tool = context.scene.my_tool 
        for i in range(0,2):
           row = layout.row()                
           wTxt = f"wgt{i+1}"
           row.label(text=wTxt)
           row = layout.row()                
           row.prop(my_tool, wTxt)

def register():
    bpy.utils.register_class(My_Settings)    
    bpy.types.Scene.my_tool = bpy.props.PointerProperty(type=My_Settings)     
    bpy.utils.register_class(HelloWorldPanel)



if __name__ == "__main__":
    register()

enter image description here

BTW, the standard number of space indentation usually used is 4, not 3.

$\endgroup$
1
  • 1
    $\begingroup$ Thank you very much for this elaborate and detailed answer! This was very helpful to me! $\endgroup$
    – Mike75
    Mar 5, 2022 at 13:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .