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
*.qmlc
.mypy_cache
.pytest_cache
#MacOS
.DS_Store

View File

@ -242,6 +242,8 @@ class BuildVolume(SceneNode):
# Mark the node as outside build volume if the set extruder is disabled
extruder_position = node.callDecoration("getActiveExtruderPosition")
if extruder_position not in self._global_container_stack.extruders:
continue
if not self._global_container_stack.extruders[extruder_position].isEnabled:
node.setOutsideBuildArea(True)
continue
@ -561,7 +563,9 @@ class BuildVolume(SceneNode):
camera = Application.getInstance().getController().getCameraTool()
if camera:
camera.setZoomRange(min = 1, max = self.getDiagonalSize() * 5) #You can zoom out up to 5 times the diagonal. This gives some space around the volume.
diagonal = self.getDiagonalSize()
if diagonal > 1:
camera.setZoomRange(min = 0.1, max = diagonal * 5) #You can zoom out up to 5 times the diagonal. This gives some space around the volume.
def _onEngineCreated(self):
self._engine_ready = True

View File

@ -1,11 +1,10 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import copy
import os
import sys
import time
from typing import cast, TYPE_CHECKING, Optional
from typing import cast, TYPE_CHECKING
import numpy
@ -106,6 +105,7 @@ from cura.Settings.ExtrudersModel import ExtrudersModel
from cura.Settings.MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler
from cura.Settings.ContainerManager import ContainerManager
from cura.Settings.SidebarCustomMenuItemsModel import SidebarCustomMenuItemsModel
import cura.Settings.cura_empty_instance_containers
from cura.ObjectsModel import ObjectsModel
@ -216,7 +216,6 @@ class CuraApplication(QtApplication):
self._message_box_callback = None
self._message_box_callback_arguments = []
self._preferred_mimetype = ""
self._i18n_catalog = None
self._currently_loading_files = []
@ -369,42 +368,23 @@ class CuraApplication(QtApplication):
# Add empty variant, material and quality containers.
# Since they are empty, they should never be serialized and instead just programmatically created.
# We need them to simplify the switching between materials.
empty_container = self._container_registry.getEmptyInstanceContainer()
self.empty_container = empty_container
self.empty_container = cura.Settings.cura_empty_instance_containers.empty_container
empty_definition_changes_container = copy.deepcopy(empty_container)
empty_definition_changes_container.setMetaDataEntry("id", "empty_definition_changes")
empty_definition_changes_container.setMetaDataEntry("type", "definition_changes")
self._container_registry.addContainer(empty_definition_changes_container)
self.empty_definition_changes_container = empty_definition_changes_container
self._container_registry.addContainer(
cura.Settings.cura_empty_instance_containers.empty_definition_changes_container)
self.empty_definition_changes_container = cura.Settings.cura_empty_instance_containers.empty_definition_changes_container
empty_variant_container = copy.deepcopy(empty_container)
empty_variant_container.setMetaDataEntry("id", "empty_variant")
empty_variant_container.setMetaDataEntry("type", "variant")
self._container_registry.addContainer(empty_variant_container)
self.empty_variant_container = empty_variant_container
self._container_registry.addContainer(cura.Settings.cura_empty_instance_containers.empty_variant_container)
self.empty_variant_container = cura.Settings.cura_empty_instance_containers.empty_variant_container
empty_material_container = copy.deepcopy(empty_container)
empty_material_container.setMetaDataEntry("id", "empty_material")
empty_material_container.setMetaDataEntry("type", "material")
self._container_registry.addContainer(empty_material_container)
self.empty_material_container = empty_material_container
self._container_registry.addContainer(cura.Settings.cura_empty_instance_containers.empty_material_container)
self.empty_material_container = cura.Settings.cura_empty_instance_containers.empty_material_container
empty_quality_container = copy.deepcopy(empty_container)
empty_quality_container.setMetaDataEntry("id", "empty_quality")
empty_quality_container.setName("Not Supported")
empty_quality_container.setMetaDataEntry("quality_type", "not_supported")
empty_quality_container.setMetaDataEntry("type", "quality")
empty_quality_container.setMetaDataEntry("supported", False)
self._container_registry.addContainer(empty_quality_container)
self.empty_quality_container = empty_quality_container
self._container_registry.addContainer(cura.Settings.cura_empty_instance_containers.empty_quality_container)
self.empty_quality_container = cura.Settings.cura_empty_instance_containers.empty_quality_container
empty_quality_changes_container = copy.deepcopy(empty_container)
empty_quality_changes_container.setMetaDataEntry("id", "empty_quality_changes")
empty_quality_changes_container.setMetaDataEntry("type", "quality_changes")
empty_quality_changes_container.setMetaDataEntry("quality_type", "not_supported")
self._container_registry.addContainer(empty_quality_changes_container)
self.empty_quality_changes_container = empty_quality_changes_container
self._container_registry.addContainer(cura.Settings.cura_empty_instance_containers.empty_quality_changes_container)
self.empty_quality_changes_container = cura.Settings.cura_empty_instance_containers.empty_quality_changes_container
# Initializes the version upgrade manager with by providing the paths for each resource type and the latest
# versions.
@ -515,9 +495,6 @@ class CuraApplication(QtApplication):
self.applicationShuttingDown.connect(self.saveSettings)
self.engineCreatedSignal.connect(self._onEngineCreated)
self.globalContainerStackChanged.connect(self._onGlobalContainerChanged)
self._onGlobalContainerChanged()
self.getCuraSceneController().setActiveBuildPlate(0) # Initialize
CuraApplication.Created = True
@ -782,7 +759,10 @@ class CuraApplication(QtApplication):
# Initialize camera
root = controller.getScene().getRoot()
camera = Camera("3d", root)
camera.setPosition(Vector(-80, 250, 700) * self.getBuildVolume().getDiagonalSize() / 375)
diagonal = self.getBuildVolume().getDiagonalSize()
if diagonal < 1: #No printer added yet. Set a default camera distance for normal-sized printers.
diagonal = 375
camera.setPosition(Vector(-80, 250, 700) * diagonal / 375)
camera.setPerspective(True)
camera.lookAt(Vector(0, 0, 0))
controller.getScene().setActiveCamera("3d")
@ -999,30 +979,14 @@ class CuraApplication(QtApplication):
self._camera_animation.setTarget(Selection.getSelectedObject(0).getWorldPosition())
self._camera_animation.start()
def _onGlobalContainerChanged(self):
if self._global_container_stack is not None:
machine_file_formats = [file_type.strip() for file_type in self._global_container_stack.getMetaDataEntry("file_formats").split(";")]
new_preferred_mimetype = ""
if machine_file_formats:
new_preferred_mimetype = machine_file_formats[0]
if new_preferred_mimetype != self._preferred_mimetype:
self._preferred_mimetype = new_preferred_mimetype
self.preferredOutputMimetypeChanged.emit()
requestAddPrinter = pyqtSignal()
activityChanged = pyqtSignal()
sceneBoundingBoxChanged = pyqtSignal()
preferredOutputMimetypeChanged = pyqtSignal()
@pyqtProperty(bool, notify = activityChanged)
def platformActivity(self):
return self._platform_activity
@pyqtProperty(str, notify=preferredOutputMimetypeChanged)
def preferredOutputMimetype(self):
return self._preferred_mimetype
@pyqtProperty(str, notify = sceneBoundingBoxChanged)
def getSceneBoundingBoxString(self):
return self._i18n_catalog.i18nc("@info 'width', 'depth' and 'height' are variable names that must NOT be translated; just translate the format of ##x##x## mm.", "%(width).1f x %(depth).1f x %(height).1f mm") % {'width' : self._scene_bounding_box.width.item(), 'depth': self._scene_bounding_box.depth.item(), 'height' : self._scene_bounding_box.height.item()}

View File

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

View File

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

View File

@ -108,16 +108,27 @@ class CuraStackBuilder:
preferred_quality_type = machine_definition.getMetaDataEntry("preferred_quality_type")
quality_group_dict = quality_manager.getQualityGroups(new_global_stack)
quality_group = quality_group_dict.get(preferred_quality_type)
new_global_stack.quality = quality_group.node_for_global.getContainer()
if not new_global_stack.quality:
if not quality_group_dict:
# There is no available quality group, set all quality containers to empty.
new_global_stack.quality = application.empty_quality_container
for position, extruder_stack in new_global_stack.extruders.items():
if position in quality_group.nodes_for_extruders and quality_group.nodes_for_extruders[position].getContainer():
extruder_stack.quality = quality_group.nodes_for_extruders[position].getContainer()
else:
for extruder_stack in new_global_stack.extruders.values():
extruder_stack.quality = application.empty_quality_container
else:
# Set the quality containers to the preferred quality type if available, otherwise use the first quality
# type that's available.
if preferred_quality_type not in quality_group_dict:
Logger.log("w", "The preferred quality {quality_type} doesn't exist for this set-up. Choosing a random one.".format(quality_type = preferred_quality_type))
preferred_quality_type = next(iter(quality_group_dict))
quality_group = quality_group_dict.get(preferred_quality_type)
new_global_stack.quality = quality_group.node_for_global.getContainer()
if not new_global_stack.quality:
new_global_stack.quality = application.empty_quality_container
for position, extruder_stack in new_global_stack.extruders.items():
if position in quality_group.nodes_for_extruders and quality_group.nodes_for_extruders[position].getContainer():
extruder_stack.quality = quality_group.nodes_for_extruders[position].getContainer()
else:
extruder_stack.quality = application.empty_quality_container
# Register the global stack after the extruder stacks are created. This prevents the registry from adding another
# extruder stack because the global stack didn't have one yet (which is enforced since Cura 3.1).

View File

@ -139,9 +139,6 @@ class ExtruderStack(CuraContainerStack):
super().deserialize(contents, file_name)
if "enabled" not in self.getMetaData():
self.setMetaDataEntry("enabled", "True")
stacks = ContainerRegistry.getInstance().findContainerStacks(id=self.getMetaDataEntry("machine", ""))
if stacks:
self.setNextStack(stacks[0])
def _onPropertiesChanged(self, key: str, properties: Dict[str, Any]) -> None:
# When there is a setting that is not settable per extruder that depends on a value from a setting that is,

View File

@ -61,6 +61,10 @@ class GlobalStack(CuraContainerStack):
name = self.variant.getName()
return name
@pyqtProperty(str, constant = True)
def preferred_output_file_formats(self) -> str:
return self.getMetaDataEntry("file_formats")
## Add an extruder to the list of extruders of this stack.
#
# \param extruder The extruder to add.

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")
extruder_stack_dict = {stack.getMetaDataEntry("position"): stack for stack in extruder_stacks}
# Make sure that those extruders have the global stack as the next stack or later some value evaluation
# will fail.
for stack in extruder_stacks:
stack.setNextStack(global_stack, connect_signals = False)
Logger.log("d", "Workspace loading is checking definitions...")
# Get all the definition files & check if they exist. If not, add them.
definition_container_files = [name for name in cura_file_names if name.endswith(self._definition_container_suffix)]

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

@ -8,6 +8,9 @@ from UM.VersionUpgrade import VersionUpgrade
deleted_settings = {"prime_tower_wall_thickness", "dual_pre_wipe", "prime_tower_purge_volume"}
changed_settings = {'retraction_combing': 'noskin'}
updated_settings = {'retraction_combing': 'infill'}
_RENAMED_MATERIAL_PROFILES = {
"dsm_arnitel2045_175_cartesio_0.25_mm": "dsm_arnitel2045_175_cartesio_0.25mm_thermoplastic_extruder",
"dsm_arnitel2045_175_cartesio_0.4_mm": "dsm_arnitel2045_175_cartesio_0.4mm_thermoplastic_extruder",
@ -127,6 +130,13 @@ class VersionUpgrade34to40(VersionUpgrade):
continue
del parser["values"][deleted_setting]
for setting_key in changed_settings:
if setting_key not in parser["values"]:
continue
if parser["values"][setting_key] == changed_settings[setting_key]:
parser["values"][setting_key] = updated_settings[setting_key]
result = io.StringIO()
parser.write(result)
return [filename], [result.getvalue()]

View File

@ -3356,13 +3356,14 @@
"retraction_combing":
{
"label": "Combing Mode",
"description": "Combing keeps the nozzle within already printed areas when traveling. This results in slightly longer travel moves but reduces the need for retractions. If combing is off, the material will retract and the nozzle moves in a straight line to the next point. It is also possible to avoid combing over top/bottom skin areas by combing within the infill only.",
"description": "Combing keeps the nozzle within already printed areas when traveling. This results in slightly longer travel moves but reduces the need for retractions. If combing is off, the material will retract and the nozzle moves in a straight line to the next point. It is also possible to avoid combing over top/bottom skin areas and also to only comb within the infill. Note that the 'Within Infill' option behaves exactly like the 'Not in Skin' option in earlier Cura releases.",
"type": "enum",
"options":
{
"off": "Off",
"all": "All",
"noskin": "Not in Skin"
"noskin": "Not in Skin",
"infill": "Within Infill"
},
"default_value": "all",
"resolve": "'noskin' if 'noskin' in extruderValues('retraction_combing') else ('all' if 'all' in extruderValues('retraction_combing') else 'off')",
@ -5073,7 +5074,7 @@
"unit": "mm",
"enabled": "resolveOrValue('prime_tower_enable')",
"default_value": 200,
"value": "machine_width - max(extruderValue(adhesion_extruder_nr, 'brim_width') * extruderValue(adhesion_extruder_nr, 'initial_layer_line_width_factor') / 100 if adhesion_type == 'brim' else (extruderValue(adhesion_extruder_nr, 'raft_margin') if adhesion_type == 'raft' else (extruderValue(adhesion_extruder_nr, 'skirt_gap') if adhesion_type == 'skirt' else 0)), max(extruderValues('travel_avoid_distance'))) - max(extruderValues('support_offset')) - sum(extruderValues('skirt_brim_line_width')) * extruderValue(adhesion_extruder_nr, 'initial_layer_line_width_factor') / 100 - 1",
"value": "machine_width - max(extruderValue(adhesion_extruder_nr, 'brim_width') * extruderValue(adhesion_extruder_nr, 'initial_layer_line_width_factor') / 100 if adhesion_type == 'brim' else (extruderValue(adhesion_extruder_nr, 'raft_margin') if adhesion_type == 'raft' else (extruderValue(adhesion_extruder_nr, 'skirt_gap') if adhesion_type == 'skirt' else 0)), max(extruderValues('travel_avoid_distance'))) - max(extruderValues('support_offset')) - sum(extruderValues('skirt_brim_line_width')) * extruderValue(adhesion_extruder_nr, 'initial_layer_line_width_factor') / 100 - (resolveOrValue('draft_shield_dist') if resolveOrValue('draft_shield_enabled') else 0) - 1",
"maximum_value": "machine_width / 2 if machine_center_is_zero else machine_width",
"minimum_value": "resolveOrValue('prime_tower_size') - machine_width / 2 if machine_center_is_zero else resolveOrValue('prime_tower_size')",
"settable_per_mesh": false,
@ -5087,7 +5088,7 @@
"unit": "mm",
"enabled": "resolveOrValue('prime_tower_enable')",
"default_value": 200,
"value": "machine_depth - prime_tower_size - max(extruderValue(adhesion_extruder_nr, 'brim_width') * extruderValue(adhesion_extruder_nr, 'initial_layer_line_width_factor') / 100 if adhesion_type == 'brim' else (extruderValue(adhesion_extruder_nr, 'raft_margin') if adhesion_type == 'raft' else (extruderValue(adhesion_extruder_nr, 'skirt_gap') if adhesion_type == 'skirt' else 0)), max(extruderValues('travel_avoid_distance'))) - max(extruderValues('support_offset')) - sum(extruderValues('skirt_brim_line_width')) * extruderValue(adhesion_extruder_nr, 'initial_layer_line_width_factor') / 100 - 1",
"value": "machine_depth - prime_tower_size - max(extruderValue(adhesion_extruder_nr, 'brim_width') * extruderValue(adhesion_extruder_nr, 'initial_layer_line_width_factor') / 100 if adhesion_type == 'brim' else (extruderValue(adhesion_extruder_nr, 'raft_margin') if adhesion_type == 'raft' else (extruderValue(adhesion_extruder_nr, 'skirt_gap') if adhesion_type == 'skirt' else 0)), max(extruderValues('travel_avoid_distance'))) - max(extruderValues('support_offset')) - sum(extruderValues('skirt_brim_line_width')) * extruderValue(adhesion_extruder_nr, 'initial_layer_line_width_factor') / 100 - (resolveOrValue('draft_shield_dist') if resolveOrValue('draft_shield_enabled') else 0) - 1",
"maximum_value": "machine_depth / 2 - resolveOrValue('prime_tower_size') if machine_center_is_zero else machine_depth - resolveOrValue('prime_tower_size')",
"minimum_value": "machine_depth / -2 if machine_center_is_zero else 0",
"settable_per_mesh": false,
@ -5626,6 +5627,19 @@
"settable_per_mesh": false,
"settable_per_extruder": true
},
"minimum_polygon_circumference":
{
"label": "Minimum Polygon Circumference",
"description": "Polygons in sliced layers that have a circumference smaller than this amount will be filtered out. Lower values lead to higher resolution mesh at the cost of slicing time. It is meant mostly for high resolution SLA printers and very tiny 3D models with a lot of details.",
"unit": "mm",
"type": "float",
"default_value": 1.0,
"minimum_value": "0.001",
"minimum_value_warning": "0.05",
"maximum_value_warning": "1.0",
"settable_per_mesh": true,
"settable_per_extruder": false
},
"meshfix_maximum_resolution":
{
"label": "Maximum Resolution",

View File

@ -120,7 +120,7 @@ UM.MainWindow
text: catalog.i18nc("@title:menu menubar:file","&Save...")
onTriggered:
{
var args = { "filter_by_machine": false, "file_type": "workspace", "preferred_mimetype": "application/x-curaproject+xml" };
var args = { "filter_by_machine": false, "file_type": "workspace", "preferred_mimetypes": "application/x-curaproject+xml" };
if(UM.Preferences.getValue("cura/dialog_on_project_save"))
{
saveWorkspaceDialog.args = args;
@ -142,7 +142,7 @@ UM.MainWindow
onTriggered:
{
var localDeviceId = "local_file";
UM.OutputDeviceManager.requestWriteToDevice(localDeviceId, PrintInformation.jobName, { "filter_by_machine": false, "preferred_mimetype": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml"});
UM.OutputDeviceManager.requestWriteToDevice(localDeviceId, PrintInformation.jobName, { "filter_by_machine": false, "preferred_mimetypes": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml"});
}
}
@ -151,7 +151,7 @@ UM.MainWindow
text: catalog.i18nc("@action:inmenu menubar:file", "Export Selection...");
enabled: UM.Selection.hasSelection;
iconName: "document-save-as";
onTriggered: UM.OutputDeviceManager.requestWriteSelectionToDevice("local_file", PrintInformation.jobName, { "filter_by_machine": false, "preferred_mimetype": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml"});
onTriggered: UM.OutputDeviceManager.requestWriteSelectionToDevice("local_file", PrintInformation.jobName, { "filter_by_machine": false, "preferred_mimetypes": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml"});
}
MenuSeparator { }

View File

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

View File

@ -7,6 +7,7 @@ import QtQuick.Controls.Styles 1.1
import QtQuick.Layouts 1.1
import UM 1.1 as UM
import Cura 1.0 as Cura
Item {
id: base;
@ -257,7 +258,8 @@ Item {
onClicked:
{
forceActiveFocus();
UM.OutputDeviceManager.requestWriteToDevice(UM.OutputDeviceManager.activeDevice, PrintInformation.jobName, { "filter_by_machine": true, "preferred_mimetype":Printer.preferredOutputMimetype });
UM.OutputDeviceManager.requestWriteToDevice(UM.OutputDeviceManager.activeDevice, PrintInformation.jobName,
{ "filter_by_machine": true, "preferred_mimetypes": Cura.MachineManager.activeMachine.preferred_output_file_formats });
}
style: ButtonStyle {

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.
import os #To find the directory with test files and find the test files.
import pytest #This module contains unit tests.
import shutil #To copy files to make a temporary file.
import unittest.mock #To mock and monkeypatch stuff.
import urllib.parse
import copy
import cura.CuraApplication
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry #The class we're testing.
from UM.Settings.DefinitionContainer import DefinitionContainer
from cura.Settings.ExtruderStack import ExtruderStack #Testing for returning the correct types of stacks.
from cura.Settings.GlobalStack import GlobalStack #Testing for returning the correct types of stacks.
from UM.Resources import Resources #Mocking some functions of this.
import UM.Settings.InstanceContainer #Creating instance containers to register.
import UM.Settings.ContainerRegistry #Making empty container stacks.
import UM.Settings.ContainerStack #Setting the container registry here properly.
from UM.Settings.DefinitionContainer import DefinitionContainer
from UM.Settings.ContainerRegistry import ContainerRegistry
def creteEmptyContainers():
empty_container = ContainerRegistry.getInstance().getEmptyInstanceContainer()
empty_variant_container = copy.deepcopy(empty_container)
empty_variant_container.setMetaDataEntry("id", "empty_variant")
empty_variant_container.setMetaDataEntry("type", "variant")
ContainerRegistry.getInstance().addContainer(empty_variant_container)
empty_material_container = copy.deepcopy(empty_container)
empty_material_container.setMetaDataEntry("id", "empty_material")
empty_material_container.setMetaDataEntry("type", "material")
ContainerRegistry.getInstance().addContainer(empty_material_container)
empty_quality_container = copy.deepcopy(empty_container)
empty_quality_container.setMetaDataEntry("id", "empty_quality")
empty_quality_container.setName("Not Supported")
empty_quality_container.setMetaDataEntry("quality_type", "not_supported")
empty_quality_container.setMetaDataEntry("type", "quality")
empty_quality_container.setMetaDataEntry("supported", False)
ContainerRegistry.getInstance().addContainer(empty_quality_container)
empty_quality_changes_container = copy.deepcopy(empty_container)
empty_quality_changes_container.setMetaDataEntry("id", "empty_quality_changes")
empty_quality_changes_container.setMetaDataEntry("type", "quality_changes")
ContainerRegistry.getInstance().addContainer(empty_quality_changes_container)
## Gives a fresh CuraContainerRegistry instance.
@pytest.fixture()
def container_registry():
registry = CuraContainerRegistry()
UM.Settings.InstanceContainer.setContainerRegistry(registry)
UM.Settings.ContainerStack.setContainerRegistry(registry)
return registry
## Gives an arbitrary definition container.
@pytest.fixture()
def definition_container():
return DefinitionContainer(container_id = "Test Definition")
def teardown():
#If the temporary file for the legacy file rename test still exists, remove it.
@ -64,44 +18,47 @@ def teardown():
os.remove(temporary_file)
## Tests whether addContainer properly converts to ExtruderStack.
def test_addContainerExtruderStack(container_registry, definition_container):
creteEmptyContainers()
def test_addContainerExtruderStack(container_registry, definition_container, definition_changes_container):
container_registry.addContainer(definition_container)
container_registry.addContainer(definition_changes_container)
container_stack = UM.Settings.ContainerStack.ContainerStack(stack_id = "Test Container Stack") #A container we're going to convert.
container_stack = UM.Settings.ContainerStack.ContainerStack(stack_id = "Test Extruder Stack") #A container we're going to convert.
container_stack.setMetaDataEntry("type", "extruder_train") #This is now an extruder train.
container_stack.insertContainer(0, definition_container) #Add a definition to it so it doesn't complain.
container_stack.insertContainer(1, definition_changes_container)
mock_super_add_container = unittest.mock.MagicMock() #Takes the role of the Uranium-ContainerRegistry where the resulting containers get registered.
with unittest.mock.patch("UM.Settings.ContainerRegistry.ContainerRegistry.addContainer", mock_super_add_container):
container_registry.addContainer(container_stack)
assert len(mock_super_add_container.call_args_list) == 2 #Called only once.
assert len(mock_super_add_container.call_args_list[1][0]) == 1 #Called with one parameter.
assert type(mock_super_add_container.call_args_list[1][0][0]) == ExtruderStack
assert len(mock_super_add_container.call_args_list) == 1 #Called only once.
assert len(mock_super_add_container.call_args_list[0][0]) == 1 #Called with one parameter.
assert type(mock_super_add_container.call_args_list[0][0][0]) == ExtruderStack
## Tests whether addContainer properly converts to GlobalStack.
def test_addContainerGlobalStack(container_registry, definition_container):
def test_addContainerGlobalStack(container_registry, definition_container, definition_changes_container):
container_registry.addContainer(definition_container)
container_registry.addContainer(definition_changes_container)
container_stack = UM.Settings.ContainerStack.ContainerStack(stack_id = "Test Container Stack") #A container we're going to convert.
container_stack = UM.Settings.ContainerStack.ContainerStack(stack_id = "Test Global Stack") #A container we're going to convert.
container_stack.setMetaDataEntry("type", "machine") #This is now a global stack.
container_stack.insertContainer(0, definition_container) #Must have a definition.
container_stack.insertContainer(1, definition_changes_container) #Must have a definition changes.
mock_super_add_container = unittest.mock.MagicMock() #Takes the role of the Uranium-ContainerRegistry where the resulting containers get registered.
with unittest.mock.patch("UM.Settings.ContainerRegistry.ContainerRegistry.addContainer", mock_super_add_container):
container_registry.addContainer(container_stack)
assert len(mock_super_add_container.call_args_list) == 2 #Called only once.
assert len(mock_super_add_container.call_args_list[1][0]) == 1 #Called with one parameter.
assert type(mock_super_add_container.call_args_list[1][0][0]) == GlobalStack
assert len(mock_super_add_container.call_args_list) == 1 #Called only once.
assert len(mock_super_add_container.call_args_list[0][0]) == 1 #Called with one parameter.
assert type(mock_super_add_container.call_args_list[0][0][0]) == GlobalStack
def test_addContainerGoodSettingVersion(container_registry, definition_container):
from cura.CuraApplication import CuraApplication
definition_container.getMetaData()["setting_version"] = CuraApplication.SettingVersion
container_registry.addContainer(definition_container)
instance = UM.Settings.InstanceContainer.InstanceContainer(container_id = "Test Instance")
instance = UM.Settings.InstanceContainer.InstanceContainer(container_id = "Test Instance Right Version")
instance.setMetaDataEntry("setting_version", CuraApplication.SettingVersion)
instance.setDefinition(definition_container.getId())
@ -116,7 +73,7 @@ def test_addContainerNoSettingVersion(container_registry, definition_container):
definition_container.getMetaData()["setting_version"] = CuraApplication.SettingVersion
container_registry.addContainer(definition_container)
instance = UM.Settings.InstanceContainer.InstanceContainer(container_id = "Test Instance")
instance = UM.Settings.InstanceContainer.InstanceContainer(container_id = "Test Instance No Version")
#Don't add setting_version metadata.
instance.setDefinition(definition_container.getId())
@ -131,7 +88,7 @@ def test_addContainerBadSettingVersion(container_registry, definition_container)
definition_container.getMetaData()["setting_version"] = CuraApplication.SettingVersion
container_registry.addContainer(definition_container)
instance = UM.Settings.InstanceContainer.InstanceContainer(container_id = "Test Instance")
instance = UM.Settings.InstanceContainer.InstanceContainer(container_id = "Test Instance Wrong Version")
instance.setMetaDataEntry("setting_version", 9001) #Wrong version!
instance.setDefinition(definition_container.getId())
@ -140,38 +97,3 @@ def test_addContainerBadSettingVersion(container_registry, definition_container)
container_registry.addContainer(instance)
mock_super_add_container.assert_not_called() #Should not get passed on to UM.Settings.ContainerRegistry.addContainer, because the setting_version doesn't match its definition!
## Tests whether loading gives objects of the correct type.
# @pytest.mark.parametrize("filename, output_class", [
# ("ExtruderLegacy.stack.cfg", ExtruderStack),
# ("MachineLegacy.stack.cfg", GlobalStack),
# ("Left.extruder.cfg", ExtruderStack),
# ("Global.global.cfg", GlobalStack),
# ("Global.stack.cfg", GlobalStack)
# ])
# def test_loadTypes(filename, output_class, container_registry):
# #Mock some dependencies.
# Resources.getAllResourcesOfType = unittest.mock.MagicMock(return_value = [os.path.join(os.path.dirname(os.path.abspath(__file__)), "stacks", filename)]) #Return just this tested file.
#
# def findContainers(container_type = 0, id = None):
# if id == "some_instance":
# return [UM.Settings.ContainerRegistry._EmptyInstanceContainer(id)]
# elif id == "some_definition":
# return [DefinitionContainer(container_id = id)]
# else:
# return []
#
# container_registry.findContainers = findContainers
#
# with unittest.mock.patch("cura.Settings.GlobalStack.GlobalStack.findContainer"):
# with unittest.mock.patch("os.remove"):
# container_registry.load()
#
# #Check whether the resulting type was correct.
# stack_id = filename.split(".")[0]
# for container_id, container in container_registry._containers.items(): #Stupid ContainerRegistry class doesn't expose any way of getting at this except by prodding the privates.
# if container_id == stack_id: #This is the one we're testing.
# assert type(container) == output_class
# break
# else:
# assert False #Container stack with specified ID was not loaded.

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.
import pytest #This module contains automated tests.
import unittest.mock #For the mocking and monkeypatching functionality.
import copy
import cura.CuraApplication
import cura.Settings.CuraContainerStack #To get the list of container types.
import UM.Settings.ContainerRegistry #To create empty instance containers.
import UM.Settings.ContainerStack #To set the container registry the container stacks use.
from UM.Settings.DefinitionContainer import DefinitionContainer #To check against the class of DefinitionContainer.
from UM.Settings.InstanceContainer import InstanceContainer #To check against the class of InstanceContainer.
import cura.Settings.ExtruderStack #The module we're testing.
from cura.Settings.Exceptions import InvalidContainerError, InvalidOperationError #To check whether the correct exceptions are raised.
from cura.Settings.ExtruderManager import ExtruderManager
from UM.Settings.ContainerRegistry import ContainerRegistry
from cura.Settings.GlobalStack import GlobalStack
## Fake container registry that always provides all containers you ask of.
@pytest.yield_fixture()
def container_registry():
registry = unittest.mock.MagicMock()
registry.return_value = unittest.mock.NonCallableMagicMock()
registry.findInstanceContainers = lambda *args, registry = registry, **kwargs: [registry.return_value]
registry.findDefinitionContainers = lambda *args, registry = registry, **kwargs: [registry.return_value]
UM.Settings.ContainerRegistry.ContainerRegistry._ContainerRegistry__instance = registry
UM.Settings.ContainerStack._containerRegistry = registry
yield registry
UM.Settings.ContainerRegistry.ContainerRegistry._ContainerRegistry__instance = None
UM.Settings.ContainerStack._containerRegistry = None
## An empty extruder stack to test with.
@pytest.fixture()
def extruder_stack() -> cura.Settings.ExtruderStack.ExtruderStack:
creteEmptyContainers()
return cura.Settings.ExtruderStack.ExtruderStack("TestStack")
from cura.Settings.cura_empty_instance_containers import empty_container
## Gets an instance container with a specified container type.
#
@ -48,31 +22,6 @@ def getInstanceContainer(container_type) -> InstanceContainer:
container.setMetaDataEntry("type", container_type)
return container
def creteEmptyContainers():
empty_container = ContainerRegistry.getInstance().getEmptyInstanceContainer()
empty_variant_container = copy.deepcopy(empty_container)
empty_variant_container.setMetaDataEntry("id", "empty_variant")
empty_variant_container.setMetaDataEntry("type", "variant")
ContainerRegistry.getInstance().addContainer(empty_variant_container)
empty_material_container = copy.deepcopy(empty_container)
empty_material_container.setMetaDataEntry("id", "empty_material")
empty_material_container.setMetaDataEntry("type", "material")
ContainerRegistry.getInstance().addContainer(empty_material_container)
empty_quality_container = copy.deepcopy(empty_container)
empty_quality_container.setMetaDataEntry("id", "empty_quality")
empty_quality_container.setName("Not Supported")
empty_quality_container.setMetaDataEntry("quality_type", "not_supported")
empty_quality_container.setMetaDataEntry("type", "quality")
empty_quality_container.setMetaDataEntry("supported", False)
ContainerRegistry.getInstance().addContainer(empty_quality_container)
empty_quality_changes_container = copy.deepcopy(empty_container)
empty_quality_changes_container.setMetaDataEntry("id", "empty_quality_changes")
empty_quality_changes_container.setMetaDataEntry("type", "quality_changes")
ContainerRegistry.getInstance().addContainer(empty_quality_changes_container)
class DefinitionContainerSubClass(DefinitionContainer):
def __init__(self):
super().__init__(container_id = "SubDefinitionContainer")
@ -179,12 +128,30 @@ def test_constrainVariantInvalid(container, extruder_stack):
def test_constrainVariantValid(container, extruder_stack):
extruder_stack.variant = container #Should not give an error.
#Tests setting definition changes profiles to invalid containers.
@pytest.mark.parametrize("container", [
getInstanceContainer(container_type = "wrong container type"),
getInstanceContainer(container_type = "material"), #Existing, but still wrong type.
DefinitionContainer(container_id = "wrong class")
])
def test_constrainDefinitionChangesInvalid(container, global_stack):
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
global_stack.definitionChanges = container
#Test setting definition changes profiles.
@pytest.mark.parametrize("container", [
getInstanceContainer(container_type = "definition_changes"),
InstanceContainerSubClass(container_type = "definition_changes")
])
def test_constrainDefinitionChangesValid(container, global_stack):
global_stack.definitionChanges = container #Should not give an error.
#Tests setting definitions to invalid containers.
@pytest.mark.parametrize("container", [
getInstanceContainer(container_type = "wrong class"),
getInstanceContainer(container_type = "material"), #Existing, but still wrong class.
])
def test_constrainVariantInvalid(container, extruder_stack):
def test_constrainDefinitionInvalid(container, extruder_stack):
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
extruder_stack.definition = container
@ -196,23 +163,22 @@ def test_constrainVariantInvalid(container, extruder_stack):
def test_constrainDefinitionValid(container, extruder_stack):
extruder_stack.definition = container #Should not give an error.
## Tests whether deserialising completes the missing containers with empty
# ones.
@pytest.mark.skip #The test currently fails because the definition container doesn't have a category, which is wrong but we don't have time to refactor that right now.
def test_deserializeCompletesEmptyContainers(extruder_stack: cura.Settings.ExtruderStack):
extruder_stack._containers = [DefinitionContainer(container_id = "definition")] #Set the internal state of this stack manually.
## Tests whether deserialising completes the missing containers with empty ones.
def test_deserializeCompletesEmptyContainers(extruder_stack):
extruder_stack._containers = [DefinitionContainer(container_id = "definition"), extruder_stack.definitionChanges] #Set the internal state of this stack manually.
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
extruder_stack.deserialize("")
assert len(extruder_stack.getContainers()) == len(cura.Settings.CuraContainerStack._ContainerIndexes.IndexTypeMap) #Needs a slot for every type.
for container_type_index in cura.Settings.CuraContainerStack._ContainerIndexes.IndexTypeMap:
if container_type_index == cura.Settings.CuraContainerStack._ContainerIndexes.Definition: #We're not checking the definition.
if container_type_index in \
(cura.Settings.CuraContainerStack._ContainerIndexes.Definition,
cura.Settings.CuraContainerStack._ContainerIndexes.DefinitionChanges): # We're not checking the definition or definition_changes
continue
assert extruder_stack.getContainer(container_type_index).getId() == "empty" #All others need to be empty.
assert extruder_stack.getContainer(container_type_index) == empty_container #All others need to be empty.
## Tests whether an instance container with the wrong type gets removed when
# deserialising.
## Tests whether an instance container with the wrong type gets removed when deserialising.
def test_deserializeRemovesWrongInstanceContainer(extruder_stack):
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = getInstanceContainer(container_type = "wrong type")
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition")
@ -222,8 +188,7 @@ def test_deserializeRemovesWrongInstanceContainer(extruder_stack):
assert extruder_stack.quality == extruder_stack._empty_instance_container #Replaced with empty.
## Tests whether a container with the wrong class gets removed when
# deserialising.
## Tests whether a container with the wrong class gets removed when deserialising.
def test_deserializeRemovesWrongContainerClass(extruder_stack):
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = DefinitionContainer(container_id = "wrong class")
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition")
@ -233,8 +198,7 @@ def test_deserializeRemovesWrongContainerClass(extruder_stack):
assert extruder_stack.quality == extruder_stack._empty_instance_container #Replaced with empty.
## Tests whether an instance container in the definition spot results in an
# error.
## Tests whether an instance container in the definition spot results in an error.
def test_deserializeWrongDefinitionClass(extruder_stack):
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = getInstanceContainer(container_type = "definition") #Correct type but wrong class.
@ -242,8 +206,7 @@ def test_deserializeWrongDefinitionClass(extruder_stack):
with pytest.raises(UM.Settings.ContainerStack.InvalidContainerStackError): #Must raise an error that there is no definition container.
extruder_stack.deserialize("")
## Tests whether an instance container with the wrong type is moved into the
# correct slot by deserialising.
## Tests whether an instance container with the wrong type is moved into the correct slot by deserialising.
def test_deserializeMoveInstanceContainer(extruder_stack):
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = getInstanceContainer(container_type = "material") #Not in the correct spot.
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition")
@ -251,26 +214,21 @@ def test_deserializeMoveInstanceContainer(extruder_stack):
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
extruder_stack.deserialize("")
assert extruder_stack.quality.getId() == "empty"
assert extruder_stack.material.getId() != "empty"
from UM.Settings.Validator import Validator
## Tests whether a definition container in the wrong spot is moved into the
# correct spot by deserialising.
@pytest.mark.skip #The test currently fails because the definition container doesn't have a category, which is wrong but we don't have time to refactor that right now.
assert extruder_stack.quality == empty_container
assert extruder_stack.material != empty_container
## Tests whether a definition container in the wrong spot is moved into the correct spot by deserialising.
def test_deserializeMoveDefinitionContainer(extruder_stack):
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Material] = DefinitionContainer(container_id = "some definition") #Not in the correct spot.
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
extruder_stack.deserialize("")
assert extruder_stack.material.getId() == "empty"
assert extruder_stack.definition.getId() != "empty"
assert extruder_stack.material == empty_container
assert extruder_stack.definition != empty_container
UM.Settings.ContainerStack._containerRegistry = None
## Tests whether getProperty properly applies the stack-like behaviour on its
# containers.
def test_getPropertyFallThrough(extruder_stack):
## Tests whether getProperty properly applies the stack-like behaviour on its containers.
def test_getPropertyFallThrough(global_stack, extruder_stack):
# ExtruderStack.setNextStack calls registerExtruder for backward compatibility, but we do not need a complete extruder manager
ExtruderManager._ExtruderManager__instance = unittest.mock.MagicMock()
@ -300,8 +258,7 @@ def test_getPropertyFallThrough(extruder_stack):
with unittest.mock.patch("cura.Settings.CuraContainerStack.DefinitionContainer", unittest.mock.MagicMock): #To guard against the type checking.
extruder_stack.definition = mock_layer_heights[container_indices.Definition] #There's a layer height in here!
stack = GlobalStack("PyTest GlobalStack")
extruder_stack.setNextStack(stack)
extruder_stack.setNextStack(global_stack)
assert extruder_stack.getProperty("layer_height", "value") == container_indices.Definition
extruder_stack.variant = mock_layer_heights[container_indices.Variant]
@ -340,4 +297,4 @@ def test_setPropertyUser(key, property, value, extruder_stack):
extruder_stack.setProperty(key, property, value) #The actual test.
extruder_stack.userChanges.setProperty.assert_called_once_with(key, property, value) #Make sure that the user container gets a setProperty call.
extruder_stack.userChanges.setProperty.assert_called_once_with(key, property, value, None, False) #Make sure that the user container gets a setProperty call.

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.
import pytest #This module contains unit tests.
import unittest.mock #To monkeypatch some mocks in place of dependencies.
import copy
import cura.CuraApplication
import cura.Settings.GlobalStack #The module we're testing.
import cura.Settings.CuraContainerStack #To get the list of container types.
from cura.Settings.Exceptions import TooManyExtrudersError, InvalidContainerError, InvalidOperationError #To test raising these errors.
from cura.Settings.Exceptions import InvalidContainerError, InvalidOperationError #To test raising these errors.
from UM.Settings.DefinitionContainer import DefinitionContainer #To test against the class DefinitionContainer.
from UM.Settings.InstanceContainer import InstanceContainer #To test against the class InstanceContainer.
from UM.Settings.SettingInstance import InstanceState
import UM.Settings.ContainerRegistry
import UM.Settings.ContainerStack
import UM.Settings.SettingDefinition #To add settings to the definition.
from UM.Settings.ContainerRegistry import ContainerRegistry
## Fake container registry that always provides all containers you ask of.
@pytest.yield_fixture()
def container_registry():
registry = unittest.mock.MagicMock()
registry.return_value = unittest.mock.NonCallableMagicMock()
registry.findInstanceContainers = lambda *args, registry = registry, **kwargs: [registry.return_value]
registry.findDefinitionContainers = lambda *args, registry = registry, **kwargs: [registry.return_value]
UM.Settings.ContainerRegistry.ContainerRegistry._ContainerRegistry__instance = registry
UM.Settings.ContainerStack._containerRegistry = registry
yield registry
UM.Settings.ContainerRegistry.ContainerRegistry._ContainerRegistry__instance = None
UM.Settings.ContainerStack._containerRegistry = None
#An empty global stack to test with.
@pytest.fixture()
def global_stack() -> cura.Settings.GlobalStack.GlobalStack:
creteEmptyContainers()
return cura.Settings.GlobalStack.GlobalStack("TestStack")
from cura.Settings.cura_empty_instance_containers import empty_container
## Gets an instance container with a specified container type.
#
@ -48,31 +24,6 @@ def getInstanceContainer(container_type) -> InstanceContainer:
container.setMetaDataEntry("type", container_type)
return container
def creteEmptyContainers():
empty_container = ContainerRegistry.getInstance().getEmptyInstanceContainer()
empty_variant_container = copy.deepcopy(empty_container)
empty_variant_container.setMetaDataEntry("id", "empty_variant")
empty_variant_container.setMetaDataEntry("type", "variant")
ContainerRegistry.getInstance().addContainer(empty_variant_container)
empty_material_container = copy.deepcopy(empty_container)
empty_material_container.setMetaDataEntry("id", "empty_material")
empty_material_container.setMetaDataEntry("type", "material")
ContainerRegistry.getInstance().addContainer(empty_material_container)
empty_quality_container = copy.deepcopy(empty_container)
empty_quality_container.setMetaDataEntry("id", "empty_quality")
empty_quality_container.setName("Not Supported")
empty_quality_container.setMetaDataEntry("quality_type", "not_supported")
empty_quality_container.setMetaDataEntry("type", "quality")
empty_quality_container.setMetaDataEntry("supported", False)
ContainerRegistry.getInstance().addContainer(empty_quality_container)
empty_quality_changes_container = copy.deepcopy(empty_container)
empty_quality_changes_container.setMetaDataEntry("id", "empty_quality_changes")
empty_quality_changes_container.setMetaDataEntry("type", "quality_changes")
ContainerRegistry.getInstance().addContainer(empty_quality_changes_container)
class DefinitionContainerSubClass(DefinitionContainer):
def __init__(self):
super().__init__(container_id = "SubDefinitionContainer")
@ -229,7 +180,7 @@ def test_constrainDefinitionChangesValid(container, global_stack):
getInstanceContainer(container_type = "wrong class"),
getInstanceContainer(container_type = "material"), #Existing, but still wrong class.
])
def test_constrainVariantInvalid(container, global_stack):
def test_constrainDefinitionInvalid(container, global_stack):
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
global_stack.definition = container
@ -241,23 +192,22 @@ def test_constrainVariantInvalid(container, global_stack):
def test_constrainDefinitionValid(container, global_stack):
global_stack.definition = container #Should not give an error.
## Tests whether deserialising completes the missing containers with empty
# ones.
@pytest.mark.skip #The test currently fails because the definition container doesn't have a category, which is wrong but we don't have time to refactor that right now.
def test_deserializeCompletesEmptyContainers(global_stack: cura.Settings.GlobalStack):
global_stack._containers = [DefinitionContainer(container_id = "definition")] #Set the internal state of this stack manually.
## Tests whether deserialising completes the missing containers with empty ones. The initial containers are just the
# definition and the definition_changes (that cannot be empty after CURA-5281)
def test_deserializeCompletesEmptyContainers(global_stack):
global_stack._containers = [DefinitionContainer(container_id = "definition"), global_stack.definitionChanges] #Set the internal state of this stack manually.
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
global_stack.deserialize("")
assert len(global_stack.getContainers()) == len(cura.Settings.CuraContainerStack._ContainerIndexes.IndexTypeMap) #Needs a slot for every type.
for container_type_index in cura.Settings.CuraContainerStack._ContainerIndexes.IndexTypeMap:
if container_type_index == cura.Settings.CuraContainerStack._ContainerIndexes.Definition: #We're not checking the definition.
if container_type_index in \
(cura.Settings.CuraContainerStack._ContainerIndexes.Definition, cura.Settings.CuraContainerStack._ContainerIndexes.DefinitionChanges): #We're not checking the definition or definition_changes
continue
assert global_stack.getContainer(container_type_index).getId() == "empty" #All others need to be empty.
assert global_stack.getContainer(container_type_index) == empty_container #All others need to be empty.
## Tests whether an instance container with the wrong type gets removed when
# deserialising.
## Tests whether an instance container with the wrong type gets removed when deserialising.
def test_deserializeRemovesWrongInstanceContainer(global_stack):
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = getInstanceContainer(container_type = "wrong type")
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition")
@ -267,8 +217,7 @@ def test_deserializeRemovesWrongInstanceContainer(global_stack):
assert global_stack.quality == global_stack._empty_instance_container #Replaced with empty.
## Tests whether a container with the wrong class gets removed when
# deserialising.
## Tests whether a container with the wrong class gets removed when deserialising.
def test_deserializeRemovesWrongContainerClass(global_stack):
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = DefinitionContainer(container_id = "wrong class")
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition")
@ -278,8 +227,7 @@ def test_deserializeRemovesWrongContainerClass(global_stack):
assert global_stack.quality == global_stack._empty_instance_container #Replaced with empty.
## Tests whether an instance container in the definition spot results in an
# error.
## Tests whether an instance container in the definition spot results in an error.
def test_deserializeWrongDefinitionClass(global_stack):
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = getInstanceContainer(container_type = "definition") #Correct type but wrong class.
@ -287,8 +235,7 @@ def test_deserializeWrongDefinitionClass(global_stack):
with pytest.raises(UM.Settings.ContainerStack.InvalidContainerStackError): #Must raise an error that there is no definition container.
global_stack.deserialize("")
## Tests whether an instance container with the wrong type is moved into the
# correct slot by deserialising.
## Tests whether an instance container with the wrong type is moved into the correct slot by deserialising.
def test_deserializeMoveInstanceContainer(global_stack):
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = getInstanceContainer(container_type = "material") #Not in the correct spot.
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition")
@ -296,25 +243,20 @@ def test_deserializeMoveInstanceContainer(global_stack):
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
global_stack.deserialize("")
assert global_stack.quality.getId() == "empty"
assert global_stack.material.getId() != "empty"
assert global_stack.quality == empty_container
assert global_stack.material != empty_container
## Tests whether a definition container in the wrong spot is moved into the
# correct spot by deserialising.
@pytest.mark.skip #The test currently fails because the definition container doesn't have a category, which is wrong but we don't have time to refactor that right now.
## Tests whether a definition container in the wrong spot is moved into the correct spot by deserialising.
def test_deserializeMoveDefinitionContainer(global_stack):
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Material] = DefinitionContainer(container_id = "some definition") #Not in the correct spot.
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
global_stack.deserialize("")
assert global_stack.material.getId() == "empty"
assert global_stack.definition.getId() != "empty"
assert global_stack.material == empty_container
assert global_stack.definition != empty_container
UM.Settings.ContainerStack._containerRegistry = None
## Tests whether getProperty properly applies the stack-like behaviour on its
# containers.
## Tests whether getProperty properly applies the stack-like behaviour on its containers.
def test_getPropertyFallThrough(global_stack):
#A few instance container mocks to put in the stack.
mock_layer_heights = {} #For each container type, a mock container that defines layer height to something unique.
@ -365,8 +307,7 @@ def test_getPropertyNoResolveInDefinition(global_stack):
global_stack.definition = value
assert global_stack.getProperty("material_bed_temperature", "value") == 10 #No resolve, so fall through to value.
## In definitions, when the value is asked and there is a resolve function, it
# must get the resolve first.
## In definitions, when the value is asked and there is a resolve function, it must get the resolve first.
def test_getPropertyResolveInDefinition(global_stack):
resolve_and_value = unittest.mock.MagicMock() #Sets the resolve and value for bed temperature.
resolve_and_value.getProperty = lambda key, property, context = None: (7.5 if property == "resolve" else 5) if (key == "material_bed_temperature" and property in ("resolve", "value")) else None #7.5 resolve, 5 value.
@ -375,8 +316,7 @@ def test_getPropertyResolveInDefinition(global_stack):
global_stack.definition = resolve_and_value
assert global_stack.getProperty("material_bed_temperature", "value") == 7.5 #Resolve wins in the definition.
## In instance containers, when the value is asked and there is a resolve
# function, it must get the value first.
## In instance containers, when the value is asked and there is a resolve function, it must get the value first.
def test_getPropertyResolveInInstance(global_stack):
container_indices = cura.Settings.CuraContainerStack._ContainerIndexes
instance_containers = {}
@ -402,8 +342,7 @@ def test_getPropertyResolveInInstance(global_stack):
global_stack.userChanges = instance_containers[container_indices.UserChanges]
assert global_stack.getProperty("material_bed_temperature", "value") == 5
## Tests whether the value in instances gets evaluated before the resolve in
# definitions.
## Tests whether the value in instances gets evaluated before the resolve in definitions.
def test_getPropertyInstancesBeforeResolve(global_stack):
value = unittest.mock.MagicMock() #Sets just the value.
value.getProperty = lambda key, property, context = None: (10 if property == "value" else (InstanceState.User if property != "limit_to_extruder" else "-1")) if key == "material_bed_temperature" else None
@ -417,8 +356,7 @@ def test_getPropertyInstancesBeforeResolve(global_stack):
assert global_stack.getProperty("material_bed_temperature", "value") == 10
## Tests whether the hasUserValue returns true for settings that are changed in
# the user-changes container.
## Tests whether the hasUserValue returns true for settings that are changed in the user-changes container.
def test_hasUserValueUserChanges(global_stack):
container = unittest.mock.MagicMock()
container.getMetaDataEntry = unittest.mock.MagicMock(return_value = "user")
@ -429,8 +367,7 @@ def test_hasUserValueUserChanges(global_stack):
assert not global_stack.hasUserValue("infill_sparse_density")
assert not global_stack.hasUserValue("")
## Tests whether the hasUserValue returns true for settings that are changed in
# the quality-changes container.
## Tests whether the hasUserValue returns true for settings that are changed in the quality-changes container.
def test_hasUserValueQualityChanges(global_stack):
container = unittest.mock.MagicMock()
container.getMetaDataEntry = unittest.mock.MagicMock(return_value = "quality_changes")
@ -441,8 +378,7 @@ def test_hasUserValueQualityChanges(global_stack):
assert not global_stack.hasUserValue("infill_sparse_density")
assert not global_stack.hasUserValue("")
## Tests whether a container in some other place on the stack is correctly not
# recognised as user value.
## Tests whether a container in some other place on the stack is correctly not recognised as user value.
def test_hasNoUserValue(global_stack):
container = unittest.mock.MagicMock()
container.getMetaDataEntry = unittest.mock.MagicMock(return_value = "quality")
@ -481,4 +417,4 @@ def test_setPropertyUser(key, property, value, global_stack):
global_stack.setProperty(key, property, value) #The actual test.
global_stack.userChanges.setProperty.assert_called_once_with(key, property, value) #Make sure that the user container gets a setProperty call.
global_stack.userChanges.setProperty.assert_called_once_with(key, property, value, None, False) #Make sure that the user container gets a setProperty call.

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
from cura.Arranging.Arrange import Arrange
from cura.Arranging.ShapeArray import ShapeArray
## Triangle of area 12
def gimmeTriangle():
return numpy.array([[-3, 1], [3, 1], [0, -3]], dtype=numpy.int32)
@ -102,7 +104,7 @@ def test_centerFirst_rectangular():
## Test centerFirst
def test_centerFirst_rectangular():
def test_centerFirst_rectangular2():
ar = Arrange(10, 20, 5, 10, scale = 1)
ar.centerFirst()
print(ar._priority)

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

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

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