1
$\begingroup$

EDIT
So after some more reading I now have the following, but still cannot modify any of the values...

class ADDHOLE_PT_redo_panel(bpy.types.Panel):
bl_label = "Add Hole Panel"
bl_idname = "ADDHOLE_PT_redo_panel"
bl_space_type = "VIEW_3D"   
bl_region_type = "UI"
bl_category = "Tools"
bl_context = "objectmode"   

def draw(self, context):
    custom_props = context.scene.custom_props
    layout = self.layout
    row = layout.row()
    row.prop(custom_props, 'holeSize')
    row = layout.row()
    row.prop(custom_props, 'holeType')
    row = layout.row()
    row.prop(custom_props, 'holeDepth')
    row = layout.row()
    row.prop(custom_props, 'holeDiaCustom')
    row = layout.row()
    row.prop(custom_props, 'isPrinting')
    row = layout.row()
    row.prop(custom_props, 'subsurfMod')
    row = layout.row()
    row.prop(custom_props, 'subsurfModLevels')
    row = layout.row()
    row.prop(custom_props, 'segmentsNum')
    row = layout.row()
    row.prop(custom_props, 'align')
    row = layout.row()
    row.prop(custom_props, 'location')
    row = layout.row()
    row.prop(custom_props, 'rotation')
    row = layout.row()
    layout.separator()

But, if I create a separate class for the panel and put it in the N-panel, I can modify the values but they have no effect on the part unless it's run again. It seams like the properties are readonly when accessing them from the redo panel...
EDIT

I have a bunch of custom properties in a class CustomPropertyGroup(bpy.types.PropertyGroup):

I'm using the following as the main class which also populates the Redo Panel:

class MESH_OT_add_object(Operator, AddObjectHelper):
"""Create a new Mesh Object"""
bl_idname = "mesh.primitive_add_hole"
bl_label = "Add Hole"
bl_options = {'REGISTER', 'UNDO'}

def draw(self, context):
    layout = self.layout
    row = layout.row()
    row.prop(context.scene.custom_props, 'holeSize')
    row = layout.row()
    row.prop(context.scene.custom_props, 'holeType')
    row = layout.row()
    row.prop(context.scene.custom_props, 'holeDepth')
    row = layout.row()
    row.prop(context.scene.custom_props, 'holeDiaCustom')
    row = layout.row()
    row.prop(context.scene.custom_props, 'isPrinting')
    row = layout.row()
    row.prop(context.scene.custom_props, 'subsurfMod')
    row = layout.row()
    row.prop(context.scene.custom_props, 'subsurfModLevels')
    row = layout.row()
    row.prop(context.scene.custom_props, 'segmentsNum')
    row = layout.row()
    row.prop(context.scene.custom_props, 'align')
    row = layout.row()
    row.prop(context.scene.custom_props, 'location')
    row = layout.row()
    row.prop(context.scene.custom_props, 'rotation')
    row = layout.row()
    layout.separator()

def execute(self, context):
    custom_props = context.scene.custom_props
    
    object_data_add(
            context,
            AddHole(self, context), # This calls a def to built an object and return a mesh
            operator=self)

    if custom_props.subsurfMod == True:
        objMod = bpy.context.selected_objects[0]
        subsurfMod = objMod.modifiers.new(name="Subsurf", type='SUBSURF')
        subsurfMod.levels = custom_props.subsurfModLevels

    return {'FINISHED'}

all registered like so:

def register():
    bpy.utils.register_class(MESH_OT_add_object)
    bpy.utils.register_class(CustomPropertyGroup)
    bpy.types.Scene.custom_props = bpy.props.PointerProperty(type=CustomPropertyGroup)
    bpy.types.VIEW3D_MT_mesh_add.append(menu_func)

but when I change any value in the Redo Panel it immediately returns to it's default value. I'm assuming it's simply re-initializing the properties.

With all the code in one long def execute the Redo Panel works as expected but I cannot organize the panel to my liking (AFAIK)

How can I make the Redo Panel update upon user input but can also have a custom layout?

$\endgroup$
5
  • $\begingroup$ Did you invoke any Blender operator inside object_data_add and AddHole? $\endgroup$
    – HikariTW
    Commented Nov 8, 2020 at 1:04
  • $\begingroup$ @HikariTW object_data_add is imported from bpy_extras.object_utils so I don't actually run anything "inside" it. AddHole does all the heavy lifting using bmesh commands to build a part. It references all the parameters created in CustomPropertyGroup then ends by converting to a mesh, bm.to_mesh(mesh) and mesh.update() then return mesh Even if I comment out the object_data_add command the Redo Panel still does not update its values as I change them $\endgroup$
    – Psyonic
    Commented Nov 8, 2020 at 6:36
  • $\begingroup$ How about change your bl_option from UNDO to UNDO_GROUPED? $\endgroup$
    – HikariTW
    Commented Nov 8, 2020 at 7:50
  • $\begingroup$ Nope, that caused the Redo Panel to not show up at all. Even F6 did nothing :( $\endgroup$
    – Psyonic
    Commented Nov 8, 2020 at 8:40
  • $\begingroup$ What works for me is to do something like this: properties : bpy.props.PointerProperty(type=CustomPropertyGroup) right under bl_info = ..., then everytime you need the properties you can say custom_props = self.properties. This doesn't use the custom properties set in the scene. $\endgroup$
    – Wannes
    Commented Dec 7, 2020 at 18:07

1 Answer 1

1
$\begingroup$

Blender can repeat arguments that can be passed to operator as key word arguments. Those arguments must be defined in operator. Non default arguments will be seen in Info editor.

This happens probably due to the way blender handles operator context overrides but it requires deep dive into source code to confirm.

See example:

import bpy


class MESH_OT_add_object(bpy.types.Operator):
    """Create a new Mesh Object"""
    bl_idname = "mesh.primitive_add_hole"
    bl_label = "Add Hole"
    bl_options = {'REGISTER', 'UNDO'}
    
    i: bpy.props.IntProperty()
    
    def draw(self, context):
        # spams a lot of text:
        # print(f'draw: {context}, {context.scene}, {context.scene.my_int}')
        # print(f'i: {self.i}')
        # print(f'my_int: {context.scene.my_int}')
        # print(f'frame_current: {context.scene.frame_current}')
        layout = self.layout
        row = layout.row()
        layout.prop(self, 'i')
        layout.prop(context.scene, 'frame_current')
        # context.scene.frame_current = 100  # remember, it is illegal to edit properties in drawing code, errors like crazy, will freeze your blender
        layout.prop(context.scene, 'my_int')

    def execute(self, context):
        print(f'execute: {context}, {context.scene}, {context.scene.my_int}')
        print(f'i: {self.i}')
        print(f'my_int: {context.scene.my_int}')
        print(f'frame_current: {context.scene.frame_current}')
        return {'FINISHED'}

def print_me(self, context):
    print(f'update my_int: {self}, {self.my_int}, {context}, {context.scene.my_int}')
    
    
def register():
    bpy.types.Scene.my_int = bpy.props.IntProperty(update=print_me)
    bpy.utils.register_class(MESH_OT_add_object)

register()

enter image description here

Please remember to provide minimal working example it will make things easier. Your example is very hard to follow. It seems in your case operator should hold a copy of properties. This can be done as shown below. Watch out for handling collection copying.

import bpy


def copy_property_group(src: bpy.types.PropertyGroup, dst: bpy.types.PropertyGroup):
    """Copy property group. Can be nested, can have collections. All collections in dst should be empty!! """
    for name, prop in src.rna_type.properties.items():
        if name == 'rna_type':
            continue
        value_src = getattr(src, name)
        value_dst = getattr(dst, name)
        if isinstance(prop, bpy.types.CollectionProperty):
            for iname, i in value_src.items():
                item = value_dst.add()
                item.name = iname
                copy_property_group(i, item)
            assert len(value_src) == len(value_dst), (value_src, value_dst)
        elif isinstance(prop, bpy.types.PointerProperty):
            copy_property_group(value_src, value_dst)
        else:
            setattr(dst, name, value_src)
    
            
class MyComplexProps(bpy.types.PropertyGroup):
    id: bpy.props.StringProperty()
    
    
def draw_my_complex_props_template(layout: bpy.types.UILayout, props: 'MyComplexProps'):
    layout.prop(props, 'id')


class MESH_OT_add_object(bpy.types.Operator):
    """Create a new Mesh Object"""
    bl_idname = "mesh.primitive_add_hole"
    bl_label = "Add Hole"
    bl_options = {'REGISTER', 'UNDO'}
    
    props: bpy.props.PointerProperty(type=MyComplexProps)
    
    def draw(self, context):
        draw_my_complex_props_template(self.layout, self.props)


    def execute(self, context):
        copy_property_group(self.props, context.scene.my_props)
        return {'FINISHED'}

    
def register():
    bpy.utils.register_class(MyComplexProps)
    bpy.types.Scene.my_props = bpy.props.PointerProperty(type=MyComplexProps)
    bpy.utils.register_class(MESH_OT_add_object)

register()
$\endgroup$

You must log in to answer this question.

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