mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-09-27 10:43:13 +08:00
Merge branch 'mypy_fixes' of ssh://github.com/Ultimaker/Cura into mypy_fixes
This commit is contained in:
commit
5bf553c63c
@ -1,32 +1,30 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
from typing import Tuple, Optional
|
||||
|
||||
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:
|
||||
"""
|
||||
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.
|
||||
|
||||
def createBackup(self) -> (bytes, dict):
|
||||
"""
|
||||
Create a new backup using the BackupsManager.
|
||||
:return: Tuple containing a ZIP file with the backup data and a dict with meta data about the backup.
|
||||
"""
|
||||
## Create a new back-up using the BackupsManager.
|
||||
# \return Tuple containing a ZIP file with the back-up data and a dict
|
||||
# with metadata about the back-up.
|
||||
def createBackup(self) -> Tuple[Optional[bytes], Optional[dict]]:
|
||||
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:
|
||||
"""
|
||||
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)
|
||||
|
@ -3,14 +3,13 @@
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
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:
|
||||
"""
|
||||
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.
|
||||
VERSION = PluginRegistry.APIVersion
|
||||
|
@ -1,10 +1,12 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
from typing import List
|
||||
|
||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
from UM.Logger import Logger
|
||||
from UM.Math.Polygon import Polygon
|
||||
from UM.Math.Vector import Vector
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from cura.Arranging.ShapeArray import ShapeArray
|
||||
from cura.Scene import ZOffsetDecorator
|
||||
|
||||
@ -85,8 +87,7 @@ class Arrange:
|
||||
# \param node
|
||||
# \param offset_shape_arr ShapeArray with offset, for placing the shape
|
||||
# \param hull_shape_arr ShapeArray without offset, used to find location
|
||||
def findNodePlacement(self, node, offset_shape_arr, hull_shape_arr, step = 1):
|
||||
new_node = copy.deepcopy(node)
|
||||
def findNodePlacement(self, node: SceneNode, offset_shape_arr: ShapeArray, hull_shape_arr: ShapeArray, step = 1):
|
||||
best_spot = self.bestSpot(
|
||||
hull_shape_arr, start_prio = self._last_priority, step = step)
|
||||
x, y = best_spot.x, best_spot.y
|
||||
@ -95,21 +96,21 @@ class Arrange:
|
||||
self._last_priority = best_spot.priority
|
||||
|
||||
# Ensure that the object is above the build platform
|
||||
new_node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator)
|
||||
if new_node.getBoundingBox():
|
||||
center_y = new_node.getWorldPosition().y - new_node.getBoundingBox().bottom
|
||||
node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator)
|
||||
if node.getBoundingBox():
|
||||
center_y = node.getWorldPosition().y - node.getBoundingBox().bottom
|
||||
else:
|
||||
center_y = 0
|
||||
|
||||
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
|
||||
self.place(x, y, offset_shape_arr) # place the object in arranger
|
||||
else:
|
||||
Logger.log("d", "Could not find spot!"),
|
||||
found_spot = False
|
||||
new_node.setPosition(Vector(200, center_y, 100))
|
||||
return new_node, found_spot
|
||||
node.setPosition(Vector(200, center_y, 100))
|
||||
return found_spot
|
||||
|
||||
## Fill priority, center is best. Lower value is better
|
||||
# This is a strategy for the arranger.
|
||||
|
@ -20,14 +20,14 @@ from typing import List
|
||||
|
||||
## Do arrangements on multiple build plates (aka builtiplexer)
|
||||
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._y = y
|
||||
self._fixed_nodes = fixed_nodes
|
||||
self._count = 0
|
||||
self._first_empty = None
|
||||
self._has_empty = False
|
||||
self._arrange = []
|
||||
self._arrange = [] # type: List[Arrange]
|
||||
|
||||
def _update_first_empty(self):
|
||||
for i, a in enumerate(self._arrange):
|
||||
@ -48,16 +48,17 @@ class ArrangeArray:
|
||||
return self._count
|
||||
|
||||
def get(self, index):
|
||||
print(self._arrange)
|
||||
return self._arrange[index]
|
||||
|
||||
def getFirstEmpty(self):
|
||||
if not self._is_empty:
|
||||
if not self._has_empty:
|
||||
self.add()
|
||||
return self._arrange[self._first_empty]
|
||||
|
||||
|
||||
class ArrangeObjectsAllBuildPlatesJob(Job):
|
||||
def __init__(self, nodes: List[SceneNode], min_offset = 8):
|
||||
def __init__(self, nodes: List[SceneNode], min_offset = 8) -> None:
|
||||
super().__init__()
|
||||
self._nodes = nodes
|
||||
self._min_offset = min_offset
|
||||
|
@ -20,7 +20,7 @@ from typing import List
|
||||
|
||||
|
||||
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__()
|
||||
self._nodes = nodes
|
||||
self._fixed_nodes = fixed_nodes
|
||||
|
@ -17,26 +17,23 @@ from UM.Resources import Resources
|
||||
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:
|
||||
"""
|
||||
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.
|
||||
IGNORED_FILES = [r"cura\.log", r"plugins\.json", r"cache", r"__pycache__", r"\.qmlc", r"\.pyc"]
|
||||
|
||||
# Re-use translation catalog.
|
||||
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.meta_data = meta_data # type: Optional[dict]
|
||||
|
||||
def makeFromCurrent(self) -> (bool, Optional[str]):
|
||||
"""
|
||||
Create a backup from the current user config folder.
|
||||
"""
|
||||
## Create a back-up from the current user config folder.
|
||||
def makeFromCurrent(self) -> None:
|
||||
cura_release = CuraApplication.getInstance().getVersion()
|
||||
version_data_dir = Resources.getDataStoragePath()
|
||||
|
||||
@ -57,6 +54,8 @@ class Backup:
|
||||
# Create an empty buffer and write the archive to it.
|
||||
buffer = io.BytesIO()
|
||||
archive = self._makeArchive(buffer, version_data_dir)
|
||||
if archive is None:
|
||||
return
|
||||
files = archive.namelist()
|
||||
|
||||
# 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)
|
||||
}
|
||||
|
||||
## 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]:
|
||||
"""
|
||||
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))
|
||||
try:
|
||||
archive = ZipFile(buffer, "w", ZIP_DEFLATED)
|
||||
@ -99,15 +96,13 @@ class Backup:
|
||||
"Could not create archive from user data directory: {}".format(error)))
|
||||
return None
|
||||
|
||||
## Show a UI message.
|
||||
def _showMessage(self, message: str) -> None:
|
||||
"""Show a UI message"""
|
||||
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:
|
||||
"""
|
||||
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):
|
||||
# We can restore without the minimum required information.
|
||||
Logger.log("w", "Tried to restore a Cura backup without having proper data or meta data.")
|
||||
@ -140,14 +135,12 @@ class Backup:
|
||||
|
||||
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
|
||||
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)
|
||||
Resources.factoryReset()
|
||||
Logger.log("d", "Extracting backup to location: %s", target_path)
|
||||
|
@ -1,25 +1,24 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# 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 cura.Backups.Backup import Backup
|
||||
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:
|
||||
"""
|
||||
The BackupsManager is responsible for managing the creating and restoring of backups.
|
||||
Backups themselves are represented in a different class.
|
||||
"""
|
||||
def __init__(self):
|
||||
self._application = CuraApplication.getInstance()
|
||||
|
||||
def createBackup(self) -> (Optional[bytes], Optional[dict]):
|
||||
"""
|
||||
Get a backup of the current configuration.
|
||||
:return: A Tuple containing a ZipFile (the actual backup) and a dict containing some meta data (like version).
|
||||
"""
|
||||
## Get a back-up of the current configuration.
|
||||
# \return A tuple containing a ZipFile (the actual back-up) and a dict
|
||||
# containing some metadata (like version).
|
||||
def createBackup(self) -> Tuple[Optional[bytes], Optional[dict]]:
|
||||
self._disableAutoSave()
|
||||
backup = Backup()
|
||||
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.
|
||||
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:
|
||||
"""
|
||||
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 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.")
|
||||
@ -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.
|
||||
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):
|
||||
"""Here we try to disable the auto-save plugin as it might interfere with restoring a backup."""
|
||||
self._application.setSaveDataEnabled(False)
|
||||
|
||||
## Re-enable auto-save after we're done.
|
||||
def _enableAutoSave(self):
|
||||
"""Re-enable auto-save after we're done."""
|
||||
self._application.setSaveDataEnabled(True)
|
||||
|
@ -225,6 +225,8 @@ class CuraApplication(QtApplication):
|
||||
|
||||
from cura.Settings.CuraContainerRegistry import 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
|
||||
# before the pre-start.
|
||||
@ -511,7 +513,6 @@ class CuraApplication(QtApplication):
|
||||
preferences.addPreference("cura/asked_dialog_on_project_save", False)
|
||||
preferences.addPreference("cura/choice_on_profile_override", "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/currency", "€")
|
||||
@ -1601,9 +1602,7 @@ class CuraApplication(QtApplication):
|
||||
self._currently_loading_files.remove(filename)
|
||||
|
||||
self.fileLoaded.emit(filename)
|
||||
arrange_objects_on_load = (
|
||||
not self.getPreferences().getValue("cura/use_multi_build_plate") or
|
||||
not self.getPreferences().getValue("cura/not_arrange_objects_on_load"))
|
||||
arrange_objects_on_load = not self.getPreferences().getValue("cura/use_multi_build_plate")
|
||||
target_build_plate = self.getMultiBuildPlateModel().activeBuildPlate if arrange_objects_on_load else -1
|
||||
|
||||
root = self.getController().getScene().getRoot()
|
||||
@ -1676,7 +1675,7 @@ class CuraApplication(QtApplication):
|
||||
return
|
||||
|
||||
# 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
|
||||
# of BuildPlateDecorator produces one that's associated with build plate -1. So, here we need to check if
|
||||
|
@ -7,8 +7,11 @@ from UM.Resources import Resources #To find storage paths for some resource type
|
||||
|
||||
|
||||
class CuraPackageManager(PackageManager):
|
||||
def __init__(self, parent = None):
|
||||
super().__init__(parent)
|
||||
def __init__(self, application, parent = None):
|
||||
super().__init__(application, parent)
|
||||
|
||||
def initialize(self):
|
||||
self._installation_dirs_dict["materials"] = Resources.getStoragePath(CuraApplication.ResourceTypes.MaterialInstanceContainer)
|
||||
self._installation_dirs_dict["qualities"] = Resources.getStoragePath(CuraApplication.ResourceTypes.QualityInstanceContainer)
|
||||
|
||||
super().initialize()
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# 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
|
||||
|
||||
@ -9,6 +9,9 @@ from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
|
||||
from UM.Logger import Logger
|
||||
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.
|
||||
@ -23,10 +26,16 @@ from UM.Settings.InstanceContainer import InstanceContainer
|
||||
class ContainerNode:
|
||||
__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.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"]:
|
||||
return self.children_map.get(child_key)
|
||||
@ -50,4 +59,4 @@ class ContainerNode:
|
||||
return self.container
|
||||
|
||||
def __str__(self) -> str:
|
||||
return "%s[%s]" % (self.__class__.__name__, self.metadata.get("id"))
|
||||
return "%s[%s]" % (self.__class__.__name__, self.getMetaDataEntry("id"))
|
||||
|
@ -18,10 +18,10 @@ from cura.Machines.MaterialNode import MaterialNode #For type checking.
|
||||
class MaterialGroup:
|
||||
__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.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]
|
||||
|
||||
def __str__(self) -> str:
|
||||
|
@ -4,6 +4,7 @@
|
||||
from collections import defaultdict, OrderedDict
|
||||
import copy
|
||||
import uuid
|
||||
from typing import Dict
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
|
||||
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.
|
||||
#
|
||||
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
|
||||
rounded_diameter = str(round(diameter))
|
||||
if rounded_diameter not in self._diameter_machine_variant_material_map:
|
||||
@ -288,7 +289,7 @@ class MaterialManager(QObject):
|
||||
# 3. generic material (for fdmprinter)
|
||||
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:
|
||||
if node is not None:
|
||||
# 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
|
||||
for node in nodes_to_remove:
|
||||
self._container_registry.removeContainer(node.metadata["id"])
|
||||
self._container_registry.removeContainer(node.getMetaDataEntry("id", ""))
|
||||
|
||||
#
|
||||
# Methods for GUI
|
||||
@ -445,22 +446,27 @@ class MaterialManager(QObject):
|
||||
#
|
||||
@pyqtSlot("QVariant", 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):
|
||||
Logger.log("w", "Cannot set name of read-only container %s.", root_material_id)
|
||||
return
|
||||
|
||||
material_group = self.getMaterialGroup(root_material_id)
|
||||
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.
|
||||
#
|
||||
@pyqtSlot("QVariant")
|
||||
def removeMaterial(self, material_node: "MaterialNode"):
|
||||
root_material_id = material_node.metadata["base_file"]
|
||||
self.removeMaterialByRootId(root_material_id)
|
||||
root_material_id = material_node.getMetaDataEntry("base_file")
|
||||
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.
|
||||
@ -539,6 +545,10 @@ class MaterialManager(QObject):
|
||||
root_material_id = self.getRootMaterialIDForDiameter(root_material_id, approximate_diameter)
|
||||
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.
|
||||
new_id = self._container_registry.uniqueName("custom_material")
|
||||
new_metadata = {"name": catalog.i18nc("@label", "Custom Material"),
|
||||
|
@ -1,7 +1,6 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from typing import Optional
|
||||
from typing import Optional, Dict
|
||||
|
||||
from .ContainerNode import ContainerNode
|
||||
|
||||
@ -15,7 +14,6 @@ from .ContainerNode import ContainerNode
|
||||
class MaterialNode(ContainerNode):
|
||||
__slots__ = ("material_map", "children_map")
|
||||
|
||||
def __init__(self, metadata: Optional[dict] = None):
|
||||
def __init__(self, metadata: Optional[dict] = None) -> None:
|
||||
super().__init__(metadata = metadata)
|
||||
self.material_map = {} # material_root_id -> material_node
|
||||
self.children_map = {} # mapping for the child nodes
|
||||
self.material_map = {} # type: Dict[str, MaterialNode] # material_root_id -> material_node
|
||||
|
@ -83,7 +83,7 @@ class QualityProfilesDropDownMenuModel(ListModel):
|
||||
|
||||
self.setItems(item_list)
|
||||
|
||||
def _fetchLayerHeight(self, quality_group: "QualityGroup"):
|
||||
def _fetchLayerHeight(self, quality_group: "QualityGroup") -> float:
|
||||
global_stack = self._machine_manager.activeMachine
|
||||
if not self._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")
|
||||
|
||||
# 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()
|
||||
|
||||
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")
|
||||
else:
|
||||
# Look for layer_height in the GlobalStack from material -> definition
|
||||
|
@ -1,22 +1,27 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
|
||||
|
||||
from .QualityGroup import QualityGroup
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from cura.Machines.QualityNode import QualityNode
|
||||
|
||||
|
||||
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)
|
||||
self._container_registry = Application.getInstance().getContainerRegistry()
|
||||
|
||||
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.
|
||||
ConfigurationErrorMessage.getInstance().addFaultyContainers(node.metadata["id"])
|
||||
ConfigurationErrorMessage.getInstance().addFaultyContainers(node.getMetaDataEntry("id"))
|
||||
return
|
||||
|
||||
if extruder_position is None: #Then we're a global quality changes profile.
|
||||
|
@ -4,7 +4,7 @@
|
||||
from typing import Dict, Optional, List, Set
|
||||
|
||||
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.
|
||||
@ -24,8 +24,8 @@ class QualityGroup(QObject):
|
||||
def __init__(self, name: str, quality_type: str, parent = None) -> None:
|
||||
super().__init__(parent)
|
||||
self.name = name
|
||||
self.node_for_global = None # type: Optional["QualityGroup"]
|
||||
self.nodes_for_extruders = {} # type: Dict[int, "QualityGroup"]
|
||||
self.node_for_global = None # type: Optional[ContainerNode]
|
||||
self.nodes_for_extruders = {} # type: Dict[int, ContainerNode]
|
||||
self.quality_type = quality_type
|
||||
self.is_available = False
|
||||
|
||||
@ -38,10 +38,12 @@ class QualityGroup(QObject):
|
||||
for node in [self.node_for_global] + list(self.nodes_for_extruders.values()):
|
||||
if node is None:
|
||||
continue
|
||||
result.update(node.getContainer().getAllKeys())
|
||||
container = node.getContainer()
|
||||
if container:
|
||||
result.update(container.getAllKeys())
|
||||
return result
|
||||
|
||||
def getAllNodes(self) -> List["QualityGroup"]:
|
||||
def getAllNodes(self) -> List[ContainerNode]:
|
||||
result = []
|
||||
if self.node_for_global is not None:
|
||||
result.append(self.node_for_global)
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# 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
|
||||
|
||||
@ -90,7 +90,7 @@ class QualityManager(QObject):
|
||||
|
||||
if definition_id not in self._machine_variant_material_quality_type_to_quality_dict:
|
||||
self._machine_variant_material_quality_type_to_quality_dict[definition_id] = QualityNode()
|
||||
machine_node = 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:
|
||||
# For global qualities, save data in the machine node
|
||||
@ -102,7 +102,7 @@ class QualityManager(QObject):
|
||||
# too.
|
||||
if variant_name not in machine_node.children_map:
|
||||
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 only variant_name is specified but material is not, add the quality/quality_changes metadata
|
||||
@ -114,7 +114,7 @@ class QualityManager(QObject):
|
||||
# material node.
|
||||
if root_material_id not in variant_node.children_map:
|
||||
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)
|
||||
|
||||
@ -123,7 +123,7 @@ class QualityManager(QObject):
|
||||
if root_material_id is not None:
|
||||
if root_material_id not in machine_node.children_map:
|
||||
machine_node.children_map[root_material_id] = QualityNode()
|
||||
material_node = machine_node.children_map[root_material_id]
|
||||
material_node = cast(QualityNode, machine_node.children_map[root_material_id])
|
||||
|
||||
material_node.addQualityMetadata(quality_type, metadata)
|
||||
|
||||
@ -351,7 +351,7 @@ class QualityManager(QObject):
|
||||
def removeQualityChangesGroup(self, quality_changes_group: "QualityChangesGroup"):
|
||||
Logger.log("i", "Removing quality changes group [%s]", quality_changes_group.name)
|
||||
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.
|
||||
@ -365,7 +365,9 @@ class QualityManager(QObject):
|
||||
|
||||
new_name = self._container_registry.uniqueName(new_name)
|
||||
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
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# 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 .QualityChangesGroup import QualityChangesGroup
|
||||
@ -12,9 +12,9 @@ from .QualityChangesGroup import QualityChangesGroup
|
||||
#
|
||||
class QualityNode(ContainerNode):
|
||||
|
||||
def __init__(self, metadata: Optional[dict] = None):
|
||||
def __init__(self, metadata: Optional[dict] = None) -> None:
|
||||
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):
|
||||
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:
|
||||
quality_type_node.children_map[name] = QualityChangesGroup(name, quality_type)
|
||||
quality_changes_group = quality_type_node.children_map[name]
|
||||
quality_changes_group.addNode(QualityNode(metadata))
|
||||
cast(QualityChangesGroup, quality_changes_group).addNode(QualityNode(metadata))
|
||||
|
@ -64,10 +64,11 @@ class MultiplyObjectsJob(Job):
|
||||
arranger.resetLastPriority()
|
||||
for i in range(self._count):
|
||||
# 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:
|
||||
new_node, solution_found = arranger.findNodePlacement(current_node, offset_shape_arr, hull_shape_arr)
|
||||
else:
|
||||
new_node = copy.deepcopy(node)
|
||||
solution_found = arranger.findNodePlacement(new_node, offset_shape_arr, hull_shape_arr)
|
||||
|
||||
if node_too_big or not solution_found:
|
||||
found_solution_for_all = False
|
||||
new_location = new_node.getPosition()
|
||||
|
@ -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
|
||||
class PickingPass(RenderPass):
|
||||
def __init__(self, width: int, height: int):
|
||||
def __init__(self, width: int, height: int) -> None:
|
||||
super().__init__("picking", width, height)
|
||||
|
||||
self._renderer = Application.getInstance().getRenderer()
|
||||
|
@ -8,6 +8,7 @@ from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
|
||||
from UM.Math.Vector import Vector
|
||||
from UM.Scene.Selection import Selection
|
||||
from UM.Scene.SceneNodeSettings import SceneNodeSettings
|
||||
|
||||
from cura.Scene.ConvexHullDecorator import ConvexHullDecorator
|
||||
|
||||
@ -80,6 +81,10 @@ class PlatformPhysics:
|
||||
|
||||
# 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"):
|
||||
# Do not move locked nodes
|
||||
if node.getSetting(SceneNodeSettings.LockPosition):
|
||||
continue
|
||||
|
||||
# Check for collisions between convex hulls
|
||||
for other_node in BreadthFirstIterator(root):
|
||||
# Ignore root, ourselves and anything that is not a normal SceneNode.
|
||||
|
@ -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.
|
||||
class PreviewPass(RenderPass):
|
||||
def __init__(self, width: int, height: int):
|
||||
def __init__(self, width: int, height: int) -> None:
|
||||
super().__init__("preview", width, height, 0)
|
||||
|
||||
self._camera = None # type: Optional[Camera]
|
||||
@ -53,20 +53,23 @@ class PreviewPass(RenderPass):
|
||||
def render(self) -> None:
|
||||
if not self._shader:
|
||||
self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "overhang.shader"))
|
||||
self._shader.setUniformValue("u_overhangAngle", 1.0)
|
||||
self._shader.setUniformValue("u_ambientColor", [0.1, 0.1, 0.1, 1.0])
|
||||
self._shader.setUniformValue("u_specularColor", [0.6, 0.6, 0.6, 1.0])
|
||||
self._shader.setUniformValue("u_shininess", 20.0)
|
||||
if self._shader:
|
||||
self._shader.setUniformValue("u_overhangAngle", 1.0)
|
||||
self._shader.setUniformValue("u_ambientColor", [0.1, 0.1, 0.1, 1.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:
|
||||
self._non_printing_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "transparent_object.shader"))
|
||||
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 self._non_printing_shader:
|
||||
self._non_printing_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "transparent_object.shader"))
|
||||
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:
|
||||
self._support_mesh_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "striped.shader"))
|
||||
self._support_mesh_shader.setUniformValue("u_vertical_stripes", True)
|
||||
self._support_mesh_shader.setUniformValue("u_width", 5.0)
|
||||
if self._support_mesh_shader:
|
||||
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.glClear(self._gl.GL_COLOR_BUFFER_BIT | self._gl.GL_DEPTH_BUFFER_BIT)
|
||||
|
@ -1,13 +1,15 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from cura.PrinterOutput.PrinterOutputController import PrinterOutputController
|
||||
from PyQt5.QtCore import QTimer
|
||||
|
||||
MYPY = False
|
||||
if MYPY:
|
||||
if TYPE_CHECKING:
|
||||
from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel
|
||||
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
|
||||
from cura.PrinterOutput.ExtruderOutputModel import ExtruderOutputModel
|
||||
|
||||
|
||||
class GenericOutputController(PrinterOutputController):
|
||||
|
@ -32,12 +32,12 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
|
||||
def __init__(self, device_id, address: str, properties: Dict[bytes, bytes], parent: QObject = None) -> None:
|
||||
super().__init__(device_id = device_id, parent = parent)
|
||||
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._timeout_time = 10 # After how many seconds of no response should a timeout occur?
|
||||
|
||||
self._last_response_time = None # type: float
|
||||
self._last_request_time = None # type: float
|
||||
self._last_response_time = None # type: Optional[float]
|
||||
self._last_request_time = None # type: Optional[float]
|
||||
|
||||
self._api_prefix = ""
|
||||
self._address = address
|
||||
@ -146,12 +146,14 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
|
||||
if time_since_last_response > self._recreate_network_manager_time:
|
||||
if self._last_manager_create_time is None:
|
||||
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()
|
||||
assert(self._manager is not None)
|
||||
elif self._connection_state == ConnectionState.closed:
|
||||
# Go out of timeout.
|
||||
self.setConnectionState(self._connection_state_before_timeout)
|
||||
self._connection_state_before_timeout = None
|
||||
if self._connection_state_before_timeout is not None: # sanity check, but it should never be None here
|
||||
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:
|
||||
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:
|
||||
if self._manager is None:
|
||||
self._createNetworkManager()
|
||||
assert(self._manager is not None)
|
||||
request = self._createEmptyRequest(target)
|
||||
self._last_request_time = time()
|
||||
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:
|
||||
if self._manager is None:
|
||||
self._createNetworkManager()
|
||||
assert(self._manager is not None)
|
||||
request = self._createEmptyRequest(target)
|
||||
self._last_request_time = time()
|
||||
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:
|
||||
if self._manager is None:
|
||||
self._createNetworkManager()
|
||||
assert(self._manager is not None)
|
||||
request = self._createEmptyRequest(target)
|
||||
self._last_request_time = time()
|
||||
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:
|
||||
if self._manager is None:
|
||||
self._createNetworkManager()
|
||||
assert(self._manager is not None)
|
||||
request = self._createEmptyRequest(target, content_type=None)
|
||||
multi_post_part = QHttpMultiPart(QHttpMultiPart.FormDataType)
|
||||
for part in parts:
|
||||
|
@ -224,5 +224,5 @@ class PrinterOutputDevice(QObject, OutputDevice):
|
||||
## Get the name of device firmware
|
||||
#
|
||||
# This name can be used to define device type
|
||||
def getFirmwareName(self) -> str:
|
||||
def getFirmwareName(self) -> Optional[str]:
|
||||
return self._firmware_name
|
@ -16,7 +16,7 @@ from UM.Signal import Signal
|
||||
class CuraSceneController(QObject):
|
||||
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__()
|
||||
|
||||
self._objects_model = objects_model
|
||||
|
@ -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
|
||||
def getPrintingExtruder(self) -> Optional[ExtruderStack]:
|
||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
if global_container_stack is None:
|
||||
return None
|
||||
|
||||
per_mesh_stack = self.callDecoration("getStack")
|
||||
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
|
||||
def collidesWithBbox(self, check_bbox: AxisAlignedBox) -> bool:
|
||||
bbox = self.getBoundingBox()
|
||||
|
||||
# Mark the node as outside the build volume if the bounding box test fails.
|
||||
if check_bbox.intersectsBox(bbox) != AxisAlignedBox.IntersectionResult.FullIntersection:
|
||||
return True
|
||||
if bbox is not None:
|
||||
# Mark the node as outside the build volume if the bounding box test fails.
|
||||
if check_bbox.intersectsBox(bbox) != AxisAlignedBox.IntersectionResult.FullIntersection:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
@ -42,6 +42,7 @@ class ContainerManager(QObject):
|
||||
self._container_registry = self._application.getContainerRegistry()
|
||||
self._machine_manager = self._application.getMachineManager()
|
||||
self._material_manager = self._application.getMaterialManager()
|
||||
self._quality_manager = self._application.getQualityManager()
|
||||
self._container_name_filters = {}
|
||||
|
||||
@pyqtSlot(str, str, result=str)
|
||||
@ -312,11 +313,19 @@ class ContainerManager(QObject):
|
||||
|
||||
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())
|
||||
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.
|
||||
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()):
|
||||
Logger.log("e", "Could not update quality of a nonexistant or read only quality profile in stack %s", stack.getId())
|
||||
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]
|
||||
self._container_registry.exportQualityProfile(container_list, path, file_type)
|
||||
|
||||
__instance = None
|
||||
__instance = None # type: ContainerManager
|
||||
|
||||
@classmethod
|
||||
def getInstance(cls, *args, **kwargs) -> "ContainerManager":
|
||||
|
@ -356,6 +356,8 @@ class CuraContainerRegistry(ContainerRegistry):
|
||||
return catalog.i18nc("@info:status", "Profile is missing a quality type.")
|
||||
|
||||
global_stack = Application.getInstance().getGlobalContainerStack()
|
||||
if global_stack is None:
|
||||
return None
|
||||
definition_id = getMachineDefinitionIDForQualitySearch(global_stack.definition)
|
||||
profile.setDefinition(definition_id)
|
||||
|
||||
|
@ -16,7 +16,7 @@ from UM.Settings.SettingInstance import SettingInstance
|
||||
from UM.Settings.ContainerStack import ContainerStack
|
||||
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:
|
||||
from cura.Settings.ExtruderStack import ExtruderStack
|
||||
@ -43,7 +43,6 @@ class ExtruderManager(QObject):
|
||||
self._selected_object_extruders = []
|
||||
self._addCurrentMachineExtruders()
|
||||
|
||||
#Application.getInstance().globalContainerStackChanged.connect(self._globalContainerStackChanged)
|
||||
Selection.selectionChanged.connect(self.resetSelectedObjectExtruders)
|
||||
|
||||
## 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.
|
||||
@pyqtProperty(str, notify = activeExtruderChanged)
|
||||
def activeExtruderStackId(self) -> Optional[str]:
|
||||
if not Application.getInstance().getGlobalContainerStack():
|
||||
if not self._application.getGlobalContainerStack():
|
||||
return None # No active machine, so no active extruder.
|
||||
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.
|
||||
return None
|
||||
|
||||
## Return extruder count according to extruder trains.
|
||||
@pyqtProperty(int, notify = extrudersChanged)
|
||||
def extruderCount(self):
|
||||
if not Application.getInstance().getGlobalContainerStack():
|
||||
if not self._application.getGlobalContainerStack():
|
||||
return 0 # No active machine, so no extruders.
|
||||
try:
|
||||
return len(self._extruder_trains[Application.getInstance().getGlobalContainerStack().getId()])
|
||||
return len(self._extruder_trains[self._application.getGlobalContainerStack().getId()])
|
||||
except KeyError:
|
||||
return 0
|
||||
|
||||
## Gets a dict with the extruder stack ids with the extruder number as the key.
|
||||
@pyqtProperty("QVariantMap", notify = extrudersChanged)
|
||||
def extruderIds(self):
|
||||
def extruderIds(self) -> Dict[str, str]:
|
||||
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:
|
||||
for position in self._extruder_trains[global_stack_id]:
|
||||
extruder_stack_ids[position] = self._extruder_trains[global_stack_id][position].getId()
|
||||
if global_stack_id in self._extruder_trains:
|
||||
for position in self._extruder_trains[global_stack_id]:
|
||||
extruder_stack_ids[position] = self._extruder_trains[global_stack_id][position].getId()
|
||||
|
||||
return extruder_stack_ids
|
||||
|
||||
@pyqtSlot(str, result = str)
|
||||
def getQualityChangesIdByExtruderStackId(self, extruder_stack_id: str) -> str:
|
||||
for position in self._extruder_trains[Application.getInstance().getGlobalContainerStack().getId()]:
|
||||
extruder = self._extruder_trains[Application.getInstance().getGlobalContainerStack().getId()][position]
|
||||
if extruder.getId() == extruder_stack_id:
|
||||
return extruder.qualityChanges.getId()
|
||||
global_container_stack = self._application.getGlobalContainerStack()
|
||||
if global_container_stack is not None:
|
||||
for position in self._extruder_trains[global_container_stack.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.
|
||||
#
|
||||
@ -141,7 +145,7 @@ class ExtruderManager(QObject):
|
||||
selected_nodes.append(node)
|
||||
|
||||
# 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())
|
||||
for node in selected_nodes:
|
||||
extruder = node.callDecoration("getActiveExtruder")
|
||||
@ -164,7 +168,7 @@ class ExtruderManager(QObject):
|
||||
|
||||
@pyqtSlot(result = QObject)
|
||||
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.getId() in self._extruder_trains:
|
||||
@ -175,7 +179,7 @@ class ExtruderManager(QObject):
|
||||
|
||||
## Get an extruder stack by index
|
||||
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.getId() in self._extruder_trains:
|
||||
if str(index) in self._extruder_trains[global_container_stack.getId()]:
|
||||
@ -186,7 +190,9 @@ class ExtruderManager(QObject):
|
||||
def getExtruderStacks(self) -> List["ExtruderStack"]:
|
||||
result = []
|
||||
for i in range(self.extruderCount):
|
||||
result.append(self.getExtruderStack(i))
|
||||
stack = self.getExtruderStack(i)
|
||||
if stack:
|
||||
result.append(stack)
|
||||
return result
|
||||
|
||||
def registerExtruder(self, extruder_train, machine_id):
|
||||
@ -252,7 +258,7 @@ class ExtruderManager(QObject):
|
||||
support_bottom_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 len(self.extruderIds) == 0:
|
||||
@ -301,10 +307,10 @@ class ExtruderManager(QObject):
|
||||
|
||||
# The platform adhesion extruder. Not used if using none.
|
||||
if global_stack.getProperty("adhesion_type", "value") != "none":
|
||||
extruder_nr = str(global_stack.getProperty("adhesion_extruder_nr", "value"))
|
||||
if extruder_nr == "-1":
|
||||
extruder_nr = Application.getInstance().getMachineManager().defaultExtruderPosition
|
||||
used_extruder_stack_ids.add(self.extruderIds[extruder_nr])
|
||||
extruder_str_nr = str(global_stack.getProperty("adhesion_extruder_nr", "value"))
|
||||
if extruder_str_nr == "-1":
|
||||
extruder_str_nr = self._application.getMachineManager().defaultExtruderPosition
|
||||
used_extruder_stack_ids.add(self.extruderIds[extruder_str_nr])
|
||||
|
||||
try:
|
||||
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.
|
||||
# \return \type{List[ContainerStack]}
|
||||
def getActiveGlobalAndExtruderStacks(self) -> Optional[List[Union["ExtruderStack", "GlobalStack"]]]:
|
||||
global_stack = Application.getInstance().getGlobalContainerStack()
|
||||
global_stack = self._application.getGlobalContainerStack()
|
||||
if not global_stack:
|
||||
return None
|
||||
|
||||
@ -347,7 +353,7 @@ class ExtruderManager(QObject):
|
||||
#
|
||||
# \return \type{List[ContainerStack]} a list of
|
||||
def getActiveExtruderStacks(self) -> List["ExtruderStack"]:
|
||||
global_stack = Application.getInstance().getGlobalContainerStack()
|
||||
global_stack = self._application.getGlobalContainerStack()
|
||||
if not global_stack:
|
||||
return []
|
||||
|
||||
@ -461,10 +467,6 @@ class ExtruderManager(QObject):
|
||||
if global_stack.definitionChanges.hasProperty(key, "value"):
|
||||
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.
|
||||
#
|
||||
# This is exposed to SettingFunction so it can be used in value functions.
|
||||
@ -556,96 +558,6 @@ class ExtruderManager(QObject):
|
||||
def getInstanceExtruderValues(self, 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.
|
||||
#
|
||||
# This is exposed to SettingFunction to use in value functions.
|
||||
@ -736,7 +648,7 @@ class ExtruderManager(QObject):
|
||||
|
||||
return resolved_value
|
||||
|
||||
__instance = None
|
||||
__instance = None # type: ExtruderManager
|
||||
|
||||
@classmethod
|
||||
def getInstance(cls, *args, **kwargs) -> "ExtruderManager":
|
||||
|
@ -53,8 +53,8 @@ class MachineManager(QObject):
|
||||
self._global_container_stack = None # type: Optional[GlobalStack]
|
||||
|
||||
self._current_root_material_id = {} # type: Dict[str, str]
|
||||
self._current_quality_group = None
|
||||
self._current_quality_changes_group = None
|
||||
self._current_quality_group = None # type: Optional[QualityGroup]
|
||||
self._current_quality_changes_group = None # type: Optional[QualityChangesGroup]
|
||||
|
||||
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():
|
||||
material_dict[position] = extruder.material.getMetaDataEntry("base_file")
|
||||
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
|
||||
quality_type = global_quality.getMetaDataEntry("quality_type")
|
||||
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.
|
||||
@pyqtSlot(str)
|
||||
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")
|
||||
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.
|
||||
@pyqtSlot()
|
||||
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())
|
||||
for extruder_stack in extruder_stacks:
|
||||
if extruder_stack != self._active_container_stack:
|
||||
@ -807,6 +816,8 @@ class MachineManager(QObject):
|
||||
return None
|
||||
|
||||
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")
|
||||
result = [] # type: List[str]
|
||||
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
|
||||
def correctExtruderSettings(self) -> None:
|
||||
if self._global_container_stack is None:
|
||||
return
|
||||
for setting_key in self.getIncompatibleSettingsOnEnabledExtruders(self._global_container_stack.userChanges):
|
||||
self._global_container_stack.userChanges.removeInstance(setting_key)
|
||||
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)
|
||||
# \param extruder_count int the number of extruders to set
|
||||
def setActiveMachineExtruderCount(self, extruder_count: int) -> None:
|
||||
if self._global_container_stack is None:
|
||||
return
|
||||
extruder_manager = self._application.getExtruderManager()
|
||||
|
||||
definition_changes_container = self._global_container_stack.definitionChanges
|
||||
@ -909,6 +924,8 @@ class MachineManager(QObject):
|
||||
return extruder
|
||||
|
||||
def updateDefaultExtruder(self) -> None:
|
||||
if self._global_container_stack is None:
|
||||
return
|
||||
extruder_items = sorted(self._global_container_stack.extruders.items())
|
||||
old_position = self._default_extruder_position
|
||||
new_default_position = "0"
|
||||
@ -921,6 +938,8 @@ class MachineManager(QObject):
|
||||
self.extruderChanged.emit()
|
||||
|
||||
def updateNumberExtrudersEnabled(self) -> None:
|
||||
if self._global_container_stack is None:
|
||||
return
|
||||
definition_changes_container = self._global_container_stack.definitionChanges
|
||||
machine_extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value")
|
||||
extruder_count = 0
|
||||
@ -933,6 +952,8 @@ class MachineManager(QObject):
|
||||
|
||||
@pyqtProperty(int, notify = numberExtrudersEnabledChanged)
|
||||
def numberExtrudersEnabled(self) -> int:
|
||||
if self._global_container_stack is None:
|
||||
return 1
|
||||
return self._global_container_stack.definitionChanges.getProperty("extruders_enabled_count", "value")
|
||||
|
||||
@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
|
||||
@pyqtSlot()
|
||||
def forceUpdateAllSettings(self) -> None:
|
||||
if self._global_container_stack is None:
|
||||
return
|
||||
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
||||
property_names = ["value", "resolve", "validationState"]
|
||||
for container in [self._global_container_stack] + list(self._global_container_stack.extruders.values()):
|
||||
@ -951,8 +974,9 @@ class MachineManager(QObject):
|
||||
@pyqtSlot(int, bool)
|
||||
def setExtruderEnabled(self, position: int, enabled: bool) -> None:
|
||||
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)
|
||||
return
|
||||
|
||||
extruder.setEnabled(enabled)
|
||||
self.updateDefaultExtruder()
|
||||
@ -988,6 +1012,8 @@ class MachineManager(QObject):
|
||||
|
||||
@pyqtSlot(str, str, str)
|
||||
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():
|
||||
container = extruder.userChanges
|
||||
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.
|
||||
@pyqtSlot(str)
|
||||
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():
|
||||
container = extruder.userChanges
|
||||
container.removeInstance(setting_name)
|
||||
@ -1038,6 +1066,8 @@ class MachineManager(QObject):
|
||||
# for all stacks in the currently active machine.
|
||||
#
|
||||
def _setEmptyQuality(self) -> None:
|
||||
if self._global_container_stack is None:
|
||||
return
|
||||
self._current_quality_group = None
|
||||
self._current_quality_changes_group = None
|
||||
self._global_container_stack.quality = self._empty_quality_container
|
||||
@ -1050,11 +1080,13 @@ class MachineManager(QObject):
|
||||
self.activeQualityChangesGroupChanged.emit()
|
||||
|
||||
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:
|
||||
self._setEmptyQuality()
|
||||
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
|
||||
for node in quality_group.nodes_for_extruders.values():
|
||||
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())
|
||||
containers = [n.getContainer() for n in nodes if n is not None]
|
||||
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"
|
||||
|
||||
def _setQualityChangesGroup(self, quality_changes_group: QualityChangesGroup) -> None:
|
||||
@ -1130,20 +1163,24 @@ class MachineManager(QObject):
|
||||
self.activeQualityChangesGroupChanged.emit()
|
||||
|
||||
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
|
||||
self._global_container_stack.extruders[position].variant = container_node.getContainer()
|
||||
self.activeVariantChanged.emit()
|
||||
|
||||
def _setGlobalVariant(self, container_node: ContainerNode) -> None:
|
||||
if self._global_container_stack is None:
|
||||
return
|
||||
self._global_container_stack.variant = container_node.getContainer()
|
||||
if not self._global_container_stack.variant:
|
||||
self._global_container_stack.variant = self._application.empty_variant_container
|
||||
|
||||
def _setMaterial(self, position: str, container_node: ContainerNode = None) -> None:
|
||||
if self._global_container_stack is None:
|
||||
return
|
||||
if container_node and 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:
|
||||
self._global_container_stack.extruders[position].material = self._empty_material_container
|
||||
root_material_id = None
|
||||
@ -1154,12 +1191,13 @@ class MachineManager(QObject):
|
||||
|
||||
def activeMaterialsCompatible(self) -> bool:
|
||||
# check material - variant compatibility
|
||||
if Util.parseBool(self._global_container_stack.getMetaDataEntry("has_materials", False)):
|
||||
for position, extruder in self._global_container_stack.extruders.items():
|
||||
if extruder.isEnabled and not extruder.material.getMetaDataEntry("compatible"):
|
||||
return False
|
||||
if not extruder.material.getMetaDataEntry("compatible"):
|
||||
return False
|
||||
if self._global_container_stack is not None:
|
||||
if Util.parseBool(self._global_container_stack.getMetaDataEntry("has_materials", False)):
|
||||
for position, extruder in self._global_container_stack.extruders.items():
|
||||
if extruder.isEnabled and not extruder.material.getMetaDataEntry("compatible"):
|
||||
return False
|
||||
if not extruder.material.getMetaDataEntry("compatible"):
|
||||
return False
|
||||
return True
|
||||
|
||||
## Update current quality type and machine after setting material
|
||||
@ -1202,7 +1240,7 @@ class MachineManager(QObject):
|
||||
current_quality_type, quality_type)
|
||||
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:
|
||||
return
|
||||
if position is None:
|
||||
@ -1210,8 +1248,8 @@ class MachineManager(QObject):
|
||||
else:
|
||||
position_list = [position]
|
||||
|
||||
for position in position_list:
|
||||
extruder = self._global_container_stack.extruders[position]
|
||||
for position_item in position_list:
|
||||
extruder = self._global_container_stack.extruders[position_item]
|
||||
|
||||
current_material_base_name = extruder.material.getMetaDataEntry("base_file")
|
||||
current_variant_name = None
|
||||
@ -1229,25 +1267,25 @@ class MachineManager(QObject):
|
||||
material_diameter)
|
||||
|
||||
if not candidate_materials:
|
||||
self._setMaterial(position, container_node = None)
|
||||
self._setMaterial(position_item, container_node = None)
|
||||
continue
|
||||
|
||||
if current_material_base_name in candidate_materials:
|
||||
new_material = candidate_materials[current_material_base_name]
|
||||
self._setMaterial(position, new_material)
|
||||
self._setMaterial(position_item, new_material)
|
||||
continue
|
||||
|
||||
# The current material is not available, find the preferred one
|
||||
material_node = self._material_manager.getDefaultMaterial(self._global_container_stack, current_variant_name)
|
||||
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
|
||||
# instance with the same network key.
|
||||
@pyqtSlot(str)
|
||||
def switchPrinterType(self, machine_name: str) -> None:
|
||||
# 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
|
||||
# Get the definition id corresponding to this machine name
|
||||
machine_definition_id = CuraContainerRegistry.getInstance().findDefinitionContainers(name = machine_name)[0].getId()
|
||||
@ -1272,6 +1310,8 @@ class MachineManager(QObject):
|
||||
|
||||
@pyqtSlot(QObject)
|
||||
def applyRemoteConfiguration(self, configuration: ConfigurationModel) -> None:
|
||||
if self._global_container_stack is None:
|
||||
return
|
||||
self.blurSettings.emit()
|
||||
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
||||
self.switchPrinterType(configuration.printerType)
|
||||
@ -1288,7 +1328,7 @@ class MachineManager(QObject):
|
||||
self._setMaterial(position, material_container_node)
|
||||
else:
|
||||
self._global_container_stack.extruders[position].material = self._empty_material_container
|
||||
self._updateMaterialWithVariant(position)
|
||||
self.updateMaterialWithVariant(position)
|
||||
|
||||
if configuration.buildplateConfiguration is not None:
|
||||
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()
|
||||
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
||||
self._setGlobalVariant(container_node)
|
||||
self._updateMaterialWithVariant(None) # Update all materials
|
||||
self.updateMaterialWithVariant(None) # Update all materials
|
||||
self._updateQualityWithMaterial()
|
||||
|
||||
@pyqtSlot(str, str)
|
||||
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
|
||||
position = str(position)
|
||||
extruder_stack = self._global_container_stack.extruders[position]
|
||||
@ -1361,6 +1403,8 @@ class MachineManager(QObject):
|
||||
|
||||
@pyqtSlot(str, str)
|
||||
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
|
||||
variant_node = self._variant_manager.getVariantNode(machine_definition_id, variant_name)
|
||||
self.setVariant(position, variant_node)
|
||||
@ -1371,7 +1415,7 @@ class MachineManager(QObject):
|
||||
self.blurSettings.emit()
|
||||
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
||||
self._setVariantNode(position, container_node)
|
||||
self._updateMaterialWithVariant(position)
|
||||
self.updateMaterialWithVariant(position)
|
||||
self._updateQualityWithMaterial()
|
||||
|
||||
# See if we need to show the Discard or Keep changes screen
|
||||
@ -1398,7 +1442,7 @@ class MachineManager(QObject):
|
||||
self._application.discardOrKeepProfileChanges()
|
||||
|
||||
@pyqtProperty(QObject, fset = setQualityGroup, notify = activeQualityGroupChanged)
|
||||
def activeQualityGroup(self) -> QualityGroup:
|
||||
def activeQualityGroup(self) -> Optional[QualityGroup]:
|
||||
return self._current_quality_group
|
||||
|
||||
@pyqtSlot(QObject)
|
||||
@ -1413,13 +1457,15 @@ class MachineManager(QObject):
|
||||
|
||||
@pyqtSlot()
|
||||
def resetToUseDefaultQuality(self) -> None:
|
||||
if self._global_container_stack is None:
|
||||
return
|
||||
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
||||
self._setQualityGroup(self._current_quality_group)
|
||||
for stack in [self._global_container_stack] + list(self._global_container_stack.extruders.values()):
|
||||
stack.userChanges.clear()
|
||||
|
||||
@pyqtProperty(QObject, fset = setQualityChangesGroup, notify = activeQualityChangesGroupChanged)
|
||||
def activeQualityChangesGroup(self) -> QualityChangesGroup:
|
||||
def activeQualityChangesGroup(self) -> Optional[QualityChangesGroup]:
|
||||
return self._current_quality_changes_group
|
||||
|
||||
@pyqtProperty(str, notify = activeQualityGroupChanged)
|
||||
@ -1435,5 +1481,5 @@ class MachineManager(QObject):
|
||||
if self._global_container_stack is None:
|
||||
return
|
||||
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
||||
self._updateMaterialWithVariant(None)
|
||||
self.updateMaterialWithVariant(None)
|
||||
self._updateQualityWithMaterial()
|
||||
|
@ -1,5 +1,6 @@
|
||||
# Copyright (c) 2017 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
from typing import List
|
||||
|
||||
from PyQt5.QtCore import QObject, QTimer, pyqtProperty, pyqtSignal
|
||||
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
|
||||
# actually do anything, as only the 'leaf' settings are used by the engine.
|
||||
from UM.Settings.ContainerStack import ContainerStack
|
||||
from UM.Settings.Interfaces import ContainerInterface
|
||||
from UM.Settings.SettingFunction import SettingFunction
|
||||
from UM.Settings.SettingInstance import InstanceState
|
||||
|
||||
@ -157,7 +159,7 @@ class SettingInheritanceManager(QObject):
|
||||
stack = self._active_container_stack
|
||||
if not stack: #No active container stack yet!
|
||||
return False
|
||||
containers = []
|
||||
containers = [] # type: List[ContainerInterface]
|
||||
|
||||
## Check if the setting has a user state. If not, it is never overwritten.
|
||||
has_user_state = stack.getProperty(key, "state") == InstanceState.User
|
||||
|
@ -63,7 +63,7 @@ class SettingOverrideDecorator(SceneNodeDecorator):
|
||||
instance_container = copy.deepcopy(self._stack.getContainer(0), memo)
|
||||
|
||||
# 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.
|
||||
deep_copy._stack.replaceContainer(0, instance_container)
|
||||
|
@ -61,8 +61,11 @@ class SingleInstance:
|
||||
|
||||
def startServer(self) -> None:
|
||||
self._single_instance_server = QLocalServer()
|
||||
self._single_instance_server.newConnection.connect(self._onClientConnected)
|
||||
self._single_instance_server.listen("ultimaker-cura")
|
||||
if self._single_instance_server:
|
||||
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:
|
||||
Logger.log("i", "New connection recevied on our single-instance server")
|
||||
|
@ -4,7 +4,7 @@
|
||||
from configparser import ConfigParser
|
||||
import zipfile
|
||||
import os
|
||||
from typing import List, Tuple
|
||||
from typing import Dict, List, Tuple
|
||||
|
||||
|
||||
import xml.etree.ElementTree as ET
|
||||
@ -13,6 +13,7 @@ from UM.Workspace.WorkspaceReader import WorkspaceReader
|
||||
from UM.Application import Application
|
||||
|
||||
from UM.Logger import Logger
|
||||
from UM.Message import Message
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.Signal import postponeSignals, CompressTechnique
|
||||
from UM.Settings.ContainerFormatError import ContainerFormatError
|
||||
@ -37,7 +38,7 @@ i18n_catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
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.serialized = serialized
|
||||
self.parser = parser
|
||||
@ -46,14 +47,14 @@ class ContainerInfo:
|
||||
|
||||
|
||||
class QualityChangesInfo:
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
self.name = None
|
||||
self.global_info = None
|
||||
self.extruder_info_dict = {}
|
||||
self.extruder_info_dict = {} # type: Dict[str, ContainerInfo]
|
||||
|
||||
|
||||
class MachineInfo:
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
self.container_id = None
|
||||
self.name = None
|
||||
self.definition_id = None
|
||||
@ -65,11 +66,11 @@ class MachineInfo:
|
||||
self.definition_changes_info = None
|
||||
self.user_changes_info = None
|
||||
|
||||
self.extruder_info_dict = {}
|
||||
self.extruder_info_dict = {} # type: Dict[str, ExtruderInfo]
|
||||
|
||||
|
||||
class ExtruderInfo:
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
self.position = None
|
||||
self.enabled = True
|
||||
self.variant_info = None
|
||||
@ -81,7 +82,7 @@ class ExtruderInfo:
|
||||
|
||||
## Base implementation for reading 3MF workspace files.
|
||||
class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
MimeTypeDatabase.addMimeType(
|
||||
@ -111,28 +112,26 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||
# - 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
|
||||
self._old_empty_profile_id_dict = {"empty_%s" % k: "empty" for k in ["material", "variant"]}
|
||||
|
||||
self._is_same_machine_type = False
|
||||
self._old_new_materials = {}
|
||||
self._materials_to_select = {}
|
||||
self._old_new_materials = {} # type: Dict[str, str]
|
||||
self._machine_info = None
|
||||
|
||||
def _clearState(self):
|
||||
self._is_same_machine_type = False
|
||||
self._id_mapping = {}
|
||||
self._old_new_materials = {}
|
||||
self._materials_to_select = {}
|
||||
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.
|
||||
# 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:
|
||||
self._id_mapping[old_id] = self._container_registry.uniqueName(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)
|
||||
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.
|
||||
if not show_dialog:
|
||||
return WorkspaceReader.PreReadResult.accepted
|
||||
@ -656,7 +669,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||
else:
|
||||
material_container = materials[0]
|
||||
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
|
||||
|
||||
if self._resolve_strategies["material"] == "override":
|
||||
|
@ -55,7 +55,7 @@ class ChangeLog(Extension, QObject,):
|
||||
|
||||
def loadChangeLogs(self):
|
||||
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_header = "" # Initialise to an empty header in case there is no "*" in the first line of the changelog
|
||||
for line in f:
|
||||
|
@ -379,6 +379,14 @@ class CuraEngineBackend(QObject, Backend):
|
||||
else:
|
||||
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 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."),
|
||||
|
@ -32,6 +32,7 @@ class StartJobResult(IntEnum):
|
||||
MaterialIncompatible = 5
|
||||
BuildPlateError = 6
|
||||
ObjectSettingError = 7 #When an error occurs in per-object settings.
|
||||
ObjectsWithDisabledExtruder = 8
|
||||
|
||||
|
||||
## 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()}
|
||||
filtered_object_groups = []
|
||||
has_model_with_disabled_extruders = False
|
||||
associated_siabled_extruders = set()
|
||||
for group in object_groups:
|
||||
stack = Application.getInstance().getGlobalContainerStack()
|
||||
skip_group = False
|
||||
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
|
||||
has_model_with_disabled_extruders = True
|
||||
associated_siabled_extruders.add(extruder_position)
|
||||
break
|
||||
if not skip_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
|
||||
# able to find a possible sequence or because there are no objects on the build plate (or they are outside
|
||||
# the build volume)
|
||||
|
@ -57,7 +57,7 @@ class GCodeProfileReader(ProfileReader):
|
||||
# TODO: Consider moving settings to the start?
|
||||
serialized = "" # Will be filled with the serialized profile.
|
||||
try:
|
||||
with open(file_name, "r") as f:
|
||||
with open(file_name, "r", encoding = "utf-8") as f:
|
||||
for line in f:
|
||||
if line.startswith(prefix):
|
||||
# Remove the prefix and the newline from the line and add it to the rest.
|
||||
|
@ -100,7 +100,7 @@ class LegacyProfileReader(ProfileReader):
|
||||
return None
|
||||
|
||||
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.
|
||||
except IOError as e:
|
||||
Logger.log("e", "Could not open DictionaryOfDoom.json for reading: %s", str(e))
|
||||
|
@ -158,4 +158,4 @@ class MachineSettingsAction(MachineAction):
|
||||
@pyqtSlot(int)
|
||||
def updateMaterialForDiameter(self, extruder_position: int):
|
||||
# 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))
|
||||
|
@ -53,7 +53,7 @@ UM.PointingRectangle {
|
||||
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
|
||||
horizontalAlignment: TextInput.AlignRight
|
||||
|
||||
@ -77,11 +77,12 @@ UM.PointingRectangle {
|
||||
if (valueLabel.text != "") {
|
||||
// -startFrom because we need to convert back to an array structure
|
||||
sliderLabelRoot.setValue(parseInt(valueLabel.text) - startFrom)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
validator: IntValidator {
|
||||
bottom:startFrom
|
||||
bottom: startFrom
|
||||
top: sliderLabelRoot.maximumValue + startFrom // +startFrom because maybe we want to start in a different value rather than 0
|
||||
}
|
||||
}
|
||||
|
@ -262,12 +262,14 @@ class Toolbox(QObject, Extension):
|
||||
# list of old plugins
|
||||
old_plugin_ids = self._plugin_registry.getInstalledPlugins()
|
||||
installed_package_ids = self._package_manager.getAllInstalledPackageIDs()
|
||||
scheduled_to_remove_package_ids = self._package_manager.getToRemovePackageIDs()
|
||||
|
||||
self._old_plugin_ids = []
|
||||
self._old_plugin_metadata = []
|
||||
|
||||
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)
|
||||
|
||||
old_metadata = self._plugin_registry.getMetaData(plugin_id)
|
||||
|
@ -13,7 +13,7 @@ def readHex(filename):
|
||||
"""
|
||||
data = []
|
||||
extra_addr = 0
|
||||
f = io.open(filename, "r")
|
||||
f = io.open(filename, "r", encoding = "utf-8")
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if len(line) < 1:
|
||||
|
@ -94,7 +94,7 @@ class VersionUpgrade22to24(VersionUpgrade):
|
||||
if variant_path.endswith("_variant.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)
|
||||
|
||||
return config_name
|
||||
@ -105,9 +105,9 @@ class VersionUpgrade22to24(VersionUpgrade):
|
||||
|
||||
result = []
|
||||
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)
|
||||
with open(entry.path, "r") as fhandle:
|
||||
with open(entry.path, "r", encoding = "utf-8") as fhandle:
|
||||
config.read_file(fhandle)
|
||||
if config.has_section("general") and config.has_option("general", "name"):
|
||||
result.append( { "path": entry.path, "name": config.get("general", "name") } )
|
||||
|
@ -249,11 +249,11 @@ class VersionUpgrade25to26(VersionUpgrade):
|
||||
definition_changes_dir = Resources.getPath(CuraApplication.ResourceTypes.DefinitionChangesContainer)
|
||||
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())
|
||||
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())
|
||||
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())
|
||||
|
||||
## Creates a definition changes container which doesn't contain anything for the Custom FDM Printers.
|
||||
|
@ -1018,7 +1018,7 @@ class XmlMaterialProfile(InstanceContainer):
|
||||
@classmethod
|
||||
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")
|
||||
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 = {key: [value] for key, value in product_to_id_map.items()}
|
||||
return product_to_id_map
|
||||
|
@ -170,7 +170,7 @@ UM.PreferencesPage
|
||||
append({ text: "日本語", code: "ja_JP" })
|
||||
append({ text: "한국어", code: "ko_KR" })
|
||||
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", code: "pt_PT" })
|
||||
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
|
||||
{
|
||||
target: UM.Preferences
|
||||
|
@ -17,6 +17,6 @@ top_bottom_thickness = 0.8
|
||||
infill_sparse_density = 20
|
||||
speed_print = 80
|
||||
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_speed = 10
|
||||
|
@ -17,6 +17,6 @@ top_bottom_thickness = 0.8
|
||||
infill_sparse_density = 20
|
||||
speed_print = 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_speed = 10
|
||||
|
@ -17,6 +17,6 @@ top_bottom_thickness = 0.8
|
||||
infill_sparse_density = 20
|
||||
speed_print = 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_speed = 10
|
||||
|
@ -17,6 +17,6 @@ top_bottom_thickness = 0.8
|
||||
infill_sparse_density = 20
|
||||
speed_print = 80
|
||||
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_speed = 10
|
||||
|
@ -17,6 +17,6 @@ top_bottom_thickness = 0.8
|
||||
infill_sparse_density = 20
|
||||
speed_print = 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_speed = 10
|
||||
|
@ -17,6 +17,6 @@ top_bottom_thickness = 0.8
|
||||
infill_sparse_density = 20
|
||||
speed_print = 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_speed = 10
|
||||
|
@ -17,6 +17,6 @@ top_bottom_thickness = 0.8
|
||||
infill_sparse_density = 20
|
||||
speed_print = 80
|
||||
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_speed = 10
|
||||
|
@ -17,6 +17,6 @@ top_bottom_thickness = 0.8
|
||||
infill_sparse_density = 20
|
||||
speed_print = 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_speed = 10
|
||||
|
@ -17,6 +17,6 @@ top_bottom_thickness = 0.8
|
||||
infill_sparse_density = 20
|
||||
speed_print = 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_speed = 10
|
||||
|
@ -41,13 +41,13 @@ retraction_speed = 40
|
||||
skirt_brim_speed = 40
|
||||
skirt_gap = 5
|
||||
skirt_line_count = 3
|
||||
speed_infill = 60
|
||||
speed_infill = =speed_print
|
||||
speed_print = 60
|
||||
speed_support = 60
|
||||
speed_topbottom = 30
|
||||
speed_topbottom = =math.ceil(speed_print * 30 / 60)
|
||||
speed_travel = 100
|
||||
speed_wall = 60
|
||||
speed_wall_x = 60
|
||||
speed_wall = =speed_print
|
||||
speed_wall_x = =speed_print
|
||||
support_angle = 60
|
||||
support_enable = True
|
||||
support_interface_enable = True
|
||||
|
@ -41,13 +41,13 @@ retraction_speed = 40
|
||||
skirt_brim_speed = 40
|
||||
skirt_gap = 5
|
||||
skirt_line_count = 3
|
||||
speed_infill = 50
|
||||
speed_infill = =speed_print
|
||||
speed_print = 50
|
||||
speed_support = 30
|
||||
speed_topbottom = 20
|
||||
speed_topbottom = =math.ceil(speed_print * 20 / 50)
|
||||
speed_travel = 50
|
||||
speed_wall = 50
|
||||
speed_wall_x = 50
|
||||
speed_wall = =speed_print
|
||||
speed_wall_x = =speed_print
|
||||
support_angle = 60
|
||||
support_enable = True
|
||||
support_interface_enable = True
|
||||
|
@ -41,13 +41,13 @@ retraction_speed = 40
|
||||
skirt_brim_speed = 40
|
||||
skirt_gap = 5
|
||||
skirt_line_count = 3
|
||||
speed_infill = 50
|
||||
speed_infill = =speed_print
|
||||
speed_print = 50
|
||||
speed_support = 30
|
||||
speed_topbottom = 20
|
||||
speed_topbottom = =math.ceil(speed_print * 20 / 50)
|
||||
speed_travel = 100
|
||||
speed_wall = 50
|
||||
speed_wall_x = 50
|
||||
speed_wall = =speed_print
|
||||
speed_wall_x = =speed_print
|
||||
support_angle = 60
|
||||
support_enable = True
|
||||
support_interface_enable = True
|
||||
|
@ -1,6 +1,6 @@
|
||||
[general]
|
||||
version = 4
|
||||
name = Coarse Quality
|
||||
name = Coarse
|
||||
definition = fdmprinter
|
||||
|
||||
[metadata]
|
||||
|
@ -43,11 +43,11 @@ skirt_brim_minimal_length = 75
|
||||
skirt_gap = 1.5
|
||||
skirt_line_count = 5
|
||||
speed_infill = =speed_print
|
||||
speed_layer_0 = 25
|
||||
speed_layer_0 = =math.ceil(speed_print * 25 / 50)
|
||||
speed_print = 50
|
||||
speed_topbottom = 40
|
||||
speed_topbottom = =math.ceil(speed_print * 40 / 50)
|
||||
speed_travel = 200
|
||||
speed_wall_0 = 40
|
||||
speed_wall_0 = =math.ceil(speed_print * 40 / 50)
|
||||
speed_wall_x = =speed_print
|
||||
support_angle = 70
|
||||
support_type = buildplate
|
||||
|
@ -43,11 +43,11 @@ skirt_brim_minimal_length = 75
|
||||
skirt_gap = 1.5
|
||||
skirt_line_count = 5
|
||||
speed_infill = =speed_print
|
||||
speed_layer_0 = 25
|
||||
speed_layer_0 = =math.ceil(speed_print * 25 / 50)
|
||||
speed_print = 50
|
||||
speed_topbottom = 40
|
||||
speed_topbottom = =math.ceil(speed_print * 40 / 50)
|
||||
speed_travel = 200
|
||||
speed_wall_0 = 40
|
||||
speed_wall_0 = =math.ceil(speed_print * 40 / 50)
|
||||
speed_wall_x = =speed_print
|
||||
support_angle = 70
|
||||
support_type = buildplate
|
||||
|
@ -43,11 +43,11 @@ skirt_brim_minimal_length = 75
|
||||
skirt_gap = 1.5
|
||||
skirt_line_count = 5
|
||||
speed_infill = =speed_print
|
||||
speed_layer_0 = 25
|
||||
speed_layer_0 = =math.ceil(speed_print * 25 / 50)
|
||||
speed_print = 50
|
||||
speed_topbottom = 40
|
||||
speed_topbottom = =math.ceil(speed_print * 40 / 50)
|
||||
speed_travel = 200
|
||||
speed_wall_0 = 40
|
||||
speed_wall_0 = =math.ceil(speed_print * 40 / 50)
|
||||
speed_wall_x = =speed_print
|
||||
support_angle = 70
|
||||
support_type = buildplate
|
||||
|
@ -1,6 +1,6 @@
|
||||
[general]
|
||||
version = 4
|
||||
name = Draft Quality
|
||||
name = Draft
|
||||
definition = fdmprinter
|
||||
|
||||
[metadata]
|
||||
|
@ -1,6 +1,6 @@
|
||||
[general]
|
||||
version = 4
|
||||
name = Extra Coarse Quality
|
||||
name = Extra Coarse
|
||||
definition = fdmprinter
|
||||
|
||||
[metadata]
|
||||
|
@ -43,11 +43,11 @@ skirt_brim_minimal_length = 75
|
||||
skirt_gap = 1.5
|
||||
skirt_line_count = 5
|
||||
speed_infill = =speed_print
|
||||
speed_layer_0 = 25
|
||||
speed_layer_0 = =math.ceil(speed_print * 25 / 30)
|
||||
speed_print = 30
|
||||
speed_topbottom = 40
|
||||
speed_topbottom = =math.ceil(speed_print * 40 / 30)
|
||||
speed_travel = 200
|
||||
speed_wall_0 = 40
|
||||
speed_wall_0 = =math.ceil(speed_print * 40 / 30)
|
||||
speed_wall_x = =speed_print
|
||||
support_angle = 70
|
||||
support_type = buildplate
|
||||
|
@ -43,11 +43,11 @@ skirt_brim_minimal_length = 75
|
||||
skirt_gap = 1.5
|
||||
skirt_line_count = 5
|
||||
speed_infill = =speed_print
|
||||
speed_layer_0 = 25
|
||||
speed_layer_0 = =math.ceil(speed_print * 25 / 30)
|
||||
speed_print = 30
|
||||
speed_topbottom = 40
|
||||
speed_topbottom = =math.ceil(speed_print * 40 / 30)
|
||||
speed_travel = 200
|
||||
speed_wall_0 = 40
|
||||
speed_wall_0 = =math.ceil(speed_print * 40 / 30)
|
||||
speed_wall_x = =speed_print
|
||||
support_angle = 70
|
||||
support_type = buildplate
|
||||
|
@ -43,11 +43,11 @@ skirt_brim_minimal_length = 75
|
||||
skirt_gap = 1.5
|
||||
skirt_line_count = 5
|
||||
speed_infill = =speed_print
|
||||
speed_layer_0 = 25
|
||||
speed_layer_0 = =math.ceil(speed_print * 25 / 30)
|
||||
speed_print = 30
|
||||
speed_topbottom = 40
|
||||
speed_topbottom = =math.ceil(speed_print * 40 / 30)
|
||||
speed_travel = 200
|
||||
speed_wall_0 = 40
|
||||
speed_wall_0 = =math.ceil(speed_print * 40 / 30)
|
||||
speed_wall_x = =speed_print
|
||||
support_angle = 70
|
||||
support_type = buildplate
|
||||
|
@ -1,6 +1,6 @@
|
||||
[general]
|
||||
version = 4
|
||||
name = Low Quality
|
||||
name = Normal
|
||||
definition = fdmprinter
|
||||
|
||||
[metadata]
|
||||
@ -14,8 +14,8 @@ global_quality = True
|
||||
infill_sparse_density = 10
|
||||
layer_height = 0.15
|
||||
cool_min_layer_time = 3
|
||||
speed_wall_0 = 40
|
||||
speed_wall_x = 80
|
||||
speed_infill = 100
|
||||
speed_wall_0 = =math.ceil(speed_print * 40 / 60)
|
||||
speed_wall_x = =math.ceil(speed_print * 80 / 60)
|
||||
speed_infill = =math.ceil(speed_print * 100 / 60)
|
||||
wall_thickness = 1
|
||||
speed_topbottom = 30
|
||||
speed_topbottom = =math.ceil(speed_print * 30 / 60)
|
||||
|
@ -12,5 +12,5 @@ global_quality = True
|
||||
|
||||
[values]
|
||||
layer_height = 0.06
|
||||
speed_topbottom = 15
|
||||
speed_infill = 80
|
||||
speed_topbottom = =math.ceil(speed_print * 15 / 60)
|
||||
speed_infill = =math.ceil(speed_print * 80 / 60)
|
||||
|
@ -43,13 +43,13 @@ skin_no_small_gaps_heuristic = False
|
||||
skirt_brim_minimal_length = 100
|
||||
skirt_brim_speed = 25
|
||||
skirt_line_count = 2
|
||||
speed_layer_0 = 14
|
||||
speed_layer_0 = =math.ceil(speed_print * 14 / 40)
|
||||
speed_print = 40
|
||||
speed_slowdown_layers = 1
|
||||
speed_topbottom = 20
|
||||
speed_topbottom = =math.ceil(speed_print * 20 / 40)
|
||||
speed_travel = 120
|
||||
speed_travel_layer_0 = 60
|
||||
speed_wall = 25
|
||||
speed_wall_x = 35
|
||||
speed_wall = =math.ceil(speed_print * 25 / 40)
|
||||
speed_wall_x = =math.ceil(speed_print * 35 / 40)
|
||||
top_thickness = =top_bottom_thickness
|
||||
wall_thickness = 0.8
|
||||
|
@ -43,13 +43,13 @@ skin_no_small_gaps_heuristic = False
|
||||
skirt_brim_minimal_length = 100
|
||||
skirt_brim_speed = 25
|
||||
skirt_line_count = 2
|
||||
speed_layer_0 = 14
|
||||
speed_layer_0 = =math.ceil(speed_print * 14 / 40)
|
||||
speed_print = 40
|
||||
speed_slowdown_layers = 1
|
||||
speed_topbottom = 20
|
||||
speed_topbottom = =math.ceil(speed_print * 20 / 40)
|
||||
speed_travel = 120
|
||||
speed_travel_layer_0 = 60
|
||||
speed_wall = 25
|
||||
speed_wall_x = 35
|
||||
speed_wall = =math.ceil(speed_print * 25 / 40)
|
||||
speed_wall_x = =math.ceil(speed_print * 35 / 40)
|
||||
top_thickness = =top_bottom_thickness
|
||||
wall_thickness = 0.8
|
||||
|
@ -43,13 +43,13 @@ skin_no_small_gaps_heuristic = False
|
||||
skirt_brim_minimal_length = 100
|
||||
skirt_brim_speed = 25
|
||||
skirt_line_count = 2
|
||||
speed_layer_0 = 14
|
||||
speed_layer_0 = =math.ceil(speed_print * 14 / 40)
|
||||
speed_print = 40
|
||||
speed_slowdown_layers = 1
|
||||
speed_topbottom = 20
|
||||
speed_topbottom = =math.ceil(speed_print * 20 / 40)
|
||||
speed_travel = 120
|
||||
speed_travel_layer_0 = 60
|
||||
speed_wall = 25
|
||||
speed_wall_x = 35
|
||||
speed_wall = =math.ceil(speed_print * 25 / 40)
|
||||
speed_wall_x = =math.ceil(speed_print * 35 / 40)
|
||||
top_thickness = =top_bottom_thickness
|
||||
wall_thickness = 0.8
|
||||
|
@ -43,13 +43,13 @@ skin_no_small_gaps_heuristic = False
|
||||
skirt_brim_minimal_length = 100
|
||||
skirt_brim_speed = 25
|
||||
skirt_line_count = 2
|
||||
speed_layer_0 = 14
|
||||
speed_layer_0 = =math.ceil(speed_print * 14 / 40)
|
||||
speed_print = 40
|
||||
speed_slowdown_layers = 1
|
||||
speed_topbottom = 20
|
||||
speed_topbottom = =math.ceil(speed_print * 20 / 40)
|
||||
speed_travel = 120
|
||||
speed_travel_layer_0 = 60
|
||||
speed_wall = 25
|
||||
speed_wall_x = 35
|
||||
speed_wall = =math.ceil(speed_print * 25 / 40)
|
||||
speed_wall_x = =math.ceil(speed_print * 35 / 40)
|
||||
top_thickness = =top_bottom_thickness
|
||||
wall_thickness = 0.8
|
||||
|
@ -41,13 +41,13 @@ skin_no_small_gaps_heuristic = False
|
||||
skirt_brim_minimal_length = 100
|
||||
skirt_brim_speed = 20
|
||||
skirt_line_count = 3
|
||||
speed_layer_0 = 20
|
||||
speed_layer_0 = =math.ceil(speed_print * 20 / 45)
|
||||
speed_print = 45
|
||||
speed_slowdown_layers = 1
|
||||
speed_topbottom = 25
|
||||
speed_topbottom = =math.ceil(speed_print * 25 / 45)
|
||||
speed_travel = 120
|
||||
speed_travel_layer_0 = 60
|
||||
speed_wall = 25
|
||||
speed_wall_x = 35
|
||||
speed_wall = =math.ceil(speed_print * 25 / 45)
|
||||
speed_wall_x = =math.ceil(speed_print * 35 / 45)
|
||||
top_thickness = 0.8
|
||||
wall_thickness = 0.8
|
||||
|
@ -41,13 +41,13 @@ skin_no_small_gaps_heuristic = False
|
||||
skirt_brim_minimal_length = 100
|
||||
skirt_brim_speed = 20
|
||||
skirt_line_count = 3
|
||||
speed_layer_0 = 20
|
||||
speed_layer_0 = =math.ceil(speed_print * 20 / 45)
|
||||
speed_print = 45
|
||||
speed_slowdown_layers = 1
|
||||
speed_topbottom = 25
|
||||
speed_topbottom = =math.ceil(speed_print * 25 / 45)
|
||||
speed_travel = 120
|
||||
speed_travel_layer_0 = 60
|
||||
speed_wall = 25
|
||||
speed_wall_x = 35
|
||||
speed_wall = =math.ceil(speed_print * 25 / 45)
|
||||
speed_wall_x = =math.ceil(speed_print * 35 / 45)
|
||||
top_thickness = 0.8
|
||||
wall_thickness = 0.8
|
||||
|
@ -42,13 +42,13 @@ skin_no_small_gaps_heuristic = False
|
||||
skirt_brim_minimal_length = 100
|
||||
skirt_brim_speed = 20
|
||||
skirt_line_count = 3
|
||||
speed_layer_0 = 20
|
||||
speed_layer_0 = =math.ceil(speed_print * 20 / 45)
|
||||
speed_print = 45
|
||||
speed_slowdown_layers = 1
|
||||
speed_topbottom = 25
|
||||
speed_topbottom = =math.ceil(speed_print * 25 / 45)
|
||||
speed_travel = 120
|
||||
speed_travel_layer_0 = 60
|
||||
speed_wall = 25
|
||||
speed_wall_x = 35
|
||||
speed_wall = =math.ceil(speed_print * 25 / 45)
|
||||
speed_wall_x = =math.ceil(speed_print * 35 / 45)
|
||||
top_thickness = 0.8
|
||||
wall_thickness = 0.8
|
||||
|
@ -42,13 +42,13 @@ skin_no_small_gaps_heuristic = False
|
||||
skirt_brim_minimal_length = 100
|
||||
skirt_brim_speed = 20
|
||||
skirt_line_count = 3
|
||||
speed_layer_0 = 20
|
||||
speed_layer_0 = =math.ceil(speed_print * 20 / 45)
|
||||
speed_print = 45
|
||||
speed_slowdown_layers = 1
|
||||
speed_topbottom = 25
|
||||
speed_topbottom = =math.ceil(speed_print * 25 / 45)
|
||||
speed_travel = 120
|
||||
speed_travel_layer_0 = 60
|
||||
speed_wall = 25
|
||||
speed_wall_x = 35
|
||||
speed_wall = =math.ceil(speed_print * 25 / 45)
|
||||
speed_wall_x = =math.ceil(speed_print * 35 / 45)
|
||||
top_thickness = 0.8
|
||||
wall_thickness = 0.8
|
||||
|
@ -41,13 +41,13 @@ skin_no_small_gaps_heuristic = False
|
||||
skirt_brim_minimal_length = 100
|
||||
skirt_brim_speed = 20
|
||||
skirt_line_count = 3
|
||||
speed_layer_0 = 20
|
||||
speed_layer_0 = =math.ceil(speed_print * 20 / 45)
|
||||
speed_print = 45
|
||||
speed_slowdown_layers = 1
|
||||
speed_topbottom = 25
|
||||
speed_topbottom = =math.ceil(speed_print * 25 / 45)
|
||||
speed_travel = 120
|
||||
speed_travel_layer_0 = 60
|
||||
speed_wall = 25
|
||||
speed_wall_x = 35
|
||||
speed_wall = =math.ceil(speed_print * 25 / 45)
|
||||
speed_wall_x = =math.ceil(speed_print * 35 / 45)
|
||||
top_thickness = 0.8
|
||||
wall_thickness = 0.8
|
||||
|
@ -41,13 +41,13 @@ skin_no_small_gaps_heuristic = False
|
||||
skirt_brim_minimal_length = 100
|
||||
skirt_brim_speed = 20
|
||||
skirt_line_count = 3
|
||||
speed_layer_0 = 20
|
||||
speed_layer_0 = =math.ceil(speed_print * 20 / 45)
|
||||
speed_print = 45
|
||||
speed_slowdown_layers = 1
|
||||
speed_topbottom = 25
|
||||
speed_topbottom = =math.ceil(speed_print * 25 / 45)
|
||||
speed_travel = 120
|
||||
speed_travel_layer_0 = 60
|
||||
speed_wall = 25
|
||||
speed_wall_x = 35
|
||||
speed_wall = =math.ceil(speed_print * 25 / 45)
|
||||
speed_wall_x = =math.ceil(speed_print * 35 / 45)
|
||||
top_thickness = 0.8
|
||||
wall_thickness = 0.8
|
||||
|
@ -43,13 +43,13 @@ skin_no_small_gaps_heuristic = False
|
||||
skirt_brim_minimal_length = 100
|
||||
skirt_brim_speed = 20
|
||||
skirt_line_count = 3
|
||||
speed_layer_0 = 20
|
||||
speed_layer_0 = =math.ceil(speed_print * 20 / 45)
|
||||
speed_print = 45
|
||||
speed_slowdown_layers = 1
|
||||
speed_topbottom = 25
|
||||
speed_topbottom = =math.ceil(speed_print * 25 / 45)
|
||||
speed_travel = 120
|
||||
speed_travel_layer_0 = 60
|
||||
speed_wall = 25
|
||||
speed_wall_x = 35
|
||||
speed_wall = =math.ceil(speed_print * 25 / 45)
|
||||
speed_wall_x = =math.ceil(speed_print * 35 / 45)
|
||||
top_thickness = 0.8
|
||||
wall_thickness = 0.8
|
||||
|
@ -43,13 +43,13 @@ skin_no_small_gaps_heuristic = False
|
||||
skirt_brim_minimal_length = 100
|
||||
skirt_brim_speed = 20
|
||||
skirt_line_count = 3
|
||||
speed_layer_0 = 20
|
||||
speed_layer_0 = =math.ceil(speed_print * 20 / 45)
|
||||
speed_print = 45
|
||||
speed_slowdown_layers = 1
|
||||
speed_topbottom = 25
|
||||
speed_topbottom = =math.ceil(speed_print * 25 / 45)
|
||||
speed_travel = 120
|
||||
speed_travel_layer_0 = 60
|
||||
speed_wall = 25
|
||||
speed_wall_x = 35
|
||||
speed_wall = =math.ceil(speed_print * 25 / 45)
|
||||
speed_wall_x = =math.ceil(speed_print * 35 / 45)
|
||||
top_thickness = 0.8
|
||||
wall_thickness = 0.8
|
||||
|
@ -15,11 +15,11 @@ layer_height = 0.35
|
||||
layer_height_0 = 0.3
|
||||
|
||||
speed_print = 70
|
||||
speed_infill = 60
|
||||
speed_layer_0 = 20
|
||||
speed_wall_0 = 30
|
||||
speed_wall_x = 50
|
||||
speed_topbottom = 30
|
||||
speed_infill = =math.ceil(speed_print * 60 / 70)
|
||||
speed_layer_0 = =math.ceil(speed_print * 20 / 70)
|
||||
speed_wall_0 = =math.ceil(speed_print * 30 / 70)
|
||||
speed_wall_x = =math.ceil(speed_print * 50 / 70)
|
||||
speed_topbottom = =math.ceil(speed_print * 30 / 70)
|
||||
speed_travel = 120
|
||||
|
||||
material_print_temperature = 246
|
||||
|
@ -15,11 +15,11 @@ layer_height = 0.06
|
||||
layer_height_0 = 0.3
|
||||
|
||||
speed_print = 40
|
||||
speed_infill = 50
|
||||
speed_layer_0 = 15
|
||||
speed_wall_0 = 20
|
||||
speed_wall_x = 40
|
||||
speed_topbottom = 20
|
||||
speed_infill = =math.ceil(speed_print * 50 / 40)
|
||||
speed_layer_0 = =math.ceil(speed_print * 15 / 40)
|
||||
speed_wall_0 = =math.ceil(speed_print * 20 / 40)
|
||||
speed_wall_x = =speed_print
|
||||
speed_topbottom = =math.ceil(speed_print * 20 / 40)
|
||||
speed_travel = 120
|
||||
|
||||
material_print_temperature = 246
|
||||
|
@ -15,11 +15,11 @@ layer_height = 0.1
|
||||
layer_height_0 = 0.3
|
||||
|
||||
speed_print = 50
|
||||
speed_infill = 60
|
||||
speed_layer_0 = 20
|
||||
speed_wall_0 = 25
|
||||
speed_wall_x = 45
|
||||
speed_topbottom = 30
|
||||
speed_infill = =math.ceil(speed_print * 60 / 50)
|
||||
speed_layer_0 = =math.ceil(speed_print * 20 / 50)
|
||||
speed_wall_0 = =math.ceil(speed_print * 25 / 50)
|
||||
speed_wall_x = =math.ceil(speed_print * 45 / 50)
|
||||
speed_topbottom = =math.ceil(speed_print * 30 / 50)
|
||||
speed_travel = 120
|
||||
|
||||
material_print_temperature = 246
|
||||
|
@ -15,11 +15,11 @@ layer_height = 0.2
|
||||
layer_height_0 = 0.3
|
||||
|
||||
speed_print = 70
|
||||
speed_infill = 60
|
||||
speed_layer_0 = 20
|
||||
speed_wall_0 = 30
|
||||
speed_wall_x = 50
|
||||
speed_topbottom = 30
|
||||
speed_infill = =math.ceil(speed_print * 60 / 70)
|
||||
speed_layer_0 = =math.ceil(speed_print * 20 / 70)
|
||||
speed_wall_0 = =math.ceil(speed_print * 30 / 70)
|
||||
speed_wall_x = =math.ceil(speed_print * 50 / 70)
|
||||
speed_topbottom = =math.ceil(speed_print * 30 / 70)
|
||||
speed_travel = 120
|
||||
|
||||
material_print_temperature = 246
|
||||
|
@ -15,11 +15,11 @@ layer_height = 0.15
|
||||
layer_height_0 = 0.3
|
||||
|
||||
speed_print = 60
|
||||
speed_infill = 50
|
||||
speed_layer_0 = 15
|
||||
speed_wall_0 = 20
|
||||
speed_wall_x = 40
|
||||
speed_topbottom = 20
|
||||
speed_infill = =math.ceil(speed_print * 50 / 60)
|
||||
speed_layer_0 = =math.ceil(speed_print * 15 / 60)
|
||||
speed_wall_0 = =math.ceil(speed_print * 20 / 60)
|
||||
speed_wall_x = =math.ceil(speed_print * 40 / 60)
|
||||
speed_topbottom = =math.ceil(speed_print * 20 / 60)
|
||||
speed_travel = 120
|
||||
|
||||
material_print_temperature = 246
|
||||
|
@ -15,11 +15,11 @@ layer_height = 0.06
|
||||
adhesion_type = skirt
|
||||
|
||||
speed_print = 40
|
||||
speed_infill = 50
|
||||
speed_layer_0 = 15
|
||||
speed_wall_0 = 20
|
||||
speed_wall_x = 40
|
||||
speed_topbottom = 20
|
||||
speed_infill = =math.ceil(speed_print * 50 / 40)
|
||||
speed_layer_0 = =math.ceil(speed_print * 15 / 40)
|
||||
speed_wall_0 = =math.ceil(speed_print * 20 / 40)
|
||||
speed_wall_x = =speed_print
|
||||
speed_topbottom = =math.ceil(speed_print * 20 / 40)
|
||||
speed_travel = 120
|
||||
|
||||
material_print_temperature = 214
|
||||
|
@ -15,11 +15,11 @@ layer_height = 0.1
|
||||
adhesion_type = skirt
|
||||
|
||||
speed_print = 50
|
||||
speed_infill = 60
|
||||
speed_layer_0 = 20
|
||||
speed_wall_0 = 25
|
||||
speed_wall_x = 45
|
||||
speed_topbottom = 30
|
||||
speed_infill = =math.ceil(speed_print * 60 / 50)
|
||||
speed_layer_0 = =math.ceil(speed_print * 20 / 50)
|
||||
speed_wall_0 = =math.ceil(speed_print * 25 / 50)
|
||||
speed_wall_x = =math.ceil(speed_print * 45 / 50)
|
||||
speed_topbottom = =math.ceil(speed_print * 30 / 50)
|
||||
speed_travel = 120
|
||||
|
||||
material_print_temperature = 214
|
||||
|
@ -15,11 +15,11 @@ layer_height = 0.15
|
||||
adhesion_type = skirt
|
||||
|
||||
speed_print = 60
|
||||
speed_infill = 50
|
||||
speed_layer_0 = 15
|
||||
speed_wall_0 = 20
|
||||
speed_wall_x = 40
|
||||
speed_topbottom = 20
|
||||
speed_infill = =math.ceil(speed_print * 50 / 60)
|
||||
speed_layer_0 = =math.ceil(speed_print * 15 / 60)
|
||||
speed_wall_0 = =math.ceil(speed_print * 20 / 60)
|
||||
speed_wall_x = =math.ceil(speed_print * 40 / 60)
|
||||
speed_topbottom = =math.ceil(speed_print * 20 / 60)
|
||||
speed_travel = 120
|
||||
|
||||
material_print_temperature = 214
|
||||
|
@ -18,6 +18,6 @@ top_bottom_thickness = 0.72
|
||||
infill_sparse_density = 22
|
||||
speed_print = 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_speed = 10
|
||||
|
@ -18,6 +18,6 @@ top_bottom_thickness = 0.72
|
||||
infill_sparse_density = 22
|
||||
speed_print = 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_speed = 10
|
||||
|
@ -18,6 +18,6 @@ top_bottom_thickness = 0.72
|
||||
infill_sparse_density = 22
|
||||
speed_print = 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_speed = 10
|
||||
|
@ -18,6 +18,6 @@ top_bottom_thickness = 0.72
|
||||
infill_sparse_density = 22
|
||||
speed_print = 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_speed = 10
|
||||
|
@ -18,6 +18,6 @@ top_bottom_thickness = 0.72
|
||||
infill_sparse_density = 22
|
||||
speed_print = 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_speed = 10
|
||||
|
@ -18,6 +18,6 @@ top_bottom_thickness = 0.72
|
||||
infill_sparse_density = 22
|
||||
speed_print = 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_speed = 10
|
||||
|
@ -18,6 +18,6 @@ top_bottom_thickness = 0.72
|
||||
infill_sparse_density = 22
|
||||
speed_print = 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_speed = 10
|
||||
|
@ -18,6 +18,6 @@ top_bottom_thickness = 0.72
|
||||
infill_sparse_density = 22
|
||||
speed_print = 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_speed = 10
|
||||
|
@ -18,6 +18,6 @@ top_bottom_thickness = 0.72
|
||||
infill_sparse_density = 22
|
||||
speed_print = 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_speed = 10
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user