4
$\begingroup$

Watching a tutorial from youtube I managed to setup an addon with a tab into VIEW_3D > UI, which contains a panel with some buttons (operators) where if you press any of them, you get a different dialog window with some properties, labels etc.

enter image description here

What I want to do is to have the ability to see changes in real time and not when I press OK. Something like this...

enter image description here

Is that possible? And if yes, how can I do that? Because I tried to execute my action through draw method but I get this error AttributeError: Writing to ID classes in this context is not allowed: Scene, Scene datablock, error setting LayerCollection.hide_viewport.

Here is an example of my code...

class fu3dm_WM_OT_Model_Prefs_Operator(bpy.types.Operator):
    bl_idname = "wm.fu3dm_model_prefs"
    bl_label = "Model Preferences"
    bl_description = "Blah blah blah"

    fu3dm_model_gender : bpy.props.EnumProperty(name="Gender", description="Choose model's gender", items= [('OP1', "Male", ""), ('OP2', "Female", "")], default='OP1')
    fu3dm_model_head_male : bpy.props.EnumProperty(name="Head", description="Choose model's head", items= [('OP1', "Generic Male", ""), ('OP2', "Christiano Ronaldo", "")], default='OP2')
    fu3dm_model_head_female : bpy.props.EnumProperty(name="Head", description="Choose model's head", items= [('OP1', "Generic Female", ""), ('OP2', "Ashlyn Harris", "")], default='OP2')
    fu3dm_model_eyes_color : bpy.props.IntProperty(name="Eyes Color", description="Choose model's eyes color", min=1, max=10, default=1)
    fu3dm_model_skin_color: bpy.props.IntProperty(name="Skin Color", description="Choose model's skin color", min=1, max=10, default=1)
    fu3dm_model_upper_body : bpy.props.EnumProperty(name="Upper Body", description="Choose model's upper body mesh", items= [('OP1', "Arms for Long Sleeves", ""), ('OP2', "Arms for Short Sleeves", ""), ('OP3', "Full Upper Body", "")], default='OP2')
    fu3dm_model_lower_body : bpy.props.EnumProperty(name="Lower Body", description="Choose model's lower body mesh", items= [('OP1', "Legs without Socks", ""), ('OP2', "Legs with Low Socks", ""), ('OP3', "Legs with Short Socks", ""), ('OP4', "Legs with Medium Socks", ""), ('OP5', "Legs with Long Socks", ""), ('OP6', "Legs with Medium Socks but without Shinpads", ""), ('OP7', "Full Lower Body", "")], default='OP4')
    
    def invoke(self, context, event):
        return context.window_manager.invoke_props_dialog(self)
        bpy.ops.wm.fu3dm_model_prefs('INVOKE_DEFAULT')
    
    def draw(self, context):    
        layout = self.layout    
        box = layout.box()
        col = box.column()

        row = col.split()
        row.label(text="Gender:")
        row.prop(self, "fu3dm_model_gender", text="")

        if self.fu3dm_model_gender == 'OP1':
            row = col.split()
            row.label(text="Head:")
            row.prop(self, "fu3dm_model_head_male", text="")
            self.do_it(self=self, context=context)
        else:
            row = col.split()
            row.label(text="Head:")
            row.prop(self, "fu3dm_model_head_female", text="")
            self.do_it(self=self, context=context)

        row = col.split()
        row.label(text="Eyes Color:")
        row.prop(self, "fu3dm_model_eyes_color", text="")

        row = col.split()
        row.label(text="Skin Color:")
        row.prop(self, "fu3dm_model_skin_color", text="")

        row = col.split()
        row.label(text="Upper Body:")
        row.prop(self, "fu3dm_model_upper_body", text="")

        row = col.split()
        row.label(text="Lower Body:")
        row.prop(self, "fu3dm_model_lower_body", text="")

    def execute(self, context):
        pass
        return {"FINISHED"}

    @staticmethod
    def do_it(self, context):
        vlayer = bpy.context.scene.view_layers['View Layer']
        if self.fu3dm_model_gender == 'OP1':
            vlayer.layer_collection.children['MALE'].hide_viewport = False
            vlayer.layer_collection.children['FEMALE'].hide_viewport = True
        else:
            vlayer.layer_collection.children['MALE'].hide_viewport = True
            vlayer.layer_collection.children['FEMALE'].hide_viewport = False

Please keep in mind that I am totally new to blender and python!!!

$\endgroup$
7
  • 3
    $\begingroup$ See blender.stackexchange.com/questions/23974/… $\endgroup$ – batFINGER Oct 6 '20 at 8:58
  • 1
    $\begingroup$ As a side note, your do_it method shouldn't be a static method and the second line of your invoke method is never executed since it is written after a return statement $\endgroup$ – Gorgious Oct 6 '20 at 9:01
  • 2
    $\begingroup$ First of all, most likely just copied from a bad tutorial (link would be helpful), replace bpy.context.* by context.* Why do_it() method has a decorator? Recommend split up your question into multiple ones and always provide a minimal working example to make your question useful to others too. $\endgroup$ – brockmann Oct 6 '20 at 9:02
  • 2
    $\begingroup$ IMO doit should instead be the execute method. Changing props fires the execute method. pasteall.org/VSiA $\endgroup$ – batFINGER Oct 6 '20 at 9:34
  • 2
    $\begingroup$ Can call any other method from execute. The execute method gets fired when props change. To throw a spanner in the works: defining a property group with update methods on properties, and layout in a sub-panel may be a more robust approach to setting preferences. $\endgroup$ – batFINGER Oct 6 '20 at 9:48
6
$\begingroup$

You need to set a Redo Panel.

In this tutorial Sybren shows how to create an operator with a redo panel (at 8:17 he starts talking about it)

At 13:38 he talks exactly about what you need. Basicly you need to add:

bl_options = {'REGISTER','UNDO'}

after your bl_description.

$\endgroup$
2
  • 1
    $\begingroup$ I did not know that is called Redo Panel. Thank you very much for your answer. Solved my problem. One more question. Do I have to keep this def invoke(self, context, event): ...etc anyway? Because I removed it and I added bl_options = {'REGISTER','UNDO'}and works fine!!! $\endgroup$ – Simonetos The Greek Oct 6 '20 at 9:45
  • 2
    $\begingroup$ @SimonetosTheGreek use invoke to have a popup at location of mouse event (eg link posted in q comment with invoke props popup) Without defaults to the bottom LH corner in 3D view. $\endgroup$ – batFINGER Oct 6 '20 at 9:52

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.