mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-08-09 14:55:03 -06:00
Merge branch 'master' of github.com:Ultimaker/Cura into CURA-5744-move-oauth-login
This commit is contained in:
commit
57334dd751
17 changed files with 196 additions and 99 deletions
44
Jenkinsfile
vendored
44
Jenkinsfile
vendored
|
@ -52,10 +52,48 @@ parallel_nodes(['linux && cura', 'windows && cura']) {
|
||||||
|
|
||||||
// Try and run the unit tests. If this stage fails, we consider the build to be "unstable".
|
// Try and run the unit tests. If this stage fails, we consider the build to be "unstable".
|
||||||
stage('Unit Test') {
|
stage('Unit Test') {
|
||||||
try {
|
if (isUnix()) {
|
||||||
|
// For Linux to show everything
|
||||||
|
def branch = env.BRANCH_NAME
|
||||||
|
if(!fileExists("${env.CURA_ENVIRONMENT_PATH}/${branch}")) {
|
||||||
|
branch = "master"
|
||||||
|
}
|
||||||
|
def uranium_dir = get_workspace_dir("Ultimaker/Uranium/${branch}")
|
||||||
|
|
||||||
|
try {
|
||||||
|
sh """
|
||||||
|
cd ..
|
||||||
|
export PYTHONPATH=.:"${uranium_dir}"
|
||||||
|
${env.CURA_ENVIRONMENT_PATH}/${branch}/bin/pytest -x --verbose --full-trace --capture=no ./tests
|
||||||
|
"""
|
||||||
|
} catch(e) {
|
||||||
|
currentBuild.result = "UNSTABLE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// For Windows
|
||||||
make('test')
|
make('test')
|
||||||
} catch(e) {
|
}
|
||||||
currentBuild.result = "UNSTABLE"
|
}
|
||||||
|
|
||||||
|
stage('Code Style') {
|
||||||
|
if (isUnix()) {
|
||||||
|
// For Linux to show everything
|
||||||
|
def branch = env.BRANCH_NAME
|
||||||
|
if(!fileExists("${env.CURA_ENVIRONMENT_PATH}/${branch}")) {
|
||||||
|
branch = "master"
|
||||||
|
}
|
||||||
|
def uranium_dir = get_workspace_dir("Ultimaker/Uranium/${branch}")
|
||||||
|
|
||||||
|
try {
|
||||||
|
sh """
|
||||||
|
cd ..
|
||||||
|
export PYTHONPATH=.:"${uranium_dir}"
|
||||||
|
${env.CURA_ENVIRONMENT_PATH}/${branch}/bin/python3 run_mypy.py
|
||||||
|
"""
|
||||||
|
} catch(e) {
|
||||||
|
currentBuild.result = "UNSTABLE"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ function(cura_add_test)
|
||||||
if (NOT ${test_exists})
|
if (NOT ${test_exists})
|
||||||
add_test(
|
add_test(
|
||||||
NAME ${_NAME}
|
NAME ${_NAME}
|
||||||
COMMAND ${PYTHON_EXECUTABLE} -m pytest --junitxml=${CMAKE_BINARY_DIR}/junit-${_NAME}.xml ${_DIRECTORY}
|
COMMAND ${PYTHON_EXECUTABLE} -m pytest --verbose --full-trace --capture=no --no-print-log --junitxml=${CMAKE_BINARY_DIR}/junit-${_NAME}.xml ${_DIRECTORY}
|
||||||
)
|
)
|
||||||
set_tests_properties(${_NAME} PROPERTIES ENVIRONMENT LANG=C)
|
set_tests_properties(${_NAME} PROPERTIES ENVIRONMENT LANG=C)
|
||||||
set_tests_properties(${_NAME} PROPERTIES ENVIRONMENT "PYTHONPATH=${_PYTHONPATH}")
|
set_tests_properties(${_NAME} PROPERTIES ENVIRONMENT "PYTHONPATH=${_PYTHONPATH}")
|
||||||
|
|
|
@ -479,8 +479,6 @@ class BuildVolume(SceneNode):
|
||||||
maximum = Vector(max_w - bed_adhesion_size - 1, max_h - self._raft_thickness - self._extra_z_clearance, max_d - disallowed_area_size + bed_adhesion_size - 1)
|
maximum = Vector(max_w - bed_adhesion_size - 1, max_h - self._raft_thickness - self._extra_z_clearance, max_d - disallowed_area_size + bed_adhesion_size - 1)
|
||||||
)
|
)
|
||||||
|
|
||||||
self._application.getController().getScene()._maximum_bounds = scale_to_max_bounds
|
|
||||||
|
|
||||||
self.updateNodeBoundaryCheck()
|
self.updateNodeBoundaryCheck()
|
||||||
|
|
||||||
def getBoundingBox(self) -> AxisAlignedBox:
|
def getBoundingBox(self) -> AxisAlignedBox:
|
||||||
|
|
|
@ -9,9 +9,6 @@ from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
from UM.Settings.InstanceContainer import InstanceContainer
|
from UM.Settings.InstanceContainer import InstanceContainer
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from cura.Machines.QualityGroup import QualityGroup
|
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# A metadata / container combination. Use getContainer() to get the container corresponding to the metadata.
|
# A metadata / container combination. Use getContainer() to get the container corresponding to the metadata.
|
||||||
|
@ -24,11 +21,11 @@ if TYPE_CHECKING:
|
||||||
# This is used in Variant, Material, and Quality Managers.
|
# This is used in Variant, Material, and Quality Managers.
|
||||||
#
|
#
|
||||||
class ContainerNode:
|
class ContainerNode:
|
||||||
__slots__ = ("_metadata", "container", "children_map")
|
__slots__ = ("_metadata", "_container", "children_map")
|
||||||
|
|
||||||
def __init__(self, metadata: Optional[Dict[str, Any]] = None) -> None:
|
def __init__(self, metadata: Optional[Dict[str, Any]] = None) -> None:
|
||||||
self._metadata = metadata
|
self._metadata = metadata
|
||||||
self.container = None
|
self._container = None # type: Optional[InstanceContainer]
|
||||||
self.children_map = OrderedDict() # type: ignore # This is because it's children are supposed to override it.
|
self.children_map = OrderedDict() # type: ignore # This is because it's children are supposed to override it.
|
||||||
|
|
||||||
## Get an entry value from the metadata
|
## Get an entry value from the metadata
|
||||||
|
@ -50,7 +47,7 @@ class ContainerNode:
|
||||||
Logger.log("e", "Cannot get container for a ContainerNode without metadata.")
|
Logger.log("e", "Cannot get container for a ContainerNode without metadata.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if self.container is None:
|
if self._container is None:
|
||||||
container_id = self._metadata["id"]
|
container_id = self._metadata["id"]
|
||||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||||
container_list = ContainerRegistry.getInstance().findInstanceContainers(id = container_id)
|
container_list = ContainerRegistry.getInstance().findInstanceContainers(id = container_id)
|
||||||
|
@ -59,9 +56,9 @@ class ContainerNode:
|
||||||
error_message = ConfigurationErrorMessage.getInstance()
|
error_message = ConfigurationErrorMessage.getInstance()
|
||||||
error_message.addFaultyContainers(container_id)
|
error_message.addFaultyContainers(container_id)
|
||||||
return None
|
return None
|
||||||
self.container = container_list[0]
|
self._container = container_list[0]
|
||||||
|
|
||||||
return self.container
|
return self._container
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return "%s[%s]" % (self.__class__.__name__, self.getMetaDataEntry("id"))
|
return "%s[%s]" % (self.__class__.__name__, self.getMetaDataEntry("id"))
|
||||||
|
|
|
@ -21,7 +21,6 @@ from .VariantType import VariantType
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||||
from UM.Settings.InstanceContainer import InstanceContainer
|
|
||||||
from cura.Settings.GlobalStack import GlobalStack
|
from cura.Settings.GlobalStack import GlobalStack
|
||||||
from cura.Settings.ExtruderStack import ExtruderStack
|
from cura.Settings.ExtruderStack import ExtruderStack
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,7 @@ class SettingVisibilityPresetsModel(ListModel):
|
||||||
break
|
break
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def _populate(self):
|
def _populate(self) -> None:
|
||||||
from cura.CuraApplication import CuraApplication
|
from cura.CuraApplication import CuraApplication
|
||||||
items = []
|
items = []
|
||||||
for file_path in Resources.getAllResourcesOfType(CuraApplication.ResourceTypes.SettingVisibilityPreset):
|
for file_path in Resources.getAllResourcesOfType(CuraApplication.ResourceTypes.SettingVisibilityPreset):
|
||||||
|
@ -147,7 +147,7 @@ class SettingVisibilityPresetsModel(ListModel):
|
||||||
def activePreset(self) -> str:
|
def activePreset(self) -> str:
|
||||||
return self._active_preset_item["id"]
|
return self._active_preset_item["id"]
|
||||||
|
|
||||||
def _onPreferencesChanged(self, name: str):
|
def _onPreferencesChanged(self, name: str) -> None:
|
||||||
if name != "general/visible_settings":
|
if name != "general/visible_settings":
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -24,9 +24,9 @@ class QualityChangesGroup(QualityGroup):
|
||||||
ConfigurationErrorMessage.getInstance().addFaultyContainers(node.getMetaDataEntry("id"))
|
ConfigurationErrorMessage.getInstance().addFaultyContainers(node.getMetaDataEntry("id"))
|
||||||
return
|
return
|
||||||
|
|
||||||
if extruder_position is None: #Then we're a global quality changes profile.
|
if extruder_position is None: # Then we're a global quality changes profile.
|
||||||
self.node_for_global = node
|
self.node_for_global = node
|
||||||
else: #This is an extruder's quality changes profile.
|
else: # This is an extruder's quality changes profile.
|
||||||
self.nodes_for_extruders[extruder_position] = node
|
self.nodes_for_extruders[extruder_position] = node
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# Copyright (c) 2018 Ultimaker B.V.
|
# Copyright (c) 2018 Ultimaker B.V.
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
from typing import Optional, Dict, cast
|
from typing import Optional, Dict, cast, Any
|
||||||
|
|
||||||
from .ContainerNode import ContainerNode
|
from .ContainerNode import ContainerNode
|
||||||
from .QualityChangesGroup import QualityChangesGroup
|
from .QualityChangesGroup import QualityChangesGroup
|
||||||
|
@ -12,21 +12,21 @@ from .QualityChangesGroup import QualityChangesGroup
|
||||||
#
|
#
|
||||||
class QualityNode(ContainerNode):
|
class QualityNode(ContainerNode):
|
||||||
|
|
||||||
def __init__(self, metadata: Optional[dict] = None) -> None:
|
def __init__(self, metadata: Optional[Dict[str, Any]] = None) -> None:
|
||||||
super().__init__(metadata = metadata)
|
super().__init__(metadata = metadata)
|
||||||
self.quality_type_map = {} # type: Dict[str, QualityNode] # quality_type -> QualityNode for InstanceContainer
|
self.quality_type_map = {} # type: Dict[str, QualityNode] # quality_type -> QualityNode for InstanceContainer
|
||||||
|
|
||||||
def getChildNode(self, child_key: str) -> Optional["QualityNode"]:
|
def getChildNode(self, child_key: str) -> Optional["QualityNode"]:
|
||||||
return self.children_map.get(child_key)
|
return self.children_map.get(child_key)
|
||||||
|
|
||||||
def addQualityMetadata(self, quality_type: str, metadata: dict):
|
def addQualityMetadata(self, quality_type: str, metadata: Dict[str, Any]):
|
||||||
if quality_type not in self.quality_type_map:
|
if quality_type not in self.quality_type_map:
|
||||||
self.quality_type_map[quality_type] = QualityNode(metadata)
|
self.quality_type_map[quality_type] = QualityNode(metadata)
|
||||||
|
|
||||||
def getQualityNode(self, quality_type: str) -> Optional["QualityNode"]:
|
def getQualityNode(self, quality_type: str) -> Optional["QualityNode"]:
|
||||||
return self.quality_type_map.get(quality_type)
|
return self.quality_type_map.get(quality_type)
|
||||||
|
|
||||||
def addQualityChangesMetadata(self, quality_type: str, metadata: dict):
|
def addQualityChangesMetadata(self, quality_type: str, metadata: Dict[str, Any]):
|
||||||
if quality_type not in self.quality_type_map:
|
if quality_type not in self.quality_type_map:
|
||||||
self.quality_type_map[quality_type] = QualityNode()
|
self.quality_type_map[quality_type] = QualityNode()
|
||||||
quality_type_node = self.quality_type_map[quality_type]
|
quality_type_node = self.quality_type_map[quality_type]
|
||||||
|
|
|
@ -13,20 +13,20 @@ class ConfigurationModel(QObject):
|
||||||
|
|
||||||
configurationChanged = pyqtSignal()
|
configurationChanged = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._printer_type = None
|
self._printer_type = ""
|
||||||
self._extruder_configurations = [] # type: List[ExtruderConfigurationModel]
|
self._extruder_configurations = [] # type: List[ExtruderConfigurationModel]
|
||||||
self._buildplate_configuration = None
|
self._buildplate_configuration = ""
|
||||||
|
|
||||||
def setPrinterType(self, printer_type):
|
def setPrinterType(self, printer_type):
|
||||||
self._printer_type = printer_type
|
self._printer_type = printer_type
|
||||||
|
|
||||||
@pyqtProperty(str, fset = setPrinterType, notify = configurationChanged)
|
@pyqtProperty(str, fset = setPrinterType, notify = configurationChanged)
|
||||||
def printerType(self):
|
def printerType(self) -> str:
|
||||||
return self._printer_type
|
return self._printer_type
|
||||||
|
|
||||||
def setExtruderConfigurations(self, extruder_configurations):
|
def setExtruderConfigurations(self, extruder_configurations: List["ExtruderConfigurationModel"]):
|
||||||
if self._extruder_configurations != extruder_configurations:
|
if self._extruder_configurations != extruder_configurations:
|
||||||
self._extruder_configurations = extruder_configurations
|
self._extruder_configurations = extruder_configurations
|
||||||
|
|
||||||
|
@ -39,16 +39,16 @@ class ConfigurationModel(QObject):
|
||||||
def extruderConfigurations(self):
|
def extruderConfigurations(self):
|
||||||
return self._extruder_configurations
|
return self._extruder_configurations
|
||||||
|
|
||||||
def setBuildplateConfiguration(self, buildplate_configuration):
|
def setBuildplateConfiguration(self, buildplate_configuration: str) -> None:
|
||||||
self._buildplate_configuration = buildplate_configuration
|
self._buildplate_configuration = buildplate_configuration
|
||||||
|
|
||||||
@pyqtProperty(str, fset = setBuildplateConfiguration, notify = configurationChanged)
|
@pyqtProperty(str, fset = setBuildplateConfiguration, notify = configurationChanged)
|
||||||
def buildplateConfiguration(self):
|
def buildplateConfiguration(self) -> str:
|
||||||
return self._buildplate_configuration
|
return self._buildplate_configuration
|
||||||
|
|
||||||
## This method is intended to indicate whether the configuration is valid or not.
|
## This method is intended to indicate whether the configuration is valid or not.
|
||||||
# The method checks if the mandatory fields are or not set
|
# The method checks if the mandatory fields are or not set
|
||||||
def isValid(self):
|
def isValid(self) -> bool:
|
||||||
if not self._extruder_configurations:
|
if not self._extruder_configurations:
|
||||||
return False
|
return False
|
||||||
for configuration in self._extruder_configurations:
|
for configuration in self._extruder_configurations:
|
||||||
|
|
|
@ -53,21 +53,8 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
|
||||||
self._sending_gcode = False
|
self._sending_gcode = False
|
||||||
self._compressing_gcode = False
|
self._compressing_gcode = False
|
||||||
self._gcode = [] # type: List[str]
|
self._gcode = [] # type: List[str]
|
||||||
|
|
||||||
self._connection_state_before_timeout = None # type: Optional[ConnectionState]
|
self._connection_state_before_timeout = None # type: Optional[ConnectionState]
|
||||||
|
|
||||||
printer_type = self._properties.get(b"machine", b"").decode("utf-8")
|
|
||||||
printer_type_identifiers = {
|
|
||||||
"9066": "ultimaker3",
|
|
||||||
"9511": "ultimaker3_extended",
|
|
||||||
"9051": "ultimaker_s5"
|
|
||||||
}
|
|
||||||
self._printer_type = "Unknown"
|
|
||||||
for key, value in printer_type_identifiers.items():
|
|
||||||
if printer_type.startswith(key):
|
|
||||||
self._printer_type = value
|
|
||||||
break
|
|
||||||
|
|
||||||
def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False, file_handler: Optional[FileHandler] = None, **kwargs: str) -> None:
|
def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False, file_handler: Optional[FileHandler] = None, **kwargs: str) -> None:
|
||||||
raise NotImplementedError("requestWrite needs to be implemented")
|
raise NotImplementedError("requestWrite needs to be implemented")
|
||||||
|
|
||||||
|
@ -341,7 +328,7 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
|
||||||
|
|
||||||
@pyqtProperty(str, constant = True)
|
@pyqtProperty(str, constant = True)
|
||||||
def printerType(self) -> str:
|
def printerType(self) -> str:
|
||||||
return self._printer_type
|
return self._properties.get(b"printer_type", b"Unknown").decode("utf-8")
|
||||||
|
|
||||||
## IP adress of this printer
|
## IP adress of this printer
|
||||||
@pyqtProperty(str, constant = True)
|
@pyqtProperty(str, constant = True)
|
||||||
|
|
|
@ -13,23 +13,31 @@ from cura.Scene import ConvexHullNode
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING, Any, Optional
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from UM.Scene.SceneNode import SceneNode
|
||||||
|
from cura.Settings.GlobalStack import GlobalStack
|
||||||
|
|
||||||
|
|
||||||
## The convex hull decorator is a scene node decorator that adds the convex hull functionality to a scene node.
|
## The convex hull decorator is a scene node decorator that adds the convex hull functionality to a scene node.
|
||||||
# If a scene node has a convex hull decorator, it will have a shadow in which other objects can not be printed.
|
# If a scene node has a convex hull decorator, it will have a shadow in which other objects can not be printed.
|
||||||
class ConvexHullDecorator(SceneNodeDecorator):
|
class ConvexHullDecorator(SceneNodeDecorator):
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self._convex_hull_node = None
|
self._convex_hull_node = None # type: Optional["SceneNode"]
|
||||||
self._init2DConvexHullCache()
|
self._init2DConvexHullCache()
|
||||||
|
|
||||||
self._global_stack = None
|
self._global_stack = None # type: Optional[GlobalStack]
|
||||||
|
|
||||||
# Make sure the timer is created on the main thread
|
# Make sure the timer is created on the main thread
|
||||||
self._recompute_convex_hull_timer = None
|
self._recompute_convex_hull_timer = None # type: Optional[QTimer]
|
||||||
Application.getInstance().callLater(self.createRecomputeConvexHullTimer)
|
|
||||||
|
if Application.getInstance() is not None:
|
||||||
|
Application.getInstance().callLater(self.createRecomputeConvexHullTimer)
|
||||||
|
|
||||||
self._raft_thickness = 0.0
|
self._raft_thickness = 0.0
|
||||||
# For raft thickness, DRY
|
|
||||||
self._build_volume = Application.getInstance().getBuildVolume()
|
self._build_volume = Application.getInstance().getBuildVolume()
|
||||||
self._build_volume.raftThicknessChanged.connect(self._onChanged)
|
self._build_volume.raftThicknessChanged.connect(self._onChanged)
|
||||||
|
|
||||||
|
@ -39,13 +47,13 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
||||||
|
|
||||||
self._onGlobalStackChanged()
|
self._onGlobalStackChanged()
|
||||||
|
|
||||||
def createRecomputeConvexHullTimer(self):
|
def createRecomputeConvexHullTimer(self) -> None:
|
||||||
self._recompute_convex_hull_timer = QTimer()
|
self._recompute_convex_hull_timer = QTimer()
|
||||||
self._recompute_convex_hull_timer.setInterval(200)
|
self._recompute_convex_hull_timer.setInterval(200)
|
||||||
self._recompute_convex_hull_timer.setSingleShot(True)
|
self._recompute_convex_hull_timer.setSingleShot(True)
|
||||||
self._recompute_convex_hull_timer.timeout.connect(self.recomputeConvexHull)
|
self._recompute_convex_hull_timer.timeout.connect(self.recomputeConvexHull)
|
||||||
|
|
||||||
def setNode(self, node):
|
def setNode(self, node: "SceneNode") -> None:
|
||||||
previous_node = self._node
|
previous_node = self._node
|
||||||
# Disconnect from previous node signals
|
# Disconnect from previous node signals
|
||||||
if previous_node is not None and node is not previous_node:
|
if previous_node is not None and node is not previous_node:
|
||||||
|
@ -63,14 +71,14 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
||||||
def __deepcopy__(self, memo):
|
def __deepcopy__(self, memo):
|
||||||
return ConvexHullDecorator()
|
return ConvexHullDecorator()
|
||||||
|
|
||||||
## Get the unmodified 2D projected convex hull of the node
|
## Get the unmodified 2D projected convex hull of the node (if any)
|
||||||
def getConvexHull(self):
|
def getConvexHull(self) -> Optional[Polygon]:
|
||||||
if self._node is None:
|
if self._node is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
hull = self._compute2DConvexHull()
|
hull = self._compute2DConvexHull()
|
||||||
|
|
||||||
if self._global_stack and self._node:
|
if self._global_stack and self._node and hull is not None:
|
||||||
# Parent can be None if node is just loaded.
|
# Parent can be None if node is just loaded.
|
||||||
if self._global_stack.getProperty("print_sequence", "value") == "one_at_a_time" and (self._node.getParent() is None or not self._node.getParent().callDecoration("isGroup")):
|
if self._global_stack.getProperty("print_sequence", "value") == "one_at_a_time" and (self._node.getParent() is None or not self._node.getParent().callDecoration("isGroup")):
|
||||||
hull = hull.getMinkowskiHull(Polygon(numpy.array(self._global_stack.getProperty("machine_head_polygon", "value"), numpy.float32)))
|
hull = hull.getMinkowskiHull(Polygon(numpy.array(self._global_stack.getProperty("machine_head_polygon", "value"), numpy.float32)))
|
||||||
|
@ -78,7 +86,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
||||||
return hull
|
return hull
|
||||||
|
|
||||||
## Get the convex hull of the node with the full head size
|
## Get the convex hull of the node with the full head size
|
||||||
def getConvexHullHeadFull(self):
|
def getConvexHullHeadFull(self) -> Optional[Polygon]:
|
||||||
if self._node is None:
|
if self._node is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -87,7 +95,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
||||||
## Get convex hull of the object + head size
|
## Get convex hull of the object + head size
|
||||||
# In case of printing all at once this is the same as the convex hull.
|
# In case of printing all at once this is the same as the convex hull.
|
||||||
# For one at the time this is area with intersection of mirrored head
|
# For one at the time this is area with intersection of mirrored head
|
||||||
def getConvexHullHead(self):
|
def getConvexHullHead(self) -> Optional[Polygon]:
|
||||||
if self._node is None:
|
if self._node is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -101,7 +109,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
||||||
## Get convex hull of the node
|
## Get convex hull of the node
|
||||||
# In case of printing all at once this is the same as the convex hull.
|
# In case of printing all at once this is the same as the convex hull.
|
||||||
# For one at the time this is the area without the head.
|
# For one at the time this is the area without the head.
|
||||||
def getConvexHullBoundary(self):
|
def getConvexHullBoundary(self) -> Optional[Polygon]:
|
||||||
if self._node is None:
|
if self._node is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -111,13 +119,14 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
||||||
return self._compute2DConvexHull()
|
return self._compute2DConvexHull()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def recomputeConvexHullDelayed(self):
|
## The same as recomputeConvexHull, but using a timer if it was set.
|
||||||
|
def recomputeConvexHullDelayed(self) -> None:
|
||||||
if self._recompute_convex_hull_timer is not None:
|
if self._recompute_convex_hull_timer is not None:
|
||||||
self._recompute_convex_hull_timer.start()
|
self._recompute_convex_hull_timer.start()
|
||||||
else:
|
else:
|
||||||
self.recomputeConvexHull()
|
self.recomputeConvexHull()
|
||||||
|
|
||||||
def recomputeConvexHull(self):
|
def recomputeConvexHull(self) -> None:
|
||||||
controller = Application.getInstance().getController()
|
controller = Application.getInstance().getController()
|
||||||
root = controller.getScene().getRoot()
|
root = controller.getScene().getRoot()
|
||||||
if self._node is None or controller.isToolOperationActive() or not self.__isDescendant(root, self._node):
|
if self._node is None or controller.isToolOperationActive() or not self.__isDescendant(root, self._node):
|
||||||
|
@ -132,17 +141,17 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
||||||
hull_node = ConvexHullNode.ConvexHullNode(self._node, convex_hull, self._raft_thickness, root)
|
hull_node = ConvexHullNode.ConvexHullNode(self._node, convex_hull, self._raft_thickness, root)
|
||||||
self._convex_hull_node = hull_node
|
self._convex_hull_node = hull_node
|
||||||
|
|
||||||
def _onSettingValueChanged(self, key, property_name):
|
def _onSettingValueChanged(self, key: str, property_name: str) -> None:
|
||||||
if property_name != "value": #Not the value that was changed.
|
if property_name != "value": # Not the value that was changed.
|
||||||
return
|
return
|
||||||
|
|
||||||
if key in self._affected_settings:
|
if key in self._affected_settings:
|
||||||
self._onChanged()
|
self._onChanged()
|
||||||
if key in self._influencing_settings:
|
if key in self._influencing_settings:
|
||||||
self._init2DConvexHullCache() #Invalidate the cache.
|
self._init2DConvexHullCache() # Invalidate the cache.
|
||||||
self._onChanged()
|
self._onChanged()
|
||||||
|
|
||||||
def _init2DConvexHullCache(self):
|
def _init2DConvexHullCache(self) -> None:
|
||||||
# Cache for the group code path in _compute2DConvexHull()
|
# Cache for the group code path in _compute2DConvexHull()
|
||||||
self._2d_convex_hull_group_child_polygon = None
|
self._2d_convex_hull_group_child_polygon = None
|
||||||
self._2d_convex_hull_group_result = None
|
self._2d_convex_hull_group_result = None
|
||||||
|
@ -152,7 +161,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
||||||
self._2d_convex_hull_mesh_world_transform = None
|
self._2d_convex_hull_mesh_world_transform = None
|
||||||
self._2d_convex_hull_mesh_result = None
|
self._2d_convex_hull_mesh_result = None
|
||||||
|
|
||||||
def _compute2DConvexHull(self):
|
def _compute2DConvexHull(self) -> Optional[Polygon]:
|
||||||
if self._node.callDecoration("isGroup"):
|
if self._node.callDecoration("isGroup"):
|
||||||
points = numpy.zeros((0, 2), dtype=numpy.int32)
|
points = numpy.zeros((0, 2), dtype=numpy.int32)
|
||||||
for child in self._node.getChildren():
|
for child in self._node.getChildren():
|
||||||
|
@ -179,8 +188,6 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
||||||
|
|
||||||
else:
|
else:
|
||||||
offset_hull = None
|
offset_hull = None
|
||||||
mesh = None
|
|
||||||
world_transform = None
|
|
||||||
if self._node.getMeshData():
|
if self._node.getMeshData():
|
||||||
mesh = self._node.getMeshData()
|
mesh = self._node.getMeshData()
|
||||||
world_transform = self._node.getWorldTransformation()
|
world_transform = self._node.getWorldTransformation()
|
||||||
|
@ -228,24 +235,33 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
||||||
|
|
||||||
return offset_hull
|
return offset_hull
|
||||||
|
|
||||||
def _getHeadAndFans(self):
|
def _getHeadAndFans(self) -> Polygon:
|
||||||
return Polygon(numpy.array(self._global_stack.getHeadAndFansCoordinates(), numpy.float32))
|
if self._global_stack:
|
||||||
|
return Polygon(numpy.array(self._global_stack.getHeadAndFansCoordinates(), numpy.float32))
|
||||||
|
return Polygon()
|
||||||
|
|
||||||
def _compute2DConvexHeadFull(self):
|
def _compute2DConvexHeadFull(self) -> Optional[Polygon]:
|
||||||
return self._compute2DConvexHull().getMinkowskiHull(self._getHeadAndFans())
|
convex_hull = self._compute2DConvexHeadFull()
|
||||||
|
if convex_hull:
|
||||||
|
return convex_hull.getMinkowskiHull(self._getHeadAndFans())
|
||||||
|
return None
|
||||||
|
|
||||||
def _compute2DConvexHeadMin(self):
|
def _compute2DConvexHeadMin(self) -> Optional[Polygon]:
|
||||||
headAndFans = self._getHeadAndFans()
|
head_and_fans = self._getHeadAndFans()
|
||||||
mirrored = headAndFans.mirror([0, 0], [0, 1]).mirror([0, 0], [1, 0]) # Mirror horizontally & vertically.
|
mirrored = head_and_fans.mirror([0, 0], [0, 1]).mirror([0, 0], [1, 0]) # Mirror horizontally & vertically.
|
||||||
head_and_fans = self._getHeadAndFans().intersectionConvexHulls(mirrored)
|
head_and_fans = self._getHeadAndFans().intersectionConvexHulls(mirrored)
|
||||||
|
|
||||||
# Min head hull is used for the push free
|
# Min head hull is used for the push free
|
||||||
min_head_hull = self._compute2DConvexHull().getMinkowskiHull(head_and_fans)
|
convex_hull = self._compute2DConvexHeadFull()
|
||||||
return min_head_hull
|
if convex_hull:
|
||||||
|
return convex_hull.getMinkowskiHull(head_and_fans)
|
||||||
|
return None
|
||||||
|
|
||||||
## Compensate given 2D polygon with adhesion margin
|
## Compensate given 2D polygon with adhesion margin
|
||||||
# \return 2D polygon with added margin
|
# \return 2D polygon with added margin
|
||||||
def _add2DAdhesionMargin(self, poly):
|
def _add2DAdhesionMargin(self, poly: Polygon) -> Polygon:
|
||||||
|
if not self._global_stack:
|
||||||
|
return Polygon()
|
||||||
# Compensate for raft/skirt/brim
|
# Compensate for raft/skirt/brim
|
||||||
# Add extra margin depending on adhesion type
|
# Add extra margin depending on adhesion type
|
||||||
adhesion_type = self._global_stack.getProperty("adhesion_type", "value")
|
adhesion_type = self._global_stack.getProperty("adhesion_type", "value")
|
||||||
|
@ -263,7 +279,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
||||||
else:
|
else:
|
||||||
raise Exception("Unknown bed adhesion type. Did you forget to update the convex hull calculations for your new bed adhesion type?")
|
raise Exception("Unknown bed adhesion type. Did you forget to update the convex hull calculations for your new bed adhesion type?")
|
||||||
|
|
||||||
# adjust head_and_fans with extra margin
|
# Adjust head_and_fans with extra margin
|
||||||
if extra_margin > 0:
|
if extra_margin > 0:
|
||||||
extra_margin_polygon = Polygon.approximatedCircle(extra_margin)
|
extra_margin_polygon = Polygon.approximatedCircle(extra_margin)
|
||||||
poly = poly.getMinkowskiHull(extra_margin_polygon)
|
poly = poly.getMinkowskiHull(extra_margin_polygon)
|
||||||
|
@ -274,7 +290,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
||||||
# \param convex_hull Polygon of the original convex hull.
|
# \param convex_hull Polygon of the original convex hull.
|
||||||
# \return New Polygon instance that is offset with everything that
|
# \return New Polygon instance that is offset with everything that
|
||||||
# influences the collision area.
|
# influences the collision area.
|
||||||
def _offsetHull(self, convex_hull):
|
def _offsetHull(self, convex_hull: Polygon) -> Polygon:
|
||||||
horizontal_expansion = max(
|
horizontal_expansion = max(
|
||||||
self._getSettingProperty("xy_offset", "value"),
|
self._getSettingProperty("xy_offset", "value"),
|
||||||
self._getSettingProperty("xy_offset_layer_0", "value")
|
self._getSettingProperty("xy_offset_layer_0", "value")
|
||||||
|
@ -295,12 +311,12 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
||||||
else:
|
else:
|
||||||
return convex_hull
|
return convex_hull
|
||||||
|
|
||||||
def _onChanged(self, *args):
|
def _onChanged(self, *args) -> None:
|
||||||
self._raft_thickness = self._build_volume.getRaftThickness()
|
self._raft_thickness = self._build_volume.getRaftThickness()
|
||||||
if not args or args[0] == self._node:
|
if not args or args[0] == self._node:
|
||||||
self.recomputeConvexHullDelayed()
|
self.recomputeConvexHullDelayed()
|
||||||
|
|
||||||
def _onGlobalStackChanged(self):
|
def _onGlobalStackChanged(self) -> None:
|
||||||
if self._global_stack:
|
if self._global_stack:
|
||||||
self._global_stack.propertyChanged.disconnect(self._onSettingValueChanged)
|
self._global_stack.propertyChanged.disconnect(self._onSettingValueChanged)
|
||||||
self._global_stack.containersChanged.disconnect(self._onChanged)
|
self._global_stack.containersChanged.disconnect(self._onChanged)
|
||||||
|
@ -321,7 +337,9 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
||||||
self._onChanged()
|
self._onChanged()
|
||||||
|
|
||||||
## Private convenience function to get a setting from the correct extruder (as defined by limit_to_extruder property).
|
## Private convenience function to get a setting from the correct extruder (as defined by limit_to_extruder property).
|
||||||
def _getSettingProperty(self, setting_key, prop = "value"):
|
def _getSettingProperty(self, setting_key: str, prop: str = "value") -> Any:
|
||||||
|
if not self._global_stack:
|
||||||
|
return None
|
||||||
per_mesh_stack = self._node.callDecoration("getStack")
|
per_mesh_stack = self._node.callDecoration("getStack")
|
||||||
if per_mesh_stack:
|
if per_mesh_stack:
|
||||||
return per_mesh_stack.getProperty(setting_key, prop)
|
return per_mesh_stack.getProperty(setting_key, prop)
|
||||||
|
@ -339,8 +357,8 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
||||||
# Limit_to_extruder is set. The global stack handles this then
|
# Limit_to_extruder is set. The global stack handles this then
|
||||||
return self._global_stack.getProperty(setting_key, prop)
|
return self._global_stack.getProperty(setting_key, prop)
|
||||||
|
|
||||||
## Returns true if node is a descendant or the same as the root node.
|
## Returns True if node is a descendant or the same as the root node.
|
||||||
def __isDescendant(self, root, node):
|
def __isDescendant(self, root: "SceneNode", node: "SceneNode") -> bool:
|
||||||
if node is None:
|
if node is None:
|
||||||
return False
|
return False
|
||||||
if root is node:
|
if root is node:
|
||||||
|
|
|
@ -1,13 +1,19 @@
|
||||||
from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
|
from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
class GCodeListDecorator(SceneNodeDecorator):
|
class GCodeListDecorator(SceneNodeDecorator):
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._gcode_list = []
|
self._gcode_list = [] # type: List[str]
|
||||||
|
|
||||||
def getGCodeList(self):
|
def getGCodeList(self) -> List[str]:
|
||||||
return self._gcode_list
|
return self._gcode_list
|
||||||
|
|
||||||
def setGCodeList(self, list):
|
def setGCodeList(self, list: List[str]):
|
||||||
self._gcode_list = list
|
self._gcode_list = list
|
||||||
|
|
||||||
|
def __deepcopy__(self, memo) -> "GCodeListDecorator":
|
||||||
|
copied_decorator = GCodeListDecorator()
|
||||||
|
copied_decorator.setGCodeList(self.getGCodeList())
|
||||||
|
return copied_decorator
|
|
@ -2,11 +2,11 @@ from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
|
||||||
|
|
||||||
|
|
||||||
class SliceableObjectDecorator(SceneNodeDecorator):
|
class SliceableObjectDecorator(SceneNodeDecorator):
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
def isSliceable(self):
|
def isSliceable(self) -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def __deepcopy__(self, memo):
|
def __deepcopy__(self, memo) -> "SliceableObjectDecorator":
|
||||||
return type(self)()
|
return type(self)()
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
|
from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
|
||||||
|
|
||||||
|
|
||||||
## A decorator that stores the amount an object has been moved below the platform.
|
## A decorator that stores the amount an object has been moved below the platform.
|
||||||
class ZOffsetDecorator(SceneNodeDecorator):
|
class ZOffsetDecorator(SceneNodeDecorator):
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._z_offset = 0
|
self._z_offset = 0.
|
||||||
|
|
||||||
def setZOffset(self, offset):
|
def setZOffset(self, offset: float) -> None:
|
||||||
self._z_offset = offset
|
self._z_offset = offset
|
||||||
|
|
||||||
def getZOffset(self):
|
def getZOffset(self) -> float:
|
||||||
return self._z_offset
|
return self._z_offset
|
||||||
|
|
||||||
def __deepcopy__(self, memo):
|
def __deepcopy__(self, memo) -> "ZOffsetDecorator":
|
||||||
copied_decorator = ZOffsetDecorator()
|
copied_decorator = ZOffsetDecorator()
|
||||||
copied_decorator.setZOffset(self.getZOffset())
|
copied_decorator.setZOffset(self.getZOffset())
|
||||||
return copied_decorator
|
return copied_decorator
|
||||||
|
|
|
@ -23,6 +23,7 @@ from .CuraContainerStack import CuraContainerStack
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from cura.Settings.ExtruderStack import ExtruderStack
|
from cura.Settings.ExtruderStack import ExtruderStack
|
||||||
|
|
||||||
|
|
||||||
## Represents the Global or Machine stack and its related containers.
|
## Represents the Global or Machine stack and its related containers.
|
||||||
#
|
#
|
||||||
class GlobalStack(CuraContainerStack):
|
class GlobalStack(CuraContainerStack):
|
||||||
|
|
|
@ -260,6 +260,19 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
|
||||||
# or "Legacy" UM3 device.
|
# or "Legacy" UM3 device.
|
||||||
cluster_size = int(properties.get(b"cluster_size", -1))
|
cluster_size = int(properties.get(b"cluster_size", -1))
|
||||||
|
|
||||||
|
printer_type = properties.get(b"machine", b"").decode("utf-8")
|
||||||
|
printer_type_identifiers = {
|
||||||
|
"9066": "ultimaker3",
|
||||||
|
"9511": "ultimaker3_extended",
|
||||||
|
"9051": "ultimaker_s5"
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value in printer_type_identifiers.items():
|
||||||
|
if printer_type.startswith(key):
|
||||||
|
properties[b"printer_type"] = bytes(value, encoding="utf8")
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
properties[b"printer_type"] = b"Unknown"
|
||||||
if cluster_size >= 0:
|
if cluster_size >= 0:
|
||||||
device = ClusterUM3OutputDevice.ClusterUM3OutputDevice(name, address, properties)
|
device = ClusterUM3OutputDevice.ClusterUM3OutputDevice(name, address, properties)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -15,6 +15,7 @@ import UM.Settings.SettingDefinition #To add settings to the definition.
|
||||||
|
|
||||||
from cura.Settings.cura_empty_instance_containers import empty_container
|
from cura.Settings.cura_empty_instance_containers import empty_container
|
||||||
|
|
||||||
|
|
||||||
## Gets an instance container with a specified container type.
|
## Gets an instance container with a specified container type.
|
||||||
#
|
#
|
||||||
# \param container_type The type metadata for the instance container.
|
# \param container_type The type metadata for the instance container.
|
||||||
|
@ -24,22 +25,27 @@ def getInstanceContainer(container_type) -> InstanceContainer:
|
||||||
container.setMetaDataEntry("type", container_type)
|
container.setMetaDataEntry("type", container_type)
|
||||||
return container
|
return container
|
||||||
|
|
||||||
|
|
||||||
class DefinitionContainerSubClass(DefinitionContainer):
|
class DefinitionContainerSubClass(DefinitionContainer):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(container_id = "SubDefinitionContainer")
|
super().__init__(container_id = "SubDefinitionContainer")
|
||||||
|
|
||||||
|
|
||||||
class InstanceContainerSubClass(InstanceContainer):
|
class InstanceContainerSubClass(InstanceContainer):
|
||||||
def __init__(self, container_type):
|
def __init__(self, container_type):
|
||||||
super().__init__(container_id = "SubInstanceContainer")
|
super().__init__(container_id = "SubInstanceContainer")
|
||||||
self.setMetaDataEntry("type", container_type)
|
self.setMetaDataEntry("type", container_type)
|
||||||
|
|
||||||
|
|
||||||
#############################START OF TEST CASES################################
|
#############################START OF TEST CASES################################
|
||||||
|
|
||||||
|
|
||||||
## Tests whether adding a container is properly forbidden.
|
## Tests whether adding a container is properly forbidden.
|
||||||
def test_addContainer(global_stack):
|
def test_addContainer(global_stack):
|
||||||
with pytest.raises(InvalidOperationError):
|
with pytest.raises(InvalidOperationError):
|
||||||
global_stack.addContainer(unittest.mock.MagicMock())
|
global_stack.addContainer(unittest.mock.MagicMock())
|
||||||
|
|
||||||
|
|
||||||
## Tests adding extruders to the global stack.
|
## Tests adding extruders to the global stack.
|
||||||
def test_addExtruder(global_stack):
|
def test_addExtruder(global_stack):
|
||||||
mock_definition = unittest.mock.MagicMock()
|
mock_definition = unittest.mock.MagicMock()
|
||||||
|
@ -67,6 +73,7 @@ def test_addExtruder(global_stack):
|
||||||
# global_stack.addExtruder(unittest.mock.MagicMock())
|
# global_stack.addExtruder(unittest.mock.MagicMock())
|
||||||
assert len(global_stack.extruders) == 2 #Didn't add the faulty extruder.
|
assert len(global_stack.extruders) == 2 #Didn't add the faulty extruder.
|
||||||
|
|
||||||
|
|
||||||
#Tests setting user changes profiles to invalid containers.
|
#Tests setting user changes profiles to invalid containers.
|
||||||
@pytest.mark.parametrize("container", [
|
@pytest.mark.parametrize("container", [
|
||||||
getInstanceContainer(container_type = "wrong container type"),
|
getInstanceContainer(container_type = "wrong container type"),
|
||||||
|
@ -77,6 +84,7 @@ def test_constrainUserChangesInvalid(container, global_stack):
|
||||||
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
||||||
global_stack.userChanges = container
|
global_stack.userChanges = container
|
||||||
|
|
||||||
|
|
||||||
#Tests setting user changes profiles.
|
#Tests setting user changes profiles.
|
||||||
@pytest.mark.parametrize("container", [
|
@pytest.mark.parametrize("container", [
|
||||||
getInstanceContainer(container_type = "user"),
|
getInstanceContainer(container_type = "user"),
|
||||||
|
@ -85,6 +93,7 @@ def test_constrainUserChangesInvalid(container, global_stack):
|
||||||
def test_constrainUserChangesValid(container, global_stack):
|
def test_constrainUserChangesValid(container, global_stack):
|
||||||
global_stack.userChanges = container #Should not give an error.
|
global_stack.userChanges = container #Should not give an error.
|
||||||
|
|
||||||
|
|
||||||
#Tests setting quality changes profiles to invalid containers.
|
#Tests setting quality changes profiles to invalid containers.
|
||||||
@pytest.mark.parametrize("container", [
|
@pytest.mark.parametrize("container", [
|
||||||
getInstanceContainer(container_type = "wrong container type"),
|
getInstanceContainer(container_type = "wrong container type"),
|
||||||
|
@ -95,6 +104,7 @@ def test_constrainQualityChangesInvalid(container, global_stack):
|
||||||
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
||||||
global_stack.qualityChanges = container
|
global_stack.qualityChanges = container
|
||||||
|
|
||||||
|
|
||||||
#Test setting quality changes profiles.
|
#Test setting quality changes profiles.
|
||||||
@pytest.mark.parametrize("container", [
|
@pytest.mark.parametrize("container", [
|
||||||
getInstanceContainer(container_type = "quality_changes"),
|
getInstanceContainer(container_type = "quality_changes"),
|
||||||
|
@ -103,6 +113,7 @@ def test_constrainQualityChangesInvalid(container, global_stack):
|
||||||
def test_constrainQualityChangesValid(container, global_stack):
|
def test_constrainQualityChangesValid(container, global_stack):
|
||||||
global_stack.qualityChanges = container #Should not give an error.
|
global_stack.qualityChanges = container #Should not give an error.
|
||||||
|
|
||||||
|
|
||||||
#Tests setting quality profiles to invalid containers.
|
#Tests setting quality profiles to invalid containers.
|
||||||
@pytest.mark.parametrize("container", [
|
@pytest.mark.parametrize("container", [
|
||||||
getInstanceContainer(container_type = "wrong container type"),
|
getInstanceContainer(container_type = "wrong container type"),
|
||||||
|
@ -113,6 +124,7 @@ def test_constrainQualityInvalid(container, global_stack):
|
||||||
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
||||||
global_stack.quality = container
|
global_stack.quality = container
|
||||||
|
|
||||||
|
|
||||||
#Test setting quality profiles.
|
#Test setting quality profiles.
|
||||||
@pytest.mark.parametrize("container", [
|
@pytest.mark.parametrize("container", [
|
||||||
getInstanceContainer(container_type = "quality"),
|
getInstanceContainer(container_type = "quality"),
|
||||||
|
@ -121,6 +133,7 @@ def test_constrainQualityInvalid(container, global_stack):
|
||||||
def test_constrainQualityValid(container, global_stack):
|
def test_constrainQualityValid(container, global_stack):
|
||||||
global_stack.quality = container #Should not give an error.
|
global_stack.quality = container #Should not give an error.
|
||||||
|
|
||||||
|
|
||||||
#Tests setting materials to invalid containers.
|
#Tests setting materials to invalid containers.
|
||||||
@pytest.mark.parametrize("container", [
|
@pytest.mark.parametrize("container", [
|
||||||
getInstanceContainer(container_type = "wrong container type"),
|
getInstanceContainer(container_type = "wrong container type"),
|
||||||
|
@ -131,6 +144,7 @@ def test_constrainMaterialInvalid(container, global_stack):
|
||||||
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
||||||
global_stack.material = container
|
global_stack.material = container
|
||||||
|
|
||||||
|
|
||||||
#Test setting materials.
|
#Test setting materials.
|
||||||
@pytest.mark.parametrize("container", [
|
@pytest.mark.parametrize("container", [
|
||||||
getInstanceContainer(container_type = "material"),
|
getInstanceContainer(container_type = "material"),
|
||||||
|
@ -139,6 +153,7 @@ def test_constrainMaterialInvalid(container, global_stack):
|
||||||
def test_constrainMaterialValid(container, global_stack):
|
def test_constrainMaterialValid(container, global_stack):
|
||||||
global_stack.material = container #Should not give an error.
|
global_stack.material = container #Should not give an error.
|
||||||
|
|
||||||
|
|
||||||
#Tests setting variants to invalid containers.
|
#Tests setting variants to invalid containers.
|
||||||
@pytest.mark.parametrize("container", [
|
@pytest.mark.parametrize("container", [
|
||||||
getInstanceContainer(container_type = "wrong container type"),
|
getInstanceContainer(container_type = "wrong container type"),
|
||||||
|
@ -149,6 +164,7 @@ def test_constrainVariantInvalid(container, global_stack):
|
||||||
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
||||||
global_stack.variant = container
|
global_stack.variant = container
|
||||||
|
|
||||||
|
|
||||||
#Test setting variants.
|
#Test setting variants.
|
||||||
@pytest.mark.parametrize("container", [
|
@pytest.mark.parametrize("container", [
|
||||||
getInstanceContainer(container_type = "variant"),
|
getInstanceContainer(container_type = "variant"),
|
||||||
|
@ -157,6 +173,7 @@ def test_constrainVariantInvalid(container, global_stack):
|
||||||
def test_constrainVariantValid(container, global_stack):
|
def test_constrainVariantValid(container, global_stack):
|
||||||
global_stack.variant = container #Should not give an error.
|
global_stack.variant = container #Should not give an error.
|
||||||
|
|
||||||
|
|
||||||
#Tests setting definition changes profiles to invalid containers.
|
#Tests setting definition changes profiles to invalid containers.
|
||||||
@pytest.mark.parametrize("container", [
|
@pytest.mark.parametrize("container", [
|
||||||
getInstanceContainer(container_type = "wrong container type"),
|
getInstanceContainer(container_type = "wrong container type"),
|
||||||
|
@ -167,6 +184,7 @@ def test_constrainDefinitionChangesInvalid(container, global_stack):
|
||||||
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
||||||
global_stack.definitionChanges = container
|
global_stack.definitionChanges = container
|
||||||
|
|
||||||
|
|
||||||
#Test setting definition changes profiles.
|
#Test setting definition changes profiles.
|
||||||
@pytest.mark.parametrize("container", [
|
@pytest.mark.parametrize("container", [
|
||||||
getInstanceContainer(container_type = "definition_changes"),
|
getInstanceContainer(container_type = "definition_changes"),
|
||||||
|
@ -175,6 +193,7 @@ def test_constrainDefinitionChangesInvalid(container, global_stack):
|
||||||
def test_constrainDefinitionChangesValid(container, global_stack):
|
def test_constrainDefinitionChangesValid(container, global_stack):
|
||||||
global_stack.definitionChanges = container #Should not give an error.
|
global_stack.definitionChanges = container #Should not give an error.
|
||||||
|
|
||||||
|
|
||||||
#Tests setting definitions to invalid containers.
|
#Tests setting definitions to invalid containers.
|
||||||
@pytest.mark.parametrize("container", [
|
@pytest.mark.parametrize("container", [
|
||||||
getInstanceContainer(container_type = "wrong class"),
|
getInstanceContainer(container_type = "wrong class"),
|
||||||
|
@ -184,6 +203,7 @@ def test_constrainDefinitionInvalid(container, global_stack):
|
||||||
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
||||||
global_stack.definition = container
|
global_stack.definition = container
|
||||||
|
|
||||||
|
|
||||||
#Test setting definitions.
|
#Test setting definitions.
|
||||||
@pytest.mark.parametrize("container", [
|
@pytest.mark.parametrize("container", [
|
||||||
DefinitionContainer(container_id = "DefinitionContainer"),
|
DefinitionContainer(container_id = "DefinitionContainer"),
|
||||||
|
@ -192,6 +212,7 @@ def test_constrainDefinitionInvalid(container, global_stack):
|
||||||
def test_constrainDefinitionValid(container, global_stack):
|
def test_constrainDefinitionValid(container, global_stack):
|
||||||
global_stack.definition = container #Should not give an error.
|
global_stack.definition = container #Should not give an error.
|
||||||
|
|
||||||
|
|
||||||
## Tests whether deserialising completes the missing containers with empty ones. The initial containers are just the
|
## Tests whether deserialising completes the missing containers with empty ones. The initial containers are just the
|
||||||
# definition and the definition_changes (that cannot be empty after CURA-5281)
|
# definition and the definition_changes (that cannot be empty after CURA-5281)
|
||||||
def test_deserializeCompletesEmptyContainers(global_stack):
|
def test_deserializeCompletesEmptyContainers(global_stack):
|
||||||
|
@ -207,6 +228,7 @@ def test_deserializeCompletesEmptyContainers(global_stack):
|
||||||
continue
|
continue
|
||||||
assert global_stack.getContainer(container_type_index) == empty_container #All others need to be empty.
|
assert global_stack.getContainer(container_type_index) == empty_container #All others need to be empty.
|
||||||
|
|
||||||
|
|
||||||
## Tests whether an instance container with the wrong type gets removed when deserialising.
|
## Tests whether an instance container with the wrong type gets removed when deserialising.
|
||||||
def test_deserializeRemovesWrongInstanceContainer(global_stack):
|
def test_deserializeRemovesWrongInstanceContainer(global_stack):
|
||||||
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = getInstanceContainer(container_type = "wrong type")
|
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = getInstanceContainer(container_type = "wrong type")
|
||||||
|
@ -217,6 +239,7 @@ def test_deserializeRemovesWrongInstanceContainer(global_stack):
|
||||||
|
|
||||||
assert global_stack.quality == global_stack._empty_instance_container #Replaced with empty.
|
assert global_stack.quality == global_stack._empty_instance_container #Replaced with empty.
|
||||||
|
|
||||||
|
|
||||||
## Tests whether a container with the wrong class gets removed when deserialising.
|
## Tests whether a container with the wrong class gets removed when deserialising.
|
||||||
def test_deserializeRemovesWrongContainerClass(global_stack):
|
def test_deserializeRemovesWrongContainerClass(global_stack):
|
||||||
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = DefinitionContainer(container_id = "wrong class")
|
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = DefinitionContainer(container_id = "wrong class")
|
||||||
|
@ -227,6 +250,7 @@ def test_deserializeRemovesWrongContainerClass(global_stack):
|
||||||
|
|
||||||
assert global_stack.quality == global_stack._empty_instance_container #Replaced with empty.
|
assert global_stack.quality == global_stack._empty_instance_container #Replaced with empty.
|
||||||
|
|
||||||
|
|
||||||
## Tests whether an instance container in the definition spot results in an error.
|
## Tests whether an instance container in the definition spot results in an error.
|
||||||
def test_deserializeWrongDefinitionClass(global_stack):
|
def test_deserializeWrongDefinitionClass(global_stack):
|
||||||
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = getInstanceContainer(container_type = "definition") #Correct type but wrong class.
|
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = getInstanceContainer(container_type = "definition") #Correct type but wrong class.
|
||||||
|
@ -235,6 +259,7 @@ def test_deserializeWrongDefinitionClass(global_stack):
|
||||||
with pytest.raises(UM.Settings.ContainerStack.InvalidContainerStackError): #Must raise an error that there is no definition container.
|
with pytest.raises(UM.Settings.ContainerStack.InvalidContainerStackError): #Must raise an error that there is no definition container.
|
||||||
global_stack.deserialize("")
|
global_stack.deserialize("")
|
||||||
|
|
||||||
|
|
||||||
## Tests whether an instance container with the wrong type is moved into the correct slot by deserialising.
|
## Tests whether an instance container with the wrong type is moved into the correct slot by deserialising.
|
||||||
def test_deserializeMoveInstanceContainer(global_stack):
|
def test_deserializeMoveInstanceContainer(global_stack):
|
||||||
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = getInstanceContainer(container_type = "material") #Not in the correct spot.
|
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = getInstanceContainer(container_type = "material") #Not in the correct spot.
|
||||||
|
@ -246,6 +271,7 @@ def test_deserializeMoveInstanceContainer(global_stack):
|
||||||
assert global_stack.quality == empty_container
|
assert global_stack.quality == empty_container
|
||||||
assert global_stack.material != empty_container
|
assert global_stack.material != empty_container
|
||||||
|
|
||||||
|
|
||||||
## Tests whether a definition container in the wrong spot is moved into the correct spot by deserialising.
|
## Tests whether a definition container in the wrong spot is moved into the correct spot by deserialising.
|
||||||
def test_deserializeMoveDefinitionContainer(global_stack):
|
def test_deserializeMoveDefinitionContainer(global_stack):
|
||||||
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Material] = DefinitionContainer(container_id = "some definition") #Not in the correct spot.
|
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Material] = DefinitionContainer(container_id = "some definition") #Not in the correct spot.
|
||||||
|
@ -256,6 +282,7 @@ def test_deserializeMoveDefinitionContainer(global_stack):
|
||||||
assert global_stack.material == empty_container
|
assert global_stack.material == empty_container
|
||||||
assert global_stack.definition != empty_container
|
assert global_stack.definition != empty_container
|
||||||
|
|
||||||
|
|
||||||
## Tests whether getProperty properly applies the stack-like behaviour on its containers.
|
## Tests whether getProperty properly applies the stack-like behaviour on its containers.
|
||||||
def test_getPropertyFallThrough(global_stack):
|
def test_getPropertyFallThrough(global_stack):
|
||||||
#A few instance container mocks to put in the stack.
|
#A few instance container mocks to put in the stack.
|
||||||
|
@ -298,6 +325,7 @@ def test_getPropertyFallThrough(global_stack):
|
||||||
global_stack.userChanges = mock_layer_heights[container_indexes.UserChanges]
|
global_stack.userChanges = mock_layer_heights[container_indexes.UserChanges]
|
||||||
assert global_stack.getProperty("layer_height", "value") == container_indexes.UserChanges
|
assert global_stack.getProperty("layer_height", "value") == container_indexes.UserChanges
|
||||||
|
|
||||||
|
|
||||||
## In definitions, test whether having no resolve allows us to find the value.
|
## In definitions, test whether having no resolve allows us to find the value.
|
||||||
def test_getPropertyNoResolveInDefinition(global_stack):
|
def test_getPropertyNoResolveInDefinition(global_stack):
|
||||||
value = unittest.mock.MagicMock() #Just sets the value for bed temperature.
|
value = unittest.mock.MagicMock() #Just sets the value for bed temperature.
|
||||||
|
@ -307,6 +335,7 @@ def test_getPropertyNoResolveInDefinition(global_stack):
|
||||||
global_stack.definition = value
|
global_stack.definition = value
|
||||||
assert global_stack.getProperty("material_bed_temperature", "value") == 10 #No resolve, so fall through to value.
|
assert global_stack.getProperty("material_bed_temperature", "value") == 10 #No resolve, so fall through to value.
|
||||||
|
|
||||||
|
|
||||||
## In definitions, when the value is asked and there is a resolve function, it must get the resolve first.
|
## In definitions, when the value is asked and there is a resolve function, it must get the resolve first.
|
||||||
def test_getPropertyResolveInDefinition(global_stack):
|
def test_getPropertyResolveInDefinition(global_stack):
|
||||||
resolve_and_value = unittest.mock.MagicMock() #Sets the resolve and value for bed temperature.
|
resolve_and_value = unittest.mock.MagicMock() #Sets the resolve and value for bed temperature.
|
||||||
|
@ -316,6 +345,7 @@ def test_getPropertyResolveInDefinition(global_stack):
|
||||||
global_stack.definition = resolve_and_value
|
global_stack.definition = resolve_and_value
|
||||||
assert global_stack.getProperty("material_bed_temperature", "value") == 7.5 #Resolve wins in the definition.
|
assert global_stack.getProperty("material_bed_temperature", "value") == 7.5 #Resolve wins in the definition.
|
||||||
|
|
||||||
|
|
||||||
## In instance containers, when the value is asked and there is a resolve function, it must get the value first.
|
## In instance containers, when the value is asked and there is a resolve function, it must get the value first.
|
||||||
def test_getPropertyResolveInInstance(global_stack):
|
def test_getPropertyResolveInInstance(global_stack):
|
||||||
container_indices = cura.Settings.CuraContainerStack._ContainerIndexes
|
container_indices = cura.Settings.CuraContainerStack._ContainerIndexes
|
||||||
|
@ -342,6 +372,7 @@ def test_getPropertyResolveInInstance(global_stack):
|
||||||
global_stack.userChanges = instance_containers[container_indices.UserChanges]
|
global_stack.userChanges = instance_containers[container_indices.UserChanges]
|
||||||
assert global_stack.getProperty("material_bed_temperature", "value") == 5
|
assert global_stack.getProperty("material_bed_temperature", "value") == 5
|
||||||
|
|
||||||
|
|
||||||
## Tests whether the value in instances gets evaluated before the resolve in definitions.
|
## Tests whether the value in instances gets evaluated before the resolve in definitions.
|
||||||
def test_getPropertyInstancesBeforeResolve(global_stack):
|
def test_getPropertyInstancesBeforeResolve(global_stack):
|
||||||
value = unittest.mock.MagicMock() #Sets just the value.
|
value = unittest.mock.MagicMock() #Sets just the value.
|
||||||
|
@ -356,6 +387,7 @@ def test_getPropertyInstancesBeforeResolve(global_stack):
|
||||||
|
|
||||||
assert global_stack.getProperty("material_bed_temperature", "value") == 10
|
assert global_stack.getProperty("material_bed_temperature", "value") == 10
|
||||||
|
|
||||||
|
|
||||||
## Tests whether the hasUserValue returns true for settings that are changed in the user-changes container.
|
## Tests whether the hasUserValue returns true for settings that are changed in the user-changes container.
|
||||||
def test_hasUserValueUserChanges(global_stack):
|
def test_hasUserValueUserChanges(global_stack):
|
||||||
container = unittest.mock.MagicMock()
|
container = unittest.mock.MagicMock()
|
||||||
|
@ -367,6 +399,7 @@ def test_hasUserValueUserChanges(global_stack):
|
||||||
assert not global_stack.hasUserValue("infill_sparse_density")
|
assert not global_stack.hasUserValue("infill_sparse_density")
|
||||||
assert not global_stack.hasUserValue("")
|
assert not global_stack.hasUserValue("")
|
||||||
|
|
||||||
|
|
||||||
## Tests whether the hasUserValue returns true for settings that are changed in the quality-changes container.
|
## Tests whether the hasUserValue returns true for settings that are changed in the quality-changes container.
|
||||||
def test_hasUserValueQualityChanges(global_stack):
|
def test_hasUserValueQualityChanges(global_stack):
|
||||||
container = unittest.mock.MagicMock()
|
container = unittest.mock.MagicMock()
|
||||||
|
@ -378,6 +411,7 @@ def test_hasUserValueQualityChanges(global_stack):
|
||||||
assert not global_stack.hasUserValue("infill_sparse_density")
|
assert not global_stack.hasUserValue("infill_sparse_density")
|
||||||
assert not global_stack.hasUserValue("")
|
assert not global_stack.hasUserValue("")
|
||||||
|
|
||||||
|
|
||||||
## Tests whether a container in some other place on the stack is correctly not recognised as user value.
|
## Tests whether a container in some other place on the stack is correctly not recognised as user value.
|
||||||
def test_hasNoUserValue(global_stack):
|
def test_hasNoUserValue(global_stack):
|
||||||
container = unittest.mock.MagicMock()
|
container = unittest.mock.MagicMock()
|
||||||
|
@ -387,21 +421,25 @@ def test_hasNoUserValue(global_stack):
|
||||||
|
|
||||||
assert not global_stack.hasUserValue("layer_height") #However this container is quality, so it's not a user value.
|
assert not global_stack.hasUserValue("layer_height") #However this container is quality, so it's not a user value.
|
||||||
|
|
||||||
|
|
||||||
## Tests whether inserting a container is properly forbidden.
|
## Tests whether inserting a container is properly forbidden.
|
||||||
def test_insertContainer(global_stack):
|
def test_insertContainer(global_stack):
|
||||||
with pytest.raises(InvalidOperationError):
|
with pytest.raises(InvalidOperationError):
|
||||||
global_stack.insertContainer(0, unittest.mock.MagicMock())
|
global_stack.insertContainer(0, unittest.mock.MagicMock())
|
||||||
|
|
||||||
|
|
||||||
## Tests whether removing a container is properly forbidden.
|
## Tests whether removing a container is properly forbidden.
|
||||||
def test_removeContainer(global_stack):
|
def test_removeContainer(global_stack):
|
||||||
with pytest.raises(InvalidOperationError):
|
with pytest.raises(InvalidOperationError):
|
||||||
global_stack.removeContainer(unittest.mock.MagicMock())
|
global_stack.removeContainer(unittest.mock.MagicMock())
|
||||||
|
|
||||||
|
|
||||||
## Tests whether changing the next stack is properly forbidden.
|
## Tests whether changing the next stack is properly forbidden.
|
||||||
def test_setNextStack(global_stack):
|
def test_setNextStack(global_stack):
|
||||||
with pytest.raises(InvalidOperationError):
|
with pytest.raises(InvalidOperationError):
|
||||||
global_stack.setNextStack(unittest.mock.MagicMock())
|
global_stack.setNextStack(unittest.mock.MagicMock())
|
||||||
|
|
||||||
|
|
||||||
## Tests setting properties directly on the global stack.
|
## Tests setting properties directly on the global stack.
|
||||||
@pytest.mark.parametrize("key, property, value", [
|
@pytest.mark.parametrize("key, property, value", [
|
||||||
("layer_height", "value", 0.1337),
|
("layer_height", "value", 0.1337),
|
||||||
|
@ -415,6 +453,7 @@ def test_setPropertyUser(key, property, value, global_stack):
|
||||||
user_changes.getMetaDataEntry = unittest.mock.MagicMock(return_value = "user")
|
user_changes.getMetaDataEntry = unittest.mock.MagicMock(return_value = "user")
|
||||||
global_stack.userChanges = user_changes
|
global_stack.userChanges = user_changes
|
||||||
|
|
||||||
global_stack.setProperty(key, property, value) #The actual test.
|
global_stack.setProperty(key, property, value) # The actual test.
|
||||||
|
|
||||||
global_stack.userChanges.setProperty.assert_called_once_with(key, property, value, None, False) #Make sure that the user container gets a setProperty call.
|
# Make sure that the user container gets a setProperty call.
|
||||||
|
global_stack.userChanges.setProperty.assert_called_once_with(key, property, value, None, False)
|
Loading…
Add table
Add a link
Reference in a new issue