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
40
Jenkinsfile
vendored
40
Jenkinsfile
vendored
|
@ -52,12 +52,50 @@ parallel_nodes(['linux && cura', 'windows && cura']) {
|
|||
|
||||
// Try and run the unit tests. If this stage fails, we consider the build to be "unstable".
|
||||
stage('Unit Test') {
|
||||
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 {
|
||||
make('test')
|
||||
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')
|
||||
}
|
||||
}
|
||||
|
||||
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})
|
||||
add_test(
|
||||
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 "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)
|
||||
)
|
||||
|
||||
self._application.getController().getScene()._maximum_bounds = scale_to_max_bounds
|
||||
|
||||
self.updateNodeBoundaryCheck()
|
||||
|
||||
def getBoundingBox(self) -> AxisAlignedBox:
|
||||
|
|
|
@ -9,9 +9,6 @@ from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
|
|||
from UM.Logger import Logger
|
||||
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.
|
||||
|
@ -24,11 +21,11 @@ if TYPE_CHECKING:
|
|||
# This is used in Variant, Material, and Quality Managers.
|
||||
#
|
||||
class ContainerNode:
|
||||
__slots__ = ("_metadata", "container", "children_map")
|
||||
__slots__ = ("_metadata", "_container", "children_map")
|
||||
|
||||
def __init__(self, metadata: Optional[Dict[str, Any]] = None) -> None:
|
||||
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.
|
||||
|
||||
## Get an entry value from the metadata
|
||||
|
@ -50,7 +47,7 @@ class ContainerNode:
|
|||
Logger.log("e", "Cannot get container for a ContainerNode without metadata.")
|
||||
return None
|
||||
|
||||
if self.container is None:
|
||||
if self._container is None:
|
||||
container_id = self._metadata["id"]
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
container_list = ContainerRegistry.getInstance().findInstanceContainers(id = container_id)
|
||||
|
@ -59,9 +56,9 @@ class ContainerNode:
|
|||
error_message = ConfigurationErrorMessage.getInstance()
|
||||
error_message.addFaultyContainers(container_id)
|
||||
return None
|
||||
self.container = container_list[0]
|
||||
self._container = container_list[0]
|
||||
|
||||
return self.container
|
||||
return self._container
|
||||
|
||||
def __str__(self) -> str:
|
||||
return "%s[%s]" % (self.__class__.__name__, self.getMetaDataEntry("id"))
|
||||
|
|
|
@ -21,7 +21,6 @@ from .VariantType import VariantType
|
|||
|
||||
if TYPE_CHECKING:
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from cura.Settings.GlobalStack import GlobalStack
|
||||
from cura.Settings.ExtruderStack import ExtruderStack
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ class SettingVisibilityPresetsModel(ListModel):
|
|||
break
|
||||
return result
|
||||
|
||||
def _populate(self):
|
||||
def _populate(self) -> None:
|
||||
from cura.CuraApplication import CuraApplication
|
||||
items = []
|
||||
for file_path in Resources.getAllResourcesOfType(CuraApplication.ResourceTypes.SettingVisibilityPreset):
|
||||
|
@ -147,7 +147,7 @@ class SettingVisibilityPresetsModel(ListModel):
|
|||
def activePreset(self) -> str:
|
||||
return self._active_preset_item["id"]
|
||||
|
||||
def _onPreferencesChanged(self, name: str):
|
||||
def _onPreferencesChanged(self, name: str) -> None:
|
||||
if name != "general/visible_settings":
|
||||
return
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# 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 .QualityChangesGroup import QualityChangesGroup
|
||||
|
@ -12,21 +12,21 @@ from .QualityChangesGroup import QualityChangesGroup
|
|||
#
|
||||
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)
|
||||
self.quality_type_map = {} # type: Dict[str, QualityNode] # quality_type -> QualityNode for InstanceContainer
|
||||
|
||||
def getChildNode(self, child_key: str) -> Optional["QualityNode"]:
|
||||
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:
|
||||
self.quality_type_map[quality_type] = QualityNode(metadata)
|
||||
|
||||
def getQualityNode(self, quality_type: str) -> Optional["QualityNode"]:
|
||||
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:
|
||||
self.quality_type_map[quality_type] = QualityNode()
|
||||
quality_type_node = self.quality_type_map[quality_type]
|
||||
|
|
|
@ -13,20 +13,20 @@ class ConfigurationModel(QObject):
|
|||
|
||||
configurationChanged = pyqtSignal()
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self._printer_type = None
|
||||
self._printer_type = ""
|
||||
self._extruder_configurations = [] # type: List[ExtruderConfigurationModel]
|
||||
self._buildplate_configuration = None
|
||||
self._buildplate_configuration = ""
|
||||
|
||||
def setPrinterType(self, printer_type):
|
||||
self._printer_type = printer_type
|
||||
|
||||
@pyqtProperty(str, fset = setPrinterType, notify = configurationChanged)
|
||||
def printerType(self):
|
||||
def printerType(self) -> str:
|
||||
return self._printer_type
|
||||
|
||||
def setExtruderConfigurations(self, extruder_configurations):
|
||||
def setExtruderConfigurations(self, extruder_configurations: List["ExtruderConfigurationModel"]):
|
||||
if self._extruder_configurations != extruder_configurations:
|
||||
self._extruder_configurations = extruder_configurations
|
||||
|
||||
|
@ -39,16 +39,16 @@ class ConfigurationModel(QObject):
|
|||
def extruderConfigurations(self):
|
||||
return self._extruder_configurations
|
||||
|
||||
def setBuildplateConfiguration(self, buildplate_configuration):
|
||||
def setBuildplateConfiguration(self, buildplate_configuration: str) -> None:
|
||||
self._buildplate_configuration = buildplate_configuration
|
||||
|
||||
@pyqtProperty(str, fset = setBuildplateConfiguration, notify = configurationChanged)
|
||||
def buildplateConfiguration(self):
|
||||
def buildplateConfiguration(self) -> str:
|
||||
return self._buildplate_configuration
|
||||
|
||||
## This method is intended to indicate whether the configuration is valid or not.
|
||||
# The method checks if the mandatory fields are or not set
|
||||
def isValid(self):
|
||||
def isValid(self) -> bool:
|
||||
if not self._extruder_configurations:
|
||||
return False
|
||||
for configuration in self._extruder_configurations:
|
||||
|
|
|
@ -53,21 +53,8 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
|
|||
self._sending_gcode = False
|
||||
self._compressing_gcode = False
|
||||
self._gcode = [] # type: List[str]
|
||||
|
||||
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:
|
||||
raise NotImplementedError("requestWrite needs to be implemented")
|
||||
|
||||
|
@ -341,7 +328,7 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
|
|||
|
||||
@pyqtProperty(str, constant = True)
|
||||
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
|
||||
@pyqtProperty(str, constant = True)
|
||||
|
|
|
@ -13,23 +13,31 @@ from cura.Scene import ConvexHullNode
|
|||
|
||||
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.
|
||||
# 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):
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
self._convex_hull_node = None
|
||||
self._convex_hull_node = None # type: Optional["SceneNode"]
|
||||
self._init2DConvexHullCache()
|
||||
|
||||
self._global_stack = None
|
||||
self._global_stack = None # type: Optional[GlobalStack]
|
||||
|
||||
# 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]
|
||||
|
||||
if Application.getInstance() is not None:
|
||||
Application.getInstance().callLater(self.createRecomputeConvexHullTimer)
|
||||
|
||||
self._raft_thickness = 0.0
|
||||
# For raft thickness, DRY
|
||||
self._build_volume = Application.getInstance().getBuildVolume()
|
||||
self._build_volume.raftThicknessChanged.connect(self._onChanged)
|
||||
|
||||
|
@ -39,13 +47,13 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||
|
||||
self._onGlobalStackChanged()
|
||||
|
||||
def createRecomputeConvexHullTimer(self):
|
||||
def createRecomputeConvexHullTimer(self) -> None:
|
||||
self._recompute_convex_hull_timer = QTimer()
|
||||
self._recompute_convex_hull_timer.setInterval(200)
|
||||
self._recompute_convex_hull_timer.setSingleShot(True)
|
||||
self._recompute_convex_hull_timer.timeout.connect(self.recomputeConvexHull)
|
||||
|
||||
def setNode(self, node):
|
||||
def setNode(self, node: "SceneNode") -> None:
|
||||
previous_node = self._node
|
||||
# Disconnect from previous node signals
|
||||
if previous_node is not None and node is not previous_node:
|
||||
|
@ -63,14 +71,14 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||
def __deepcopy__(self, memo):
|
||||
return ConvexHullDecorator()
|
||||
|
||||
## Get the unmodified 2D projected convex hull of the node
|
||||
def getConvexHull(self):
|
||||
## Get the unmodified 2D projected convex hull of the node (if any)
|
||||
def getConvexHull(self) -> Optional[Polygon]:
|
||||
if self._node is None:
|
||||
return None
|
||||
|
||||
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.
|
||||
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)))
|
||||
|
@ -78,7 +86,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||
return hull
|
||||
|
||||
## 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:
|
||||
return None
|
||||
|
||||
|
@ -87,7 +95,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||
## Get convex hull of the object + head size
|
||||
# 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
|
||||
def getConvexHullHead(self):
|
||||
def getConvexHullHead(self) -> Optional[Polygon]:
|
||||
if self._node is None:
|
||||
return None
|
||||
|
||||
|
@ -101,7 +109,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||
## Get convex hull of the node
|
||||
# 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.
|
||||
def getConvexHullBoundary(self):
|
||||
def getConvexHullBoundary(self) -> Optional[Polygon]:
|
||||
if self._node is None:
|
||||
return None
|
||||
|
||||
|
@ -111,13 +119,14 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||
return self._compute2DConvexHull()
|
||||
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:
|
||||
self._recompute_convex_hull_timer.start()
|
||||
else:
|
||||
self.recomputeConvexHull()
|
||||
|
||||
def recomputeConvexHull(self):
|
||||
def recomputeConvexHull(self) -> None:
|
||||
controller = Application.getInstance().getController()
|
||||
root = controller.getScene().getRoot()
|
||||
if self._node is None or controller.isToolOperationActive() or not self.__isDescendant(root, self._node):
|
||||
|
@ -132,7 +141,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||
hull_node = ConvexHullNode.ConvexHullNode(self._node, convex_hull, self._raft_thickness, root)
|
||||
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.
|
||||
return
|
||||
|
||||
|
@ -142,7 +151,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||
self._init2DConvexHullCache() # Invalidate the cache.
|
||||
self._onChanged()
|
||||
|
||||
def _init2DConvexHullCache(self):
|
||||
def _init2DConvexHullCache(self) -> None:
|
||||
# Cache for the group code path in _compute2DConvexHull()
|
||||
self._2d_convex_hull_group_child_polygon = 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_result = None
|
||||
|
||||
def _compute2DConvexHull(self):
|
||||
def _compute2DConvexHull(self) -> Optional[Polygon]:
|
||||
if self._node.callDecoration("isGroup"):
|
||||
points = numpy.zeros((0, 2), dtype=numpy.int32)
|
||||
for child in self._node.getChildren():
|
||||
|
@ -179,8 +188,6 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||
|
||||
else:
|
||||
offset_hull = None
|
||||
mesh = None
|
||||
world_transform = None
|
||||
if self._node.getMeshData():
|
||||
mesh = self._node.getMeshData()
|
||||
world_transform = self._node.getWorldTransformation()
|
||||
|
@ -228,24 +235,33 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||
|
||||
return offset_hull
|
||||
|
||||
def _getHeadAndFans(self):
|
||||
def _getHeadAndFans(self) -> Polygon:
|
||||
if self._global_stack:
|
||||
return Polygon(numpy.array(self._global_stack.getHeadAndFansCoordinates(), numpy.float32))
|
||||
return Polygon()
|
||||
|
||||
def _compute2DConvexHeadFull(self):
|
||||
return self._compute2DConvexHull().getMinkowskiHull(self._getHeadAndFans())
|
||||
def _compute2DConvexHeadFull(self) -> Optional[Polygon]:
|
||||
convex_hull = self._compute2DConvexHeadFull()
|
||||
if convex_hull:
|
||||
return convex_hull.getMinkowskiHull(self._getHeadAndFans())
|
||||
return None
|
||||
|
||||
def _compute2DConvexHeadMin(self):
|
||||
headAndFans = self._getHeadAndFans()
|
||||
mirrored = headAndFans.mirror([0, 0], [0, 1]).mirror([0, 0], [1, 0]) # Mirror horizontally & vertically.
|
||||
def _compute2DConvexHeadMin(self) -> Optional[Polygon]:
|
||||
head_and_fans = self._getHeadAndFans()
|
||||
mirrored = head_and_fans.mirror([0, 0], [0, 1]).mirror([0, 0], [1, 0]) # Mirror horizontally & vertically.
|
||||
head_and_fans = self._getHeadAndFans().intersectionConvexHulls(mirrored)
|
||||
|
||||
# Min head hull is used for the push free
|
||||
min_head_hull = self._compute2DConvexHull().getMinkowskiHull(head_and_fans)
|
||||
return min_head_hull
|
||||
convex_hull = self._compute2DConvexHeadFull()
|
||||
if convex_hull:
|
||||
return convex_hull.getMinkowskiHull(head_and_fans)
|
||||
return None
|
||||
|
||||
## Compensate given 2D polygon with adhesion 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
|
||||
# Add extra margin depending on adhesion type
|
||||
adhesion_type = self._global_stack.getProperty("adhesion_type", "value")
|
||||
|
@ -263,7 +279,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||
else:
|
||||
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:
|
||||
extra_margin_polygon = Polygon.approximatedCircle(extra_margin)
|
||||
poly = poly.getMinkowskiHull(extra_margin_polygon)
|
||||
|
@ -274,7 +290,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||
# \param convex_hull Polygon of the original convex hull.
|
||||
# \return New Polygon instance that is offset with everything that
|
||||
# influences the collision area.
|
||||
def _offsetHull(self, convex_hull):
|
||||
def _offsetHull(self, convex_hull: Polygon) -> Polygon:
|
||||
horizontal_expansion = max(
|
||||
self._getSettingProperty("xy_offset", "value"),
|
||||
self._getSettingProperty("xy_offset_layer_0", "value")
|
||||
|
@ -295,12 +311,12 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||
else:
|
||||
return convex_hull
|
||||
|
||||
def _onChanged(self, *args):
|
||||
def _onChanged(self, *args) -> None:
|
||||
self._raft_thickness = self._build_volume.getRaftThickness()
|
||||
if not args or args[0] == self._node:
|
||||
self.recomputeConvexHullDelayed()
|
||||
|
||||
def _onGlobalStackChanged(self):
|
||||
def _onGlobalStackChanged(self) -> None:
|
||||
if self._global_stack:
|
||||
self._global_stack.propertyChanged.disconnect(self._onSettingValueChanged)
|
||||
self._global_stack.containersChanged.disconnect(self._onChanged)
|
||||
|
@ -321,7 +337,9 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||
self._onChanged()
|
||||
|
||||
## 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")
|
||||
if per_mesh_stack:
|
||||
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
|
||||
return self._global_stack.getProperty(setting_key, prop)
|
||||
|
||||
## Returns true if node is a descendant or the same as the root node.
|
||||
def __isDescendant(self, root, node):
|
||||
## Returns True if node is a descendant or the same as the root node.
|
||||
def __isDescendant(self, root: "SceneNode", node: "SceneNode") -> bool:
|
||||
if node is None:
|
||||
return False
|
||||
if root is node:
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
|
||||
from typing import List
|
||||
|
||||
|
||||
class GCodeListDecorator(SceneNodeDecorator):
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self._gcode_list = []
|
||||
self._gcode_list = [] # type: List[str]
|
||||
|
||||
def getGCodeList(self):
|
||||
def getGCodeList(self) -> List[str]:
|
||||
return self._gcode_list
|
||||
|
||||
def setGCodeList(self, list):
|
||||
def setGCodeList(self, list: List[str]):
|
||||
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):
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
def isSliceable(self):
|
||||
def isSliceable(self) -> bool:
|
||||
return True
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
def __deepcopy__(self, memo) -> "SliceableObjectDecorator":
|
||||
return type(self)()
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
|
||||
|
||||
|
||||
## A decorator that stores the amount an object has been moved below the platform.
|
||||
class ZOffsetDecorator(SceneNodeDecorator):
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self._z_offset = 0
|
||||
self._z_offset = 0.
|
||||
|
||||
def setZOffset(self, offset):
|
||||
def setZOffset(self, offset: float) -> None:
|
||||
self._z_offset = offset
|
||||
|
||||
def getZOffset(self):
|
||||
def getZOffset(self) -> float:
|
||||
return self._z_offset
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
def __deepcopy__(self, memo) -> "ZOffsetDecorator":
|
||||
copied_decorator = ZOffsetDecorator()
|
||||
copied_decorator.setZOffset(self.getZOffset())
|
||||
return copied_decorator
|
||||
|
|
|
@ -23,6 +23,7 @@ from .CuraContainerStack import CuraContainerStack
|
|||
if TYPE_CHECKING:
|
||||
from cura.Settings.ExtruderStack import ExtruderStack
|
||||
|
||||
|
||||
## Represents the Global or Machine stack and its related containers.
|
||||
#
|
||||
class GlobalStack(CuraContainerStack):
|
||||
|
|
|
@ -260,6 +260,19 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
|
|||
# or "Legacy" UM3 device.
|
||||
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:
|
||||
device = ClusterUM3OutputDevice.ClusterUM3OutputDevice(name, address, properties)
|
||||
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
|
||||
|
||||
|
||||
## Gets an instance container with a specified container type.
|
||||
#
|
||||
# \param container_type The type metadata for the instance container.
|
||||
|
@ -24,22 +25,27 @@ def getInstanceContainer(container_type) -> InstanceContainer:
|
|||
container.setMetaDataEntry("type", container_type)
|
||||
return container
|
||||
|
||||
|
||||
class DefinitionContainerSubClass(DefinitionContainer):
|
||||
def __init__(self):
|
||||
super().__init__(container_id = "SubDefinitionContainer")
|
||||
|
||||
|
||||
class InstanceContainerSubClass(InstanceContainer):
|
||||
def __init__(self, container_type):
|
||||
super().__init__(container_id = "SubInstanceContainer")
|
||||
self.setMetaDataEntry("type", container_type)
|
||||
|
||||
|
||||
#############################START OF TEST CASES################################
|
||||
|
||||
|
||||
## Tests whether adding a container is properly forbidden.
|
||||
def test_addContainer(global_stack):
|
||||
with pytest.raises(InvalidOperationError):
|
||||
global_stack.addContainer(unittest.mock.MagicMock())
|
||||
|
||||
|
||||
## Tests adding extruders to the global stack.
|
||||
def test_addExtruder(global_stack):
|
||||
mock_definition = unittest.mock.MagicMock()
|
||||
|
@ -67,6 +73,7 @@ def test_addExtruder(global_stack):
|
|||
# global_stack.addExtruder(unittest.mock.MagicMock())
|
||||
assert len(global_stack.extruders) == 2 #Didn't add the faulty extruder.
|
||||
|
||||
|
||||
#Tests setting user changes profiles to invalid containers.
|
||||
@pytest.mark.parametrize("container", [
|
||||
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.
|
||||
global_stack.userChanges = container
|
||||
|
||||
|
||||
#Tests setting user changes profiles.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "user"),
|
||||
|
@ -85,6 +93,7 @@ def test_constrainUserChangesInvalid(container, global_stack):
|
|||
def test_constrainUserChangesValid(container, global_stack):
|
||||
global_stack.userChanges = container #Should not give an error.
|
||||
|
||||
|
||||
#Tests setting quality changes profiles to invalid containers.
|
||||
@pytest.mark.parametrize("container", [
|
||||
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.
|
||||
global_stack.qualityChanges = container
|
||||
|
||||
|
||||
#Test setting quality changes profiles.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "quality_changes"),
|
||||
|
@ -103,6 +113,7 @@ def test_constrainQualityChangesInvalid(container, global_stack):
|
|||
def test_constrainQualityChangesValid(container, global_stack):
|
||||
global_stack.qualityChanges = container #Should not give an error.
|
||||
|
||||
|
||||
#Tests setting quality profiles to invalid containers.
|
||||
@pytest.mark.parametrize("container", [
|
||||
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.
|
||||
global_stack.quality = container
|
||||
|
||||
|
||||
#Test setting quality profiles.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "quality"),
|
||||
|
@ -121,6 +133,7 @@ def test_constrainQualityInvalid(container, global_stack):
|
|||
def test_constrainQualityValid(container, global_stack):
|
||||
global_stack.quality = container #Should not give an error.
|
||||
|
||||
|
||||
#Tests setting materials to invalid containers.
|
||||
@pytest.mark.parametrize("container", [
|
||||
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.
|
||||
global_stack.material = container
|
||||
|
||||
|
||||
#Test setting materials.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "material"),
|
||||
|
@ -139,6 +153,7 @@ def test_constrainMaterialInvalid(container, global_stack):
|
|||
def test_constrainMaterialValid(container, global_stack):
|
||||
global_stack.material = container #Should not give an error.
|
||||
|
||||
|
||||
#Tests setting variants to invalid containers.
|
||||
@pytest.mark.parametrize("container", [
|
||||
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.
|
||||
global_stack.variant = container
|
||||
|
||||
|
||||
#Test setting variants.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "variant"),
|
||||
|
@ -157,6 +173,7 @@ def test_constrainVariantInvalid(container, global_stack):
|
|||
def test_constrainVariantValid(container, global_stack):
|
||||
global_stack.variant = container #Should not give an error.
|
||||
|
||||
|
||||
#Tests setting definition changes profiles to invalid containers.
|
||||
@pytest.mark.parametrize("container", [
|
||||
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.
|
||||
global_stack.definitionChanges = container
|
||||
|
||||
|
||||
#Test setting definition changes profiles.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "definition_changes"),
|
||||
|
@ -175,6 +193,7 @@ def test_constrainDefinitionChangesInvalid(container, global_stack):
|
|||
def test_constrainDefinitionChangesValid(container, global_stack):
|
||||
global_stack.definitionChanges = container #Should not give an error.
|
||||
|
||||
|
||||
#Tests setting definitions to invalid containers.
|
||||
@pytest.mark.parametrize("container", [
|
||||
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.
|
||||
global_stack.definition = container
|
||||
|
||||
|
||||
#Test setting definitions.
|
||||
@pytest.mark.parametrize("container", [
|
||||
DefinitionContainer(container_id = "DefinitionContainer"),
|
||||
|
@ -192,6 +212,7 @@ def test_constrainDefinitionInvalid(container, global_stack):
|
|||
def test_constrainDefinitionValid(container, global_stack):
|
||||
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
|
||||
# definition and the definition_changes (that cannot be empty after CURA-5281)
|
||||
def test_deserializeCompletesEmptyContainers(global_stack):
|
||||
|
@ -207,6 +228,7 @@ def test_deserializeCompletesEmptyContainers(global_stack):
|
|||
continue
|
||||
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.
|
||||
def test_deserializeRemovesWrongInstanceContainer(global_stack):
|
||||
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.
|
||||
|
||||
|
||||
## Tests whether a container with the wrong class gets removed when deserialising.
|
||||
def test_deserializeRemovesWrongContainerClass(global_stack):
|
||||
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.
|
||||
|
||||
|
||||
## Tests whether an instance container in the definition spot results in an error.
|
||||
def test_deserializeWrongDefinitionClass(global_stack):
|
||||
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.
|
||||
global_stack.deserialize("")
|
||||
|
||||
|
||||
## Tests whether an instance container with the wrong type is moved into the correct slot by deserialising.
|
||||
def test_deserializeMoveInstanceContainer(global_stack):
|
||||
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.material != empty_container
|
||||
|
||||
|
||||
## Tests whether a definition container in the wrong spot is moved into the correct spot by deserialising.
|
||||
def test_deserializeMoveDefinitionContainer(global_stack):
|
||||
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.definition != empty_container
|
||||
|
||||
|
||||
## Tests whether getProperty properly applies the stack-like behaviour on its containers.
|
||||
def test_getPropertyFallThrough(global_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]
|
||||
assert global_stack.getProperty("layer_height", "value") == container_indexes.UserChanges
|
||||
|
||||
|
||||
## In definitions, test whether having no resolve allows us to find the value.
|
||||
def test_getPropertyNoResolveInDefinition(global_stack):
|
||||
value = unittest.mock.MagicMock() #Just sets the value for bed temperature.
|
||||
|
@ -307,6 +335,7 @@ def test_getPropertyNoResolveInDefinition(global_stack):
|
|||
global_stack.definition = 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.
|
||||
def test_getPropertyResolveInDefinition(global_stack):
|
||||
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
|
||||
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.
|
||||
def test_getPropertyResolveInInstance(global_stack):
|
||||
container_indices = cura.Settings.CuraContainerStack._ContainerIndexes
|
||||
|
@ -342,6 +372,7 @@ def test_getPropertyResolveInInstance(global_stack):
|
|||
global_stack.userChanges = instance_containers[container_indices.UserChanges]
|
||||
assert global_stack.getProperty("material_bed_temperature", "value") == 5
|
||||
|
||||
|
||||
## Tests whether the value in instances gets evaluated before the resolve in definitions.
|
||||
def test_getPropertyInstancesBeforeResolve(global_stack):
|
||||
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
|
||||
|
||||
|
||||
## Tests whether the hasUserValue returns true for settings that are changed in the user-changes container.
|
||||
def test_hasUserValueUserChanges(global_stack):
|
||||
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("")
|
||||
|
||||
|
||||
## Tests whether the hasUserValue returns true for settings that are changed in the quality-changes container.
|
||||
def test_hasUserValueQualityChanges(global_stack):
|
||||
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("")
|
||||
|
||||
|
||||
## Tests whether a container in some other place on the stack is correctly not recognised as user value.
|
||||
def test_hasNoUserValue(global_stack):
|
||||
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.
|
||||
|
||||
|
||||
## Tests whether inserting a container is properly forbidden.
|
||||
def test_insertContainer(global_stack):
|
||||
with pytest.raises(InvalidOperationError):
|
||||
global_stack.insertContainer(0, unittest.mock.MagicMock())
|
||||
|
||||
|
||||
## Tests whether removing a container is properly forbidden.
|
||||
def test_removeContainer(global_stack):
|
||||
with pytest.raises(InvalidOperationError):
|
||||
global_stack.removeContainer(unittest.mock.MagicMock())
|
||||
|
||||
|
||||
## Tests whether changing the next stack is properly forbidden.
|
||||
def test_setNextStack(global_stack):
|
||||
with pytest.raises(InvalidOperationError):
|
||||
global_stack.setNextStack(unittest.mock.MagicMock())
|
||||
|
||||
|
||||
## Tests setting properties directly on the global stack.
|
||||
@pytest.mark.parametrize("key, property, value", [
|
||||
("layer_height", "value", 0.1337),
|
||||
|
@ -417,4 +455,5 @@ def test_setPropertyUser(key, property, value, global_stack):
|
|||
|
||||
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