mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-06-26 01:15:28 -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
|
@ -161,6 +161,10 @@ pycharm_targets:
|
|||
module_name: Cura
|
||||
name: pytest in TestGCodeListDecorator.py
|
||||
script_name: tests/TestGCodeListDecorator.py
|
||||
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
|
||||
module_name: Cura
|
||||
name: pytest in TestHitChecker.py
|
||||
script_name: tests/TestHitChecker.py
|
||||
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
|
||||
module_name: Cura
|
||||
name: pytest in TestIntentManager.py
|
||||
|
@ -189,6 +193,10 @@ pycharm_targets:
|
|||
module_name: Cura
|
||||
name: pytest in TestPrintInformation.py
|
||||
script_name: tests/TestPrintInformation.py
|
||||
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
|
||||
module_name: Cura
|
||||
name: pytest in TestPrintOrderManager.py
|
||||
script_name: tests/TestPrintOrderManager.py
|
||||
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
|
||||
module_name: Cura
|
||||
name: pytest in TestProfileRequirements.py
|
||||
|
|
|
@ -125,6 +125,7 @@ from .Machines.Models.CompatibleMachineModel import CompatibleMachineModel
|
|||
from .Machines.Models.MachineListModel import MachineListModel
|
||||
from .Machines.Models.ActiveIntentQualitiesModel import ActiveIntentQualitiesModel
|
||||
from .Machines.Models.IntentSelectionModel import IntentSelectionModel
|
||||
from .PrintOrderManager import PrintOrderManager
|
||||
from .SingleInstance import SingleInstance
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
@ -202,6 +203,7 @@ class CuraApplication(QtApplication):
|
|||
self._container_manager = None
|
||||
|
||||
self._object_manager = None
|
||||
self._print_order_manager = None
|
||||
self._extruders_model = None
|
||||
self._extruders_model_with_optional = None
|
||||
self._build_plate_model = None
|
||||
|
@ -899,6 +901,7 @@ class CuraApplication(QtApplication):
|
|||
# initialize info objects
|
||||
self._print_information = PrintInformation.PrintInformation(self)
|
||||
self._cura_actions = CuraActions.CuraActions(self)
|
||||
self._print_order_manager = PrintOrderManager(self.getObjectsModel().getNodes)
|
||||
self.processEvents()
|
||||
# Initialize setting visibility presets model.
|
||||
self._setting_visibility_presets_model = SettingVisibilityPresetsModel(self.getPreferences(), parent = self)
|
||||
|
@ -979,6 +982,7 @@ class CuraApplication(QtApplication):
|
|||
t.setEnabledAxis([ToolHandle.XAxis, ToolHandle.YAxis, ToolHandle.ZAxis])
|
||||
|
||||
Selection.selectionChanged.connect(self.onSelectionChanged)
|
||||
self._print_order_manager.printOrderChanged.connect(self._onPrintOrderChanged)
|
||||
|
||||
# Set default background color for scene
|
||||
self.getRenderer().setBackgroundColor(QColor(245, 245, 245))
|
||||
|
@ -1218,6 +1222,7 @@ class CuraApplication(QtApplication):
|
|||
self.processEvents()
|
||||
engine.rootContext().setContextProperty("Printer", self)
|
||||
engine.rootContext().setContextProperty("CuraApplication", self)
|
||||
engine.rootContext().setContextProperty("PrintOrderManager", self._print_order_manager)
|
||||
engine.rootContext().setContextProperty("PrintInformation", self._print_information)
|
||||
engine.rootContext().setContextProperty("CuraActions", self._cura_actions)
|
||||
engine.rootContext().setContextProperty("CuraSDKVersion", ApplicationMetadata.CuraSDKVersion)
|
||||
|
@ -1715,8 +1720,12 @@ class CuraApplication(QtApplication):
|
|||
Selection.remove(node)
|
||||
Selection.add(group_node)
|
||||
|
||||
all_nodes = self.getObjectsModel().getNodes()
|
||||
PrintOrderManager.updatePrintOrdersAfterGroupOperation(all_nodes, group_node, selected_nodes)
|
||||
|
||||
@pyqtSlot()
|
||||
def ungroupSelected(self) -> None:
|
||||
all_nodes = self.getObjectsModel().getNodes()
|
||||
selected_objects = Selection.getAllSelectedObjects().copy()
|
||||
for node in selected_objects:
|
||||
if node.callDecoration("isGroup"):
|
||||
|
@ -1724,21 +1733,30 @@ class CuraApplication(QtApplication):
|
|||
|
||||
group_parent = node.getParent()
|
||||
children = node.getChildren().copy()
|
||||
for child in children:
|
||||
# Ungroup only 1 level deep
|
||||
if child.getParent() != node:
|
||||
continue
|
||||
|
||||
# Ungroup only 1 level deep
|
||||
children_to_ungroup = list(filter(lambda child: child.getParent() == node, children))
|
||||
for child in children_to_ungroup:
|
||||
# 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)
|
||||
|
||||
PrintOrderManager.updatePrintOrdersAfterUngroupOperation(all_nodes, node, children_to_ungroup)
|
||||
op.push()
|
||||
# Note: The group removes itself from the scene once all its children have left it,
|
||||
# see GroupDecorator._onChildrenChanged
|
||||
|
||||
def _onPrintOrderChanged(self) -> None:
|
||||
# update object list
|
||||
scene = self.getController().getScene()
|
||||
scene.sceneChanged.emit(scene.getRoot())
|
||||
|
||||
# reset if already was sliced
|
||||
Application.getInstance().getBackend().needsSlicing()
|
||||
Application.getInstance().getBackend().tickle()
|
||||
|
||||
def _createSplashScreen(self) -> Optional[CuraSplashScreen.CuraSplashScreen]:
|
||||
if self._is_headless:
|
||||
return None
|
||||
|
|
88
cura/HitChecker.py
Normal file
88
cura/HitChecker.py
Normal file
|
@ -0,0 +1,88 @@
|
|||
from typing import List, Dict
|
||||
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||
|
||||
|
||||
class HitChecker:
|
||||
"""Checks if nodes can be printed without causing any collisions and interference"""
|
||||
|
||||
def __init__(self, nodes: List[CuraSceneNode]) -> None:
|
||||
self._hit_map = self._buildHitMap(nodes)
|
||||
|
||||
def anyTwoNodesBlockEachOther(self, nodes: List[CuraSceneNode]) -> bool:
|
||||
"""Returns True if any 2 nodes block each other"""
|
||||
for a in nodes:
|
||||
for b in nodes:
|
||||
if self._hit_map[a][b] and self._hit_map[b][a]:
|
||||
return True
|
||||
return False
|
||||
|
||||
def canPrintBefore(self, node: CuraSceneNode, other_nodes: List[CuraSceneNode]) -> bool:
|
||||
"""Returns True if node doesn't block other_nodes and can be printed before them"""
|
||||
no_hits = all(not self._hit_map[node][other_node] for other_node in other_nodes)
|
||||
return no_hits
|
||||
|
||||
def canPrintAfter(self, node: CuraSceneNode, other_nodes: List[CuraSceneNode]) -> bool:
|
||||
"""Returns True if node doesn't hit other nodes and can be printed after them"""
|
||||
no_hits = all(not self._hit_map[other_node][node] for other_node in other_nodes)
|
||||
return no_hits
|
||||
|
||||
def calculateScore(self, a: CuraSceneNode, b: CuraSceneNode) -> int:
|
||||
"""Calculate score simply sums the number of other objects it 'blocks'
|
||||
|
||||
:param a: node
|
||||
:param b: node
|
||||
:return: sum of the number of other objects
|
||||
"""
|
||||
|
||||
score_a = sum(self._hit_map[a].values())
|
||||
score_b = sum(self._hit_map[b].values())
|
||||
return score_a - score_b
|
||||
|
||||
def canPrintNodesInProvidedOrder(self, ordered_nodes: List[CuraSceneNode]) -> bool:
|
||||
"""Returns True If nodes don't have any hits in provided order"""
|
||||
for node_index, node in enumerate(ordered_nodes):
|
||||
nodes_before = ordered_nodes[:node_index - 1] if node_index - 1 >= 0 else []
|
||||
nodes_after = ordered_nodes[node_index + 1:] if node_index + 1 < len(ordered_nodes) else []
|
||||
if not self.canPrintBefore(node, nodes_after) or not self.canPrintAfter(node, nodes_before):
|
||||
return False
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _buildHitMap(nodes: List[CuraSceneNode]) -> Dict[CuraSceneNode, CuraSceneNode]:
|
||||
"""Pre-computes all hits between all objects
|
||||
|
||||
:nodes: nodes that need to be checked for collisions
|
||||
:return: dictionary where hit_map[node1][node2] is False if there node1 can be printed before node2
|
||||
"""
|
||||
hit_map = {j: {i: HitChecker._checkHit(j, i) for i in nodes} for j in nodes}
|
||||
return hit_map
|
||||
|
||||
@staticmethod
|
||||
def _checkHit(a: CuraSceneNode, b: CuraSceneNode) -> bool:
|
||||
"""Checks if a can be printed before b
|
||||
|
||||
:param a: node
|
||||
:param b: node
|
||||
:return: False if a can be printed before b
|
||||
"""
|
||||
|
||||
if a == b:
|
||||
return False
|
||||
|
||||
a_hit_hull = a.callDecoration("getConvexHullBoundary")
|
||||
b_hit_hull = b.callDecoration("getConvexHullHeadFull")
|
||||
overlap = a_hit_hull.intersectsPolygon(b_hit_hull)
|
||||
|
||||
if overlap:
|
||||
return True
|
||||
|
||||
# Adhesion areas must never overlap, regardless of printing order
|
||||
# This would cause over-extrusion
|
||||
a_hit_hull = a.callDecoration("getAdhesionArea")
|
||||
b_hit_hull = b.callDecoration("getAdhesionArea")
|
||||
overlap = a_hit_hull.intersectsPolygon(b_hit_hull)
|
||||
|
||||
if overlap:
|
||||
return True
|
||||
else:
|
||||
return False
|
|
@ -7,6 +7,11 @@ from UM.Scene.Iterator import Iterator
|
|||
from UM.Scene.SceneNode import SceneNode
|
||||
from functools import cmp_to_key
|
||||
|
||||
from cura.HitChecker import HitChecker
|
||||
from cura.PrintOrderManager import PrintOrderManager
|
||||
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||
|
||||
|
||||
class OneAtATimeIterator(Iterator.Iterator):
|
||||
"""Iterator that returns a list of nodes in the order that they need to be printed
|
||||
|
||||
|
@ -16,8 +21,6 @@ class OneAtATimeIterator(Iterator.Iterator):
|
|||
|
||||
def __init__(self, scene_node) -> None:
|
||||
super().__init__(scene_node) # Call super to make multiple inheritance work.
|
||||
self._hit_map = [[]] # type: List[List[bool]] # For each node, which other nodes this hits. A grid of booleans on which nodes hit which.
|
||||
self._original_node_list = [] # type: List[SceneNode] # The nodes that need to be checked for collisions.
|
||||
|
||||
def _fillStack(self) -> None:
|
||||
"""Fills the ``_node_stack`` with a list of scene nodes that need to be printed in order. """
|
||||
|
@ -38,104 +41,50 @@ class OneAtATimeIterator(Iterator.Iterator):
|
|||
self._node_stack = node_list[:]
|
||||
return
|
||||
|
||||
# Copy the list
|
||||
self._original_node_list = node_list[:]
|
||||
hit_checker = HitChecker(node_list)
|
||||
|
||||
# Initialise the hit map (pre-compute all hits between all objects)
|
||||
self._hit_map = [[self._checkHit(i, j) for i in node_list] for j in node_list]
|
||||
if PrintOrderManager.isUserDefinedPrintOrderEnabled():
|
||||
self._node_stack = self._getNodesOrderedByUser(hit_checker, node_list)
|
||||
else:
|
||||
self._node_stack = self._getNodesOrderedAutomatically(hit_checker, node_list)
|
||||
|
||||
# Check if we have to files that block each other. If this is the case, there is no solution!
|
||||
for a in range(0, len(node_list)):
|
||||
for b in range(0, len(node_list)):
|
||||
if a != b and self._hit_map[a][b] and self._hit_map[b][a]:
|
||||
return
|
||||
# update print orders so that user can try to arrange the nodes automatically first
|
||||
# and if result is not satisfactory he/she can switch to manual mode and change it
|
||||
for index, node in enumerate(self._node_stack):
|
||||
node.printOrder = index + 1
|
||||
|
||||
@staticmethod
|
||||
def _getNodesOrderedByUser(hit_checker: HitChecker, node_list: List[CuraSceneNode]) -> List[CuraSceneNode]:
|
||||
nodes_ordered_by_user = sorted(node_list, key=lambda n: n.printOrder)
|
||||
if hit_checker.canPrintNodesInProvidedOrder(nodes_ordered_by_user):
|
||||
return nodes_ordered_by_user
|
||||
return [] # No solution
|
||||
|
||||
@staticmethod
|
||||
def _getNodesOrderedAutomatically(hit_checker: HitChecker, node_list: List[CuraSceneNode]) -> List[CuraSceneNode]:
|
||||
# Check if we have two files that block each other. If this is the case, there is no solution!
|
||||
if hit_checker.anyTwoNodesBlockEachOther(node_list):
|
||||
return [] # No solution
|
||||
|
||||
# Sort the original list so that items that block the most other objects are at the beginning.
|
||||
# This does not decrease the worst case running time, but should improve it in most cases.
|
||||
sorted(node_list, key = cmp_to_key(self._calculateScore))
|
||||
sorted(node_list, key = cmp_to_key(hit_checker.calculateScore))
|
||||
|
||||
todo_node_list = [_ObjectOrder([], node_list)]
|
||||
while len(todo_node_list) > 0:
|
||||
current = todo_node_list.pop()
|
||||
for node in current.todo:
|
||||
# Check if the object can be placed with what we have and still allows for a solution in the future
|
||||
if not self._checkHitMultiple(node, current.order) and not self._checkBlockMultiple(node, current.todo):
|
||||
if hit_checker.canPrintAfter(node, current.order) and hit_checker.canPrintBefore(node, current.todo):
|
||||
# We found a possible result. Create new todo & order list.
|
||||
new_todo_list = current.todo[:]
|
||||
new_todo_list.remove(node)
|
||||
new_order = current.order[:] + [node]
|
||||
if len(new_todo_list) == 0:
|
||||
# We have no more nodes to check, so quit looking.
|
||||
self._node_stack = new_order
|
||||
return
|
||||
return new_order # Solution found!
|
||||
todo_node_list.append(_ObjectOrder(new_order, new_todo_list))
|
||||
self._node_stack = [] #No result found!
|
||||
|
||||
|
||||
# Check if first object can be printed before the provided list (using the hit map)
|
||||
def _checkHitMultiple(self, node: SceneNode, other_nodes: List[SceneNode]) -> bool:
|
||||
node_index = self._original_node_list.index(node)
|
||||
for other_node in other_nodes:
|
||||
other_node_index = self._original_node_list.index(other_node)
|
||||
if self._hit_map[node_index][other_node_index]:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _checkBlockMultiple(self, node: SceneNode, other_nodes: List[SceneNode]) -> bool:
|
||||
"""Check for a node whether it hits any of the other nodes.
|
||||
|
||||
:param node: The node to check whether it collides with the other nodes.
|
||||
:param other_nodes: The nodes to check for collisions.
|
||||
:return: returns collision between nodes
|
||||
"""
|
||||
|
||||
node_index = self._original_node_list.index(node)
|
||||
for other_node in other_nodes:
|
||||
other_node_index = self._original_node_list.index(other_node)
|
||||
if self._hit_map[other_node_index][node_index] and node_index != other_node_index:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _calculateScore(self, a: SceneNode, b: SceneNode) -> int:
|
||||
"""Calculate score simply sums the number of other objects it 'blocks'
|
||||
|
||||
:param a: node
|
||||
:param b: node
|
||||
:return: sum of the number of other objects
|
||||
"""
|
||||
|
||||
score_a = sum(self._hit_map[self._original_node_list.index(a)])
|
||||
score_b = sum(self._hit_map[self._original_node_list.index(b)])
|
||||
return score_a - score_b
|
||||
|
||||
def _checkHit(self, a: SceneNode, b: SceneNode) -> bool:
|
||||
"""Checks if a can be printed before b
|
||||
|
||||
:param a: node
|
||||
:param b: node
|
||||
:return: true if a can be printed before b
|
||||
"""
|
||||
|
||||
if a == b:
|
||||
return False
|
||||
|
||||
a_hit_hull = a.callDecoration("getConvexHullBoundary")
|
||||
b_hit_hull = b.callDecoration("getConvexHullHeadFull")
|
||||
overlap = a_hit_hull.intersectsPolygon(b_hit_hull)
|
||||
|
||||
if overlap:
|
||||
return True
|
||||
|
||||
# Adhesion areas must never overlap, regardless of printing order
|
||||
# This would cause over-extrusion
|
||||
a_hit_hull = a.callDecoration("getAdhesionArea")
|
||||
b_hit_hull = b.callDecoration("getAdhesionArea")
|
||||
overlap = a_hit_hull.intersectsPolygon(b_hit_hull)
|
||||
|
||||
if overlap:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
return [] # No result found!
|
||||
|
||||
|
||||
class _ObjectOrder:
|
||||
|
|
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()
|
|
@ -25,10 +25,19 @@ class CuraSceneNode(SceneNode):
|
|||
if not no_setting_override:
|
||||
self.addDecorator(SettingOverrideDecorator()) # Now we always have a getActiveExtruderPosition, unless explicitly disabled
|
||||
self._outside_buildarea = False
|
||||
self._print_order = 0
|
||||
|
||||
def setOutsideBuildArea(self, new_value: bool) -> None:
|
||||
self._outside_buildarea = new_value
|
||||
|
||||
@property
|
||||
def printOrder(self):
|
||||
return self._print_order
|
||||
|
||||
@printOrder.setter
|
||||
def printOrder(self, new_value):
|
||||
self._print_order = new_value
|
||||
|
||||
def isOutsideBuildArea(self) -> bool:
|
||||
return self._outside_buildarea or self.callDecoration("getBuildPlateNumber") < 0
|
||||
|
||||
|
@ -157,3 +166,6 @@ class CuraSceneNode(SceneNode):
|
|||
|
||||
def transformChanged(self) -> None:
|
||||
self._transformChanged()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "{print_order}. {name}".format(print_order = self._print_order, name = self.getName())
|
||||
|
|
|
@ -14,6 +14,9 @@ from UM.Scene.SceneNode import SceneNode
|
|||
from UM.Scene.Selection import Selection
|
||||
from UM.i18n import i18nCatalog
|
||||
|
||||
from cura.PrintOrderManager import PrintOrderManager
|
||||
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
|
@ -76,6 +79,9 @@ class ObjectsModel(ListModel):
|
|||
self._build_plate_number = nr
|
||||
self._update()
|
||||
|
||||
def getNodes(self) -> List[CuraSceneNode]:
|
||||
return list(map(lambda n: n["node"], self.items))
|
||||
|
||||
def _updateSceneDelayed(self, source) -> None:
|
||||
if not isinstance(source, Camera):
|
||||
self._update_timer.start()
|
||||
|
@ -175,6 +181,10 @@ class ObjectsModel(ListModel):
|
|||
|
||||
all_nodes = self._renameNodes(name_to_node_info_dict)
|
||||
|
||||
user_defined_print_order_enabled = PrintOrderManager.isUserDefinedPrintOrderEnabled()
|
||||
if user_defined_print_order_enabled:
|
||||
PrintOrderManager.initializePrintOrders(all_nodes)
|
||||
|
||||
for node in all_nodes:
|
||||
if hasattr(node, "isOutsideBuildArea"):
|
||||
is_outside_build_area = node.isOutsideBuildArea() # type: ignore
|
||||
|
@ -223,8 +233,13 @@ class ObjectsModel(ListModel):
|
|||
# for anti overhang meshes and groups the extruder nr is irrelevant
|
||||
extruder_number = -1
|
||||
|
||||
if not user_defined_print_order_enabled:
|
||||
name = node.getName()
|
||||
else:
|
||||
name = "{print_order}. {name}".format(print_order = node.printOrder, name = node.getName())
|
||||
|
||||
nodes.append({
|
||||
"name": node.getName(),
|
||||
"name": name,
|
||||
"selected": Selection.isSelected(node),
|
||||
"outside_build_area": is_outside_build_area,
|
||||
"buildplate_number": node_build_plate_number,
|
||||
|
@ -234,5 +249,5 @@ class ObjectsModel(ListModel):
|
|||
"node": node
|
||||
})
|
||||
|
||||
nodes = sorted(nodes, key=lambda n: n["name"])
|
||||
nodes = sorted(nodes, key=lambda n: n["name"] if not user_defined_print_order_enabled else n["node"].printOrder)
|
||||
self.setItems(nodes)
|
||||
|
|
|
@ -177,6 +177,9 @@ class ThreeMFReader(MeshReader):
|
|||
else:
|
||||
Logger.log("w", "Unable to find extruder in position %s", setting_value)
|
||||
continue
|
||||
if key == "print_order":
|
||||
um_node.printOrder = int(setting_value)
|
||||
continue
|
||||
if key in known_setting_keys:
|
||||
setting_container.setProperty(key, "value", setting_value)
|
||||
else:
|
||||
|
|
|
@ -20,6 +20,7 @@ from cura.CuraApplication import CuraApplication
|
|||
from cura.CuraPackageManager import CuraPackageManager
|
||||
from cura.Settings import CuraContainerStack
|
||||
from cura.Utils.Threading import call_on_qt_thread
|
||||
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||
from cura.Snapshot import Snapshot
|
||||
|
||||
from PyQt6.QtCore import QBuffer
|
||||
|
@ -137,6 +138,9 @@ class ThreeMFWriter(MeshWriter):
|
|||
for key in changed_setting_keys:
|
||||
savitar_node.setSetting("cura:" + key, str(stack.getProperty(key, "value")))
|
||||
|
||||
if isinstance(um_node, CuraSceneNode):
|
||||
savitar_node.setSetting("cura:print_order", str(um_node.printOrder))
|
||||
|
||||
# Store the metadata.
|
||||
for key, value in um_node.metadata.items():
|
||||
savitar_node.setSetting(key, value)
|
||||
|
|
|
@ -7060,6 +7060,16 @@
|
|||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"user_defined_print_order_enabled":
|
||||
{
|
||||
"label": "Set Print Sequence Manually",
|
||||
"description": "Allows to order the object list to set the print sequence manually. First object from the list will be printed first.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"enabled": "print_sequence == 'one_at_a_time'"
|
||||
},
|
||||
"infill_mesh":
|
||||
{
|
||||
"label": "Infill Mesh",
|
||||
|
|
|
@ -4946,6 +4946,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr "Rozdělit modely"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr "Tisknout před"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr "Tisknout po"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr "Odinstalovat"
|
||||
|
|
|
@ -2583,6 +2583,14 @@ msgctxt "print_sequence label"
|
|||
msgid "Print Sequence"
|
||||
msgstr "Tisková sekvence"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr "Nastavit tiskovou sekvenci ručně"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr "Umožňuje řadit seznam objektů pro ruční nastavení tiskové sekvence. První objekt ze seznamu bude vytisknut jako první."
|
||||
|
||||
msgctxt "speed_print label"
|
||||
msgid "Print Speed"
|
||||
msgstr "Rychlost tisku"
|
||||
|
|
|
@ -4565,6 +4565,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr ""
|
||||
|
|
|
@ -4930,6 +4930,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr "Gruppierung für Modelle aufheben"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr "Vor dem Drucken"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr "Nach dem Drucken"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr "Deinstallieren"
|
||||
|
|
|
@ -2580,6 +2580,14 @@ msgctxt "print_sequence label"
|
|||
msgid "Print Sequence"
|
||||
msgstr "Druckreihenfolge"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr "Druckreihenfolge manuell einstellen"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr "Ermöglicht das Ordnen der Objektliste, um die Druckreihenfolge manuell festzulegen. Das erste Objekt aus der Liste wird zuerst gedruckt."
|
||||
|
||||
msgctxt "speed_print label"
|
||||
msgid "Print Speed"
|
||||
msgstr "Druckgeschwindigkeit"
|
||||
|
|
|
@ -4931,6 +4931,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr "Desagrupar modelos"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr "Imprimir antes"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr "Imprimir después"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr "Desinstalar"
|
||||
|
|
|
@ -2580,6 +2580,14 @@ msgctxt "print_sequence label"
|
|||
msgid "Print Sequence"
|
||||
msgstr "Secuencia de impresión"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr "Establecer secuencia de impresión manualmente"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr "Permite ordenar la lista de objetos para establecer la secuencia de impresión manualmente. El primer objeto de la lista se imprimirá primero."
|
||||
|
||||
msgctxt "speed_print label"
|
||||
msgid "Print Speed"
|
||||
msgstr "Velocidad de impresión"
|
||||
|
|
|
@ -4596,6 +4596,14 @@ msgctxt "print_sequence option one_at_a_time"
|
|||
msgid "One at a Time"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr ""
|
||||
|
||||
msgctxt "infill_mesh label"
|
||||
msgid "Infill Mesh"
|
||||
msgstr ""
|
||||
|
|
|
@ -4899,6 +4899,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr "Poista mallien ryhmitys"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr "Tulosta ennen"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr "Tulosta jälkeen"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr ""
|
||||
|
|
|
@ -2578,6 +2578,14 @@ msgctxt "print_sequence label"
|
|||
msgid "Print Sequence"
|
||||
msgstr "Tulostusjärjestys"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr "Aseta tulostusjärjestys manuaalisesti"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr "Mahdollistaa kohteiden järjestämisen tulostusjärjestyksen manuaaliseen asettamiseen. Listan ensimmäinen kohde tulostetaan ensin."
|
||||
|
||||
msgctxt "speed_print label"
|
||||
msgid "Print Speed"
|
||||
msgstr "Tulostusnopeus"
|
||||
|
|
|
@ -4928,6 +4928,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr "Dégrouper les modèles"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr "Imprimer avant"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr "Imprimer après"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr "Désinstaller"
|
||||
|
|
|
@ -2580,6 +2580,14 @@ msgctxt "print_sequence label"
|
|||
msgid "Print Sequence"
|
||||
msgstr "Séquence d'impression"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr "Définir la séquence d'impression manuellement"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr "Permet de classer la liste des objets pour définir manuellement la séquence d'impression. Le premier objet de la liste sera imprimé en premier."
|
||||
|
||||
msgctxt "speed_print label"
|
||||
msgid "Print Speed"
|
||||
msgstr "Vitesse d’impression"
|
||||
|
|
|
@ -4913,6 +4913,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr "Csoport bontása"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr "Nyomtatás előtt"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr "Nyomtatás után"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr ""
|
||||
|
|
|
@ -2585,6 +2585,14 @@ msgctxt "print_sequence label"
|
|||
msgid "Print Sequence"
|
||||
msgstr "Nyomtatási sorrend"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr "Nyomtatási sorrend kézi beállítása"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr "Lehetővé teszi az objektumlista rendezését a nyomtatási sorrend kézi beállításához. A lista első objektuma lesz először nyomtatva."
|
||||
|
||||
msgctxt "speed_print label"
|
||||
msgid "Print Speed"
|
||||
msgstr "Nyomtatási sebesség"
|
||||
|
|
|
@ -4931,6 +4931,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr "Separa modelli"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr "Stampa prima"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr "Stampa dopo"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr "Disinstalla"
|
||||
|
|
|
@ -2580,6 +2580,14 @@ msgctxt "print_sequence label"
|
|||
msgid "Print Sequence"
|
||||
msgstr "Sequenza di stampa"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr "Imposta manualmente la sequenza di stampa"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr "Consente di ordinare l'elenco degli oggetti per impostare manualmente la sequenza di stampa. Il primo oggetto dell'elenco sarà stampato per primo."
|
||||
|
||||
msgctxt "speed_print label"
|
||||
msgid "Print Speed"
|
||||
msgstr "Velocità di stampa"
|
||||
|
|
|
@ -4914,6 +4914,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr "モデルを非グループ化"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr "印刷前"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr "印刷後"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr "アンインストール"
|
||||
|
|
|
@ -2580,6 +2580,14 @@ msgctxt "print_sequence label"
|
|||
msgid "Print Sequence"
|
||||
msgstr "印刷頻度"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr "手動で印刷順序を設定する"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr "オブジェクトリストを並べ替えて、手動で印刷順序を設定することができます。リストの最初のオブジェクトが最初に印刷されます。"
|
||||
|
||||
msgctxt "speed_print label"
|
||||
msgid "Print Speed"
|
||||
msgstr "印刷速度"
|
||||
|
|
|
@ -4917,6 +4917,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr "모델 그룹 해제"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr "인쇄 전"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr "인쇄 후"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr "설치 제거"
|
||||
|
|
|
@ -2580,6 +2580,14 @@ msgctxt "print_sequence label"
|
|||
msgid "Print Sequence"
|
||||
msgstr "프린팅 순서"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr "수동으로 인쇄 순서 설정"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr "객체 목록을 정렬하여 수동으로 인쇄 순서를 설정할 수 있습니다. 목록의 첫 번째 객체가 먼저 인쇄됩니다."
|
||||
|
||||
msgctxt "speed_print label"
|
||||
msgid "Print Speed"
|
||||
msgstr "프린팅 속도"
|
||||
|
|
|
@ -4925,6 +4925,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr "Groeperen van Modellen Opheffen"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr "Afdrukken voor"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr "Afdrukken na"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr "De-installeren"
|
||||
|
|
|
@ -2580,6 +2580,14 @@ msgctxt "print_sequence label"
|
|||
msgid "Print Sequence"
|
||||
msgstr "Printvolgorde"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr "Handmatig afdrukvolgorde instellen"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr "Maakt het mogelijk de objectlijst te ordenen om de afdrukvolgorde handmatig in te stellen. Het eerste object van de lijst wordt als eerste afgedrukt."
|
||||
|
||||
msgctxt "speed_print label"
|
||||
msgid "Print Speed"
|
||||
msgstr "Printsnelheid"
|
||||
|
|
|
@ -4916,6 +4916,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr "Rozgrupuj modele"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr "Drukuj przed"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr "Drukuj po"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr ""
|
||||
|
|
|
@ -2584,6 +2584,14 @@ msgctxt "print_sequence label"
|
|||
msgid "Print Sequence"
|
||||
msgstr "Sekwencja Wydruku"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr "Ręczne ustawienie kolejności drukowania"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr "Umożliwia ręczne ustawienie kolejności drukowania na liście obiektów. Pierwszy obiekt z listy zostanie wydrukowany jako pierwszy."
|
||||
|
||||
msgctxt "speed_print label"
|
||||
msgid "Print Speed"
|
||||
msgstr "Prędkość Druku"
|
||||
|
|
|
@ -4942,6 +4942,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr "Desagrupar Modelos"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr "Imprimir antes"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr "Imprimir depois"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr "Desinstalar"
|
||||
|
|
|
@ -2585,6 +2585,14 @@ msgctxt "print_sequence label"
|
|||
msgid "Print Sequence"
|
||||
msgstr "Sequência de Impressão"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr "Definir sequência de impressão manualmente"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr "Permite ordenar a lista de objetos para definir a sequência de impressão manualmente. O primeiro objeto da lista será impresso primeiro."
|
||||
|
||||
msgctxt "speed_print label"
|
||||
msgid "Print Speed"
|
||||
msgstr "Velocidade de Impressão"
|
||||
|
|
|
@ -4932,6 +4932,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr "Desagrupar Modelos"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr "Imprimir antes"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr "Imprimir depois"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr "Desinstalar"
|
||||
|
|
|
@ -2580,6 +2580,14 @@ msgctxt "print_sequence label"
|
|||
msgid "Print Sequence"
|
||||
msgstr "Sequência de impressão"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr "Definir sequência de impressão manualmente"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr "Permite ordenar a lista de objetos para definir a sequência de impressão manualmente. O primeiro objeto da lista será impresso primeiro."
|
||||
|
||||
msgctxt "speed_print label"
|
||||
msgid "Print Speed"
|
||||
msgstr "Velocidade de Impressão"
|
||||
|
|
|
@ -4955,6 +4955,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr "Разгруппировать модели"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr "Печатать до"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr "Печатать после"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr "Удалить"
|
||||
|
|
|
@ -2580,6 +2580,14 @@ msgctxt "print_sequence label"
|
|||
msgid "Print Sequence"
|
||||
msgstr "Последовательная печать"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr "Установить последовательность печати вручную"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr "Позволяет упорядочить список объектов для ручной настройки последовательности печати. Первый объект из списка будет напечатан первым."
|
||||
|
||||
msgctxt "speed_print label"
|
||||
msgid "Print Speed"
|
||||
msgstr "Скорость печати"
|
||||
|
|
|
@ -4931,6 +4931,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr "Model Grubunu Çöz"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr "Önce Yazdır"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr "Sonra Yazdır"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr "Kaldır"
|
||||
|
|
|
@ -2580,6 +2580,14 @@ msgctxt "print_sequence label"
|
|||
msgid "Print Sequence"
|
||||
msgstr "Yazdırma Dizisi"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr "Baskı Sırasını Manuel Olarak Ayarla"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr "Nesne listesini sıralayarak baskı sırasını manuel olarak ayarlamayı sağlar. Listeden ilk nesne ilk olarak basılacak."
|
||||
|
||||
msgctxt "speed_print label"
|
||||
msgid "Print Speed"
|
||||
msgstr "Yazdırma Hızı"
|
||||
|
|
|
@ -4919,6 +4919,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr "拆分模型"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr "打印前"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr "打印后"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr "卸载"
|
||||
|
|
|
@ -2580,6 +2580,14 @@ msgctxt "print_sequence label"
|
|||
msgid "Print Sequence"
|
||||
msgstr "打印序列"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr "手动设置打印顺序"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr "允许对对象列表进行排序,以手动设置打印顺序。列表中的第一个对象将首先被打印。"
|
||||
|
||||
msgctxt "speed_print label"
|
||||
msgid "Print Speed"
|
||||
msgstr "打印速度"
|
||||
|
|
|
@ -4911,6 +4911,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr "取消模型群組"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr "列印前"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr "列印後"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr ""
|
||||
|
|
|
@ -2585,6 +2585,14 @@ msgctxt "print_sequence label"
|
|||
msgid "Print Sequence"
|
||||
msgstr "列印順序"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr "手動設置列印順序"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr "允許手動設置物件列表以設定列印順序。列表中的第一個物件將首先被列印。"
|
||||
|
||||
msgctxt "speed_print label"
|
||||
msgid "Print Speed"
|
||||
msgstr "列印速度"
|
||||
|
|
|
@ -35,6 +35,9 @@ Item
|
|||
property alias mergeObjects: mergeObjectsAction
|
||||
//property alias unMergeObjects: unMergeObjectsAction
|
||||
|
||||
property alias printObjectBeforePrevious: printObjectBeforePreviousAction
|
||||
property alias printObjectAfterNext: printObjectAfterNextAction
|
||||
|
||||
property alias multiplyObject: multiplyObjectAction
|
||||
|
||||
property alias selectAll: selectAllAction
|
||||
|
@ -406,6 +409,26 @@ Item
|
|||
onTriggered: CuraApplication.ungroupSelected()
|
||||
}
|
||||
|
||||
Action
|
||||
{
|
||||
id: printObjectBeforePreviousAction
|
||||
text: catalog.i18nc("@action:inmenu menubar:edit","Print Before") + " " + PrintOrderManager.previousNodeName
|
||||
enabled: PrintOrderManager.shouldEnablePrintBeforeAction
|
||||
icon.name: "print-before"
|
||||
shortcut: "PgUp"
|
||||
onTriggered: PrintOrderManager.swapSelectedAndPreviousNodes()
|
||||
}
|
||||
|
||||
Action
|
||||
{
|
||||
id: printObjectAfterNextAction
|
||||
text: catalog.i18nc("@action:inmenu menubar:edit","Print After") + " " + PrintOrderManager.nextNodeName
|
||||
enabled: PrintOrderManager.shouldEnablePrintAfterAction
|
||||
icon.name: "print-after"
|
||||
shortcut: "PgDown"
|
||||
onTriggered: PrintOrderManager.swapSelectedAndNextNodes()
|
||||
}
|
||||
|
||||
Action
|
||||
{
|
||||
id: mergeObjectsAction
|
||||
|
|
|
@ -78,6 +78,19 @@ Cura.Menu
|
|||
Cura.MenuItem { action: Cura.Actions.mergeObjects }
|
||||
Cura.MenuItem { action: Cura.Actions.unGroupObjects }
|
||||
|
||||
// Edit print sequence actions
|
||||
Cura.MenuSeparator { visible: PrintOrderManager.shouldShowEditPrintOrderActions }
|
||||
Cura.MenuItem
|
||||
{
|
||||
action: Cura.Actions.printObjectBeforePrevious
|
||||
visible: PrintOrderManager.shouldShowEditPrintOrderActions
|
||||
}
|
||||
Cura.MenuItem
|
||||
{
|
||||
action: Cura.Actions.printObjectAfterNext
|
||||
visible: PrintOrderManager.shouldShowEditPrintOrderActions
|
||||
}
|
||||
|
||||
Connections
|
||||
{
|
||||
target: UM.Controller
|
||||
|
|
|
@ -25,4 +25,17 @@ Cura.Menu
|
|||
Cura.MenuItem { action: Cura.Actions.groupObjects }
|
||||
Cura.MenuItem { action: Cura.Actions.mergeObjects }
|
||||
Cura.MenuItem { action: Cura.Actions.unGroupObjects }
|
||||
|
||||
// Edit print sequence actions
|
||||
Cura.MenuSeparator { visible: PrintOrderManager.shouldShowEditPrintOrderActions }
|
||||
Cura.MenuItem
|
||||
{
|
||||
action: Cura.Actions.printObjectBeforePrevious
|
||||
visible: PrintOrderManager.shouldShowEditPrintOrderActions
|
||||
}
|
||||
Cura.MenuItem
|
||||
{
|
||||
action: Cura.Actions.printObjectAfterNext
|
||||
visible: PrintOrderManager.shouldShowEditPrintOrderActions
|
||||
}
|
||||
}
|
|
@ -136,6 +136,7 @@ prime_tower_brim_enable
|
|||
|
||||
[blackmagic]
|
||||
print_sequence
|
||||
user_defined_print_order_enabled
|
||||
magic_mesh_surface_mode
|
||||
magic_spiralize
|
||||
smooth_spiralized_contours
|
||||
|
|
|
@ -386,6 +386,7 @@ meshfix_fluid_motion_angle
|
|||
|
||||
[blackmagic]
|
||||
print_sequence
|
||||
user_defined_print_order_enabled
|
||||
infill_mesh
|
||||
infill_mesh_order
|
||||
cutting_mesh
|
||||
|
|
141
tests/TestHitChecker.py
Normal file
141
tests/TestHitChecker.py
Normal file
|
@ -0,0 +1,141 @@
|
|||
from unittest.mock import patch
|
||||
|
||||
from cura.HitChecker import HitChecker
|
||||
from cura.OneAtATimeIterator import OneAtATimeIterator
|
||||
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||
|
||||
|
||||
def test_anyTwoNodesBlockEachOther_True():
|
||||
node1 = CuraSceneNode(no_setting_override=True)
|
||||
node2 = CuraSceneNode(no_setting_override=True)
|
||||
# node1 and node2 block each other
|
||||
hit_map = {
|
||||
node1: {node1: 0, node2: 1},
|
||||
node2: {node1: 1, node2: 0}
|
||||
}
|
||||
|
||||
with patch.object(HitChecker, "_buildHitMap", return_value=hit_map):
|
||||
hit_checker = HitChecker([node1, node2])
|
||||
assert hit_checker.anyTwoNodesBlockEachOther([node1, node2])
|
||||
assert hit_checker.anyTwoNodesBlockEachOther([node2, node1])
|
||||
|
||||
|
||||
def test_anyTwoNodesBlockEachOther_False():
|
||||
node1 = CuraSceneNode(no_setting_override=True)
|
||||
node2 = CuraSceneNode(no_setting_override=True)
|
||||
# node1 blocks node2, but node2 doesn't block node1
|
||||
hit_map = {
|
||||
node1: {node1: 0, node2: 1},
|
||||
node2: {node1: 0, node2: 0}
|
||||
}
|
||||
|
||||
with patch.object(HitChecker, "_buildHitMap", return_value=hit_map):
|
||||
hit_checker = HitChecker([node1, node2])
|
||||
assert not hit_checker.anyTwoNodesBlockEachOther([node1, node2])
|
||||
assert not hit_checker.anyTwoNodesBlockEachOther([node2, node1])
|
||||
|
||||
|
||||
def test_canPrintBefore():
|
||||
node1 = CuraSceneNode(no_setting_override=True)
|
||||
node2 = CuraSceneNode(no_setting_override=True)
|
||||
node3 = CuraSceneNode(no_setting_override=True)
|
||||
# nodes can be printed only in order node1 -> node2 -> node3
|
||||
hit_map = {
|
||||
node1: {node1: 0, node2: 0, node3: 0},
|
||||
node2: {node1: 1, node2: 0, node3: 0},
|
||||
node3: {node1: 1, node2: 1, node3: 0},
|
||||
}
|
||||
|
||||
with patch.object(HitChecker, "_buildHitMap", return_value=hit_map):
|
||||
hit_checker = HitChecker([node1, node2, node3])
|
||||
|
||||
assert hit_checker.canPrintBefore(node1, [node2])
|
||||
assert hit_checker.canPrintBefore(node1, [node3])
|
||||
assert hit_checker.canPrintBefore(node1, [node2, node3])
|
||||
assert hit_checker.canPrintBefore(node1, [node3, node2])
|
||||
|
||||
assert hit_checker.canPrintBefore(node2, [node3])
|
||||
assert not hit_checker.canPrintBefore(node2, [node1])
|
||||
assert not hit_checker.canPrintBefore(node2, [node1, node3])
|
||||
assert not hit_checker.canPrintBefore(node2, [node3, node1])
|
||||
|
||||
assert not hit_checker.canPrintBefore(node3, [node1])
|
||||
assert not hit_checker.canPrintBefore(node3, [node2])
|
||||
assert not hit_checker.canPrintBefore(node3, [node1, node2])
|
||||
assert not hit_checker.canPrintBefore(node3, [node2, node1])
|
||||
|
||||
|
||||
def test_canPrintAfter():
|
||||
node1 = CuraSceneNode(no_setting_override=True)
|
||||
node2 = CuraSceneNode(no_setting_override=True)
|
||||
node3 = CuraSceneNode(no_setting_override=True)
|
||||
|
||||
# nodes can be printed only in order node1 -> node2 -> node3
|
||||
hit_map = {
|
||||
node1: {node1: 0, node2: 0, node3: 0},
|
||||
node2: {node1: 1, node2: 0, node3: 0},
|
||||
node3: {node1: 1, node2: 1, node3: 0},
|
||||
}
|
||||
|
||||
with patch.object(HitChecker, "_buildHitMap", return_value=hit_map):
|
||||
hit_checker = HitChecker([node1, node2, node3])
|
||||
|
||||
assert not hit_checker.canPrintAfter(node1, [node2])
|
||||
assert not hit_checker.canPrintAfter(node1, [node3])
|
||||
assert not hit_checker.canPrintAfter(node1, [node2, node3])
|
||||
assert not hit_checker.canPrintAfter(node1, [node3, node2])
|
||||
|
||||
assert hit_checker.canPrintAfter(node2, [node1])
|
||||
assert not hit_checker.canPrintAfter(node2, [node3])
|
||||
assert not hit_checker.canPrintAfter(node2, [node1, node3])
|
||||
assert not hit_checker.canPrintAfter(node2, [node3, node1])
|
||||
|
||||
assert hit_checker.canPrintAfter(node3, [node1])
|
||||
assert hit_checker.canPrintAfter(node3, [node2])
|
||||
assert hit_checker.canPrintAfter(node3, [node1, node2])
|
||||
assert hit_checker.canPrintAfter(node3, [node2, node1])
|
||||
|
||||
|
||||
def test_calculateScore():
|
||||
node1 = CuraSceneNode(no_setting_override=True)
|
||||
node2 = CuraSceneNode(no_setting_override=True)
|
||||
node3 = CuraSceneNode(no_setting_override=True)
|
||||
|
||||
hit_map = {
|
||||
node1: {node1: 0, node2: 0, node3: 0}, # sum is 0
|
||||
node2: {node1: 1, node2: 0, node3: 0}, # sum is 1
|
||||
node3: {node1: 1, node2: 1, node3: 0}, # sum is 2
|
||||
}
|
||||
|
||||
with patch.object(HitChecker, "_buildHitMap", return_value=hit_map):
|
||||
hit_checker = HitChecker([node1, node2, node3])
|
||||
|
||||
# score is a diff between sums
|
||||
assert hit_checker.calculateScore(node1, node2) == -1
|
||||
assert hit_checker.calculateScore(node2, node1) == 1
|
||||
assert hit_checker.calculateScore(node1, node3) == -2
|
||||
assert hit_checker.calculateScore(node3, node1) == 2
|
||||
assert hit_checker.calculateScore(node2, node3) == -1
|
||||
assert hit_checker.calculateScore(node3, node2) == 1
|
||||
|
||||
|
||||
def test_canPrintNodesInProvidedOrder():
|
||||
node1 = CuraSceneNode(no_setting_override=True)
|
||||
node2 = CuraSceneNode(no_setting_override=True)
|
||||
node3 = CuraSceneNode(no_setting_override=True)
|
||||
|
||||
# nodes can be printed only in order node1 -> node2 -> node3
|
||||
hit_map = {
|
||||
node1: {node1: 0, node2: 0, node3: 0}, # 0
|
||||
node2: {node1: 1, node2: 0, node3: 0}, # 1
|
||||
node3: {node1: 1, node2: 1, node3: 0}, # 2
|
||||
}
|
||||
|
||||
with patch.object(HitChecker, "_buildHitMap", return_value=hit_map):
|
||||
hit_checker = HitChecker([node1, node2, node3])
|
||||
assert hit_checker.canPrintNodesInProvidedOrder([node1, node2, node3])
|
||||
assert not hit_checker.canPrintNodesInProvidedOrder([node1, node3, node2])
|
||||
assert not hit_checker.canPrintNodesInProvidedOrder([node2, node1, node3])
|
||||
assert not hit_checker.canPrintNodesInProvidedOrder([node2, node3, node1])
|
||||
assert not hit_checker.canPrintNodesInProvidedOrder([node3, node1, node2])
|
||||
assert not hit_checker.canPrintNodesInProvidedOrder([node3, node2, node1])
|
175
tests/TestPrintOrderManager.py
Normal file
175
tests/TestPrintOrderManager.py
Normal file
|
@ -0,0 +1,175 @@
|
|||
from unittest.mock import patch, MagicMock
|
||||
|
||||
from cura.PrintOrderManager import PrintOrderManager
|
||||
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||
|
||||
|
||||
def test_getNodeName():
|
||||
node1 = CuraSceneNode(name="cat", no_setting_override=True)
|
||||
node2 = CuraSceneNode(name="dog", no_setting_override=True)
|
||||
assert PrintOrderManager._getNodeName(node1) == "cat"
|
||||
assert PrintOrderManager._getNodeName(node2) == "dog"
|
||||
assert PrintOrderManager._getNodeName(None) == ""
|
||||
|
||||
|
||||
def test_getNodeName_truncatesLongName():
|
||||
node = CuraSceneNode(name="some_name_longer_than_30_characters", no_setting_override=True)
|
||||
assert PrintOrderManager._getNodeName(node) == "some_name_longer_than_30_chara"
|
||||
assert PrintOrderManager._getNodeName(node, max_length=10) == "some_name_"
|
||||
|
||||
|
||||
def test_getSingleSelectedNode():
|
||||
node1 = CuraSceneNode(no_setting_override=True)
|
||||
with patch("UM.Scene.Selection.Selection.getAllSelectedObjects", MagicMock(return_value=[node1])):
|
||||
with patch("UM.Scene.Selection.Selection.getSelectedObject", MagicMock(return_value=node1)):
|
||||
assert PrintOrderManager._getSingleSelectedNode() == node1
|
||||
|
||||
|
||||
def test_getSingleSelectedNode_returnsNoneIfNothingSelected():
|
||||
with patch("UM.Scene.Selection.Selection.getAllSelectedObjects", MagicMock(return_value=[])):
|
||||
assert PrintOrderManager._getSingleSelectedNode() is None
|
||||
|
||||
|
||||
def test_getSingleSelectedNode_returnsNoneIfMultipleObjectsSelected():
|
||||
node1 = CuraSceneNode(no_setting_override=True)
|
||||
node2 = CuraSceneNode(no_setting_override=True)
|
||||
with patch("UM.Scene.Selection.Selection.getAllSelectedObjects", MagicMock(return_value=[node1, node2])):
|
||||
assert PrintOrderManager._getSingleSelectedNode() is None
|
||||
|
||||
|
||||
def test_neighborNodeNamesCorrect_WhenSomeNodeSelected():
|
||||
node1 = CuraSceneNode(no_setting_override=True, name="node1")
|
||||
node2 = CuraSceneNode(no_setting_override=True, name="node2")
|
||||
node3 = CuraSceneNode(no_setting_override=True, name="node3")
|
||||
node1.printOrder = 1
|
||||
node2.printOrder = 2
|
||||
node3.printOrder = 3
|
||||
with patch.object(PrintOrderManager, "_configureEvents", return_value=None):
|
||||
with patch.object(PrintOrderManager, "_getSingleSelectedNode", return_value=node1):
|
||||
print_order_manager = PrintOrderManager(get_nodes=lambda: [node1, node2, node3])
|
||||
|
||||
assert print_order_manager.previousNodeName == ""
|
||||
assert print_order_manager.nextNodeName == "node2"
|
||||
assert not print_order_manager.shouldEnablePrintBeforeAction
|
||||
assert print_order_manager.shouldEnablePrintAfterAction
|
||||
|
||||
print_order_manager.swapSelectedAndNextNodes() # swaps node1 with node2, result: [node2, node1, node3]
|
||||
assert print_order_manager.previousNodeName == "node2"
|
||||
assert print_order_manager.nextNodeName == "node3"
|
||||
assert print_order_manager.shouldEnablePrintBeforeAction
|
||||
assert print_order_manager.shouldEnablePrintAfterAction
|
||||
|
||||
print_order_manager.swapSelectedAndNextNodes() # swaps node1 with node3, result: [node2, node3, node1]
|
||||
assert print_order_manager.previousNodeName == "node3"
|
||||
assert print_order_manager.nextNodeName == ""
|
||||
assert print_order_manager.shouldEnablePrintBeforeAction
|
||||
assert not print_order_manager.shouldEnablePrintAfterAction
|
||||
|
||||
print_order_manager.swapSelectedAndPreviousNodes() # swaps node1 with node3, result: [node2, node1, node3]
|
||||
assert print_order_manager.previousNodeName == "node2"
|
||||
assert print_order_manager.nextNodeName == "node3"
|
||||
assert print_order_manager.shouldEnablePrintBeforeAction
|
||||
assert print_order_manager.shouldEnablePrintAfterAction
|
||||
|
||||
print_order_manager.swapSelectedAndPreviousNodes() # swaps node1 with node2, result: [node1, node2, node3]
|
||||
assert print_order_manager.previousNodeName == ""
|
||||
assert print_order_manager.nextNodeName == "node2"
|
||||
assert not print_order_manager.shouldEnablePrintBeforeAction
|
||||
assert print_order_manager.shouldEnablePrintAfterAction
|
||||
|
||||
|
||||
def test_neighborNodeNamesEmpty_WhenNothingSelected():
|
||||
node1 = CuraSceneNode(no_setting_override=True, name="node1")
|
||||
node2 = CuraSceneNode(no_setting_override=True, name="node2")
|
||||
node3 = CuraSceneNode(no_setting_override=True, name="node3")
|
||||
node1.printOrder = 1
|
||||
node2.printOrder = 2
|
||||
node3.printOrder = 3
|
||||
with patch.object(PrintOrderManager, "_configureEvents", return_value=None):
|
||||
with patch.object(PrintOrderManager, "_getSingleSelectedNode", return_value=None):
|
||||
print_order_manager = PrintOrderManager(get_nodes=lambda: [node1, node2, node3])
|
||||
assert print_order_manager.previousNodeName == ""
|
||||
assert print_order_manager.nextNodeName == ""
|
||||
assert not print_order_manager.shouldEnablePrintBeforeAction
|
||||
assert not print_order_manager.shouldEnablePrintAfterAction
|
||||
|
||||
|
||||
def test_initializePrintOrders():
|
||||
node1 = CuraSceneNode(no_setting_override=True)
|
||||
node2 = CuraSceneNode(no_setting_override=True)
|
||||
|
||||
# assume print orders are 0
|
||||
assert node1.printOrder == 0
|
||||
assert node2.printOrder == 0
|
||||
|
||||
PrintOrderManager.initializePrintOrders([node1, node2])
|
||||
|
||||
# assert print orders initialized
|
||||
assert node1.printOrder == 1
|
||||
assert node2.printOrder == 2
|
||||
|
||||
node3 = CuraSceneNode(no_setting_override=True)
|
||||
node4 = CuraSceneNode(no_setting_override=True)
|
||||
# assume print orders are 0
|
||||
assert node3.printOrder == 0
|
||||
assert node4.printOrder == 0
|
||||
|
||||
PrintOrderManager.initializePrintOrders([node2, node1, node3, node4])
|
||||
|
||||
# assert print orders not changed for node1 and node2 and initialized for node3 and node4
|
||||
assert node1.printOrder == 1
|
||||
assert node2.printOrder == 2
|
||||
assert node3.printOrder == 3
|
||||
assert node4.printOrder == 4
|
||||
|
||||
|
||||
def test_updatePrintOrdersAfterGroupOperation():
|
||||
node1 = CuraSceneNode(no_setting_override=True)
|
||||
node2 = CuraSceneNode(no_setting_override=True)
|
||||
node3 = CuraSceneNode(no_setting_override=True)
|
||||
node4 = CuraSceneNode(no_setting_override=True)
|
||||
node5 = CuraSceneNode(no_setting_override=True)
|
||||
node1.printOrder = 1
|
||||
node2.printOrder = 2
|
||||
node3.printOrder = 3
|
||||
node4.printOrder = 4
|
||||
node5.printOrder = 5
|
||||
|
||||
all_nodes = [node1, node2, node3, node4, node5]
|
||||
grouped_nodes = [node2, node4]
|
||||
group_node = CuraSceneNode(no_setting_override=True)
|
||||
|
||||
PrintOrderManager.updatePrintOrdersAfterGroupOperation(all_nodes, group_node, grouped_nodes)
|
||||
|
||||
assert node1.printOrder == 1
|
||||
assert group_node.printOrder == 2
|
||||
assert node3.printOrder == 3
|
||||
assert node5.printOrder == 4
|
||||
|
||||
|
||||
def test_updatePrintOrdersAfterUngroupOperation():
|
||||
node1 = CuraSceneNode(no_setting_override=True)
|
||||
node2 = CuraSceneNode(no_setting_override=True)
|
||||
node3 = CuraSceneNode(no_setting_override=True)
|
||||
node1.printOrder = 1
|
||||
node2.printOrder = 2
|
||||
node3.printOrder = 3
|
||||
|
||||
all_nodes = [node1, node2, node3]
|
||||
node4 = CuraSceneNode(no_setting_override=True)
|
||||
node5 = CuraSceneNode(no_setting_override=True)
|
||||
|
||||
group_node = node2
|
||||
ungrouped_nodes = [node4, node5]
|
||||
PrintOrderManager.updatePrintOrdersAfterUngroupOperation(all_nodes, group_node, ungrouped_nodes)
|
||||
|
||||
assert node1.printOrder == 1
|
||||
assert node4.printOrder == 2
|
||||
assert node5.printOrder == 3
|
||||
assert node3.printOrder == 4
|
||||
|
||||
assert node1 in all_nodes
|
||||
assert node2 not in all_nodes
|
||||
assert node3 in all_nodes
|
||||
assert node4 in all_nodes
|
||||
assert node5 in all_nodes
|
Loading…
Add table
Add a link
Reference in a new issue