mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-06-04 11:14:21 +08:00
Merge branch 'master' of github.com:Ultimaker/Cura into cura_connect_UI_rework
This commit is contained in:
commit
8c2cd510d8
1
.gitignore
vendored
1
.gitignore
vendored
@ -15,6 +15,7 @@ LC_MESSAGES
|
||||
.cache
|
||||
*.qmlc
|
||||
.mypy_cache
|
||||
.pytest_cache
|
||||
|
||||
#MacOS
|
||||
.DS_Store
|
||||
|
@ -242,6 +242,8 @@ class BuildVolume(SceneNode):
|
||||
|
||||
# Mark the node as outside build volume if the set extruder is disabled
|
||||
extruder_position = node.callDecoration("getActiveExtruderPosition")
|
||||
if extruder_position not in self._global_container_stack.extruders:
|
||||
continue
|
||||
if not self._global_container_stack.extruders[extruder_position].isEnabled:
|
||||
node.setOutsideBuildArea(True)
|
||||
continue
|
||||
@ -561,7 +563,9 @@ class BuildVolume(SceneNode):
|
||||
|
||||
camera = Application.getInstance().getController().getCameraTool()
|
||||
if camera:
|
||||
camera.setZoomRange(min = 1, max = self.getDiagonalSize() * 5) #You can zoom out up to 5 times the diagonal. This gives some space around the volume.
|
||||
diagonal = self.getDiagonalSize()
|
||||
if diagonal > 1:
|
||||
camera.setZoomRange(min = 0.1, max = diagonal * 5) #You can zoom out up to 5 times the diagonal. This gives some space around the volume.
|
||||
|
||||
def _onEngineCreated(self):
|
||||
self._engine_ready = True
|
||||
|
@ -1,11 +1,10 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import copy
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from typing import cast, TYPE_CHECKING, Optional
|
||||
from typing import cast, TYPE_CHECKING
|
||||
|
||||
import numpy
|
||||
|
||||
@ -106,6 +105,7 @@ from cura.Settings.ExtrudersModel import ExtrudersModel
|
||||
from cura.Settings.MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler
|
||||
from cura.Settings.ContainerManager import ContainerManager
|
||||
from cura.Settings.SidebarCustomMenuItemsModel import SidebarCustomMenuItemsModel
|
||||
import cura.Settings.cura_empty_instance_containers
|
||||
|
||||
from cura.ObjectsModel import ObjectsModel
|
||||
|
||||
@ -216,7 +216,6 @@ class CuraApplication(QtApplication):
|
||||
|
||||
self._message_box_callback = None
|
||||
self._message_box_callback_arguments = []
|
||||
self._preferred_mimetype = ""
|
||||
self._i18n_catalog = None
|
||||
|
||||
self._currently_loading_files = []
|
||||
@ -369,42 +368,23 @@ class CuraApplication(QtApplication):
|
||||
# Add empty variant, material and quality containers.
|
||||
# Since they are empty, they should never be serialized and instead just programmatically created.
|
||||
# We need them to simplify the switching between materials.
|
||||
empty_container = self._container_registry.getEmptyInstanceContainer()
|
||||
self.empty_container = empty_container
|
||||
self.empty_container = cura.Settings.cura_empty_instance_containers.empty_container
|
||||
|
||||
empty_definition_changes_container = copy.deepcopy(empty_container)
|
||||
empty_definition_changes_container.setMetaDataEntry("id", "empty_definition_changes")
|
||||
empty_definition_changes_container.setMetaDataEntry("type", "definition_changes")
|
||||
self._container_registry.addContainer(empty_definition_changes_container)
|
||||
self.empty_definition_changes_container = empty_definition_changes_container
|
||||
self._container_registry.addContainer(
|
||||
cura.Settings.cura_empty_instance_containers.empty_definition_changes_container)
|
||||
self.empty_definition_changes_container = cura.Settings.cura_empty_instance_containers.empty_definition_changes_container
|
||||
|
||||
empty_variant_container = copy.deepcopy(empty_container)
|
||||
empty_variant_container.setMetaDataEntry("id", "empty_variant")
|
||||
empty_variant_container.setMetaDataEntry("type", "variant")
|
||||
self._container_registry.addContainer(empty_variant_container)
|
||||
self.empty_variant_container = empty_variant_container
|
||||
self._container_registry.addContainer(cura.Settings.cura_empty_instance_containers.empty_variant_container)
|
||||
self.empty_variant_container = cura.Settings.cura_empty_instance_containers.empty_variant_container
|
||||
|
||||
empty_material_container = copy.deepcopy(empty_container)
|
||||
empty_material_container.setMetaDataEntry("id", "empty_material")
|
||||
empty_material_container.setMetaDataEntry("type", "material")
|
||||
self._container_registry.addContainer(empty_material_container)
|
||||
self.empty_material_container = empty_material_container
|
||||
self._container_registry.addContainer(cura.Settings.cura_empty_instance_containers.empty_material_container)
|
||||
self.empty_material_container = cura.Settings.cura_empty_instance_containers.empty_material_container
|
||||
|
||||
empty_quality_container = copy.deepcopy(empty_container)
|
||||
empty_quality_container.setMetaDataEntry("id", "empty_quality")
|
||||
empty_quality_container.setName("Not Supported")
|
||||
empty_quality_container.setMetaDataEntry("quality_type", "not_supported")
|
||||
empty_quality_container.setMetaDataEntry("type", "quality")
|
||||
empty_quality_container.setMetaDataEntry("supported", False)
|
||||
self._container_registry.addContainer(empty_quality_container)
|
||||
self.empty_quality_container = empty_quality_container
|
||||
self._container_registry.addContainer(cura.Settings.cura_empty_instance_containers.empty_quality_container)
|
||||
self.empty_quality_container = cura.Settings.cura_empty_instance_containers.empty_quality_container
|
||||
|
||||
empty_quality_changes_container = copy.deepcopy(empty_container)
|
||||
empty_quality_changes_container.setMetaDataEntry("id", "empty_quality_changes")
|
||||
empty_quality_changes_container.setMetaDataEntry("type", "quality_changes")
|
||||
empty_quality_changes_container.setMetaDataEntry("quality_type", "not_supported")
|
||||
self._container_registry.addContainer(empty_quality_changes_container)
|
||||
self.empty_quality_changes_container = empty_quality_changes_container
|
||||
self._container_registry.addContainer(cura.Settings.cura_empty_instance_containers.empty_quality_changes_container)
|
||||
self.empty_quality_changes_container = cura.Settings.cura_empty_instance_containers.empty_quality_changes_container
|
||||
|
||||
# Initializes the version upgrade manager with by providing the paths for each resource type and the latest
|
||||
# versions.
|
||||
@ -515,9 +495,6 @@ class CuraApplication(QtApplication):
|
||||
self.applicationShuttingDown.connect(self.saveSettings)
|
||||
self.engineCreatedSignal.connect(self._onEngineCreated)
|
||||
|
||||
self.globalContainerStackChanged.connect(self._onGlobalContainerChanged)
|
||||
self._onGlobalContainerChanged()
|
||||
|
||||
self.getCuraSceneController().setActiveBuildPlate(0) # Initialize
|
||||
|
||||
CuraApplication.Created = True
|
||||
@ -782,7 +759,10 @@ class CuraApplication(QtApplication):
|
||||
# Initialize camera
|
||||
root = controller.getScene().getRoot()
|
||||
camera = Camera("3d", root)
|
||||
camera.setPosition(Vector(-80, 250, 700) * self.getBuildVolume().getDiagonalSize() / 375)
|
||||
diagonal = self.getBuildVolume().getDiagonalSize()
|
||||
if diagonal < 1: #No printer added yet. Set a default camera distance for normal-sized printers.
|
||||
diagonal = 375
|
||||
camera.setPosition(Vector(-80, 250, 700) * diagonal / 375)
|
||||
camera.setPerspective(True)
|
||||
camera.lookAt(Vector(0, 0, 0))
|
||||
controller.getScene().setActiveCamera("3d")
|
||||
@ -999,30 +979,14 @@ class CuraApplication(QtApplication):
|
||||
self._camera_animation.setTarget(Selection.getSelectedObject(0).getWorldPosition())
|
||||
self._camera_animation.start()
|
||||
|
||||
def _onGlobalContainerChanged(self):
|
||||
if self._global_container_stack is not None:
|
||||
machine_file_formats = [file_type.strip() for file_type in self._global_container_stack.getMetaDataEntry("file_formats").split(";")]
|
||||
new_preferred_mimetype = ""
|
||||
if machine_file_formats:
|
||||
new_preferred_mimetype = machine_file_formats[0]
|
||||
|
||||
if new_preferred_mimetype != self._preferred_mimetype:
|
||||
self._preferred_mimetype = new_preferred_mimetype
|
||||
self.preferredOutputMimetypeChanged.emit()
|
||||
|
||||
requestAddPrinter = pyqtSignal()
|
||||
activityChanged = pyqtSignal()
|
||||
sceneBoundingBoxChanged = pyqtSignal()
|
||||
preferredOutputMimetypeChanged = pyqtSignal()
|
||||
|
||||
@pyqtProperty(bool, notify = activityChanged)
|
||||
def platformActivity(self):
|
||||
return self._platform_activity
|
||||
|
||||
@pyqtProperty(str, notify=preferredOutputMimetypeChanged)
|
||||
def preferredOutputMimetype(self):
|
||||
return self._preferred_mimetype
|
||||
|
||||
@pyqtProperty(str, notify = sceneBoundingBoxChanged)
|
||||
def getSceneBoundingBoxString(self):
|
||||
return self._i18n_catalog.i18nc("@info 'width', 'depth' and 'height' are variable names that must NOT be translated; just translate the format of ##x##x## mm.", "%(width).1f x %(depth).1f x %(height).1f mm") % {'width' : self._scene_bounding_box.width.item(), 'depth': self._scene_bounding_box.depth.item(), 'height' : self._scene_bounding_box.height.item()}
|
||||
|
@ -76,6 +76,8 @@ class OneAtATimeIterator(Iterator):
|
||||
continue
|
||||
|
||||
bounding_box = node.getBoundingBox()
|
||||
if not bounding_box:
|
||||
continue
|
||||
from UM.Math.Polygon import Polygon
|
||||
bounding_box_polygon = Polygon([[bounding_box.left, bounding_box.front],
|
||||
[bounding_box.left, bounding_box.back],
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from typing import Any, cast, List, Optional, Union
|
||||
from typing import Any, cast, List, Optional
|
||||
from PyQt5.QtCore import pyqtProperty, pyqtSignal, QObject
|
||||
|
||||
from UM.Application import Application
|
||||
@ -13,6 +13,7 @@ from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.Settings.Interfaces import ContainerInterface, DefinitionContainerInterface
|
||||
from cura.Settings import cura_empty_instance_containers
|
||||
|
||||
from . import Exceptions
|
||||
|
||||
@ -39,14 +40,12 @@ class CuraContainerStack(ContainerStack):
|
||||
def __init__(self, container_id: str) -> None:
|
||||
super().__init__(container_id)
|
||||
|
||||
self._container_registry = ContainerRegistry.getInstance() #type: ContainerRegistry
|
||||
self._empty_instance_container = cura_empty_instance_containers.empty_container #type: InstanceContainer
|
||||
|
||||
self._empty_instance_container = self._container_registry.getEmptyInstanceContainer() #type: InstanceContainer
|
||||
|
||||
self._empty_quality_changes = self._container_registry.findInstanceContainers(id = "empty_quality_changes")[0] #type: InstanceContainer
|
||||
self._empty_quality = self._container_registry.findInstanceContainers(id = "empty_quality")[0] #type: InstanceContainer
|
||||
self._empty_material = self._container_registry.findInstanceContainers(id = "empty_material")[0] #type: InstanceContainer
|
||||
self._empty_variant = self._container_registry.findInstanceContainers(id = "empty_variant")[0] #type: InstanceContainer
|
||||
self._empty_quality_changes = cura_empty_instance_containers.empty_quality_changes_container #type: InstanceContainer
|
||||
self._empty_quality = cura_empty_instance_containers.empty_quality_container #type: InstanceContainer
|
||||
self._empty_material = cura_empty_instance_containers.empty_material_container #type: InstanceContainer
|
||||
self._empty_variant = cura_empty_instance_containers.empty_variant_container #type: InstanceContainer
|
||||
|
||||
self._containers = [self._empty_instance_container for i in range(len(_ContainerIndexes.IndexTypeMap))] #type: List[ContainerInterface]
|
||||
self._containers[_ContainerIndexes.QualityChanges] = self._empty_quality_changes
|
||||
|
@ -108,16 +108,27 @@ class CuraStackBuilder:
|
||||
|
||||
preferred_quality_type = machine_definition.getMetaDataEntry("preferred_quality_type")
|
||||
quality_group_dict = quality_manager.getQualityGroups(new_global_stack)
|
||||
quality_group = quality_group_dict.get(preferred_quality_type)
|
||||
|
||||
new_global_stack.quality = quality_group.node_for_global.getContainer()
|
||||
if not new_global_stack.quality:
|
||||
if not quality_group_dict:
|
||||
# There is no available quality group, set all quality containers to empty.
|
||||
new_global_stack.quality = application.empty_quality_container
|
||||
for position, extruder_stack in new_global_stack.extruders.items():
|
||||
if position in quality_group.nodes_for_extruders and quality_group.nodes_for_extruders[position].getContainer():
|
||||
extruder_stack.quality = quality_group.nodes_for_extruders[position].getContainer()
|
||||
else:
|
||||
for extruder_stack in new_global_stack.extruders.values():
|
||||
extruder_stack.quality = application.empty_quality_container
|
||||
else:
|
||||
# Set the quality containers to the preferred quality type if available, otherwise use the first quality
|
||||
# type that's available.
|
||||
if preferred_quality_type not in quality_group_dict:
|
||||
Logger.log("w", "The preferred quality {quality_type} doesn't exist for this set-up. Choosing a random one.".format(quality_type = preferred_quality_type))
|
||||
preferred_quality_type = next(iter(quality_group_dict))
|
||||
quality_group = quality_group_dict.get(preferred_quality_type)
|
||||
|
||||
new_global_stack.quality = quality_group.node_for_global.getContainer()
|
||||
if not new_global_stack.quality:
|
||||
new_global_stack.quality = application.empty_quality_container
|
||||
for position, extruder_stack in new_global_stack.extruders.items():
|
||||
if position in quality_group.nodes_for_extruders and quality_group.nodes_for_extruders[position].getContainer():
|
||||
extruder_stack.quality = quality_group.nodes_for_extruders[position].getContainer()
|
||||
else:
|
||||
extruder_stack.quality = application.empty_quality_container
|
||||
|
||||
# Register the global stack after the extruder stacks are created. This prevents the registry from adding another
|
||||
# extruder stack because the global stack didn't have one yet (which is enforced since Cura 3.1).
|
||||
|
@ -139,9 +139,6 @@ class ExtruderStack(CuraContainerStack):
|
||||
super().deserialize(contents, file_name)
|
||||
if "enabled" not in self.getMetaData():
|
||||
self.setMetaDataEntry("enabled", "True")
|
||||
stacks = ContainerRegistry.getInstance().findContainerStacks(id=self.getMetaDataEntry("machine", ""))
|
||||
if stacks:
|
||||
self.setNextStack(stacks[0])
|
||||
|
||||
def _onPropertiesChanged(self, key: str, properties: Dict[str, Any]) -> None:
|
||||
# When there is a setting that is not settable per extruder that depends on a value from a setting that is,
|
||||
|
@ -61,6 +61,10 @@ class GlobalStack(CuraContainerStack):
|
||||
name = self.variant.getName()
|
||||
return name
|
||||
|
||||
@pyqtProperty(str, constant = True)
|
||||
def preferred_output_file_formats(self) -> str:
|
||||
return self.getMetaDataEntry("file_formats")
|
||||
|
||||
## Add an extruder to the list of extruders of this stack.
|
||||
#
|
||||
# \param extruder The extruder to add.
|
||||
|
56
cura/Settings/cura_empty_instance_containers.py
Normal file
56
cura/Settings/cura_empty_instance_containers.py
Normal file
@ -0,0 +1,56 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import copy
|
||||
|
||||
from UM.Settings.constant_instance_containers import EMPTY_CONTAINER_ID, empty_container
|
||||
|
||||
|
||||
# Empty definition changes
|
||||
EMPTY_DEFINITION_CHANGES_CONTAINER_ID = "empty_definition_changes"
|
||||
empty_definition_changes_container = copy.deepcopy(empty_container)
|
||||
empty_definition_changes_container.setMetaDataEntry("id", EMPTY_DEFINITION_CHANGES_CONTAINER_ID)
|
||||
empty_definition_changes_container.setMetaDataEntry("type", "definition_changes")
|
||||
|
||||
# Empty variant
|
||||
EMPTY_VARIANT_CONTAINER_ID = "empty_variant"
|
||||
empty_variant_container = copy.deepcopy(empty_container)
|
||||
empty_variant_container.setMetaDataEntry("id", EMPTY_VARIANT_CONTAINER_ID)
|
||||
empty_variant_container.setMetaDataEntry("type", "variant")
|
||||
|
||||
# Empty material
|
||||
EMPTY_MATERIAL_CONTAINER_ID = "empty_material"
|
||||
empty_material_container = copy.deepcopy(empty_container)
|
||||
empty_material_container.setMetaDataEntry("id", EMPTY_MATERIAL_CONTAINER_ID)
|
||||
empty_material_container.setMetaDataEntry("type", "material")
|
||||
|
||||
# Empty quality
|
||||
EMPTY_QUALITY_CONTAINER_ID = "empty_quality"
|
||||
empty_quality_container = copy.deepcopy(empty_container)
|
||||
empty_quality_container.setMetaDataEntry("id", EMPTY_QUALITY_CONTAINER_ID)
|
||||
empty_quality_container.setName("Not Supported")
|
||||
empty_quality_container.setMetaDataEntry("quality_type", "not_supported")
|
||||
empty_quality_container.setMetaDataEntry("type", "quality")
|
||||
empty_quality_container.setMetaDataEntry("supported", False)
|
||||
|
||||
# Empty quality changes
|
||||
EMPTY_QUALITY_CHANGES_CONTAINER_ID = "empty_quality_changes"
|
||||
empty_quality_changes_container = copy.deepcopy(empty_container)
|
||||
empty_quality_changes_container.setMetaDataEntry("id", EMPTY_QUALITY_CHANGES_CONTAINER_ID)
|
||||
empty_quality_changes_container.setMetaDataEntry("type", "quality_changes")
|
||||
empty_quality_changes_container.setMetaDataEntry("quality_type", "not_supported")
|
||||
|
||||
|
||||
__all__ = ["EMPTY_CONTAINER_ID",
|
||||
"empty_container", # For convenience
|
||||
"EMPTY_DEFINITION_CHANGES_CONTAINER_ID",
|
||||
"empty_definition_changes_container",
|
||||
"EMPTY_VARIANT_CONTAINER_ID",
|
||||
"empty_variant_container",
|
||||
"EMPTY_MATERIAL_CONTAINER_ID",
|
||||
"empty_material_container",
|
||||
"EMPTY_QUALITY_CHANGES_CONTAINER_ID",
|
||||
"empty_quality_changes_container",
|
||||
"EMPTY_QUALITY_CONTAINER_ID",
|
||||
"empty_quality_container"
|
||||
]
|
@ -630,6 +630,11 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||
type = "extruder_train")
|
||||
extruder_stack_dict = {stack.getMetaDataEntry("position"): stack for stack in extruder_stacks}
|
||||
|
||||
# Make sure that those extruders have the global stack as the next stack or later some value evaluation
|
||||
# will fail.
|
||||
for stack in extruder_stacks:
|
||||
stack.setNextStack(global_stack, connect_signals = False)
|
||||
|
||||
Logger.log("d", "Workspace loading is checking definitions...")
|
||||
# Get all the definition files & check if they exist. If not, add them.
|
||||
definition_container_files = [name for name in cura_file_names if name.endswith(self._definition_container_suffix)]
|
||||
|
@ -17,6 +17,10 @@ catalog = i18nCatalog("cura")
|
||||
#
|
||||
# If you're zipping g-code, you might as well use gzip!
|
||||
class GCodeGzWriter(MeshWriter):
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__(add_to_recent_files = False)
|
||||
|
||||
## Writes the gzipped g-code to a stream.
|
||||
#
|
||||
# Note that even though the function accepts a collection of nodes, the
|
||||
|
@ -16,7 +16,8 @@ def getMetaData():
|
||||
"extension": file_extension,
|
||||
"description": catalog.i18nc("@item:inlistbox", "Compressed G-code File"),
|
||||
"mime_type": "application/gzip",
|
||||
"mode": GCodeGzWriter.GCodeGzWriter.OutputMode.BinaryMode
|
||||
"mode": GCodeGzWriter.GCodeGzWriter.OutputMode.BinaryMode,
|
||||
"hide_in_file_dialog": True,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ class GCodeWriter(MeshWriter):
|
||||
_setting_keyword = ";SETTING_"
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
super().__init__(add_to_recent_files = False)
|
||||
|
||||
self._application = Application.getInstance()
|
||||
|
||||
|
@ -28,7 +28,7 @@ class PauseAtHeight(Script):
|
||||
"pause_height":
|
||||
{
|
||||
"label": "Pause Height",
|
||||
"description": "At what height should the pause occur",
|
||||
"description": "At what height should the pause occur?",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"default_value": 5.0,
|
||||
@ -39,7 +39,7 @@ class PauseAtHeight(Script):
|
||||
"pause_layer":
|
||||
{
|
||||
"label": "Pause Layer",
|
||||
"description": "At what layer should the pause occur",
|
||||
"description": "At what layer should the pause occur?",
|
||||
"type": "int",
|
||||
"value": "math.floor((pause_height - 0.27) / 0.1) + 1",
|
||||
"minimum_value": "0",
|
||||
@ -142,13 +142,14 @@ class PauseAtHeight(Script):
|
||||
standby_temperature = self.getSettingValueByKey("standby_temperature")
|
||||
firmware_retract = Application.getInstance().getGlobalContainerStack().getProperty("machine_firmware_retract", "value")
|
||||
control_temperatures = Application.getInstance().getGlobalContainerStack().getProperty("machine_nozzle_temp_enabled", "value")
|
||||
initial_layer_height = Application.getInstance().getGlobalContainerStack().getProperty("layer_height_0", "value")
|
||||
|
||||
is_griffin = False
|
||||
|
||||
# T = ExtruderManager.getInstance().getActiveExtruderStack().getProperty("material_print_temperature", "value")
|
||||
|
||||
# use offset to calculate the current height: <current_height> = <current_z> - <layer_0_z>
|
||||
layer_0_z = 0.
|
||||
layer_0_z = 0
|
||||
current_z = 0
|
||||
got_first_g_cmd_on_layer_0 = False
|
||||
current_t = 0 #Tracks the current extruder for tracking the target temperature.
|
||||
@ -195,11 +196,10 @@ class PauseAtHeight(Script):
|
||||
# This block is executed once, the first time there is a G
|
||||
# command, to get the z offset (z for first positive layer)
|
||||
if not got_first_g_cmd_on_layer_0:
|
||||
layer_0_z = current_z
|
||||
layer_0_z = current_z - initial_layer_height
|
||||
got_first_g_cmd_on_layer_0 = True
|
||||
|
||||
current_height = current_z - layer_0_z
|
||||
|
||||
if current_height < pause_height:
|
||||
break # Try the next layer.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2017 Ultimaker B.V.
|
||||
// Copyright (c) 2018 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.4
|
||||
@ -12,30 +12,43 @@ import Cura 1.0 as Cura
|
||||
Item
|
||||
{
|
||||
id: base
|
||||
width: {
|
||||
if (UM.SimulationView.compatibilityMode) {
|
||||
width:
|
||||
{
|
||||
if (UM.SimulationView.compatibilityMode)
|
||||
{
|
||||
return UM.Theme.getSize("layerview_menu_size_compatibility").width;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return UM.Theme.getSize("layerview_menu_size").width;
|
||||
}
|
||||
}
|
||||
height: {
|
||||
if (viewSettings.collapsed) {
|
||||
if (UM.SimulationView.compatibilityMode) {
|
||||
if (viewSettings.collapsed)
|
||||
{
|
||||
if (UM.SimulationView.compatibilityMode)
|
||||
{
|
||||
return UM.Theme.getSize("layerview_menu_size_compatibility_collapsed").height;
|
||||
}
|
||||
return UM.Theme.getSize("layerview_menu_size_collapsed").height;
|
||||
} else if (UM.SimulationView.compatibilityMode) {
|
||||
}
|
||||
else if (UM.SimulationView.compatibilityMode)
|
||||
{
|
||||
return UM.Theme.getSize("layerview_menu_size_compatibility").height;
|
||||
} else if (UM.Preferences.getValue("layerview/layer_view_type") == 0) {
|
||||
}
|
||||
else if (UM.Preferences.getValue("layerview/layer_view_type") == 0)
|
||||
{
|
||||
return UM.Theme.getSize("layerview_menu_size_material_color_mode").height + UM.SimulationView.extruderCount * (UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("layerview_row_spacing").height)
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return UM.Theme.getSize("layerview_menu_size").height + UM.SimulationView.extruderCount * (UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("layerview_row_spacing").height)
|
||||
}
|
||||
}
|
||||
Behavior on height { NumberAnimation { duration: 100 } }
|
||||
|
||||
property var buttonTarget: {
|
||||
property var buttonTarget:
|
||||
{
|
||||
if(parent != null)
|
||||
{
|
||||
var force_binding = parent.y; // ensure this gets reevaluated when the panel moves
|
||||
@ -44,7 +57,8 @@ Item
|
||||
return Qt.point(0,0)
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Rectangle
|
||||
{
|
||||
id: layerViewMenu
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
@ -83,7 +97,8 @@ Item
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
ColumnLayout
|
||||
{
|
||||
id: viewSettings
|
||||
|
||||
property bool collapsed: false
|
||||
@ -195,7 +210,8 @@ Item
|
||||
width: width
|
||||
}
|
||||
|
||||
Connections {
|
||||
Connections
|
||||
{
|
||||
target: UM.Preferences
|
||||
onPreferenceChanged:
|
||||
{
|
||||
@ -212,18 +228,22 @@ Item
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
Repeater
|
||||
{
|
||||
model: Cura.ExtrudersModel{}
|
||||
CheckBox {
|
||||
CheckBox
|
||||
{
|
||||
id: extrudersModelCheckBox
|
||||
checked: viewSettings.extruder_opacities[index] > 0.5 || viewSettings.extruder_opacities[index] == undefined || viewSettings.extruder_opacities[index] == ""
|
||||
onClicked: {
|
||||
onClicked:
|
||||
{
|
||||
viewSettings.extruder_opacities[index] = checked ? 1.0 : 0.0
|
||||
UM.Preferences.setValue("layerview/extruder_opacities", viewSettings.extruder_opacities.join("|"));
|
||||
}
|
||||
visible: !UM.SimulationView.compatibilityMode
|
||||
enabled: index + 1 <= 4
|
||||
Rectangle {
|
||||
Rectangle
|
||||
{
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: extrudersModelCheckBox.right
|
||||
width: UM.Theme.getSize("layerview_legend_size").width
|
||||
@ -253,8 +273,10 @@ Item
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: ListModel {
|
||||
Repeater
|
||||
{
|
||||
model: ListModel
|
||||
{
|
||||
id: typesLegendModel
|
||||
Component.onCompleted:
|
||||
{
|
||||
@ -285,13 +307,16 @@ Item
|
||||
}
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
CheckBox
|
||||
{
|
||||
id: legendModelCheckBox
|
||||
checked: model.initialValue
|
||||
onClicked: {
|
||||
onClicked:
|
||||
{
|
||||
UM.Preferences.setValue(model.preference, checked);
|
||||
}
|
||||
Rectangle {
|
||||
Rectangle
|
||||
{
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: legendModelCheckBox.right
|
||||
width: UM.Theme.getSize("layerview_legend_size").width
|
||||
@ -320,18 +345,22 @@ Item
|
||||
}
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
CheckBox
|
||||
{
|
||||
checked: viewSettings.only_show_top_layers
|
||||
onClicked: {
|
||||
onClicked:
|
||||
{
|
||||
UM.Preferences.setValue("view/only_show_top_layers", checked ? 1.0 : 0.0);
|
||||
}
|
||||
text: catalog.i18nc("@label", "Only Show Top Layers")
|
||||
visible: UM.SimulationView.compatibilityMode
|
||||
style: UM.Theme.styles.checkbox
|
||||
}
|
||||
CheckBox {
|
||||
CheckBox
|
||||
{
|
||||
checked: viewSettings.top_layer_count == 5
|
||||
onClicked: {
|
||||
onClicked:
|
||||
{
|
||||
UM.Preferences.setValue("view/top_layer_count", checked ? 5 : 1);
|
||||
}
|
||||
text: catalog.i18nc("@label", "Show 5 Detailed Layers On Top")
|
||||
@ -339,8 +368,10 @@ Item
|
||||
style: UM.Theme.styles.checkbox
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: ListModel {
|
||||
Repeater
|
||||
{
|
||||
model: ListModel
|
||||
{
|
||||
id: typesLegendModelNoCheck
|
||||
Component.onCompleted:
|
||||
{
|
||||
@ -355,11 +386,13 @@ Item
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Label
|
||||
{
|
||||
text: label
|
||||
visible: viewSettings.show_legend
|
||||
id: typesLegendModelLabel
|
||||
Rectangle {
|
||||
Rectangle
|
||||
{
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: typesLegendModelLabel.right
|
||||
width: UM.Theme.getSize("layerview_legend_size").width
|
||||
@ -378,30 +411,37 @@ Item
|
||||
}
|
||||
|
||||
// Text for the minimum, maximum and units for the feedrates and layer thickness
|
||||
Item {
|
||||
Item
|
||||
{
|
||||
id: gradientLegend
|
||||
visible: viewSettings.show_gradient
|
||||
width: parent.width
|
||||
height: UM.Theme.getSize("layerview_row").height
|
||||
anchors {
|
||||
anchors
|
||||
{
|
||||
topMargin: UM.Theme.getSize("slider_layerview_margin").height
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
Label {
|
||||
Label
|
||||
{
|
||||
text: minText()
|
||||
anchors.left: parent.left
|
||||
color: UM.Theme.getColor("setting_control_text")
|
||||
font: UM.Theme.getFont("default")
|
||||
|
||||
function minText() {
|
||||
if (UM.SimulationView.layerActivity && CuraApplication.platformActivity) {
|
||||
function minText()
|
||||
{
|
||||
if (UM.SimulationView.layerActivity && CuraApplication.platformActivity)
|
||||
{
|
||||
// Feedrate selected
|
||||
if (UM.Preferences.getValue("layerview/layer_view_type") == 2) {
|
||||
if (UM.Preferences.getValue("layerview/layer_view_type") == 2)
|
||||
{
|
||||
return parseFloat(UM.SimulationView.getMinFeedrate()).toFixed(2)
|
||||
}
|
||||
// Layer thickness selected
|
||||
if (UM.Preferences.getValue("layerview/layer_view_type") == 3) {
|
||||
if (UM.Preferences.getValue("layerview/layer_view_type") == 3)
|
||||
{
|
||||
return parseFloat(UM.SimulationView.getMinThickness()).toFixed(2)
|
||||
}
|
||||
}
|
||||
@ -409,20 +449,25 @@ Item
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Label
|
||||
{
|
||||
text: unitsText()
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: UM.Theme.getColor("setting_control_text")
|
||||
font: UM.Theme.getFont("default")
|
||||
|
||||
function unitsText() {
|
||||
if (UM.SimulationView.layerActivity && CuraApplication.platformActivity) {
|
||||
function unitsText()
|
||||
{
|
||||
if (UM.SimulationView.layerActivity && CuraApplication.platformActivity)
|
||||
{
|
||||
// Feedrate selected
|
||||
if (UM.Preferences.getValue("layerview/layer_view_type") == 2) {
|
||||
if (UM.Preferences.getValue("layerview/layer_view_type") == 2)
|
||||
{
|
||||
return "mm/s"
|
||||
}
|
||||
// Layer thickness selected
|
||||
if (UM.Preferences.getValue("layerview/layer_view_type") == 3) {
|
||||
if (UM.Preferences.getValue("layerview/layer_view_type") == 3)
|
||||
{
|
||||
return "mm"
|
||||
}
|
||||
}
|
||||
@ -430,20 +475,25 @@ Item
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Label
|
||||
{
|
||||
text: maxText()
|
||||
anchors.right: parent.right
|
||||
color: UM.Theme.getColor("setting_control_text")
|
||||
font: UM.Theme.getFont("default")
|
||||
|
||||
function maxText() {
|
||||
if (UM.SimulationView.layerActivity && CuraApplication.platformActivity) {
|
||||
function maxText()
|
||||
{
|
||||
if (UM.SimulationView.layerActivity && CuraApplication.platformActivity)
|
||||
{
|
||||
// Feedrate selected
|
||||
if (UM.Preferences.getValue("layerview/layer_view_type") == 2) {
|
||||
if (UM.Preferences.getValue("layerview/layer_view_type") == 2)
|
||||
{
|
||||
return parseFloat(UM.SimulationView.getMaxFeedrate()).toFixed(2)
|
||||
}
|
||||
// Layer thickness selected
|
||||
if (UM.Preferences.getValue("layerview/layer_view_type") == 3) {
|
||||
if (UM.Preferences.getValue("layerview/layer_view_type") == 3)
|
||||
{
|
||||
return parseFloat(UM.SimulationView.getMaxThickness()).toFixed(2)
|
||||
}
|
||||
}
|
||||
@ -453,7 +503,8 @@ Item
|
||||
}
|
||||
|
||||
// Gradient colors for feedrate
|
||||
Rectangle { // In QML 5.9 can be changed by LinearGradient
|
||||
Rectangle
|
||||
{ // In QML 5.9 can be changed by LinearGradient
|
||||
// Invert values because then the bar is rotated 90 degrees
|
||||
id: feedrateGradient
|
||||
visible: viewSettings.show_feedrate_gradient
|
||||
@ -463,20 +514,25 @@ Item
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
border.color: UM.Theme.getColor("lining")
|
||||
transform: Rotation {origin.x: 0; origin.y: 0; angle: 90}
|
||||
gradient: Gradient {
|
||||
GradientStop {
|
||||
gradient: Gradient
|
||||
{
|
||||
GradientStop
|
||||
{
|
||||
position: 0.000
|
||||
color: Qt.rgba(1, 0.5, 0, 1)
|
||||
}
|
||||
GradientStop {
|
||||
GradientStop
|
||||
{
|
||||
position: 0.625
|
||||
color: Qt.rgba(0.375, 0.5, 0, 1)
|
||||
}
|
||||
GradientStop {
|
||||
GradientStop
|
||||
{
|
||||
position: 0.75
|
||||
color: Qt.rgba(0.25, 1, 0, 1)
|
||||
}
|
||||
GradientStop {
|
||||
GradientStop
|
||||
{
|
||||
position: 1.0
|
||||
color: Qt.rgba(0, 0, 1, 1)
|
||||
}
|
||||
@ -484,7 +540,8 @@ Item
|
||||
}
|
||||
|
||||
// Gradient colors for layer thickness (similar to parula colormap)
|
||||
Rectangle { // In QML 5.9 can be changed by LinearGradient
|
||||
Rectangle // In QML 5.9 can be changed by LinearGradient
|
||||
{
|
||||
// Invert values because then the bar is rotated 90 degrees
|
||||
id: thicknessGradient
|
||||
visible: viewSettings.show_thickness_gradient
|
||||
@ -494,24 +551,30 @@ Item
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
border.color: UM.Theme.getColor("lining")
|
||||
transform: Rotation {origin.x: 0; origin.y: 0; angle: 90}
|
||||
gradient: Gradient {
|
||||
GradientStop {
|
||||
gradient: Gradient
|
||||
{
|
||||
GradientStop
|
||||
{
|
||||
position: 0.000
|
||||
color: Qt.rgba(1, 1, 0, 1)
|
||||
}
|
||||
GradientStop {
|
||||
GradientStop
|
||||
{
|
||||
position: 0.25
|
||||
color: Qt.rgba(1, 0.75, 0.25, 1)
|
||||
}
|
||||
GradientStop {
|
||||
GradientStop
|
||||
{
|
||||
position: 0.5
|
||||
color: Qt.rgba(0, 0.75, 0.5, 1)
|
||||
}
|
||||
GradientStop {
|
||||
GradientStop
|
||||
{
|
||||
position: 0.75
|
||||
color: Qt.rgba(0, 0.375, 0.75, 1)
|
||||
}
|
||||
GradientStop {
|
||||
GradientStop
|
||||
{
|
||||
position: 1.0
|
||||
color: Qt.rgba(0, 0, 0.5, 1)
|
||||
}
|
||||
@ -520,19 +583,22 @@ Item
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Item
|
||||
{
|
||||
id: slidersBox
|
||||
|
||||
width: parent.width
|
||||
visible: UM.SimulationView.layerActivity && CuraApplication.platformActivity
|
||||
|
||||
anchors {
|
||||
anchors
|
||||
{
|
||||
top: parent.bottom
|
||||
topMargin: UM.Theme.getSize("slider_layerview_margin").height
|
||||
left: parent.left
|
||||
}
|
||||
|
||||
PathSlider {
|
||||
PathSlider
|
||||
{
|
||||
id: pathSlider
|
||||
|
||||
height: UM.Theme.getSize("slider_handle").width
|
||||
@ -553,25 +619,29 @@ Item
|
||||
rangeColor: UM.Theme.getColor("slider_groove_fill")
|
||||
|
||||
// update values when layer data changes
|
||||
Connections {
|
||||
Connections
|
||||
{
|
||||
target: UM.SimulationView
|
||||
onMaxPathsChanged: pathSlider.setHandleValue(UM.SimulationView.currentPath)
|
||||
onCurrentPathChanged: pathSlider.setHandleValue(UM.SimulationView.currentPath)
|
||||
}
|
||||
|
||||
// make sure the slider handlers show the correct value after switching views
|
||||
Component.onCompleted: {
|
||||
Component.onCompleted:
|
||||
{
|
||||
pathSlider.setHandleValue(UM.SimulationView.currentPath)
|
||||
}
|
||||
}
|
||||
|
||||
LayerSlider {
|
||||
LayerSlider
|
||||
{
|
||||
id: layerSlider
|
||||
|
||||
width: UM.Theme.getSize("slider_handle").width
|
||||
height: UM.Theme.getSize("layerview_menu_size").height
|
||||
|
||||
anchors {
|
||||
anchors
|
||||
{
|
||||
top: !UM.SimulationView.compatibilityMode ? pathSlider.bottom : parent.top
|
||||
topMargin: !UM.SimulationView.compatibilityMode ? UM.Theme.getSize("default_margin").height : 0
|
||||
right: parent.right
|
||||
@ -593,7 +663,8 @@ Item
|
||||
handleLabelWidth: UM.Theme.getSize("slider_layerview_background").width
|
||||
|
||||
// update values when layer data changes
|
||||
Connections {
|
||||
Connections
|
||||
{
|
||||
target: UM.SimulationView
|
||||
onMaxLayersChanged: layerSlider.setUpperValue(UM.SimulationView.currentLayer)
|
||||
onMinimumLayerChanged: layerSlider.setLowerValue(UM.SimulationView.minimumLayer)
|
||||
@ -601,45 +672,54 @@ Item
|
||||
}
|
||||
|
||||
// make sure the slider handlers show the correct value after switching views
|
||||
Component.onCompleted: {
|
||||
Component.onCompleted:
|
||||
{
|
||||
layerSlider.setLowerValue(UM.SimulationView.minimumLayer)
|
||||
layerSlider.setUpperValue(UM.SimulationView.currentLayer)
|
||||
}
|
||||
}
|
||||
|
||||
// Play simulation button
|
||||
Button {
|
||||
Button
|
||||
{
|
||||
id: playButton
|
||||
iconSource: "./resources/simulation_resume.svg"
|
||||
style: UM.Theme.styles.small_tool_button
|
||||
visible: !UM.SimulationView.compatibilityMode
|
||||
anchors {
|
||||
anchors
|
||||
{
|
||||
verticalCenter: pathSlider.verticalCenter
|
||||
}
|
||||
|
||||
property var status: 0 // indicates if it's stopped (0) or playing (1)
|
||||
|
||||
onClicked: {
|
||||
switch(status) {
|
||||
case 0: {
|
||||
onClicked:
|
||||
{
|
||||
switch(status)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
resumeSimulation()
|
||||
break
|
||||
}
|
||||
case 1: {
|
||||
case 1:
|
||||
{
|
||||
pauseSimulation()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function pauseSimulation() {
|
||||
function pauseSimulation()
|
||||
{
|
||||
UM.SimulationView.setSimulationRunning(false)
|
||||
iconSource = "./resources/simulation_resume.svg"
|
||||
simulationTimer.stop()
|
||||
status = 0
|
||||
}
|
||||
|
||||
function resumeSimulation() {
|
||||
function resumeSimulation()
|
||||
{
|
||||
UM.SimulationView.setSimulationRunning(true)
|
||||
iconSource = "./resources/simulation_pause.svg"
|
||||
simulationTimer.start()
|
||||
@ -652,7 +732,8 @@ Item
|
||||
interval: 100
|
||||
running: false
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
onTriggered:
|
||||
{
|
||||
var currentPath = UM.SimulationView.currentPath
|
||||
var numPaths = UM.SimulationView.numPaths
|
||||
var currentLayer = UM.SimulationView.currentLayer
|
||||
@ -697,7 +778,8 @@ Item
|
||||
}
|
||||
}
|
||||
|
||||
FontMetrics {
|
||||
FontMetrics
|
||||
{
|
||||
id: fontMetrics
|
||||
font: UM.Theme.getFont("default")
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ catalog = i18nCatalog("cura")
|
||||
|
||||
class UFPWriter(MeshWriter):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
super().__init__(add_to_recent_files = False)
|
||||
self._snapshot = None
|
||||
Application.getInstance().getOutputDeviceManager().writeStarted.connect(self._createSnapshot)
|
||||
|
||||
|
@ -8,6 +8,9 @@ from UM.VersionUpgrade import VersionUpgrade
|
||||
|
||||
deleted_settings = {"prime_tower_wall_thickness", "dual_pre_wipe", "prime_tower_purge_volume"}
|
||||
|
||||
changed_settings = {'retraction_combing': 'noskin'}
|
||||
updated_settings = {'retraction_combing': 'infill'}
|
||||
|
||||
_RENAMED_MATERIAL_PROFILES = {
|
||||
"dsm_arnitel2045_175_cartesio_0.25_mm": "dsm_arnitel2045_175_cartesio_0.25mm_thermoplastic_extruder",
|
||||
"dsm_arnitel2045_175_cartesio_0.4_mm": "dsm_arnitel2045_175_cartesio_0.4mm_thermoplastic_extruder",
|
||||
@ -127,6 +130,13 @@ class VersionUpgrade34to40(VersionUpgrade):
|
||||
continue
|
||||
del parser["values"][deleted_setting]
|
||||
|
||||
for setting_key in changed_settings:
|
||||
if setting_key not in parser["values"]:
|
||||
continue
|
||||
|
||||
if parser["values"][setting_key] == changed_settings[setting_key]:
|
||||
parser["values"][setting_key] = updated_settings[setting_key]
|
||||
|
||||
result = io.StringIO()
|
||||
parser.write(result)
|
||||
return [filename], [result.getvalue()]
|
||||
|
@ -3356,13 +3356,14 @@
|
||||
"retraction_combing":
|
||||
{
|
||||
"label": "Combing Mode",
|
||||
"description": "Combing keeps the nozzle within already printed areas when traveling. This results in slightly longer travel moves but reduces the need for retractions. If combing is off, the material will retract and the nozzle moves in a straight line to the next point. It is also possible to avoid combing over top/bottom skin areas by combing within the infill only.",
|
||||
"description": "Combing keeps the nozzle within already printed areas when traveling. This results in slightly longer travel moves but reduces the need for retractions. If combing is off, the material will retract and the nozzle moves in a straight line to the next point. It is also possible to avoid combing over top/bottom skin areas and also to only comb within the infill. Note that the 'Within Infill' option behaves exactly like the 'Not in Skin' option in earlier Cura releases.",
|
||||
"type": "enum",
|
||||
"options":
|
||||
{
|
||||
"off": "Off",
|
||||
"all": "All",
|
||||
"noskin": "Not in Skin"
|
||||
"noskin": "Not in Skin",
|
||||
"infill": "Within Infill"
|
||||
},
|
||||
"default_value": "all",
|
||||
"resolve": "'noskin' if 'noskin' in extruderValues('retraction_combing') else ('all' if 'all' in extruderValues('retraction_combing') else 'off')",
|
||||
@ -5073,7 +5074,7 @@
|
||||
"unit": "mm",
|
||||
"enabled": "resolveOrValue('prime_tower_enable')",
|
||||
"default_value": 200,
|
||||
"value": "machine_width - max(extruderValue(adhesion_extruder_nr, 'brim_width') * extruderValue(adhesion_extruder_nr, 'initial_layer_line_width_factor') / 100 if adhesion_type == 'brim' else (extruderValue(adhesion_extruder_nr, 'raft_margin') if adhesion_type == 'raft' else (extruderValue(adhesion_extruder_nr, 'skirt_gap') if adhesion_type == 'skirt' else 0)), max(extruderValues('travel_avoid_distance'))) - max(extruderValues('support_offset')) - sum(extruderValues('skirt_brim_line_width')) * extruderValue(adhesion_extruder_nr, 'initial_layer_line_width_factor') / 100 - 1",
|
||||
"value": "machine_width - max(extruderValue(adhesion_extruder_nr, 'brim_width') * extruderValue(adhesion_extruder_nr, 'initial_layer_line_width_factor') / 100 if adhesion_type == 'brim' else (extruderValue(adhesion_extruder_nr, 'raft_margin') if adhesion_type == 'raft' else (extruderValue(adhesion_extruder_nr, 'skirt_gap') if adhesion_type == 'skirt' else 0)), max(extruderValues('travel_avoid_distance'))) - max(extruderValues('support_offset')) - sum(extruderValues('skirt_brim_line_width')) * extruderValue(adhesion_extruder_nr, 'initial_layer_line_width_factor') / 100 - (resolveOrValue('draft_shield_dist') if resolveOrValue('draft_shield_enabled') else 0) - 1",
|
||||
"maximum_value": "machine_width / 2 if machine_center_is_zero else machine_width",
|
||||
"minimum_value": "resolveOrValue('prime_tower_size') - machine_width / 2 if machine_center_is_zero else resolveOrValue('prime_tower_size')",
|
||||
"settable_per_mesh": false,
|
||||
@ -5087,7 +5088,7 @@
|
||||
"unit": "mm",
|
||||
"enabled": "resolveOrValue('prime_tower_enable')",
|
||||
"default_value": 200,
|
||||
"value": "machine_depth - prime_tower_size - max(extruderValue(adhesion_extruder_nr, 'brim_width') * extruderValue(adhesion_extruder_nr, 'initial_layer_line_width_factor') / 100 if adhesion_type == 'brim' else (extruderValue(adhesion_extruder_nr, 'raft_margin') if adhesion_type == 'raft' else (extruderValue(adhesion_extruder_nr, 'skirt_gap') if adhesion_type == 'skirt' else 0)), max(extruderValues('travel_avoid_distance'))) - max(extruderValues('support_offset')) - sum(extruderValues('skirt_brim_line_width')) * extruderValue(adhesion_extruder_nr, 'initial_layer_line_width_factor') / 100 - 1",
|
||||
"value": "machine_depth - prime_tower_size - max(extruderValue(adhesion_extruder_nr, 'brim_width') * extruderValue(adhesion_extruder_nr, 'initial_layer_line_width_factor') / 100 if adhesion_type == 'brim' else (extruderValue(adhesion_extruder_nr, 'raft_margin') if adhesion_type == 'raft' else (extruderValue(adhesion_extruder_nr, 'skirt_gap') if adhesion_type == 'skirt' else 0)), max(extruderValues('travel_avoid_distance'))) - max(extruderValues('support_offset')) - sum(extruderValues('skirt_brim_line_width')) * extruderValue(adhesion_extruder_nr, 'initial_layer_line_width_factor') / 100 - (resolveOrValue('draft_shield_dist') if resolveOrValue('draft_shield_enabled') else 0) - 1",
|
||||
"maximum_value": "machine_depth / 2 - resolveOrValue('prime_tower_size') if machine_center_is_zero else machine_depth - resolveOrValue('prime_tower_size')",
|
||||
"minimum_value": "machine_depth / -2 if machine_center_is_zero else 0",
|
||||
"settable_per_mesh": false,
|
||||
@ -5626,6 +5627,19 @@
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"minimum_polygon_circumference":
|
||||
{
|
||||
"label": "Minimum Polygon Circumference",
|
||||
"description": "Polygons in sliced layers that have a circumference smaller than this amount will be filtered out. Lower values lead to higher resolution mesh at the cost of slicing time. It is meant mostly for high resolution SLA printers and very tiny 3D models with a lot of details.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"default_value": 1.0,
|
||||
"minimum_value": "0.001",
|
||||
"minimum_value_warning": "0.05",
|
||||
"maximum_value_warning": "1.0",
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": false
|
||||
},
|
||||
"meshfix_maximum_resolution":
|
||||
{
|
||||
"label": "Maximum Resolution",
|
||||
|
@ -120,7 +120,7 @@ UM.MainWindow
|
||||
text: catalog.i18nc("@title:menu menubar:file","&Save...")
|
||||
onTriggered:
|
||||
{
|
||||
var args = { "filter_by_machine": false, "file_type": "workspace", "preferred_mimetype": "application/x-curaproject+xml" };
|
||||
var args = { "filter_by_machine": false, "file_type": "workspace", "preferred_mimetypes": "application/x-curaproject+xml" };
|
||||
if(UM.Preferences.getValue("cura/dialog_on_project_save"))
|
||||
{
|
||||
saveWorkspaceDialog.args = args;
|
||||
@ -142,7 +142,7 @@ UM.MainWindow
|
||||
onTriggered:
|
||||
{
|
||||
var localDeviceId = "local_file";
|
||||
UM.OutputDeviceManager.requestWriteToDevice(localDeviceId, PrintInformation.jobName, { "filter_by_machine": false, "preferred_mimetype": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml"});
|
||||
UM.OutputDeviceManager.requestWriteToDevice(localDeviceId, PrintInformation.jobName, { "filter_by_machine": false, "preferred_mimetypes": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml"});
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,7 +151,7 @@ UM.MainWindow
|
||||
text: catalog.i18nc("@action:inmenu menubar:file", "Export Selection...");
|
||||
enabled: UM.Selection.hasSelection;
|
||||
iconName: "document-save-as";
|
||||
onTriggered: UM.OutputDeviceManager.requestWriteSelectionToDevice("local_file", PrintInformation.jobName, { "filter_by_machine": false, "preferred_mimetype": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml"});
|
||||
onTriggered: UM.OutputDeviceManager.requestWriteSelectionToDevice("local_file", PrintInformation.jobName, { "filter_by_machine": false, "preferred_mimetypes": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml"});
|
||||
}
|
||||
|
||||
MenuSeparator { }
|
||||
|
@ -30,20 +30,24 @@ TabView
|
||||
property bool reevaluateLinkedMaterials: false
|
||||
property string linkedMaterialNames:
|
||||
{
|
||||
if (reevaluateLinkedMaterials) {
|
||||
if (reevaluateLinkedMaterials)
|
||||
{
|
||||
reevaluateLinkedMaterials = false;
|
||||
}
|
||||
if (!base.containerId || !base.editingEnabled) {
|
||||
if (!base.containerId || !base.editingEnabled)
|
||||
{
|
||||
return ""
|
||||
}
|
||||
var linkedMaterials = Cura.ContainerManager.getLinkedMaterials(base.currentMaterialNode, true);
|
||||
if (linkedMaterials.length == 0) {
|
||||
if (linkedMaterials.length == 0)
|
||||
{
|
||||
return ""
|
||||
}
|
||||
return linkedMaterials.join(", ");
|
||||
}
|
||||
|
||||
function getApproximateDiameter(diameter) {
|
||||
function getApproximateDiameter(diameter)
|
||||
{
|
||||
return Math.round(diameter);
|
||||
}
|
||||
|
||||
@ -154,13 +158,15 @@ TabView
|
||||
}
|
||||
|
||||
Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Color") }
|
||||
Row {
|
||||
Row
|
||||
{
|
||||
width: scrollView.columnWidth
|
||||
height: parent.rowHeight
|
||||
spacing: Math.round(UM.Theme.getSize("default_margin").width / 2)
|
||||
|
||||
// color indicator square
|
||||
Rectangle {
|
||||
Rectangle
|
||||
{
|
||||
id: colorSelector
|
||||
color: properties.color_code
|
||||
|
||||
@ -171,7 +177,8 @@ TabView
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
// open the color selection dialog on click
|
||||
MouseArea {
|
||||
MouseArea
|
||||
{
|
||||
anchors.fill: parent
|
||||
onClicked: colorDialog.open()
|
||||
enabled: base.editingEnabled
|
||||
@ -179,7 +186,8 @@ TabView
|
||||
}
|
||||
|
||||
// pretty color name text field
|
||||
ReadOnlyTextField {
|
||||
ReadOnlyTextField
|
||||
{
|
||||
id: colorLabel;
|
||||
text: properties.color_name;
|
||||
readOnly: !base.editingEnabled
|
||||
@ -188,7 +196,8 @@ TabView
|
||||
|
||||
// popup dialog to select a new color
|
||||
// if successful it sets the properties.color_code value to the new color
|
||||
ColorDialog {
|
||||
ColorDialog
|
||||
{
|
||||
id: colorDialog
|
||||
color: properties.color_code
|
||||
onAccepted: base.setMetaDataEntry("color_code", properties.color_code, color)
|
||||
@ -258,7 +267,8 @@ TabView
|
||||
decimals: 2
|
||||
maximumValue: 100000000
|
||||
|
||||
onValueChanged: {
|
||||
onValueChanged:
|
||||
{
|
||||
base.setMaterialPreferenceValue(properties.guid, "spool_cost", parseFloat(value))
|
||||
updateCostPerMeter()
|
||||
}
|
||||
@ -275,7 +285,8 @@ TabView
|
||||
decimals: 0
|
||||
maximumValue: 10000
|
||||
|
||||
onValueChanged: {
|
||||
onValueChanged:
|
||||
{
|
||||
base.setMaterialPreferenceValue(properties.guid, "spool_weight", parseFloat(value))
|
||||
updateCostPerMeter()
|
||||
}
|
||||
@ -401,7 +412,8 @@ TabView
|
||||
{
|
||||
id: spinBox
|
||||
anchors.left: label.right
|
||||
value: {
|
||||
value:
|
||||
{
|
||||
// In case the setting is not in the material...
|
||||
if (!isNaN(parseFloat(materialPropertyProvider.properties.value)))
|
||||
{
|
||||
@ -493,8 +505,10 @@ TabView
|
||||
}
|
||||
|
||||
// Tiny convenience function to check if a value really changed before trying to set it.
|
||||
function setMetaDataEntry(entry_name, old_value, new_value) {
|
||||
if (old_value != new_value) {
|
||||
function setMetaDataEntry(entry_name, old_value, new_value)
|
||||
{
|
||||
if (old_value != new_value)
|
||||
{
|
||||
Cura.ContainerManager.setContainerMetaDataEntry(base.currentMaterialNode, entry_name, new_value)
|
||||
// make sure the UI properties are updated as well since we don't re-fetch the entire model here
|
||||
// When the entry_name is something like properties/diameter, we take the last part of the entry_name
|
||||
@ -546,10 +560,11 @@ TabView
|
||||
}
|
||||
|
||||
// update the display name of the material
|
||||
function updateMaterialDisplayName (old_name, new_name)
|
||||
function updateMaterialDisplayName(old_name, new_name)
|
||||
{
|
||||
// don't change when new name is the same
|
||||
if (old_name == new_name) {
|
||||
if (old_name == new_name)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ import QtQuick.Controls.Styles 1.1
|
||||
import QtQuick.Layouts 1.1
|
||||
|
||||
import UM 1.1 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
Item {
|
||||
id: base;
|
||||
@ -257,7 +258,8 @@ Item {
|
||||
onClicked:
|
||||
{
|
||||
forceActiveFocus();
|
||||
UM.OutputDeviceManager.requestWriteToDevice(UM.OutputDeviceManager.activeDevice, PrintInformation.jobName, { "filter_by_machine": true, "preferred_mimetype":Printer.preferredOutputMimetype });
|
||||
UM.OutputDeviceManager.requestWriteToDevice(UM.OutputDeviceManager.activeDevice, PrintInformation.jobName,
|
||||
{ "filter_by_machine": true, "preferred_mimetypes": Cura.MachineManager.activeMachine.preferred_output_file_formats });
|
||||
}
|
||||
|
||||
style: ButtonStyle {
|
||||
|
@ -1,61 +1,15 @@
|
||||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import os #To find the directory with test files and find the test files.
|
||||
import pytest #This module contains unit tests.
|
||||
import shutil #To copy files to make a temporary file.
|
||||
import unittest.mock #To mock and monkeypatch stuff.
|
||||
import urllib.parse
|
||||
import copy
|
||||
|
||||
import cura.CuraApplication
|
||||
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry #The class we're testing.
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||
from cura.Settings.ExtruderStack import ExtruderStack #Testing for returning the correct types of stacks.
|
||||
from cura.Settings.GlobalStack import GlobalStack #Testing for returning the correct types of stacks.
|
||||
from UM.Resources import Resources #Mocking some functions of this.
|
||||
import UM.Settings.InstanceContainer #Creating instance containers to register.
|
||||
import UM.Settings.ContainerRegistry #Making empty container stacks.
|
||||
import UM.Settings.ContainerStack #Setting the container registry here properly.
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
|
||||
def creteEmptyContainers():
|
||||
empty_container = ContainerRegistry.getInstance().getEmptyInstanceContainer()
|
||||
empty_variant_container = copy.deepcopy(empty_container)
|
||||
empty_variant_container.setMetaDataEntry("id", "empty_variant")
|
||||
empty_variant_container.setMetaDataEntry("type", "variant")
|
||||
ContainerRegistry.getInstance().addContainer(empty_variant_container)
|
||||
|
||||
empty_material_container = copy.deepcopy(empty_container)
|
||||
empty_material_container.setMetaDataEntry("id", "empty_material")
|
||||
empty_material_container.setMetaDataEntry("type", "material")
|
||||
ContainerRegistry.getInstance().addContainer(empty_material_container)
|
||||
|
||||
empty_quality_container = copy.deepcopy(empty_container)
|
||||
empty_quality_container.setMetaDataEntry("id", "empty_quality")
|
||||
empty_quality_container.setName("Not Supported")
|
||||
empty_quality_container.setMetaDataEntry("quality_type", "not_supported")
|
||||
empty_quality_container.setMetaDataEntry("type", "quality")
|
||||
empty_quality_container.setMetaDataEntry("supported", False)
|
||||
ContainerRegistry.getInstance().addContainer(empty_quality_container)
|
||||
|
||||
empty_quality_changes_container = copy.deepcopy(empty_container)
|
||||
empty_quality_changes_container.setMetaDataEntry("id", "empty_quality_changes")
|
||||
empty_quality_changes_container.setMetaDataEntry("type", "quality_changes")
|
||||
ContainerRegistry.getInstance().addContainer(empty_quality_changes_container)
|
||||
|
||||
## Gives a fresh CuraContainerRegistry instance.
|
||||
@pytest.fixture()
|
||||
def container_registry():
|
||||
registry = CuraContainerRegistry()
|
||||
UM.Settings.InstanceContainer.setContainerRegistry(registry)
|
||||
UM.Settings.ContainerStack.setContainerRegistry(registry)
|
||||
return registry
|
||||
|
||||
## Gives an arbitrary definition container.
|
||||
@pytest.fixture()
|
||||
def definition_container():
|
||||
return DefinitionContainer(container_id = "Test Definition")
|
||||
|
||||
def teardown():
|
||||
#If the temporary file for the legacy file rename test still exists, remove it.
|
||||
@ -64,44 +18,47 @@ def teardown():
|
||||
os.remove(temporary_file)
|
||||
|
||||
## Tests whether addContainer properly converts to ExtruderStack.
|
||||
def test_addContainerExtruderStack(container_registry, definition_container):
|
||||
creteEmptyContainers()
|
||||
def test_addContainerExtruderStack(container_registry, definition_container, definition_changes_container):
|
||||
container_registry.addContainer(definition_container)
|
||||
container_registry.addContainer(definition_changes_container)
|
||||
|
||||
container_stack = UM.Settings.ContainerStack.ContainerStack(stack_id = "Test Container Stack") #A container we're going to convert.
|
||||
container_stack = UM.Settings.ContainerStack.ContainerStack(stack_id = "Test Extruder Stack") #A container we're going to convert.
|
||||
container_stack.setMetaDataEntry("type", "extruder_train") #This is now an extruder train.
|
||||
container_stack.insertContainer(0, definition_container) #Add a definition to it so it doesn't complain.
|
||||
container_stack.insertContainer(1, definition_changes_container)
|
||||
|
||||
mock_super_add_container = unittest.mock.MagicMock() #Takes the role of the Uranium-ContainerRegistry where the resulting containers get registered.
|
||||
with unittest.mock.patch("UM.Settings.ContainerRegistry.ContainerRegistry.addContainer", mock_super_add_container):
|
||||
container_registry.addContainer(container_stack)
|
||||
|
||||
assert len(mock_super_add_container.call_args_list) == 2 #Called only once.
|
||||
assert len(mock_super_add_container.call_args_list[1][0]) == 1 #Called with one parameter.
|
||||
assert type(mock_super_add_container.call_args_list[1][0][0]) == ExtruderStack
|
||||
assert len(mock_super_add_container.call_args_list) == 1 #Called only once.
|
||||
assert len(mock_super_add_container.call_args_list[0][0]) == 1 #Called with one parameter.
|
||||
assert type(mock_super_add_container.call_args_list[0][0][0]) == ExtruderStack
|
||||
|
||||
## Tests whether addContainer properly converts to GlobalStack.
|
||||
def test_addContainerGlobalStack(container_registry, definition_container):
|
||||
def test_addContainerGlobalStack(container_registry, definition_container, definition_changes_container):
|
||||
container_registry.addContainer(definition_container)
|
||||
container_registry.addContainer(definition_changes_container)
|
||||
|
||||
container_stack = UM.Settings.ContainerStack.ContainerStack(stack_id = "Test Container Stack") #A container we're going to convert.
|
||||
container_stack = UM.Settings.ContainerStack.ContainerStack(stack_id = "Test Global Stack") #A container we're going to convert.
|
||||
container_stack.setMetaDataEntry("type", "machine") #This is now a global stack.
|
||||
container_stack.insertContainer(0, definition_container) #Must have a definition.
|
||||
container_stack.insertContainer(1, definition_changes_container) #Must have a definition changes.
|
||||
|
||||
mock_super_add_container = unittest.mock.MagicMock() #Takes the role of the Uranium-ContainerRegistry where the resulting containers get registered.
|
||||
with unittest.mock.patch("UM.Settings.ContainerRegistry.ContainerRegistry.addContainer", mock_super_add_container):
|
||||
container_registry.addContainer(container_stack)
|
||||
|
||||
assert len(mock_super_add_container.call_args_list) == 2 #Called only once.
|
||||
assert len(mock_super_add_container.call_args_list[1][0]) == 1 #Called with one parameter.
|
||||
assert type(mock_super_add_container.call_args_list[1][0][0]) == GlobalStack
|
||||
assert len(mock_super_add_container.call_args_list) == 1 #Called only once.
|
||||
assert len(mock_super_add_container.call_args_list[0][0]) == 1 #Called with one parameter.
|
||||
assert type(mock_super_add_container.call_args_list[0][0][0]) == GlobalStack
|
||||
|
||||
def test_addContainerGoodSettingVersion(container_registry, definition_container):
|
||||
from cura.CuraApplication import CuraApplication
|
||||
definition_container.getMetaData()["setting_version"] = CuraApplication.SettingVersion
|
||||
container_registry.addContainer(definition_container)
|
||||
|
||||
instance = UM.Settings.InstanceContainer.InstanceContainer(container_id = "Test Instance")
|
||||
instance = UM.Settings.InstanceContainer.InstanceContainer(container_id = "Test Instance Right Version")
|
||||
instance.setMetaDataEntry("setting_version", CuraApplication.SettingVersion)
|
||||
instance.setDefinition(definition_container.getId())
|
||||
|
||||
@ -116,7 +73,7 @@ def test_addContainerNoSettingVersion(container_registry, definition_container):
|
||||
definition_container.getMetaData()["setting_version"] = CuraApplication.SettingVersion
|
||||
container_registry.addContainer(definition_container)
|
||||
|
||||
instance = UM.Settings.InstanceContainer.InstanceContainer(container_id = "Test Instance")
|
||||
instance = UM.Settings.InstanceContainer.InstanceContainer(container_id = "Test Instance No Version")
|
||||
#Don't add setting_version metadata.
|
||||
instance.setDefinition(definition_container.getId())
|
||||
|
||||
@ -131,7 +88,7 @@ def test_addContainerBadSettingVersion(container_registry, definition_container)
|
||||
definition_container.getMetaData()["setting_version"] = CuraApplication.SettingVersion
|
||||
container_registry.addContainer(definition_container)
|
||||
|
||||
instance = UM.Settings.InstanceContainer.InstanceContainer(container_id = "Test Instance")
|
||||
instance = UM.Settings.InstanceContainer.InstanceContainer(container_id = "Test Instance Wrong Version")
|
||||
instance.setMetaDataEntry("setting_version", 9001) #Wrong version!
|
||||
instance.setDefinition(definition_container.getId())
|
||||
|
||||
@ -140,38 +97,3 @@ def test_addContainerBadSettingVersion(container_registry, definition_container)
|
||||
container_registry.addContainer(instance)
|
||||
|
||||
mock_super_add_container.assert_not_called() #Should not get passed on to UM.Settings.ContainerRegistry.addContainer, because the setting_version doesn't match its definition!
|
||||
|
||||
## Tests whether loading gives objects of the correct type.
|
||||
# @pytest.mark.parametrize("filename, output_class", [
|
||||
# ("ExtruderLegacy.stack.cfg", ExtruderStack),
|
||||
# ("MachineLegacy.stack.cfg", GlobalStack),
|
||||
# ("Left.extruder.cfg", ExtruderStack),
|
||||
# ("Global.global.cfg", GlobalStack),
|
||||
# ("Global.stack.cfg", GlobalStack)
|
||||
# ])
|
||||
# def test_loadTypes(filename, output_class, container_registry):
|
||||
# #Mock some dependencies.
|
||||
# Resources.getAllResourcesOfType = unittest.mock.MagicMock(return_value = [os.path.join(os.path.dirname(os.path.abspath(__file__)), "stacks", filename)]) #Return just this tested file.
|
||||
#
|
||||
# def findContainers(container_type = 0, id = None):
|
||||
# if id == "some_instance":
|
||||
# return [UM.Settings.ContainerRegistry._EmptyInstanceContainer(id)]
|
||||
# elif id == "some_definition":
|
||||
# return [DefinitionContainer(container_id = id)]
|
||||
# else:
|
||||
# return []
|
||||
#
|
||||
# container_registry.findContainers = findContainers
|
||||
#
|
||||
# with unittest.mock.patch("cura.Settings.GlobalStack.GlobalStack.findContainer"):
|
||||
# with unittest.mock.patch("os.remove"):
|
||||
# container_registry.load()
|
||||
#
|
||||
# #Check whether the resulting type was correct.
|
||||
# stack_id = filename.split(".")[0]
|
||||
# for container_id, container in container_registry._containers.items(): #Stupid ContainerRegistry class doesn't expose any way of getting at this except by prodding the privates.
|
||||
# if container_id == stack_id: #This is the one we're testing.
|
||||
# assert type(container) == output_class
|
||||
# break
|
||||
# else:
|
||||
# assert False #Container stack with specified ID was not loaded.
|
@ -1,43 +1,17 @@
|
||||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import pytest #This module contains automated tests.
|
||||
import unittest.mock #For the mocking and monkeypatching functionality.
|
||||
import copy
|
||||
|
||||
import cura.CuraApplication
|
||||
import cura.Settings.CuraContainerStack #To get the list of container types.
|
||||
import UM.Settings.ContainerRegistry #To create empty instance containers.
|
||||
import UM.Settings.ContainerStack #To set the container registry the container stacks use.
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer #To check against the class of DefinitionContainer.
|
||||
from UM.Settings.InstanceContainer import InstanceContainer #To check against the class of InstanceContainer.
|
||||
import cura.Settings.ExtruderStack #The module we're testing.
|
||||
from cura.Settings.Exceptions import InvalidContainerError, InvalidOperationError #To check whether the correct exceptions are raised.
|
||||
|
||||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from cura.Settings.GlobalStack import GlobalStack
|
||||
|
||||
## Fake container registry that always provides all containers you ask of.
|
||||
@pytest.yield_fixture()
|
||||
def container_registry():
|
||||
registry = unittest.mock.MagicMock()
|
||||
registry.return_value = unittest.mock.NonCallableMagicMock()
|
||||
registry.findInstanceContainers = lambda *args, registry = registry, **kwargs: [registry.return_value]
|
||||
registry.findDefinitionContainers = lambda *args, registry = registry, **kwargs: [registry.return_value]
|
||||
|
||||
UM.Settings.ContainerRegistry.ContainerRegistry._ContainerRegistry__instance = registry
|
||||
UM.Settings.ContainerStack._containerRegistry = registry
|
||||
|
||||
yield registry
|
||||
|
||||
UM.Settings.ContainerRegistry.ContainerRegistry._ContainerRegistry__instance = None
|
||||
UM.Settings.ContainerStack._containerRegistry = None
|
||||
|
||||
## An empty extruder stack to test with.
|
||||
@pytest.fixture()
|
||||
def extruder_stack() -> cura.Settings.ExtruderStack.ExtruderStack:
|
||||
creteEmptyContainers()
|
||||
return cura.Settings.ExtruderStack.ExtruderStack("TestStack")
|
||||
from cura.Settings.cura_empty_instance_containers import empty_container
|
||||
|
||||
## Gets an instance container with a specified container type.
|
||||
#
|
||||
@ -48,31 +22,6 @@ def getInstanceContainer(container_type) -> InstanceContainer:
|
||||
container.setMetaDataEntry("type", container_type)
|
||||
return container
|
||||
|
||||
def creteEmptyContainers():
|
||||
empty_container = ContainerRegistry.getInstance().getEmptyInstanceContainer()
|
||||
empty_variant_container = copy.deepcopy(empty_container)
|
||||
empty_variant_container.setMetaDataEntry("id", "empty_variant")
|
||||
empty_variant_container.setMetaDataEntry("type", "variant")
|
||||
ContainerRegistry.getInstance().addContainer(empty_variant_container)
|
||||
|
||||
empty_material_container = copy.deepcopy(empty_container)
|
||||
empty_material_container.setMetaDataEntry("id", "empty_material")
|
||||
empty_material_container.setMetaDataEntry("type", "material")
|
||||
ContainerRegistry.getInstance().addContainer(empty_material_container)
|
||||
|
||||
empty_quality_container = copy.deepcopy(empty_container)
|
||||
empty_quality_container.setMetaDataEntry("id", "empty_quality")
|
||||
empty_quality_container.setName("Not Supported")
|
||||
empty_quality_container.setMetaDataEntry("quality_type", "not_supported")
|
||||
empty_quality_container.setMetaDataEntry("type", "quality")
|
||||
empty_quality_container.setMetaDataEntry("supported", False)
|
||||
ContainerRegistry.getInstance().addContainer(empty_quality_container)
|
||||
|
||||
empty_quality_changes_container = copy.deepcopy(empty_container)
|
||||
empty_quality_changes_container.setMetaDataEntry("id", "empty_quality_changes")
|
||||
empty_quality_changes_container.setMetaDataEntry("type", "quality_changes")
|
||||
ContainerRegistry.getInstance().addContainer(empty_quality_changes_container)
|
||||
|
||||
class DefinitionContainerSubClass(DefinitionContainer):
|
||||
def __init__(self):
|
||||
super().__init__(container_id = "SubDefinitionContainer")
|
||||
@ -179,12 +128,30 @@ def test_constrainVariantInvalid(container, extruder_stack):
|
||||
def test_constrainVariantValid(container, extruder_stack):
|
||||
extruder_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"),
|
||||
getInstanceContainer(container_type = "material"), #Existing, but still wrong type.
|
||||
DefinitionContainer(container_id = "wrong class")
|
||||
])
|
||||
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"),
|
||||
InstanceContainerSubClass(container_type = "definition_changes")
|
||||
])
|
||||
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"),
|
||||
getInstanceContainer(container_type = "material"), #Existing, but still wrong class.
|
||||
])
|
||||
def test_constrainVariantInvalid(container, extruder_stack):
|
||||
def test_constrainDefinitionInvalid(container, extruder_stack):
|
||||
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
||||
extruder_stack.definition = container
|
||||
|
||||
@ -196,23 +163,22 @@ def test_constrainVariantInvalid(container, extruder_stack):
|
||||
def test_constrainDefinitionValid(container, extruder_stack):
|
||||
extruder_stack.definition = container #Should not give an error.
|
||||
|
||||
## Tests whether deserialising completes the missing containers with empty
|
||||
# ones.
|
||||
@pytest.mark.skip #The test currently fails because the definition container doesn't have a category, which is wrong but we don't have time to refactor that right now.
|
||||
def test_deserializeCompletesEmptyContainers(extruder_stack: cura.Settings.ExtruderStack):
|
||||
extruder_stack._containers = [DefinitionContainer(container_id = "definition")] #Set the internal state of this stack manually.
|
||||
## Tests whether deserialising completes the missing containers with empty ones.
|
||||
def test_deserializeCompletesEmptyContainers(extruder_stack):
|
||||
extruder_stack._containers = [DefinitionContainer(container_id = "definition"), extruder_stack.definitionChanges] #Set the internal state of this stack manually.
|
||||
|
||||
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
|
||||
extruder_stack.deserialize("")
|
||||
|
||||
assert len(extruder_stack.getContainers()) == len(cura.Settings.CuraContainerStack._ContainerIndexes.IndexTypeMap) #Needs a slot for every type.
|
||||
for container_type_index in cura.Settings.CuraContainerStack._ContainerIndexes.IndexTypeMap:
|
||||
if container_type_index == cura.Settings.CuraContainerStack._ContainerIndexes.Definition: #We're not checking the definition.
|
||||
if container_type_index in \
|
||||
(cura.Settings.CuraContainerStack._ContainerIndexes.Definition,
|
||||
cura.Settings.CuraContainerStack._ContainerIndexes.DefinitionChanges): # We're not checking the definition or definition_changes
|
||||
continue
|
||||
assert extruder_stack.getContainer(container_type_index).getId() == "empty" #All others need to be empty.
|
||||
assert extruder_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(extruder_stack):
|
||||
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = getInstanceContainer(container_type = "wrong type")
|
||||
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition")
|
||||
@ -222,8 +188,7 @@ def test_deserializeRemovesWrongInstanceContainer(extruder_stack):
|
||||
|
||||
assert extruder_stack.quality == extruder_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(extruder_stack):
|
||||
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = DefinitionContainer(container_id = "wrong class")
|
||||
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition")
|
||||
@ -233,8 +198,7 @@ def test_deserializeRemovesWrongContainerClass(extruder_stack):
|
||||
|
||||
assert extruder_stack.quality == extruder_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(extruder_stack):
|
||||
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = getInstanceContainer(container_type = "definition") #Correct type but wrong class.
|
||||
|
||||
@ -242,8 +206,7 @@ def test_deserializeWrongDefinitionClass(extruder_stack):
|
||||
with pytest.raises(UM.Settings.ContainerStack.InvalidContainerStackError): #Must raise an error that there is no definition container.
|
||||
extruder_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(extruder_stack):
|
||||
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = getInstanceContainer(container_type = "material") #Not in the correct spot.
|
||||
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition")
|
||||
@ -251,26 +214,21 @@ def test_deserializeMoveInstanceContainer(extruder_stack):
|
||||
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
|
||||
extruder_stack.deserialize("")
|
||||
|
||||
assert extruder_stack.quality.getId() == "empty"
|
||||
assert extruder_stack.material.getId() != "empty"
|
||||
from UM.Settings.Validator import Validator
|
||||
## Tests whether a definition container in the wrong spot is moved into the
|
||||
# correct spot by deserialising.
|
||||
@pytest.mark.skip #The test currently fails because the definition container doesn't have a category, which is wrong but we don't have time to refactor that right now.
|
||||
assert extruder_stack.quality == empty_container
|
||||
assert extruder_stack.material != empty_container
|
||||
|
||||
## Tests whether a definition container in the wrong spot is moved into the correct spot by deserialising.
|
||||
def test_deserializeMoveDefinitionContainer(extruder_stack):
|
||||
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Material] = DefinitionContainer(container_id = "some definition") #Not in the correct spot.
|
||||
|
||||
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
|
||||
extruder_stack.deserialize("")
|
||||
|
||||
assert extruder_stack.material.getId() == "empty"
|
||||
assert extruder_stack.definition.getId() != "empty"
|
||||
assert extruder_stack.material == empty_container
|
||||
assert extruder_stack.definition != empty_container
|
||||
|
||||
UM.Settings.ContainerStack._containerRegistry = None
|
||||
|
||||
## Tests whether getProperty properly applies the stack-like behaviour on its
|
||||
# containers.
|
||||
def test_getPropertyFallThrough(extruder_stack):
|
||||
## Tests whether getProperty properly applies the stack-like behaviour on its containers.
|
||||
def test_getPropertyFallThrough(global_stack, extruder_stack):
|
||||
# ExtruderStack.setNextStack calls registerExtruder for backward compatibility, but we do not need a complete extruder manager
|
||||
ExtruderManager._ExtruderManager__instance = unittest.mock.MagicMock()
|
||||
|
||||
@ -300,8 +258,7 @@ def test_getPropertyFallThrough(extruder_stack):
|
||||
with unittest.mock.patch("cura.Settings.CuraContainerStack.DefinitionContainer", unittest.mock.MagicMock): #To guard against the type checking.
|
||||
extruder_stack.definition = mock_layer_heights[container_indices.Definition] #There's a layer height in here!
|
||||
|
||||
stack = GlobalStack("PyTest GlobalStack")
|
||||
extruder_stack.setNextStack(stack)
|
||||
extruder_stack.setNextStack(global_stack)
|
||||
|
||||
assert extruder_stack.getProperty("layer_height", "value") == container_indices.Definition
|
||||
extruder_stack.variant = mock_layer_heights[container_indices.Variant]
|
||||
@ -340,4 +297,4 @@ def test_setPropertyUser(key, property, value, extruder_stack):
|
||||
|
||||
extruder_stack.setProperty(key, property, value) #The actual test.
|
||||
|
||||
extruder_stack.userChanges.setProperty.assert_called_once_with(key, property, value) #Make sure that the user container gets a setProperty call.
|
||||
extruder_stack.userChanges.setProperty.assert_called_once_with(key, property, value, None, False) #Make sure that the user container gets a setProperty call.
|
@ -1,43 +1,19 @@
|
||||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import pytest #This module contains unit tests.
|
||||
import unittest.mock #To monkeypatch some mocks in place of dependencies.
|
||||
import copy
|
||||
|
||||
import cura.CuraApplication
|
||||
import cura.Settings.GlobalStack #The module we're testing.
|
||||
import cura.Settings.CuraContainerStack #To get the list of container types.
|
||||
from cura.Settings.Exceptions import TooManyExtrudersError, InvalidContainerError, InvalidOperationError #To test raising these errors.
|
||||
from cura.Settings.Exceptions import InvalidContainerError, InvalidOperationError #To test raising these errors.
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer #To test against the class DefinitionContainer.
|
||||
from UM.Settings.InstanceContainer import InstanceContainer #To test against the class InstanceContainer.
|
||||
from UM.Settings.SettingInstance import InstanceState
|
||||
import UM.Settings.ContainerRegistry
|
||||
import UM.Settings.ContainerStack
|
||||
import UM.Settings.SettingDefinition #To add settings to the definition.
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
|
||||
## Fake container registry that always provides all containers you ask of.
|
||||
@pytest.yield_fixture()
|
||||
def container_registry():
|
||||
registry = unittest.mock.MagicMock()
|
||||
registry.return_value = unittest.mock.NonCallableMagicMock()
|
||||
registry.findInstanceContainers = lambda *args, registry = registry, **kwargs: [registry.return_value]
|
||||
registry.findDefinitionContainers = lambda *args, registry = registry, **kwargs: [registry.return_value]
|
||||
|
||||
UM.Settings.ContainerRegistry.ContainerRegistry._ContainerRegistry__instance = registry
|
||||
UM.Settings.ContainerStack._containerRegistry = registry
|
||||
|
||||
yield registry
|
||||
|
||||
UM.Settings.ContainerRegistry.ContainerRegistry._ContainerRegistry__instance = None
|
||||
UM.Settings.ContainerStack._containerRegistry = None
|
||||
|
||||
#An empty global stack to test with.
|
||||
@pytest.fixture()
|
||||
def global_stack() -> cura.Settings.GlobalStack.GlobalStack:
|
||||
creteEmptyContainers()
|
||||
return cura.Settings.GlobalStack.GlobalStack("TestStack")
|
||||
from cura.Settings.cura_empty_instance_containers import empty_container
|
||||
|
||||
## Gets an instance container with a specified container type.
|
||||
#
|
||||
@ -48,31 +24,6 @@ def getInstanceContainer(container_type) -> InstanceContainer:
|
||||
container.setMetaDataEntry("type", container_type)
|
||||
return container
|
||||
|
||||
def creteEmptyContainers():
|
||||
empty_container = ContainerRegistry.getInstance().getEmptyInstanceContainer()
|
||||
empty_variant_container = copy.deepcopy(empty_container)
|
||||
empty_variant_container.setMetaDataEntry("id", "empty_variant")
|
||||
empty_variant_container.setMetaDataEntry("type", "variant")
|
||||
ContainerRegistry.getInstance().addContainer(empty_variant_container)
|
||||
|
||||
empty_material_container = copy.deepcopy(empty_container)
|
||||
empty_material_container.setMetaDataEntry("id", "empty_material")
|
||||
empty_material_container.setMetaDataEntry("type", "material")
|
||||
ContainerRegistry.getInstance().addContainer(empty_material_container)
|
||||
|
||||
empty_quality_container = copy.deepcopy(empty_container)
|
||||
empty_quality_container.setMetaDataEntry("id", "empty_quality")
|
||||
empty_quality_container.setName("Not Supported")
|
||||
empty_quality_container.setMetaDataEntry("quality_type", "not_supported")
|
||||
empty_quality_container.setMetaDataEntry("type", "quality")
|
||||
empty_quality_container.setMetaDataEntry("supported", False)
|
||||
ContainerRegistry.getInstance().addContainer(empty_quality_container)
|
||||
|
||||
empty_quality_changes_container = copy.deepcopy(empty_container)
|
||||
empty_quality_changes_container.setMetaDataEntry("id", "empty_quality_changes")
|
||||
empty_quality_changes_container.setMetaDataEntry("type", "quality_changes")
|
||||
ContainerRegistry.getInstance().addContainer(empty_quality_changes_container)
|
||||
|
||||
class DefinitionContainerSubClass(DefinitionContainer):
|
||||
def __init__(self):
|
||||
super().__init__(container_id = "SubDefinitionContainer")
|
||||
@ -229,7 +180,7 @@ def test_constrainDefinitionChangesValid(container, global_stack):
|
||||
getInstanceContainer(container_type = "wrong class"),
|
||||
getInstanceContainer(container_type = "material"), #Existing, but still wrong class.
|
||||
])
|
||||
def test_constrainVariantInvalid(container, global_stack):
|
||||
def test_constrainDefinitionInvalid(container, global_stack):
|
||||
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
||||
global_stack.definition = container
|
||||
|
||||
@ -241,23 +192,22 @@ def test_constrainVariantInvalid(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.
|
||||
@pytest.mark.skip #The test currently fails because the definition container doesn't have a category, which is wrong but we don't have time to refactor that right now.
|
||||
def test_deserializeCompletesEmptyContainers(global_stack: cura.Settings.GlobalStack):
|
||||
global_stack._containers = [DefinitionContainer(container_id = "definition")] #Set the internal state of this stack manually.
|
||||
## 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):
|
||||
global_stack._containers = [DefinitionContainer(container_id = "definition"), global_stack.definitionChanges] #Set the internal state of this stack manually.
|
||||
|
||||
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
|
||||
global_stack.deserialize("")
|
||||
|
||||
assert len(global_stack.getContainers()) == len(cura.Settings.CuraContainerStack._ContainerIndexes.IndexTypeMap) #Needs a slot for every type.
|
||||
for container_type_index in cura.Settings.CuraContainerStack._ContainerIndexes.IndexTypeMap:
|
||||
if container_type_index == cura.Settings.CuraContainerStack._ContainerIndexes.Definition: #We're not checking the definition.
|
||||
if container_type_index in \
|
||||
(cura.Settings.CuraContainerStack._ContainerIndexes.Definition, cura.Settings.CuraContainerStack._ContainerIndexes.DefinitionChanges): #We're not checking the definition or definition_changes
|
||||
continue
|
||||
assert global_stack.getContainer(container_type_index).getId() == "empty" #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):
|
||||
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = getInstanceContainer(container_type = "wrong type")
|
||||
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition")
|
||||
@ -267,8 +217,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.
|
||||
## 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")
|
||||
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition")
|
||||
@ -278,8 +227,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.
|
||||
## 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.
|
||||
|
||||
@ -287,8 +235,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.
|
||||
## 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.
|
||||
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition")
|
||||
@ -296,25 +243,20 @@ def test_deserializeMoveInstanceContainer(global_stack):
|
||||
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
|
||||
global_stack.deserialize("")
|
||||
|
||||
assert global_stack.quality.getId() == "empty"
|
||||
assert global_stack.material.getId() != "empty"
|
||||
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.
|
||||
@pytest.mark.skip #The test currently fails because the definition container doesn't have a category, which is wrong but we don't have time to refactor that right now.
|
||||
## 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.
|
||||
|
||||
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
|
||||
global_stack.deserialize("")
|
||||
|
||||
assert global_stack.material.getId() == "empty"
|
||||
assert global_stack.definition.getId() != "empty"
|
||||
assert global_stack.material == empty_container
|
||||
assert global_stack.definition != empty_container
|
||||
|
||||
UM.Settings.ContainerStack._containerRegistry = None
|
||||
|
||||
## 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):
|
||||
#A few instance container mocks to put in the stack.
|
||||
mock_layer_heights = {} #For each container type, a mock container that defines layer height to something unique.
|
||||
@ -365,8 +307,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.
|
||||
## 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.
|
||||
resolve_and_value.getProperty = lambda key, property, context = None: (7.5 if property == "resolve" else 5) if (key == "material_bed_temperature" and property in ("resolve", "value")) else None #7.5 resolve, 5 value.
|
||||
@ -375,8 +316,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.
|
||||
## 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
|
||||
instance_containers = {}
|
||||
@ -402,8 +342,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.
|
||||
## 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.
|
||||
value.getProperty = lambda key, property, context = None: (10 if property == "value" else (InstanceState.User if property != "limit_to_extruder" else "-1")) if key == "material_bed_temperature" else None
|
||||
@ -417,8 +356,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.
|
||||
## 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()
|
||||
container.getMetaDataEntry = unittest.mock.MagicMock(return_value = "user")
|
||||
@ -429,8 +367,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.
|
||||
## 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()
|
||||
container.getMetaDataEntry = unittest.mock.MagicMock(return_value = "quality_changes")
|
||||
@ -441,8 +378,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.
|
||||
## 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()
|
||||
container.getMetaDataEntry = unittest.mock.MagicMock(return_value = "quality")
|
||||
@ -481,4 +417,4 @@ 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) #Make sure that the user container gets a setProperty call.
|
||||
global_stack.userChanges.setProperty.assert_called_once_with(key, property, value, None, False) #Make sure that the user container gets a setProperty call.
|
54
tests/Settings/conftest.py
Normal file
54
tests/Settings/conftest.py
Normal file
@ -0,0 +1,54 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Uranium is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
# The purpose of this class is to create fixtures or methods that can be shared among all settings tests.
|
||||
|
||||
import pytest
|
||||
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.Settings.ContainerStack import setContainerRegistry
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer #To provide definition containers in the registry fixtures.
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry
|
||||
from cura.Settings.ExtruderStack import ExtruderStack
|
||||
from cura.Settings.GlobalStack import GlobalStack
|
||||
import cura.Settings.CuraContainerStack
|
||||
|
||||
# Returns the CuraContainerRegistry instance with some empty containers.
|
||||
@pytest.fixture()
|
||||
def container_registry(application) -> CuraContainerRegistry:
|
||||
ContainerRegistry._ContainerRegistry__instance= None # Need to reset since we only allow one instance
|
||||
registry = CuraContainerRegistry(application)
|
||||
setContainerRegistry(registry)
|
||||
return registry
|
||||
|
||||
# Gives an arbitrary definition container.
|
||||
@pytest.fixture()
|
||||
def definition_container() -> DefinitionContainer:
|
||||
return DefinitionContainer(container_id = "Test Definition")
|
||||
|
||||
# Gives an arbitrary definition changes container.
|
||||
@pytest.fixture()
|
||||
def definition_changes_container() -> InstanceContainer:
|
||||
definition_changes_container = InstanceContainer(container_id = "Test Definition Changes")
|
||||
definition_changes_container.setMetaDataEntry("type", "definition_changes")
|
||||
# Add current setting version to the instance container
|
||||
from cura.CuraApplication import CuraApplication
|
||||
definition_changes_container.getMetaData()["setting_version"] = CuraApplication.SettingVersion
|
||||
return definition_changes_container
|
||||
|
||||
# An empty global stack to test with.
|
||||
# There is a restriction here that the definition changes cannot be an empty container. Added in CURA-5281
|
||||
@pytest.fixture()
|
||||
def global_stack(definition_changes_container) -> GlobalStack:
|
||||
global_stack = GlobalStack("TestGlobalStack")
|
||||
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.DefinitionChanges] = definition_changes_container
|
||||
return global_stack
|
||||
|
||||
# An empty extruder stack to test with.
|
||||
# There is a restriction here that the definition changes cannot be an empty container. Added in CURA-5281
|
||||
@pytest.fixture()
|
||||
def extruder_stack(definition_changes_container) -> ExtruderStack:
|
||||
extruder_stack= ExtruderStack("TestExtruderStack")
|
||||
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.DefinitionChanges] = definition_changes_container
|
||||
return extruder_stack
|
@ -1,12 +0,0 @@
|
||||
[general]
|
||||
version = 3
|
||||
name = Complete
|
||||
id = Complete
|
||||
|
||||
[containers]
|
||||
0 = some_user_changes
|
||||
1 = some_quality_changes
|
||||
2 = some_quality
|
||||
3 = some_material
|
||||
4 = some_variant
|
||||
5 = some_definition
|
@ -1,13 +0,0 @@
|
||||
[general]
|
||||
version = 3
|
||||
name = Complete
|
||||
id = Complete
|
||||
|
||||
[containers]
|
||||
0 = some_user_changes
|
||||
1 = some_quality_changes
|
||||
2 = some_quality
|
||||
3 = some_material
|
||||
4 = some_variant
|
||||
5 = some_definition_changes
|
||||
6 = some_definition
|
@ -1,11 +0,0 @@
|
||||
[general]
|
||||
version = 3
|
||||
name = Legacy Extruder Stack
|
||||
id = ExtruderLegacy
|
||||
|
||||
[metadata]
|
||||
type = extruder_train
|
||||
|
||||
[containers]
|
||||
3 = some_instance
|
||||
5 = some_definition
|
@ -1,8 +0,0 @@
|
||||
[general]
|
||||
version = 3
|
||||
name = Global
|
||||
id = Global
|
||||
|
||||
[containers]
|
||||
3 = some_instance
|
||||
6 = some_definition
|
@ -1,11 +0,0 @@
|
||||
[general]
|
||||
version = 3
|
||||
name = Global
|
||||
id = Global
|
||||
|
||||
[metadata]
|
||||
type = machine
|
||||
|
||||
[containers]
|
||||
3 = some_instance
|
||||
6 = some_definition
|
@ -1,8 +0,0 @@
|
||||
[general]
|
||||
version = 3
|
||||
name = Left
|
||||
id = Left
|
||||
|
||||
[containers]
|
||||
3 = some_instance
|
||||
5 = some_definition
|
@ -1,11 +0,0 @@
|
||||
[general]
|
||||
version = 3
|
||||
name = Legacy Global Stack
|
||||
id = MachineLegacy
|
||||
|
||||
[metadata]
|
||||
type = machine
|
||||
|
||||
[containers]
|
||||
3 = some_instance
|
||||
6 = some_definition
|
@ -1,7 +0,0 @@
|
||||
[general]
|
||||
version = 3
|
||||
name = Only Definition
|
||||
id = OnlyDefinition
|
||||
|
||||
[containers]
|
||||
5 = some_definition
|
@ -1,7 +0,0 @@
|
||||
[general]
|
||||
version = 3
|
||||
name = Only Definition
|
||||
id = OnlyDefinition
|
||||
|
||||
[containers]
|
||||
6 = some_definition
|
@ -1,8 +0,0 @@
|
||||
[general]
|
||||
version = 3
|
||||
name = Only Definition Changes
|
||||
id = OnlyDefinitionChanges
|
||||
|
||||
[containers]
|
||||
5 = some_instance
|
||||
6 = some_definition
|
@ -1,8 +0,0 @@
|
||||
[general]
|
||||
version = 3
|
||||
name = Only Material
|
||||
id = OnlyMaterial
|
||||
|
||||
[containers]
|
||||
3 = some_instance
|
||||
5 = some_definition
|
@ -1,8 +0,0 @@
|
||||
[general]
|
||||
version = 3
|
||||
name = Only Material
|
||||
id = OnlyMaterial
|
||||
|
||||
[containers]
|
||||
3 = some_instance
|
||||
6 = some_definition
|
@ -1,8 +0,0 @@
|
||||
[general]
|
||||
version = 3
|
||||
name = Only Quality
|
||||
id = OnlyQuality
|
||||
|
||||
[containers]
|
||||
2 = some_instance
|
||||
5 = some_definition
|
@ -1,8 +0,0 @@
|
||||
[general]
|
||||
version = 3
|
||||
name = Only Quality
|
||||
id = OnlyQuality
|
||||
|
||||
[containers]
|
||||
2 = some_instance
|
||||
6 = some_definition
|
@ -1,8 +0,0 @@
|
||||
[general]
|
||||
version = 3
|
||||
name = Only Quality Changes
|
||||
id = OnlyQualityChanges
|
||||
|
||||
[containers]
|
||||
1 = some_instance
|
||||
5 = some_definition
|
@ -1,8 +0,0 @@
|
||||
[general]
|
||||
version = 3
|
||||
name = Only Quality Changes
|
||||
id = OnlyQualityChanges
|
||||
|
||||
[containers]
|
||||
1 = some_instance
|
||||
6 = some_definition
|
@ -1,8 +0,0 @@
|
||||
[general]
|
||||
version = 3
|
||||
name = Only User
|
||||
id = OnlyUser
|
||||
|
||||
[containers]
|
||||
0 = some_instance
|
||||
5 = some_definition
|
@ -1,8 +0,0 @@
|
||||
[general]
|
||||
version = 3
|
||||
name = Only User
|
||||
id = OnlyUser
|
||||
|
||||
[containers]
|
||||
0 = some_instance
|
||||
6 = some_definition
|
@ -1,8 +0,0 @@
|
||||
[general]
|
||||
version = 3
|
||||
name = Only Variant
|
||||
id = OnlyVariant
|
||||
|
||||
[containers]
|
||||
4 = some_instance
|
||||
5 = some_definition
|
@ -1,8 +0,0 @@
|
||||
[general]
|
||||
version = 3
|
||||
name = Only Variant
|
||||
id = OnlyVariant
|
||||
|
||||
[containers]
|
||||
4 = some_instance
|
||||
6 = some_definition
|
@ -1,9 +1,11 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import numpy
|
||||
|
||||
from cura.Arranging.Arrange import Arrange
|
||||
from cura.Arranging.ShapeArray import ShapeArray
|
||||
|
||||
|
||||
## Triangle of area 12
|
||||
def gimmeTriangle():
|
||||
return numpy.array([[-3, 1], [3, 1], [0, -3]], dtype=numpy.int32)
|
||||
@ -102,7 +104,7 @@ def test_centerFirst_rectangular():
|
||||
|
||||
|
||||
## Test centerFirst
|
||||
def test_centerFirst_rectangular():
|
||||
def test_centerFirst_rectangular2():
|
||||
ar = Arrange(10, 20, 5, 10, scale = 1)
|
||||
ar.centerFirst()
|
||||
print(ar._priority)
|
||||
|
@ -1,11 +1,10 @@
|
||||
#Todo: Write tests
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import pytest
|
||||
# QtApplication needs to be imported first to prevent import errors.
|
||||
from UM.Qt.QtApplication import QtApplication
|
||||
|
||||
from cura.MachineAction import MachineAction
|
||||
from cura.MachineActionManager import MachineActionManager, NotUniqueMachineActionError, UnknownMachineActionError
|
||||
from cura.MachineActionManager import NotUniqueMachineActionError, UnknownMachineActionError
|
||||
|
||||
class Machine:
|
||||
def __init__(self, key = ""):
|
||||
@ -14,66 +13,64 @@ class Machine:
|
||||
def getKey(self):
|
||||
return self._key
|
||||
|
||||
def test_addMachineAction():
|
||||
|
||||
machine_manager = MachineActionManager()
|
||||
def test_addMachineAction(machine_action_manager):
|
||||
|
||||
test_action = MachineAction(key = "test_action")
|
||||
test_action_2 = MachineAction(key = "test_action_2")
|
||||
test_machine = Machine("test_machine")
|
||||
machine_manager.addMachineAction(test_action)
|
||||
machine_manager.addMachineAction(test_action_2)
|
||||
machine_action_manager.addMachineAction(test_action)
|
||||
machine_action_manager.addMachineAction(test_action_2)
|
||||
|
||||
assert machine_manager.getMachineAction("test_action") == test_action
|
||||
assert machine_manager.getMachineAction("key_that_doesnt_exist") is None
|
||||
assert machine_action_manager.getMachineAction("test_action") == test_action
|
||||
assert machine_action_manager.getMachineAction("key_that_doesnt_exist") is None
|
||||
|
||||
# Adding the same machine action is not allowed.
|
||||
with pytest.raises(NotUniqueMachineActionError):
|
||||
machine_manager.addMachineAction(test_action)
|
||||
machine_action_manager.addMachineAction(test_action)
|
||||
|
||||
# Check that the machine has no supported actions yet.
|
||||
assert machine_manager.getSupportedActions(test_machine) == list()
|
||||
assert machine_action_manager.getSupportedActions(test_machine) == list()
|
||||
|
||||
# Check if adding a supported action works.
|
||||
machine_manager.addSupportedAction(test_machine, "test_action")
|
||||
assert machine_manager.getSupportedActions(test_machine) == [test_action, ]
|
||||
machine_action_manager.addSupportedAction(test_machine, "test_action")
|
||||
assert machine_action_manager.getSupportedActions(test_machine) == [test_action, ]
|
||||
|
||||
# Check that adding a unknown action doesn't change anything.
|
||||
machine_manager.addSupportedAction(test_machine, "key_that_doesnt_exist")
|
||||
assert machine_manager.getSupportedActions(test_machine) == [test_action, ]
|
||||
machine_action_manager.addSupportedAction(test_machine, "key_that_doesnt_exist")
|
||||
assert machine_action_manager.getSupportedActions(test_machine) == [test_action, ]
|
||||
|
||||
# Check if adding multiple supported actions works.
|
||||
machine_manager.addSupportedAction(test_machine, "test_action_2")
|
||||
assert machine_manager.getSupportedActions(test_machine) == [test_action, test_action_2]
|
||||
machine_action_manager.addSupportedAction(test_machine, "test_action_2")
|
||||
assert machine_action_manager.getSupportedActions(test_machine) == [test_action, test_action_2]
|
||||
|
||||
# Check that the machine has no required actions yet.
|
||||
assert machine_manager.getRequiredActions(test_machine) == set()
|
||||
assert machine_action_manager.getRequiredActions(test_machine) == set()
|
||||
|
||||
## Ensure that only known actions can be added.
|
||||
with pytest.raises(UnknownMachineActionError):
|
||||
machine_manager.addRequiredAction(test_machine, "key_that_doesnt_exist")
|
||||
machine_action_manager.addRequiredAction(test_machine, "key_that_doesnt_exist")
|
||||
|
||||
## Check if adding single required action works
|
||||
machine_manager.addRequiredAction(test_machine, "test_action")
|
||||
assert machine_manager.getRequiredActions(test_machine) == [test_action, ]
|
||||
machine_action_manager.addRequiredAction(test_machine, "test_action")
|
||||
assert machine_action_manager.getRequiredActions(test_machine) == [test_action, ]
|
||||
|
||||
# Check if adding multiple required actions works.
|
||||
machine_manager.addRequiredAction(test_machine, "test_action_2")
|
||||
assert machine_manager.getRequiredActions(test_machine) == [test_action, test_action_2]
|
||||
machine_action_manager.addRequiredAction(test_machine, "test_action_2")
|
||||
assert machine_action_manager.getRequiredActions(test_machine) == [test_action, test_action_2]
|
||||
|
||||
# Ensure that firstStart actions are empty by default.
|
||||
assert machine_manager.getFirstStartActions(test_machine) == []
|
||||
assert machine_action_manager.getFirstStartActions(test_machine) == []
|
||||
|
||||
# Check if adding multiple (the same) actions to first start actions work.
|
||||
machine_manager.addFirstStartAction(test_machine, "test_action")
|
||||
machine_manager.addFirstStartAction(test_machine, "test_action")
|
||||
assert machine_manager.getFirstStartActions(test_machine) == [test_action, test_action]
|
||||
machine_action_manager.addFirstStartAction(test_machine, "test_action")
|
||||
machine_action_manager.addFirstStartAction(test_machine, "test_action")
|
||||
assert machine_action_manager.getFirstStartActions(test_machine) == [test_action, test_action]
|
||||
|
||||
# Check if inserting an action works
|
||||
machine_manager.addFirstStartAction(test_machine, "test_action_2", index = 1)
|
||||
assert machine_manager.getFirstStartActions(test_machine) == [test_action, test_action_2, test_action]
|
||||
machine_action_manager.addFirstStartAction(test_machine, "test_action_2", index = 1)
|
||||
assert machine_action_manager.getFirstStartActions(test_machine) == [test_action, test_action_2, test_action]
|
||||
|
||||
# Check that adding a unknown action doesn't change anything.
|
||||
machine_manager.addFirstStartAction(test_machine, "key_that_doesnt_exist", index = 1)
|
||||
assert machine_manager.getFirstStartActions(test_machine) == [test_action, test_action_2, test_action]
|
||||
machine_action_manager.addFirstStartAction(test_machine, "key_that_doesnt_exist", index = 1)
|
||||
assert machine_action_manager.getFirstStartActions(test_machine) == [test_action, test_action_2, test_action]
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import configparser #To read the profiles.
|
||||
import os #To join paths.
|
||||
import pytest
|
||||
@ -11,6 +14,7 @@ import pytest
|
||||
# often that we updated the variants for the UM3 but forgot about the UM3E.
|
||||
@pytest.mark.parametrize("um3_file, um3e_file", [
|
||||
#List the corresponding files below.
|
||||
("ultimaker3_aa0.25.inst.cfg", "ultimaker3_extended_aa0.25.inst.cfg"),
|
||||
("ultimaker3_aa0.8.inst.cfg", "ultimaker3_extended_aa0.8.inst.cfg"),
|
||||
("ultimaker3_aa04.inst.cfg", "ultimaker3_extended_aa04.inst.cfg"),
|
||||
("ultimaker3_bb0.8.inst.cfg", "ultimaker3_extended_bb0.8.inst.cfg"),
|
||||
|
23
tests/conftest.py
Normal file
23
tests/conftest.py
Normal file
@ -0,0 +1,23 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
# The purpose of this class is to create fixtures or methods that can be shared among all tests.
|
||||
|
||||
import pytest
|
||||
from UM.Qt.QtApplication import QtApplication #QtApplication import is required, even though it isn't used.
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from cura.MachineActionManager import MachineActionManager
|
||||
|
||||
# Create a CuraApplication object that will be shared among all tests. It needs to be initialized.
|
||||
# Since we need to use it more that once, we create the application the first time and use its instance afterwards.
|
||||
@pytest.fixture()
|
||||
def application() -> CuraApplication:
|
||||
application = CuraApplication.getInstance()
|
||||
if application is None:
|
||||
application = CuraApplication()
|
||||
return application
|
||||
|
||||
# Returns a MachineActionManager instance.
|
||||
@pytest.fixture()
|
||||
def machine_action_manager(application) -> MachineActionManager:
|
||||
return MachineActionManager(application)
|
Loading…
x
Reference in New Issue
Block a user