Beispiel #1
0
    def _setUpFilePathForBlender(self):
        """Checks if the selection of objects is correct and allowed and calls the function to build the command. """

        # Checks if path to this plugin and path to blender are correct.
        CuraBlender.verifyBlenderPath(manual=False)

        # Only continues if correct path to blender is set.
        if verified_blender_path and not self._checkGrouped():
            # If no object is selected, check if the objects belong to more than one file.
            if len(Selection.getAllSelectedObjects()) == 0:
                open_files = set()
                for node in DepthFirstIterator(Application.getInstance(
                ).getController().getScene().getRoot()):
                    if isinstance(node, CuraSceneNode) and node.getMeshData(
                    ).getFileName():
                        if '_curasplit_' in node.getMeshData().getFileName():
                            file_path = node.getMeshData().getFileName()
                            file_path = '{}.blend'.format(
                                file_path[:file_path.index('_curasplit_')])
                        else:
                            file_path = node.getMeshData().getFileName()
                        open_files.add(file_path)
                # Opens the objects in blender, if they belong to only one file.
                if len(open_files) == 1:
                    self._buildCommandForBlender(open_files.pop())
                else:
                    message = Message(
                        text=catalog.i18nc('@info', 'Select Object first.'),
                        title=catalog.i18nc(
                            '@info:title',
                            'Please select the object you want to open.'))
                    message.show()
            # If one object is selected, opens it's file reference (file name).
            elif len(Selection.getAllSelectedObjects()) == 1:
                for selection in Selection.getAllSelectedObjects():
                    file_path = selection.getMeshData().getFileName()
                    if '_curasplit_' in file_path:
                        file_path = '{}.blend'.format(
                            file_path[:file_path.index('_curasplit_')])
                    self._buildCommandForBlender(file_path)
            # If multiple objects are selected, checks if they belong to more than one file.
            else:
                files = set()
                for selection in Selection.getAllSelectedObjects():
                    file_path = selection.getMeshData().getFileName()
                    if '_curasplit_' in file_path:
                        file_path = '{}.blend'.format(
                            file_path[:file_path.index('_curasplit_')])
                    files.add(file_path)
                # Opens the objects in blender, if they belong to only one file.
                if len(files) == 1:
                    self._buildCommandForBlender(file_path)
                else:
                    message = Message(
                        text=catalog.i18nc('@info',
                                           'Please rethink your selection.'),
                        title=catalog.i18nc(
                            '@info:title',
                            'Select only objects from same file'))
                    message.show()
Beispiel #2
0
 def setSelected(self, key):
     for index in range(0,len(self.items)):
         if self.items[index]["key"] == key:
             for node in Application.getInstance().getController().getScene().getRoot().getAllChildren():
                 if id(node) == key:
                     if node not in Selection.getAllSelectedObjects(): #node already selected
                         Selection.add(node)
                         if self.items[index]["depth"] == 1: #Its a group node
                             for child_node in node.getChildren(): 
                                 if child_node not in Selection.getAllSelectedObjects(): #Set all children to parent state (if they arent already)
                                     Selection.add(child_node) 
                     else:
                         Selection.remove(node)
                         if self.items[index]["depth"] == 1: #Its a group
                             for child_node in node.getChildren():
                                 if child_node in Selection.getAllSelectedObjects():
                                     Selection.remove(child_node)    
                        
     all_children_selected = True
     #Check all group nodes to see if all their children are selected (if so, they also need to be selected!)
     for index in range(0,len(self.items)):
         if self.items[index]["depth"] == 1:
             for node in Application.getInstance().getController().getScene().getRoot().getAllChildren():
                 if node.hasChildren():
                     if id(node) == self.items[index]["key"] and id(node) != key: 
                         for index, child_node in enumerate(node.getChildren()):
                             if not Selection.isSelected(child_node):
                                 all_children_selected = False #At least one of its children is not selected, dont change state
                                 break 
                         if all_children_selected:
                             Selection.add(node)
                         else:
                             Selection.remove(node)
     #Force update                  
     self.updateList(Application.getInstance().getController().getScene().getRoot())
Beispiel #3
0
    def test_addRemoveSelection(self):
        node_1 = SceneNode()
        Selection.add(node_1)

        assert Selection.getAllSelectedObjects() == [node_1]

        Selection.remove(node_1)
        assert Selection.getAllSelectedObjects() == []
Beispiel #4
0
    def test_clearSelection(self):
        node_1 = SceneNode()
        node_2 = SceneNode()
        Selection.add(node_1)
        Selection.add(node_2)
        # Ensure that the objects we want selected are selected
        assert Selection.getAllSelectedObjects() == [node_1, node_2]

        Selection.clear()
        assert Selection.getAllSelectedObjects() == []
Beispiel #5
0
    def test_addRemoveSelection(self):
        node_1 = SceneNode()
        Selection.add(node_1)
        Selection.setFace(node_1, 99)

        assert Selection.getAllSelectedObjects() == [node_1]

        Selection.remove(node_1)
        assert Selection.getAllSelectedObjects() == []
        assert Selection.getSelectedFace() is None
Beispiel #6
0
 def multiplySelection(self, count: int) -> None:
     min_offset = Application.getInstance().getBuildVolume(
     ).getEdgeDisallowedSize() + 2  # Allow for some rounding errors
     job = MultiplyObjectsJob(Selection.getAllSelectedObjects(),
                              count,
                              min_offset=max(min_offset, 8))
     job.start()
Beispiel #7
0
    def _onSelectedFaceChanged(self):
        self._handle.setEnabled(not Selection.getFaceSelectMode())

        selected_face = Selection.getSelectedFace()
        if not Selection.getSelectedFace() or not (Selection.hasSelection() and Selection.getFaceSelectMode()):
            return

        original_node, face_id = selected_face
        meshdata = original_node.getMeshDataTransformed()
        if not meshdata or face_id < 0:
            return

        rotation_point, face_normal = meshdata.getFacePlane(face_id)
        rotation_point_vector = Vector(rotation_point[0], rotation_point[1], rotation_point[2])
        face_normal_vector = Vector(face_normal[0], face_normal[1], face_normal[2])
        rotation_quaternion = Quaternion.rotationTo(face_normal_vector.normalized(), Vector(0.0, -1.0, 0.0))

        operation = GroupedOperation()
        current_node = None  # type: Optional[SceneNode]
        for node in Selection.getAllSelectedObjects():
            current_node = node
            parent_node = current_node.getParent()
            while parent_node and parent_node.callDecoration("isGroup"):
                current_node = parent_node
                parent_node = current_node.getParent()
        if current_node is None:
            return

        rotate_operation = RotateOperation(current_node, rotation_quaternion, rotation_point_vector)
        operation.addOperation(rotate_operation)
        operation.push()
    def _calculateMesh(self):
        scene = Application.getInstance().getController().getScene()
        nodes = Selection.getAllSelectedObjects()

        if len(nodes) > 0:
            sn = nodes[0]
            #self._handle._connector._proxy._activeExtruderStack = nodes[0].callDecoration("getExtruderStack")

            if self._scene_node_name is None or sn.getName() != self._scene_node_name:

                mesh_data = sn.getMeshData()

                if mesh_data:
                    Logger.log('d', 'Compute interactive mesh from SceneNode {}'.format(sn.getName()))
                    
                    self._scene_node_name = sn.getName()
                    self._interactive_mesh = makeInteractiveMesh(mesh_data)
                    self._load_face = None
                    self._anchor_face = None

                    self._handle.clearSelection()
                    self._handle._connector._proxy._anchorsApplied = 0
                    self._handle._connector._proxy._loadsApplied = 0
                    self.extension.cloud._onApplicationActivityChanged()

                    controller = Application.getInstance().getController()
                    camTool = controller.getCameraTool()
                    aabb = sn.getBoundingBox()
                    if aabb:
                        camTool.setOrigin(aabb.center)
Beispiel #9
0
    def _checkGrouped(self):
        """Checks if the selected objects are grouped.

        :return: The boolean value if objects are grouped.
        """

        is_grouped = False
        for selection in Selection.getAllSelectedObjects():
            if selection.callDecoration("isGroup"):
                is_grouped = True
                message = Message(text=catalog.i18nc(
                    '@info', 'Ungroup selected group?'),
                                  title=catalog.i18nc(
                                      '@info:title',
                                      'Only nodes without group are allowed.'))
                message.addAction(
                    'Ungroup',
                    catalog.i18nc('@action:button', 'Ungroup'),
                    '[no_icon]',
                    '[no_description]',
                    button_align=Message.ActionButtonAlignment.ALIGN_LEFT)
                message.addAction(
                    'Ignore',
                    catalog.i18nc('@action:button', 'Ignore'),
                    '[no_icon]',
                    '[no_description]',
                    button_style=Message.ActionButtonStyle.SECONDARY,
                    button_align=Message.ActionButtonAlignment.ALIGN_RIGHT)
                message.actionTriggered.connect(self._ungroupTrigger)
                message.show()
        return is_grouped
Beispiel #10
0
    def resetRotation(self):

        for node in Selection.getAllSelectedObjects():
            node.setMirror(Vector(1, 1, 1))

        Selection.applyOperation(SetTransformOperation, None, Quaternion(),
                                 None)
Beispiel #11
0
    def selectedObjectExtruders(self) -> List[Union[str, "ExtruderStack"]]:
        if not self._selected_object_extruders:
            object_extruders = set()

            # First, build a list of the actual selected objects (including children of groups, excluding group nodes)
            selected_nodes = []  # type: List["SceneNode"]
            for node in Selection.getAllSelectedObjects():
                if node.callDecoration("isGroup"):
                    for grouped_node in BreadthFirstIterator(node):
                        if grouped_node.callDecoration("isGroup"):
                            continue

                        selected_nodes.append(grouped_node)
                else:
                    selected_nodes.append(node)

            # Then, figure out which nodes are used by those selected nodes.
            current_extruder_trains = self.getActiveExtruderStacks()
            for node in selected_nodes:
                extruder = node.callDecoration("getActiveExtruder")
                if extruder:
                    object_extruders.add(extruder)
                elif current_extruder_trains:
                    object_extruders.add(current_extruder_trains[0].getId())

            self._selected_object_extruders = list(object_extruders)

        return self._selected_object_extruders
Beispiel #12
0
    def multiplySelection(self, count: int) -> None:
        application = cura.CuraApplication.CuraApplication.getInstance()
        global_container_stack = application.getGlobalContainerStack()

        if not global_container_stack:
            return

        definition_container = global_container_stack.getBottom()
        if definition_container and definition_container.getId() not in [
                "blackbelt", "blackbeltvd"
        ]:
            # for all other printers do the normal multiply/arrange
            super().multiplySelection(count)
            return

        scene_root = application.getController().getScene().getRoot()

        current_nodes = []
        for node in DepthFirstIterator(
                scene_root
        ):  #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
            if node.callDecoration("isSliceable") or node.callDecoration(
                    "isGroup"):
                current_nodes.append(node)

        new_nodes = []  # type: List[CuraSceneNode]
        processed_nodes = []  # type: List[CuraSceneNode]

        active_build_plate = application.getMultiBuildPlateModel(
        ).activeBuildPlate

        for node in Selection.getAllSelectedObjects()[:]:
            # If object is part of a group, multiply group
            while node.getParent() and (
                    node.getParent().callDecoration("isGroup")
                    or node.getParent().callDecoration("isSliceable")):
                node = node.getParent()

            if node in processed_nodes:
                continue
            processed_nodes.append(node)

            for i in range(count):
                new_node = copy.deepcopy(node)

                new_node.callDecoration("setBuildPlateNumber",
                                        active_build_plate)
                for child in new_node.getChildren():
                    child.callDecoration("setBuildPlateNumber",
                                         active_build_plate)

                new_nodes.append(new_node)

        if new_nodes:
            op = GroupedOperation()
            for new_node in new_nodes:
                op.addOperation(AddSceneNodeOperation(new_node, scene_root))
            op.push()

        application.arrange(new_nodes, current_nodes)
Beispiel #13
0
    def selectedObjectExtruders(self) -> List[str]:
        if not self._selected_object_extruders:
            object_extruders = set()

            # First, build a list of the actual selected objects (including children of groups, excluding group nodes)
            selected_nodes = []
            for node in Selection.getAllSelectedObjects():
                if node.callDecoration("isGroup"):
                    for grouped_node in BreadthFirstIterator(node):
                        if grouped_node.callDecoration("isGroup"):
                            continue

                        selected_nodes.append(grouped_node)
                else:
                    selected_nodes.append(node)

            # Then, figure out which nodes are used by those selected nodes.
            global_stack = Application.getInstance().getGlobalContainerStack()
            current_extruder_trains = self._extruder_trains.get(global_stack.getId())
            for node in selected_nodes:
                extruder = node.callDecoration("getActiveExtruder")
                if extruder:
                    object_extruders.add(extruder)
                elif current_extruder_trains:
                    object_extruders.add(current_extruder_trains["0"].getId())

            self._selected_object_extruders = list(object_extruders)

        return self._selected_object_extruders
Beispiel #14
0
 def centerSelection(self) -> None:
     operation = GroupedOperation()
     for node in Selection.getAllSelectedObjects():
         current_node = node
         while current_node.getParent() and current_node.getParent(
         ).callDecoration("isGroup"):
             current_node = current_node.getParent()
         vector = Vector()
         print_mode_enabled = Application.getInstance(
         ).getGlobalContainerStack().getProperty("print_mode", "enabled")
         if print_mode_enabled:
             print_mode = Application.getInstance().getGlobalContainerStack(
             ).getProperty("print_mode", "value")
             if print_mode != "regular":
                 machine_width = Application.getInstance(
                 ).getGlobalContainerStack().getProperty(
                     "machine_width", "value")
                 center = -machine_width / 4
                 if print_mode == "mirror":
                     machine_head_with_fans_polygon = Application.getInstance(
                     ).getGlobalContainerStack().getProperty(
                         "machine_head_with_fans_polygon", "value")
                     machine_head_size = math.fabs(
                         machine_head_with_fans_polygon[0][0] -
                         machine_head_with_fans_polygon[2][0])
                     center -= machine_head_size / 4
                 vector = Vector(center, 0, 0)
         center_operation = SetTransformOperation(current_node, vector)
         operation.addOperation(center_operation)
     operation.push()
Beispiel #15
0
    def selectedObjectExtruders(self) -> List[str]:
        if not self._selected_object_extruders:
            object_extruders = set()

            # First, build a list of the actual selected objects (including children of groups, excluding group nodes)
            selected_nodes = []
            for node in Selection.getAllSelectedObjects():
                if node.callDecoration("isGroup"):
                    for grouped_node in BreadthFirstIterator(node):
                        if grouped_node.callDecoration("isGroup"):
                            continue

                        selected_nodes.append(grouped_node)
                else:
                    selected_nodes.append(node)

            # Then, figure out which nodes are used by those selected nodes.
            global_stack = Application.getInstance().getGlobalContainerStack()
            current_extruder_trains = self._extruder_trains.get(global_stack.getId())
            for node in selected_nodes:
                extruder = node.callDecoration("getActiveExtruder")
                if extruder:
                    object_extruders.add(extruder)
                elif current_extruder_trains:
                    object_extruders.add(current_extruder_trains["0"].getId())

            self._selected_object_extruders = list(object_extruders)

        return self._selected_object_extruders
Beispiel #16
0
    def ungroupSelected(self):
        ungrouped_nodes = []
        selected_objects = Selection.getAllSelectedObjects(
        )[:]  #clone the list
        for node in selected_objects:
            if node.callDecoration("isGroup"):
                children_to_move = []
                for child in node.getChildren():
                    if type(child) is SceneNode:
                        children_to_move.append(child)

                for child in children_to_move:
                    child.setParent(node.getParent())
                    print(node.getPosition())
                    child.translate(node.getPosition())
                    child.setPosition(child.getPosition().scale(
                        node.getScale()))
                    child.scale(node.getScale())
                    child.rotate(node.getOrientation())

                    Selection.add(child)
                    child.callDecoration("setConvexHull", None)
                node.setParent(None)
                ungrouped_nodes.append(node)
        for node in ungrouped_nodes:
            Selection.remove(node)
Beispiel #17
0
    def selectedObjectExtruders(self) -> List[Union[str, "ExtruderStack"]]:
        if not self._selected_object_extruders:
            object_extruders = set()

            # First, build a list of the actual selected objects (including children of groups, excluding group nodes)
            selected_nodes = []  # type: List["SceneNode"]
            for node in Selection.getAllSelectedObjects():
                if node.callDecoration("isGroup"):
                    for grouped_node in BreadthFirstIterator(node): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
                        if grouped_node.callDecoration("isGroup"):
                            continue

                        selected_nodes.append(grouped_node)
                else:
                    selected_nodes.append(node)

            # Then, figure out which nodes are used by those selected nodes.
            current_extruder_trains = self.getActiveExtruderStacks()
            for node in selected_nodes:
                extruder = node.callDecoration("getActiveExtruder")
                if extruder:
                    object_extruders.add(extruder)
                elif current_extruder_trains:
                    object_extruders.add(current_extruder_trains[0].getId())

            self._selected_object_extruders = list(object_extruders)  # type: List[Union[str, "ExtruderStack"]]

        return self._selected_object_extruders
Beispiel #18
0
    def setExtruderForSelection(self, extruder_id: str) -> None:
        operation = GroupedOperation()

        nodes_to_change = []
        for node in Selection.getAllSelectedObjects():
            # Do not change any nodes that already have the right extruder set.
            if node.callDecoration("getActiveExtruder") == extruder_id:
                continue

            # If the node is a group, apply the active extruder to all children of the group.
            if node.callDecoration("isGroup"):
                for grouped_node in BreadthFirstIterator(node):
                    if grouped_node.callDecoration("getActiveExtruder") == extruder_id:
                        continue

                    if grouped_node.callDecoration("isGroup"):
                        continue

                    nodes_to_change.append(grouped_node)
                continue

            nodes_to_change.append(node)

        if not nodes_to_change:
            # If there are no changes to make, we still need to reset the selected extruders.
            # This is a workaround for checked menu items being deselected while still being
            # selected.
            ExtruderManager.getInstance().resetSelectedObjectExtruders()
            return

        for node in nodes_to_change:
            operation.addOperation(SetObjectExtruderOperation(node, extruder_id))
        operation.push()
Beispiel #19
0
    def layFlat(self):
        self.operationStarted.emit(self)
        self._progress_message = Message("Laying object flat on buildplate...",
                                         lifetime=0,
                                         dismissable=False)
        self._progress_message.setProgress(0)

        self._iterations = 0
        self._total_iterations = 0
        for selected_object in Selection.getAllSelectedObjects():
            if selected_object.callDecoration(
                    "isGroup"):  # 2.1 hack. TODO: fix this properly
                self.operationStopped.emit(self)
                Logger.log("w", "Layflat is not supported for grouped objects")
                return
            self._total_iterations += len(
                selected_object.getMeshDataTransformed().getVertices()) * 2

        self._progress_message.show()

        operations = Selection.applyOperation(LayFlatOperation)
        for op in operations:
            op.progress.connect(self._layFlatProgress)

        job = LayFlatJob(operations)
        job.finished.connect(self._layFlatFinished)
        job.start()
    def setExtruderForSelection(self, extruder_id: str) -> None:
        operation = GroupedOperation()

        nodes_to_change = []
        for node in Selection.getAllSelectedObjects():
            # If the node is a group, apply the active extruder to all children of the group.
            if node.callDecoration("isGroup"):
                for grouped_node in BreadthFirstIterator(node): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
                    if grouped_node.callDecoration("getActiveExtruder") == extruder_id:
                        continue

                    if grouped_node.callDecoration("isGroup"):
                        continue

                    nodes_to_change.append(grouped_node)
                continue

            # Do not change any nodes that already have the right extruder set.
            if node.callDecoration("getActiveExtruder") == extruder_id:
                continue

            nodes_to_change.append(node)

        if not nodes_to_change:
            # If there are no changes to make, we still need to reset the selected extruders.
            # This is a workaround for checked menu items being deselected while still being
            # selected.
            ExtruderManager.getInstance().resetSelectedObjectExtruders()
            return

        for node in nodes_to_change:
            operation.addOperation(SetObjectExtruderOperation(node, extruder_id))
        operation.push()
Beispiel #21
0
    def setZ(self, Z):
        if float(Z) != self._Z_angle:
            self._angle = ((float(Z) % 360) - (self._Z_angle % 360)) % 360

            self._Z_angle = float(Z)

            #rotation = Quaternion.fromAngleAxis(math.radians( self._angle ), Vector.Unit_Z)
            rotation = Quaternion()
            rotation.setByAngleAxis(math.radians(self._angle), Vector.Unit_Z)

            # Save the current positions of the node, as we want to rotate around their current centres
            self._saved_node_positions = []
            for node in Selection.getAllSelectedObjects():
                self._saved_node_positions.append((node, node.getPosition()))
                node._rotationZ = self._Z_angle

            # Rate-limit the angle change notification
            # This is done to prevent the UI from being flooded with property change notifications,
            # which in turn would trigger constant repaints.
            new_time = time.monotonic()
            if not self._angle_update_time or new_time - self._angle_update_time > 0.1:
                self._angle_update_time = new_time

                # Rotate around the saved centeres of all selected nodes
                op = GroupedOperation()
                for node, position in self._saved_node_positions:
                    op.addOperation(
                        RotateOperation(node,
                                        rotation,
                                        rotate_around_point=position))
                op.push()

            self._angle = 0
            self.propertyChanged.emit()
Beispiel #22
0
    def doAutoOrientation(self, extended_mode):
        # If we still had a message open from last time, hide it.
        if self._message:
            self._message.hide()

        selected_nodes = Selection.getAllSelectedObjects()
        if len(selected_nodes) == 0:
            self._message = Message(i18n_catalog.i18nc(
                "@info:status",
                "No objects selected to orient. Please select one or more objects and try again."
            ),
                                    title=i18n_catalog.i18nc(
                                        "@title", "Auto-Orientation"))
            self._message.show()
            return

        message = Message(i18n_catalog.i18nc(
            "@info:status", "Calculating the optimal orientation..."),
                          0,
                          False,
                          -1,
                          title=i18n_catalog.i18nc("@title",
                                                   "Auto-Orientation"))
        message.show()

        job = CalculateOrientationJob(selected_nodes,
                                      extended_mode=extended_mode,
                                      message=message)
        job.finished.connect(self._onFinished)
        job.start()
Beispiel #23
0
    def mergeSelected(self):
        self.groupSelected()
        try:
            group_node = Selection.getAllSelectedObjects()[0]
        except Exception as e:
            Logger.log("d", "mergeSelected: Exception:", e)
            return

        # Compute the center of the objects when their origins are aligned.
        object_centers = [
            node.getBoundingBox().center for node in group_node.getChildren()
        ]
        if object_centers and len(object_centers) > 0:
            middle_x = sum([v.x for v in object_centers]) / len(object_centers)
            middle_y = sum([v.y for v in object_centers]) / len(object_centers)
            middle_z = sum([v.z for v in object_centers]) / len(object_centers)
            offset = Vector(middle_x, middle_y, middle_z)
        else:
            offset = Vector(0, 0, 0)
        # Move each node to the same position.
        for center, node in zip(object_centers, group_node.getChildren()):
            # Align the object and also apply the offset to center it inside the group.
            node.translate(-1 * (center - offset),
                           SceneNode.TransformSpace.World)

        # Use the previously found center of the group bounding box as the new location of the group
        group_node.setPosition(group_node.getBoundingBox().center)
Beispiel #24
0
    def deleteSelection(self) -> None:
        if not cura.CuraApplication.CuraApplication.getInstance(
        ).getController().getToolsEnabled():
            return

        removed_group_nodes = []  #type: List[SceneNode]
        op = GroupedOperation()
        nodes = Selection.getAllSelectedObjects()
        for node in nodes:
            op.addOperation(RemoveSceneNodeOperation(node))
            group_node = node.getParent()
            if group_node and group_node.callDecoration(
                    "isGroup") and group_node not in removed_group_nodes:
                remaining_nodes_in_group = list(
                    set(group_node.getChildren()) - set(nodes))
                if len(remaining_nodes_in_group) == 1:
                    removed_group_nodes.append(group_node)
                    op.addOperation(
                        SetParentOperation(remaining_nodes_in_group[0],
                                           group_node.getParent()))
                    op.addOperation(RemoveSceneNodeOperation(group_node))

            # Reset the print information
            cura.CuraApplication.CuraApplication.getInstance().getController(
            ).getScene().sceneChanged.emit(node)

        op.push()
Beispiel #25
0
    def layFlat(self):
        self.operationStarted.emit(self)
        self._progress_message = Message("Laying object flat on buildplate...",
                                         lifetime=0,
                                         dismissable=False)
        self._progress_message.setProgress(0)

        self._iterations = 0
        self._total_iterations = 0
        for selected_object in Selection.getAllSelectedObjects():
            if not selected_object.callDecoration("isGroup"):
                self._total_iterations += len(
                    selected_object.getMeshDataTransformed().getVertices()) * 2
            else:
                for child in selected_object.getChildren():
                    self._total_iterations += len(
                        child.getMeshDataTransformed().getVertices()) * 2

        self._progress_message.show()

        operations = Selection.applyOperation(LayFlatOperation)
        for op in operations:
            op.progress.connect(self._layFlatProgress)

        job = LayFlatJob(operations)
        job.finished.connect(self._layFlatFinished)
        job.start()
Beispiel #26
0
    def deleteSelection(self) -> None:
        if not Application.getInstance().getController().getToolsEnabled():
            return

        removed_group_nodes = []
        op = GroupedOperation()
        nodes = Selection.getAllSelectedObjects()

        for node in nodes:
            print_mode_enabled = Application.getInstance(
            ).getGlobalContainerStack().getProperty("print_mode", "enabled")
            if print_mode_enabled:
                node_dup = PrintModeManager.getInstance().getDuplicatedNode(
                    node)
                op.addOperation(RemoveNodesOperation(node_dup))
            else:
                op.addOperation(RemoveSceneNodeOperation(node))
            group_node = node.getParent()
            if group_node and group_node.callDecoration(
                    "isGroup") and group_node not in removed_group_nodes:
                remaining_nodes_in_group = list(
                    set(group_node.getChildren()) - set(nodes))
                if len(remaining_nodes_in_group) == 1:
                    removed_group_nodes.append(group_node)
                    op.addOperation(
                        SetParentOperation(remaining_nodes_in_group[0],
                                           group_node.getParent()))
                    op.addOperation(RemoveSceneNodeOperation(group_node))
        op.push()
Beispiel #27
0
    def setBuildPlateForSelection(self, build_plate_nr: int) -> None:
        Logger.log("d", "Setting build plate number... %d" % build_plate_nr)
        operation = GroupedOperation()

        root = cura.CuraApplication.CuraApplication.getInstance(
        ).getController().getScene().getRoot()

        nodes_to_change = []
        for node in Selection.getAllSelectedObjects():
            parent_node = node  # Find the parent node to change instead
            while parent_node.getParent() != root:
                parent_node = parent_node.getParent()

            for single_node in BreadthFirstIterator(
                    parent_node
            ):  #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
                nodes_to_change.append(single_node)

        if not nodes_to_change:
            Logger.log("d", "Nothing to change.")
            return

        for node in nodes_to_change:
            operation.addOperation(
                SetBuildPlateNumberOperation(node, build_plate_nr))
        operation.push()

        Selection.clear()
    def requestWriteSelectionToDevice(self, device_id: str, file_name: str,
                                      kwargs: Mapping[str, str]) -> None:
        """Request that the current selection is written to the output device.

        The output device to write with will be selected based on the device_id.
        A file format is chosen from the list of available file formats by the
        output device.

        :param device_id: The handle of the device to write to.
        :param file_name: A suggestion for the file name to write
        to. Can be freely ignored if providing a file name makes no sense.
        :param kwargs: Keyword arguments:
            limit_mimetypes: Limit the possible mimetypes to use for writing to these types.
        """

        if not Selection.hasSelection():
            return

        limit_mimetypes = kwargs.get("limit_mimetypes", False)
        preferred_mimetypes = kwargs.get("preferred_mimetypes", None)
        # On Windows, calling requestWrite() on LocalFileOutputDevice crashes when called from a signal
        # handler attached to a QML MenuItem. So instead, defer the call to the next run of the event
        # loop, since that does work.
        Application.getInstance().callLater(
            self._writeToDevice,
            Selection.getAllSelectedObjects(),
            device_id,
            file_name,
            limit_mimetypes,
            preferred_mimetypes=preferred_mimetypes)
Beispiel #29
0
    def event(self, event):
        super().event(event)

        if event.type == Event.MousePressEvent and self._controller.getToolsEnabled():
            # Initialise a mirror operation
            if MouseEvent.LeftButton not in event.buttons:
                return False

            id = self._selection_pass.getIdAtPosition(event.x, event.y)
            if not id:
                return False

            if ToolHandle.isAxis(id):
                self.setLockedAxis(id)
                self._operation_started = True
                self.operationStarted.emit(self)
                return True

        if event.type == Event.MouseReleaseEvent:
            if self._operation_started:
                self._operation_started = False
                self.operationStopped.emit(self)

            # Perform a mirror operation
            if self.getLockedAxis():
                op = None
                if Selection.getCount() == 1:
                    node = Selection.getSelectedObject(0)
                    if self.getLockedAxis() == ToolHandle.XAxis:
                        mirror = Vector(-1, 1, 1)
                    elif self.getLockedAxis() == ToolHandle.YAxis:
                        mirror = Vector(1, -1, 1)
                    elif self.getLockedAxis() == ToolHandle.ZAxis:
                        mirror = Vector(1, 1, -1)
                    else:
                        mirror = Vector(1, 1, 1)
                    op = MirrorOperation(node, mirror, mirror_around_center = True)
                else:
                    op = GroupedOperation()

                    for node in Selection.getAllSelectedObjects():
                        if self.getLockedAxis() == ToolHandle.XAxis:
                            mirror = Vector(-1, 1, 1)
                        elif self.getLockedAxis() == ToolHandle.YAxis:
                            mirror = Vector(1, -1, 1)
                        elif self.getLockedAxis() == ToolHandle.ZAxis:
                            mirror = Vector(1, 1, -1)
                        else:
                            mirror = Vector(1, 1, 1)

                        op.addOperation(MirrorOperation(node, mirror, mirror_around_center = True))

                op.push()

                self.setLockedAxis(None)
                return True

        return False
Beispiel #30
0
    def removeSelection(self):
        if not Selection.hasSelection():
            return

        op = GroupedOperation()
        for node in Selection.getAllSelectedObjects():
            op.addOperation(RemoveSceneNodeOperation(node))
        op.push()
        Selection.clear()
Beispiel #31
0
 def _updateEnabled(self):
     selected_objects = Selection.getAllSelectedObjects()
     if len(selected_objects)> 1:
         self._single_model_selected = False
     elif len(selected_objects) == 1 and selected_objects[0].callDecoration("isGroup"):
         self._single_model_selected = False # Group is selected, so tool needs to be disabled
     else:
         self._single_model_selected = True
     Application.getInstance().getController().toolEnabledChanged.emit(self._plugin_id, self._advanced_mode and self._single_model_selected)
Beispiel #32
0
    def removeSelection(self):
        if not Selection.hasSelection():
            return

        op = GroupedOperation()
        for node in Selection.getAllSelectedObjects():
            op.addOperation(RemoveSceneNodeOperation(node))
        op.push()
        Selection.clear()
Beispiel #33
0
    def setSelected(self, key):
        for index in range(0, len(self.items)):
            if self.items[index]["key"] == key:
                for node in Application.getInstance().getController().getScene(
                ).getRoot().getAllChildren():
                    if id(node) == key:
                        if node not in Selection.getAllSelectedObjects(
                        ):  #node already selected
                            Selection.add(node)
                            if node.callDecoration(
                                    "isGroup"):  #Its a group node
                                for child_node in node.getChildren():
                                    if child_node not in Selection.getAllSelectedObjects(
                                    ):  #Set all children to parent state (if they arent already)
                                        Selection.add(child_node)
                        else:
                            Selection.remove(node)
                            if node.callDecoration("isGroup"):  #Its a group
                                for child_node in node.getChildren():
                                    if child_node in Selection.getAllSelectedObjects(
                                    ):
                                        Selection.remove(child_node)

        all_children_selected = True
        #Check all group nodes to see if all their children are selected (if so, they also need to be selected!)
        for index in range(0, len(self.items)):
            if self.items[index]["is_group"]:
                for node in Application.getInstance().getController().getScene(
                ).getRoot().getAllChildren():
                    if node.hasChildren():
                        if id(node) == self.items[index]["key"] and id(
                                node) != key:
                            for index, child_node in enumerate(
                                    node.getChildren()):
                                if not Selection.isSelected(child_node):
                                    all_children_selected = False  #At least one of its children is not selected, dont change state
                                    break
                            if all_children_selected:
                                Selection.add(node)
                            else:
                                Selection.remove(node)
        #Force update
        self.updateList(
            Application.getInstance().getController().getScene().getRoot())
 def getSelectedNodes(self):
     self._message.hide()
     selection = Selection.getAllSelectedObjects()[:]
     if selection:
         return selection
     else:
         self._message.setText(
             catalog.i18nc("@info:status",
                           "Please select one or more models first"))
         self._message.show()
Beispiel #35
0
    def centerSelection(self) -> None:
        operation = GroupedOperation()
        for node in Selection.getAllSelectedObjects():
            current_node = node
            while current_node.getParent() and current_node.getParent().callDecoration("isGroup"):
                current_node = current_node.getParent()

            center_operation = SetTransformOperation(current_node, Vector())
            operation.addOperation(center_operation)
        operation.push()
    def requestWriteSelectionToDevice(self, device_id: str, file_name: str, kwargs: Mapping[str, str]) -> None:
        if not Selection.hasSelection():
            return

        limit_mimetypes = kwargs.get("limit_mimetypes", False)
        preferred_mimetypes = kwargs.get("preferred_mimetypes", None)
        # On Windows, calling requestWrite() on LocalFileOutputDevice crashes when called from a signal
        # handler attached to a QML MenuItem. So instead, defer the call to the next run of the event 
        # loop, since that does work.
        Application.getInstance().callLater(self._writeToDevice, Selection.getAllSelectedObjects(), device_id, file_name, limit_mimetypes, preferred_mimetypes = preferred_mimetypes)
    def requestWriteSelectionToDevice(self, device_id, file_name, kwargs):
        if not Selection.hasSelection():
            return

        limit_mimetypes = kwargs.get("limit_mimetypes", False)
        preferred_mimetype = kwargs.get("preferred_mimetype", None)
        # On Windows, calling requestWrite() on LocalFileOutputDevice crashes when called from a signal
        # handler attached to a QML MenuItem. So instead, defer the call to the next run of the event 
        # loop, since that does work.
        Application.getInstance().callLater(self._writeToDevice, Selection.getAllSelectedObjects(), device_id, file_name, limit_mimetypes, preferred_mimetype = preferred_mimetype)
Beispiel #38
0
    def setX(self, x):
        bounding_box = Selection.getBoundingBox()

        op = GroupedOperation()
        for selected_node in Selection.getAllSelectedObjects():
            world_position = selected_node.getWorldPosition()
            new_position = world_position.set(x=float(x) + (world_position.x - bounding_box.center.x))
            node_op = TranslateOperation(selected_node, new_position, set_position = True)
            op.addOperation(node_op)
        op.push()
        self.operationStopped.emit(self)
Beispiel #39
0
    def deleteSelection(self):
        if not self.getController().getToolsEnabled():
            return

        op = GroupedOperation()
        nodes = Selection.getAllSelectedObjects()
        for node in nodes:
            op.addOperation(RemoveSceneNodeOperation(node))

        op.push()

        pass
    def event(self, event):
        super().event(event)

        if event.type == Event.MousePressEvent and self._controller.getToolsEnabled():
            # Initialise a mirror operation
            if MouseEvent.LeftButton not in event.buttons:
                return False

            id = self._selection_pass.getIdAtPosition(event.x, event.y)
            if not id:
                return False

            if ToolHandle.isAxis(id):
                self.setLockedAxis(id)
                return True

        if event.type == Event.MouseReleaseEvent:
            # Perform a mirror operation
            if self.getLockedAxis():
                op = None
                if Selection.getCount() == 1:
                    node = Selection.getSelectedObject(0)
                    if self.getLockedAxis() == ToolHandle.XAxis:
                        mirror = Vector(-1, 1, 1)
                    elif self.getLockedAxis() == ToolHandle.YAxis:
                        mirror = Vector(1, -1, 1)
                    elif self.getLockedAxis() == ToolHandle.ZAxis:
                        mirror = Vector(1, 1, -1)
                    else:
                        mirror = Vector(1, 1, 1)
                    op = MirrorOperation(node, mirror, mirror_around_center = True)
                else:
                    op = GroupedOperation()

                    for node in Selection.getAllSelectedObjects():
                        if self.getLockedAxis() == ToolHandle.XAxis:
                            mirror = Vector(-1, 1, 1)
                        elif self.getLockedAxis() == ToolHandle.YAxis:
                            mirror = Vector(1, -1, 1)
                        elif self.getLockedAxis() == ToolHandle.ZAxis:
                            mirror = Vector(1, 1, -1)
                        else:
                            mirror = Vector(1, 1, 1)

                        op.addOperation(MirrorOperation(node, mirror, mirror_around_center = True))

                op.push()

                self.setLockedAxis(None)
                return True

        return False
Beispiel #41
0
    def setZ(self, z):
        bounding_box = Selection.getBoundingBox()

        op = GroupedOperation()
        for selected_node in Selection.getAllSelectedObjects():
            # Note: The switching of z & y is intentional. We display z as up for the user,
            # But store the data in openGL space.
            world_position = selected_node.getWorldPosition()
            new_position = world_position.set(y=float(z) + (world_position.y - bounding_box.bottom))
            node_op = TranslateOperation(selected_node, new_position, set_position = True)
            op.addOperation(node_op)
        op.push()
        self.operationStopped.emit(self)
Beispiel #42
0
 def groupSelected(self):
     group_node = SceneNode()
     group_decorator = GroupDecorator()
     group_node.addDecorator(group_decorator)
     group_node.setParent(self.getController().getScene().getRoot())
     
     for node in Selection.getAllSelectedObjects():
         node.setParent(group_node)
     
     for node in group_node.getChildren():
         Selection.remove(node)
     
     Selection.add(group_node)
Beispiel #43
0
    def setX(self, x):
        parsed_x = self._parseInt(x)
        bounding_box = Selection.getBoundingBox()

        op = GroupedOperation()
        if not Float.fuzzyCompare(parsed_x, float(bounding_box.center.x), DIMENSION_TOLERANCE):
            for selected_node in Selection.getAllSelectedObjects():
                world_position = selected_node.getWorldPosition()
                new_position = world_position.set(x=parsed_x + (world_position.x - bounding_box.center.x))
                node_op = TranslateOperation(selected_node, new_position, set_position = True)
                op.addOperation(node_op)
            op.push()
        self._controller.toolOperationStopped.emit(self)
Beispiel #44
0
    def centerSelection(self) -> None:
        operation = GroupedOperation()
        for node in Selection.getAllSelectedObjects():
            current_node = node
            while current_node.getParent() and current_node.getParent().callDecoration("isGroup"):
                current_node = current_node.getParent()

            #   This was formerly done with SetTransformOperation but because of
            #   unpredictable matrix deconstruction it was possible that mirrors
            #   could manifest as rotations. Centering is therefore done by
            #   moving the node to negative whatever its position is:
            center_operation = TranslateOperation(current_node, -current_node._position)
            operation.addOperation(center_operation)
        operation.push()
Beispiel #45
0
    def _onToolOperationStopped(self, tool):
        if tool.getPluginId() == "TranslateTool":
            for node in Selection.getAllSelectedObjects():
                if node.getBoundingBox().bottom < 0:
                    if not node.getDecorator(ZOffsetDecorator.ZOffsetDecorator):
                        node.addDecorator(ZOffsetDecorator.ZOffsetDecorator())

                    node.callDecoration("setZOffset", node.getBoundingBox().bottom)
                else:
                    if node.getDecorator(ZOffsetDecorator.ZOffsetDecorator):
                        node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator)

        self._enabled = True
        self._onChangeTimerFinished()
Beispiel #46
0
    def event(self, event):
        super().event(event)

        if event.type == Event.MousePressEvent:
            if MouseEvent.LeftButton not in event.buttons:
                return False

            id = self._renderer.getIdAtCoordinate(event.x, event.y)
            if not id:
                return False

            if ToolHandle.isAxis(id):
                self.setLockedAxis(id)
                return True

        if event.type == Event.MouseReleaseEvent:
            if self.getLockedAxis():
                op = None
                if Selection.getCount() == 1:
                    node = Selection.getSelectedObject(0)
                    scale = node.getScale()
                    if self.getLockedAxis() == ToolHandle.XAxis:
                        scale.setX(-scale.x)
                    elif self.getLockedAxis() == ToolHandle.YAxis:
                        scale.setY(-scale.y)
                    elif self.getLockedAxis() == ToolHandle.ZAxis:
                        scale.setZ(-scale.z)

                    op = ScaleOperation(node, scale, set_scale=True)
                else:
                    op = GroupedOperation()

                    for node in Selection.getAllSelectedObjects():
                        scale = node.getScale()
                        if self.getLockedAxis() == ToolHandle.XAxis:
                            scale.setX(-scale.x)
                        elif self.getLockedAxis() == ToolHandle.YAxis:
                            scale.setY(-scale.y)
                        elif self.getLockedAxis() == ToolHandle.ZAxis:
                            scale.setZ(-scale.z)

                        op.addOperation(ScaleOperation(node, scale, set_scale = True))

                op.push()

                self.setLockedAxis(None)
                return True

        return False
Beispiel #47
0
    def event(self, event):
        super().event(event)

        if event.type == Event.MousePressEvent:
            if MouseEvent.LeftButton not in event.buttons:
                return False

            id = self._selection_pass.getIdAtPosition(event.x, event.y)
            if not id:
                return False

            if ToolHandle.isAxis(id):
                self.setLockedAxis(id)
                return True

        if event.type == Event.MouseReleaseEvent:
            if self.getLockedAxis():
                op = None
                if Selection.getCount() == 1:
                    node = Selection.getSelectedObject(0)
                    mirror = node.getMirror()
                    if self.getLockedAxis() == ToolHandle.XAxis:
                        mirror.setX(-mirror.x)
                    elif self.getLockedAxis() == ToolHandle.YAxis:
                        mirror.setY(-mirror.y)
                    elif self.getLockedAxis() == ToolHandle.ZAxis:
                        mirror.setZ(-mirror.z)

                    op = MirrorOperation(node, mirror, set_mirror=True)
                else:
                    op = GroupedOperation()

                    for node in Selection.getAllSelectedObjects():
                        mirror = node.getMirror()
                        if self.getLockedAxis() == ToolHandle.XAxis:
                            mirror.setX(-mirror.x)
                        elif self.getLockedAxis() == ToolHandle.YAxis:
                            mirror.setY(-mirror.y)
                        elif self.getLockedAxis() == ToolHandle.ZAxis:
                            mirror.setZ(-mirror.z)

                        op.addOperation(MirrorOperation(node, mirror, set_mirror = True))

                op.push()

                self.setLockedAxis(None)
                return True

        return False
Beispiel #48
0
    def setZ(self, z):
        parsed_z = self._parseInt(z)
        bounding_box = Selection.getBoundingBox()

        op = GroupedOperation()
        if not Float.fuzzyCompare(parsed_z, float(bounding_box.center.y), DIMENSION_TOLERANCE):
            for selected_node in Selection.getAllSelectedObjects():
                # Note: The switching of z & y is intentional. We display z as up for the user,
                # But store the data in openGL space.
                world_position = selected_node.getWorldPosition()
                new_position = world_position.set(y=parsed_z + (world_position.y - bounding_box.bottom))
                node_op = TranslateOperation(selected_node, new_position, set_position = True)
                op.addOperation(node_op)
            op.push()
        self._controller.toolOperationStopped.emit(self)
Beispiel #49
0
 def groupSelected(self):
     group_node = SceneNode()
     group_decorator = GroupDecorator()
     group_node.addDecorator(group_decorator)
     group_node.setParent(self.getController().getScene().getRoot())
     
     for node in Selection.getAllSelectedObjects():
         node.setParent(group_node)
     group_node.setCenterPosition(group_node.getBoundingBox().center)
     #group_node.translate(Vector(0,group_node.getBoundingBox().center.y,0))
     group_node.translate(group_node.getBoundingBox().center)
     for node in group_node.getChildren():
         Selection.remove(node)
     
     Selection.add(group_node)
Beispiel #50
0
 def mergeSelected(self):
     self.groupSelected()
     try:
         group_node = Selection.getAllSelectedObjects()[0]
     except Exception as e:
         return
     multi_material_decorator = MultiMaterialDecorator.MultiMaterialDecorator()
     group_node.addDecorator(multi_material_decorator)
     # Reset the position of each node
     for node in group_node.getChildren():
         new_position = node.getMeshData().getCenterPosition()
         new_position.setY(0)
         node.setPosition(new_position)
     
     # Use the previously found center of the group bounding box as the new location of the group
     group_node.setPosition(group_node.getBoundingBox().center)
Beispiel #51
0
 def deleteSelection(self):
     if not self.getController().getToolsEnabled():
         return
     removed_group_nodes = []
     op = GroupedOperation()
     nodes = Selection.getAllSelectedObjects()
     for node in nodes:
         op.addOperation(RemoveSceneNodeOperation(node))
         group_node = node.getParent()
         if group_node and group_node.callDecoration("isGroup") and group_node not in removed_group_nodes:
             remaining_nodes_in_group = list(set(group_node.getChildren()) - set(nodes))
             if len(remaining_nodes_in_group) == 1:
                 removed_group_nodes.append(group_node)
                 op.addOperation(SetParentOperation(remaining_nodes_in_group[0], group_node.getParent()))
                 op.addOperation(RemoveSceneNodeOperation(group_node))
     op.push()
Beispiel #52
0
    def ungroupSelected(self):
        selected_objects = Selection.getAllSelectedObjects().copy()
        for node in selected_objects:
            if node.callDecoration("isGroup"):
                op = GroupedOperation()

                group_parent = node.getParent()
                children = node.getChildren().copy()
                for child in children:
                    # Set the parent of the children to the parent of the group-node
                    op.addOperation(SetParentOperation(child, group_parent))

                    # Add all individual nodes to the selection
                    Selection.add(child)

                op.push()
Beispiel #53
0
    def _getSelectedObjectsWithoutSelectedAncestors(self) -> List[SceneNode]:
        if not isinstance(self._selected_objects_without_selected_ancestors, list):
            nodes = Selection.getAllSelectedObjects()
            self._selected_objects_without_selected_ancestors = []
            for node in nodes:
                has_selected_ancestor = False
                ancestor = node.getParent()
                while ancestor:
                    if Selection.isSelected(ancestor):
                        has_selected_ancestor = True
                        break
                    ancestor = ancestor.getParent()

                if not has_selected_ancestor:
                    self._selected_objects_without_selected_ancestors.append(node)

        return self._selected_objects_without_selected_ancestors
Beispiel #54
0
 def ungroupSelected(self):
     ungrouped_nodes = []
     selected_objects = Selection.getAllSelectedObjects()[:] #clone the list
     for node in selected_objects:
         if node.callDecoration("isGroup" ):
             children_to_move = []
             for child in node.getChildren():
                 if type(child) is SceneNode:
                     children_to_move.append(child)
                    
             for child in children_to_move:
                 child.setParent(node.getParent())
                 Selection.add(child)
                 child.callDecoration("setConvexHull",None)
             node.setParent(None)
             ungrouped_nodes.append(node)
     for node in ungrouped_nodes:
         Selection.remove(node)
Beispiel #55
0
    def groupSelected(self):
        group_node = SceneNode()
        group_decorator = GroupDecorator()
        group_node.addDecorator(group_decorator)
        group_node.setParent(self.getController().getScene().getRoot())
        center = Selection.getSelectionCenter()
        group_node.setPosition(center)
        group_node.setCenterPosition(center)

        for node in Selection.getAllSelectedObjects():
            world = node.getWorldPosition()
            node.setParent(group_node)
            node.setPosition(world - center)

        for node in group_node.getChildren():
            Selection.remove(node)

        Selection.add(group_node)
Beispiel #56
0
    def layFlat(self):
        self.operationStarted.emit(self)
        self._progress_message = Message("Laying object flat on buildplate...", lifetime = 0, dismissable = False)
        self._progress_message.setProgress(0)

        self._iterations = 0
        self._total_iterations = 0
        for selected_object in Selection.getAllSelectedObjects():
            self._total_iterations += len(selected_object.getMeshDataTransformed().getVertices()) * 2

        self._progress_message.show()

        operations = Selection.applyOperation(LayFlatOperation)
        for op in operations:
            op.progress.connect(self._layFlatProgress)

        job = LayFlatJob(operations)
        job.finished.connect(self._layFlatFinished)
        job.start()
Beispiel #57
0
    def deleteSelection(self) -> None:
        if not Application.getInstance().getController().getToolsEnabled():
            return

        removed_group_nodes = []
        op = GroupedOperation()
        nodes = Selection.getAllSelectedObjects()
        for node in nodes:
            op.addOperation(RemoveSceneNodeOperation(node))
            group_node = node.getParent()
            if group_node and group_node.callDecoration("isGroup") and group_node not in removed_group_nodes:
                remaining_nodes_in_group = list(set(group_node.getChildren()) - set(nodes))
                if len(remaining_nodes_in_group) == 1:
                    removed_group_nodes.append(group_node)
                    op.addOperation(SetParentOperation(remaining_nodes_in_group[0], group_node.getParent()))
                    op.addOperation(RemoveSceneNodeOperation(group_node))

            # Reset the print information
            Application.getInstance().getController().getScene().sceneChanged.emit(node)

        op.push()