1
$\begingroup$

i'm using 2.83.4 and if someone could point me to a tutorial on hooking controls from a panel and how to get the redo panel to work right, that would help immensely!

HalfKill is basically the same as the native blender Symmetrize command, but i want to have the option to not remove the mirror modifier at the end.

i have three parameters for it:

x_pos tells it to delete the left side or the right side do_mir tells it to add a mirror modifier (in case i just wanted to delete a side) app_mir tells it to apply the mirror modifier, like symmetrize

my problems: A: checkboxes for these parameters do NOT appear in the panel, although i have created them there. B: those checkboxes DO appear in the redo panel, but when i toggle them, only the first change has any affect

also when i change the checkboxes i get this in the info: bpy.data.window_managers["WinMan"].(null) = True

i... have no idea what that is trying to tell me. ** i have figured this out: when i turn a checkbox on, the winman null is True, and when i turn a checkbox off, the winman null is False. so... it's telling SOMETHING that i'm checking/unchecking things. (how it knows what things, i don't know.)

code: i have updated the code to have both operators and the panel in one script. oh, let me take out 'mirror and split' because that is simple and works fine...

## Select faces on the Right (X neg) half of the model, and delete them.
## Also, add a Mirror Modifier, if desired.  And apply it, if desired.

import bpy
import bmesh


################ HALF KILL
class HalfKill(bpy.types.Operator):
    """Delete half the mesh, add Mirror Modifer"""
    bl_idname = "object.half_kill"
    bl_label = "Half Kill"
    bl_options = { 'REGISTER', 'UNDO'}
    
    ## need some properties (variables) for the panel switches
# https://blender.stackexchange.com/questions/159321/give-a-variable-to-an-operator    
    
    x_pos : bpy.props.BoolProperty(name="Delete Left",
                                   description="Remove X+ side or X-",
                                   default = False) ##if TRUE, delete X POS side
    do_mir : bpy.props.BoolProperty(name="Add Mirror",
                                    description="Add Mirror Modifier",
                                    default=True) ##if TRUE, add the mirror mod
    app_mir: bpy.props.BoolProperty(name="Apply Mirror",
                                    description="Apply Mirror when done",
                                    default=False ) ##if TRUE, apply the mirror mod
    
    @classmethod
    def poll(cls, context):
        return context.active_object is not None
    
    def execute(self, context):
        print('executing')
## Error 1: is object selected?
        ob = context.active_object
        if not (ob.select_get()):
            self.report({'WARNING'}, "No Object Selected.")
            return {'CANCELLED'}
        
        ## Error 2: is there already a mirror on it?
        if (any([m for m in bpy.context.object.modifiers if m.type == "Mirror"])):
            self.report({'WARNING'}, "Object already has Mirror Modifier.")
            return {'CANCELLED'}
        
        ## reference the properties with a variable
        xpos = self.x_pos
        domir = self.do_mir
        appmir = self.app_mir
        
        ## Grab the Middle Line
        bpy.ops.object.mode_set(mode = 'EDIT', toggle = False)
        bpy.ops.mesh.select_all(action = 'DESELECT')
        
        ## use bmesh to access select history and manipulate object
        mesh = ob.data
        bm = bmesh.from_edit_mesh(mesh)
        
        ## Select Middle Vert
        bpy.ops.mesh.select_mode(type = 'VERT')
        
        vtest = False ##check for center vertex
        for vert in bm.verts:
            if(vert.co[0] == 0):  #coordinate 0 (x) is zero (center)
                vert.select = True
                ## to make it active, you need to add it to sel history in bmesh
                bm.select_history.add(vert)
                vtest = True
                break
            
        if(vtest): ##we found a center vert!
            # select side of ative, based on x_pos
            if(self.x_pos):
                bpy.ops.mesh.select_axis(sign='POS')
            else:
                bpy.ops.mesh.select_axis(sign='NEG')
            ## switch to face mode and delete; don't forget to flush :X
            bm.select_mode = {'VERT', 'EDGE', 'FACE'}
            bm.select_flush_mode()
            # activate face select mode
            context.tool_settings.mesh_select_mode = (False, False, True)
            # Delete
            bpy.ops.mesh.delete(type='FACE')    

        else:
            print("no centerline found, using bisect")
            # No Center, make one!  use Bisect
            # need to select all first
            for vert in bm.verts:
                vert.select = True            
            # co = position, it is global, not local. no(rmals) 1,0,0 = face X+
            ## clear_inner=True (delete X- side)  clear_outer=True (delete X+ side)
            ## USING BOTH AT ONCE CANCELS IT!
            ## start and end x/y are...??? wtf??? default = -inf:inf   
            if(xpos):
                print('clear inner')
                bpy.ops.mesh.bisect(plane_co=(0, 0, 0), plane_no=(1, 0, 0),  clear_inner=True) #,threshold=0.0001) ##, xstart=463, xend=462, ystart=144, yend=509)
            else:
                print('clear outer')
                bpy.ops.mesh.bisect(plane_co=(0, 0, 0), plane_no=(1, 0, 0),  clear_outer=True)           
            print("well, did it bisect?")     
            ## i think you need to update the thing? :/
                            
            # self.report('WARNING', "No centerline found.")
            # return {'CANCELLED'}
        
        ## go to object mode, cuz staying in edit looks funny
        bpy.ops.object.mode_set(mode='OBJECT')

        ## in this part, i determine if the Mirror toggle is on?
        #test = self.do_mir
        if(domir):
            print("doing mirror")
            bpy.ops.object.modifier_add(type='MIRROR')
            
        ## and, determine if you apply it
        #test = self.app_mir
        if(appmir):
            print("applying mirror")
            bpy.ops.object.modifier_apply(apply_as='DATA', modifier="Mirror") 
            
        #bm.to_mesh(mesh)    
        #bm.update()      

    ## that should do it
        print('finished')
        return {'FINISHED'}        

    
    def invoke(self, context, param):
        #darkfall's floating panel
        return context.window_manager.invoke_props_dialog(self)
        

########################### PANEL
class MirrorAndSplitPanel(bpy.types.Panel):
    """Split Mirror & HalfKill"""
    bl_label = "Mirror Ops"  ##on the section header
    bl_idname = "OBJECT_PT_mirror_and_split"  ##ui name for it
## ^^set automatically if not defined here.^^
    bl_space_type = 'VIEW_3D'  ##Where
    bl_region_type = 'UI'  ##what shelf
#[‘WINDOW’, ‘HEADER’, ‘CHANNELS’, ‘TEMPORARY’, ‘UI’, ‘TOOLS’, ‘TOOL_PROPS’, ‘PREVIEW’, ‘HUD’, ‘NAVIGATION_BAR’, ‘EXECUTE’, ‘FOOTER’, ‘TOOL_HEADER’], default ‘WINDOW’    
    bl_category = "Edit"  ##Name of Tab
    #bl_context = "objectmode" or "mesh_mode" ## what mode, objectmode or mesh_mode or...?
    bl_options = {"DEFAULT_CLOSED"}
    
    @classmethod
    def poll(self,context):
        test = False
        if context.mode in {"EDIT_MESH", "OBJECT"}:
            test = True
        if context.object is not None:
            test = True
        return test            
        #return (context.mode in {"EDIT_MESH", "OBJECT"})
    #        return (context.mode in {'EDIT_MESH','OBJECT'}) or (context.object is not None)

    def draw(self, context):
        layout = self.layout  ## create a layout for yourself
        #scene = context.scene ## dunno, but checkbox examples use it
        #mytool = scene.mytool ## no idea, but checkboxes need it
        # it causes the buttons to not appear

        #obj = context.object  ## what the function is operating on: object --not yet!

        #row = layout.row()
        #row.label(text= bl_label, icon = 'MODIFIERS')
        

        row = layout.row()
        row.operator("object.mirror_and_split")
        row = layout.row()
        row.operator("object.half_kill")
        #sub row
        #sub = row.row()
        # layout.prop(mytool, "my_bool", text="Bool Property")
        ## where mytool = scene.mytool :X
        ##    & parameter names are in quotes
        
        ##Darkfall doesn't 'draw' these... they appear automatically???
        #row=layout.row()
        #what is difference between layout.prop and row.prop? neither work
        #row.prop(obj, "x_pos", text="Remove X+ Side")
        #row.prop(obj, "do_mir", text="Add Mirror")
        #row.prop(obj, "app_mir", text="Apply Mirror")
        ## why do these only appear on redo panel?  cuz undo is on?
        
        layout.separator()  ## small blank 'whitespace'




########################## REGISTER

    
def register():
    bpy.utils.register_class(Mirror_and_Split)
    bpy.utils.register_class(HalfKill)
    bpy.utils.register_class(MirrorAndSplitPanel)
    
def unregister():
    bpy.utils.unregister_class(Mirror_and_Split)
    bpy.utils.unregister_class(HalfKill)
    bpy.utils.unregister_class(MirrorAndSplitPanel)
    
if __name__ == "__main__":
    register()
          

PS: if you post example codes, could you please comment it. i'm REALLY new at this and especially python/OOP, and if it's not clear where something came from, i'm lost. thanks!

$\endgroup$
1
  • $\begingroup$ okay, i converted this back to a 2.79 script, and it works flawlessly. the redo panel shows up, the checkboxes activate/deactivate the functions properly in real-time... i'm astounded. what am i doing wrong for 2.8+? $\endgroup$
    – blr
    Sep 22 '20 at 15:27

Your Answer

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

Browse other questions tagged or ask your own question.