Merge branch 'mypy_fixes' of ssh://github.com/Ultimaker/Cura into mypy_fixes

This commit is contained in:
Ghostkeeper 2018-06-13 17:03:31 +02:00
commit 5bf553c63c
No known key found for this signature in database
GPG Key ID: 5252B696FB5E7C7A
253 changed files with 805 additions and 742 deletions

View File

@ -1,32 +1,30 @@
# Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from typing import Tuple, Optional
from cura.Backups.BackupsManager import BackupsManager from cura.Backups.BackupsManager import BackupsManager
## The back-ups API provides a version-proof bridge between Cura's
# BackupManager and plug-ins that hook into it.
#
# Usage:
# ``from cura.API import CuraAPI
# api = CuraAPI()
# api.backups.createBackup()
# api.backups.restoreBackup(my_zip_file, {"cura_release": "3.1"})``
class Backups: class Backups:
"""
The backups API provides a version-proof bridge between Cura's BackupManager and plugins that hook into it.
Usage:
from cura.API import CuraAPI
api = CuraAPI()
api.backups.createBackup()
api.backups.restoreBackup(my_zip_file, {"cura_release": "3.1"})
"""
manager = BackupsManager() # Re-used instance of the backups manager. manager = BackupsManager() # Re-used instance of the backups manager.
def createBackup(self) -> (bytes, dict): ## Create a new back-up using the BackupsManager.
""" # \return Tuple containing a ZIP file with the back-up data and a dict
Create a new backup using the BackupsManager. # with metadata about the back-up.
:return: Tuple containing a ZIP file with the backup data and a dict with meta data about the backup. def createBackup(self) -> Tuple[Optional[bytes], Optional[dict]]:
"""
return self.manager.createBackup() return self.manager.createBackup()
## Restore a back-up using the BackupsManager.
# \param zip_file A ZIP file containing the actual back-up data.
# \param meta_data Some metadata needed for restoring a back-up, like the
# Cura version number.
def restoreBackup(self, zip_file: bytes, meta_data: dict) -> None: def restoreBackup(self, zip_file: bytes, meta_data: dict) -> None:
"""
Restore a backup using the BackupManager.
:param zip_file: A ZIP file containing the actual backup data.
:param meta_data: Some meta data needed for restoring a backup, like the Cura version number.
"""
return self.manager.restoreBackup(zip_file, meta_data) return self.manager.restoreBackup(zip_file, meta_data)

View File

@ -3,14 +3,13 @@
from UM.PluginRegistry import PluginRegistry from UM.PluginRegistry import PluginRegistry
from cura.API.Backups import Backups from cura.API.Backups import Backups
## The official Cura API that plug-ins can use to interact with Cura.
#
# Python does not technically prevent talking to other classes as well, but
# this API provides a version-safe interface with proper deprecation warnings
# etc. Usage of any other methods than the ones provided in this API can cause
# plug-ins to be unstable.
class CuraAPI: class CuraAPI:
"""
The official Cura API that plugins can use to interact with Cura.
Python does not technically prevent talking to other classes as well,
but 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 plugins to be unstable.
"""
# 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

View File

@ -1,10 +1,12 @@
# Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from typing import List
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Logger import Logger from UM.Logger import Logger
from UM.Math.Polygon import Polygon from UM.Math.Polygon import Polygon
from UM.Math.Vector import Vector from UM.Math.Vector import Vector
from UM.Scene.SceneNode import SceneNode
from cura.Arranging.ShapeArray import ShapeArray from cura.Arranging.ShapeArray import ShapeArray
from cura.Scene import ZOffsetDecorator from cura.Scene import ZOffsetDecorator
@ -85,8 +87,7 @@ class Arrange:
# \param node # \param node
# \param offset_shape_arr ShapeArray with offset, for placing the shape # \param offset_shape_arr ShapeArray with offset, for placing the shape
# \param hull_shape_arr ShapeArray without offset, used to find location # \param hull_shape_arr ShapeArray without offset, used to find location
def findNodePlacement(self, node, offset_shape_arr, hull_shape_arr, step = 1): def findNodePlacement(self, node: SceneNode, offset_shape_arr: ShapeArray, hull_shape_arr: ShapeArray, step = 1):
new_node = copy.deepcopy(node)
best_spot = self.bestSpot( best_spot = self.bestSpot(
hull_shape_arr, start_prio = self._last_priority, step = step) hull_shape_arr, start_prio = self._last_priority, step = step)
x, y = best_spot.x, best_spot.y x, y = best_spot.x, best_spot.y
@ -95,21 +96,21 @@ class Arrange:
self._last_priority = best_spot.priority self._last_priority = best_spot.priority
# Ensure that the object is above the build platform # Ensure that the object is above the build platform
new_node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator) node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator)
if new_node.getBoundingBox(): if node.getBoundingBox():
center_y = new_node.getWorldPosition().y - new_node.getBoundingBox().bottom center_y = node.getWorldPosition().y - node.getBoundingBox().bottom
else: else:
center_y = 0 center_y = 0
if x is not None: # We could find a place if x is not None: # We could find a place
new_node.setPosition(Vector(x, center_y, y)) node.setPosition(Vector(x, center_y, y))
found_spot = True found_spot = True
self.place(x, y, offset_shape_arr) # place the object in arranger self.place(x, y, offset_shape_arr) # place the object in arranger
else: else:
Logger.log("d", "Could not find spot!"), Logger.log("d", "Could not find spot!"),
found_spot = False found_spot = False
new_node.setPosition(Vector(200, center_y, 100)) node.setPosition(Vector(200, center_y, 100))
return new_node, found_spot return found_spot
## Fill priority, center is best. Lower value is better ## Fill priority, center is best. Lower value is better
# This is a strategy for the arranger. # This is a strategy for the arranger.

View File

@ -20,14 +20,14 @@ from typing import List
## Do arrangements on multiple build plates (aka builtiplexer) ## Do arrangements on multiple build plates (aka builtiplexer)
class ArrangeArray: class ArrangeArray:
def __init__(self, x: int, y: int, fixed_nodes: List[SceneNode]): def __init__(self, x: int, y: int, fixed_nodes: List[SceneNode]) -> None:
self._x = x self._x = x
self._y = y self._y = y
self._fixed_nodes = fixed_nodes self._fixed_nodes = fixed_nodes
self._count = 0 self._count = 0
self._first_empty = None self._first_empty = None
self._has_empty = False self._has_empty = False
self._arrange = [] self._arrange = [] # type: List[Arrange]
def _update_first_empty(self): def _update_first_empty(self):
for i, a in enumerate(self._arrange): for i, a in enumerate(self._arrange):
@ -48,16 +48,17 @@ class ArrangeArray:
return self._count return self._count
def get(self, index): def get(self, index):
print(self._arrange)
return self._arrange[index] return self._arrange[index]
def getFirstEmpty(self): def getFirstEmpty(self):
if not self._is_empty: if not self._has_empty:
self.add() self.add()
return self._arrange[self._first_empty] return self._arrange[self._first_empty]
class ArrangeObjectsAllBuildPlatesJob(Job): class ArrangeObjectsAllBuildPlatesJob(Job):
def __init__(self, nodes: List[SceneNode], min_offset = 8): def __init__(self, nodes: List[SceneNode], min_offset = 8) -> None:
super().__init__() super().__init__()
self._nodes = nodes self._nodes = nodes
self._min_offset = min_offset self._min_offset = min_offset

View File

@ -20,7 +20,7 @@ from typing import List
class ArrangeObjectsJob(Job): class ArrangeObjectsJob(Job):
def __init__(self, nodes: List[SceneNode], fixed_nodes: List[SceneNode], min_offset = 8): def __init__(self, nodes: List[SceneNode], fixed_nodes: List[SceneNode], min_offset = 8) -> None:
super().__init__() super().__init__()
self._nodes = nodes self._nodes = nodes
self._fixed_nodes = fixed_nodes self._fixed_nodes = fixed_nodes

View File

@ -17,26 +17,23 @@ from UM.Resources import Resources
from cura.CuraApplication import CuraApplication from cura.CuraApplication import CuraApplication
## The back-up class holds all data about a back-up.
#
# It is also responsible for reading and writing the zip file to the user data
# folder.
class Backup: class Backup:
"""
The backup class holds all data about a backup.
It is also responsible for reading and writing the zip file to the user data folder.
"""
# These files should be ignored when making a backup. # These files should be ignored when making a backup.
IGNORED_FILES = [r"cura\.log", r"plugins\.json", r"cache", r"__pycache__", r"\.qmlc", r"\.pyc"] IGNORED_FILES = [r"cura\.log", r"plugins\.json", r"cache", r"__pycache__", r"\.qmlc", r"\.pyc"]
# Re-use translation catalog. # Re-use translation catalog.
catalog = i18nCatalog("cura") catalog = i18nCatalog("cura")
def __init__(self, zip_file: bytes = None, meta_data: dict = None): def __init__(self, zip_file: bytes = None, meta_data: dict = None) -> None:
self.zip_file = zip_file # type: Optional[bytes] self.zip_file = zip_file # type: Optional[bytes]
self.meta_data = meta_data # type: Optional[dict] self.meta_data = meta_data # type: Optional[dict]
def makeFromCurrent(self) -> (bool, Optional[str]): ## Create a back-up from the current user config folder.
""" def makeFromCurrent(self) -> None:
Create a backup from the current user config folder.
"""
cura_release = CuraApplication.getInstance().getVersion() cura_release = CuraApplication.getInstance().getVersion()
version_data_dir = Resources.getDataStoragePath() version_data_dir = Resources.getDataStoragePath()
@ -57,6 +54,8 @@ class Backup:
# Create an empty buffer and write the archive to it. # Create an empty buffer and write the archive to it.
buffer = io.BytesIO() buffer = io.BytesIO()
archive = self._makeArchive(buffer, version_data_dir) archive = self._makeArchive(buffer, version_data_dir)
if archive is None:
return
files = archive.namelist() files = archive.namelist()
# Count the metadata items. We do this in a rather naive way at the moment. # Count the metadata items. We do this in a rather naive way at the moment.
@ -75,12 +74,10 @@ class Backup:
"plugin_count": str(plugin_count) "plugin_count": str(plugin_count)
} }
## Make a full archive from the given root path with the given name.
# \param root_path The root directory to archive recursively.
# \return The archive as bytes.
def _makeArchive(self, buffer: "io.BytesIO", root_path: str) -> Optional[ZipFile]: def _makeArchive(self, buffer: "io.BytesIO", root_path: str) -> Optional[ZipFile]:
"""
Make a full archive from the given root path with the given name.
:param root_path: The root directory to archive recursively.
:return: The archive as bytes.
"""
ignore_string = re.compile("|".join(self.IGNORED_FILES)) ignore_string = re.compile("|".join(self.IGNORED_FILES))
try: try:
archive = ZipFile(buffer, "w", ZIP_DEFLATED) archive = ZipFile(buffer, "w", ZIP_DEFLATED)
@ -99,15 +96,13 @@ class Backup:
"Could not create archive from user data directory: {}".format(error))) "Could not create archive from user data directory: {}".format(error)))
return None return None
## Show a UI message.
def _showMessage(self, message: str) -> None: def _showMessage(self, message: str) -> None:
"""Show a UI message"""
Message(message, title=self.catalog.i18nc("@info:title", "Backup"), lifetime=30).show() Message(message, title=self.catalog.i18nc("@info:title", "Backup"), lifetime=30).show()
## Restore this back-up.
# \return Whether we had success or not.
def restore(self) -> bool: def restore(self) -> bool:
"""
Restore this backups
:return: A boolean whether we had success or not.
"""
if not self.zip_file or not self.meta_data or not self.meta_data.get("cura_release", None): if not self.zip_file or not self.meta_data or not self.meta_data.get("cura_release", None):
# We can restore without the minimum required information. # We can restore without the minimum required information.
Logger.log("w", "Tried to restore a Cura backup without having proper data or meta data.") Logger.log("w", "Tried to restore a Cura backup without having proper data or meta data.")
@ -140,14 +135,12 @@ class Backup:
return extracted return extracted
## Extract the whole archive to the given target path.
# \param archive The archive as ZipFile.
# \param target_path The target path.
# \return Whether we had success or not.
@staticmethod @staticmethod
def _extractArchive(archive: "ZipFile", target_path: str) -> bool: def _extractArchive(archive: "ZipFile", target_path: str) -> bool:
"""
Extract the whole archive to the given target path.
:param archive: The archive as ZipFile.
:param target_path: The target path.
:return: A boolean whether we had success or not.
"""
Logger.log("d", "Removing current data in location: %s", target_path) Logger.log("d", "Removing current data in location: %s", target_path)
Resources.factoryReset() Resources.factoryReset()
Logger.log("d", "Extracting backup to location: %s", target_path) Logger.log("d", "Extracting backup to location: %s", target_path)

View File

@ -1,25 +1,24 @@
# Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from typing import Optional from typing import Optional, Tuple
from UM.Logger import Logger from UM.Logger import Logger
from cura.Backups.Backup import Backup from cura.Backups.Backup import Backup
from cura.CuraApplication import CuraApplication from cura.CuraApplication import CuraApplication
## The BackupsManager is responsible for managing the creating and restoring of
# back-ups.
#
# Back-ups themselves are represented in a different class.
class BackupsManager: class BackupsManager:
"""
The BackupsManager is responsible for managing the creating and restoring of backups.
Backups themselves are represented in a different class.
"""
def __init__(self): def __init__(self):
self._application = CuraApplication.getInstance() self._application = CuraApplication.getInstance()
def createBackup(self) -> (Optional[bytes], Optional[dict]): ## Get a back-up of the current configuration.
""" # \return A tuple containing a ZipFile (the actual back-up) and a dict
Get a backup of the current configuration. # containing some metadata (like version).
:return: A Tuple containing a ZipFile (the actual backup) and a dict containing some meta data (like version). def createBackup(self) -> Tuple[Optional[bytes], Optional[dict]]:
"""
self._disableAutoSave() self._disableAutoSave()
backup = Backup() backup = Backup()
backup.makeFromCurrent() backup.makeFromCurrent()
@ -27,12 +26,11 @@ class BackupsManager:
# We don't return a Backup here because we want plugins only to interact with our API and not full objects. # We don't return a Backup here because we want plugins only to interact with our API and not full objects.
return backup.zip_file, backup.meta_data return backup.zip_file, backup.meta_data
## Restore a back-up from a given ZipFile.
# \param zip_file A bytes object containing the actual back-up.
# \param meta_data A dict containing some metadata that is needed to
# restore the back-up correctly.
def restoreBackup(self, zip_file: bytes, meta_data: dict) -> None: def restoreBackup(self, zip_file: bytes, meta_data: dict) -> None:
"""
Restore a backup from a given ZipFile.
:param zip_file: A bytes object containing the actual backup.
:param meta_data: A dict containing some meta data that is needed to restore the backup correctly.
"""
if not meta_data.get("cura_release", None): if not meta_data.get("cura_release", None):
# If there is no "cura_release" specified in the meta data, we don't execute a backup restore. # If there is no "cura_release" specified in the meta data, we don't execute a backup restore.
Logger.log("w", "Tried to restore a backup without specifying a Cura version number.") Logger.log("w", "Tried to restore a backup without specifying a Cura version number.")
@ -47,10 +45,11 @@ class BackupsManager:
# We don't want to store the data at this point as that would override the just-restored backup. # We don't want to store the data at this point as that would override the just-restored backup.
self._application.windowClosed(save_data=False) self._application.windowClosed(save_data=False)
## Here we try to disable the auto-save plug-in as it might interfere with
# restoring a back-up.
def _disableAutoSave(self): def _disableAutoSave(self):
"""Here we try to disable the auto-save plugin as it might interfere with restoring a backup."""
self._application.setSaveDataEnabled(False) self._application.setSaveDataEnabled(False)
## Re-enable auto-save after we're done.
def _enableAutoSave(self): def _enableAutoSave(self):
"""Re-enable auto-save after we're done."""
self._application.setSaveDataEnabled(True) self._application.setSaveDataEnabled(True)

View File

@ -225,6 +225,8 @@ class CuraApplication(QtApplication):
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry from cura.Settings.CuraContainerRegistry import CuraContainerRegistry
self._container_registry_class = CuraContainerRegistry self._container_registry_class = CuraContainerRegistry
from cura.CuraPackageManager import CuraPackageManager
self._package_manager_class = CuraPackageManager
# Adds command line options to the command line parser. This should be called after the application is created and # Adds command line options to the command line parser. This should be called after the application is created and
# before the pre-start. # before the pre-start.
@ -511,7 +513,6 @@ class CuraApplication(QtApplication):
preferences.addPreference("cura/asked_dialog_on_project_save", False) preferences.addPreference("cura/asked_dialog_on_project_save", False)
preferences.addPreference("cura/choice_on_profile_override", "always_ask") preferences.addPreference("cura/choice_on_profile_override", "always_ask")
preferences.addPreference("cura/choice_on_open_project", "always_ask") preferences.addPreference("cura/choice_on_open_project", "always_ask")
preferences.addPreference("cura/not_arrange_objects_on_load", False)
preferences.addPreference("cura/use_multi_build_plate", False) preferences.addPreference("cura/use_multi_build_plate", False)
preferences.addPreference("cura/currency", "") preferences.addPreference("cura/currency", "")
@ -1601,9 +1602,7 @@ class CuraApplication(QtApplication):
self._currently_loading_files.remove(filename) self._currently_loading_files.remove(filename)
self.fileLoaded.emit(filename) self.fileLoaded.emit(filename)
arrange_objects_on_load = ( arrange_objects_on_load = not self.getPreferences().getValue("cura/use_multi_build_plate")
not self.getPreferences().getValue("cura/use_multi_build_plate") or
not self.getPreferences().getValue("cura/not_arrange_objects_on_load"))
target_build_plate = self.getMultiBuildPlateModel().activeBuildPlate if arrange_objects_on_load else -1 target_build_plate = self.getMultiBuildPlateModel().activeBuildPlate if arrange_objects_on_load else -1
root = self.getController().getScene().getRoot() root = self.getController().getScene().getRoot()
@ -1676,7 +1675,7 @@ class CuraApplication(QtApplication):
return return
# Step is for skipping tests to make it a lot faster. it also makes the outcome somewhat rougher # Step is for skipping tests to make it a lot faster. it also makes the outcome somewhat rougher
node, _ = arranger.findNodePlacement(node, offset_shape_arr, hull_shape_arr, step = 10) arranger.findNodePlacement(node, offset_shape_arr, hull_shape_arr, step = 10)
# This node is deep copied from some other node which already has a BuildPlateDecorator, but the deepcopy # This node is deep copied from some other node which already has a BuildPlateDecorator, but the deepcopy
# of BuildPlateDecorator produces one that's associated with build plate -1. So, here we need to check if # of BuildPlateDecorator produces one that's associated with build plate -1. So, here we need to check if

View File

@ -7,8 +7,11 @@ from UM.Resources import Resources #To find storage paths for some resource type
class CuraPackageManager(PackageManager): class CuraPackageManager(PackageManager):
def __init__(self, parent = None): def __init__(self, application, parent = None):
super().__init__(parent) super().__init__(application, parent)
def initialize(self):
self._installation_dirs_dict["materials"] = Resources.getStoragePath(CuraApplication.ResourceTypes.MaterialInstanceContainer) self._installation_dirs_dict["materials"] = Resources.getStoragePath(CuraApplication.ResourceTypes.MaterialInstanceContainer)
self._installation_dirs_dict["qualities"] = Resources.getStoragePath(CuraApplication.ResourceTypes.QualityInstanceContainer) self._installation_dirs_dict["qualities"] = Resources.getStoragePath(CuraApplication.ResourceTypes.QualityInstanceContainer)
super().initialize()

View File

@ -1,7 +1,7 @@
# Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from typing import Optional from typing import Optional, Any, Dict, Union, TYPE_CHECKING
from collections import OrderedDict from collections import OrderedDict
@ -9,6 +9,9 @@ from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
from UM.Logger import Logger from UM.Logger import Logger
from UM.Settings.InstanceContainer import InstanceContainer from UM.Settings.InstanceContainer import InstanceContainer
if TYPE_CHECKING:
from cura.Machines.QualityGroup import QualityGroup
## ##
# A metadata / container combination. Use getContainer() to get the container corresponding to the metadata. # A metadata / container combination. Use getContainer() to get the container corresponding to the metadata.
@ -23,10 +26,16 @@ from UM.Settings.InstanceContainer import InstanceContainer
class ContainerNode: class ContainerNode:
__slots__ = ("metadata", "container", "children_map") __slots__ = ("metadata", "container", "children_map")
def __init__(self, metadata: Optional[dict] = None): def __init__(self, metadata: Optional[Dict[str, Any]] = None) -> None:
self.metadata = metadata self.metadata = metadata
self.container = None self.container = None
self.children_map = OrderedDict() self.children_map = OrderedDict() #type: OrderedDict[str, Union[QualityGroup, ContainerNode]]
## Get an entry value from the metadata
def getMetaDataEntry(self, entry: str, default: Any = None) -> Any:
if self.metadata is None:
return default
return self.metadata.get(entry, default)
def getChildNode(self, child_key: str) -> Optional["ContainerNode"]: def getChildNode(self, child_key: str) -> Optional["ContainerNode"]:
return self.children_map.get(child_key) return self.children_map.get(child_key)
@ -50,4 +59,4 @@ class ContainerNode:
return self.container return self.container
def __str__(self) -> str: def __str__(self) -> str:
return "%s[%s]" % (self.__class__.__name__, self.metadata.get("id")) return "%s[%s]" % (self.__class__.__name__, self.getMetaDataEntry("id"))

View File

@ -18,10 +18,10 @@ from cura.Machines.MaterialNode import MaterialNode #For type checking.
class MaterialGroup: class MaterialGroup:
__slots__ = ("name", "is_read_only", "root_material_node", "derived_material_node_list") __slots__ = ("name", "is_read_only", "root_material_node", "derived_material_node_list")
def __init__(self, name: str, root_material_node: MaterialNode): def __init__(self, name: str, root_material_node: MaterialNode) -> None:
self.name = name self.name = name
self.is_read_only = False self.is_read_only = False
self.root_material_node = root_material_node self.root_material_node = root_material_node # type: MaterialNode
self.derived_material_node_list = [] #type: List[MaterialNode] self.derived_material_node_list = [] #type: List[MaterialNode]
def __str__(self) -> str: def __str__(self) -> str:

View File

@ -4,6 +4,7 @@
from collections import defaultdict, OrderedDict from collections import defaultdict, OrderedDict
import copy import copy
import uuid import uuid
from typing import Dict
from typing import 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
@ -263,7 +264,7 @@ 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", extruder_variant_name: Optional[str],
diameter: float) -> dict: 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_variant_material_map:
@ -288,7 +289,7 @@ class MaterialManager(QObject):
# 3. generic material (for fdmprinter) # 3. 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() material_id_metadata_dict = dict() # type: Dict[str, MaterialNode]
for node in nodes_to_check: for node in nodes_to_check:
if node is not None: if node is not None:
# Only exclude the materials that are explicitly specified in the "exclude_materials" field. # Only exclude the materials that are explicitly specified in the "exclude_materials" field.
@ -434,7 +435,7 @@ class MaterialManager(QObject):
nodes_to_remove = [material_group.root_material_node] + material_group.derived_material_node_list nodes_to_remove = [material_group.root_material_node] + material_group.derived_material_node_list
for node in nodes_to_remove: for node in nodes_to_remove:
self._container_registry.removeContainer(node.metadata["id"]) self._container_registry.removeContainer(node.getMetaDataEntry("id", ""))
# #
# Methods for GUI # Methods for GUI
@ -445,22 +446,27 @@ class MaterialManager(QObject):
# #
@pyqtSlot("QVariant", str) @pyqtSlot("QVariant", str)
def setMaterialName(self, material_node: "MaterialNode", name: str): def setMaterialName(self, material_node: "MaterialNode", name: str):
root_material_id = material_node.metadata["base_file"] root_material_id = material_node.getMetaDataEntry("base_file")
if root_material_id is None:
return
if self._container_registry.isReadOnly(root_material_id): if self._container_registry.isReadOnly(root_material_id):
Logger.log("w", "Cannot set name of read-only container %s.", root_material_id) Logger.log("w", "Cannot set name of read-only container %s.", root_material_id)
return return
material_group = self.getMaterialGroup(root_material_id) material_group = self.getMaterialGroup(root_material_id)
if material_group: if material_group:
material_group.root_material_node.getContainer().setName(name) container = material_group.root_material_node.getContainer()
if container:
container.setName(name)
# #
# Removes the given material. # Removes the given material.
# #
@pyqtSlot("QVariant") @pyqtSlot("QVariant")
def removeMaterial(self, material_node: "MaterialNode"): def removeMaterial(self, material_node: "MaterialNode"):
root_material_id = material_node.metadata["base_file"] root_material_id = material_node.getMetaDataEntry("base_file")
self.removeMaterialByRootId(root_material_id) if root_material_id is not None:
self.removeMaterialByRootId(root_material_id)
# #
# Creates a duplicate of a material, which has the same GUID and base_file metadata. # Creates a duplicate of a material, which has the same GUID and base_file metadata.
@ -539,6 +545,10 @@ class MaterialManager(QObject):
root_material_id = self.getRootMaterialIDForDiameter(root_material_id, approximate_diameter) root_material_id = self.getRootMaterialIDForDiameter(root_material_id, approximate_diameter)
material_group = self.getMaterialGroup(root_material_id) material_group = self.getMaterialGroup(root_material_id)
if not material_group: # This should never happen
Logger.log("w", "Cannot get the material group of %s.", root_material_id)
return ""
# Create a new ID & container to hold the data. # Create a new ID & container to hold the data.
new_id = self._container_registry.uniqueName("custom_material") new_id = self._container_registry.uniqueName("custom_material")
new_metadata = {"name": catalog.i18nc("@label", "Custom Material"), new_metadata = {"name": catalog.i18nc("@label", "Custom Material"),

View File

@ -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 typing import Optional, Dict
from typing import Optional
from .ContainerNode import ContainerNode from .ContainerNode import ContainerNode
@ -15,7 +14,6 @@ from .ContainerNode import ContainerNode
class MaterialNode(ContainerNode): class MaterialNode(ContainerNode):
__slots__ = ("material_map", "children_map") __slots__ = ("material_map", "children_map")
def __init__(self, metadata: Optional[dict] = None): def __init__(self, metadata: Optional[dict] = None) -> None:
super().__init__(metadata = metadata) super().__init__(metadata = metadata)
self.material_map = {} # material_root_id -> material_node self.material_map = {} # type: Dict[str, MaterialNode] # material_root_id -> material_node
self.children_map = {} # mapping for the child nodes

View File

@ -83,7 +83,7 @@ class QualityProfilesDropDownMenuModel(ListModel):
self.setItems(item_list) self.setItems(item_list)
def _fetchLayerHeight(self, quality_group: "QualityGroup"): def _fetchLayerHeight(self, quality_group: "QualityGroup") -> float:
global_stack = self._machine_manager.activeMachine global_stack = self._machine_manager.activeMachine
if not self._layer_height_unit: if not self._layer_height_unit:
unit = global_stack.definition.getProperty("layer_height", "unit") unit = global_stack.definition.getProperty("layer_height", "unit")
@ -94,10 +94,12 @@ class QualityProfilesDropDownMenuModel(ListModel):
default_layer_height = global_stack.definition.getProperty("layer_height", "value") default_layer_height = global_stack.definition.getProperty("layer_height", "value")
# Get layer_height from the quality profile for the GlobalStack # Get layer_height from the quality profile for the GlobalStack
if quality_group.node_for_global is None:
return float(default_layer_height)
container = quality_group.node_for_global.getContainer() container = quality_group.node_for_global.getContainer()
layer_height = default_layer_height layer_height = default_layer_height
if container.hasProperty("layer_height", "value"): if container and container.hasProperty("layer_height", "value"):
layer_height = container.getProperty("layer_height", "value") layer_height = container.getProperty("layer_height", "value")
else: else:
# Look for layer_height in the GlobalStack from material -> definition # Look for layer_height in the GlobalStack from material -> definition

View File

@ -1,22 +1,27 @@
# Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from typing import TYPE_CHECKING
from UM.Application import Application from UM.Application import Application
from UM.ConfigurationErrorMessage import ConfigurationErrorMessage from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
from .QualityGroup import QualityGroup from .QualityGroup import QualityGroup
if TYPE_CHECKING:
from cura.Machines.QualityNode import QualityNode
class QualityChangesGroup(QualityGroup): class QualityChangesGroup(QualityGroup):
def __init__(self, name: str, quality_type: str, parent = None): def __init__(self, name: str, quality_type: str, parent = None) -> None:
super().__init__(name, quality_type, parent) super().__init__(name, quality_type, parent)
self._container_registry = Application.getInstance().getContainerRegistry() self._container_registry = Application.getInstance().getContainerRegistry()
def addNode(self, node: "QualityNode"): def addNode(self, node: "QualityNode"):
extruder_position = node.metadata.get("position") extruder_position = node.getMetaDataEntry("position")
if extruder_position is None and self.node_for_global is not None or extruder_position in self.nodes_for_extruders: #We would be overwriting another node. if extruder_position is None and self.node_for_global is not None or extruder_position in self.nodes_for_extruders: #We would be overwriting another node.
ConfigurationErrorMessage.getInstance().addFaultyContainers(node.metadata["id"]) ConfigurationErrorMessage.getInstance().addFaultyContainers(node.getMetaDataEntry("id"))
return return
if extruder_position is None: #Then we're a global quality changes profile. if extruder_position is None: #Then we're a global quality changes profile.

View File

@ -4,7 +4,7 @@
from typing import Dict, Optional, List, Set from typing import Dict, Optional, List, Set
from PyQt5.QtCore import QObject, pyqtSlot from PyQt5.QtCore import QObject, pyqtSlot
from cura.Machines.ContainerNode import ContainerNode
# #
# A QualityGroup represents a group of containers that must be applied to each ContainerStack when it's used. # A QualityGroup represents a group of containers that must be applied to each ContainerStack when it's used.
@ -24,8 +24,8 @@ class QualityGroup(QObject):
def __init__(self, name: str, quality_type: str, parent = None) -> None: def __init__(self, name: str, quality_type: str, parent = None) -> None:
super().__init__(parent) super().__init__(parent)
self.name = name self.name = name
self.node_for_global = None # type: Optional["QualityGroup"] self.node_for_global = None # type: Optional[ContainerNode]
self.nodes_for_extruders = {} # type: Dict[int, "QualityGroup"] self.nodes_for_extruders = {} # type: Dict[int, ContainerNode]
self.quality_type = quality_type self.quality_type = quality_type
self.is_available = False self.is_available = False
@ -38,10 +38,12 @@ class QualityGroup(QObject):
for node in [self.node_for_global] + list(self.nodes_for_extruders.values()): for node in [self.node_for_global] + list(self.nodes_for_extruders.values()):
if node is None: if node is None:
continue continue
result.update(node.getContainer().getAllKeys()) container = node.getContainer()
if container:
result.update(container.getAllKeys())
return result return result
def getAllNodes(self) -> List["QualityGroup"]: def getAllNodes(self) -> List[ContainerNode]:
result = [] result = []
if self.node_for_global is not None: if self.node_for_global is not None:
result.append(self.node_for_global) result.append(self.node_for_global)

View File

@ -1,7 +1,7 @@
# Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from typing import TYPE_CHECKING, Optional from typing import TYPE_CHECKING, Optional, cast
from PyQt5.QtCore import QObject, QTimer, pyqtSignal, pyqtSlot from PyQt5.QtCore import QObject, QTimer, pyqtSignal, pyqtSlot
@ -90,7 +90,7 @@ class QualityManager(QObject):
if definition_id not in self._machine_variant_material_quality_type_to_quality_dict: if definition_id not in self._machine_variant_material_quality_type_to_quality_dict:
self._machine_variant_material_quality_type_to_quality_dict[definition_id] = QualityNode() self._machine_variant_material_quality_type_to_quality_dict[definition_id] = QualityNode()
machine_node = self._machine_variant_material_quality_type_to_quality_dict[definition_id] machine_node = cast(QualityNode, self._machine_variant_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
@ -102,7 +102,7 @@ class QualityManager(QObject):
# too. # too.
if variant_name not in machine_node.children_map: if variant_name not in machine_node.children_map:
machine_node.children_map[variant_name] = QualityNode() machine_node.children_map[variant_name] = QualityNode()
variant_node = machine_node.children_map[variant_name] variant_node = cast(QualityNode, machine_node.children_map[variant_name])
if root_material_id is None: if root_material_id is None:
# If only variant_name is specified but material is not, add the quality/quality_changes metadata # If only variant_name is specified but material is not, add the quality/quality_changes metadata
@ -114,7 +114,7 @@ class QualityManager(QObject):
# material node. # material node.
if root_material_id not in variant_node.children_map: if root_material_id not in variant_node.children_map:
variant_node.children_map[root_material_id] = QualityNode() variant_node.children_map[root_material_id] = QualityNode()
material_node = variant_node.children_map[root_material_id] material_node = cast(QualityNode, variant_node.children_map[root_material_id])
material_node.addQualityMetadata(quality_type, metadata) material_node.addQualityMetadata(quality_type, metadata)
@ -123,7 +123,7 @@ class QualityManager(QObject):
if root_material_id is not None: if root_material_id is not None:
if root_material_id not in machine_node.children_map: if root_material_id not in machine_node.children_map:
machine_node.children_map[root_material_id] = QualityNode() machine_node.children_map[root_material_id] = QualityNode()
material_node = machine_node.children_map[root_material_id] material_node = cast(QualityNode, machine_node.children_map[root_material_id])
material_node.addQualityMetadata(quality_type, metadata) material_node.addQualityMetadata(quality_type, metadata)
@ -351,7 +351,7 @@ class QualityManager(QObject):
def removeQualityChangesGroup(self, quality_changes_group: "QualityChangesGroup"): def removeQualityChangesGroup(self, quality_changes_group: "QualityChangesGroup"):
Logger.log("i", "Removing quality changes group [%s]", quality_changes_group.name) Logger.log("i", "Removing quality changes group [%s]", quality_changes_group.name)
for node in quality_changes_group.getAllNodes(): for node in quality_changes_group.getAllNodes():
self._container_registry.removeContainer(node.metadata["id"]) self._container_registry.removeContainer(node.getMetaDataEntry("id"))
# #
# Rename a set of quality changes containers. Returns the new name. # Rename a set of quality changes containers. Returns the new name.
@ -365,7 +365,9 @@ class QualityManager(QObject):
new_name = self._container_registry.uniqueName(new_name) new_name = self._container_registry.uniqueName(new_name)
for node in quality_changes_group.getAllNodes(): for node in quality_changes_group.getAllNodes():
node.getContainer().setName(new_name) container = node.getContainer()
if container:
container.setName(new_name)
quality_changes_group.name = new_name quality_changes_group.name = new_name

View File

@ -1,7 +1,7 @@
# Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from typing import Optional from typing import Optional, Dict, cast
from .ContainerNode import ContainerNode from .ContainerNode import ContainerNode
from .QualityChangesGroup import QualityChangesGroup from .QualityChangesGroup import QualityChangesGroup
@ -12,9 +12,9 @@ from .QualityChangesGroup import QualityChangesGroup
# #
class QualityNode(ContainerNode): class QualityNode(ContainerNode):
def __init__(self, metadata: Optional[dict] = None): def __init__(self, metadata: Optional[dict] = None) -> None:
super().__init__(metadata = metadata) super().__init__(metadata = metadata)
self.quality_type_map = {} # quality_type -> QualityNode for InstanceContainer self.quality_type_map = {} # type: Dict[str, QualityNode] # quality_type -> QualityNode for InstanceContainer
def addQualityMetadata(self, quality_type: str, metadata: dict): def addQualityMetadata(self, quality_type: str, metadata: dict):
if quality_type not in self.quality_type_map: if quality_type not in self.quality_type_map:
@ -32,4 +32,4 @@ class QualityNode(ContainerNode):
if name not in quality_type_node.children_map: if name not in quality_type_node.children_map:
quality_type_node.children_map[name] = QualityChangesGroup(name, quality_type) quality_type_node.children_map[name] = QualityChangesGroup(name, quality_type)
quality_changes_group = quality_type_node.children_map[name] quality_changes_group = quality_type_node.children_map[name]
quality_changes_group.addNode(QualityNode(metadata)) cast(QualityChangesGroup, quality_changes_group).addNode(QualityNode(metadata))

View File

@ -64,10 +64,11 @@ class MultiplyObjectsJob(Job):
arranger.resetLastPriority() arranger.resetLastPriority()
for i in range(self._count): for i in range(self._count):
# We do place the nodes one by one, as we want to yield in between. # We do place the nodes one by one, as we want to yield in between.
new_node = copy.deepcopy(node)
solution_found = False
if not node_too_big: if not node_too_big:
new_node, solution_found = arranger.findNodePlacement(current_node, offset_shape_arr, hull_shape_arr) solution_found = arranger.findNodePlacement(new_node, offset_shape_arr, hull_shape_arr)
else:
new_node = copy.deepcopy(node)
if node_too_big or not solution_found: if node_too_big or not solution_found:
found_solution_for_all = False found_solution_for_all = False
new_location = new_node.getPosition() new_location = new_node.getPosition()

View File

@ -16,7 +16,7 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
# #
# Note that in order to increase precision, the 24 bit depth value is encoded into all three of the R,G & B channels # Note that in order to increase precision, the 24 bit depth value is encoded into all three of the R,G & B channels
class PickingPass(RenderPass): class PickingPass(RenderPass):
def __init__(self, width: int, height: int): def __init__(self, width: int, height: int) -> None:
super().__init__("picking", width, height) super().__init__("picking", width, height)
self._renderer = Application.getInstance().getRenderer() self._renderer = Application.getInstance().getRenderer()

View File

@ -8,6 +8,7 @@ from UM.Scene.SceneNode import SceneNode
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
from UM.Math.Vector import Vector from UM.Math.Vector import Vector
from UM.Scene.Selection import Selection from UM.Scene.Selection import Selection
from UM.Scene.SceneNodeSettings import SceneNodeSettings
from cura.Scene.ConvexHullDecorator import ConvexHullDecorator from cura.Scene.ConvexHullDecorator import ConvexHullDecorator
@ -80,6 +81,10 @@ class PlatformPhysics:
# only push away objects if this node is a printing mesh # only push away objects if this node is a printing mesh
if not node.callDecoration("isNonPrintingMesh") and Application.getInstance().getPreferences().getValue("physics/automatic_push_free"): if not node.callDecoration("isNonPrintingMesh") and Application.getInstance().getPreferences().getValue("physics/automatic_push_free"):
# Do not move locked nodes
if node.getSetting(SceneNodeSettings.LockPosition):
continue
# Check for collisions between convex hulls # Check for collisions between convex hulls
for other_node in BreadthFirstIterator(root): for other_node in BreadthFirstIterator(root):
# Ignore root, ourselves and anything that is not a normal SceneNode. # Ignore root, ourselves and anything that is not a normal SceneNode.

View File

@ -33,7 +33,7 @@ def prettier_color(color_list):
# #
# This is useful to get a preview image of a scene taken from a different location as the active camera. # This is useful to get a preview image of a scene taken from a different location as the active camera.
class PreviewPass(RenderPass): class PreviewPass(RenderPass):
def __init__(self, width: int, height: int): def __init__(self, width: int, height: int) -> None:
super().__init__("preview", width, height, 0) super().__init__("preview", width, height, 0)
self._camera = None # type: Optional[Camera] self._camera = None # type: Optional[Camera]
@ -53,20 +53,23 @@ class PreviewPass(RenderPass):
def render(self) -> None: def render(self) -> None:
if not self._shader: if not self._shader:
self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "overhang.shader")) self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "overhang.shader"))
self._shader.setUniformValue("u_overhangAngle", 1.0) if self._shader:
self._shader.setUniformValue("u_ambientColor", [0.1, 0.1, 0.1, 1.0]) self._shader.setUniformValue("u_overhangAngle", 1.0)
self._shader.setUniformValue("u_specularColor", [0.6, 0.6, 0.6, 1.0]) self._shader.setUniformValue("u_ambientColor", [0.1, 0.1, 0.1, 1.0])
self._shader.setUniformValue("u_shininess", 20.0) self._shader.setUniformValue("u_specularColor", [0.6, 0.6, 0.6, 1.0])
self._shader.setUniformValue("u_shininess", 20.0)
if not self._non_printing_shader: if not self._non_printing_shader:
self._non_printing_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "transparent_object.shader")) if self._non_printing_shader:
self._non_printing_shader.setUniformValue("u_diffuseColor", [0.5, 0.5, 0.5, 0.5]) self._non_printing_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "transparent_object.shader"))
self._non_printing_shader.setUniformValue("u_opacity", 0.6) self._non_printing_shader.setUniformValue("u_diffuseColor", [0.5, 0.5, 0.5, 0.5])
self._non_printing_shader.setUniformValue("u_opacity", 0.6)
if not self._support_mesh_shader: if not self._support_mesh_shader:
self._support_mesh_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "striped.shader")) self._support_mesh_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "striped.shader"))
self._support_mesh_shader.setUniformValue("u_vertical_stripes", True) if self._support_mesh_shader:
self._support_mesh_shader.setUniformValue("u_width", 5.0) self._support_mesh_shader.setUniformValue("u_vertical_stripes", True)
self._support_mesh_shader.setUniformValue("u_width", 5.0)
self._gl.glClearColor(0.0, 0.0, 0.0, 0.0) self._gl.glClearColor(0.0, 0.0, 0.0, 0.0)
self._gl.glClear(self._gl.GL_COLOR_BUFFER_BIT | self._gl.GL_DEPTH_BUFFER_BIT) self._gl.glClear(self._gl.GL_COLOR_BUFFER_BIT | self._gl.GL_DEPTH_BUFFER_BIT)

View File

@ -1,13 +1,15 @@
# Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from typing import TYPE_CHECKING
from cura.PrinterOutput.PrinterOutputController import PrinterOutputController from cura.PrinterOutput.PrinterOutputController import PrinterOutputController
from PyQt5.QtCore import QTimer from PyQt5.QtCore import QTimer
MYPY = False if TYPE_CHECKING:
if MYPY:
from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
from cura.PrinterOutput.ExtruderOutputModel import ExtruderOutputModel
class GenericOutputController(PrinterOutputController): class GenericOutputController(PrinterOutputController):

View File

@ -32,12 +32,12 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
def __init__(self, device_id, address: str, properties: Dict[bytes, bytes], parent: QObject = None) -> None: def __init__(self, device_id, address: str, properties: Dict[bytes, bytes], parent: QObject = None) -> None:
super().__init__(device_id = device_id, parent = parent) super().__init__(device_id = device_id, parent = parent)
self._manager = None # type: QNetworkAccessManager self._manager = None # type: QNetworkAccessManager
self._last_manager_create_time = None # type: float self._last_manager_create_time = None # type: Optional[float]
self._recreate_network_manager_time = 30 self._recreate_network_manager_time = 30
self._timeout_time = 10 # After how many seconds of no response should a timeout occur? self._timeout_time = 10 # After how many seconds of no response should a timeout occur?
self._last_response_time = None # type: float self._last_response_time = None # type: Optional[float]
self._last_request_time = None # type: float self._last_request_time = None # type: Optional[float]
self._api_prefix = "" self._api_prefix = ""
self._address = address self._address = address
@ -146,12 +146,14 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
if time_since_last_response > self._recreate_network_manager_time: if time_since_last_response > self._recreate_network_manager_time:
if self._last_manager_create_time is None: if self._last_manager_create_time is None:
self._createNetworkManager() self._createNetworkManager()
if time() - self._last_manager_create_time > self._recreate_network_manager_time: elif time() - self._last_manager_create_time > self._recreate_network_manager_time:
self._createNetworkManager() self._createNetworkManager()
assert(self._manager is not None)
elif self._connection_state == ConnectionState.closed: elif self._connection_state == ConnectionState.closed:
# Go out of timeout. # Go out of timeout.
self.setConnectionState(self._connection_state_before_timeout) if self._connection_state_before_timeout is not None: # sanity check, but it should never be None here
self._connection_state_before_timeout = None self.setConnectionState(self._connection_state_before_timeout)
self._connection_state_before_timeout = None
def _createEmptyRequest(self, target: str, content_type: Optional[str] = "application/json") -> QNetworkRequest: def _createEmptyRequest(self, target: str, content_type: Optional[str] = "application/json") -> QNetworkRequest:
url = QUrl("http://" + self._address + self._api_prefix + target) url = QUrl("http://" + self._address + self._api_prefix + target)
@ -190,6 +192,7 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
def put(self, target: str, data: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: def put(self, target: str, data: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None:
if self._manager is None: if self._manager is None:
self._createNetworkManager() self._createNetworkManager()
assert(self._manager is not None)
request = self._createEmptyRequest(target) request = self._createEmptyRequest(target)
self._last_request_time = time() self._last_request_time = time()
reply = self._manager.put(request, data.encode()) reply = self._manager.put(request, data.encode())
@ -198,6 +201,7 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
def get(self, target: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: def get(self, target: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None:
if self._manager is None: if self._manager is None:
self._createNetworkManager() self._createNetworkManager()
assert(self._manager is not None)
request = self._createEmptyRequest(target) request = self._createEmptyRequest(target)
self._last_request_time = time() self._last_request_time = time()
reply = self._manager.get(request) reply = self._manager.get(request)
@ -206,6 +210,7 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
def post(self, target: str, data: str, onFinished: Optional[Callable[[QNetworkReply], None]], on_progress: Callable = None) -> None: def post(self, target: str, data: str, onFinished: Optional[Callable[[QNetworkReply], None]], on_progress: Callable = None) -> None:
if self._manager is None: if self._manager is None:
self._createNetworkManager() self._createNetworkManager()
assert(self._manager is not None)
request = self._createEmptyRequest(target) request = self._createEmptyRequest(target)
self._last_request_time = time() self._last_request_time = time()
reply = self._manager.post(request, data) reply = self._manager.post(request, data)
@ -216,6 +221,7 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
def postFormWithParts(self, target:str, parts: List[QHttpPart], on_finished: Optional[Callable[[QNetworkReply], None]], on_progress: Callable = None) -> None: def postFormWithParts(self, target:str, parts: List[QHttpPart], on_finished: Optional[Callable[[QNetworkReply], None]], on_progress: Callable = None) -> None:
if self._manager is None: if self._manager is None:
self._createNetworkManager() self._createNetworkManager()
assert(self._manager is not None)
request = self._createEmptyRequest(target, content_type=None) request = self._createEmptyRequest(target, content_type=None)
multi_post_part = QHttpMultiPart(QHttpMultiPart.FormDataType) multi_post_part = QHttpMultiPart(QHttpMultiPart.FormDataType)
for part in parts: for part in parts:

View File

@ -224,5 +224,5 @@ class PrinterOutputDevice(QObject, OutputDevice):
## Get the name of device firmware ## Get the name of device firmware
# #
# This name can be used to define device type # This name can be used to define device type
def getFirmwareName(self) -> str: def getFirmwareName(self) -> Optional[str]:
return self._firmware_name return self._firmware_name

View File

@ -16,7 +16,7 @@ from UM.Signal import Signal
class CuraSceneController(QObject): class CuraSceneController(QObject):
activeBuildPlateChanged = Signal() activeBuildPlateChanged = Signal()
def __init__(self, objects_model: ObjectsModel, multi_build_plate_model: MultiBuildPlateModel): def __init__(self, objects_model: ObjectsModel, multi_build_plate_model: MultiBuildPlateModel) -> None:
super().__init__() super().__init__()
self._objects_model = objects_model self._objects_model = objects_model

View File

@ -39,6 +39,9 @@ class CuraSceneNode(SceneNode):
# TODO The best way to do it is by adding the setActiveExtruder decorator to every node when is loaded # TODO The best way to do it is by adding the setActiveExtruder decorator to every node when is loaded
def getPrintingExtruder(self) -> Optional[ExtruderStack]: def getPrintingExtruder(self) -> Optional[ExtruderStack]:
global_container_stack = Application.getInstance().getGlobalContainerStack() global_container_stack = Application.getInstance().getGlobalContainerStack()
if global_container_stack is None:
return None
per_mesh_stack = self.callDecoration("getStack") per_mesh_stack = self.callDecoration("getStack")
extruders = list(global_container_stack.extruders.values()) extruders = list(global_container_stack.extruders.values())
@ -85,10 +88,10 @@ class CuraSceneNode(SceneNode):
## Return if the provided bbox collides with the bbox of this scene node ## Return if the provided bbox collides with the bbox of this scene node
def collidesWithBbox(self, check_bbox: AxisAlignedBox) -> bool: def collidesWithBbox(self, check_bbox: AxisAlignedBox) -> bool:
bbox = self.getBoundingBox() bbox = self.getBoundingBox()
if bbox is not None:
# Mark the node as outside the build volume if the bounding box test fails. # Mark the node as outside the build volume if the bounding box test fails.
if check_bbox.intersectsBox(bbox) != AxisAlignedBox.IntersectionResult.FullIntersection: if check_bbox.intersectsBox(bbox) != AxisAlignedBox.IntersectionResult.FullIntersection:
return True return True
return False return False

View File

@ -42,6 +42,7 @@ class ContainerManager(QObject):
self._container_registry = self._application.getContainerRegistry() self._container_registry = self._application.getContainerRegistry()
self._machine_manager = self._application.getMachineManager() self._machine_manager = self._application.getMachineManager()
self._material_manager = self._application.getMaterialManager() self._material_manager = self._application.getMaterialManager()
self._quality_manager = self._application.getQualityManager()
self._container_name_filters = {} self._container_name_filters = {}
@pyqtSlot(str, str, result=str) @pyqtSlot(str, str, result=str)
@ -312,11 +313,19 @@ class ContainerManager(QObject):
self._machine_manager.blurSettings.emit() self._machine_manager.blurSettings.emit()
global_stack = self._machine_manager.activeMachine current_quality_changes_name = global_stack.qualityChanges.getName()
current_quality_type = global_stack.quality.getMetaDataEntry("quality_type")
extruder_stacks = list(global_stack.extruders.values()) extruder_stacks = list(global_stack.extruders.values())
for stack in [global_stack] + extruder_stacks: for stack in [global_stack] + extruder_stacks:
# Find the quality_changes container for this stack and merge the contents of the top container into it. # Find the quality_changes container for this stack and merge the contents of the top container into it.
quality_changes = stack.qualityChanges quality_changes = stack.qualityChanges
if quality_changes.getId() == "empty_quality_changes":
quality_changes = self._quality_manager._createQualityChanges(current_quality_type, current_quality_changes_name,
global_stack, stack)
self._container_registry.addContainer(quality_changes)
stack.qualityChanges = quality_changes
if not quality_changes or self._container_registry.isReadOnly(quality_changes.getId()): if not quality_changes or self._container_registry.isReadOnly(quality_changes.getId()):
Logger.log("e", "Could not update quality of a nonexistant or read only quality profile in stack %s", stack.getId()) Logger.log("e", "Could not update quality of a nonexistant or read only quality profile in stack %s", stack.getId())
continue continue
@ -459,7 +468,7 @@ class ContainerManager(QObject):
container_list = [n.getContainer() for n in quality_changes_group.getAllNodes() if n.getContainer() is not None] container_list = [n.getContainer() for n in quality_changes_group.getAllNodes() if n.getContainer() is not None]
self._container_registry.exportQualityProfile(container_list, path, file_type) self._container_registry.exportQualityProfile(container_list, path, file_type)
__instance = None __instance = None # type: ContainerManager
@classmethod @classmethod
def getInstance(cls, *args, **kwargs) -> "ContainerManager": def getInstance(cls, *args, **kwargs) -> "ContainerManager":

View File

@ -356,6 +356,8 @@ class CuraContainerRegistry(ContainerRegistry):
return catalog.i18nc("@info:status", "Profile is missing a quality type.") return catalog.i18nc("@info:status", "Profile is missing a quality type.")
global_stack = Application.getInstance().getGlobalContainerStack() global_stack = Application.getInstance().getGlobalContainerStack()
if global_stack is None:
return None
definition_id = getMachineDefinitionIDForQualitySearch(global_stack.definition) definition_id = getMachineDefinitionIDForQualitySearch(global_stack.definition)
profile.setDefinition(definition_id) profile.setDefinition(definition_id)

View File

@ -16,7 +16,7 @@ from UM.Settings.SettingInstance import SettingInstance
from UM.Settings.ContainerStack import ContainerStack from UM.Settings.ContainerStack import ContainerStack
from UM.Settings.PropertyEvaluationContext import PropertyEvaluationContext from UM.Settings.PropertyEvaluationContext import PropertyEvaluationContext
from typing import Optional, List, TYPE_CHECKING, Union from typing import Optional, List, TYPE_CHECKING, Union, Dict
if TYPE_CHECKING: if TYPE_CHECKING:
from cura.Settings.ExtruderStack import ExtruderStack from cura.Settings.ExtruderStack import ExtruderStack
@ -43,7 +43,6 @@ class ExtruderManager(QObject):
self._selected_object_extruders = [] self._selected_object_extruders = []
self._addCurrentMachineExtruders() self._addCurrentMachineExtruders()
#Application.getInstance().globalContainerStackChanged.connect(self._globalContainerStackChanged)
Selection.selectionChanged.connect(self.resetSelectedObjectExtruders) Selection.selectionChanged.connect(self.resetSelectedObjectExtruders)
## Signal to notify other components when the list of extruders for a machine definition changes. ## Signal to notify other components when the list of extruders for a machine definition changes.
@ -60,42 +59,47 @@ class ExtruderManager(QObject):
# \return The unique ID of the currently active extruder stack. # \return The unique ID of the currently active extruder stack.
@pyqtProperty(str, notify = activeExtruderChanged) @pyqtProperty(str, notify = activeExtruderChanged)
def activeExtruderStackId(self) -> Optional[str]: def activeExtruderStackId(self) -> Optional[str]:
if not Application.getInstance().getGlobalContainerStack(): if not self._application.getGlobalContainerStack():
return None # No active machine, so no active extruder. return None # No active machine, so no active extruder.
try: try:
return self._extruder_trains[Application.getInstance().getGlobalContainerStack().getId()][str(self._active_extruder_index)].getId() return self._extruder_trains[self._application.getGlobalContainerStack().getId()][str(self._active_extruder_index)].getId()
except KeyError: # Extruder index could be -1 if the global tab is selected, or the entry doesn't exist if the machine definition is wrong. except KeyError: # Extruder index could be -1 if the global tab is selected, or the entry doesn't exist if the machine definition is wrong.
return None return None
## Return extruder count according to extruder trains. ## Return extruder count according to extruder trains.
@pyqtProperty(int, notify = extrudersChanged) @pyqtProperty(int, notify = extrudersChanged)
def extruderCount(self): def extruderCount(self):
if not Application.getInstance().getGlobalContainerStack(): if not self._application.getGlobalContainerStack():
return 0 # No active machine, so no extruders. return 0 # No active machine, so no extruders.
try: try:
return len(self._extruder_trains[Application.getInstance().getGlobalContainerStack().getId()]) return len(self._extruder_trains[self._application.getGlobalContainerStack().getId()])
except KeyError: except KeyError:
return 0 return 0
## Gets a dict with the extruder stack ids with the extruder number as the key. ## Gets a dict with the extruder stack ids with the extruder number as the key.
@pyqtProperty("QVariantMap", notify = extrudersChanged) @pyqtProperty("QVariantMap", notify = extrudersChanged)
def extruderIds(self): def extruderIds(self) -> Dict[str, str]:
extruder_stack_ids = {} extruder_stack_ids = {}
global_stack_id = Application.getInstance().getGlobalContainerStack().getId() global_container_stack = self._application.getGlobalContainerStack()
if global_container_stack:
global_stack_id = global_container_stack.getId()
if global_stack_id in self._extruder_trains: if global_stack_id in self._extruder_trains:
for position in self._extruder_trains[global_stack_id]: for position in self._extruder_trains[global_stack_id]:
extruder_stack_ids[position] = self._extruder_trains[global_stack_id][position].getId() extruder_stack_ids[position] = self._extruder_trains[global_stack_id][position].getId()
return extruder_stack_ids return extruder_stack_ids
@pyqtSlot(str, result = str) @pyqtSlot(str, result = str)
def getQualityChangesIdByExtruderStackId(self, extruder_stack_id: str) -> str: def getQualityChangesIdByExtruderStackId(self, extruder_stack_id: str) -> str:
for position in self._extruder_trains[Application.getInstance().getGlobalContainerStack().getId()]: global_container_stack = self._application.getGlobalContainerStack()
extruder = self._extruder_trains[Application.getInstance().getGlobalContainerStack().getId()][position] if global_container_stack is not None:
if extruder.getId() == extruder_stack_id: for position in self._extruder_trains[global_container_stack.getId()]:
return extruder.qualityChanges.getId() extruder = self._extruder_trains[global_container_stack.getId()][position]
if extruder.getId() == extruder_stack_id:
return extruder.qualityChanges.getId()
return ""
## Changes the active extruder by index. ## Changes the active extruder by index.
# #
@ -141,7 +145,7 @@ class ExtruderManager(QObject):
selected_nodes.append(node) selected_nodes.append(node)
# Then, figure out which nodes are used by those selected nodes. # Then, figure out which nodes are used by those selected nodes.
global_stack = Application.getInstance().getGlobalContainerStack() global_stack = self._application.getGlobalContainerStack()
current_extruder_trains = self._extruder_trains.get(global_stack.getId()) current_extruder_trains = self._extruder_trains.get(global_stack.getId())
for node in selected_nodes: for node in selected_nodes:
extruder = node.callDecoration("getActiveExtruder") extruder = node.callDecoration("getActiveExtruder")
@ -164,7 +168,7 @@ class ExtruderManager(QObject):
@pyqtSlot(result = QObject) @pyqtSlot(result = QObject)
def getActiveExtruderStack(self) -> Optional["ExtruderStack"]: def getActiveExtruderStack(self) -> Optional["ExtruderStack"]:
global_container_stack = Application.getInstance().getGlobalContainerStack() global_container_stack = self._application.getGlobalContainerStack()
if global_container_stack: if global_container_stack:
if global_container_stack.getId() in self._extruder_trains: if global_container_stack.getId() in self._extruder_trains:
@ -175,7 +179,7 @@ class ExtruderManager(QObject):
## Get an extruder stack by index ## Get an extruder stack by index
def getExtruderStack(self, index) -> Optional["ExtruderStack"]: def getExtruderStack(self, index) -> Optional["ExtruderStack"]:
global_container_stack = Application.getInstance().getGlobalContainerStack() global_container_stack = self._application.getGlobalContainerStack()
if global_container_stack: if global_container_stack:
if global_container_stack.getId() in self._extruder_trains: if global_container_stack.getId() in self._extruder_trains:
if str(index) in self._extruder_trains[global_container_stack.getId()]: if str(index) in self._extruder_trains[global_container_stack.getId()]:
@ -186,7 +190,9 @@ class ExtruderManager(QObject):
def getExtruderStacks(self) -> List["ExtruderStack"]: def getExtruderStacks(self) -> List["ExtruderStack"]:
result = [] result = []
for i in range(self.extruderCount): for i in range(self.extruderCount):
result.append(self.getExtruderStack(i)) stack = self.getExtruderStack(i)
if stack:
result.append(stack)
return result return result
def registerExtruder(self, extruder_train, machine_id): def registerExtruder(self, extruder_train, machine_id):
@ -252,7 +258,7 @@ class ExtruderManager(QObject):
support_bottom_enabled = False support_bottom_enabled = False
support_roof_enabled = False support_roof_enabled = False
scene_root = Application.getInstance().getController().getScene().getRoot() scene_root = self._application.getController().getScene().getRoot()
# If no extruders are registered in the extruder manager yet, return an empty array # If no extruders are registered in the extruder manager yet, return an empty array
if len(self.extruderIds) == 0: if len(self.extruderIds) == 0:
@ -301,10 +307,10 @@ class ExtruderManager(QObject):
# The platform adhesion extruder. Not used if using none. # The platform adhesion extruder. Not used if using none.
if global_stack.getProperty("adhesion_type", "value") != "none": if global_stack.getProperty("adhesion_type", "value") != "none":
extruder_nr = str(global_stack.getProperty("adhesion_extruder_nr", "value")) extruder_str_nr = str(global_stack.getProperty("adhesion_extruder_nr", "value"))
if extruder_nr == "-1": if extruder_str_nr == "-1":
extruder_nr = Application.getInstance().getMachineManager().defaultExtruderPosition extruder_str_nr = self._application.getMachineManager().defaultExtruderPosition
used_extruder_stack_ids.add(self.extruderIds[extruder_nr]) used_extruder_stack_ids.add(self.extruderIds[extruder_str_nr])
try: try:
return [container_registry.findContainerStacks(id = stack_id)[0] for stack_id in used_extruder_stack_ids] return [container_registry.findContainerStacks(id = stack_id)[0] for stack_id in used_extruder_stack_ids]
@ -335,7 +341,7 @@ class ExtruderManager(QObject):
# The first element is the global container stack, followed by any extruder stacks. # The first element is the global container stack, followed by any extruder stacks.
# \return \type{List[ContainerStack]} # \return \type{List[ContainerStack]}
def getActiveGlobalAndExtruderStacks(self) -> Optional[List[Union["ExtruderStack", "GlobalStack"]]]: def getActiveGlobalAndExtruderStacks(self) -> Optional[List[Union["ExtruderStack", "GlobalStack"]]]:
global_stack = Application.getInstance().getGlobalContainerStack() global_stack = self._application.getGlobalContainerStack()
if not global_stack: if not global_stack:
return None return None
@ -347,7 +353,7 @@ class ExtruderManager(QObject):
# #
# \return \type{List[ContainerStack]} a list of # \return \type{List[ContainerStack]} a list of
def getActiveExtruderStacks(self) -> List["ExtruderStack"]: def getActiveExtruderStacks(self) -> List["ExtruderStack"]:
global_stack = Application.getInstance().getGlobalContainerStack() global_stack = self._application.getGlobalContainerStack()
if not global_stack: if not global_stack:
return [] return []
@ -461,10 +467,6 @@ class ExtruderManager(QObject):
if global_stack.definitionChanges.hasProperty(key, "value"): if global_stack.definitionChanges.hasProperty(key, "value"):
global_stack.definitionChanges.removeInstance(key, postpone_emit = True) global_stack.definitionChanges.removeInstance(key, postpone_emit = True)
# Update material diameter for extruders
for position in extruder_positions_to_update:
self.updateMaterialForDiameter(position, global_stack = global_stack)
## Get all extruder values for a certain setting. ## Get all extruder values for a certain setting.
# #
# This is exposed to SettingFunction so it can be used in value functions. # This is exposed to SettingFunction so it can be used in value functions.
@ -556,96 +558,6 @@ class ExtruderManager(QObject):
def getInstanceExtruderValues(self, key): def getInstanceExtruderValues(self, key):
return ExtruderManager.getExtruderValues(key) return ExtruderManager.getExtruderValues(key)
## Updates the material container to a material that matches the material diameter set for the printer
def updateMaterialForDiameter(self, extruder_position: int, global_stack = None):
if not global_stack:
global_stack = Application.getInstance().getGlobalContainerStack()
if not global_stack:
return
if not global_stack.getMetaDataEntry("has_materials", False):
return
extruder_stack = global_stack.extruders[str(extruder_position)]
material_diameter = extruder_stack.material.getProperty("material_diameter", "value")
if not material_diameter:
# in case of "empty" material
material_diameter = 0
material_approximate_diameter = str(round(material_diameter))
material_diameter = extruder_stack.definitionChanges.getProperty("material_diameter", "value")
setting_provider = extruder_stack
if not material_diameter:
if extruder_stack.definition.hasProperty("material_diameter", "value"):
material_diameter = extruder_stack.definition.getProperty("material_diameter", "value")
else:
material_diameter = global_stack.definition.getProperty("material_diameter", "value")
setting_provider = global_stack
if isinstance(material_diameter, SettingFunction):
material_diameter = material_diameter(setting_provider)
machine_approximate_diameter = str(round(material_diameter))
if material_approximate_diameter != machine_approximate_diameter:
Logger.log("i", "The the currently active material(s) do not match the diameter set for the printer. Finding alternatives.")
if global_stack.getMetaDataEntry("has_machine_materials", False):
materials_definition = global_stack.definition.getId()
has_material_variants = global_stack.getMetaDataEntry("has_variants", False)
else:
materials_definition = "fdmprinter"
has_material_variants = False
old_material = extruder_stack.material
search_criteria = {
"type": "material",
"approximate_diameter": machine_approximate_diameter,
"material": old_material.getMetaDataEntry("material", "value"),
"brand": old_material.getMetaDataEntry("brand", "value"),
"supplier": old_material.getMetaDataEntry("supplier", "value"),
"color_name": old_material.getMetaDataEntry("color_name", "value"),
"definition": materials_definition
}
if has_material_variants:
search_criteria["variant"] = extruder_stack.variant.getId()
container_registry = Application.getInstance().getContainerRegistry()
empty_material = container_registry.findInstanceContainers(id = "empty_material")[0]
if old_material == empty_material:
search_criteria.pop("material", None)
search_criteria.pop("supplier", None)
search_criteria.pop("brand", None)
search_criteria.pop("definition", None)
search_criteria["id"] = extruder_stack.getMetaDataEntry("preferred_material")
materials = container_registry.findInstanceContainers(**search_criteria)
if not materials:
# Same material with new diameter is not found, search for generic version of the same material type
search_criteria.pop("supplier", None)
search_criteria.pop("brand", None)
search_criteria["color_name"] = "Generic"
materials = container_registry.findInstanceContainers(**search_criteria)
if not materials:
# Generic material with new diameter is not found, search for preferred material
search_criteria.pop("color_name", None)
search_criteria.pop("material", None)
search_criteria["id"] = extruder_stack.getMetaDataEntry("preferred_material")
materials = container_registry.findInstanceContainers(**search_criteria)
if not materials:
# Preferred material with new diameter is not found, search for any material
search_criteria.pop("id", None)
materials = container_registry.findInstanceContainers(**search_criteria)
if not materials:
# Just use empty material as a final fallback
materials = [empty_material]
Logger.log("i", "Selecting new material: %s", materials[0].getId())
extruder_stack.material = materials[0]
## Get the value for a setting from a specific extruder. ## Get the value for a setting from a specific extruder.
# #
# This is exposed to SettingFunction to use in value functions. # This is exposed to SettingFunction to use in value functions.
@ -736,7 +648,7 @@ class ExtruderManager(QObject):
return resolved_value return resolved_value
__instance = None __instance = None # type: ExtruderManager
@classmethod @classmethod
def getInstance(cls, *args, **kwargs) -> "ExtruderManager": def getInstance(cls, *args, **kwargs) -> "ExtruderManager":

View File

@ -53,8 +53,8 @@ class MachineManager(QObject):
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]
self._current_quality_group = None self._current_quality_group = None # type: Optional[QualityGroup]
self._current_quality_changes_group = None self._current_quality_changes_group = None # type: Optional[QualityChangesGroup]
self._default_extruder_position = "0" # to be updated when extruders are switched on and off self._default_extruder_position = "0" # to be updated when extruders are switched on and off
@ -307,6 +307,11 @@ class MachineManager(QObject):
for position, extruder in global_stack.extruders.items(): for position, extruder in global_stack.extruders.items():
material_dict[position] = extruder.material.getMetaDataEntry("base_file") material_dict[position] = extruder.material.getMetaDataEntry("base_file")
self._current_root_material_id = material_dict self._current_root_material_id = material_dict
# Update materials to make sure that the diameters match with the machine's
for position in global_stack.extruders:
self.updateMaterialWithVariant(position)
global_quality = global_stack.quality global_quality = global_stack.quality
quality_type = global_quality.getMetaDataEntry("quality_type") quality_type = global_quality.getMetaDataEntry("quality_type")
global_quality_changes = global_stack.qualityChanges global_quality_changes = global_stack.qualityChanges
@ -626,6 +631,8 @@ class MachineManager(QObject):
## Copy the value of the setting of the current extruder to all other extruders as well as the global container. ## Copy the value of the setting of the current extruder to all other extruders as well as the global container.
@pyqtSlot(str) @pyqtSlot(str)
def copyValueToExtruders(self, key: str) -> None: def copyValueToExtruders(self, key: str) -> None:
if self._active_container_stack is None or self._global_container_stack is None:
return
new_value = self._active_container_stack.getProperty(key, "value") new_value = self._active_container_stack.getProperty(key, "value")
extruder_stacks = [stack for stack in ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())] extruder_stacks = [stack for stack in ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())]
@ -637,6 +644,8 @@ class MachineManager(QObject):
## Copy the value of all manually changed settings of the current extruder to all other extruders. ## Copy the value of all manually changed settings of the current extruder to all other extruders.
@pyqtSlot() @pyqtSlot()
def copyAllValuesToExtruders(self) -> None: def copyAllValuesToExtruders(self) -> None:
if self._active_container_stack is None or self._global_container_stack is None:
return
extruder_stacks = list(self._global_container_stack.extruders.values()) extruder_stacks = list(self._global_container_stack.extruders.values())
for extruder_stack in extruder_stacks: for extruder_stack in extruder_stacks:
if extruder_stack != self._active_container_stack: if extruder_stack != self._active_container_stack:
@ -807,6 +816,8 @@ class MachineManager(QObject):
return None return None
def getIncompatibleSettingsOnEnabledExtruders(self, container: InstanceContainer) -> List[str]: def getIncompatibleSettingsOnEnabledExtruders(self, container: InstanceContainer) -> List[str]:
if self._global_container_stack is None:
return []
extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value") extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value")
result = [] # type: List[str] result = [] # type: List[str]
for setting_instance in container.findInstances(): for setting_instance in container.findInstances():
@ -831,6 +842,8 @@ class MachineManager(QObject):
## Update extruder number to a valid value when the number of extruders are changed, or when an extruder is changed ## Update extruder number to a valid value when the number of extruders are changed, or when an extruder is changed
def correctExtruderSettings(self) -> None: def correctExtruderSettings(self) -> None:
if self._global_container_stack is None:
return
for setting_key in self.getIncompatibleSettingsOnEnabledExtruders(self._global_container_stack.userChanges): for setting_key in self.getIncompatibleSettingsOnEnabledExtruders(self._global_container_stack.userChanges):
self._global_container_stack.userChanges.removeInstance(setting_key) self._global_container_stack.userChanges.removeInstance(setting_key)
add_user_changes = self.getIncompatibleSettingsOnEnabledExtruders(self._global_container_stack.qualityChanges) add_user_changes = self.getIncompatibleSettingsOnEnabledExtruders(self._global_container_stack.qualityChanges)
@ -848,6 +861,8 @@ class MachineManager(QObject):
## Set the amount of extruders on the active machine (global stack) ## Set the amount of extruders on the active machine (global stack)
# \param extruder_count int the number of extruders to set # \param extruder_count int the number of extruders to set
def setActiveMachineExtruderCount(self, extruder_count: int) -> None: def setActiveMachineExtruderCount(self, extruder_count: int) -> None:
if self._global_container_stack is None:
return
extruder_manager = self._application.getExtruderManager() extruder_manager = self._application.getExtruderManager()
definition_changes_container = self._global_container_stack.definitionChanges definition_changes_container = self._global_container_stack.definitionChanges
@ -909,6 +924,8 @@ class MachineManager(QObject):
return extruder return extruder
def updateDefaultExtruder(self) -> None: def updateDefaultExtruder(self) -> None:
if self._global_container_stack is None:
return
extruder_items = sorted(self._global_container_stack.extruders.items()) extruder_items = sorted(self._global_container_stack.extruders.items())
old_position = self._default_extruder_position old_position = self._default_extruder_position
new_default_position = "0" new_default_position = "0"
@ -921,6 +938,8 @@ class MachineManager(QObject):
self.extruderChanged.emit() self.extruderChanged.emit()
def updateNumberExtrudersEnabled(self) -> None: def updateNumberExtrudersEnabled(self) -> None:
if self._global_container_stack is None:
return
definition_changes_container = self._global_container_stack.definitionChanges definition_changes_container = self._global_container_stack.definitionChanges
machine_extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value") machine_extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value")
extruder_count = 0 extruder_count = 0
@ -933,6 +952,8 @@ class MachineManager(QObject):
@pyqtProperty(int, notify = numberExtrudersEnabledChanged) @pyqtProperty(int, notify = numberExtrudersEnabledChanged)
def numberExtrudersEnabled(self) -> int: def numberExtrudersEnabled(self) -> int:
if self._global_container_stack is None:
return 1
return self._global_container_stack.definitionChanges.getProperty("extruders_enabled_count", "value") return self._global_container_stack.definitionChanges.getProperty("extruders_enabled_count", "value")
@pyqtProperty(str, notify = extruderChanged) @pyqtProperty(str, notify = extruderChanged)
@ -942,6 +963,8 @@ class MachineManager(QObject):
## This will fire the propertiesChanged for all settings so they will be updated in the front-end ## This will fire the propertiesChanged for all settings so they will be updated in the front-end
@pyqtSlot() @pyqtSlot()
def forceUpdateAllSettings(self) -> None: def forceUpdateAllSettings(self) -> None:
if self._global_container_stack is None:
return
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue): with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
property_names = ["value", "resolve", "validationState"] property_names = ["value", "resolve", "validationState"]
for container in [self._global_container_stack] + list(self._global_container_stack.extruders.values()): for container in [self._global_container_stack] + list(self._global_container_stack.extruders.values()):
@ -951,8 +974,9 @@ class MachineManager(QObject):
@pyqtSlot(int, bool) @pyqtSlot(int, bool)
def setExtruderEnabled(self, position: int, enabled: bool) -> None: def setExtruderEnabled(self, position: int, enabled: bool) -> None:
extruder = self.getExtruder(position) extruder = self.getExtruder(position)
if not extruder: if not extruder or self._global_container_stack is None:
Logger.log("w", "Could not find extruder on position %s", position) Logger.log("w", "Could not find extruder on position %s", position)
return
extruder.setEnabled(enabled) extruder.setEnabled(enabled)
self.updateDefaultExtruder() self.updateDefaultExtruder()
@ -988,6 +1012,8 @@ class MachineManager(QObject):
@pyqtSlot(str, str, str) @pyqtSlot(str, str, str)
def setSettingForAllExtruders(self, setting_name: str, property_name: str, property_value: str) -> None: def setSettingForAllExtruders(self, setting_name: str, property_name: str, property_value: str) -> None:
if self._global_container_stack is None:
return
for key, extruder in self._global_container_stack.extruders.items(): for key, extruder in self._global_container_stack.extruders.items():
container = extruder.userChanges container = extruder.userChanges
container.setProperty(setting_name, property_name, property_value) container.setProperty(setting_name, property_name, property_value)
@ -996,6 +1022,8 @@ class MachineManager(QObject):
# \param setting_name The ID of the setting to reset. # \param setting_name The ID of the setting to reset.
@pyqtSlot(str) @pyqtSlot(str)
def resetSettingForAllExtruders(self, setting_name: str) -> None: def resetSettingForAllExtruders(self, setting_name: str) -> None:
if self._global_container_stack is None:
return
for key, extruder in self._global_container_stack.extruders.items(): for key, extruder in self._global_container_stack.extruders.items():
container = extruder.userChanges container = extruder.userChanges
container.removeInstance(setting_name) container.removeInstance(setting_name)
@ -1038,6 +1066,8 @@ class MachineManager(QObject):
# for all stacks in the currently active machine. # for all stacks in the currently active machine.
# #
def _setEmptyQuality(self) -> None: def _setEmptyQuality(self) -> None:
if self._global_container_stack is None:
return
self._current_quality_group = None self._current_quality_group = None
self._current_quality_changes_group = None self._current_quality_changes_group = None
self._global_container_stack.quality = self._empty_quality_container self._global_container_stack.quality = self._empty_quality_container
@ -1050,11 +1080,13 @@ class MachineManager(QObject):
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:
return
if quality_group is None: if quality_group is None:
self._setEmptyQuality() self._setEmptyQuality()
return return
if quality_group.node_for_global.getContainer() is None: if quality_group.node_for_global is None or quality_group.node_for_global.getContainer() is None:
return return
for node in quality_group.nodes_for_extruders.values(): for node in quality_group.nodes_for_extruders.values():
if node.getContainer() is None: if node.getContainer() is None:
@ -1082,7 +1114,8 @@ class MachineManager(QObject):
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:
container.setMetaDataEntry("quality_type", "not_supported") if container:
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:
@ -1130,20 +1163,24 @@ class MachineManager(QObject):
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: 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:
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: ContainerNode = None) -> None:
if self._global_container_stack is None:
return
if container_node and container_node.getContainer(): if container_node and container_node.getContainer():
self._global_container_stack.extruders[position].material = container_node.getContainer() self._global_container_stack.extruders[position].material = container_node.getContainer()
root_material_id = container_node.metadata["base_file"] root_material_id = container_node.getMetaDataEntry("base_file", None)
else: else:
self._global_container_stack.extruders[position].material = self._empty_material_container self._global_container_stack.extruders[position].material = self._empty_material_container
root_material_id = None root_material_id = None
@ -1154,12 +1191,13 @@ class MachineManager(QObject):
def activeMaterialsCompatible(self) -> bool: def activeMaterialsCompatible(self) -> bool:
# check material - variant compatibility # check material - variant compatibility
if Util.parseBool(self._global_container_stack.getMetaDataEntry("has_materials", False)): if self._global_container_stack is not None:
for position, extruder in self._global_container_stack.extruders.items(): if Util.parseBool(self._global_container_stack.getMetaDataEntry("has_materials", False)):
if extruder.isEnabled and not extruder.material.getMetaDataEntry("compatible"): for position, extruder in self._global_container_stack.extruders.items():
return False if extruder.isEnabled and not extruder.material.getMetaDataEntry("compatible"):
if not extruder.material.getMetaDataEntry("compatible"): return False
return False if not extruder.material.getMetaDataEntry("compatible"):
return False
return True return True
## Update current quality type and machine after setting material ## Update current quality type and machine after setting material
@ -1202,7 +1240,7 @@ class MachineManager(QObject):
current_quality_type, quality_type) current_quality_type, quality_type)
self._setQualityGroup(candidate_quality_groups[quality_type], empty_quality_changes = True) self._setQualityGroup(candidate_quality_groups[quality_type], empty_quality_changes = True)
def _updateMaterialWithVariant(self, position: Optional[str]) -> None: def updateMaterialWithVariant(self, position: Optional[str]) -> None:
if self._global_container_stack is None: if self._global_container_stack is None:
return return
if position is None: if position is None:
@ -1210,8 +1248,8 @@ class MachineManager(QObject):
else: else:
position_list = [position] position_list = [position]
for position in position_list: for position_item in position_list:
extruder = self._global_container_stack.extruders[position] 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_variant_name = None
@ -1229,25 +1267,25 @@ class MachineManager(QObject):
material_diameter) material_diameter)
if not candidate_materials: if not candidate_materials:
self._setMaterial(position, container_node = None) self._setMaterial(position_item, container_node = None)
continue continue
if current_material_base_name in candidate_materials: if current_material_base_name in candidate_materials:
new_material = candidate_materials[current_material_base_name] new_material = candidate_materials[current_material_base_name]
self._setMaterial(position, new_material) self._setMaterial(position_item, new_material)
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, current_variant_name) material_node = self._material_manager.getDefaultMaterial(self._global_container_stack, current_variant_name)
if material_node is not None: if material_node is not None:
self._setMaterial(position, material_node) self._setMaterial(position_item, material_node)
## Given a printer definition name, select the right machine instance. In case it doesn't exist, create a new ## Given a printer definition name, select the right machine instance. In case it doesn't exist, create a new
# instance with the same network key. # instance with the same network key.
@pyqtSlot(str) @pyqtSlot(str)
def switchPrinterType(self, machine_name: str) -> None: def switchPrinterType(self, machine_name: str) -> None:
# Don't switch if the user tries to change to the same type of printer # Don't switch if the user tries to change to the same type of printer
if self.activeMachineDefinitionName == machine_name: if self._global_container_stack is None or self.self.activeMachineDefinitionName == machine_name:
return return
# Get the definition id corresponding to this machine name # Get the definition id corresponding to this machine name
machine_definition_id = CuraContainerRegistry.getInstance().findDefinitionContainers(name = machine_name)[0].getId() machine_definition_id = CuraContainerRegistry.getInstance().findDefinitionContainers(name = machine_name)[0].getId()
@ -1272,6 +1310,8 @@ class MachineManager(QObject):
@pyqtSlot(QObject) @pyqtSlot(QObject)
def applyRemoteConfiguration(self, configuration: ConfigurationModel) -> None: def applyRemoteConfiguration(self, configuration: ConfigurationModel) -> None:
if self._global_container_stack is None:
return
self.blurSettings.emit() self.blurSettings.emit()
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue): with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
self.switchPrinterType(configuration.printerType) self.switchPrinterType(configuration.printerType)
@ -1288,7 +1328,7 @@ class MachineManager(QObject):
self._setMaterial(position, material_container_node) self._setMaterial(position, material_container_node)
else: else:
self._global_container_stack.extruders[position].material = self._empty_material_container self._global_container_stack.extruders[position].material = self._empty_material_container
self._updateMaterialWithVariant(position) self.updateMaterialWithVariant(position)
if configuration.buildplateConfiguration is not None: if configuration.buildplateConfiguration is not None:
global_variant_container_node = self._variant_manager.getBuildplateVariantNode(self._global_container_stack.definition.getId(), configuration.buildplateConfiguration) global_variant_container_node = self._variant_manager.getBuildplateVariantNode(self._global_container_stack.definition.getId(), configuration.buildplateConfiguration)
@ -1334,11 +1374,13 @@ class MachineManager(QObject):
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)
self._updateMaterialWithVariant(None) # Update all materials self.updateMaterialWithVariant(None) # Update all materials
self._updateQualityWithMaterial() self._updateQualityWithMaterial()
@pyqtSlot(str, str) @pyqtSlot(str, str)
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:
return
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]
@ -1361,6 +1403,8 @@ class MachineManager(QObject):
@pyqtSlot(str, str) @pyqtSlot(str, str)
def setVariantByName(self, position: str, variant_name: str) -> None: def setVariantByName(self, position: str, variant_name: str) -> None:
if self._global_container_stack is None:
return
machine_definition_id = self._global_container_stack.definition.id machine_definition_id = self._global_container_stack.definition.id
variant_node = self._variant_manager.getVariantNode(machine_definition_id, variant_name) variant_node = self._variant_manager.getVariantNode(machine_definition_id, variant_name)
self.setVariant(position, variant_node) self.setVariant(position, variant_node)
@ -1371,7 +1415,7 @@ class MachineManager(QObject):
self.blurSettings.emit() self.blurSettings.emit()
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue): with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
self._setVariantNode(position, container_node) self._setVariantNode(position, container_node)
self._updateMaterialWithVariant(position) self.updateMaterialWithVariant(position)
self._updateQualityWithMaterial() self._updateQualityWithMaterial()
# See if we need to show the Discard or Keep changes screen # See if we need to show the Discard or Keep changes screen
@ -1398,7 +1442,7 @@ class MachineManager(QObject):
self._application.discardOrKeepProfileChanges() self._application.discardOrKeepProfileChanges()
@pyqtProperty(QObject, fset = setQualityGroup, notify = activeQualityGroupChanged) @pyqtProperty(QObject, fset = setQualityGroup, notify = activeQualityGroupChanged)
def activeQualityGroup(self) -> QualityGroup: def activeQualityGroup(self) -> Optional[QualityGroup]:
return self._current_quality_group return self._current_quality_group
@pyqtSlot(QObject) @pyqtSlot(QObject)
@ -1413,13 +1457,15 @@ class MachineManager(QObject):
@pyqtSlot() @pyqtSlot()
def resetToUseDefaultQuality(self) -> None: def resetToUseDefaultQuality(self) -> None:
if self._global_container_stack is None:
return
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue): with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
self._setQualityGroup(self._current_quality_group) self._setQualityGroup(self._current_quality_group)
for stack in [self._global_container_stack] + list(self._global_container_stack.extruders.values()): for stack in [self._global_container_stack] + list(self._global_container_stack.extruders.values()):
stack.userChanges.clear() stack.userChanges.clear()
@pyqtProperty(QObject, fset = setQualityChangesGroup, notify = activeQualityChangesGroupChanged) @pyqtProperty(QObject, fset = setQualityChangesGroup, notify = activeQualityChangesGroupChanged)
def activeQualityChangesGroup(self) -> 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)
@ -1435,5 +1481,5 @@ class MachineManager(QObject):
if self._global_container_stack is None: if self._global_container_stack is None:
return return
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue): with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
self._updateMaterialWithVariant(None) self.updateMaterialWithVariant(None)
self._updateQualityWithMaterial() self._updateQualityWithMaterial()

View File

@ -1,5 +1,6 @@
# Copyright (c) 2017 Ultimaker B.V. # Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from typing import List
from PyQt5.QtCore import QObject, QTimer, pyqtProperty, pyqtSignal from PyQt5.QtCore import QObject, QTimer, pyqtProperty, pyqtSignal
from UM.FlameProfiler import pyqtSlot from UM.FlameProfiler import pyqtSlot
@ -13,6 +14,7 @@ from UM.Logger import Logger
# speed settings. If all the children of print_speed have a single value override, changing the speed won't # speed settings. If all the children of print_speed have a single value override, changing the speed won't
# actually do anything, as only the 'leaf' settings are used by the engine. # actually do anything, as only the 'leaf' settings are used by the engine.
from UM.Settings.ContainerStack import ContainerStack from UM.Settings.ContainerStack import ContainerStack
from UM.Settings.Interfaces import ContainerInterface
from UM.Settings.SettingFunction import SettingFunction from UM.Settings.SettingFunction import SettingFunction
from UM.Settings.SettingInstance import InstanceState from UM.Settings.SettingInstance import InstanceState
@ -157,7 +159,7 @@ class SettingInheritanceManager(QObject):
stack = self._active_container_stack stack = self._active_container_stack
if not stack: #No active container stack yet! if not stack: #No active container stack yet!
return False return False
containers = [] containers = [] # type: List[ContainerInterface]
## Check if the setting has a user state. If not, it is never overwritten. ## Check if the setting has a user state. If not, it is never overwritten.
has_user_state = stack.getProperty(key, "state") == InstanceState.User has_user_state = stack.getProperty(key, "state") == InstanceState.User

View File

@ -63,7 +63,7 @@ class SettingOverrideDecorator(SceneNodeDecorator):
instance_container = copy.deepcopy(self._stack.getContainer(0), memo) instance_container = copy.deepcopy(self._stack.getContainer(0), memo)
# A unique name must be added, or replaceContainer will not replace it # A unique name must be added, or replaceContainer will not replace it
instance_container.setMetaDataEntry("id", self._generateUniqueName) instance_container.setMetaDataEntry("id", self._generateUniqueName())
## Set the copied instance as the first (and only) instance container of the stack. ## Set the copied instance as the first (and only) instance container of the stack.
deep_copy._stack.replaceContainer(0, instance_container) deep_copy._stack.replaceContainer(0, instance_container)

View File

@ -61,8 +61,11 @@ class SingleInstance:
def startServer(self) -> None: def startServer(self) -> None:
self._single_instance_server = QLocalServer() self._single_instance_server = QLocalServer()
self._single_instance_server.newConnection.connect(self._onClientConnected) if self._single_instance_server:
self._single_instance_server.listen("ultimaker-cura") self._single_instance_server.newConnection.connect(self._onClientConnected)
self._single_instance_server.listen("ultimaker-cura")
else:
Logger.log("e", "Single instance server was not created.")
def _onClientConnected(self) -> None: def _onClientConnected(self) -> None:
Logger.log("i", "New connection recevied on our single-instance server") Logger.log("i", "New connection recevied on our single-instance server")

View File

@ -4,7 +4,7 @@
from configparser import ConfigParser from configparser import ConfigParser
import zipfile import zipfile
import os import os
from typing import List, Tuple from typing import Dict, List, Tuple
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
@ -13,6 +13,7 @@ from UM.Workspace.WorkspaceReader import WorkspaceReader
from UM.Application import Application from UM.Application import Application
from UM.Logger import Logger from UM.Logger import Logger
from UM.Message import Message
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
from UM.Signal import postponeSignals, CompressTechnique from UM.Signal import postponeSignals, CompressTechnique
from UM.Settings.ContainerFormatError import ContainerFormatError from UM.Settings.ContainerFormatError import ContainerFormatError
@ -37,7 +38,7 @@ i18n_catalog = i18nCatalog("cura")
class ContainerInfo: class ContainerInfo:
def __init__(self, file_name: str, serialized: str, parser: ConfigParser): def __init__(self, file_name: str, serialized: str, parser: ConfigParser) -> None:
self.file_name = file_name self.file_name = file_name
self.serialized = serialized self.serialized = serialized
self.parser = parser self.parser = parser
@ -46,14 +47,14 @@ class ContainerInfo:
class QualityChangesInfo: class QualityChangesInfo:
def __init__(self): def __init__(self) -> None:
self.name = None self.name = None
self.global_info = None self.global_info = None
self.extruder_info_dict = {} self.extruder_info_dict = {} # type: Dict[str, ContainerInfo]
class MachineInfo: class MachineInfo:
def __init__(self): def __init__(self) -> None:
self.container_id = None self.container_id = None
self.name = None self.name = None
self.definition_id = None self.definition_id = None
@ -65,11 +66,11 @@ class MachineInfo:
self.definition_changes_info = None self.definition_changes_info = None
self.user_changes_info = None self.user_changes_info = None
self.extruder_info_dict = {} self.extruder_info_dict = {} # type: Dict[str, ExtruderInfo]
class ExtruderInfo: class ExtruderInfo:
def __init__(self): def __init__(self) -> None:
self.position = None self.position = None
self.enabled = True self.enabled = True
self.variant_info = None self.variant_info = None
@ -81,7 +82,7 @@ class ExtruderInfo:
## Base implementation for reading 3MF workspace files. ## Base implementation for reading 3MF workspace files.
class ThreeMFWorkspaceReader(WorkspaceReader): class ThreeMFWorkspaceReader(WorkspaceReader):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
MimeTypeDatabase.addMimeType( MimeTypeDatabase.addMimeType(
@ -111,28 +112,26 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# - variant # - variant
self._ignored_instance_container_types = {"quality", "variant"} self._ignored_instance_container_types = {"quality", "variant"}
self._resolve_strategies = {} self._resolve_strategies = {} # type: Dict[str, str]
self._id_mapping = {} self._id_mapping = {} # type: Dict[str, str]
# In Cura 2.5 and 2.6, the empty profiles used to have those long names # In Cura 2.5 and 2.6, the empty profiles used to have those long names
self._old_empty_profile_id_dict = {"empty_%s" % k: "empty" for k in ["material", "variant"]} self._old_empty_profile_id_dict = {"empty_%s" % k: "empty" for k in ["material", "variant"]}
self._is_same_machine_type = False self._is_same_machine_type = False
self._old_new_materials = {} self._old_new_materials = {} # type: Dict[str, str]
self._materials_to_select = {}
self._machine_info = None self._machine_info = None
def _clearState(self): def _clearState(self):
self._is_same_machine_type = False self._is_same_machine_type = False
self._id_mapping = {} self._id_mapping = {}
self._old_new_materials = {} self._old_new_materials = {}
self._materials_to_select = {}
self._machine_info = None self._machine_info = None
## Get a unique name based on the old_id. This is different from directly calling the registry in that it caches results. ## Get a unique name based on the old_id. This is different from directly calling the registry in that it caches results.
# This has nothing to do with speed, but with getting consistent new naming for instances & objects. # This has nothing to do with speed, but with getting consistent new naming for instances & objects.
def getNewId(self, old_id): def getNewId(self, old_id: str):
if old_id not in self._id_mapping: if old_id not in self._id_mapping:
self._id_mapping[old_id] = self._container_registry.uniqueName(old_id) self._id_mapping[old_id] = self._container_registry.uniqueName(old_id)
return self._id_mapping[old_id] return self._id_mapping[old_id]
@ -470,6 +469,20 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
Logger.log("w", "File %s is not a valid workspace.", file_name) Logger.log("w", "File %s is not a valid workspace.", file_name)
return WorkspaceReader.PreReadResult.failed return WorkspaceReader.PreReadResult.failed
# Check if the machine definition exists. If not, indicate failure because we do not import definition files.
def_results = self._container_registry.findDefinitionContainersMetadata(id = machine_definition_id)
if not def_results:
message = Message(i18n_catalog.i18nc("@info:status Don't translate the XML tags <filename> or <message>!",
"Project file <filename>{0}</filename> contains an unknown machine type"
" <message>{1}</message>. Cannot import the machine."
" Models will be imported instead.", file_name, machine_definition_id),
title = i18n_catalog.i18nc("@info:title", "Open Project File"))
message.show()
Logger.log("i", "Could unknown machine definition %s in project file %s, cannot import it.",
self._machine_info.definition_id, file_name)
return WorkspaceReader.PreReadResult.failed
# In case we use preRead() to check if a file is a valid project file, we don't want to show a dialog. # In case we use preRead() to check if a file is a valid project file, we don't want to show a dialog.
if not show_dialog: if not show_dialog:
return WorkspaceReader.PreReadResult.accepted return WorkspaceReader.PreReadResult.accepted
@ -656,7 +669,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
else: else:
material_container = materials[0] material_container = materials[0]
old_material_root_id = material_container.getMetaDataEntry("base_file") old_material_root_id = material_container.getMetaDataEntry("base_file")
if not self._container_registry.isReadOnly(old_material_root_id): # Only create new materials if they are not read only. if old_material_root_id is not None and not self._container_registry.isReadOnly(old_material_root_id): # Only create new materials if they are not read only.
to_deserialize_material = True to_deserialize_material = True
if self._resolve_strategies["material"] == "override": if self._resolve_strategies["material"] == "override":

View File

@ -55,7 +55,7 @@ class ChangeLog(Extension, QObject,):
def loadChangeLogs(self): def loadChangeLogs(self):
self._change_logs = collections.OrderedDict() self._change_logs = collections.OrderedDict()
with open(os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), "ChangeLog.txt"), "r",-1, "utf-8") as f: with open(os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), "ChangeLog.txt"), "r", encoding = "utf-8") as f:
open_version = None open_version = None
open_header = "" # Initialise to an empty header in case there is no "*" in the first line of the changelog open_header = "" # Initialise to an empty header in case there is no "*" in the first line of the changelog
for line in f: for line in f:

View File

@ -379,6 +379,14 @@ class CuraEngineBackend(QObject, Backend):
else: else:
self.backendStateChange.emit(BackendState.NotStarted) self.backendStateChange.emit(BackendState.NotStarted)
if job.getResult() == StartSliceJob.StartJobResult.ObjectsWithDisabledExtruder:
self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice because there are objects associated with disabled Extruder %s." % job.getMessage()),
title = catalog.i18nc("@info:title", "Unable to slice"))
self._error_message.show()
self.backendStateChange.emit(BackendState.Error)
self.backendError.emit(job)
return
if job.getResult() == StartSliceJob.StartJobResult.NothingToSlice: if job.getResult() == StartSliceJob.StartJobResult.NothingToSlice:
if Application.getInstance().platformActivity: if Application.getInstance().platformActivity:
self._error_message = Message(catalog.i18nc("@info:status", "Nothing to slice because none of the models fit the build volume. Please scale or rotate models to fit."), self._error_message = Message(catalog.i18nc("@info:status", "Nothing to slice because none of the models fit the build volume. Please scale or rotate models to fit."),

View File

@ -32,6 +32,7 @@ class StartJobResult(IntEnum):
MaterialIncompatible = 5 MaterialIncompatible = 5
BuildPlateError = 6 BuildPlateError = 6
ObjectSettingError = 7 #When an error occurs in per-object settings. ObjectSettingError = 7 #When an error occurs in per-object settings.
ObjectsWithDisabledExtruder = 8
## Formatter class that handles token expansion in start/end gcod ## Formatter class that handles token expansion in start/end gcod
@ -213,16 +214,27 @@ class StartSliceJob(Job):
extruders_enabled = {position: stack.isEnabled for position, stack in Application.getInstance().getGlobalContainerStack().extruders.items()} extruders_enabled = {position: stack.isEnabled for position, stack in Application.getInstance().getGlobalContainerStack().extruders.items()}
filtered_object_groups = [] filtered_object_groups = []
has_model_with_disabled_extruders = False
associated_siabled_extruders = set()
for group in object_groups: for group in object_groups:
stack = Application.getInstance().getGlobalContainerStack() stack = Application.getInstance().getGlobalContainerStack()
skip_group = False skip_group = False
for node in group: for node in group:
if not extruders_enabled[node.callDecoration("getActiveExtruderPosition")]: extruder_position = node.callDecoration("getActiveExtruderPosition")
if not extruders_enabled[extruder_position]:
skip_group = True skip_group = True
has_model_with_disabled_extruders = True
associated_siabled_extruders.add(extruder_position)
break break
if not skip_group: if not skip_group:
filtered_object_groups.append(group) filtered_object_groups.append(group)
if has_model_with_disabled_extruders:
self.setResult(StartJobResult.ObjectsWithDisabledExtruder)
associated_siabled_extruders = [str(c) for c in sorted([int(p) + 1 for p in associated_siabled_extruders])]
self.setMessage(", ".join(associated_siabled_extruders))
return
# There are cases when there is nothing to slice. This can happen due to one at a time slicing not being # There are cases when there is nothing to slice. This can happen due to one at a time slicing not being
# able to find a possible sequence or because there are no objects on the build plate (or they are outside # able to find a possible sequence or because there are no objects on the build plate (or they are outside
# the build volume) # the build volume)

View File

@ -57,7 +57,7 @@ class GCodeProfileReader(ProfileReader):
# TODO: Consider moving settings to the start? # TODO: Consider moving settings to the start?
serialized = "" # Will be filled with the serialized profile. serialized = "" # Will be filled with the serialized profile.
try: try:
with open(file_name, "r") as f: with open(file_name, "r", encoding = "utf-8") as f:
for line in f: for line in f:
if line.startswith(prefix): if line.startswith(prefix):
# Remove the prefix and the newline from the line and add it to the rest. # Remove the prefix and the newline from the line and add it to the rest.

View File

@ -100,7 +100,7 @@ class LegacyProfileReader(ProfileReader):
return None return None
try: try:
with open(os.path.join(PluginRegistry.getInstance().getPluginPath("LegacyProfileReader"), "DictionaryOfDoom.json"), "r", -1, "utf-8") as f: with open(os.path.join(PluginRegistry.getInstance().getPluginPath("LegacyProfileReader"), "DictionaryOfDoom.json"), "r", encoding = "utf-8") as f:
dict_of_doom = json.load(f) # Parse the Dictionary of Doom. dict_of_doom = json.load(f) # Parse the Dictionary of Doom.
except IOError as e: except IOError as e:
Logger.log("e", "Could not open DictionaryOfDoom.json for reading: %s", str(e)) Logger.log("e", "Could not open DictionaryOfDoom.json for reading: %s", str(e))

View File

@ -158,4 +158,4 @@ class MachineSettingsAction(MachineAction):
@pyqtSlot(int) @pyqtSlot(int)
def updateMaterialForDiameter(self, extruder_position: int): def updateMaterialForDiameter(self, extruder_position: int):
# Updates the material container to a material that matches the material diameter set for the printer # Updates the material container to a material that matches the material diameter set for the printer
self._application.getExtruderManager().updateMaterialForDiameter(extruder_position) self._application.getMachineManager().updateMaterialWithVariant(str(extruder_position))

View File

@ -53,7 +53,7 @@ UM.PointingRectangle {
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
} }
width: 40 * screenScaleFactor width: maximumValue.toString().length * 12 * screenScaleFactor
text: sliderLabelRoot.value + startFrom // the current handle value, add 1 because layers is an array text: sliderLabelRoot.value + startFrom // the current handle value, add 1 because layers is an array
horizontalAlignment: TextInput.AlignRight horizontalAlignment: TextInput.AlignRight
@ -77,11 +77,12 @@ UM.PointingRectangle {
if (valueLabel.text != "") { if (valueLabel.text != "") {
// -startFrom because we need to convert back to an array structure // -startFrom because we need to convert back to an array structure
sliderLabelRoot.setValue(parseInt(valueLabel.text) - startFrom) sliderLabelRoot.setValue(parseInt(valueLabel.text) - startFrom)
} }
} }
validator: IntValidator { validator: IntValidator {
bottom:startFrom bottom: startFrom
top: sliderLabelRoot.maximumValue + startFrom // +startFrom because maybe we want to start in a different value rather than 0 top: sliderLabelRoot.maximumValue + startFrom // +startFrom because maybe we want to start in a different value rather than 0
} }
} }

View File

@ -262,12 +262,14 @@ class Toolbox(QObject, Extension):
# list of old plugins # list of old plugins
old_plugin_ids = self._plugin_registry.getInstalledPlugins() old_plugin_ids = self._plugin_registry.getInstalledPlugins()
installed_package_ids = self._package_manager.getAllInstalledPackageIDs() installed_package_ids = self._package_manager.getAllInstalledPackageIDs()
scheduled_to_remove_package_ids = self._package_manager.getToRemovePackageIDs()
self._old_plugin_ids = [] self._old_plugin_ids = []
self._old_plugin_metadata = [] self._old_plugin_metadata = []
for plugin_id in old_plugin_ids: for plugin_id in old_plugin_ids:
if plugin_id not in installed_package_ids: # Neither the installed packages nor the packages that are scheduled to remove are old plugins
if plugin_id not in installed_package_ids and plugin_id not in scheduled_to_remove_package_ids:
Logger.log('i', 'Found a plugin that was installed with the old plugin browser: %s', plugin_id) Logger.log('i', 'Found a plugin that was installed with the old plugin browser: %s', plugin_id)
old_metadata = self._plugin_registry.getMetaData(plugin_id) old_metadata = self._plugin_registry.getMetaData(plugin_id)

View File

@ -13,7 +13,7 @@ def readHex(filename):
""" """
data = [] data = []
extra_addr = 0 extra_addr = 0
f = io.open(filename, "r") f = io.open(filename, "r", encoding = "utf-8")
for line in f: for line in f:
line = line.strip() line = line.strip()
if len(line) < 1: if len(line) < 1:

View File

@ -94,7 +94,7 @@ class VersionUpgrade22to24(VersionUpgrade):
if variant_path.endswith("_variant.inst.cfg"): if variant_path.endswith("_variant.inst.cfg"):
variant_path = variant_path[:-len("_variant.inst.cfg")] + "_settings.inst.cfg" variant_path = variant_path[:-len("_variant.inst.cfg")] + "_settings.inst.cfg"
with open(os.path.join(machine_instances_dir, os.path.basename(variant_path)), "w") as fp: with open(os.path.join(machine_instances_dir, os.path.basename(variant_path)), "w", encoding = "utf-8") as fp:
variant_config.write(fp) variant_config.write(fp)
return config_name return config_name
@ -105,9 +105,9 @@ class VersionUpgrade22to24(VersionUpgrade):
result = [] result = []
for entry in os.scandir(variants_dir): for entry in os.scandir(variants_dir):
if entry.name.endswith('.inst.cfg') and entry.is_file(): if entry.name.endswith(".inst.cfg") and entry.is_file():
config = configparser.ConfigParser(interpolation = None) config = configparser.ConfigParser(interpolation = None)
with open(entry.path, "r") as fhandle: with open(entry.path, "r", encoding = "utf-8") as fhandle:
config.read_file(fhandle) config.read_file(fhandle)
if config.has_section("general") and config.has_option("general", "name"): if config.has_section("general") and config.has_option("general", "name"):
result.append( { "path": entry.path, "name": config.get("general", "name") } ) result.append( { "path": entry.path, "name": config.get("general", "name") } )

View File

@ -249,11 +249,11 @@ class VersionUpgrade25to26(VersionUpgrade):
definition_changes_dir = Resources.getPath(CuraApplication.ResourceTypes.DefinitionChangesContainer) definition_changes_dir = Resources.getPath(CuraApplication.ResourceTypes.DefinitionChangesContainer)
user_settings_dir = Resources.getPath(CuraApplication.ResourceTypes.UserInstanceContainer) user_settings_dir = Resources.getPath(CuraApplication.ResourceTypes.UserInstanceContainer)
with open(os.path.join(definition_changes_dir, definition_changes_filename), "w") as f: with open(os.path.join(definition_changes_dir, definition_changes_filename), "w", encoding = "utf-8") as f:
f.write(definition_changes_output.getvalue()) f.write(definition_changes_output.getvalue())
with open(os.path.join(user_settings_dir, user_settings_filename), "w") as f: with open(os.path.join(user_settings_dir, user_settings_filename), "w", encoding = "utf-8") as f:
f.write(user_settings_output.getvalue()) f.write(user_settings_output.getvalue())
with open(os.path.join(extruder_stack_dir, extruder_filename), "w") as f: with open(os.path.join(extruder_stack_dir, extruder_filename), "w", encoding = "utf-8") as f:
f.write(extruder_output.getvalue()) f.write(extruder_output.getvalue())
## Creates a definition changes container which doesn't contain anything for the Custom FDM Printers. ## Creates a definition changes container which doesn't contain anything for the Custom FDM Printers.

View File

@ -1018,7 +1018,7 @@ class XmlMaterialProfile(InstanceContainer):
@classmethod @classmethod
def getProductIdMap(cls) -> Dict[str, List[str]]: def getProductIdMap(cls) -> Dict[str, List[str]]:
product_to_id_file = os.path.join(os.path.dirname(sys.modules[cls.__module__].__file__), "product_to_id.json") product_to_id_file = os.path.join(os.path.dirname(sys.modules[cls.__module__].__file__), "product_to_id.json")
with open(product_to_id_file) as f: with open(product_to_id_file, encoding = "utf-8") as f:
product_to_id_map = json.load(f) product_to_id_map = json.load(f)
product_to_id_map = {key: [value] for key, value in product_to_id_map.items()} product_to_id_map = {key: [value] for key, value in product_to_id_map.items()}
return product_to_id_map return product_to_id_map

View File

@ -170,7 +170,7 @@ UM.PreferencesPage
append({ text: "日本語", code: "ja_JP" }) append({ text: "日本語", code: "ja_JP" })
append({ text: "한국어", code: "ko_KR" }) append({ text: "한국어", code: "ko_KR" })
append({ text: "Nederlands", code: "nl_NL" }) append({ text: "Nederlands", code: "nl_NL" })
append({ text: "Polski", code: "pl_PL" }) //Polish is disabled for being incomplete: append({ text: "Polski", code: "pl_PL" })
append({ text: "Português do Brasil", code: "pt_BR" }) append({ text: "Português do Brasil", code: "pt_BR" })
append({ text: "Português", code: "pt_PT" }) append({ text: "Português", code: "pt_PT" })
append({ text: "Русский", code: "ru_RU" }) append({ text: "Русский", code: "ru_RU" })
@ -741,21 +741,6 @@ UM.PreferencesPage
} }
} }
UM.TooltipArea
{
width: childrenRect.width
height: childrenRect.height
text: catalog.i18nc("@info:tooltip", "Should newly loaded models be arranged on the build plate? Used in conjunction with multi build plate (EXPERIMENTAL)")
CheckBox
{
id: arrangeOnLoadCheckbox
text: catalog.i18nc("@option:check", "Do not arrange objects on load")
checked: boolCheck(UM.Preferences.getValue("cura/not_arrange_objects_on_load"))
onCheckedChanged: UM.Preferences.setValue("cura/not_arrange_objects_on_load", checked)
}
}
Connections Connections
{ {
target: UM.Preferences target: UM.Preferences

View File

@ -17,6 +17,6 @@ top_bottom_thickness = 0.8
infill_sparse_density = 20 infill_sparse_density = 20
speed_print = 80 speed_print = 80
speed_layer_0 = =round(speed_print * 30 / 50) speed_layer_0 = =round(speed_print * 30 / 50)
speed_topbottom = 20 speed_topbottom = =math.ceil(speed_print * 20 / 80)
cool_min_layer_time = 5 cool_min_layer_time = 5
cool_min_speed = 10 cool_min_speed = 10

View File

@ -17,6 +17,6 @@ top_bottom_thickness = 0.8
infill_sparse_density = 20 infill_sparse_density = 20
speed_print = 50 speed_print = 50
speed_layer_0 = =round(speed_print * 30 / 50) speed_layer_0 = =round(speed_print * 30 / 50)
speed_topbottom = 20 speed_topbottom = =math.ceil(speed_print * 20 / 50)
cool_min_layer_time = 5 cool_min_layer_time = 5
cool_min_speed = 10 cool_min_speed = 10

View File

@ -17,6 +17,6 @@ top_bottom_thickness = 0.8
infill_sparse_density = 20 infill_sparse_density = 20
speed_print = 50 speed_print = 50
speed_layer_0 = =round(speed_print * 30 / 50) speed_layer_0 = =round(speed_print * 30 / 50)
speed_topbottom = 20 speed_topbottom = =math.ceil(speed_print * 20 / 50)
cool_min_layer_time = 5 cool_min_layer_time = 5
cool_min_speed = 10 cool_min_speed = 10

View File

@ -17,6 +17,6 @@ top_bottom_thickness = 0.8
infill_sparse_density = 20 infill_sparse_density = 20
speed_print = 80 speed_print = 80
speed_layer_0 = =round(speed_print * 30 / 50) speed_layer_0 = =round(speed_print * 30 / 50)
speed_topbottom = 20 speed_topbottom = =math.ceil(speed_print * 20 / 80)
cool_min_layer_time = 5 cool_min_layer_time = 5
cool_min_speed = 10 cool_min_speed = 10

View File

@ -17,6 +17,6 @@ top_bottom_thickness = 0.8
infill_sparse_density = 20 infill_sparse_density = 20
speed_print = 50 speed_print = 50
speed_layer_0 = =round(speed_print * 30 / 50) speed_layer_0 = =round(speed_print * 30 / 50)
speed_topbottom = 20 speed_topbottom = =math.ceil(speed_print * 20 / 50)
cool_min_layer_time = 5 cool_min_layer_time = 5
cool_min_speed = 10 cool_min_speed = 10

View File

@ -17,6 +17,6 @@ top_bottom_thickness = 0.8
infill_sparse_density = 20 infill_sparse_density = 20
speed_print = 50 speed_print = 50
speed_layer_0 = =round(speed_print * 30 / 50) speed_layer_0 = =round(speed_print * 30 / 50)
speed_topbottom = 20 speed_topbottom = =math.ceil(speed_print * 20 / 50)
cool_min_layer_time = 5 cool_min_layer_time = 5
cool_min_speed = 10 cool_min_speed = 10

View File

@ -17,6 +17,6 @@ top_bottom_thickness = 0.8
infill_sparse_density = 20 infill_sparse_density = 20
speed_print = 80 speed_print = 80
speed_layer_0 = =round(speed_print * 30 / 50) speed_layer_0 = =round(speed_print * 30 / 50)
speed_topbottom = 20 speed_topbottom = =math.ceil(speed_print * 20 / 80)
cool_min_layer_time = 5 cool_min_layer_time = 5
cool_min_speed = 10 cool_min_speed = 10

View File

@ -17,6 +17,6 @@ top_bottom_thickness = 0.8
infill_sparse_density = 20 infill_sparse_density = 20
speed_print = 50 speed_print = 50
speed_layer_0 = =round(speed_print * 30 / 50) speed_layer_0 = =round(speed_print * 30 / 50)
speed_topbottom = 20 speed_topbottom = =math.ceil(speed_print * 20 / 50)
cool_min_layer_time = 5 cool_min_layer_time = 5
cool_min_speed = 10 cool_min_speed = 10

View File

@ -17,6 +17,6 @@ top_bottom_thickness = 0.8
infill_sparse_density = 20 infill_sparse_density = 20
speed_print = 50 speed_print = 50
speed_layer_0 = =round(speed_print * 30 / 50) speed_layer_0 = =round(speed_print * 30 / 50)
speed_topbottom = 20 speed_topbottom = =math.ceil(speed_print * 20 / 50)
cool_min_layer_time = 5 cool_min_layer_time = 5
cool_min_speed = 10 cool_min_speed = 10

View File

@ -41,13 +41,13 @@ retraction_speed = 40
skirt_brim_speed = 40 skirt_brim_speed = 40
skirt_gap = 5 skirt_gap = 5
skirt_line_count = 3 skirt_line_count = 3
speed_infill = 60 speed_infill = =speed_print
speed_print = 60 speed_print = 60
speed_support = 60 speed_support = 60
speed_topbottom = 30 speed_topbottom = =math.ceil(speed_print * 30 / 60)
speed_travel = 100 speed_travel = 100
speed_wall = 60 speed_wall = =speed_print
speed_wall_x = 60 speed_wall_x = =speed_print
support_angle = 60 support_angle = 60
support_enable = True support_enable = True
support_interface_enable = True support_interface_enable = True

View File

@ -41,13 +41,13 @@ retraction_speed = 40
skirt_brim_speed = 40 skirt_brim_speed = 40
skirt_gap = 5 skirt_gap = 5
skirt_line_count = 3 skirt_line_count = 3
speed_infill = 50 speed_infill = =speed_print
speed_print = 50 speed_print = 50
speed_support = 30 speed_support = 30
speed_topbottom = 20 speed_topbottom = =math.ceil(speed_print * 20 / 50)
speed_travel = 50 speed_travel = 50
speed_wall = 50 speed_wall = =speed_print
speed_wall_x = 50 speed_wall_x = =speed_print
support_angle = 60 support_angle = 60
support_enable = True support_enable = True
support_interface_enable = True support_interface_enable = True

View File

@ -41,13 +41,13 @@ retraction_speed = 40
skirt_brim_speed = 40 skirt_brim_speed = 40
skirt_gap = 5 skirt_gap = 5
skirt_line_count = 3 skirt_line_count = 3
speed_infill = 50 speed_infill = =speed_print
speed_print = 50 speed_print = 50
speed_support = 30 speed_support = 30
speed_topbottom = 20 speed_topbottom = =math.ceil(speed_print * 20 / 50)
speed_travel = 100 speed_travel = 100
speed_wall = 50 speed_wall = =speed_print
speed_wall_x = 50 speed_wall_x = =speed_print
support_angle = 60 support_angle = 60
support_enable = True support_enable = True
support_interface_enable = True support_interface_enable = True

View File

@ -1,6 +1,6 @@
[general] [general]
version = 4 version = 4
name = Coarse Quality name = Coarse
definition = fdmprinter definition = fdmprinter
[metadata] [metadata]

View File

@ -43,11 +43,11 @@ skirt_brim_minimal_length = 75
skirt_gap = 1.5 skirt_gap = 1.5
skirt_line_count = 5 skirt_line_count = 5
speed_infill = =speed_print speed_infill = =speed_print
speed_layer_0 = 25 speed_layer_0 = =math.ceil(speed_print * 25 / 50)
speed_print = 50 speed_print = 50
speed_topbottom = 40 speed_topbottom = =math.ceil(speed_print * 40 / 50)
speed_travel = 200 speed_travel = 200
speed_wall_0 = 40 speed_wall_0 = =math.ceil(speed_print * 40 / 50)
speed_wall_x = =speed_print speed_wall_x = =speed_print
support_angle = 70 support_angle = 70
support_type = buildplate support_type = buildplate

View File

@ -43,11 +43,11 @@ skirt_brim_minimal_length = 75
skirt_gap = 1.5 skirt_gap = 1.5
skirt_line_count = 5 skirt_line_count = 5
speed_infill = =speed_print speed_infill = =speed_print
speed_layer_0 = 25 speed_layer_0 = =math.ceil(speed_print * 25 / 50)
speed_print = 50 speed_print = 50
speed_topbottom = 40 speed_topbottom = =math.ceil(speed_print * 40 / 50)
speed_travel = 200 speed_travel = 200
speed_wall_0 = 40 speed_wall_0 = =math.ceil(speed_print * 40 / 50)
speed_wall_x = =speed_print speed_wall_x = =speed_print
support_angle = 70 support_angle = 70
support_type = buildplate support_type = buildplate

View File

@ -43,11 +43,11 @@ skirt_brim_minimal_length = 75
skirt_gap = 1.5 skirt_gap = 1.5
skirt_line_count = 5 skirt_line_count = 5
speed_infill = =speed_print speed_infill = =speed_print
speed_layer_0 = 25 speed_layer_0 = =math.ceil(speed_print * 25 / 50)
speed_print = 50 speed_print = 50
speed_topbottom = 40 speed_topbottom = =math.ceil(speed_print * 40 / 50)
speed_travel = 200 speed_travel = 200
speed_wall_0 = 40 speed_wall_0 = =math.ceil(speed_print * 40 / 50)
speed_wall_x = =speed_print speed_wall_x = =speed_print
support_angle = 70 support_angle = 70
support_type = buildplate support_type = buildplate

View File

@ -1,6 +1,6 @@
[general] [general]
version = 4 version = 4
name = Draft Quality name = Draft
definition = fdmprinter definition = fdmprinter
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 4 version = 4
name = Extra Coarse Quality name = Extra Coarse
definition = fdmprinter definition = fdmprinter
[metadata] [metadata]

View File

@ -43,11 +43,11 @@ skirt_brim_minimal_length = 75
skirt_gap = 1.5 skirt_gap = 1.5
skirt_line_count = 5 skirt_line_count = 5
speed_infill = =speed_print speed_infill = =speed_print
speed_layer_0 = 25 speed_layer_0 = =math.ceil(speed_print * 25 / 30)
speed_print = 30 speed_print = 30
speed_topbottom = 40 speed_topbottom = =math.ceil(speed_print * 40 / 30)
speed_travel = 200 speed_travel = 200
speed_wall_0 = 40 speed_wall_0 = =math.ceil(speed_print * 40 / 30)
speed_wall_x = =speed_print speed_wall_x = =speed_print
support_angle = 70 support_angle = 70
support_type = buildplate support_type = buildplate

View File

@ -43,11 +43,11 @@ skirt_brim_minimal_length = 75
skirt_gap = 1.5 skirt_gap = 1.5
skirt_line_count = 5 skirt_line_count = 5
speed_infill = =speed_print speed_infill = =speed_print
speed_layer_0 = 25 speed_layer_0 = =math.ceil(speed_print * 25 / 30)
speed_print = 30 speed_print = 30
speed_topbottom = 40 speed_topbottom = =math.ceil(speed_print * 40 / 30)
speed_travel = 200 speed_travel = 200
speed_wall_0 = 40 speed_wall_0 = =math.ceil(speed_print * 40 / 30)
speed_wall_x = =speed_print speed_wall_x = =speed_print
support_angle = 70 support_angle = 70
support_type = buildplate support_type = buildplate

View File

@ -43,11 +43,11 @@ skirt_brim_minimal_length = 75
skirt_gap = 1.5 skirt_gap = 1.5
skirt_line_count = 5 skirt_line_count = 5
speed_infill = =speed_print speed_infill = =speed_print
speed_layer_0 = 25 speed_layer_0 = =math.ceil(speed_print * 25 / 30)
speed_print = 30 speed_print = 30
speed_topbottom = 40 speed_topbottom = =math.ceil(speed_print * 40 / 30)
speed_travel = 200 speed_travel = 200
speed_wall_0 = 40 speed_wall_0 = =math.ceil(speed_print * 40 / 30)
speed_wall_x = =speed_print speed_wall_x = =speed_print
support_angle = 70 support_angle = 70
support_type = buildplate support_type = buildplate

View File

@ -1,6 +1,6 @@
[general] [general]
version = 4 version = 4
name = Low Quality name = Normal
definition = fdmprinter definition = fdmprinter
[metadata] [metadata]
@ -14,8 +14,8 @@ global_quality = True
infill_sparse_density = 10 infill_sparse_density = 10
layer_height = 0.15 layer_height = 0.15
cool_min_layer_time = 3 cool_min_layer_time = 3
speed_wall_0 = 40 speed_wall_0 = =math.ceil(speed_print * 40 / 60)
speed_wall_x = 80 speed_wall_x = =math.ceil(speed_print * 80 / 60)
speed_infill = 100 speed_infill = =math.ceil(speed_print * 100 / 60)
wall_thickness = 1 wall_thickness = 1
speed_topbottom = 30 speed_topbottom = =math.ceil(speed_print * 30 / 60)

View File

@ -12,5 +12,5 @@ global_quality = True
[values] [values]
layer_height = 0.06 layer_height = 0.06
speed_topbottom = 15 speed_topbottom = =math.ceil(speed_print * 15 / 60)
speed_infill = 80 speed_infill = =math.ceil(speed_print * 80 / 60)

View File

@ -43,13 +43,13 @@ skin_no_small_gaps_heuristic = False
skirt_brim_minimal_length = 100 skirt_brim_minimal_length = 100
skirt_brim_speed = 25 skirt_brim_speed = 25
skirt_line_count = 2 skirt_line_count = 2
speed_layer_0 = 14 speed_layer_0 = =math.ceil(speed_print * 14 / 40)
speed_print = 40 speed_print = 40
speed_slowdown_layers = 1 speed_slowdown_layers = 1
speed_topbottom = 20 speed_topbottom = =math.ceil(speed_print * 20 / 40)
speed_travel = 120 speed_travel = 120
speed_travel_layer_0 = 60 speed_travel_layer_0 = 60
speed_wall = 25 speed_wall = =math.ceil(speed_print * 25 / 40)
speed_wall_x = 35 speed_wall_x = =math.ceil(speed_print * 35 / 40)
top_thickness = =top_bottom_thickness top_thickness = =top_bottom_thickness
wall_thickness = 0.8 wall_thickness = 0.8

View File

@ -43,13 +43,13 @@ skin_no_small_gaps_heuristic = False
skirt_brim_minimal_length = 100 skirt_brim_minimal_length = 100
skirt_brim_speed = 25 skirt_brim_speed = 25
skirt_line_count = 2 skirt_line_count = 2
speed_layer_0 = 14 speed_layer_0 = =math.ceil(speed_print * 14 / 40)
speed_print = 40 speed_print = 40
speed_slowdown_layers = 1 speed_slowdown_layers = 1
speed_topbottom = 20 speed_topbottom = =math.ceil(speed_print * 20 / 40)
speed_travel = 120 speed_travel = 120
speed_travel_layer_0 = 60 speed_travel_layer_0 = 60
speed_wall = 25 speed_wall = =math.ceil(speed_print * 25 / 40)
speed_wall_x = 35 speed_wall_x = =math.ceil(speed_print * 35 / 40)
top_thickness = =top_bottom_thickness top_thickness = =top_bottom_thickness
wall_thickness = 0.8 wall_thickness = 0.8

View File

@ -43,13 +43,13 @@ skin_no_small_gaps_heuristic = False
skirt_brim_minimal_length = 100 skirt_brim_minimal_length = 100
skirt_brim_speed = 25 skirt_brim_speed = 25
skirt_line_count = 2 skirt_line_count = 2
speed_layer_0 = 14 speed_layer_0 = =math.ceil(speed_print * 14 / 40)
speed_print = 40 speed_print = 40
speed_slowdown_layers = 1 speed_slowdown_layers = 1
speed_topbottom = 20 speed_topbottom = =math.ceil(speed_print * 20 / 40)
speed_travel = 120 speed_travel = 120
speed_travel_layer_0 = 60 speed_travel_layer_0 = 60
speed_wall = 25 speed_wall = =math.ceil(speed_print * 25 / 40)
speed_wall_x = 35 speed_wall_x = =math.ceil(speed_print * 35 / 40)
top_thickness = =top_bottom_thickness top_thickness = =top_bottom_thickness
wall_thickness = 0.8 wall_thickness = 0.8

View File

@ -43,13 +43,13 @@ skin_no_small_gaps_heuristic = False
skirt_brim_minimal_length = 100 skirt_brim_minimal_length = 100
skirt_brim_speed = 25 skirt_brim_speed = 25
skirt_line_count = 2 skirt_line_count = 2
speed_layer_0 = 14 speed_layer_0 = =math.ceil(speed_print * 14 / 40)
speed_print = 40 speed_print = 40
speed_slowdown_layers = 1 speed_slowdown_layers = 1
speed_topbottom = 20 speed_topbottom = =math.ceil(speed_print * 20 / 40)
speed_travel = 120 speed_travel = 120
speed_travel_layer_0 = 60 speed_travel_layer_0 = 60
speed_wall = 25 speed_wall = =math.ceil(speed_print * 25 / 40)
speed_wall_x = 35 speed_wall_x = =math.ceil(speed_print * 35 / 40)
top_thickness = =top_bottom_thickness top_thickness = =top_bottom_thickness
wall_thickness = 0.8 wall_thickness = 0.8

View File

@ -41,13 +41,13 @@ skin_no_small_gaps_heuristic = False
skirt_brim_minimal_length = 100 skirt_brim_minimal_length = 100
skirt_brim_speed = 20 skirt_brim_speed = 20
skirt_line_count = 3 skirt_line_count = 3
speed_layer_0 = 20 speed_layer_0 = =math.ceil(speed_print * 20 / 45)
speed_print = 45 speed_print = 45
speed_slowdown_layers = 1 speed_slowdown_layers = 1
speed_topbottom = 25 speed_topbottom = =math.ceil(speed_print * 25 / 45)
speed_travel = 120 speed_travel = 120
speed_travel_layer_0 = 60 speed_travel_layer_0 = 60
speed_wall = 25 speed_wall = =math.ceil(speed_print * 25 / 45)
speed_wall_x = 35 speed_wall_x = =math.ceil(speed_print * 35 / 45)
top_thickness = 0.8 top_thickness = 0.8
wall_thickness = 0.8 wall_thickness = 0.8

View File

@ -41,13 +41,13 @@ skin_no_small_gaps_heuristic = False
skirt_brim_minimal_length = 100 skirt_brim_minimal_length = 100
skirt_brim_speed = 20 skirt_brim_speed = 20
skirt_line_count = 3 skirt_line_count = 3
speed_layer_0 = 20 speed_layer_0 = =math.ceil(speed_print * 20 / 45)
speed_print = 45 speed_print = 45
speed_slowdown_layers = 1 speed_slowdown_layers = 1
speed_topbottom = 25 speed_topbottom = =math.ceil(speed_print * 25 / 45)
speed_travel = 120 speed_travel = 120
speed_travel_layer_0 = 60 speed_travel_layer_0 = 60
speed_wall = 25 speed_wall = =math.ceil(speed_print * 25 / 45)
speed_wall_x = 35 speed_wall_x = =math.ceil(speed_print * 35 / 45)
top_thickness = 0.8 top_thickness = 0.8
wall_thickness = 0.8 wall_thickness = 0.8

View File

@ -42,13 +42,13 @@ skin_no_small_gaps_heuristic = False
skirt_brim_minimal_length = 100 skirt_brim_minimal_length = 100
skirt_brim_speed = 20 skirt_brim_speed = 20
skirt_line_count = 3 skirt_line_count = 3
speed_layer_0 = 20 speed_layer_0 = =math.ceil(speed_print * 20 / 45)
speed_print = 45 speed_print = 45
speed_slowdown_layers = 1 speed_slowdown_layers = 1
speed_topbottom = 25 speed_topbottom = =math.ceil(speed_print * 25 / 45)
speed_travel = 120 speed_travel = 120
speed_travel_layer_0 = 60 speed_travel_layer_0 = 60
speed_wall = 25 speed_wall = =math.ceil(speed_print * 25 / 45)
speed_wall_x = 35 speed_wall_x = =math.ceil(speed_print * 35 / 45)
top_thickness = 0.8 top_thickness = 0.8
wall_thickness = 0.8 wall_thickness = 0.8

View File

@ -42,13 +42,13 @@ skin_no_small_gaps_heuristic = False
skirt_brim_minimal_length = 100 skirt_brim_minimal_length = 100
skirt_brim_speed = 20 skirt_brim_speed = 20
skirt_line_count = 3 skirt_line_count = 3
speed_layer_0 = 20 speed_layer_0 = =math.ceil(speed_print * 20 / 45)
speed_print = 45 speed_print = 45
speed_slowdown_layers = 1 speed_slowdown_layers = 1
speed_topbottom = 25 speed_topbottom = =math.ceil(speed_print * 25 / 45)
speed_travel = 120 speed_travel = 120
speed_travel_layer_0 = 60 speed_travel_layer_0 = 60
speed_wall = 25 speed_wall = =math.ceil(speed_print * 25 / 45)
speed_wall_x = 35 speed_wall_x = =math.ceil(speed_print * 35 / 45)
top_thickness = 0.8 top_thickness = 0.8
wall_thickness = 0.8 wall_thickness = 0.8

View File

@ -41,13 +41,13 @@ skin_no_small_gaps_heuristic = False
skirt_brim_minimal_length = 100 skirt_brim_minimal_length = 100
skirt_brim_speed = 20 skirt_brim_speed = 20
skirt_line_count = 3 skirt_line_count = 3
speed_layer_0 = 20 speed_layer_0 = =math.ceil(speed_print * 20 / 45)
speed_print = 45 speed_print = 45
speed_slowdown_layers = 1 speed_slowdown_layers = 1
speed_topbottom = 25 speed_topbottom = =math.ceil(speed_print * 25 / 45)
speed_travel = 120 speed_travel = 120
speed_travel_layer_0 = 60 speed_travel_layer_0 = 60
speed_wall = 25 speed_wall = =math.ceil(speed_print * 25 / 45)
speed_wall_x = 35 speed_wall_x = =math.ceil(speed_print * 35 / 45)
top_thickness = 0.8 top_thickness = 0.8
wall_thickness = 0.8 wall_thickness = 0.8

View File

@ -41,13 +41,13 @@ skin_no_small_gaps_heuristic = False
skirt_brim_minimal_length = 100 skirt_brim_minimal_length = 100
skirt_brim_speed = 20 skirt_brim_speed = 20
skirt_line_count = 3 skirt_line_count = 3
speed_layer_0 = 20 speed_layer_0 = =math.ceil(speed_print * 20 / 45)
speed_print = 45 speed_print = 45
speed_slowdown_layers = 1 speed_slowdown_layers = 1
speed_topbottom = 25 speed_topbottom = =math.ceil(speed_print * 25 / 45)
speed_travel = 120 speed_travel = 120
speed_travel_layer_0 = 60 speed_travel_layer_0 = 60
speed_wall = 25 speed_wall = =math.ceil(speed_print * 25 / 45)
speed_wall_x = 35 speed_wall_x = =math.ceil(speed_print * 35 / 45)
top_thickness = 0.8 top_thickness = 0.8
wall_thickness = 0.8 wall_thickness = 0.8

View File

@ -43,13 +43,13 @@ skin_no_small_gaps_heuristic = False
skirt_brim_minimal_length = 100 skirt_brim_minimal_length = 100
skirt_brim_speed = 20 skirt_brim_speed = 20
skirt_line_count = 3 skirt_line_count = 3
speed_layer_0 = 20 speed_layer_0 = =math.ceil(speed_print * 20 / 45)
speed_print = 45 speed_print = 45
speed_slowdown_layers = 1 speed_slowdown_layers = 1
speed_topbottom = 25 speed_topbottom = =math.ceil(speed_print * 25 / 45)
speed_travel = 120 speed_travel = 120
speed_travel_layer_0 = 60 speed_travel_layer_0 = 60
speed_wall = 25 speed_wall = =math.ceil(speed_print * 25 / 45)
speed_wall_x = 35 speed_wall_x = =math.ceil(speed_print * 35 / 45)
top_thickness = 0.8 top_thickness = 0.8
wall_thickness = 0.8 wall_thickness = 0.8

View File

@ -43,13 +43,13 @@ skin_no_small_gaps_heuristic = False
skirt_brim_minimal_length = 100 skirt_brim_minimal_length = 100
skirt_brim_speed = 20 skirt_brim_speed = 20
skirt_line_count = 3 skirt_line_count = 3
speed_layer_0 = 20 speed_layer_0 = =math.ceil(speed_print * 20 / 45)
speed_print = 45 speed_print = 45
speed_slowdown_layers = 1 speed_slowdown_layers = 1
speed_topbottom = 25 speed_topbottom = =math.ceil(speed_print * 25 / 45)
speed_travel = 120 speed_travel = 120
speed_travel_layer_0 = 60 speed_travel_layer_0 = 60
speed_wall = 25 speed_wall = =math.ceil(speed_print * 25 / 45)
speed_wall_x = 35 speed_wall_x = =math.ceil(speed_print * 35 / 45)
top_thickness = 0.8 top_thickness = 0.8
wall_thickness = 0.8 wall_thickness = 0.8

View File

@ -15,11 +15,11 @@ layer_height = 0.35
layer_height_0 = 0.3 layer_height_0 = 0.3
speed_print = 70 speed_print = 70
speed_infill = 60 speed_infill = =math.ceil(speed_print * 60 / 70)
speed_layer_0 = 20 speed_layer_0 = =math.ceil(speed_print * 20 / 70)
speed_wall_0 = 30 speed_wall_0 = =math.ceil(speed_print * 30 / 70)
speed_wall_x = 50 speed_wall_x = =math.ceil(speed_print * 50 / 70)
speed_topbottom = 30 speed_topbottom = =math.ceil(speed_print * 30 / 70)
speed_travel = 120 speed_travel = 120
material_print_temperature = 246 material_print_temperature = 246

View File

@ -15,11 +15,11 @@ layer_height = 0.06
layer_height_0 = 0.3 layer_height_0 = 0.3
speed_print = 40 speed_print = 40
speed_infill = 50 speed_infill = =math.ceil(speed_print * 50 / 40)
speed_layer_0 = 15 speed_layer_0 = =math.ceil(speed_print * 15 / 40)
speed_wall_0 = 20 speed_wall_0 = =math.ceil(speed_print * 20 / 40)
speed_wall_x = 40 speed_wall_x = =speed_print
speed_topbottom = 20 speed_topbottom = =math.ceil(speed_print * 20 / 40)
speed_travel = 120 speed_travel = 120
material_print_temperature = 246 material_print_temperature = 246

View File

@ -15,11 +15,11 @@ layer_height = 0.1
layer_height_0 = 0.3 layer_height_0 = 0.3
speed_print = 50 speed_print = 50
speed_infill = 60 speed_infill = =math.ceil(speed_print * 60 / 50)
speed_layer_0 = 20 speed_layer_0 = =math.ceil(speed_print * 20 / 50)
speed_wall_0 = 25 speed_wall_0 = =math.ceil(speed_print * 25 / 50)
speed_wall_x = 45 speed_wall_x = =math.ceil(speed_print * 45 / 50)
speed_topbottom = 30 speed_topbottom = =math.ceil(speed_print * 30 / 50)
speed_travel = 120 speed_travel = 120
material_print_temperature = 246 material_print_temperature = 246

View File

@ -15,11 +15,11 @@ layer_height = 0.2
layer_height_0 = 0.3 layer_height_0 = 0.3
speed_print = 70 speed_print = 70
speed_infill = 60 speed_infill = =math.ceil(speed_print * 60 / 70)
speed_layer_0 = 20 speed_layer_0 = =math.ceil(speed_print * 20 / 70)
speed_wall_0 = 30 speed_wall_0 = =math.ceil(speed_print * 30 / 70)
speed_wall_x = 50 speed_wall_x = =math.ceil(speed_print * 50 / 70)
speed_topbottom = 30 speed_topbottom = =math.ceil(speed_print * 30 / 70)
speed_travel = 120 speed_travel = 120
material_print_temperature = 246 material_print_temperature = 246

View File

@ -15,11 +15,11 @@ layer_height = 0.15
layer_height_0 = 0.3 layer_height_0 = 0.3
speed_print = 60 speed_print = 60
speed_infill = 50 speed_infill = =math.ceil(speed_print * 50 / 60)
speed_layer_0 = 15 speed_layer_0 = =math.ceil(speed_print * 15 / 60)
speed_wall_0 = 20 speed_wall_0 = =math.ceil(speed_print * 20 / 60)
speed_wall_x = 40 speed_wall_x = =math.ceil(speed_print * 40 / 60)
speed_topbottom = 20 speed_topbottom = =math.ceil(speed_print * 20 / 60)
speed_travel = 120 speed_travel = 120
material_print_temperature = 246 material_print_temperature = 246

View File

@ -15,11 +15,11 @@ layer_height = 0.06
adhesion_type = skirt adhesion_type = skirt
speed_print = 40 speed_print = 40
speed_infill = 50 speed_infill = =math.ceil(speed_print * 50 / 40)
speed_layer_0 = 15 speed_layer_0 = =math.ceil(speed_print * 15 / 40)
speed_wall_0 = 20 speed_wall_0 = =math.ceil(speed_print * 20 / 40)
speed_wall_x = 40 speed_wall_x = =speed_print
speed_topbottom = 20 speed_topbottom = =math.ceil(speed_print * 20 / 40)
speed_travel = 120 speed_travel = 120
material_print_temperature = 214 material_print_temperature = 214

View File

@ -15,11 +15,11 @@ layer_height = 0.1
adhesion_type = skirt adhesion_type = skirt
speed_print = 50 speed_print = 50
speed_infill = 60 speed_infill = =math.ceil(speed_print * 60 / 50)
speed_layer_0 = 20 speed_layer_0 = =math.ceil(speed_print * 20 / 50)
speed_wall_0 = 25 speed_wall_0 = =math.ceil(speed_print * 25 / 50)
speed_wall_x = 45 speed_wall_x = =math.ceil(speed_print * 45 / 50)
speed_topbottom = 30 speed_topbottom = =math.ceil(speed_print * 30 / 50)
speed_travel = 120 speed_travel = 120
material_print_temperature = 214 material_print_temperature = 214

View File

@ -15,11 +15,11 @@ layer_height = 0.15
adhesion_type = skirt adhesion_type = skirt
speed_print = 60 speed_print = 60
speed_infill = 50 speed_infill = =math.ceil(speed_print * 50 / 60)
speed_layer_0 = 15 speed_layer_0 = =math.ceil(speed_print * 15 / 60)
speed_wall_0 = 20 speed_wall_0 = =math.ceil(speed_print * 20 / 60)
speed_wall_x = 40 speed_wall_x = =math.ceil(speed_print * 40 / 60)
speed_topbottom = 20 speed_topbottom = =math.ceil(speed_print * 20 / 60)
speed_travel = 120 speed_travel = 120
material_print_temperature = 214 material_print_temperature = 214

View File

@ -18,6 +18,6 @@ top_bottom_thickness = 0.72
infill_sparse_density = 22 infill_sparse_density = 22
speed_print = 50 speed_print = 50
speed_layer_0 = =round(speed_print * 30 / 50) speed_layer_0 = =round(speed_print * 30 / 50)
speed_topbottom = 20 speed_topbottom = =math.ceil(speed_print * 20 / 50)
cool_min_layer_time = 5 cool_min_layer_time = 5
cool_min_speed = 10 cool_min_speed = 10

View File

@ -18,6 +18,6 @@ top_bottom_thickness = 0.72
infill_sparse_density = 22 infill_sparse_density = 22
speed_print = 50 speed_print = 50
speed_layer_0 = =round(speed_print * 30 / 50) speed_layer_0 = =round(speed_print * 30 / 50)
speed_topbottom = 20 speed_topbottom = =math.ceil(speed_print * 20 / 50)
cool_min_layer_time = 5 cool_min_layer_time = 5
cool_min_speed = 10 cool_min_speed = 10

View File

@ -18,6 +18,6 @@ top_bottom_thickness = 0.72
infill_sparse_density = 22 infill_sparse_density = 22
speed_print = 50 speed_print = 50
speed_layer_0 = =round(speed_print * 30 / 50) speed_layer_0 = =round(speed_print * 30 / 50)
speed_topbottom = 20 speed_topbottom = =math.ceil(speed_print * 20 / 50)
cool_min_layer_time = 5 cool_min_layer_time = 5
cool_min_speed = 10 cool_min_speed = 10

View File

@ -18,6 +18,6 @@ top_bottom_thickness = 0.72
infill_sparse_density = 22 infill_sparse_density = 22
speed_print = 50 speed_print = 50
speed_layer_0 = =round(speed_print * 30 / 50) speed_layer_0 = =round(speed_print * 30 / 50)
speed_topbottom = 20 speed_topbottom = =math.ceil(speed_print * 20 / 50)
cool_min_layer_time = 5 cool_min_layer_time = 5
cool_min_speed = 10 cool_min_speed = 10

View File

@ -18,6 +18,6 @@ top_bottom_thickness = 0.72
infill_sparse_density = 22 infill_sparse_density = 22
speed_print = 50 speed_print = 50
speed_layer_0 = =round(speed_print * 30 / 50) speed_layer_0 = =round(speed_print * 30 / 50)
speed_topbottom = 20 speed_topbottom = =math.ceil(speed_print * 20 / 50)
cool_min_layer_time = 5 cool_min_layer_time = 5
cool_min_speed = 10 cool_min_speed = 10

View File

@ -18,6 +18,6 @@ top_bottom_thickness = 0.72
infill_sparse_density = 22 infill_sparse_density = 22
speed_print = 50 speed_print = 50
speed_layer_0 = =round(speed_print * 30 / 50) speed_layer_0 = =round(speed_print * 30 / 50)
speed_topbottom = 20 speed_topbottom = =math.ceil(speed_print * 20 / 50)
cool_min_layer_time = 5 cool_min_layer_time = 5
cool_min_speed = 10 cool_min_speed = 10

View File

@ -18,6 +18,6 @@ top_bottom_thickness = 0.72
infill_sparse_density = 22 infill_sparse_density = 22
speed_print = 50 speed_print = 50
speed_layer_0 = =round(speed_print * 30 / 50) speed_layer_0 = =round(speed_print * 30 / 50)
speed_topbottom = 20 speed_topbottom = =math.ceil(speed_print * 20 / 50)
cool_min_layer_time = 5 cool_min_layer_time = 5
cool_min_speed = 10 cool_min_speed = 10

View File

@ -18,6 +18,6 @@ top_bottom_thickness = 0.72
infill_sparse_density = 22 infill_sparse_density = 22
speed_print = 50 speed_print = 50
speed_layer_0 = =round(speed_print * 30 / 50) speed_layer_0 = =round(speed_print * 30 / 50)
speed_topbottom = 20 speed_topbottom = =math.ceil(speed_print * 20 / 50)
cool_min_layer_time = 5 cool_min_layer_time = 5
cool_min_speed = 10 cool_min_speed = 10

View File

@ -18,6 +18,6 @@ top_bottom_thickness = 0.72
infill_sparse_density = 22 infill_sparse_density = 22
speed_print = 50 speed_print = 50
speed_layer_0 = =round(speed_print * 30 / 50) speed_layer_0 = =round(speed_print * 30 / 50)
speed_topbottom = 20 speed_topbottom = =math.ceil(speed_print * 20 / 50)
cool_min_layer_time = 5 cool_min_layer_time = 5
cool_min_speed = 10 cool_min_speed = 10

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