Files
Nexus/2023/scripts/animation_tools/dwpicker/selection.py
2025-11-23 23:31:18 +08:00

138 lines
4.1 KiB
Python

from maya import cmds
class NameclashError(BaseException):
def __init__(self, nodes=None):
self.clashes = [node for node in nodes or [] if len(cmds.ls(node)) > 1]
message = 'Some nodes exists more than once:\n'
nodes = '\n - '.join(self.clashes)
super(NameclashError, self).__init__(message + nodes)
def select_targets(shapes, selection_mode='replace'):
shapes = [s for s in shapes if s.targets()]
hovered = [s for s in shapes if s.hovered]
targets = [t for s in hovered for t in s.targets() if cmds.objExists(t)]
targets = list(dict.fromkeys(targets))
current_selection = cmds.ls(selection=True, long=True)
targets_selection = cmds.ls(targets, long=True)
if len(set(targets_selection)) != len(set(targets)):
raise NameclashError(targets)
if selection_mode == 'add':
new_selection = list(
dict.fromkeys(current_selection + targets_selection))
elif selection_mode == 'replace':
new_selection = targets_selection
elif selection_mode == 'invert':
new_selection = current_selection[:]
for target in targets_selection:
if target not in new_selection:
new_selection.append(target)
else:
new_selection.remove(target)
elif selection_mode == 'remove':
new_selection = [
s for s in current_selection if s not in targets_selection]
else:
raise NotImplementedError(
'Unsupported selection mode {}'.format(selection_mode))
# Only call cmds.select if it will actually change the selection.
# This is needed to prevent "empty undoes", where Maya will register an
# undo for all calls to cmds.select.
# SEE: https://forums.autodesk.com/t5/maya-ideas/consolidate-undo-steps-for-selection/idi-p/13331011
if current_selection != new_selection:
cmds.select(new_selection)
def select_shapes_from_selection(shapes):
selection = cmds.ls(sl=True)
for shape in shapes:
if not shape.targets():
shape.selected = False
continue
for target in shape.targets():
if target not in selection:
shape.selected = False
break
else:
shape.selected = True
class Selection():
def __init__(self, document=None):
self.document = document
self.ids = []
self.mode = 'replace'
def set(self, shapes):
if self.mode == 'add':
if shapes is None:
return
return self.add(shapes)
elif self.mode == 'replace':
if shapes is None:
return self.clear()
return self.replace(shapes)
elif self.mode == 'invert':
if shapes is None:
return
return self.invert(shapes)
elif self.mode == 'remove':
if shapes is None:
return
for shape in shapes:
if shape in self.shapes:
self.remove(shape)
def replace(self, shapes):
self.ids = [s.options['id'] for s in shapes]
def add(self, shapes):
self.ids.extend([s.options['id'] for s in shapes if s not in self])
def remove(self, shape):
self.ids.remove(shape.options['id'])
def invert(self, shapes):
for shape in shapes:
if shape.options['id'] not in self.ids:
self.add([shape])
else:
self.remove(shape)
@property
def shapes(self):
shapes = [self.document.shapes_by_id.get(id_) for id_ in self.ids]
return [shape for shape in shapes if shape is not None]
def clear(self):
self.ids = []
def __len__(self):
return len(self.ids)
def __bool__(self):
return bool(self.ids)
__nonzero__ = __bool__
def __getitem__(self, i):
return self.document.shapes_by_id[self.ids[i]]
def __iter__(self):
return self.shapes.__iter__()
def get_selection_mode(ctrl, shift):
if not ctrl and not shift:
return 'replace'
elif ctrl and shift:
return 'invert'
elif shift:
return 'add'
return 'remove'