Remove the rest of the Delete menu for meshes in edit mode

Reiner's picture
Project: 
Bforartists Tracker

There is still a delete menu quiz left with some entries. We need to remove this at one point. There is a smart delete addon to buy. I don't have it, but it shows that it is technically possible to replace the Delete menu by a single hotkey that deletes the selected elements dependant of the mode and what is selected.

We have already separated all dissolve operations. We might have to separate the delete edgeloop tool too. But the remaining menu items should be manageable then. There is not this much left.

Status: 
Closed (fixed)
Priority: 
Normal
Category: 
Task
Component: 
User interface
Assigned: 
Images: 
Reporter: 
Created: 
Mon, 08/15/2016 - 09:24
Updated: 
Thu, 09/22/2016 - 10:40

Comments

20
Reiner's picture

Body: Old » New
Reiner's picture

Body: Old » New
Reiner's picture

Assigned: Unassigned » Reiner
Reiner's picture

This remaining rest is in Edit mode. And there is unfortunately more than one menu left. One for meshes, one for curves ...

So this task needs to be split. Let's make this task for meshes in edit mode.

Reiner's picture

Title: Remove the rest of the Delete menu. » Remove the rest of the Delete menu for meshes in edit mode
Reiner's picture

Hm, this is writing an addon. Not in the mood for this at the moment. Postponing.

Reiner's picture

Status: Active » Postponed
Reiner's picture

Assigned: Reiner » Unassigned
Reiner's picture

Assigned: Unassigned » Reiner
Reiner's picture

Stumbled across a script that exactly does what i need. So investigating now.

Reiner's picture

Status: Postponed » Active
Reiner's picture

Looking good. It seems to be a fragment from the original Smart Delete addon. With some parts missing and a quirk. It nevertheless does the job from what i can see.

I will make it an addon again now, fix the quirks, and put it into the bfa standard addons then. I will not make it a Blender addon though. For Blender this addon already exists, even when it's commercial. So no need to rerelease it for Blender.

 

import bpy
bl_info = {
"name": "Smart Delete :)",
"location": "View3D > Add > Mesh > Smart Delete,",
"description": "Auto detect a delete elements",
"author": "Vladislav Kindushov",
"version": (0,1),
"blender": (2, 7, 7),
"category": "Mesh",
}

def find_connected_verts(me, found_index):
edges = me.edges
connecting_edges = [i for i in edges if found_index in i.vertices[:]]
print('connecting_edges',len(connecting_edges))
return len(connecting_edges)

class MeshDissolveContextual(bpy.types.Operator):
""" Dissolves mesh elements based on context instead
of forcing the user to select from a menu what
it should dissolve.
"""
bl_idname = "mesh.dissolve_contextual"
bl_label = "Mesh Dissolve Contextual"
bl_options = {'UNDO'}

use_verts = bpy.props.BoolProperty(name="Use Verts", default=False)

@classmethod
def poll(cls, context):
return (context.active_object is not None)# and (context.mode == "EDIT_MESH")

def execute(self, context):
if bpy.context.mode == 'OBJECT':
bpy.ops.object.delete()
print ('hello')
elif bpy.context.mode == 'EDIT_MESH':
select_mode = context.tool_settings.mesh_select_mode
me = context.object.data
if select_mode[0]:
bpy.ops.mesh.dissolve_verts()
elif select_mode[1] and not select_mode[2]:
bpy.ops.mesh.dissolve_edges(use_verts=self.use_verts)
bpy.ops.mesh.select_mode(type='VERT')
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.mode_set(mode='EDIT')
vs = [v.index for v in me.vertices if v.select]
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.object.mode_set(mode='OBJECT')

for v in vs:
vv = find_connected_verts(me, v)
if vv==2:
me.vertices[v].select = True
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.dissolve_verts()
bpy.ops.mesh.select_all(action='DESELECT')

for v in vs:
me.vertices[v].select = True

elif select_mode[2] and not select_mode[1]:
bpy.ops.mesh.delete(type='FACE')
else:
bpy.ops.mesh.dissolve_verts()

return {'FINISHED'}

bpy.utils.register_class(MeshDissolveContextual)

def register():
#bpy.utils.register_class(SmartDelete)

kc = bpy.context.window_manager.keyconfigs.addon
if kc:
km = kc.keymaps.new(name="3D View", space_type="VIEW_3D")
kmi = km.keymap_items.new('mesh.dissolve_contextual', 'X', 'PRESS',)

def unregister():
bpy.utils.unregister_class(MeshDissolveContextual)
kc = bpy.context.window_manager.keyconfigs.addon
if kc:
km = kc.keymaps["3D View"]
for kmi in km.keymap_items:
if kmi.idname == 'mesh.dissolve_contextual':
km.keymap_items.remove(kmi)
break

if __name__ == "__main__":
register()

 

Reiner's picture

Would have been too easy i guess I-m so happy

The addon throws an error when you try to delete all vertices. Investigating ...

Current state:

# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

#This script is based at the work of Vladislav Kindushov and its Smart Delete addon

import bpy
bl_info = {
"name": "Smart Delete bfa",
"description": "Auto detect a delete elements",
"author": "Reiner 'Tiles' Prokein",
"version": (0,1),
"blender": (2, 7, 6),
"category": "Mesh",
}

def find_connected_verts(me, found_index):
edges = me.edges
connecting_edges = [i for i in edges if found_index in i.vertices[:]]
#print('connecting_edges',len(connecting_edges)) # Do not want to polute the console.
return len(connecting_edges)

class MeshDissolveContextual_bfa(bpy.types.Operator):
""" Dissolves mesh elements based on context instead
of forcing the user to select from a menu what
it should dissolve.
"""
bl_idname = "mesh.dissolve_contextual_bfa"
bl_label = "Mesh Dissolve Contextual"
bl_options = {'UNDO'}

use_verts = bpy.props.BoolProperty(name="Use Verts", default=False)

mymode = 0 # The script changes the modes at one point. We want to store the mode before the operation.

@classmethod
def poll(cls, context):
return (context.active_object is not None)# and (context.mode == "EDIT_MESH")

def execute(self, context):

if bpy.context.mode == 'EDIT_MESH':
select_mode = context.tool_settings.mesh_select_mode
me = context.object.data
# Vertices select
if select_mode[0]:
mymode = 0
bpy.ops.mesh.dissolve_verts()
# Edge select
elif select_mode[1] and not select_mode[2]:
mymode = 1
bpy.ops.mesh.dissolve_edges(use_verts=self.use_verts)
bpy.ops.mesh.select_mode(type='VERT')
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.mode_set(mode='EDIT')
vs = [v.index for v in me.vertices if v.select]
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.object.mode_set(mode='OBJECT')

for v in vs:
vv = find_connected_verts(me, v)
if vv==2:
me.vertices[v].select = True
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.dissolve_verts()
bpy.ops.mesh.select_all(action='DESELECT')

for v in vs:
me.vertices[v].select = True
# Face Select
elif select_mode[2] and not select_mode[1]:
mymode = 2
bpy.ops.mesh.delete(type='FACE')
#Dissolve Vertices
else:
bpy.ops.mesh.dissolve_verts()

# back to previous select mode! When you delete in edge select mode
# then the selection method can jump to vertex select mode. Unwanted behaviour.
# So we put it back to edge select mode manually after done.

if mymode == 1:
bpy.ops.mesh.select_mode(type='EDGE')

return {'FINISHED'}

def register():
bpy.utils.register_class(MeshDissolveContextual_bfa)

def unregister():
bpy.utils.unregister_class(MeshDissolveContextual_bfa)

if __name__ == "__main__":
register()

 

Reiner's picture

The script makes trouble, dependant of the selected geometry.  I guess that this fragment was from an early development stage of the original script. Seems that i have to write my own script instead. It nevertheless gives me a few good ideas and hints.

The bad news is, Blender gives the exact same error when you try to dissolve this geometry by edge. Hmmmmm. Seems that i am trapped by a Blender quirk here I-m so happy

Image: 
Reiner's picture

Here are the two vital scripts. The one is the BFA smart delete. The other is the important step to catch the error from the Blender Stack Exchange. Many thanks for help. Which fixes the upper big error message, and displays just the small string as for performing the dissolve operation directly. And i have added a little message to the console that explains the problem a bit better.

So delete is working as i want it now. At least for now. The workflow development cycle will show how useful this method is. This error message when the dissolve fails may or may not be in the way for further work.

But nothing is lost. The old delete menu quiz will remain onboard. I will give it the shortcut ctrl + del now. For that i have to remove the three hotkeys for the vertice, edge and face select methods. They are not longer needed anyways since now we have the smart delete method which is mode dependant. And the delete menu quiz also still exists. So nothing is lost here.

The addon:

# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

#This script is based at the work of Vladislav Kindushov and its Smart Delete addon

import bpy
bl_info = {
"name": "Smart Delete bfa",
"description": "Auto detect a delete elements",
"author": "Reiner 'Tiles' Prokein",
"version": (0,1),
"blender": (2, 7, 6),
"category": "Mesh",
}

def find_connected_verts(me, found_index):
edges = me.edges
connecting_edges = [i for i in edges if found_index in i.vertices[:]]
#print('connecting_edges',len(connecting_edges)) # Do not want to polute the console.
return len(connecting_edges)

class MeshDissolveContextual_bfa(bpy.types.Operator):
""" Dissolves mesh elements based on context instead
of forcing the user to select from a menu what
it should dissolve.
"""
bl_idname = "mesh.dissolve_contextual_bfa"
bl_label = "Mesh Dissolve Contextual"
bl_options = {'UNDO'}

use_verts = bpy.props.BoolProperty(name="Use Verts", default=False)

mymode = 0 # The script changes the modes at one point. We want to store the mode before the operation.

@classmethod
def poll(cls, context):
return (context.active_object is not None)# and (context.mode == "EDIT_MESH")

def execute(self, context):

if bpy.context.mode == 'EDIT_MESH':
select_mode = context.tool_settings.mesh_select_mode
me = context.object.data
# Vertices select
if select_mode[0]:
mymode = 0

# bpy.ops.mesh.dissolve_verts() # This throws a full error report in the face of the user.
# So we need the below method-
# Dissolve with catching the error report so that the user does not have the impression that the script is broken.
if bpy.ops.mesh.dissolve_verts.poll():

try:
bpy.ops.mesh.dissolve_verts()

except RuntimeError as exception:
error = " ".join(exception.args)
print("Invalid boundary region to join faces\nYou cannot delete this geometry that way.\nTry another delete method or another selection")
self.report({'ERROR'}, error)

# Edge select
elif select_mode[1] and not select_mode[2]:
mymode = 1

# bpy.ops.mesh.dissolve_edges(use_verts=self.use_verts) # This throws a full error report in the face of the user.
# So we need the below method-
# Dissolve with catching the error report so that the user does not have the impression that the script is broken.
if bpy.ops.mesh.dissolve_edges.poll():

try:
bpy.ops.mesh.dissolve_edges(use_verts=self.use_verts)

bpy.ops.mesh.select_mode(type='VERT')
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.mode_set(mode='EDIT')
vs = [v.index for v in me.vertices if v.select]
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.object.mode_set(mode='OBJECT')

for v in vs:
vv = find_connected_verts(me, v)
if vv==2:
me.vertices[v].select = True
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.dissolve_verts()
bpy.ops.mesh.select_all(action='DESELECT')

for v in vs:
me.vertices[v].select = True

except RuntimeError as exception:
error = " ".join(exception.args)
print("Invalid boundary region to join faces\nYou cannot delete this geometry that way. \nTry another delete method or another selection")
self.report({'ERROR'}, error)

# Face Select
elif select_mode[2] and not select_mode[1]:
mymode = 2
bpy.ops.mesh.delete(type='FACE')
#Dissolve Vertices
else:
bpy.ops.mesh.dissolve_verts()

# back to previous select mode! When you delete in edge select mode
# then the selection method can jump to vertex select mode. Unwanted behaviour.
# So we put it back to edge select mode manually after done.

if mymode == 1:
bpy.ops.mesh.select_mode(type='EDGE')

return {'FINISHED'}

def register():
bpy.utils.register_class(MeshDissolveContextual_bfa)

def unregister():
bpy.utils.unregister_class(MeshDissolveContextual_bfa)

if __name__ == "__main__":
register()

The vital solution to the report error. Big thanks goes to poor:

# http://blender.stackexchange.com/q/63347/3710

import bpy

class CustomDissolveEdgeOperator(bpy.types.Operator):
bl_idname = 'custom.dissolve_edges'
bl_label = 'Custom Edge Dissolve'
bl_options = {'REGISTER', 'UNDO'}

'''
@classmethod
def poll(cls, context):
return bpy.ops.mesh.dissolve_edges.poll()
'''

def execute(self, context):

if bpy.ops.mesh.dissolve_edges.poll():
print("Context correct")
try:
bpy.ops.mesh.dissolve_edges()
except RuntimeError as exception:
error = " ".join(exception.args)
print("error:", error)
self.report({'ERROR'}, error)
else:
print ("Context incorrect")
self.report({'ERROR'}, "Context incorrect")

return {'FINISHED'}

def draw_edge_dissolve(self, context):
layout = self.layout
layout.operator("custom.dissolve_edges")

def register():
bpy.utils.register_class(CustomDissolveEdgeOperator)
bpy.types.VIEW3D_MT_edit_mesh_delete.append(draw_edge_dissolve)

def unregister():
bpy.utils.unregister_class(CustomDissolveEdgeOperator)
bpy.types.VIEW3D_MT_edit_mesh_delete.remove(draw_edge_dissolve)

if __name__ == "__main__":
register()

 

Image: 
Reiner's picture

Note, this script just covers the mesh delete cases. The other stuff needs further investigation.

Reiner's picture

Investigated, and looks okay. So let's finish this task here. The addon needs a menu entry, the three sub menu items needs the hotkeys removed, the delete button needs another hotkey so that the smart delete button has the delete key ...

I don't see a useful way to remove the delete quiz for curve and surface in edit mode. But that's just one menu left where there were a dozen before. And that's okay for me, for now.

Image: 
Reiner's picture

Small ajdustment at the script to have a menu entry ...

# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

#This script is based at the work of Vladislav Kindushov and its Smart Delete addon

import bpy
bl_info = {
"name": "Smart Delete bfa",
"description": "Auto detect a delete elements",
"author": "Reiner 'Tiles' Prokein",
"version": (0,1),
"blender": (2, 7, 6),
"category": "Mesh",
}

def find_connected_verts(me, found_index):
edges = me.edges
connecting_edges = [i for i in edges if found_index in i.vertices[:]]
#print('connecting_edges',len(connecting_edges)) # Do not want to polute the console.
return len(connecting_edges)

class MeshDissolveContextual_bfa(bpy.types.Operator):
""" Dissolves mesh elements based on context instead
of forcing the user to select from a menu what
it should dissolve.
"""
bl_idname = "mesh.dissolve_contextual_bfa"
bl_label = "Smart Delete"
bl_options = {'UNDO'}

use_verts = bpy.props.BoolProperty(name="Use Verts", default=False)

mymode = 0 # The script changes the modes at one point. We want to store the mode before the operation.

@classmethod
def poll(cls, context):
return (context.active_object is not None)# and (context.mode == "EDIT_MESH")

def execute(self, context):

if bpy.context.mode == 'EDIT_MESH':
select_mode = context.tool_settings.mesh_select_mode
me = context.object.data
# Vertices select
if select_mode[0]:
mymode = 0

# bpy.ops.mesh.dissolve_verts() # This throws a full error report in the face of the user.
# So we need the below method-
# Dissolve with catching the error report so that the user does not have the impression that the script is broken.
if bpy.ops.mesh.dissolve_verts.poll():

try:
bpy.ops.mesh.dissolve_verts()

except RuntimeError as exception:
error = " ".join(exception.args)
print("Invalid boundary region to join faces\nYou cannot delete this geometry that way.\nTry another delete method or another selection")
self.report({'ERROR'}, error)

# Edge select
elif select_mode[1] and not select_mode[2]:
mymode = 1

# bpy.ops.mesh.dissolve_edges(use_verts=self.use_verts) # This throws a full error report in the face of the user.
# So we need the below method-
# Dissolve with catching the error report so that the user does not have the impression that the script is broken.
if bpy.ops.mesh.dissolve_edges.poll():

try:
bpy.ops.mesh.dissolve_edges(use_verts=self.use_verts)

bpy.ops.mesh.select_mode(type='VERT')
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.mode_set(mode='EDIT')
vs = [v.index for v in me.vertices if v.select]
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.object.mode_set(mode='OBJECT')

for v in vs:
vv = find_connected_verts(me, v)
if vv==2:
me.vertices[v].select = True
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.dissolve_verts()
bpy.ops.mesh.select_all(action='DESELECT')

for v in vs:
me.vertices[v].select = True

except RuntimeError as exception:
error = " ".join(exception.args)
print("Invalid boundary region to join faces\nYou cannot delete this geometry that way. \nTry another delete method or another selection")
self.report({'ERROR'}, error)

# Face Select
elif select_mode[2] and not select_mode[1]:
mymode = 2
bpy.ops.mesh.delete(type='FACE')
#Dissolve Vertices
else:
bpy.ops.mesh.dissolve_verts()

# back to previous select mode! When you delete in edge select mode
# then the selection method can jump to vertex select mode. Unwanted behaviour.
# So we put it back to edge select mode manually after done.

if mymode == 1:
bpy.ops.mesh.select_mode(type='EDGE')

return {'FINISHED'}

def menu_func(self, context):
self.layout.operator(MeshDissolveContextual_bfa.bl_idname)

def register():
bpy.utils.register_class(MeshDissolveContextual_bfa)
bpy.types.VIEW3D_MT_edit_mesh.append(menu_func)

def unregister():
bpy.utils.unregister_class(MeshDissolveContextual_bfa)
bpy.types.VIEW3D_MT_edit_mesh.remove(menu_func)

if __name__ == "__main__":
register()

 

Reiner's picture

Reiner's picture

Status: Active » Closed (fixed)