Merge branch 'master' into feature_firmware_updater

This commit is contained in:
fieldOfView 2018-08-22 12:16:25 +02:00
commit c1d7ca7bff
127 changed files with 2140 additions and 1067 deletions

View file

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

View 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()

View 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()

View file

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

View file

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

View file

@ -4,15 +4,20 @@ from PyQt5.QtCore import QSize
from UM.Application import Application
class CameraImageProvider(QQuickImageProvider):
def __init__(self):
QQuickImageProvider.__init__(self, QQuickImageProvider.Image)
super().__init__(QQuickImageProvider.Image)
## Request a new image.
def requestImage(self, id, size):
for output_device in Application.getInstance().getOutputDeviceManager().getOutputDevices():
try:
return output_device.activePrinter.camera.getImage(), QSize(15, 15)
image = output_device.activePrinter.camera.getImage()
if image.isNull():
image = QImage()
return image, QSize(15, 15)
except AttributeError:
pass
return QImage(), QSize(15, 15)

View file

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

View file

@ -1,11 +1,10 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import copy
import os
import sys
import time
from typing import cast, TYPE_CHECKING, Optional
from typing import cast, TYPE_CHECKING
import numpy
@ -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):
@ -226,6 +228,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 +368,23 @@ class CuraApplication(QtApplication):
# Add empty variant, material and quality containers.
# Since they are empty, they should never be serialized and instead just programmatically created.
# We need them to simplify the switching between materials.
empty_container = self._container_registry.getEmptyInstanceContainer()
self.empty_container = empty_container
self.empty_container = cura.Settings.cura_empty_instance_containers.empty_container
empty_definition_changes_container = copy.deepcopy(empty_container)
empty_definition_changes_container.setMetaDataEntry("id", "empty_definition_changes")
empty_definition_changes_container.setMetaDataEntry("type", "definition_changes")
self._container_registry.addContainer(empty_definition_changes_container)
self.empty_definition_changes_container = empty_definition_changes_container
self._container_registry.addContainer(
cura.Settings.cura_empty_instance_containers.empty_definition_changes_container)
self.empty_definition_changes_container = cura.Settings.cura_empty_instance_containers.empty_definition_changes_container
empty_variant_container = copy.deepcopy(empty_container)
empty_variant_container.setMetaDataEntry("id", "empty_variant")
empty_variant_container.setMetaDataEntry("type", "variant")
self._container_registry.addContainer(empty_variant_container)
self.empty_variant_container = empty_variant_container
self._container_registry.addContainer(cura.Settings.cura_empty_instance_containers.empty_variant_container)
self.empty_variant_container = cura.Settings.cura_empty_instance_containers.empty_variant_container
empty_material_container = copy.deepcopy(empty_container)
empty_material_container.setMetaDataEntry("id", "empty_material")
empty_material_container.setMetaDataEntry("type", "material")
self._container_registry.addContainer(empty_material_container)
self.empty_material_container = empty_material_container
self._container_registry.addContainer(cura.Settings.cura_empty_instance_containers.empty_material_container)
self.empty_material_container = cura.Settings.cura_empty_instance_containers.empty_material_container
empty_quality_container = copy.deepcopy(empty_container)
empty_quality_container.setMetaDataEntry("id", "empty_quality")
empty_quality_container.setName("Not Supported")
empty_quality_container.setMetaDataEntry("quality_type", "not_supported")
empty_quality_container.setMetaDataEntry("type", "quality")
empty_quality_container.setMetaDataEntry("supported", False)
self._container_registry.addContainer(empty_quality_container)
self.empty_quality_container = empty_quality_container
self._container_registry.addContainer(cura.Settings.cura_empty_instance_containers.empty_quality_container)
self.empty_quality_container = cura.Settings.cura_empty_instance_containers.empty_quality_container
empty_quality_changes_container = copy.deepcopy(empty_container)
empty_quality_changes_container.setMetaDataEntry("id", "empty_quality_changes")
empty_quality_changes_container.setMetaDataEntry("type", "quality_changes")
empty_quality_changes_container.setMetaDataEntry("quality_type", "not_supported")
self._container_registry.addContainer(empty_quality_changes_container)
self.empty_quality_changes_container = empty_quality_changes_container
self._container_registry.addContainer(cura.Settings.cura_empty_instance_containers.empty_quality_changes_container)
self.empty_quality_changes_container = cura.Settings.cura_empty_instance_containers.empty_quality_changes_container
# Initializes the version upgrade manager with by providing the paths for each resource type and the latest
# versions.
@ -625,7 +612,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 +761,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 +898,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 +933,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")))
@ -1729,3 +1721,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

View file

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

View file

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

View file

@ -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([])

View file

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

View file

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

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

View file

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

View file

@ -267,6 +267,7 @@ class PrintInformation(QObject):
new_active_build_plate = self._multi_build_plate_model.activeBuildPlate
if new_active_build_plate != self._active_build_plate:
self._active_build_plate = new_active_build_plate
self._updateJobName()
self._initVariablesWithBuildPlate(self._active_build_plate)
@ -320,6 +321,15 @@ class PrintInformation(QObject):
else:
self._job_name = base_name
# In case there are several buildplates, a suffix is attached
if self._multi_build_plate_model.maxBuildPlate > 0:
connector = "_#"
suffix = connector + str(self._active_build_plate + 1)
if connector in self._job_name:
self._job_name = self._job_name.split(connector)[0] # get the real name
if self._active_build_plate != 0:
self._job_name += suffix
self.jobNameChanged.emit()
@pyqtSlot(str)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -55,6 +55,12 @@ 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
## Add an extruder to the list of extruders of this stack.
#
# \param extruder The extruder to add.
@ -96,6 +102,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 +181,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(

View file

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

View 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

View file

@ -0,0 +1,56 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import copy
from UM.Settings.constant_instance_containers import EMPTY_CONTAINER_ID, empty_container
# Empty definition changes
EMPTY_DEFINITION_CHANGES_CONTAINER_ID = "empty_definition_changes"
empty_definition_changes_container = copy.deepcopy(empty_container)
empty_definition_changes_container.setMetaDataEntry("id", EMPTY_DEFINITION_CHANGES_CONTAINER_ID)
empty_definition_changes_container.setMetaDataEntry("type", "definition_changes")
# Empty variant
EMPTY_VARIANT_CONTAINER_ID = "empty_variant"
empty_variant_container = copy.deepcopy(empty_container)
empty_variant_container.setMetaDataEntry("id", EMPTY_VARIANT_CONTAINER_ID)
empty_variant_container.setMetaDataEntry("type", "variant")
# Empty material
EMPTY_MATERIAL_CONTAINER_ID = "empty_material"
empty_material_container = copy.deepcopy(empty_container)
empty_material_container.setMetaDataEntry("id", EMPTY_MATERIAL_CONTAINER_ID)
empty_material_container.setMetaDataEntry("type", "material")
# Empty quality
EMPTY_QUALITY_CONTAINER_ID = "empty_quality"
empty_quality_container = copy.deepcopy(empty_container)
empty_quality_container.setMetaDataEntry("id", EMPTY_QUALITY_CONTAINER_ID)
empty_quality_container.setName("Not Supported")
empty_quality_container.setMetaDataEntry("quality_type", "not_supported")
empty_quality_container.setMetaDataEntry("type", "quality")
empty_quality_container.setMetaDataEntry("supported", False)
# Empty quality changes
EMPTY_QUALITY_CHANGES_CONTAINER_ID = "empty_quality_changes"
empty_quality_changes_container = copy.deepcopy(empty_container)
empty_quality_changes_container.setMetaDataEntry("id", EMPTY_QUALITY_CHANGES_CONTAINER_ID)
empty_quality_changes_container.setMetaDataEntry("type", "quality_changes")
empty_quality_changes_container.setMetaDataEntry("quality_type", "not_supported")
__all__ = ["EMPTY_CONTAINER_ID",
"empty_container", # For convenience
"EMPTY_DEFINITION_CHANGES_CONTAINER_ID",
"empty_definition_changes_container",
"EMPTY_VARIANT_CONTAINER_ID",
"empty_variant_container",
"EMPTY_MATERIAL_CONTAINER_ID",
"empty_material_container",
"EMPTY_QUALITY_CHANGES_CONTAINER_ID",
"empty_quality_changes_container",
"EMPTY_QUALITY_CONTAINER_ID",
"empty_quality_container"
]

View file

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

View file

@ -25,6 +25,9 @@ except ImportError:
import zipfile
import UM.Application
from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")
class ThreeMFWriter(MeshWriter):
def __init__(self):
@ -173,6 +176,7 @@ class ThreeMFWriter(MeshWriter):
archive.writestr(relations_file, b'<?xml version="1.0" encoding="UTF-8"?> \n' + ET.tostring(relations_element))
except Exception as e:
Logger.logException("e", "Error writing zip file")
self.setInformation(catalog.i18nc("@error:zip", "Error writing 3mf file."))
return False
finally:
if not self._store_archive:

View file

@ -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
@ -179,7 +180,15 @@ class CuraEngineBackend(QObject, Backend):
# \return list of commands and args / parameters.
def getEngineCommand(self) -> List[str]:
json_path = Resources.getPath(Resources.DefinitionContainers, "fdmprinter.def.json")
return [self._application.getPreferences().getValue("backend/location"), "connect", "127.0.0.1:{0}".format(self._port), "-j", json_path, ""]
command = [self._application.getPreferences().getValue("backend/location"), "connect", "127.0.0.1:{0}".format(self._port), "-j", json_path, ""]
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.
@ -541,6 +550,9 @@ class CuraEngineBackend(QObject, Backend):
## Remove old layer data (if any)
def _clearLayerData(self, build_plate_numbers: Set = None) -> None:
# Clear out any old gcode
self._scene.gcode_dict = {} # type: ignore
for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
if node.callDecoration("getLayerData"):
if not build_plate_numbers or node.callDecoration("getBuildPlateNumber") in build_plate_numbers:

View file

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

View file

@ -10,6 +10,9 @@ from UM.Mesh.MeshWriter import MeshWriter #The class we're extending/implementin
from UM.PluginRegistry import PluginRegistry
from UM.Scene.SceneNode import SceneNode #For typing.
from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")
## A file writer that writes gzipped g-code.
#
# If you're zipping g-code, you might as well use gzip!
@ -28,12 +31,15 @@ class GCodeGzWriter(MeshWriter):
def write(self, stream: BufferedIOBase, nodes: List[SceneNode], mode = MeshWriter.OutputMode.BinaryMode) -> bool:
if mode != MeshWriter.OutputMode.BinaryMode:
Logger.log("e", "GCodeGzWriter does not support text mode.")
self.setInformation(catalog.i18nc("@error:not supported", "GCodeGzWriter does not support text mode."))
return False
#Get the g-code from the g-code writer.
gcode_textio = StringIO() #We have to convert the g-code into bytes.
success = cast(MeshWriter, PluginRegistry.getInstance().getPluginObject("GCodeWriter")).write(gcode_textio, None)
gcode_writer = cast(MeshWriter, PluginRegistry.getInstance().getPluginObject("GCodeWriter"))
success = gcode_writer.write(gcode_textio, None)
if not success: #Writing the g-code failed. Then I can also not write the gzipped g-code.
self.setInformation(gcode_writer.getInformation())
return False
result = gzip.compress(gcode_textio.getvalue().encode("utf-8"))

View file

@ -12,6 +12,8 @@ from UM.Settings.InstanceContainer import InstanceContainer
from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch
from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")
## Writes g-code to a file.
#
@ -62,11 +64,13 @@ class GCodeWriter(MeshWriter):
def write(self, stream, nodes, mode = MeshWriter.OutputMode.TextMode):
if mode != MeshWriter.OutputMode.TextMode:
Logger.log("e", "GCodeWriter does not support non-text mode.")
self.setInformation(catalog.i18nc("@error:not supported", "GCodeWriter does not support non-text mode."))
return False
active_build_plate = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate
scene = Application.getInstance().getController().getScene()
if not hasattr(scene, "gcode_dict"):
self.setInformation(catalog.i18nc("@warning:status", "Please generate G-code before saving."))
return False
gcode_dict = getattr(scene, "gcode_dict")
gcode_list = gcode_dict.get(active_build_plate, None)
@ -82,6 +86,7 @@ class GCodeWriter(MeshWriter):
stream.write(settings)
return True
self.setInformation(catalog.i18nc("@warning:status", "Please generate G-code before saving."))
return False
## Create a new container with container 2 as base and container 1 written over it.

View file

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

View file

@ -62,13 +62,12 @@ class GCodeStep():
delta_step_y = _getValue(line, "Y", 0)
delta_step_z = _getValue(line, "Z", 0)
delta_step_e = _getValue(line, "E", 0)
delta_step_f = _getValue(line, "F", 0)
self.step_x += delta_step_x
self.step_y += delta_step_y
self.step_z += delta_step_z
self.step_e += delta_step_e
self.step_f += delta_step_f
self.step_f = _getValue(line, "F", self.step_f) # the feedrate is not relative
def copyPosFrom(self, step):
"""

View file

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

View file

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

View file

@ -25,10 +25,6 @@ UM.PointingRectangle {
width: valueLabel.width + UM.Theme.getSize("default_margin").width
visible: false
// make sure the text field is focussed when pressing the parent handle
// needed to connect the key bindings when switching active handle
onVisibleChanged: if (visible) valueLabel.forceActiveFocus()
color: UM.Theme.getColor("tool_panel_background")
borderColor: UM.Theme.getColor("lining")
borderWidth: UM.Theme.getSize("default_lining").width

View file

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

View file

@ -29,6 +29,16 @@ Item
anchors.topMargin: UM.Theme.getSize("default_margin").height
width: parent.width
frameVisible: false
// Workaround for scroll issues (QTBUG-49652)
flickableItem.interactive: false
Component.onCompleted:
{
for (var i = 0; i < flickableItem.children.length; ++i)
{
flickableItem.children[i].enabled = false
}
}
selectionMode: 0
model: packageData.supported_configs
headerDelegate: Rectangle

View file

@ -114,7 +114,10 @@ Item
else
{
toolbox.viewPage = "author"
toolbox.filterModelByProp("packages", "author_id", model.id)
toolbox.setFilters("packages", {
"author_id": model.id,
"type": "material"
})
}
break
default:

View file

@ -105,8 +105,21 @@ Rectangle
switch(toolbox.viewCategory)
{
case "material":
// If model has a type, it must be a package
if (model.type !== undefined)
{
toolbox.viewPage = "detail"
toolbox.filterModelByProp("packages", "id", model.id)
}
else
{
toolbox.viewPage = "author"
toolbox.filterModelByProp("packages", "author_name", model.name)
toolbox.setFilters("packages", {
"author_id": model.id,
"type": "material"
})
}
break
default:
toolbox.viewPage = "detail"

View file

@ -776,17 +776,25 @@ class Toolbox(QObject, Extension):
# Filter Models:
# --------------------------------------------------------------------------
@pyqtSlot(str, str, str)
def filterModelByProp(self, modelType: str, filterType: str, parameter: str):
if not self._models[modelType]:
Logger.log("w", "Toolbox: Couldn't filter %s model because it doesn't exist.", modelType)
def filterModelByProp(self, model_type: str, filter_type: str, parameter: str) -> None:
if not self._models[model_type]:
Logger.log("w", "Toolbox: Couldn't filter %s model because it doesn't exist.", model_type)
return
self._models[modelType].setFilter({ filterType: parameter })
self._models[model_type].setFilter({filter_type: parameter})
self.filterChanged.emit()
@pyqtSlot()
def removeFilters(self, modelType: str):
if not self._models[modelType]:
Logger.log("w", "Toolbox: Couldn't remove filters on %s model because it doesn't exist.", modelType)
@pyqtSlot(str, "QVariantMap")
def setFilters(self, model_type: str, filter_dict: dict) -> None:
if not self._models[model_type]:
Logger.log("w", "Toolbox: Couldn't filter %s model because it doesn't exist.", model_type)
return
self._models[modelType].setFilter({})
self._models[model_type].setFilter(filter_dict)
self.filterChanged.emit()
@pyqtSlot(str)
def removeFilters(self, model_type: str) -> None:
if not self._models[model_type]:
Logger.log("w", "Toolbox: Couldn't remove filters on %s model because it doesn't exist.", model_type)
return
self._models[model_type].setFilter({})
self.filterChanged.emit()

View file

@ -1,5 +1,6 @@
#Copyright (c) 2018 Ultimaker B.V.
#Cura is released under the terms of the LGPLv3 or higher.
from typing import cast
from Charon.VirtualFile import VirtualFile #To open UFP files.
from Charon.OpenMode import OpenMode #To indicate that we want to write to UFP files.
@ -13,6 +14,9 @@ from PyQt5.QtCore import QBuffer
from cura.Snapshot import Snapshot
from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")
class UFPWriter(MeshWriter):
def __init__(self):
@ -32,7 +36,11 @@ class UFPWriter(MeshWriter):
#Store the g-code from the scene.
archive.addContentType(extension = "gcode", mime_type = "text/x-gcode")
gcode_textio = StringIO() #We have to convert the g-code into bytes.
PluginRegistry.getInstance().getPluginObject("GCodeWriter").write(gcode_textio, None)
gcode_writer = cast(MeshWriter, PluginRegistry.getInstance().getPluginObject("GCodeWriter"))
success = gcode_writer.write(gcode_textio, None)
if not success: #Writing the g-code failed. Then I can also not write the gzipped g-code.
self.setInformation(gcode_writer.getInformation())
return False
gcode = archive.getStream("/3D/model.gcode")
gcode.write(gcode_textio.getvalue().encode("UTF-8"))
archive.addRelation(virtual_path = "/3D/model.gcode", relation_type = "http://schemas.ultimaker.org/package/2018/relationships/gcode")

View file

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

View file

@ -9,6 +9,7 @@ Component
{
Rectangle
{
id: monitorFrame
width: maximumWidth
height: maximumHeight
color: UM.Theme.getColor("viewport_background")
@ -103,5 +104,15 @@ Component
visible: OutputDevice.activePrinter != null
anchors.fill:parent
}
onVisibleChanged:
{
if (!monitorFrame.visible)
{
// After switching the Tab ensure that active printer is Null, the video stream image
// might be active
OutputDevice.setActivePrinter(null)
}
}
}
}

View file

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

View file

@ -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));
}
}
@ -349,11 +349,6 @@ Cura.MachineAction
addressField.focus = true;
}
onAccepted:
{
manager.setManualDevice(printerKey, addressText)
}
Column {
anchors.fill: parent
spacing: UM.Theme.getSize("default_margin").height
@ -393,7 +388,7 @@ Cura.MachineAction
text: catalog.i18nc("@action:button", "OK")
onClicked:
{
manualPrinterDialog.accept()
manager.setManualDevice(manualPrinterDialog.printerKey, manualPrinterDialog.addressText)
manualPrinterDialog.hide()
}
enabled: manualPrinterDialog.addressText.trim() != ""

View file

@ -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
@ -90,8 +90,10 @@ Item
MouseArea
{
anchors.fill: cameraImage
onClicked: { /* no-op */ }
onClicked:
{
OutputDevice.setActivePrinter(null)
}
z: 1
}
}

View file

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

View file

@ -198,7 +198,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
has_cluster_capable_firmware = Version(system_info["firmware"]) > self._min_cluster_version
instance_name = "manual:%s" % address
properties = {
b"name": system_info["name"].encode("utf-8"),
b"name": (system_info["name"] + " (manual)").encode("utf-8"),
b"address": address.encode("utf-8"),
b"firmware_version": system_info["firmware"].encode("utf-8"),
b"manual": b"true",

View file

@ -1,4 +1,4 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
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:

View file

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

View file

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

View file

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

View file

@ -0,0 +1,50 @@
{
"version": 2,
"name": "TiZYX K25",
"inherits": "fdmprinter",
"metadata":
{
"visible": true,
"author": "TiZYX",
"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,
"has_materials": true,
"machine_extruder_trains":
{
"0": "tizyx_k25_extruder_0"
}
},
"overrides":
{
"machine_name": { "default_value": "TiZYX K25" },
"machine_heated_bed": { "default_value": true },
"machine_width": { "default_value": 255 },
"machine_height": { "default_value": 255 },
"machine_depth": { "default_value": 255 },
"machine_center_is_zero": { "default_value": false },
"gantry_height": { "default_value": 500 },
"machine_head_with_fans_polygon": {
"default_value": [
[25, 49],
[25, -49],
[-25, -49],
[25, 49]
]
},
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
"machine_start_gcode":
{
"default_value": "M82\nG90\nG28 X\nG28 Y\nG28 Z\nG29\nG91\nG1 Z0\nG90\nM82\nG92 E0\nG1 X125 Y245 F3000\nG1 Z0"
},
"machine_end_gcode":
{
"default_value": "M104 S0\nM140 S0\nG91\nG1 E-5 F300\nG1 Z+3 F3000\nG1 Y245 F3000\nM84"
}
}
}

View file

@ -0,0 +1,137 @@
{
"version": 2,
"name": "Winbo Dragon(L)4",
"inherits": "fdmprinter",
"metadata": {
"author": "Winbo",
"manufacturer": "Winbo Smart Tech Co., Ltd.",
"category": "Other",
"visible": true,
"file_formats": "text/x-gcode",
"supports_usb_connection": false,
"machine_extruder_trains":
{
"0": "winbo_dragonl4_extruder"
}
},
"overrides": {
"machine_name": { "default_value": "Winbo Dragon(L)4" },
"machine_width": { "default_value": 615 },
"machine_depth": { "default_value": 463 },
"machine_height": { "default_value": 615 },
"machine_heated_bed": { "default_value": true },
"material_bed_temp_wait": { "default_value": false },
"machine_filament_park_distance": { "value": "machine_heat_zone_length" },
"machine_nozzle_heat_up_speed": { "default_value": 1.4 },
"machine_nozzle_cool_down_speed": { "default_value": 0.8 },
"machine_head_with_fans_polygon":
{
"default_value":
[
[ -50, 90 ],[ -50, -60 ],[ 50, -60 ],[ 50, 90 ]
]
},
"machine_gcode_flavor": { "default_value": "Marlin" },
"machine_max_feedrate_x": { "default_value": 300 },
"machine_max_feedrate_y": { "default_value": 300 },
"machine_max_feedrate_z": { "default_value": 40 },
"machine_acceleration": { "default_value": 2000 },
"gantry_height": { "default_value": 80 },
"machine_extruder_count": { "default_value": 1 },
"machine_start_gcode": { "default_value": "G21\nG90\nM82\nM107\nM9998\nG28 X0 Y0\nG28 Z0\nG1 F6000 Z0.3\nG92 E0\nG1 F800 X585 E12\nG92 E0" },
"machine_end_gcode": { "default_value": "M104 S0\nM140 S0\nG92 E2\nG1 E0 F200\nG28 X0 Y0\nM84 X Y E" },
"prime_blob_enable": { "enabled": true },
"acceleration_enabled": { "value": "True" },
"acceleration_layer_0": { "value": "acceleration_topbottom" },
"acceleration_prime_tower": { "value": "math.ceil(acceleration_print * 2000 / 4000)" },
"acceleration_print": { "value": "1800" },
"acceleration_travel": { "value": "2000" },
"acceleration_support": { "value": "math.ceil(acceleration_print * 2000 / 4000)" },
"acceleration_support_interface": { "value": "acceleration_topbottom" },
"acceleration_topbottom": { "value": "math.ceil(acceleration_print * 500 / 4000)" },
"acceleration_wall": { "value": "math.ceil(acceleration_print * 1000 / 4000)" },
"acceleration_wall_0": { "value": "math.ceil(acceleration_wall * 500 / 1000)" },
"brim_width": { "value": "4" },
"cool_fan_full_at_height": { "value": "layer_height_0 + 2 * layer_height" },
"cool_fan_speed": { "value": "100" },
"cool_fan_speed_max": { "value": "100" },
"cool_min_speed": { "value": "5" },
"fill_outline_gaps": { "value": "True" },
"infill_overlap": { "value": "0" },
"min_infill_area": { "value": "1" },
"min_skin_width_for_expansion": { "value": "2" },
"jerk_enabled": { "value": "True" },
"jerk_layer_0": { "value": "jerk_topbottom" },
"jerk_prime_tower": { "value": "math.ceil(jerk_print * 15 / 25)" },
"jerk_print": { "value": "25" },
"jerk_support": { "value": "math.ceil(jerk_print * 15 / 25)" },
"jerk_support_interface": { "value": "jerk_topbottom" },
"jerk_topbottom": { "value": "math.ceil(jerk_print * 5 / 25)" },
"jerk_wall": { "value": "math.ceil(jerk_print * 10 / 25)" },
"jerk_wall_0": { "value": "math.ceil(jerk_wall * 5 / 10)" },
"wall_thickness": { "value": "2.4"},
"line_width": { "value": "extruderValue(-1,'machine_nozzle_size')" },
"wall_0_inset": { "value": "0.05" },
"wall_line_width_x": { "value": "line_width" },
"wall_line_width_0": { "value": "line_width-0.05" },
"support_line_width": { "value": "max(min(line_width,0.4),line_width/2)" },
"support_interface_line_width": { "value": "support_line_width" },
"machine_min_cool_heat_time_window": { "value": "15" },
"default_material_print_temperature": { "value": "200" },
"material_print_temperature_layer_0": { "value": "material_print_temperature - 5" },
"material_bed_temperature": { "maximum_value": "100" },
"material_bed_temperature_layer_0": { "maximum_value": "100" },
"raft_airgap": { "value": "0" },
"raft_base_thickness": { "value": "0.3" },
"raft_interface_line_spacing": { "value": "0.5" },
"raft_interface_line_width": { "value": "0.5" },
"raft_interface_thickness": { "value": "0.2" },
"raft_jerk": { "value": "jerk_layer_0" },
"raft_margin": { "value": "5" },
"raft_surface_layers": { "value": "2" },
"retraction_amount": { "value": "4" },
"retraction_count_max": { "value": "10" },
"retraction_extrusion_window": { "value": "1" },
"retraction_hop": { "value": "0.5" },
"retraction_hop_enabled": { "value": "True" },
"retraction_hop_only_when_collides": { "value": "True" },
"retraction_min_travel": { "value": "5" },
"retraction_prime_speed": { "value": "25" },
"skin_overlap": { "value": "10" },
"speed_layer_0": { "value": "25" },
"speed_print": { "value": "70" },
"speed_support": { "value": "speed_print*line_width/support_line_width" },
"speed_support_interface": { "value": "speed_print*line_width/support_interface_line_width" },
"speed_topbottom": { "value": "speed_print*line_width/skin_line_width" },
"speed_travel": { "value": "100" },
"speed_infill": { "value": "speed_print*line_width/infill_line_width" },
"speed_wall": { "value": "speed_print*wall_line_width_0/line_width" },
"speed_wall_0": { "value": "math.ceil(speed_wall * 0.6)" },
"speed_wall_x": { "value": "speed_wall" },
"speed_equalize_flow_enabled": { "value": "False" },
"support_angle": { "value": "50" },
"support_xy_distance": { "value": "1" },
"support_z_distance": { "value": "max((0.2 if(0.2%layer_height==0) else layer_height*int((0.2+layer_height)/layer_height)),layer_height)" },
"support_bottom_distance": { "value": "max(support_z_distance,layer_height*int(0.45/layer_height))" },
"top_bottom_thickness": { "value": "max(1.2,layer_height*6)" },
"travel_avoid_distance": { "value": "3" },
"gradual_support_infill_step_height": { "value": "0.2" },
"gradual_support_infill_steps": { "value": "1" },
"infill_sparse_density": { "value": "20" },
"gradual_infill_step_height": { "value": "1" },
"initial_layer_line_width_factor": { "value": "120" },
"jerk_travel": { "value": "25" },
"support_bottom_enable": { "value": "True" },
"support_bottom_height": { "value": "max((0.15 if(0.15%layer_height==0) else layer_height*int((0.15+layer_height)/layer_height)),layer_height)" },
"support_bottom_pattern": { "value": "'zigzag'" },
"support_connect_zigzags": { "value": "False" },
"support_infill_rate": { "value": "8" },
"support_interface_density": { "value": "80" },
"support_interface_enable": { "value": "True" },
"support_interface_height": { "value": "0.5" },
"support_roof_pattern": { "value": "'concentric'" },
"z_seam_type": { "value": "'shortest'" },
"speed_equalize_flow_max": { "value": "65" }
}
}

View file

@ -0,0 +1,137 @@
{
"version": 2,
"name": "Winbo Mini2",
"inherits": "fdmprinter",
"metadata": {
"author": "Winbo",
"manufacturer": "Winbo Smart Tech Co., Ltd.",
"category": "Other",
"visible": true,
"file_formats": "text/x-gcode",
"supports_usb_connection": true,
"machine_extruder_trains":
{
"0": "winbo_mini2_extruder"
}
},
"overrides": {
"machine_name": { "default_value": "Winbo Mini2" },
"machine_width": { "default_value": 205 },
"machine_depth": { "default_value": 155 },
"machine_height": { "default_value": 205 },
"machine_heated_bed": { "default_value": false },
"material_bed_temp_wait": { "default_value": false },
"machine_filament_park_distance": { "value": "machine_heat_zone_length" },
"machine_nozzle_heat_up_speed": { "default_value": 1.4 },
"machine_nozzle_cool_down_speed": { "default_value": 0.8 },
"machine_head_with_fans_polygon":
{
"default_value":
[
[ -52, 30 ],[ -52, -40 ],[ 13, -40 ],[ 13, 30 ]
]
},
"machine_gcode_flavor": { "default_value": "Marlin" },
"machine_max_feedrate_x": { "default_value": 250 },
"machine_max_feedrate_y": { "default_value": 200 },
"machine_max_feedrate_z": { "default_value": 40 },
"machine_acceleration": { "default_value": 3000 },
"gantry_height": { "default_value": 75 },
"machine_extruder_count": { "default_value": 1 },
"machine_start_gcode": { "default_value": "G21\nG90\nM82\nM107\nG28 X0 Y0\nG28 Z0\nG1 F1000 Z3\nG1 F4000 X0\nG1 F4000 Y0\nG1 F1000 Z0.2\nG92 E0\nG1 F1000 X30 E8\nG92 E0\nM117 Printing." },
"machine_end_gcode": { "default_value": "M104 S0\nM140 S0\nG92 E2\nG1 E0 F200\nG28 X0 Y0\nM84 X Y E" },
"prime_blob_enable": { "enabled": true },
"acceleration_enabled": { "value": "True" },
"acceleration_layer_0": { "value": "acceleration_topbottom" },
"acceleration_prime_tower": { "value": "math.ceil(acceleration_print * 2000 / 4000)" },
"acceleration_print": { "value": "2000" },
"acceleration_travel": { "value": "2500" },
"acceleration_support": { "value": "math.ceil(acceleration_print * 2000 / 4000)" },
"acceleration_support_interface": { "value": "acceleration_topbottom" },
"acceleration_topbottom": { "value": "math.ceil(acceleration_print * 500 / 4000)" },
"acceleration_wall": { "value": "math.ceil(acceleration_print * 1000 / 4000)" },
"acceleration_wall_0": { "value": "math.ceil(acceleration_wall * 500 / 1000)" },
"brim_width": { "value": "3" },
"cool_fan_full_at_height": { "value": "layer_height_0 + 2 * layer_height" },
"cool_fan_speed": { "value": "100" },
"cool_fan_speed_max": { "value": "100" },
"cool_min_speed": { "value": "5" },
"fill_outline_gaps": { "value": "True" },
"infill_overlap": { "value": "0" },
"min_infill_area": { "value": "1" },
"min_skin_width_for_expansion": { "value": "2" },
"jerk_enabled": { "value": "True" },
"jerk_layer_0": { "value": "jerk_topbottom" },
"jerk_prime_tower": { "value": "math.ceil(jerk_print * 15 / 25)" },
"jerk_print": { "value": "25" },
"jerk_support": { "value": "math.ceil(jerk_print * 15 / 25)" },
"jerk_support_interface": { "value": "jerk_topbottom" },
"jerk_topbottom": { "value": "math.ceil(jerk_print * 5 / 25)" },
"jerk_wall": { "value": "math.ceil(jerk_print * 10 / 25)" },
"jerk_wall_0": { "value": "math.ceil(jerk_wall * 5 / 10)" },
"wall_thickness": { "value": "1.2"},
"line_width": { "value": "extruderValue(-1,'machine_nozzle_size')" },
"wall_0_inset": { "value": "0.05" },
"wall_line_width_x": { "value": "line_width" },
"wall_line_width_0": { "value": "line_width-0.05" },
"support_line_width": { "value": "max(min(line_width,0.4),line_width/2)" },
"support_interface_line_width": { "value": "support_line_width" },
"machine_min_cool_heat_time_window": { "value": "15" },
"default_material_print_temperature": { "value": "200" },
"material_print_temperature_layer_0": { "value": "material_print_temperature - 5" },
"material_bed_temperature": { "maximum_value": "115" },
"material_bed_temperature_layer_0": { "maximum_value": "115" },
"raft_airgap": { "value": "0" },
"raft_base_thickness": { "value": "0.3" },
"raft_interface_line_spacing": { "value": "0.5" },
"raft_interface_line_width": { "value": "0.5" },
"raft_interface_thickness": { "value": "0.2" },
"raft_jerk": { "value": "jerk_layer_0" },
"raft_margin": { "value": "10" },
"raft_surface_layers": { "value": "1" },
"retraction_amount": { "value": "4" },
"retraction_count_max": { "value": "10" },
"retraction_extrusion_window": { "value": "1" },
"retraction_hop": { "value": "0.5" },
"retraction_hop_enabled": { "value": "True" },
"retraction_hop_only_when_collides": { "value": "True" },
"retraction_min_travel": { "value": "5" },
"retraction_prime_speed": { "value": "25" },
"skin_overlap": { "value": "10" },
"speed_layer_0": { "value": "20" },
"speed_print": { "value": "50" },
"speed_support": { "value": "speed_print*line_width/support_line_width" },
"speed_support_interface": { "value": "speed_print*line_width/support_interface_line_width" },
"speed_topbottom": { "value": "speed_print*line_width/skin_line_width" },
"speed_travel": { "value": "90" },
"speed_infill": { "value": "speed_print*line_width/infill_line_width" },
"speed_wall": { "value": "speed_print*wall_line_width_0/line_width" },
"speed_wall_0": { "value": "math.ceil(speed_wall * 0.6)" },
"speed_wall_x": { "value": "speed_wall" },
"speed_equalize_flow_enabled": { "value": "False" },
"support_angle": { "value": "50" },
"support_xy_distance": { "value": "1" },
"support_z_distance": { "value": "max((0.2 if(0.2%layer_height==0) else layer_height*int((0.2+layer_height)/layer_height)),layer_height)" },
"support_bottom_distance": { "value": "max(support_z_distance,layer_height*int(0.45/layer_height))" },
"top_bottom_thickness": { "value": "max(1.2,layer_height*6)" },
"travel_avoid_distance": { "value": "3" },
"gradual_support_infill_step_height": { "value": "0.2" },
"gradual_support_infill_steps": { "value": "1" },
"infill_sparse_density": { "value": "20" },
"gradual_infill_step_height": { "value": "1" },
"initial_layer_line_width_factor": { "value": "120" },
"jerk_travel": { "value": "25" },
"support_bottom_enable": { "value": "True" },
"support_bottom_height": { "value": "max((0.15 if(0.15%layer_height==0) else layer_height*int((0.15+layer_height)/layer_height)),layer_height)" },
"support_bottom_pattern": { "value": "'zigzag'" },
"support_connect_zigzags": { "value": "False" },
"support_infill_rate": { "value": "8" },
"support_interface_density": { "value": "80" },
"support_interface_enable": { "value": "True" },
"support_interface_height": { "value": "0.5" },
"support_roof_pattern": { "value": "'concentric'" },
"z_seam_type": { "value": "'shortest'" },
"speed_equalize_flow_max": { "value": "65" }
}
}

View file

@ -0,0 +1,16 @@
{
"id": "tizyx_k25_extruder_0",
"version": 2,
"name": "Extruder 1",
"inherits": "fdmextruder",
"metadata": {
"machine": "tizyx_k25",
"position": "0"
},
"overrides": {
"extruder_nr": { "default_value": 0 },
"machine_nozzle_size": { "default_value": 0.4 },
"material_diameter": { "default_value": 1.75 }
}
}

View file

@ -0,0 +1,20 @@
{
"version": 2,
"name": "Extruder 1",
"inherits": "fdmextruder",
"metadata": {
"machine": "winbo_dragonl4",
"position": "0"
},
"overrides": {
"extruder_nr": {
"default_value": 0,
"maximum_value": "2"
},
"material_diameter": { "default_value": 3.0 },
"machine_nozzle_size": { "default_value": 0.8 },
"machine_nozzle_offset_x": { "default_value": 0.0 },
"machine_nozzle_offset_y": { "default_value": 0.0 }
}
}

View file

@ -0,0 +1,20 @@
{
"version": 2,
"name": "Extruder 1",
"inherits": "fdmextruder",
"metadata": {
"machine": "winbo_mini2",
"position": "0"
},
"overrides": {
"extruder_nr": {
"default_value": 0,
"maximum_value": "2"
},
"material_diameter": { "default_value": 1.75 },
"machine_nozzle_size": { "default_value": 0.4 },
"machine_nozzle_offset_x": { "default_value": 0.0 },
"machine_nozzle_offset_y": { "default_value": 0.0 }
}
}

View file

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

Binary file not shown.

View file

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

View file

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

View file

@ -26,7 +26,6 @@ Column
OutputDeviceHeader
{
width: parent.width
outputDevice: connectedDevice
}

View file

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

View file

@ -48,16 +48,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 +167,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 +180,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 +237,6 @@ Item {
}
label: Item { }
}
Connections {
target: UM.Backend
onStateChanged:
{
if ([2, 3].indexOf(UM.Backend.state) != -1)
{
prepareButton.preparingToSlice = false;
}
}
}
}
Button {

View file

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

View file

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

View file

@ -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 != ""
}
}

View file

@ -1,7 +1,7 @@
[general]
version = 4
name = Fast
definition = dagoma_neva
definition = dagoma_neva_magis
[metadata]
setting_version = 5

View file

@ -1,7 +1,7 @@
[general]
version = 4
name = Fine
definition = dagoma_neva
definition = dagoma_neva_magis
[metadata]
setting_version = 5

View file

@ -1,7 +1,7 @@
[general]
version = 4
name = Standard
definition = dagoma_neva
definition = dagoma_neva_magis
[metadata]
setting_version = 5

View file

@ -0,0 +1,31 @@
[general]
version = 4
name = TiZYX K25 Normal
definition = tizyx_k25
[metadata]
quality_type = normal
setting_version = 5
type = quality
global_quality = True
[values]
adhesion_type = skirt
skirt_line_count = 2
skirt_gap = 2
cool_fan_speed_0 = 100
fill_outline_gaps = True
infill_angles = [0,90 ]
infill_sparse_density = 15
layer_height = 0.2
layer_height_0 = 0.25
material_diameter = 1.75
retraction_amount = 2.5
retraction_min_travel = 2
retraction_speed = 30
skin_angles = [0,90]
speed_print = 60
speed_topbottom = 50
speed_wall_0 = 40
top_layers = 4
wall_line_count = 2

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

Some files were not shown because too many files have changed in this diff Show more