Merge branch 'master' of github.com:Ultimaker/Cura into cura_connect_UI_rework

This commit is contained in:
Jaime van Kessel 2018-08-23 12:45:41 +02:00
commit 8c2cd510d8
49 changed files with 561 additions and 671 deletions

1
.gitignore vendored
View File

@ -15,6 +15,7 @@ LC_MESSAGES
.cache .cache
*.qmlc *.qmlc
.mypy_cache .mypy_cache
.pytest_cache
#MacOS #MacOS
.DS_Store .DS_Store

View File

@ -242,6 +242,8 @@ class BuildVolume(SceneNode):
# Mark the node as outside build volume if the set extruder is disabled # Mark the node as outside build volume if the set extruder is disabled
extruder_position = node.callDecoration("getActiveExtruderPosition") 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: if not self._global_container_stack.extruders[extruder_position].isEnabled:
node.setOutsideBuildArea(True) node.setOutsideBuildArea(True)
continue continue
@ -561,7 +563,9 @@ class BuildVolume(SceneNode):
camera = Application.getInstance().getController().getCameraTool() camera = Application.getInstance().getController().getCameraTool()
if camera: 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): def _onEngineCreated(self):
self._engine_ready = True self._engine_ready = True

View File

@ -1,11 +1,10 @@
# Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
import copy
import os import os
import sys import sys
import time import time
from typing import cast, TYPE_CHECKING, Optional from typing import cast, TYPE_CHECKING
import numpy import numpy
@ -106,6 +105,7 @@ from cura.Settings.ExtrudersModel import ExtrudersModel
from cura.Settings.MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler from cura.Settings.MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler
from cura.Settings.ContainerManager import ContainerManager from cura.Settings.ContainerManager import ContainerManager
from cura.Settings.SidebarCustomMenuItemsModel import SidebarCustomMenuItemsModel from cura.Settings.SidebarCustomMenuItemsModel import SidebarCustomMenuItemsModel
import cura.Settings.cura_empty_instance_containers
from cura.ObjectsModel import ObjectsModel from cura.ObjectsModel import ObjectsModel
@ -216,7 +216,6 @@ class CuraApplication(QtApplication):
self._message_box_callback = None self._message_box_callback = None
self._message_box_callback_arguments = [] self._message_box_callback_arguments = []
self._preferred_mimetype = ""
self._i18n_catalog = None self._i18n_catalog = None
self._currently_loading_files = [] self._currently_loading_files = []
@ -369,42 +368,23 @@ class CuraApplication(QtApplication):
# Add empty variant, material and quality containers. # Add empty variant, material and quality containers.
# Since they are empty, they should never be serialized and instead just programmatically created. # Since they are empty, they should never be serialized and instead just programmatically created.
# We need them to simplify the switching between materials. # We need them to simplify the switching between materials.
empty_container = self._container_registry.getEmptyInstanceContainer() self.empty_container = cura.Settings.cura_empty_instance_containers.empty_container
self.empty_container = empty_container
empty_definition_changes_container = copy.deepcopy(empty_container) self._container_registry.addContainer(
empty_definition_changes_container.setMetaDataEntry("id", "empty_definition_changes") cura.Settings.cura_empty_instance_containers.empty_definition_changes_container)
empty_definition_changes_container.setMetaDataEntry("type", "definition_changes") self.empty_definition_changes_container = cura.Settings.cura_empty_instance_containers.empty_definition_changes_container
self._container_registry.addContainer(empty_definition_changes_container)
self.empty_definition_changes_container = empty_definition_changes_container
empty_variant_container = copy.deepcopy(empty_container) self._container_registry.addContainer(cura.Settings.cura_empty_instance_containers.empty_variant_container)
empty_variant_container.setMetaDataEntry("id", "empty_variant") self.empty_variant_container = cura.Settings.cura_empty_instance_containers.empty_variant_container
empty_variant_container.setMetaDataEntry("type", "variant")
self._container_registry.addContainer(empty_variant_container)
self.empty_variant_container = empty_variant_container
empty_material_container = copy.deepcopy(empty_container) self._container_registry.addContainer(cura.Settings.cura_empty_instance_containers.empty_material_container)
empty_material_container.setMetaDataEntry("id", "empty_material") self.empty_material_container = cura.Settings.cura_empty_instance_containers.empty_material_container
empty_material_container.setMetaDataEntry("type", "material")
self._container_registry.addContainer(empty_material_container)
self.empty_material_container = empty_material_container
empty_quality_container = copy.deepcopy(empty_container) self._container_registry.addContainer(cura.Settings.cura_empty_instance_containers.empty_quality_container)
empty_quality_container.setMetaDataEntry("id", "empty_quality") self.empty_quality_container = cura.Settings.cura_empty_instance_containers.empty_quality_container
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
empty_quality_changes_container = copy.deepcopy(empty_container) self._container_registry.addContainer(cura.Settings.cura_empty_instance_containers.empty_quality_changes_container)
empty_quality_changes_container.setMetaDataEntry("id", "empty_quality_changes") self.empty_quality_changes_container = cura.Settings.cura_empty_instance_containers.empty_quality_changes_container
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
# Initializes the version upgrade manager with by providing the paths for each resource type and the latest # Initializes the version upgrade manager with by providing the paths for each resource type and the latest
# versions. # versions.
@ -515,9 +495,6 @@ class CuraApplication(QtApplication):
self.applicationShuttingDown.connect(self.saveSettings) self.applicationShuttingDown.connect(self.saveSettings)
self.engineCreatedSignal.connect(self._onEngineCreated) self.engineCreatedSignal.connect(self._onEngineCreated)
self.globalContainerStackChanged.connect(self._onGlobalContainerChanged)
self._onGlobalContainerChanged()
self.getCuraSceneController().setActiveBuildPlate(0) # Initialize self.getCuraSceneController().setActiveBuildPlate(0) # Initialize
CuraApplication.Created = True CuraApplication.Created = True
@ -782,7 +759,10 @@ class CuraApplication(QtApplication):
# Initialize camera # Initialize camera
root = controller.getScene().getRoot() root = controller.getScene().getRoot()
camera = Camera("3d", root) 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.setPerspective(True)
camera.lookAt(Vector(0, 0, 0)) camera.lookAt(Vector(0, 0, 0))
controller.getScene().setActiveCamera("3d") controller.getScene().setActiveCamera("3d")
@ -999,30 +979,14 @@ class CuraApplication(QtApplication):
self._camera_animation.setTarget(Selection.getSelectedObject(0).getWorldPosition()) self._camera_animation.setTarget(Selection.getSelectedObject(0).getWorldPosition())
self._camera_animation.start() 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() requestAddPrinter = pyqtSignal()
activityChanged = pyqtSignal() activityChanged = pyqtSignal()
sceneBoundingBoxChanged = pyqtSignal() sceneBoundingBoxChanged = pyqtSignal()
preferredOutputMimetypeChanged = pyqtSignal()
@pyqtProperty(bool, notify = activityChanged) @pyqtProperty(bool, notify = activityChanged)
def platformActivity(self): def platformActivity(self):
return self._platform_activity return self._platform_activity
@pyqtProperty(str, notify=preferredOutputMimetypeChanged)
def preferredOutputMimetype(self):
return self._preferred_mimetype
@pyqtProperty(str, notify = sceneBoundingBoxChanged) @pyqtProperty(str, notify = sceneBoundingBoxChanged)
def getSceneBoundingBoxString(self): 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()} 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()}

View File

@ -76,6 +76,8 @@ class OneAtATimeIterator(Iterator):
continue continue
bounding_box = node.getBoundingBox() bounding_box = node.getBoundingBox()
if not bounding_box:
continue
from UM.Math.Polygon import Polygon from UM.Math.Polygon import Polygon
bounding_box_polygon = Polygon([[bounding_box.left, bounding_box.front], bounding_box_polygon = Polygon([[bounding_box.left, bounding_box.front],
[bounding_box.left, bounding_box.back], [bounding_box.left, bounding_box.back],

View File

@ -1,7 +1,7 @@
# Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from typing import Any, cast, List, Optional, Union from typing import Any, cast, List, Optional
from PyQt5.QtCore import pyqtProperty, pyqtSignal, QObject from PyQt5.QtCore import pyqtProperty, pyqtSignal, QObject
from UM.Application import Application from UM.Application import Application
@ -13,6 +13,7 @@ from UM.Settings.InstanceContainer import InstanceContainer
from UM.Settings.DefinitionContainer import DefinitionContainer from UM.Settings.DefinitionContainer import DefinitionContainer
from UM.Settings.ContainerRegistry import ContainerRegistry from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.Interfaces import ContainerInterface, DefinitionContainerInterface from UM.Settings.Interfaces import ContainerInterface, DefinitionContainerInterface
from cura.Settings import cura_empty_instance_containers
from . import Exceptions from . import Exceptions
@ -39,14 +40,12 @@ class CuraContainerStack(ContainerStack):
def __init__(self, container_id: str) -> None: def __init__(self, container_id: str) -> None:
super().__init__(container_id) 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 = cura_empty_instance_containers.empty_quality_changes_container #type: InstanceContainer
self._empty_quality = cura_empty_instance_containers.empty_quality_container #type: InstanceContainer
self._empty_quality_changes = self._container_registry.findInstanceContainers(id = "empty_quality_changes")[0] #type: InstanceContainer self._empty_material = cura_empty_instance_containers.empty_material_container #type: InstanceContainer
self._empty_quality = self._container_registry.findInstanceContainers(id = "empty_quality")[0] #type: InstanceContainer self._empty_variant = cura_empty_instance_containers.empty_variant_container #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._containers = [self._empty_instance_container for i in range(len(_ContainerIndexes.IndexTypeMap))] #type: List[ContainerInterface] self._containers = [self._empty_instance_container for i in range(len(_ContainerIndexes.IndexTypeMap))] #type: List[ContainerInterface]
self._containers[_ContainerIndexes.QualityChanges] = self._empty_quality_changes self._containers[_ContainerIndexes.QualityChanges] = self._empty_quality_changes

View File

@ -108,6 +108,17 @@ class CuraStackBuilder:
preferred_quality_type = machine_definition.getMetaDataEntry("preferred_quality_type") preferred_quality_type = machine_definition.getMetaDataEntry("preferred_quality_type")
quality_group_dict = quality_manager.getQualityGroups(new_global_stack) quality_group_dict = quality_manager.getQualityGroups(new_global_stack)
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 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) quality_group = quality_group_dict.get(preferred_quality_type)
new_global_stack.quality = quality_group.node_for_global.getContainer() new_global_stack.quality = quality_group.node_for_global.getContainer()

View File

@ -139,9 +139,6 @@ class ExtruderStack(CuraContainerStack):
super().deserialize(contents, file_name) super().deserialize(contents, file_name)
if "enabled" not in self.getMetaData(): if "enabled" not in self.getMetaData():
self.setMetaDataEntry("enabled", "True") 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: 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, # When there is a setting that is not settable per extruder that depends on a value from a setting that is,

View File

@ -61,6 +61,10 @@ class GlobalStack(CuraContainerStack):
name = self.variant.getName() name = self.variant.getName()
return name 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. ## Add an extruder to the list of extruders of this stack.
# #
# \param extruder The extruder to add. # \param extruder The extruder to add.

View 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"
]

View File

@ -630,6 +630,11 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
type = "extruder_train") type = "extruder_train")
extruder_stack_dict = {stack.getMetaDataEntry("position"): stack for stack in extruder_stacks} 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...") Logger.log("d", "Workspace loading is checking definitions...")
# Get all the definition files & check if they exist. If not, add them. # 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)] definition_container_files = [name for name in cura_file_names if name.endswith(self._definition_container_suffix)]

View File

@ -17,6 +17,10 @@ catalog = i18nCatalog("cura")
# #
# If you're zipping g-code, you might as well use gzip! # If you're zipping g-code, you might as well use gzip!
class GCodeGzWriter(MeshWriter): class GCodeGzWriter(MeshWriter):
def __init__(self) -> None:
super().__init__(add_to_recent_files = False)
## Writes the gzipped g-code to a stream. ## Writes the gzipped g-code to a stream.
# #
# Note that even though the function accepts a collection of nodes, the # Note that even though the function accepts a collection of nodes, the

View File

@ -16,7 +16,8 @@ def getMetaData():
"extension": file_extension, "extension": file_extension,
"description": catalog.i18nc("@item:inlistbox", "Compressed G-code File"), "description": catalog.i18nc("@item:inlistbox", "Compressed G-code File"),
"mime_type": "application/gzip", "mime_type": "application/gzip",
"mode": GCodeGzWriter.GCodeGzWriter.OutputMode.BinaryMode "mode": GCodeGzWriter.GCodeGzWriter.OutputMode.BinaryMode,
"hide_in_file_dialog": True,
}] }]
} }
} }

View File

@ -47,7 +47,7 @@ class GCodeWriter(MeshWriter):
_setting_keyword = ";SETTING_" _setting_keyword = ";SETTING_"
def __init__(self): def __init__(self):
super().__init__() super().__init__(add_to_recent_files = False)
self._application = Application.getInstance() self._application = Application.getInstance()

View File

@ -28,7 +28,7 @@ class PauseAtHeight(Script):
"pause_height": "pause_height":
{ {
"label": "Pause Height", "label": "Pause Height",
"description": "At what height should the pause occur", "description": "At what height should the pause occur?",
"unit": "mm", "unit": "mm",
"type": "float", "type": "float",
"default_value": 5.0, "default_value": 5.0,
@ -39,7 +39,7 @@ class PauseAtHeight(Script):
"pause_layer": "pause_layer":
{ {
"label": "Pause Layer", "label": "Pause Layer",
"description": "At what layer should the pause occur", "description": "At what layer should the pause occur?",
"type": "int", "type": "int",
"value": "math.floor((pause_height - 0.27) / 0.1) + 1", "value": "math.floor((pause_height - 0.27) / 0.1) + 1",
"minimum_value": "0", "minimum_value": "0",
@ -142,13 +142,14 @@ class PauseAtHeight(Script):
standby_temperature = self.getSettingValueByKey("standby_temperature") standby_temperature = self.getSettingValueByKey("standby_temperature")
firmware_retract = Application.getInstance().getGlobalContainerStack().getProperty("machine_firmware_retract", "value") firmware_retract = Application.getInstance().getGlobalContainerStack().getProperty("machine_firmware_retract", "value")
control_temperatures = Application.getInstance().getGlobalContainerStack().getProperty("machine_nozzle_temp_enabled", "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 is_griffin = False
# T = ExtruderManager.getInstance().getActiveExtruderStack().getProperty("material_print_temperature", "value") # T = ExtruderManager.getInstance().getActiveExtruderStack().getProperty("material_print_temperature", "value")
# use offset to calculate the current height: <current_height> = <current_z> - <layer_0_z> # 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 current_z = 0
got_first_g_cmd_on_layer_0 = False got_first_g_cmd_on_layer_0 = False
current_t = 0 #Tracks the current extruder for tracking the target temperature. 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 # This block is executed once, the first time there is a G
# command, to get the z offset (z for first positive layer) # command, to get the z offset (z for first positive layer)
if not got_first_g_cmd_on_layer_0: 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 got_first_g_cmd_on_layer_0 = True
current_height = current_z - layer_0_z current_height = current_z - layer_0_z
if current_height < pause_height: if current_height < pause_height:
break # Try the next layer. break # Try the next layer.

View File

@ -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. // Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.4 import QtQuick 2.4
@ -12,30 +12,43 @@ import Cura 1.0 as Cura
Item Item
{ {
id: base id: base
width: { width:
if (UM.SimulationView.compatibilityMode) { {
if (UM.SimulationView.compatibilityMode)
{
return UM.Theme.getSize("layerview_menu_size_compatibility").width; return UM.Theme.getSize("layerview_menu_size_compatibility").width;
} else { }
else
{
return UM.Theme.getSize("layerview_menu_size").width; return UM.Theme.getSize("layerview_menu_size").width;
} }
} }
height: { height: {
if (viewSettings.collapsed) { if (viewSettings.collapsed)
if (UM.SimulationView.compatibilityMode) { {
if (UM.SimulationView.compatibilityMode)
{
return UM.Theme.getSize("layerview_menu_size_compatibility_collapsed").height; return UM.Theme.getSize("layerview_menu_size_compatibility_collapsed").height;
} }
return UM.Theme.getSize("layerview_menu_size_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; 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) 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) 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 } } Behavior on height { NumberAnimation { duration: 100 } }
property var buttonTarget: { property var buttonTarget:
{
if(parent != null) if(parent != null)
{ {
var force_binding = parent.y; // ensure this gets reevaluated when the panel moves var force_binding = parent.y; // ensure this gets reevaluated when the panel moves
@ -44,7 +57,8 @@ Item
return Qt.point(0,0) return Qt.point(0,0)
} }
Rectangle { Rectangle
{
id: layerViewMenu id: layerViewMenu
anchors.right: parent.right anchors.right: parent.right
anchors.top: parent.top anchors.top: parent.top
@ -83,7 +97,8 @@ Item
} }
} }
ColumnLayout { ColumnLayout
{
id: viewSettings id: viewSettings
property bool collapsed: false property bool collapsed: false
@ -195,7 +210,8 @@ Item
width: width width: width
} }
Connections { Connections
{
target: UM.Preferences target: UM.Preferences
onPreferenceChanged: onPreferenceChanged:
{ {
@ -212,18 +228,22 @@ Item
} }
} }
Repeater { Repeater
{
model: Cura.ExtrudersModel{} model: Cura.ExtrudersModel{}
CheckBox { CheckBox
{
id: extrudersModelCheckBox id: extrudersModelCheckBox
checked: viewSettings.extruder_opacities[index] > 0.5 || viewSettings.extruder_opacities[index] == undefined || viewSettings.extruder_opacities[index] == "" 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 viewSettings.extruder_opacities[index] = checked ? 1.0 : 0.0
UM.Preferences.setValue("layerview/extruder_opacities", viewSettings.extruder_opacities.join("|")); UM.Preferences.setValue("layerview/extruder_opacities", viewSettings.extruder_opacities.join("|"));
} }
visible: !UM.SimulationView.compatibilityMode visible: !UM.SimulationView.compatibilityMode
enabled: index + 1 <= 4 enabled: index + 1 <= 4
Rectangle { Rectangle
{
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.right: extrudersModelCheckBox.right anchors.right: extrudersModelCheckBox.right
width: UM.Theme.getSize("layerview_legend_size").width width: UM.Theme.getSize("layerview_legend_size").width
@ -253,8 +273,10 @@ Item
} }
} }
Repeater { Repeater
model: ListModel { {
model: ListModel
{
id: typesLegendModel id: typesLegendModel
Component.onCompleted: Component.onCompleted:
{ {
@ -285,13 +307,16 @@ Item
} }
} }
CheckBox { CheckBox
{
id: legendModelCheckBox id: legendModelCheckBox
checked: model.initialValue checked: model.initialValue
onClicked: { onClicked:
{
UM.Preferences.setValue(model.preference, checked); UM.Preferences.setValue(model.preference, checked);
} }
Rectangle { Rectangle
{
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.right: legendModelCheckBox.right anchors.right: legendModelCheckBox.right
width: UM.Theme.getSize("layerview_legend_size").width width: UM.Theme.getSize("layerview_legend_size").width
@ -320,18 +345,22 @@ Item
} }
} }
CheckBox { CheckBox
{
checked: viewSettings.only_show_top_layers checked: viewSettings.only_show_top_layers
onClicked: { onClicked:
{
UM.Preferences.setValue("view/only_show_top_layers", checked ? 1.0 : 0.0); UM.Preferences.setValue("view/only_show_top_layers", checked ? 1.0 : 0.0);
} }
text: catalog.i18nc("@label", "Only Show Top Layers") text: catalog.i18nc("@label", "Only Show Top Layers")
visible: UM.SimulationView.compatibilityMode visible: UM.SimulationView.compatibilityMode
style: UM.Theme.styles.checkbox style: UM.Theme.styles.checkbox
} }
CheckBox { CheckBox
{
checked: viewSettings.top_layer_count == 5 checked: viewSettings.top_layer_count == 5
onClicked: { onClicked:
{
UM.Preferences.setValue("view/top_layer_count", checked ? 5 : 1); UM.Preferences.setValue("view/top_layer_count", checked ? 5 : 1);
} }
text: catalog.i18nc("@label", "Show 5 Detailed Layers On Top") text: catalog.i18nc("@label", "Show 5 Detailed Layers On Top")
@ -339,8 +368,10 @@ Item
style: UM.Theme.styles.checkbox style: UM.Theme.styles.checkbox
} }
Repeater { Repeater
model: ListModel { {
model: ListModel
{
id: typesLegendModelNoCheck id: typesLegendModelNoCheck
Component.onCompleted: Component.onCompleted:
{ {
@ -355,11 +386,13 @@ Item
} }
} }
Label { Label
{
text: label text: label
visible: viewSettings.show_legend visible: viewSettings.show_legend
id: typesLegendModelLabel id: typesLegendModelLabel
Rectangle { Rectangle
{
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.right: typesLegendModelLabel.right anchors.right: typesLegendModelLabel.right
width: UM.Theme.getSize("layerview_legend_size").width 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 // Text for the minimum, maximum and units for the feedrates and layer thickness
Item { Item
{
id: gradientLegend id: gradientLegend
visible: viewSettings.show_gradient visible: viewSettings.show_gradient
width: parent.width width: parent.width
height: UM.Theme.getSize("layerview_row").height height: UM.Theme.getSize("layerview_row").height
anchors { anchors
{
topMargin: UM.Theme.getSize("slider_layerview_margin").height topMargin: UM.Theme.getSize("slider_layerview_margin").height
horizontalCenter: parent.horizontalCenter horizontalCenter: parent.horizontalCenter
} }
Label { Label
{
text: minText() text: minText()
anchors.left: parent.left anchors.left: parent.left
color: UM.Theme.getColor("setting_control_text") color: UM.Theme.getColor("setting_control_text")
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
function minText() { function minText()
if (UM.SimulationView.layerActivity && CuraApplication.platformActivity) { {
if (UM.SimulationView.layerActivity && CuraApplication.platformActivity)
{
// Feedrate selected // 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) return parseFloat(UM.SimulationView.getMinFeedrate()).toFixed(2)
} }
// Layer thickness selected // 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) return parseFloat(UM.SimulationView.getMinThickness()).toFixed(2)
} }
} }
@ -409,20 +449,25 @@ Item
} }
} }
Label { Label
{
text: unitsText() text: unitsText()
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
color: UM.Theme.getColor("setting_control_text") color: UM.Theme.getColor("setting_control_text")
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
function unitsText() { function unitsText()
if (UM.SimulationView.layerActivity && CuraApplication.platformActivity) { {
if (UM.SimulationView.layerActivity && CuraApplication.platformActivity)
{
// Feedrate selected // Feedrate selected
if (UM.Preferences.getValue("layerview/layer_view_type") == 2) { if (UM.Preferences.getValue("layerview/layer_view_type") == 2)
{
return "mm/s" return "mm/s"
} }
// Layer thickness selected // Layer thickness selected
if (UM.Preferences.getValue("layerview/layer_view_type") == 3) { if (UM.Preferences.getValue("layerview/layer_view_type") == 3)
{
return "mm" return "mm"
} }
} }
@ -430,20 +475,25 @@ Item
} }
} }
Label { Label
{
text: maxText() text: maxText()
anchors.right: parent.right anchors.right: parent.right
color: UM.Theme.getColor("setting_control_text") color: UM.Theme.getColor("setting_control_text")
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
function maxText() { function maxText()
if (UM.SimulationView.layerActivity && CuraApplication.platformActivity) { {
if (UM.SimulationView.layerActivity && CuraApplication.platformActivity)
{
// Feedrate selected // 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) return parseFloat(UM.SimulationView.getMaxFeedrate()).toFixed(2)
} }
// Layer thickness selected // 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) return parseFloat(UM.SimulationView.getMaxThickness()).toFixed(2)
} }
} }
@ -453,7 +503,8 @@ Item
} }
// Gradient colors for feedrate // 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 // Invert values because then the bar is rotated 90 degrees
id: feedrateGradient id: feedrateGradient
visible: viewSettings.show_feedrate_gradient visible: viewSettings.show_feedrate_gradient
@ -463,20 +514,25 @@ Item
border.width: UM.Theme.getSize("default_lining").width border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("lining") border.color: UM.Theme.getColor("lining")
transform: Rotation {origin.x: 0; origin.y: 0; angle: 90} transform: Rotation {origin.x: 0; origin.y: 0; angle: 90}
gradient: Gradient { gradient: Gradient
GradientStop { {
GradientStop
{
position: 0.000 position: 0.000
color: Qt.rgba(1, 0.5, 0, 1) color: Qt.rgba(1, 0.5, 0, 1)
} }
GradientStop { GradientStop
{
position: 0.625 position: 0.625
color: Qt.rgba(0.375, 0.5, 0, 1) color: Qt.rgba(0.375, 0.5, 0, 1)
} }
GradientStop { GradientStop
{
position: 0.75 position: 0.75
color: Qt.rgba(0.25, 1, 0, 1) color: Qt.rgba(0.25, 1, 0, 1)
} }
GradientStop { GradientStop
{
position: 1.0 position: 1.0
color: Qt.rgba(0, 0, 1, 1) color: Qt.rgba(0, 0, 1, 1)
} }
@ -484,7 +540,8 @@ Item
} }
// Gradient colors for layer thickness (similar to parula colormap) // 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 // Invert values because then the bar is rotated 90 degrees
id: thicknessGradient id: thicknessGradient
visible: viewSettings.show_thickness_gradient visible: viewSettings.show_thickness_gradient
@ -494,24 +551,30 @@ Item
border.width: UM.Theme.getSize("default_lining").width border.width: UM.Theme.getSize("default_lining").width
border.color: UM.Theme.getColor("lining") border.color: UM.Theme.getColor("lining")
transform: Rotation {origin.x: 0; origin.y: 0; angle: 90} transform: Rotation {origin.x: 0; origin.y: 0; angle: 90}
gradient: Gradient { gradient: Gradient
GradientStop { {
GradientStop
{
position: 0.000 position: 0.000
color: Qt.rgba(1, 1, 0, 1) color: Qt.rgba(1, 1, 0, 1)
} }
GradientStop { GradientStop
{
position: 0.25 position: 0.25
color: Qt.rgba(1, 0.75, 0.25, 1) color: Qt.rgba(1, 0.75, 0.25, 1)
} }
GradientStop { GradientStop
{
position: 0.5 position: 0.5
color: Qt.rgba(0, 0.75, 0.5, 1) color: Qt.rgba(0, 0.75, 0.5, 1)
} }
GradientStop { GradientStop
{
position: 0.75 position: 0.75
color: Qt.rgba(0, 0.375, 0.75, 1) color: Qt.rgba(0, 0.375, 0.75, 1)
} }
GradientStop { GradientStop
{
position: 1.0 position: 1.0
color: Qt.rgba(0, 0, 0.5, 1) color: Qt.rgba(0, 0, 0.5, 1)
} }
@ -520,19 +583,22 @@ Item
} }
} }
Item { Item
{
id: slidersBox id: slidersBox
width: parent.width width: parent.width
visible: UM.SimulationView.layerActivity && CuraApplication.platformActivity visible: UM.SimulationView.layerActivity && CuraApplication.platformActivity
anchors { anchors
{
top: parent.bottom top: parent.bottom
topMargin: UM.Theme.getSize("slider_layerview_margin").height topMargin: UM.Theme.getSize("slider_layerview_margin").height
left: parent.left left: parent.left
} }
PathSlider { PathSlider
{
id: pathSlider id: pathSlider
height: UM.Theme.getSize("slider_handle").width height: UM.Theme.getSize("slider_handle").width
@ -553,25 +619,29 @@ Item
rangeColor: UM.Theme.getColor("slider_groove_fill") rangeColor: UM.Theme.getColor("slider_groove_fill")
// update values when layer data changes // update values when layer data changes
Connections { Connections
{
target: UM.SimulationView target: UM.SimulationView
onMaxPathsChanged: pathSlider.setHandleValue(UM.SimulationView.currentPath) onMaxPathsChanged: pathSlider.setHandleValue(UM.SimulationView.currentPath)
onCurrentPathChanged: pathSlider.setHandleValue(UM.SimulationView.currentPath) onCurrentPathChanged: pathSlider.setHandleValue(UM.SimulationView.currentPath)
} }
// make sure the slider handlers show the correct value after switching views // make sure the slider handlers show the correct value after switching views
Component.onCompleted: { Component.onCompleted:
{
pathSlider.setHandleValue(UM.SimulationView.currentPath) pathSlider.setHandleValue(UM.SimulationView.currentPath)
} }
} }
LayerSlider { LayerSlider
{
id: layerSlider id: layerSlider
width: UM.Theme.getSize("slider_handle").width width: UM.Theme.getSize("slider_handle").width
height: UM.Theme.getSize("layerview_menu_size").height height: UM.Theme.getSize("layerview_menu_size").height
anchors { anchors
{
top: !UM.SimulationView.compatibilityMode ? pathSlider.bottom : parent.top top: !UM.SimulationView.compatibilityMode ? pathSlider.bottom : parent.top
topMargin: !UM.SimulationView.compatibilityMode ? UM.Theme.getSize("default_margin").height : 0 topMargin: !UM.SimulationView.compatibilityMode ? UM.Theme.getSize("default_margin").height : 0
right: parent.right right: parent.right
@ -593,7 +663,8 @@ Item
handleLabelWidth: UM.Theme.getSize("slider_layerview_background").width handleLabelWidth: UM.Theme.getSize("slider_layerview_background").width
// update values when layer data changes // update values when layer data changes
Connections { Connections
{
target: UM.SimulationView target: UM.SimulationView
onMaxLayersChanged: layerSlider.setUpperValue(UM.SimulationView.currentLayer) onMaxLayersChanged: layerSlider.setUpperValue(UM.SimulationView.currentLayer)
onMinimumLayerChanged: layerSlider.setLowerValue(UM.SimulationView.minimumLayer) onMinimumLayerChanged: layerSlider.setLowerValue(UM.SimulationView.minimumLayer)
@ -601,45 +672,54 @@ Item
} }
// make sure the slider handlers show the correct value after switching views // make sure the slider handlers show the correct value after switching views
Component.onCompleted: { Component.onCompleted:
{
layerSlider.setLowerValue(UM.SimulationView.minimumLayer) layerSlider.setLowerValue(UM.SimulationView.minimumLayer)
layerSlider.setUpperValue(UM.SimulationView.currentLayer) layerSlider.setUpperValue(UM.SimulationView.currentLayer)
} }
} }
// Play simulation button // Play simulation button
Button { Button
{
id: playButton id: playButton
iconSource: "./resources/simulation_resume.svg" iconSource: "./resources/simulation_resume.svg"
style: UM.Theme.styles.small_tool_button style: UM.Theme.styles.small_tool_button
visible: !UM.SimulationView.compatibilityMode visible: !UM.SimulationView.compatibilityMode
anchors { anchors
{
verticalCenter: pathSlider.verticalCenter verticalCenter: pathSlider.verticalCenter
} }
property var status: 0 // indicates if it's stopped (0) or playing (1) property var status: 0 // indicates if it's stopped (0) or playing (1)
onClicked: { onClicked:
switch(status) { {
case 0: { switch(status)
{
case 0:
{
resumeSimulation() resumeSimulation()
break break
} }
case 1: { case 1:
{
pauseSimulation() pauseSimulation()
break break
} }
} }
} }
function pauseSimulation() { function pauseSimulation()
{
UM.SimulationView.setSimulationRunning(false) UM.SimulationView.setSimulationRunning(false)
iconSource = "./resources/simulation_resume.svg" iconSource = "./resources/simulation_resume.svg"
simulationTimer.stop() simulationTimer.stop()
status = 0 status = 0
} }
function resumeSimulation() { function resumeSimulation()
{
UM.SimulationView.setSimulationRunning(true) UM.SimulationView.setSimulationRunning(true)
iconSource = "./resources/simulation_pause.svg" iconSource = "./resources/simulation_pause.svg"
simulationTimer.start() simulationTimer.start()
@ -652,7 +732,8 @@ Item
interval: 100 interval: 100
running: false running: false
repeat: true repeat: true
onTriggered: { onTriggered:
{
var currentPath = UM.SimulationView.currentPath var currentPath = UM.SimulationView.currentPath
var numPaths = UM.SimulationView.numPaths var numPaths = UM.SimulationView.numPaths
var currentLayer = UM.SimulationView.currentLayer var currentLayer = UM.SimulationView.currentLayer
@ -697,7 +778,8 @@ Item
} }
} }
FontMetrics { FontMetrics
{
id: fontMetrics id: fontMetrics
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
} }

View File

@ -20,7 +20,7 @@ catalog = i18nCatalog("cura")
class UFPWriter(MeshWriter): class UFPWriter(MeshWriter):
def __init__(self): def __init__(self):
super().__init__() super().__init__(add_to_recent_files = False)
self._snapshot = None self._snapshot = None
Application.getInstance().getOutputDeviceManager().writeStarted.connect(self._createSnapshot) Application.getInstance().getOutputDeviceManager().writeStarted.connect(self._createSnapshot)

View File

@ -8,6 +8,9 @@ from UM.VersionUpgrade import VersionUpgrade
deleted_settings = {"prime_tower_wall_thickness", "dual_pre_wipe", "prime_tower_purge_volume"} 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 = { _RENAMED_MATERIAL_PROFILES = {
"dsm_arnitel2045_175_cartesio_0.25_mm": "dsm_arnitel2045_175_cartesio_0.25mm_thermoplastic_extruder", "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", "dsm_arnitel2045_175_cartesio_0.4_mm": "dsm_arnitel2045_175_cartesio_0.4mm_thermoplastic_extruder",
@ -127,6 +130,13 @@ class VersionUpgrade34to40(VersionUpgrade):
continue continue
del parser["values"][deleted_setting] 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() result = io.StringIO()
parser.write(result) parser.write(result)
return [filename], [result.getvalue()] return [filename], [result.getvalue()]

View File

@ -3356,13 +3356,14 @@
"retraction_combing": "retraction_combing":
{ {
"label": "Combing Mode", "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", "type": "enum",
"options": "options":
{ {
"off": "Off", "off": "Off",
"all": "All", "all": "All",
"noskin": "Not in Skin" "noskin": "Not in Skin",
"infill": "Within Infill"
}, },
"default_value": "all", "default_value": "all",
"resolve": "'noskin' if 'noskin' in extruderValues('retraction_combing') else ('all' if 'all' in extruderValues('retraction_combing') else 'off')", "resolve": "'noskin' if 'noskin' in extruderValues('retraction_combing') else ('all' if 'all' in extruderValues('retraction_combing') else 'off')",
@ -5073,7 +5074,7 @@
"unit": "mm", "unit": "mm",
"enabled": "resolveOrValue('prime_tower_enable')", "enabled": "resolveOrValue('prime_tower_enable')",
"default_value": 200, "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", "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')", "minimum_value": "resolveOrValue('prime_tower_size') - machine_width / 2 if machine_center_is_zero else resolveOrValue('prime_tower_size')",
"settable_per_mesh": false, "settable_per_mesh": false,
@ -5087,7 +5088,7 @@
"unit": "mm", "unit": "mm",
"enabled": "resolveOrValue('prime_tower_enable')", "enabled": "resolveOrValue('prime_tower_enable')",
"default_value": 200, "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')", "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", "minimum_value": "machine_depth / -2 if machine_center_is_zero else 0",
"settable_per_mesh": false, "settable_per_mesh": false,
@ -5626,6 +5627,19 @@
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true "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": "meshfix_maximum_resolution":
{ {
"label": "Maximum Resolution", "label": "Maximum Resolution",

View File

@ -120,7 +120,7 @@ UM.MainWindow
text: catalog.i18nc("@title:menu menubar:file","&Save...") text: catalog.i18nc("@title:menu menubar:file","&Save...")
onTriggered: 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")) if(UM.Preferences.getValue("cura/dialog_on_project_save"))
{ {
saveWorkspaceDialog.args = args; saveWorkspaceDialog.args = args;
@ -142,7 +142,7 @@ UM.MainWindow
onTriggered: onTriggered:
{ {
var localDeviceId = "local_file"; 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..."); text: catalog.i18nc("@action:inmenu menubar:file", "Export Selection...");
enabled: UM.Selection.hasSelection; enabled: UM.Selection.hasSelection;
iconName: "document-save-as"; 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 { } MenuSeparator { }

View File

@ -30,20 +30,24 @@ TabView
property bool reevaluateLinkedMaterials: false property bool reevaluateLinkedMaterials: false
property string linkedMaterialNames: property string linkedMaterialNames:
{ {
if (reevaluateLinkedMaterials) { if (reevaluateLinkedMaterials)
{
reevaluateLinkedMaterials = false; reevaluateLinkedMaterials = false;
} }
if (!base.containerId || !base.editingEnabled) { if (!base.containerId || !base.editingEnabled)
{
return "" return ""
} }
var linkedMaterials = Cura.ContainerManager.getLinkedMaterials(base.currentMaterialNode, true); var linkedMaterials = Cura.ContainerManager.getLinkedMaterials(base.currentMaterialNode, true);
if (linkedMaterials.length == 0) { if (linkedMaterials.length == 0)
{
return "" return ""
} }
return linkedMaterials.join(", "); return linkedMaterials.join(", ");
} }
function getApproximateDiameter(diameter) { function getApproximateDiameter(diameter)
{
return Math.round(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") } Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Color") }
Row { Row
{
width: scrollView.columnWidth width: scrollView.columnWidth
height: parent.rowHeight height: parent.rowHeight
spacing: Math.round(UM.Theme.getSize("default_margin").width / 2) spacing: Math.round(UM.Theme.getSize("default_margin").width / 2)
// color indicator square // color indicator square
Rectangle { Rectangle
{
id: colorSelector id: colorSelector
color: properties.color_code color: properties.color_code
@ -171,7 +177,8 @@ TabView
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
// open the color selection dialog on click // open the color selection dialog on click
MouseArea { MouseArea
{
anchors.fill: parent anchors.fill: parent
onClicked: colorDialog.open() onClicked: colorDialog.open()
enabled: base.editingEnabled enabled: base.editingEnabled
@ -179,7 +186,8 @@ TabView
} }
// pretty color name text field // pretty color name text field
ReadOnlyTextField { ReadOnlyTextField
{
id: colorLabel; id: colorLabel;
text: properties.color_name; text: properties.color_name;
readOnly: !base.editingEnabled readOnly: !base.editingEnabled
@ -188,7 +196,8 @@ TabView
// popup dialog to select a new color // popup dialog to select a new color
// if successful it sets the properties.color_code value to the new color // if successful it sets the properties.color_code value to the new color
ColorDialog { ColorDialog
{
id: colorDialog id: colorDialog
color: properties.color_code color: properties.color_code
onAccepted: base.setMetaDataEntry("color_code", properties.color_code, color) onAccepted: base.setMetaDataEntry("color_code", properties.color_code, color)
@ -258,7 +267,8 @@ TabView
decimals: 2 decimals: 2
maximumValue: 100000000 maximumValue: 100000000
onValueChanged: { onValueChanged:
{
base.setMaterialPreferenceValue(properties.guid, "spool_cost", parseFloat(value)) base.setMaterialPreferenceValue(properties.guid, "spool_cost", parseFloat(value))
updateCostPerMeter() updateCostPerMeter()
} }
@ -275,7 +285,8 @@ TabView
decimals: 0 decimals: 0
maximumValue: 10000 maximumValue: 10000
onValueChanged: { onValueChanged:
{
base.setMaterialPreferenceValue(properties.guid, "spool_weight", parseFloat(value)) base.setMaterialPreferenceValue(properties.guid, "spool_weight", parseFloat(value))
updateCostPerMeter() updateCostPerMeter()
} }
@ -401,7 +412,8 @@ TabView
{ {
id: spinBox id: spinBox
anchors.left: label.right anchors.left: label.right
value: { value:
{
// In case the setting is not in the material... // In case the setting is not in the material...
if (!isNaN(parseFloat(materialPropertyProvider.properties.value))) 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. // Tiny convenience function to check if a value really changed before trying to set it.
function setMetaDataEntry(entry_name, old_value, new_value) { function setMetaDataEntry(entry_name, old_value, new_value)
if (old_value != new_value) { {
if (old_value != new_value)
{
Cura.ContainerManager.setContainerMetaDataEntry(base.currentMaterialNode, entry_name, 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 // 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 // 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 // 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 // don't change when new name is the same
if (old_name == new_name) { if (old_name == new_name)
{
return; return;
} }

View File

@ -7,6 +7,7 @@ import QtQuick.Controls.Styles 1.1
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.1
import UM 1.1 as UM import UM 1.1 as UM
import Cura 1.0 as Cura
Item { Item {
id: base; id: base;
@ -257,7 +258,8 @@ Item {
onClicked: onClicked:
{ {
forceActiveFocus(); 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 { style: ButtonStyle {

View File

@ -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. # 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 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 unittest.mock #To mock and monkeypatch stuff.
import urllib.parse
import copy
import cura.CuraApplication from UM.Settings.DefinitionContainer import DefinitionContainer
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry #The class we're testing.
from cura.Settings.ExtruderStack import ExtruderStack #Testing for returning the correct types of stacks. 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 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.InstanceContainer #Creating instance containers to register.
import UM.Settings.ContainerRegistry #Making empty container stacks. import UM.Settings.ContainerRegistry #Making empty container stacks.
import UM.Settings.ContainerStack #Setting the container registry here properly. 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(): def teardown():
#If the temporary file for the legacy file rename test still exists, remove it. #If the temporary file for the legacy file rename test still exists, remove it.
@ -64,44 +18,47 @@ def teardown():
os.remove(temporary_file) os.remove(temporary_file)
## Tests whether addContainer properly converts to ExtruderStack. ## Tests whether addContainer properly converts to ExtruderStack.
def test_addContainerExtruderStack(container_registry, definition_container): def test_addContainerExtruderStack(container_registry, definition_container, definition_changes_container):
creteEmptyContainers()
container_registry.addContainer(definition_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.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(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. 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): with unittest.mock.patch("UM.Settings.ContainerRegistry.ContainerRegistry.addContainer", mock_super_add_container):
container_registry.addContainer(container_stack) 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 #Called only once.
assert len(mock_super_add_container.call_args_list[1][0]) == 1 #Called with one parameter. 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[1][0][0]) == ExtruderStack assert type(mock_super_add_container.call_args_list[0][0][0]) == ExtruderStack
## Tests whether addContainer properly converts to GlobalStack. ## 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_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.setMetaDataEntry("type", "machine") #This is now a global stack.
container_stack.insertContainer(0, definition_container) #Must have a definition. 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. 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): with unittest.mock.patch("UM.Settings.ContainerRegistry.ContainerRegistry.addContainer", mock_super_add_container):
container_registry.addContainer(container_stack) 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 #Called only once.
assert len(mock_super_add_container.call_args_list[1][0]) == 1 #Called with one parameter. 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[1][0][0]) == GlobalStack assert type(mock_super_add_container.call_args_list[0][0][0]) == GlobalStack
def test_addContainerGoodSettingVersion(container_registry, definition_container): def test_addContainerGoodSettingVersion(container_registry, definition_container):
from cura.CuraApplication import CuraApplication from cura.CuraApplication import CuraApplication
definition_container.getMetaData()["setting_version"] = CuraApplication.SettingVersion definition_container.getMetaData()["setting_version"] = CuraApplication.SettingVersion
container_registry.addContainer(definition_container) 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.setMetaDataEntry("setting_version", CuraApplication.SettingVersion)
instance.setDefinition(definition_container.getId()) instance.setDefinition(definition_container.getId())
@ -116,7 +73,7 @@ def test_addContainerNoSettingVersion(container_registry, definition_container):
definition_container.getMetaData()["setting_version"] = CuraApplication.SettingVersion definition_container.getMetaData()["setting_version"] = CuraApplication.SettingVersion
container_registry.addContainer(definition_container) 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. #Don't add setting_version metadata.
instance.setDefinition(definition_container.getId()) instance.setDefinition(definition_container.getId())
@ -131,7 +88,7 @@ def test_addContainerBadSettingVersion(container_registry, definition_container)
definition_container.getMetaData()["setting_version"] = CuraApplication.SettingVersion definition_container.getMetaData()["setting_version"] = CuraApplication.SettingVersion
container_registry.addContainer(definition_container) 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.setMetaDataEntry("setting_version", 9001) #Wrong version!
instance.setDefinition(definition_container.getId()) instance.setDefinition(definition_container.getId())
@ -140,38 +97,3 @@ def test_addContainerBadSettingVersion(container_registry, definition_container)
container_registry.addContainer(instance) 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! 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.

View File

@ -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. # Cura is released under the terms of the LGPLv3 or higher.
import pytest #This module contains automated tests. import pytest #This module contains automated tests.
import unittest.mock #For the mocking and monkeypatching functionality. 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.ContainerRegistry #To create empty instance containers.
import UM.Settings.ContainerStack #To set the container registry the container stacks use. 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.DefinitionContainer import DefinitionContainer #To check against the class of DefinitionContainer.
from UM.Settings.InstanceContainer import InstanceContainer #To check against the class of InstanceContainer. 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.Exceptions import InvalidContainerError, InvalidOperationError #To check whether the correct exceptions are raised.
from cura.Settings.ExtruderManager import ExtruderManager from cura.Settings.ExtruderManager import ExtruderManager
from UM.Settings.ContainerRegistry import ContainerRegistry from cura.Settings.cura_empty_instance_containers import empty_container
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")
## Gets an instance container with a specified container type. ## Gets an instance container with a specified container type.
# #
@ -48,31 +22,6 @@ def getInstanceContainer(container_type) -> InstanceContainer:
container.setMetaDataEntry("type", container_type) container.setMetaDataEntry("type", container_type)
return container 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): class DefinitionContainerSubClass(DefinitionContainer):
def __init__(self): def __init__(self):
super().__init__(container_id = "SubDefinitionContainer") super().__init__(container_id = "SubDefinitionContainer")
@ -179,12 +128,30 @@ def test_constrainVariantInvalid(container, extruder_stack):
def test_constrainVariantValid(container, extruder_stack): def test_constrainVariantValid(container, extruder_stack):
extruder_stack.variant = container #Should not give an error. 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. #Tests setting definitions to invalid containers.
@pytest.mark.parametrize("container", [ @pytest.mark.parametrize("container", [
getInstanceContainer(container_type = "wrong class"), getInstanceContainer(container_type = "wrong class"),
getInstanceContainer(container_type = "material"), #Existing, but still 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. with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
extruder_stack.definition = container extruder_stack.definition = container
@ -196,23 +163,22 @@ def test_constrainVariantInvalid(container, extruder_stack):
def test_constrainDefinitionValid(container, extruder_stack): def test_constrainDefinitionValid(container, extruder_stack):
extruder_stack.definition = container #Should not give an error. extruder_stack.definition = container #Should not give an error.
## Tests whether deserialising completes the missing containers with empty ## Tests whether deserialising completes the missing containers with empty ones.
# ones. def test_deserializeCompletesEmptyContainers(extruder_stack):
@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. extruder_stack._containers = [DefinitionContainer(container_id = "definition"), extruder_stack.definitionChanges] #Set the internal state of this stack manually.
def test_deserializeCompletesEmptyContainers(extruder_stack: cura.Settings.ExtruderStack):
extruder_stack._containers = [DefinitionContainer(container_id = "definition")] #Set the internal state of this stack manually.
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize. with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
extruder_stack.deserialize("") extruder_stack.deserialize("")
assert len(extruder_stack.getContainers()) == len(cura.Settings.CuraContainerStack._ContainerIndexes.IndexTypeMap) #Needs a slot for every type. 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: 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 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 ## Tests whether an instance container with the wrong type gets removed when deserialising.
# deserialising.
def test_deserializeRemovesWrongInstanceContainer(extruder_stack): 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.Quality] = getInstanceContainer(container_type = "wrong type")
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition") 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. assert extruder_stack.quality == extruder_stack._empty_instance_container #Replaced with empty.
## Tests whether a container with the wrong class gets removed when ## Tests whether a container with the wrong class gets removed when deserialising.
# deserialising.
def test_deserializeRemovesWrongContainerClass(extruder_stack): 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.Quality] = DefinitionContainer(container_id = "wrong class")
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition") 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. assert extruder_stack.quality == extruder_stack._empty_instance_container #Replaced with empty.
## Tests whether an instance container in the definition spot results in an ## Tests whether an instance container in the definition spot results in an error.
# error.
def test_deserializeWrongDefinitionClass(extruder_stack): def test_deserializeWrongDefinitionClass(extruder_stack):
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = getInstanceContainer(container_type = "definition") #Correct type but wrong class. 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. with pytest.raises(UM.Settings.ContainerStack.InvalidContainerStackError): #Must raise an error that there is no definition container.
extruder_stack.deserialize("") extruder_stack.deserialize("")
## Tests whether an instance container with the wrong type is moved into the ## Tests whether an instance container with the wrong type is moved into the correct slot by deserialising.
# correct slot by deserialising.
def test_deserializeMoveInstanceContainer(extruder_stack): 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.Quality] = getInstanceContainer(container_type = "material") #Not in the correct spot.
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition") 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. with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
extruder_stack.deserialize("") extruder_stack.deserialize("")
assert extruder_stack.quality.getId() == "empty" assert extruder_stack.quality == empty_container
assert extruder_stack.material.getId() != "empty" assert extruder_stack.material != empty_container
from UM.Settings.Validator import Validator
## Tests whether a definition container in the wrong spot is moved into the ## Tests whether a definition container in the wrong spot is moved into the correct spot by deserialising.
# 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.
def test_deserializeMoveDefinitionContainer(extruder_stack): def test_deserializeMoveDefinitionContainer(extruder_stack):
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Material] = DefinitionContainer(container_id = "some definition") #Not in the correct spot. 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. with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
extruder_stack.deserialize("") extruder_stack.deserialize("")
assert extruder_stack.material.getId() == "empty" assert extruder_stack.material == empty_container
assert extruder_stack.definition.getId() != "empty" 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(global_stack, extruder_stack):
## Tests whether getProperty properly applies the stack-like behaviour on its
# containers.
def test_getPropertyFallThrough(extruder_stack):
# ExtruderStack.setNextStack calls registerExtruder for backward compatibility, but we do not need a complete extruder manager # ExtruderStack.setNextStack calls registerExtruder for backward compatibility, but we do not need a complete extruder manager
ExtruderManager._ExtruderManager__instance = unittest.mock.MagicMock() 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. 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! extruder_stack.definition = mock_layer_heights[container_indices.Definition] #There's a layer height in here!
stack = GlobalStack("PyTest GlobalStack") extruder_stack.setNextStack(global_stack)
extruder_stack.setNextStack(stack)
assert extruder_stack.getProperty("layer_height", "value") == container_indices.Definition assert extruder_stack.getProperty("layer_height", "value") == container_indices.Definition
extruder_stack.variant = mock_layer_heights[container_indices.Variant] 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.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.

View File

@ -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. # Cura is released under the terms of the LGPLv3 or higher.
import pytest #This module contains unit tests. import pytest #This module contains unit tests.
import unittest.mock #To monkeypatch some mocks in place of dependencies. 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. 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.DefinitionContainer import DefinitionContainer #To test against the class DefinitionContainer.
from UM.Settings.InstanceContainer import InstanceContainer #To test against the class InstanceContainer. from UM.Settings.InstanceContainer import InstanceContainer #To test against the class InstanceContainer.
from UM.Settings.SettingInstance import InstanceState from UM.Settings.SettingInstance import InstanceState
import UM.Settings.ContainerRegistry import UM.Settings.ContainerRegistry
import UM.Settings.ContainerStack import UM.Settings.ContainerStack
import UM.Settings.SettingDefinition #To add settings to the definition. 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. from cura.Settings.cura_empty_instance_containers import empty_container
@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")
## Gets an instance container with a specified container type. ## Gets an instance container with a specified container type.
# #
@ -48,31 +24,6 @@ def getInstanceContainer(container_type) -> InstanceContainer:
container.setMetaDataEntry("type", container_type) container.setMetaDataEntry("type", container_type)
return container 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): class DefinitionContainerSubClass(DefinitionContainer):
def __init__(self): def __init__(self):
super().__init__(container_id = "SubDefinitionContainer") super().__init__(container_id = "SubDefinitionContainer")
@ -229,7 +180,7 @@ def test_constrainDefinitionChangesValid(container, global_stack):
getInstanceContainer(container_type = "wrong class"), getInstanceContainer(container_type = "wrong class"),
getInstanceContainer(container_type = "material"), #Existing, but still 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. with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
global_stack.definition = container global_stack.definition = container
@ -241,23 +192,22 @@ def test_constrainVariantInvalid(container, global_stack):
def test_constrainDefinitionValid(container, global_stack): def test_constrainDefinitionValid(container, global_stack):
global_stack.definition = container #Should not give an error. global_stack.definition = container #Should not give an error.
## Tests whether deserialising completes the missing containers with empty ## Tests whether deserialising completes the missing containers with empty ones. The initial containers are just the
# ones. # definition and the definition_changes (that cannot be empty after CURA-5281)
@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):
def test_deserializeCompletesEmptyContainers(global_stack: cura.Settings.GlobalStack): global_stack._containers = [DefinitionContainer(container_id = "definition"), global_stack.definitionChanges] #Set the internal state of this stack manually.
global_stack._containers = [DefinitionContainer(container_id = "definition")] #Set the internal state of this stack manually.
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize. with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
global_stack.deserialize("") global_stack.deserialize("")
assert len(global_stack.getContainers()) == len(cura.Settings.CuraContainerStack._ContainerIndexes.IndexTypeMap) #Needs a slot for every type. 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: 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 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 ## Tests whether an instance container with the wrong type gets removed when deserialising.
# deserialising.
def test_deserializeRemovesWrongInstanceContainer(global_stack): def test_deserializeRemovesWrongInstanceContainer(global_stack):
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = getInstanceContainer(container_type = "wrong type") global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = getInstanceContainer(container_type = "wrong type")
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition") 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. assert global_stack.quality == global_stack._empty_instance_container #Replaced with empty.
## Tests whether a container with the wrong class gets removed when ## Tests whether a container with the wrong class gets removed when deserialising.
# deserialising.
def test_deserializeRemovesWrongContainerClass(global_stack): def test_deserializeRemovesWrongContainerClass(global_stack):
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = DefinitionContainer(container_id = "wrong class") global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = DefinitionContainer(container_id = "wrong class")
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition") 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. assert global_stack.quality == global_stack._empty_instance_container #Replaced with empty.
## Tests whether an instance container in the definition spot results in an ## Tests whether an instance container in the definition spot results in an error.
# error.
def test_deserializeWrongDefinitionClass(global_stack): def test_deserializeWrongDefinitionClass(global_stack):
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = getInstanceContainer(container_type = "definition") #Correct type but wrong class. global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = getInstanceContainer(container_type = "definition") #Correct type but wrong class.
@ -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. with pytest.raises(UM.Settings.ContainerStack.InvalidContainerStackError): #Must raise an error that there is no definition container.
global_stack.deserialize("") global_stack.deserialize("")
## Tests whether an instance container with the wrong type is moved into the ## Tests whether an instance container with the wrong type is moved into the correct slot by deserialising.
# correct slot by deserialising.
def test_deserializeMoveInstanceContainer(global_stack): def test_deserializeMoveInstanceContainer(global_stack):
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = getInstanceContainer(container_type = "material") #Not in the correct spot. global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = getInstanceContainer(container_type = "material") #Not in the correct spot.
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition") 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. with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
global_stack.deserialize("") global_stack.deserialize("")
assert global_stack.quality.getId() == "empty" assert global_stack.quality == empty_container
assert global_stack.material.getId() != "empty" assert global_stack.material != empty_container
## Tests whether a definition container in the wrong spot is moved into the ## Tests whether a definition container in the wrong spot is moved into the correct spot by deserialising.
# 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.
def test_deserializeMoveDefinitionContainer(global_stack): def test_deserializeMoveDefinitionContainer(global_stack):
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Material] = DefinitionContainer(container_id = "some definition") #Not in the correct spot. global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Material] = DefinitionContainer(container_id = "some definition") #Not in the correct spot.
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize. with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
global_stack.deserialize("") global_stack.deserialize("")
assert global_stack.material.getId() == "empty" assert global_stack.material == empty_container
assert global_stack.definition.getId() != "empty" 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): def test_getPropertyFallThrough(global_stack):
#A few instance container mocks to put in the stack. #A few instance container mocks to put in the stack.
mock_layer_heights = {} #For each container type, a mock container that defines layer height to something unique. 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 global_stack.definition = value
assert global_stack.getProperty("material_bed_temperature", "value") == 10 #No resolve, so fall through to value. assert global_stack.getProperty("material_bed_temperature", "value") == 10 #No resolve, so fall through to value.
## In definitions, when the value is asked and there is a resolve function, it ## In definitions, when the value is asked and there is a resolve function, it must get the resolve first.
# must get the resolve first.
def test_getPropertyResolveInDefinition(global_stack): def test_getPropertyResolveInDefinition(global_stack):
resolve_and_value = unittest.mock.MagicMock() #Sets the resolve and value for bed temperature. resolve_and_value = unittest.mock.MagicMock() #Sets the resolve and value for bed temperature.
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. 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 global_stack.definition = resolve_and_value
assert global_stack.getProperty("material_bed_temperature", "value") == 7.5 #Resolve wins in the definition. assert global_stack.getProperty("material_bed_temperature", "value") == 7.5 #Resolve wins in the definition.
## In instance containers, when the value is asked and there is a resolve ## In instance containers, when the value is asked and there is a resolve function, it must get the value first.
# function, it must get the value first.
def test_getPropertyResolveInInstance(global_stack): def test_getPropertyResolveInInstance(global_stack):
container_indices = cura.Settings.CuraContainerStack._ContainerIndexes container_indices = cura.Settings.CuraContainerStack._ContainerIndexes
instance_containers = {} instance_containers = {}
@ -402,8 +342,7 @@ def test_getPropertyResolveInInstance(global_stack):
global_stack.userChanges = instance_containers[container_indices.UserChanges] global_stack.userChanges = instance_containers[container_indices.UserChanges]
assert global_stack.getProperty("material_bed_temperature", "value") == 5 assert global_stack.getProperty("material_bed_temperature", "value") == 5
## Tests whether the value in instances gets evaluated before the resolve in ## Tests whether the value in instances gets evaluated before the resolve in definitions.
# definitions.
def test_getPropertyInstancesBeforeResolve(global_stack): def test_getPropertyInstancesBeforeResolve(global_stack):
value = unittest.mock.MagicMock() #Sets just the value. value = unittest.mock.MagicMock() #Sets just the value.
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 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 assert global_stack.getProperty("material_bed_temperature", "value") == 10
## Tests whether the hasUserValue returns true for settings that are changed in ## Tests whether the hasUserValue returns true for settings that are changed in the user-changes container.
# the user-changes container.
def test_hasUserValueUserChanges(global_stack): def test_hasUserValueUserChanges(global_stack):
container = unittest.mock.MagicMock() container = unittest.mock.MagicMock()
container.getMetaDataEntry = unittest.mock.MagicMock(return_value = "user") 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("infill_sparse_density")
assert not global_stack.hasUserValue("") assert not global_stack.hasUserValue("")
## Tests whether the hasUserValue returns true for settings that are changed in ## Tests whether the hasUserValue returns true for settings that are changed in the quality-changes container.
# the quality-changes container.
def test_hasUserValueQualityChanges(global_stack): def test_hasUserValueQualityChanges(global_stack):
container = unittest.mock.MagicMock() container = unittest.mock.MagicMock()
container.getMetaDataEntry = unittest.mock.MagicMock(return_value = "quality_changes") 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("infill_sparse_density")
assert not global_stack.hasUserValue("") assert not global_stack.hasUserValue("")
## Tests whether a container in some other place on the stack is correctly not ## Tests whether a container in some other place on the stack is correctly not recognised as user value.
# recognised as user value.
def test_hasNoUserValue(global_stack): def test_hasNoUserValue(global_stack):
container = unittest.mock.MagicMock() container = unittest.mock.MagicMock()
container.getMetaDataEntry = unittest.mock.MagicMock(return_value = "quality") 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.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.

View 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

View File

@ -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

View File

@ -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

View File

@ -1,11 +0,0 @@
[general]
version = 3
name = Legacy Extruder Stack
id = ExtruderLegacy
[metadata]
type = extruder_train
[containers]
3 = some_instance
5 = some_definition

View File

@ -1,8 +0,0 @@
[general]
version = 3
name = Global
id = Global
[containers]
3 = some_instance
6 = some_definition

View File

@ -1,11 +0,0 @@
[general]
version = 3
name = Global
id = Global
[metadata]
type = machine
[containers]
3 = some_instance
6 = some_definition

View File

@ -1,8 +0,0 @@
[general]
version = 3
name = Left
id = Left
[containers]
3 = some_instance
5 = some_definition

View File

@ -1,11 +0,0 @@
[general]
version = 3
name = Legacy Global Stack
id = MachineLegacy
[metadata]
type = machine
[containers]
3 = some_instance
6 = some_definition

View File

@ -1,7 +0,0 @@
[general]
version = 3
name = Only Definition
id = OnlyDefinition
[containers]
5 = some_definition

View File

@ -1,7 +0,0 @@
[general]
version = 3
name = Only Definition
id = OnlyDefinition
[containers]
6 = some_definition

View File

@ -1,8 +0,0 @@
[general]
version = 3
name = Only Definition Changes
id = OnlyDefinitionChanges
[containers]
5 = some_instance
6 = some_definition

View File

@ -1,8 +0,0 @@
[general]
version = 3
name = Only Material
id = OnlyMaterial
[containers]
3 = some_instance
5 = some_definition

View File

@ -1,8 +0,0 @@
[general]
version = 3
name = Only Material
id = OnlyMaterial
[containers]
3 = some_instance
6 = some_definition

View File

@ -1,8 +0,0 @@
[general]
version = 3
name = Only Quality
id = OnlyQuality
[containers]
2 = some_instance
5 = some_definition

View File

@ -1,8 +0,0 @@
[general]
version = 3
name = Only Quality
id = OnlyQuality
[containers]
2 = some_instance
6 = some_definition

View File

@ -1,8 +0,0 @@
[general]
version = 3
name = Only Quality Changes
id = OnlyQualityChanges
[containers]
1 = some_instance
5 = some_definition

View File

@ -1,8 +0,0 @@
[general]
version = 3
name = Only Quality Changes
id = OnlyQualityChanges
[containers]
1 = some_instance
6 = some_definition

View File

@ -1,8 +0,0 @@
[general]
version = 3
name = Only User
id = OnlyUser
[containers]
0 = some_instance
5 = some_definition

View File

@ -1,8 +0,0 @@
[general]
version = 3
name = Only User
id = OnlyUser
[containers]
0 = some_instance
6 = some_definition

View File

@ -1,8 +0,0 @@
[general]
version = 3
name = Only Variant
id = OnlyVariant
[containers]
4 = some_instance
5 = some_definition

View File

@ -1,8 +0,0 @@
[general]
version = 3
name = Only Variant
id = OnlyVariant
[containers]
4 = some_instance
6 = some_definition

View File

@ -1,9 +1,11 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import numpy import numpy
from cura.Arranging.Arrange import Arrange from cura.Arranging.Arrange import Arrange
from cura.Arranging.ShapeArray import ShapeArray from cura.Arranging.ShapeArray import ShapeArray
## Triangle of area 12 ## Triangle of area 12
def gimmeTriangle(): def gimmeTriangle():
return numpy.array([[-3, 1], [3, 1], [0, -3]], dtype=numpy.int32) return numpy.array([[-3, 1], [3, 1], [0, -3]], dtype=numpy.int32)
@ -102,7 +104,7 @@ def test_centerFirst_rectangular():
## Test centerFirst ## Test centerFirst
def test_centerFirst_rectangular(): def test_centerFirst_rectangular2():
ar = Arrange(10, 20, 5, 10, scale = 1) ar = Arrange(10, 20, 5, 10, scale = 1)
ar.centerFirst() ar.centerFirst()
print(ar._priority) print(ar._priority)

View File

@ -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 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.MachineAction import MachineAction
from cura.MachineActionManager import MachineActionManager, NotUniqueMachineActionError, UnknownMachineActionError from cura.MachineActionManager import NotUniqueMachineActionError, UnknownMachineActionError
class Machine: class Machine:
def __init__(self, key = ""): def __init__(self, key = ""):
@ -14,66 +13,64 @@ class Machine:
def getKey(self): def getKey(self):
return self._key return self._key
def test_addMachineAction(): def test_addMachineAction(machine_action_manager):
machine_manager = MachineActionManager()
test_action = MachineAction(key = "test_action") test_action = MachineAction(key = "test_action")
test_action_2 = MachineAction(key = "test_action_2") test_action_2 = MachineAction(key = "test_action_2")
test_machine = Machine("test_machine") test_machine = Machine("test_machine")
machine_manager.addMachineAction(test_action) machine_action_manager.addMachineAction(test_action)
machine_manager.addMachineAction(test_action_2) machine_action_manager.addMachineAction(test_action_2)
assert machine_manager.getMachineAction("test_action") == test_action assert machine_action_manager.getMachineAction("test_action") == test_action
assert machine_manager.getMachineAction("key_that_doesnt_exist") is None assert machine_action_manager.getMachineAction("key_that_doesnt_exist") is None
# Adding the same machine action is not allowed. # Adding the same machine action is not allowed.
with pytest.raises(NotUniqueMachineActionError): with pytest.raises(NotUniqueMachineActionError):
machine_manager.addMachineAction(test_action) machine_action_manager.addMachineAction(test_action)
# Check that the machine has no supported actions yet. # 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. # Check if adding a supported action works.
machine_manager.addSupportedAction(test_machine, "test_action") machine_action_manager.addSupportedAction(test_machine, "test_action")
assert machine_manager.getSupportedActions(test_machine) == [test_action, ] assert machine_action_manager.getSupportedActions(test_machine) == [test_action, ]
# Check that adding a unknown action doesn't change anything. # Check that adding a unknown action doesn't change anything.
machine_manager.addSupportedAction(test_machine, "key_that_doesnt_exist") machine_action_manager.addSupportedAction(test_machine, "key_that_doesnt_exist")
assert machine_manager.getSupportedActions(test_machine) == [test_action, ] assert machine_action_manager.getSupportedActions(test_machine) == [test_action, ]
# Check if adding multiple supported actions works. # Check if adding multiple supported actions works.
machine_manager.addSupportedAction(test_machine, "test_action_2") machine_action_manager.addSupportedAction(test_machine, "test_action_2")
assert machine_manager.getSupportedActions(test_machine) == [test_action, test_action_2] assert machine_action_manager.getSupportedActions(test_machine) == [test_action, test_action_2]
# Check that the machine has no required actions yet. # 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. ## Ensure that only known actions can be added.
with pytest.raises(UnknownMachineActionError): 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 ## Check if adding single required action works
machine_manager.addRequiredAction(test_machine, "test_action") machine_action_manager.addRequiredAction(test_machine, "test_action")
assert machine_manager.getRequiredActions(test_machine) == [test_action, ] assert machine_action_manager.getRequiredActions(test_machine) == [test_action, ]
# Check if adding multiple required actions works. # Check if adding multiple required actions works.
machine_manager.addRequiredAction(test_machine, "test_action_2") machine_action_manager.addRequiredAction(test_machine, "test_action_2")
assert machine_manager.getRequiredActions(test_machine) == [test_action, test_action_2] assert machine_action_manager.getRequiredActions(test_machine) == [test_action, test_action_2]
# Ensure that firstStart actions are empty by default. # 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. # Check if adding multiple (the same) actions to first start actions work.
machine_manager.addFirstStartAction(test_machine, "test_action") machine_action_manager.addFirstStartAction(test_machine, "test_action")
machine_manager.addFirstStartAction(test_machine, "test_action") machine_action_manager.addFirstStartAction(test_machine, "test_action")
assert machine_manager.getFirstStartActions(test_machine) == [test_action, test_action] assert machine_action_manager.getFirstStartActions(test_machine) == [test_action, test_action]
# Check if inserting an action works # Check if inserting an action works
machine_manager.addFirstStartAction(test_machine, "test_action_2", index = 1) machine_action_manager.addFirstStartAction(test_machine, "test_action_2", index = 1)
assert machine_manager.getFirstStartActions(test_machine) == [test_action, test_action_2, test_action] assert machine_action_manager.getFirstStartActions(test_machine) == [test_action, test_action_2, test_action]
# Check that adding a unknown action doesn't change anything. # Check that adding a unknown action doesn't change anything.
machine_manager.addFirstStartAction(test_machine, "key_that_doesnt_exist", index = 1) machine_action_manager.addFirstStartAction(test_machine, "key_that_doesnt_exist", index = 1)
assert machine_manager.getFirstStartActions(test_machine) == [test_action, test_action_2, test_action] assert machine_action_manager.getFirstStartActions(test_machine) == [test_action, test_action_2, test_action]

View File

@ -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 configparser #To read the profiles.
import os #To join paths. import os #To join paths.
import pytest import pytest
@ -11,6 +14,7 @@ import pytest
# often that we updated the variants for the UM3 but forgot about the UM3E. # often that we updated the variants for the UM3 but forgot about the UM3E.
@pytest.mark.parametrize("um3_file, um3e_file", [ @pytest.mark.parametrize("um3_file, um3e_file", [
#List the corresponding files below. #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_aa0.8.inst.cfg", "ultimaker3_extended_aa0.8.inst.cfg"),
("ultimaker3_aa04.inst.cfg", "ultimaker3_extended_aa04.inst.cfg"), ("ultimaker3_aa04.inst.cfg", "ultimaker3_extended_aa04.inst.cfg"),
("ultimaker3_bb0.8.inst.cfg", "ultimaker3_extended_bb0.8.inst.cfg"), ("ultimaker3_bb0.8.inst.cfg", "ultimaker3_extended_bb0.8.inst.cfg"),

23
tests/conftest.py Normal file
View 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)