1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
|
# Copyright (c) 2015 Ultimaker B.V.
# Uranium is released under the terms of the LGPLv3 or higher.
from typing import List
from UM.Signal import Signal
from UM.Math.Vector import Vector
from UM.Math.AxisAlignedBox import AxisAlignedBox
from UM.Scene.SceneNode import SceneNode
from UM.Operations.GroupedOperation import GroupedOperation
import copy
## This class is responsible for keeping track of what objects are selected
# It uses signals to notify others of changes in the selection
# It also has a convenience function that allows it to apply a single operation
# to all selected objects.
class Selection:
@classmethod
def add(cls, object):
if object not in cls.__selection:
cls.__selection.append(object)
object.transformationChanged.connect(cls._onTransformationChanged)
cls._onTransformationChanged(object)
cls.selectionChanged.emit()
@classmethod
def remove(cls, object):
if object in cls.__selection:
cls.__selection.remove(object)
object.transformationChanged.disconnect(cls._onTransformationChanged)
cls._onTransformationChanged(object)
cls.selectionChanged.emit()
@classmethod
## Get number of selected objects
def getCount(cls):
return len(cls.__selection)
@classmethod
def getAllSelectedObjects(cls):
return cls.__selection
@classmethod
def getBoundingBox(cls):
bounding_box = None # don't start with an empty bounding box, because that includes (0,0,0)
for node in cls.__selection:
if not isinstance(node, SceneNode):
continue
if not bounding_box:
bounding_box = node.getBoundingBox()
else:
bounding_box = bounding_box + node.getBoundingBox()
if not bounding_box:
bounding_box = AxisAlignedBox.Null
return bounding_box
@classmethod
## Get selected object by index
# \param index index of the object to return
# \returns selected object or None if index was incorrect / not found
def getSelectedObject(cls, index):
try:
return cls.__selection[index]
except:
return None
@classmethod
def isSelected(cls, object):
return object in cls.__selection
@classmethod
def clear(cls):
cls.__selection.clear()
cls.selectionChanged.emit()
@classmethod
## Check if anything is selected at all.
def hasSelection(cls):
return bool(cls.__selection)
selectionChanged = Signal()
selectionCenterChanged = Signal()
@classmethod
def getSelectionCenter(cls):
if not cls.__selection:
cls.__selection_center = Vector.Null
return cls.__selection_center
## Apply an operation to the entire selection
#
# This will create and push an operation onto the operation stack. Dependent
# on whether there is one item selected or multiple it will be just the
# operation or a grouped operation containing the operation for each selected
# node.
#
# \param operation \type{Class} The operation to create and push. It should take a SceneNode as first positional parameter.
# \param args The additional positional arguments passed along to the operation constructor.
# \param kwargs The additional keyword arguments that will be passed along to the operation constructor.
#
# \return list of instantiated operations
@classmethod
def applyOperation(cls, operation, *args, **kwargs):
if not cls.__selection:
return
operations = []
if len(cls.__selection) == 1:
node = cls.__selection[0]
op = operation(node, *args, **kwargs)
operations.append(op)
else:
op = GroupedOperation()
for node in Selection.getAllSelectedObjects():
sub_op = operation(node, *args, **kwargs)
op.addOperation(sub_op)
operations.append(sub_op)
op.push()
return operations
@classmethod
def _onTransformationChanged(cls, node):
cls.__selection_center = Vector.Null
for object in cls.__selection:
cls.__selection_center = cls.__selection_center + object.getWorldPosition()
cls.__selection_center = cls.__selection_center / len(cls.__selection)
cls.selectionCenterChanged.emit()
__selection = [] # type: List[SceneNode]
__selection_center = Vector(0, 0, 0)
|