mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-08 07:27:29 -06:00
Allow to set print sequence manually
This commit is contained in:
parent
6aaee84b95
commit
2b05a370ca
53 changed files with 1021 additions and 88 deletions
171
cura/PrintOrderManager.py
Normal file
171
cura/PrintOrderManager.py
Normal file
|
@ -0,0 +1,171 @@
|
|||
from typing import List, Callable, Optional, Any
|
||||
|
||||
from PyQt6.QtCore import pyqtProperty, pyqtSignal, QObject, pyqtSlot
|
||||
from UM.Application import Application
|
||||
from UM.Scene.Selection import Selection
|
||||
|
||||
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||
|
||||
|
||||
class PrintOrderManager(QObject):
|
||||
"""Allows to order the object list to set the print sequence manually"""
|
||||
|
||||
def __init__(self, get_nodes: Callable[[], List[CuraSceneNode]]) -> None:
|
||||
super().__init__()
|
||||
self._get_nodes = get_nodes
|
||||
self._configureEvents()
|
||||
|
||||
_settingsChanged = pyqtSignal()
|
||||
_uiActionsOutdated = pyqtSignal()
|
||||
printOrderChanged = pyqtSignal()
|
||||
|
||||
@pyqtSlot()
|
||||
def swapSelectedAndPreviousNodes(self) -> None:
|
||||
selected_node, previous_node, next_node = self._getSelectedAndNeighborNodes()
|
||||
self._swapPrintOrders(selected_node, previous_node)
|
||||
|
||||
@pyqtSlot()
|
||||
def swapSelectedAndNextNodes(self) -> None:
|
||||
selected_node, previous_node, next_node = self._getSelectedAndNeighborNodes()
|
||||
self._swapPrintOrders(selected_node, next_node)
|
||||
|
||||
@pyqtProperty(str, notify=_uiActionsOutdated)
|
||||
def previousNodeName(self) -> str:
|
||||
selected_node, previous_node, next_node = self._getSelectedAndNeighborNodes()
|
||||
return self._getNodeName(previous_node)
|
||||
|
||||
@pyqtProperty(str, notify=_uiActionsOutdated)
|
||||
def nextNodeName(self) -> str:
|
||||
selected_node, previous_node, next_node = self._getSelectedAndNeighborNodes()
|
||||
return self._getNodeName(next_node)
|
||||
|
||||
@pyqtProperty(bool, notify=_uiActionsOutdated)
|
||||
def shouldEnablePrintBeforeAction(self) -> bool:
|
||||
selected_node, previous_node, next_node = self._getSelectedAndNeighborNodes()
|
||||
can_swap_with_previous_node = selected_node is not None and previous_node is not None
|
||||
return can_swap_with_previous_node
|
||||
|
||||
@pyqtProperty(bool, notify=_uiActionsOutdated)
|
||||
def shouldEnablePrintAfterAction(self) -> bool:
|
||||
selected_node, previous_node, next_node = self._getSelectedAndNeighborNodes()
|
||||
can_swap_with_next_node = selected_node is not None and next_node is not None
|
||||
return can_swap_with_next_node
|
||||
|
||||
@pyqtProperty(bool, notify=_settingsChanged)
|
||||
def shouldShowEditPrintOrderActions(self) -> bool:
|
||||
return PrintOrderManager.isUserDefinedPrintOrderEnabled()
|
||||
|
||||
@staticmethod
|
||||
def isUserDefinedPrintOrderEnabled() -> bool:
|
||||
stack = Application.getInstance().getGlobalContainerStack()
|
||||
is_enabled = stack and \
|
||||
stack.getProperty("print_sequence", "value") == "one_at_a_time" and \
|
||||
stack.getProperty("user_defined_print_order_enabled", "value")
|
||||
return is_enabled
|
||||
|
||||
@staticmethod
|
||||
def initializePrintOrders(nodes: List[CuraSceneNode]) -> None:
|
||||
"""Just created (loaded from file) nodes have print order 0.
|
||||
|
||||
This method initializes print orders with max value to put nodes at the end of object list"""
|
||||
max_print_order = max(map(lambda n: n.printOrder, nodes), default=0)
|
||||
for node in nodes:
|
||||
if node.printOrder == 0:
|
||||
max_print_order += 1
|
||||
node.printOrder = max_print_order
|
||||
|
||||
@staticmethod
|
||||
def updatePrintOrdersAfterGroupOperation(
|
||||
all_nodes: List[CuraSceneNode],
|
||||
group_node: CuraSceneNode,
|
||||
grouped_nodes: List[CuraSceneNode]
|
||||
) -> None:
|
||||
group_node.printOrder = min(map(lambda n: n.printOrder, grouped_nodes))
|
||||
|
||||
all_nodes.append(group_node)
|
||||
for node in grouped_nodes:
|
||||
all_nodes.remove(node)
|
||||
|
||||
# reassign print orders so there won't be gaps like 1 2 5 6 7
|
||||
sorted_nodes = sorted(all_nodes, key=lambda n: n.printOrder)
|
||||
for i, node in enumerate(sorted_nodes):
|
||||
node.printOrder = i + 1
|
||||
|
||||
@staticmethod
|
||||
def updatePrintOrdersAfterUngroupOperation(
|
||||
all_nodes: List[CuraSceneNode],
|
||||
group_node: CuraSceneNode,
|
||||
ungrouped_nodes: List[CuraSceneNode]
|
||||
) -> None:
|
||||
all_nodes.remove(group_node)
|
||||
nodes_to_update_print_order = filter(lambda n: n.printOrder > group_node.printOrder, all_nodes)
|
||||
for node in nodes_to_update_print_order:
|
||||
node.printOrder += len(ungrouped_nodes) - 1
|
||||
|
||||
for i, child in enumerate(ungrouped_nodes):
|
||||
child.printOrder = group_node.printOrder + i
|
||||
all_nodes.append(child)
|
||||
|
||||
def _swapPrintOrders(self, node1: CuraSceneNode, node2: CuraSceneNode) -> None:
|
||||
if node1 and node2:
|
||||
node1.printOrder, node2.printOrder = node2.printOrder, node1.printOrder # swap print orders
|
||||
self.printOrderChanged.emit() # update object list first
|
||||
self._uiActionsOutdated.emit() # then update UI actions
|
||||
|
||||
def _getSelectedAndNeighborNodes(self
|
||||
) -> (Optional[CuraSceneNode], Optional[CuraSceneNode], Optional[CuraSceneNode]):
|
||||
nodes = self._get_nodes()
|
||||
ordered_nodes = sorted(nodes, key=lambda n: n.printOrder)
|
||||
selected_node = PrintOrderManager._getSingleSelectedNode()
|
||||
if selected_node and selected_node in ordered_nodes:
|
||||
selected_node_index = ordered_nodes.index(selected_node)
|
||||
else:
|
||||
selected_node_index = None
|
||||
|
||||
if selected_node_index is not None and selected_node_index - 1 >= 0:
|
||||
previous_node = ordered_nodes[selected_node_index - 1]
|
||||
else:
|
||||
previous_node = None
|
||||
|
||||
if selected_node_index is not None and selected_node_index + 1 < len(ordered_nodes):
|
||||
next_node = ordered_nodes[selected_node_index + 1]
|
||||
else:
|
||||
next_node = None
|
||||
|
||||
return selected_node, previous_node, next_node
|
||||
|
||||
@staticmethod
|
||||
def _getNodeName(node: CuraSceneNode, max_length: int = 30) -> str:
|
||||
node_name = node.getName() if node else ""
|
||||
truncated_node_name = node_name[:max_length]
|
||||
return truncated_node_name
|
||||
|
||||
@staticmethod
|
||||
def _getSingleSelectedNode() -> Optional[CuraSceneNode]:
|
||||
if len(Selection.getAllSelectedObjects()) == 1:
|
||||
selected_node = Selection.getSelectedObject(0)
|
||||
return selected_node
|
||||
return None
|
||||
|
||||
def _configureEvents(self) -> None:
|
||||
Selection.selectionChanged.connect(self._onSelectionChanged)
|
||||
self._global_stack = None
|
||||
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged)
|
||||
self._onGlobalStackChanged()
|
||||
|
||||
def _onGlobalStackChanged(self) -> None:
|
||||
if self._global_stack:
|
||||
self._global_stack.propertyChanged.disconnect(self._onSettingsChanged)
|
||||
self._global_stack.containersChanged.disconnect(self._onSettingsChanged)
|
||||
|
||||
self._global_stack = Application.getInstance().getGlobalContainerStack()
|
||||
|
||||
if self._global_stack:
|
||||
self._global_stack.propertyChanged.connect(self._onSettingsChanged)
|
||||
self._global_stack.containersChanged.connect(self._onSettingsChanged)
|
||||
|
||||
def _onSettingsChanged(self, *args: Any) -> None:
|
||||
self._settingsChanged.emit()
|
||||
|
||||
def _onSelectionChanged(self) -> None:
|
||||
self._uiActionsOutdated.emit()
|
Loading…
Add table
Add a link
Reference in a new issue