mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-07 15:07:28 -06:00
Merge branch 'master' into refactor_singleton_settingsbase
Resolved a conflict in CuraEngineBackend due to refactor removing JSON from the command while master added optional verbose mode in debug mode. Contributes to issue CURA-4410.
This commit is contained in:
commit
06525bed4b
110 changed files with 1801 additions and 1139 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -15,6 +15,7 @@ LC_MESSAGES
|
|||
.cache
|
||||
*.qmlc
|
||||
.mypy_cache
|
||||
.pytest_cache
|
||||
|
||||
#MacOS
|
||||
.DS_Store
|
||||
|
|
|
@ -13,6 +13,7 @@ from cura.Backups.BackupsManager import BackupsManager
|
|||
# api = CuraAPI()
|
||||
# api.backups.createBackup()
|
||||
# api.backups.restoreBackup(my_zip_file, {"cura_release": "3.1"})``
|
||||
|
||||
class Backups:
|
||||
manager = BackupsManager() # Re-used instance of the backups manager.
|
||||
|
||||
|
|
33
cura/API/Interface/Settings.py
Normal file
33
cura/API/Interface/Settings.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
|
||||
## The Interface.Settings API provides a version-proof bridge between Cura's
|
||||
# (currently) sidebar UI and plug-ins that hook into it.
|
||||
#
|
||||
# Usage:
|
||||
# ``from cura.API import CuraAPI
|
||||
# api = CuraAPI()
|
||||
# api.interface.settings.getContextMenuItems()
|
||||
# data = {
|
||||
# "name": "My Plugin Action",
|
||||
# "iconName": "my-plugin-icon",
|
||||
# "actions": my_menu_actions,
|
||||
# "menu_item": MyPluginAction(self)
|
||||
# }
|
||||
# api.interface.settings.addContextMenuItem(data)``
|
||||
|
||||
class Settings:
|
||||
# Re-used instance of Cura:
|
||||
application = CuraApplication.getInstance() # type: CuraApplication
|
||||
|
||||
## Add items to the sidebar context menu.
|
||||
# \param menu_item dict containing the menu item to add.
|
||||
def addContextMenuItem(self, menu_item: dict) -> None:
|
||||
self.application.addSidebarCustomMenuItem(menu_item)
|
||||
|
||||
## Get all custom items currently added to the sidebar context menu.
|
||||
# \return List containing all custom context menu items.
|
||||
def getContextMenuItems(self) -> list:
|
||||
return self.application.getSidebarCustomMenuItems()
|
24
cura/API/Interface/__init__.py
Normal file
24
cura/API/Interface/__init__.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from cura.API.Interface.Settings import Settings
|
||||
|
||||
## The Interface class serves as a common root for the specific API
|
||||
# methods for each interface element.
|
||||
#
|
||||
# Usage:
|
||||
# ``from cura.API import CuraAPI
|
||||
# api = CuraAPI()
|
||||
# api.interface.settings.addContextMenuItem()
|
||||
# api.interface.viewport.addOverlay() # Not implemented, just a hypothetical
|
||||
# api.interface.toolbar.getToolButtonCount() # Not implemented, just a hypothetical
|
||||
# # etc.``
|
||||
|
||||
class Interface:
|
||||
|
||||
# For now we use the same API version to be consistent.
|
||||
VERSION = PluginRegistry.APIVersion
|
||||
|
||||
# API methods specific to the settings portion of the UI
|
||||
settings = Settings()
|
|
@ -2,6 +2,7 @@
|
|||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from cura.API.Backups import Backups
|
||||
from cura.API.Interface import Interface
|
||||
|
||||
## The official Cura API that plug-ins can use to interact with Cura.
|
||||
#
|
||||
|
@ -9,10 +10,14 @@ from cura.API.Backups import Backups
|
|||
# this API provides a version-safe interface with proper deprecation warnings
|
||||
# etc. Usage of any other methods than the ones provided in this API can cause
|
||||
# plug-ins to be unstable.
|
||||
|
||||
class CuraAPI:
|
||||
|
||||
# For now we use the same API version to be consistent.
|
||||
VERSION = PluginRegistry.APIVersion
|
||||
|
||||
# Backups API.
|
||||
# Backups API
|
||||
backups = Backups()
|
||||
|
||||
# Interface API
|
||||
interface = Interface()
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
from UM.Application import Application #To modify the maximum zoom level.
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.Scene.Platform import Platform
|
||||
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
|
||||
|
@ -170,6 +171,12 @@ class BuildVolume(SceneNode):
|
|||
if shape:
|
||||
self._shape = shape
|
||||
|
||||
## Get the length of the 3D diagonal through the build volume.
|
||||
#
|
||||
# This gives a sense of the scale of the build volume in general.
|
||||
def getDiagonalSize(self) -> float:
|
||||
return math.sqrt(self._width * self._width + self._height * self._height + self._depth * self._depth)
|
||||
|
||||
def getDisallowedAreas(self) -> List[Polygon]:
|
||||
return self._disallowed_areas
|
||||
|
||||
|
@ -235,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
|
||||
|
@ -552,6 +561,12 @@ class BuildVolume(SceneNode):
|
|||
if self._engine_ready:
|
||||
self.rebuild()
|
||||
|
||||
camera = Application.getInstance().getController().getCameraTool()
|
||||
if camera:
|
||||
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
|
||||
self.rebuild()
|
||||
|
|
|
@ -50,7 +50,8 @@ class CuraActions(QObject):
|
|||
scene = cura.CuraApplication.CuraApplication.getInstance().getController().getScene()
|
||||
camera = scene.getActiveCamera()
|
||||
if camera:
|
||||
camera.setPosition(Vector(-80, 250, 700))
|
||||
diagonal_size = cura.CuraApplication.CuraApplication.getInstance().getBuildVolume().getDiagonalSize()
|
||||
camera.setPosition(Vector(-80, 250, 700) * diagonal_size / 375)
|
||||
camera.setPerspective(True)
|
||||
camera.lookAt(Vector(0, 0, 0))
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -104,6 +103,8 @@ from cura.Settings.UserChangesModel import UserChangesModel
|
|||
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
|
||||
|
||||
|
@ -117,11 +118,12 @@ if TYPE_CHECKING:
|
|||
numpy.seterr(all = "ignore")
|
||||
|
||||
try:
|
||||
from cura.CuraVersion import CuraVersion, CuraBuildType, CuraDebugMode
|
||||
from cura.CuraVersion import CuraVersion, CuraBuildType, CuraDebugMode, CuraSDKVersion
|
||||
except ImportError:
|
||||
CuraVersion = "master" # [CodeStyle: Reflecting imported value]
|
||||
CuraBuildType = ""
|
||||
CuraDebugMode = False
|
||||
CuraSDKVersion = ""
|
||||
|
||||
|
||||
class CuraApplication(QtApplication):
|
||||
|
@ -213,7 +215,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 = []
|
||||
|
@ -226,6 +227,10 @@ class CuraApplication(QtApplication):
|
|||
|
||||
self._need_to_show_user_agreement = True
|
||||
|
||||
self._sidebar_custom_menu_items = [] # type: list # Keeps list of custom menu items for the side bar
|
||||
|
||||
self._plugins_loaded = False
|
||||
|
||||
# Backups
|
||||
self._auto_save = None
|
||||
self._save_data_enabled = True
|
||||
|
@ -362,42 +367,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.
|
||||
|
@ -508,9 +494,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
|
||||
|
@ -625,7 +608,7 @@ class CuraApplication(QtApplication):
|
|||
# Cura has multiple locations where instance containers need to be saved, so we need to handle this differently.
|
||||
def saveSettings(self):
|
||||
if not self.started or not self._save_data_enabled:
|
||||
# Do not do saving during application start or when data should not be safed on quit.
|
||||
# Do not do saving during application start or when data should not be saved on quit.
|
||||
return
|
||||
ContainerRegistry.getInstance().saveDirtyContainers()
|
||||
self.savePreferences()
|
||||
|
@ -774,7 +757,10 @@ class CuraApplication(QtApplication):
|
|||
# Initialize camera
|
||||
root = controller.getScene().getRoot()
|
||||
camera = Camera("3d", root)
|
||||
camera.setPosition(Vector(-80, 250, 700))
|
||||
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")
|
||||
|
@ -908,6 +894,7 @@ class CuraApplication(QtApplication):
|
|||
engine.rootContext().setContextProperty("CuraApplication", self)
|
||||
engine.rootContext().setContextProperty("PrintInformation", self._print_information)
|
||||
engine.rootContext().setContextProperty("CuraActions", self._cura_actions)
|
||||
engine.rootContext().setContextProperty("CuraSDKVersion", CuraSDKVersion)
|
||||
|
||||
qmlRegisterUncreatableType(CuraApplication, "Cura", 1, 0, "ResourceTypes", "Just an Enum type")
|
||||
|
||||
|
@ -942,6 +929,7 @@ class CuraApplication(QtApplication):
|
|||
qmlRegisterType(MachineNameValidator, "Cura", 1, 0, "MachineNameValidator")
|
||||
qmlRegisterType(UserChangesModel, "Cura", 1, 0, "UserChangesModel")
|
||||
qmlRegisterSingletonType(ContainerManager, "Cura", 1, 0, "ContainerManager", ContainerManager.getInstance)
|
||||
qmlRegisterType(SidebarCustomMenuItemsModel, "Cura", 1, 0, "SidebarCustomMenuItemsModel")
|
||||
|
||||
# As of Qt5.7, it is necessary to get rid of any ".." in the path for the singleton to work.
|
||||
actions_url = QUrl.fromLocalFile(os.path.abspath(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Actions.qml")))
|
||||
|
@ -989,30 +977,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()}
|
||||
|
@ -1729,3 +1701,10 @@ class CuraApplication(QtApplication):
|
|||
@pyqtSlot()
|
||||
def showMoreInformationDialogForAnonymousDataCollection(self):
|
||||
cast(SliceInfo, self._plugin_registry.getPluginObject("SliceInfoPlugin")).showMoreInfoDialog()
|
||||
|
||||
def addSidebarCustomMenuItem(self, menu_item: dict) -> None:
|
||||
self._sidebar_custom_menu_items.append(menu_item)
|
||||
|
||||
def getSidebarCustomMenuItems(self) -> list:
|
||||
return self._sidebar_custom_menu_items
|
||||
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
from collections import defaultdict, OrderedDict
|
||||
import copy
|
||||
import uuid
|
||||
from typing import Dict, cast
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
from typing import Dict, Optional, TYPE_CHECKING
|
||||
|
||||
from PyQt5.Qt import QTimer, QObject, pyqtSignal, pyqtSlot
|
||||
|
||||
|
@ -18,6 +17,7 @@ from UM.Util import parseBool
|
|||
|
||||
from .MaterialNode import MaterialNode
|
||||
from .MaterialGroup import MaterialGroup
|
||||
from .VariantType import VariantType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||
|
@ -47,7 +47,7 @@ class MaterialManager(QObject):
|
|||
|
||||
self._fallback_materials_map = dict() # material_type -> generic material metadata
|
||||
self._material_group_map = dict() # root_material_id -> MaterialGroup
|
||||
self._diameter_machine_variant_material_map = dict() # approximate diameter str -> dict(machine_definition_id -> MaterialNode)
|
||||
self._diameter_machine_nozzle_buildplate_material_map = dict() # approximate diameter str -> dict(machine_definition_id -> MaterialNode)
|
||||
|
||||
# We're using these two maps to convert between the specific diameter material id and the generic material id
|
||||
# because the generic material ids are used in qualities and definitions, while the specific diameter material is meant
|
||||
|
@ -187,51 +187,77 @@ class MaterialManager(QObject):
|
|||
self._diameter_material_map[root_material_id] = default_root_material_id
|
||||
|
||||
# Map #4
|
||||
# "machine" -> "variant_name" -> "root material ID" -> specific material InstanceContainer
|
||||
# Construct the "machine" -> "variant" -> "root material ID" -> specific material InstanceContainer
|
||||
self._diameter_machine_variant_material_map = dict()
|
||||
# "machine" -> "nozzle name" -> "buildplate name" -> "root material ID" -> specific material InstanceContainer
|
||||
self._diameter_machine_nozzle_buildplate_material_map = dict()
|
||||
for material_metadata in material_metadatas.values():
|
||||
self.__addMaterialMetadataIntoLookupTree(material_metadata)
|
||||
|
||||
self.materialsUpdated.emit()
|
||||
|
||||
def __addMaterialMetadataIntoLookupTree(self, material_metadata: dict) -> None:
|
||||
material_id = material_metadata["id"]
|
||||
|
||||
# We don't store empty material in the lookup tables
|
||||
if material_metadata["id"] == "empty_material":
|
||||
continue
|
||||
if material_id == "empty_material":
|
||||
return
|
||||
|
||||
root_material_id = material_metadata["base_file"]
|
||||
definition = material_metadata["definition"]
|
||||
approximate_diameter = material_metadata["approximate_diameter"]
|
||||
|
||||
if approximate_diameter not in self._diameter_machine_variant_material_map:
|
||||
self._diameter_machine_variant_material_map[approximate_diameter] = {}
|
||||
if approximate_diameter not in self._diameter_machine_nozzle_buildplate_material_map:
|
||||
self._diameter_machine_nozzle_buildplate_material_map[approximate_diameter] = {}
|
||||
|
||||
machine_variant_material_map = self._diameter_machine_variant_material_map[approximate_diameter]
|
||||
if definition not in machine_variant_material_map:
|
||||
machine_variant_material_map[definition] = MaterialNode()
|
||||
machine_nozzle_buildplate_material_map = self._diameter_machine_nozzle_buildplate_material_map[
|
||||
approximate_diameter]
|
||||
if definition not in machine_nozzle_buildplate_material_map:
|
||||
machine_nozzle_buildplate_material_map[definition] = MaterialNode()
|
||||
|
||||
machine_node = machine_variant_material_map[definition]
|
||||
variant_name = material_metadata.get("variant_name")
|
||||
if not variant_name:
|
||||
# if there is no variant, this material is for the machine, so put its metadata in the machine node.
|
||||
machine_node.material_map[root_material_id] = MaterialNode(material_metadata)
|
||||
else:
|
||||
# this material is variant-specific, so we save it in a variant-specific node under the
|
||||
# machine-specific node
|
||||
# This is a list of information regarding the intermediate nodes:
|
||||
# nozzle -> buildplate
|
||||
nozzle_name = material_metadata.get("variant_name")
|
||||
buildplate_name = material_metadata.get("buildplate_name")
|
||||
intermediate_node_info_list = [(nozzle_name, VariantType.NOZZLE),
|
||||
(buildplate_name, VariantType.BUILD_PLATE),
|
||||
]
|
||||
|
||||
# Check first if the variant exist in the manager
|
||||
existing_variant = self._application.getVariantManager().getVariantNode(definition, variant_name)
|
||||
if existing_variant is not None:
|
||||
if variant_name not in machine_node.children_map:
|
||||
machine_node.children_map[variant_name] = MaterialNode()
|
||||
variant_manager = self._application.getVariantManager()
|
||||
|
||||
variant_node = machine_node.children_map[variant_name]
|
||||
if root_material_id in variant_node.material_map: # We shouldn't have duplicated variant-specific materials for the same machine.
|
||||
ConfigurationErrorMessage.getInstance().addFaultyContainers(root_material_id)
|
||||
continue
|
||||
variant_node.material_map[root_material_id] = MaterialNode(material_metadata)
|
||||
else:
|
||||
# Add this container id to the wrong containers list in the registry
|
||||
Logger.log("w", "Not adding {id} to the material manager because the variant does not exist.".format(id = material_metadata["id"]))
|
||||
machine_node = machine_nozzle_buildplate_material_map[definition]
|
||||
current_node = machine_node
|
||||
current_intermediate_node_info_idx = 0
|
||||
error_message = None # type: Optional[str]
|
||||
while current_intermediate_node_info_idx < len(intermediate_node_info_list):
|
||||
variant_name, variant_type = intermediate_node_info_list[current_intermediate_node_info_idx]
|
||||
if variant_name is not None:
|
||||
# The new material has a specific variant, so it needs to be added to that specific branch in the tree.
|
||||
variant = variant_manager.getVariantNode(definition, variant_name, variant_type)
|
||||
if variant is None:
|
||||
error_message = "Material {id} contains a variant {name} that does not exist.".format(
|
||||
id = material_metadata["id"], name = variant_name)
|
||||
break
|
||||
|
||||
# Update the current node to advance to a more specific branch
|
||||
if variant_name not in current_node.children_map:
|
||||
current_node.children_map[variant_name] = MaterialNode()
|
||||
current_node = current_node.children_map[variant_name]
|
||||
|
||||
current_intermediate_node_info_idx += 1
|
||||
|
||||
if error_message is not None:
|
||||
Logger.log("e", "%s It will not be added into the material lookup tree.", error_message)
|
||||
self._container_registry.addWrongContainerId(material_metadata["id"])
|
||||
return
|
||||
|
||||
self.materialsUpdated.emit()
|
||||
# Add the material to the current tree node, which is the deepest (the most specific) branch we can find.
|
||||
# Sanity check: Make sure that there is no duplicated materials.
|
||||
if root_material_id in current_node.material_map:
|
||||
Logger.log("e", "Duplicated material [%s] with root ID [%s]. It has already been added.",
|
||||
material_id, root_material_id)
|
||||
ConfigurationErrorMessage.getInstance().addFaultyContainers(root_material_id)
|
||||
return
|
||||
|
||||
current_node.material_map[root_material_id] = MaterialNode(material_metadata)
|
||||
|
||||
def _updateMaps(self):
|
||||
Logger.log("i", "Updating material lookup data ...")
|
||||
|
@ -263,38 +289,45 @@ class MaterialManager(QObject):
|
|||
#
|
||||
# Return a dict with all root material IDs (k) and ContainerNodes (v) that's suitable for the given setup.
|
||||
#
|
||||
def getAvailableMaterials(self, machine_definition: "DefinitionContainer", extruder_variant_name: Optional[str],
|
||||
diameter: float) -> Dict[str, MaterialNode]:
|
||||
def getAvailableMaterials(self, machine_definition: "DefinitionContainer", nozzle_name: Optional[str],
|
||||
buildplate_name: Optional[str], diameter: float) -> Dict[str, MaterialNode]:
|
||||
# round the diameter to get the approximate diameter
|
||||
rounded_diameter = str(round(diameter))
|
||||
if rounded_diameter not in self._diameter_machine_variant_material_map:
|
||||
if rounded_diameter not in self._diameter_machine_nozzle_buildplate_material_map:
|
||||
Logger.log("i", "Cannot find materials with diameter [%s] (rounded to [%s])", diameter, rounded_diameter)
|
||||
return dict()
|
||||
|
||||
machine_definition_id = machine_definition.getId()
|
||||
|
||||
# If there are variant materials, get the variant material
|
||||
machine_variant_material_map = self._diameter_machine_variant_material_map[rounded_diameter]
|
||||
machine_node = machine_variant_material_map.get(machine_definition_id)
|
||||
default_machine_node = machine_variant_material_map.get(self._default_machine_definition_id)
|
||||
variant_node = None
|
||||
if extruder_variant_name is not None and machine_node is not None:
|
||||
variant_node = machine_node.getChildNode(extruder_variant_name)
|
||||
# If there are nozzle-and-or-buildplate materials, get the nozzle-and-or-buildplate material
|
||||
machine_nozzle_buildplate_material_map = self._diameter_machine_nozzle_buildplate_material_map[rounded_diameter]
|
||||
machine_node = machine_nozzle_buildplate_material_map.get(machine_definition_id)
|
||||
default_machine_node = machine_nozzle_buildplate_material_map.get(self._default_machine_definition_id)
|
||||
nozzle_node = None
|
||||
buildplate_node = None
|
||||
if nozzle_name is not None and machine_node is not None:
|
||||
nozzle_node = machine_node.getChildNode(nozzle_name)
|
||||
# Get buildplate node if possible
|
||||
if nozzle_node is not None and buildplate_name is not None:
|
||||
buildplate_node = nozzle_node.getChildNode(buildplate_name)
|
||||
|
||||
nodes_to_check = [variant_node, machine_node, default_machine_node]
|
||||
nodes_to_check = [buildplate_node, nozzle_node, machine_node, default_machine_node]
|
||||
|
||||
# Fallback mechanism of finding materials:
|
||||
# 1. variant-specific material
|
||||
# 2. machine-specific material
|
||||
# 3. generic material (for fdmprinter)
|
||||
# 1. buildplate-specific material
|
||||
# 2. nozzle-specific material
|
||||
# 3. machine-specific material
|
||||
# 4. generic material (for fdmprinter)
|
||||
machine_exclude_materials = machine_definition.getMetaDataEntry("exclude_materials", [])
|
||||
|
||||
material_id_metadata_dict = dict() # type: Dict[str, MaterialNode]
|
||||
for node in nodes_to_check:
|
||||
if node is not None:
|
||||
for current_node in nodes_to_check:
|
||||
if current_node is None:
|
||||
continue
|
||||
|
||||
# Only exclude the materials that are explicitly specified in the "exclude_materials" field.
|
||||
# Do not exclude other materials that are of the same type.
|
||||
for material_id, node in node.material_map.items():
|
||||
for material_id, node in current_node.material_map.items():
|
||||
if material_id in machine_exclude_materials:
|
||||
Logger.log("d", "Exclude material [%s] for machine [%s]",
|
||||
material_id, machine_definition.getId())
|
||||
|
@ -310,13 +343,14 @@ class MaterialManager(QObject):
|
|||
#
|
||||
def getAvailableMaterialsForMachineExtruder(self, machine: "GlobalStack",
|
||||
extruder_stack: "ExtruderStack") -> Optional[dict]:
|
||||
variant_name = None
|
||||
buildplate_name = machine.getBuildplateName()
|
||||
nozzle_name = None
|
||||
if extruder_stack.variant.getId() != "empty_variant":
|
||||
variant_name = extruder_stack.variant.getName()
|
||||
nozzle_name = extruder_stack.variant.getName()
|
||||
diameter = extruder_stack.approximateMaterialDiameter
|
||||
|
||||
# Fetch the available materials (ContainerNode) for the current active machine and extruder setup.
|
||||
return self.getAvailableMaterials(machine.definition, variant_name, diameter)
|
||||
return self.getAvailableMaterials(machine.definition, nozzle_name, buildplate_name, diameter)
|
||||
|
||||
#
|
||||
# Gets MaterialNode for the given extruder and machine with the given material name.
|
||||
|
@ -324,32 +358,36 @@ class MaterialManager(QObject):
|
|||
# 1. the given machine doesn't have materials;
|
||||
# 2. cannot find any material InstanceContainers with the given settings.
|
||||
#
|
||||
def getMaterialNode(self, machine_definition_id: str, extruder_variant_name: Optional[str],
|
||||
diameter: float, root_material_id: str) -> Optional["InstanceContainer"]:
|
||||
def getMaterialNode(self, machine_definition_id: str, nozzle_name: Optional[str],
|
||||
buildplate_name: Optional[str], diameter: float, root_material_id: str) -> Optional["InstanceContainer"]:
|
||||
# round the diameter to get the approximate diameter
|
||||
rounded_diameter = str(round(diameter))
|
||||
if rounded_diameter not in self._diameter_machine_variant_material_map:
|
||||
if rounded_diameter not in self._diameter_machine_nozzle_buildplate_material_map:
|
||||
Logger.log("i", "Cannot find materials with diameter [%s] (rounded to [%s]) for root material id [%s]",
|
||||
diameter, rounded_diameter, root_material_id)
|
||||
return None
|
||||
|
||||
# If there are variant materials, get the variant material
|
||||
machine_variant_material_map = self._diameter_machine_variant_material_map[rounded_diameter]
|
||||
machine_node = machine_variant_material_map.get(machine_definition_id)
|
||||
variant_node = None
|
||||
# If there are nozzle materials, get the nozzle-specific material
|
||||
machine_nozzle_buildplate_material_map = self._diameter_machine_nozzle_buildplate_material_map[rounded_diameter]
|
||||
machine_node = machine_nozzle_buildplate_material_map.get(machine_definition_id)
|
||||
nozzle_node = None
|
||||
buildplate_node = None
|
||||
|
||||
# Fallback for "fdmprinter" if the machine-specific materials cannot be found
|
||||
if machine_node is None:
|
||||
machine_node = machine_variant_material_map.get(self._default_machine_definition_id)
|
||||
if machine_node is not None and extruder_variant_name is not None:
|
||||
variant_node = machine_node.getChildNode(extruder_variant_name)
|
||||
machine_node = machine_nozzle_buildplate_material_map.get(self._default_machine_definition_id)
|
||||
if machine_node is not None and nozzle_name is not None:
|
||||
nozzle_node = machine_node.getChildNode(nozzle_name)
|
||||
if nozzle_node is not None and buildplate_name is not None:
|
||||
buildplate_node = nozzle_node.getChildNode(buildplate_name)
|
||||
|
||||
# Fallback mechanism of finding materials:
|
||||
# 1. variant-specific material
|
||||
# 2. machine-specific material
|
||||
# 3. generic material (for fdmprinter)
|
||||
nodes_to_check = [variant_node, machine_node,
|
||||
machine_variant_material_map.get(self._default_machine_definition_id)]
|
||||
# 1. buildplate-specific material
|
||||
# 2. nozzle-specific material
|
||||
# 3. machine-specific material
|
||||
# 4. generic material (for fdmprinter)
|
||||
nodes_to_check = [buildplate_node, nozzle_node, machine_node,
|
||||
machine_nozzle_buildplate_material_map.get(self._default_machine_definition_id)]
|
||||
|
||||
material_node = None
|
||||
for node in nodes_to_check:
|
||||
|
@ -366,7 +404,8 @@ class MaterialManager(QObject):
|
|||
# 1. the given machine doesn't have materials;
|
||||
# 2. cannot find any material InstanceContainers with the given settings.
|
||||
#
|
||||
def getMaterialNodeByType(self, global_stack: "GlobalStack", position: str, extruder_variant_name: str, material_guid: str) -> Optional["MaterialNode"]:
|
||||
def getMaterialNodeByType(self, global_stack: "GlobalStack", position: str, nozzle_name: str,
|
||||
buildplate_name: Optional[str], material_guid: str) -> Optional["MaterialNode"]:
|
||||
node = None
|
||||
machine_definition = global_stack.definition
|
||||
extruder_definition = global_stack.extruders[position].definition
|
||||
|
@ -385,7 +424,7 @@ class MaterialManager(QObject):
|
|||
Logger.log("i", "Cannot find materials with guid [%s] ", material_guid)
|
||||
return None
|
||||
|
||||
node = self.getMaterialNode(machine_definition.getId(), extruder_variant_name,
|
||||
node = self.getMaterialNode(machine_definition.getId(), nozzle_name, buildplate_name,
|
||||
material_diameter, root_material_id)
|
||||
return node
|
||||
|
||||
|
@ -413,13 +452,17 @@ class MaterialManager(QObject):
|
|||
else:
|
||||
return None
|
||||
|
||||
## Get default material for given global stack, extruder position and extruder variant name
|
||||
## Get default material for given global stack, extruder position and extruder nozzle name
|
||||
# you can provide the extruder_definition and then the position is ignored (useful when building up global stack in CuraStackBuilder)
|
||||
def getDefaultMaterial(self, global_stack: "GlobalStack", position: str, extruder_variant_name: Optional[str], extruder_definition: Optional["DefinitionContainer"] = None) -> Optional["MaterialNode"]:
|
||||
def getDefaultMaterial(self, global_stack: "GlobalStack", position: str, nozzle_name: Optional[str],
|
||||
extruder_definition: Optional["DefinitionContainer"] = None) -> Optional["MaterialNode"]:
|
||||
node = None
|
||||
|
||||
buildplate_name = global_stack.getBuildplateName()
|
||||
machine_definition = global_stack.definition
|
||||
if extruder_definition is None:
|
||||
extruder_definition = global_stack.extruders[position].definition
|
||||
|
||||
if extruder_definition and parseBool(global_stack.getMetaDataEntry("has_materials", False)):
|
||||
# At this point the extruder_definition is not None
|
||||
material_diameter = extruder_definition.getProperty("material_diameter", "value")
|
||||
|
@ -428,7 +471,7 @@ class MaterialManager(QObject):
|
|||
approximate_material_diameter = str(round(material_diameter))
|
||||
root_material_id = machine_definition.getMetaDataEntry("preferred_material")
|
||||
root_material_id = self.getRootMaterialIDForDiameter(root_material_id, approximate_material_diameter)
|
||||
node = self.getMaterialNode(machine_definition.getId(), extruder_variant_name,
|
||||
node = self.getMaterialNode(machine_definition.getId(), nozzle_name, buildplate_name,
|
||||
material_diameter, root_material_id)
|
||||
return node
|
||||
|
||||
|
@ -515,8 +558,8 @@ class MaterialManager(QObject):
|
|||
if container_to_copy.getMetaDataEntry("definition") != "fdmprinter":
|
||||
new_id += "_" + container_to_copy.getMetaDataEntry("definition")
|
||||
if container_to_copy.getMetaDataEntry("variant_name"):
|
||||
variant_name = container_to_copy.getMetaDataEntry("variant_name")
|
||||
new_id += "_" + variant_name.replace(" ", "_")
|
||||
nozzle_name = container_to_copy.getMetaDataEntry("variant_name")
|
||||
new_id += "_" + nozzle_name.replace(" ", "_")
|
||||
|
||||
new_container = copy.deepcopy(container_to_copy)
|
||||
new_container.getMetaData()["id"] = new_id
|
||||
|
|
|
@ -8,7 +8,7 @@ from UM.Logger import Logger
|
|||
from UM.Qt.ListModel import ListModel
|
||||
from UM.Util import parseBool
|
||||
|
||||
from cura.Machines.VariantManager import VariantType
|
||||
from cura.Machines.VariantType import VariantType
|
||||
|
||||
|
||||
class BuildPlateModel(ListModel):
|
||||
|
|
|
@ -8,6 +8,8 @@ from UM.Logger import Logger
|
|||
from UM.Qt.ListModel import ListModel
|
||||
from UM.Util import parseBool
|
||||
|
||||
from cura.Machines.VariantType import VariantType
|
||||
|
||||
|
||||
class NozzleModel(ListModel):
|
||||
IdRole = Qt.UserRole + 1
|
||||
|
@ -43,7 +45,6 @@ class NozzleModel(ListModel):
|
|||
self.setItems([])
|
||||
return
|
||||
|
||||
from cura.Machines.VariantManager import VariantType
|
||||
variant_node_dict = self._variant_manager.getVariantNodes(global_stack, VariantType.NOZZLE)
|
||||
if not variant_node_dict:
|
||||
self.setItems([])
|
||||
|
|
|
@ -45,7 +45,7 @@ class QualityManager(QObject):
|
|||
self._empty_quality_container = self._application.empty_quality_container
|
||||
self._empty_quality_changes_container = self._application.empty_quality_changes_container
|
||||
|
||||
self._machine_variant_material_quality_type_to_quality_dict = {} # for quality lookup
|
||||
self._machine_nozzle_buildplate_material_quality_type_to_quality_dict = {} # for quality lookup
|
||||
self._machine_quality_type_to_quality_changes_dict = {} # for quality_changes lookup
|
||||
|
||||
self._default_machine_definition_id = "fdmprinter"
|
||||
|
@ -64,10 +64,10 @@ class QualityManager(QObject):
|
|||
|
||||
def initialize(self):
|
||||
# Initialize the lookup tree for quality profiles with following structure:
|
||||
# <machine> -> <variant> -> <material>
|
||||
# -> <material>
|
||||
# <machine> -> <nozzle> -> <buildplate> -> <material>
|
||||
# <machine> -> <material>
|
||||
|
||||
self._machine_variant_material_quality_type_to_quality_dict = {} # for quality lookup
|
||||
self._machine_nozzle_buildplate_material_quality_type_to_quality_dict = {} # for quality lookup
|
||||
self._machine_quality_type_to_quality_changes_dict = {} # for quality_changes lookup
|
||||
|
||||
quality_metadata_list = self._container_registry.findContainersMetadata(type = "quality")
|
||||
|
@ -79,53 +79,41 @@ class QualityManager(QObject):
|
|||
quality_type = metadata["quality_type"]
|
||||
|
||||
root_material_id = metadata.get("material")
|
||||
variant_name = metadata.get("variant")
|
||||
nozzle_name = metadata.get("variant")
|
||||
buildplate_name = metadata.get("buildplate")
|
||||
is_global_quality = metadata.get("global_quality", False)
|
||||
is_global_quality = is_global_quality or (root_material_id is None and variant_name is None)
|
||||
is_global_quality = is_global_quality or (root_material_id is None and nozzle_name is None and buildplate_name is None)
|
||||
|
||||
# Sanity check: material+variant and is_global_quality cannot be present at the same time
|
||||
if is_global_quality and (root_material_id or variant_name):
|
||||
if is_global_quality and (root_material_id or nozzle_name):
|
||||
ConfigurationErrorMessage.getInstance().addFaultyContainers(metadata["id"])
|
||||
continue
|
||||
|
||||
if definition_id not in self._machine_variant_material_quality_type_to_quality_dict:
|
||||
self._machine_variant_material_quality_type_to_quality_dict[definition_id] = QualityNode()
|
||||
machine_node = cast(QualityNode, self._machine_variant_material_quality_type_to_quality_dict[definition_id])
|
||||
if definition_id not in self._machine_nozzle_buildplate_material_quality_type_to_quality_dict:
|
||||
self._machine_nozzle_buildplate_material_quality_type_to_quality_dict[definition_id] = QualityNode()
|
||||
machine_node = cast(QualityNode, self._machine_nozzle_buildplate_material_quality_type_to_quality_dict[definition_id])
|
||||
|
||||
if is_global_quality:
|
||||
# For global qualities, save data in the machine node
|
||||
machine_node.addQualityMetadata(quality_type, metadata)
|
||||
continue
|
||||
|
||||
if variant_name is not None:
|
||||
# If variant_name is specified in the quality/quality_changes profile, check if material is specified,
|
||||
# too.
|
||||
if variant_name not in machine_node.children_map:
|
||||
machine_node.children_map[variant_name] = QualityNode()
|
||||
variant_node = cast(QualityNode, machine_node.children_map[variant_name])
|
||||
current_node = machine_node
|
||||
intermediate_node_info_list = [nozzle_name, buildplate_name, root_material_id]
|
||||
current_intermediate_node_info_idx = 0
|
||||
|
||||
if root_material_id is None:
|
||||
# If only variant_name is specified but material is not, add the quality/quality_changes metadata
|
||||
# into the current variant node.
|
||||
variant_node.addQualityMetadata(quality_type, metadata)
|
||||
else:
|
||||
# If only variant_name and material are both specified, go one level deeper: create a material node
|
||||
# under the current variant node, and then add the quality/quality_changes metadata into the
|
||||
# material node.
|
||||
if root_material_id not in variant_node.children_map:
|
||||
variant_node.children_map[root_material_id] = QualityNode()
|
||||
material_node = cast(QualityNode, variant_node.children_map[root_material_id])
|
||||
while current_intermediate_node_info_idx < len(intermediate_node_info_list):
|
||||
node_name = intermediate_node_info_list[current_intermediate_node_info_idx]
|
||||
if node_name is not None:
|
||||
# There is specific information, update the current node to go deeper so we can add this quality
|
||||
# at the most specific branch in the lookup tree.
|
||||
if node_name not in current_node.children_map:
|
||||
current_node.children_map[node_name] = QualityNode()
|
||||
current_node = cast(QualityNode, current_node.children_map[node_name])
|
||||
|
||||
material_node.addQualityMetadata(quality_type, metadata)
|
||||
current_intermediate_node_info_idx += 1
|
||||
|
||||
else:
|
||||
# If variant_name is not specified, check if material is specified.
|
||||
if root_material_id is not None:
|
||||
if root_material_id not in machine_node.children_map:
|
||||
machine_node.children_map[root_material_id] = QualityNode()
|
||||
material_node = cast(QualityNode, machine_node.children_map[root_material_id])
|
||||
|
||||
material_node.addQualityMetadata(quality_type, metadata)
|
||||
current_node.addQualityMetadata(quality_type, metadata)
|
||||
|
||||
# Initialize the lookup tree for quality_changes profiles with following structure:
|
||||
# <machine> -> <quality_type> -> <name>
|
||||
|
@ -217,8 +205,8 @@ class QualityManager(QObject):
|
|||
# To find the quality container for the GlobalStack, check in the following fall-back manner:
|
||||
# (1) the machine-specific node
|
||||
# (2) the generic node
|
||||
machine_node = self._machine_variant_material_quality_type_to_quality_dict.get(machine_definition_id)
|
||||
default_machine_node = self._machine_variant_material_quality_type_to_quality_dict.get(self._default_machine_definition_id)
|
||||
machine_node = self._machine_nozzle_buildplate_material_quality_type_to_quality_dict.get(machine_definition_id)
|
||||
default_machine_node = self._machine_nozzle_buildplate_material_quality_type_to_quality_dict.get(self._default_machine_definition_id)
|
||||
nodes_to_check = [machine_node, default_machine_node]
|
||||
|
||||
# Iterate over all quality_types in the machine node
|
||||
|
@ -238,16 +226,19 @@ class QualityManager(QObject):
|
|||
quality_group_dict[quality_type] = quality_group
|
||||
break
|
||||
|
||||
buildplate_name = machine.getBuildplateName()
|
||||
|
||||
# Iterate over all extruders to find quality containers for each extruder
|
||||
for position, extruder in machine.extruders.items():
|
||||
variant_name = None
|
||||
nozzle_name = None
|
||||
if extruder.variant.getId() != "empty_variant":
|
||||
variant_name = extruder.variant.getName()
|
||||
nozzle_name = extruder.variant.getName()
|
||||
|
||||
# This is a list of root material IDs to use for searching for suitable quality profiles.
|
||||
# The root material IDs in this list are in prioritized order.
|
||||
root_material_id_list = []
|
||||
has_material = False # flag indicating whether this extruder has a material assigned
|
||||
root_material_id = None
|
||||
if extruder.material.getId() != "empty_material":
|
||||
has_material = True
|
||||
root_material_id = extruder.material.getMetaDataEntry("base_file")
|
||||
|
@ -264,35 +255,40 @@ class QualityManager(QObject):
|
|||
# Here we construct a list of nodes we want to look for qualities with the highest priority first.
|
||||
# The use case is that, when we look for qualities for a machine, we first want to search in the following
|
||||
# order:
|
||||
# 1. machine-variant-and-material-specific qualities if exist
|
||||
# 2. machine-variant-specific qualities if exist
|
||||
# 3. machine-material-specific qualities if exist
|
||||
# 4. machine-specific qualities if exist
|
||||
# 5. generic qualities if exist
|
||||
# 1. machine-nozzle-buildplate-and-material-specific qualities if exist
|
||||
# 2. machine-nozzle-and-material-specific qualities if exist
|
||||
# 3. machine-nozzle-specific qualities if exist
|
||||
# 4. machine-material-specific qualities if exist
|
||||
# 5. machine-specific qualities if exist
|
||||
# 6. generic qualities if exist
|
||||
# Each points above can be represented as a node in the lookup tree, so here we simply put those nodes into
|
||||
# the list with priorities as the order. Later, we just need to loop over each node in this list and fetch
|
||||
# qualities from there.
|
||||
node_info_list_0 = [nozzle_name, buildplate_name, root_material_id]
|
||||
nodes_to_check = []
|
||||
|
||||
if variant_name:
|
||||
# In this case, we have both a specific variant and a specific material
|
||||
variant_node = machine_node.getChildNode(variant_name)
|
||||
if variant_node and has_material:
|
||||
for root_material_id in root_material_id_list:
|
||||
material_node = variant_node.getChildNode(root_material_id)
|
||||
if material_node:
|
||||
nodes_to_check.append(material_node)
|
||||
break
|
||||
nodes_to_check.append(variant_node)
|
||||
# This function tries to recursively find the deepest (the most specific) branch and add those nodes to
|
||||
# the search list in the order described above. So, by iterating over that search node list, we first look
|
||||
# in the more specific branches and then the less specific (generic) ones.
|
||||
def addNodesToCheck(node, nodes_to_check_list, node_info_list, node_info_idx):
|
||||
if node_info_idx < len(node_info_list):
|
||||
node_name = node_info_list[node_info_idx]
|
||||
if node_name is not None:
|
||||
current_node = node.getChildNode(node_name)
|
||||
if current_node is not None and has_material:
|
||||
addNodesToCheck(current_node, nodes_to_check_list, node_info_list, node_info_idx + 1)
|
||||
|
||||
# In this case, we only have a specific material but NOT a variant
|
||||
if has_material:
|
||||
for root_material_id in root_material_id_list:
|
||||
material_node = machine_node.getChildNode(root_material_id)
|
||||
for rmid in root_material_id_list:
|
||||
material_node = node.getChildNode(rmid)
|
||||
if material_node:
|
||||
nodes_to_check.append(material_node)
|
||||
nodes_to_check_list.append(material_node)
|
||||
break
|
||||
|
||||
nodes_to_check_list.append(node)
|
||||
|
||||
addNodesToCheck(machine_node, nodes_to_check, node_info_list_0, 0)
|
||||
|
||||
nodes_to_check += [machine_node, default_machine_node]
|
||||
for node in nodes_to_check:
|
||||
if node and node.quality_type_map:
|
||||
|
@ -309,8 +305,8 @@ class QualityManager(QObject):
|
|||
quality_group_dict[quality_type] = quality_group
|
||||
|
||||
quality_group = quality_group_dict[quality_type]
|
||||
if position not in quality_group.nodes_for_extruders:
|
||||
quality_group.nodes_for_extruders[position] = quality_node
|
||||
break
|
||||
|
||||
# Update availabilities for each quality group
|
||||
self._updateQualityGroupsAvailability(machine, quality_group_dict.values())
|
||||
|
@ -323,8 +319,8 @@ class QualityManager(QObject):
|
|||
# To find the quality container for the GlobalStack, check in the following fall-back manner:
|
||||
# (1) the machine-specific node
|
||||
# (2) the generic node
|
||||
machine_node = self._machine_variant_material_quality_type_to_quality_dict.get(machine_definition_id)
|
||||
default_machine_node = self._machine_variant_material_quality_type_to_quality_dict.get(
|
||||
machine_node = self._machine_nozzle_buildplate_material_quality_type_to_quality_dict.get(machine_definition_id)
|
||||
default_machine_node = self._machine_nozzle_buildplate_material_quality_type_to_quality_dict.get(
|
||||
self._default_machine_definition_id)
|
||||
nodes_to_check = [machine_node, default_machine_node]
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from enum import Enum
|
||||
from collections import OrderedDict
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
|
||||
|
@ -11,20 +10,13 @@ from UM.Settings.ContainerRegistry import ContainerRegistry
|
|||
from UM.Util import parseBool
|
||||
|
||||
from cura.Machines.ContainerNode import ContainerNode
|
||||
from cura.Machines.VariantType import VariantType, ALL_VARIANT_TYPES
|
||||
from cura.Settings.GlobalStack import GlobalStack
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||
|
||||
|
||||
class VariantType(Enum):
|
||||
BUILD_PLATE = "buildplate"
|
||||
NOZZLE = "nozzle"
|
||||
|
||||
|
||||
ALL_VARIANT_TYPES = (VariantType.BUILD_PLATE, VariantType.NOZZLE)
|
||||
|
||||
|
||||
#
|
||||
# VariantManager is THE place to look for a specific variant. It maintains two variant lookup tables with the following
|
||||
# structure:
|
||||
|
|
15
cura/Machines/VariantType.py
Normal file
15
cura/Machines/VariantType.py
Normal file
|
@ -0,0 +1,15 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class VariantType(Enum):
|
||||
BUILD_PLATE = "buildplate"
|
||||
NOZZLE = "nozzle"
|
||||
|
||||
|
||||
ALL_VARIANT_TYPES = (VariantType.BUILD_PLATE, VariantType.NOZZLE)
|
||||
|
||||
|
||||
__all__ = ["VariantType", "ALL_VARIANT_TYPES"]
|
|
@ -1,112 +1,149 @@
|
|||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from UM.Scene.Iterator import Iterator
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from functools import cmp_to_key
|
||||
from UM.Application import Application
|
||||
import sys
|
||||
|
||||
from shapely import affinity
|
||||
from shapely.geometry import Polygon
|
||||
|
||||
from UM.Scene.Iterator.Iterator import Iterator
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
|
||||
|
||||
# Iterator that determines the object print order when one-at a time mode is enabled.
|
||||
#
|
||||
# In one-at-a-time mode, only one extruder can be enabled to print. In order to maximize the number of objects we can
|
||||
# print, we need to print from the corner that's closest to the extruder that's being used. Here is an illustration:
|
||||
#
|
||||
# +--------------------------------+
|
||||
# | |
|
||||
# | |
|
||||
# | | - Rectangle represents the complete print head including fans, etc.
|
||||
# | X X | y - X's are the nozzles
|
||||
# | (1) (2) | ^
|
||||
# | | |
|
||||
# +--------------------------------+ +--> x
|
||||
#
|
||||
# In this case, the nozzles are symmetric, nozzle (1) is closer to the bottom left corner while (2) is closer to the
|
||||
# bottom right. If we use nozzle (1) to print, then we better off printing from the bottom left corner so the print
|
||||
# head will not collide into an object on its top-right side, which is a very large unused area. Following the same
|
||||
# logic, if we are printing with nozzle (2), then it's better to print from the bottom-right side.
|
||||
#
|
||||
# This iterator determines the print order following the rules above.
|
||||
#
|
||||
class OneAtATimeIterator(Iterator):
|
||||
|
||||
## Iterator that returns a list of nodes in the order that they need to be printed
|
||||
# If there is no solution an empty list is returned.
|
||||
# Take note that the list of nodes can have children (that may or may not contain mesh data)
|
||||
class OneAtATimeIterator(Iterator.Iterator):
|
||||
def __init__(self, scene_node):
|
||||
super().__init__(scene_node) # Call super to make multiple inheritence work.
|
||||
self._hit_map = [[]]
|
||||
from cura.CuraApplication import CuraApplication
|
||||
self._global_stack = CuraApplication.getInstance().getGlobalContainerStack()
|
||||
self._original_node_list = []
|
||||
|
||||
super().__init__(scene_node) # Call super to make multiple inheritance work.
|
||||
|
||||
def getMachineNearestCornerToExtruder(self, global_stack):
|
||||
head_and_fans_coordinates = global_stack.getHeadAndFansCoordinates()
|
||||
|
||||
used_extruder = None
|
||||
for extruder in global_stack.extruders.values():
|
||||
if extruder.isEnabled:
|
||||
used_extruder = extruder
|
||||
break
|
||||
|
||||
extruder_offsets = [used_extruder.getProperty("machine_nozzle_offset_x", "value"),
|
||||
used_extruder.getProperty("machine_nozzle_offset_y", "value")]
|
||||
|
||||
# find the corner that's closest to the origin
|
||||
min_distance2 = sys.maxsize
|
||||
min_coord = None
|
||||
for coord in head_and_fans_coordinates:
|
||||
x = coord[0] - extruder_offsets[0]
|
||||
y = coord[1] - extruder_offsets[1]
|
||||
|
||||
distance2 = x**2 + y**2
|
||||
if distance2 <= min_distance2:
|
||||
min_distance2 = distance2
|
||||
min_coord = coord
|
||||
|
||||
return min_coord
|
||||
|
||||
def _checkForCollisions(self) -> bool:
|
||||
all_nodes = []
|
||||
for node in self._scene_node.getChildren():
|
||||
if not issubclass(type(node), SceneNode):
|
||||
continue
|
||||
convex_hull = node.callDecoration("getConvexHullHead")
|
||||
if not convex_hull:
|
||||
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],
|
||||
[bounding_box.right, bounding_box.back],
|
||||
[bounding_box.right, bounding_box.front]])
|
||||
|
||||
all_nodes.append({"node": node,
|
||||
"bounding_box": bounding_box_polygon,
|
||||
"convex_hull": convex_hull})
|
||||
|
||||
has_collisions = False
|
||||
for i, node_dict in enumerate(all_nodes):
|
||||
for j, other_node_dict in enumerate(all_nodes):
|
||||
if i == j:
|
||||
continue
|
||||
if node_dict["bounding_box"].intersectsPolygon(other_node_dict["convex_hull"]):
|
||||
has_collisions = True
|
||||
break
|
||||
|
||||
if has_collisions:
|
||||
break
|
||||
|
||||
return has_collisions
|
||||
|
||||
def _fillStack(self):
|
||||
min_coord = self.getMachineNearestCornerToExtruder(self._global_stack)
|
||||
transform_x = -int(round(min_coord[0] / abs(min_coord[0])))
|
||||
transform_y = -int(round(min_coord[1] / abs(min_coord[1])))
|
||||
|
||||
machine_size = [self._global_stack.getProperty("machine_width", "value"),
|
||||
self._global_stack.getProperty("machine_depth", "value")]
|
||||
|
||||
def flip_x(polygon):
|
||||
tm2 = [-1, 0, 0, 1, 0, 0]
|
||||
return affinity.affine_transform(affinity.translate(polygon, xoff = -machine_size[0]), tm2)
|
||||
|
||||
def flip_y(polygon):
|
||||
tm2 = [1, 0, 0, -1, 0, 0]
|
||||
return affinity.affine_transform(affinity.translate(polygon, yoff = -machine_size[1]), tm2)
|
||||
|
||||
if self._checkForCollisions():
|
||||
self._node_stack = []
|
||||
return
|
||||
|
||||
node_list = []
|
||||
for node in self._scene_node.getChildren():
|
||||
if not issubclass(type(node), SceneNode):
|
||||
continue
|
||||
|
||||
if node.callDecoration("getConvexHull"):
|
||||
node_list.append(node)
|
||||
convex_hull = node.callDecoration("getConvexHull")
|
||||
if convex_hull:
|
||||
xmin = min(x for x, _ in convex_hull._points)
|
||||
xmax = max(x for x, _ in convex_hull._points)
|
||||
ymin = min(y for _, y in convex_hull._points)
|
||||
ymax = max(y for _, y in convex_hull._points)
|
||||
|
||||
convex_hull_polygon = Polygon.from_bounds(xmin, ymin, xmax, ymax)
|
||||
if transform_x < 0:
|
||||
convex_hull_polygon = flip_x(convex_hull_polygon)
|
||||
if transform_y < 0:
|
||||
convex_hull_polygon = flip_y(convex_hull_polygon)
|
||||
|
||||
if len(node_list) < 2:
|
||||
self._node_stack = node_list[:]
|
||||
return
|
||||
node_list.append({"node": node,
|
||||
"min_coord": [convex_hull_polygon.bounds[0], convex_hull_polygon.bounds[1]],
|
||||
})
|
||||
|
||||
# Copy the list
|
||||
self._original_node_list = node_list[:]
|
||||
|
||||
## Initialise the hit map (pre-compute all hits between all objects)
|
||||
self._hit_map = [[self._checkHit(i,j) for i in node_list] for j in node_list]
|
||||
|
||||
# Check if we have to files that block eachother. If this is the case, there is no solution!
|
||||
for a in range(0,len(node_list)):
|
||||
for b in range(0,len(node_list)):
|
||||
if a != b and self._hit_map[a][b] and self._hit_map[b][a]:
|
||||
return
|
||||
|
||||
# Sort the original list so that items that block the most other objects are at the beginning.
|
||||
# This does not decrease the worst case running time, but should improve it in most cases.
|
||||
sorted(node_list, key = cmp_to_key(self._calculateScore))
|
||||
|
||||
todo_node_list = [_ObjectOrder([], node_list)]
|
||||
while len(todo_node_list) > 0:
|
||||
current = todo_node_list.pop()
|
||||
for node in current.todo:
|
||||
# Check if the object can be placed with what we have and still allows for a solution in the future
|
||||
if not self._checkHitMultiple(node, current.order) and not self._checkBlockMultiple(node, current.todo):
|
||||
# We found a possible result. Create new todo & order list.
|
||||
new_todo_list = current.todo[:]
|
||||
new_todo_list.remove(node)
|
||||
new_order = current.order[:] + [node]
|
||||
if len(new_todo_list) == 0:
|
||||
# We have no more nodes to check, so quit looking.
|
||||
todo_node_list = None
|
||||
self._node_stack = new_order
|
||||
|
||||
return
|
||||
todo_node_list.append(_ObjectOrder(new_order, new_todo_list))
|
||||
self._node_stack = [] #No result found!
|
||||
|
||||
|
||||
# Check if first object can be printed before the provided list (using the hit map)
|
||||
def _checkHitMultiple(self, node, other_nodes):
|
||||
node_index = self._original_node_list.index(node)
|
||||
for other_node in other_nodes:
|
||||
other_node_index = self._original_node_list.index(other_node)
|
||||
if self._hit_map[node_index][other_node_index]:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _checkBlockMultiple(self, node, other_nodes):
|
||||
node_index = self._original_node_list.index(node)
|
||||
for other_node in other_nodes:
|
||||
other_node_index = self._original_node_list.index(other_node)
|
||||
if self._hit_map[other_node_index][node_index] and node_index != other_node_index:
|
||||
return True
|
||||
return False
|
||||
|
||||
## Calculate score simply sums the number of other objects it 'blocks'
|
||||
def _calculateScore(self, a, b):
|
||||
score_a = sum(self._hit_map[self._original_node_list.index(a)])
|
||||
score_b = sum(self._hit_map[self._original_node_list.index(b)])
|
||||
return score_a - score_b
|
||||
|
||||
# Checks if A can be printed before B
|
||||
def _checkHit(self, a, b):
|
||||
if a == b:
|
||||
return False
|
||||
|
||||
overlap = a.callDecoration("getConvexHullBoundary").intersectsPolygon(b.callDecoration("getConvexHullHeadFull"))
|
||||
if overlap:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
## Internal object used to keep track of a possible order in which to print objects.
|
||||
class _ObjectOrder():
|
||||
def __init__(self, order, todo):
|
||||
"""
|
||||
:param order: List of indexes in which to print objects, ordered by printing order.
|
||||
:param todo: List of indexes which are not yet inserted into the order list.
|
||||
"""
|
||||
self.order = order
|
||||
self.todo = todo
|
||||
node_list = sorted(node_list, key = lambda d: d["min_coord"])
|
||||
|
||||
self._node_stack = [d["node"] for d in node_list]
|
||||
|
|
|
@ -120,7 +120,7 @@ class PrinterOutputModel(QObject):
|
|||
|
||||
@pyqtProperty(QVariant, notify = headPositionChanged)
|
||||
def headPosition(self):
|
||||
return {"x": self._head_position.x, "y": self._head_position.y, "z": self.head_position_z}
|
||||
return {"x": self._head_position.x, "y": self._head_position.y, "z": self.head_position.z}
|
||||
|
||||
def updateHeadPosition(self, x, y, z):
|
||||
if self._head_position.x != x or self._head_position.y != y or self._head_position.z != z:
|
||||
|
|
|
@ -229,7 +229,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||
return offset_hull
|
||||
|
||||
def _getHeadAndFans(self):
|
||||
return Polygon(numpy.array(self._global_stack.getProperty("machine_head_with_fans_polygon", "value"), numpy.float32))
|
||||
return Polygon(numpy.array(self._global_stack.getHeadAndFansCoordinates(), numpy.float32))
|
||||
|
||||
def _compute2DConvexHeadFull(self):
|
||||
return self._compute2DConvexHull().getMinkowskiHull(self._getHeadAndFans())
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -8,7 +8,7 @@ from UM.Logger import Logger
|
|||
from UM.Settings.Interfaces import DefinitionContainerInterface
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
|
||||
from cura.Machines.VariantManager import VariantType
|
||||
from cura.Machines.VariantType import VariantType
|
||||
from .GlobalStack import GlobalStack
|
||||
from .ExtruderStack import ExtruderStack
|
||||
|
||||
|
@ -108,6 +108,17 @@ class CuraStackBuilder:
|
|||
|
||||
preferred_quality_type = machine_definition.getMetaDataEntry("preferred_quality_type")
|
||||
quality_group_dict = quality_manager.getQualityGroups(new_global_stack)
|
||||
if not quality_group_dict:
|
||||
# There is no available quality group, set all quality containers to empty.
|
||||
new_global_stack.quality = application.empty_quality_container
|
||||
for extruder_stack in new_global_stack.extruders.values():
|
||||
extruder_stack.quality = application.empty_quality_container
|
||||
else:
|
||||
# Set the quality containers to the preferred quality type if available, otherwise use the first quality
|
||||
# type that's available.
|
||||
if preferred_quality_type not in quality_group_dict:
|
||||
Logger.log("w", "The preferred quality {quality_type} doesn't exist for this set-up. Choosing a random one.".format(quality_type = preferred_quality_type))
|
||||
preferred_quality_type = next(iter(quality_group_dict))
|
||||
quality_group = quality_group_dict.get(preferred_quality_type)
|
||||
|
||||
new_global_stack.quality = quality_group.node_for_global.getContainer()
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -55,6 +55,16 @@ class GlobalStack(CuraContainerStack):
|
|||
return "machine_stack"
|
||||
return configuration_type
|
||||
|
||||
def getBuildplateName(self) -> Optional[str]:
|
||||
name = None
|
||||
if self.variant.getId() != "empty_variant":
|
||||
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.
|
||||
|
@ -96,6 +106,9 @@ class GlobalStack(CuraContainerStack):
|
|||
|
||||
# Handle the "resolve" property.
|
||||
#TODO: Why the hell does this involve threading?
|
||||
# Answer: Because if multiple threads start resolving properties that have the same underlying properties that's
|
||||
# related, without taking a note of which thread a resolve paths belongs to, they can bump into each other and
|
||||
# generate unexpected behaviours.
|
||||
if self._shouldResolve(key, property_name, context):
|
||||
current_thread = threading.current_thread()
|
||||
self._resolving_settings[current_thread.name].add(key)
|
||||
|
@ -172,6 +185,9 @@ class GlobalStack(CuraContainerStack):
|
|||
return False
|
||||
return True
|
||||
|
||||
def getHeadAndFansCoordinates(self):
|
||||
return self.getProperty("machine_head_with_fans_polygon", "value")
|
||||
|
||||
|
||||
## private:
|
||||
global_stack_mime = MimeType(
|
||||
|
|
|
@ -21,9 +21,6 @@ from UM.Settings.SettingFunction import SettingFunction
|
|||
from UM.Signal import postponeSignals, CompressTechnique
|
||||
|
||||
import cura.CuraApplication
|
||||
from cura.Machines.ContainerNode import ContainerNode #For typing.
|
||||
from cura.Machines.QualityChangesGroup import QualityChangesGroup #For typing.
|
||||
from cura.Machines.QualityGroup import QualityGroup #For typing.
|
||||
from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch
|
||||
from cura.PrinterOutputDevice import PrinterOutputDevice
|
||||
from cura.PrinterOutput.ConfigurationModel import ConfigurationModel
|
||||
|
@ -44,12 +41,16 @@ if TYPE_CHECKING:
|
|||
from cura.Machines.MaterialManager import MaterialManager
|
||||
from cura.Machines.QualityManager import QualityManager
|
||||
from cura.Machines.VariantManager import VariantManager
|
||||
from cura.Machines.ContainerNode import ContainerNode
|
||||
from cura.Machines.QualityChangesGroup import QualityChangesGroup
|
||||
from cura.Machines.QualityGroup import QualityGroup
|
||||
|
||||
|
||||
class MachineManager(QObject):
|
||||
def __init__(self, parent: QObject = None) -> None:
|
||||
super().__init__(parent)
|
||||
|
||||
self._active_container_stack = None # type: Optional[ExtruderManager]
|
||||
self._active_container_stack = None # type: Optional[ExtruderStack]
|
||||
self._global_container_stack = None # type: Optional[GlobalStack]
|
||||
|
||||
self._current_root_material_id = {} # type: Dict[str, str]
|
||||
|
@ -1087,7 +1088,7 @@ class MachineManager(QObject):
|
|||
self.activeQualityGroupChanged.emit()
|
||||
self.activeQualityChangesGroupChanged.emit()
|
||||
|
||||
def _setQualityGroup(self, quality_group: Optional[QualityGroup], empty_quality_changes: bool = True) -> None:
|
||||
def _setQualityGroup(self, quality_group: Optional["QualityGroup"], empty_quality_changes: bool = True) -> None:
|
||||
if self._global_container_stack is None:
|
||||
return
|
||||
if quality_group is None:
|
||||
|
@ -1118,7 +1119,7 @@ class MachineManager(QObject):
|
|||
self.activeQualityGroupChanged.emit()
|
||||
self.activeQualityChangesGroupChanged.emit()
|
||||
|
||||
def _fixQualityChangesGroupToNotSupported(self, quality_changes_group: QualityChangesGroup) -> None:
|
||||
def _fixQualityChangesGroupToNotSupported(self, quality_changes_group: "QualityChangesGroup") -> None:
|
||||
nodes = [quality_changes_group.node_for_global] + list(quality_changes_group.nodes_for_extruders.values())
|
||||
containers = [n.getContainer() for n in nodes if n is not None]
|
||||
for container in containers:
|
||||
|
@ -1126,7 +1127,7 @@ class MachineManager(QObject):
|
|||
container.setMetaDataEntry("quality_type", "not_supported")
|
||||
quality_changes_group.quality_type = "not_supported"
|
||||
|
||||
def _setQualityChangesGroup(self, quality_changes_group: QualityChangesGroup) -> None:
|
||||
def _setQualityChangesGroup(self, quality_changes_group: "QualityChangesGroup") -> None:
|
||||
if self._global_container_stack is None:
|
||||
return #Can't change that.
|
||||
quality_type = quality_changes_group.quality_type
|
||||
|
@ -1170,20 +1171,20 @@ class MachineManager(QObject):
|
|||
self.activeQualityGroupChanged.emit()
|
||||
self.activeQualityChangesGroupChanged.emit()
|
||||
|
||||
def _setVariantNode(self, position: str, container_node: ContainerNode) -> None:
|
||||
def _setVariantNode(self, position: str, container_node: "ContainerNode") -> None:
|
||||
if container_node.getContainer() is None or self._global_container_stack is None:
|
||||
return
|
||||
self._global_container_stack.extruders[position].variant = container_node.getContainer()
|
||||
self.activeVariantChanged.emit()
|
||||
|
||||
def _setGlobalVariant(self, container_node: ContainerNode) -> None:
|
||||
def _setGlobalVariant(self, container_node: "ContainerNode") -> None:
|
||||
if self._global_container_stack is None:
|
||||
return
|
||||
self._global_container_stack.variant = container_node.getContainer()
|
||||
if not self._global_container_stack.variant:
|
||||
self._global_container_stack.variant = self._application.empty_variant_container
|
||||
|
||||
def _setMaterial(self, position: str, container_node: ContainerNode = None) -> None:
|
||||
def _setMaterial(self, position: str, container_node: Optional["ContainerNode"] = None) -> None:
|
||||
if self._global_container_stack is None:
|
||||
return
|
||||
if container_node and container_node.getContainer():
|
||||
|
@ -1256,13 +1257,17 @@ class MachineManager(QObject):
|
|||
else:
|
||||
position_list = [position]
|
||||
|
||||
buildplate_name = None
|
||||
if self._global_container_stack.variant.getId() != "empty_variant":
|
||||
buildplate_name = self._global_container_stack.variant.getName()
|
||||
|
||||
for position_item in position_list:
|
||||
extruder = self._global_container_stack.extruders[position_item]
|
||||
|
||||
current_material_base_name = extruder.material.getMetaDataEntry("base_file")
|
||||
current_variant_name = None
|
||||
current_nozzle_name = None
|
||||
if extruder.variant.getId() != self._empty_variant_container.getId():
|
||||
current_variant_name = extruder.variant.getMetaDataEntry("name")
|
||||
current_nozzle_name = extruder.variant.getMetaDataEntry("name")
|
||||
|
||||
from UM.Settings.Interfaces import PropertyEvaluationContext
|
||||
from cura.Settings.CuraContainerStack import _ContainerIndexes
|
||||
|
@ -1271,7 +1276,8 @@ class MachineManager(QObject):
|
|||
material_diameter = extruder.getProperty("material_diameter", "value", context)
|
||||
candidate_materials = self._material_manager.getAvailableMaterials(
|
||||
self._global_container_stack.definition,
|
||||
current_variant_name,
|
||||
current_nozzle_name,
|
||||
buildplate_name,
|
||||
material_diameter)
|
||||
|
||||
if not candidate_materials:
|
||||
|
@ -1284,7 +1290,7 @@ class MachineManager(QObject):
|
|||
continue
|
||||
|
||||
# The current material is not available, find the preferred one
|
||||
material_node = self._material_manager.getDefaultMaterial(self._global_container_stack, position_item, current_variant_name)
|
||||
material_node = self._material_manager.getDefaultMaterial(self._global_container_stack, position_item, current_nozzle_name)
|
||||
if material_node is not None:
|
||||
self._setMaterial(position_item, material_node)
|
||||
|
||||
|
@ -1326,7 +1332,12 @@ class MachineManager(QObject):
|
|||
for extruder_configuration in configuration.extruderConfigurations:
|
||||
position = str(extruder_configuration.position)
|
||||
variant_container_node = self._variant_manager.getVariantNode(self._global_container_stack.definition.getId(), extruder_configuration.hotendID)
|
||||
material_container_node = self._material_manager.getMaterialNodeByType(self._global_container_stack, position, extruder_configuration.hotendID, extruder_configuration.material.guid)
|
||||
material_container_node = self._material_manager.getMaterialNodeByType(self._global_container_stack,
|
||||
position,
|
||||
extruder_configuration.hotendID,
|
||||
configuration.buildplateConfiguration,
|
||||
extruder_configuration.material.guid)
|
||||
|
||||
if variant_container_node:
|
||||
self._setVariantNode(position, variant_container_node)
|
||||
else:
|
||||
|
@ -1378,7 +1389,7 @@ class MachineManager(QObject):
|
|||
return bool(containers)
|
||||
|
||||
@pyqtSlot("QVariant")
|
||||
def setGlobalVariant(self, container_node: ContainerNode) -> None:
|
||||
def setGlobalVariant(self, container_node: "ContainerNode") -> None:
|
||||
self.blurSettings.emit()
|
||||
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
||||
self._setGlobalVariant(container_node)
|
||||
|
@ -1389,12 +1400,17 @@ class MachineManager(QObject):
|
|||
def setMaterialById(self, position: str, root_material_id: str) -> None:
|
||||
if self._global_container_stack is None:
|
||||
return
|
||||
buildplate_name = None
|
||||
if self._global_container_stack.variant.getId() != "empty_variant":
|
||||
buildplate_name = self._global_container_stack.variant.getName()
|
||||
|
||||
machine_definition_id = self._global_container_stack.definition.id
|
||||
position = str(position)
|
||||
extruder_stack = self._global_container_stack.extruders[position]
|
||||
variant_name = extruder_stack.variant.getName()
|
||||
nozzle_name = extruder_stack.variant.getName()
|
||||
material_diameter = extruder_stack.approximateMaterialDiameter
|
||||
material_node = self._material_manager.getMaterialNode(machine_definition_id, variant_name, material_diameter, root_material_id)
|
||||
material_node = self._material_manager.getMaterialNode(machine_definition_id, nozzle_name, buildplate_name,
|
||||
material_diameter, root_material_id)
|
||||
self.setMaterial(position, material_node)
|
||||
|
||||
## global_stack: if you want to provide your own global_stack instead of the current active one
|
||||
|
@ -1423,7 +1439,7 @@ class MachineManager(QObject):
|
|||
self.setVariant(position, variant_node)
|
||||
|
||||
@pyqtSlot(str, "QVariant")
|
||||
def setVariant(self, position: str, container_node: ContainerNode) -> None:
|
||||
def setVariant(self, position: str, container_node: "ContainerNode") -> None:
|
||||
position = str(position)
|
||||
self.blurSettings.emit()
|
||||
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
||||
|
@ -1447,7 +1463,7 @@ class MachineManager(QObject):
|
|||
## Optionally provide global_stack if you want to use your own
|
||||
# The active global_stack is treated differently.
|
||||
@pyqtSlot(QObject)
|
||||
def setQualityGroup(self, quality_group: QualityGroup, no_dialog: bool = False, global_stack: Optional["GlobalStack"] = None) -> None:
|
||||
def setQualityGroup(self, quality_group: "QualityGroup", no_dialog: bool = False, global_stack: Optional["GlobalStack"] = None) -> None:
|
||||
if global_stack is not None and global_stack != self._global_container_stack:
|
||||
if quality_group is None:
|
||||
Logger.log("e", "Could not set quality group because quality group is None")
|
||||
|
@ -1455,9 +1471,14 @@ class MachineManager(QObject):
|
|||
if quality_group.node_for_global is None:
|
||||
Logger.log("e", "Could not set quality group [%s] because it has no node_for_global", str(quality_group))
|
||||
return
|
||||
# This is not changing the quality for the active machine !!!!!!!!
|
||||
global_stack.quality = quality_group.node_for_global.getContainer()
|
||||
for extruder_nr, extruder_stack in global_stack.extruders.items():
|
||||
extruder_stack.quality = quality_group.nodes_for_extruders[extruder_nr].getContainer()
|
||||
quality_container = self._empty_quality_container
|
||||
if extruder_nr in quality_group.nodes_for_extruders:
|
||||
container = quality_group.nodes_for_extruders[extruder_nr].getContainer()
|
||||
quality_container = container if container is not None else quality_container
|
||||
extruder_stack.quality = quality_container
|
||||
return
|
||||
|
||||
self.blurSettings.emit()
|
||||
|
@ -1469,11 +1490,11 @@ class MachineManager(QObject):
|
|||
self._application.discardOrKeepProfileChanges()
|
||||
|
||||
@pyqtProperty(QObject, fset = setQualityGroup, notify = activeQualityGroupChanged)
|
||||
def activeQualityGroup(self) -> Optional[QualityGroup]:
|
||||
def activeQualityGroup(self) -> Optional["QualityGroup"]:
|
||||
return self._current_quality_group
|
||||
|
||||
@pyqtSlot(QObject)
|
||||
def setQualityChangesGroup(self, quality_changes_group: QualityChangesGroup, no_dialog: bool = False) -> None:
|
||||
def setQualityChangesGroup(self, quality_changes_group: "QualityChangesGroup", no_dialog: bool = False) -> None:
|
||||
self.blurSettings.emit()
|
||||
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
||||
self._setQualityChangesGroup(quality_changes_group)
|
||||
|
@ -1492,7 +1513,7 @@ class MachineManager(QObject):
|
|||
stack.userChanges.clear()
|
||||
|
||||
@pyqtProperty(QObject, fset = setQualityChangesGroup, notify = activeQualityChangesGroupChanged)
|
||||
def activeQualityChangesGroup(self) -> Optional[QualityChangesGroup]:
|
||||
def activeQualityChangesGroup(self) -> Optional["QualityChangesGroup"]:
|
||||
return self._current_quality_changes_group
|
||||
|
||||
@pyqtProperty(str, notify = activeQualityGroupChanged)
|
||||
|
|
41
cura/Settings/SidebarCustomMenuItemsModel.py
Normal file
41
cura/Settings/SidebarCustomMenuItemsModel.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from typing import Any
|
||||
|
||||
from UM.Qt.ListModel import ListModel
|
||||
from PyQt5.QtCore import pyqtSlot, Qt
|
||||
|
||||
|
||||
class SidebarCustomMenuItemsModel(ListModel):
|
||||
name_role = Qt.UserRole + 1
|
||||
actions_role = Qt.UserRole + 2
|
||||
menu_item_role = Qt.UserRole + 3
|
||||
menu_item_icon_name_role = Qt.UserRole + 5
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.addRoleName(self.name_role, "name")
|
||||
self.addRoleName(self.actions_role, "actions")
|
||||
self.addRoleName(self.menu_item_role, "menu_item")
|
||||
self.addRoleName(self.menu_item_icon_name_role, "iconName")
|
||||
self._updateExtensionList()
|
||||
|
||||
def _updateExtensionList(self)-> None:
|
||||
from cura.CuraApplication import CuraApplication
|
||||
for menu_item in CuraApplication.getInstance().getSidebarCustomMenuItems():
|
||||
|
||||
self.appendItem({
|
||||
"name": menu_item["name"],
|
||||
"icon_name": menu_item["icon_name"],
|
||||
"actions": menu_item["actions"],
|
||||
"menu_item": menu_item["menu_item"]
|
||||
})
|
||||
|
||||
@pyqtSlot(str, "QVariantList", "QVariantMap")
|
||||
def callMenuItemMethod(self, menu_item_name: str, menu_item_actions: list, kwargs: Any) -> None:
|
||||
for item in self._items:
|
||||
if menu_item_name == item["name"]:
|
||||
for method in menu_item_actions:
|
||||
getattr(item["menu_item"], method)(kwargs)
|
||||
break
|
56
cura/Settings/cura_empty_instance_containers.py
Normal file
56
cura/Settings/cura_empty_instance_containers.py
Normal file
|
@ -0,0 +1,56 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import copy
|
||||
|
||||
from UM.Settings.constant_instance_containers import EMPTY_CONTAINER_ID, empty_container
|
||||
|
||||
|
||||
# Empty definition changes
|
||||
EMPTY_DEFINITION_CHANGES_CONTAINER_ID = "empty_definition_changes"
|
||||
empty_definition_changes_container = copy.deepcopy(empty_container)
|
||||
empty_definition_changes_container.setMetaDataEntry("id", EMPTY_DEFINITION_CHANGES_CONTAINER_ID)
|
||||
empty_definition_changes_container.setMetaDataEntry("type", "definition_changes")
|
||||
|
||||
# Empty variant
|
||||
EMPTY_VARIANT_CONTAINER_ID = "empty_variant"
|
||||
empty_variant_container = copy.deepcopy(empty_container)
|
||||
empty_variant_container.setMetaDataEntry("id", EMPTY_VARIANT_CONTAINER_ID)
|
||||
empty_variant_container.setMetaDataEntry("type", "variant")
|
||||
|
||||
# Empty material
|
||||
EMPTY_MATERIAL_CONTAINER_ID = "empty_material"
|
||||
empty_material_container = copy.deepcopy(empty_container)
|
||||
empty_material_container.setMetaDataEntry("id", EMPTY_MATERIAL_CONTAINER_ID)
|
||||
empty_material_container.setMetaDataEntry("type", "material")
|
||||
|
||||
# Empty quality
|
||||
EMPTY_QUALITY_CONTAINER_ID = "empty_quality"
|
||||
empty_quality_container = copy.deepcopy(empty_container)
|
||||
empty_quality_container.setMetaDataEntry("id", EMPTY_QUALITY_CONTAINER_ID)
|
||||
empty_quality_container.setName("Not Supported")
|
||||
empty_quality_container.setMetaDataEntry("quality_type", "not_supported")
|
||||
empty_quality_container.setMetaDataEntry("type", "quality")
|
||||
empty_quality_container.setMetaDataEntry("supported", False)
|
||||
|
||||
# Empty quality changes
|
||||
EMPTY_QUALITY_CHANGES_CONTAINER_ID = "empty_quality_changes"
|
||||
empty_quality_changes_container = copy.deepcopy(empty_container)
|
||||
empty_quality_changes_container.setMetaDataEntry("id", EMPTY_QUALITY_CHANGES_CONTAINER_ID)
|
||||
empty_quality_changes_container.setMetaDataEntry("type", "quality_changes")
|
||||
empty_quality_changes_container.setMetaDataEntry("quality_type", "not_supported")
|
||||
|
||||
|
||||
__all__ = ["EMPTY_CONTAINER_ID",
|
||||
"empty_container", # For convenience
|
||||
"EMPTY_DEFINITION_CHANGES_CONTAINER_ID",
|
||||
"empty_definition_changes_container",
|
||||
"EMPTY_VARIANT_CONTAINER_ID",
|
||||
"empty_variant_container",
|
||||
"EMPTY_MATERIAL_CONTAINER_ID",
|
||||
"empty_material_container",
|
||||
"EMPTY_QUALITY_CHANGES_CONTAINER_ID",
|
||||
"empty_quality_changes_container",
|
||||
"EMPTY_QUALITY_CONTAINER_ID",
|
||||
"empty_quality_container"
|
||||
]
|
|
@ -24,6 +24,7 @@ from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType
|
|||
from UM.Job import Job
|
||||
from UM.Preferences import Preferences
|
||||
|
||||
from cura.Machines.VariantType import VariantType
|
||||
from cura.Settings.CuraStackBuilder import CuraStackBuilder
|
||||
from cura.Settings.ExtruderStack import ExtruderStack
|
||||
from cura.Settings.GlobalStack import GlobalStack
|
||||
|
@ -629,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)]
|
||||
|
@ -889,7 +895,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
parser = self._machine_info.variant_info.parser
|
||||
variant_name = parser["general"]["name"]
|
||||
|
||||
from cura.Machines.VariantManager import VariantType
|
||||
variant_type = VariantType.BUILD_PLATE
|
||||
|
||||
node = variant_manager.getVariantNode(global_stack.definition.getId(), variant_name, variant_type)
|
||||
|
@ -905,7 +910,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
parser = extruder_info.variant_info.parser
|
||||
|
||||
variant_name = parser["general"]["name"]
|
||||
from cura.Machines.VariantManager import VariantType
|
||||
variant_type = VariantType.NOZZLE
|
||||
|
||||
node = variant_manager.getVariantNode(global_stack.definition.getId(), variant_name, variant_type)
|
||||
|
@ -929,12 +933,16 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
root_material_id = extruder_info.root_material_id
|
||||
root_material_id = self._old_new_materials.get(root_material_id, root_material_id)
|
||||
|
||||
build_plate_id = global_stack.variant.getId()
|
||||
|
||||
# get material diameter of this extruder
|
||||
machine_material_diameter = extruder_stack.materialDiameter
|
||||
material_node = material_manager.getMaterialNode(global_stack.definition.getId(),
|
||||
extruder_stack.variant.getName(),
|
||||
build_plate_id,
|
||||
machine_material_diameter,
|
||||
root_material_id)
|
||||
|
||||
if material_node is not None and material_node.getContainer() is not None:
|
||||
extruder_stack.material = material_node.getContainer()
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import argparse #To run the engine in debug mode if the front-end is in debug mode.
|
||||
from collections import defaultdict
|
||||
import os
|
||||
from PyQt5.QtCore import QObject, QTimer, pyqtSlot
|
||||
|
@ -178,7 +179,15 @@ class CuraEngineBackend(QObject, Backend):
|
|||
# This is useful for debugging and used to actually start the engine.
|
||||
# \return list of commands and args / parameters.
|
||||
def getEngineCommand(self) -> List[str]:
|
||||
return [self._application.getPreferences().getValue("backend/location"), "connect", "127.0.0.1:{0}".format(self._port), ""]
|
||||
command = [self._application.getPreferences().getValue("backend/location"), "connect", "127.0.0.1:{0}".format(self._port), ""]
|
||||
|
||||
parser = argparse.ArgumentParser(prog = "cura", add_help = False)
|
||||
parser.add_argument("--debug", action = "store_true", default = False, help = "Turn on the debug mode by setting this option.")
|
||||
known_args = vars(parser.parse_known_args()[0])
|
||||
if known_args["debug"]:
|
||||
command.append("-vvv")
|
||||
|
||||
return command
|
||||
|
||||
## Emitted when we get a message containing print duration and material amount.
|
||||
# This also implies the slicing has finished.
|
||||
|
|
|
@ -41,7 +41,7 @@ class StartJobResult(IntEnum):
|
|||
|
||||
## Formatter class that handles token expansion in start/end gcode
|
||||
class GcodeStartEndFormatter(Formatter):
|
||||
def get_value(self, key: str, *args: str, default_extruder_nr: str = "-1", **kwargs) -> str: #type: ignore # [CodeStyle: get_value is an overridden function from the Formatter class]
|
||||
def get_value(self, key: str, args: str, kwargs: dict, default_extruder_nr: str = "-1") -> str: #type: ignore # [CodeStyle: get_value is an overridden function from the Formatter class]
|
||||
# The kwargs dictionary contains a dictionary for each stack (with a string of the extruder_nr as their key),
|
||||
# and a default_extruder_nr to use when no extruder_nr is specified
|
||||
|
||||
|
|
|
@ -17,6 +17,10 @@ catalog = i18nCatalog("cura")
|
|||
#
|
||||
# If you're zipping g-code, you might as well use gzip!
|
||||
class GCodeGzWriter(MeshWriter):
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__(add_to_recent_files = False)
|
||||
|
||||
## Writes the gzipped g-code to a stream.
|
||||
#
|
||||
# Note that even though the function accepts a collection of nodes, the
|
||||
|
|
|
@ -16,7 +16,8 @@ def getMetaData():
|
|||
"extension": file_extension,
|
||||
"description": catalog.i18nc("@item:inlistbox", "Compressed G-code File"),
|
||||
"mime_type": "application/gzip",
|
||||
"mode": GCodeGzWriter.GCodeGzWriter.OutputMode.BinaryMode
|
||||
"mode": GCodeGzWriter.GCodeGzWriter.OutputMode.BinaryMode,
|
||||
"hide_in_file_dialog": True,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ class GCodeWriter(MeshWriter):
|
|||
_setting_keyword = ";SETTING_"
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
super().__init__(add_to_recent_files = False)
|
||||
|
||||
self._application = Application.getInstance()
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ class PauseAtHeight(Script):
|
|||
"pause_height":
|
||||
{
|
||||
"label": "Pause Height",
|
||||
"description": "At what height should the pause occur",
|
||||
"description": "At what height should the pause occur?",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"default_value": 5.0,
|
||||
|
@ -39,7 +39,7 @@ class PauseAtHeight(Script):
|
|||
"pause_layer":
|
||||
{
|
||||
"label": "Pause Layer",
|
||||
"description": "At what layer should the pause occur",
|
||||
"description": "At what layer should the pause occur?",
|
||||
"type": "int",
|
||||
"value": "math.floor((pause_height - 0.27) / 0.1) + 1",
|
||||
"minimum_value": "0",
|
||||
|
@ -142,13 +142,14 @@ class PauseAtHeight(Script):
|
|||
standby_temperature = self.getSettingValueByKey("standby_temperature")
|
||||
firmware_retract = Application.getInstance().getGlobalContainerStack().getProperty("machine_firmware_retract", "value")
|
||||
control_temperatures = Application.getInstance().getGlobalContainerStack().getProperty("machine_nozzle_temp_enabled", "value")
|
||||
initial_layer_height = Application.getInstance().getGlobalContainerStack().getProperty("layer_height_0", "value")
|
||||
|
||||
is_griffin = False
|
||||
|
||||
# T = ExtruderManager.getInstance().getActiveExtruderStack().getProperty("material_print_temperature", "value")
|
||||
|
||||
# use offset to calculate the current height: <current_height> = <current_z> - <layer_0_z>
|
||||
layer_0_z = 0.
|
||||
layer_0_z = 0
|
||||
current_z = 0
|
||||
got_first_g_cmd_on_layer_0 = False
|
||||
current_t = 0 #Tracks the current extruder for tracking the target temperature.
|
||||
|
@ -195,11 +196,10 @@ class PauseAtHeight(Script):
|
|||
# This block is executed once, the first time there is a G
|
||||
# command, to get the z offset (z for first positive layer)
|
||||
if not got_first_g_cmd_on_layer_0:
|
||||
layer_0_z = current_z
|
||||
layer_0_z = current_z - initial_layer_height
|
||||
got_first_g_cmd_on_layer_0 = True
|
||||
|
||||
current_height = current_z - layer_0_z
|
||||
|
||||
if current_height < pause_height:
|
||||
break # Try the next layer.
|
||||
|
||||
|
|
|
@ -40,33 +40,37 @@ Item {
|
|||
|
||||
property bool layersVisible: true
|
||||
|
||||
function getUpperValueFromSliderHandle () {
|
||||
function getUpperValueFromSliderHandle() {
|
||||
return upperHandle.getValue()
|
||||
}
|
||||
|
||||
function setUpperValue (value) {
|
||||
function setUpperValue(value) {
|
||||
upperHandle.setValue(value)
|
||||
updateRangeHandle()
|
||||
}
|
||||
|
||||
function getLowerValueFromSliderHandle () {
|
||||
function getLowerValueFromSliderHandle() {
|
||||
return lowerHandle.getValue()
|
||||
}
|
||||
|
||||
function setLowerValue (value) {
|
||||
function setLowerValue(value) {
|
||||
lowerHandle.setValue(value)
|
||||
updateRangeHandle()
|
||||
}
|
||||
|
||||
function updateRangeHandle () {
|
||||
function updateRangeHandle() {
|
||||
rangeHandle.height = lowerHandle.y - (upperHandle.y + upperHandle.height)
|
||||
}
|
||||
|
||||
// set the active handle to show only one label at a time
|
||||
function setActiveHandle (handle) {
|
||||
function setActiveHandle(handle) {
|
||||
activeHandle = handle
|
||||
}
|
||||
|
||||
function normalizeValue(value) {
|
||||
return Math.min(Math.max(value, sliderRoot.minimumValue), sliderRoot.maximumValue)
|
||||
}
|
||||
|
||||
// slider track
|
||||
Rectangle {
|
||||
id: track
|
||||
|
@ -188,6 +192,8 @@ Item {
|
|||
|
||||
// set the slider position based on the upper value
|
||||
function setValue (value) {
|
||||
// Normalize values between range, since using arrow keys will create out-of-the-range values
|
||||
value = sliderRoot.normalizeValue(value)
|
||||
|
||||
UM.SimulationView.setCurrentLayer(value)
|
||||
|
||||
|
@ -274,6 +280,8 @@ Item {
|
|||
|
||||
// set the slider position based on the lower value
|
||||
function setValue (value) {
|
||||
// Normalize values between range, since using arrow keys will create out-of-the-range values
|
||||
value = sliderRoot.normalizeValue(value)
|
||||
|
||||
UM.SimulationView.setMinimumLayer(value)
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ Item {
|
|||
|
||||
// value properties
|
||||
property real maximumValue: 100
|
||||
property real minimumValue: 0
|
||||
property bool roundValues: true
|
||||
property real handleValue: maximumValue
|
||||
|
||||
|
@ -47,6 +48,10 @@ Item {
|
|||
rangeHandle.width = handle.x - sliderRoot.handleSize
|
||||
}
|
||||
|
||||
function normalizeValue(value) {
|
||||
return Math.min(Math.max(value, sliderRoot.minimumValue), sliderRoot.maximumValue)
|
||||
}
|
||||
|
||||
// slider track
|
||||
Rectangle {
|
||||
id: track
|
||||
|
@ -110,6 +115,8 @@ Item {
|
|||
|
||||
// set the slider position based on the value
|
||||
function setValue (value) {
|
||||
// Normalize values between range, since using arrow keys will create out-of-the-range values
|
||||
value = sliderRoot.normalizeValue(value)
|
||||
|
||||
UM.SimulationView.setCurrentPath(value)
|
||||
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -25,10 +25,12 @@ from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
|
|||
|
||||
from UM.Settings.SettingInstance import SettingInstance
|
||||
|
||||
import numpy
|
||||
|
||||
class SupportEraser(Tool):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._shortcut_key = Qt.Key_G
|
||||
self._shortcut_key = Qt.Key_E
|
||||
self._controller = self.getController()
|
||||
|
||||
self._selection_pass = None
|
||||
|
@ -96,8 +98,7 @@ class SupportEraser(Tool):
|
|||
|
||||
node.setName("Eraser")
|
||||
node.setSelectable(True)
|
||||
mesh = MeshBuilder()
|
||||
mesh.addCube(10,10,10)
|
||||
mesh = self._createCube(10)
|
||||
node.setMeshData(mesh.build())
|
||||
|
||||
active_build_plate = CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate
|
||||
|
@ -160,3 +161,28 @@ class SupportEraser(Tool):
|
|||
self._skip_press = False
|
||||
|
||||
self._had_selection = has_selection
|
||||
|
||||
def _createCube(self, size):
|
||||
mesh = MeshBuilder()
|
||||
|
||||
# Can't use MeshBuilder.addCube() because that does not get per-vertex normals
|
||||
# Per-vertex normals require duplication of vertices
|
||||
s = size / 2
|
||||
verts = [ # 6 faces with 4 corners each
|
||||
[-s, -s, s], [-s, s, s], [ s, s, s], [ s, -s, s],
|
||||
[-s, s, -s], [-s, -s, -s], [ s, -s, -s], [ s, s, -s],
|
||||
[ s, -s, -s], [-s, -s, -s], [-s, -s, s], [ s, -s, s],
|
||||
[-s, s, -s], [ s, s, -s], [ s, s, s], [-s, s, s],
|
||||
[-s, -s, s], [-s, -s, -s], [-s, s, -s], [-s, s, s],
|
||||
[ s, -s, -s], [ s, -s, s], [ s, s, s], [ s, s, -s]
|
||||
]
|
||||
mesh.setVertices(numpy.asarray(verts, dtype=numpy.float32))
|
||||
|
||||
indices = []
|
||||
for i in range(0, 24, 4): # All 6 quads (12 triangles)
|
||||
indices.append([i, i+2, i+1])
|
||||
indices.append([i, i+3, i+2])
|
||||
mesh.setIndices(numpy.asarray(indices, dtype=numpy.int32))
|
||||
|
||||
mesh.calculateNormals()
|
||||
return mesh
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -30,7 +30,12 @@ Component
|
|||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||
anchors.right:parent.right
|
||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||
text: Cura.MachineManager.printerOutputDevices[0].name
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Rectangle
|
||||
|
|
|
@ -113,7 +113,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
|||
machine_file_formats = global_stack.getMetaDataEntry("file_formats").split(";")
|
||||
machine_file_formats = [file_type.strip() for file_type in machine_file_formats]
|
||||
#Exception for UM3 firmware version >=4.4: UFP is now supported and should be the preferred file format.
|
||||
if "application/x-ufp" not in machine_file_formats and self.printerType == "ultimaker3" and Version(self.firmwareVersion) >= Version("4.4"):
|
||||
if "application/x-ufp" not in machine_file_formats and Version(self.firmwareVersion) >= Version("4.4"):
|
||||
machine_file_formats = ["application/x-ufp"] + machine_file_formats
|
||||
|
||||
# Take the intersection between file_formats and machine_file_formats.
|
||||
|
|
|
@ -299,11 +299,11 @@ Cura.MachineAction
|
|||
}
|
||||
else if (base.selectedDevice.clusterSize === 0)
|
||||
{
|
||||
return catalog.i18nc("@label", "This printer is not set up to host a group of Ultimaker 3 printers.");
|
||||
return catalog.i18nc("@label", "This printer is not set up to host a group of printers.");
|
||||
}
|
||||
else
|
||||
{
|
||||
return catalog.i18nc("@label", "This printer is the host for a group of %1 Ultimaker 3 printers.".arg(base.selectedDevice.clusterSize));
|
||||
return catalog.i18nc("@label", "This printer is the host for a group of %1 printers.".arg(base.selectedDevice.clusterSize));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ Item
|
|||
anchors.bottomMargin: UM.Theme.getSize("default_margin").width
|
||||
anchors.right: cameraImage.right
|
||||
|
||||
// TODO: Harcoded sizes
|
||||
// TODO: Hardcoded sizes
|
||||
width: 20 * screenScaleFactor
|
||||
height: 20 * screenScaleFactor
|
||||
|
||||
|
|
|
@ -39,12 +39,12 @@ class SendMaterialJob(Job):
|
|||
try:
|
||||
remote_materials_list = json.loads(remote_materials_list)
|
||||
except json.JSONDecodeError:
|
||||
Logger.log("e", "Current material storage on printer was a corrupted reply.")
|
||||
Logger.log("e", "Request material storage on printer: I didn't understand the printer's answer.")
|
||||
return
|
||||
try:
|
||||
remote_materials_by_guid = {material["guid"]: material for material in remote_materials_list} #Index by GUID.
|
||||
except KeyError:
|
||||
Logger.log("e", "Current material storage on printer was an invalid reply (missing GUIDs).")
|
||||
Logger.log("e", "Request material storage on printer: Printer's answer was missing GUIDs.")
|
||||
return
|
||||
|
||||
container_registry = ContainerRegistry.getInstance()
|
||||
|
|
|
@ -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.
|
||||
|
||||
from UM.Job import Job
|
||||
|
@ -21,7 +21,6 @@ class AutoDetectBaudJob(Job):
|
|||
|
||||
def run(self):
|
||||
Logger.log("d", "Auto detect baud rate started.")
|
||||
timeout = 3
|
||||
wait_response_timeouts = [3, 15, 30]
|
||||
wait_bootloader_times = [1.5, 5, 15]
|
||||
write_timeout = 3
|
||||
|
@ -52,7 +51,7 @@ class AutoDetectBaudJob(Job):
|
|||
if serial is None:
|
||||
try:
|
||||
serial = Serial(str(self._serial_port), baud_rate, timeout = read_timeout, writeTimeout = write_timeout)
|
||||
except SerialException as e:
|
||||
except SerialException:
|
||||
Logger.logException("w", "Unable to create serial")
|
||||
continue
|
||||
else:
|
||||
|
|
|
@ -15,7 +15,7 @@ from cura.PrinterOutput.GenericOutputController import GenericOutputController
|
|||
from .AutoDetectBaudJob import AutoDetectBaudJob
|
||||
from .avr_isp import stk500v2, intelHex
|
||||
|
||||
from PyQt5.QtCore import pyqtSlot, pyqtSignal, pyqtProperty
|
||||
from PyQt5.QtCore import pyqtSlot, pyqtSignal, pyqtProperty, QUrl
|
||||
|
||||
from serial import Serial, SerialException, SerialTimeoutException
|
||||
from threading import Thread, Event
|
||||
|
@ -146,8 +146,11 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
|
||||
@pyqtSlot(str)
|
||||
def updateFirmware(self, file):
|
||||
# the file path is qurl encoded.
|
||||
self._firmware_location = file.replace("file://", "")
|
||||
# the file path could be url-encoded.
|
||||
if file.startswith("file://"):
|
||||
self._firmware_location = QUrl(file).toLocalFile()
|
||||
else:
|
||||
self._firmware_location = file
|
||||
self.showFirmwareInterface()
|
||||
self.setFirmwareUpdateState(FirmwareUpdateState.updating)
|
||||
self._update_firmware_thread.start()
|
||||
|
|
|
@ -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()]
|
||||
|
|
|
@ -6,21 +6,22 @@ import io
|
|||
import json #To parse the product-to-id mapping file.
|
||||
import os.path #To find the product-to-id mapping.
|
||||
import sys
|
||||
from typing import Any, Dict, List, Optional, cast
|
||||
from typing import Any, Dict, List, Optional, Tuple, cast
|
||||
import xml.etree.ElementTree as ET
|
||||
from typing import Dict
|
||||
from typing import Iterator
|
||||
|
||||
from UM.Resources import Resources
|
||||
from UM.Logger import Logger
|
||||
from cura.CuraApplication import CuraApplication
|
||||
import UM.Dictionary
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
|
||||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from cura.Machines.VariantType import VariantType
|
||||
|
||||
from .XmlMaterialValidator import XmlMaterialValidator
|
||||
|
||||
|
||||
## Handles serializing and deserializing material containers from an XML file
|
||||
class XmlMaterialProfile(InstanceContainer):
|
||||
CurrentFdmMaterialVersion = "1.3"
|
||||
|
@ -269,7 +270,6 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
buildplate_dict = {} # type: Dict[str, Any]
|
||||
for variant_name, variant_dict in machine_variant_map[definition_id].items():
|
||||
variant_type = variant_dict["variant_node"].metadata["hardware_type"]
|
||||
from cura.Machines.VariantManager import VariantType
|
||||
variant_type = VariantType(variant_type)
|
||||
if variant_type == VariantType.NOZZLE:
|
||||
# The hotend identifier is not the containers name, but its "name".
|
||||
|
@ -693,74 +693,38 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
if buildplate_id is None:
|
||||
continue
|
||||
|
||||
from cura.Machines.VariantManager import VariantType
|
||||
variant_manager = CuraApplication.getInstance().getVariantManager()
|
||||
variant_node = variant_manager.getVariantNode(machine_id, buildplate_id,
|
||||
variant_type = VariantType.BUILD_PLATE)
|
||||
if not variant_node:
|
||||
continue
|
||||
|
||||
buildplate_compatibility = machine_compatibility
|
||||
buildplate_recommended = machine_compatibility
|
||||
settings = buildplate.iterfind("./um:setting", self.__namespaces)
|
||||
for entry in settings:
|
||||
key = entry.get("key")
|
||||
if key in self.__unmapped_settings:
|
||||
if key == "hardware compatible":
|
||||
buildplate_compatibility = self._parseCompatibleValue(entry.text)
|
||||
elif key == "hardware recommended":
|
||||
buildplate_recommended = self._parseCompatibleValue(entry.text)
|
||||
else:
|
||||
Logger.log("d", "Unsupported material setting %s", key)
|
||||
_, buildplate_unmapped_settings_dict = self._getSettingsDictForNode(buildplate)
|
||||
|
||||
buildplate_compatibility = buildplate_unmapped_settings_dict.get("hardware compatible",
|
||||
machine_compatibility)
|
||||
buildplate_recommended = buildplate_unmapped_settings_dict.get("hardware recommended",
|
||||
machine_compatibility)
|
||||
|
||||
buildplate_map["buildplate_compatible"][buildplate_id] = buildplate_compatibility
|
||||
buildplate_map["buildplate_recommended"][buildplate_id] = buildplate_recommended
|
||||
|
||||
hotends = machine.iterfind("./um:hotend", self.__namespaces)
|
||||
for hotend in hotends:
|
||||
# The "id" field for hotends in material profiles are actually
|
||||
# The "id" field for hotends in material profiles is actually name
|
||||
hotend_name = hotend.get("id")
|
||||
if hotend_name is None:
|
||||
continue
|
||||
|
||||
variant_manager = CuraApplication.getInstance().getVariantManager()
|
||||
variant_node = variant_manager.getVariantNode(machine_id, hotend_name)
|
||||
variant_node = variant_manager.getVariantNode(machine_id, hotend_name, VariantType.NOZZLE)
|
||||
if not variant_node:
|
||||
continue
|
||||
|
||||
hotend_compatibility = machine_compatibility
|
||||
hotend_setting_values = {}
|
||||
settings = hotend.iterfind("./um:setting", self.__namespaces)
|
||||
for entry in settings:
|
||||
key = entry.get("key")
|
||||
if key in self.__material_settings_setting_map:
|
||||
if key == "processing temperature graph": #This setting has no setting text but subtags.
|
||||
graph_nodes = entry.iterfind("./um:point", self.__namespaces)
|
||||
graph_points = []
|
||||
for graph_node in graph_nodes:
|
||||
flow = float(graph_node.get("flow"))
|
||||
temperature = float(graph_node.get("temperature"))
|
||||
graph_points.append([flow, temperature])
|
||||
hotend_setting_values[self.__material_settings_setting_map[key]] = str(graph_points)
|
||||
else:
|
||||
hotend_setting_values[self.__material_settings_setting_map[key]] = entry.text
|
||||
elif key in self.__unmapped_settings:
|
||||
if key == "hardware compatible":
|
||||
hotend_compatibility = self._parseCompatibleValue(entry.text)
|
||||
else:
|
||||
Logger.log("d", "Unsupported material setting %s", key)
|
||||
|
||||
# Add namespaced Cura-specific settings
|
||||
settings = hotend.iterfind("./cura:setting", self.__namespaces)
|
||||
for entry in settings:
|
||||
value = entry.text
|
||||
if value.lower() == "yes":
|
||||
value = True
|
||||
elif value.lower() == "no":
|
||||
value = False
|
||||
key = entry.get("key")
|
||||
hotend_setting_values[key] = value
|
||||
hotend_mapped_settings, hotend_unmapped_settings = self._getSettingsDictForNode(hotend)
|
||||
hotend_compatibility = hotend_unmapped_settings.get("hardware compatible", machine_compatibility)
|
||||
|
||||
# Generate container ID for the hotend-specific material container
|
||||
new_hotend_specific_material_id = self.getId() + "_" + machine_id + "_" + hotend_name.replace(" ", "_")
|
||||
|
||||
# Same as machine compatibility, keep the derived material containers consistent with the parent material
|
||||
|
@ -785,7 +749,7 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
new_hotend_material.getMetaData()["buildplate_recommended"] = buildplate_map["buildplate_recommended"]
|
||||
|
||||
cached_hotend_setting_properties = cached_machine_setting_properties.copy()
|
||||
cached_hotend_setting_properties.update(hotend_setting_values)
|
||||
cached_hotend_setting_properties.update(hotend_mapped_settings)
|
||||
|
||||
new_hotend_material.setCachedValues(cached_hotend_setting_properties)
|
||||
|
||||
|
@ -794,6 +758,61 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
if is_new_material:
|
||||
containers_to_add.append(new_hotend_material)
|
||||
|
||||
#
|
||||
# Build plates in hotend
|
||||
#
|
||||
buildplates = hotend.iterfind("./um:buildplate", self.__namespaces)
|
||||
for buildplate in buildplates:
|
||||
# The "id" field for buildplate in material profiles is actually name
|
||||
buildplate_name = buildplate.get("id")
|
||||
if buildplate_name is None:
|
||||
continue
|
||||
|
||||
variant_manager = CuraApplication.getInstance().getVariantManager()
|
||||
variant_node = variant_manager.getVariantNode(machine_id, buildplate_name, VariantType.BUILD_PLATE)
|
||||
if not variant_node:
|
||||
continue
|
||||
|
||||
buildplate_mapped_settings, buildplate_unmapped_settings = self._getSettingsDictForNode(buildplate)
|
||||
buildplate_compatibility = buildplate_unmapped_settings.get("hardware compatible",
|
||||
buildplate_map["buildplate_compatible"])
|
||||
buildplate_recommended = buildplate_unmapped_settings.get("hardware recommended",
|
||||
buildplate_map["buildplate_recommended"])
|
||||
|
||||
# Generate container ID for the hotend-and-buildplate-specific material container
|
||||
new_hotend_and_buildplate_specific_material_id = new_hotend_specific_material_id + "_" + buildplate_name.replace(" ", "_")
|
||||
|
||||
# Same as machine compatibility, keep the derived material containers consistent with the parent material
|
||||
if ContainerRegistry.getInstance().isLoaded(new_hotend_and_buildplate_specific_material_id):
|
||||
new_hotend_and_buildplate_material = ContainerRegistry.getInstance().findContainers(id = new_hotend_and_buildplate_specific_material_id)[0]
|
||||
is_new_material = False
|
||||
else:
|
||||
new_hotend_and_buildplate_material = XmlMaterialProfile(new_hotend_and_buildplate_specific_material_id)
|
||||
is_new_material = True
|
||||
|
||||
new_hotend_and_buildplate_material.setMetaData(copy.deepcopy(new_hotend_material.getMetaData()))
|
||||
new_hotend_and_buildplate_material.getMetaData()["id"] = new_hotend_and_buildplate_specific_material_id
|
||||
new_hotend_and_buildplate_material.getMetaData()["name"] = self.getName()
|
||||
new_hotend_and_buildplate_material.getMetaData()["variant_name"] = hotend_name
|
||||
new_hotend_and_buildplate_material.getMetaData()["buildplate_name"] = buildplate_name
|
||||
new_hotend_and_buildplate_material.setDefinition(machine_id)
|
||||
# Don't use setMetadata, as that overrides it for all materials with same base file
|
||||
new_hotend_and_buildplate_material.getMetaData()["compatible"] = buildplate_compatibility
|
||||
new_hotend_and_buildplate_material.getMetaData()["machine_manufacturer"] = machine_manufacturer
|
||||
new_hotend_and_buildplate_material.getMetaData()["definition"] = machine_id
|
||||
new_hotend_and_buildplate_material.getMetaData()["buildplate_compatible"] = buildplate_compatibility
|
||||
new_hotend_and_buildplate_material.getMetaData()["buildplate_recommended"] = buildplate_recommended
|
||||
|
||||
cached_hotend_and_buildplate_setting_properties = cached_hotend_setting_properties.copy()
|
||||
cached_hotend_and_buildplate_setting_properties.update(buildplate_mapped_settings)
|
||||
|
||||
new_hotend_and_buildplate_material.setCachedValues(cached_hotend_and_buildplate_setting_properties)
|
||||
|
||||
new_hotend_and_buildplate_material._dirty = False
|
||||
|
||||
if is_new_material:
|
||||
containers_to_add.append(new_hotend_and_buildplate_material)
|
||||
|
||||
# there is only one ID for a machine. Once we have reached here, it means we have already found
|
||||
# a workable ID for that machine, so there is no need to continue
|
||||
break
|
||||
|
@ -801,6 +820,54 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
for container_to_add in containers_to_add:
|
||||
ContainerRegistry.getInstance().addContainer(container_to_add)
|
||||
|
||||
@classmethod
|
||||
def _getSettingsDictForNode(cls, node) -> Tuple[dict, dict]:
|
||||
node_mapped_settings_dict = dict()
|
||||
node_unmapped_settings_dict = dict()
|
||||
|
||||
# Fetch settings in the "um" namespace
|
||||
um_settings = node.iterfind("./um:setting", cls.__namespaces)
|
||||
for um_setting_entry in um_settings:
|
||||
setting_key = um_setting_entry.get("key")
|
||||
|
||||
# Mapped settings
|
||||
if setting_key in cls.__material_settings_setting_map:
|
||||
if setting_key == "processing temperature graph": # This setting has no setting text but subtags.
|
||||
graph_nodes = um_setting_entry.iterfind("./um:point", cls.__namespaces)
|
||||
graph_points = []
|
||||
for graph_node in graph_nodes:
|
||||
flow = float(graph_node.get("flow"))
|
||||
temperature = float(graph_node.get("temperature"))
|
||||
graph_points.append([flow, temperature])
|
||||
node_mapped_settings_dict[cls.__material_settings_setting_map[setting_key]] = str(
|
||||
graph_points)
|
||||
else:
|
||||
node_mapped_settings_dict[cls.__material_settings_setting_map[setting_key]] = um_setting_entry.text
|
||||
|
||||
# Unmapped settings
|
||||
elif setting_key in cls.__unmapped_settings:
|
||||
if setting_key in ("hardware compatible", "hardware recommended"):
|
||||
node_unmapped_settings_dict[setting_key] = cls._parseCompatibleValue(um_setting_entry.text)
|
||||
|
||||
# Unknown settings
|
||||
else:
|
||||
Logger.log("w", "Unsupported material setting %s", setting_key)
|
||||
|
||||
# Fetch settings in the "cura" namespace
|
||||
cura_settings = node.iterfind("./cura:setting", cls.__namespaces)
|
||||
for cura_setting_entry in cura_settings:
|
||||
value = cura_setting_entry.text
|
||||
if value.lower() == "yes":
|
||||
value = True
|
||||
elif value.lower() == "no":
|
||||
value = False
|
||||
key = cura_setting_entry.get("key")
|
||||
|
||||
# Cura settings are all mapped
|
||||
node_mapped_settings_dict[key] = value
|
||||
|
||||
return node_mapped_settings_dict, node_unmapped_settings_dict
|
||||
|
||||
@classmethod
|
||||
def deserializeMetadata(cls, serialized: str, container_id: str) -> List[Dict[str, Any]]:
|
||||
result_metadata = [] #All the metadata that we found except the base (because the base is returned).
|
||||
|
@ -983,6 +1050,36 @@ class XmlMaterialProfile(InstanceContainer):
|
|||
|
||||
result_metadata.append(new_hotend_material_metadata)
|
||||
|
||||
#
|
||||
# Buildplates in Hotends
|
||||
#
|
||||
buildplates = hotend.iterfind("./um:buildplate", cls.__namespaces)
|
||||
for buildplate in buildplates:
|
||||
# The "id" field for buildplate in material profiles is actually name
|
||||
buildplate_name = buildplate.get("id")
|
||||
if buildplate_name is None:
|
||||
continue
|
||||
|
||||
buildplate_mapped_settings, buildplate_unmapped_settings = cls._getSettingsDictForNode(buildplate)
|
||||
buildplate_compatibility = buildplate_unmapped_settings.get("hardware compatible",
|
||||
buildplate_map["buildplate_compatible"])
|
||||
buildplate_recommended = buildplate_unmapped_settings.get("hardware recommended",
|
||||
buildplate_map["buildplate_recommended"])
|
||||
|
||||
# Generate container ID for the hotend-and-buildplate-specific material container
|
||||
new_hotend_and_buildplate_specific_material_id = new_hotend_specific_material_id + "_" + buildplate_name.replace(
|
||||
" ", "_")
|
||||
|
||||
new_hotend_and_buildplate_material_metadata = {}
|
||||
new_hotend_and_buildplate_material_metadata.update(new_hotend_material_metadata)
|
||||
new_hotend_and_buildplate_material_metadata["id"] = new_hotend_and_buildplate_specific_material_id
|
||||
new_hotend_and_buildplate_material_metadata["buildplate_name"] = buildplate_name
|
||||
new_hotend_and_buildplate_material_metadata["compatible"] = buildplate_compatibility
|
||||
new_hotend_and_buildplate_material_metadata["buildplate_compatible"] = buildplate_compatibility
|
||||
new_hotend_and_buildplate_material_metadata["buildplate_recommended"] = buildplate_recommended
|
||||
|
||||
result_metadata.append(new_hotend_and_buildplate_material_metadata)
|
||||
|
||||
# there is only one ID for a machine. Once we have reached here, it means we have already found
|
||||
# a workable ID for that machine, so there is no need to continue
|
||||
break
|
||||
|
|
|
@ -2127,6 +2127,7 @@
|
|||
"type": "float",
|
||||
"default_value": 60,
|
||||
"value": "default_material_bed_temperature",
|
||||
"resolve": "max(extruderValues('material_bed_temperature'))",
|
||||
"minimum_value": "-273.15",
|
||||
"minimum_value_warning": "0",
|
||||
"maximum_value_warning": "130",
|
||||
|
@ -3025,7 +3026,7 @@
|
|||
{
|
||||
"label": "Initial Layer Print Acceleration",
|
||||
"description": "The acceleration during the printing of the initial layer.",
|
||||
"unit": "mm/s",
|
||||
"unit": "mm/s²",
|
||||
"type": "float",
|
||||
"default_value": 3000,
|
||||
"value": "acceleration_layer_0",
|
||||
|
@ -3039,7 +3040,7 @@
|
|||
{
|
||||
"label": "Initial Layer Travel Acceleration",
|
||||
"description": "The acceleration for travel moves in the initial layer.",
|
||||
"unit": "mm/s",
|
||||
"unit": "mm/s²",
|
||||
"type": "float",
|
||||
"default_value": 3000,
|
||||
"value": "acceleration_layer_0 * acceleration_travel / acceleration_print",
|
||||
|
@ -3355,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')",
|
||||
|
@ -5057,7 +5059,7 @@
|
|||
"description": "The minimum volume for each layer of the prime tower in order to purge enough material.",
|
||||
"unit": "mm³",
|
||||
"type": "float",
|
||||
"default_value": 5,
|
||||
"default_value": 6,
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "((resolveOrValue('prime_tower_size') * 0.5) ** 2 * 3.14159 * resolveOrValue('layer_height') if prime_tower_circular else resolveOrValue('prime_tower_size') ** 2 * resolveOrValue('layer_height')) - sum(extruderValues('prime_tower_min_volume')) + prime_tower_min_volume",
|
||||
"enabled": "resolveOrValue('prime_tower_enable')",
|
||||
|
@ -5072,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,
|
||||
|
@ -5086,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,
|
||||
|
@ -5625,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",
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
"manufacturer": "TiZYX",
|
||||
"file_formats": "text/x-gcode",
|
||||
"platform": "tizyx_k25_platform.stl",
|
||||
"platform_offset": [0, -4, 0],
|
||||
"exclude_materials": ["chromatik_pla", "dsm_arnitel2045_175", "dsm_novamid1070_175", "fabtotum_abs", "fabtotum_nylon", "fabtotum_pla", "fabtotum_tpu", "fiberlogy_hd_pla", "filo3d_pla", "filo3d_pla_green", "filo3d_pla_red", "generic_abs", "generic_abs_175", "generic_bam", "generic_cpe", "generic_cpe_175", "generic_cpe_plus", "generic_hips", "generic_hips_175", "generic_nylon", "generic_nylon_175", "generic_pc", "generic_pc_175", "generic_petg", "generic_petg_175", "generic_pla", "generic_pla_175", "generic_pp", "generic_pva", "generic_pva_175", "generic_tough_pla", "generic_tpu", "imade3d_petg_green", "imade3d_petg_pink", "imade3d_pla_green", "imade3d_pla_pink", "innofill_innoflex60_175", "octofiber_pla", "polyflex_pla", "polymax_pla", "polyplus_pla", "polywood_pla", "ultimaker_abs_black", "ultimaker_abs_blue", "ultimaker_abs_green", "ultimaker_abs_grey", "ultimaker_abs_orange", "ultimaker_abs_pearl-gold", "ultimaker_abs_red", "ultimaker_abs_silver-metallic", "ultimaker_abs_white", "ultimaker_abs_yellow", "ultimaker_bam", "ultimaker_cpe_black", "ultimaker_cpe_blue", "ultimaker_cpe_dark-grey", "ultimaker_cpe_green", "ultimaker_cpe_light-grey", "ultimaker_cpe_plus_black", "ultimaker_cpe_plus_transparent", "ultimaker_cpe_plus_white", "ultimaker_cpe_red", "ultimaker_cpe_transparent", "ultimaker_cpe_white", "ultimaker_cpe_yellow", "ultimaker_nylon_black", "ultimaker_nylon_transparent", "ultimaker_pc_black", "ultimaker_pc_transparent", "ultimaker_pc_white", "ultimaker_pla_black", "ultimaker_pla_blue", "ultimaker_pla_green", "ultimaker_pla_magenta", "ultimaker_pla_orange", "ultimaker_pla_pearl-white", "ultimaker_pla_red", "ultimaker_pla_silver-metallic", "ultimaker_pla_transparent", "ultimaker_pla_white", "ultimaker_pla_yellow", "ultimaker_pp_transparent", "ultimaker_pva", "ultimaker_tough_pla_black", "ultimaker_tough_pla_green", "ultimaker_tough_pla_red", "ultimaker_tough_pla_white", "ultimaker_tpu_black", "ultimaker_tpu_blue", "ultimaker_tpu_red", "ultimaker_tpu_white", "verbatim_bvoh_175", "Vertex_Delta_ABS", "Vertex_Delta_PET", "Vertex_Delta_PLA", "Vertex_Delta_TPU", "zyyx_pro_flex", "zyyx_pro_pla" ],
|
||||
"preferred_material": "tizyx_pla",
|
||||
"has_machine_quality": true,
|
||||
|
|
|
@ -4036,7 +4036,7 @@ msgstr "&Ansicht"
|
|||
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:184
|
||||
msgctxt "@title:menu"
|
||||
msgid "&Settings"
|
||||
msgstr "&Einstellungen"
|
||||
msgstr "&Konfiguration"
|
||||
|
||||
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:186
|
||||
msgctxt "@title:menu menubar:toplevel"
|
||||
|
|
|
@ -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 { }
|
||||
|
@ -231,8 +231,8 @@ UM.MainWindow
|
|||
onObjectRemoved: settingsMenu.removeItem(object)
|
||||
}
|
||||
|
||||
// TODO Temporary hidden, add back again when feature ready
|
||||
// BuildplateMenu { title: catalog.i18nc("@title:menu", "&Build plate"); visible: Cura.MachineManager.hasVariantBuildplates }
|
||||
// TODO Only show in dev mode. Remove check when feature ready
|
||||
BuildplateMenu { title: catalog.i18nc("@title:menu", "&Build plate"); visible: CuraSDKVersion == "dev" ? Cura.MachineManager.hasVariantBuildplates : false }
|
||||
ProfileMenu { title: catalog.i18nc("@title:menu", "&Profile"); }
|
||||
|
||||
MenuSeparator { }
|
||||
|
|
|
@ -30,20 +30,24 @@ TabView
|
|||
property bool reevaluateLinkedMaterials: false
|
||||
property string linkedMaterialNames:
|
||||
{
|
||||
if (reevaluateLinkedMaterials) {
|
||||
if (reevaluateLinkedMaterials)
|
||||
{
|
||||
reevaluateLinkedMaterials = false;
|
||||
}
|
||||
if (!base.containerId || !base.editingEnabled) {
|
||||
if (!base.containerId || !base.editingEnabled)
|
||||
{
|
||||
return ""
|
||||
}
|
||||
var linkedMaterials = Cura.ContainerManager.getLinkedMaterials(base.currentMaterialNode, true);
|
||||
if (linkedMaterials.length == 0) {
|
||||
if (linkedMaterials.length == 0)
|
||||
{
|
||||
return ""
|
||||
}
|
||||
return linkedMaterials.join(", ");
|
||||
}
|
||||
|
||||
function getApproximateDiameter(diameter) {
|
||||
function getApproximateDiameter(diameter)
|
||||
{
|
||||
return Math.round(diameter);
|
||||
}
|
||||
|
||||
|
@ -154,13 +158,15 @@ TabView
|
|||
}
|
||||
|
||||
Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Color") }
|
||||
Row {
|
||||
Row
|
||||
{
|
||||
width: scrollView.columnWidth
|
||||
height: parent.rowHeight
|
||||
spacing: Math.round(UM.Theme.getSize("default_margin").width / 2)
|
||||
|
||||
// color indicator square
|
||||
Rectangle {
|
||||
Rectangle
|
||||
{
|
||||
id: colorSelector
|
||||
color: properties.color_code
|
||||
|
||||
|
@ -171,7 +177,8 @@ TabView
|
|||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
// open the color selection dialog on click
|
||||
MouseArea {
|
||||
MouseArea
|
||||
{
|
||||
anchors.fill: parent
|
||||
onClicked: colorDialog.open()
|
||||
enabled: base.editingEnabled
|
||||
|
@ -179,7 +186,8 @@ TabView
|
|||
}
|
||||
|
||||
// pretty color name text field
|
||||
ReadOnlyTextField {
|
||||
ReadOnlyTextField
|
||||
{
|
||||
id: colorLabel;
|
||||
text: properties.color_name;
|
||||
readOnly: !base.editingEnabled
|
||||
|
@ -188,7 +196,8 @@ TabView
|
|||
|
||||
// popup dialog to select a new color
|
||||
// if successful it sets the properties.color_code value to the new color
|
||||
ColorDialog {
|
||||
ColorDialog
|
||||
{
|
||||
id: colorDialog
|
||||
color: properties.color_code
|
||||
onAccepted: base.setMetaDataEntry("color_code", properties.color_code, color)
|
||||
|
@ -258,7 +267,8 @@ TabView
|
|||
decimals: 2
|
||||
maximumValue: 100000000
|
||||
|
||||
onValueChanged: {
|
||||
onValueChanged:
|
||||
{
|
||||
base.setMaterialPreferenceValue(properties.guid, "spool_cost", parseFloat(value))
|
||||
updateCostPerMeter()
|
||||
}
|
||||
|
@ -275,7 +285,8 @@ TabView
|
|||
decimals: 0
|
||||
maximumValue: 10000
|
||||
|
||||
onValueChanged: {
|
||||
onValueChanged:
|
||||
{
|
||||
base.setMaterialPreferenceValue(properties.guid, "spool_weight", parseFloat(value))
|
||||
updateCostPerMeter()
|
||||
}
|
||||
|
@ -401,7 +412,8 @@ TabView
|
|||
{
|
||||
id: spinBox
|
||||
anchors.left: label.right
|
||||
value: {
|
||||
value:
|
||||
{
|
||||
// In case the setting is not in the material...
|
||||
if (!isNaN(parseFloat(materialPropertyProvider.properties.value)))
|
||||
{
|
||||
|
@ -493,8 +505,10 @@ TabView
|
|||
}
|
||||
|
||||
// Tiny convenience function to check if a value really changed before trying to set it.
|
||||
function setMetaDataEntry(entry_name, old_value, new_value) {
|
||||
if (old_value != new_value) {
|
||||
function setMetaDataEntry(entry_name, old_value, new_value)
|
||||
{
|
||||
if (old_value != new_value)
|
||||
{
|
||||
Cura.ContainerManager.setContainerMetaDataEntry(base.currentMaterialNode, entry_name, new_value)
|
||||
// make sure the UI properties are updated as well since we don't re-fetch the entire model here
|
||||
// When the entry_name is something like properties/diameter, we take the last part of the entry_name
|
||||
|
@ -546,10 +560,11 @@ TabView
|
|||
}
|
||||
|
||||
// update the display name of the material
|
||||
function updateMaterialDisplayName (old_name, new_name)
|
||||
function updateMaterialDisplayName(old_name, new_name)
|
||||
{
|
||||
// don't change when new name is the same
|
||||
if (old_name == new_name) {
|
||||
if (old_name == new_name)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@ Column
|
|||
|
||||
OutputDeviceHeader
|
||||
{
|
||||
width: parent.width
|
||||
outputDevice: connectedDevice
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ Item
|
|||
|
||||
Rectangle
|
||||
{
|
||||
anchors.fill: parent
|
||||
height: childrenRect.height
|
||||
color: UM.Theme.getColor("setting_category")
|
||||
property var activePrinter: outputDevice != null ? outputDevice.activePrinter : null
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
@ -48,16 +49,15 @@ Item {
|
|||
}
|
||||
|
||||
function sliceOrStopSlicing() {
|
||||
if ([1, 5].indexOf(base.backendState) != -1)
|
||||
{
|
||||
prepareButton.preparingToSlice = true;
|
||||
try {
|
||||
if ([1, 5].indexOf(base.backendState) != -1) {
|
||||
CuraApplication.backend.forceSlice();
|
||||
}
|
||||
else
|
||||
{
|
||||
prepareButton.preparingToSlice = false;
|
||||
} else {
|
||||
CuraApplication.backend.stopSlicing();
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('Could not start or stop slicing', e)
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
|
@ -168,10 +168,10 @@ Item {
|
|||
// Prepare button, only shows if auto_slice is off
|
||||
Button {
|
||||
id: prepareButton
|
||||
property bool preparingToSlice: false
|
||||
|
||||
tooltip: [1, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@info:tooltip","Slice current printjob") : catalog.i18nc("@info:tooltip","Cancel slicing process")
|
||||
// 1 = not started, 2 = Processing
|
||||
enabled: !preparingToSlice && base.backendState != "undefined" && ([1, 2].indexOf(base.backendState) != -1) && base.activity
|
||||
enabled: base.backendState != "undefined" && ([1, 2].indexOf(base.backendState) != -1) && base.activity
|
||||
visible: base.backendState != "undefined" && !autoSlice && ([1, 2, 4].indexOf(base.backendState) != -1) && base.activity
|
||||
property bool autoSlice
|
||||
height: UM.Theme.getSize("save_button_save_to_button").height
|
||||
|
@ -181,23 +181,7 @@ Item {
|
|||
anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width
|
||||
|
||||
// 1 = not started, 4 = error, 5 = disabled
|
||||
text: {
|
||||
if (preparingToSlice)
|
||||
{
|
||||
return catalog.i18nc("@label:Printjob", "Preparing");
|
||||
}
|
||||
else
|
||||
{
|
||||
if ([1, 4, 5].indexOf(base.backendState) != -1)
|
||||
{
|
||||
return catalog.i18nc("@label:Printjob", "Prepare");
|
||||
}
|
||||
else
|
||||
{
|
||||
return catalog.i18nc("@label:Printjob", "Cancel")
|
||||
}
|
||||
}
|
||||
}
|
||||
text: [1, 4, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@label:Printjob", "Prepare") : catalog.i18nc("@label:Printjob", "Cancel")
|
||||
onClicked:
|
||||
{
|
||||
sliceOrStopSlicing();
|
||||
|
@ -254,17 +238,6 @@ Item {
|
|||
}
|
||||
label: Item { }
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: UM.Backend
|
||||
onStateChanged:
|
||||
{
|
||||
if ([2, 3].indexOf(UM.Backend.state) != -1)
|
||||
{
|
||||
prepareButton.preparingToSlice = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
|
@ -285,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 {
|
||||
|
|
|
@ -561,6 +561,28 @@ Item
|
|||
visible: machineExtruderCount.properties.value > 1
|
||||
}
|
||||
|
||||
Instantiator
|
||||
{
|
||||
id: customMenuItems
|
||||
model: Cura.SidebarCustomMenuItemsModel { }
|
||||
MenuItem
|
||||
{
|
||||
text: model.name
|
||||
iconName: model.icon_name
|
||||
onTriggered:
|
||||
{
|
||||
customMenuItems.model.callMenuItemMethod(name, model.actions, {"key": contextMenu.key})
|
||||
}
|
||||
}
|
||||
onObjectAdded: contextMenu.insertItem(index, object)
|
||||
onObjectRemoved: contextMenu.removeItem(object)
|
||||
}
|
||||
|
||||
MenuSeparator
|
||||
{
|
||||
visible: customMenuItems.count > 0
|
||||
}
|
||||
|
||||
MenuItem
|
||||
{
|
||||
//: Settings context menu action
|
||||
|
|
|
@ -476,8 +476,8 @@ Column
|
|||
{
|
||||
id: buildplateRow
|
||||
height: UM.Theme.getSize("sidebar_setup").height
|
||||
// TODO Temporary hidden, add back again when feature ready
|
||||
visible: false //Cura.MachineManager.hasVariantBuildplates && !sidebar.hideSettings
|
||||
// TODO Only show in dev mode. Remove check when feature ready
|
||||
visible: CuraSDKVersion == "dev" ? Cura.MachineManager.hasVariantBuildplates && !sidebar.hideSettings : false
|
||||
|
||||
anchors
|
||||
{
|
||||
|
@ -533,6 +533,7 @@ Column
|
|||
rightMargin: UM.Theme.getSize("sidebar_margin").width
|
||||
}
|
||||
|
||||
// TODO This was added to replace the buildplate selector. Remove this component when the feature is ready
|
||||
Label
|
||||
{
|
||||
id: materialCompatibilityLabel
|
||||
|
@ -542,7 +543,7 @@ Column
|
|||
text: catalog.i18nc("@label", "Use glue with this material combination")
|
||||
font: UM.Theme.getFont("very_small")
|
||||
color: UM.Theme.getColor("text")
|
||||
visible: buildplateCompatibilityError || buildplateCompatibilityWarning
|
||||
visible: CuraSDKVersion == "dev" ? false : buildplateCompatibilityError || buildplateCompatibilityWarning
|
||||
wrapMode: Text.WordWrap
|
||||
opacity: 0.5
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2015 Ultimaker B.V.
|
||||
// Copyright (c) 2018 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
|
@ -21,8 +21,8 @@ Item
|
|||
{
|
||||
id: buttons;
|
||||
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
spacing: UM.Theme.getSize("button_lining").width
|
||||
|
||||
Repeater
|
||||
|
@ -34,20 +34,22 @@ Item
|
|||
height: childrenRect.height
|
||||
Button
|
||||
{
|
||||
text: model.name
|
||||
text: model.name + (model.shortcut ? (" (" + model.shortcut + ")") : "")
|
||||
iconSource: (UM.Theme.getIcon(model.icon) != "") ? UM.Theme.getIcon(model.icon) : "file:///" + model.location + "/" + model.icon
|
||||
checkable: true
|
||||
checked: model.active
|
||||
enabled: model.enabled && UM.Selection.hasSelection && UM.Controller.toolsEnabled
|
||||
style: UM.Theme.styles.tool_button
|
||||
|
||||
onCheckedChanged: {
|
||||
if (checked) {
|
||||
base.activeY = y
|
||||
onCheckedChanged:
|
||||
{
|
||||
if (checked)
|
||||
{
|
||||
base.activeY = y;
|
||||
}
|
||||
}
|
||||
|
||||
//Workaround since using ToolButton"s onClicked would break the binding of the checked property, instead
|
||||
//Workaround since using ToolButton's onClicked would break the binding of the checked property, instead
|
||||
//just catch the click so we do not trigger that behaviour.
|
||||
MouseArea
|
||||
{
|
||||
|
@ -57,7 +59,7 @@ Item
|
|||
forceActiveFocus() //First grab focus, so all the text fields are updated
|
||||
if(parent.checked)
|
||||
{
|
||||
UM.Controller.setActiveTool(null)
|
||||
UM.Controller.setActiveTool(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -96,11 +98,13 @@ Item
|
|||
|
||||
width:
|
||||
{
|
||||
if (panel.item && panel.width > 0){
|
||||
return Math.max(panel.width + 2 * UM.Theme.getSize("default_margin").width)
|
||||
if (panel.item && panel.width > 0)
|
||||
{
|
||||
return Math.max(panel.width + 2 * UM.Theme.getSize("default_margin").width);
|
||||
}
|
||||
else {
|
||||
return 0
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
height: panel.item ? panel.height + 2 * UM.Theme.getSize("default_margin").height : 0;
|
||||
|
@ -124,7 +128,7 @@ Item
|
|||
x: UM.Theme.getSize("default_margin").width;
|
||||
y: UM.Theme.getSize("default_margin").height;
|
||||
|
||||
source: UM.ActiveTool.valid ? UM.ActiveTool.activeToolPanel : "";
|
||||
source: UM.ActiveTool.valid ? UM.ActiveTool.activeToolPanel : ""
|
||||
enabled: UM.Controller.toolsEnabled;
|
||||
}
|
||||
}
|
||||
|
@ -148,6 +152,6 @@ Item
|
|||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
visible: toolHint.text != "";
|
||||
visible: toolHint.text != ""
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,8 +15,6 @@ variant = AA 0.25
|
|||
cool_fan_speed = 40
|
||||
infill_overlap = 15
|
||||
material_final_print_temperature = =material_print_temperature - 5
|
||||
prime_tower_size = 12
|
||||
prime_tower_min_volume = 2
|
||||
retraction_prime_speed = 25
|
||||
speed_topbottom = =math.ceil(speed_print * 30 / 55)
|
||||
wall_thickness = 0.92
|
||||
|
|
|
@ -12,8 +12,6 @@ material = generic_cpe
|
|||
variant = AA 0.25
|
||||
|
||||
[values]
|
||||
prime_tower_size = 12
|
||||
prime_tower_min_volume = 2
|
||||
retraction_extrusion_window = 0.5
|
||||
speed_infill = =math.ceil(speed_print * 40 / 55)
|
||||
speed_topbottom = =math.ceil(speed_print * 30 / 55)
|
||||
|
|
|
@ -34,7 +34,6 @@ material_standby_temperature = 100
|
|||
multiple_mesh_overlap = 0.2
|
||||
prime_tower_enable = True
|
||||
prime_tower_flow = 100
|
||||
prime_tower_min_volume = 10
|
||||
retract_at_layer_change = False
|
||||
retraction_count_max = 12
|
||||
retraction_extra_prime_amount = 0.5
|
||||
|
|
|
@ -35,7 +35,6 @@ material_standby_temperature = 100
|
|||
multiple_mesh_overlap = 0.2
|
||||
prime_tower_enable = True
|
||||
prime_tower_flow = 100
|
||||
prime_tower_min_volume = 15
|
||||
retract_at_layer_change = False
|
||||
retraction_count_max = 12
|
||||
retraction_extra_prime_amount = 0.5
|
||||
|
|
|
@ -34,7 +34,6 @@ material_standby_temperature = 100
|
|||
multiple_mesh_overlap = 0.2
|
||||
prime_tower_enable = True
|
||||
prime_tower_flow = 100
|
||||
prime_tower_min_volume = 20
|
||||
retract_at_layer_change = False
|
||||
retraction_count_max = 12
|
||||
retraction_extra_prime_amount = 0.5
|
||||
|
|
|
@ -15,8 +15,6 @@ variant = AA 0.25
|
|||
cool_fan_speed = 40
|
||||
infill_overlap = 15
|
||||
material_final_print_temperature = =material_print_temperature - 5
|
||||
prime_tower_size = 12
|
||||
prime_tower_min_volume = 2
|
||||
retraction_prime_speed = 25
|
||||
speed_topbottom = =math.ceil(speed_print * 30 / 55)
|
||||
wall_thickness = 0.92
|
||||
|
|
|
@ -12,8 +12,6 @@ material = generic_cpe
|
|||
variant = AA 0.25
|
||||
|
||||
[values]
|
||||
prime_tower_size = 12
|
||||
prime_tower_min_volume = 2
|
||||
retraction_extrusion_window = 0.5
|
||||
speed_infill = =math.ceil(speed_print * 40 / 55)
|
||||
speed_topbottom = =math.ceil(speed_print * 30 / 55)
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
[general]
|
||||
version = 4
|
||||
name = Extra Fine
|
||||
definition = ultimaker_s5
|
||||
|
||||
[metadata]
|
||||
setting_version = 5
|
||||
type = quality
|
||||
quality_type = high
|
||||
weight = 1
|
||||
material = generic_abs
|
||||
variant = AA 0.4
|
||||
buildplate = Aluminum
|
||||
|
||||
[values]
|
||||
cool_min_speed = 12
|
||||
layer_height_0 = 0.17
|
||||
machine_nozzle_cool_down_speed = 0.8
|
||||
machine_nozzle_heat_up_speed = 1.5
|
||||
material_bed_temperature = 90
|
||||
material_bed_temperature_layer_0 = 100
|
||||
material_print_temperature = =default_material_print_temperature + 5
|
||||
material_initial_print_temperature = =material_print_temperature - 15
|
||||
material_final_print_temperature = =material_print_temperature - 20
|
||||
prime_blob_enable = False
|
||||
prime_tower_enable = False
|
||||
speed_print = 50
|
||||
speed_layer_0 = =math.ceil(speed_print * 20 / 50)
|
||||
speed_topbottom = =math.ceil(speed_print * 30 / 50)
|
||||
speed_wall = =math.ceil(speed_print * 30 / 50)
|
||||
|
||||
infill_line_width = =round(line_width * 0.4 / 0.35, 2)
|
||||
speed_infill = =math.ceil(speed_print * 40 / 50)
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
[general]
|
||||
version = 4
|
||||
name = Extra Fine
|
||||
definition = ultimaker_s5
|
||||
|
||||
[metadata]
|
||||
setting_version = 5
|
||||
type = quality
|
||||
quality_type = high
|
||||
weight = 1
|
||||
material = generic_cpe_plus
|
||||
variant = AA 0.4
|
||||
buildplate = Aluminum
|
||||
|
||||
[values]
|
||||
acceleration_enabled = True
|
||||
acceleration_print = 4000
|
||||
cool_fan_speed_max = 50
|
||||
cool_min_speed = 5
|
||||
infill_line_width = =round(line_width * 0.35 / 0.35, 2)
|
||||
infill_overlap = 0
|
||||
infill_wipe_dist = 0
|
||||
jerk_enabled = True
|
||||
jerk_print = 25
|
||||
layer_height_0 = 0.17
|
||||
machine_min_cool_heat_time_window = 15
|
||||
machine_nozzle_cool_down_speed = 0.85
|
||||
machine_nozzle_heat_up_speed = 1.5
|
||||
material_bed_temperature = 105
|
||||
material_bed_temperature_layer_0 = 115
|
||||
material_final_print_temperature = =material_print_temperature - 10
|
||||
material_initial_print_temperature = =material_print_temperature - 5
|
||||
material_print_temperature = =default_material_print_temperature + 2
|
||||
material_print_temperature_layer_0 = =material_print_temperature
|
||||
multiple_mesh_overlap = 0
|
||||
prime_blob_enable = False
|
||||
prime_tower_enable = True
|
||||
prime_tower_wipe_enabled = True
|
||||
retraction_combing = off
|
||||
retraction_extrusion_window = 1
|
||||
retraction_hop = 0.2
|
||||
retraction_hop_enabled = False
|
||||
retraction_hop_only_when_collides = True
|
||||
skin_overlap = 20
|
||||
speed_layer_0 = =math.ceil(speed_print * 20 / 40)
|
||||
speed_print = 40
|
||||
speed_topbottom = =math.ceil(speed_print * 30 / 35)
|
||||
|
||||
speed_wall = =math.ceil(speed_print * 35 / 40)
|
||||
speed_wall_0 = =math.ceil(speed_wall * 30 / 35)
|
||||
support_bottom_distance = =support_z_distance
|
||||
support_z_distance = =layer_height
|
||||
wall_0_inset = 0
|
|
@ -0,0 +1,32 @@
|
|||
[general]
|
||||
version = 4
|
||||
name = Extra Fine
|
||||
definition = ultimaker_s5
|
||||
|
||||
[metadata]
|
||||
setting_version = 5
|
||||
type = quality
|
||||
quality_type = high
|
||||
weight = 1
|
||||
material = generic_cpe
|
||||
variant = AA 0.4
|
||||
buildplate = Aluminum
|
||||
|
||||
[values]
|
||||
cool_min_speed = 12
|
||||
layer_height_0 = 0.17
|
||||
machine_nozzle_cool_down_speed = 0.85
|
||||
machine_nozzle_heat_up_speed = 1.5
|
||||
material_bed_temperature = 80
|
||||
material_bed_temperature_layer_0 = 90
|
||||
material_print_temperature = =default_material_print_temperature - 5
|
||||
material_initial_print_temperature = =material_print_temperature - 5
|
||||
material_final_print_temperature = =material_print_temperature - 10
|
||||
prime_blob_enable = False
|
||||
speed_print = 50
|
||||
speed_layer_0 = =math.ceil(speed_print * 20 / 50)
|
||||
speed_topbottom = =math.ceil(speed_print * 30 / 50)
|
||||
speed_wall = =math.ceil(speed_print * 30 / 50)
|
||||
|
||||
infill_pattern = zigzag
|
||||
speed_infill = =math.ceil(speed_print * 40 / 50)
|
|
@ -0,0 +1,71 @@
|
|||
[general]
|
||||
version = 4
|
||||
name = Extra Fine
|
||||
definition = ultimaker_s5
|
||||
|
||||
[metadata]
|
||||
setting_version = 5
|
||||
type = quality
|
||||
quality_type = high
|
||||
weight = 1
|
||||
material = generic_pc
|
||||
variant = AA 0.4
|
||||
buildplate = Aluminum
|
||||
|
||||
[values]
|
||||
acceleration_enabled = True
|
||||
acceleration_print = 4000
|
||||
adhesion_type = brim
|
||||
brim_width = 10
|
||||
cool_fan_full_at_height = =layer_height_0 + layer_height
|
||||
cool_fan_speed_max = 50
|
||||
cool_min_layer_time_fan_speed_max = 5
|
||||
cool_min_speed = 8
|
||||
infill_line_width = =round(line_width * 0.4 / 0.35, 2)
|
||||
infill_overlap = 0
|
||||
infill_overlap_mm = 0.05
|
||||
infill_pattern = triangles
|
||||
infill_wipe_dist = 0.1
|
||||
jerk_enabled = True
|
||||
jerk_print = 25
|
||||
layer_height_0 = 0.17
|
||||
machine_min_cool_heat_time_window = 15
|
||||
machine_nozzle_cool_down_speed = 0.85
|
||||
machine_nozzle_heat_up_speed = 1.5
|
||||
material_bed_temperature = 115
|
||||
material_bed_temperature_layer_0 = 125
|
||||
material_final_print_temperature = =material_print_temperature - 10
|
||||
material_initial_print_temperature = =material_print_temperature - 5
|
||||
material_print_temperature = =default_material_print_temperature - 10
|
||||
material_standby_temperature = 100
|
||||
multiple_mesh_overlap = 0
|
||||
ooze_shield_angle = 40
|
||||
prime_blob_enable = False
|
||||
prime_tower_enable = True
|
||||
prime_tower_wipe_enabled = True
|
||||
raft_airgap = 0.25
|
||||
raft_interface_thickness = =max(layer_height * 1.5, 0.225)
|
||||
retraction_count_max = 80
|
||||
retraction_extrusion_window = 1
|
||||
retraction_hop = 2
|
||||
retraction_hop_only_when_collides = True
|
||||
retraction_min_travel = 0.8
|
||||
retraction_prime_speed = 15
|
||||
skin_overlap = 30
|
||||
speed_layer_0 = =math.ceil(speed_print * 25 / 50)
|
||||
speed_print = 50
|
||||
speed_topbottom = =math.ceil(speed_print * 25 / 50)
|
||||
|
||||
speed_wall = =math.ceil(speed_print * 40 / 50)
|
||||
speed_wall_0 = =math.ceil(speed_wall * 25 / 40)
|
||||
support_bottom_distance = =support_z_distance
|
||||
support_interface_density = 87.5
|
||||
support_interface_pattern = lines
|
||||
switch_extruder_prime_speed = 15
|
||||
switch_extruder_retraction_amount = 20
|
||||
switch_extruder_retraction_speeds = 35
|
||||
wall_0_inset = 0
|
||||
wall_line_width_x = =round(line_width * 0.4 / 0.35, 2)
|
||||
wall_thickness = 1.2
|
||||
|
||||
|
|
@ -32,7 +32,6 @@ material_standby_temperature = 100
|
|||
multiple_mesh_overlap = 0.2
|
||||
prime_tower_enable = True
|
||||
prime_tower_flow = 100
|
||||
prime_tower_min_volume = 10
|
||||
retract_at_layer_change = False
|
||||
retraction_count_max = 12
|
||||
retraction_extra_prime_amount = 0.5
|
||||
|
|
|
@ -33,7 +33,6 @@ material_standby_temperature = 100
|
|||
multiple_mesh_overlap = 0.2
|
||||
prime_tower_enable = True
|
||||
prime_tower_flow = 100
|
||||
prime_tower_min_volume = 20
|
||||
retract_at_layer_change = False
|
||||
retraction_count_max = 12
|
||||
retraction_extra_prime_amount = 0.5
|
||||
|
|
|
@ -32,7 +32,6 @@ material_standby_temperature = 100
|
|||
multiple_mesh_overlap = 0.2
|
||||
prime_tower_enable = True
|
||||
prime_tower_flow = 100
|
||||
prime_tower_min_volume = 15
|
||||
retract_at_layer_change = False
|
||||
retraction_count_max = 12
|
||||
retraction_extra_prime_amount = 0.5
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
[general]
|
||||
version = 4
|
||||
name = Fast
|
||||
definition = ultimaker_s5
|
||||
|
||||
[metadata]
|
||||
setting_version = 5
|
||||
type = quality
|
||||
quality_type = draft
|
||||
weight = -2
|
||||
material = generic_abs
|
||||
variant = AA 0.8
|
||||
buildplate = Aluminum
|
||||
|
||||
[values]
|
||||
layer_height_0 = 0.3
|
||||
line_width = =machine_nozzle_size * 0.875
|
||||
material_bed_temperature = 90
|
||||
material_bed_temperature_layer_0 = 100
|
||||
material_print_temperature = =default_material_print_temperature + 20
|
||||
material_standby_temperature = 100
|
||||
prime_blob_enable = False
|
||||
speed_print = 50
|
||||
speed_topbottom = =math.ceil(speed_print * 30 / 50)
|
||||
speed_wall = =math.ceil(speed_print * 40 / 50)
|
||||
speed_wall_0 = =math.ceil(speed_wall * 30 / 40)
|
||||
retract_at_layer_change = False
|
|
@ -0,0 +1,43 @@
|
|||
[general]
|
||||
version = 4
|
||||
name = Fast - Experimental
|
||||
definition = ultimaker_s5
|
||||
|
||||
[metadata]
|
||||
setting_version = 5
|
||||
type = quality
|
||||
quality_type = draft
|
||||
weight = -2
|
||||
material = generic_cpe_plus
|
||||
variant = AA 0.8
|
||||
buildplate = Aluminum
|
||||
|
||||
[values]
|
||||
brim_width = 14
|
||||
cool_fan_full_at_height = =layer_height_0 + 14 * layer_height
|
||||
infill_before_walls = True
|
||||
layer_height_0 = 0.3
|
||||
line_width = =machine_nozzle_size * 0.9375
|
||||
machine_nozzle_cool_down_speed = 0.9
|
||||
machine_nozzle_heat_up_speed = 1.4
|
||||
material_bed_temperature = 105
|
||||
material_bed_temperature_layer_0 = 115
|
||||
material_print_temperature = =default_material_print_temperature - 10
|
||||
material_print_temperature_layer_0 = =material_print_temperature
|
||||
material_standby_temperature = 100
|
||||
prime_blob_enable = False
|
||||
prime_tower_enable = True
|
||||
retraction_combing = off
|
||||
retraction_hop = 0.1
|
||||
retraction_hop_enabled = False
|
||||
skin_overlap = 0
|
||||
speed_layer_0 = =math.ceil(speed_print * 15 / 50)
|
||||
speed_print = 50
|
||||
speed_slowdown_layers = 15
|
||||
speed_topbottom = =math.ceil(speed_print * 35 / 50)
|
||||
speed_wall = =math.ceil(speed_print * 40 / 50)
|
||||
speed_wall_0 = =math.ceil(speed_wall * 35 / 40)
|
||||
support_bottom_distance = =support_z_distance
|
||||
support_line_width = =round(line_width * 0.6 / 0.7, 2)
|
||||
support_z_distance = =layer_height
|
||||
top_bottom_thickness = 1.2
|
|
@ -0,0 +1,29 @@
|
|||
[general]
|
||||
version = 4
|
||||
name = Fast
|
||||
definition = ultimaker_s5
|
||||
|
||||
[metadata]
|
||||
setting_version = 5
|
||||
type = quality
|
||||
quality_type = draft
|
||||
weight = -2
|
||||
material = generic_cpe
|
||||
variant = AA 0.8
|
||||
buildplate = Aluminum
|
||||
|
||||
[values]
|
||||
brim_width = 15
|
||||
layer_height_0 = 0.3
|
||||
line_width = =machine_nozzle_size * 0.875
|
||||
material_bed_temperature = 80
|
||||
material_bed_temperature_layer_0 = 90
|
||||
material_print_temperature = =default_material_print_temperature + 15
|
||||
material_standby_temperature = 100
|
||||
prime_blob_enable = False
|
||||
prime_tower_enable = True
|
||||
speed_print = 40
|
||||
speed_topbottom = =math.ceil(speed_print * 25 / 40)
|
||||
speed_wall = =math.ceil(speed_print * 30 / 40)
|
||||
|
||||
jerk_travel = 50
|
|
@ -0,0 +1,36 @@
|
|||
[general]
|
||||
version = 4
|
||||
name = Fast - Experimental
|
||||
definition = ultimaker_s5
|
||||
|
||||
[metadata]
|
||||
setting_version = 5
|
||||
type = quality
|
||||
quality_type = draft
|
||||
weight = 0
|
||||
material = generic_pc
|
||||
variant = AA 0.8
|
||||
buildplate = Aluminum
|
||||
|
||||
[values]
|
||||
brim_width = 10
|
||||
cool_fan_full_at_height = =layer_height_0 + 14 * layer_height
|
||||
infill_before_walls = True
|
||||
layer_height_0 = 0.3
|
||||
line_width = =machine_nozzle_size * 0.875
|
||||
material_bed_temperature = 115
|
||||
material_bed_temperature_layer_0 = 125
|
||||
material_print_temperature = =default_material_print_temperature - 5
|
||||
material_print_temperature_layer_0 = =material_print_temperature
|
||||
material_standby_temperature = 100
|
||||
prime_blob_enable = False
|
||||
raft_airgap = 0.5
|
||||
raft_margin = 15
|
||||
skin_overlap = 0
|
||||
speed_layer_0 = =math.ceil(speed_print * 15 / 50)
|
||||
speed_print = 50
|
||||
speed_slowdown_layers = 15
|
||||
speed_topbottom = =math.ceil(speed_print * 25 / 50)
|
||||
speed_wall = =math.ceil(speed_print * 40 / 50)
|
||||
speed_wall_0 = =math.ceil(speed_wall * 30 / 40)
|
||||
support_line_width = =round(line_width * 0.6 / 0.7, 2)
|
|
@ -0,0 +1,53 @@
|
|||
[general]
|
||||
version = 4
|
||||
name = Fast
|
||||
definition = ultimaker_s5
|
||||
|
||||
[metadata]
|
||||
setting_version = 5
|
||||
type = quality
|
||||
quality_type = draft
|
||||
weight = -2
|
||||
material = generic_pp
|
||||
variant = AA 0.8
|
||||
buildplate = Aluminum
|
||||
|
||||
[values]
|
||||
brim_width = 25
|
||||
cool_min_layer_time_fan_speed_max = 6
|
||||
cool_min_speed = 17
|
||||
top_skin_expand_distance = =line_width * 2
|
||||
infill_before_walls = True
|
||||
infill_line_width = =round(line_width * 0.7 / 0.8, 2)
|
||||
infill_pattern = tetrahedral
|
||||
jerk_prime_tower = =math.ceil(jerk_print * 25 / 25)
|
||||
jerk_support = =math.ceil(jerk_print * 25 / 25)
|
||||
jerk_wall_0 = =math.ceil(jerk_wall * 15 / 25)
|
||||
material_bed_temperature_layer_0 = =material_bed_temperature + 5
|
||||
material_print_temperature = =default_material_print_temperature - 2
|
||||
material_print_temperature_layer_0 = =default_material_print_temperature + 2
|
||||
material_standby_temperature = 100
|
||||
multiple_mesh_overlap = 0.2
|
||||
prime_tower_enable = True
|
||||
prime_tower_flow = 100
|
||||
prime_tower_min_volume = 10
|
||||
retract_at_layer_change = False
|
||||
retraction_count_max = 12
|
||||
retraction_extra_prime_amount = 0.5
|
||||
retraction_hop = 0.5
|
||||
retraction_min_travel = 1.5
|
||||
retraction_prime_speed = 15
|
||||
skin_line_width = =round(line_width * 0.78 / 0.8, 2)
|
||||
|
||||
speed_wall_x = =math.ceil(speed_wall * 30 / 30)
|
||||
support_bottom_distance = =support_z_distance
|
||||
support_line_width = =round(line_width * 0.7 / 0.8, 2)
|
||||
support_offset = =line_width
|
||||
switch_extruder_prime_speed = 15
|
||||
switch_extruder_retraction_amount = 20
|
||||
switch_extruder_retraction_speeds = 45
|
||||
top_bottom_thickness = 1.6
|
||||
travel_compensate_overlapping_walls_0_enabled = False
|
||||
wall_0_wipe_dist = =line_width * 2
|
||||
wall_line_width_x = =round(line_width * 0.8 / 0.8, 2)
|
||||
wall_thickness = 1.6
|
|
@ -40,7 +40,6 @@ material_print_temperature = =default_material_print_temperature + 10
|
|||
material_standby_temperature = 100
|
||||
multiple_mesh_overlap = 0
|
||||
prime_tower_enable = False
|
||||
prime_tower_min_volume = 20
|
||||
prime_tower_wipe_enabled = True
|
||||
raft_acceleration = =acceleration_layer_0
|
||||
raft_airgap = 0
|
||||
|
|
|
@ -22,7 +22,6 @@ jerk_support_bottom = =math.ceil(jerk_support_interface * 1 / 10)
|
|||
machine_nozzle_heat_up_speed = 1.5
|
||||
machine_nozzle_id = BB 0.4
|
||||
machine_nozzle_tip_outer_diameter = 1.0
|
||||
prime_tower_min_volume = 15
|
||||
raft_base_speed = 20
|
||||
raft_interface_speed = 20
|
||||
raft_speed = 25
|
||||
|
|
|
@ -11,7 +11,6 @@ hardware_type = nozzle
|
|||
[values]
|
||||
brim_width = 7
|
||||
infill_line_width = 0.23
|
||||
infill_overlap = 0
|
||||
layer_height_0 = 0.17
|
||||
line_width = 0.23
|
||||
machine_nozzle_cool_down_speed = 0.85
|
||||
|
@ -21,10 +20,18 @@ machine_nozzle_size = 0.25
|
|||
machine_nozzle_tip_outer_diameter = 0.65
|
||||
material_final_print_temperature = =material_print_temperature - 10
|
||||
material_initial_print_temperature = =material_print_temperature - 5
|
||||
raft_airgap = 0.3
|
||||
raft_base_thickness = =resolveOrValue('layer_height_0') * 1.2
|
||||
raft_interface_line_spacing = =raft_interface_line_width + 0.2
|
||||
raft_interface_line_width = =line_width * 2
|
||||
raft_interface_thickness = =layer_height * 1.5
|
||||
raft_jerk = =jerk_print
|
||||
raft_margin = 15
|
||||
raft_surface_layers = 2
|
||||
retraction_count_max = 25
|
||||
retraction_extrusion_window = 1
|
||||
retraction_min_travel = 0.7
|
||||
retraction_prime_speed = =retraction_speed
|
||||
skin_overlap = 15
|
||||
speed_layer_0 = 20
|
||||
speed_print = 55
|
||||
|
@ -32,8 +39,12 @@ speed_topbottom = 20
|
|||
speed_wall = =math.ceil(speed_print * 30 / 55)
|
||||
support_angle = 60
|
||||
support_bottom_distance = =support_z_distance / 2
|
||||
support_pattern = zigzag
|
||||
support_top_distance = =support_z_distance
|
||||
support_use_towers = True
|
||||
support_z_distance = =layer_height * 2
|
||||
switch_extruder_prime_speed = =switch_extruder_retraction_speeds
|
||||
switch_extruder_retraction_amount = =machine_heat_zone_length
|
||||
top_bottom_thickness = 1.2
|
||||
wall_line_width_x = 0.23
|
||||
wall_thickness = 1.3
|
||||
|
|
|
@ -40,7 +40,6 @@ material_print_temperature = =default_material_print_temperature + 10
|
|||
material_standby_temperature = 100
|
||||
multiple_mesh_overlap = 0
|
||||
prime_tower_enable = False
|
||||
prime_tower_min_volume = 20
|
||||
prime_tower_wipe_enabled = True
|
||||
raft_acceleration = =acceleration_layer_0
|
||||
raft_airgap = 0
|
||||
|
|
|
@ -22,7 +22,6 @@ jerk_support_bottom = =math.ceil(jerk_support_interface * 1 / 10)
|
|||
machine_nozzle_heat_up_speed = 1.5
|
||||
machine_nozzle_id = BB 0.4
|
||||
machine_nozzle_tip_outer_diameter = 1.0
|
||||
prime_tower_min_volume = 15
|
||||
raft_base_speed = 20
|
||||
raft_interface_speed = 20
|
||||
raft_speed = 25
|
||||
|
|
|
@ -40,7 +40,6 @@ material_print_temperature = =default_material_print_temperature + 10
|
|||
material_standby_temperature = 100
|
||||
multiple_mesh_overlap = 0
|
||||
prime_tower_enable = False
|
||||
prime_tower_min_volume = 20
|
||||
prime_tower_wipe_enabled = True
|
||||
raft_acceleration = =acceleration_layer_0
|
||||
raft_airgap = 0
|
||||
|
|
|
@ -22,7 +22,6 @@ jerk_support_bottom = =math.ceil(jerk_support_interface * 1 / 10)
|
|||
machine_nozzle_heat_up_speed = 1.5
|
||||
machine_nozzle_id = BB 0.4
|
||||
machine_nozzle_tip_outer_diameter = 1.0
|
||||
prime_tower_min_volume = 20
|
||||
raft_base_speed = 20
|
||||
raft_interface_speed = 20
|
||||
raft_speed = 25
|
||||
|
|
|
@ -1,61 +1,15 @@
|
|||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import os #To find the directory with test files and find the test files.
|
||||
import pytest #This module contains unit tests.
|
||||
import shutil #To copy files to make a temporary file.
|
||||
import unittest.mock #To mock and monkeypatch stuff.
|
||||
import urllib.parse
|
||||
import copy
|
||||
|
||||
import cura.CuraApplication
|
||||
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry #The class we're testing.
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||
from cura.Settings.ExtruderStack import ExtruderStack #Testing for returning the correct types of stacks.
|
||||
from cura.Settings.GlobalStack import GlobalStack #Testing for returning the correct types of stacks.
|
||||
from UM.Resources import Resources #Mocking some functions of this.
|
||||
import UM.Settings.InstanceContainer #Creating instance containers to register.
|
||||
import UM.Settings.ContainerRegistry #Making empty container stacks.
|
||||
import UM.Settings.ContainerStack #Setting the container registry here properly.
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
|
||||
def creteEmptyContainers():
|
||||
empty_container = ContainerRegistry.getInstance().getEmptyInstanceContainer()
|
||||
empty_variant_container = copy.deepcopy(empty_container)
|
||||
empty_variant_container.setMetaDataEntry("id", "empty_variant")
|
||||
empty_variant_container.setMetaDataEntry("type", "variant")
|
||||
ContainerRegistry.getInstance().addContainer(empty_variant_container)
|
||||
|
||||
empty_material_container = copy.deepcopy(empty_container)
|
||||
empty_material_container.setMetaDataEntry("id", "empty_material")
|
||||
empty_material_container.setMetaDataEntry("type", "material")
|
||||
ContainerRegistry.getInstance().addContainer(empty_material_container)
|
||||
|
||||
empty_quality_container = copy.deepcopy(empty_container)
|
||||
empty_quality_container.setMetaDataEntry("id", "empty_quality")
|
||||
empty_quality_container.setName("Not Supported")
|
||||
empty_quality_container.setMetaDataEntry("quality_type", "not_supported")
|
||||
empty_quality_container.setMetaDataEntry("type", "quality")
|
||||
empty_quality_container.setMetaDataEntry("supported", False)
|
||||
ContainerRegistry.getInstance().addContainer(empty_quality_container)
|
||||
|
||||
empty_quality_changes_container = copy.deepcopy(empty_container)
|
||||
empty_quality_changes_container.setMetaDataEntry("id", "empty_quality_changes")
|
||||
empty_quality_changes_container.setMetaDataEntry("type", "quality_changes")
|
||||
ContainerRegistry.getInstance().addContainer(empty_quality_changes_container)
|
||||
|
||||
## Gives a fresh CuraContainerRegistry instance.
|
||||
@pytest.fixture()
|
||||
def container_registry():
|
||||
registry = CuraContainerRegistry()
|
||||
UM.Settings.InstanceContainer.setContainerRegistry(registry)
|
||||
UM.Settings.ContainerStack.setContainerRegistry(registry)
|
||||
return registry
|
||||
|
||||
## Gives an arbitrary definition container.
|
||||
@pytest.fixture()
|
||||
def definition_container():
|
||||
return DefinitionContainer(container_id = "Test Definition")
|
||||
|
||||
def teardown():
|
||||
#If the temporary file for the legacy file rename test still exists, remove it.
|
||||
|
@ -64,44 +18,47 @@ def teardown():
|
|||
os.remove(temporary_file)
|
||||
|
||||
## Tests whether addContainer properly converts to ExtruderStack.
|
||||
def test_addContainerExtruderStack(container_registry, definition_container):
|
||||
creteEmptyContainers()
|
||||
def test_addContainerExtruderStack(container_registry, definition_container, definition_changes_container):
|
||||
container_registry.addContainer(definition_container)
|
||||
container_registry.addContainer(definition_changes_container)
|
||||
|
||||
container_stack = UM.Settings.ContainerStack.ContainerStack(stack_id = "Test Container Stack") #A container we're going to convert.
|
||||
container_stack = UM.Settings.ContainerStack.ContainerStack(stack_id = "Test Extruder Stack") #A container we're going to convert.
|
||||
container_stack.setMetaDataEntry("type", "extruder_train") #This is now an extruder train.
|
||||
container_stack.insertContainer(0, definition_container) #Add a definition to it so it doesn't complain.
|
||||
container_stack.insertContainer(1, definition_changes_container)
|
||||
|
||||
mock_super_add_container = unittest.mock.MagicMock() #Takes the role of the Uranium-ContainerRegistry where the resulting containers get registered.
|
||||
with unittest.mock.patch("UM.Settings.ContainerRegistry.ContainerRegistry.addContainer", mock_super_add_container):
|
||||
container_registry.addContainer(container_stack)
|
||||
|
||||
assert len(mock_super_add_container.call_args_list) == 2 #Called only once.
|
||||
assert len(mock_super_add_container.call_args_list[1][0]) == 1 #Called with one parameter.
|
||||
assert type(mock_super_add_container.call_args_list[1][0][0]) == ExtruderStack
|
||||
assert len(mock_super_add_container.call_args_list) == 1 #Called only once.
|
||||
assert len(mock_super_add_container.call_args_list[0][0]) == 1 #Called with one parameter.
|
||||
assert type(mock_super_add_container.call_args_list[0][0][0]) == ExtruderStack
|
||||
|
||||
## Tests whether addContainer properly converts to GlobalStack.
|
||||
def test_addContainerGlobalStack(container_registry, definition_container):
|
||||
def test_addContainerGlobalStack(container_registry, definition_container, definition_changes_container):
|
||||
container_registry.addContainer(definition_container)
|
||||
container_registry.addContainer(definition_changes_container)
|
||||
|
||||
container_stack = UM.Settings.ContainerStack.ContainerStack(stack_id = "Test Container Stack") #A container we're going to convert.
|
||||
container_stack = UM.Settings.ContainerStack.ContainerStack(stack_id = "Test Global Stack") #A container we're going to convert.
|
||||
container_stack.setMetaDataEntry("type", "machine") #This is now a global stack.
|
||||
container_stack.insertContainer(0, definition_container) #Must have a definition.
|
||||
container_stack.insertContainer(1, definition_changes_container) #Must have a definition changes.
|
||||
|
||||
mock_super_add_container = unittest.mock.MagicMock() #Takes the role of the Uranium-ContainerRegistry where the resulting containers get registered.
|
||||
with unittest.mock.patch("UM.Settings.ContainerRegistry.ContainerRegistry.addContainer", mock_super_add_container):
|
||||
container_registry.addContainer(container_stack)
|
||||
|
||||
assert len(mock_super_add_container.call_args_list) == 2 #Called only once.
|
||||
assert len(mock_super_add_container.call_args_list[1][0]) == 1 #Called with one parameter.
|
||||
assert type(mock_super_add_container.call_args_list[1][0][0]) == GlobalStack
|
||||
assert len(mock_super_add_container.call_args_list) == 1 #Called only once.
|
||||
assert len(mock_super_add_container.call_args_list[0][0]) == 1 #Called with one parameter.
|
||||
assert type(mock_super_add_container.call_args_list[0][0][0]) == GlobalStack
|
||||
|
||||
def test_addContainerGoodSettingVersion(container_registry, definition_container):
|
||||
from cura.CuraApplication import CuraApplication
|
||||
definition_container.getMetaData()["setting_version"] = CuraApplication.SettingVersion
|
||||
container_registry.addContainer(definition_container)
|
||||
|
||||
instance = UM.Settings.InstanceContainer.InstanceContainer(container_id = "Test Instance")
|
||||
instance = UM.Settings.InstanceContainer.InstanceContainer(container_id = "Test Instance Right Version")
|
||||
instance.setMetaDataEntry("setting_version", CuraApplication.SettingVersion)
|
||||
instance.setDefinition(definition_container.getId())
|
||||
|
||||
|
@ -116,7 +73,7 @@ def test_addContainerNoSettingVersion(container_registry, definition_container):
|
|||
definition_container.getMetaData()["setting_version"] = CuraApplication.SettingVersion
|
||||
container_registry.addContainer(definition_container)
|
||||
|
||||
instance = UM.Settings.InstanceContainer.InstanceContainer(container_id = "Test Instance")
|
||||
instance = UM.Settings.InstanceContainer.InstanceContainer(container_id = "Test Instance No Version")
|
||||
#Don't add setting_version metadata.
|
||||
instance.setDefinition(definition_container.getId())
|
||||
|
||||
|
@ -131,7 +88,7 @@ def test_addContainerBadSettingVersion(container_registry, definition_container)
|
|||
definition_container.getMetaData()["setting_version"] = CuraApplication.SettingVersion
|
||||
container_registry.addContainer(definition_container)
|
||||
|
||||
instance = UM.Settings.InstanceContainer.InstanceContainer(container_id = "Test Instance")
|
||||
instance = UM.Settings.InstanceContainer.InstanceContainer(container_id = "Test Instance Wrong Version")
|
||||
instance.setMetaDataEntry("setting_version", 9001) #Wrong version!
|
||||
instance.setDefinition(definition_container.getId())
|
||||
|
||||
|
@ -140,38 +97,3 @@ def test_addContainerBadSettingVersion(container_registry, definition_container)
|
|||
container_registry.addContainer(instance)
|
||||
|
||||
mock_super_add_container.assert_not_called() #Should not get passed on to UM.Settings.ContainerRegistry.addContainer, because the setting_version doesn't match its definition!
|
||||
|
||||
## Tests whether loading gives objects of the correct type.
|
||||
# @pytest.mark.parametrize("filename, output_class", [
|
||||
# ("ExtruderLegacy.stack.cfg", ExtruderStack),
|
||||
# ("MachineLegacy.stack.cfg", GlobalStack),
|
||||
# ("Left.extruder.cfg", ExtruderStack),
|
||||
# ("Global.global.cfg", GlobalStack),
|
||||
# ("Global.stack.cfg", GlobalStack)
|
||||
# ])
|
||||
# def test_loadTypes(filename, output_class, container_registry):
|
||||
# #Mock some dependencies.
|
||||
# Resources.getAllResourcesOfType = unittest.mock.MagicMock(return_value = [os.path.join(os.path.dirname(os.path.abspath(__file__)), "stacks", filename)]) #Return just this tested file.
|
||||
#
|
||||
# def findContainers(container_type = 0, id = None):
|
||||
# if id == "some_instance":
|
||||
# return [UM.Settings.ContainerRegistry._EmptyInstanceContainer(id)]
|
||||
# elif id == "some_definition":
|
||||
# return [DefinitionContainer(container_id = id)]
|
||||
# else:
|
||||
# return []
|
||||
#
|
||||
# container_registry.findContainers = findContainers
|
||||
#
|
||||
# with unittest.mock.patch("cura.Settings.GlobalStack.GlobalStack.findContainer"):
|
||||
# with unittest.mock.patch("os.remove"):
|
||||
# container_registry.load()
|
||||
#
|
||||
# #Check whether the resulting type was correct.
|
||||
# stack_id = filename.split(".")[0]
|
||||
# for container_id, container in container_registry._containers.items(): #Stupid ContainerRegistry class doesn't expose any way of getting at this except by prodding the privates.
|
||||
# if container_id == stack_id: #This is the one we're testing.
|
||||
# assert type(container) == output_class
|
||||
# break
|
||||
# else:
|
||||
# assert False #Container stack with specified ID was not loaded.
|
|
@ -1,43 +1,17 @@
|
|||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import pytest #This module contains automated tests.
|
||||
import unittest.mock #For the mocking and monkeypatching functionality.
|
||||
import copy
|
||||
|
||||
import cura.CuraApplication
|
||||
import cura.Settings.CuraContainerStack #To get the list of container types.
|
||||
import UM.Settings.ContainerRegistry #To create empty instance containers.
|
||||
import UM.Settings.ContainerStack #To set the container registry the container stacks use.
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer #To check against the class of DefinitionContainer.
|
||||
from UM.Settings.InstanceContainer import InstanceContainer #To check against the class of InstanceContainer.
|
||||
import cura.Settings.ExtruderStack #The module we're testing.
|
||||
from cura.Settings.Exceptions import InvalidContainerError, InvalidOperationError #To check whether the correct exceptions are raised.
|
||||
|
||||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from cura.Settings.GlobalStack import GlobalStack
|
||||
|
||||
## Fake container registry that always provides all containers you ask of.
|
||||
@pytest.yield_fixture()
|
||||
def container_registry():
|
||||
registry = unittest.mock.MagicMock()
|
||||
registry.return_value = unittest.mock.NonCallableMagicMock()
|
||||
registry.findInstanceContainers = lambda *args, registry = registry, **kwargs: [registry.return_value]
|
||||
registry.findDefinitionContainers = lambda *args, registry = registry, **kwargs: [registry.return_value]
|
||||
|
||||
UM.Settings.ContainerRegistry.ContainerRegistry._ContainerRegistry__instance = registry
|
||||
UM.Settings.ContainerStack._containerRegistry = registry
|
||||
|
||||
yield registry
|
||||
|
||||
UM.Settings.ContainerRegistry.ContainerRegistry._ContainerRegistry__instance = None
|
||||
UM.Settings.ContainerStack._containerRegistry = None
|
||||
|
||||
## An empty extruder stack to test with.
|
||||
@pytest.fixture()
|
||||
def extruder_stack() -> cura.Settings.ExtruderStack.ExtruderStack:
|
||||
creteEmptyContainers()
|
||||
return cura.Settings.ExtruderStack.ExtruderStack("TestStack")
|
||||
from cura.Settings.cura_empty_instance_containers import empty_container
|
||||
|
||||
## Gets an instance container with a specified container type.
|
||||
#
|
||||
|
@ -48,31 +22,6 @@ def getInstanceContainer(container_type) -> InstanceContainer:
|
|||
container.setMetaDataEntry("type", container_type)
|
||||
return container
|
||||
|
||||
def creteEmptyContainers():
|
||||
empty_container = ContainerRegistry.getInstance().getEmptyInstanceContainer()
|
||||
empty_variant_container = copy.deepcopy(empty_container)
|
||||
empty_variant_container.setMetaDataEntry("id", "empty_variant")
|
||||
empty_variant_container.setMetaDataEntry("type", "variant")
|
||||
ContainerRegistry.getInstance().addContainer(empty_variant_container)
|
||||
|
||||
empty_material_container = copy.deepcopy(empty_container)
|
||||
empty_material_container.setMetaDataEntry("id", "empty_material")
|
||||
empty_material_container.setMetaDataEntry("type", "material")
|
||||
ContainerRegistry.getInstance().addContainer(empty_material_container)
|
||||
|
||||
empty_quality_container = copy.deepcopy(empty_container)
|
||||
empty_quality_container.setMetaDataEntry("id", "empty_quality")
|
||||
empty_quality_container.setName("Not Supported")
|
||||
empty_quality_container.setMetaDataEntry("quality_type", "not_supported")
|
||||
empty_quality_container.setMetaDataEntry("type", "quality")
|
||||
empty_quality_container.setMetaDataEntry("supported", False)
|
||||
ContainerRegistry.getInstance().addContainer(empty_quality_container)
|
||||
|
||||
empty_quality_changes_container = copy.deepcopy(empty_container)
|
||||
empty_quality_changes_container.setMetaDataEntry("id", "empty_quality_changes")
|
||||
empty_quality_changes_container.setMetaDataEntry("type", "quality_changes")
|
||||
ContainerRegistry.getInstance().addContainer(empty_quality_changes_container)
|
||||
|
||||
class DefinitionContainerSubClass(DefinitionContainer):
|
||||
def __init__(self):
|
||||
super().__init__(container_id = "SubDefinitionContainer")
|
||||
|
@ -179,12 +128,30 @@ def test_constrainVariantInvalid(container, extruder_stack):
|
|||
def test_constrainVariantValid(container, extruder_stack):
|
||||
extruder_stack.variant = container #Should not give an error.
|
||||
|
||||
#Tests setting definition changes profiles to invalid containers.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "wrong container type"),
|
||||
getInstanceContainer(container_type = "material"), #Existing, but still wrong type.
|
||||
DefinitionContainer(container_id = "wrong class")
|
||||
])
|
||||
def test_constrainDefinitionChangesInvalid(container, global_stack):
|
||||
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
||||
global_stack.definitionChanges = container
|
||||
|
||||
#Test setting definition changes profiles.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "definition_changes"),
|
||||
InstanceContainerSubClass(container_type = "definition_changes")
|
||||
])
|
||||
def test_constrainDefinitionChangesValid(container, global_stack):
|
||||
global_stack.definitionChanges = container #Should not give an error.
|
||||
|
||||
#Tests setting definitions to invalid containers.
|
||||
@pytest.mark.parametrize("container", [
|
||||
getInstanceContainer(container_type = "wrong class"),
|
||||
getInstanceContainer(container_type = "material"), #Existing, but still wrong class.
|
||||
])
|
||||
def test_constrainVariantInvalid(container, extruder_stack):
|
||||
def test_constrainDefinitionInvalid(container, extruder_stack):
|
||||
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
||||
extruder_stack.definition = container
|
||||
|
||||
|
@ -196,23 +163,22 @@ def test_constrainVariantInvalid(container, extruder_stack):
|
|||
def test_constrainDefinitionValid(container, extruder_stack):
|
||||
extruder_stack.definition = container #Should not give an error.
|
||||
|
||||
## Tests whether deserialising completes the missing containers with empty
|
||||
# ones.
|
||||
@pytest.mark.skip #The test currently fails because the definition container doesn't have a category, which is wrong but we don't have time to refactor that right now.
|
||||
def test_deserializeCompletesEmptyContainers(extruder_stack: cura.Settings.ExtruderStack):
|
||||
extruder_stack._containers = [DefinitionContainer(container_id = "definition")] #Set the internal state of this stack manually.
|
||||
## Tests whether deserialising completes the missing containers with empty ones.
|
||||
def test_deserializeCompletesEmptyContainers(extruder_stack):
|
||||
extruder_stack._containers = [DefinitionContainer(container_id = "definition"), extruder_stack.definitionChanges] #Set the internal state of this stack manually.
|
||||
|
||||
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
|
||||
extruder_stack.deserialize("")
|
||||
|
||||
assert len(extruder_stack.getContainers()) == len(cura.Settings.CuraContainerStack._ContainerIndexes.IndexTypeMap) #Needs a slot for every type.
|
||||
for container_type_index in cura.Settings.CuraContainerStack._ContainerIndexes.IndexTypeMap:
|
||||
if container_type_index == cura.Settings.CuraContainerStack._ContainerIndexes.Definition: #We're not checking the definition.
|
||||
if container_type_index in \
|
||||
(cura.Settings.CuraContainerStack._ContainerIndexes.Definition,
|
||||
cura.Settings.CuraContainerStack._ContainerIndexes.DefinitionChanges): # We're not checking the definition or definition_changes
|
||||
continue
|
||||
assert extruder_stack.getContainer(container_type_index).getId() == "empty" #All others need to be empty.
|
||||
assert extruder_stack.getContainer(container_type_index) == empty_container #All others need to be empty.
|
||||
|
||||
## Tests whether an instance container with the wrong type gets removed when
|
||||
# deserialising.
|
||||
## Tests whether an instance container with the wrong type gets removed when deserialising.
|
||||
def test_deserializeRemovesWrongInstanceContainer(extruder_stack):
|
||||
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = getInstanceContainer(container_type = "wrong type")
|
||||
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition")
|
||||
|
@ -222,8 +188,7 @@ def test_deserializeRemovesWrongInstanceContainer(extruder_stack):
|
|||
|
||||
assert extruder_stack.quality == extruder_stack._empty_instance_container #Replaced with empty.
|
||||
|
||||
## Tests whether a container with the wrong class gets removed when
|
||||
# deserialising.
|
||||
## Tests whether a container with the wrong class gets removed when deserialising.
|
||||
def test_deserializeRemovesWrongContainerClass(extruder_stack):
|
||||
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = DefinitionContainer(container_id = "wrong class")
|
||||
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition")
|
||||
|
@ -233,8 +198,7 @@ def test_deserializeRemovesWrongContainerClass(extruder_stack):
|
|||
|
||||
assert extruder_stack.quality == extruder_stack._empty_instance_container #Replaced with empty.
|
||||
|
||||
## Tests whether an instance container in the definition spot results in an
|
||||
# error.
|
||||
## Tests whether an instance container in the definition spot results in an error.
|
||||
def test_deserializeWrongDefinitionClass(extruder_stack):
|
||||
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = getInstanceContainer(container_type = "definition") #Correct type but wrong class.
|
||||
|
||||
|
@ -242,8 +206,7 @@ def test_deserializeWrongDefinitionClass(extruder_stack):
|
|||
with pytest.raises(UM.Settings.ContainerStack.InvalidContainerStackError): #Must raise an error that there is no definition container.
|
||||
extruder_stack.deserialize("")
|
||||
|
||||
## Tests whether an instance container with the wrong type is moved into the
|
||||
# correct slot by deserialising.
|
||||
## Tests whether an instance container with the wrong type is moved into the correct slot by deserialising.
|
||||
def test_deserializeMoveInstanceContainer(extruder_stack):
|
||||
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = getInstanceContainer(container_type = "material") #Not in the correct spot.
|
||||
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition")
|
||||
|
@ -251,26 +214,21 @@ def test_deserializeMoveInstanceContainer(extruder_stack):
|
|||
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
|
||||
extruder_stack.deserialize("")
|
||||
|
||||
assert extruder_stack.quality.getId() == "empty"
|
||||
assert extruder_stack.material.getId() != "empty"
|
||||
from UM.Settings.Validator import Validator
|
||||
## Tests whether a definition container in the wrong spot is moved into the
|
||||
# correct spot by deserialising.
|
||||
@pytest.mark.skip #The test currently fails because the definition container doesn't have a category, which is wrong but we don't have time to refactor that right now.
|
||||
assert extruder_stack.quality == empty_container
|
||||
assert extruder_stack.material != empty_container
|
||||
|
||||
## Tests whether a definition container in the wrong spot is moved into the correct spot by deserialising.
|
||||
def test_deserializeMoveDefinitionContainer(extruder_stack):
|
||||
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Material] = DefinitionContainer(container_id = "some definition") #Not in the correct spot.
|
||||
|
||||
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
|
||||
extruder_stack.deserialize("")
|
||||
|
||||
assert extruder_stack.material.getId() == "empty"
|
||||
assert extruder_stack.definition.getId() != "empty"
|
||||
assert extruder_stack.material == empty_container
|
||||
assert extruder_stack.definition != empty_container
|
||||
|
||||
UM.Settings.ContainerStack._containerRegistry = None
|
||||
|
||||
## Tests whether getProperty properly applies the stack-like behaviour on its
|
||||
# containers.
|
||||
def test_getPropertyFallThrough(extruder_stack):
|
||||
## Tests whether getProperty properly applies the stack-like behaviour on its containers.
|
||||
def test_getPropertyFallThrough(global_stack, extruder_stack):
|
||||
# ExtruderStack.setNextStack calls registerExtruder for backward compatibility, but we do not need a complete extruder manager
|
||||
ExtruderManager._ExtruderManager__instance = unittest.mock.MagicMock()
|
||||
|
||||
|
@ -300,8 +258,7 @@ def test_getPropertyFallThrough(extruder_stack):
|
|||
with unittest.mock.patch("cura.Settings.CuraContainerStack.DefinitionContainer", unittest.mock.MagicMock): #To guard against the type checking.
|
||||
extruder_stack.definition = mock_layer_heights[container_indices.Definition] #There's a layer height in here!
|
||||
|
||||
stack = GlobalStack("PyTest GlobalStack")
|
||||
extruder_stack.setNextStack(stack)
|
||||
extruder_stack.setNextStack(global_stack)
|
||||
|
||||
assert extruder_stack.getProperty("layer_height", "value") == container_indices.Definition
|
||||
extruder_stack.variant = mock_layer_heights[container_indices.Variant]
|
||||
|
@ -340,4 +297,4 @@ def test_setPropertyUser(key, property, value, extruder_stack):
|
|||
|
||||
extruder_stack.setProperty(key, property, value) #The actual test.
|
||||
|
||||
extruder_stack.userChanges.setProperty.assert_called_once_with(key, property, value) #Make sure that the user container gets a setProperty call.
|
||||
extruder_stack.userChanges.setProperty.assert_called_once_with(key, property, value, None, False) #Make sure that the user container gets a setProperty call.
|
|
@ -1,43 +1,19 @@
|
|||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import pytest #This module contains unit tests.
|
||||
import unittest.mock #To monkeypatch some mocks in place of dependencies.
|
||||
import copy
|
||||
|
||||
import cura.CuraApplication
|
||||
import cura.Settings.GlobalStack #The module we're testing.
|
||||
import cura.Settings.CuraContainerStack #To get the list of container types.
|
||||
from cura.Settings.Exceptions import TooManyExtrudersError, InvalidContainerError, InvalidOperationError #To test raising these errors.
|
||||
from cura.Settings.Exceptions import InvalidContainerError, InvalidOperationError #To test raising these errors.
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer #To test against the class DefinitionContainer.
|
||||
from UM.Settings.InstanceContainer import InstanceContainer #To test against the class InstanceContainer.
|
||||
from UM.Settings.SettingInstance import InstanceState
|
||||
import UM.Settings.ContainerRegistry
|
||||
import UM.Settings.ContainerStack
|
||||
import UM.Settings.SettingDefinition #To add settings to the definition.
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
|
||||
## Fake container registry that always provides all containers you ask of.
|
||||
@pytest.yield_fixture()
|
||||
def container_registry():
|
||||
registry = unittest.mock.MagicMock()
|
||||
registry.return_value = unittest.mock.NonCallableMagicMock()
|
||||
registry.findInstanceContainers = lambda *args, registry = registry, **kwargs: [registry.return_value]
|
||||
registry.findDefinitionContainers = lambda *args, registry = registry, **kwargs: [registry.return_value]
|
||||
|
||||
UM.Settings.ContainerRegistry.ContainerRegistry._ContainerRegistry__instance = registry
|
||||
UM.Settings.ContainerStack._containerRegistry = registry
|
||||
|
||||
yield registry
|
||||
|
||||
UM.Settings.ContainerRegistry.ContainerRegistry._ContainerRegistry__instance = None
|
||||
UM.Settings.ContainerStack._containerRegistry = None
|
||||
|
||||
#An empty global stack to test with.
|
||||
@pytest.fixture()
|
||||
def global_stack() -> cura.Settings.GlobalStack.GlobalStack:
|
||||
creteEmptyContainers()
|
||||
return cura.Settings.GlobalStack.GlobalStack("TestStack")
|
||||
from cura.Settings.cura_empty_instance_containers import empty_container
|
||||
|
||||
## Gets an instance container with a specified container type.
|
||||
#
|
||||
|
@ -48,31 +24,6 @@ def getInstanceContainer(container_type) -> InstanceContainer:
|
|||
container.setMetaDataEntry("type", container_type)
|
||||
return container
|
||||
|
||||
def creteEmptyContainers():
|
||||
empty_container = ContainerRegistry.getInstance().getEmptyInstanceContainer()
|
||||
empty_variant_container = copy.deepcopy(empty_container)
|
||||
empty_variant_container.setMetaDataEntry("id", "empty_variant")
|
||||
empty_variant_container.setMetaDataEntry("type", "variant")
|
||||
ContainerRegistry.getInstance().addContainer(empty_variant_container)
|
||||
|
||||
empty_material_container = copy.deepcopy(empty_container)
|
||||
empty_material_container.setMetaDataEntry("id", "empty_material")
|
||||
empty_material_container.setMetaDataEntry("type", "material")
|
||||
ContainerRegistry.getInstance().addContainer(empty_material_container)
|
||||
|
||||
empty_quality_container = copy.deepcopy(empty_container)
|
||||
empty_quality_container.setMetaDataEntry("id", "empty_quality")
|
||||
empty_quality_container.setName("Not Supported")
|
||||
empty_quality_container.setMetaDataEntry("quality_type", "not_supported")
|
||||
empty_quality_container.setMetaDataEntry("type", "quality")
|
||||
empty_quality_container.setMetaDataEntry("supported", False)
|
||||
ContainerRegistry.getInstance().addContainer(empty_quality_container)
|
||||
|
||||
empty_quality_changes_container = copy.deepcopy(empty_container)
|
||||
empty_quality_changes_container.setMetaDataEntry("id", "empty_quality_changes")
|
||||
empty_quality_changes_container.setMetaDataEntry("type", "quality_changes")
|
||||
ContainerRegistry.getInstance().addContainer(empty_quality_changes_container)
|
||||
|
||||
class DefinitionContainerSubClass(DefinitionContainer):
|
||||
def __init__(self):
|
||||
super().__init__(container_id = "SubDefinitionContainer")
|
||||
|
@ -229,7 +180,7 @@ def test_constrainDefinitionChangesValid(container, global_stack):
|
|||
getInstanceContainer(container_type = "wrong class"),
|
||||
getInstanceContainer(container_type = "material"), #Existing, but still wrong class.
|
||||
])
|
||||
def test_constrainVariantInvalid(container, global_stack):
|
||||
def test_constrainDefinitionInvalid(container, global_stack):
|
||||
with pytest.raises(InvalidContainerError): #Invalid container, should raise an error.
|
||||
global_stack.definition = container
|
||||
|
||||
|
@ -241,23 +192,22 @@ def test_constrainVariantInvalid(container, global_stack):
|
|||
def test_constrainDefinitionValid(container, global_stack):
|
||||
global_stack.definition = container #Should not give an error.
|
||||
|
||||
## Tests whether deserialising completes the missing containers with empty
|
||||
# ones.
|
||||
@pytest.mark.skip #The test currently fails because the definition container doesn't have a category, which is wrong but we don't have time to refactor that right now.
|
||||
def test_deserializeCompletesEmptyContainers(global_stack: cura.Settings.GlobalStack):
|
||||
global_stack._containers = [DefinitionContainer(container_id = "definition")] #Set the internal state of this stack manually.
|
||||
## Tests whether deserialising completes the missing containers with empty ones. The initial containers are just the
|
||||
# definition and the definition_changes (that cannot be empty after CURA-5281)
|
||||
def test_deserializeCompletesEmptyContainers(global_stack):
|
||||
global_stack._containers = [DefinitionContainer(container_id = "definition"), global_stack.definitionChanges] #Set the internal state of this stack manually.
|
||||
|
||||
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
|
||||
global_stack.deserialize("")
|
||||
|
||||
assert len(global_stack.getContainers()) == len(cura.Settings.CuraContainerStack._ContainerIndexes.IndexTypeMap) #Needs a slot for every type.
|
||||
for container_type_index in cura.Settings.CuraContainerStack._ContainerIndexes.IndexTypeMap:
|
||||
if container_type_index == cura.Settings.CuraContainerStack._ContainerIndexes.Definition: #We're not checking the definition.
|
||||
if container_type_index in \
|
||||
(cura.Settings.CuraContainerStack._ContainerIndexes.Definition, cura.Settings.CuraContainerStack._ContainerIndexes.DefinitionChanges): #We're not checking the definition or definition_changes
|
||||
continue
|
||||
assert global_stack.getContainer(container_type_index).getId() == "empty" #All others need to be empty.
|
||||
assert global_stack.getContainer(container_type_index) == empty_container #All others need to be empty.
|
||||
|
||||
## Tests whether an instance container with the wrong type gets removed when
|
||||
# deserialising.
|
||||
## Tests whether an instance container with the wrong type gets removed when deserialising.
|
||||
def test_deserializeRemovesWrongInstanceContainer(global_stack):
|
||||
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = getInstanceContainer(container_type = "wrong type")
|
||||
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition")
|
||||
|
@ -267,8 +217,7 @@ def test_deserializeRemovesWrongInstanceContainer(global_stack):
|
|||
|
||||
assert global_stack.quality == global_stack._empty_instance_container #Replaced with empty.
|
||||
|
||||
## Tests whether a container with the wrong class gets removed when
|
||||
# deserialising.
|
||||
## Tests whether a container with the wrong class gets removed when deserialising.
|
||||
def test_deserializeRemovesWrongContainerClass(global_stack):
|
||||
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = DefinitionContainer(container_id = "wrong class")
|
||||
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition")
|
||||
|
@ -278,8 +227,7 @@ def test_deserializeRemovesWrongContainerClass(global_stack):
|
|||
|
||||
assert global_stack.quality == global_stack._empty_instance_container #Replaced with empty.
|
||||
|
||||
## Tests whether an instance container in the definition spot results in an
|
||||
# error.
|
||||
## Tests whether an instance container in the definition spot results in an error.
|
||||
def test_deserializeWrongDefinitionClass(global_stack):
|
||||
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = getInstanceContainer(container_type = "definition") #Correct type but wrong class.
|
||||
|
||||
|
@ -287,8 +235,7 @@ def test_deserializeWrongDefinitionClass(global_stack):
|
|||
with pytest.raises(UM.Settings.ContainerStack.InvalidContainerStackError): #Must raise an error that there is no definition container.
|
||||
global_stack.deserialize("")
|
||||
|
||||
## Tests whether an instance container with the wrong type is moved into the
|
||||
# correct slot by deserialising.
|
||||
## Tests whether an instance container with the wrong type is moved into the correct slot by deserialising.
|
||||
def test_deserializeMoveInstanceContainer(global_stack):
|
||||
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = getInstanceContainer(container_type = "material") #Not in the correct spot.
|
||||
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = DefinitionContainer(container_id = "some definition")
|
||||
|
@ -296,25 +243,20 @@ def test_deserializeMoveInstanceContainer(global_stack):
|
|||
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
|
||||
global_stack.deserialize("")
|
||||
|
||||
assert global_stack.quality.getId() == "empty"
|
||||
assert global_stack.material.getId() != "empty"
|
||||
assert global_stack.quality == empty_container
|
||||
assert global_stack.material != empty_container
|
||||
|
||||
## Tests whether a definition container in the wrong spot is moved into the
|
||||
# correct spot by deserialising.
|
||||
@pytest.mark.skip #The test currently fails because the definition container doesn't have a category, which is wrong but we don't have time to refactor that right now.
|
||||
## Tests whether a definition container in the wrong spot is moved into the correct spot by deserialising.
|
||||
def test_deserializeMoveDefinitionContainer(global_stack):
|
||||
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Material] = DefinitionContainer(container_id = "some definition") #Not in the correct spot.
|
||||
|
||||
with unittest.mock.patch("UM.Settings.ContainerStack.ContainerStack.deserialize", unittest.mock.MagicMock()): #Prevent calling super().deserialize.
|
||||
global_stack.deserialize("")
|
||||
|
||||
assert global_stack.material.getId() == "empty"
|
||||
assert global_stack.definition.getId() != "empty"
|
||||
assert global_stack.material == empty_container
|
||||
assert global_stack.definition != empty_container
|
||||
|
||||
UM.Settings.ContainerStack._containerRegistry = None
|
||||
|
||||
## Tests whether getProperty properly applies the stack-like behaviour on its
|
||||
# containers.
|
||||
## Tests whether getProperty properly applies the stack-like behaviour on its containers.
|
||||
def test_getPropertyFallThrough(global_stack):
|
||||
#A few instance container mocks to put in the stack.
|
||||
mock_layer_heights = {} #For each container type, a mock container that defines layer height to something unique.
|
||||
|
@ -365,8 +307,7 @@ def test_getPropertyNoResolveInDefinition(global_stack):
|
|||
global_stack.definition = value
|
||||
assert global_stack.getProperty("material_bed_temperature", "value") == 10 #No resolve, so fall through to value.
|
||||
|
||||
## In definitions, when the value is asked and there is a resolve function, it
|
||||
# must get the resolve first.
|
||||
## In definitions, when the value is asked and there is a resolve function, it must get the resolve first.
|
||||
def test_getPropertyResolveInDefinition(global_stack):
|
||||
resolve_and_value = unittest.mock.MagicMock() #Sets the resolve and value for bed temperature.
|
||||
resolve_and_value.getProperty = lambda key, property, context = None: (7.5 if property == "resolve" else 5) if (key == "material_bed_temperature" and property in ("resolve", "value")) else None #7.5 resolve, 5 value.
|
||||
|
@ -375,8 +316,7 @@ def test_getPropertyResolveInDefinition(global_stack):
|
|||
global_stack.definition = resolve_and_value
|
||||
assert global_stack.getProperty("material_bed_temperature", "value") == 7.5 #Resolve wins in the definition.
|
||||
|
||||
## In instance containers, when the value is asked and there is a resolve
|
||||
# function, it must get the value first.
|
||||
## In instance containers, when the value is asked and there is a resolve function, it must get the value first.
|
||||
def test_getPropertyResolveInInstance(global_stack):
|
||||
container_indices = cura.Settings.CuraContainerStack._ContainerIndexes
|
||||
instance_containers = {}
|
||||
|
@ -402,8 +342,7 @@ def test_getPropertyResolveInInstance(global_stack):
|
|||
global_stack.userChanges = instance_containers[container_indices.UserChanges]
|
||||
assert global_stack.getProperty("material_bed_temperature", "value") == 5
|
||||
|
||||
## Tests whether the value in instances gets evaluated before the resolve in
|
||||
# definitions.
|
||||
## Tests whether the value in instances gets evaluated before the resolve in definitions.
|
||||
def test_getPropertyInstancesBeforeResolve(global_stack):
|
||||
value = unittest.mock.MagicMock() #Sets just the value.
|
||||
value.getProperty = lambda key, property, context = None: (10 if property == "value" else (InstanceState.User if property != "limit_to_extruder" else "-1")) if key == "material_bed_temperature" else None
|
||||
|
@ -417,8 +356,7 @@ def test_getPropertyInstancesBeforeResolve(global_stack):
|
|||
|
||||
assert global_stack.getProperty("material_bed_temperature", "value") == 10
|
||||
|
||||
## Tests whether the hasUserValue returns true for settings that are changed in
|
||||
# the user-changes container.
|
||||
## Tests whether the hasUserValue returns true for settings that are changed in the user-changes container.
|
||||
def test_hasUserValueUserChanges(global_stack):
|
||||
container = unittest.mock.MagicMock()
|
||||
container.getMetaDataEntry = unittest.mock.MagicMock(return_value = "user")
|
||||
|
@ -429,8 +367,7 @@ def test_hasUserValueUserChanges(global_stack):
|
|||
assert not global_stack.hasUserValue("infill_sparse_density")
|
||||
assert not global_stack.hasUserValue("")
|
||||
|
||||
## Tests whether the hasUserValue returns true for settings that are changed in
|
||||
# the quality-changes container.
|
||||
## Tests whether the hasUserValue returns true for settings that are changed in the quality-changes container.
|
||||
def test_hasUserValueQualityChanges(global_stack):
|
||||
container = unittest.mock.MagicMock()
|
||||
container.getMetaDataEntry = unittest.mock.MagicMock(return_value = "quality_changes")
|
||||
|
@ -441,8 +378,7 @@ def test_hasUserValueQualityChanges(global_stack):
|
|||
assert not global_stack.hasUserValue("infill_sparse_density")
|
||||
assert not global_stack.hasUserValue("")
|
||||
|
||||
## Tests whether a container in some other place on the stack is correctly not
|
||||
# recognised as user value.
|
||||
## Tests whether a container in some other place on the stack is correctly not recognised as user value.
|
||||
def test_hasNoUserValue(global_stack):
|
||||
container = unittest.mock.MagicMock()
|
||||
container.getMetaDataEntry = unittest.mock.MagicMock(return_value = "quality")
|
||||
|
@ -481,4 +417,4 @@ def test_setPropertyUser(key, property, value, global_stack):
|
|||
|
||||
global_stack.setProperty(key, property, value) #The actual test.
|
||||
|
||||
global_stack.userChanges.setProperty.assert_called_once_with(key, property, value) #Make sure that the user container gets a setProperty call.
|
||||
global_stack.userChanges.setProperty.assert_called_once_with(key, property, value, None, False) #Make sure that the user container gets a setProperty call.
|
54
tests/Settings/conftest.py
Normal file
54
tests/Settings/conftest.py
Normal file
|
@ -0,0 +1,54 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Uranium is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
# The purpose of this class is to create fixtures or methods that can be shared among all settings tests.
|
||||
|
||||
import pytest
|
||||
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.Settings.ContainerStack import setContainerRegistry
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer #To provide definition containers in the registry fixtures.
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry
|
||||
from cura.Settings.ExtruderStack import ExtruderStack
|
||||
from cura.Settings.GlobalStack import GlobalStack
|
||||
import cura.Settings.CuraContainerStack
|
||||
|
||||
# Returns the CuraContainerRegistry instance with some empty containers.
|
||||
@pytest.fixture()
|
||||
def container_registry(application) -> CuraContainerRegistry:
|
||||
ContainerRegistry._ContainerRegistry__instance= None # Need to reset since we only allow one instance
|
||||
registry = CuraContainerRegistry(application)
|
||||
setContainerRegistry(registry)
|
||||
return registry
|
||||
|
||||
# Gives an arbitrary definition container.
|
||||
@pytest.fixture()
|
||||
def definition_container() -> DefinitionContainer:
|
||||
return DefinitionContainer(container_id = "Test Definition")
|
||||
|
||||
# Gives an arbitrary definition changes container.
|
||||
@pytest.fixture()
|
||||
def definition_changes_container() -> InstanceContainer:
|
||||
definition_changes_container = InstanceContainer(container_id = "Test Definition Changes")
|
||||
definition_changes_container.setMetaDataEntry("type", "definition_changes")
|
||||
# Add current setting version to the instance container
|
||||
from cura.CuraApplication import CuraApplication
|
||||
definition_changes_container.getMetaData()["setting_version"] = CuraApplication.SettingVersion
|
||||
return definition_changes_container
|
||||
|
||||
# An empty global stack to test with.
|
||||
# There is a restriction here that the definition changes cannot be an empty container. Added in CURA-5281
|
||||
@pytest.fixture()
|
||||
def global_stack(definition_changes_container) -> GlobalStack:
|
||||
global_stack = GlobalStack("TestGlobalStack")
|
||||
global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.DefinitionChanges] = definition_changes_container
|
||||
return global_stack
|
||||
|
||||
# An empty extruder stack to test with.
|
||||
# There is a restriction here that the definition changes cannot be an empty container. Added in CURA-5281
|
||||
@pytest.fixture()
|
||||
def extruder_stack(definition_changes_container) -> ExtruderStack:
|
||||
extruder_stack= ExtruderStack("TestExtruderStack")
|
||||
extruder_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.DefinitionChanges] = definition_changes_container
|
||||
return extruder_stack
|
|
@ -1,12 +0,0 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Complete
|
||||
id = Complete
|
||||
|
||||
[containers]
|
||||
0 = some_user_changes
|
||||
1 = some_quality_changes
|
||||
2 = some_quality
|
||||
3 = some_material
|
||||
4 = some_variant
|
||||
5 = some_definition
|
|
@ -1,13 +0,0 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Complete
|
||||
id = Complete
|
||||
|
||||
[containers]
|
||||
0 = some_user_changes
|
||||
1 = some_quality_changes
|
||||
2 = some_quality
|
||||
3 = some_material
|
||||
4 = some_variant
|
||||
5 = some_definition_changes
|
||||
6 = some_definition
|
|
@ -1,11 +0,0 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Legacy Extruder Stack
|
||||
id = ExtruderLegacy
|
||||
|
||||
[metadata]
|
||||
type = extruder_train
|
||||
|
||||
[containers]
|
||||
3 = some_instance
|
||||
5 = some_definition
|
|
@ -1,8 +0,0 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Global
|
||||
id = Global
|
||||
|
||||
[containers]
|
||||
3 = some_instance
|
||||
6 = some_definition
|
|
@ -1,11 +0,0 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Global
|
||||
id = Global
|
||||
|
||||
[metadata]
|
||||
type = machine
|
||||
|
||||
[containers]
|
||||
3 = some_instance
|
||||
6 = some_definition
|
|
@ -1,8 +0,0 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Left
|
||||
id = Left
|
||||
|
||||
[containers]
|
||||
3 = some_instance
|
||||
5 = some_definition
|
|
@ -1,11 +0,0 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Legacy Global Stack
|
||||
id = MachineLegacy
|
||||
|
||||
[metadata]
|
||||
type = machine
|
||||
|
||||
[containers]
|
||||
3 = some_instance
|
||||
6 = some_definition
|
|
@ -1,7 +0,0 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Only Definition
|
||||
id = OnlyDefinition
|
||||
|
||||
[containers]
|
||||
5 = some_definition
|
|
@ -1,7 +0,0 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Only Definition
|
||||
id = OnlyDefinition
|
||||
|
||||
[containers]
|
||||
6 = some_definition
|
|
@ -1,8 +0,0 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Only Definition Changes
|
||||
id = OnlyDefinitionChanges
|
||||
|
||||
[containers]
|
||||
5 = some_instance
|
||||
6 = some_definition
|
|
@ -1,8 +0,0 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Only Material
|
||||
id = OnlyMaterial
|
||||
|
||||
[containers]
|
||||
3 = some_instance
|
||||
5 = some_definition
|
|
@ -1,8 +0,0 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Only Material
|
||||
id = OnlyMaterial
|
||||
|
||||
[containers]
|
||||
3 = some_instance
|
||||
6 = some_definition
|
|
@ -1,8 +0,0 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Only Quality
|
||||
id = OnlyQuality
|
||||
|
||||
[containers]
|
||||
2 = some_instance
|
||||
5 = some_definition
|
|
@ -1,8 +0,0 @@
|
|||
[general]
|
||||
version = 3
|
||||
name = Only Quality
|
||||
id = OnlyQuality
|
||||
|
||||
[containers]
|
||||
2 = some_instance
|
||||
6 = some_definition
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue