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!