mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-05-21 20:19:32 +08:00
Merge branch 'master' of github.com:Ultimaker/Cura into cura_connect_UI_rework
This commit is contained in:
commit
d46c754616
@ -13,6 +13,7 @@ from cura.Backups.BackupsManager import BackupsManager
|
|||||||
# api = CuraAPI()
|
# api = CuraAPI()
|
||||||
# api.backups.createBackup()
|
# api.backups.createBackup()
|
||||||
# api.backups.restoreBackup(my_zip_file, {"cura_release": "3.1"})``
|
# api.backups.restoreBackup(my_zip_file, {"cura_release": "3.1"})``
|
||||||
|
|
||||||
class Backups:
|
class Backups:
|
||||||
manager = BackupsManager() # Re-used instance of the backups manager.
|
manager = BackupsManager() # Re-used instance of the backups manager.
|
||||||
|
|
||||||
|
33
cura/API/Interface/Settings.py
Normal file
33
cura/API/Interface/Settings.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
from cura.CuraApplication import CuraApplication
|
||||||
|
|
||||||
|
## The Interface.Settings API provides a version-proof bridge between Cura's
|
||||||
|
# (currently) sidebar UI and plug-ins that hook into it.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ``from cura.API import CuraAPI
|
||||||
|
# api = CuraAPI()
|
||||||
|
# api.interface.settings.getContextMenuItems()
|
||||||
|
# data = {
|
||||||
|
# "name": "My Plugin Action",
|
||||||
|
# "iconName": "my-plugin-icon",
|
||||||
|
# "actions": my_menu_actions,
|
||||||
|
# "menu_item": MyPluginAction(self)
|
||||||
|
# }
|
||||||
|
# api.interface.settings.addContextMenuItem(data)``
|
||||||
|
|
||||||
|
class Settings:
|
||||||
|
# Re-used instance of Cura:
|
||||||
|
application = CuraApplication.getInstance() # type: CuraApplication
|
||||||
|
|
||||||
|
## Add items to the sidebar context menu.
|
||||||
|
# \param menu_item dict containing the menu item to add.
|
||||||
|
def addContextMenuItem(self, menu_item: dict) -> None:
|
||||||
|
self.application.addSidebarCustomMenuItem(menu_item)
|
||||||
|
|
||||||
|
## Get all custom items currently added to the sidebar context menu.
|
||||||
|
# \return List containing all custom context menu items.
|
||||||
|
def getContextMenuItems(self) -> list:
|
||||||
|
return self.application.getSidebarCustomMenuItems()
|
24
cura/API/Interface/__init__.py
Normal file
24
cura/API/Interface/__init__.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
from UM.PluginRegistry import PluginRegistry
|
||||||
|
from cura.API.Interface.Settings import Settings
|
||||||
|
|
||||||
|
## The Interface class serves as a common root for the specific API
|
||||||
|
# methods for each interface element.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ``from cura.API import CuraAPI
|
||||||
|
# api = CuraAPI()
|
||||||
|
# api.interface.settings.addContextMenuItem()
|
||||||
|
# api.interface.viewport.addOverlay() # Not implemented, just a hypothetical
|
||||||
|
# api.interface.toolbar.getToolButtonCount() # Not implemented, just a hypothetical
|
||||||
|
# # etc.``
|
||||||
|
|
||||||
|
class Interface:
|
||||||
|
|
||||||
|
# For now we use the same API version to be consistent.
|
||||||
|
VERSION = PluginRegistry.APIVersion
|
||||||
|
|
||||||
|
# API methods specific to the settings portion of the UI
|
||||||
|
settings = Settings()
|
@ -2,6 +2,7 @@
|
|||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
from UM.PluginRegistry import PluginRegistry
|
from UM.PluginRegistry import PluginRegistry
|
||||||
from cura.API.Backups import Backups
|
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.
|
## 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
|
# 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
|
# etc. Usage of any other methods than the ones provided in this API can cause
|
||||||
# plug-ins to be unstable.
|
# plug-ins to be unstable.
|
||||||
|
|
||||||
class CuraAPI:
|
class CuraAPI:
|
||||||
|
|
||||||
# For now we use the same API version to be consistent.
|
# For now we use the same API version to be consistent.
|
||||||
VERSION = PluginRegistry.APIVersion
|
VERSION = PluginRegistry.APIVersion
|
||||||
|
|
||||||
# Backups API.
|
# Backups API
|
||||||
backups = Backups()
|
backups = Backups()
|
||||||
|
|
||||||
|
# Interface API
|
||||||
|
interface = Interface()
|
||||||
|
@ -104,6 +104,7 @@ from cura.Settings.UserChangesModel import UserChangesModel
|
|||||||
from cura.Settings.ExtrudersModel import ExtrudersModel
|
from cura.Settings.ExtrudersModel import ExtrudersModel
|
||||||
from cura.Settings.MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler
|
from cura.Settings.MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler
|
||||||
from cura.Settings.ContainerManager import ContainerManager
|
from cura.Settings.ContainerManager import ContainerManager
|
||||||
|
from cura.Settings.SidebarCustomMenuItemsModel import SidebarCustomMenuItemsModel
|
||||||
|
|
||||||
from cura.ObjectsModel import ObjectsModel
|
from cura.ObjectsModel import ObjectsModel
|
||||||
|
|
||||||
@ -117,11 +118,12 @@ if TYPE_CHECKING:
|
|||||||
numpy.seterr(all = "ignore")
|
numpy.seterr(all = "ignore")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from cura.CuraVersion import CuraVersion, CuraBuildType, CuraDebugMode
|
from cura.CuraVersion import CuraVersion, CuraBuildType, CuraDebugMode, CuraSDKVersion
|
||||||
except ImportError:
|
except ImportError:
|
||||||
CuraVersion = "master" # [CodeStyle: Reflecting imported value]
|
CuraVersion = "master" # [CodeStyle: Reflecting imported value]
|
||||||
CuraBuildType = ""
|
CuraBuildType = ""
|
||||||
CuraDebugMode = False
|
CuraDebugMode = False
|
||||||
|
CuraSDKVerion = ""
|
||||||
|
|
||||||
|
|
||||||
class CuraApplication(QtApplication):
|
class CuraApplication(QtApplication):
|
||||||
@ -226,6 +228,8 @@ class CuraApplication(QtApplication):
|
|||||||
|
|
||||||
self._need_to_show_user_agreement = True
|
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
|
self._plugins_loaded = False
|
||||||
|
|
||||||
# Backups
|
# Backups
|
||||||
@ -627,7 +631,7 @@ class CuraApplication(QtApplication):
|
|||||||
# Cura has multiple locations where instance containers need to be saved, so we need to handle this differently.
|
# Cura has multiple locations where instance containers need to be saved, so we need to handle this differently.
|
||||||
def saveSettings(self):
|
def saveSettings(self):
|
||||||
if not self.started or not self._save_data_enabled:
|
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
|
return
|
||||||
ContainerRegistry.getInstance().saveDirtyContainers()
|
ContainerRegistry.getInstance().saveDirtyContainers()
|
||||||
self.savePreferences()
|
self.savePreferences()
|
||||||
@ -910,6 +914,7 @@ class CuraApplication(QtApplication):
|
|||||||
engine.rootContext().setContextProperty("CuraApplication", self)
|
engine.rootContext().setContextProperty("CuraApplication", self)
|
||||||
engine.rootContext().setContextProperty("PrintInformation", self._print_information)
|
engine.rootContext().setContextProperty("PrintInformation", self._print_information)
|
||||||
engine.rootContext().setContextProperty("CuraActions", self._cura_actions)
|
engine.rootContext().setContextProperty("CuraActions", self._cura_actions)
|
||||||
|
engine.rootContext().setContextProperty("CuraSDKVersion", CuraSDKVersion)
|
||||||
|
|
||||||
qmlRegisterUncreatableType(CuraApplication, "Cura", 1, 0, "ResourceTypes", "Just an Enum type")
|
qmlRegisterUncreatableType(CuraApplication, "Cura", 1, 0, "ResourceTypes", "Just an Enum type")
|
||||||
|
|
||||||
@ -944,6 +949,7 @@ class CuraApplication(QtApplication):
|
|||||||
qmlRegisterType(MachineNameValidator, "Cura", 1, 0, "MachineNameValidator")
|
qmlRegisterType(MachineNameValidator, "Cura", 1, 0, "MachineNameValidator")
|
||||||
qmlRegisterType(UserChangesModel, "Cura", 1, 0, "UserChangesModel")
|
qmlRegisterType(UserChangesModel, "Cura", 1, 0, "UserChangesModel")
|
||||||
qmlRegisterSingletonType(ContainerManager, "Cura", 1, 0, "ContainerManager", ContainerManager.getInstance)
|
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.
|
# 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")))
|
actions_url = QUrl.fromLocalFile(os.path.abspath(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Actions.qml")))
|
||||||
@ -1731,3 +1737,10 @@ class CuraApplication(QtApplication):
|
|||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def showMoreInformationDialogForAnonymousDataCollection(self):
|
def showMoreInformationDialogForAnonymousDataCollection(self):
|
||||||
cast(SliceInfo, self._plugin_registry.getPluginObject("SliceInfoPlugin")).showMoreInfoDialog()
|
cast(SliceInfo, self._plugin_registry.getPluginObject("SliceInfoPlugin")).showMoreInfoDialog()
|
||||||
|
|
||||||
|
def addSidebarCustomMenuItem(self, menu_item: dict) -> None:
|
||||||
|
self._sidebar_custom_menu_items.append(menu_item)
|
||||||
|
|
||||||
|
def getSidebarCustomMenuItems(self) -> list:
|
||||||
|
return self._sidebar_custom_menu_items
|
||||||
|
|
||||||
|
@ -4,8 +4,7 @@
|
|||||||
from collections import defaultdict, OrderedDict
|
from collections import defaultdict, OrderedDict
|
||||||
import copy
|
import copy
|
||||||
import uuid
|
import uuid
|
||||||
from typing import Dict, cast
|
from typing import Dict, Optional, TYPE_CHECKING
|
||||||
from typing import Optional, TYPE_CHECKING
|
|
||||||
|
|
||||||
from PyQt5.Qt import QTimer, QObject, pyqtSignal, pyqtSlot
|
from PyQt5.Qt import QTimer, QObject, pyqtSignal, pyqtSlot
|
||||||
|
|
||||||
@ -18,6 +17,7 @@ from UM.Util import parseBool
|
|||||||
|
|
||||||
from .MaterialNode import MaterialNode
|
from .MaterialNode import MaterialNode
|
||||||
from .MaterialGroup import MaterialGroup
|
from .MaterialGroup import MaterialGroup
|
||||||
|
from .VariantType import VariantType
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||||
@ -47,7 +47,7 @@ class MaterialManager(QObject):
|
|||||||
|
|
||||||
self._fallback_materials_map = dict() # material_type -> generic material metadata
|
self._fallback_materials_map = dict() # material_type -> generic material metadata
|
||||||
self._material_group_map = dict() # root_material_id -> MaterialGroup
|
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
|
# 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
|
# because the generic material ids are used in qualities and definitions, while the specific diameter material is meant
|
||||||
@ -187,52 +187,78 @@ class MaterialManager(QObject):
|
|||||||
self._diameter_material_map[root_material_id] = default_root_material_id
|
self._diameter_material_map[root_material_id] = default_root_material_id
|
||||||
|
|
||||||
# Map #4
|
# Map #4
|
||||||
# "machine" -> "variant_name" -> "root material ID" -> specific material InstanceContainer
|
# "machine" -> "nozzle name" -> "buildplate name" -> "root material ID" -> specific material InstanceContainer
|
||||||
# Construct the "machine" -> "variant" -> "root material ID" -> specific material InstanceContainer
|
self._diameter_machine_nozzle_buildplate_material_map = dict()
|
||||||
self._diameter_machine_variant_material_map = dict()
|
|
||||||
for material_metadata in material_metadatas.values():
|
for material_metadata in material_metadatas.values():
|
||||||
# We don't store empty material in the lookup tables
|
self.__addMaterialMetadataIntoLookupTree(material_metadata)
|
||||||
if material_metadata["id"] == "empty_material":
|
|
||||||
continue
|
|
||||||
|
|
||||||
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] = {}
|
|
||||||
|
|
||||||
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_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
|
|
||||||
|
|
||||||
# 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_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"]))
|
|
||||||
self._container_registry.addWrongContainerId(material_metadata["id"])
|
|
||||||
|
|
||||||
self.materialsUpdated.emit()
|
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_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_nozzle_buildplate_material_map:
|
||||||
|
self._diameter_machine_nozzle_buildplate_material_map[approximate_diameter] = {}
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
# 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),
|
||||||
|
]
|
||||||
|
|
||||||
|
variant_manager = self._application.getVariantManager()
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
# 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):
|
def _updateMaps(self):
|
||||||
Logger.log("i", "Updating material lookup data ...")
|
Logger.log("i", "Updating material lookup data ...")
|
||||||
self.initialize()
|
self.initialize()
|
||||||
@ -263,45 +289,52 @@ class MaterialManager(QObject):
|
|||||||
#
|
#
|
||||||
# Return a dict with all root material IDs (k) and ContainerNodes (v) that's suitable for the given setup.
|
# 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],
|
def getAvailableMaterials(self, machine_definition: "DefinitionContainer", nozzle_name: Optional[str],
|
||||||
diameter: float) -> Dict[str, MaterialNode]:
|
buildplate_name: Optional[str], diameter: float) -> Dict[str, MaterialNode]:
|
||||||
# round the diameter to get the approximate diameter
|
# round the diameter to get the approximate diameter
|
||||||
rounded_diameter = str(round(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)
|
Logger.log("i", "Cannot find materials with diameter [%s] (rounded to [%s])", diameter, rounded_diameter)
|
||||||
return dict()
|
return dict()
|
||||||
|
|
||||||
machine_definition_id = machine_definition.getId()
|
machine_definition_id = machine_definition.getId()
|
||||||
|
|
||||||
# If there are variant materials, get the variant material
|
# If there are nozzle-and-or-buildplate materials, get the nozzle-and-or-buildplate material
|
||||||
machine_variant_material_map = self._diameter_machine_variant_material_map[rounded_diameter]
|
machine_nozzle_buildplate_material_map = self._diameter_machine_nozzle_buildplate_material_map[rounded_diameter]
|
||||||
machine_node = machine_variant_material_map.get(machine_definition_id)
|
machine_node = machine_nozzle_buildplate_material_map.get(machine_definition_id)
|
||||||
default_machine_node = machine_variant_material_map.get(self._default_machine_definition_id)
|
default_machine_node = machine_nozzle_buildplate_material_map.get(self._default_machine_definition_id)
|
||||||
variant_node = None
|
nozzle_node = None
|
||||||
if extruder_variant_name is not None and machine_node is not None:
|
buildplate_node = None
|
||||||
variant_node = machine_node.getChildNode(extruder_variant_name)
|
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:
|
# Fallback mechanism of finding materials:
|
||||||
# 1. variant-specific material
|
# 1. buildplate-specific material
|
||||||
# 2. machine-specific material
|
# 2. nozzle-specific material
|
||||||
# 3. generic material (for fdmprinter)
|
# 3. machine-specific material
|
||||||
|
# 4. generic material (for fdmprinter)
|
||||||
machine_exclude_materials = machine_definition.getMetaDataEntry("exclude_materials", [])
|
machine_exclude_materials = machine_definition.getMetaDataEntry("exclude_materials", [])
|
||||||
|
|
||||||
material_id_metadata_dict = dict() # type: Dict[str, MaterialNode]
|
material_id_metadata_dict = dict() # type: Dict[str, MaterialNode]
|
||||||
for node in nodes_to_check:
|
for current_node in nodes_to_check:
|
||||||
if node is not None:
|
if current_node is None:
|
||||||
# Only exclude the materials that are explicitly specified in the "exclude_materials" field.
|
continue
|
||||||
# Do not exclude other materials that are of the same type.
|
|
||||||
for material_id, node in 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())
|
|
||||||
continue
|
|
||||||
|
|
||||||
if material_id not in material_id_metadata_dict:
|
# Only exclude the materials that are explicitly specified in the "exclude_materials" field.
|
||||||
material_id_metadata_dict[material_id] = node
|
# Do not exclude other materials that are of the same type.
|
||||||
|
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())
|
||||||
|
continue
|
||||||
|
|
||||||
|
if material_id not in material_id_metadata_dict:
|
||||||
|
material_id_metadata_dict[material_id] = node
|
||||||
|
|
||||||
return material_id_metadata_dict
|
return material_id_metadata_dict
|
||||||
|
|
||||||
@ -310,13 +343,14 @@ class MaterialManager(QObject):
|
|||||||
#
|
#
|
||||||
def getAvailableMaterialsForMachineExtruder(self, machine: "GlobalStack",
|
def getAvailableMaterialsForMachineExtruder(self, machine: "GlobalStack",
|
||||||
extruder_stack: "ExtruderStack") -> Optional[dict]:
|
extruder_stack: "ExtruderStack") -> Optional[dict]:
|
||||||
variant_name = None
|
buildplate_name = machine.getBuildplateName()
|
||||||
|
nozzle_name = None
|
||||||
if extruder_stack.variant.getId() != "empty_variant":
|
if extruder_stack.variant.getId() != "empty_variant":
|
||||||
variant_name = extruder_stack.variant.getName()
|
nozzle_name = extruder_stack.variant.getName()
|
||||||
diameter = extruder_stack.approximateMaterialDiameter
|
diameter = extruder_stack.approximateMaterialDiameter
|
||||||
|
|
||||||
# Fetch the available materials (ContainerNode) for the current active machine and extruder setup.
|
# 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.
|
# 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;
|
# 1. the given machine doesn't have materials;
|
||||||
# 2. cannot find any material InstanceContainers with the given settings.
|
# 2. cannot find any material InstanceContainers with the given settings.
|
||||||
#
|
#
|
||||||
def getMaterialNode(self, machine_definition_id: str, extruder_variant_name: Optional[str],
|
def getMaterialNode(self, machine_definition_id: str, nozzle_name: Optional[str],
|
||||||
diameter: float, root_material_id: str) -> Optional["InstanceContainer"]:
|
buildplate_name: Optional[str], diameter: float, root_material_id: str) -> Optional["InstanceContainer"]:
|
||||||
# round the diameter to get the approximate diameter
|
# round the diameter to get the approximate diameter
|
||||||
rounded_diameter = str(round(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]",
|
Logger.log("i", "Cannot find materials with diameter [%s] (rounded to [%s]) for root material id [%s]",
|
||||||
diameter, rounded_diameter, root_material_id)
|
diameter, rounded_diameter, root_material_id)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# If there are variant materials, get the variant material
|
# If there are nozzle materials, get the nozzle-specific material
|
||||||
machine_variant_material_map = self._diameter_machine_variant_material_map[rounded_diameter]
|
machine_nozzle_buildplate_material_map = self._diameter_machine_nozzle_buildplate_material_map[rounded_diameter]
|
||||||
machine_node = machine_variant_material_map.get(machine_definition_id)
|
machine_node = machine_nozzle_buildplate_material_map.get(machine_definition_id)
|
||||||
variant_node = None
|
nozzle_node = None
|
||||||
|
buildplate_node = None
|
||||||
|
|
||||||
# Fallback for "fdmprinter" if the machine-specific materials cannot be found
|
# Fallback for "fdmprinter" if the machine-specific materials cannot be found
|
||||||
if machine_node is None:
|
if machine_node is None:
|
||||||
machine_node = machine_variant_material_map.get(self._default_machine_definition_id)
|
machine_node = machine_nozzle_buildplate_material_map.get(self._default_machine_definition_id)
|
||||||
if machine_node is not None and extruder_variant_name is not None:
|
if machine_node is not None and nozzle_name is not None:
|
||||||
variant_node = machine_node.getChildNode(extruder_variant_name)
|
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:
|
# Fallback mechanism of finding materials:
|
||||||
# 1. variant-specific material
|
# 1. buildplate-specific material
|
||||||
# 2. machine-specific material
|
# 2. nozzle-specific material
|
||||||
# 3. generic material (for fdmprinter)
|
# 3. machine-specific material
|
||||||
nodes_to_check = [variant_node, machine_node,
|
# 4. generic material (for fdmprinter)
|
||||||
machine_variant_material_map.get(self._default_machine_definition_id)]
|
nodes_to_check = [buildplate_node, nozzle_node, machine_node,
|
||||||
|
machine_nozzle_buildplate_material_map.get(self._default_machine_definition_id)]
|
||||||
|
|
||||||
material_node = None
|
material_node = None
|
||||||
for node in nodes_to_check:
|
for node in nodes_to_check:
|
||||||
@ -366,7 +404,8 @@ class MaterialManager(QObject):
|
|||||||
# 1. the given machine doesn't have materials;
|
# 1. the given machine doesn't have materials;
|
||||||
# 2. cannot find any material InstanceContainers with the given settings.
|
# 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
|
node = None
|
||||||
machine_definition = global_stack.definition
|
machine_definition = global_stack.definition
|
||||||
extruder_definition = global_stack.extruders[position].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)
|
Logger.log("i", "Cannot find materials with guid [%s] ", material_guid)
|
||||||
return None
|
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)
|
material_diameter, root_material_id)
|
||||||
return node
|
return node
|
||||||
|
|
||||||
@ -413,13 +452,17 @@ class MaterialManager(QObject):
|
|||||||
else:
|
else:
|
||||||
return None
|
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)
|
# 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
|
node = None
|
||||||
|
|
||||||
|
buildplate_name = global_stack.getBuildplateName()
|
||||||
machine_definition = global_stack.definition
|
machine_definition = global_stack.definition
|
||||||
if extruder_definition is None:
|
if extruder_definition is None:
|
||||||
extruder_definition = global_stack.extruders[position].definition
|
extruder_definition = global_stack.extruders[position].definition
|
||||||
|
|
||||||
if extruder_definition and parseBool(global_stack.getMetaDataEntry("has_materials", False)):
|
if extruder_definition and parseBool(global_stack.getMetaDataEntry("has_materials", False)):
|
||||||
# At this point the extruder_definition is not None
|
# At this point the extruder_definition is not None
|
||||||
material_diameter = extruder_definition.getProperty("material_diameter", "value")
|
material_diameter = extruder_definition.getProperty("material_diameter", "value")
|
||||||
@ -428,7 +471,7 @@ class MaterialManager(QObject):
|
|||||||
approximate_material_diameter = str(round(material_diameter))
|
approximate_material_diameter = str(round(material_diameter))
|
||||||
root_material_id = machine_definition.getMetaDataEntry("preferred_material")
|
root_material_id = machine_definition.getMetaDataEntry("preferred_material")
|
||||||
root_material_id = self.getRootMaterialIDForDiameter(root_material_id, approximate_material_diameter)
|
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)
|
material_diameter, root_material_id)
|
||||||
return node
|
return node
|
||||||
|
|
||||||
@ -515,8 +558,8 @@ class MaterialManager(QObject):
|
|||||||
if container_to_copy.getMetaDataEntry("definition") != "fdmprinter":
|
if container_to_copy.getMetaDataEntry("definition") != "fdmprinter":
|
||||||
new_id += "_" + container_to_copy.getMetaDataEntry("definition")
|
new_id += "_" + container_to_copy.getMetaDataEntry("definition")
|
||||||
if container_to_copy.getMetaDataEntry("variant_name"):
|
if container_to_copy.getMetaDataEntry("variant_name"):
|
||||||
variant_name = container_to_copy.getMetaDataEntry("variant_name")
|
nozzle_name = container_to_copy.getMetaDataEntry("variant_name")
|
||||||
new_id += "_" + variant_name.replace(" ", "_")
|
new_id += "_" + nozzle_name.replace(" ", "_")
|
||||||
|
|
||||||
new_container = copy.deepcopy(container_to_copy)
|
new_container = copy.deepcopy(container_to_copy)
|
||||||
new_container.getMetaData()["id"] = new_id
|
new_container.getMetaData()["id"] = new_id
|
||||||
|
@ -8,7 +8,7 @@ from UM.Logger import Logger
|
|||||||
from UM.Qt.ListModel import ListModel
|
from UM.Qt.ListModel import ListModel
|
||||||
from UM.Util import parseBool
|
from UM.Util import parseBool
|
||||||
|
|
||||||
from cura.Machines.VariantManager import VariantType
|
from cura.Machines.VariantType import VariantType
|
||||||
|
|
||||||
|
|
||||||
class BuildPlateModel(ListModel):
|
class BuildPlateModel(ListModel):
|
||||||
|
@ -8,6 +8,8 @@ from UM.Logger import Logger
|
|||||||
from UM.Qt.ListModel import ListModel
|
from UM.Qt.ListModel import ListModel
|
||||||
from UM.Util import parseBool
|
from UM.Util import parseBool
|
||||||
|
|
||||||
|
from cura.Machines.VariantType import VariantType
|
||||||
|
|
||||||
|
|
||||||
class NozzleModel(ListModel):
|
class NozzleModel(ListModel):
|
||||||
IdRole = Qt.UserRole + 1
|
IdRole = Qt.UserRole + 1
|
||||||
@ -43,7 +45,6 @@ class NozzleModel(ListModel):
|
|||||||
self.setItems([])
|
self.setItems([])
|
||||||
return
|
return
|
||||||
|
|
||||||
from cura.Machines.VariantManager import VariantType
|
|
||||||
variant_node_dict = self._variant_manager.getVariantNodes(global_stack, VariantType.NOZZLE)
|
variant_node_dict = self._variant_manager.getVariantNodes(global_stack, VariantType.NOZZLE)
|
||||||
if not variant_node_dict:
|
if not variant_node_dict:
|
||||||
self.setItems([])
|
self.setItems([])
|
||||||
|
@ -45,7 +45,7 @@ class QualityManager(QObject):
|
|||||||
self._empty_quality_container = self._application.empty_quality_container
|
self._empty_quality_container = self._application.empty_quality_container
|
||||||
self._empty_quality_changes_container = self._application.empty_quality_changes_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._machine_quality_type_to_quality_changes_dict = {} # for quality_changes lookup
|
||||||
|
|
||||||
self._default_machine_definition_id = "fdmprinter"
|
self._default_machine_definition_id = "fdmprinter"
|
||||||
@ -64,10 +64,10 @@ class QualityManager(QObject):
|
|||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
# Initialize the lookup tree for quality profiles with following structure:
|
# Initialize the lookup tree for quality profiles with following structure:
|
||||||
# <machine> -> <variant> -> <material>
|
# <machine> -> <nozzle> -> <buildplate> -> <material>
|
||||||
# -> <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
|
self._machine_quality_type_to_quality_changes_dict = {} # for quality_changes lookup
|
||||||
|
|
||||||
quality_metadata_list = self._container_registry.findContainersMetadata(type = "quality")
|
quality_metadata_list = self._container_registry.findContainersMetadata(type = "quality")
|
||||||
@ -79,53 +79,41 @@ class QualityManager(QObject):
|
|||||||
quality_type = metadata["quality_type"]
|
quality_type = metadata["quality_type"]
|
||||||
|
|
||||||
root_material_id = metadata.get("material")
|
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 = 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
|
# 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"])
|
ConfigurationErrorMessage.getInstance().addFaultyContainers(metadata["id"])
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if definition_id not in self._machine_variant_material_quality_type_to_quality_dict:
|
if definition_id not in self._machine_nozzle_buildplate_material_quality_type_to_quality_dict:
|
||||||
self._machine_variant_material_quality_type_to_quality_dict[definition_id] = QualityNode()
|
self._machine_nozzle_buildplate_material_quality_type_to_quality_dict[definition_id] = QualityNode()
|
||||||
machine_node = cast(QualityNode, self._machine_variant_material_quality_type_to_quality_dict[definition_id])
|
machine_node = cast(QualityNode, self._machine_nozzle_buildplate_material_quality_type_to_quality_dict[definition_id])
|
||||||
|
|
||||||
if is_global_quality:
|
if is_global_quality:
|
||||||
# For global qualities, save data in the machine node
|
# For global qualities, save data in the machine node
|
||||||
machine_node.addQualityMetadata(quality_type, metadata)
|
machine_node.addQualityMetadata(quality_type, metadata)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if variant_name is not None:
|
current_node = machine_node
|
||||||
# If variant_name is specified in the quality/quality_changes profile, check if material is specified,
|
intermediate_node_info_list = [nozzle_name, buildplate_name, root_material_id]
|
||||||
# too.
|
current_intermediate_node_info_idx = 0
|
||||||
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])
|
|
||||||
|
|
||||||
if root_material_id is None:
|
while current_intermediate_node_info_idx < len(intermediate_node_info_list):
|
||||||
# If only variant_name is specified but material is not, add the quality/quality_changes metadata
|
node_name = intermediate_node_info_list[current_intermediate_node_info_idx]
|
||||||
# into the current variant node.
|
if node_name is not None:
|
||||||
variant_node.addQualityMetadata(quality_type, metadata)
|
# There is specific information, update the current node to go deeper so we can add this quality
|
||||||
else:
|
# at the most specific branch in the lookup tree.
|
||||||
# If only variant_name and material are both specified, go one level deeper: create a material node
|
if node_name not in current_node.children_map:
|
||||||
# under the current variant node, and then add the quality/quality_changes metadata into the
|
current_node.children_map[node_name] = QualityNode()
|
||||||
# material node.
|
current_node = cast(QualityNode, current_node.children_map[node_name])
|
||||||
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])
|
|
||||||
|
|
||||||
material_node.addQualityMetadata(quality_type, metadata)
|
current_intermediate_node_info_idx += 1
|
||||||
|
|
||||||
else:
|
current_node.addQualityMetadata(quality_type, metadata)
|
||||||
# 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)
|
|
||||||
|
|
||||||
# Initialize the lookup tree for quality_changes profiles with following structure:
|
# Initialize the lookup tree for quality_changes profiles with following structure:
|
||||||
# <machine> -> <quality_type> -> <name>
|
# <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:
|
# To find the quality container for the GlobalStack, check in the following fall-back manner:
|
||||||
# (1) the machine-specific node
|
# (1) the machine-specific node
|
||||||
# (2) the generic node
|
# (2) the generic node
|
||||||
machine_node = self._machine_variant_material_quality_type_to_quality_dict.get(machine_definition_id)
|
machine_node = self._machine_nozzle_buildplate_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)
|
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]
|
nodes_to_check = [machine_node, default_machine_node]
|
||||||
|
|
||||||
# Iterate over all quality_types in the 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
|
quality_group_dict[quality_type] = quality_group
|
||||||
break
|
break
|
||||||
|
|
||||||
|
buildplate_name = machine.getBuildplateName()
|
||||||
|
|
||||||
# Iterate over all extruders to find quality containers for each extruder
|
# Iterate over all extruders to find quality containers for each extruder
|
||||||
for position, extruder in machine.extruders.items():
|
for position, extruder in machine.extruders.items():
|
||||||
variant_name = None
|
nozzle_name = None
|
||||||
if extruder.variant.getId() != "empty_variant":
|
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.
|
# 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.
|
# The root material IDs in this list are in prioritized order.
|
||||||
root_material_id_list = []
|
root_material_id_list = []
|
||||||
has_material = False # flag indicating whether this extruder has a material assigned
|
has_material = False # flag indicating whether this extruder has a material assigned
|
||||||
|
root_material_id = None
|
||||||
if extruder.material.getId() != "empty_material":
|
if extruder.material.getId() != "empty_material":
|
||||||
has_material = True
|
has_material = True
|
||||||
root_material_id = extruder.material.getMetaDataEntry("base_file")
|
root_material_id = extruder.material.getMetaDataEntry("base_file")
|
||||||
@ -264,34 +255,39 @@ class QualityManager(QObject):
|
|||||||
# Here we construct a list of nodes we want to look for qualities with the highest priority first.
|
# 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
|
# The use case is that, when we look for qualities for a machine, we first want to search in the following
|
||||||
# order:
|
# order:
|
||||||
# 1. machine-variant-and-material-specific qualities if exist
|
# 1. machine-nozzle-buildplate-and-material-specific qualities if exist
|
||||||
# 2. machine-variant-specific qualities if exist
|
# 2. machine-nozzle-and-material-specific qualities if exist
|
||||||
# 3. machine-material-specific qualities if exist
|
# 3. machine-nozzle-specific qualities if exist
|
||||||
# 4. machine-specific qualities if exist
|
# 4. machine-material-specific qualities if exist
|
||||||
# 5. generic 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
|
# 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
|
# the list with priorities as the order. Later, we just need to loop over each node in this list and fetch
|
||||||
# qualities from there.
|
# qualities from there.
|
||||||
|
node_info_list_0 = [nozzle_name, buildplate_name, root_material_id]
|
||||||
nodes_to_check = []
|
nodes_to_check = []
|
||||||
|
|
||||||
if variant_name:
|
# This function tries to recursively find the deepest (the most specific) branch and add those nodes to
|
||||||
# In this case, we have both a specific variant and a specific material
|
# the search list in the order described above. So, by iterating over that search node list, we first look
|
||||||
variant_node = machine_node.getChildNode(variant_name)
|
# in the more specific branches and then the less specific (generic) ones.
|
||||||
if variant_node and has_material:
|
def addNodesToCheck(node, nodes_to_check_list, node_info_list, node_info_idx):
|
||||||
for root_material_id in root_material_id_list:
|
if node_info_idx < len(node_info_list):
|
||||||
material_node = variant_node.getChildNode(root_material_id)
|
node_name = node_info_list[node_info_idx]
|
||||||
if material_node:
|
if node_name is not None:
|
||||||
nodes_to_check.append(material_node)
|
current_node = node.getChildNode(node_name)
|
||||||
break
|
if current_node is not None and has_material:
|
||||||
nodes_to_check.append(variant_node)
|
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:
|
||||||
if has_material:
|
for rmid in root_material_id_list:
|
||||||
for root_material_id in root_material_id_list:
|
material_node = node.getChildNode(rmid)
|
||||||
material_node = machine_node.getChildNode(root_material_id)
|
if material_node:
|
||||||
if material_node:
|
nodes_to_check_list.append(material_node)
|
||||||
nodes_to_check.append(material_node)
|
break
|
||||||
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]
|
nodes_to_check += [machine_node, default_machine_node]
|
||||||
for node in nodes_to_check:
|
for node in nodes_to_check:
|
||||||
@ -309,8 +305,8 @@ class QualityManager(QObject):
|
|||||||
quality_group_dict[quality_type] = quality_group
|
quality_group_dict[quality_type] = quality_group
|
||||||
|
|
||||||
quality_group = quality_group_dict[quality_type]
|
quality_group = quality_group_dict[quality_type]
|
||||||
quality_group.nodes_for_extruders[position] = quality_node
|
if position not in quality_group.nodes_for_extruders:
|
||||||
break
|
quality_group.nodes_for_extruders[position] = quality_node
|
||||||
|
|
||||||
# Update availabilities for each quality group
|
# Update availabilities for each quality group
|
||||||
self._updateQualityGroupsAvailability(machine, quality_group_dict.values())
|
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:
|
# To find the quality container for the GlobalStack, check in the following fall-back manner:
|
||||||
# (1) the machine-specific node
|
# (1) the machine-specific node
|
||||||
# (2) the generic node
|
# (2) the generic node
|
||||||
machine_node = self._machine_variant_material_quality_type_to_quality_dict.get(machine_definition_id)
|
machine_node = self._machine_nozzle_buildplate_material_quality_type_to_quality_dict.get(machine_definition_id)
|
||||||
default_machine_node = self._machine_variant_material_quality_type_to_quality_dict.get(
|
default_machine_node = self._machine_nozzle_buildplate_material_quality_type_to_quality_dict.get(
|
||||||
self._default_machine_definition_id)
|
self._default_machine_definition_id)
|
||||||
nodes_to_check = [machine_node, default_machine_node]
|
nodes_to_check = [machine_node, default_machine_node]
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
# Copyright (c) 2018 Ultimaker B.V.
|
# Copyright (c) 2018 Ultimaker B.V.
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
from enum import Enum
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from typing import Optional, TYPE_CHECKING
|
from typing import Optional, TYPE_CHECKING
|
||||||
|
|
||||||
@ -11,20 +10,13 @@ from UM.Settings.ContainerRegistry import ContainerRegistry
|
|||||||
from UM.Util import parseBool
|
from UM.Util import parseBool
|
||||||
|
|
||||||
from cura.Machines.ContainerNode import ContainerNode
|
from cura.Machines.ContainerNode import ContainerNode
|
||||||
|
from cura.Machines.VariantType import VariantType, ALL_VARIANT_TYPES
|
||||||
from cura.Settings.GlobalStack import GlobalStack
|
from cura.Settings.GlobalStack import GlobalStack
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
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
|
# VariantManager is THE place to look for a specific variant. It maintains two variant lookup tables with the following
|
||||||
# structure:
|
# structure:
|
||||||
|
15
cura/Machines/VariantType.py
Normal file
15
cura/Machines/VariantType.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
|
class VariantType(Enum):
|
||||||
|
BUILD_PLATE = "buildplate"
|
||||||
|
NOZZLE = "nozzle"
|
||||||
|
|
||||||
|
|
||||||
|
ALL_VARIANT_TYPES = (VariantType.BUILD_PLATE, VariantType.NOZZLE)
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["VariantType", "ALL_VARIANT_TYPES"]
|
@ -1,112 +1,147 @@
|
|||||||
# Copyright (c) 2015 Ultimaker B.V.
|
# Copyright (c) 2018 Ultimaker B.V.
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
from UM.Scene.Iterator import Iterator
|
import sys
|
||||||
from UM.Scene.SceneNode import SceneNode
|
|
||||||
from functools import cmp_to_key
|
from shapely import affinity
|
||||||
from UM.Application import Application
|
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):
|
def __init__(self, scene_node):
|
||||||
super().__init__(scene_node) # Call super to make multiple inheritence work.
|
from cura.CuraApplication import CuraApplication
|
||||||
self._hit_map = [[]]
|
self._global_stack = CuraApplication.getInstance().getGlobalContainerStack()
|
||||||
self._original_node_list = []
|
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()
|
||||||
|
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):
|
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 = []
|
node_list = []
|
||||||
for node in self._scene_node.getChildren():
|
for node in self._scene_node.getChildren():
|
||||||
if not issubclass(type(node), SceneNode):
|
if not issubclass(type(node), SceneNode):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if node.callDecoration("getConvexHull"):
|
convex_hull = node.callDecoration("getConvexHull")
|
||||||
node_list.append(node)
|
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:
|
node_list.append({"node": node,
|
||||||
self._node_stack = node_list[:]
|
"min_coord": [convex_hull_polygon.bounds[0], convex_hull_polygon.bounds[1]],
|
||||||
return
|
})
|
||||||
|
|
||||||
# Copy the list
|
node_list = sorted(node_list, key = lambda d: d["min_coord"])
|
||||||
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
|
|
||||||
|
|
||||||
|
self._node_stack = [d["node"] for d in node_list]
|
||||||
|
@ -229,7 +229,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||||||
return offset_hull
|
return offset_hull
|
||||||
|
|
||||||
def _getHeadAndFans(self):
|
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):
|
def _compute2DConvexHeadFull(self):
|
||||||
return self._compute2DConvexHull().getMinkowskiHull(self._getHeadAndFans())
|
return self._compute2DConvexHull().getMinkowskiHull(self._getHeadAndFans())
|
||||||
|
@ -8,7 +8,7 @@ from UM.Logger import Logger
|
|||||||
from UM.Settings.Interfaces import DefinitionContainerInterface
|
from UM.Settings.Interfaces import DefinitionContainerInterface
|
||||||
from UM.Settings.InstanceContainer import InstanceContainer
|
from UM.Settings.InstanceContainer import InstanceContainer
|
||||||
|
|
||||||
from cura.Machines.VariantManager import VariantType
|
from cura.Machines.VariantType import VariantType
|
||||||
from .GlobalStack import GlobalStack
|
from .GlobalStack import GlobalStack
|
||||||
from .ExtruderStack import ExtruderStack
|
from .ExtruderStack import ExtruderStack
|
||||||
|
|
||||||
|
@ -55,6 +55,12 @@ class GlobalStack(CuraContainerStack):
|
|||||||
return "machine_stack"
|
return "machine_stack"
|
||||||
return configuration_type
|
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.
|
## Add an extruder to the list of extruders of this stack.
|
||||||
#
|
#
|
||||||
# \param extruder The extruder to add.
|
# \param extruder The extruder to add.
|
||||||
@ -96,6 +102,9 @@ class GlobalStack(CuraContainerStack):
|
|||||||
|
|
||||||
# Handle the "resolve" property.
|
# Handle the "resolve" property.
|
||||||
#TODO: Why the hell does this involve threading?
|
#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):
|
if self._shouldResolve(key, property_name, context):
|
||||||
current_thread = threading.current_thread()
|
current_thread = threading.current_thread()
|
||||||
self._resolving_settings[current_thread.name].add(key)
|
self._resolving_settings[current_thread.name].add(key)
|
||||||
@ -172,6 +181,9 @@ class GlobalStack(CuraContainerStack):
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def getHeadAndFansCoordinates(self):
|
||||||
|
return self.getProperty("machine_head_with_fans_polygon", "value")
|
||||||
|
|
||||||
|
|
||||||
## private:
|
## private:
|
||||||
global_stack_mime = MimeType(
|
global_stack_mime = MimeType(
|
||||||
|
@ -21,9 +21,6 @@ from UM.Settings.SettingFunction import SettingFunction
|
|||||||
from UM.Signal import postponeSignals, CompressTechnique
|
from UM.Signal import postponeSignals, CompressTechnique
|
||||||
|
|
||||||
import cura.CuraApplication
|
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.Machines.QualityManager import getMachineDefinitionIDForQualitySearch
|
||||||
from cura.PrinterOutputDevice import PrinterOutputDevice
|
from cura.PrinterOutputDevice import PrinterOutputDevice
|
||||||
from cura.PrinterOutput.ConfigurationModel import ConfigurationModel
|
from cura.PrinterOutput.ConfigurationModel import ConfigurationModel
|
||||||
@ -44,12 +41,16 @@ if TYPE_CHECKING:
|
|||||||
from cura.Machines.MaterialManager import MaterialManager
|
from cura.Machines.MaterialManager import MaterialManager
|
||||||
from cura.Machines.QualityManager import QualityManager
|
from cura.Machines.QualityManager import QualityManager
|
||||||
from cura.Machines.VariantManager import VariantManager
|
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):
|
class MachineManager(QObject):
|
||||||
def __init__(self, parent: QObject = None) -> None:
|
def __init__(self, parent: QObject = None) -> None:
|
||||||
super().__init__(parent)
|
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._global_container_stack = None # type: Optional[GlobalStack]
|
||||||
|
|
||||||
self._current_root_material_id = {} # type: Dict[str, str]
|
self._current_root_material_id = {} # type: Dict[str, str]
|
||||||
@ -1087,7 +1088,7 @@ class MachineManager(QObject):
|
|||||||
self.activeQualityGroupChanged.emit()
|
self.activeQualityGroupChanged.emit()
|
||||||
self.activeQualityChangesGroupChanged.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:
|
if self._global_container_stack is None:
|
||||||
return
|
return
|
||||||
if quality_group is None:
|
if quality_group is None:
|
||||||
@ -1118,7 +1119,7 @@ class MachineManager(QObject):
|
|||||||
self.activeQualityGroupChanged.emit()
|
self.activeQualityGroupChanged.emit()
|
||||||
self.activeQualityChangesGroupChanged.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())
|
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]
|
containers = [n.getContainer() for n in nodes if n is not None]
|
||||||
for container in containers:
|
for container in containers:
|
||||||
@ -1126,7 +1127,7 @@ class MachineManager(QObject):
|
|||||||
container.setMetaDataEntry("quality_type", "not_supported")
|
container.setMetaDataEntry("quality_type", "not_supported")
|
||||||
quality_changes_group.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:
|
if self._global_container_stack is None:
|
||||||
return #Can't change that.
|
return #Can't change that.
|
||||||
quality_type = quality_changes_group.quality_type
|
quality_type = quality_changes_group.quality_type
|
||||||
@ -1170,20 +1171,20 @@ class MachineManager(QObject):
|
|||||||
self.activeQualityGroupChanged.emit()
|
self.activeQualityGroupChanged.emit()
|
||||||
self.activeQualityChangesGroupChanged.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:
|
if container_node.getContainer() is None or self._global_container_stack is None:
|
||||||
return
|
return
|
||||||
self._global_container_stack.extruders[position].variant = container_node.getContainer()
|
self._global_container_stack.extruders[position].variant = container_node.getContainer()
|
||||||
self.activeVariantChanged.emit()
|
self.activeVariantChanged.emit()
|
||||||
|
|
||||||
def _setGlobalVariant(self, container_node: ContainerNode) -> None:
|
def _setGlobalVariant(self, container_node: "ContainerNode") -> None:
|
||||||
if self._global_container_stack is None:
|
if self._global_container_stack is None:
|
||||||
return
|
return
|
||||||
self._global_container_stack.variant = container_node.getContainer()
|
self._global_container_stack.variant = container_node.getContainer()
|
||||||
if not self._global_container_stack.variant:
|
if not self._global_container_stack.variant:
|
||||||
self._global_container_stack.variant = self._application.empty_variant_container
|
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:
|
if self._global_container_stack is None:
|
||||||
return
|
return
|
||||||
if container_node and container_node.getContainer():
|
if container_node and container_node.getContainer():
|
||||||
@ -1256,13 +1257,17 @@ class MachineManager(QObject):
|
|||||||
else:
|
else:
|
||||||
position_list = [position]
|
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:
|
for position_item in position_list:
|
||||||
extruder = self._global_container_stack.extruders[position_item]
|
extruder = self._global_container_stack.extruders[position_item]
|
||||||
|
|
||||||
current_material_base_name = extruder.material.getMetaDataEntry("base_file")
|
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():
|
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 UM.Settings.Interfaces import PropertyEvaluationContext
|
||||||
from cura.Settings.CuraContainerStack import _ContainerIndexes
|
from cura.Settings.CuraContainerStack import _ContainerIndexes
|
||||||
@ -1271,7 +1276,8 @@ class MachineManager(QObject):
|
|||||||
material_diameter = extruder.getProperty("material_diameter", "value", context)
|
material_diameter = extruder.getProperty("material_diameter", "value", context)
|
||||||
candidate_materials = self._material_manager.getAvailableMaterials(
|
candidate_materials = self._material_manager.getAvailableMaterials(
|
||||||
self._global_container_stack.definition,
|
self._global_container_stack.definition,
|
||||||
current_variant_name,
|
current_nozzle_name,
|
||||||
|
buildplate_name,
|
||||||
material_diameter)
|
material_diameter)
|
||||||
|
|
||||||
if not candidate_materials:
|
if not candidate_materials:
|
||||||
@ -1284,7 +1290,7 @@ class MachineManager(QObject):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# The current material is not available, find the preferred one
|
# 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:
|
if material_node is not None:
|
||||||
self._setMaterial(position_item, material_node)
|
self._setMaterial(position_item, material_node)
|
||||||
|
|
||||||
@ -1326,7 +1332,12 @@ class MachineManager(QObject):
|
|||||||
for extruder_configuration in configuration.extruderConfigurations:
|
for extruder_configuration in configuration.extruderConfigurations:
|
||||||
position = str(extruder_configuration.position)
|
position = str(extruder_configuration.position)
|
||||||
variant_container_node = self._variant_manager.getVariantNode(self._global_container_stack.definition.getId(), extruder_configuration.hotendID)
|
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:
|
if variant_container_node:
|
||||||
self._setVariantNode(position, variant_container_node)
|
self._setVariantNode(position, variant_container_node)
|
||||||
else:
|
else:
|
||||||
@ -1378,7 +1389,7 @@ class MachineManager(QObject):
|
|||||||
return bool(containers)
|
return bool(containers)
|
||||||
|
|
||||||
@pyqtSlot("QVariant")
|
@pyqtSlot("QVariant")
|
||||||
def setGlobalVariant(self, container_node: ContainerNode) -> None:
|
def setGlobalVariant(self, container_node: "ContainerNode") -> None:
|
||||||
self.blurSettings.emit()
|
self.blurSettings.emit()
|
||||||
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
||||||
self._setGlobalVariant(container_node)
|
self._setGlobalVariant(container_node)
|
||||||
@ -1389,12 +1400,17 @@ class MachineManager(QObject):
|
|||||||
def setMaterialById(self, position: str, root_material_id: str) -> None:
|
def setMaterialById(self, position: str, root_material_id: str) -> None:
|
||||||
if self._global_container_stack is None:
|
if self._global_container_stack is None:
|
||||||
return
|
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
|
machine_definition_id = self._global_container_stack.definition.id
|
||||||
position = str(position)
|
position = str(position)
|
||||||
extruder_stack = self._global_container_stack.extruders[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_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)
|
self.setMaterial(position, material_node)
|
||||||
|
|
||||||
## global_stack: if you want to provide your own global_stack instead of the current active one
|
## 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)
|
self.setVariant(position, variant_node)
|
||||||
|
|
||||||
@pyqtSlot(str, "QVariant")
|
@pyqtSlot(str, "QVariant")
|
||||||
def setVariant(self, position: str, container_node: ContainerNode) -> None:
|
def setVariant(self, position: str, container_node: "ContainerNode") -> None:
|
||||||
position = str(position)
|
position = str(position)
|
||||||
self.blurSettings.emit()
|
self.blurSettings.emit()
|
||||||
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
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
|
## Optionally provide global_stack if you want to use your own
|
||||||
# The active global_stack is treated differently.
|
# The active global_stack is treated differently.
|
||||||
@pyqtSlot(QObject)
|
@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 global_stack is not None and global_stack != self._global_container_stack:
|
||||||
if quality_group is None:
|
if quality_group is None:
|
||||||
Logger.log("e", "Could not set quality group because quality group is None")
|
Logger.log("e", "Could not set quality group because quality group is None")
|
||||||
@ -1474,11 +1490,11 @@ class MachineManager(QObject):
|
|||||||
self._application.discardOrKeepProfileChanges()
|
self._application.discardOrKeepProfileChanges()
|
||||||
|
|
||||||
@pyqtProperty(QObject, fset = setQualityGroup, notify = activeQualityGroupChanged)
|
@pyqtProperty(QObject, fset = setQualityGroup, notify = activeQualityGroupChanged)
|
||||||
def activeQualityGroup(self) -> Optional[QualityGroup]:
|
def activeQualityGroup(self) -> Optional["QualityGroup"]:
|
||||||
return self._current_quality_group
|
return self._current_quality_group
|
||||||
|
|
||||||
@pyqtSlot(QObject)
|
@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()
|
self.blurSettings.emit()
|
||||||
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
||||||
self._setQualityChangesGroup(quality_changes_group)
|
self._setQualityChangesGroup(quality_changes_group)
|
||||||
@ -1497,7 +1513,7 @@ class MachineManager(QObject):
|
|||||||
stack.userChanges.clear()
|
stack.userChanges.clear()
|
||||||
|
|
||||||
@pyqtProperty(QObject, fset = setQualityChangesGroup, notify = activeQualityChangesGroupChanged)
|
@pyqtProperty(QObject, fset = setQualityChangesGroup, notify = activeQualityChangesGroupChanged)
|
||||||
def activeQualityChangesGroup(self) -> Optional[QualityChangesGroup]:
|
def activeQualityChangesGroup(self) -> Optional["QualityChangesGroup"]:
|
||||||
return self._current_quality_changes_group
|
return self._current_quality_changes_group
|
||||||
|
|
||||||
@pyqtProperty(str, notify = activeQualityGroupChanged)
|
@pyqtProperty(str, notify = activeQualityGroupChanged)
|
||||||
|
41
cura/Settings/SidebarCustomMenuItemsModel.py
Normal file
41
cura/Settings/SidebarCustomMenuItemsModel.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from UM.Qt.ListModel import ListModel
|
||||||
|
from PyQt5.QtCore import pyqtSlot, Qt
|
||||||
|
|
||||||
|
|
||||||
|
class SidebarCustomMenuItemsModel(ListModel):
|
||||||
|
name_role = Qt.UserRole + 1
|
||||||
|
actions_role = Qt.UserRole + 2
|
||||||
|
menu_item_role = Qt.UserRole + 3
|
||||||
|
menu_item_icon_name_role = Qt.UserRole + 5
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.addRoleName(self.name_role, "name")
|
||||||
|
self.addRoleName(self.actions_role, "actions")
|
||||||
|
self.addRoleName(self.menu_item_role, "menu_item")
|
||||||
|
self.addRoleName(self.menu_item_icon_name_role, "iconName")
|
||||||
|
self._updateExtensionList()
|
||||||
|
|
||||||
|
def _updateExtensionList(self)-> None:
|
||||||
|
from cura.CuraApplication import CuraApplication
|
||||||
|
for menu_item in CuraApplication.getInstance().getSidebarCustomMenuItems():
|
||||||
|
|
||||||
|
self.appendItem({
|
||||||
|
"name": menu_item["name"],
|
||||||
|
"icon_name": menu_item["icon_name"],
|
||||||
|
"actions": menu_item["actions"],
|
||||||
|
"menu_item": menu_item["menu_item"]
|
||||||
|
})
|
||||||
|
|
||||||
|
@pyqtSlot(str, "QVariantList", "QVariantMap")
|
||||||
|
def callMenuItemMethod(self, menu_item_name: str, menu_item_actions: list, kwargs: Any) -> None:
|
||||||
|
for item in self._items:
|
||||||
|
if menu_item_name == item["name"]:
|
||||||
|
for method in menu_item_actions:
|
||||||
|
getattr(item["menu_item"], method)(kwargs)
|
||||||
|
break
|
@ -24,6 +24,7 @@ from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType
|
|||||||
from UM.Job import Job
|
from UM.Job import Job
|
||||||
from UM.Preferences import Preferences
|
from UM.Preferences import Preferences
|
||||||
|
|
||||||
|
from cura.Machines.VariantType import VariantType
|
||||||
from cura.Settings.CuraStackBuilder import CuraStackBuilder
|
from cura.Settings.CuraStackBuilder import CuraStackBuilder
|
||||||
from cura.Settings.ExtruderStack import ExtruderStack
|
from cura.Settings.ExtruderStack import ExtruderStack
|
||||||
from cura.Settings.GlobalStack import GlobalStack
|
from cura.Settings.GlobalStack import GlobalStack
|
||||||
@ -889,7 +890,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||||||
parser = self._machine_info.variant_info.parser
|
parser = self._machine_info.variant_info.parser
|
||||||
variant_name = parser["general"]["name"]
|
variant_name = parser["general"]["name"]
|
||||||
|
|
||||||
from cura.Machines.VariantManager import VariantType
|
|
||||||
variant_type = VariantType.BUILD_PLATE
|
variant_type = VariantType.BUILD_PLATE
|
||||||
|
|
||||||
node = variant_manager.getVariantNode(global_stack.definition.getId(), variant_name, variant_type)
|
node = variant_manager.getVariantNode(global_stack.definition.getId(), variant_name, variant_type)
|
||||||
@ -905,7 +905,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||||||
parser = extruder_info.variant_info.parser
|
parser = extruder_info.variant_info.parser
|
||||||
|
|
||||||
variant_name = parser["general"]["name"]
|
variant_name = parser["general"]["name"]
|
||||||
from cura.Machines.VariantManager import VariantType
|
|
||||||
variant_type = VariantType.NOZZLE
|
variant_type = VariantType.NOZZLE
|
||||||
|
|
||||||
node = variant_manager.getVariantNode(global_stack.definition.getId(), variant_name, variant_type)
|
node = variant_manager.getVariantNode(global_stack.definition.getId(), variant_name, variant_type)
|
||||||
@ -929,12 +928,16 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||||||
root_material_id = extruder_info.root_material_id
|
root_material_id = extruder_info.root_material_id
|
||||||
root_material_id = self._old_new_materials.get(root_material_id, 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
|
# get material diameter of this extruder
|
||||||
machine_material_diameter = extruder_stack.materialDiameter
|
machine_material_diameter = extruder_stack.materialDiameter
|
||||||
material_node = material_manager.getMaterialNode(global_stack.definition.getId(),
|
material_node = material_manager.getMaterialNode(global_stack.definition.getId(),
|
||||||
extruder_stack.variant.getName(),
|
extruder_stack.variant.getName(),
|
||||||
|
build_plate_id,
|
||||||
machine_material_diameter,
|
machine_material_diameter,
|
||||||
root_material_id)
|
root_material_id)
|
||||||
|
|
||||||
if material_node is not None and material_node.getContainer() is not None:
|
if material_node is not None and material_node.getContainer() is not None:
|
||||||
extruder_stack.material = material_node.getContainer()
|
extruder_stack.material = material_node.getContainer()
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ class StartJobResult(IntEnum):
|
|||||||
|
|
||||||
## Formatter class that handles token expansion in start/end gcode
|
## Formatter class that handles token expansion in start/end gcode
|
||||||
class GcodeStartEndFormatter(Formatter):
|
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),
|
# 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
|
# and a default_extruder_nr to use when no extruder_nr is specified
|
||||||
|
|
||||||
|
@ -40,33 +40,37 @@ Item {
|
|||||||
|
|
||||||
property bool layersVisible: true
|
property bool layersVisible: true
|
||||||
|
|
||||||
function getUpperValueFromSliderHandle () {
|
function getUpperValueFromSliderHandle() {
|
||||||
return upperHandle.getValue()
|
return upperHandle.getValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
function setUpperValue (value) {
|
function setUpperValue(value) {
|
||||||
upperHandle.setValue(value)
|
upperHandle.setValue(value)
|
||||||
updateRangeHandle()
|
updateRangeHandle()
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLowerValueFromSliderHandle () {
|
function getLowerValueFromSliderHandle() {
|
||||||
return lowerHandle.getValue()
|
return lowerHandle.getValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
function setLowerValue (value) {
|
function setLowerValue(value) {
|
||||||
lowerHandle.setValue(value)
|
lowerHandle.setValue(value)
|
||||||
updateRangeHandle()
|
updateRangeHandle()
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateRangeHandle () {
|
function updateRangeHandle() {
|
||||||
rangeHandle.height = lowerHandle.y - (upperHandle.y + upperHandle.height)
|
rangeHandle.height = lowerHandle.y - (upperHandle.y + upperHandle.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the active handle to show only one label at a time
|
// set the active handle to show only one label at a time
|
||||||
function setActiveHandle (handle) {
|
function setActiveHandle(handle) {
|
||||||
activeHandle = handle
|
activeHandle = handle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeValue(value) {
|
||||||
|
return Math.min(Math.max(value, sliderRoot.minimumValue), sliderRoot.maximumValue)
|
||||||
|
}
|
||||||
|
|
||||||
// slider track
|
// slider track
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: track
|
id: track
|
||||||
@ -188,6 +192,8 @@ Item {
|
|||||||
|
|
||||||
// set the slider position based on the upper value
|
// set the slider position based on the upper value
|
||||||
function setValue (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)
|
UM.SimulationView.setCurrentLayer(value)
|
||||||
|
|
||||||
@ -274,6 +280,8 @@ Item {
|
|||||||
|
|
||||||
// set the slider position based on the lower value
|
// set the slider position based on the lower value
|
||||||
function setValue (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)
|
UM.SimulationView.setMinimumLayer(value)
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ Item {
|
|||||||
|
|
||||||
// value properties
|
// value properties
|
||||||
property real maximumValue: 100
|
property real maximumValue: 100
|
||||||
|
property real minimumValue: 0
|
||||||
property bool roundValues: true
|
property bool roundValues: true
|
||||||
property real handleValue: maximumValue
|
property real handleValue: maximumValue
|
||||||
|
|
||||||
@ -47,6 +48,10 @@ Item {
|
|||||||
rangeHandle.width = handle.x - sliderRoot.handleSize
|
rangeHandle.width = handle.x - sliderRoot.handleSize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeValue(value) {
|
||||||
|
return Math.min(Math.max(value, sliderRoot.minimumValue), sliderRoot.maximumValue)
|
||||||
|
}
|
||||||
|
|
||||||
// slider track
|
// slider track
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: track
|
id: track
|
||||||
@ -110,6 +115,8 @@ Item {
|
|||||||
|
|
||||||
// set the slider position based on the value
|
// set the slider position based on the value
|
||||||
function setValue (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)
|
UM.SimulationView.setCurrentPath(value)
|
||||||
|
|
||||||
|
@ -25,10 +25,12 @@ from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
|
|||||||
|
|
||||||
from UM.Settings.SettingInstance import SettingInstance
|
from UM.Settings.SettingInstance import SettingInstance
|
||||||
|
|
||||||
|
import numpy
|
||||||
|
|
||||||
class SupportEraser(Tool):
|
class SupportEraser(Tool):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._shortcut_key = Qt.Key_G
|
self._shortcut_key = Qt.Key_E
|
||||||
self._controller = self.getController()
|
self._controller = self.getController()
|
||||||
|
|
||||||
self._selection_pass = None
|
self._selection_pass = None
|
||||||
@ -96,8 +98,7 @@ class SupportEraser(Tool):
|
|||||||
|
|
||||||
node.setName("Eraser")
|
node.setName("Eraser")
|
||||||
node.setSelectable(True)
|
node.setSelectable(True)
|
||||||
mesh = MeshBuilder()
|
mesh = self._createCube(10)
|
||||||
mesh.addCube(10,10,10)
|
|
||||||
node.setMeshData(mesh.build())
|
node.setMeshData(mesh.build())
|
||||||
|
|
||||||
active_build_plate = CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate
|
active_build_plate = CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate
|
||||||
@ -160,3 +161,28 @@ class SupportEraser(Tool):
|
|||||||
self._skip_press = False
|
self._skip_press = False
|
||||||
|
|
||||||
self._had_selection = has_selection
|
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
|
||||||
|
@ -16,9 +16,9 @@ Item
|
|||||||
|
|
||||||
MouseArea
|
MouseArea
|
||||||
{
|
{
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: OutputDevice.setActivePrinter(null)
|
onClicked: OutputDevice.setActivePrinter(null)
|
||||||
z: 0
|
z: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
Button
|
Button
|
||||||
@ -28,7 +28,7 @@ Item
|
|||||||
anchors.bottomMargin: UM.Theme.getSize("default_margin").width
|
anchors.bottomMargin: UM.Theme.getSize("default_margin").width
|
||||||
anchors.right: cameraImage.right
|
anchors.right: cameraImage.right
|
||||||
|
|
||||||
// TODO: Harcoded sizes
|
// TODO: Hardcoded sizes
|
||||||
width: 20 * screenScaleFactor
|
width: 20 * screenScaleFactor
|
||||||
height: 20 * screenScaleFactor
|
height: 20 * screenScaleFactor
|
||||||
|
|
||||||
|
@ -39,12 +39,12 @@ class SendMaterialJob(Job):
|
|||||||
try:
|
try:
|
||||||
remote_materials_list = json.loads(remote_materials_list)
|
remote_materials_list = json.loads(remote_materials_list)
|
||||||
except json.JSONDecodeError:
|
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
|
return
|
||||||
try:
|
try:
|
||||||
remote_materials_by_guid = {material["guid"]: material for material in remote_materials_list} #Index by GUID.
|
remote_materials_by_guid = {material["guid"]: material for material in remote_materials_list} #Index by GUID.
|
||||||
except KeyError:
|
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
|
return
|
||||||
|
|
||||||
container_registry = ContainerRegistry.getInstance()
|
container_registry = ContainerRegistry.getInstance()
|
||||||
|
@ -15,7 +15,7 @@ from cura.PrinterOutput.GenericOutputController import GenericOutputController
|
|||||||
from .AutoDetectBaudJob import AutoDetectBaudJob
|
from .AutoDetectBaudJob import AutoDetectBaudJob
|
||||||
from .avr_isp import stk500v2, intelHex
|
from .avr_isp import stk500v2, intelHex
|
||||||
|
|
||||||
from PyQt5.QtCore import pyqtSlot, pyqtSignal, pyqtProperty
|
from PyQt5.QtCore import pyqtSlot, pyqtSignal, pyqtProperty, QUrl
|
||||||
|
|
||||||
from serial import Serial, SerialException, SerialTimeoutException
|
from serial import Serial, SerialException, SerialTimeoutException
|
||||||
from threading import Thread, Event
|
from threading import Thread, Event
|
||||||
@ -146,8 +146,11 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||||||
|
|
||||||
@pyqtSlot(str)
|
@pyqtSlot(str)
|
||||||
def updateFirmware(self, file):
|
def updateFirmware(self, file):
|
||||||
# the file path is qurl encoded.
|
# the file path could be url-encoded.
|
||||||
self._firmware_location = file.replace("file://", "")
|
if file.startswith("file://"):
|
||||||
|
self._firmware_location = QUrl(file).toLocalFile()
|
||||||
|
else:
|
||||||
|
self._firmware_location = file
|
||||||
self.showFirmwareInterface()
|
self.showFirmwareInterface()
|
||||||
self.setFirmwareUpdateState(FirmwareUpdateState.updating)
|
self.setFirmwareUpdateState(FirmwareUpdateState.updating)
|
||||||
self._update_firmware_thread.start()
|
self._update_firmware_thread.start()
|
||||||
|
@ -6,21 +6,22 @@ import io
|
|||||||
import json #To parse the product-to-id mapping file.
|
import json #To parse the product-to-id mapping file.
|
||||||
import os.path #To find the product-to-id mapping.
|
import os.path #To find the product-to-id mapping.
|
||||||
import sys
|
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
|
import xml.etree.ElementTree as ET
|
||||||
from typing import Dict
|
|
||||||
from typing import Iterator
|
|
||||||
|
|
||||||
from UM.Resources import Resources
|
from UM.Resources import Resources
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
from cura.CuraApplication import CuraApplication
|
|
||||||
import UM.Dictionary
|
import UM.Dictionary
|
||||||
from UM.Settings.InstanceContainer import InstanceContainer
|
from UM.Settings.InstanceContainer import InstanceContainer
|
||||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||||
from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
|
from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
|
||||||
|
|
||||||
|
from cura.CuraApplication import CuraApplication
|
||||||
|
from cura.Machines.VariantType import VariantType
|
||||||
|
|
||||||
from .XmlMaterialValidator import XmlMaterialValidator
|
from .XmlMaterialValidator import XmlMaterialValidator
|
||||||
|
|
||||||
|
|
||||||
## Handles serializing and deserializing material containers from an XML file
|
## Handles serializing and deserializing material containers from an XML file
|
||||||
class XmlMaterialProfile(InstanceContainer):
|
class XmlMaterialProfile(InstanceContainer):
|
||||||
CurrentFdmMaterialVersion = "1.3"
|
CurrentFdmMaterialVersion = "1.3"
|
||||||
@ -269,7 +270,6 @@ class XmlMaterialProfile(InstanceContainer):
|
|||||||
buildplate_dict = {} # type: Dict[str, Any]
|
buildplate_dict = {} # type: Dict[str, Any]
|
||||||
for variant_name, variant_dict in machine_variant_map[definition_id].items():
|
for variant_name, variant_dict in machine_variant_map[definition_id].items():
|
||||||
variant_type = variant_dict["variant_node"].metadata["hardware_type"]
|
variant_type = variant_dict["variant_node"].metadata["hardware_type"]
|
||||||
from cura.Machines.VariantManager import VariantType
|
|
||||||
variant_type = VariantType(variant_type)
|
variant_type = VariantType(variant_type)
|
||||||
if variant_type == VariantType.NOZZLE:
|
if variant_type == VariantType.NOZZLE:
|
||||||
# The hotend identifier is not the containers name, but its "name".
|
# The hotend identifier is not the containers name, but its "name".
|
||||||
@ -693,74 +693,38 @@ class XmlMaterialProfile(InstanceContainer):
|
|||||||
if buildplate_id is None:
|
if buildplate_id is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
from cura.Machines.VariantManager import VariantType
|
|
||||||
variant_manager = CuraApplication.getInstance().getVariantManager()
|
variant_manager = CuraApplication.getInstance().getVariantManager()
|
||||||
variant_node = variant_manager.getVariantNode(machine_id, buildplate_id,
|
variant_node = variant_manager.getVariantNode(machine_id, buildplate_id,
|
||||||
variant_type = VariantType.BUILD_PLATE)
|
variant_type = VariantType.BUILD_PLATE)
|
||||||
if not variant_node:
|
if not variant_node:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
buildplate_compatibility = machine_compatibility
|
_, buildplate_unmapped_settings_dict = self._getSettingsDictForNode(buildplate)
|
||||||
buildplate_recommended = machine_compatibility
|
|
||||||
settings = buildplate.iterfind("./um:setting", self.__namespaces)
|
buildplate_compatibility = buildplate_unmapped_settings_dict.get("hardware compatible",
|
||||||
for entry in settings:
|
machine_compatibility)
|
||||||
key = entry.get("key")
|
buildplate_recommended = buildplate_unmapped_settings_dict.get("hardware recommended",
|
||||||
if key in self.__unmapped_settings:
|
machine_compatibility)
|
||||||
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_map["buildplate_compatible"][buildplate_id] = buildplate_compatibility
|
buildplate_map["buildplate_compatible"][buildplate_id] = buildplate_compatibility
|
||||||
buildplate_map["buildplate_recommended"][buildplate_id] = buildplate_recommended
|
buildplate_map["buildplate_recommended"][buildplate_id] = buildplate_recommended
|
||||||
|
|
||||||
hotends = machine.iterfind("./um:hotend", self.__namespaces)
|
hotends = machine.iterfind("./um:hotend", self.__namespaces)
|
||||||
for hotend in hotends:
|
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")
|
hotend_name = hotend.get("id")
|
||||||
if hotend_name is None:
|
if hotend_name is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
variant_manager = CuraApplication.getInstance().getVariantManager()
|
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:
|
if not variant_node:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
hotend_compatibility = machine_compatibility
|
hotend_mapped_settings, hotend_unmapped_settings = self._getSettingsDictForNode(hotend)
|
||||||
hotend_setting_values = {}
|
hotend_compatibility = hotend_unmapped_settings.get("hardware compatible", machine_compatibility)
|
||||||
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
|
|
||||||
|
|
||||||
|
# Generate container ID for the hotend-specific material container
|
||||||
new_hotend_specific_material_id = self.getId() + "_" + machine_id + "_" + hotend_name.replace(" ", "_")
|
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
|
# 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"]
|
new_hotend_material.getMetaData()["buildplate_recommended"] = buildplate_map["buildplate_recommended"]
|
||||||
|
|
||||||
cached_hotend_setting_properties = cached_machine_setting_properties.copy()
|
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)
|
new_hotend_material.setCachedValues(cached_hotend_setting_properties)
|
||||||
|
|
||||||
@ -794,6 +758,61 @@ class XmlMaterialProfile(InstanceContainer):
|
|||||||
if is_new_material:
|
if is_new_material:
|
||||||
containers_to_add.append(new_hotend_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
|
# 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
|
# a workable ID for that machine, so there is no need to continue
|
||||||
break
|
break
|
||||||
@ -801,6 +820,54 @@ class XmlMaterialProfile(InstanceContainer):
|
|||||||
for container_to_add in containers_to_add:
|
for container_to_add in containers_to_add:
|
||||||
ContainerRegistry.getInstance().addContainer(container_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
|
@classmethod
|
||||||
def deserializeMetadata(cls, serialized: str, container_id: str) -> List[Dict[str, Any]]:
|
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).
|
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)
|
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
|
# 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
|
# a workable ID for that machine, so there is no need to continue
|
||||||
break
|
break
|
||||||
|
@ -2127,6 +2127,7 @@
|
|||||||
"type": "float",
|
"type": "float",
|
||||||
"default_value": 60,
|
"default_value": 60,
|
||||||
"value": "default_material_bed_temperature",
|
"value": "default_material_bed_temperature",
|
||||||
|
"resolve": "max(extruderValues('material_bed_temperature'))",
|
||||||
"minimum_value": "-273.15",
|
"minimum_value": "-273.15",
|
||||||
"minimum_value_warning": "0",
|
"minimum_value_warning": "0",
|
||||||
"maximum_value_warning": "130",
|
"maximum_value_warning": "130",
|
||||||
|
@ -4036,7 +4036,7 @@ msgstr "&Ansicht"
|
|||||||
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:184
|
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:184
|
||||||
msgctxt "@title:menu"
|
msgctxt "@title:menu"
|
||||||
msgid "&Settings"
|
msgid "&Settings"
|
||||||
msgstr "&Einstellungen"
|
msgstr "&Konfiguration"
|
||||||
|
|
||||||
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:186
|
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:186
|
||||||
msgctxt "@title:menu menubar:toplevel"
|
msgctxt "@title:menu menubar:toplevel"
|
||||||
|
@ -231,8 +231,8 @@ UM.MainWindow
|
|||||||
onObjectRemoved: settingsMenu.removeItem(object)
|
onObjectRemoved: settingsMenu.removeItem(object)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Temporary hidden, add back again when feature ready
|
// TODO Only show in dev mode. Remove check when feature ready
|
||||||
// BuildplateMenu { title: catalog.i18nc("@title:menu", "&Build plate"); visible: Cura.MachineManager.hasVariantBuildplates }
|
BuildplateMenu { title: catalog.i18nc("@title:menu", "&Build plate"); visible: CuraSDKVersion == "dev" ? Cura.MachineManager.hasVariantBuildplates : false }
|
||||||
ProfileMenu { title: catalog.i18nc("@title:menu", "&Profile"); }
|
ProfileMenu { title: catalog.i18nc("@title:menu", "&Profile"); }
|
||||||
|
|
||||||
MenuSeparator { }
|
MenuSeparator { }
|
||||||
|
@ -48,15 +48,14 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function sliceOrStopSlicing() {
|
function sliceOrStopSlicing() {
|
||||||
if ([1, 5].indexOf(base.backendState) != -1)
|
try {
|
||||||
{
|
if ([1, 5].indexOf(base.backendState) != -1) {
|
||||||
prepareButton.preparingToSlice = true;
|
CuraApplication.backend.forceSlice();
|
||||||
CuraApplication.backend.forceSlice();
|
} else {
|
||||||
}
|
CuraApplication.backend.stopSlicing();
|
||||||
else
|
}
|
||||||
{
|
} catch (e) {
|
||||||
prepareButton.preparingToSlice = false;
|
console.log('Could not start or stop slicing', e)
|
||||||
CuraApplication.backend.stopSlicing();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,10 +167,10 @@ Item {
|
|||||||
// Prepare button, only shows if auto_slice is off
|
// Prepare button, only shows if auto_slice is off
|
||||||
Button {
|
Button {
|
||||||
id: prepareButton
|
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")
|
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
|
// 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
|
visible: base.backendState != "undefined" && !autoSlice && ([1, 2, 4].indexOf(base.backendState) != -1) && base.activity
|
||||||
property bool autoSlice
|
property bool autoSlice
|
||||||
height: UM.Theme.getSize("save_button_save_to_button").height
|
height: UM.Theme.getSize("save_button_save_to_button").height
|
||||||
@ -181,23 +180,7 @@ Item {
|
|||||||
anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width
|
anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width
|
||||||
|
|
||||||
// 1 = not started, 4 = error, 5 = disabled
|
// 1 = not started, 4 = error, 5 = disabled
|
||||||
text: {
|
text: [1, 4, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@label:Printjob", "Prepare") : catalog.i18nc("@label:Printjob", "Cancel")
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onClicked:
|
onClicked:
|
||||||
{
|
{
|
||||||
sliceOrStopSlicing();
|
sliceOrStopSlicing();
|
||||||
@ -254,17 +237,6 @@ Item {
|
|||||||
}
|
}
|
||||||
label: Item { }
|
label: Item { }
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: UM.Backend
|
|
||||||
onStateChanged:
|
|
||||||
{
|
|
||||||
if ([2, 3].indexOf(UM.Backend.state) != -1)
|
|
||||||
{
|
|
||||||
prepareButton.preparingToSlice = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
|
@ -561,6 +561,28 @@ Item
|
|||||||
visible: machineExtruderCount.properties.value > 1
|
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
|
MenuItem
|
||||||
{
|
{
|
||||||
//: Settings context menu action
|
//: Settings context menu action
|
||||||
|
@ -476,8 +476,8 @@ Column
|
|||||||
{
|
{
|
||||||
id: buildplateRow
|
id: buildplateRow
|
||||||
height: UM.Theme.getSize("sidebar_setup").height
|
height: UM.Theme.getSize("sidebar_setup").height
|
||||||
// TODO Temporary hidden, add back again when feature ready
|
// TODO Only show in dev mode. Remove check when feature ready
|
||||||
visible: false //Cura.MachineManager.hasVariantBuildplates && !sidebar.hideSettings
|
visible: CuraSDKVersion == "dev" ? Cura.MachineManager.hasVariantBuildplates && !sidebar.hideSettings : false
|
||||||
|
|
||||||
anchors
|
anchors
|
||||||
{
|
{
|
||||||
@ -533,6 +533,7 @@ Column
|
|||||||
rightMargin: UM.Theme.getSize("sidebar_margin").width
|
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
|
Label
|
||||||
{
|
{
|
||||||
id: materialCompatibilityLabel
|
id: materialCompatibilityLabel
|
||||||
@ -542,7 +543,7 @@ Column
|
|||||||
text: catalog.i18nc("@label", "Use glue with this material combination")
|
text: catalog.i18nc("@label", "Use glue with this material combination")
|
||||||
font: UM.Theme.getFont("very_small")
|
font: UM.Theme.getFont("very_small")
|
||||||
color: UM.Theme.getColor("text")
|
color: UM.Theme.getColor("text")
|
||||||
visible: buildplateCompatibilityError || buildplateCompatibilityWarning
|
visible: CuraSDKVersion == "dev" ? false : buildplateCompatibilityError || buildplateCompatibilityWarning
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
opacity: 0.5
|
opacity: 0.5
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) 2015 Ultimaker B.V.
|
// Copyright (c) 2018 Ultimaker B.V.
|
||||||
// Cura is released under the terms of the LGPLv3 or higher.
|
// Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
import QtQuick 2.2
|
import QtQuick 2.2
|
||||||
@ -21,8 +21,8 @@ Item
|
|||||||
{
|
{
|
||||||
id: buttons;
|
id: buttons;
|
||||||
|
|
||||||
anchors.bottom: parent.bottom;
|
anchors.bottom: parent.bottom
|
||||||
anchors.left: parent.left;
|
anchors.left: parent.left
|
||||||
spacing: UM.Theme.getSize("button_lining").width
|
spacing: UM.Theme.getSize("button_lining").width
|
||||||
|
|
||||||
Repeater
|
Repeater
|
||||||
@ -34,20 +34,22 @@ Item
|
|||||||
height: childrenRect.height
|
height: childrenRect.height
|
||||||
Button
|
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
|
iconSource: (UM.Theme.getIcon(model.icon) != "") ? UM.Theme.getIcon(model.icon) : "file:///" + model.location + "/" + model.icon
|
||||||
checkable: true
|
checkable: true
|
||||||
checked: model.active
|
checked: model.active
|
||||||
enabled: model.enabled && UM.Selection.hasSelection && UM.Controller.toolsEnabled
|
enabled: model.enabled && UM.Selection.hasSelection && UM.Controller.toolsEnabled
|
||||||
style: UM.Theme.styles.tool_button
|
style: UM.Theme.styles.tool_button
|
||||||
|
|
||||||
onCheckedChanged: {
|
onCheckedChanged:
|
||||||
if (checked) {
|
{
|
||||||
base.activeY = y
|
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.
|
//just catch the click so we do not trigger that behaviour.
|
||||||
MouseArea
|
MouseArea
|
||||||
{
|
{
|
||||||
@ -57,7 +59,7 @@ Item
|
|||||||
forceActiveFocus() //First grab focus, so all the text fields are updated
|
forceActiveFocus() //First grab focus, so all the text fields are updated
|
||||||
if(parent.checked)
|
if(parent.checked)
|
||||||
{
|
{
|
||||||
UM.Controller.setActiveTool(null)
|
UM.Controller.setActiveTool(null);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -96,11 +98,13 @@ Item
|
|||||||
|
|
||||||
width:
|
width:
|
||||||
{
|
{
|
||||||
if (panel.item && panel.width > 0){
|
if (panel.item && panel.width > 0)
|
||||||
return Math.max(panel.width + 2 * UM.Theme.getSize("default_margin").width)
|
{
|
||||||
|
return Math.max(panel.width + 2 * UM.Theme.getSize("default_margin").width);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
return 0
|
{
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
height: panel.item ? panel.height + 2 * UM.Theme.getSize("default_margin").height : 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;
|
x: UM.Theme.getSize("default_margin").width;
|
||||||
y: UM.Theme.getSize("default_margin").height;
|
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;
|
enabled: UM.Controller.toolsEnabled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,6 +152,6 @@ Item
|
|||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
visible: toolHint.text != "";
|
visible: toolHint.text != ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
[general]
|
||||||
|
version = 4
|
||||||
|
name = Extra Fine
|
||||||
|
definition = ultimaker_s5
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
setting_version = 5
|
||||||
|
type = quality
|
||||||
|
quality_type = high
|
||||||
|
weight = 1
|
||||||
|
material = generic_abs
|
||||||
|
variant = AA 0.4
|
||||||
|
buildplate = Aluminum
|
||||||
|
|
||||||
|
[values]
|
||||||
|
cool_min_speed = 12
|
||||||
|
layer_height_0 = 0.17
|
||||||
|
machine_nozzle_cool_down_speed = 0.8
|
||||||
|
machine_nozzle_heat_up_speed = 1.5
|
||||||
|
material_bed_temperature = 90
|
||||||
|
material_bed_temperature_layer_0 = 100
|
||||||
|
material_print_temperature = =default_material_print_temperature + 5
|
||||||
|
material_initial_print_temperature = =material_print_temperature - 15
|
||||||
|
material_final_print_temperature = =material_print_temperature - 20
|
||||||
|
prime_blob_enable = False
|
||||||
|
prime_tower_enable = False
|
||||||
|
speed_print = 50
|
||||||
|
speed_layer_0 = =math.ceil(speed_print * 20 / 50)
|
||||||
|
speed_topbottom = =math.ceil(speed_print * 30 / 50)
|
||||||
|
speed_wall = =math.ceil(speed_print * 30 / 50)
|
||||||
|
|
||||||
|
infill_line_width = =round(line_width * 0.4 / 0.35, 2)
|
||||||
|
speed_infill = =math.ceil(speed_print * 40 / 50)
|
||||||
|
|
@ -0,0 +1,53 @@
|
|||||||
|
[general]
|
||||||
|
version = 4
|
||||||
|
name = Extra Fine
|
||||||
|
definition = ultimaker_s5
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
setting_version = 5
|
||||||
|
type = quality
|
||||||
|
quality_type = high
|
||||||
|
weight = 1
|
||||||
|
material = generic_cpe_plus
|
||||||
|
variant = AA 0.4
|
||||||
|
buildplate = Aluminum
|
||||||
|
|
||||||
|
[values]
|
||||||
|
acceleration_enabled = True
|
||||||
|
acceleration_print = 4000
|
||||||
|
cool_fan_speed_max = 50
|
||||||
|
cool_min_speed = 5
|
||||||
|
infill_line_width = =round(line_width * 0.35 / 0.35, 2)
|
||||||
|
infill_overlap = 0
|
||||||
|
infill_wipe_dist = 0
|
||||||
|
jerk_enabled = True
|
||||||
|
jerk_print = 25
|
||||||
|
layer_height_0 = 0.17
|
||||||
|
machine_min_cool_heat_time_window = 15
|
||||||
|
machine_nozzle_cool_down_speed = 0.85
|
||||||
|
machine_nozzle_heat_up_speed = 1.5
|
||||||
|
material_bed_temperature = 105
|
||||||
|
material_bed_temperature_layer_0 = 115
|
||||||
|
material_final_print_temperature = =material_print_temperature - 10
|
||||||
|
material_initial_print_temperature = =material_print_temperature - 5
|
||||||
|
material_print_temperature = =default_material_print_temperature + 2
|
||||||
|
material_print_temperature_layer_0 = =material_print_temperature
|
||||||
|
multiple_mesh_overlap = 0
|
||||||
|
prime_blob_enable = False
|
||||||
|
prime_tower_enable = True
|
||||||
|
prime_tower_wipe_enabled = True
|
||||||
|
retraction_combing = off
|
||||||
|
retraction_extrusion_window = 1
|
||||||
|
retraction_hop = 0.2
|
||||||
|
retraction_hop_enabled = False
|
||||||
|
retraction_hop_only_when_collides = True
|
||||||
|
skin_overlap = 20
|
||||||
|
speed_layer_0 = =math.ceil(speed_print * 20 / 40)
|
||||||
|
speed_print = 40
|
||||||
|
speed_topbottom = =math.ceil(speed_print * 30 / 35)
|
||||||
|
|
||||||
|
speed_wall = =math.ceil(speed_print * 35 / 40)
|
||||||
|
speed_wall_0 = =math.ceil(speed_wall * 30 / 35)
|
||||||
|
support_bottom_distance = =support_z_distance
|
||||||
|
support_z_distance = =layer_height
|
||||||
|
wall_0_inset = 0
|
@ -0,0 +1,32 @@
|
|||||||
|
[general]
|
||||||
|
version = 4
|
||||||
|
name = Extra Fine
|
||||||
|
definition = ultimaker_s5
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
setting_version = 5
|
||||||
|
type = quality
|
||||||
|
quality_type = high
|
||||||
|
weight = 1
|
||||||
|
material = generic_cpe
|
||||||
|
variant = AA 0.4
|
||||||
|
buildplate = Aluminum
|
||||||
|
|
||||||
|
[values]
|
||||||
|
cool_min_speed = 12
|
||||||
|
layer_height_0 = 0.17
|
||||||
|
machine_nozzle_cool_down_speed = 0.85
|
||||||
|
machine_nozzle_heat_up_speed = 1.5
|
||||||
|
material_bed_temperature = 80
|
||||||
|
material_bed_temperature_layer_0 = 90
|
||||||
|
material_print_temperature = =default_material_print_temperature - 5
|
||||||
|
material_initial_print_temperature = =material_print_temperature - 5
|
||||||
|
material_final_print_temperature = =material_print_temperature - 10
|
||||||
|
prime_blob_enable = False
|
||||||
|
speed_print = 50
|
||||||
|
speed_layer_0 = =math.ceil(speed_print * 20 / 50)
|
||||||
|
speed_topbottom = =math.ceil(speed_print * 30 / 50)
|
||||||
|
speed_wall = =math.ceil(speed_print * 30 / 50)
|
||||||
|
|
||||||
|
infill_pattern = zigzag
|
||||||
|
speed_infill = =math.ceil(speed_print * 40 / 50)
|
@ -0,0 +1,71 @@
|
|||||||
|
[general]
|
||||||
|
version = 4
|
||||||
|
name = Extra Fine
|
||||||
|
definition = ultimaker_s5
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
setting_version = 5
|
||||||
|
type = quality
|
||||||
|
quality_type = high
|
||||||
|
weight = 1
|
||||||
|
material = generic_pc
|
||||||
|
variant = AA 0.4
|
||||||
|
buildplate = Aluminum
|
||||||
|
|
||||||
|
[values]
|
||||||
|
acceleration_enabled = True
|
||||||
|
acceleration_print = 4000
|
||||||
|
adhesion_type = brim
|
||||||
|
brim_width = 10
|
||||||
|
cool_fan_full_at_height = =layer_height_0 + layer_height
|
||||||
|
cool_fan_speed_max = 50
|
||||||
|
cool_min_layer_time_fan_speed_max = 5
|
||||||
|
cool_min_speed = 8
|
||||||
|
infill_line_width = =round(line_width * 0.4 / 0.35, 2)
|
||||||
|
infill_overlap = 0
|
||||||
|
infill_overlap_mm = 0.05
|
||||||
|
infill_pattern = triangles
|
||||||
|
infill_wipe_dist = 0.1
|
||||||
|
jerk_enabled = True
|
||||||
|
jerk_print = 25
|
||||||
|
layer_height_0 = 0.17
|
||||||
|
machine_min_cool_heat_time_window = 15
|
||||||
|
machine_nozzle_cool_down_speed = 0.85
|
||||||
|
machine_nozzle_heat_up_speed = 1.5
|
||||||
|
material_bed_temperature = 115
|
||||||
|
material_bed_temperature_layer_0 = 125
|
||||||
|
material_final_print_temperature = =material_print_temperature - 10
|
||||||
|
material_initial_print_temperature = =material_print_temperature - 5
|
||||||
|
material_print_temperature = =default_material_print_temperature - 10
|
||||||
|
material_standby_temperature = 100
|
||||||
|
multiple_mesh_overlap = 0
|
||||||
|
ooze_shield_angle = 40
|
||||||
|
prime_blob_enable = False
|
||||||
|
prime_tower_enable = True
|
||||||
|
prime_tower_wipe_enabled = True
|
||||||
|
raft_airgap = 0.25
|
||||||
|
raft_interface_thickness = =max(layer_height * 1.5, 0.225)
|
||||||
|
retraction_count_max = 80
|
||||||
|
retraction_extrusion_window = 1
|
||||||
|
retraction_hop = 2
|
||||||
|
retraction_hop_only_when_collides = True
|
||||||
|
retraction_min_travel = 0.8
|
||||||
|
retraction_prime_speed = 15
|
||||||
|
skin_overlap = 30
|
||||||
|
speed_layer_0 = =math.ceil(speed_print * 25 / 50)
|
||||||
|
speed_print = 50
|
||||||
|
speed_topbottom = =math.ceil(speed_print * 25 / 50)
|
||||||
|
|
||||||
|
speed_wall = =math.ceil(speed_print * 40 / 50)
|
||||||
|
speed_wall_0 = =math.ceil(speed_wall * 25 / 40)
|
||||||
|
support_bottom_distance = =support_z_distance
|
||||||
|
support_interface_density = 87.5
|
||||||
|
support_interface_pattern = lines
|
||||||
|
switch_extruder_prime_speed = 15
|
||||||
|
switch_extruder_retraction_amount = 20
|
||||||
|
switch_extruder_retraction_speeds = 35
|
||||||
|
wall_0_inset = 0
|
||||||
|
wall_line_width_x = =round(line_width * 0.4 / 0.35, 2)
|
||||||
|
wall_thickness = 1.2
|
||||||
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
|||||||
|
[general]
|
||||||
|
version = 4
|
||||||
|
name = Fast
|
||||||
|
definition = ultimaker_s5
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
setting_version = 5
|
||||||
|
type = quality
|
||||||
|
quality_type = draft
|
||||||
|
weight = -2
|
||||||
|
material = generic_abs
|
||||||
|
variant = AA 0.8
|
||||||
|
buildplate = Aluminum
|
||||||
|
|
||||||
|
[values]
|
||||||
|
layer_height_0 = 0.3
|
||||||
|
line_width = =machine_nozzle_size * 0.875
|
||||||
|
material_bed_temperature = 90
|
||||||
|
material_bed_temperature_layer_0 = 100
|
||||||
|
material_print_temperature = =default_material_print_temperature + 20
|
||||||
|
material_standby_temperature = 100
|
||||||
|
prime_blob_enable = False
|
||||||
|
speed_print = 50
|
||||||
|
speed_topbottom = =math.ceil(speed_print * 30 / 50)
|
||||||
|
speed_wall = =math.ceil(speed_print * 40 / 50)
|
||||||
|
speed_wall_0 = =math.ceil(speed_wall * 30 / 40)
|
||||||
|
retract_at_layer_change = False
|
@ -0,0 +1,43 @@
|
|||||||
|
[general]
|
||||||
|
version = 4
|
||||||
|
name = Fast - Experimental
|
||||||
|
definition = ultimaker_s5
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
setting_version = 5
|
||||||
|
type = quality
|
||||||
|
quality_type = draft
|
||||||
|
weight = -2
|
||||||
|
material = generic_cpe_plus
|
||||||
|
variant = AA 0.8
|
||||||
|
buildplate = Aluminum
|
||||||
|
|
||||||
|
[values]
|
||||||
|
brim_width = 14
|
||||||
|
cool_fan_full_at_height = =layer_height_0 + 14 * layer_height
|
||||||
|
infill_before_walls = True
|
||||||
|
layer_height_0 = 0.3
|
||||||
|
line_width = =machine_nozzle_size * 0.9375
|
||||||
|
machine_nozzle_cool_down_speed = 0.9
|
||||||
|
machine_nozzle_heat_up_speed = 1.4
|
||||||
|
material_bed_temperature = 105
|
||||||
|
material_bed_temperature_layer_0 = 115
|
||||||
|
material_print_temperature = =default_material_print_temperature - 10
|
||||||
|
material_print_temperature_layer_0 = =material_print_temperature
|
||||||
|
material_standby_temperature = 100
|
||||||
|
prime_blob_enable = False
|
||||||
|
prime_tower_enable = True
|
||||||
|
retraction_combing = off
|
||||||
|
retraction_hop = 0.1
|
||||||
|
retraction_hop_enabled = False
|
||||||
|
skin_overlap = 0
|
||||||
|
speed_layer_0 = =math.ceil(speed_print * 15 / 50)
|
||||||
|
speed_print = 50
|
||||||
|
speed_slowdown_layers = 15
|
||||||
|
speed_topbottom = =math.ceil(speed_print * 35 / 50)
|
||||||
|
speed_wall = =math.ceil(speed_print * 40 / 50)
|
||||||
|
speed_wall_0 = =math.ceil(speed_wall * 35 / 40)
|
||||||
|
support_bottom_distance = =support_z_distance
|
||||||
|
support_line_width = =round(line_width * 0.6 / 0.7, 2)
|
||||||
|
support_z_distance = =layer_height
|
||||||
|
top_bottom_thickness = 1.2
|
@ -0,0 +1,29 @@
|
|||||||
|
[general]
|
||||||
|
version = 4
|
||||||
|
name = Fast
|
||||||
|
definition = ultimaker_s5
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
setting_version = 5
|
||||||
|
type = quality
|
||||||
|
quality_type = draft
|
||||||
|
weight = -2
|
||||||
|
material = generic_cpe
|
||||||
|
variant = AA 0.8
|
||||||
|
buildplate = Aluminum
|
||||||
|
|
||||||
|
[values]
|
||||||
|
brim_width = 15
|
||||||
|
layer_height_0 = 0.3
|
||||||
|
line_width = =machine_nozzle_size * 0.875
|
||||||
|
material_bed_temperature = 80
|
||||||
|
material_bed_temperature_layer_0 = 90
|
||||||
|
material_print_temperature = =default_material_print_temperature + 15
|
||||||
|
material_standby_temperature = 100
|
||||||
|
prime_blob_enable = False
|
||||||
|
prime_tower_enable = True
|
||||||
|
speed_print = 40
|
||||||
|
speed_topbottom = =math.ceil(speed_print * 25 / 40)
|
||||||
|
speed_wall = =math.ceil(speed_print * 30 / 40)
|
||||||
|
|
||||||
|
jerk_travel = 50
|
@ -0,0 +1,36 @@
|
|||||||
|
[general]
|
||||||
|
version = 4
|
||||||
|
name = Fast - Experimental
|
||||||
|
definition = ultimaker_s5
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
setting_version = 5
|
||||||
|
type = quality
|
||||||
|
quality_type = draft
|
||||||
|
weight = 0
|
||||||
|
material = generic_pc
|
||||||
|
variant = AA 0.8
|
||||||
|
buildplate = Aluminum
|
||||||
|
|
||||||
|
[values]
|
||||||
|
brim_width = 10
|
||||||
|
cool_fan_full_at_height = =layer_height_0 + 14 * layer_height
|
||||||
|
infill_before_walls = True
|
||||||
|
layer_height_0 = 0.3
|
||||||
|
line_width = =machine_nozzle_size * 0.875
|
||||||
|
material_bed_temperature = 115
|
||||||
|
material_bed_temperature_layer_0 = 125
|
||||||
|
material_print_temperature = =default_material_print_temperature - 5
|
||||||
|
material_print_temperature_layer_0 = =material_print_temperature
|
||||||
|
material_standby_temperature = 100
|
||||||
|
prime_blob_enable = False
|
||||||
|
raft_airgap = 0.5
|
||||||
|
raft_margin = 15
|
||||||
|
skin_overlap = 0
|
||||||
|
speed_layer_0 = =math.ceil(speed_print * 15 / 50)
|
||||||
|
speed_print = 50
|
||||||
|
speed_slowdown_layers = 15
|
||||||
|
speed_topbottom = =math.ceil(speed_print * 25 / 50)
|
||||||
|
speed_wall = =math.ceil(speed_print * 40 / 50)
|
||||||
|
speed_wall_0 = =math.ceil(speed_wall * 30 / 40)
|
||||||
|
support_line_width = =round(line_width * 0.6 / 0.7, 2)
|
@ -0,0 +1,53 @@
|
|||||||
|
[general]
|
||||||
|
version = 4
|
||||||
|
name = Fast
|
||||||
|
definition = ultimaker_s5
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
setting_version = 5
|
||||||
|
type = quality
|
||||||
|
quality_type = draft
|
||||||
|
weight = -2
|
||||||
|
material = generic_pp
|
||||||
|
variant = AA 0.8
|
||||||
|
buildplate = Aluminum
|
||||||
|
|
||||||
|
[values]
|
||||||
|
brim_width = 25
|
||||||
|
cool_min_layer_time_fan_speed_max = 6
|
||||||
|
cool_min_speed = 17
|
||||||
|
top_skin_expand_distance = =line_width * 2
|
||||||
|
infill_before_walls = True
|
||||||
|
infill_line_width = =round(line_width * 0.7 / 0.8, 2)
|
||||||
|
infill_pattern = tetrahedral
|
||||||
|
jerk_prime_tower = =math.ceil(jerk_print * 25 / 25)
|
||||||
|
jerk_support = =math.ceil(jerk_print * 25 / 25)
|
||||||
|
jerk_wall_0 = =math.ceil(jerk_wall * 15 / 25)
|
||||||
|
material_bed_temperature_layer_0 = =material_bed_temperature + 5
|
||||||
|
material_print_temperature = =default_material_print_temperature - 2
|
||||||
|
material_print_temperature_layer_0 = =default_material_print_temperature + 2
|
||||||
|
material_standby_temperature = 100
|
||||||
|
multiple_mesh_overlap = 0.2
|
||||||
|
prime_tower_enable = True
|
||||||
|
prime_tower_flow = 100
|
||||||
|
prime_tower_min_volume = 10
|
||||||
|
retract_at_layer_change = False
|
||||||
|
retraction_count_max = 12
|
||||||
|
retraction_extra_prime_amount = 0.5
|
||||||
|
retraction_hop = 0.5
|
||||||
|
retraction_min_travel = 1.5
|
||||||
|
retraction_prime_speed = 15
|
||||||
|
skin_line_width = =round(line_width * 0.78 / 0.8, 2)
|
||||||
|
|
||||||
|
speed_wall_x = =math.ceil(speed_wall * 30 / 30)
|
||||||
|
support_bottom_distance = =support_z_distance
|
||||||
|
support_line_width = =round(line_width * 0.7 / 0.8, 2)
|
||||||
|
support_offset = =line_width
|
||||||
|
switch_extruder_prime_speed = 15
|
||||||
|
switch_extruder_retraction_amount = 20
|
||||||
|
switch_extruder_retraction_speeds = 45
|
||||||
|
top_bottom_thickness = 1.6
|
||||||
|
travel_compensate_overlapping_walls_0_enabled = False
|
||||||
|
wall_0_wipe_dist = =line_width * 2
|
||||||
|
wall_line_width_x = =round(line_width * 0.8 / 0.8, 2)
|
||||||
|
wall_thickness = 1.6
|
Loading…
x
Reference in New Issue
Block a user